From 0dca3ce9e86f10216ef0903d969218ccf849e302 Mon Sep 17 00:00:00 2001 From: Raymond Yang Date: Thu, 9 Feb 2023 16:16:49 +0800 Subject: [PATCH] =?UTF-8?q?C=20lib=E5=8A=A0=E4=B8=8Atag=E5=8D=80=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ray650128/gstreamer_demo_app/Constants.kt | 2 +- .../ui/mainScreen/MainActivity.kt | 10 ++-- .../ui/mainScreen/MainViewModel.kt | 18 +++--- .../ui/mainScreen/VideoView.kt | 29 ++-------- gstreamer_player/jni/gst_player.c | 58 +++++++++++++------ .../hisharp/gstreamer_player/GstLibrary.java | 34 ++++++++++- 6 files changed, 91 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/com/ray650128/gstreamer_demo_app/Constants.kt b/app/src/main/java/com/ray650128/gstreamer_demo_app/Constants.kt index de162e4..0568cce 100644 --- a/app/src/main/java/com/ray650128/gstreamer_demo_app/Constants.kt +++ b/app/src/main/java/com/ray650128/gstreamer_demo_app/Constants.kt @@ -1,5 +1,5 @@ package com.ray650128.gstreamer_demo_app object Constants { - const val CONF_DELAY_BASE_MILLIS = 500L + const val CONF_DELAY_BASE_MILLIS = 1000L } \ No newline at end of file diff --git a/app/src/main/java/com/ray650128/gstreamer_demo_app/ui/mainScreen/MainActivity.kt b/app/src/main/java/com/ray650128/gstreamer_demo_app/ui/mainScreen/MainActivity.kt index 87bc5ab..e08d11e 100644 --- a/app/src/main/java/com/ray650128/gstreamer_demo_app/ui/mainScreen/MainActivity.kt +++ b/app/src/main/java/com/ray650128/gstreamer_demo_app/ui/mainScreen/MainActivity.kt @@ -76,15 +76,15 @@ class MainActivity : AppCompatActivity() { if (state == ViewPager2.SCROLL_STATE_IDLE && currentPage == oldPage) { splitVideoViewAdapter.play(currentPage) } - Log.d("Split", "onPageScrollStateChanged: ${ + /*Log.d("Split", "onPageScrollStateChanged: ${ when(state) { ViewPager2.SCROLL_STATE_IDLE -> "SCROLL_STATE_IDLE" ViewPager2.SCROLL_STATE_DRAGGING -> "SCROLL_STATE_DRAGGING" ViewPager2.SCROLL_STATE_SETTLING -> "SCROLL_STATE_SETTLING" else -> "" } - }") - Log.d("Split", "oldPage: $oldPage, currentPage: $currentPage") + }")*/ + //Log.d("Split", "oldPage: $oldPage, currentPage: $currentPage") } override fun onPageScrolled( position: Int, @@ -92,13 +92,13 @@ class MainActivity : AppCompatActivity() { positionOffsetPixels: Int ) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) - Log.d("Split", "onPageScrolled: $currentPage") + //Log.d("Split", "onPageScrolled: $currentPage") } override fun onPageSelected(position: Int) { super.onPageSelected(position) currentPage = position splitViewModel.activePage.postValue(position) - Log.d("Split", "currentPage: $currentPage") + //Log.d("Split", "currentPage: $currentPage") } }) } diff --git a/app/src/main/java/com/ray650128/gstreamer_demo_app/ui/mainScreen/MainViewModel.kt b/app/src/main/java/com/ray650128/gstreamer_demo_app/ui/mainScreen/MainViewModel.kt index afe07c6..76faf93 100644 --- a/app/src/main/java/com/ray650128/gstreamer_demo_app/ui/mainScreen/MainViewModel.kt +++ b/app/src/main/java/com/ray650128/gstreamer_demo_app/ui/mainScreen/MainViewModel.kt @@ -10,15 +10,6 @@ class MainViewModel: ViewModel() { private val uriList: List by lazy { listOf( - Device( - deviceName = "192.168.0.73", - ip = "192.168.0.73", - rtspPort = "554", - account = "admin", - password = "hs22601576", - stream1 = "/media/video1", - stream2 = "/media/video2", - ), Device( deviceName = "192.168.0.77", ip = "192.168.0.77", @@ -28,6 +19,15 @@ class MainViewModel: ViewModel() { stream1 = "/media/video1", stream2 = "/media/video2", ), + Device( + deviceName = "192.168.0.73", + ip = "192.168.0.73", + rtspPort = "554", + account = "admin", + password = "hs22601576", + stream1 = "/media/video1", + stream2 = "/media/video2", + ), Device( deviceName = "192.168.0.79", ip = "192.168.0.79", diff --git a/app/src/main/java/com/ray650128/gstreamer_demo_app/ui/mainScreen/VideoView.kt b/app/src/main/java/com/ray650128/gstreamer_demo_app/ui/mainScreen/VideoView.kt index 124493b..d89ee66 100644 --- a/app/src/main/java/com/ray650128/gstreamer_demo_app/ui/mainScreen/VideoView.kt +++ b/app/src/main/java/com/ray650128/gstreamer_demo_app/ui/mainScreen/VideoView.kt @@ -18,7 +18,7 @@ import com.ray650128.gstreamer_demo_app.databinding.ItemVideoViewBinding import com.ray650128.gstreamer_demo_app.extensions.getStreamPath import com.ray650128.gstreamer_demo_app.model.Device -class VideoView : ConstraintLayout, SurfaceHolder.Callback, GstCallback { +class VideoView : ConstraintLayout, GstCallback { constructor(context: Context) : super(context) { initView(context) @@ -69,8 +69,9 @@ class VideoView : ConstraintLayout, SurfaceHolder.Callback, GstCallback { view = ItemVideoViewBinding.inflate(layoutInflater, this, true) view.baseView.clipToOutline = true - videoView.holder.addCallback(this) + //videoView.holder.addCallback(this) gstLibrary = GstLibrary(context) + gstLibrary.setSurfaceHolder(videoView.holder) gstLibrary.setOnStatusChangeListener(this) // View 預設狀態 @@ -91,6 +92,7 @@ class VideoView : ConstraintLayout, SurfaceHolder.Callback, GstCallback { view.textDeviceName.text = device.deviceName view.textDeviceName.isVisible = true val rtspUrl = this.data?.getStreamPath(streamType) ?: return // 如果 null 就不指派給 Gstreamer 了 + gstLibrary.setTag(this.data!!.deviceName) gstLibrary.setRtspUrl(rtspUrl) Log.d("${TAG}_$tag", "Set device to: $device, rtspUrl = $rtspUrl") } @@ -100,11 +102,13 @@ class VideoView : ConstraintLayout, SurfaceHolder.Callback, GstCallback { } fun play() { + if (data == null) return videoView.postInvalidate() gstLibrary.play() } fun stop() { + if (data == null) return isPlaying = false if (this::gstLibrary.isInitialized) { gstLibrary.stop() @@ -126,27 +130,6 @@ class VideoView : ConstraintLayout, SurfaceHolder.Callback, GstCallback { mHandler.removeCallbacks(retryRunnable) } - override fun surfaceCreated(holder: SurfaceHolder) { - Log.d("${TAG}_$tag", "Surface created: " + holder.surface) - if (this::gstLibrary.isInitialized) { - gstLibrary.setSurfaceHolder(holder) - } - } - - override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { - Log.d("${TAG}_$tag", "Surface changed, format: $format, width: $width, height: $height") - /*if (this::gstLibrary.isInitialized) { - gstLibrary.setSurfaceHolder(holder) - }*/ - } - - override fun surfaceDestroyed(p0: SurfaceHolder) { - Log.d("${TAG}_$tag", "Surface destroyed") - if (this::gstLibrary.isInitialized) { - gstLibrary.releaseSurface() - } - } - override fun onStatus(gstStatus: GstStatus?) { // onStatus 不是在主執行緒,因此透過 Handler 發訊息到主執行緒去執行 when (gstStatus) { GstStatus.PLAYING -> mHandler.sendMessage(Message().apply { what = MSG_PLAY }) diff --git a/gstreamer_player/jni/gst_player.c b/gstreamer_player/jni/gst_player.c index e8398a6..e41a009 100644 --- a/gstreamer_player/jni/gst_player.c +++ b/gstreamer_player/jni/gst_player.c @@ -35,6 +35,7 @@ typedef struct _CustomData { GstState state; /* Current pipeline state */ GstState target_state; /* Desired pipeline state, to be set once buffering is complete */ gboolean is_live; /* Live streams do not use buffering */ + gchar * tag; } CustomData; /* playbin2 flags */ @@ -94,7 +95,7 @@ static JNIEnv *get_jni_env(void) { /* 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); + //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)) { @@ -125,8 +126,9 @@ static void error_cb(GstBus *bus, GstMessage *msg, CustomData *data) { /* Called when the End Of the Stream is reached. Just move to the beginning of the media and pause. */ static void eos_cb(GstBus *bus, GstMessage *msg, CustomData *data) { data->target_state = GST_STATE_PAUSED; - data->is_live |= (gst_element_set_state(data->pipeline, - GST_STATE_PAUSED) == GST_STATE_CHANGE_NO_PREROLL); + GstStateChangeReturn status = gst_element_set_state(data->pipeline,GST_STATE_PAUSED); + data->is_live |= (status == GST_STATE_CHANGE_NO_PREROLL); + GST_DEBUG ("eos_cb, %s", data->is_live ? "true" : "false"); } /* Called when buffering messages are received. We inform the UI about the current buffering level and @@ -321,16 +323,16 @@ 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..."); + GST_DEBUG ("[%s]Quitting main loop...", data->tag); g_main_loop_quit(data->main_loop); - GST_DEBUG ("Waiting for thread to finish..."); + GST_DEBUG ("[%s]Waiting for thread to finish...", data->tag); pthread_join(gst_app_thread, NULL); - GST_DEBUG ("Deleting GlobalRef for app object at %p", data->app); + GST_DEBUG ("[%s]Deleting GlobalRef for app object at %p", data->tag, data->app); (*env)->DeleteGlobalRef(env, data->app); - GST_DEBUG ("Freeing CustomData at %p", data); + GST_DEBUG ("[%s]Freeing CustomData at %p", data->tag, data); g_free(data); SET_CUSTOM_DATA (env, thiz, custom_data_field_id, NULL); - GST_DEBUG ("Done finalizing"); + GST_DEBUG ("[%s]Done finalizing", data->tag); } /* Set playbin2's URI */ @@ -345,7 +347,21 @@ void gst_native_set_uri(JNIEnv *env, jobject thiz, jstring uri) { g_object_set(data->pipeline, "uri", char_uri, NULL); (*env)->ReleaseStringUTFChars(env, uri, char_uri); g_object_set(data->pipeline, "latency", 250, NULL); - data->is_live = (gst_element_set_state(data->pipeline, data->target_state) == GST_STATE_CHANGE_NO_PREROLL); + GstStateChangeReturn status = gst_element_set_state(data->pipeline, data->target_state); + data->is_live = (status == GST_STATE_CHANGE_NO_PREROLL); + GST_DEBUG ("[%s]gst_native_set_uri, %s", data->tag, data->is_live ? "true" : "false"); +} + + +/* Set name */ +void gst_native_set_tag(JNIEnv *env, jobject thiz, jstring tag) { + CustomData *data = GET_CUSTOM_DATA (env, thiz, custom_data_field_id); + if (!data || !data->pipeline) + return; + const gchar *char_tag = (*env)->GetStringUTFChars(env, tag, NULL); + GST_DEBUG ("Setting TAG to %s", char_tag); + data->tag = g_strdup (char_tag); + (*env)->ReleaseStringUTFChars(env, tag, char_tag); } /* Set pipeline to PLAYING state */ @@ -353,9 +369,12 @@ 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"); - data->is_live = (gst_element_set_state(data->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_NO_PREROLL); + //GST_DEBUG ("Setting state to PLAYING"); + + GstStateChangeReturn status = gst_element_set_state(data->pipeline, GST_STATE_PLAYING); + data->is_live = (status == GST_STATE_CHANGE_ASYNC); data->target_state = GST_STATE_PLAYING; + GST_DEBUG ("[%s]Setting state to PLAYING, %s", data->tag, data->is_live ? "true" : "false"); } /* Set pipeline to PAUSED state */ @@ -363,9 +382,11 @@ 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"); - data->is_live = (gst_element_set_state(data->pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_NO_PREROLL); + //GST_DEBUG ("Setting state to PAUSED"); + GstStateChangeReturn status = gst_element_set_state(data->pipeline, GST_STATE_PAUSED); + data->is_live = (status == GST_STATE_CHANGE_ASYNC); data->target_state = GST_STATE_PAUSED; + GST_DEBUG ("[%s]Setting state to PAUSED, %s", data->tag, data->is_live ? "true" : "false"); } /* Static class initializer: retrieve method and field IDs */ @@ -392,20 +413,18 @@ static void gst_native_surface_init(JNIEnv *env, jobject thiz, jobject surface) if (!data) return; ANativeWindow *new_native_window = ANativeWindow_fromSurface(env, surface); - GST_DEBUG ("Received surface %p (native window %p)", surface, - new_native_window); + GST_DEBUG ("[%s]Received surface %p (native window %p)", data->tag, surface, new_native_window); if (data->native_window) { ANativeWindow_release(data->native_window); if (data->native_window == new_native_window) { - GST_DEBUG ("New native window is the same as the previous one %p", - data->native_window); + GST_DEBUG ("[%s]New native window is the same as the previous one %p", data->tag, data->native_window); if (data->pipeline) { gst_video_overlay_expose(GST_VIDEO_OVERLAY (data->pipeline)); } return; } else { - GST_DEBUG ("Released previous native window %p", data->native_window); + GST_DEBUG ("[%s]Released previous native window %p", data->tag, data->native_window); data->initialized = FALSE; } } @@ -418,7 +437,7 @@ static void gst_native_surface_finalize(JNIEnv *env, jobject thiz) { CustomData *data = GET_CUSTOM_DATA (env, thiz, custom_data_field_id); if (!data) return; - GST_DEBUG ("Releasing Native Window %p", data->native_window); + GST_DEBUG ("[%s]Releasing Native Window %p", data->tag, data->native_window); if (data->pipeline) { gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY (data->pipeline), @@ -436,6 +455,7 @@ static JNINativeMethod native_methods[] = { {"nativeInit", "()V", (void *) gst_native_init}, {"nativeFinalize", "()V", (void *) gst_native_finalize}, {"nativeSetUri", "(Ljava/lang/String;)V", (void *) gst_native_set_uri}, + {"nativeSetTag", "(Ljava/lang/String;)V", (void *) gst_native_set_tag}, {"nativePlay", "()V", (void *) gst_native_play}, {"nativePause", "()V", (void *) gst_native_pause}, {"nativeSurfaceInit", "(Ljava/lang/Object;)V", diff --git a/gstreamer_player/src/com/hisharp/gstreamer_player/GstLibrary.java b/gstreamer_player/src/com/hisharp/gstreamer_player/GstLibrary.java index fd6d6be..540fb7e 100644 --- a/gstreamer_player/src/com/hisharp/gstreamer_player/GstLibrary.java +++ b/gstreamer_player/src/com/hisharp/gstreamer_player/GstLibrary.java @@ -13,7 +13,9 @@ import org.freedesktop.gstreamer.GStreamer; import java.io.Closeable; import java.io.IOException; -public class GstLibrary implements Closeable { +public class GstLibrary implements Closeable, SurfaceHolder.Callback { + + private static final String TAG = "GStreamer";//GstLibrary.class.getSimpleName(); final Context mAppContext; @@ -21,6 +23,8 @@ public class GstLibrary implements Closeable { private String rtspUrl; + private String tag = ""; + public GstLibrary(Context context) { this.mAppContext = context.getApplicationContext(); @@ -38,6 +42,7 @@ public class GstLibrary implements Closeable { private native void nativeInit(); // Initialize native code, build pipeline, etc private native void nativeFinalize(); // Destroy pipeline and shutdown native code private native void nativeSetUri(String uri); // Set the URI of the media to play + private native void nativeSetTag(String tag); // Set the URI of the media to play 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 @@ -59,6 +64,11 @@ public class GstLibrary implements Closeable { this.rtspUrl = rtspUrl; } + public void setTag(String tag) { + this.tag = tag; + nativeSetTag(tag); + } + public void releaseSurface() { nativeSurfaceFinalize(); } @@ -68,7 +78,7 @@ public class GstLibrary implements Closeable { } public void setSurfaceHolder(SurfaceHolder holder) { - nativeSurfaceInit(holder.getSurface()); + holder.addCallback(this); } // Called from native code. This sets the content of the TextView from the UI thread. @@ -108,7 +118,7 @@ public class GstLibrary implements Closeable { // Called from native code when the size of the media changes or is first detected. // Inform the video surface about the new size and recalculate the layout. private void onMediaSizeChanged (int width, int height) { - Log.i ("GStreamer", "Media size changed to " + width + "x" + height); + Log.i (TAG + "+" + tag, String.format("Media size changed to %d x %d", width, height)); /*mainHandler.post(() -> { surfaceView.media_width = width; surfaceView.media_height = height; @@ -130,4 +140,22 @@ public class GstLibrary implements Closeable { e.printStackTrace(); } } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + Log.d(TAG + "+" + tag, "Surface created: " + holder.getSurface()); + nativeSurfaceInit(holder.getSurface()); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.d(TAG + "+" + tag, String.format("Surface changed, format: %d, width: %d, height: %d", format, width, height)); + /*setSurfaceHolder(holder);*/ + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + Log.d(TAG + "+" + tag, "Surface destroyed"); + releaseSurface(); + } }