Android教程1:链接GStreamer
目标
这个第一个Android教程非常简单:它只是检索GStreamer版本并将其显示在屏幕上。它示例了如何从Java访问GStreamer C代码,并验证没有链接问题。
你好GStreamer[Java代码]
教程代码位于gst-docs子目录中。 此目录包含通常的Android NDK结构:一个用于Java代码的src文件夹,一个用于C代码的jni文件夹和一个用于UI资源的res文件夹。
我们建议您在Eclipse中打开这个项目(如前所述 在安装为Android开发),这样您可以轻松查看所有部分是如何组合在一起的。
让我们首先介绍Java代码,然后是C代码,最后是允许集成GStreamer的makefile。
src/org/freedesktop/gstreamer/tutorials/tutorial_1/Tutorial1.java
package org.freedesktop.gstreamer.tutorials.tutorial_1;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
import org.freedesktop.gstreamer.GStreamer;
public class Tutorial1 extends Activity {
private native String nativeGetGStreamerInfo();
// Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
try {
GStreamer.init(this);
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
finish();
return;
}
setContentView(R.layout.main);
TextView tv = (TextView)findViewById(R.id.textview_info);
tv.setText("Welcome to " + nativeGetGStreamerInfo() + " !");
}
static {
System.loadLibrary("gstreamer_android");
System.loadLibrary("tutorial-1");
}
}
从Java到C的调用通过本地方法进行,如下所声明:
private native String nativeGetGStreamerInfo();
这告诉Java存在一个具有此签名的方法,因此它可以愉快地编译。确保在运行时此方法是可访问的是你的责任。稍后显示的C代码完成了这一点。
实际执行的第一段代码是类的静态初始化器:
static {
System.loadLibrary("gstreamer_android");
System.loadLibrary("tutorial-1");
}
它加载libgstreamer_android.so,其中包含所有GStreamer方法,以及libtutorial-1.so,其中包含本教程的C部分,如下所述。
在加载时,这些库的JNI_OnLoad()方法会被执行。它基本上注册了这些库公开的本地方法。GStreamer库仅公开了一个init()方法,该方法初始化GStreamer并注册所有插件(稍后将在下面解释教程库)。
try {
GStreamer.init(this);
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
finish();
return;
}
接下来,在Activity的OnCreate()方法中,我们通过调用GStreamer.init()来实际初始化GStreamer。此方法需要一个Context,因此不能从静态初始化器中调用,但多次调用也没有危险,因为除了第一次之外的所有调用都将被忽略。
如果初始化失败,init()方法将抛出一个异常,其中包含GStreamer库提供的详细信息。 然后,调用本地方法nativeGetGStreamerInfo()并检索一个字符串,该字符串用于格式化UI中TextView的内容。
这就完成了本教程的UI部分。让我们看一下C代码:
TextView tv = (TextView)findViewById(R.id.textview_info);
tv.setText("Welcome to " + nativeGetGStreamerInfo() + " !");
然后,调用本机方法nativeGetGStreamerInfo()
并 检索字符串,用于格式化内容 文本视图 在UI中。
本教程的UI部分到此结束。让我们看一下C代码:
你好GStreamer[C代码]
jni/tutorial-1.c
#include <string.h>
#include <jni.h>
#include <android/log.h>
#include <gst/gst.h>
/*
* Java Bindings
*/
static jstring gst_native_get_gstreamer_info (JNIEnv* env, jobject thiz) {
char *version_utf8 = gst_version_string();
jstring *version_jstring = (*env)->NewStringUTF(env, version_utf8);
g_free (version_utf8);
return version_jstring;
}
static JNINativeMethod native_methods[] = {
{ "nativeGetGStreamerInfo", "()Ljava/lang/String;", (void *) gst_native_get_gstreamer_info}
};
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
__android_log_print (ANDROID_LOG_ERROR, "tutorial-1", "Could not retrieve JNIEnv");
return 0;
}
jclass klass = (*env)->FindClass (env, "org/freedesktop/gstreamer/tutorials/tutorial_1/Tutorial1");
(*env)->RegisterNatives (env, klass, native_methods, G_N_ELEMENTS(native_methods));
return JNI_VERSION_1_4;
}
JNI_OnLoad()方法是在Java虚拟机(VM)每次加载库时执行的。
在这里,我们检索与Java交互所需的JNI环境:
JNIEnv *env = NULL;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
__android_log_print (ANDROID_LOG_ERROR, "tutorial-1", "Could not retrieve JNIEnv");
return 0;
}
然后使用FindClass()定位包含本教程UI部分的类:
jclass klass = (*env)->FindClass (env, "org/freedesktop/gstreamer/tutorials/tutorial_1/Tutorial1");
最后,我们使用RegisterNatives()注册我们的本地方法,也就是说,我们为使用Java中的native关键字声明的方法提供代码:
(*env)->RegisterNatives (env, klass, native_methods, G_N_ELEMENTS(native_methods));
native_methods数组描述了要注册的每个方法(本教程中只有一个)。对于每个方法,它提供了其Java名称、类型签名和指向实现它的C函数的指针:
static JNINativeMethod native_methods[] = {
{ "nativeGetGStreamerInfo", "()Ljava/lang/String;", (void *) gst_native_get_gstreamer_info}
};
本教程中使用的唯一本地方法是nativeGetGStreamerInfo():
jstring gst_native_get_gstreamer_info (JNIEnv* env, jobject thiz) {
char *version_utf8 = gst_version_string();
jstring *version_jstring = (*env)->NewStringUTF(env, version_utf8);
g_free (version_utf8);
return version_jstring;
}
它简单地调用gst_version_string()以获取描述此版本GStreamer的字符串。然后,这个修改过的UTF8字符串通过NewStringUTF()转换为Java所需的UTF16,并返回。Java将负责释放新UTF16 String使用的内存,但我们必须释放gst_version_string()返回的char *。
你好GStreamer[Android.mk]
JNI/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := tutorial-1
LOCAL_SRC_FILES := tutorial-1.c
LOCAL_SHARED_LIBRARIES := gstreamer_android
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
ifndef GSTREAMER_ROOT
ifndef GSTREAMER_ROOT_ANDROID
$(error GSTREAMER_ROOT_ANDROID is not defined!)
endif
GSTREAMER_ROOT := $(GSTREAMER_ROOT_ANDROID)
endif
GSTREAMER_NDK_BUILD_PATH := $(GSTREAMER_ROOT)/share/gst-android/ndk-build/
GSTREAMER_PLUGINS := coreelements
include $(GSTREAMER_NDK_BUILD_PATH)/gstreamer-1.0.mk
这是一个支持GStreamer的项目的简单makefile。它仅仅声明它依赖于libgstreamer_android.so库(第7行),并且需要coreelements插件(第18行)。更复杂的应用程序可能会在Android.mk中添加更多的库和插件。
结论
这结束了第一个Android教程。它展示了,除了Java和C之间的互连(遵循标准的JNI程序)之外,将GStreamer支持添加到Android应用程序并不比将其添加到桌面应用程序复杂。
后续教程将详细介绍在专门为Android平台开发时需要注意的几个方面。