使用FFMPEG库将内存中的H264跟AAC数据流合成MP4文件

之前使用的FFMPEG基本都是打开指定链接或者文件读取数据进行操作, 此次要实现的功能是在服务器中读取内存中的H264跟AAC裸流进行文件合成保存, 这就有些难度了, 经过了两个周的资料搜索研究终于完成了该功能, 在此记录说明下, DEMO开源地址:

https://gitee.com/careye_open_source_platform_group/MP4MuxerTest

首先要感谢两篇博客, 没有这两篇博客是几乎没法完成这个功能的:

雷神的:https://blog.csdn.net/leixiaohua1020/article/details/39802913

Jons伯恩特: https://blog.csdn.net/huangyifei_1111/article/details/46955021

总体思路是利用avio_alloc_context函数创建一个带回调读取数据的AVIOContext实体类, 这样就可以通过队列的方式将内存数据追加到队列中, 然后在ffmpeg读取媒体帧的时候在回调函数中输出队列中的数据, 这样达成使用FFMPEG读取内存数据的目的.

程序合成流程是追加数据后就进行流的读取, 所以自制了一个简单的队列结构, 定义如下:

// 用于存储流数据的队列项结构
typedef struct _CE_QUEUE_ITEM_T_
{
	// 申请的缓冲区指针
	uint8_t* Bytes;
	// 申请的缓冲区大小
	int TotalSize;
	// 入队填充的数据字节数
	int EnqueueSize;
	// 出队读取的数据字节数
	int DequeueSize;
}CEQueueItem;

// 一个简单的队列结构, 用于暂存流数据
typedef struct _CE_QUEUE_T_
{
	// 队列项指针
	CEQueueItem *Items;
	// 队列的标识, 使用者可以自定义标识进行队列区分
	uint8_t Flag;
	// 队列项个数
	int Count;
	// 出队计数索引
	int DequeueIndex;
	// 入队索引
	int EnqueueIndex;
}CESimpleQueue;

没有用到链表的结构, 使用数组的方式对队列进行管理.

混流器做成了一个实体类, 方便外部调用, 对外接口也非常简单:

/*
	* Comments: 混流器初始化 实例化后首先调用该方法进行内部参数的初始化
	* Param aFileName: 要保存的文件名
	* @Return 是否成功
	*/
	bool MP4Muxer::Start(std::string aFileName);

	/*
	* Comments: 追加一帧AAC数据到混流器
	* Param aBytes: 要追加的字节数据
	* Param aSize: 追加的字节数
	* @Return 成功与否
	*/
	bool MP4Muxer::AppendAudio(uint8_t* aBytes, int aSize);

	/*
	* Comments: 追加一帧H264视频数据到混流器
	* Param aBytes: 要追加的字节数据
	* Param aSize: 追加的字节数
	* @Return 成功与否
	*/
	bool MP4Muxer::AppendVideo(uint8_t* aBytes, int aSize);

	/*
	* Comments: 关闭并释放输出格式上下文
	* Param : None
	* @Return None
	*/
	void MP4Muxer::Stop(void);

Start则申请各种资源, 有些参数在方法实现内部固定了, 可根据需要自己进行修改, Stop则释放申请的各种资源并结束文件录制.

Append两个方法分别是追加音视频帧, 输入的数据要是一个完整帧, 另外做了一个该实体类的使用DEMO:


int main()
{
	AVFormatContext* fmt_h264 = NULL;
	AVFormatContext* fmt_aac = NULL;
	AVPacket pkt_v;
	int result = 0;
	bool video_finished = false, audio_finished = false;

    std::cout << "Start muxer..." << std::endl;

	av_register_all();

	if ((result = avformat_open_input(&fmt_h264, TestH264, 0, 0)) < 0)
	{
		DEBUG_E("Could not open input file: -%08X.", -result);
		goto end;
	}
	if ((result = avformat_find_stream_info(fmt_h264, 0)) < 0)
	{
		DEBUG_E("Failed to retrieve input stream information -%08X.", -result);
		goto end;
	}

	if ((result = avformat_open_input(&fmt_aac, TestAAC, 0, 0)) < 0)
	{
		DEBUG_E("Could not open input file: -%08X.", -result);
		goto end;
	}
	if ((result = avformat_find_stream_info(fmt_aac, 0)) < 0)
	{
		DEBUG_E("Failed to retrieve input stream information -%08X.", -result);
		goto end;
	}

	// 输出文件信息
	av_dump_format(fmt_h264, 0, TestH264, 0);
	av_dump_format(fmt_aac, 0, TestAAC, 0);

	// 实例化混流器
	MP4Muxer *muxer = new MP4Muxer();
	if (!muxer->Start("./test.mp4"))
	{
		delete muxer;
		DEBUG_E("Muxer start fail.");
		goto end;
	}

	while (1)
	{
		// 读取一帧视频数据
		if (!video_finished)
		{
			if ((result = av_read_frame(fmt_h264, &pkt_v)) < 0)
			{
				DEBUG_W("Read frame fail: -%08X.", -result);
				video_finished = true;
				if (audio_finished)
				{
					break;
				}
			}
			// 需要确保传入的是一帧数据
			muxer->AppendVideo(pkt_v.data, pkt_v.size);
		}

		// 读取一帧音频数据
		if (!audio_finished)
		{
			if ((result = av_read_frame(fmt_aac, &pkt_v)) < 0)
			{
				DEBUG_W("Read frame fail: -%08X.", -result);
				audio_finished = true;
				if (video_finished)
				{
					break;
				}
			}
			// 需要确保传入的是一帧数据
			muxer->AppendAudio(pkt_v.data, pkt_v.size);
		}
	}

	av_free_packet(&pkt_v);
	avformat_close_input(&fmt_h264);
	muxer->Stop();
	delete muxer;

end:
	_DEBUG_I("Muxer finished.\n");
	getchar();
}

DEMO就是利用FFMPEG读取一帧帧音视频数据, 然后将字节数组传入到混流器中进行合成, 示例中的音视频测试文件使用了雷神的264跟AAC文件.

话说真的不想写FFMPEG的博客, 每次都绕不过雷神的博客, 看到雷神的博客就伤感, 天妒英才, 哎~! 在此感谢各位开源先驱们~!

car-eye开源官方网址:www.car-eye.cn  

car-eye项目地址:https://github.com/Car-eye-team

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 20
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值