音视频开发:ffplay使用sonic实现倍速播放

前言

现在的播放器通常都需要倍速播放功能,而且声音要求变速不变调。一般来说视频可以通过修改pts加速播放,音频如果通过修改采样率加速播放则会出现变调的现象,所以视频变速功能主要是实现音频的变速。音频要做到变速不变调,就要对音频数据进行一定的压缩或者拓展,我们可以使用一些音频处理库来达这种效果,比如谷歌的sonic。

一、sonic的基本用法

//创建对象
sonicStream sncStream=sonicCreateStream(sample_rate, nb_channels);
//设置倍速
sonicSetSpeed(sncStream, 1.25);
//写入音频裸流
int ret = sonicWriteFloatToStream(sncStream,audio_buf, nb_samples);
int numSamples = af->frame->nb_samples / speed;
if (ret) {
		// 从流中读取处理好的数据
		int new_nb_samples = sonicReadFloatFromStream(sncStream, audio_buf, numSamples);
	}
//销毁对象
sonicDestroyStream(is->sncStream);

二、ffplay中使用sonic

要实现倍速有一个做法是,只对声音处理,把处理后的nb_samples更新给播放器 ,并且时钟同步到音频,这样视频也会跟踪音频一起改变速度。

在ffplay中对音频数据进行处理,有两个地方比较适合,一个是音频重采样方法audio_decode_frame中,还一个是音频设备回调方法sdl_audio_callback中。这里我们选择在audio_decode_frame中对音频数据进行倍速处理。

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

1、初始化sonicStream

(1)、定义字段:

可以在VideoState中添加sonicStream 字段

sonicStream sncStream

(2)、初始化

初始化方法可以放在stream_component_open中伪代码如下:

static int stream_component_open(VideoState* is, int stream_index)
{    
	...ffplay源代码
	switch (avctx->codec_type) {
	case AVMEDIA_TYPE_AUDIO:
		...ffplay源代码
	    //初始化sonicStream
		is->sncStream = sonicCreateStream(sample_rate, nb_channels);
		break;
	}
}

2、倍速处理

在audio_decode_frame中重采样后的音频数据回放在is->audio_buf指向的数据中,我们只需要对其处理即可。如果倍速大于1,则直接写回is->audio_buf指向的缓冲区,如果小于1则新建一个缓冲区装载数据,然后让is->audio_buf指向此缓冲区。

(1)、定义字段

在VideoState中定义速度参数以及数据缓冲区。

double speed;
char*  speed_buf;
int    speed_buf_size;

(2)、处理数据

在audio_decode_frame中重采样后倍速处理,下列代码位置放在重采样后,更新时钟之前。

double speed = is->speed;	
if (speed != 1)
{
	//设置倍速
	sonicSetSpeed(is->sncStream, speed);
	sonicSetQuality(is->sncStream, 1);
	//写入音频数据
	int ret = sonicWriteShortToStream(is->sncStream, is->audio_buf, af->frame->nb_samples);
	if (ret) {
		//计算新的nb_samples,乘以2是为了保证sonicReadShortFromStream能一次读取全部的数据,否则可能造成累计延迟。
		int numSamples = 2 * af->frame->nb_samples / speed;
		if (speed < 1)
			//倍速小于1时使用自己的缓冲区
		{
			//计算缓冲区大小
			int size = numSamples * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt);
			if (is->speed_buf_size < size)
			{
				is->speed_buf = av_realloc(is->speed_buf, size);
				is->speed_buf_size = size;
			}
			is->audio_buf = is->speed_buf;
		}
		//读取处理后的数据
		int new_nb_samples = sonicReadShortFromStream(is->sncStream, is->audio_buf, numSamples);
		//重新计算数据大小
		resampled_data_size = new_nb_samples * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt);
		//设置新的nb_samples
		af->frame->nb_samples = new_nb_samples;
	}
}

3、释放资源

在stream_component_close中的case AVMEDIA_TYPE_AUDIO中释放资源

if (is->speed_buf)
	{
		av_free(is->speed_buf);
		is->speed_buf = NULL;
	}
if (is->sncStream) {
		sonicDestroyStream(is->sncStream);
        is->sncStream=NULL;
	}

如果你对音视频开发感兴趣,觉得文章对您有帮助,别忘了点赞、收藏哦!或者对本文的一些阐述有自己的看法,有任何问题,欢迎在下方评论区讨论! 

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

猜你喜欢

转载自blog.csdn.net/m0_60259116/article/details/127463414