VLC播放器调试经验总结

一、前言

在使用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 ) );
    ................
} 
时间: 2024-09-29 13:08:42

VLC播放器调试经验总结的相关文章

教你在Centos 上使用mmsh协议收听猫扑网络电台 VLC播放器

安装CentOS已经有一段时间了,但是由于在Linux下除了学习,其他是事情都干不了.今 天想闲来无事开了CentOS就想听一下歌,突然想起应该可以在Linux下听网络电台.网络电台 其实用的是mms协议.例如猫扑网络电台是 mms://ting.mop.com/mopradio . 由于 CentOS默认安装的播放器听不了mms协议.所以就google了一下有什么播放器可以听mms协议 .VLC media player 可以用. 1. 先到/etc/yum.repos.d/目录下ls一下,看

有图为证 Win 8版VLC播放器即将完成

  VLC播放器是一款支持ARMv7处理器的开源播放器,此前有团队在Kickstarter上募资,其目标是开发出Win8版VLC播放器.近日,该团队在其Kickstarter上的主页中表示,Win8版VLC即将完成开发. Win8版VLC播放器的开发者在其项目更新日志中表示,他们已经取得了巨大的进步,但是在正式发布之前,仍然有许多工作要做.当初这个团队将Win8版VLC播放器的项目放到Kickstarter上时,他们的预期目标是4万美元(约合人民币24.8万元),而该项目最终吸引了三千多位的支持

VLC播放器中文字幕乱码问题解决方法

  VLC对于Mac用户来说算得上是必备软件.其相当于PC上的"暴风影音",但Mac新手使用VLC播放avi时都会碰到字幕乱码的问题.avi字幕的格 式有多种,这里假设你使用常见的.srt字幕.VLC默认支持的字幕内码为utf-8,而网上提供的.srt字幕基本上都是GBK码,所以在初装 VLC后的默认状态下,加载.srt字幕都会出现乱码.VLC播放器中文字幕乱码问题解决方法如下 正如上面所说的,VLC默认支持的字幕内码为utf-8,而我们从网上下载的.srt字幕基本上都是GBK码,因此

rtmp-安卓手机上vlc播放器优化

问题描述 安卓手机上vlc播放器优化 vlc播放自己的直播摄像网络视频,一开始我们改进后从点击到画面显示最快速度可以到2秒内启动,但是后来发现两个问题: 1.没有声音播放出来 2.随着播放时间越久延时越来越厉害 为了解决这两个问题,改进之后启动就需要7秒左右了,请问哪位大神能优化启动速到在3秒左右或者更短,前提是保证有声音和延时没太大问题 解决方案 我做的在VLC上的二次开发,用开播放网络监控视频也是遇到了播放延时很严重的问题,针对这个问题我现在的想法是对VLC的解码进行优化,具体怎么做还没开始

VLC 播放器被 APP Store 下架

VLC 播放器是使用 GPLv2 许可证协议的开源播放器软件. 目前尚不清楚这件事到底是什么时候发生的,但是现在在苹果的应用商店的列表中已经找不到VLC播放器了. 就像苹果以前下架应用的时候一样,莫名其妙的,也不给解释,目前VLC开发团队也没有人站出来说明一下,虽然在它的官方论坛里面已经由好几个相关的话题在讨论了. 这已经不是第一次了,苹果之前也有过(不止一次)认为VLC违反规则,直接下架. 好在VLC并不是唯一的选择,这款开源播放器的代码也被其他无数的应用所使用,所以我们大可以从App Sto

java vlc播放器 vlcj.jar有人用过么

问题描述 java vlc播放器 vlcj.jar有人用过么 vlcj.jar有人用过么 视频播放不支持中文路径怎么办.....将中文路径转换成UTF-8编码 也不起作用啊

移动端的播放器设计经验:与VLC的考量点完全不同

移动播放器面临的情况: 1.渲染时按照时间戳渲染 2.播放端来的流是抖动不平滑的,可快可慢,可能延时只来一帧,后紧跟N帧. VLC针对抖动的处理方式 1.收流时在收到第一帧TS1的时候取本地绝对时间,作为绝对时戳absPts1,第二帧TS2到来时取本地绝对时戳absPts2.差值计算absDvalue = absPts2 - absPts1  TsDvalue = Ts2-Ts1 ,如果absDvalue > TsDvalue 说明数据延时到来,否则时提前到来.这种方式的缺陷是:以第一帧作为参考

C#的VLC播放器播放一段时间之后就停止播放

问题描述 播放大概10分钟左右画面就静止不动视频本身没有问题怎么能解决 解决方案

基于VLC的播放器开发

VLC的C++封装 因为工作需要,研究了一段时间的播放器开发,如果从头开始做,可以学习下FFmpeg(http://www.ffmpeg.org/),很多播放器都是基于FFmpeg开发的,但是这样工作量和难度都比较大,如果想很快能拿出一个播放器来用的,可以研究下开源的播放器,参考下射手播放器作者的文章:媒体播放器三大底层架构. 对比下现有的主流播放器:媒体播放器列表,VLC是在各个方面都表现很突出的一款.VLC 是一款免费.自由.开源的跨平台多媒体播放器及框架,可播放大多数多媒体文件,DVD.音