1/*
2 * GStreamer
3 * Copyright (C) 2013 Fluendo S.L. <support@fluendo.com>
4 *    Authors: Andoni Morales Alastruey <amorales@fluendo.com>
5 * Copyright (C) 2014 Collabora Ltd.
6 *    Authors: Matthieu Bouron <matthieu.bouron@collabora.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23
24/**
25 * SECTION:element-plugin
26 *
27 * Read and decode samples from AVFoundation assets using the AVFAssetReader API
28 *
29 * <refsect2>
30 * <title>Example launch line</title>
31 * |[
32 * gst-launch-1.0 -v -m avfassetsrc uri="file://movie.mp4" ! autovideosink
33 * ]|
34 * </refsect2>
35 */
36
37#ifdef HAVE_CONFIG_H
38#  include <config.h>
39#endif
40
41#include "avfassetsrc.h"
42#include "coremediabuffer.h"
43
44GST_DEBUG_CATEGORY_STATIC (gst_avf_asset_src_debug);
45#define GST_CAT_DEFAULT gst_avf_asset_src_debug
46
47#define CMTIME_TO_GST_TIME(x) \
48    (x.value == 0 ? 0 : (guint64)(x.value * GST_SECOND / x.timescale));
49#define GST_AVF_ASSET_SRC_LOCK(x) (g_mutex_lock (&x->lock));
50#define GST_AVF_ASSET_SRC_UNLOCK(x) (g_mutex_unlock (&x->lock));
51#define MEDIA_TYPE_TO_STR(x) \
52    (x == GST_AVF_ASSET_READER_MEDIA_TYPE_AUDIO ? "audio" : "video")
53#define AVF_ASSET_READER_HAS_AUDIO(x) \
54    ([GST_AVF_ASSET_SRC_READER(self) hasMediaType:GST_AVF_ASSET_READER_MEDIA_TYPE_AUDIO])
55#define AVF_ASSET_READER_HAS_VIDEO(x) \
56    ([GST_AVF_ASSET_SRC_READER(self) hasMediaType:GST_AVF_ASSET_READER_MEDIA_TYPE_VIDEO])
57
58enum
59{
60  PROP_0,
61  PROP_URI
62};
63
64static GstStaticPadTemplate audio_factory = GST_STATIC_PAD_TEMPLATE ("audio",
65    GST_PAD_SRC,
66    GST_PAD_SOMETIMES,
67    GST_STATIC_CAPS ("audio/x-raw, "
68        "format = (string) F32LE, "
69        "rate = " GST_AUDIO_RATE_RANGE ", "
70        "channels = (int) [1, 2], "
71        "layout = (string) interleaved"
72    )
73);
74
75static GstStaticPadTemplate video_factory = GST_STATIC_PAD_TEMPLATE ("video",
76    GST_PAD_SRC,
77    GST_PAD_SOMETIMES,
78    GST_STATIC_CAPS ("video/x-raw, "
79        "format = (string) NV12, "
80        "framerate = " GST_VIDEO_FPS_RANGE ", "
81        "width = " GST_VIDEO_SIZE_RANGE ", "
82        "height = " GST_VIDEO_SIZE_RANGE
83    )
84);
85
86static void gst_avf_asset_src_set_property (GObject * object, guint prop_id,
87    const GValue * value, GParamSpec * pspec);
88static void gst_avf_asset_src_get_property (GObject * object, guint prop_id,
89    GValue * value, GParamSpec * pspec);
90static void gst_avf_asset_src_dispose (GObject *object);
91
92static GstStateChangeReturn gst_avf_asset_src_change_state (GstElement * element,
93    GstStateChange transition);
94
95static gboolean gst_avf_asset_src_query (GstPad *pad, GstObject * parent, GstQuery *query);
96static gboolean gst_avf_asset_src_event (GstPad *pad, GstObject * parent, GstEvent *event);
97static gboolean gst_avf_asset_src_send_event (GstAVFAssetSrc *self,
98    GstEvent *event);
99
100static void gst_avf_asset_src_read_audio (GstAVFAssetSrc *self);
101static void gst_avf_asset_src_read_video (GstAVFAssetSrc *self);
102static void gst_avf_asset_src_start (GstAVFAssetSrc *self);
103static void gst_avf_asset_src_stop (GstAVFAssetSrc *self);
104static gboolean gst_avf_asset_src_start_reading (GstAVFAssetSrc *self);
105static void gst_avf_asset_src_stop_reading (GstAVFAssetSrc *self);
106static void gst_avf_asset_src_stop_all (GstAVFAssetSrc *self);
107static void gst_avf_asset_src_uri_handler_init (gpointer g_iface,
108    gpointer iface_data);
109
110static void
111_do_init (GType avf_assetsrc_type)
112{
113  static const GInterfaceInfo urihandler_info = {
114    gst_avf_asset_src_uri_handler_init,
115    NULL,
116    NULL
117  };
118
119  g_type_add_interface_static (avf_assetsrc_type, GST_TYPE_URI_HANDLER,
120      &urihandler_info);
121  GST_DEBUG_CATEGORY_INIT (gst_avf_asset_src_debug, "avfassetsrc",
122      0, "avfassetsrc element");
123}
124
125G_DEFINE_TYPE_WITH_CODE (GstAVFAssetSrc, gst_avf_asset_src, GST_TYPE_ELEMENT,
126    _do_init (g_define_type_id));
127
128
129/* GObject vmethod implementations */
130
131static void
132gst_avf_asset_src_class_init (GstAVFAssetSrcClass * klass)
133{
134  GObjectClass *gobject_class;
135  GstElementClass *gstelement_class;
136
137  gobject_class = (GObjectClass *) klass;
138  gstelement_class = (GstElementClass *) klass;
139
140  gst_element_class_set_static_metadata (gstelement_class,
141    "Source and decoder for AVFoundation assets",
142    "Source/Codec",
143    "Read and decode samples from AVFoundation assets using the AVFAssetReader API",
144    "Andoni Morales Alastruey amorales@fluendo.com");
145
146  gst_element_class_add_static_pad_template (gstelement_class, &audio_factory);
147  gst_element_class_add_static_pad_template (gstelement_class, &video_factory);
148
149  gobject_class->set_property = gst_avf_asset_src_set_property;
150  gobject_class->get_property = gst_avf_asset_src_get_property;
151  gobject_class->dispose = gst_avf_asset_src_dispose;
152
153  /**
154   * GstAVFAssetSrc:uri
155   *
156   * URI of the asset to read
157   *
158   **/
159  g_object_class_install_property (gobject_class, PROP_URI,
160      g_param_spec_string ("uri", "Asset URI",
161          "URI of the asset to read", NULL,
162          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
163          GST_PARAM_MUTABLE_READY));
164
165  gstelement_class->change_state = gst_avf_asset_src_change_state;
166
167}
168
169static void
170gst_avf_asset_src_init (GstAVFAssetSrc * self)
171{
172  self->selected_audio_track = 0;
173  self->selected_video_track = 0;
174  self->last_audio_pad_ret = GST_FLOW_OK;
175  self->last_video_pad_ret = GST_FLOW_OK;
176  g_mutex_init (&self->lock);
177}
178
179static void
180gst_avf_asset_src_dispose (GObject *object)
181{
182  GstAVFAssetSrc *self = GST_AVF_ASSET_SRC (object);
183
184  if (self->uri != NULL) {
185    g_free (self->uri);
186    self->uri = NULL;
187  }
188
189  if (self->seek_event) {
190    gst_event_unref (self->seek_event);
191    self->seek_event = NULL;
192  }
193}
194
195static void
196gst_avf_asset_src_set_property (GObject * object, guint prop_id,
197    const GValue * value, GParamSpec * pspec)
198{
199  GstAVFAssetSrc *self = GST_AVF_ASSET_SRC (object);
200
201  switch (prop_id) {
202    case PROP_URI:
203      g_free (self->uri);
204      self->uri = g_value_dup_string (value);
205      break;
206    default:
207      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
208      break;
209  }
210}
211
212static void
213gst_avf_asset_src_get_property (GObject * object, guint prop_id,
214    GValue * value, GParamSpec * pspec)
215{
216  GstAVFAssetSrc *self = GST_AVF_ASSET_SRC (object);
217
218  switch (prop_id) {
219    case PROP_URI:
220      g_value_set_string (value, self->uri);
221      break;
222    default:
223      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224      break;
225  }
226}
227
228static GstStateChangeReturn
229gst_avf_asset_src_change_state (GstElement * element, GstStateChange transition)
230{
231  GstAVFAssetSrc *self = GST_AVF_ASSET_SRC (element);
232  GstStateChangeReturn ret;
233  GError *error;
234
235  GST_DEBUG ("%s => %s",
236      gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
237      gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
238
239  switch (transition) {
240    case GST_STATE_CHANGE_NULL_TO_READY: {
241      self->state = GST_AVF_ASSET_SRC_STATE_STOPPED;
242      if (!self->uri) {
243        GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
244            ("\"uri\" property not set"), (NULL));
245        gst_avf_asset_src_stop_all (self);
246        return GST_STATE_CHANGE_FAILURE;
247      }
248      self->reader = (__bridge_retained gpointer)([[GstAVFAssetReader alloc] initWithURI:self->uri:&error]);
249      if (error) {
250        GST_ELEMENT_ERROR (element, RESOURCE, FAILED, ("AVFAssetReader error"),
251            ("%s", error->message));
252        g_error_free (error);
253        gst_avf_asset_src_stop_all (self);
254        return GST_STATE_CHANGE_FAILURE;
255      }
256      break;
257    }
258    case GST_STATE_CHANGE_READY_TO_PAUSED:
259      gst_avf_asset_src_start (self);
260      gst_avf_asset_src_start_reading (self);
261      break;
262    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
263      break;
264    default:
265      break;
266  }
267
268  ret = GST_ELEMENT_CLASS (gst_avf_asset_src_parent_class)->change_state (element, transition);
269
270  switch (transition) {
271    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
272      break;
273    case GST_STATE_CHANGE_PAUSED_TO_READY:
274      gst_avf_asset_src_stop_reading (self);
275      gst_avf_asset_src_stop (self);
276      break;
277    case GST_STATE_CHANGE_READY_TO_NULL:
278      CFBridgingRelease(self->reader);
279      break;
280    default:
281      break;
282  }
283  return ret;
284}
285
286static GstCaps *
287gst_avf_asset_src_get_caps(GstAVFAssetSrc * self, GstPad * pad, GstCaps * filter)
288{
289  GstCaps * caps;
290
291  caps = gst_pad_get_current_caps (pad);
292  if (!caps) {
293    caps = gst_pad_get_pad_template_caps (pad);
294  }
295
296  if (filter) {
297    GstCaps *intersection = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
298    gst_caps_unref (caps);
299    caps = intersection;
300  }
301
302  return caps;
303}
304
305static gboolean
306gst_avf_asset_src_query (GstPad *pad, GstObject * parent, GstQuery *query)
307{
308    gboolean ret = FALSE;
309    GstCaps *caps;
310    GstAVFAssetSrc *self = GST_AVF_ASSET_SRC (parent);
311
312    switch (GST_QUERY_TYPE (query)) {
313      case GST_QUERY_URI:
314        gst_query_set_uri (query, self->uri);
315        ret = TRUE;
316        break;
317      case GST_QUERY_DURATION:
318        gst_query_set_duration (query, GST_FORMAT_TIME, GST_AVF_ASSET_SRC_READER(self).duration);
319        ret = TRUE;
320        break;
321      case GST_QUERY_POSITION:
322        gst_query_set_position (query, GST_FORMAT_TIME, GST_AVF_ASSET_SRC_READER(self).position);
323        ret = TRUE;
324        break;
325      case GST_QUERY_SEEKING: {
326        GstFormat fmt;
327        gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
328        if (fmt == GST_FORMAT_TIME) {
329          gst_query_set_seeking (query, GST_FORMAT_TIME, TRUE, 0, GST_AVF_ASSET_SRC_READER(self).duration);
330          ret = TRUE;
331        }
332        break;
333      }
334      case GST_QUERY_CAPS: {
335        GstCaps *filter = NULL;
336        gst_query_parse_caps (query, &filter);
337        caps = gst_avf_asset_src_get_caps (self, pad, filter);
338        gst_query_set_caps_result (query, caps);
339        ret = TRUE;
340        break;
341      }
342      default:
343        ret = FALSE;
344        break;
345    }
346
347    return ret;
348}
349
350static gboolean
351gst_avf_asset_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
352{
353  GstAVFAssetSrc *self;
354  gboolean res = TRUE;
355  GError *error = NULL;
356
357  self = GST_AVF_ASSET_SRC (gst_pad_get_parent_element (pad));
358
359  switch (GST_EVENT_TYPE (event)) {
360    case GST_EVENT_SEEK: {
361      GstFormat format;
362      GstSeekFlags flags;
363      gdouble rate;
364      GstSeekType start_type, stop_type;
365      gint64 start, stop;
366      GstSegment segment;
367
368      GST_DEBUG ("Processing SEEK event");
369
370      GST_AVF_ASSET_SRC_LOCK (self);
371      if (self->seek_event && gst_event_get_seqnum (event) ==
372          gst_event_get_seqnum (self->seek_event)) {
373        GST_AVF_ASSET_SRC_UNLOCK (self);
374        break;
375      }
376      self->seek_event = gst_event_ref (event);
377      GST_AVF_ASSET_SRC_UNLOCK (self);
378
379      /* pause tasks before re-acquiring the object's lock */
380      gst_avf_asset_src_stop_reading (self);
381      GST_AVF_ASSET_SRC_LOCK (self);
382
383      gst_event_parse_seek (event, &rate, &format, &flags, &start_type,
384          &start, &stop_type, &stop);
385
386      if (rate < 0) {
387        GST_WARNING ("Negative rates are not supported yet");
388        GST_AVF_ASSET_SRC_UNLOCK (self);
389        res = FALSE;
390        break;
391      }
392
393      if (format != GST_FORMAT_TIME || start_type == GST_SEEK_TYPE_NONE) {
394        GST_AVF_ASSET_SRC_UNLOCK(self);
395        gst_avf_asset_src_start_reading (self);
396        res = FALSE;
397        break;
398      }
399      if (stop_type == GST_SEEK_TYPE_NONE) {
400        stop = GST_CLOCK_TIME_NONE;
401      }
402      gst_avf_asset_src_send_event (self, gst_event_new_flush_start ());
403      [GST_AVF_ASSET_SRC_READER(self) seekTo: start: stop: &error];
404
405      gst_segment_init (&segment, GST_FORMAT_TIME);
406      segment.rate = rate;
407      segment.start = start;
408      segment.stop = stop;
409      segment.position = start;
410
411      gst_avf_asset_src_send_event (self, gst_event_new_flush_stop (TRUE));
412      gst_avf_asset_src_send_event (self, gst_event_new_segment (&segment));
413
414      if (error != NULL) {
415        GST_ELEMENT_ERROR (self, RESOURCE, SEEK,
416            ("AVFAssetReader seek failed"), ("%s", error->message));
417        g_error_free(error);
418        res = FALSE;
419      }
420      GST_AVF_ASSET_SRC_UNLOCK (self);
421      gst_event_unref (event);
422
423      /* start tasks after releasing the object's lock */
424      gst_avf_asset_src_start_reading (self);
425      break;
426    }
427    default:
428      res = gst_pad_event_default (pad, parent, event);
429      break;
430  }
431
432  gst_object_unref (self);
433  return res;
434}
435
436static GstFlowReturn
437gst_avf_asset_src_send_start_stream (GstAVFAssetSrc * self, GstPad * pad)
438{
439  GstEvent *event;
440  gchar *stream_id;
441  GstFlowReturn ret;
442
443  stream_id = gst_pad_create_stream_id (pad, GST_ELEMENT_CAST (self), NULL);
444  GST_DEBUG_OBJECT (self, "Pushing STREAM START");
445  event = gst_event_new_stream_start (stream_id);
446  gst_event_set_group_id (event, gst_util_group_id_next ());
447
448  ret = gst_pad_push_event (pad, event);
449  g_free (stream_id);
450
451  return ret;
452}
453
454static GstFlowReturn
455gst_avf_asset_src_combine_flows (GstAVFAssetSrc * self, GstAVFAssetReaderMediaType type,
456    GstFlowReturn ret)
457{
458  gboolean has_other_pad;
459  GstFlowReturn last_other_pad_ret;
460
461  GST_AVF_ASSET_SRC_LOCK (self);
462  if (type == GST_AVF_ASSET_READER_MEDIA_TYPE_AUDIO) {
463    self->last_audio_pad_ret = ret;
464    has_other_pad = AVF_ASSET_READER_HAS_VIDEO (ret);
465    last_other_pad_ret = self->last_video_pad_ret;
466  } else if (type == GST_AVF_ASSET_READER_MEDIA_TYPE_VIDEO) {
467    self->last_video_pad_ret = ret;
468    has_other_pad = AVF_ASSET_READER_HAS_AUDIO (ret);
469    last_other_pad_ret = self->last_audio_pad_ret;
470  } else {
471    GST_ERROR ("Unsupported media type");
472    ret = GST_FLOW_ERROR;
473    goto exit;
474  }
475
476  if (!has_other_pad || ret != GST_FLOW_NOT_LINKED)
477    goto exit;
478
479  ret = last_other_pad_ret;
480
481exit:
482  GST_AVF_ASSET_SRC_UNLOCK (self);
483  return ret;
484}
485
486static void
487gst_avf_asset_src_read_data (GstAVFAssetSrc *self, GstPad *pad,
488    GstAVFAssetReaderMediaType type)
489{
490  GstBuffer *buf;
491  GstFlowReturn ret, combined_ret;
492  GError *error;
493
494
495  GST_AVF_ASSET_SRC_LOCK (self);
496  if (self->state != GST_AVF_ASSET_SRC_STATE_READING) {
497    GST_AVF_ASSET_SRC_UNLOCK (self);
498    return;
499  }
500
501  buf = [GST_AVF_ASSET_SRC_READER(self) nextBuffer:type:&error];
502  GST_AVF_ASSET_SRC_UNLOCK (self);
503
504  if (buf == NULL) {
505    if (error != NULL) {
506      GST_ELEMENT_ERROR (self, RESOURCE, READ, ("Error reading next buffer"),
507          ("%s", error->message));
508      g_error_free (error);
509
510      gst_avf_asset_src_combine_flows (self, type, GST_FLOW_ERROR);
511      gst_pad_pause_task (pad);
512        return;
513    }
514
515    gst_pad_push_event (pad, gst_event_new_eos ());
516    gst_avf_asset_src_combine_flows (self, type, GST_FLOW_EOS);
517    gst_pad_pause_task (pad);
518    return;
519  }
520
521  ret = gst_pad_push (pad, buf);
522  combined_ret = gst_avf_asset_src_combine_flows (self, type, ret);
523
524  if (ret != GST_FLOW_OK) {
525    GST_WARNING ("Error pushing %s buffer on pad %" GST_PTR_FORMAT
526        ", reason %s", MEDIA_TYPE_TO_STR (type), pad, gst_flow_get_name (ret));
527
528    if (ret == GST_FLOW_EOS) {
529      gst_pad_push_event (pad, gst_event_new_eos ());
530    }
531
532    if (combined_ret != GST_FLOW_OK) {
533      GST_ELEMENT_FLOW_ERROR (self, ret);
534    }
535
536    gst_pad_pause_task (pad);
537  }
538
539}
540
541static void
542gst_avf_asset_src_read_audio (GstAVFAssetSrc *self)
543{
544  gst_avf_asset_src_read_data (self, self->audiopad,
545      GST_AVF_ASSET_READER_MEDIA_TYPE_AUDIO);
546}
547
548static void
549gst_avf_asset_src_read_video (GstAVFAssetSrc *self)
550{
551  gst_avf_asset_src_read_data (self, self->videopad,
552      GST_AVF_ASSET_READER_MEDIA_TYPE_VIDEO);
553}
554
555static gboolean
556gst_avf_asset_src_start_reader (GstAVFAssetSrc * self)
557{
558  GError *error = NULL;
559  gboolean ret = TRUE;
560
561
562  [GST_AVF_ASSET_SRC_READER(self) start: &error];
563  if (error != NULL) {
564    GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
565        ("AVFAssetReader could not start reading"), ("%s", error->message));
566    g_error_free (error);
567    ret = FALSE;
568    goto exit;
569  }
570
571exit:
572  return ret;
573}
574
575static gboolean
576gst_avf_asset_src_send_event (GstAVFAssetSrc *self, GstEvent *event)
577{
578  gboolean ret = TRUE;
579
580
581  if (AVF_ASSET_READER_HAS_VIDEO (self)) {
582    ret |= gst_pad_push_event (self->videopad, gst_event_ref (event));
583  }
584  if (AVF_ASSET_READER_HAS_AUDIO (self)) {
585    ret |= gst_pad_push_event (self->audiopad, gst_event_ref (event));
586  }
587
588  gst_event_unref (event);
589  return ret;
590}
591
592static void
593gst_avf_asset_src_start (GstAVFAssetSrc *self)
594{
595  GstSegment segment;
596
597  if (self->state == GST_AVF_ASSET_SRC_STATE_STARTED) {
598    return;
599  }
600
601  GST_DEBUG_OBJECT (self, "Creating pads and starting reader");
602
603  gst_segment_init (&segment, GST_FORMAT_TIME);
604  segment.duration = GST_AVF_ASSET_SRC_READER(self).duration;
605
606  /* We call AVFAssetReader's startReading when the pads are linked
607   * and no outputs can be added afterwards, so the tracks must be
608   * selected before adding any of the new pads */
609  if (AVF_ASSET_READER_HAS_AUDIO (self)) {
610    [GST_AVF_ASSET_SRC_READER(self) selectTrack: GST_AVF_ASSET_READER_MEDIA_TYPE_AUDIO:
611        self->selected_audio_track];
612  }
613  if (AVF_ASSET_READER_HAS_VIDEO (self)) {
614    [GST_AVF_ASSET_SRC_READER(self) selectTrack: GST_AVF_ASSET_READER_MEDIA_TYPE_VIDEO:
615         self->selected_video_track];
616  }
617
618  if (AVF_ASSET_READER_HAS_AUDIO (self)) {
619    self->audiopad = gst_pad_new_from_static_template (&audio_factory, "audio");
620    gst_pad_set_query_function (self->audiopad,
621        gst_avf_asset_src_query);
622    gst_pad_set_event_function(self->audiopad,
623        gst_avf_asset_src_event);
624    gst_pad_use_fixed_caps (self->audiopad);
625    gst_pad_set_active (self->audiopad, TRUE);
626    gst_avf_asset_src_send_start_stream (self, self->audiopad);
627    gst_pad_set_caps (self->audiopad,
628        [GST_AVF_ASSET_SRC_READER(self) getCaps: GST_AVF_ASSET_READER_MEDIA_TYPE_AUDIO]);
629    gst_pad_push_event (self->audiopad, gst_event_new_caps (
630        [GST_AVF_ASSET_SRC_READER(self) getCaps: GST_AVF_ASSET_READER_MEDIA_TYPE_AUDIO]));
631    gst_pad_push_event (self->audiopad, gst_event_new_segment (&segment));
632    gst_element_add_pad (GST_ELEMENT (self), self->audiopad);
633  }
634  if (AVF_ASSET_READER_HAS_VIDEO (self)) {
635    self->videopad = gst_pad_new_from_static_template (&video_factory, "video");
636    gst_pad_set_query_function (self->videopad,
637        gst_avf_asset_src_query);
638    gst_pad_set_event_function(self->videopad,
639        gst_avf_asset_src_event);
640    gst_pad_use_fixed_caps (self->videopad);
641    gst_pad_set_active (self->videopad, TRUE);
642    gst_avf_asset_src_send_start_stream (self, self->videopad);
643    gst_pad_set_caps (self->videopad,
644        [GST_AVF_ASSET_SRC_READER(self) getCaps: GST_AVF_ASSET_READER_MEDIA_TYPE_VIDEO]);
645    gst_pad_push_event (self->videopad, gst_event_new_caps (
646        [GST_AVF_ASSET_SRC_READER(self) getCaps: GST_AVF_ASSET_READER_MEDIA_TYPE_VIDEO]));
647    gst_pad_push_event (self->videopad, gst_event_new_segment (&segment));
648    gst_element_add_pad (GST_ELEMENT (self), self->videopad);
649  }
650  gst_element_no_more_pads (GST_ELEMENT (self));
651
652  self->state = GST_AVF_ASSET_SRC_STATE_STARTED;
653}
654
655static void
656gst_avf_asset_src_stop (GstAVFAssetSrc *self)
657{
658  gboolean has_audio, has_video;
659
660  if (self->state == GST_AVF_ASSET_SRC_STATE_STOPPED) {
661    return;
662  }
663
664  GST_DEBUG ("Stopping tasks and removing pads");
665
666  has_audio = AVF_ASSET_READER_HAS_AUDIO (self);
667  has_video = AVF_ASSET_READER_HAS_VIDEO (self);
668  [GST_AVF_ASSET_SRC_READER(self) stop];
669
670  if (has_audio) {
671    gst_pad_stop_task (self->audiopad);
672    gst_element_remove_pad (GST_ELEMENT (self), self->audiopad);
673  }
674  if (has_video) {
675    gst_pad_stop_task (self->videopad);
676    gst_element_remove_pad (GST_ELEMENT (self), self->videopad);
677  }
678
679  self->state = GST_AVF_ASSET_SRC_STATE_STOPPED;
680}
681
682static gboolean
683gst_avf_asset_src_start_reading (GstAVFAssetSrc *self)
684{
685  gboolean ret = TRUE;
686
687  if (self->state != GST_AVF_ASSET_SRC_STATE_STARTED) {
688    goto exit;
689  }
690
691  GST_DEBUG_OBJECT (self, "Start reading");
692
693  if ((ret = gst_avf_asset_src_start_reader (self)) != TRUE) {
694    goto exit;
695  }
696
697  if (AVF_ASSET_READER_HAS_AUDIO (self)) {
698    ret = gst_pad_start_task (self->audiopad, (GstTaskFunction)gst_avf_asset_src_read_audio, self, NULL);
699    if (!ret) {
700      GST_ERROR ("Failed to start audio task");
701      goto exit;
702    }
703  }
704
705  if (AVF_ASSET_READER_HAS_VIDEO (self)) {
706    ret = gst_pad_start_task (self->videopad, (GstTaskFunction)gst_avf_asset_src_read_video, self, NULL);
707    if (!ret) {
708      GST_ERROR ("Failed to start video task");
709      goto exit;
710    }
711  }
712
713  self->state = GST_AVF_ASSET_SRC_STATE_READING;
714
715exit:
716  return ret;
717}
718
719static void
720gst_avf_asset_src_stop_reading (GstAVFAssetSrc * self)
721{
722  if (self->state != GST_AVF_ASSET_SRC_STATE_READING) {
723    return;
724  }
725
726  GST_DEBUG_OBJECT (self, "Stop reading");
727
728  if (AVF_ASSET_READER_HAS_AUDIO (self)) {
729    gst_pad_pause_task (self->audiopad);
730  }
731  if (AVF_ASSET_READER_HAS_VIDEO (self)) {
732    gst_pad_pause_task (self->videopad);
733  }
734
735  self->state = GST_AVF_ASSET_SRC_STATE_STARTED;
736}
737
738static void
739gst_avf_asset_src_stop_all (GstAVFAssetSrc *self)
740{
741  GST_AVF_ASSET_SRC_LOCK (self);
742  gst_avf_asset_src_stop_reading (self);
743  gst_avf_asset_src_stop (self);
744  GST_AVF_ASSET_SRC_UNLOCK (self);
745}
746
747static GQuark
748gst_avf_asset_src_error_quark (void)
749{
750  static GQuark q;              /* 0 */
751
752  if (G_UNLIKELY (q == 0)) {
753      q = g_quark_from_static_string ("avfasset-src-error-quark");
754  }
755  return q;
756}
757
758static GstURIType
759gst_avf_asset_src_uri_get_type (GType type)
760{
761  return GST_URI_SRC;
762}
763
764static const gchar * const *
765gst_avf_asset_src_uri_get_protocols (GType type)
766{
767  static const gchar * const protocols[] = { "file", "ipod-library", NULL };
768
769  return protocols;
770}
771
772static gchar *
773gst_avf_asset_src_uri_get_uri (GstURIHandler * handler)
774{
775  GstAVFAssetSrc *self = GST_AVF_ASSET_SRC (handler);
776
777  return g_strdup (self->uri);
778}
779
780static gboolean
781gst_avf_asset_src_uri_set_uri (GstURIHandler * handler, const gchar * uri, GError **error)
782{
783  GstAVFAssetSrc *self = GST_AVF_ASSET_SRC (handler);
784  NSString *str;
785  NSURL *url;
786  AVAsset *asset;
787  gboolean ret = FALSE;
788
789  str = [NSString stringWithUTF8String: uri];
790  url = [[NSURL alloc] initWithString: str];
791  asset = [AVAsset assetWithURL: url];
792
793  if (asset.playable) {
794    ret = TRUE;
795    g_free (self->uri);
796    self->uri = g_strdup (uri);
797  } else {
798    g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
799        "Invalid URI '%s' for avfassetsrc", uri);
800  }
801  return ret;
802}
803
804static void
805gst_avf_asset_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
806{
807  GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
808
809  iface->get_type = gst_avf_asset_src_uri_get_type;
810  iface->get_protocols = gst_avf_asset_src_uri_get_protocols;
811  iface->get_uri = gst_avf_asset_src_uri_get_uri;
812  iface->set_uri = gst_avf_asset_src_uri_set_uri;
813}
814
815@implementation GstAVFAssetReader
816
817@synthesize duration;
818@synthesize position;
819
820- (NSDictionary *) capsToAudioSettings
821{
822  gint depth;
823  gboolean isFloat;
824  GstAudioInfo info;
825
826  if (!gst_caps_is_fixed (audio_caps))
827    return NULL;
828
829  gst_audio_info_from_caps (&info, audio_caps);
830  isFloat = GST_AUDIO_INFO_IS_FLOAT(&info);
831  depth = GST_AUDIO_INFO_DEPTH(&info);
832
833  return [NSDictionary dictionaryWithObjectsAndKeys:
834      [NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey,
835      [NSNumber numberWithFloat:info.rate], AVSampleRateKey,
836      [NSNumber numberWithInt:info.channels], AVNumberOfChannelsKey,
837      //[NSData dataWithBytes:&channelLayout length:sizeof(AudioChannelLayout)],
838      //AVChannelLayoutKey,
839      [NSNumber numberWithInt:depth], AVLinearPCMBitDepthKey,
840      [NSNumber numberWithBool:isFloat],AVLinearPCMIsFloatKey,
841      [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
842      [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey,
843      nil];
844}
845
846- (void) releaseReader
847{
848  video_track = nil;
849  audio_track = nil;
850  video_tracks = nil;
851  audio_tracks = nil;
852  reader = nil;
853}
854
855- (void) initReader: (GError **) error
856{
857  NSError *nserror;
858
859  reader = [[AVAssetReader alloc] initWithAsset:asset error:&nserror];
860  if (nserror != NULL) {
861    GST_ERROR ("Error initializing reader: %s",
862        [nserror.description UTF8String]);
863    *error = g_error_new (GST_AVF_ASSET_SRC_ERROR, GST_AVF_ASSET_ERROR_INIT, "%s",
864        [nserror.description UTF8String]);
865
866    return;
867  }
868
869  audio_tracks = [asset tracksWithMediaType:AVMediaTypeAudio];
870  video_tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
871  reader.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
872  GST_INFO ("Found %lu video tracks and %lu audio tracks",
873      (unsigned long)[video_tracks count], (unsigned long)[audio_tracks count]);
874}
875
876- (id) initWithURI:(gchar*)uri : (GError **)error;
877{
878  NSString *str;
879  NSURL *url;
880
881  GST_INFO ("Initializing AVFAssetReader with uri: %s", uri);
882  *error = NULL;
883
884  str = [NSString stringWithUTF8String: uri];
885  url = [[NSURL alloc] initWithString: str];
886  asset = [AVAsset assetWithURL: url];
887
888  if (!asset.playable) {
889    *error = g_error_new (GST_AVF_ASSET_SRC_ERROR, GST_AVF_ASSET_ERROR_NOT_PLAYABLE,
890        "Media is not playable");
891    asset = nil;
892    return nil;
893  }
894
895  selected_audio_track = -1;
896  selected_video_track = -1;
897  reading = FALSE;
898  position = 0;
899  duration = CMTIME_TO_GST_TIME (asset.duration);
900
901  /* FIXME: use fixed caps here until we found a way to determine
902   * the native audio format */
903  audio_caps = gst_caps_from_string ("audio/x-raw, "
904      "format=F32LE, rate=44100, channels=2, layout=interleaved");
905
906  [self initReader: error];
907  if (*error) {
908    return nil;
909  }
910
911  self = [super init];
912  return self;
913}
914
915- (BOOL) selectTrack: (GstAVFAssetReaderMediaType) type : (gint) index
916{
917  NSArray *tracks;
918  AVAssetTrack *track;
919  AVAssetReaderOutput * __strong *output;
920  NSDictionary *settings;
921  NSString *mediaType;
922  gint *selected_track;
923
924  GST_INFO ("Selecting %s track %d", MEDIA_TYPE_TO_STR(type), index);
925
926  if (type == GST_AVF_ASSET_READER_MEDIA_TYPE_AUDIO) {
927    mediaType = AVMediaTypeAudio;
928    selected_track = &selected_audio_track;
929    output = &audio_track;
930    settings = [self capsToAudioSettings];
931  } else if (type == GST_AVF_ASSET_READER_MEDIA_TYPE_VIDEO) {
932    mediaType = AVMediaTypeVideo;
933    selected_track = &selected_video_track;
934    output = &video_track;
935    settings = [NSDictionary dictionaryWithObjectsAndKeys:
936        [NSNumber numberWithInt:
937        kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange],
938        kCVPixelBufferPixelFormatTypeKey, nil];
939  } else {
940    return FALSE;
941  }
942
943  tracks = [asset tracksWithMediaType:mediaType];
944  if ([tracks count] == 0 || [tracks count] < index + 1) {
945    return FALSE;
946  }
947
948  track = [tracks objectAtIndex:index];
949  *selected_track = index;
950  *output  = [AVAssetReaderTrackOutput
951      assetReaderTrackOutputWithTrack:track
952      outputSettings:settings];
953  [reader addOutput:*output];
954  return TRUE;
955}
956
957- (void) start: (GError **)error
958{
959  if (reading)
960    return;
961
962  if (![reader startReading]) {
963    *error = g_error_new (GST_AVF_ASSET_SRC_ERROR, GST_AVF_ASSET_ERROR_START,
964        "%s", [reader.error.description UTF8String]);
965    reading = FALSE;
966    return;
967  }
968  reading = TRUE;
969}
970
971- (void) stop
972{
973  [reader cancelReading];
974  reading = FALSE;
975}
976
977- (BOOL) hasMediaType: (GstAVFAssetReaderMediaType) type
978{
979  if (type == GST_AVF_ASSET_READER_MEDIA_TYPE_AUDIO) {
980    return [audio_tracks count] != 0;
981  }
982  if (type == GST_AVF_ASSET_READER_MEDIA_TYPE_VIDEO) {
983    return [video_tracks count] != 0;
984  }
985  return FALSE;
986}
987
988- (void) seekTo: (guint64) startTS : (guint64) stopTS : (GError **) error
989{
990  CMTime startTime = kCMTimeZero, stopTime = kCMTimePositiveInfinity;
991
992  if (startTS != GST_CLOCK_TIME_NONE) {
993    startTime = CMTimeMake (startTS, GST_SECOND);
994  }
995  if (stopTS != GST_CLOCK_TIME_NONE) {
996    stopTime = CMTimeMake (stopTS, GST_SECOND);
997  }
998
999  /* AVFAssetReader needs to be recreated before changing the
1000   * timerange property */
1001  [self stop];
1002  [self releaseReader];
1003  [self initReader: error];
1004  if (*error) {
1005    return;
1006  }
1007
1008  GST_DEBUG ("Seeking to start:%" GST_TIME_FORMAT " stop:%" GST_TIME_FORMAT,
1009      GST_TIME_ARGS(startTS), GST_TIME_ARGS(stopTS));
1010
1011  reader.timeRange = CMTimeRangeMake(startTime, stopTime);
1012  [self selectTrack: GST_AVF_ASSET_READER_MEDIA_TYPE_AUDIO:selected_audio_track];
1013  [self selectTrack: GST_AVF_ASSET_READER_MEDIA_TYPE_VIDEO:selected_video_track];
1014  [self start: error];
1015}
1016
1017- (GstBuffer *) nextBuffer: (GstAVFAssetReaderMediaType) type : (GError **) error
1018{
1019  CMSampleBufferRef cmbuf;
1020  AVAssetReaderTrackOutput *areader = NULL;
1021  GstCaps *caps;
1022  GstBuffer *buf;
1023  CMTime dur, ts;
1024
1025  GST_LOG ("Reading %s next buffer", MEDIA_TYPE_TO_STR (type));
1026  if (type == GST_AVF_ASSET_READER_MEDIA_TYPE_AUDIO && audio_track != NULL) {
1027    areader = audio_track;
1028    caps = audio_caps;
1029  } else if (type == GST_AVF_ASSET_READER_MEDIA_TYPE_VIDEO &&
1030      video_track != NULL) {
1031    areader = video_track;
1032    caps = video_caps;
1033  }
1034
1035  if (areader == NULL) {
1036    return NULL;
1037  }
1038
1039  *error = NULL;
1040  cmbuf = [areader copyNextSampleBuffer];
1041  if (cmbuf == NULL) {
1042    if (reader.error != NULL) {
1043      *error = g_error_new (GST_AVF_ASSET_SRC_ERROR, GST_AVF_ASSET_ERROR_READ,
1044          "%s", [reader.error.description UTF8String]);
1045    }
1046    /* EOS */
1047    return NULL;
1048  }
1049
1050  buf = gst_core_media_buffer_new (cmbuf, FALSE, NULL);
1051  CFRelease (cmbuf);
1052  if (buf == NULL)
1053    return NULL;
1054  /* cmbuf is now retained by buf (in meta) */
1055  dur = CMSampleBufferGetDuration (cmbuf);
1056  ts = CMSampleBufferGetPresentationTimeStamp (cmbuf);
1057  if (dur.value != 0) {
1058    GST_BUFFER_DURATION (buf) = CMTIME_TO_GST_TIME (dur);
1059  }
1060  GST_BUFFER_TIMESTAMP (buf) = CMTIME_TO_GST_TIME (ts);
1061  GST_LOG ("Copying next %s buffer ts:%" GST_TIME_FORMAT " dur:%"
1062      GST_TIME_FORMAT, MEDIA_TYPE_TO_STR (type),
1063      GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (buf)),
1064      GST_TIME_ARGS(GST_BUFFER_DURATION (buf)));
1065  if (GST_BUFFER_TIMESTAMP (buf) > position) {
1066    position = GST_BUFFER_TIMESTAMP (buf);
1067  }
1068  return buf;
1069}
1070
1071- (GstCaps *) getCaps: (GstAVFAssetReaderMediaType) type
1072{
1073  GstCaps *caps = NULL;
1074  AVAssetTrack *track;
1075
1076  if (type == GST_AVF_ASSET_READER_MEDIA_TYPE_AUDIO) {
1077    caps = gst_caps_ref (audio_caps);
1078    GST_INFO ("Using audio caps: %" GST_PTR_FORMAT, caps);
1079  } else if (type == GST_AVF_ASSET_READER_MEDIA_TYPE_VIDEO) {
1080    gint fr_n, fr_d;
1081
1082    track = [video_tracks objectAtIndex: selected_video_track];
1083    gst_util_double_to_fraction(track.nominalFrameRate, &fr_n, &fr_d);
1084    caps = gst_caps_new_simple ("video/x-raw",
1085        "format", G_TYPE_STRING, "NV12",
1086        "width", G_TYPE_INT, (int) track.naturalSize.width,
1087        "height", G_TYPE_INT, (int) track.naturalSize.height,
1088        "framerate", GST_TYPE_FRACTION, fr_n, fr_d, NULL);
1089    GST_INFO ("Using video caps: %" GST_PTR_FORMAT, caps);
1090    video_caps = gst_caps_ref (caps);
1091  }
1092
1093  return caps;
1094}
1095
1096- (void) dealloc
1097{
1098  asset = nil;
1099  [self releaseReader];
1100
1101  if (audio_caps != NULL) {
1102    gst_caps_unref (audio_caps);
1103  }
1104
1105  if (video_caps != NULL) {
1106    gst_caps_unref (audio_caps);
1107  }
1108}
1109
1110@end
1111