• 62305阅读
  • 78回复

用Qt和FFmpeg写了个播放器 QtAV库 [复制链接]

上一主题 下一主题
离线carmen816

只看该作者 40楼 发表于: 2013-12-17
Re:回 38楼(carmen816) 的帖子
引用第39楼novesky于2013-12-15 19:18发表的 回 38楼(carmen816) 的帖子 :
这个还真不知道,为什么要这么做?
不同步音频的话应该同步什么?系统时间?我现在的是有问题,来不及画的就丢掉


VideoThread.cpp
        qreal pts = pkt.pts;
        d.delay = pts - d.clock->value();
        /*
         *after seeking forward, a packet may be the old, v packet may be
         *the new packet, then the d.delay is very large, omit it.
         *TODO: 1. how to choose the value
         * 2. use last delay when seeking
        */
        bool skip_render = false;
        if (qAbs(d.delay) < 2.718) {
            if (d.delay < -kSyncThreshold) { //Speed up. drop frame?
                //continue;
            }
            while (d.delay > kSyncThreshold) { //Slow down
                //d.delay_cond.wait(&d.mutex, d.delay*1000); //replay may fail. why?
                //qDebug("~~~~~wating for %f msecs", d.delay*1000);
                usleep(kSyncThreshold * 1000000UL);
                if (d.stop)
                    d.delay = 0;
                else
                    d.delay -= kSyncThreshold;
            }
            if (d.delay > 0)
                usleep(d.delay * 1000000UL);
        } else { //when to drop off?
            qDebug("delay %f/%f", d.delay, d.clock->value());
            if (d.delay > 0) {
                msleep(64);
            } else {
                // FIXME: if continue without decoding, hw decoding may crash, why?
                //audio packet not cleaned up?
                //continue;
                skip_render = true;
            }
        }
        d.clock->updateVideoPts(pts); //here?
        if (d.stop) {
            qDebug("video thread stop before decode()");
            break;
        }

        if (!dec->decode(pkt.data)) {
            pkt = Packet();
            continue;
        } else {
            int undecoded = dec->undecodedSize();
            if (undecoded > 0) {
                pkt.data.remove(0, pkt.data.size() - undecoded);
            } else {
                pkt = Packet();
            }
        }


        if (skip_render)
            continue;


msleep(64); 25帧为例,1000 / 25 = 40,这边的64值得商榷。
您将解码和帧的刷新放在一个线程中,会影响同步。
解码过程消耗时间比较长,而且解码耗时不是个固定值。会影响“与kSyncThreshold比较”。
所以那些播放器会缓冲解码后的原始音视频数据
ffplay中,使用video_thread解码后queue_picture,放入队列。在video_refresh(另一个线程)中刷视频。


PS:FIXME: if continue without decoding, hw decoding may crash, why?
        软件解码不crash,播放也会出现花屏(或者是黑屏几帧,直到下个I帧出现,看解码器的优化程度)。
        因为视频的解码时,通常解码器会参考上一个包的结果。“hw decoder”或者“hw decoder与ffmpeg之间的连接”写的不健壮那就只能crash了。。。



AudioThread.cpp
        if (!dec->decode(pkt.data)) {
            qWarning("Decode audio failed");
            qreal dt = pkt.pts - d.last_pts;
            if (dt > 0.618 || dt < 0) {
                dt = 0;
            }
            //qDebug("sleep %f", dt);
            //TODO: avoid acummulative error. External clock?
            msleep((unsigned long)(dt*1000.0));
            pkt = Packet();
            d.last_pts = d.clock->value(); //not pkt.pts! the delay is updated!
            continue;
        }
        QByteArray decoded(dec->data());
        int decodedSize = decoded.size();
        int decodedPos = 0;
        qreal delay =0;
        //AudioFormat.durationForBytes() calculates int type internally. not accurate
        AudioFormat &af = dec->resampler()->inAudioFormat();
        qreal byte_rate = af.bytesPerSecond();
        while (decodedSize > 0) {
            if (d.stop) {
                qDebug("audio thread stop after decode()");
                break;
            }
            int chunk = qMin(decodedSize, int(max_len*byte_rate));
            //AudioFormat.bytesForDuration
            qreal chunk_delay = (qreal)chunk/(qreal)byte_rate;
            pkt.pts += chunk_delay;
            d.clock->updateDelay(delay += chunk_delay);

AVClock 默认是 ClockType AudioClock
int chunk = qMin(decodedSize, int(max_len*byte_rate)); --->>> 意味着AVClock每0.02秒更新一次。
这导致您设计的AVClock不是线性的,而是离散的(相对视频而言,如果视频帧是30帧,每个视频帧间隔0.033秒,时钟和帧间隔处于同一数量级。。。),造成丢帧。


不同步音频的话应该同步什么?系统时间?
音频时间和系统时间结合。。。
下面文章的方案和ffplay所使用的如出一辙
http://www.rosoo.net/a/201203/15806.html   (三、音视频同步)

PS:十分惊叹楼主在QT,C++,架构设计等方面的造诣,使我受益匪浅。
        再次感谢楼主开源的精神。

离线novesky

只看该作者 41楼 发表于: 2013-12-17
回 40楼(carmen816) 的帖子
很感谢你的回复,很有价值。同步那块里面的数字阿什么的有些是随便写的。同时用绝对时间(比如系统时间)来同步也想过,不过有个问题就是如果音频慢了怎么办?是重采样来降低音频播放速度还是直接丢掉一部分?我还没试过。不知道你怎么看。
硬解的话我也猜到了会和前几帧相关,但是能否丢掉特殊的帧呢?看了下I、P、B都是互相联系的,好像丢什么都不行。这样的话seek时就有问题了,直接从中间某一帧开始解,会崩溃。
另外,渲染视频画面是在gui线程,不在解码线程,这个跟Qt有关。
其他的我回家再看看。
离线carmen816

只看该作者 42楼 发表于: 2013-12-17
Re:回 40楼(carmen816) 的帖子
引用第41楼novesky于2013-12-17 15:35发表的 回 40楼(carmen816) 的帖子 :
很感谢你的回复,很有价值。同步那块里面的数字阿什么的有些是随便写的。同时用绝对时间(比如系统时间)来同步也想过,不过有个问题就是如果音频慢了怎么办?是重采样来降低音频播放速度还是直接丢掉一部分?我还没试过。不知道你怎么看。
硬解的话我也猜到了会和前几帧相关,但是能否丢掉特殊的帧呢?看了下I、P、B都是互相联系的,好像丢什么都不行。这样的话seek时就有问题了,直接从中间某一帧开始解,会崩溃。
另外,渲染视频画面是在gui线程,不在解码线程,这个跟Qt有关。
其他的我回家再看看。

1.硬解的话我也猜到了会和前几帧相关,但是能否丢掉特殊的帧呢?看了下I、P、B都是互相联系的,好像丢什么都不行。这样的话seek时就有问题了,直接从中间某一帧开始解,会崩溃。
       seek后,下一个I 帧之前的数据全部丢弃。音频需要跟着同步。


2.另外,渲染视频画面是在gui线程,不在解码线程,这个跟Qt有关。
       最后渲染是在GUI线程没错,但是您对于帧的处理,超时丢弃,定时等待,格式转换,outputSet->sendVideoFrame,这些是在视频解码同一线程,并不是指最后画的过程。
        视频解码线程通常情况比较忙,应该在一个空闲线程控制上述过程。


离线novesky

只看该作者 43楼 发表于: 2013-12-17
回 42楼(carmen816) 的帖子
恩,谢谢,我回去试下。你是搞多媒体的吗?很专业啊。
离线novesky

只看该作者 44楼 发表于: 2013-12-18
回 42楼(carmen816) 的帖子
AVPacket只能判断是否含有关键帧,我把没关键帧的丢了,还是崩溃。
离线novesky

只看该作者 45楼 发表于: 2013-12-18
回 42楼(carmen816) 的帖子
看了下vlc的相关代码,好复杂,不过发现AVCodecContext.skip_frame = AVDISCARD_NONREF就不会崩溃了。默认的AVDISCARD_DEFAULT
只是跳过无用的包。
离线novesky

只看该作者 46楼 发表于: 2013-12-18
回 42楼(carmen816) 的帖子
有QQ么?
离线carmen816

只看该作者 47楼 发表于: 2013-12-19
Re:回 42楼(carmen816) 的帖子
引用第46楼novesky于2013-12-18 14:39发表的 回 42楼(carmen816) 的帖子 :
有QQ么?

离线chenjieat

只看该作者 48楼 发表于: 2013-12-26
太赞了,我的目标啊!
离线blueseait

只看该作者 49楼 发表于: 2013-12-31
1.3版本功能上有很大进步,但是如果要支持dxva硬解码需要重新编译ffmpeg并且加入dxva的外部包支持,我在网上找了一下没有找到关于FFMPEG与DXVA结合编译的文章,不知道楼主可否指导一下如何如何编译出支持DXVA的FFMPEG?
离线novesky

只看该作者 50楼 发表于: 2013-12-31
回 49楼(blueseait) 的帖子
有dxva相关的头文件和库,然后configure配置下,配置可以看我播放器 关于 对话框里的ffmpeg信息
离线blueseait

只看该作者 51楼 发表于: 2014-02-26
你好楼主,感谢你一直以来的努力和对开源的支持,也让我等学到了不少关于视频方面的知识,最近想在mac ox上学习一下,用你的最新版本,设置了cpath|library_path等环境变量,编译通过了,但是运行出现找不到库的错误:
dyld: Library not loaded: @rpath/Frameworks/libQtAV.1.dylib
  Referenced from: /users/nphd/workspace/QtAV-master/build/bin/player.app/Contents/MacOS/player
  Reason: image not found
请问下楼主在mac上面编译运行需要做哪些设置才能正常运行,出现我这个问题是什么原因呢,谢谢楼主!

离线blueseait

只看该作者 52楼 发表于: 2014-02-26
我补充一下:我的编译和运行环境是Qt5.2.1,在qtcreator中设置的环境变量,并在qtcreator中编译和运行时出现上述问题的。望楼主不吝指教!
离线novesky

只看该作者 53楼 发表于: 2014-02-28
回 blueseait 的帖子
blueseait:你好楼主,感谢你一直以来的努力和对开源的支持,也让我等学到了不少关于视频方面的知识,最近想在mac ox上学习一下,用你的最新版本,设置了cpath|library_path等环境变量,编译通过了,但是运行出现找不到库的错误:
dyld: Library not loaded: @rpath/Frameworks/libQtAV.1.dyl .. (2014-02-26 14:35) 

要把libQtAV.1.dylib拷到player.app/Contents/Frameworks下。另外,最后用macdeployqt打包的话会有问题,要手工把QtCreator里相应的Qt库拷过来,qt的打包工具问题很多。
离线blueseait

只看该作者 54楼 发表于: 2014-03-07
谢谢楼主指导,按你的方法做程序在mac上面已经可以运行了,但是播放没有声音,只有视频,我用brew install portaudio 安装了portaudio也不行,不知道声音出来还需要怎么做,不好意思,mac和视频初学者又来麻烦缕主了,谢谢!
离线blueseait

只看该作者 55楼 发表于: 2014-03-11
我发现videographicsitem这个工程在mac上运行是有声音的,player没有声音,不知道怎么回事。
离线xsjqqq123

只看该作者 56楼 发表于: 2014-03-22
Qt5.2.1 mingw的如何编译它啊?没学过编译原理之类的,业余爱好。那些depends如何处理?放在哪?
离线a408815041

只看该作者 57楼 发表于: 2014-05-10
6666666
Qt专业定制, widget\qml。upnp\onvif\GB28181\播放器。
QQ: 408815041
email: zyb920@hotmail.com
离线yangkun52131

只看该作者 58楼 发表于: 2014-05-30
播放器很不错。有个问题,视频有时候解码应该是滤镜不行,经常出现色块。还有同一个视频,若不加音轨两个视频竟然不同步。
离线novesky

只看该作者 59楼 发表于: 2014-06-03
回 yangkun52131 的帖子
yangkun52131:播放器很不错。有个问题,视频有时候解码应该是滤镜不行,经常出现色块。还有同一个视频,若不加音轨两个视频竟然不同步。 (2014-05-30 16:27)

出现色块是QtAV哪个版本?什么视频什么解码器?
没音频的同步有问题我也发现了,平时都测有音频的
离线yangkun52131

只看该作者 60楼 发表于: 2014-07-16
解码器是最新的ffmpeg版本,我用任何一个版本都一样。有的视频没事。一般有色块的播放的时候都是同一个地方出现。
离线foruok

只看该作者 61楼 发表于: 2014-07-17
这个强悍的说,赞一个。
我的博客:http://blog.csdn.net/foruok
我的微信订阅号:程序视界
离线foruok

只看该作者 62楼 发表于: 2014-07-17
不知道是否可以用在 Android 上?
我的博客:http://blog.csdn.net/foruok
我的微信订阅号:程序视界
离线libin88211

只看该作者 63楼 发表于: 2014-07-23
楼主你好,你做的很不错,可以看出付出了很多努力,使用方法也很方便,但是希望您 有空了做一下文档,介绍一下类的接口使用方法,这样才有更多的人能够使用。
离线libin88211

只看该作者 64楼 发表于: 2014-07-23
楼主你好,我现在要实现播放视频文件中某指定两帧之间的视频段,或是指定时间段内的视频段能在您的这个类库基础上做吗?通过什么方式找到指定帧进行播放?如蒙答复不胜感激!
离线x_greatwall

只看该作者 65楼 发表于: 2014-09-27
太赞了,真是学习了
离线fansgq

只看该作者 66楼 发表于: 2014-11-07
最新版(1.4.1)的下了,播放视频很容易崩溃。。。。
离线qyvlik

只看该作者 67楼 发表于: 2015-01-04
ubiboot
离线electri

只看该作者 68楼 发表于: 2015-01-22
编译的时候出现了以下的错误,libQtAV.so编译没问题,倒是在编译examples时出现了以下的错误
离线atsunlun

只看该作者 69楼 发表于: 2015-11-29
我在使用QTAV的时候,播放网络视频,不流畅。请问,我想设置它的缓存大小应该怎么做?还有随便将视频保存下来,应该怎么做?
离线zhuzhuwonder

只看该作者 70楼 发表于: 2015-12-17
请问楼主如何使用QtAV里面的例子?就比如说我想把simple player 单独拿出来整合到我的工程里面,而不想把它放在总的QtAV工程里面运行,但是单独编译这个工程会报错:
No rule to make target 'e:/QTAV/QtAV-master/examples/build-simpleplayer-Qt_4_8_6-Release/../out/lib_win_/QtAV.dll', needed by '..\out\bin\simpleplayer.exe'.  Stop.
请问该如何操作呢?谢谢!
离线novesky

只看该作者 71楼 发表于: 2015-12-21
回 zhuzhuwonder 的帖子
zhuzhuwonder:请问楼主如何使用QtAV里面的例子?就比如说我想把simple player 单独拿出来整合到我的工程里面,而不想把它放在总的QtAV工程里面运行,但是单独编译这个工程会报错:
No rule to make target 'e:/QTAV/QtAV-master/examples/build-simpleplayer-Qt_4_8_6-Release/../out/lib_w .. (2015-12-17 19:52) 

编译好qtav运行编译目录的sdk_install.bat,然后可以调用qtav模块了。选择simpleplayer_sdk.pro就是使用qtav模块脱离源码树编译,放到任何地方都能编译
离线zhuzhuwonder

只看该作者 72楼 发表于: 2015-12-23
回 novesky 的帖子
novesky:编译好qtav运行编译目录的sdk_install.bat,然后可以调用qtav模块了。选择simpleplayer_sdk.pro就是使用qtav模块脱离源码树编译,放到任何地方都能编译 (2015-12-21 23:54) 

那其他没有sdk的工程该如何独立编译呢,比如videogroup,videowall。还有videowall运行之后就只有一个9宫格,不能拖放视频进行播放,按快捷键也没有响应。
离线tianp2010

只看该作者 73楼 发表于: 2015-12-25
非常谢谢楼主的分享, 我正再做 ffmpeg 编码部分 ,下载研究学习
离线shallow_wy

只看该作者 74楼 发表于: 2016-03-16
为什么我源码总是编译不成功,出现一堆警告,最后程序异常结束
Starting F:\QtAV-master\build-QtAV-Desktop_Qt_5_5_1_MSVC2013_64bit-Debug\bin\extract.exe...
程序异常结束。
F:\QtAV-master\build-QtAV-Desktop_Qt_5_5_1_MSVC2013_64bit-Debug\bin\extract.exe crashed
离线qq413317226

只看该作者 75楼 发表于: 2016-04-04
厉害
离线姜小白

只看该作者 76楼 发表于: 2016-04-05
顶一个。。。
我整理的一些文章,持续更新中,有兴趣的小伙伴可以关注。
https://xiaozhuanlan.com/sorghum-cpp?rel=sorghum

Qt技术交流:QQ2499971906
离线xushuai_sio

只看该作者 77楼 发表于: 2016-05-11
厉害,学习了
离线foxgod

只看该作者 78楼 发表于: 2017-09-04
ffmepg是不是需要sdk一起才能播放视频呢
快速回复
限100 字节
 
上一个 下一个