ffmpeg音视频同步(音视频写入顺序问题)

本文介绍使用FFmpeg录制桌面视频及系统内部声音的方法,并探讨如何实现先录制视频后录制音频的技术细节。通过实例演示,文章展示了如何创建缓冲区保存音频数据,并在完成视频录制后进行音频数据的写入。

目前能看到的ffmpeg博客,在音视频录制同步时,都是音频和视频根据时间换算,交错写入文件。
现在问题来了,音频和视频在ffmpeg里面是两个通道,能否先写入所有录制的视频,再写入所有录制的音频呢,这个经过验证是可以的。

首先ffmpeg的doc目录下,有个doc/examples/muxing.c文件,对其进行改造,先写视频,再写音频,如下所示:
在这里插入图片描述
红色方框注释的部分,是原有的逻辑,下面的两个while循环分别用于写视频和音频,结果是ok的,音视频同步。

上面的例子太简单,而且数据量不大,为此本人单独写了一个例子,将桌面视频和系统声音录制同时录制下来,录制了五分钟,所有的桌面视频写入文件后,再开始写入录制的系统声音,结果也是好的。

在进行这项测试之前,本人按照正常方式,写了一篇音视频同步的博客。
ffmpeg录制桌面视频和系统内部声音(音视频同步)

本人在此基础上,进行了如下修改:
首先,录制五分钟期间,音频数据要在五分钟结束之后写,故本人特意创建一个比较大的缓冲区,用来装音频数据。
在这里插入图片描述
最后,在音视频混合时,本人先写完视频,再写音频,下面的两个循环中,第一个循环while(m_bRecord)里面写入的是视频文件,后面的while(1)写入的是音频文件,红色方框内的音视频时间自然不要理会。
在这里插入图片描述

下面给出修改的文件ULinkRecord.cpp的内容,其他文件就不需要再贴了,读者参考博客
ffmpeg录制桌面视频和系统内部声音(音视频同步)

#include "ULinkRecord.h"
#include "log/log.h"
#include "appfun/appfun.h"

#include "CaptureScreen.h"



typedef struct BufferSourceContext {
   
   
	const AVClass    *bscclass;
	AVFifoBuffer     *fifo;
	AVRational        time_base;     ///< time_base to set in the output link
	AVRational        frame_rate;    ///< frame_rate to set in the output link
	unsigned          nb_failed_requests;
	unsigned          warning_limit;

	/* video only */
	int               w, h;
	enum AVPixelFormat  pix_fmt;
	AVRational        pixel_aspect;
	char              *sws_param;

	AVBufferRef *hw_frames_ctx;

	/* audio only */
	int sample_rate;
	enum AVSampleFormat sample_fmt;
	int channels;
	uint64_t channel_layout;
	char    *channel_layout_str;

	int got_format_from_params;
	int eof;
} BufferSourceContext;


static char *dup_wchar_to_utf8(const wchar_t *w)
{
   
   
	char *s = NULL;
	int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
	s = (char *)av_malloc(l);
	if (s)
		WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0);
	return s;
}


/* just pick the highest supported samplerate */
static int select_sample_rate(const AVCodec *codec)
{
   
   
	const int *p;
	int best_samplerate = 0;

	if (!codec->supported_samplerates)
		return 44100;

	p = codec->supported_samplerates;
	while (*p) {
   
   
		if (!best_samplerate || abs(44100 - *p) < abs(44100 - best_samplerate))
			best_samplerate = *p;
		p++;
	}
	return best_samplerate;
}




/* select layout with the highest channel count */
static int select_channel_layout(const AVCodec *codec)
{
   
   
	const uint64_t *p;
	uint64_t best_ch_layout = 0;
	int best_nb_channels = 0;

	if (!codec->channel_layouts)
		return AV_CH_LAYOUT_STEREO;

	p = codec->channel_layouts;
	while (*p) {
   
   
		int nb_channels = av_get_channel_layout_nb_channels(*p);

		if (nb_channels > best_nb_channels) {
   
   
			best_ch_layout = *p;
			best_nb_channels = nb_channels;
		}
		p++;
	}
	return best_ch_layout;
}


unsigned char clip_value(unsigned char x, unsigned char min_val, unsigned char  max_val) {
   
   
	if (x > max_val) {
   
   
		return max_val;
	}
	else if (x < min_val) {
   
   
		return min_val;
	}
	else {
   
   
		return x;
	}
}

//RGB to YUV420
bool RGB24_TO_YUV420(unsigned char *RgbBuf, int w, int h, unsigned char *yuvBuf)
{
   
   
	unsigned char*ptrY, *ptrU, *ptrV, *ptrRGB;
	memset(yuvBuf, 0, w*h * 3 / 2);
	ptrY = yuvBuf;
	ptrU = yuvBuf + w * h;
	ptrV = ptrU + (w*h * 1 / 4);
	unsigned char y, u, v, r, g, b;
	for (int j = h - 1; j >= 0; j--) {
   
   
		ptrRGB = RgbBuf + w * j * 3;
		for (int i = 0; i < w; i++) {
   
   

			b = *(ptrRGB++);
			g = *(ptrRGB++);
			r = *(ptrRGB++);


			y = (unsigned char)((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
			u = (unsigned char)((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
			v = (unsigned char)((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
			*(ptrY++) = clip_value(y, 0, 255);
			if (j % 2 == 0 && i % 2 == 0) {
   
   
				*(ptrU++) = clip_value(u, 0, 255);
			}
			else {
   
   
				if (i % 2 == 0) {
   
   
					*(ptrV++) = clip_value(v, 0, 255);
				}
			}
		}
	}
	return true;
}


ULinkRecord::ULinkRecord()
{
   
   
	InitializeCriticalSection(&m_csVideoSection);
	InitializeCriticalSection(&m_csAudioInnerSection);
	InitializeCriticalSection(&m_csAudioInnerResampleSection);
	InitializeCriticalSection(&m_csAudioMicSection);
	InitializeCriticalSection(&m_csAudioMixSection);

	avdevice_register_all();
}

ULinkRecord::~ULinkRecord()
{
   
   
	DeleteCriticalSection(&m_csVideoSection);
	DeleteCriticalSection(&m_csAudioInnerSection);
	DeleteCriticalSection(&m_csAudioInnerResampleSection);
	DeleteCriticalSection(&m_csAudioMicSection);
	DeleteCriticalSection(&m_csAudioMixSection);
}

void ULinkRecord::SetRecordPath(const char* pRecordPath)
{
   
   
	m_strRecordPath = pRecordPath;
	if (!m_strRecordPath.empty())
	{
   
   
		if (m_strRecordPath[m_strRecordPath.length() - 1] != '\\')
		{
   
   
			m_strRecordPath = m_strRecordPath + "\\";
		}
	}
}

void ULinkRecord::SetRecordRect(RECT rectRecord)
{
   
   
	m_iRecordPosX = rectRecord.left;
	m_iRecordPosY = rectRecord.top;
	m_iRecordWidth = rectRecord.right - rectRecord.left;
	m_iRecordHeight = rectRecord.bottom - rectRecord.top;
}

int ULinkRecord::StartRecord()
{
   
   
	int iRet = -1;
	do 
	{
   
   
		Clear();

		m_pAudioConvertCtx = swr_alloc();
		av_opt_set_channel_layout(m_pAudioConvertCtx, "in_channel_layout", AV_CH_LAYOUT_STEREO
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值