gstreamer/subprojects/gst-docs/markdown/tutorials/android/link-against-gstreamer.md

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

283 lines
8.6 KiB
Markdown
Raw Normal View History

# Android tutorial 1: Link against GStreamer
2016-05-16 14:30:34 +00:00
2016-06-17 00:01:54 +00:00
## Goal!
![screenshot]
2016-05-16 14:30:34 +00:00
This first Android tutorial is extremely simple: it just retrieves the
GStreamer version and displays it on the screen. It exemplifies how to
access GStreamer C code from Java and verifies that there have been no
linkage problems.
2016-05-16 14:30:34 +00:00
2016-06-17 00:01:54 +00:00
## Hello GStreamer \[Java code\]
2016-05-16 14:30:34 +00:00
The tutorial code is in the [gst-docs subdirectory](https://gitlab.freedesktop.org/gstreamer/gstreamer/-/tree/main/subprojects/gst-docs/examples/tutorials/android/android-tutorial-1).
This directory contains the usual Android NDK structure: a `src` folder for the Java code,
2016-06-17 00:01:54 +00:00
a `jni` folder for the C code and a `res` folder for UI resources.
2016-05-16 14:30:34 +00:00
We recommend that you open this project in Eclipse (as explained
in [](installing/for-android-development.md)) so you can
2016-05-16 14:30:34 +00:00
easily see how all the pieces fit together.
Lets first introduce the Java code, then the C code and finally the
makefile that allows GStreamer integration.
2016-06-17 00:01:54 +00:00
**src/org/freedesktop/gstreamer/tutorials/tutorial_1/Tutorial1.java**
2016-05-16 14:30:34 +00:00
2016-06-06 00:58:09 +00:00
``` java
package org.freedesktop.gstreamer.tutorials.tutorial_1;
2016-05-16 14:30:34 +00:00
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
import org.freedesktop.gstreamer.GStreamer;
2016-05-16 14:30:34 +00:00
public class Tutorial1 extends Activity {
private native String nativeGetGStreamerInfo();
2016-05-16 14:30:34 +00:00
// Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
2016-05-16 14:30:34 +00:00
try {
GStreamer.init(this);
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
finish();
2016-05-16 14:30:34 +00:00
return;
}
2016-05-16 14:30:34 +00:00
setContentView(R.layout.main);
2016-05-16 14:30:34 +00:00
TextView tv = (TextView)findViewById(R.id.textview_info);
tv.setText("Welcome to " + nativeGetGStreamerInfo() + " !");
}
2016-05-16 14:30:34 +00:00
static {
System.loadLibrary("gstreamer_android");
System.loadLibrary("tutorial-1");
}
2016-05-16 14:30:34 +00:00
}
```
Calls from Java to C happen through native methods, like the one
declared here:
2016-06-06 00:58:09 +00:00
``` java
2016-05-16 14:30:34 +00:00
private native String nativeGetGStreamerInfo();
```
This tells Java that there exists a method with this signature somewhere
so it compiles happily. It is your responsibility to ensure that, **at
runtime**, this method is accessible. This is accomplished by the C code
shown later.
The first bit of code that gets actually executed is the static
initializer of the class:
2016-06-06 00:58:09 +00:00
``` java
2016-05-16 14:30:34 +00:00
static {
System.loadLibrary("gstreamer_android");
System.loadLibrary("tutorial-1");
}
```
It loads `libgstreamer_android.so`, which contains all GStreamer
methods, and `libtutorial-1.so`, which contains the C part of this
tutorial, explained below.
2016-06-17 00:01:54 +00:00
Upon loading, each of these libraries `JNI_OnLoad()` method is
2016-05-16 14:30:34 +00:00
executed. It basically registers the native methods that these libraries
2016-06-17 00:01:54 +00:00
expose. The GStreamer library only exposes a `init()` method, which
2016-05-16 14:30:34 +00:00
initializes GStreamer and registers all plugins (The tutorial library is
explained later below).
2016-06-06 00:58:09 +00:00
``` java
try {
GStreamer.init(this);
} catch (Exception e) {
2016-05-16 14:30:34 +00:00
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
finish();
return;
}
2016-05-16 14:30:34 +00:00
```
2016-06-17 00:01:54 +00:00
Next, in the `OnCreate()` method of the
2016-05-16 14:30:34 +00:00
[Activity](http://developer.android.com/reference/android/app/Activity.html)
we actually initialize GStreamer by calling `GStreamer.init()`. This
method requires a
[Context](http://developer.android.com/reference/android/content/Context.html)
so it cannot be called from the static initializer, but there is no
danger in calling it multiple times, as all but the first time the calls
will be ignored.
2016-06-17 00:01:54 +00:00
Should initialization fail, the `init()` method would throw an
2016-05-16 14:30:34 +00:00
[Exception](http://developer.android.com/reference/java/lang/Exception.html)
with the details provided by the GStreamer library.
2016-06-06 00:58:09 +00:00
``` java
TextView tv = (TextView)findViewById(R.id.textview_info);
tv.setText("Welcome to " + nativeGetGStreamerInfo() + " !");
2016-05-16 14:30:34 +00:00
```
Then, the native method `nativeGetGStreamerInfo()` is called and a
string is retrieved, which is used to format the content of the
[TextView](http://developer.android.com/reference/android/widget/TextView.html)
in the UI.
This finishes the UI part of this tutorial. Lets take a look at the C
code:
2016-06-17 00:01:54 +00:00
## Hello GStreamer \[C code\]
2016-05-16 14:30:34 +00:00
**jni/tutorial-1.c**
2016-06-06 00:58:09 +00:00
``` c
2016-05-16 14:30:34 +00:00
#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) {
2016-05-16 14:30:34 +00:00
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");
2016-05-16 14:30:34 +00:00
(*env)->RegisterNatives (env, klass, native_methods, G_N_ELEMENTS(native_methods));
return JNI_VERSION_1_4;
}
```
2016-06-17 00:01:54 +00:00
The `JNI_OnLoad()` method is executed every time the Java Virtual
2016-05-16 14:30:34 +00:00
Machine (VM) loads a library.
Here, we retrieve the JNI environment needed to make calls that interact
with Java:
2016-06-06 00:58:09 +00:00
``` c
2016-05-16 14:30:34 +00:00
JNIEnv *env = NULL;
2016-05-16 14:30:34 +00:00
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;
}
2016-05-16 14:30:34 +00:00
```
And then locate the class containing the UI part of this tutorial using
`
FindClass()`:
2016-06-06 00:58:09 +00:00
``` c
jclass klass = (*env)->FindClass (env, "org/freedesktop/gstreamer/tutorials/tutorial_1/Tutorial1");
2016-05-16 14:30:34 +00:00
```
2016-06-17 00:01:54 +00:00
Finally, we register our native methods with `RegisterNatives()`, this
2016-05-16 14:30:34 +00:00
is, we provide the code for the methods we advertised in Java using the
**`native`**
2016-06-17 00:01:54 +00:00
keyword:
2016-05-16 14:30:34 +00:00
2016-06-06 00:58:09 +00:00
``` c
2016-05-16 14:30:34 +00:00
(*env)->RegisterNatives (env, klass, native_methods, G_N_ELEMENTS(native_methods));
```
2016-06-17 00:01:54 +00:00
The `native_methods` array describes each one of the methods to register
(only one in this tutorial). For each method, it provides its Java
2016-05-16 14:30:34 +00:00
name, its [type
signature](http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html#wp276)
and a pointer to the C function implementing it:
2016-06-06 00:58:09 +00:00
``` c
2016-05-16 14:30:34 +00:00
static JNINativeMethod native_methods[] = {
{ "nativeGetGStreamerInfo", "()Ljava/lang/String;", (void *) gst_native_get_gstreamer_info}
};
```
The only native method used in this tutorial
2016-06-17 00:01:54 +00:00
is `nativeGetGStreamerInfo()`:
2016-05-16 14:30:34 +00:00
2016-06-06 00:58:09 +00:00
``` c
2016-05-16 14:30:34 +00:00
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;
}
```
2016-06-17 00:01:54 +00:00
It simply calls `gst_version_string()` to obtain a string describing
2016-05-16 14:30:34 +00:00
this version of GStreamer. This [Modified
UTF8](http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8) string is then
converted to [UTF16](http://en.wikipedia.org/wiki/UTF-16) by `
2016-06-17 00:01:54 +00:00
NewStringUTF()` as required by Java and returned. Java will be
2016-05-16 14:30:34 +00:00
responsible for freeing the memory used by the new UTF16 String, but we
must free the `char *` returned by `gst_version_string()`.
2016-06-17 00:01:54 +00:00
## Hello GStreamer \[Android.mk\]
2016-05-16 14:30:34 +00:00
**jni/Android.mk**
2016-06-06 00:58:09 +00:00
``` ruby
2016-05-16 14:30:34 +00:00
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!)
2016-05-16 14:30:34 +00:00
endif
GSTREAMER_ROOT := $(GSTREAMER_ROOT_ANDROID)
2016-05-16 14:30:34 +00:00
endif
GSTREAMER_NDK_BUILD_PATH := $(GSTREAMER_ROOT)/share/gst-android/ndk-build/
2016-05-16 14:30:34 +00:00
GSTREAMER_PLUGINS := coreelements
include $(GSTREAMER_NDK_BUILD_PATH)/gstreamer-1.0.mk
2016-05-16 14:30:34 +00:00
```
This is a barebones makefile for a project with GStreamer support. It
2016-06-17 00:01:54 +00:00
simply states that it depends on the `libgstreamer_android.so` library
(line 7), and requires the `coreelements` plugin (line 18). More complex
2016-05-16 14:30:34 +00:00
applications will probably add more libraries and plugins
2016-06-17 00:01:54 +00:00
to `Android.mk`
2016-05-16 14:30:34 +00:00
2016-06-17 00:01:54 +00:00
## Conclusion
2016-05-16 14:30:34 +00:00
This ends the first Android tutorial. It has shown that, besides the
interconnection between Java and C (which abides to the standard JNI
procedure), adding GStreamer support to an Android application is not
any more complicated than adding it to a desktop application.
The following tutorials detail the few places in which care has to be
taken when developing specifically for the Android platform.
As usual, it has been a pleasure having you here, and see you soon\!
[screenshot]: images/tutorials/android-link-against-gstreamer-screenshot.png