最简单的基于FFMPEG的图像编码器(YUV编码为JPEG)_雷霄骅的博客-CSDN博客_ffmpeg 编码图片
简介:参考雷神的文章。实现一个最简单的视频编码器。
ffmpeg 版本:Releases · ShiftMediaProject/FFmpeg · GitHub
4.4 版本最新的。
一、加载yuv 到avframe:
注意这里写死的yuv420p 960 * 400
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
extern "C"
{
#include
#include
#include
#include
#include
#include
#include
#include
}
#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;
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_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下载