最简单的基于FFMPEG 4.4 的图形编码器(参考雷神的文章)

最简单的基于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 4.4 的图形编码器(参考雷神的文章)_第1张图片

搜索ffmpeg 代码发现:

最简单的基于FFMPEG 4.4 的图形编码器(参考雷神的文章)_第2张图片

 因此,ffmpeg 的 mpjeg 编码器只支持特定类型的pix_fmt。

其实所有的编码器,已经表明了自己需要的类型:

最简单的基于FFMPEG 4.4 的图形编码器(参考雷神的文章)_第3张图片

 这里显示支持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下载

你可能感兴趣的:(ffmpeg,学习,音视频)