【音视频】将图片作为输入源录制MP4(6-2)

最近突然想将以图片为背景录制一个静态音视频文件,比如可以录制一个音乐文件,以某个喜欢的图片为视频内容再加上录制电脑播放器的声音,就可以完成音乐文件录制。扬声器的录制模块已经有了,需要做的是将图片作为一个输入源,其思想是解封装–>解码–>转码–>编码–>写入文件,其实可以在《【音视频】使用FFMPEG读取本地|网络音视频流(3-4)》基础上进行修改,因为VStreamCaptor是一个外部输入的视频采集模块,即在VStreamCaptor采集器的视频采集线程修改部分逻辑行为即可。

主要思想是依赖于原来的采集线程方法不断向下一层传输解码之后的数据的逻辑,而图片解码一帧就到文件尾了,我的方法是存储上一帧frame和相关信息,在出现读取到文件尾时,不再解码直接传递存储的frame给下一层。下面是修改之后的captureProcess方法,可以注意注释的地方,即为修改的地方:

void VStreamCaptor::captureProcess()
{
	int err = ERROR_CODE_OK;

	AVPacket packet;
	AVFrame* frame = av_frame_alloc();
	// 因为图片只有一帧,添加一个Frame缓存,当读到文件尾之后使用缓存的Frame往后续的callback传递:转码-->编码……
	AVFrame* preFrame = av_frame_alloc();
	// 记录前一个packet的stream index下标,使用缓存Frame时要用来判断
	int preStreamIndex = -1;
	UINT interval = AV_TIME_BASE / m_fps, preTs = 0;
	int64_t startTime = av_gettime();
	while (m_running) {
		int ret = av_read_frame(m_formatCtx, &packet);
		// 单独判断读到文件尾,如果stream index吻合且callback不为空,则往下一层传递缓存frame数据
		if (ret == AVERROR_EOF && preStreamIndex == m_streamIndex && m_onVideoCaptureData != nullptr) {
			m_onVideoCaptureData(preFrame, m_index);
			// 控制帧率
			av_usleep(interval);
			continue;
		}
		// 其他小于0的情况视为报错,退出采集循环
		else if (ret < 0) {
			err = ERROR_CODE_NO_CAPTURE_SOURCE;
			LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] read frame error: %s", __FUNCTION__,
				HCMDR_GET_ERROR_DESC(err));
			if (m_onVideoCaptureError != nullptr) {
				m_onVideoCaptureError(err, m_index);
			}
			break;
		}

		if (packet.stream_index == m_streamIndex) {
			ret = avcodec_send_packet(m_decodeCtx, &packet);
			while (ret >= 0) {
				ret = avcodec_receive_frame(m_decodeCtx, frame);
				if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
					break;
				}
				else if (ret < 0) {
					err = ERROR_CODE_FFMPEG_DECODE_FAILED;
					LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] decode packet error: %s", __FUNCTION__,
						HCMDR_GET_ERROR_DESC(err));
					if (m_onVideoCaptureError != nullptr) {
						m_onVideoCaptureError(err, m_index);
					}
					break;
				}

				if (m_onVideoCaptureData != nullptr) {
					m_onVideoCaptureData(frame, m_index);
				}

				// 从解码frame中拷贝数据和属性到缓存frame中
				av_frame_ref(preFrame, frame);
				
				AVRational timebaseQ = { 1, AV_TIME_BASE };
				int64_t ptsTime = av_rescale_q(packet.pts, m_timebase, timebaseQ);
				int64_t nowTime = av_gettime() - startTime;
				if (ptsTime > nowTime) {
					av_usleep(ptsTime - nowTime);
				}
			}
		}
		if (ret == AVERROR_EOF) {
			avcodec_flush_buffers(m_decodeCtx);
		}
		
		// 记录packet中stream index
		preStreamIndex = packet.stream_index;
		
		av_packet_unref(&packet);
	}
	
	// 释放缓存Frame
	av_frame_unref(preFrame);
	av_frame_free(&preFrame);
	
	av_frame_free(&frame);
}

到这里,可以DIY录制自己的音乐文件啦!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值