diff --git a/utils/MediaBase/src/FfmpegEncoder.cpp b/utils/MediaBase/src/FfmpegEncoder.cpp index 3ea3cb9..78a128e 100644 --- a/utils/MediaBase/src/FfmpegEncoder.cpp +++ b/utils/MediaBase/src/FfmpegEncoder.cpp @@ -53,7 +53,7 @@ FfmpegEncoder::FfmpegEncoder(const enum AVCodecID &codecId, const int &width, co mSamplesCount(0), mSwrCtx(nullptr), next_pts(0), mVideoWidth(width), mVideoHeight(height) { } -bool FfmpegEncoder::Init(int &outputFlags) +bool FfmpegEncoder::Init(const int &outputFlags) { mTmpPkt = av_packet_alloc(); if (!mTmpPkt) { diff --git a/utils/MediaBase/src/FfmpegEncoder.h b/utils/MediaBase/src/FfmpegEncoder.h index 642953e..cffcfe4 100644 --- a/utils/MediaBase/src/FfmpegEncoder.h +++ b/utils/MediaBase/src/FfmpegEncoder.h @@ -46,7 +46,7 @@ public: */ FfmpegEncoder(const enum AVCodecID &codecId, const int &width, const int &height); virtual ~FfmpegEncoder() = default; - bool Init(int &outputFlags); + bool Init(const int &outputFlags); void UnInit(void); AVRational GetTimeBase(void); bool OpenEncoder(AVDictionary *optArg, AVStream *stream); diff --git a/utils/MediaBase/src/FfmpegOutputStream.cpp b/utils/MediaBase/src/FfmpegOutputStream.cpp index f84892a..3df3b78 100644 --- a/utils/MediaBase/src/FfmpegOutputStream.cpp +++ b/utils/MediaBase/src/FfmpegOutputStream.cpp @@ -162,6 +162,9 @@ bool FfmpegOutputStream::CheckStreamHeader(const void *data, const size_t &size) LogInfo("Found extradata\n"); mH264Data2Jpeg = (char *)malloc(size + 1); memcpy(mH264Data2Jpeg, data, size); + /** + * @brief Find the first I-frame and decode it ->encode it into a JPEG image file. + */ FfmpegOutputStream::CreateThumbnailFile(mH264Data2Jpeg, size); memcpy(extradata, pData, i); mStream->codecpar->extradata = extradata; @@ -196,7 +199,8 @@ void FfmpegOutputStream::CreateThumbnailFile(const void *frame, const size_t &si void FfmpegOutputStream::CreateThumbnailFileThread(const void *frame, const size_t &size) { FfmpegThumbnail thumbnail(AV_CODEC_ID_MJPEG, AV_CODEC_ID_H264); - thumbnail.Init(); + ThumbnailInfo info(1920, 2160, 640, 480); // TODO: + thumbnail.Init(info); thumbnail.CreateThumbnail(mThumbnailFileName, frame, size); thumbnail.UnInit(); LogInfo("CreateThumbnailFile end.\n"); diff --git a/utils/MediaBase/src/FfmpegThumbnail.cpp b/utils/MediaBase/src/FfmpegThumbnail.cpp index a511b93..502648c 100644 --- a/utils/MediaBase/src/FfmpegThumbnail.cpp +++ b/utils/MediaBase/src/FfmpegThumbnail.cpp @@ -41,15 +41,24 @@ extern "C" { #include #include #include -FfmpegThumbnail::FfmpegThumbnail(const AVCodecID &encodecId, const AVCodecID &dncodecId) - : mOutputFormat(nullptr), mStream(nullptr), sws_ctx(nullptr) +thumbnail_info::thumbnail_info(const int &srouceWidth, const int &srouceHeight, const int &targetWidth, + const int &targetHeight) + : mSrouceWidth(srouceWidth), mSrouceHeight(srouceHeight), mTargetWidth(targetWidth), mTargetHeight(targetHeight) { - mDecoder = std::make_shared(dncodecId, 1920, 2160); - mEncoder = std::make_shared(encodecId, 640, 480); } -void FfmpegThumbnail::Init(void) +FfmpegThumbnail::FfmpegThumbnail(const AVCodecID &encodecId, const AVCodecID &decodecId) + : mOutputFormat(nullptr), mStream(nullptr), mSwsCtx(nullptr), mEncodecId(encodecId), mDecodecId(decodecId) +{ +} +void FfmpegThumbnail::Init(const ThumbnailInfo &thumbnailInfo) { LogInfo("FfmpegThumbnail Init\n"); + mSrouceWidth = thumbnailInfo.mSrouceWidth; + mSrouceHeight = thumbnailInfo.mSrouceHeight; + mTargetWidth = thumbnailInfo.mTargetWidth; + mTargetHeight = thumbnailInfo.mTargetHeight; + mDecoder = std::make_shared(mDecodecId, mSrouceWidth, mSrouceHeight); + mEncoder = std::make_shared(mEncodecId, mTargetWidth, mTargetHeight); } void FfmpegThumbnail::UnInit(void) { @@ -73,9 +82,9 @@ void FfmpegThumbnail::UnInit(void) } avformat_free_context(mOutputFormat); fx_system_v2("sync"); - if (sws_ctx) { - sws_freeContext(sws_ctx); - sws_ctx = nullptr; + if (mSwsCtx) { + sws_freeContext(mSwsCtx); + mSwsCtx = nullptr; } } bool FfmpegThumbnail::CreateThumbnail(const std::string &outputFile, const void *data, const size_t &size) @@ -86,8 +95,6 @@ bool FfmpegThumbnail::CreateThumbnail(const std::string &outputFile, const void } mDecodeCallback = std::bind(&FfmpegThumbnail::GetDecodeDataCallback, this, std::placeholders::_1); mEncodeCallback = std::bind(&FfmpegThumbnail::GetEncodeDataCallback, this, std::placeholders::_1, outputFile); - AVDictionary *opt = nullptr; - int ret = 0; /* allocate the output media context */ avformat_alloc_output_context2(&mOutputFormat, nullptr, "image2", outputFile.c_str()); if (!mOutputFormat) { @@ -97,6 +104,10 @@ bool FfmpegThumbnail::CreateThumbnail(const std::string &outputFile, const void /* Add the audio and video streams using the default format codecs * and initialize the codecs. */ if (mOutputFormat->oformat->video_codec != AV_CODEC_ID_NONE) { + /** + * @brief Maybe there is no need to use avformat_alloc_output_context2 function to create ffmpeg container. + * TODO: if mOutputFormat can be deleted here? + */ mStream = avformat_new_stream(mOutputFormat, nullptr); if (!mStream) { LogError("Could not allocate stream\n"); @@ -105,22 +116,9 @@ bool FfmpegThumbnail::CreateThumbnail(const std::string &outputFile, const void mStream->id = mOutputFormat->nb_streams - 1; LogInfo("Create video stream\n"); } - av_dump_format(mOutputFormat, 0, outputFile.c_str(), 1); - LogInfo("Open output file\n"); - // } - av_dict_set_int(&opt, "use_editlist", 0, 0); - - /* Write the stream header, if any. */ - ret = avformat_write_header(mOutputFormat, &opt); - if (ret < 0) { - char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; - LogError("Error occurred when opening output file: %s\n", - av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); - return false; - } - av_dict_free(&opt); mDecoder->Init(); - mEncoder->Init(mOutputFormat->flags); + constexpr int NO_FLAGS = 0; + mEncoder->Init(NO_FLAGS); mStream->time_base = mEncoder->GetTimeBase(); mEncoder->OpenEncoder(nullptr, mStream); LogInfo("Start to decode data\n"); @@ -131,55 +129,41 @@ bool FfmpegThumbnail::CreateThumbnail(const std::string &outputFile, const void void FfmpegThumbnail::GetDecodeDataCallback(AVFrame *frame) { LogInfo("GetDecodeDataCallback frame->width = %d, frame->height=%d\n", frame->width, frame->height); - AVFrame *output_frame = av_frame_alloc(); - output_frame->format = AV_PIX_FMT_YUV420P; - output_frame->width = 640; - output_frame->height = 480; - // output_frame->linesize[0] = 640; // Y plane 的跨度 - // output_frame->linesize[1] = 320; // U/V planes 的跨度 - // output_frame->linesize[2] = 320; // U/V planes 的跨度 + AVFrame *thumbnailFrame = av_frame_alloc(); + thumbnailFrame->format = AV_PIX_FMT_YUV420P; + thumbnailFrame->width = mTargetWidth; + thumbnailFrame->height = mTargetHeight; - int jpeg_buf_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 1920, 2160, 1); - LogInfo("jpeg_buf_size: %d\n", jpeg_buf_size); - uint8_t *jpeg_buf = (uint8_t *)av_malloc(jpeg_buf_size); + int jpegBufSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, mSrouceWidth, mSrouceHeight, 1); + LogInfo("jpegBufSize: %d\n", jpegBufSize); + uint8_t *jpegBuf = (uint8_t *)av_malloc(jpegBufSize); av_image_fill_arrays( - output_frame->data, output_frame->linesize, jpeg_buf, AV_PIX_FMT_YUV420P, frame->width, frame->height, 1); + thumbnailFrame->data, thumbnailFrame->linesize, jpegBuf, AV_PIX_FMT_YUV420P, frame->width, frame->height, 1); - sws_ctx = sws_getContext(1920, - 2160, + mSwsCtx = sws_getContext(mSrouceWidth, + mSrouceHeight, static_cast(frame->format), - output_frame->width, - output_frame->height, + thumbnailFrame->width, + thumbnailFrame->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, nullptr, nullptr, nullptr); - // 进行像素格式转换 - sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, output_frame->data, output_frame->linesize); + // Perform pixel format conversion. + sws_scale(mSwsCtx, frame->data, frame->linesize, 0, frame->height, thumbnailFrame->data, thumbnailFrame->linesize); if (mEncoder) { - mEncoder->EncodeData(output_frame, mStream, mEncodeCallback); - // return; + mEncoder->EncodeData(thumbnailFrame, mStream, mEncodeCallback); } - av_frame_free(&output_frame); - av_free(jpeg_buf); + av_frame_free(&thumbnailFrame); + av_free(jpegBuf); return; } void FfmpegThumbnail::GetEncodeDataCallback(AVPacket *pkt, const std::string &fileName) { LogInfo("GetEncodeDataCallback, save thumbnail file %s\n", fileName.c_str()); - return; - int ret = 0; - ret = av_interleaved_write_frame(mOutputFormat, pkt); - /* pkt is now blank (av_interleaved_write_frame() takes ownership of - * its contents and resets pkt), so that no unreferencing is necessary. - * This would be different if one used av_write_frame(). */ - if (ret < 0) { - char error_str[AV_ERROR_MAX_STRING_SIZE] = {0}; - LogInfo("Error while writing output packet: %s\n", - av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret)); - } + SaveThumbnailFile(fileName, pkt->data, pkt->size); } bool FfmpegThumbnail::SaveThumbnailFile(const std::string &fileName, const void *data, const size_t &size) { diff --git a/utils/MediaBase/src/FfmpegThumbnail.h b/utils/MediaBase/src/FfmpegThumbnail.h index d23e2c3..12d2b18 100644 --- a/utils/MediaBase/src/FfmpegThumbnail.h +++ b/utils/MediaBase/src/FfmpegThumbnail.h @@ -36,12 +36,20 @@ extern "C" { #endif #include #include +typedef struct thumbnail_info +{ + thumbnail_info(const int &srouceWidth, const int &srouceHeight, const int &targetWidth, const int &targetHeight); + const int mSrouceWidth; + const int mSrouceHeight; + const int mTargetWidth; + const int mTargetHeight; +} ThumbnailInfo; class FfmpegThumbnail { public: - FfmpegThumbnail(const AVCodecID &encodecId, const AVCodecID &dncodecId); + FfmpegThumbnail(const AVCodecID &encodecId, const AVCodecID &decodecId); virtual ~FfmpegThumbnail() = default; - void Init(void); + void Init(const ThumbnailInfo &thumbnailInfo); void UnInit(void); bool CreateThumbnail(const std::string &outputFile, const void *data, const size_t &size); @@ -59,6 +67,12 @@ private: std::shared_ptr mDecoder; AVFormatContext *mOutputFormat; AVStream *mStream; - struct SwsContext *sws_ctx; + struct SwsContext *mSwsCtx; + int mSrouceWidth; + int mSrouceHeight; + int mTargetWidth; + int mTargetHeight; + const AVCodecID mEncodecId; + const AVCodecID mDecodecId; }; #endif \ No newline at end of file