• FFmpeg RGB转MP4


    下面是一个读取本地 RGB 文件,转换并输出 MP4 视频文件的一个例子,具体步骤如下:

    1、创建编码器

    2、创建输出视频上下文

    3、添加视频流

    4、rgb转yuv

    5、写视频文件头

    6、写视频文件,循环内部进行H264编码

    完整代码如下:

    #include <iostream>
    
    extern "C"
    {
    	#include <libavformat/avformat.h>
    	#include <libswscale/swscale.h>
    }
    
    #pragma comment(lib,"avformat.lib")
    #pragma comment(lib,"avcodec.lib")
    #pragma comment(lib,"avutil.lib")
    #pragma comment(lib,"swscale.lib")
    
    using namespace std;
    
    int main()
    {
    	char infile[] = "out.rgb";
    	char outfile[] = "rgb.mp4";
    
    	// 注册所有和编解码器有关的组件
    	av_register_all();
    
    	// 打开RGB文件
    	FILE *fp = fopen(infile, "rb");
    	if (!fp)
    	{
    		cout << infile << " open failed!" << endl;
    		getchar();
    		return -1;
    	}
    
    	// 源图像参数
    	int width = 848;
    	int height = 480;
    	int fps = 25;
    
    	///1 创建编码器
    	// 查找编码器
    	AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    	if (!codec)
    	{
    		cout << " avcodec_find_encoder AV_CODEC_ID_H264 failed!" << endl;
    		getchar();
    		return -1;
    	}
    
    	// 给编码器分配内存,返回对应编码器上下文
    	AVCodecContext *c = avcodec_alloc_context3(codec);
    	if (!c)
    	{
    		cout << " avcodec_alloc_context3  failed!" << endl;
    		getchar();
    		return -1;
    	}
    	// 配置编码器上下文的成员
    	c->bit_rate = 400000000; // 比特率(码率),越高视频质量越好
    	c->width = width; // 设置编码视频宽度 
    	c->height = height; // 设置编码视频高度
    	c->time_base.num = 1;
    	c->time_base.den = 25; // 设置帧率,num为分子,den为分母,如果是1/25则表示25帧/s
    
    	c->gop_size = 50; // 画面组大小,关键帧
    	c->max_b_frames = 0; // 设置B帧最大数,该值表示在两个非B帧之间,所允许插入的B帧的最大帧数
    
    	c->pix_fmt = AV_PIX_FMT_YUV420P; // 设置输出像素格式
    	c->codec_id = AV_CODEC_ID_H264; // 设置编码格式
    	c->thread_count = 8; // 线程数量
    	c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; // 全局的编码信息
    
    	// 打开编码器
    	int ret = avcodec_open2(c, codec, NULL);
    	if (ret < 0)
    	{
    		cout << " avcodec_open2  failed!" << endl;
    		getchar();
    		return -1;
    	}
    	cout << "avcodec_open2 success!" << endl;
    
    	///2 创建输出视频上下文
    	AVFormatContext *oc = NULL;
    	avformat_alloc_output_context2(&oc, 0, 0, outfile);
    
    	///3 添加视频流
    	AVStream *st = avformat_new_stream(oc, NULL);
    	st->id = 0;
    	st->codecpar->codec_tag = 0;
    	// 将AVCodecContext信息拷贝到AVCodecParameterst结构体中
    	avcodec_parameters_from_context(st->codecpar, c);
    
    	cout << "===============================================" << endl;
    	// 打印AVFormatContext的内容
    	av_dump_format(oc, 0, outfile, 1);
    	cout << "===============================================" << endl;
    
    	///4 rgb转yuv
    	SwsContext *ctx = NULL;
    	ctx = sws_getCachedContext(ctx,
    		width, height, AV_PIX_FMT_BGRA,
    		width, height, AV_PIX_FMT_YUV420P,
    		SWS_BICUBIC,
    		NULL, NULL, NULL
    		);
    	// 分配输入空间
    	unsigned char *rgb = new unsigned char[width*height * 4];
    
    	// 分配输出空间
    	AVFrame *yuv = av_frame_alloc();
    	yuv->format = AV_PIX_FMT_YUV420P;
    	yuv->width = width;
    	yuv->height = height;
    	ret = av_frame_get_buffer(yuv, 32);
    	if (ret < 0)
    	{
    		cout << " av_frame_get_buffer  failed!" << endl;
    		getchar();
    		return -1;
    	}
    
    	///5 写视频文件头
    	// 创建并初始化AVIOContext以访问outfile指示的资源
    	ret = avio_open(&oc->pb, outfile, AVIO_FLAG_WRITE);
    	if (ret < 0)
    	{
    		cout << " avio_open  failed!" << endl;
    		getchar();
    		return -1;
    	}
    	// 写视频文件头
    	ret = avformat_write_header(oc, NULL);
    	if (ret < 0)
    	{
    		cout << " avformat_write_header  failed!" << endl;
    		getchar();
    		return -1;
    	}
    
    	// 循环写视频文件
    	int pts = 0;
    	for (;;)
    	{
    		// 读取RGB文件的像素数据长度
    		int len = fread(rgb, 1, width*height * 4, fp);
    		if (len <= 0)
    		{
    			break;
    		}
    
    		// 像素格式转换:RGB转YUV
    		uint8_t *indata[AV_NUM_DATA_POINTERS] = { 0 };
    		indata[0] = rgb;
    		int inlinesize[AV_NUM_DATA_POINTERS] = { 0 };
    		inlinesize[0] = width * 4;
    		int h = sws_scale(ctx, indata, inlinesize, 0, height,
    			yuv->data, yuv->linesize
    			);
    		if (h <= 0)
    			break;
    
    		///6 H264编码
    		// 将未压缩的AVFrame数据(yuv)给编码器
    		yuv->pts = pts;
    		//yuv->pict_type = AV_PICTURE_TYPE_I;
    		pts = pts + 3600;
    		ret = avcodec_send_frame(c, yuv);
    		if (ret != 0)
    		{
    			continue;
    		}
    		// 将编码数据保存在AVPacket
    		AVPacket pkt;
    		av_init_packet(&pkt);
    		ret = avcodec_receive_packet(c, &pkt);
    		if (ret != 0)
    			continue;
    
    		// 将AVPacket写入输出媒体文件
    		//av_write_frame(oc, &pkt);
    		//av_packet_unref(&pkt);
    		av_interleaved_write_frame(oc, &pkt);
    
    		cout << "<"<<pkt.size<<">";
    	}
    	
    	//写入视频索引
    	av_write_trailer(oc);
    
    	//关闭视频输出io
    	avio_close(oc->pb);
    
    	//清理封装输出上下文
    	avformat_free_context(oc);
    
    	//关闭编码器
    	avcodec_close(c);
    
    	//清理编码器上下文
    	avcodec_free_context(&c);
    
    	//清理视频重采样上下文
    	sws_freeContext(ctx);
    
    	cout << "======================end=========================" << endl;
    
    	delete rgb;
    	getchar();
    	return 0;
    }
    

  • 相关阅读:
    vue 回到顶部效果实现
    C# rabbitmq 安装步骤以及使用方法
    ​Everspin MRAM常见问题解答
    带有ECC的异步SRAM存储器适用于各种应用
    ​MCU是控制电子产品的大脑
    全球MCU市场供需失衡情况愈发严重
    如何选择非易失性SRAM
    SRAM VS DRAM
    SoC上的内存
    EEPROM和FLASH在大多数应用场合中无法替代SRAM
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/12148057.html
Copyright © 2020-2023  润新知