#include "VideoCoder.h" #include "Debuger.h" FILE *p = nullptr; int VideoCoder::OnBuffer(double dblSampleTime, BYTE * pBuffer, long lBufferSize) { this->Encode(pBuffer, lBufferSize, AV_PIX_FMT_YUV420P); return 0; } int VideoCoder::OnCameraData(uint8_t * dat, uint32_t size) { this->Encode(dat, size, AV_PIX_FMT_YUV420P); return 0; } int VideoCoder::SetDestPix(uint8_t width, uint8_t height) { this->mDestHeight = height; this->mDestWidth = width; return 0; } VideoCoder::VideoCoder(int width, int height, AVPixelFormat formt) :mObserver(nullptr),mFrame(nullptr), mPitureBuffer(nullptr), mFormatCtx(nullptr), mOutputFmt(nullptr), mVideoStream(nullptr), mCodecCtx(nullptr), mCodec(nullptr) { AVCodecID codec_id = AV_CODEC_ID_H264; mCodec = avcodec_find_encoder(codec_id); av_register_all(); if (nullptr == p) { p = fopen("shit.h264", "wb"); } this->mWidth = width; this->mHeight = height; this->mInformat = formt; if (!mCodec) { printf("Codec not found\n"); } this->mFormatCtx = avformat_alloc_context(); //原文链接:https ://blog.csdn.net/leixiaohua1020/article/details/25430425 引用来自雷神的文章,雷神保佑 this->mOutputFmt = av_guess_format(NULL, "shit.h264", NULL); this->mFormatCtx->oformat = mOutputFmt; mCodecCtx = avcodec_alloc_context3(mCodec); if (!mCodecCtx) { printf("Could not allocate video codec context\n"); } mCodecCtx->bit_rate = 1000; this->mDestHeight = 480; this->mDestWidth = 640; mCodecCtx->width = this->mDestWidth; mCodecCtx->height = this->mDestHeight; mCodecCtx->time_base.num = 1; mCodecCtx->time_base.den = 10; mCodecCtx->max_b_frames = 0; mCodecCtx->qmin = 10; mCodecCtx->qmax = 25; //mCodecCtx->flags |= AV_CODEC_FLAG_LOW_DELAY; mCodecCtx->gop_size = 10; mCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; av_opt_set(mCodecCtx->priv_data, "preset", "superfast", 0); av_opt_set(mCodecCtx->priv_data, "tune", "zerolatency", 0); if (avcodec_open2(mCodecCtx, mCodec, NULL) < 0) { printf("Could not open codec\n"); } mFrame = av_frame_alloc(); if (!mFrame) { printf("Could not allocate video frame\n"); } mFrame->format = mCodecCtx->pix_fmt; mFrame->width = mCodecCtx->width/2; mFrame->height = mCodecCtx->height/2; mFrame->pts = 0; int ret = av_image_alloc(mFrame->data, mFrame->linesize, mCodecCtx->width, mCodecCtx->height, mCodecCtx->pix_fmt, 8); if (ret < 0) { printf("Could not allocate raw picture buffer\n"); } // 让我们假设分辨率都是不可改变的,AvPack可以复用 avformat_write_header(mFormatCtx, NULL); int picture_size = avpicture_get_size(AV_PIX_FMT_YUV420P, mCodecCtx->width, mCodecCtx->height); } VideoCoder::~VideoCoder() { fclose(p); } void VideoCoder::Encode(uint8_t * src, int size, enum AVPixelFormat format) { uint8_t *pFrame[4]; int lineSize[4]; static int debugs = 1; //如果不是yuv420p就转成yuv420p int iFramesize; av_init_packet(&mAVPack); mAVPack.data = NULL; // packet data will be allocated by the encoder int ret = av_image_alloc(pFrame, lineSize, mWidth, mHeight, AV_PIX_FMT_YUV420P, 1); if (ret< 0) { Debuger::Debug(L"Could not allocate destination image\n"); } if (this->mInformat != AV_PIX_FMT_YUV420P || (this->mDestHeight != mHeight)) { int size = avpicture_get_size(this->mInformat,mWidth,mHeight); this->forceYUV420P(src, size, mInformat, (uint8_t ***)&pFrame,&iFramesize); //仅仅支持yuv420p mFrame->data[0] = pFrame[0]; //Y mFrame->data[1] = pFrame[1]; //U mFrame->data[2] = pFrame[2]; //V } else { mFrame->data[0] = src; //Y mFrame->data[1] = src + mWidth*mHeight; //U mFrame->data[2] = src + mWidth*mHeight + mWidth*mHeight/4; //V } //PTS mFrame->pts++; int got_picture = 0; //Encode avcodec_encode_video2(mCodecCtx, &mAVPack, mFrame, &got_picture); if (got_picture > 0) { if(nullptr != this->mObserver) this->mObserver->OnGetCodeFrame(mAVPack.data, mAVPack.size); } //Debuger::Debug(L"Succeed to encode frame: %5d\tsize:%5d\n", 1, mAVPack.size); //fwrite(mAVPack.data, 1, mAVPack.size, p); //fflush(p); // 刷新coder,防止包挤压 av_packet_unref(&mAVPack); av_freep(&pFrame[0]); free(pFrame[0]); //av_freep(&mFrame->data[0]); //av_freep(&mFrame->data[0]); } void VideoCoder::SetOutPutPixel(unsigned int width, unsigned int height) { this->mHeight = height; this->mWidth = width; } int VideoCoder::flushCoder(AVFormatContext *fmt_ctx, unsigned int stream_index) { int ret; int got_frame; AVPacket enc_pkt; if (!(this->mFormatCtx->streams[stream_index]->codec->codec->capabilities )) return 0; while (1) { enc_pkt.data = NULL; enc_pkt.size = 0; av_init_packet(&enc_pkt); ret = avcodec_encode_video2(fmt_ctx->streams[stream_index]->codec, &enc_pkt, NULL, &got_frame); av_frame_free(NULL); if (ret < 0) break; if (!got_frame) { ret = 0; break; } Debuger::Debug(L"Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n", enc_pkt.size); /* mux encoded frame */ ret = av_write_frame(fmt_ctx, &enc_pkt); if (ret < 0) break; } return ret; } // 强制把其他个数的数据转换成libav可以认得到的数据 int VideoCoder::forceYUV420P(uint8_t * src, int size, AVPixelFormat format,uint8_t **dst[4],int *len) { uint8_t *src_data[4]; int src_linesize[4]; uint8_t *dst_data[4]; int dst_linesize[4]; struct SwsContext *img_convert_ctx; int ret = 0; if (nullptr == dst || nullptr == len) { return -2; } int src_bpp = av_get_bits_per_pixel(av_pix_fmt_desc_get(format)); AVPixelFormat dst_pixfmt = AV_PIX_FMT_YUV420P; int dst_bpp = av_get_bits_per_pixel(av_pix_fmt_desc_get(dst_pixfmt)); ret = av_image_alloc(src_data, src_linesize, mWidth, mHeight, format, 1); if (ret< 0) { Debuger::Debug(L"Could not allocate source image\n"); return -1; } ret = av_image_alloc(dst_data, dst_linesize, mDestWidth, mDestHeight, AV_PIX_FMT_YUV420P, 1); if (ret< 0) { Debuger::Debug(L"Could not allocate destination image\n"); return -1; } img_convert_ctx = sws_alloc_context(); //Show AVOption //av_opt_show2(img_convert_ctx, stdout, AV_OPT_FLAG_VIDEO_PARAM, 0); //Set Value av_opt_set_int(img_convert_ctx, "sws_flags", SWS_BICUBIC | SWS_PRINT_INFO, 0); av_opt_set_int(img_convert_ctx, "srcw", mWidth, 0); av_opt_set_int(img_convert_ctx, "srch", mHeight, 0); av_opt_set_int(img_convert_ctx, "src_format", format, 0); av_opt_set_int(img_convert_ctx, "src_range", 1, 0); av_opt_set_int(img_convert_ctx, "dstw", mDestWidth, 0); av_opt_set_int(img_convert_ctx, "dsth", mDestHeight, 0); av_opt_set_int(img_convert_ctx, "dst_format", dst_pixfmt, 0); av_opt_set_int(img_convert_ctx, "dst_range", 1, 0); sws_init_context(img_convert_ctx, NULL, NULL); // 设置输入 switch (format) { case AV_PIX_FMT_GRAY8: { memcpy(src_data[0], src, mWidth*mHeight); break; } case AV_PIX_FMT_YUV420P: { memcpy(src_data[0], src, mWidth*mHeight); //Y memcpy(src_data[1], src + mWidth*mHeight, mWidth*mHeight / 4); //U memcpy(src_data[2], src + mWidth*mHeight * 5 / 4, mWidth*mHeight / 4); //V break; } case AV_PIX_FMT_YUV422P: { memcpy(src_data[0], src, mWidth*mHeight); //Y memcpy(src_data[1], src + mWidth*mHeight, mWidth*mHeight / 2); //U memcpy(src_data[2], src + mWidth*mHeight * 3 / 2, mWidth*mHeight / 2); //V break; } case AV_PIX_FMT_YUV444P: { memcpy(src_data[0], src, mWidth*mHeight); //Y memcpy(src_data[1], src + mWidth*mHeight, mWidth*mHeight); //U memcpy(src_data[2], src + mWidth*mHeight * 2, mWidth*mHeight); //V break; } case AV_PIX_FMT_YUYV422: { memcpy(src_data[0], src, mWidth*mHeight * 2); //Packed break; } case AV_PIX_FMT_RGB24: { memcpy(src_data[0], src, mWidth*mHeight * 3); //Packed break; } case AV_PIX_FMT_RGB32: { memcpy(src_data[0], src, mWidth*mHeight *4); //Packed break; } default: { Debuger::Debug(L"Not Support Input Pixel Format.\n"); break; } } // 转换数据 ret = sws_scale(img_convert_ctx, src_data, src_linesize, 0, mHeight, dst_data, dst_linesize); if (ret < 0) { return ret; } memcpy(dst[0], dst_data[0], mDestWidth*mDestHeight); memcpy(dst[1], dst_data[1], mDestWidth*mDestHeight /4); memcpy(dst[2], dst_data[2], mDestWidth*mDestHeight /4); *len = mDestWidth*mDestHeight + mDestWidth*mDestHeight / 2; // source此时就不需要了,但是dst要在外面free av_freep(&src_data[0]); av_freep(&dst_data[0]); sws_freeContext(img_convert_ctx); return 0; }