Improve:FfmpegThumbnail.cpp

This commit is contained in:
Fancy code 2024-07-20 15:00:47 +08:00
parent 35f8b4093b
commit 918210d695
5 changed files with 65 additions and 63 deletions

View File

@ -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) 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(); mTmpPkt = av_packet_alloc();
if (!mTmpPkt) { if (!mTmpPkt) {

View File

@ -46,7 +46,7 @@ public:
*/ */
FfmpegEncoder(const enum AVCodecID &codecId, const int &width, const int &height); FfmpegEncoder(const enum AVCodecID &codecId, const int &width, const int &height);
virtual ~FfmpegEncoder() = default; virtual ~FfmpegEncoder() = default;
bool Init(int &outputFlags); bool Init(const int &outputFlags);
void UnInit(void); void UnInit(void);
AVRational GetTimeBase(void); AVRational GetTimeBase(void);
bool OpenEncoder(AVDictionary *optArg, AVStream *stream); bool OpenEncoder(AVDictionary *optArg, AVStream *stream);

View File

@ -162,6 +162,9 @@ bool FfmpegOutputStream::CheckStreamHeader(const void *data, const size_t &size)
LogInfo("Found extradata\n"); LogInfo("Found extradata\n");
mH264Data2Jpeg = (char *)malloc(size + 1); mH264Data2Jpeg = (char *)malloc(size + 1);
memcpy(mH264Data2Jpeg, data, size); memcpy(mH264Data2Jpeg, data, size);
/**
* @brief Find the first I-frame and decode it ->encode it into a JPEG image file.
*/
FfmpegOutputStream::CreateThumbnailFile(mH264Data2Jpeg, size); FfmpegOutputStream::CreateThumbnailFile(mH264Data2Jpeg, size);
memcpy(extradata, pData, i); memcpy(extradata, pData, i);
mStream->codecpar->extradata = extradata; 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) void FfmpegOutputStream::CreateThumbnailFileThread(const void *frame, const size_t &size)
{ {
FfmpegThumbnail thumbnail(AV_CODEC_ID_MJPEG, AV_CODEC_ID_H264); 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.CreateThumbnail(mThumbnailFileName, frame, size);
thumbnail.UnInit(); thumbnail.UnInit();
LogInfo("CreateThumbnailFile end.\n"); LogInfo("CreateThumbnailFile end.\n");

View File

@ -41,15 +41,24 @@ extern "C" {
#include <memory> #include <memory>
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
FfmpegThumbnail::FfmpegThumbnail(const AVCodecID &encodecId, const AVCodecID &dncodecId) thumbnail_info::thumbnail_info(const int &srouceWidth, const int &srouceHeight, const int &targetWidth,
: mOutputFormat(nullptr), mStream(nullptr), sws_ctx(nullptr) const int &targetHeight)
: mSrouceWidth(srouceWidth), mSrouceHeight(srouceHeight), mTargetWidth(targetWidth), mTargetHeight(targetHeight)
{ {
mDecoder = std::make_shared<FfmpegDecoder>(dncodecId, 1920, 2160);
mEncoder = std::make_shared<FfmpegEncoder>(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"); LogInfo("FfmpegThumbnail Init\n");
mSrouceWidth = thumbnailInfo.mSrouceWidth;
mSrouceHeight = thumbnailInfo.mSrouceHeight;
mTargetWidth = thumbnailInfo.mTargetWidth;
mTargetHeight = thumbnailInfo.mTargetHeight;
mDecoder = std::make_shared<FfmpegDecoder>(mDecodecId, mSrouceWidth, mSrouceHeight);
mEncoder = std::make_shared<FfmpegEncoder>(mEncodecId, mTargetWidth, mTargetHeight);
} }
void FfmpegThumbnail::UnInit(void) void FfmpegThumbnail::UnInit(void)
{ {
@ -73,9 +82,9 @@ void FfmpegThumbnail::UnInit(void)
} }
avformat_free_context(mOutputFormat); avformat_free_context(mOutputFormat);
fx_system_v2("sync"); fx_system_v2("sync");
if (sws_ctx) { if (mSwsCtx) {
sws_freeContext(sws_ctx); sws_freeContext(mSwsCtx);
sws_ctx = nullptr; mSwsCtx = nullptr;
} }
} }
bool FfmpegThumbnail::CreateThumbnail(const std::string &outputFile, const void *data, const size_t &size) 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); mDecodeCallback = std::bind(&FfmpegThumbnail::GetDecodeDataCallback, this, std::placeholders::_1);
mEncodeCallback = std::bind(&FfmpegThumbnail::GetEncodeDataCallback, this, std::placeholders::_1, outputFile); mEncodeCallback = std::bind(&FfmpegThumbnail::GetEncodeDataCallback, this, std::placeholders::_1, outputFile);
AVDictionary *opt = nullptr;
int ret = 0;
/* allocate the output media context */ /* allocate the output media context */
avformat_alloc_output_context2(&mOutputFormat, nullptr, "image2", outputFile.c_str()); avformat_alloc_output_context2(&mOutputFormat, nullptr, "image2", outputFile.c_str());
if (!mOutputFormat) { 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 /* Add the audio and video streams using the default format codecs
* and initialize the codecs. */ * and initialize the codecs. */
if (mOutputFormat->oformat->video_codec != AV_CODEC_ID_NONE) { 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); mStream = avformat_new_stream(mOutputFormat, nullptr);
if (!mStream) { if (!mStream) {
LogError("Could not allocate stream\n"); 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; mStream->id = mOutputFormat->nb_streams - 1;
LogInfo("Create video stream\n"); 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(); mDecoder->Init();
mEncoder->Init(mOutputFormat->flags); constexpr int NO_FLAGS = 0;
mEncoder->Init(NO_FLAGS);
mStream->time_base = mEncoder->GetTimeBase(); mStream->time_base = mEncoder->GetTimeBase();
mEncoder->OpenEncoder(nullptr, mStream); mEncoder->OpenEncoder(nullptr, mStream);
LogInfo("Start to decode data\n"); LogInfo("Start to decode data\n");
@ -131,55 +129,41 @@ bool FfmpegThumbnail::CreateThumbnail(const std::string &outputFile, const void
void FfmpegThumbnail::GetDecodeDataCallback(AVFrame *frame) void FfmpegThumbnail::GetDecodeDataCallback(AVFrame *frame)
{ {
LogInfo("GetDecodeDataCallback frame->width = %d, frame->height=%d\n", frame->width, frame->height); LogInfo("GetDecodeDataCallback frame->width = %d, frame->height=%d\n", frame->width, frame->height);
AVFrame *output_frame = av_frame_alloc(); AVFrame *thumbnailFrame = av_frame_alloc();
output_frame->format = AV_PIX_FMT_YUV420P; thumbnailFrame->format = AV_PIX_FMT_YUV420P;
output_frame->width = 640; thumbnailFrame->width = mTargetWidth;
output_frame->height = 480; thumbnailFrame->height = mTargetHeight;
// output_frame->linesize[0] = 640; // Y plane 的跨度
// output_frame->linesize[1] = 320; // U/V planes 的跨度
// output_frame->linesize[2] = 320; // U/V planes 的跨度
int jpeg_buf_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, 1920, 2160, 1); int jpegBufSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, mSrouceWidth, mSrouceHeight, 1);
LogInfo("jpeg_buf_size: %d\n", jpeg_buf_size); LogInfo("jpegBufSize: %d\n", jpegBufSize);
uint8_t *jpeg_buf = (uint8_t *)av_malloc(jpeg_buf_size); uint8_t *jpegBuf = (uint8_t *)av_malloc(jpegBufSize);
av_image_fill_arrays( 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, mSwsCtx = sws_getContext(mSrouceWidth,
2160, mSrouceHeight,
static_cast<AVPixelFormat>(frame->format), static_cast<AVPixelFormat>(frame->format),
output_frame->width, thumbnailFrame->width,
output_frame->height, thumbnailFrame->height,
AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P,
SWS_BILINEAR, SWS_BILINEAR,
nullptr, nullptr,
nullptr, nullptr,
nullptr); nullptr);
// 进行像素格式转换 // Perform pixel format conversion.
sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, output_frame->data, output_frame->linesize); sws_scale(mSwsCtx, frame->data, frame->linesize, 0, frame->height, thumbnailFrame->data, thumbnailFrame->linesize);
if (mEncoder) { if (mEncoder) {
mEncoder->EncodeData(output_frame, mStream, mEncodeCallback); mEncoder->EncodeData(thumbnailFrame, mStream, mEncodeCallback);
// return;
} }
av_frame_free(&output_frame); av_frame_free(&thumbnailFrame);
av_free(jpeg_buf); av_free(jpegBuf);
return; return;
} }
void FfmpegThumbnail::GetEncodeDataCallback(AVPacket *pkt, const std::string &fileName) void FfmpegThumbnail::GetEncodeDataCallback(AVPacket *pkt, const std::string &fileName)
{ {
LogInfo("GetEncodeDataCallback, save thumbnail file %s\n", fileName.c_str()); LogInfo("GetEncodeDataCallback, save thumbnail file %s\n", fileName.c_str());
return; SaveThumbnailFile(fileName, pkt->data, pkt->size);
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));
}
} }
bool FfmpegThumbnail::SaveThumbnailFile(const std::string &fileName, const void *data, const size_t &size) bool FfmpegThumbnail::SaveThumbnailFile(const std::string &fileName, const void *data, const size_t &size)
{ {

View File

@ -36,12 +36,20 @@ extern "C" {
#endif #endif
#include <functional> #include <functional>
#include <memory> #include <memory>
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 class FfmpegThumbnail
{ {
public: public:
FfmpegThumbnail(const AVCodecID &encodecId, const AVCodecID &dncodecId); FfmpegThumbnail(const AVCodecID &encodecId, const AVCodecID &decodecId);
virtual ~FfmpegThumbnail() = default; virtual ~FfmpegThumbnail() = default;
void Init(void); void Init(const ThumbnailInfo &thumbnailInfo);
void UnInit(void); void UnInit(void);
bool CreateThumbnail(const std::string &outputFile, const void *data, const size_t &size); bool CreateThumbnail(const std::string &outputFile, const void *data, const size_t &size);
@ -59,6 +67,12 @@ private:
std::shared_ptr<FfmpegDecoder> mDecoder; std::shared_ptr<FfmpegDecoder> mDecoder;
AVFormatContext *mOutputFormat; AVFormatContext *mOutputFormat;
AVStream *mStream; AVStream *mStream;
struct SwsContext *sws_ctx; struct SwsContext *mSwsCtx;
int mSrouceWidth;
int mSrouceHeight;
int mTargetWidth;
int mTargetHeight;
const AVCodecID mEncodecId;
const AVCodecID mDecodecId;
}; };
#endif #endif