• 音视频播放器开发——实现变速播放


    获取播放速率

    通过捕获SDL_KEYDOWN按键按下事件,并通过标志位判断当前的键位SDL_Event::SDL_KeyboardEvent::SDL_Keysym::SDL_Keycode为SDLK_1、SDLK_2和SDLK_5,分别对应1倍速、2倍速和0.5倍速。

    视频和音频播放变速

    改变视频播放速率的方法为直接改变帧率即改变每帧的帧持续时间,加速时缩短帧持续时间,反之减小帧持续时间。但是变速后帧持续时间过短,就要采取丢帧的处理方式。

    改变音频播放速率的方法有三种:

    改变采样率,即对原PCM数据进行重采样处理。加速播放时提高采样率,反之减小采样率。但是这种方法会导致声音在变速的同时变调。

    丢帧和填充帧,即加速播放时丢帧处理,反之在原始帧之间填充空白帧。但是这种方法会导致播放期间有杂音存在。

    使用相关音频处理算法实现变速不变调,如soundtouch和sonic。

    具体代码实现

    由于时间关系,此处使用了一个简化版本的变速播放方案,即视频变速采用改变每帧持续时间的方法,音频变速采用丢帧和填充帧的方法;同时只支持了2倍速率和0.5倍速率的变速档位。

    事件捕获后,采用一个变量记录当前的播放速率,默认为1。

    1. switch (_event.type) {
    2. case SDL_KEYDOWN:
    3. switch (_event.key.keysym.sym) {
    4. case SDLK_2:
    5. avc->play_rate = 2;
    6. break;
    7. case SDLK_1:
    8. avc->play_rate = 1;
    9. break;
    10. case SDLK_5:
    11. avc->play_rate = 0.5;
    12. break;
    13. }
    14. break;
    15. ................
    16. default:
    17. break;
    18. }

    在更新当前帧持续时间时,改变帧持续时间的基准值。

    1. /*计算延时时间*/
    2. uint32_t AVCtrl::get_delay(double aclk, double vclk)
    3. {
    4. int64_t delay;
    5. double _duration, diff;
    6. /*根据播放速率改变帧持续时间的基准值*/
    7. _duration = vc.get_duration() / play_rate;
    8. if (_isnan(aclk) || _isnan(vclk))
    9. return _duration;
    10. diff = (aclk - vclk) * MICROSECOND_TO_SECOND;
    11. if (diff < 0) {
    12. if (-diff > 3 * _duration) delay = (-diff - _duration);
    13. else delay = _duration;
    14. } else {
    15. delay = _duration - diff;
    16. delay = delay < 0 ? 0 : delay;
    17. }
    18. return delay;
    19. }

    在音频回调函数中,根据播放速率进行丢帧和填充空白帧处理。

    1. void fill_audio_callback(void *userdata, Uint8 * stream, int len)
    2. {
    3. static int play_rate_ctrl = 1;
    4. ...............
    5. if (avc->play_rate == 2) { /*两倍速下,多解码一帧音频帧,但回调PCM数据的操作在下面步奏中*/
    6. avc->ac.pktq.packet_queue_get(&pkt); //获取未解码帧数据
    7. while (avc->ac.get_frame(&pkt) == -1); //解码并获取重采样的音频数据
    8. } else if (avc->play_rate == 0.5) { /*0.5倍速下,隔次回调返回空白数据*/
    9. play_rate_ctrl = -play_rate_ctrl;
    10. if (play_rate_ctrl == 1) SDL_memset(stream, 0, len);
    11. return;
    12. }
    13. ...............
    14. }
    • 因为视频帧持续时间的获取是根据 基于pts的音视频时钟差和初始持续时间 来获取的,所以当音频和视频同时变速时,不需要更改音频帧和视频帧的pts数据,即可维持两者的pts在同一量级上。

    大致流程

    • 因为采用的变速方案比较简单,因此在原先基础上改动的地方很少,且流程比较简单

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

  • 相关阅读:
    消息队列-RabbitMQ
    什么是腾讯云关系型数据库(MySQL/SQL Server/MariaDB/PostgreSQL详解)
    基于哈夫曼树的数据压缩算法
    C语言——文件
    Linux之 4 种休眠模式
    大语言模型量化方法对比:GPTQ、GGUF、AWQ
    ASTM D2863: 塑料最低氧气浓度测试
    合作式智能运输系统 应用层交互技术要求 第 1 部分:意图共享与协作
    探索RocketMQ中的分布式事务消息:原理与实践
    【云原生】3.3 Kubernetes 中间件部署实战
  • 原文地址:https://blog.csdn.net/m0_60259116/article/details/126468412