首页 > 其他分享 >34_音视频播放器_音视频同步

34_音视频播放器_音视频同步

时间:2022-11-08 20:45:57浏览次数:71  
标签:播放器 视频 音频 videoplayer 34 VideoPlayer 音视频 时钟

目录

简介

音视频同步的实现可以有两种方式

  1. 视频同步到音频
  2. 音频同步到视频

我们这里选择第一种,因为音频播放的频率是SDL控制的,我们没法改变,所以使用视频同步到音频。

实现音视频同步

videoplayer.h先定义两个全局变量,用于记录音频时钟和视频时钟

/** 音频时钟,当前音频包对应的时间值 */
double _aTime = 0;
/** 视频时钟,当前视频包对应的时间值 */
double _vTime = 0;

在音频处理类videoplayer_audio.cpp里保存音频时钟

// 保存音频时钟
if (pkt.pts != AV_NOPTS_VALUE) {
    _aTime = av_q2d(_aStream->time_base) *pkt.pts;
}

videoplayer_video.cpp里的解码视频方法decodeVideo里记录视频时钟并进行音画同步

// 视频时钟
if (pkt.dts != AV_NOPTS_VALUE) {
    _vTime = av_q2d(_vStream->time_base) * pkt.dts;
}

// 如果视频包过早被解码出来,那就需要等待对应的音频时钟到达
while (_vTime > _aTime && _state == Playing) {
    SDL_Delay(5);
}

处理没有音频的播放

上面音视频同步是一个视频文件里有音频的情况下可以正常使用,但是如果一个视频文件没有音频是不能正常使用。

videoplayer.cppreadFile方法里的局部变量判断是否有音频和视频弄成成员变量:

// videoplayer.h
/** 是否有音频流 */
bool _hasAudio = false;
/** 是否有视频流 */
bool _hasVideo = false;

// videoplayer.cpp
// 初始化音频信息
_hasAudio = initAudioInfo() >= 0;
// 初始化视频信息
_hasVideo = initVideoInfo() >= 0;
if (!_hasAudio && !_hasVideo) {
   emit playFailed(this);
   free();
   return;
}

videoplayer_video.cpp中音画同步的地方添加是否有音频判断

if(_hasAudio){// 有音频
    // 如果视频包过早被解码出来,那就需要等待对应的音频时钟到达
    while (_vTime > _aTime && _state == Playing) {
        SDL_Delay(5);
    }
}

更新界面播放的进度条

因为我们音视频同步是根据音频为基准的,所以我们可以在音频获取音频时钟的地方,把音频时钟发送到界面里进行更新。

videoplayer.cpp中给外界提供获取当前的播放时刻的方法

/** 当前的播放时刻(单位是秒) */
int VideoPlayer::getTime(){
    return round(_aTime);// round四舍五入的意思
}

在提供一个信号量用于给外界发送消息

void timeChanged(VideoPlayer *player);

然后在videoplayer_audio.cpp里的获取音频当前时刻的地方,发送信号给外界

// 保存音频时钟
if (pkt.pts != AV_NOPTS_VALUE) {
    _aTime = av_q2d(_aStream->time_base) *pkt.pts;
    // 通知外界:播放时间点发生了改变
    emit timeChanged(this);
}

在界面类mainwindow.cpp里注册当前播放时刻的监听

connect(_player, &VideoPlayer::timeChanged,
            this, &MainWindow::onPlayerTimeChanged);
            
void MainWindow::onPlayerTimeChanged(VideoPlayer *player) {
    ui->currentSlider->setValue(player->getTime());
}

处理切换音视频时还保留上一个视频的最后一帧画面

我们在播放一个视频文件时,点击停止后在去播放另外一个视频文件,会出现上一个视频的最后一帧画面。

我们点击停止后,会发出停止的信号出来,而我们的VideoWidget类里也有出来状态改变的监听,那么还是会出现上一个视频的最后一帧画面呢?

// 状态改变的监听
void VideoWidget::onPlayerStateChanged(VideoPlayer *player) {
    if (player->getState() != VideoPlayer::Stopped) return;

    freeImage();
    update();
}

这是因为VideoWidget类里onPlayerFrameDecoded方法是在主线程,而发射它的信号(frameDecoded)是在子线程中,所以有多线程的情况,就会导致出现这种情况,现在我们需要在onPlayerFrameDecoded方法里添加判断

void VideoWidget::onPlayerFrameDecoded(VideoPlayer *player,
                                       uint8_t *data,
                                       VideoPlayer::VideoSwsSpec &spec) {
    if (player->getState() == VideoPlayer::Stopped) return;
    ......
}

源码链接

标签:播放器,视频,音频,videoplayer,34,VideoPlayer,音视频,时钟
From: https://www.cnblogs.com/zuojie/p/16871086.html

相关文章

  • AGC034F RNG and XOR(FWT,*)
    AGC034FRNGandXOR\(x\)初始为\(0\),每次会有\(p_{i(/2^N)}\)的概率变成\(x\oplusi\),问对于所有\(0\lek<2^N\),\(x\)第一次变成\(k\)的期望次数。\(N......
  • 33_音视频播放器_画面显示
    目录一、简介二、视频解码2.1解码视频2.2调用解码视频方法三、像素格式转换3.1初始化3.2像素格式转换四、显示画面4.1定义信号4.2发送信号4.3定义槽函数4.4注册监......
  • 32_音视频播放器_SDL播放
    目录一、简介二、音频重采样2.1引入头文件2.2定义重采样相关属性2.3初始化重采样2.4重采样三、SDL播放四、停止功能五、处理读完音频包的情况六、实现调节音量七、实现......
  • 视频融合平台EasyCVR继承播放器,但是无法播放该如何解决?
    EasyCVR视频融合云平台基于云边端一体化架构,兼容性高、拓展性强,可支持多类型设备、多协议方式接入,包括国标GB/T28181、RTMP、RTSP/Onvif协议,以及厂家的私有协议,如:海康Ehome......
  • 30_音视频播放器_解封装
    目录一、简介二、读出文件三、初始化3.1读取文件3.2初始化音频信息和视频信息3.3初始化解码器四、实现视频时长一、简介我们使用QT+ffmpeg实现一个播放器,这里我们主要......
  • 【操作说明】全能型H.265播放器如何使用?
    本播放器集成了公司业务的接口,包含了实播,回放,云台控制和回放速度控制,截图和全屏功能可以根据type直接初始化接口地址如果是第三方业务对接,也可以单独配置接口地址正确使用......
  • 音视频开发进阶|第六讲:色彩和色彩空间·下篇
    在前两篇推文中,我们了解了色彩空间、像素、图像和视频之间的组成关系,并且比较详细的学习了色彩空间RGB、YUV的采样&存储格式。今天,我们基于这些内容,再补充一些重要的关联......
  • 融云 CDN 播放器 2.0 版本正式上线
    近期,融云自研CDN播放插件2.0版本正式上线。在原有版本支持融云内置CDN直播流播放的基础上,开放了相关API,满足开发者的多种业务需求。关注【融云全球互联网通信云】了......
  • P3834 【模板】可持久化线段树 2
    先用结构体实现了下,发现写错了(只有20分),后面直接用的数组了  #include<bits/stdc++.h>usingnamespacestd;constintN=2e5+7;inttl[N<<6],tr[N<<6],sum......
  • 3417. 砝码称重
    https://www.acwing.com/problem/content/3420/一眼dp,但是还是先走个暴搜看看,开始的时候我这样想,由于天平两边都可以称,需要减的操作,数组无法存储负数下标,于是我这里......