VLC分析记录           

VLC分析记录


Schedual

  1. 1

Details:


1 JNI(Java Native Interface)
	本地库接口。
	VLC的内核引擎主要还是集中于 libvlc,libcore等库构建的。这部分在 /vlc/ 下的

2 android.mk 是关键。探索整个工程的构建,从底层库到java层。

3 对src/org.videolan.vlc.gui.video.VideoPlayerActivity.java的分析看

	mLibVLC ( an instance of LibVLC.class)是控制整个播放的关键。先阶段的思路是从mLibVLC 入手

4 分析 LibVLC.java

     /*
             * Set higher caching values if using iomx decoding, since some omx
             * decoders have a very high latency, and if the preroll data isn't
             * enough to make the decoder output a frame, the playback timing gets
             * started too soon, and every decoded frame appears to be too late.
             * On Nexus One, the decoder latency seems to be 25 input packets
             * for 320x170 H.264, a few packets less on higher resolutions.
             * On Nexus S, the decoder latency seems to be about 7 packets.
             */

    latency : 延迟   preroll:  前滚 timing :


5 视频基本原理 
	播放一个视频分为四个步骤:
	
	1 access 访问 :理解为获取,接收,得到
	2 demux  解复用:就是把通常合在一起的音频和视频分离(还有可能有字幕)
	3 decode 解码:包括音频视频的解码
	4 output 输出 :包括音频(aout)和视频(vout)的输出

	ES PES TS

6 流媒体跟踪

	1. 'libvlc_new' : libvlc_instance_t * libvlc_new( int argc, const char *const *argv ) [in /vlc/lib/core.c]

7 vlc 代码分析(not for android)
  
7.1  Libvlc 是vlc的核心,它是一个提供接口的库,比如给vlc 提供某些功能接口:流的接入,音频和视频输出,插件管理,线程系统。所有的LibVlc源码位于vlc/src/及其子目录:
	interface/:包含与用户交互的代码,如按键和设备弹出。
		-----dialog.c  interface.c
        playlist/: 管理播放列表的交互,如停止,播放,下一个,或者随机播放。
                          ××××   msg_Dbg( obj, "corks count: %"PRId64" -> %"PRId64, old.i_int, cur.i_int );  ---打印调试
		-----aout.c  art.c  art.h  control.c  engine.c  fetcher.c  fetcher.h  item.c  loadsave.c  playlist_internal.h  preparser.c  preparser.h  search.c  services_discovery.c  sort.c  thread.c  tree.c
	input/:代开一个输入组件,读包,解析她们并且将被还原的基本流传递给解码器
		----access.c  clock.h    decoder.h          demux.h   es_out_timeshift.c  event.h  input_interface.h  item.h      resource.h  stream_demux.c   stream_memory.c  vlm.c        vlm_internal.h
		    access.h  control.c  decoder_synchro.c  es_out.c  es_out_timeshift.h  info.h   input_internal.h   meta.c      stats.c     stream_filter.c  subtitles.c      vlm_event.c  vlmshell.c
		    clock.c   decoder.c  demux.c            es_out.h  event.c             input.c  item.c             resource.c  stream.c    stream.h         var.c            vlm_event.h
	video_output/:初始化video显示器,从解码器得到所有的图片和子图片(如subtitle),随意将她们转换为其他格式(如:YUV到RGB)并且播放
		----chrono.h   control.h  display.h  inhibit.c  interlacing.c  opengl.c    snapshot.h   video_epg.c     video_text.c     vout_control.h   vout_intf.c         vout_wrapper.c
		    control.c  display.c  event.h    inhibit.h  interlacing.h  snapshot.c  statistic.h  video_output.c  video_widgets.c  vout_internal.h  vout_subpictures.c  window.c
	audio_output/: 初始化音频mixer(混合器)。如发现正确的播放频率,然后重新制作从解码器接受过来的音频。
		----aout_internal.h  common.c  dec.c  filters.c  output.c  volume.c
	stream_output/: 类似audio_output
		---sap.c  sdp.c  stream_output.c  stream_output.h
	misc/:被libvlc其他部分使用的杂项,如线程系统,消息队列,cpu探测,对象查询系统,或者特定平台代码
		---addons.c  epg.c        events.c  filter_chain.c   http_auth.c    md5.c       mtime.c    picture_fifo.c  rand.c        text_style.c  update_crypto.c  variables.h
			block.c   error.c      exit.c    fingerprinter.c  httpcookies.c  messages.c  objects.c  picture_pool.c  subpicture.c  threads.c     update.h         xml.c
			cpu.c     es_format.c  filter.c  fourcc.c         image.c        mime.c      picture.c  probe.c         subpicture.h  update.c      variables.c

7.2 vlc基本是围绕着libVLC写成的程序。

7.3 组件。组件基本位于 module\子目录,在运行时被加载,每个组件提供不同的特性适应特定的文件环境。另外大量的不断编写的可移植功能位于 auto_output\ video_output\ interface\等,支持不同平台

------------------------------------------------------------------------------------------------------------------------------------------

追踪状态似乎有眉目了

	感谢不甘心的阿甘带我走出了死胡同。
	
	手动追踪一下代码
	
	Java-Level :
		org.videolan.vlc.LibVLC.java :
			[line 649]   
			/**
     			* Returns true if any media is playing
     			*/
    			public native boolean isPlaying();
		
	JNI : 
		libvlcjni.c :
			[line 478]
			jboolean Java_org_videolan_libvlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
			{
    				libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    				if (mp)
        			return !!libvlc_media_player_is_playing(mp);
    				else
        			return 0;
			}
	Native-Level:
		vlc/lib/media_player.c :
			  930	/**************************************************************************
			  931	 * Tells whether the media player is currently playing.
			  932	 *
			  933	 * Enter with lock held.
 			  934	 **************************************************************************/
			  935	int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi )
			  936	{
  			  937	    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
			  938	    return (libvlc_Playing == state) || (libvlc_Buffering == state);
			  939	}
  			  940
         
		vlc/include/vlc/libvlc_media.h			  		
			/**
			 * Note the order of libvlc_state_t enum must match exactly the order of
			 * \see mediacontrol_PlayerStatus, \see input_state_e enums,
			 * and VideoLAN.LibVLC.State (at bindings/cil/src/media.cs).
			 *
			 * Expected states by web plugins are:
			 * IDLE/CLOSE=0, OPENING=1, BUFFERING=2, PLAYING=3, PAUSED=4,
			 * STOPPING=5, ENDED=6, ERROR=7
			 */
			typedef enum libvlc_state_t
			{
			    libvlc_NothingSpecial=0,
			    libvlc_Opening,
			    libvlc_Buffering,
			    libvlc_Playing,
			    libvlc_Paused,
			    libvlc_Stopped,
			    libvlc_Ended,
			    libvlc_Error
			} libvlc_state_t;



我们可以自己去定义一些状态函数,然后进行监听,具体可以参考 上面那个链接的前辈的思路


-------------------------------------------------------------------------------------------

继续追踪源代码


android/vlc/lib/media_player.c :
	[line 893]:	void libvlc_media_player_set_pause( libvlc_media_player_t *p_mi, int paused )
			{
	………………
	[line 899]:		    libvlc_state_t state = libvlc_media_player_get_state( p_mi );
	
	
所有跟媒体有关的,比如 长度,播放时间,都跟 p_input_thread (input_thread_t* 类型)




android/vlc/lib/media.c :

/**************************************************************************
 * Getter for statistics information
 **************************************************************************/
int libvlc_media_get_stats( libvlc_media_t *p_md,
                            libvlc_media_stats_t *p_stats )
{
    if( !p_md->p_input_item )
        return false;

    input_stats_t *p_itm_stats = p_md->p_input_item->p_stats;
    vlc_mutex_lock( &p_itm_stats->lock );
    p_stats->i_read_bytes = p_itm_stats->i_read_bytes;
    p_stats->f_input_bitrate = p_itm_stats->f_input_bitrate;

    p_stats->i_demux_read_bytes = p_itm_stats->i_demux_read_bytes;
    p_stats->f_demux_bitrate = p_itm_stats->f_demux_bitrate;
    p_stats->i_demux_corrupted = p_itm_stats->i_demux_corrupted;
    p_stats->i_demux_discontinuity = p_itm_stats->i_demux_discontinuity;

    p_stats->i_decoded_video = p_itm_stats->i_decoded_video;
    p_stats->i_decoded_audio = p_itm_stats->i_decoded_audio;

    p_stats->i_displayed_pictures = p_itm_stats->i_displayed_pictures;
    p_stats->i_lost_pictures = p_itm_stats->i_lost_pictures;

    p_stats->i_played_abuffers = p_itm_stats->i_played_abuffers;
    p_stats->i_lost_abuffers = p_itm_stats->i_lost_abuffers;

    p_stats->i_sent_packets = p_itm_stats->i_sent_packets;
    p_stats->i_sent_bytes = p_itm_stats->i_sent_bytes;
    p_stats->f_send_bitrate = p_itm_stats->f_send_bitrate;
    vlc_mutex_unlock( &p_itm_stats->lock );
    return true;
}i


看来许多重要的API集中于 vlc/lib/


------------------------------------

记录一条跟踪:

这次在机器上尝试着打开德国DASH服务器上的视频,卡顿了,看了一下log 大概如下



追踪了一下源码:
jangwee@jangwee-OptiPlex-3020:~/workspace/wp_android$ grep "picture is too late to be displayed" * -r
匹配到二进制文件 android/vlc-android/obj/local/armeabi-v7a/libvlcjni.so
匹配到二进制文件 android/vlc-android/libs/armeabi-v7a/libvlcjni.so
匹配到二进制文件 android/vlc/build-android-arm-linux-androideabi/src/video_output/video_output.o
匹配到二进制文件 android/vlc/build-android-arm-linux-androideabi/src/.libs/libvlccore.a
android/vlc/src/video_output/video_output.c:                        msg_Warn(vout, "picture is too late to be displayed (missing %"PRId64" ms)", late/1000);

在video_output.c文件里。

   815	static int ThreadDisplayPreparePicture(vout_thread_t *vout, bool reuse, bool frame_by_frame)
   816	{
   817	    bool is_late_dropped = vout->p->is_late_dropped && !vout->p->pause.is_on && !frame_by_frame;
   818	
   819	    vlc_mutex_lock(&vout->p->filter.lock);
   820	
   821	    picture_t *picture = filter_chain_VideoFilter(vout->p->filter.chain_static, NULL);
   822	    assert(!reuse || !picture);
   823	
   824	    while (!picture) {
   825	        picture_t *decoded;
   826	        if (reuse && vout->p->displayed.decoded) {
   827	            decoded = picture_Hold(vout->p->displayed.decoded);
   828	        } else {
   829	            decoded = picture_fifo_Pop(vout->p->decoder_fifo);
   830	            if (decoded) {
   831	                if (is_late_dropped && !decoded->b_force) {
   832	                    const mtime_t predicted = mdate() + 0; /* TODO improve */
   833	                    const mtime_t late = predicted - decoded->date;
   834	                    if (late > VOUT_DISPLAY_LATE_THRESHOLD) {
   835	                        msg_Warn(vout, "picture is too late to be displayed (missing %"PRId64" ms)", late/1000);
   836	                        picture_Release(decoded);
   837	                        vout_statistic_AddLost(&vout->p->statistic, 1);
   838	                        continue;
   839	                    } else if (late > 0) {
   840	                        msg_Dbg(vout, "picture might be displayed late (missing %"PRId64" ms)", late/1000);
   841	                    }
   842	                }
   843	                if (!VideoFormatIsCropArEqual(&decoded->format, &vout->p->filter.format))
   844	                    ThreadChangeFilters(vout, &decoded->format, vout->p->filter.configuration, true);
   845	            }
   846	        }
   847	
   848	        if (!decoded)
   849	            break;
   850	        reuse = false;
   851	
   852	        if (vout->p->displayed.decoded)
   853	            picture_Release(vout->p->displayed.decoded);
   854	
   855	        vout->p->displayed.decoded       = picture_Hold(decoded);
   856	        vout->p->displayed.timestamp     = decoded->date;
   857	        vout->p->displayed.is_interlaced = !decoded->b_progressive;
   858	
   859	        picture = filter_chain_VideoFilter(vout->p->filter.chain_static, decoded);
   860	    }
   861	
   862	    vlc_mutex_unlock(&vout->p->filter.lock);
   863	
   864	    if (!picture)
   865	        return VLC_EGENERIC;
   866	
   867	    assert(!vout->p->displayed.next);
   868	    if (!vout->p->displayed.current)
   869	        vout->p->displayed.current = picture;
   870	    else
   871	        vout->p->displayed.next    = picture;
   872	    return VLC_SUCCESS;
   873	}
   874	

--------------------------------------------------------------------------------------------


同时,追踪了一下 core input: Buffering 30%

在 vlc/src/input/es_out.c 


   597	static void EsOutDecodersStopBuffering( es_out_t *out, bool b_forced )
   598	{
   599	    es_out_sys_t *p_sys = out->p_sys;
   600	    int i_ret;
   601	
   602	    mtime_t i_stream_start;
   603	    mtime_t i_system_start;
   604	    mtime_t i_stream_duration;
   605	    mtime_t i_system_duration;
   606	    if (input_clock_GetState( p_sys->p_pgrm->p_clock,
   607	                                  &i_stream_start, &i_system_start,
   608	                                  &i_stream_duration, &i_system_duration ))
   609	        return;
   610	
   611	    mtime_t i_preroll_duration = 0;
   612	    if( p_sys->i_preroll_end >= 0 )
   613	        i_preroll_duration = __MAX( p_sys->i_preroll_end - i_stream_start, 0 );
   614	
   615	    const mtime_t i_buffering_duration = p_sys->i_pts_delay +
   616	                                         i_preroll_duration +
   617	                                         p_sys->i_buffering_extra_stream - p_sys->i_buffering_extra_initial;
   618	
   619	    if( i_stream_duration <= i_buffering_duration && !b_forced )
   620	    {
   621	        double f_level;
   622	        if (i_buffering_duration == 0)
   623	            f_level = 0;
   624	        else
   625	            f_level = __MAX( (double)i_stream_duration / i_buffering_duration, 0 );
   626	        input_SendEventCache( p_sys->p_input, f_level );
   627	
   628	        msg_Dbg( p_sys->p_input, "Buffering %d%%", (int)(100 * f_level) );
   629	        return;
   630	    }
   631	    input_SendEventCache( p_sys->p_input, 1.0 );
   632	
   633	    msg_Dbg( p_sys->p_input, "Stream buffering done (%d ms in %d ms)",
   634	              (int)(i_stream_duration/1000), (int)(i_system_duration/1000) );
   635	    p_sys->b_buffering = false;
   636	    p_sys->i_preroll_end = -1;

QoE的 Key Point 在这里!

[619] 行 :    if( i_stream_duration <= i_buffering_duration && !b_forced ) 
	是一个判断,i_stream_duration就是当前未解析缓存,i_buffering_duration 是一个设定的阈值。注意 i_system_duration 也应该是一个buffering.系统的?不明确。

	先参考一下他的这个消息记录方式:
	step 1 call :input_clock_GetState()
	step 2 call : msg_Dbg()

下面就了解一下各个函数的功能:
	input_clock_GetState:
	This function returns current clock state or VLC_EGENERIC if there is not a reference point.
  469 int input_clock_GetState( input_clock_t *cl,
  470                           mtime_t *pi_stream_start, mtime_t *pi_system_start,
  471                           mtime_t *pi_stream_duration, mtime_t *pi_system_duration )
  472 {
  473     vlc_mutex_lock( &cl->lock );
  474 
  475     if( !cl->b_has_reference )
  476     {
  477         vlc_mutex_unlock( &cl->lock );
  478         return VLC_EGENERIC;
  479     }
  480 
  481     *pi_stream_start = cl->ref.i_stream;
  482     *pi_system_start = cl->ref.i_system;
  483 
  484     *pi_stream_duration = cl->last.i_stream - cl->ref.i_stream;
  485     *pi_system_duration = cl->last.i_system - cl->ref.i_system;
  486 
  487     vlc_mutex_unlock( &cl->lock );
  488 
  489     return VLC_SUCCESS;
  490 }
	
		
	msg_Dbg()
   69 #define msg_GenericVa(a, b, c, d, e) vlc_vaLog(VLC_OBJECT(a), b, c, d, e)
   70 
   71 #define msg_Info( p_this, ... ) \
   72     vlc_Log( VLC_OBJECT(p_this), VLC_MSG_INFO, MODULE_STRING, __VA_ARGS__ )
   73 #define msg_Err( p_this, ... ) \
   74     vlc_Log( VLC_OBJECT(p_this), VLC_MSG_ERR,  MODULE_STRING, __VA_ARGS__ )
   75 #define msg_Warn( p_this, ... ) \
   76     vlc_Log( VLC_OBJECT(p_this), VLC_MSG_WARN, MODULE_STRING, __VA_ARGS__ )
   77 #define msg_Dbg( p_this, ... ) \
   78     vlc_Log( VLC_OBJECT(p_this), VLC_MSG_DBG,  MODULE_STRING, __VA_ARGS__ )




JNI 部分


------------------------------------libvlc-track.c
[line 268] jobject Java_org_videolan_libvlc_LibVLC_getStats(JNIEnv *env, jobject thiz):
	似乎是得到的统计信息,返回一个hash表


Java层可以改动部分

	VideoPlayerActivity.java :
[line 945] :
 /**
     *  Handle libvlc asynchronous events
 */

感觉可以从这里入手.
这个地方是处理事件的入口,那么从哪抛出的事件呢?

在libvlcjni.c :
[line 122] static void vlc_event_callback(const libvlc_event_t *ev, void *data) 

[line 400] Java_org_videolan_libvlc_LibVLC_playMRL 调用上面的本地方法

注意
在EventHandler.java :
[line 122]    
 /** This method is called by a native thread **/
    public void callback(int event, Bundle b) {

说明 JNI 回调了Java层的方法,post 消息

在jni/下检索 : 

jangwee@jangwee-OptiPlex-3020:~/workspace/wp_android/android/vlc-android/jni$ grep '"callback"' -n -r *
libvlcjni.c:197:    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(ILandroid/os/Bundle;)V");
libvlcjni-util.c:142:    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(ILandroid/os/Bundle;)V");

先回溯libvlcjni.c 197: 位于函数:
static void vlc_event_callback(const libvlc_event_t *ev, void *data) 
是一个本地函数

调用这个函数的是 :
void Java_org_videolan_libvlc_LibVLC_playMRL(JNIEnv *env, jobject thiz, jlong instance,
                                             jstring mrl, jobjectArray mediaOptions)

通过名字可以看到,调用他的是
org.videolan.libvlc.LibVLC.java中的playMRL:    private native void playMRL(long instance, String mrl, String[] mediaOptions);

这下一切都明朗了

注意758行的    public native Map getStats();
log 一下!



现在的思路是在认为暂停之后,调用getStats();
	1986

----------------------
被分布式的东西打断了节奏。

上面提到的 public native Map getStats();

在JNI/libvlc_jni-track.c中已经实现!这个文件中实现了很多的track!
jobject Java_org_videolan_libvlc_LibVLC_getStats(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (!mp)
        return NULL;

    libvlc_media_t *p_mp = libvlc_media_player_get_media(mp);
    if (!p_mp)
        return NULL;

    libvlc_media_stats_t p_stats;
    libvlc_media_get_stats(p_mp, &p_stats);

    jclass mapClass = (*env)->FindClass(env, "java/util/Map");
    jclass hashMapClass = (*env)->FindClass(env, "java/util/HashMap");
    jmethodID mapPut = (*env)->GetMethodID(env, mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
    /* We need a concrete map to start */
    jmethodID mapInit = (*env)->GetMethodID(env, hashMapClass, "", "()V");
    jclass integerCls = (*env)->FindClass(env, "java/lang/Integer");
    jmethodID integerConstructor = (*env)->GetMethodID(env, integerCls, "", "(I)V");
    jclass floatCls = (*env)->FindClass(env, "java/lang/Float");
    jmethodID floatConstructor = (*env)->GetMethodID(env, floatCls, "", "(F)V");

    jobject statistics = (*env)->NewObject(env, hashMapClass, mapInit);
    jobject value = (*env)->NewObject(env, floatCls, floatConstructor, p_stats.f_demux_bitrate);
    jstring name = (*env)->NewStringUTF(env, "demuxBitrate");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, floatCls, floatConstructor, p_stats.f_input_bitrate);
    name = (*env)->NewStringUTF(env, "inputBitrate");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, floatCls, floatConstructor, p_stats.f_send_bitrate);
    name = (*env)->NewStringUTF(env, "sendBitrate");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, integerCls, integerConstructor, p_stats.i_decoded_audio);
    name = (*env)->NewStringUTF(env, "decodedAudio");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, integerCls, integerConstructor, p_stats.i_decoded_video);
    name = (*env)->NewStringUTF(env, "decodedVideo");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, integerCls, integerConstructor, p_stats.i_demux_corrupted);
    name = (*env)->NewStringUTF(env, "demuxCorrupted");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, integerCls, integerConstructor, p_stats.i_demux_discontinuity);
    name = (*env)->NewStringUTF(env, "demuxDiscontinuity");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, integerCls, integerConstructor, p_stats.i_demux_read_bytes);
    name = (*env)->NewStringUTF(env, "demuxReadBytes");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, integerCls, integerConstructor, p_stats.i_displayed_pictures);
    name = (*env)->NewStringUTF(env, "displayedPictures");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, integerCls, integerConstructor, p_stats.i_lost_abuffers);
    name = (*env)->NewStringUTF(env, "lostAbuffers");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, integerCls, integerConstructor, p_stats.i_lost_pictures);
    name = (*env)->NewStringUTF(env, "lostPictures");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, integerCls, integerConstructor, p_stats.i_played_abuffers);
    name = (*env)->NewStringUTF(env, "playedAbuffers");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, integerCls, integerConstructor, p_stats.i_read_bytes);
    name = (*env)->NewStringUTF(env, "readBytes");
    (*env)->CallObjectMethod(env, statistics, mapPut, value, name);

    value = (*env)->NewObject(env, integerCls, integerConstructor, p_stats.i_sent_bytes);
    name = (*env)->NewStringUTF(env, "sentBytes");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    value = (*env)->NewObject(env, integerCls, integerConstructor, p_stats.i_sent_packets);
    name = (*env)->NewStringUTF(env, "sentPackets");
    (*env)->CallObjectMethod(env, statistics, mapPut, name, value);

    // Clean up local references
    (*env)->DeleteLocalRef(env, mapClass);
    (*env)->DeleteLocalRef(env, hashMapClass);
    (*env)->DeleteLocalRef(env, integerCls);
    (*env)->DeleteLocalRef(env, floatCls);

    return statistics;
}

所以我在src/org/videolan/vlc/gui/video/VideoPlayerActivity.java 中 
    1641:  //添加测量
        Log.i(MEASURE_TAG, mLibVLC.getStats().toString()

关注 clock.c

继续看源代码,发现了这么一个东西



视频流解析



从接收到数据流到播放视频的过程分析
   从网络接收到流->对数据流进行视频和音频分离->对视频用解码器解码->显示解码后的视频流
   视频显示部分走势线:分离->解码->新的VOUT缓冲区->VOUT线程
  Demux(modules\demux\mpeg\ps.c)->DemuxPs(modules\demux\mpeg\system.c)-> ParsePS->input_SelectES(src\input\input_programs.c)->input_RunDecoder(src\input\input_dec.c)->CreateDecoder->
  vout_new_buffer->vout_Request(src\video_output\video_output.c)->vout_Create->RunThread->vout_RenderPicture(src\video_output\vout_pictures.c)->pf_display
  注意:p_dec->pf_vout_buffer_new = vout_new_buffer的pf_vout_buffer_new在ffmpeg_NewPictBuf(modules\codec\ffmpeg\video.c)函数中激活
   解码部分走势线:
  Demux(modules\demux\mpeg\ps.c)->DemuxPs(modules\demux\mpeg\system.c)-> ParsePS->input_SelectES(src\input\input_programs.c)->input_RunDecoder(src\input\input_dec.c)->CreateDecoder->
  DecoderThread
   注意:在解码线程中对数据流(AUDIO 或者VIDEO)进行解码
  详细资料 http://developers.videolan.org/vlc/ VLC API documentation 或者VLC developer documentation
   
  
  Chapter 5. The video output layer 
  
  Data structures and main loop 
  Important data structures are defined in include/video.h and include/video_output.h. The main data structure is picture_t, which describes everything a video decoder thread needs. Please refer to this file for more information. Typically, p_data will be a pointer to YUV planar picture. 
  Note also the subpicture_t structure. In fact the VLC SPU decoder only parses the SPU header, and converts the SPU graphical data to an internal format which can be rendered much faster. So a part of the "real" SPU decoder lies in src/video_output/video_spu.c. 
  The vout_thread_t structure is much more complex, but you needn't understand everything. Basically the video output thread manages a heap of pictures and subpictures (5 by default). Every picture has a status (displayed, destroyed, empty...) and eventually a presentation time. The main job of the video output is an infinite loop to : [this is subject to change in the near future] 
  
  Find the next picture to display in the heap. 
  Find the current subpicture to display. 
  Render the picture (if the video output plug-in doesn't support YUV overlay). Rendering will call an optimized YUV plug-in, which will also do the scaling, add subtitles and an optional picture information field. 
  Sleep until the specified date. 
  Display the picture (plug-in function). For outputs which display RGB data, it is often accomplished with a buffer switching. p_vout->p_buffer is an array of two buffers where the YUV transform takes place, and p_vout->i_buffer_index indicates the currently displayed buffer. 
  Manage events. 
  
  Methods used by video decoders 
  The video output exports a bunch of functions so that decoders can send their decoded data. The most important function is vout_CreatePicture which allocates the picture buffer to the size indicated by the video decoder. It then just needs to feed (void *) p_picture->p_data with the decoded data, and call vout_DisplayPicture and vout_DatePicture upon necessary. 
  
  picture_t * vout_CreatePicture ( vout_thread_t *p_vout, int i_type, int i_width, int i_height ) : Returns an allocated picture buffer. i_type will be for instance YUV_420_PICTURE, and i_width and i_height are in pixels. 
  Warning
  If no picture is available in the heap, vout_CreatePicture will return NULL. 
  vout_LinkPicture ( vout_thread_t *p_vout, picture_t *p_pic ) : Increases the refcount of the picture, so that it doesn't get accidently freed while the decoder still needs it. For instance, an I or P picture can still be needed after displaying to decode interleaved B pictures. 
  vout_UnlinkPicture ( vout_thread_t *p_vout, picture_t *p_pic ) : Decreases the refcount of the picture. An unlink must be done for every link previously made. 
  vout_DatePicture ( vout_thread_t *p_vout, picture_t *p_pic ) : Gives the picture a presentation date. You can start working on a picture before knowing precisely at what time it will be displayed. For instance to date an I or P picture, you must wait until you have decoded all previous B pictures (which are indeed placed after - decoding order != presentation order). 
  vout_DisplayPicture ( vout_thread_t *p_vout, picture_t *p_pic ) : Tells the video output that a picture has been completely decoded and is ready to be rendered. It can be called before or after vout_DatePicture. 
  vout_DestroyPicture ( vout_thread_t *p_vout, picture_t *p_pic ) : Marks the picture as empty (useful in case of a stream parsing error). 
  subpicture_t * vout_CreateSubPicture ( vout_thread_t *p_vout, int i_channel, int i_type ) : Returns an allocated subpicture buffer. i_channel is the ID of the subpicture channel, i_type is DVD_SUBPICTURE or TEXT_SUBPICTURE, i_size is the length in bytes of the packet. 
  vout_DisplaySubPicture ( vout_thread_t *p_vout, subpicture_t *p_subpic ) : Tells the video output that a subpicture has been completely decoded. It obsoletes the previous subpicture. 
  vout_DestroySubPicture ( vout_thread_t *p_vout, subpicture_t *p_subpic ) : Marks the subpicture as empty

方案

	1 vlc 播放 android平台demo (js 备选)
	2 前期自己部署服务器,去get  后期 可以利用VParse 去解析地址,然后http get
	3 参数采集:
		3.1 寻找工具,
		3.2 抓包,分析 时延,
			eg ping -c

			网速测试 android::TrafficStats 类
	4 在url的消息触发之后,hook住它,Vparse 地址,然后调用本地程序。
 

key words


1MPEG-TS流


2 源码中涉及到一些C技巧。
	2.1宏 结构体 实现继承
	2.2
	static int RandomCallback( vlc_object_t *p_this, char const *psz_cmd,
                           vlc_value_t oldval, vlc_value_t newval, void *a )
	{
    		(void)psz_cmd; (void)oldval; (void)newval; (void)a;


	这一句有什么作用呢?
	请教之后,因为这三个变量在这个函数中都没有用到,所以当编译器警告级别开到最大的时候,会输出编译警告,告诉你这些变量可能没被引用。
	所以所,这样的话,就相当告诉编译器我用过了。
	我猜测,接口这样设计,可能是为了将来扩展好用。

        	

3 看内核时总遇到if(likely( )){}或是if(unlikely( ))这样的语句,最初不解其意,现在有所了解,所以也想介绍一下。

likely() 与 unlikely()是内核(我看的是2.6.22.6版本,2.6的版本应该都有)中定义的两个宏。位于/include/linux/compiler.h中,
具体定义如下:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

__builtin_expect是gcc(版本>=2.96,网上写的,我没验证过)中提供的一个预处理命令(这个名词也是网上写的,我想叫函数更好些),有利于代码优化。gcc(version 4.4.0)具体定义如下:
long __builtin_expect (long exp, long c) [Built-in Function]

注解为:
You may use __builtin_expect to provide the compiler with branch prediction information. In general, you should prefer to use actual profile feedback for this (‘-fprofile-arcs’), as programmers are notoriously bad at predicting how their programs actually perform. However, there are applications in which this data is hard to collect.The return value is the value of exp, which should be an integral expression. The semantics of the built-in are that it is expected that exp == c.

它的意思是:我们可以使用这个函数人为告诉编绎器一些分支预测信息“exp==c” 是“很可能发生的”。

#define likely(x) __builtin_expect(!!(x), 1)也就是说明x==1是“经常发生的”或是“很可能发生的”。
使用likely ,执行if后面语句的可能性大些,编译器将if{}是的内容编译到前面, 使用unlikely ,执行else后面语句的可能性大些,编译器将else{}里的内容编译到前面。这样有利于cpu预取,提高预取指令的正确率,因而可提高效率。

举个例子(内核版本2.6.22.6):/kernel/shed.c中有一段:
if (likely(!active_balance)) {
/* We were unbalanced, so reset the balancing interval */
sd->balance_interval = sd->min_interval;
} else {
/*
* If we've begun active balancing, start to back off. This
* case may not be covered by the all_pinned logic if there
* is only 1 task on the busy runqueue (because we don't call
* move_tasks).
*/
if (sd->balance_interval max_interval)
sd->balance_interval *= 2;
}

编译过程中,会将if后面{}里的内容编译到前面,else 后面{}里的内容编译到后面。若将likely换成unlikely 则正好相反。

总之,likely与unlikely互换或不用都不会影响程序的正确性。但可能会影响程序的效率。

if(likely(foo))  //认为foo通常为1
if(unlikely(foo)) //认为foo通常为0

感谢各位光顾!
不知道有没有写清楚,望指正!
疑惑:
为什么likely或是unlikely要定义成__builtin_expect(!!(x), 1),而不直接用__builtin_expect(x, 1)?" !!(x) "与" x "有什么不同?

另外内核2.6.31.5中likely和unlikely还有一种定义:
# ifndef likely
# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
# endif

# ifndef unlikely
# define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0))
# endif


注:
!!(x)可以将非bool型转化成bool型。(C++)



Welcome to contact me,the friends who like Machine-Learning and Data-Mining.

:)

27 Nov 2014