Qt音视频开发09-ffmpeg内核音视频同步

Posted feiyangqingyun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt音视频开发09-ffmpeg内核音视频同步相关的知识,希望对你有一定的参考价值。

一、前言

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

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

常用的音视频同步方法:

通过fps来控制,fps表示一秒钟播放多少帧,比如25帧,可以自行计算一帧解码用掉的时间,一帧占用(1000/25=40毫秒),通过延时来处理,这其实是最渣渣的办法。
记住开始解码的时间startTime,通过av_rescale_q计算pts时间,两者的差值就是需要延时的时间,调用av_usleep来延时,这种只有部分文件正常,很多时候不正常。

Qt音视频开发37-USB摄像头解码ffmpeg方案

一、前言

用ffmpeg来处理USB摄像头,是前段时间研究视频监控ffmpeg内核的时候搞定的,既然ffmpeg这么牛逼的库可以解析各种音视频,我想处理个本地USB摄像头应该也不是什么难事,果真搜索也是一大堆,当然主要也是因为有个项目的应用需要用到ffmpeg来处理本地USB摄像头,需要拿到每张图片做智能分析,用Qt自带的camera类不大好处理,刚好将ffmpeg的处理流程都搞清楚了,索性直接用ffmpeg来直接处理好了,用上这么强大的解码库,理论上支持各种USB摄像头。本地USB摄像机不需要硬解码,视频流编码类型为 AV_CODEC_ID_RAWVIDEO 像素格式为 AV_PIX_FMT_YUYV422 不经过解码操作直接就可显示。

ffmpeg方案处理流程:

  1. 引入avdevice.h头文件,调用avdevice_register_all();注册本地设备处理。
  2. 调用av_dict_set设置分辨率(video_size)、帧率(framerate)等参数。
  3. 调用av_find_input_format设置输入格式。
  4. 调用avformat_open_input打开文件。
  5. 调用av_find_best_stream找到视频流地址。
  6. 调用avcodec_find_decoder设置视频解码器。
  7. 调用av_read_frame循环解码读取帧数据。
  8. 调用avcodec_send_packet avcodec_receive_frame解码数据。
  9. 处理完以后调用av_frame_free avcodec_close等释放资源。

二、功能特点

  1. 同时支持windows、linux、嵌入式linux上的USB摄像头实时采集。
  2. 支持多路USB摄像头多线程实时采集。
  3. 在嵌入式linux设备上,自动查找USB设备文件并加载。
  4. 可手动设置设备文件名称,手动设置后按照手动设置的设备文件加载。
  5. 在嵌入式linux设备上支持人脸识别接口,实时绘制人脸框。
  6. 具有打开、暂停、继续、关闭、截图等常规功能。
  7. 可设置两路OSD标签,分别设置文本、颜色、字号、位置等。
  8. 可作为视频监控系统使用。

三、效果图

技术图片

四、相关站点

  1. 国内站点:https://gitee.com/feiyangqingyun/QWidgetDemo
  2. 国际站点:https://github.com/feiyangqingyun/QWidgetDemo
  3. 个人主页:https://blog.csdn.net/feiyangqingyun
  4. 知乎主页:https://www.zhihu.com/people/feiyangqingyun/
  5. 体验地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652

五、核心代码

void FFmpegThread::initOption()
{
    //在打开码流前指定各种参数比如:探测时间/超时时间/最大延时等
    //设置缓存大小,1080p可将值调大
    av_dict_set(&options, "buffer_size", "8192000", 0);
    //以tcp方式打开,如果以udp方式打开将tcp替换为udp
    av_dict_set(&options, "rtsp_transport", transport.toUtf8().constData(), 0);
    //设置超时断开连接时间,单位微秒,3000000表示3秒
    av_dict_set(&options, "stimeout", "3000000", 0);
    //设置最大时延,单位微秒,1000000表示1秒
    av_dict_set(&options, "max_delay", "1000000", 0);
    //自动开启线程数
    av_dict_set(&options, "threads", "auto", 0);

    //单独对USB摄像机设置参数
    if (isUsbCamera) {
        //设置输入格式
        //av_dict_set(&options, "input_format", "mjpeg", 0);
        //设置分辨率
        QString size = QString("%1x%2").arg(videoWidth).arg(videoHeight);
        av_dict_set(&options, "video_size", size.toUtf8().constData(), 0);
        //设置帧率
        av_dict_set(&options, "framerate", "25", 0);
    }

    //本地USB摄像机不需要硬解码,强制改成回调运行和无硬解码
    //视频流编码类型为 AV_CODEC_ID_RAWVIDEO 像素格式为 AV_PIX_FMT_YUYV422 不经过解码操作直接就可显示
    if (isUsbCamera) {
        callback = true;
        hardware = "none";
    }

    //没有启用opengl则强制改为回调
#ifndef opengl
    callback = true;
#endif

    //rtmp视频流强制改成存储成h264裸流,目前存储成mp4还有问题
    if (url.startsWith("rtmp", Qt::CaseInsensitive)) {
        saveMp4 = false;
    }
}

bool FFmpegThread::initInput()
{
    //实例化格式处理上下文
    formatCtx = avformat_alloc_context();
    //设置超时回调,有些不存在的地址或者网络不好的情况下要卡很久
    formatCtx->interrupt_callback.callback = AVInterruptCallBackFun;
    formatCtx->interrupt_callback.opaque = this;

    //必须要有tryOpen标志位来控制超时回调,由他来控制是否继续阻塞等待打开
    tryOpen = false;

    //先判断是否是本地设备(video=设备名字符串),打开的方式不一样
    QByteArray urlData = url.toUtf8();
    AVInputFormat *ifmt = NULL;
    if (isUsbCamera) {
#if defined(Q_OS_WIN)
        ifmt = av_find_input_format("dshow");
#elif defined(Q_OS_LINUX)
        //ifmt = av_find_input_format("v4l2");
        ifmt = av_find_input_format("video4linux2");
#elif defined(Q_OS_MAC)
        ifmt = av_find_input_format("avfoundation");
#endif
    }

    int result = avformat_open_input(&formatCtx, urlData.data(), ifmt, &options);
    tryOpen = true;
    if (result < 0) {
        qDebug() << TIMEMS << "open input error" << url;
        return false;
    }

    //释放设置参数
    if (options != NULL) {
        av_dict_free(&options);
    }

    //获取流信息
    result = avformat_find_stream_info(formatCtx, NULL);
    if (result < 0) {
        qDebug() << TIMEMS << "find stream info error";
        return false;
    }

    return true;
}

以上是关于Qt音视频开发09-ffmpeg内核音视频同步的主要内容,如果未能解决你的问题,请参考以下文章

Qt音视频开发17-vlc内核回调拿图片进行绘制

Qt音视频开发19-vlc内核各种事件通知

Qt音视频开发20-vlc内核动态保存录像文件(不需要重新编译源码)

QT软件开发:基于libVLC内核设计视频播放器

QT软件开发:基于libVLC内核设计视频播放器

Qt音视频开发14-音视频文件保存基类的设计