音视频开发:ffplay使用ffmpeg滤镜实现倍速播放

前言

曾经为实现倍速播放使用过ffmpeg,对音频使用atempo滤镜即可实现变速不变调。但是当时效果并不是特别好,和soundtouch相比处理后的音质有明显的区别。最近用新版本的ffmpeg滤镜重新实现了倍速播放,发现效果变好,已经达到可接受的程度,所以在此分享具体实现。

一、ffmpeg滤镜

ffmpeg倍速滤镜一般可以用下面这几个:

(1)视频

setpts=0.5*PTS

(2)音频

atempo = 2.0

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

二、ffplay中使用滤镜

在ffplay中可以直接使用ffmpeg的滤镜,在这里实现倍速播放我们只需要处理音频,然后让时钟同步到音频即可。没有音频的情况比较简单,只需要修改视频的pts即可,这里不做说明。

1、定义字段

在VideoState中添加滤镜刷新字段,用以实现动态控制倍速。

默认值为0,当值为1时则刷新滤镜。

int req_afilter_reconfigure;

2、设置滤镜

直接使用音频滤镜即可,atempo的取值范围是0.5-2.0即倍速值。

afilters = "atempo=2";

定义一个设置倍速方法,方便使用。(作为示例没有加线程安全机制)

void set_speed(double value)
{
	if (value < 0.5 || value>2)
		return;
	VideoState* is = (VideoState*)play;
	is->speed = value;
#if CONFIG_AVFILTER
	if (!is->afilters)
	{
		is->afilters = av_malloc(32);
	}
	sprintf(is->afilters, "atempo=%lf", value);
	is->req_afilter_reconfigure = 1;
#endif
}

3、更新滤镜

修改了倍速后,需要播放器动态响应。在ffplay的audio_thread中已经有了更新滤镜的机制,如果音频参数修改了则会进行滤镜更新,我们只需要使用上面定义的req_afilter_reconfigure来触发其更新滤镜即可。代码如下:(作为示例没有加线程安全机制)

if (reconfigure||is->req_afilter_reconfigure) {
				is->req_afilter_reconfigure = 0;
//ffplay更新滤镜源码
}

4、调整时钟

经过滤镜处理的数据得到的avframe时间戳pts以及pkt_pos也被相应的调整了,在ffplay中播放效果是声音变快了,但时钟不变,画面也按照原来速度播放,这肯定是不符合需求的,所以需要重新调整时间相关参数。其实有一种方法,直接使用滤镜处理前的时间参数即可:由于倍速处理后nb_samples和bufferSize都变了,而时间参数(pts以及pkt_pos)不变就会使得sdl_audio_callback同步时钟发生相应变化。

在audio_thread中:

先记录avframe原来的参数

//使用原来的pts
double pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
double pos = frame->pkt_pos;
int serial = is->auddec.pkt_serial;
double duration = av_q2d((AVRational) { frame->nb_samples, frame->sample_rate });
//使用原来的pts --end

滤镜处理后使用原来的参数

//使用原来的pts
af->pts = pts;// (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
af->pos = pos;//frame->pkt_pos;
af->serial = serial;// is->auddec.pkt_serial;
af->duration = duration;// av_q2d((AVRational) { frame->nb_samples, frame->sample_rate });
//使用原来的pts --end

5、释放资源

由于上述方法中afilters指向的是av_malloc的空间,所以关闭播放器时需要将其释放。
在stream_close中添加

#if	CONFIG_AVFILTER
	if (is->afilters)
	{
		av_free(is->afilters);
		is->afilters = NULL;
	}	
#endif

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

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

猜你喜欢

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