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