ffmpeg,Stagefright 的时间管理及a/v同步

来源:互联网 发布:初中英语单词大全软件 编辑:程序博客网 时间:2024/05/06 07:02
一、ffmpeg中的时间, dts, pts
    stream由一个个packet组成,packet有两个时间:
    dts: 解码时间
    pts: 显示时间(Presentation timestamp)
    因为解码的时候,可能会有一些包需要在另一些包之前解,所以需要两个时间分开,
    比如,输出的时候 a b c d ,但解包的时候需要按 b a c d 的顺序去解,这样 a b 的pts dts就不是按顺序来。涉及到I B P帧的概念,这点有待验证。
    最后的输出以pts为准,另外每个stream有一个time_base(即ffmpeg的时间单位,由一个分子分母组成的struct)的概念,最后packet的输出的时机为:
    timestamp_output = pts * av_q2d(st->time_base)

二、Stagefright中的时间:
    1、Stagefright中音频的播放最后由AudioTrack进行播放速度的控制(代码分析参考后面4)。
        无论有无Video,XXXSource返回的kKeyTime不影响音频的播放(但必须返回,否则CHECK通不过)
        
    2、如果只有Video,以video的kKeyTime控制视频速度,
    
    3、如果两者都有,则以音频为准进行a/v同步:
        1)、取audio时间,计算latenessUs
            // mediaTimeUs为audio的kKeyTime
            // realTimeUs为AudioTrack返回的实际播放时间
            mAudioPlayer->getMediaTimeMapping(&realTimeUs, &mediaTimeUs);
            mTimeSourceDeltaUs = realTimeUs - mediaTimeUs;
            nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs;
            // timeUs为video的kKeyTime
            latenessUs = nowUs - timeUs;
            
        2)、如果快了,视频解码休息
            if (latenessUs < -10000) { // 10ms
                postVideoEvent_l(10000); // 即下一个mVideoEvent延迟触发。
            }
            事实上只有video时,控制解码速度也是通过这里控制的,即控制mVideoEvent触发频率。
            
        3)、慢了,往前seek,或丢帧
            if (latenessUs > 500000ll) { // 500ms
                mSeekTimeUs = mediaTimeUs; // 直接seek
            }
            if (latenessUs > 40000) { // 40ms
                mVideoBuffer = NULL; // 丢帧,立即触发下一个mVideoEvent
                postVideoEvent_l();
            }
            
    4、AudioTrack对音频播放速度的控制
        1)、AudioTrack与Output的数据交换通过共享内存实现,即AudioTrack中的mCblk,(本文不具体分析mCblk的建立及数据流程)
        2)、AudioTrack建立的时候,初始化了一个AudioTrackThread,线程循环进入 AudioTrack::processAudioBuffer,不停的去获取解码后的数据,buffer写入mCblk
        3)、
        AudioTrack::processAudioBuffer
        {
            obtainBuffer(&audioBuffer, -1);
            // 这里回调 AudioPlayer::fillBuffer() 返回解码后的数据
            // 解码流程与video一致,可参考前文《Stagefright,omx与Component的交互》的分析
            mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);  
            releaseBuffer(&audioBuffer);
        }
        4)、
        AudioTrack::obtainBuffer()
        {
            while (framesAvail == 0) {
                // 这里根据cblk控制了Buffer的获得,也就控制了最后回调获取解码后数据的频率
                // 也间接控制了audio解包速度
                // 但最后audio播放速度的控制应该是由output层控制,有待output层分析考证!
                result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
            }
        }
    
三、 Stagefright Extractor使用ffmpeg的demux时,时间的控制
    XXXSource中read返回的mKeyTime 使用 ffmpeg中timestamp_output的计算方法,转换为微妙(us)

四、以音频为例,这里顺便简单介绍一下Stagefright的线程管理
    1、Awesomeplyer中的mAudioSource分两种:
        当没有Decode流程(即Extractor返回解码后数据,如FLAC)时,为XXXSource
        由Decode流程时,为OMXCode

        两者都是继承自MediaSource。


    2、驱动数据流的线程主要有三个:
        a、Awesomeplayer的message处理线程,通过onVideoEvent()驱动
        b、AudioTrack的回调线程,通过AudioTrack::processAudioBuffer驱动

        c、OMX的Aloop线程,通过回调 OMXCodec::on_message驱动


    3、mAudioSource 为 XXXSource时,AudioTrack线程回调驱动XXXSource::read直接得到解码数据,并返回。
        mAudioSource 为OMXCodec时,通过OMXCodec::on_message驱动 (注意此时AudioTrack线程也在回调,但最后只是取回解码数据,并不驱动read)