vlc-android最新原始碼截圖功能實現

穿越人海0225發表於2018-01-30

前言

2018/1月 vlc-android最新原始碼截圖功能實現
專案需要vlc截圖的功能,但是預設的vlc原始碼編譯是不包含截圖功能的,所以需要在原始碼中設定相應的編譯選項,並且在jni中新增相應的介面。
網上能搜到好幾篇基於 vlc android截圖功能實現的文件,實現方式都一樣,都是對android/configure.sh進行修改,刪掉其中的–disable-sout,另外儲存圖片為png格式,需要讓ffmpeg增加–enable-encoder=png的 編碼器(在android/vlc/contrib/src/ffmpeg/rules.mak中修改),然後在libvlcjni.c中增加截圖函式。照著 這樣的方式在最新的vlc android中並不能實現截圖功能。以下分析原因。

按照網上的方式,在\vlc-android\libvlc\jni\libvlcjni.c中增加函式:

jboolean Java_org_videolan_vlc_LibVLC_takeSnapShot(JNIEnv *env, jobject thiz,jint number, jstring path, jint width,jint height)  
{  
    jboolean isCopy;  
   libvlc_media_player_t *mp = getMediaPlayer(env, thiz);  
     /* Get C string */  
   const char* psz_path = (*env)->GetStringUTFChars(env, path, &isCopy);  

   if (mp)  
        if(libvlc_video_take_snapshot(mp, (int)number,psz_path , (int)width,(int)height)==0)  
            return JNI_TRUE;  
   return JNI_FALSE;  

}  

注:最新的原始碼中沒有getMediaPlayer方法,獲取libvlc_media_player_t可通過:

vlcjni_object *p_obj = VLCJniObject_getInstance(env, thiz);
libvlc_media_player_t *mp = gp_obj->u.p_mp; 

在\vlc-android\libvlc\src\org\videolan\libvlc\LibVlc.java中增加native函式的介面

private native boolean takeSnapShot( int num, String file, int width, int height);  

和呼叫方法

public boolean takeSnapShot(String file, int width, int height) {  
    return takeSnapShot(0, file, width, height);  
}  

但是當我在上層通過takeSnapShot截圖時沒有任何反應,於是打log發現libvlcjni.c中呼叫libvlc_video_take_snapshot時返回的是JNI_FALSE,於是分析下libvlc_video_take_snapshot函式的流程:

\vlc-android\libvlc\jni\Libvlcjni.c  Java_org_videolan_libvlc_LibVLC_nativeTakeSnapShot ==>

\vlc-android\vlc\lib\video.c         libvlc_video_take_snapshot==>GetVout==>GetVouts==>

\vlc-android\vlc\lib\media_player.c  libvlc_get_input_thread(libvlc_media_player_t *p_mi)

發現libvlc_get_input_thread中有一句:

p_input_thread = p_mi->input.p_thread;

由於傳入的libvlc_media_player_t取得的p_mi->input.p_thread為空導致出錯;
通過原始碼全域性搜尋發現只有在

\vlc-android\vlc\lib\media_player.c  
libvlc_media_player_play(libvlc_media_player_t *p_mi)

中會對p_mi->input.p_thread賦值,
所以只要能拿到libvlc_media_player_play中的libvlc_media_player_t 傳入libvlc_video_take_snapshot即可;
由於呼叫libvlc_media_player_play是在:

\vlc-android\libvlc\jni\Libvlcjni-mediaplayer.c 
Java_org_videolan_libvlc_MediaPlayer_nativePlay()

所以我把:

\vlc-android\libvlc\jni\Libvlcjni.c 
Java_org_videolan_libvlc_LibVLC_nativeTakeSnapShot

放在

\vlc-android\libvlc\jni\Libvlcjni-mediaplayer.c  
jboolean 
Java_org_videolan_libvlc_MediaPlayer_nativeTakeSnapShot(JNIEnv *env, jobject thiz,jint number, jstring path, jint width,jint height)  
{   
    vlcjni_object *p_obj = VLCJniObject_getInstance(env, thiz);

    if (!p_obj)
        return JNI_FALSE;
   jboolean isCopy; 
     /* Get C string */  
   const char* psz_path = (*env)->GetStringUTFChars(env, path, &isCopy);  

   if (p_obj->u.p_mp)
   {
       if(libvlc_video_take_snapshot(p_obj->u.p_mp, (int)number, psz_path , (int)width,      
                   (int)height)==0){
           return JNI_TRUE;  
       }            
   }

   return JNI_FALSE;  

} 

同樣的native函式介面和呼叫方法挪到
\vlc-android\libvlc\src\org\videolan\libvlc\Mediaplayer.java
改完再次呼叫takeSnapShot截圖發現libvlc_video_take_snapshot時返回的是JNI_TRUE,看來是成功了一半了。但是仍然還有報錯:

E/VLC     (16492): [b8c39ae8/406c] libvlc video output: Encoding support not compiled-in!
E/VLC     (16492): [b8c39ae8/406c] libvlc video output: Failed to convert image for snapshot

大概意思是編碼器沒有編譯進來。
搜尋發現報錯的原始碼在:

\vlc-android\vlc\src\Missing.c
#undef sout_EncoderCreate
encoder_t *sout_EncoderCreate( vlc_object_t *p_this )
{
    msg_Err (p_this, "Encoding support not compiled-in!");
    return NULL;
}

搜尋發現在 \vlc-android\vlc\src\streamoutput\Streamoutput.c也有sout_EncoderCreate的實現

\vlc-android\vlc\src\Missing.c
#undef sout_EncoderCreate
encoder_t *sout_EncoderCreate( vlc_object_t *p_this )
{
    return vlc_custom_create( p_this, sizeof( encoder_t ), "encoder" );
}

猜測是編譯時需要開啟某個配置才會把Stream_output.c編譯進來,否則就會編譯Missing.c

應該就是和前言中說到的那個–disable-sout有關,想必sout就是指Stream_output,但是我下載的最新的vlc原始碼並沒有android/configure.sh這個檔案,於是全域性搜尋–disable-sout,在/vlc-android/compile-libvlc.sh中發現–disable-sout的蹤影

###########################
# VLC BOOTSTRAP ARGUMENTS #
###########################

VLC_BOOTSTRAP_ARGS="\
    --disable-disc \
    --enable-dvdread \
    --enable-dvdnav \
    --disable-dca \
    --disable-out \
    ......
   "
###########################
# VLC CONFIGURE ARGUMENTS #
###########################

VLC_CONFIGURE_ARGS="\
    --disable-nls \
    --enable-live555 --enable-realrtsp \
    --enable-avformat \
    --enable-swscale \
    ......
    --disable-out \
   "

嘗試先把VLC BOOTSTRAP ARGUMENTS中的–disable-out刪除,再次編譯,發現會下載幾個依賴包,但是此時還是沒有編譯Stream_output;
接著嘗試把VLC CONFIGURE ARGUMENTS中的–disable-out刪除,再次編譯,發現還是沒有編譯到Stream_output,觀察了一下編譯列印的資訊發現:

running CONFIG_SHELL=/bin/bash /bin/bash ../configure –host=arm-linux-androideabi –build=x86_64-unknown-linux –with-contrib=/workspace/huanghengwei/work2/r52-v6.0/src/emb_android/vlc/src/vlc-android/vlc/contrib/arm-linux-androideabi –enable-neon –disable-nls –enable-live555 –enable-realrtsp –enable-avformat –enable-swscale –enable-avcodec –enable-opus –enable-opensles –enable-matroska –enable-taglib –enable-dvbpsi –disable-vlc –disable-shared –disable-update-check –disable-vlm –disable-dbus –enable-lua –disable-vcd –disable-v4l2 –enable-dvdread –enable-dvdnav –disable-bluray –disable-linsys –disable-decklink –disable-libva –disable-dv1394 –enable-mod –disable-sid –disable-gme –disable-tremor –disable-mad –enable-mpg123 –disable-dca –disable-sdl-image –enable-zvbi –disable-fluidsynth –enable-fluidlite –disable-jack –disable-pulse –disable-alsa –disable-samplerate –disable-sdl –disable-xcb –disable-qt –disable-skins2 –disable-mtp –disable-notify –enable-libass –disable-svg –disable-udev –enable-libxml2 –disable-caca –enable-gles2 –disable-goom –disable-projectm –disable-sout –enable-vorbis –disable-faad –disable-x264 –disable-schroedinger –disable-vncclient –disable-vnc –enable-jpeg

這裡列出來的選項就是VLC CONFIGURE ARGUMENTS中列的,其中還是有–disable-sout,可見刪除了disable-sout之後VLC CONFIGURE ARGUMENTS並沒有被重新識別,於是在–compile-libvlc.sh中檢視VLC CONFIGURE ARGUMENTS相關的指令碼內容:

if [ ! -e ./config.h -o "$RELEASE" = 1 ]; then
CFLAGS="${VLC_CFLAGS} ${EXTRA_CFLAGS}" \
CXXFLAGS="${VLC_CXXFLAGS} ${EXTRA_CFLAGS} ${EXTRA_CXXFLAGS}" \
CC="${CROSS_TOOLS}clang" \
CXX="${CROSS_TOOLS}clang++" \
NM="${CROSS_TOOLS}nm" \
STRIP="${CROSS_TOOLS}strip" \
RANLIB="${CROSS_TOOLS}ranlib" \
AR="${CROSS_TOOLS}ar" \
PKG_CONFIG_LIBDIR=$VLC_SRC_DIR/contrib/$TARGET_TUPLE/lib/pkgconfig \
PKG_CONFIG_PATH=$VLC_SRC_DIR/contrib/$TARGET_TUPLE/lib/pkgconfig \
PATH=../contrib/bin:$PATH \
sh ../configure --host=$TARGET_TUPLE --build=x86_64-unknown-linux \
    --with-contrib=${VLC_SRC_DIR}/contrib/${TARGET_TUPLE} \
    ${EXTRA_PARAMS} ${VLC_CONFIGURE_ARGS} ${OPTS}
checkfail "vlc: configure failed"
fi

從指令碼中可見,要重新識別VLC CONFIGURE ARGUMENTS要麼把config.h刪了,要麼在編譯的時候帶上–release,我試著編譯時帶上–release,終於編譯到Stream_output了。替換了編譯後的庫,呼叫takeSnapShot截圖成功!

相關文章