• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* GStreamer
2 * Copyright (C) 2013 Fluendo S.L. <support@fluendo.com>
3 *   Authors:    2013 Andoni Morales Alastruey <amorales@fluendo.com>
4 * Copyright (C) 2013 Sebastian Dröge <slomo@circular-chaos.org>
5 *
6 * gstios_assetsrc.c:
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 * SECTION:element-iosassetsrc
25 * @see_also: #GstIOSAssetSrc
26 *
27 * Read data from an iOS asset from the media library.
28 *
29 * ## Example launch line
30 *
31 * |[
32 * gst-launch-1.0 iosassetsrc uri=assets-library://asset/asset.M4V?id=11&ext=M4V ! decodebin ! autoaudiosink
33 * ]| Plays asset with id a song.ogg from local dir.
34 */
35
36#ifdef HAVE_CONFIG_H
37#  include "config.h"
38#endif
39
40#include <gst/gst.h>
41#include <gst/base/base.h>
42#include "iosassetsrc.h"
43
44static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
45    GST_PAD_SRC,
46    GST_PAD_ALWAYS,
47    GST_STATIC_CAPS_ANY);
48
49GST_DEBUG_CATEGORY_STATIC (gst_ios_asset_src_debug);
50#define GST_CAT_DEFAULT gst_ios_asset_src_debug
51
52
53#define DEFAULT_BLOCKSIZE       4*1024
54
55enum
56{
57  PROP_0,
58  PROP_URI,
59};
60
61static void gst_ios_asset_src_finalize (GObject * object);
62
63static void gst_ios_asset_src_set_property (GObject * object, guint prop_id,
64    const GValue * value, GParamSpec * pspec);
65static void gst_ios_asset_src_get_property (GObject * object, guint prop_id,
66    GValue * value, GParamSpec * pspec);
67
68static gboolean gst_ios_asset_src_start (GstBaseSrc * basesrc);
69static gboolean gst_ios_asset_src_stop (GstBaseSrc * basesrc);
70
71static gboolean gst_ios_asset_src_is_seekable (GstBaseSrc * src);
72static gboolean gst_ios_asset_src_get_size (GstBaseSrc * src, guint64 * size);
73static GstFlowReturn gst_ios_asset_src_create (GstBaseSrc * src, guint64 offset,
74    guint length, GstBuffer ** buffer);
75static gboolean gst_ios_asset_src_query (GstBaseSrc * src, GstQuery * query);
76
77static void gst_ios_asset_src_uri_handler_init (gpointer g_iface,
78    gpointer iface_data);
79
80static void
81_do_init (GType ios_assetsrc_type)
82{
83  static const GInterfaceInfo urihandler_info = {
84    gst_ios_asset_src_uri_handler_init,
85    NULL,
86    NULL
87  };
88
89  g_type_add_interface_static (ios_assetsrc_type, GST_TYPE_URI_HANDLER,
90      &urihandler_info);
91  GST_DEBUG_CATEGORY_INIT (gst_ios_asset_src_debug, "iosassetsrc", 0, "iosassetsrc element");
92}
93
94G_DEFINE_TYPE_WITH_CODE (GstIOSAssetSrc, gst_ios_asset_src, GST_TYPE_BASE_SRC,
95    _do_init (g_define_type_id));
96
97static void
98gst_ios_asset_src_class_init (GstIOSAssetSrcClass * klass)
99{
100  GObjectClass *gobject_class;
101  GstElementClass *gstelement_class;
102  GstBaseSrcClass *gstbasesrc_class;
103
104  gobject_class = G_OBJECT_CLASS (klass);
105  gstelement_class = GST_ELEMENT_CLASS (klass);
106  gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
107
108  gobject_class->set_property = gst_ios_asset_src_set_property;
109  gobject_class->get_property = gst_ios_asset_src_get_property;
110
111  g_object_class_install_property (gobject_class, PROP_URI,
112      g_param_spec_string ("uri", "Asset URI",
113          "URI of the asset to read", NULL,
114          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
115          GST_PARAM_MUTABLE_READY));
116
117  gobject_class->finalize = gst_ios_asset_src_finalize;
118
119  gst_element_class_set_static_metadata (gstelement_class,
120      "IOSAsset Source",
121      "Source/File",
122      "Read from arbitrary point in a iOS asset",
123      "Andoni Morales Alastruey <amorales@fluendo.com>");
124
125  gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
126
127  gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_ios_asset_src_start);
128  gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_ios_asset_src_stop);
129  gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_ios_asset_src_is_seekable);
130  gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_ios_asset_src_get_size);
131  gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_ios_asset_src_create);
132  gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_ios_asset_src_query);
133}
134
135static void
136gst_ios_asset_src_init (GstIOSAssetSrc * src)
137{
138  src->uri = NULL;
139  src->asset = NULL;
140  src->library = (__bridge_retained gpointer)[[GstAssetsLibrary alloc] init];
141  gst_base_src_set_blocksize (GST_BASE_SRC (src), DEFAULT_BLOCKSIZE);
142}
143
144static void
145gst_ios_asset_src_free_resources (GstIOSAssetSrc *src)
146{
147  if (src->asset != NULL) {
148    CFBridgingRelease(src->asset);
149    src->asset = NULL;
150  }
151
152  if (src->url != NULL) {
153    CFBridgingRelease(src->url);
154    src->url = NULL;
155  }
156
157  if (src->uri != NULL) {
158    g_free (src->uri);
159    src->uri = NULL;
160  }
161}
162
163static void
164gst_ios_asset_src_finalize (GObject * object)
165{
166  GstIOSAssetSrc *src;
167
168  src = GST_IOS_ASSET_SRC (object);
169  gst_ios_asset_src_free_resources (src);
170  CFBridgingRelease(src->library);
171
172  G_OBJECT_CLASS (gst_ios_asset_src_parent_class)->finalize (object);
173}
174
175static gboolean
176gst_ios_asset_src_set_uri (GstIOSAssetSrc * src, const gchar * uri, GError **err)
177{
178  GstState state;
179  NSString *nsuristr;
180  NSURL *url;
181
182  /* the element must be stopped in order to do this */
183  GST_OBJECT_LOCK (src);
184  state = GST_STATE (src);
185  if (state != GST_STATE_READY && state != GST_STATE_NULL)
186    goto wrong_state;
187  GST_OBJECT_UNLOCK (src);
188
189  gst_ios_asset_src_free_resources (src);
190
191  nsuristr = [[NSString alloc] initWithUTF8String:uri];
192  url = [[NSURL alloc] initWithString:nsuristr];
193
194  if (url == NULL) {
195    GST_ERROR_OBJECT (src, "Invalid URI: %s", uri);
196    g_set_error (err, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
197        "Invalid URI: %s", uri);
198    return FALSE;
199  }
200
201  GST_INFO_OBJECT (src, "URI      : %s", src->uri);
202  src->url = (__bridge_retained gpointer)url;
203  src->uri = g_strdup (uri);
204  g_object_notify (G_OBJECT (src), "uri");
205
206  return TRUE;
207
208  /* ERROR */
209wrong_state:
210  {
211    g_warning ("Changing the 'uri' property on iosassetsrc when an asset is "
212        "open is not supported.");
213    g_set_error (err, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE,
214        "Changing the 'uri' property on iosassetsrc when an asset is "
215        "open is not supported.");
216    GST_OBJECT_UNLOCK (src);
217    return FALSE;
218  }
219}
220
221static void
222gst_ios_asset_src_set_property (GObject * object, guint prop_id,
223    const GValue * value, GParamSpec * pspec)
224{
225  GstIOSAssetSrc *src;
226
227  g_return_if_fail (GST_IS_IOS_ASSET_SRC (object));
228
229  src = GST_IOS_ASSET_SRC (object);
230
231  switch (prop_id) {
232    case PROP_URI:
233      gst_ios_asset_src_set_uri (src, g_value_get_string (value), NULL);
234      break;
235    default:
236      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
237      break;
238  }
239}
240
241static void
242gst_ios_asset_src_get_property (GObject * object, guint prop_id, GValue * value,
243    GParamSpec * pspec)
244{
245  GstIOSAssetSrc *src;
246
247  g_return_if_fail (GST_IS_IOS_ASSET_SRC (object));
248
249  src = GST_IOS_ASSET_SRC (object);
250
251  switch (prop_id) {
252    case PROP_URI:
253      g_value_set_string (value, src->uri);
254      break;
255    default:
256      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
257      break;
258  }
259}
260
261static GstFlowReturn
262gst_ios_asset_src_create (GstBaseSrc * basesrc, guint64 offset, guint length,
263    GstBuffer ** buffer)
264{
265  GstBuffer *buf = NULL;
266  GstMapInfo info;
267  NSError *err = nil;
268  guint bytes_read;
269  GstFlowReturn ret;
270  GstIOSAssetSrc *src = GST_IOS_ASSET_SRC (basesrc);
271
272  buf = gst_buffer_new_and_alloc (length);
273  if (G_UNLIKELY (buf == NULL && length > 0)) {
274    GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", length);
275    ret = GST_FLOW_ERROR;
276    goto exit;
277  }
278
279  gst_buffer_map (buf, &info, GST_MAP_READWRITE);
280
281  /* No need to read anything if length is 0 */
282  bytes_read = [GST_IOS_ASSET_SRC_ASSET(src) getBytes: info.data
283      fromOffset:offset
284          length:length
285           error:&err];
286  if (G_UNLIKELY (err != NULL)) {
287    goto could_not_read;
288  }
289
290  /* we should eos if we read less than what was requested */
291  if (G_UNLIKELY (bytes_read < length)) {
292    GST_DEBUG ("EOS");
293    ret = GST_FLOW_EOS;
294  } else {
295    ret = GST_FLOW_OK;
296  }
297
298  gst_buffer_unmap (buf, &info);
299  gst_buffer_set_size (buf, bytes_read);
300
301  GST_BUFFER_OFFSET (buf) = offset;
302  GST_BUFFER_OFFSET_END (buf) = offset + bytes_read;
303
304  *buffer = buf;
305
306  goto exit;
307
308  /* ERROR */
309could_not_read:
310  {
311    GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
312    gst_buffer_unmap (buf, &info);
313    gst_buffer_unref (buf);
314    ret = GST_FLOW_ERROR;
315    goto exit;
316  }
317exit:
318  {
319    return ret;
320  }
321
322}
323
324static gboolean
325gst_ios_asset_src_query (GstBaseSrc * basesrc, GstQuery * query)
326{
327  gboolean ret = FALSE;
328  GstIOSAssetSrc *src = GST_IOS_ASSET_SRC (basesrc);
329
330  switch (GST_QUERY_TYPE (query)) {
331    case GST_QUERY_URI:
332      gst_query_set_uri (query, src->uri);
333      ret = TRUE;
334      break;
335    default:
336      ret = FALSE;
337      break;
338  }
339
340  if (!ret)
341    ret = GST_BASE_SRC_CLASS (gst_ios_asset_src_parent_class)->query (basesrc, query);
342
343  return ret;
344}
345
346static gboolean
347gst_ios_asset_src_is_seekable (GstBaseSrc * basesrc)
348{
349  return TRUE;
350}
351
352static gboolean
353gst_ios_asset_src_get_size (GstBaseSrc * basesrc, guint64 * size)
354{
355  GstIOSAssetSrc *src;
356
357  src = GST_IOS_ASSET_SRC (basesrc);
358
359  *size = (guint64) [GST_IOS_ASSET_SRC_ASSET(src) size];
360  return TRUE;
361}
362
363static gboolean
364gst_ios_asset_src_start (GstBaseSrc * basesrc)
365{
366  GstIOSAssetSrc *src = GST_IOS_ASSET_SRC (basesrc);
367  gboolean ret = TRUE;
368
369  src->asset = (__bridge_retained gpointer)[GST_IOS_ASSET_SRC_LIBRARY(src) assetForURLSync: GST_IOS_ASSET_SRC_URL(src)];
370
371  if (src->asset == NULL) {
372    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
373        ("Could not open asset \"%s\" for reading.", src->uri),
374        GST_ERROR_SYSTEM);
375    ret = FALSE;
376  };
377
378  return ret;
379}
380
381/* unmap and close the ios_asset */
382static gboolean
383gst_ios_asset_src_stop (GstBaseSrc * basesrc)
384{
385  GstIOSAssetSrc *src = GST_IOS_ASSET_SRC (basesrc);
386
387  CFBridgingRelease(src->asset);
388  return TRUE;
389}
390
391static GstURIType
392gst_ios_asset_src_uri_get_type (GType type)
393{
394  return GST_URI_SRC;
395}
396
397static const gchar * const *
398gst_ios_asset_src_uri_get_protocols (GType type)
399{
400  static const gchar * const protocols[] = { "assets-library", NULL };
401
402  return protocols;
403}
404
405static gchar *
406gst_ios_asset_src_uri_get_uri (GstURIHandler * handler)
407{
408  GstIOSAssetSrc *src = GST_IOS_ASSET_SRC (handler);
409
410  return g_strdup (src->uri);
411}
412
413static gboolean
414gst_ios_asset_src_uri_set_uri (GstURIHandler * handler, const gchar * uri, GError **err)
415{
416  GstIOSAssetSrc *src = GST_IOS_ASSET_SRC (handler);
417
418  if (! g_str_has_prefix (uri, "assets-library://")) {
419    GST_WARNING_OBJECT (src, "Invalid URI '%s' for ios_assetsrc", uri);
420    g_set_error (err, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
421        "Invalid URI '%s' for ios_assetsrc", uri);
422    return FALSE;
423  }
424
425  return gst_ios_asset_src_set_uri (src, uri, err);
426}
427
428static void
429gst_ios_asset_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
430{
431  GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
432
433  iface->get_type = gst_ios_asset_src_uri_get_type;
434  iface->get_protocols = gst_ios_asset_src_uri_get_protocols;
435  iface->get_uri = gst_ios_asset_src_uri_get_uri;
436  iface->set_uri = gst_ios_asset_src_uri_set_uri;
437}
438
439
440@implementation GstAssetsLibrary
441
442@synthesize asset;
443@synthesize result;
444
445- (id) init
446{
447  self = [super init];
448
449  return self;
450}
451
452- (ALAssetRepresentation *) assetForURLSync:(NSURL*) uri
453{
454  dispatch_semaphore_t sema = dispatch_semaphore_create(0);
455  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
456
457  dispatch_async(queue, ^{
458    [self assetForURL:uri resultBlock:
459         ^(ALAsset *myasset)
460         {
461           self.asset = myasset;
462           self.result = [myasset defaultRepresentation];
463
464           dispatch_semaphore_signal(sema);
465         }
466             failureBlock:
467         ^(NSError *myerror)
468         {
469           self.result = nil;
470           dispatch_semaphore_signal(sema);
471         }
472    ];
473  });
474
475  dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
476
477  return self.result;
478}
479@end
480