前言
因为需要视频录制和截图,所以编译了一下VLC,做下记录;该版本已经提供了视频录制接口,需要新增截图功能
特别注意:如果需要视频录制和截图,需要关闭硬件解码
media.setHWDecoderEnabled(false, false);
前篇
时间
2019-05-11
开始
主要讲的是思路,结果很简单;
1、根据Vlc 提供MediaPlayer类录制接口
public boolean record(String directory) {
return this.nativeRecord(directory);
}
private native boolean nativeRecord(String var1);
调用的是jni接口进行录制;
定位视频录制接口jni层位置,文件位置:./android-vlc-20190510/libvlc/jni/libvlcjni-mediaplayer.c
2、查看Java_org_videolan_libvlc_MediaPlayer_nativeRecord函数
jboolean
Java_org_videolan_libvlc_MediaPlayer_nativeRecord(JNIEnv *env, jobject thiz,
jstring jdirectory)
{
vlcjni_object *p_obj = VLCJniObject_getInstance(env, thiz);
const char *psz_directory;
if (!p_obj)
return false;
int (*record_func)(libvlc_media_player_t *, const char *) =
dlsym(RTLD_DEFAULT, "libvlc_media_player_record");
if (!record_func)
return false;
if (jdirectory)
{
psz_directory = (*env)->GetStringUTFChars(env, jdirectory, 0);
if (!psz_directory)
{
throw_Exception(env, VLCJNI_EX_ILLEGAL_ARGUMENT, "directory invalid");
return false;
}
}
else
psz_directory = NULL;
jboolean ret = record_func(p_obj->u.p_mp, psz_directory) == 0;
if (psz_directory)
{
(*env)->ReleaseStringUTFChars(env, jdirectory, psz_directory);
}
return ret;
}
发现没什么内容,最主要的是下一句:
dlsym(RTLD_DEFAULT, "libvlc_media_player_record");
这是一个动态链接函数&变量地址的函数;拿到函数指针后,直接调用函数进行视频录制,所以这个libvlc_media_player_record在哪里?
经过搜索,最后定位两个文件:
1、./android-vlc-20190510/vlc/lib/media_player.c
2、./android-vlc-20190510vlc/include/vlc/libvlc_media_player.h
文件media_player.c文件内容:
int libvlc_media_player_record( libvlc_media_player_t *p_mi,
const char *directory )
{
vlc_value_t val = { .psz_string = (char *)directory };
const bool enable = directory != NULL;
input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
if( !p_input_thread )
return VLC_EGENERIC;
if( enable )
var_Set( p_mi, "input-record-path", val );
var_SetBool( p_input_thread, "record", enable);
vlc_object_release( p_input_thread );
return VLC_SUCCESS;
}
文件libvlc_media_player.h内容:
LIBVLC_API int libvlc_media_player_record(libvlc_media_player_t *p_mi,
const char *directory);
是的,libvlc_media_player.h中的是声明,media_player.c是实现,我还在libvlc_media_player.h中看到了惊喜
/**
* Take a snapshot of the current video window.
*
* If i_width AND i_height is 0, original size is used.
* If i_width XOR i_height is 0, original aspect-ratio is preserved.
*
* \param p_mi media player instance
* \param num number of video output (typically 0 for the first/only one)
* \param psz_filepath the path of a file or a folder to save the screenshot into
* \param i_width the snapshot's width
* \param i_height the snapshot's height
* \return 0 on success, -1 if the video was not found
*/
LIBVLC_API
int libvlc_video_take_snapshot( libvlc_media_player_t *p_mi, unsigned num,
const char *psz_filepath, unsigned int i_width,
unsigned int i_height );
这个不就是我们想要的?
为了验证猜想,我在vlc目录继续搜索了一下libvlc_video_take_snapshot关键字
很相识的结果,这两个文件看起来很不错
1、./android-vlc-20190510/vlc/lib/video.c
2、./android-vlc-20190510vlc/include/vlc/libvlc_media_player.h
打开video.c文件看一眼
int
libvlc_video_take_snapshot( libvlc_media_player_t *p_mi, unsigned num,
const char *psz_filepath,
unsigned int i_width, unsigned int i_height )
{
assert( psz_filepath );
vout_thread_t *p_vout = GetVout (p_mi, num);
if (p_vout == NULL)
return -1;
/* FIXME: This is not atomic. All parameters should be passed at once
* (obviously _not_ with var_*()). Also, the libvlc object should not be
* used for the callbacks: that breaks badly if there are concurrent
* media players in the instance. */
var_Create( p_vout, "snapshot-width", VLC_VAR_INTEGER );
var_SetInteger( p_vout, "snapshot-width", i_width);
var_Create( p_vout, "snapshot-height", VLC_VAR_INTEGER );
var_SetInteger( p_vout, "snapshot-height", i_height );
var_Create( p_vout, "snapshot-path", VLC_VAR_STRING );
var_SetString( p_vout, "snapshot-path", psz_filepath );
var_Create( p_vout, "snapshot-format", VLC_VAR_STRING );
var_SetString( p_vout, "snapshot-format", "png" );
var_TriggerCallback( p_vout, "video-snapshot" );
vlc_object_release( p_vout );
return 0;
}
对啊,这就是截图的实现啊~
思路有了吧!
2、添加截图接口
在./android-vlc-20190510/libvlc/jni/libvlcjni-mediaplayer.c文件中Java_org_videolan_libvlc_MediaPlayer_nativeRecord函数后添加
jboolean
Java_org_videolan_libvlc_MediaPlayer_nativeSnapshot(JNIEnv *env, jobject thiz,
jstring jdirectory,jint w,jint h)
{
vlcjni_object *p_obj = VLCJniObject_getInstance(env, thiz);
const char *psz_directory;
if (!p_obj)
return false;
int (*snapshot_func)(libvlc_media_player_t *p_mi, unsigned num,const char *psz_filepath, unsigned int i_width,unsigned int i_height) =dlsym(RTLD_DEFAULT, "libvlc_video_take_snapshot");
if (!snapshot_func)
return false;
if (jdirectory)
{
psz_directory = (*env)->GetStringUTFChars(env, jdirectory, 0);
if (!psz_directory)
{
throw_Exception(env, VLCJNI_EX_ILLEGAL_ARGUMENT, "directory invalid");
return false;
}
}
else
psz_directory = NULL;
jboolean ret = snapshot_func(p_obj->u.p_mp, 0,psz_directory,0,0) == 0;
if (psz_directory)
{
(*env)->ReleaseStringUTFChars(env, jdirectory, psz_directory);
}
return ret;
}
根据函数声明,我把num,width,height都传入0,具体含义看函数说明;
根据jni接口函数声明,我们知道libvlcjni-mediaplayer.c对应的是MediaPlayer类,所以我们需要去修改MediaPlayer.java
这个文件在:/home/xiaozd/Github/android-vlc-20190510/libvlc/src/org/videolan/libvlc/MediaPlayer.java
在nativeRecord函数下方添加nativeSnapshot函数
private native boolean nativeSnapshot(String directory);
再record函数下方添加getSnapshot函数
public boolean getSnapshot(String directory) {
return nativeSnapshot(directory);
}
完成~
3、重新编译
执行./compile.sh
编译完成即可获得aar
4、注意
截图路径需要到文件名如:xxx/xxx/xxx.jpg
视频录制路径只能到文件夹如:xxx/xxx/xxx/