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__ )
------------------------------------libvlc-track.c [line 268] jobject Java_org_videolan_libvlc_LibVLC_getStats(JNIEnv *env, jobject thiz): 似乎是得到的统计信息,返回一个hash表
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 MapgetStats(); 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()
继续看源代码,发现了这么一个东西
从接收到数据流到播放视频的过程分析 从网络接收到流->对数据流进行视频和音频分离->对视频用解码器解码->显示解码后的视频流 视频显示部分走势线:分离->解码->新的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 地址,然后调用本地程序。
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