Split out documentation into subfolders.
[platform/upstream/gstreamer.git] / examples / tutorials / xcode iOS / Tutorial 3 / GStreamerBackend.m
1 #import "GStreamerBackend.h"
2
3 #include <gst/gst.h>
4 #include <gst/video/video.h>
5
6 GST_DEBUG_CATEGORY_STATIC (debug_category);
7 #define GST_CAT_DEFAULT debug_category
8
9 @interface GStreamerBackend()
10 -(void)setUIMessage:(gchar*) message;
11 -(void)app_function;
12 -(void)check_initialization_complete;
13 @end
14
15 @implementation GStreamerBackend {
16     id ui_delegate;        /* Class that we use to interact with the user interface */
17     GstElement *pipeline;  /* The running pipeline */
18     GstElement *video_sink;/* The video sink element which receives XOverlay commands */
19     GMainContext *context; /* GLib context used to run the main loop */
20     GMainLoop *main_loop;  /* GLib main loop */
21     gboolean initialized;  /* To avoid informing the UI multiple times about the initialization */
22     UIView *ui_video_view; /* UIView that holds the video */
23 }
24
25 /*
26  * Interface methods
27  */
28
29 -(id) init:(id) uiDelegate videoView:(UIView *)video_view
30 {
31     if (self = [super init])
32     {
33         self->ui_delegate = uiDelegate;
34         self->ui_video_view = video_view;
35
36         GST_DEBUG_CATEGORY_INIT (debug_category, "tutorial-3", 0, "iOS tutorial 3");
37         gst_debug_set_threshold_for_name("tutorial-3", GST_LEVEL_DEBUG);
38
39         /* Start the bus monitoring task */
40         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
41             [self app_function];
42         });
43     }
44
45     return self;
46 }
47
48 -(void) dealloc
49 {
50     if (pipeline) {
51         GST_DEBUG("Setting the pipeline to NULL");
52         gst_element_set_state(pipeline, GST_STATE_NULL);
53         gst_object_unref(pipeline);
54         pipeline = NULL;
55     }
56 }
57
58 -(void) play
59 {
60     if(gst_element_set_state(pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
61         [self setUIMessage:"Failed to set pipeline to playing"];
62     }
63 }
64
65 -(void) pause
66 {
67     if(gst_element_set_state(pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
68         [self setUIMessage:"Failed to set pipeline to paused"];
69     }
70 }
71
72 /*
73  * Private methods
74  */
75
76 /* Change the message on the UI through the UI delegate */
77 -(void)setUIMessage:(gchar*) message
78 {
79     NSString *string = [NSString stringWithUTF8String:message];
80     if(ui_delegate && [ui_delegate respondsToSelector:@selector(gstreamerSetUIMessage:)])
81     {
82         [ui_delegate gstreamerSetUIMessage:string];
83     }
84 }
85
86 /* Retrieve errors from the bus and show them on the UI */
87 static void error_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *self)
88 {
89     GError *err;
90     gchar *debug_info;
91     gchar *message_string;
92
93     gst_message_parse_error (msg, &err, &debug_info);
94     message_string = g_strdup_printf ("Error received from element %s: %s", GST_OBJECT_NAME (msg->src), err->message);
95     g_clear_error (&err);
96     g_free (debug_info);
97     [self setUIMessage:message_string];
98     g_free (message_string);
99     gst_element_set_state (self->pipeline, GST_STATE_NULL);
100 }
101
102 /* Notify UI about pipeline state changes */
103 static void state_changed_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *self)
104 {
105     GstState old_state, new_state, pending_state;
106     gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
107     /* Only pay attention to messages coming from the pipeline, not its children */
108     if (GST_MESSAGE_SRC (msg) == GST_OBJECT (self->pipeline)) {
109         gchar *message = g_strdup_printf("State changed to %s", gst_element_state_get_name(new_state));
110         [self setUIMessage:message];
111         g_free (message);
112     }
113 }
114
115 /* Check if all conditions are met to report GStreamer as initialized.
116  * These conditions will change depending on the application */
117 -(void) check_initialization_complete
118 {
119     if (!initialized && main_loop) {
120         GST_DEBUG ("Initialization complete, notifying application.");
121         if (ui_delegate && [ui_delegate respondsToSelector:@selector(gstreamerInitialized)])
122         {
123             [ui_delegate gstreamerInitialized];
124         }
125         initialized = TRUE;
126     }
127 }
128
129 /* Main method for the bus monitoring code */
130 -(void) app_function
131 {
132     GstBus *bus;
133     GSource *bus_source;
134     GError *error = NULL;
135
136     GST_DEBUG ("Creating pipeline");
137
138     /* Create our own GLib Main Context and make it the default one */
139     context = g_main_context_new ();
140     g_main_context_push_thread_default(context);
141
142     /* Build pipeline */
143     pipeline = gst_parse_launch("videotestsrc ! warptv ! videoconvert ! autovideosink", &error);
144     if (error) {
145         gchar *message = g_strdup_printf("Unable to build pipeline: %s", error->message);
146         g_clear_error (&error);
147         [self setUIMessage:message];
148         g_free (message);
149         return;
150     }
151
152     /* Set the pipeline to READY, so it can already accept a window handle */
153     gst_element_set_state(pipeline, GST_STATE_READY);
154
155     video_sink = gst_bin_get_by_interface(GST_BIN(pipeline), GST_TYPE_VIDEO_OVERLAY);
156     if (!video_sink) {
157         GST_ERROR ("Could not retrieve video sink");
158         return;
159     }
160     gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(video_sink), (guintptr) (id) ui_video_view);
161
162     /* Instruct the bus to emit signals for each received message, and connect to the interesting signals */
163     bus = gst_element_get_bus (pipeline);
164     bus_source = gst_bus_create_watch (bus);
165     g_source_set_callback (bus_source, (GSourceFunc) gst_bus_async_signal_func, NULL, NULL);
166     g_source_attach (bus_source, context);
167     g_source_unref (bus_source);
168     g_signal_connect (G_OBJECT (bus), "message::error", (GCallback)error_cb, (__bridge void *)self);
169     g_signal_connect (G_OBJECT (bus), "message::state-changed", (GCallback)state_changed_cb, (__bridge void *)self);
170     gst_object_unref (bus);
171
172     /* Create a GLib Main Loop and set it to run */
173     GST_DEBUG ("Entering main loop...");
174     main_loop = g_main_loop_new (context, FALSE);
175     [self check_initialization_complete];
176     g_main_loop_run (main_loop);
177     GST_DEBUG ("Exited main loop");
178     g_main_loop_unref (main_loop);
179     main_loop = NULL;
180
181     /* Free resources */
182     g_main_context_pop_thread_default(context);
183     g_main_context_unref (context);
184     gst_element_set_state (pipeline, GST_STATE_NULL);
185     gst_object_unref (pipeline);
186
187     return;
188 }
189
190 @end