最简单的基础FFMPEG图像编码器(YUV编码为JPEG)_雷霄华博客-CSDN博客_ffmpeg 编码图片
简介:参考雷神的文章。实现最简单的视频编码器。
ffmpeg 版本:Releases · ShiftMediaProject/FFmpeg · GitHub
4.4 最新版本。
一、加载yuv 到avframe:
注意这里写死的yuv420p 960* 400
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <thread> #include <vector> #include <functional> #include <cassert> #include <chrono> #include <string> extern "C" { #include <libswresample/swresample.h> #include <libavformat/avformat.h> #include <libavutil/imgutils.h> #include <libswscale/swscale.h> #include <libavcodec/avcodec.h> #include <libavcodec/codec.h> #include <libswscale/swscale.h> #include <libavutil/opt.h> } #pragma comment(lib, "avutil.lib") #pragma comment(lib, "avcodec.lib") #pragma comment(lib, "avformat.lib") #pragma comment(lib, "swscale.lib") std::string GetFFmpegErorString(int errnum) { char g_av_error[AV_ERROR_MAX_STRING_SIZE] = { 0 }; return std::string(av_make_error_string(g_av_error, AV_ERROR_MAX_STRING_SIZE, errnum)); } std::string strInputYUVFileName = "yuv420p_960_400_99_yuv420.yuv"; int width = 960; int height = 400; AVPixelFormat format = AVPixelFormat::AV_PIX_FMT_YUV420P; AVFrame* load_yuv_file_to_avframe(std::string filePath) { AVFrame* avFrameYUV = nullptr; FILE* fReadYUV = nullptr; do { FILE* fReadYUV = fopen(strInputYUVFileName.c_str(), "rb"); if (!fReadYUV) { std::cout << "Failed to open file[" << strInputYUVFileName << "]" << std::endl; break; } avFrameYUV = av_frame_alloc();// 需要使用av_frame_free 释放,要重复使用,需要使用av_frame_unref 将avFrame 设置为初始默认值 int bufferSize = av_image_alloc(avFrameYUV->data, avFrameYUV->linesize, width, height, format, 1); if (bufferSize < 0) { std::cout << GetFFmpegErorString(bufferSize) << std::endl; av_frame_free(&avFrameYUV); break; } avFrameYUV->format = format; avFrameYUV->width = width; avFrameYUV->height = height; fread(avFrameYUV->data[0], 1, avFrameYUV->linesize[0] * avFrameYUV->height, fReadYUV); fread(avFrameYUV->data[1], 1, avFrameYUV->linesize[1] * avFrameYUV->height, fReadYUV); fread(avFrameYUV->data[2], 1, avFrameYUV->linesize[2] * avFrameYUV->height, fReadYUV); } while (0); if (fReadYUV) { fclose(fReadYUV); fReadYUV = nullptr; } return avFrameYUV; }
二、保存avFrame 为jpeg 文件
int save_frame_as_mjpeg(AVFrame* pFrame) { std::string strName = "output.jpeg"; if (!pFrame) { return AVERROR(EINVAL); } const AVCodec* jpegCodec = avcodec_find_encoder(AV_CODEC_ID_MJPEG); if (!jpegCodec) { return AVERROR(EINVAL); } AVCodecContext* jpegContext = avcodec_alloc_context3(jpegCodec); if (!jpegContext) { return AVERROR(EINVAL); } jpegContext->pix_fmt = (AVPixelFormat)pFrame->format; jpegContext->height = pFrame->height; jpegContext->width = pFrame->width; jpegContext->time_base.num = 1; jpegContext->time_base.den = 30; jpegContext->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL; int ret = 0; if ((ret = avcodec_open2(jpegContext, jpegCodec, NULL)) < 0) { std::cout << GetFFmpegErorString(ret) << std::endl; return -1; } FILE* JPEGFile; char JPEGFName[256]; AVPacket* packet = av_packet_alloc(); int gotFrame; ret = avcodec_send_frame(jpegContext, pFrame); if (ret < 0) { std::cout << GetFFmpegErorString(ret) << std::endl; return -1; } ret = avcodec_receive_packet(jpegContext, packet); if (ret < 0) { std::cout << GetFFmpegErorString(ret) << std::endl; return -1; } JPEGFile = fopen(strName.c_str(), "wb"); fwrite(packet->data, 1, packet->size, JPEGFile); fclose(JPEGFile); av_packet_free(&packet); avcodec_close(jpegContext); return 0; }
报告以下错误:

搜索ffmpeg 代码发现:
因此,ffmpeg 的 mpjeg 编码器只支持特定类型的编码器pix_fmt。
事实上,所有的编码器都显示了他们需要的类型:
显示支持yuv420p,但需要特定的参数。试着修改:
int save_frame_as_mjpeg(AVFrame* pFrame) { std::string strName = "output.jpeg"; if (!pFrame) { return AVERROR(EINVAL); } const AVCodec* jpegCodec = avcodec_find_encoder(AV_CODEC_ID_MJPEG); if (!jpegCodec) { return AVERROR(EINVAL); } AVCodecContext* jpegContext = avcodec_alloc_context3(jpegCodec); if (!jpegContext) { return AVERROR(EINVAL); } jpegContext->pix_fmt = (AVPixelFormat)pFrame->format; jpegContext->height = pFrame->height; jpegCotext->width = pFrame->width;
jpegContext->time_base.num = 1;
jpegContext->time_base.den = 30;
jpegContext->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL;
int ret = 0;
if ((ret = avcodec_open2(jpegContext, jpegCodec, NULL)) < 0) {
std::cout << GetFFmpegErorString(ret) << std::endl;
return -1;
}
FILE* JPEGFile;
char JPEGFName[256];
AVPacket* packet = av_packet_alloc();
int gotFrame;
ret = avcodec_send_frame(jpegContext, pFrame);
if (ret < 0) {
std::cout << GetFFmpegErorString(ret) << std::endl;
return -1;
}
ret = avcodec_receive_packet(jpegContext, packet);
if (ret < 0) {
std::cout << GetFFmpegErorString(ret) << std::endl;
return -1;
}
JPEGFile = fopen(strName.c_str(), "wb");
fwrite(packet->data, 1, packet->size, JPEGFile);
fclose(JPEGFile);
av_packet_unref(packet);
avcodec_close(jpegContext);
return 0;
}
成功了。
另外:ffplay -video_size 960x400 -pixel_format yuv420p -i D:\learn\FFmpegEncoder\x64\Release\yuv420p_960_400_99_yuv420.yuv 可以使用ffplay 来播放指定的文件。
ffmpegavframetojpeg-编解码文档类资源-CSDN下载