音视频播放器—变速播放

本文介绍了如何使用SDL库实现多媒体播放器的视频和音频变速功能。通过监听键盘事件改变播放速率,视频通过调整帧持续时间实现变速,音频则采用丢帧和填充帧的方式。代码示例展示了在2倍速和0.5倍速下的处理方法,以及如何维持音视频同步。
摘要由CSDN通过智能技术生成

------------------------------------全系列文章目录------------------------------------

获取播放速率
  • 通过捕获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。

    switch (_event.type) {
            case SDL_KEYDOWN:
                switch (_event.key.keysym.sym) {
                case SDLK_2:
                    avc->play_rate = 2;
                    break;
                case SDLK_1:
                    avc->play_rate = 1;
                    break;
                case SDLK_5:
                    avc->play_rate = 0.5;
                    break;
                } 
                break;
    
            ................
    
            default:
                break;
    }
    
  • 在更新当前帧持续时间时,改变帧持续时间的基准值。

    /*计算延时时间*/
    uint32_t AVCtrl::get_delay(double aclk, double vclk)
    {
        int64_t        delay;
        double        _duration, diff; 
        /*根据播放速率改变帧持续时间的基准值*/
        _duration    = vc.get_duration() / play_rate;
    
        if (_isnan(aclk) || _isnan(vclk))
            return _duration;
    
        diff        = (aclk - vclk) * MICROSECOND_TO_SECOND;
        if (diff < 0) {
            if (-diff > 3 * _duration)    delay = (-diff - _duration);
            else delay = _duration;
        } else {
            delay = _duration - diff;
            delay = delay < 0 ? 0 : delay;
        }
    
        return delay;
    }
    
  • 在音频回调函数中,根据播放速率进行丢帧和填充空白帧处理。

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

大致流程
  • 因为采用的变速方案比较简单,因此在原先基础上改动的地方很少,且流程比较简单。
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值