VLC播放器调试经验总结

来源:互联网 发布:painter软件对应中文 编辑:程序博客网 时间:2024/06/06 07:29

一、前言

在使用VS学习VLC源码时,可以打断点分析变量数据,跟踪代码流程,方便我们理解源码。但是在定位音视频卡顿、延时等疑难问题时,这一招就不管用了,因为打上断点就会导致实时计算的pts值不准确,影响复现真实场景。所以音视频卡顿、延时类问题,更需要我们抓包、打印每一帧数据的Timestamp、pts及clock转换中的关键数据。这里引入一个简单的方法:增加收流、解码、渲染一条线上的时间戳,便于分析。

二、时间戳日志打印具体方法

1、将live/liveMedia/include/RTPSource.hh中的fCurPacketRTPTimestamp变量修改为pubilc类型

class RTPSource: public FramedSource {public:    <span style="color:#3333FF;">u_int32_t fCurPacketRTPTimestamp;</span>  static Boolean lookupByName(UsageEnvironment& env, char const* sourceName,      RTPSource*& resultSource);
2、在live\liveMedia\FramedSource.cpp中包含RTPSource.hh头文件,修改FramedSource::afterGetting函数,将裸码流中的Timestamp传递出去

void FramedSource::afterGetting(FramedSource* source) {  source->fIsCurrentlyAwaitingData = False;      // indicates that we can be read again      // Note that this needs to be done here, in case the "fAfterFunc"      // called below tries to read another frame (which it usually will)  <span style="color:#3333FF;">// m by yagerfgcs begin 用时间戳fCurPacketRTPTimestamp替换fDurationInMicroseconds,传递到live555::StreamRead函数中  if (source->fAfterGettingFunc != NULL) {    (*(source->fAfterGettingFunc))(source->fAfterGettingClientData,   source->fFrameSize, source->fNumTruncatedBytes,   source->fPresentationTime,                   ((RTPSource*)source)->fCurPacketRTPTimestamp);  }    /*if (source->fAfterGettingFunc != NULL) {      (*(source->fAfterGettingFunc))(source->fAfterGettingClientData,      source->fFrameSize, source->fNumTruncatedBytes,      source->fPresentationTime,      source->fDurationInMicroseconds);      }*/  // end</span>}
3、在modules\access\live555.cpp中StreamRead函数中借用p_block->i_dts存储Timestamp。传递出去

    /* Update our global npt value */    if( tk->f_npt > 0 &&        ( tk->f_npt < p_sys->f_npt_length || p_sys->f_npt_length <= 0 ) )        p_sys->f_npt = tk->f_npt;    if( p_block )    {        if( !tk->b_muxed && !tk->b_asf )        {            if( i_pts != tk->i_pts )                p_block->i_pts = VLC_TS_0 + i_pts;            /*FIXME: for h264 you should check that packetization-mode=1 in sdp-file */            p_block->i_dts = ( tk->fmt.i_codec == VLC_CODEC_MPGV ) ? VLC_TS_INVALID : (VLC_TS_0 + i_pts);            <span style="color:#3333FF;">// yagerfgcs for 借dts值赋值给timestamp;            p_block->i_dts = duration;</span>        }        if( tk->b_muxed )            stream_DemuxSend( tk->p_out_muxed, p_block );        else if( tk->b_asf )            stream_DemuxSend( p_sys->p_out_asf, p_block );        else            es_out_Send(p_demux->out, tk->p_es, p_block);    }

4、在src\input\es_out.c中EsOutSend函数中可以分别打印音视频码流时间戳timestamp、显示时间戳pts。也可以在此处屏蔽音频或视频的输入,避免送入到解码模块。

static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ){    es_out_sys_t   *p_sys = out->p_sys;    input_thread_t *p_input = p_sys->p_input;    if( libvlc_stats( p_input ) )    {        uint64_t i_total;        vlc_mutex_lock( &p_input->p->counters.counters_lock );        stats_Update( p_input->p->counters.p_demux_read,                      p_block->i_buffer, &i_total );        stats_Update( p_input->p->counters.p_demux_bitrate, i_total, NULL );        /* Update number of corrupted data packats */        if( p_block->i_flags & BLOCK_FLAG_CORRUPTED )        {            stats_Update( p_input->p->counters.p_demux_corrupted, 1, NULL );        }        /* Update number of discontinuities */        if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY )        {            stats_Update( p_input->p->counters.p_demux_discontinuity, 1, NULL );        }        vlc_mutex_unlock( &p_input->p->counters.counters_lock );    }    vlc_mutex_lock( &p_sys->lock );    /* Mark preroll blocks */    if( p_sys->i_preroll_end >= 0 )    {        int64_t i_date = p_block->i_pts;        if( p_block->i_pts <= VLC_TS_INVALID )            i_date = p_block->i_dts;        if( i_date < p_sys->i_preroll_end )            p_block->i_flags |= BLOCK_FLAG_PREROLL;    }    if( !es->p_dec )    {        block_Release( p_block );        vlc_mutex_unlock( &p_sys->lock );        return VLC_SUCCESS;    }    /* Check for sout mode */    if( p_input->p->p_sout )    {        /* FIXME review this, proper lock may be missing */        if( p_input->p->p_sout->i_out_pace_nocontrol > 0 &&            p_input->p->b_out_pace_control )        {            msg_Dbg( p_input, "switching to sync mode" );            p_input->p->b_out_pace_control = false;        }        else if( p_input->p->p_sout->i_out_pace_nocontrol <= 0 &&                 !p_input->p->b_out_pace_control )        {            msg_Dbg( p_input, "switching to async mode" );            p_input->p->b_out_pace_control = true;        }    }<span style="color:#FF0000;">   <span style="color:#3333FF;"> // add by yagerfgcs for log 音视频打印不同日志,便于定位    if (es->p_dec->fmt_out.i_cat == VIDEO_ES)    {        msg_Dbg(p_input, "[TS es_out::EsOutSend] video pts[%llu] timestamp[%llu]", p_block->i_pts, p_block->i_dts);    }    else if (es->p_dec->fmt_out.i_cat == AUDIO_ES)    {        msg_Dbg(p_input, "[TS es_out::EsOutSend] audio pts[%llu] timestamp[%llu]", p_block->i_pts, p_block->i_dts);    }    // end by add</span></span>    /* Decode */    if( es->p_dec_record )    {        block_t *p_dup = block_Duplicate( p_block );        if( p_dup )            input_DecoderDecode( es->p_dec_record, p_dup,                                 p_input->p->b_out_pace_control );    }    input_DecoderDecode(es->p_dec, p_block,                        p_input->p->b_out_pace_control);<span style="color:#FF0000;">    <span style="color:#3333FF;">// yagerfgcs test for:有时在定位视频问题时,为了排查干扰,可以屏蔽音频。反之亦然。需要的同仁,可以放开这段代码。    /*if (es->p_dec->fmt_out.i_cat == VIDEO_ES)    {        input_DecoderDecode(es->p_dec, p_block,            p_input->p->b_out_pace_control);    }    else    {        block_Release(p_block);    }*/</span></span>        es_format_t fmt_dsc;    vlc_meta_t  *p_meta_dsc;    if( input_DecoderHasFormatChanged( es->p_dec, &fmt_dsc, &p_meta_dsc ) )    {        EsOutUpdateInfo( out, es, &fmt_dsc, p_meta_dsc );        es_format_Clean( &fmt_dsc );        if( p_meta_dsc )            vlc_meta_Delete( p_meta_dsc );    }    /* Check CC status */    bool pb_cc[4];    input_DecoderIsCcPresent( es->p_dec, pb_cc );    for( int i = 0; i < 4; i++ )    {        es_format_t fmt;        if(  es->pb_cc_present[i] || !pb_cc[i] )            continue;        msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, es->i_id );        es_format_Init( &fmt, SPU_ES, EsOutFourccClosedCaptions[i] );        fmt.i_group = es->fmt.i_group;        if( asprintf( &fmt.psz_description,                      _("Closed captions %u"), 1 + i ) == -1 )            fmt.psz_description = NULL;        es->pp_cc_es[i] = EsOutAdd( out, &fmt );        es->pp_cc_es[i]->p_master = es;        es_format_Clean( &fmt );        /* */        es->pb_cc_present[i] = true;    }    vlc_mutex_unlock( &p_sys->lock );    return VLC_SUCCESS;}

5、在src\input\decoder.c中DecoderProcess函数中打印音视频时间戳

static void DecoderProcess( decoder_t *p_dec, block_t *p_block ){    decoder_owner_sys_t *p_owner = (decoder_owner_sys_t *)p_dec->p_owner;    const bool b_flush_request = p_block && (p_block->i_flags & BLOCK_FLAG_CORE_FLUSH);    if( p_dec->b_error )    {        if( p_block )            block_Release( p_block );        goto flush;    }    if( p_block && p_block->i_buffer <= 0 )    {        assert( !b_flush_request );        block_Release( p_block );        return;    }#ifdef ENABLE_SOUT    if( p_owner->b_packetizer )    {        if( p_block )            p_block->i_flags &= ~BLOCK_FLAG_CORE_PRIVATE_MASK;        DecoderProcessSout( p_dec, p_block );    }    else#endif    {        bool b_flush = false;        if( p_block )        {            const bool b_flushing = p_owner->i_preroll_end == INT64_MAX;            DecoderUpdatePreroll( &p_owner->i_preroll_end, p_block );            b_flush = !b_flushing && b_flush_request;            p_block->i_flags &= ~BLOCK_FLAG_CORE_PRIVATE_MASK;        }        if( p_dec->fmt_out.i_cat == AUDIO_ES )        {            <span style="color:#3333FF;">//add by yagerfgcs for log            if (p_block)            {                msg_Dbg(p_dec, "[DS 01 decoder::DecoderProcess] audio pts[%llu]", p_block->i_pts);            }            //end</span>                        DecoderProcessAudio( p_dec, p_block, b_flush );        }        else if( p_dec->fmt_out.i_cat == VIDEO_ES )        {            <span style="color:#3333FF;">//add by yagerfgcs for log            if (p_block)            {                msg_Dbg(p_dec, "[DS 01 decoder::DecoderProcess] video pts[%llu]", p_block->i_pts);            }            //end</span>            DecoderProcessVideo( p_dec, p_block, b_flush );        }        else if( p_dec->fmt_out.i_cat == SPU_ES )        {            DecoderProcessSpu( p_dec, p_block, b_flush );        }        else        {            msg_Err( p_dec, "unknown ES format" );            p_dec->b_error = true;        }    }    /* */flush:    if( b_flush_request )        DecoderProcessOnFlush( p_dec );}

6、在modules\codec\avcodec\video.c中DecodeVideo函数增加日志

picture_t *DecodeVideo( decoder_t *p_dec, block_t **pp_block ){    .................    if( p_block)    {       <span style="color:#3333FF;"> //yagerfgcs for log        msg_Dbg(p_dec, "[DS 02 video::DecodeVideo] video pts[%llu]", p_block->i_pts);</span>        if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) )        {            p_sys->i_pts = VLC_TS_INVALID; /* To make sure we recover properly */            p_sys->i_late_frames = 0;            post_mt( p_sys );            if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY )                avcodec_flush_buffers( p_context );            wait_mt( p_sys );            block_Release( p_block );            return NULL;        }        if( p_block->i_flags & BLOCK_FLAG_PREROLL )        {            /* Do not care about late frames when prerolling             * TODO avoid decoding of non reference frame             * (ie all B except for H264 where it depends only on nal_ref_idc) */            p_sys->i_late_frames = 0;            <span style="color:#3333FF;">//yagerfgcs for log            msg_Dbg(p_dec, "[DS video::DecodeVideo] p_block->i_flags == BLOCK_FLAG_PREROLL");</span>        }    }    ....................}

7、src\input\decoder.c中DecoderProcess函数

static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block ){    decoder_owner_sys_t *p_owner = p_dec->p_owner;    picture_t      *p_pic;    int i_lost = 0;    int i_decoded = 0;    int i_displayed = 0;    while( (p_pic = p_dec->pf_decode_video( p_dec, &p_block )) )    {        <span style="color:#3333FF;">//yagerfgcs for log        msg_Dbg(p_dec, "[DS 03 decoder::DecoderDecodeVideo] video pts[%llu]", p_pic->date);</span>        vout_thread_t  *p_vout = p_owner->p_vout;        if( DecoderIsExitRequested( p_dec ) )        {            /* It prevent freezing VLC in case of broken decoder */            vout_ReleasePicture( p_vout, p_pic );            if( p_block )                block_Release( p_block );            break;        }        .....................     }     ......................}

8、src\input\decoder.c中DecoderProcess函数

static void DecoderPlayVideo( decoder_t *p_dec, picture_t *p_picture,                              int *pi_played_sum, int *pi_lost_sum ){    ...........................    const bool b_dated = p_picture->date > VLC_TS_INVALID;    int i_rate = INPUT_RATE_DEFAULT;        mtime_t dateBefore = p_picture->date;        DecoderFixTs( p_dec, &p_picture->date, NULL, NULL,                  &i_rate, DECODER_BOGUS_VIDEO_DELAY );    <span style="color:#3333FF;">//yagerfgcs for log    msg_Dbg(p_dec, "[DS 04 decoder::DecoderPlayVideo] video date before[%llu] after DecoderFixTs[%llu]",             dateBefore, p_picture->date);</span>    vlc_mutex_unlock( &p_owner->lock );    /* */    if( !p_picture->b_force && p_picture->date <= VLC_TS_INVALID ) // FIXME --VLC_TS_INVALID verify video_output/*        b_reject = true;    ............................} 

三、让VLC默认打印debug日志,方便保存的方法

1、通过VLC菜单->工具->消息,可以将日志级别改为“2(调试)”,这样就可以打印出所有的调试日志,点击“另存为”可保存到文件中


2、也可以通过修改代码,让vlc默认打印debug日志

在modules\gui\qt4\dialogs\messages.cpp中MessagesDialog::MessagesDialog函数,修改默认级别

MessagesDialog::MessagesDialog( intf_thread_t *_p_intf)               : QVLCFrame( _p_intf ){    setWindowTitle( qtr( "Messages" ) );    setWindowRole( "vlc-messages" );    /* Build Ui */    ui.setupUi( this );    ui.bottomButtonsBox->addButton( new QPushButton( qtr("&Close"), this ),                                         QDialogButtonBox::RejectRole );    /* Modules tree */    ui.modulesTree->setHeaderHidden( true );    /* Buttons and general layout */    ui.saveLogButton->setToolTip( qtr( "Saves all the displayed logs to a file" ) );    <span style="color:#3333FF;">int i_verbosity = 2;// var_InheritInteger(p_intf, "verbose");</span>    changeVerbosity( i_verbosity );    ui.verbosityBox->setValue( qMin( i_verbosity, 2 ) );    ................} 

2 0
原创粉丝点击