Prechádzať zdrojové kódy

gstreamer 插件代码初始化

weizhengliang 10 mesiacov pred
commit
8bf13e465e
55 zmenil súbory, kde vykonal 6733 pridanie a 0 odobranie
  1. 16 0
      .gitignore
  2. 1 0
      app/.gitignore
  3. 76 0
      app/build.gradle
  4. 643 0
      app/gst-build-arm64-v8a/gstreamer_android.c
  5. BIN
      app/gst-build-arm64-v8a/gstreamer_android.o
  6. BIN
      app/gst-build-arm64-v8a/libgstreamer_android.so
  7. 33 0
      app/jni/Android.mk
  8. 3 0
      app/jni/Application.mk
  9. 0 0
      app/jni/dummy.cpp
  10. 355 0
      app/jni/wdkl-broadcast.c
  11. 21 0
      app/proguard-rules.pro
  12. 26 0
      app/src/androidTest/java/com/wdkl/gstreamer/demo/ExampleInstrumentedTest.java
  13. 26 0
      app/src/main/AndroidManifest.xml
  14. 126 0
      app/src/main/assets/fontconfig/fonts.conf
  15. BIN
      app/src/main/assets/fontconfig/fonts/truetype/Ubuntu-R.ttf
  16. 4291 0
      app/src/main/assets/ssl/certs/ca-certificates.crt
  17. 120 0
      app/src/main/java/com/wdkl/gstreamer/demo/MainActivity.java
  18. 54 0
      app/src/main/java/com/wdkl/gstreamer/demo/MyGStreamManager.java
  19. 34 0
      app/src/main/java/com/wdkl/gstreamer/demo/MyGstream.java
  20. 104 0
      app/src/main/java/org/freedesktop/gstreamer/GStreamer.java
  21. 60 0
      app/src/main/java/org/freedesktop/gstreamer/androidmedia/GstAhcCallback.java
  22. 54 0
      app/src/main/java/org/freedesktop/gstreamer/androidmedia/GstAhsCallback.java
  23. 43 0
      app/src/main/java/org/freedesktop/gstreamer/androidmedia/GstAmcOnFrameAvailableListener.java
  24. 30 0
      app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  25. 170 0
      app/src/main/res/drawable/ic_launcher_background.xml
  26. 36 0
      app/src/main/res/layout/activity_main.xml
  27. 5 0
      app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  28. 5 0
      app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  29. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.png
  30. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_round.png
  31. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.png
  32. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_round.png
  33. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.png
  34. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  35. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  36. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  37. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  38. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  39. 16 0
      app/src/main/res/values-night/themes.xml
  40. 10 0
      app/src/main/res/values/colors.xml
  41. 3 0
      app/src/main/res/values/strings.xml
  42. 16 0
      app/src/main/res/values/themes.xml
  43. 17 0
      app/src/test/java/com/wdkl/gstreamer/demo/ExampleUnitTest.java
  44. 24 0
      build.gradle
  45. 22 0
      gradle.properties
  46. BIN
      gradle/wrapper/gradle-wrapper.jar
  47. 6 0
      gradle/wrapper/gradle-wrapper.properties
  48. 172 0
      gradlew
  49. 84 0
      gradlew.bat
  50. 12 0
      local.properties
  51. 17 0
      readme.md
  52. BIN
      release/armeabi-v7a/libc++_shared.so
  53. BIN
      release/armeabi-v7a/libgstreamer_android.so
  54. BIN
      release/armeabi-v7a/libwdkl-broadcast.so
  55. 2 0
      settings.gradle

+ 16 - 0
.gitignore

@@ -0,0 +1,16 @@
+*.iml
+.gradle
+#/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+/.idea
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+#local.properties

+ 1 - 0
app/.gitignore

@@ -0,0 +1 @@
+/build

+ 76 - 0
app/build.gradle

@@ -0,0 +1,76 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 30
+    buildToolsVersion "30.0.3"
+
+    defaultConfig {
+        applicationId "com.wdkl.gstreamer.demo"
+        minSdkVersion 17
+        targetSdkVersion 30
+        versionCode 1
+        versionName '1.0.0'
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+        externalNativeBuild {
+            ndkBuild {
+                def gstRoot
+
+                if (project.hasProperty('gstAndroidRoot'))
+                    gstRoot = project.gstAndroidRoot
+                else
+                    gstRoot = System.env.GSTREAMER_ROOT_ANDROID
+
+                if (gstRoot == null)
+                    throw new GradleException('GSTREAMER_ROOT_ANDROID must be set, or "gstAndroidRoot" must be defined in your gradle.properties in the top level directory of the unpacked universal GStreamer Android binaries')
+
+                arguments "NDK_APPLICATION_MK=jni/Application.mk", "GSTREAMER_JAVA_SRC_DIR=src/main/java", "GSTREAMER_ROOT_ANDROID=$gstRoot", "GSTREAMER_ASSETS_DIR=src/main/java/assets"
+
+                targets "wdkl-broadcast"
+
+                // All archs except MIPS and MIPS64 are supported
+//                abiFilters  'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
+                abiFilters  'armeabi-v7a','arm64-v8a'
+            }
+        }
+    }
+
+    externalNativeBuild {
+        ndkBuild {
+            path 'jni/Android.mk'
+        }
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    sourceSets {
+        main.java.srcDirs += 'src/main/java'
+    }
+}
+
+afterEvaluate {
+    if (project.hasProperty('compileDebugJavaWithJavac'))
+        project.compileDebugJavaWithJavac.dependsOn 'externalNativeBuildDebug'
+    if (project.hasProperty('compileReleaseJavaWithJavac'))
+        project.compileReleaseJavaWithJavac.dependsOn 'externalNativeBuildRelease'
+}
+
+dependencies {
+
+    implementation 'androidx.appcompat:appcompat:1.1.0'
+    implementation 'com.google.android.material:material:1.1.0'
+    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+    testImplementation 'junit:junit:4.+'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 643 - 0
app/gst-build-arm64-v8a/gstreamer_android.c


BIN
app/gst-build-arm64-v8a/gstreamer_android.o


BIN
app/gst-build-arm64-v8a/libgstreamer_android.so


+ 33 - 0
app/jni/Android.mk

@@ -0,0 +1,33 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE    := wdkl-broadcast
+LOCAL_SRC_FILES := wdkl-broadcast.c dummy.cpp
+LOCAL_SHARED_LIBRARIES := gstreamer_android
+LOCAL_LDLIBS := -llog
+include $(BUILD_SHARED_LIBRARY)
+
+ifndef GSTREAMER_ROOT_ANDROID
+$(error GSTREAMER_ROOT_ANDROID is not defined!)
+endif
+
+ifeq ($(TARGET_ARCH_ABI),armeabi)
+GSTREAMER_ROOT        := $(GSTREAMER_ROOT_ANDROID)/arm
+else ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
+GSTREAMER_ROOT        := $(GSTREAMER_ROOT_ANDROID)/armv7
+else ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
+GSTREAMER_ROOT        := $(GSTREAMER_ROOT_ANDROID)/arm64
+else ifeq ($(TARGET_ARCH_ABI),x86)
+GSTREAMER_ROOT        := $(GSTREAMER_ROOT_ANDROID)/x86
+else ifeq ($(TARGET_ARCH_ABI),x86_64)
+GSTREAMER_ROOT        := $(GSTREAMER_ROOT_ANDROID)/x86_64
+else
+$(error Target arch ABI not supported: $(TARGET_ARCH_ABI))
+endif
+
+GSTREAMER_NDK_BUILD_PATH  := $(GSTREAMER_ROOT)/share/gst-android/ndk-build/
+include $(GSTREAMER_NDK_BUILD_PATH)/plugins.mk
+GSTREAMER_PLUGINS         := $(GSTREAMER_PLUGINS_CORE) $(GSTREAMER_PLUGINS_SYS) $(GSTREAMER_PLUGINS_PLAYBACK) $(GSTREAMER_PLUGINS_CODECS) $(GSTREAMER_PLUGINS_EFFECTS) $(GSTREAMER_PLUGINS_NET)
+GSTREAMER_EXTRA_DEPS      := glib-2.0 gstreamer-base-1.0 gstreamer-audio-1.0 gstreamer-player-1.0 gstreamer-sdp-1.0 gstreamer-webrtc-1.0
+include $(GSTREAMER_NDK_BUILD_PATH)/gstreamer-1.0.mk

+ 3 - 0
app/jni/Application.mk

@@ -0,0 +1,3 @@
+#APP_ABI = armeabi armeabi-v7a arm64-v8a x86 x86_64
+APP_ABI = armeabi armeabi-v7a arm64-v8a
+APP_STL = c++_shared

+ 0 - 0
app/jni/dummy.cpp


+ 355 - 0
app/jni/wdkl-broadcast.c

@@ -0,0 +1,355 @@
+#include <string.h>
+#include <jni.h>
+#include <android/log.h>
+#include <gst/gst.h>
+#include <pthread.h>
+
+GST_DEBUG_CATEGORY_STATIC (debug_category);
+#define GST_CAT_DEFAULT debug_category
+
+/*
+ * These macros provide a way to store the native pointer to CustomData, which might be 32 or 64 bits, into
+ * a jlong, which is always 64 bits, without warnings.
+ */
+#if GLIB_SIZEOF_VOID_P == 8
+# define GET_CUSTOM_DATA(env, thiz, fieldID) (CustomData *)(*env)->GetLongField (env, thiz, fieldID)
+# define SET_CUSTOM_DATA(env, thiz, fieldID, data) (*env)->SetLongField (env, thiz, fieldID, (jlong)data)
+#else
+# define GET_CUSTOM_DATA(env, thiz, fieldID) (CustomData *)(jint)(*env)->GetLongField (env, thiz, fieldID)
+# define SET_CUSTOM_DATA(env, thiz, fieldID, data) (*env)->SetLongField (env, thiz, fieldID, (jlong)(jint)data)
+#endif
+
+/* Structure to contain all our information, so we can pass it to callbacks */
+typedef struct _CustomData
+{
+  jobject app;                  /* Application instance, used to call its methods. A global reference is kept. */
+  GstElement *pipeline;         /* The running pipeline */
+  GMainContext *context;        /* GLib context used to run the main loop */
+  GMainLoop *main_loop;         /* GLib main loop */
+  gboolean initialized;         /* To avoid informing the UI multiple times about the initialization */
+  const gchar *server_str;                  //udp server string
+} CustomData;
+
+/* These global variables cache values which are not changing during execution */
+static pthread_t gst_app_thread;
+static pthread_key_t current_jni_env;
+static JavaVM *java_vm;
+static jfieldID custom_data_field_id;
+static jmethodID set_message_method_id;
+static jmethodID on_gstreamer_initialized_method_id;
+
+/*
+ * Private methods
+ */
+
+/* Register this thread with the VM */
+static JNIEnv *
+attach_current_thread (void)
+{
+  JNIEnv *env;
+  JavaVMAttachArgs args;
+
+  GST_DEBUG ("Attaching thread %p", g_thread_self ());
+  args.version = JNI_VERSION_1_4;
+  args.name = NULL;
+  args.group = NULL;
+
+  if ((*java_vm)->AttachCurrentThread (java_vm, &env, &args) < 0) {
+    GST_ERROR ("Failed to attach current thread");
+    return NULL;
+  }
+
+  return env;
+}
+
+/* Unregister this thread from the VM */
+static void
+detach_current_thread (void *env)
+{
+  GST_DEBUG ("Detaching thread %p", g_thread_self ());
+  (*java_vm)->DetachCurrentThread (java_vm);
+}
+
+/* Retrieve the JNI environment for this thread */
+static JNIEnv *
+get_jni_env (void)
+{
+  JNIEnv *env;
+
+  if ((env = pthread_getspecific (current_jni_env)) == NULL) {
+    env = attach_current_thread ();
+    pthread_setspecific (current_jni_env, env);
+  }
+
+  return env;
+}
+
+/* Change the content of the UI's TextView */
+static void
+set_ui_message (const gchar * message, CustomData * data)
+{
+  JNIEnv *env = get_jni_env ();
+  GST_DEBUG ("Setting message to: %s", message);
+  jstring jmessage = (*env)->NewStringUTF (env, message);
+  (*env)->CallVoidMethod (env, data->app, set_message_method_id, jmessage);
+  if ((*env)->ExceptionCheck (env)) {
+    GST_ERROR ("Failed to call Java method");
+    (*env)->ExceptionClear (env);
+  }
+  (*env)->DeleteLocalRef (env, jmessage);
+}
+
+/* Retrieve errors from the bus and show them on the UI */
+static void
+error_cb (GstBus * bus, GstMessage * msg, CustomData * data)
+{
+  GError *err;
+  gchar *debug_info;
+  gchar *message_string;
+
+  gst_message_parse_error (msg, &err, &debug_info);
+  message_string =
+      g_strdup_printf ("Error received from element %s: %s",
+      GST_OBJECT_NAME (msg->src), err->message);
+  g_clear_error (&err);
+  g_free (debug_info);
+  set_ui_message (message_string, data);
+  g_free (message_string);
+  gst_element_set_state (data->pipeline, GST_STATE_NULL);
+}
+
+/* Notify UI about pipeline state changes */
+static void
+state_changed_cb (GstBus * bus, GstMessage * msg, CustomData * data)
+{
+  GstState old_state, new_state, pending_state;
+  gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
+  /* Only pay attention to messages coming from the pipeline, not its children */
+  if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data->pipeline)) {
+    gchar *message = g_strdup_printf ("State changed to %s",
+        gst_element_state_get_name (new_state));
+    set_ui_message (message, data);
+    g_free (message);
+  }
+}
+
+/* Check if all conditions are met to report GStreamer as initialized.
+ * These conditions will change depending on the application */
+static void
+check_initialization_complete (CustomData * data)
+{
+  JNIEnv *env = get_jni_env ();
+  if (!data->initialized && data->main_loop) {
+    GST_DEBUG (" 装载完成 Initialization complete, notifying application. main_loop:%p",
+        data->main_loop);
+    (*env)->CallVoidMethod (env, data->app, on_gstreamer_initialized_method_id);
+    if ((*env)->ExceptionCheck (env)) {
+      GST_ERROR ("装载失败 Failed to call Java method");
+      (*env)->ExceptionClear (env);
+    }
+    data->initialized = TRUE;
+  }
+}
+
+/* Main method for the native code. This is executed on its own thread. */
+static void *
+app_function (void *userdata)
+{
+  JavaVMAttachArgs args;
+  GstBus *bus;
+  CustomData *data = (CustomData *) userdata;
+  GSource *bus_source;
+  GError *error = NULL;
+
+  GST_DEBUG ("Creating pipeline in CustomData at %p", data);
+
+  /* Create our own GLib Main Context and make it the default one */
+  data->context = g_main_context_new ();
+  g_main_context_push_thread_default (data->context);
+
+  //char* cmd_str = "autoaudiosrc name=\"audiosrc\" ! decodebin ! queue ! audioresample ! audioconvert ! opusenc ! rtpopuspay ! udpsink ";
+  GST_DEBUG (">>>>>>>>>>>>>>>>> %s", data->server_str);
+
+  gchar *cmd =
+          g_strdup_printf ("autoaudiosrc name=\"audiosrc\" ! decodebin ! queue ! audioresample ! audioconvert ! opusenc ! rtpopuspay ! udpsink %s", data->server_str);
+
+  //char cmd[100];
+  //strcpy(cmd,cmd_str);
+  //strcat(cmd,data->server_str);
+  GST_DEBUG (">>>>>>>>>>>>>>>>> %s", cmd);
+
+  /* Build pipeline */
+//  playbin uri=file:
+//  filesrc location=/sdcard/leg.flac ! decodebin ! queue !
+  data->pipeline =
+      gst_parse_launch
+//      ("audiotestsrc ! audioconvert ! audioresample ! autoaudiosink", &error);
+//                  ("audiotestsrc ! audioresample ! audioconvert ! opusenc ! rtpopuspay ! udpsink host=192.168.1.55 port=5004", &error);
+//                  ("playbin uri=file:/sdcard/leg.flac", &error);
+//                  ("filesrc location=/sdcard/leg.flac ! decodebin ! queue ! audioresample ! audioconvert ! opusenc ! rtpopuspay ! udpsink host=192.168.1.55 port=5004", &error);
+//    ("autoaudiosrc name=\"audiosrc\" ! decodebin ! queue ! audioresample ! audioconvert ! opusenc ! rtpopuspay ! udpsink host=192.168.1.55 port=5004", &error);
+                  (cmd, &error);
+
+  if (error) {
+    gchar *message =
+        g_strdup_printf ("Unable to build pipeline: %s", error->message);
+    g_clear_error (&error);
+    set_ui_message (message, data);
+    g_free (message);
+    return NULL;
+  }
+
+  /* Instruct the bus to emit signals for each received message, and connect to the interesting signals */
+  bus = gst_element_get_bus (data->pipeline);
+  bus_source = gst_bus_create_watch (bus);
+  g_source_set_callback (bus_source, (GSourceFunc) gst_bus_async_signal_func,
+      NULL, NULL);
+  g_source_attach (bus_source, data->context);
+  g_source_unref (bus_source);
+  g_signal_connect (G_OBJECT (bus), "message::error", (GCallback) error_cb,
+      data);
+  g_signal_connect (G_OBJECT (bus), "message::state-changed",
+      (GCallback) state_changed_cb, data);
+  gst_object_unref (bus);
+
+  /* Create a GLib Main Loop and set it to run */
+  GST_DEBUG ("Entering main loop... (CustomData:%p)", data);
+  data->main_loop = g_main_loop_new (data->context, FALSE);
+  check_initialization_complete (data);
+  g_main_loop_run (data->main_loop);
+  GST_DEBUG ("Exited main loop");
+  g_main_loop_unref (data->main_loop);
+  data->main_loop = NULL;
+
+  /* Free resources */
+  g_main_context_pop_thread_default (data->context);
+  g_main_context_unref (data->context);
+  gst_element_set_state (data->pipeline, GST_STATE_NULL);
+  gst_object_unref (data->pipeline);
+
+  g_free(cmd);
+  //g_free(cmd_str);
+
+  return NULL;
+}
+
+/*
+ * Java Bindings
+ */
+
+/* Instruct the native code to create its internal data structure, pipeline and thread */
+static void
+gst_native_init (JNIEnv * env, jobject thiz, jstring server_uri)
+{
+  CustomData *data = g_new0 (CustomData, 1);
+  SET_CUSTOM_DATA (env, thiz, custom_data_field_id, data);
+  GST_DEBUG_CATEGORY_INIT (debug_category, "tutorial-2", 0,
+      "Android tutorial 2");
+  gst_debug_set_threshold_for_name ("tutorial-2", GST_LEVEL_DEBUG);
+  GST_DEBUG ("Created CustomData at %p", data);
+  data->app = (*env)->NewGlobalRef (env, thiz);
+  GST_DEBUG ("Created GlobalRef for app object at %p", data->app);
+  const gchar *char_uri = (*env)->GetStringUTFChars (env, server_uri, NULL);
+//  GST_DEBUG (">>>>>>>>>>>>>>>>> %s", char_uri);
+  data->server_str = char_uri;
+//  GST_DEBUG (">>>>>>>>>>>>>>>>> %s", data->server_str);
+  pthread_create (&gst_app_thread, NULL, &app_function, data);
+}
+
+/* Quit the main loop, remove the native thread and free resources */
+static void
+gst_native_finalize (JNIEnv * env, jobject thiz)
+{
+  CustomData *data = GET_CUSTOM_DATA (env, thiz, custom_data_field_id);
+  if (!data)
+    return;
+  GST_DEBUG ("Quitting main loop...");
+  g_main_loop_quit (data->main_loop);
+  GST_DEBUG ("Waiting for thread to finish...");
+  pthread_join (gst_app_thread, NULL);
+  GST_DEBUG ("Deleting GlobalRef for app object at %p", data->app);
+  (*env)->DeleteGlobalRef (env, data->app);
+  GST_DEBUG ("Freeing CustomData at %p", data);
+  g_free (data);
+  SET_CUSTOM_DATA (env, thiz, custom_data_field_id, NULL);
+  GST_DEBUG ("Done finalizing");
+}
+
+/* Set pipeline to PLAYING state */
+static void
+gst_native_play (JNIEnv * env, jobject thiz)
+{
+  CustomData *data = GET_CUSTOM_DATA (env, thiz, custom_data_field_id);
+  if (!data)
+    return;
+  GST_DEBUG ("Setting state to PLAYING");
+  gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
+}
+
+/* Set pipeline to PAUSED state */
+static void
+gst_native_pause (JNIEnv * env, jobject thiz)
+{
+  CustomData *data = GET_CUSTOM_DATA (env, thiz, custom_data_field_id);
+  if (!data)
+    return;
+  GST_DEBUG ("Setting state to PAUSED");
+  gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
+}
+
+/* Static class initializer: retrieve method and field IDs */
+static jboolean
+gst_native_class_init (JNIEnv * env, jclass klass)
+{
+  custom_data_field_id =
+      (*env)->GetFieldID (env, klass, "native_custom_data", "J");
+  set_message_method_id =
+      (*env)->GetMethodID (env, klass, "setMessage", "(Ljava/lang/String;)V");
+  on_gstreamer_initialized_method_id =
+      (*env)->GetMethodID (env, klass, "onGStreamerInitialized", "()V");
+
+  if (!custom_data_field_id || !set_message_method_id
+      || !on_gstreamer_initialized_method_id) {
+    /* We emit this message through the Android log instead of the GStreamer log because the later
+     * has not been initialized yet.
+     */
+    __android_log_print (ANDROID_LOG_ERROR, "tutorial-2",
+        "The calling class does not implement all necessary interface methods");
+    return JNI_FALSE;
+  }
+  return JNI_TRUE;
+}
+
+/* List of implemented native methods */
+static JNINativeMethod native_methods[] = {
+  {"nativeInit", "(Ljava/lang/String;)V", (void *) gst_native_init},
+  {"nativeFinalize", "()V", (void *) gst_native_finalize},
+  {"nativePlay", "()V", (void *) gst_native_play},
+  {"nativePause", "()V", (void *) gst_native_pause},
+  {"nativeClassInit", "()Z", (void *) gst_native_class_init}
+};
+
+/* Library initializer */
+jint
+JNI_OnLoad (JavaVM * vm, void *reserved)
+{
+  JNIEnv *env = NULL;
+
+  java_vm = vm;
+
+  if ((*vm)->GetEnv (vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+    __android_log_print (ANDROID_LOG_ERROR, "wdkl-broadcast",
+        "Could not retrieve JNIEnv");
+    return 0;
+  }
+  //jclass klass = (*env)->FindClass (env,
+  //    "com/wdkl/gstreamer/demo/MainActivity");
+
+  jclass klass = (*env)->FindClass (env, "com/wdkl/gstreamer/demo/MyGstream");
+  (*env)->RegisterNatives (env, klass, native_methods,
+      G_N_ELEMENTS (native_methods));
+
+  pthread_key_create (&current_jni_env, detach_current_thread);
+
+  return JNI_VERSION_1_4;
+}

+ 21 - 0
app/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 26 - 0
app/src/androidTest/java/com/wdkl/gstreamer/demo/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package com.wdkl.gstreamer.demo;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertEquals("com.wdkl.gstreamer.demo", appContext.getPackageName());
+    }
+}

+ 26 - 0
app/src/main/AndroidManifest.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.wdkl.gstreamer.demo">
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.Gstreamerdemo">
+        <activity android:name=".MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>

+ 126 - 0
app/src/main/assets/fontconfig/fonts.conf

@@ -0,0 +1,126 @@
+<?xml version="1.0"?>
+<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+<!-- /etc/fonts/fonts.conf file to configure system font access -->
+<fontconfig>
+
+<!-- Font directory list -->
+
+	<dir prefix="xdg">fontconfig/fonts</dir>
+
+<!-- Font cache directory list -->
+
+	<cachedir prefix="xdg">fontconfig</cachedir>
+
+<!--
+  Accept deprecated 'mono' alias, replacing it with 'monospace'
+-->
+	<match target="pattern">
+		<test qual="any" name="family">
+			<string>mono</string>
+		</test>
+		<edit name="family" mode="assign" binding="same">
+			<string>monospace</string>
+		</edit>
+	</match>
+
+<!--
+  Accept alternate 'sans serif' spelling, replacing it with 'sans-serif'
+-->
+	<match target="pattern">
+		<test qual="any" name="family">
+			<string>sans serif</string>
+		</test>
+		<edit name="family" mode="assign" binding="same">
+			<string>sans-serif</string>
+		</edit>
+	</match>
+
+<!--
+  Accept deprecated 'sans' alias, replacing it with 'sans-serif'
+-->
+	<match target="pattern">
+		<test qual="any" name="family">
+			<string>sans</string>
+		</test>
+		<edit name="family" mode="assign" binding="same">
+			<string>sans-serif</string>
+		</edit>
+	</match>
+
+	<config>
+<!--
+  These are the default Unicode chars that are expected to be blank
+  in fonts.  All other blank chars are assumed to be broken and
+  won't appear in the resulting charsets
+ -->
+		<blank>
+			<int>0x0020</int>	<!-- SPACE -->
+			<int>0x00A0</int>	<!-- NO-BREAK SPACE -->
+			<int>0x00AD</int>	<!-- SOFT HYPHEN -->
+			<int>0x034F</int>	<!-- COMBINING GRAPHEME JOINER -->
+			<int>0x0600</int>	<!-- ARABIC NUMBER SIGN -->
+			<int>0x0601</int>	<!-- ARABIC SIGN SANAH -->
+			<int>0x0602</int>	<!-- ARABIC FOOTNOTE MARKER -->
+			<int>0x0603</int>	<!-- ARABIC SIGN SAFHA -->
+			<int>0x06DD</int>	<!-- ARABIC END OF AYAH -->
+			<int>0x070F</int>	<!-- SYRIAC ABBREVIATION MARK -->
+			<int>0x115F</int>	<!-- HANGUL CHOSEONG FILLER -->
+			<int>0x1160</int>	<!-- HANGUL JUNGSEONG FILLER -->
+			<int>0x1680</int>	<!-- OGHAM SPACE MARK -->
+			<int>0x17B4</int>	<!-- KHMER VOWEL INHERENT AQ -->
+			<int>0x17B5</int>	<!-- KHMER VOWEL INHERENT AA -->
+			<int>0x180E</int>	<!-- MONGOLIAN VOWEL SEPARATOR -->
+			<int>0x2000</int>	<!-- EN QUAD -->
+			<int>0x2001</int>	<!-- EM QUAD -->
+			<int>0x2002</int>	<!-- EN SPACE -->
+			<int>0x2003</int>	<!-- EM SPACE -->
+			<int>0x2004</int>	<!-- THREE-PER-EM SPACE -->
+			<int>0x2005</int>	<!-- FOUR-PER-EM SPACE -->
+			<int>0x2006</int>	<!-- SIX-PER-EM SPACE -->
+			<int>0x2007</int>	<!-- FIGURE SPACE -->
+			<int>0x2008</int>	<!-- PUNCTUATION SPACE -->
+			<int>0x2009</int>	<!-- THIN SPACE -->
+			<int>0x200A</int>	<!-- HAIR SPACE -->
+			<int>0x200B</int>	<!-- ZERO WIDTH SPACE -->
+			<int>0x200C</int>	<!-- ZERO WIDTH NON-JOINER -->
+			<int>0x200D</int>	<!-- ZERO WIDTH JOINER -->
+			<int>0x200E</int>	<!-- LEFT-TO-RIGHT MARK -->
+			<int>0x200F</int>	<!-- RIGHT-TO-LEFT MARK -->
+			<int>0x2028</int>	<!-- LINE SEPARATOR -->
+			<int>0x2029</int>	<!-- PARAGRAPH SEPARATOR -->
+			<int>0x202A</int>	<!-- LEFT-TO-RIGHT EMBEDDING -->
+			<int>0x202B</int>	<!-- RIGHT-TO-LEFT EMBEDDING -->
+			<int>0x202C</int>	<!-- POP DIRECTIONAL FORMATTING -->
+			<int>0x202D</int>	<!-- LEFT-TO-RIGHT OVERRIDE -->
+			<int>0x202E</int>	<!-- RIGHT-TO-LEFT OVERRIDE -->
+			<int>0x202F</int>	<!-- NARROW NO-BREAK SPACE -->
+			<int>0x205F</int>	<!-- MEDIUM MATHEMATICAL SPACE -->
+			<int>0x2060</int>	<!-- WORD JOINER -->
+			<int>0x2061</int>	<!-- FUNCTION APPLICATION -->
+			<int>0x2062</int>	<!-- INVISIBLE TIMES -->
+			<int>0x2063</int>	<!-- INVISIBLE SEPARATOR -->
+			<int>0x206A</int>	<!-- INHIBIT SYMMETRIC SWAPPING -->
+			<int>0x206B</int>	<!-- ACTIVATE SYMMETRIC SWAPPING -->
+			<int>0x206C</int>	<!-- INHIBIT ARABIC FORM SHAPING -->
+			<int>0x206D</int>	<!-- ACTIVATE ARABIC FORM SHAPING -->
+			<int>0x206E</int>	<!-- NATIONAL DIGIT SHAPES -->
+			<int>0x206F</int>	<!-- NOMINAL DIGIT SHAPES -->
+			<int>0x2800</int>	<!-- BRAILLE PATTERN BLANK -->
+			<int>0x3000</int>	<!-- IDEOGRAPHIC SPACE -->
+			<int>0x3164</int>	<!-- HANGUL FILLER -->
+			<int>0xFEFF</int>	<!-- ZERO WIDTH NO-BREAK SPACE -->
+			<int>0xFFA0</int>	<!-- HALFWIDTH HANGUL FILLER -->
+			<int>0xFFF9</int>	<!-- INTERLINEAR ANNOTATION ANCHOR -->
+			<int>0xFFFA</int>	<!-- INTERLINEAR ANNOTATION SEPARATOR -->
+			<int>0xFFFB</int>	<!-- INTERLINEAR ANNOTATION TERMINATOR -->
+		</blank>
+<!--
+  Rescan configuration every 30 seconds when FcFontSetList is called
+ -->
+		<rescan>
+			<int>30</int>
+		</rescan>
+	</config>
+
+</fontconfig>
+

BIN
app/src/main/assets/fontconfig/fonts/truetype/Ubuntu-R.ttf


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 4291 - 0
app/src/main/assets/ssl/certs/ca-certificates.crt


+ 120 - 0
app/src/main/java/com/wdkl/gstreamer/demo/MainActivity.java

@@ -0,0 +1,120 @@
+package com.wdkl.gstreamer.demo;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.freedesktop.gstreamer.GStreamer;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+public class MainActivity extends AppCompatActivity {
+    //c 调用
+    private native void nativeInit(String serverUri);     // Initialize native code, build pipeline, etc
+    private native void nativeFinalize(); // Destroy pipeline and shutdown native code
+    private native void nativePlay();     // Set pipeline to PLAYING
+    private native void nativePause();    // Set pipeline to PAUSED
+    private static native boolean nativeClassInit(); // Initialize native class: cache Method IDs for callbacks
+    private long native_custom_data;      // Native code will use this to keep private data
+
+    //初始化 c 库
+    static {
+        //System.loadLibrary("gstreamer_android");
+        System.loadLibrary("wdkl-broadcast");
+        nativeClassInit();
+    }
+
+    private boolean is_playing_desired;   // Whether the user asked to go to PLAYING
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // 初始化 GStreamer
+        try {
+            GStreamer.init(this);
+        } catch (Exception e) {
+            Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
+            finish();
+            return;
+        }
+
+        setContentView(R.layout.activity_main);
+
+        Button play = (Button) this.findViewById(R.id.button_play);
+        play.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                is_playing_desired = true;
+                nativePlay();
+            }
+        });
+
+        Button pause = (Button) this.findViewById(R.id.button_stop);
+        pause.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                is_playing_desired = false;
+                nativePause();
+            }
+        });
+
+        if (savedInstanceState != null) {
+            is_playing_desired = savedInstanceState.getBoolean("playing");
+            Log.i ("wdkl-broadcast", "Activity created. Saved state is playing:" + is_playing_desired);
+        } else {
+            is_playing_desired = false;
+            Log.i ("wdkl-broadcast", "Activity created. There is no saved state, playing: false");
+        }
+
+        // Start with disabled buttons, until native code is initialized
+        this.findViewById(R.id.button_play).setEnabled(false);
+        this.findViewById(R.id.button_stop).setEnabled(false);
+
+        //指定服务器 IP 和端口
+        nativeInit("host=192.168.1.55 port=5004");
+    }
+
+    protected void onSaveInstanceState (Bundle outState) {
+        super.onSaveInstanceState(outState);
+        Log.d("wdkl-broadcast", "Saving state, playing:" + is_playing_desired);
+        outState.putBoolean("playing", is_playing_desired);
+    }
+
+    protected void onDestroy() {
+        nativeFinalize();
+        super.onDestroy();
+    }
+
+    // 必须实现此方法。给 c 调用,用于输出状态
+    private void setMessage(final String message) {
+        final TextView tv = (TextView) this.findViewById(R.id.textview_message);
+        runOnUiThread (new Runnable() {
+            public void run() {
+                tv.setText(message);
+            }
+        });
+    }
+
+    // 必须实现此方法。给 c 调用,用于判断是否加载完成,才能执行命令
+    private void onGStreamerInitialized () {
+        Log.i ("wdkl-broadcast", "广播初始化完成。 playing:" + is_playing_desired);
+        // Restore previous playing state
+        if (is_playing_desired) {
+            nativePlay();
+        } else {
+            nativePause();
+        }
+
+        // Re-enable buttons, now that GStreamer is initialized
+        final Activity activity = this;
+        runOnUiThread(new Runnable() {
+            public void run() {
+                activity.findViewById(R.id.button_play).setEnabled(true);
+                activity.findViewById(R.id.button_stop).setEnabled(true);
+            }
+        });
+    }
+}

+ 54 - 0
app/src/main/java/com/wdkl/gstreamer/demo/MyGStreamManager.java

@@ -0,0 +1,54 @@
+package com.wdkl.gstreamer.demo;
+
+public class MyGStreamManager extends MyGstream {
+    private static MyGStreamManager sInstance = null;
+    private StreamerCallBack callBack = null;
+
+    public static MyGStreamManager getInstance() {
+        if (sInstance == null) {
+            synchronized (MyGStreamManager.class) {
+                if (sInstance == null) {
+                    sInstance = new MyGStreamManager();
+                }
+            }
+        }
+        return sInstance;
+    }
+
+    public void setCallBack(StreamerCallBack callBack) {
+        this.callBack = callBack;
+    }
+
+    public void init(String serverUri) {
+        nativeInit(serverUri);
+    }
+
+    public void release() {
+        nativeFinalize();
+    }
+
+    public void play() {
+        nativePlay();
+    }
+
+    public void pause() {
+        nativePause();
+    }
+
+    protected void setMessage(String message) {
+        if (callBack != null) {
+            callBack.onMessage(message);
+        }
+    }
+
+    protected void onGStreamerInitialized () {
+        if (callBack != null) {
+            callBack.onStreamerInitialized();
+        }
+    }
+
+    public interface StreamerCallBack {
+        void onMessage(final String message);
+        void onStreamerInitialized();
+    }
+}

+ 34 - 0
app/src/main/java/com/wdkl/gstreamer/demo/MyGstream.java

@@ -0,0 +1,34 @@
+package com.wdkl.gstreamer.demo;
+
+import android.util.Log;
+
+public class MyGstream {
+
+    //c 调用
+    protected native void nativeInit(String serverUri);     // Initialize native code, build pipeline, etc
+    protected native void nativeFinalize(); // Destroy pipeline and shutdown native code
+    protected native void nativePlay();     // Set pipeline to PLAYING
+    protected native void nativePause();    // Set pipeline to PAUSED
+    protected static native boolean nativeClassInit(); // Initialize native class: cache Method IDs for callbacks
+
+    protected long native_custom_data;      // Native code will use this to keep private data
+
+
+    // 必须实现此方法。给 c 调用,用于输出状态
+    protected void setMessage(final String message) {
+        Log.d("Streamer", "onMessage: " + message);
+    }
+
+    // 必须实现此方法。给 c 调用,用于判断是否加载完成,才能执行命令
+    protected void onGStreamerInitialized () {
+        Log.d("Streamer", "onGStreamerInitialized");
+    }
+
+
+    //初始化 c 库
+    static {
+        //System.loadLibrary("gstreamer_android");
+        System.loadLibrary("wdkl-broadcast");
+        nativeClassInit();
+    }
+}

+ 104 - 0
app/src/main/java/org/freedesktop/gstreamer/GStreamer.java

@@ -0,0 +1,104 @@
+package org.freedesktop.gstreamer;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+
+public class GStreamer {
+    private static native void nativeInit(Context context) throws Exception;
+
+    //初始化 c 库
+    static {
+        System.loadLibrary("gstreamer_android");
+    }
+
+    public static void init(Context context) throws Exception {
+        copyCaCertificates(context);
+        copyFonts(context);
+        nativeInit(context);
+    }
+
+    private static void copyFonts(Context context) {
+        AssetManager assetManager = context.getAssets();
+        File filesDir = context.getFilesDir();
+        File fontsFCDir = new File (filesDir, "fontconfig");
+        File fontsDir = new File (fontsFCDir, "fonts");
+        File fontsCfg = new File (fontsFCDir, "fonts.conf");
+
+        fontsDir.mkdirs();
+
+        try {
+            /* Copy the config file */
+            copyFile (assetManager, "fontconfig/fonts.conf", fontsCfg);
+            /* Copy the fonts */
+            for(String filename : assetManager.list("fontconfig/fonts/truetype")) {
+                File font = new File(fontsDir, filename);
+                copyFile (assetManager, "fontconfig/fonts/truetype/" + filename, font);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void copyCaCertificates(Context context) {
+        AssetManager assetManager = context.getAssets();
+        File filesDir = context.getFilesDir();
+        File sslDir = new File (filesDir, "ssl");
+        File certsDir = new File (sslDir, "certs");
+        File certs = new File (certsDir, "ca-certificates.crt");
+
+        certsDir.mkdirs();
+
+        try {
+            /* Copy the certificates file */
+            copyFile (assetManager, "ssl/certs/ca-certificates.crt", certs);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void copyFile(AssetManager assetManager, String assetPath, File outFile) throws IOException {
+        InputStream in = null;
+        OutputStream out = null;
+        IOException exception = null;
+
+        if (outFile.exists())
+            outFile.delete();
+
+        try {
+            in = assetManager.open(assetPath);
+            out = new FileOutputStream(outFile);
+
+            byte[] buffer = new byte[1024];
+            int read;
+            while ((read = in.read(buffer)) != -1) {
+                out.write(buffer, 0, read);
+            }
+            out.flush();
+        } catch (IOException e) {
+            exception = e;
+        } finally {
+            if (in != null)
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    if (exception == null)
+                        exception = e;
+                }
+            if (out != null)
+                try {
+                    out.close();
+                } catch (IOException e) {
+                    if (exception == null)
+                        exception = e;
+                }
+            if (exception != null)
+                throw exception;
+        }
+    }
+}

+ 60 - 0
app/src/main/java/org/freedesktop/gstreamer/androidmedia/GstAhcCallback.java

@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012, Collabora Ltd.
+ *   Author: Youness Alaoui
+ *
+ * Copyright (C) 2015, Collabora Ltd.
+ *   Author: Justin Kim <justin.kim@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+package org.freedesktop.gstreamer.androidmedia;
+
+import android.hardware.Camera;
+
+public class GstAhcCallback implements Camera.PreviewCallback,
+                                       Camera.ErrorCallback,
+                                       Camera.AutoFocusCallback {
+    public long mUserData;
+    public long mCallback;
+
+    public static native void gst_ah_camera_on_preview_frame(byte[] data, Camera camera,
+                                                             long callback, long user_data);
+    public static native void gst_ah_camera_on_error(int error, Camera camera,
+                                                     long callback, long user_data);
+    public static native void gst_ah_camera_on_auto_focus(boolean success, Camera camera,
+                                                             long callback, long user_data);
+
+    public GstAhcCallback(long callback, long user_data) {
+        mCallback = callback;
+        mUserData = user_data;
+    }
+
+    @Override
+    public void onPreviewFrame(byte[] data, Camera camera) {
+        gst_ah_camera_on_preview_frame(data, camera, mCallback, mUserData);
+    }
+
+    @Override
+    public void onError(int error, Camera camera) {
+        gst_ah_camera_on_error(error, camera, mCallback, mUserData);
+    }
+
+    @Override
+    public void onAutoFocus(boolean success, Camera camera) {
+        gst_ah_camera_on_auto_focus(success, camera, mCallback, mUserData);
+    }
+}

+ 54 - 0
app/src/main/java/org/freedesktop/gstreamer/androidmedia/GstAhsCallback.java

@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 SurroundIO
+ *   Author: Martin Kelly <martin@surround.io>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+package org.freedesktop.gstreamer.androidmedia;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+
+public class GstAhsCallback implements SensorEventListener {
+    public long mUserData;
+    public long mSensorCallback;
+    public long mAccuracyCallback;
+
+    public static native void gst_ah_sensor_on_sensor_changed(SensorEvent event,
+                                                              long callback, long user_data);
+    public static native void gst_ah_sensor_on_accuracy_changed(Sensor sensor, int accuracy,
+                                                                long callback, long user_data);
+
+    public GstAhsCallback(long sensor_callback,
+        long accuracy_callback, long user_data) {
+        mSensorCallback = sensor_callback;
+        mAccuracyCallback = accuracy_callback;
+        mUserData = user_data;
+    }
+
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+      gst_ah_sensor_on_sensor_changed(event, mSensorCallback, mUserData);
+    }
+
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+      gst_ah_sensor_on_accuracy_changed(sensor, accuracy,
+          mAccuracyCallback, mUserData);
+    }
+}

+ 43 - 0
app/src/main/java/org/freedesktop/gstreamer/androidmedia/GstAmcOnFrameAvailableListener.java

@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015, Collabora Ltd.
+ *   Author: Matthieu Bouron <matthieu.bouron@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+package org.freedesktop.gstreamer.androidmedia;
+
+import android.graphics.SurfaceTexture;
+import android.graphics.SurfaceTexture.OnFrameAvailableListener;
+
+public class GstAmcOnFrameAvailableListener implements OnFrameAvailableListener
+{
+    private long context = 0;
+
+    public synchronized void onFrameAvailable (SurfaceTexture surfaceTexture) {
+        native_onFrameAvailable(context, surfaceTexture);
+    }
+
+    public synchronized long getContext () {
+        return context;
+    }
+
+    public synchronized void setContext (long c) {
+        context = c;
+    }
+
+    private native void native_onFrameAvailable (long context, SurfaceTexture surfaceTexture);
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 30 - 0
app/src/main/res/drawable-v24/ic_launcher_foreground.xml


+ 170 - 0
app/src/main/res/drawable/ic_launcher_background.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillColor="#3DDC84"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+</vector>

+ 36 - 0
app/src/main/res/layout/activity_main.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity">
+
+    <TextView
+        android:id="@+id/textview_message"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Hello World!"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <Button
+        android:id="@+id/button_play"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toBottomOf="@id/textview_message"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        android:text="play"/>
+    <Button
+        android:id="@+id/button_stop"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toBottomOf="@id/button_play"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        android:text="pause"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 5 - 0
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>

+ 5 - 0
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>

BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png


BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png


+ 16 - 0
app/src/main/res/values-night/themes.xml

@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.Gstreamerdemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_200</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/black</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_200</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
+        <!-- Customize your theme here. -->
+    </style>
+</resources>

+ 10 - 0
app/src/main/res/values/colors.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="purple_200">#FFBB86FC</color>
+    <color name="purple_500">#FF6200EE</color>
+    <color name="purple_700">#FF3700B3</color>
+    <color name="teal_200">#FF03DAC5</color>
+    <color name="teal_700">#FF018786</color>
+    <color name="black">#FF000000</color>
+    <color name="white">#FFFFFFFF</color>
+</resources>

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">维鼎康联广播demo</string>
+</resources>

+ 16 - 0
app/src/main/res/values/themes.xml

@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.Gstreamerdemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_500</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/white</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_700</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
+        <!-- Customize your theme here. -->
+    </style>
+</resources>

+ 17 - 0
app/src/test/java/com/wdkl/gstreamer/demo/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.wdkl.gstreamer.demo;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}

+ 24 - 0
build.gradle

@@ -0,0 +1,24 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+    repositories {
+        google()
+        jcenter()
+    }
+    dependencies {
+        classpath "com.android.tools.build:gradle:3.1.4"
+
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}

+ 22 - 0
gradle.properties

@@ -0,0 +1,22 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+
+# 必须设置
+gstAndroidRoot=/Users/allen/LocalDoc/android/gstreamer_demo/resource/gstreamer-1.0-android-universal-1.20.3

BIN
gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Fri Jun 24 14:07:35 CST 2022
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

+ 172 - 0
gradlew

@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"

+ 84 - 0
gradlew.bat

@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 12 - 0
local.properties

@@ -0,0 +1,12 @@
+## This file is automatically generated by Android Studio.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file should *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+sdk.dir=/Users/allen/Library/Android/sdk
+# 必须设置,使用 NDK 编译
+ndk.dir=/Users/allen/Library/Android/sdk/ndk/20.0.5594570

+ 17 - 0
readme.md

@@ -0,0 +1,17 @@
+### 必须设置
+- gradle.properties 文件中
+```
+gstAndroidRoot=/Users/allen/Downloads/gstreamer-1.0-android-universal-1.20.3
+```
+此资源放置于 resource 文件夹中
+- local.properties 文件中
+```
+ndk.dir=/Users/allen/Library/Android/sdk/ndk/20.0.5594570
+```
+- app/jni/wdkl-broadcast.c
+340行,设置要实现的包名和类名
+
+- ap/src/main/java
+其中 assets 和 org.freedesktop.gstreamer 两个包为自动生成
+
+- 权限手动给予

BIN
release/armeabi-v7a/libc++_shared.so


BIN
release/armeabi-v7a/libgstreamer_android.so


BIN
release/armeabi-v7a/libwdkl-broadcast.so


+ 2 - 0
settings.gradle

@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "gstreamer.demo"