8d4afaa0b189a5c25c0bf3ee01a05a071ac6e3e0
[platform/upstream/gstreamer.git] / ext / smoothstreaming / gstmssdemux.c
1 /* GStreamer
2  * Copyright (C) 2012 Smart TV Alliance
3  *  Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>, Collabora Ltd.
4  *
5  * gstmssdemux.c:
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /**
24  * SECTION:element-mssdemux
25  *
26  * Demuxes a Microsoft's Smooth Streaming manifest into its audio and/or video streams.
27  *
28  * TODO
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include "gst/gst-i18n-plugin.h"
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #include "gstmssdemux.h"
42
43 GST_DEBUG_CATEGORY (mssdemux_debug);
44
45 static GstStaticPadTemplate gst_mss_demux_sink_template =
46 GST_STATIC_PAD_TEMPLATE ("sink",
47     GST_PAD_SINK,
48     GST_PAD_ALWAYS,
49     GST_STATIC_CAPS ("application/vnd.ms-sstr+xml")
50     );
51
52 static GstStaticPadTemplate gst_mss_demux_videosrc_template =
53 GST_STATIC_PAD_TEMPLATE ("video_%02u",
54     GST_PAD_SRC,
55     GST_PAD_SOMETIMES,
56     GST_STATIC_CAPS_ANY);
57
58 static GstStaticPadTemplate gst_mss_demux_audiosrc_template =
59 GST_STATIC_PAD_TEMPLATE ("audio_%02u",
60     GST_PAD_SRC,
61     GST_PAD_SOMETIMES,
62     GST_STATIC_CAPS_ANY);
63
64 GST_BOILERPLATE (GstMssDemux, gst_mss_demux, GstMssDemux, GST_TYPE_ELEMENT);
65
66 static void gst_mss_demux_dispose (GObject * object);
67 static GstStateChangeReturn
68 gst_mss_demux_change_state (GstElement * element, GstStateChange transition);
69 static GstFlowReturn gst_mss_demux_chain (GstPad * pad, GstBuffer * buffer);
70 static GstFlowReturn gst_mss_demux_event (GstPad * pad, GstEvent * event);
71
72 static void gst_mss_demux_stream_loop (GstMssDemuxStream * stream);
73
74 static void gst_mss_demux_process_manifest (GstMssDemux * mssdemux);
75
76 static void
77 gst_mss_demux_base_init (gpointer klass)
78 {
79   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
80
81   gst_element_class_add_static_pad_template (element_class,
82       &gst_mss_demux_sink_template);
83   gst_element_class_add_static_pad_template (element_class,
84       &gst_mss_demux_videosrc_template);
85   gst_element_class_add_static_pad_template (element_class,
86       &gst_mss_demux_audiosrc_template);
87   gst_element_class_set_details_simple (element_class, "Smooth Streaming "
88       "demuxer", "Demuxer",
89       "Parse and demultiplex a Smooth Streaming manifest into audio and video "
90       "streams", "Thiago Santos <thiago.sousa.santos@collabora.com>");
91
92   GST_DEBUG_CATEGORY_INIT (mssdemux_debug, "mssdemux", 0, "mssdemux plugin");
93 }
94
95 static void
96 gst_mss_demux_class_init (GstMssDemuxClass * klass)
97 {
98   GObjectClass *gobject_class;
99   GstElementClass *gstelement_class;
100
101   gobject_class = (GObjectClass *) klass;
102   gstelement_class = (GstElementClass *) klass;
103
104   parent_class = g_type_class_peek_parent (klass);
105
106   gobject_class->dispose = gst_mss_demux_dispose;
107
108   gstelement_class->change_state =
109       GST_DEBUG_FUNCPTR (gst_mss_demux_change_state);
110 }
111
112 static void
113 gst_mss_demux_init (GstMssDemux * mssdemux, GstMssDemuxClass * klass)
114 {
115   mssdemux->sinkpad =
116       gst_pad_new_from_static_template (&gst_mss_demux_sink_template, "sink");
117   gst_pad_set_chain_function (mssdemux->sinkpad,
118       GST_DEBUG_FUNCPTR (gst_mss_demux_chain));
119   gst_pad_set_event_function (mssdemux->sinkpad,
120       GST_DEBUG_FUNCPTR (gst_mss_demux_event));
121   gst_element_add_pad (GST_ELEMENT_CAST (mssdemux), mssdemux->sinkpad);
122 }
123
124 static GstMssDemuxStream *
125 gst_mss_demux_stream_new (GstMssDemux * mssdemux,
126     GstMssManifestStream * manifeststream, GstPad * srcpad)
127 {
128   GstMssDemuxStream *stream;
129
130   stream = g_new0 (GstMssDemuxStream, 1);
131   stream->downloader = gst_uri_downloader_new ();
132
133   /* Streaming task */
134   g_static_rec_mutex_init (&stream->stream_lock);
135   stream->stream_task =
136       gst_task_create ((GstTaskFunction) gst_mss_demux_stream_loop, stream);
137   gst_task_set_lock (stream->stream_task, &stream->stream_lock);
138
139   stream->pad = srcpad;
140   stream->manifest_stream = manifeststream;
141   stream->parent = mssdemux;
142
143   return stream;
144 }
145
146 static void
147 gst_mss_demux_stream_free (GstMssDemuxStream * stream)
148 {
149   if (stream->stream_task) {
150     if (GST_TASK_STATE (stream->stream_task) != GST_TASK_STOPPED) {
151       GST_DEBUG_OBJECT (stream->parent, "Leaving streaming task %s:%s",
152           GST_DEBUG_PAD_NAME (stream->pad));
153       gst_task_stop (stream->stream_task);
154       g_static_rec_mutex_lock (&stream->stream_lock);
155       g_static_rec_mutex_unlock (&stream->stream_lock);
156       gst_task_join (stream->stream_task);
157     }
158     gst_object_unref (stream->stream_task);
159     g_static_rec_mutex_free (&stream->stream_lock);
160     stream->stream_task = NULL;
161   }
162
163   if (stream->downloader != NULL) {
164     g_object_unref (stream->downloader);
165     stream->downloader = NULL;
166   }
167   g_free (stream);
168 }
169
170 static void
171 gst_mss_demux_reset (GstMssDemux * mssdemux)
172 {
173   GSList *iter;
174   if (mssdemux->manifest_buffer) {
175     gst_buffer_unref (mssdemux->manifest_buffer);
176     mssdemux->manifest_buffer = NULL;
177   }
178
179   for (iter = mssdemux->streams; iter; iter = g_slist_next (iter)) {
180     GstMssDemuxStream *stream = iter->data;
181     gst_element_remove_pad (GST_ELEMENT_CAST (mssdemux), stream->pad);
182     gst_mss_demux_stream_free (stream);
183   }
184   g_slist_free (mssdemux->streams);
185   mssdemux->streams = NULL;
186
187   if (mssdemux->manifest) {
188     gst_mss_manifest_free (mssdemux->manifest);
189     mssdemux->manifest = NULL;
190   }
191
192   mssdemux->n_videos = mssdemux->n_audios = 0;
193 }
194
195 static void
196 gst_mss_demux_dispose (GObject * object)
197 {
198   /* GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (object); */
199
200   G_OBJECT_CLASS (parent_class)->dispose (object);
201 }
202
203 static GstStateChangeReturn
204 gst_mss_demux_change_state (GstElement * element, GstStateChange transition)
205 {
206   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (element);
207   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
208
209   switch (transition) {
210     case GST_STATE_CHANGE_PAUSED_TO_READY:
211       break;
212     case GST_STATE_CHANGE_READY_TO_NULL:
213       gst_mss_demux_reset (mssdemux);
214       break;
215     default:
216       break;
217   }
218
219   result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
220
221   switch (transition) {
222     case GST_STATE_CHANGE_PAUSED_TO_READY:{
223       break;
224     }
225     default:
226       break;
227   }
228
229   return result;
230 }
231
232 static GstFlowReturn
233 gst_mss_demux_chain (GstPad * pad, GstBuffer * buffer)
234 {
235   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (GST_PAD_PARENT (pad));
236   if (mssdemux->manifest_buffer == NULL)
237     mssdemux->manifest_buffer = buffer;
238   else
239     mssdemux->manifest_buffer =
240         gst_buffer_join (mssdemux->manifest_buffer, buffer);
241
242   return GST_FLOW_OK;
243 }
244
245 static gboolean
246 gst_mss_demux_event (GstPad * pad, GstEvent * event)
247 {
248   GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (GST_PAD_PARENT (pad));
249   gboolean forward = TRUE;
250   gboolean ret = TRUE;
251
252   switch (GST_EVENT_TYPE (event)) {
253     case GST_EVENT_EOS:
254       if (mssdemux->manifest_buffer == NULL) {
255         GST_WARNING_OBJECT (mssdemux, "Received EOS without a manifest.");
256         break;
257       }
258
259       gst_mss_demux_process_manifest (mssdemux);
260       forward = FALSE;
261       break;
262     default:
263       break;
264   }
265
266   if (forward) {
267     ret = gst_pad_event_default (pad, event);
268   } else {
269     gst_event_unref (event);
270   }
271
272   return ret;
273 }
274
275 static void
276 gst_mss_demux_create_streams (GstMssDemux * mssdemux)
277 {
278   GSList *streams = gst_mss_manifest_get_streams (mssdemux->manifest);
279   GSList *iter;
280
281   if (streams == NULL) {
282     GST_INFO_OBJECT (mssdemux, "No streams found in the manifest");
283     /* TODO  post eos? */
284   }
285
286   for (iter = streams; iter; iter = g_slist_next (iter)) {
287     gchar *name;
288     GstPad *srcpad = NULL;
289     GstMssDemuxStream *stream = NULL;
290     GstMssManifestStream *manifeststream = iter->data;
291     GstMssManifestStreamType streamtype;
292
293     streamtype = gst_mss_manifest_stream_get_type (manifeststream);
294     GST_DEBUG_OBJECT (mssdemux, "Found stream of type: %s",
295         gst_mss_manifest_stream_type_name (streamtype));
296
297     /* TODO use stream's name as the pad name? */
298     if (streamtype == MSS_STREAM_TYPE_VIDEO) {
299       name = g_strdup_printf ("video_%02u", mssdemux->n_videos++);
300       srcpad =
301           gst_pad_new_from_static_template (&gst_mss_demux_videosrc_template,
302           name);
303       g_free (name);
304     } else if (streamtype == MSS_STREAM_TYPE_AUDIO) {
305       name = g_strdup_printf ("audio_%02u", mssdemux->n_audios++);
306       srcpad =
307           gst_pad_new_from_static_template (&gst_mss_demux_audiosrc_template,
308           name);
309       g_free (name);
310     }
311
312     if (!srcpad) {
313       GST_WARNING_OBJECT (mssdemux, "Ignoring unknown type stream");
314       continue;
315     }
316
317     stream = gst_mss_demux_stream_new (mssdemux, manifeststream, srcpad);
318     mssdemux->streams = g_slist_append (mssdemux->streams, stream);
319   }
320 }
321
322 static void
323 gst_mss_demux_expose_stream (GstMssDemux * mssdemux, GstMssDemuxStream * stream)
324 {
325   GstCaps *caps;
326   GstPad *pad = stream->pad;
327
328   caps = gst_mss_manifest_stream_get_caps (stream->manifest_stream);
329
330   if (caps) {
331     gst_pad_set_caps (pad, caps);
332     gst_caps_unref (caps);
333
334     gst_pad_set_active (pad, TRUE);
335     GST_INFO_OBJECT (mssdemux, "Adding srcpad %s:%s with caps %" GST_PTR_FORMAT,
336         GST_DEBUG_PAD_NAME (pad), caps);
337     gst_element_add_pad (GST_ELEMENT_CAST (mssdemux), pad);
338   } else {
339     GST_WARNING_OBJECT (mssdemux, "Not exposing stream of unrecognized format");
340   }
341 }
342
343 static void
344 gst_mss_demux_process_manifest (GstMssDemux * mssdemux)
345 {
346   GstQuery *query;
347   gchar *uri = NULL;
348   gboolean ret;
349   GSList *iter;
350
351   g_return_if_fail (mssdemux->manifest_buffer != NULL);
352   g_return_if_fail (mssdemux->manifest == NULL);
353
354   query = gst_query_new_uri ();
355   ret = gst_pad_peer_query (mssdemux->sinkpad, query);
356   if (ret) {
357     gst_query_parse_uri (query, &uri);
358     /* TODO use this to get the base url for the fragments */
359     g_free (uri);
360   }
361   gst_query_unref (query);
362
363   mssdemux->manifest = gst_mss_manifest_new (mssdemux->manifest_buffer);
364   if (!mssdemux->manifest) {
365     GST_ELEMENT_ERROR (mssdemux, STREAM, FORMAT, ("Bad manifest file"),
366         ("Xml manifest file couldn't be parsed"));
367     return;
368   }
369
370   gst_mss_demux_create_streams (mssdemux);
371   for (iter = mssdemux->streams; iter; iter = g_slist_next (iter)) {
372     gst_mss_demux_expose_stream (mssdemux, iter->data);
373   }
374 }
375
376 static void
377 gst_mss_demux_stream_loop (GstMssDemuxStream * stream)
378 {
379   gchar *url;
380   GstFragment *fragment;
381   GstBufferList *buflist;
382   GstFlowReturn ret;
383
384   ret = gst_mss_manifest_stream_get_fragment_url (stream->manifest_stream,
385       &url);
386   switch (ret) {
387     default:
388       break;
389   }
390   if (!url) {
391     /* TODO */
392   }
393
394   fragment = gst_uri_downloader_fetch_uri (stream->downloader, url);
395   g_free (url);
396
397   buflist = gst_fragment_get_buffer_list (fragment);
398
399   ret = gst_pad_push_list (stream->pad, buflist);       /* TODO check return */
400
401   ret = gst_mss_manifest_stream_advance_fragment (stream->manifest_stream);
402 }