现在的播放器通常都需要倍速播放功能,而且声音要求变速不变调。一般来说视频可以通过修改pts加速播放,音频如果通过修改采样率加速播放则会出现变调的现象,所以视频变速功能主要是实现音频的变速。音频要做到变速不变调,就要对音频数据进行一定的压缩或者拓展,我们可以使用一些音频处理库来达这种效果,比如谷歌的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);
要实现倍速有一个做法是,只对声音处理,把处理后的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;
- }
- }
在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)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓