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