Qt利用ffmpeg实现音视频同步

目录
  • 一、前言
  • 二、效果图
  • 三、体验地址
  • 四、相关代码
  • 五、功能特点
    • 5.1 基础功能
    • 5.2 特色功能
    • 5.3 视频控件
    • 5.4 内核ffmpeg

一、前言

用ffmpeg来做音视频同步,个人认为这个是ffmpeg基础处理中最难的一个,无数人就卡在这里,怎么也不准,本人也是尝试过网上各种demo,基本上都是渣渣,要么仅仅支持极其少量的视频文件比如收到的数据包是一帧视频一帧音频的,要么根本没法同步歪七八糟的,要么进度跳过去直接蹦蹦蹦崩溃的,其实最完美的音视频同步处理demo就是ffplay,我亲测过几十种各种各样的音视频本地文件,数十种视频流文件,都是非常完美,当然啦这是亲生的啦,不完美还玩个屁。

如果仅仅是播放视频流(不带音频流),可能不需要音视频同步,所以最开始只做rtsp视频流播放的时候根本没有考虑同步的问题,因为没遇到也不需要,等到后期发现各种rtmp、http、m3u8这种视频流的时候,问题大了去了,他是hls格式的视频流文件一次性过来的,一个个小视频文件过来的,如果没有同步的话,意味着突然之间刷刷刷的图片过去很多,下一次来的又是刷刷的,这就需要自己计算同步了,上次接收到的数据包放入队列,到了需要显示的时候就显示。

常用的音视频同步方法:

通过fps来控制,fps表示一秒钟播放多少帧,比如25帧,可以自行计算一帧解码用掉的时间,一帧占用(1000/25=40毫秒),通过延时来处理,这其实是最渣渣的办法。 记住开始解码的时间startTime,通过av_rescale_q计算pts时间,两者的差值就是需要延时的时间,调用av_usleep来延时,这种只有部分文件正常,很多时候不正常。 音频同步到视频,视频时钟作为主时钟,没试过,网上很多人说这个办法不好。 视频同步到音频,音频时钟作为主时钟,没试过,据说大部分人采用的此办法。 音视频同步到外部时钟,外部时钟作为主时钟,最终采用的办法,容易理解互不干扰,各自按照外部时钟去同步自己。 ffplay自身内置了三种同步策略,可以通过参数来控制采用何种策略,默认是视频同步到音频。

二、效果图

三、体验地址

国内站点:https://gitee.com/feiyangqingyun

国际站点:https://github.com/feiyangqingyun

体验地址:https://pan.baidu.com/s/1YOVD8nkoOSYwX9KgSauLeQ 提取码:kcgz 文件名:bin_video_demo/bin_linux_video。

四、相关代码

#include "ffmpegsync.h"
#include "ffmpeghelper.h"
#include "ffmpegthread.h"

FFmpegSync::FFmpegSync(quint8 type, QObject *parent) : QThread(parent)
{
    this->stopped = false;
    this->type = type;
    this->thread = (FFmpegThread *)parent;
}

FFmpegSync::~FFmpegSync()
{

}

void FFmpegSync::run()
{
    if (!thread) {
        return;
    }

    this->reset();
    while (!stopped) {
        //暂停状态或者切换进度中或者队列中没有帧则不处理
        if (!thread->isPause && !thread->changePosition && packets.size() > 0) {
            mutex.lock();
            AVPacket *packet = packets.first();
            mutex.unlock();

            //h264的裸流文件同步有问题因为获取不到pts和dts(暂时用最蠢的延时办法解决)
            if (thread->formatName == "h264") {
                int sleepTime = (1000 / thread->frameRate) - 5;
                msleep(sleepTime);
            }

            //计算当前帧显示时间(外部时钟同步)
            ptsTime = FFmpegHelper::getPtsTime(thread->formatCtx, packet);
            if (!this->checkPtsTime()) {
                msleep(1);
                continue;
            }

            //显示当前的播放进度
            this->checkShowTime();

            //如果解码线程停止了则不用处理
            if (!thread->stopped) {
                //0-表示音频 1-表示视频
                if (type == 0) {
                    thread->decodeAudio1(packet);
                } else if (type == 1) {
                    thread->decodeVideo1(packet);
                }
            }

            //释放资源并移除
            mutex.lock();
            FFmpegHelper::freePacket(packet);
            packets.removeFirst();
            mutex.unlock();
        }

        msleep(1);
    }

    this->reset();
    this->clear();
    stopped = false;
}

void FFmpegSync::stop()
{
    if (this->isRunning()) {
        stopped = true;
        this->wait();
    }
}

void FFmpegSync::clear()
{
    mutex.lock();
    //释放还没有来得及处理的剩余的帧
    foreach (AVPacket *packet, packets) {
        FFmpegHelper::freePacket(packet);
    }
    packets.clear();
    mutex.unlock();
}

void FFmpegSync::reset()
{
    //复位音频外部时钟
    showTime = 0;
    bufferTime = 0;
    offsetTime = -1;
    startTime = av_gettime();
}

void FFmpegSync::append(AVPacket *packet)
{
    mutex.lock();
    packets << packet;
    mutex.unlock();
}

int FFmpegSync::getPacketCount()
{
    return this->packets.size();
}

bool FFmpegSync::checkPtsTime()
{
    //下面这几个时间值是参考的别人的
    bool ok = false;
    if (ptsTime > 0) {
        if (ptsTime > offsetTime + 100000) {
            bufferTime = ptsTime - offsetTime + 100000;
        }

        int offset = (type == 0 ? 1000 : 5000);
        //做梦都想不到倍速播放就这里控制个系数就行
        offsetTime = (av_gettime() - startTime) * thread->speed + bufferTime;
        if ((offsetTime <= ptsTime && ptsTime - offsetTime <= offset) || (offsetTime > ptsTime)) {
            ok = true;
        }
    } else {
        ok = true;
    }

    return ok;
}

void FFmpegSync::checkShowTime()
{
    //必须是文件(本地文件或网络文件)才有播放进度
    if (!thread->getIsFile()) {
        return;
    }

    //过滤重复发送播放时间
    bool showPosition = false;
    bool existVideo = (thread->videoIndex >= 0);
    if (type == 0) {
        //音频同步线程不能存在视频
        if (!existVideo) {
            showPosition = true;
        }
    } else if (type == 1) {
        //视频同步线程必须存在视频
        if (existVideo) {
            showPosition = true;
        }
    }

    //需要显示时间的时候发送对应进度(限定差值大于200毫秒没必要频繁发送)
    if (showPosition && (offsetTime - showTime > 200000)) {
        showTime = offsetTime;
        thread->position = showTime / 1000;
        emit receivePosition(thread->position);
    }
}

五、功能特点

5.1 基础功能

  • 支持各种音频视频文件格式,比如mp3、wav、mp4、asf、rm、rmvb、mkv等。
  • 支持本地摄像头设备,可指定分辨率、帧率。
  • 支持各种视频流格式,比如rtp、rtsp、rtmp、http等。
  • 本地音视频文件和网络音视频文件,自动识别文件长度、播放进度、音量大小、静音状态等。
  • 文件可以指定播放位置、调节音量大小、设置静音状态等。
  • 支持倍速播放文件,可选0.5倍、1.0倍、2.5倍、5.0倍等速度,相当于慢放和快放。
  • 支持开始播放、停止播放、暂停播放、继续播放。
  • 支持抓拍截图,可指定文件路径,可选抓拍完成是否自动显示预览。
  • 支持录像存储,手动开始录像、停止录像,部分内核支持暂停录像后继续录像,跳过不需要录像的部分。
  • 支持无感知切换循环播放、自动重连等机制。
  • 提供播放成功、播放完成、收到解码图片、收到抓拍图片、视频尺寸变化、录像状态变化等信号。
  • 多线程处理,一个解码一个线程,不卡主界面。

5.2 特色功能

  • 同时支持多种解码内核,包括qmedia内核(Qt4/Qt5/Qt6)、ffmpeg内核(ffmpeg2/ffmpeg3/ffmpeg4/ffmpeg5)、vlc内核(vlc2/vlc3)、mpv内核(mpv1/mp2)、海康sdk、easyplayer内核等。
  • 非常完善的多重基类设计,新增一种解码内核只需要实现极少的代码量,就可以应用整套机制。
  • 同时支持多种画面显示策略,自动调整(原始分辨率小于显示控件尺寸则按照原始分辨率大小显示,否则等比例缩放)、等比例缩放(永远等比例缩放)、拉伸填充(永远拉伸填充)。所有内核和所有视频显示模式下都支持三种画面显示策略。
  • 同时支持多种视频显示模式,句柄模式(传入控件句柄交给对方绘制控制)、绘制模式(回调拿到数据后转成QImage用QPainter绘制)、GPU模式(回调拿到数据后转成yuv用QOpenglWidget绘制)。
  • 支持多种硬件加速类型,ffmpeg可选dxva2、d3d11va等,mpv可选auto、dxva2、d3d11va,vlc可选any、dxva2、d3d11va。不同的系统环境有不同的类型选择,比如linux系统有vaapi、vdpau,macos系统有videotoolbox。
  • 解码线程和显示窗体分离,可指定任意解码内核挂载到任意显示窗体,动态切换。
  • 支持共享解码线程,默认开启并且自动处理,当识别到相同的视频地址,共享一个解码线程,在网络视频环境中可以大大节约网络流量以及对方设备的推流压力。国内顶尖视频厂商均采用此策略。这样只要拉一路视频流就可以共享到几十个几百个通道展示。
  • 自动识别视频旋转角度并绘制,比如手机上拍摄的视频一般是旋转了90度的,播放的时候要自动旋转处理,不然默认是倒着的。
  • 自动识别视频流播放过程中分辨率的变化,在视频控件上自动调整尺寸。比如摄像机可以在使用过程中动态配置分辨率,当分辨率改动后对应视频控件也要做出同步反应。
  • 音视频文件无感知自动切换循环播放,不会出现切换期间黑屏等肉眼可见的切换痕迹。
  • 视频控件同时支持任意解码内核、任意画面显示策略、任意视频显示模式。
  • 视频控件悬浮条同时支持句柄、绘制、GPU三种模式,非绝对坐标移来移去。
  • 本地摄像头设备支持指定设备名称、分辨率、帧率进行播放。
  • 录像文件同时支持打开的视频文件、本地摄像头、网络视频流等。
  • 瞬间响应打开和关闭,无论是打开不存在的视频或者网络流,探测设备是否存在,读取中的超时等待,收到关闭指令立即中断之前的操作并响应。
  • 支持打开各种图片文件,支持本地音视频文件拖曳播放。
  • 视频控件悬浮条自带开始和停止录像切换、声音静音切换、抓拍截图、关闭视频等功能。
  • 音频组件支持声音波形值数据解析,可以根据该值绘制波形曲线和柱状声音条,默认提供了声音振幅信号。
  • 各组件中极其详细的打印信息提示,尤其是报错信息提示,封装的统一打印格式。针对现场复杂的设备环境测试极其方便有用,相当于精确定位到具体哪个通道哪个步骤出错。
  • 代码框架和结构优化到最优,性能强悍,持续迭代更新升级。
  • 源码支持Qt4、Qt5、Qt6,兼容所有版本。

5.3 视频控件

  • 可动态添加任意多个osd标签信息,标签信息包括名字、是否可见、字号大小、文本文字、文本颜色、标签图片、标签坐标、标签格式(文本、日期、时间、日期时间、图片)、标签位置(左上角、左下角、右上角、右下角、居中、自定义坐标)。
  • 可动态添加任意多个图形信息,这个非常有用,比如人工智能算法解析后的图形区域信息直接发给视频控件即可。图形信息支持任意形状,直接绘制在原始图片上,采用绝对坐标。
  • 图形信息包括名字、边框大小、边框颜色、背景颜色、矩形区域、路径集合、点坐标集合等。
  • 每个图形信息都可指定三种区域中的一种或者多种,指定了的都会绘制。
  • 内置悬浮条控件,悬浮条位置支持顶部、底部、左侧、右侧。
  • 悬浮条控件参数包括边距、间距、背景透明度、背景颜色、文本颜色、按下颜色、位置、按钮图标代码集合、按钮名称标识集合、按钮提示信息集合。
  • 悬浮条控件一排工具按钮可自定义,通过结构体参数设置,图标可选图形字体还是自定义图片。
  • 悬浮条按钮内部实现了录像切换、抓拍截图、静音切换、关闭视频等功能,也可以自行在源码中增加自己对应的功能。
  • 悬浮条按钮对应实现了功能的按钮,有对应图标切换处理,比如录像按钮按下后会切换到正在录像中的图标,声音按钮切换后变成静音图标,再次切换还原。
  • 悬浮条按钮单击后都用名称唯一标识作为信号发出,可以自行关联响应处理。
  • 悬浮条空白区域可以显示提示信息,默认显示当前视频分辨率大小,可以增加帧率、码流大小等信息。
  • 视频控件参数包括边框大小、边框颜色、焦点颜色、背景颜色(默认透明)、文字颜色(默认全局文字颜色)、填充颜色(视频外的空白处填充黑色)、背景文字、背景图片(如果设置了图片优先取图片)、是否拷贝图片、缩放显示模式(自动调整、等比例缩放、拉伸填充)、视频显示模式(句柄、绘制、GPU)、启用悬浮条、悬浮条尺寸(横向为高度、纵向为宽度)、悬浮条位置(顶部、底部、左侧、右侧)。

5.4 内核ffmpeg

  • 支持各种音视频文件、本地摄像头设备,各种视频流网络流。
  • 支持开始播放、暂停播放、继续播放、停止播放、设置播放进度、倍速播放。
  • 可设置音量、静音切换、抓拍图片、录像存储。
  • 自动提取专辑信息比如标题、艺术家、专辑、专辑封面,自动显示专辑封面。
  • 完美支持音视频同步和倍速播放。
  • 解码策略支持速度优先、质量优先、均衡处理、最快速度。
  • 支持手机视频旋转角度显示,比如一般手机拍摄的视频是旋转了90度的,解码显示的时候需要重新旋转90度才是正的。
  • 自动转换yuv420格式,比如本地摄像头是yuyv422格式,有些视频文件是xx格式,统一将非yuv420格式转换,然后再进行处理。
  • 支持硬解码dxva2、d3d11va等,性能极高尤其是大分辨率比如4K视频。
  • 视频响应极低延迟0.2s左右,极速响应打开视频流0.5s左右,专门做了优化处理。
  • 硬解码和GPU绘制组合,极低CPU占用,比海康大华等客户端更优。
  • 支持视频流中的各种音频格式,AAC、PCM、G.726、G.711A、G.711Mu、G.711ulaw、G.711alaw、MP2L2等都支持,推荐选择AAC兼容性跨平台性最好。
  • 视频存储支持yuv、h264、mp4多种格式,音频存储支持pcm、wav、aac多种格式。默认视频mp4格式、音频aac格式。
  • 支持分开存储音频视频文件,也支持合并到一个mp4文件,默认策略是无论何种音视频文件格式存储,最终都转成mp4及aac格式,然后合并成音视频一起的mp4文件。
  • 支持本地摄像头实时视频显示带音频输入输出,音视频录制合并到一个mp4文件。
  • 支持H264/H265编码(现在越来越多的监控摄像头是H265视频流格式)生成视频文件,内部自动识别切换编码格式。
  • 自动识别视频流动态分辨率改动,重新打开视频流。
  • 支持用户信息中包含特殊字符(比如用户信息中包含+#@等字符)的视频流播放,内置解析转义处理。
  • 纯qt+ffmpeg解码,非sdl等第三方绘制播放依赖,gpu绘制采用qopenglwidget,音频播放采用qaudiooutput。
  • 同时支持ffmpeg2、ffmpeg3、ffmpeg4、ffmpeg5版本,全部做了兼容处理。如果需要支持xp需要选用ffmpeg3及以下。

到此这篇关于Qt利用ffmpeg实现音视频同步的文章就介绍到这了,更多相关Qt ffmpeg音视频同步内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Qt音视频开发之利用ffmpeg实现倍速播放

    目录 一.前言 二.功能特点 2.1 基础功能 2.2 特色功能 2.3 视频控件 2.4 内核ffmpeg 三.体验地址 四.效果图 五.相关代码 一.前言 用ffmpeg做倍速播放,是好多年都一直没有实现的功能,有个做法是根据倍速参数,不断切换播放位置,实现效果不是很好,ffplay中的倍速就做得很好,而且声音无论倍速多少还非常柔和,有特别的降噪处理啥的,ffplay中的倍速使用的滤镜去实现,并动态调整pts/dts的值,整个处理过程看起来比较复杂,想着有没有稍微简单一点的办法,在经过一个朋

  • QT+ffmpeg实现视频解析的示例详解

    目录 一.创建QT项目 二.引入ffmpeg 1.复制头文件和lib 2.复制bin文件 3.简单测试 三.视频解析 1.创建线程 2.创建自定义绘制控件 3.使用自定义控件 4.开启线程,进行视频解析 一.创建QT项目 首先安装了最新的Community版本,Creator是8.0.1版本了. 然后进行项目的创建. 得到的项目没有pro文件,而是CMakeLists.txt. 二.引入ffmpeg 从下面下载的ffmpeg-5.0.1-full_build-shared.7z. https:/

  • Qt+FFMPEG实现循环解码详解

    目录 一.结果 二.解码准备工作+循环解码相关操作 videodecode.h .cpp main.cpp 一.结果 可以设置延时函数-----遍历每一帧的信息进行打印 25(fps)*30(秒)=750帧 二.解码准备工作+循环解码相关操作 videodecode.h .cpp #ifndef VIDEODECODE_H #define VIDEODECODE_H #include <QObject> //当前C++兼容C语言 extern "C" { //avcodec

  • Qt利用ffmpeg实现音视频同步

    目录 一.前言 二.效果图 三.体验地址 四.相关代码 五.功能特点 5.1 基础功能 5.2 特色功能 5.3 视频控件 5.4 内核ffmpeg 一.前言 用ffmpeg来做音视频同步,个人认为这个是ffmpeg基础处理中最难的一个,无数人就卡在这里,怎么也不准,本人也是尝试过网上各种demo,基本上都是渣渣,要么仅仅支持极其少量的视频文件比如收到的数据包是一帧视频一帧音频的,要么根本没法同步歪七八糟的,要么进度跳过去直接蹦蹦蹦崩溃的,其实最完美的音视频同步处理demo就是ffplay,我亲

  • Javacv使用ffmpeg实现音视频同步播放

    最近用javaCV的ffmpeg包的FFmpegFrameGrabber帧捕捉器对捕捉到的音频帧和视频帧做了同步的播放.采用的同步方法是视频向音频同步. 程序和源码 具体的思路如下: (1)首先介绍ffmpeg是如何捕捉视频文件的图像和声音的 FFmpegFrameGrabber fg = new FFmpegFrameGrabber("a video file path or a url); 得到帧捕捉器对象后,调用它的grab()方法就会返回捕捉到的Frame对象.这个Frame可以是视频帧

  • android采用FFmpeg实现音视频合成与分离

    上一篇文章谈到音频剪切.混音.拼接与转码,也详细介绍cMake配置与涉及FFmpeg文件的导入: android端采用FFmpeg进行音频混合与拼接剪切.现在接着探讨音视频的合成与分离. 1.音频提取 从多媒体文件中提取音频,关键命令为"-acodec copy -vn",其中"-acodec copy"是采用音频编码器拷贝音频流,"-vn"是去掉video视频流: /** * 使用ffmpeg命令行进行抽取音频 * @param srcFile

  • C# 调用FFmpeg处理音视频的示例

    FFmpeg 开源.跨平台.体积小.功能强大,提供了录制.转换以及流化音视频的完整解决方案. 官网:https://www.ffmpeg.org/ 百科:https://baike.baidu.com/item/ffmpeg/2665727?fr=aladdin FFmpeg 应用非常广泛,可以用来播放本地视频甚至网络视频,查看音视频信息,还可以用于从视频中提取音频,转换音视频文件格式等等,本文主要介绍如何调用 FFmpeg 来查看音视频信息.从视频中提取音频.转换音视频格式等. 1. 调用FF

  • C语言结合ffmpeg打印音视频信息

    目录 一.通过此文可以得到什么 二.实现思路 三.实现效果 四.实现源代码  一.通过此文可以得到什么 通过此练习: 1.知道了如何计算一个音频和视频的播放时间: 2.知道了音视频解码的思路的大体流程,之后无非就是在这个流程上进行扩充细节: 3.知道了如何通过C语言或者C++编程语言结合ffmpeg拿到一些音视频的关键信息,例如:帧率等: 二.实现思路 三.实现效果 zhenghui@zh-pc:/data/project/VSCProject/ffmpegStudy$ make make al

  • C#调用FFmpeg操作音视频的实现示例

    目录 项目背景 FFmpeg介绍 FFmpeg相关教程 博客示例源码 下载FFmpeg.exe安装包 C#进程调用FFmpeg操作音视频 项目背景 因为公司需要对音视频做一些操作,比如说对系统用户的发音和背景视频进行合成,以及对多个音视频之间进行合成,还有就是在指定的源背景音频中按照对应的规则在视频的多少秒钟内插入一段客户发音等一些复杂的音视频操作.本篇文章主要讲解的是使用C#进程(Process)调用FFmpeg.exe进行视频合并,音频合并,音频与视频合并成视频这几个简单的音视频操作,还有些

  • 利用Ffmpeg获得flv视频缩略图和视频时间的代码

    问题描述:获得flv视频的缩略图和视频时间长度 谷歌了半天发现可以使用Ffmpeg获得视频的一些信息,先介绍一下FFMEPG 这里简单说一下:FFmpeg是用于录制.转换和流化音频和视频的完整解决方案,一套领先的音/视频编解码类库.官方正式版ffmpeg不支持rmvb和rm格式. 不过有很多解决方法 FFmpeg的官方网址是 http://ffmpeg.mplayerhq.hu/ . 中文Wiki是 http://www.ffmpeg.com.cn/ ,资料很多. ㈠安装FFMEPG 操作系统:

  • Qt音视频开发之视频文件保存功能的实现

    目录 一.前言 二.效果图 三.体验地址 四.相关代码 五.功能特点 5.1 基础功能 5.2 特色功能 5.3 视频控件 5.4 内核ffmpeg 一.前言 和音频存储类似,视频的存储也对应三种格式,视频最原始的数据是yuv(音频对应pcm),视频压缩后的数据是h264(音频对应aac),由于很多播放器或者早期的播放器不支持直接播放h264文件,所以需要用编码器编码成mp4格式,这块就需要用到ffmpeg里面一整套的编码流程,对yuv数据进行编码成MP4格式存储. 在经过对各种视频文件或者视频

  • 浅谈音视频 pts dts基本概念及理解

    目录 I.P.B 帧 DTS.PTS 的概念 音视频的同步 PTS和DTS的时间基 视频的播放过程可以简单理解为一帧一帧的画面按照时间顺序呈现出来的过程,就像在一个本子的每一页画上画,然后快速翻动的感觉.但是在实际应用中,并不是每一帧都是完整的画面,因为如果每一帧画面都是完整的图片,那么一个视频的体积就会很大,这样对于网络传输或者视频数据存储来说成本太高,所以通常会对视频流中的一部分画面进行压缩(编码)处理.由于压缩处理的方式不同,视频中的画面帧就分为了不同的类别,其中包括:I 帧.P 帧.B

随机推荐