在使用MediaPlayer时无法java实时读取log文件视频文件,哪位大神帮忙指点一下,以下是主要代码

博主最新文章
博主热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)「音视频直播技术」看ijkplayer如何使用JNI「音视频直播技术」看ijkplayer如何使用JNI视界经纬百家号前言ijklayer可以说是目前最火的一款移动端播放器了。它同时支持Android和iOS,它之所以如此流行,主要是代码写的太美了,我认为把它当作艺术品也不过分。ijkplayer为了提高性能做了大量的优化,其中一个关键点是使用了JNI。播边器里最关键的部分全部由C来实现。今天我们就来看看 jikplayer 是如何使用JNI的。导入动态库ijkplayer 创建 IJKMediaPlayer 对象时,在其构造函数里会调到 loadLibrariesOnce 方法。代码如下:public static void loadLibrariesOnce(IjkLibLoader libLoader) {......libLoader.loadLibrary("ijkffmpeg"); //加载 ffmpeg 用于解码libLoader.loadLibrary("ijksdl"); // 加载 sdk 用于渲染libLoader.loadLibrary("ijkplayer"); // 加载 ijkplayer 核心播放器......}其中,loadLibrariesOnce方法中的libLoader是ijkplayer定义的IjkLibLoader类对象。该对象的 loadLibrary 方法最终会调用 System.loadLibrary 函数完成共享库的加载。经过上面操作后 ijkffmpeg、ijksdl及ijkplayer就被加载到JavaVM里了。在Android系统下,每一个进程只能有一个JavaVM。我们第一步看下在ijkplayer里,如何通过 Java代码调到 C/C++接口。在Java层定义本地方法想通过 Java 调用 C/C++ 接口,首先需要让 Java 程序知道都有哪些 C/C++ 接口可用。这有点像C/C++中常说的符号表(名子与地址的对应关系表)。如何能做到这点呢?方法很简单,就是在 Java 类方法的前边加上 "native" 关键字。我们看一下 IJKPlayer 都提供了哪些本地方法吧:......private native void _setDataSource(IMediaDataSource mediaDataSource);public native void _prepareAsync() throws IllegalStateEprivate native void _start() throws IllegalStateEprivate native void _stop() throws IllegalStateEprivate native void _pause() throws IllegalStateEpublic native void seekTo(long msec) throws IllegalStateEpublic native long getDuration();private native void _release();public native void setVolume(float leftVolume, float rightVolume);private static native void native_init();......这一步是不是非常简单?当然,只做到这一步还无法调用 C/C++接口,因为你还没告诉JavaVM你的C/C++接口在哪儿呢。注册C/C++方法仅在Java层定义本地方法只完成了工作的一半。当Java代码真正调用 “native” 方法时,JavaVM虚拟机会在符号表中查找有没有 Java 程序想调用的函数。如果此时没有的话,JavaVM 就会报错。所以现在我们要将 C/C++ 提供的接口注册到 JavaVM中。首先,建好函数对应表。此表中的每一项都包括三个元素,分别是 外部调用的接口名、signature、内部真正的实现函数。signature 后面有专门的讲解。代码如下:static JNINativeMethod g_methods[] = {......{ "_setDataSource", "(Ltv/danmaku/ijk/media/player/misc/IMediaDataS)V", (void *)IjkMediaPlayer_setDataSourceCallback},{ "_prepareAsync", "()V", (void *) IjkMediaPlayer_prepareAsync },{ "_start", "()V", (void *) IjkMediaPlayer_start },{ "_stop", "()V", (void *) IjkMediaPlayer_stop },{ "seekTo", "(J)V", (void *) IjkMediaPlayer_seekTo },{ "_pause", "()V", (void *) IjkMediaPlayer_pause },{ "getDuration", "()J", (void *) IjkMediaPlayer_getDuration },{ "_release", "()V", (void *) IjkMediaPlayer_release },{ "setVolume", "(FF)V", (void *) IjkMediaPlayer_setVolume },{ "native_init", "()V", (void *) IjkMediaPlayer_native_init },......};看看这里的外部调用函数名是不是与上面在 Java 层定义的方法名是一样的呢?只有一样它们之前才能建立起对应关系来。然后,将上面表中的方法注册到JavaVM中。代码如下:......//注册native方法,并与 IjkMediaPlayer 关联起来。//g_clazz.clazz 存放的是 IjkMediaPlayer 类的 jclass 对象//g_methods 就是上面定义的函数表//NELEM(g_methods) 计算函数表中一共有几项(*env)-&RegisterNatives(env, g_clazz.clazz, g_methods, NELEM(g_methods) );......ijkplayer通过FindClass找到IjkMediaPlayer类的 jclass 对象,再通过RegisterNatives函数将C/C++接口注册到JavaVM中,并与IjkMediaPlayer类绑定在一起。在哪儿注册最好上面我们知道了如何注册C/C++方法,那么在什么地方注册好呢?答案是在 JNI_OnLoad 函数中。因为在加载动态链接库时,JavaVM会主动调用JNI_OnLoad(JavaVM * jvm, void * reserved)。看一下ijkplayer的实现:JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved){JNIEnv* env = NULL;g_jvm =if ((*vm)-&GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {return -1;}......// FindClass returns GlobleReferenceIJK_FIND_JAVA_CLASS(env, g_clazz.clazz, JNI_CLASS_IJKPLAYER);(*env)-&RegisterNatives(env, g_clazz.clazz, g_methods, NELEM(g_methods) );......return JNI_VERSION_1_4;}JNIEXPORT void JNI_OnUnload(JavaVM *jvm, void *reserved){......}在 JNI_OnLoad 函数中首先获取 JNIEnv,之后找到 IjkMediaPlayer 类,最后注册C/C++方法,并将注册的方法与IjkMediaPlayer类关联起来。当然,有了 JNI_OnLoad 还要有 JNI_OnUnload 函数。它在共享库被卸载时调用,可以在这里释放一些资源。通过上面的操作我们就可以从 Java 调用 C++的代码了。有没有赶快去试试的冲动?先别急,现在只介绍了如何从 Java 调用 C/C++的方法。那么反回来如何从 C/C++ 调 Java 代码呢?C/C++调用Java方法ijkplayer 会使用C调用android下的 MediaCodec 类中的方法。首先,通过 FindClass 获取MediaCodec的 jclass 对象。然后设置该对象为全局引用,并将它的本地引用删除。这些方法的调用都要做异常判断,如果出现异常所有的结果都是无效的值。我们就以这个为例子,看一下它是如何从C调用的java方法吧。......//对异常的处理bool J4A_ExceptionCheck__catchAll(JNIEnv *env){if ((*env)-&ExceptionCheck(env)) {(*env)-&ExceptionDescribe(env);(*env)-&ExceptionClear(env);}}//通过 FindClass 得到jclass对象jclass J4A_FindClass__catchAll(JNIEnv *env, const char *class_sign){jclass clazz = (*env)-&FindClass(env, class_sign);......}//拿到 jclass 对象,并设置其为全局引用jclass J4A_FindClass__asGlobalRef__catchAll(JNIEnv *env, const char *class_sign){jclass clazz_global = NULL;jclass clazz = J4A_FindClass__catchAll(env, class_sign);......//设置为全局引用clazz_global = J4A_NewGlobalRef__catchAll(env, clazz);......fail:J4A_DeleteLocalRef__p(env, &clazz);return clazz_}......//设置要获取的类名sign = "android/media/MediaCodec";class_J4AC_android_media_MediaCodec.id =J4A_FindClass__asGlobalRef__catchAll(env, sign);......获得了 jclass 后,就可以通过 Get&Type&MethodID 获取类方法的jmethodID对象。......jmethodID J4A_GetStaticMethodID__catchAll(JNIEnv *env, jclass clazz, const char *method_name, const char *method_sign){jmethodID method_id = (*env)-&GetStaticMethodID(env, clazz, method_name, method_sign);......fail:return method_}......class_id = class_J4AC_android_media_MediaCodec. //jclassname = "createByCodecName"; //方法名sign = "(Ljava/lang/S)Landroid/media/MediaC"; //signatureclass_J4AC_android_media_MediaCodec.method_createByCodecName =J4A_GetStaticMethodID__catchAll(env, class_id, name, sign);......最后,通过 Call&Type&Method 调用 java 方法。......jobject J4AC_android_media_MediaCodec__createByCodecName(JNIEnv *env, jstring name){return (*env)-&CallStaticObjectMethod(env,class_J4AC_android_media_MediaCodec.id,class_J4AC_android_media_MediaCodec.method_createByCodecName,name);}......现在 C/C++ 也可以调用 Java 方法了。最后,我们再来看一下C/C++如何访问 java 的字段吧,这个就更简单了。C/C++访问Java字段有了 C/C++访问Java的基础,再看访问Java字段就容易多了。它也是先获取 jclass, 之后通过 jclass 得到 jfieldID,最终 Get/Set java 字段。jclass的获取我们就不讲了,重点说说获取 jfieldID 和 Get/Set。......jfieldID J4A_GetFieldID__catchAll(JNIEnv *env, jclass clazz, const char *field_name, const char *field_sign){//获得 jfieldIDjfieldID field_id = (*env)-&GetFieldID(env, clazz, field_name, field_sign);//异常判断if (J4A_ExceptionCheck__catchAll(env) || !field_id) {......}fail:return field_}......class_id = class_J4AC_android_media_MediaCodec__BufferInfo. //jclassname = "flags"; // java 字段名sign = "I"; // signatureclass_J4AC_android_media_MediaCodec__BufferInfo.field_flags =J4A_GetFieldID__catchAll(env, class_id, name, sign);......上面的代码通过GetFieldID方法就得到了我们想要的 jfieldID。下一步看看如何进行 Get/Set。......jint J4AC_android_media_MediaCodec__BufferInfo__flags__get(JNIEnv *env, jobject thiz){return (*env)-&GetIntField(env, thiz, class_J4AC_android_media_MediaCodec__BufferInfo.field_flags);}......void J4AC_android_media_MediaCodec__BufferInfo__flags__set(JNIEnv *env, jobject thiz, jint value){(*env)-&SetIntField(env, thiz, class_J4AC_android_media_MediaCodec__BufferInfo.field_flags, value);}......非常简单,JNI调用 Get&Type&Field或Set&Type&Field方法获取或设置Java的字段。至此我们就分析完了 ijkplayer 对 JNI的使用。后面附上 Signature 的说明。Signature在JNI中Signature主要用于操作Java类中的方法。Signature一般由两部分组成:方法参数;方法返回值。方法参数包含在“()”中,返回值在括号外。方法参数个数较多时会依次以“;”隔开。当参数或者返回值是基本数据类型时,必须用其在JNI中的描述符表示。下表就是Java基本数据类型对应的JNI中的描述符。Java类型符号booleanZbyteBcharCshortSintIlongLfloatFdoublDvoidV方法参数或者返回值为java中的对象时,必须以“L”加上其路径,不过路径必须以“/”分开,自定义的对象也使用本规则,不在包中时直接“L”加上类名称。当参数或者返回值为数组时,前面必须加上“[”。以上就是Signature表示方法的规则!看看下面一些Signature,你能一个个转换为相应的方法吗?([LS)[LS([I[Ljava/lang/S[LS)Ljava/lang/O([LS[LS)[LS([Ljava/util/I)[Ljava/util/E([Ljava/lang/O)[Ljava/lang/O([Ljava/lang/S)[Ljava/lang/S(LS)LS小结本篇文章介绍了ijkplayer是如何使用JNI的,主要包以下几点内容:Java 如何调用 C/C++ 接口。C/C++ 如何调用 Java 方法。C/C++ 如何设置/获取 Java 字段的值。希望本文能对您有所帮助,并请多多观注。谢谢!参考JNI编程常见问题本文仅代表作者观点,不代表百度立场。系作者授权百家号发表,未经许可不得转载。视界经纬百家号最近更新:简介:科技视界,视界经纬。作者最新文章相关文章Android鎻愰珮绗?竴绡囦箣MediaPlayer &
榄旇眴涔嬭矾
Android鎻愰珮绗?竴绡囦箣MediaPlayer
Android鎻愰珮绗?竴绡囦箣MediaPlayer
聽聽聽聽聽聽聽 鍓嶉潰鍐欎簡鍗佸洓绡囧叧浜庣晫闈㈢殑鍏ラ棬鏂囩珷锛屽ぇ瀹堕兘鐪嬪畬鍜岃窡鐫

我要回帖

更多关于 js 读取文件创建时间 的文章

 

随机推荐