ffplay自定义系列
第一章 自定义播放器接口
第二章 倍速播放(本章)
______第一节 sonic实现倍速播放(本节)
______第二节 soundtouch实现倍速播放
______第三节 ffmpeg滤镜实现倍速播放
第三章 dxva2硬解渲染
第四章 提供C#接口
第五章 制作wpf播放器
前言
现在的播放器通常都需要倍速播放功能,而且声音要求变速不变调。一般来说视频可以通过修改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中对音频数据进行倍速处理。
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;
}
三、参考
sonic的使用参考了这篇文章,里边附有sonic.c的完整源码
音频倍速(变速不变调)的实现_小伙儿在这儿烤地瓜-CSDN博客_音频变速不变调