#import "GStreamerBackend.h" #include #include GST_DEBUG_CATEGORY_STATIC (debug_category); #define GST_CAT_DEFAULT debug_category @interface GStreamerBackend() -(void)setUIMessage:(gchar*) message; -(void)app_function:(gchar*) pipeline_string; -(void)check_initialization_complete; @end @implementation GStreamerBackend { /* id ui_delegate; /1* Class that we use to interact with the user interface *1/ */ GstElement *pipeline; /* The running pipeline */ GstElement *video_sink;/* The video sink element which receives XOverlay commands */ 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 */ UIView *ui_video_view; /* UIView that holds the video */ } -(id) init:(NSString*) pipeline videoView:(UIView*) video_view { if (self = [super init]) { /* self->ui_delegate = uiDelegate; */ self->ui_video_view = video_view; GST_DEBUG_CATEGORY_INIT (debug_category, "tutorial-3", 0, "iOS tutorial 3"); gst_debug_set_threshold_for_name("tutorial-3", GST_LEVEL_DEBUG); /* Start the bus monitoring task */ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self app_function: (gchar*)[pipeline UTF8String]]; }); } return self; } -(void) dealloc { if (pipeline) { NSLog(@"Setting the pipeline to NULL"); gst_element_set_state(pipeline, GST_STATE_NULL); gst_object_unref(pipeline); pipeline = NULL; } } /* * Private methods */ /* Change the message on the UI through the UI delegate */ -(void)setUIMessage:(gchar*) message { NSString *string = [NSString stringWithUTF8String:message]; /* if(ui_delegate && [ui_delegate respondsToSelector:@selector(gstreamerSetUIMessage:)]) */ /* { */ /* [ui_delegate gstreamerSetUIMessage:string]; */ /* } */ } /* Retrieve errors from the bus and show them on the UI */ static void error_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *self) { 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); [self setUIMessage:message_string]; g_free (message_string); gst_element_set_state (self->pipeline, GST_STATE_NULL); } /* Notify UI about pipeline state changes */ static void state_changed_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *self) { 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 (self->pipeline)) { gchar *message = g_strdup_printf("State changed to %s", gst_element_state_get_name(new_state)); [self setUIMessage:message]; g_free (message); } } /* Check if all conditions are met to report GStreamer as initialized. * These conditions will change depending on the application */ -(void) check_initialization_complete { if (!initialized && main_loop) { NSLog (@"Initialization complete, notifying application."); /* if (ui_delegate && [ui_delegate respondsToSelector:@selector(gstreamerInitialized)]) */ /* { */ /* [ui_delegate gstreamerInitialized]; */ /* } */ initialized = TRUE; NSLog(@"Initialization complete, notifying application."); if(gst_element_set_state(pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { [self setUIMessage:"Failed to set pipeline to playing"]; } } } /* Main method for the bus monitoring code */ -(void) app_function:(gchar*) pipeline_string { GstBus *bus; GSource *bus_source; GError *error = NULL; NSLog (@"Creating pipeline"); /* Create our own GLib Main Context and make it the default one */ context = g_main_context_new (); g_main_context_push_thread_default(context); /* Build pipeline */ /* pipeline = gst_parse_launch("videotestsrc ! warptv ! videoconvert ! autovideosink", &error); */ /* pipeline = gst_parse_launch("rtspsrc location= \ */ /* rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4 latency=0 ! \ */ /* decodebin ! \ */ /* autovideosink", &error); */ NSLog(@"Pipeline 1: %s", pipeline_string); NSLog(@"Pipeline 2: %s", pipeline_string); pipeline = gst_parse_launch(pipeline_string, &error); if (error) { NSLog (@"Unable to build pipeline: %s", error->message); gchar *message = g_strdup_printf("Unable to build pipeline: %s", error->message); g_clear_error (&error); [self setUIMessage:message]; g_free (message); return; } /* Set the pipeline to READY, so it can already accept a window handle */ gst_element_set_state(pipeline, GST_STATE_READY); video_sink = gst_bin_get_by_interface(GST_BIN(pipeline), GST_TYPE_VIDEO_OVERLAY); if (!video_sink) { NSLog (@"Could not retrieve video sink"); return; } gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(video_sink), (guintptr) (id) ui_video_view); /* Instruct the bus to emit signals for each received message, and connect to the interesting signals */ bus = gst_element_get_bus (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, context); g_source_unref (bus_source); g_signal_connect (G_OBJECT (bus), "message::error", (GCallback)error_cb, (__bridge void *)self); g_signal_connect (G_OBJECT (bus), "message::state-changed", (GCallback)state_changed_cb, (__bridge void *)self); gst_object_unref (bus); /* Create a GLib Main Loop and set it to run */ NSLog (@"Entering main loop..."); main_loop = g_main_loop_new (context, FALSE); [self check_initialization_complete]; g_main_loop_run (main_loop); NSLog (@"Exited main loop"); g_main_loop_unref (main_loop); main_loop = NULL; /* Free resources */ g_main_context_pop_thread_default(context); g_main_context_unref (context); gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (pipeline); return; } @end