Merge missing tizen patch
[platform/upstream/gst-plugins-good.git] / gst / isomp4 / qtdemux.c
index d7fdae8..f801b8f 100644 (file)
@@ -79,7 +79,7 @@
 #include <gst/math-compat.h>
 
 #ifdef HAVE_ZLIB
-# include <zlib.h>
+#include <zlib.h>
 #endif
 
 /* max. size considered 'sane' for non-mdat atoms */
@@ -117,6 +117,34 @@ struct _QtDemuxSample
   gboolean keyframe;            /* TRUE when this packet is a keyframe */
 };
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+typedef struct _QtDemuxSphericalMetadata QtDemuxSphericalMetadata;
+
+struct _QtDemuxSphericalMetadata
+{
+  gboolean is_spherical;
+  gboolean is_stitched;
+  char *stitching_software;
+  char *projection_type;
+  char *stereo_mode;
+  int source_count;
+  int init_view_heading;
+  int init_view_pitch;
+  int init_view_roll;
+  int timestamp;
+  int full_pano_width_pixels;
+  int full_pano_height_pixels;
+  int cropped_area_image_width;
+  int cropped_area_image_height;
+  int cropped_area_left;
+  int cropped_area_top;
+  QTDEMUX_AMBISONIC_TYPE ambisonic_type;
+  QTDEMUX_AMBISONIC_FORMAT ambisonic_format;
+  QTDEMUX_AMBISONIC_ORDER ambisonic_order;
+};
+
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
 /* Macros for converting to/from timescale */
 #define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
 #define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))
@@ -414,6 +442,7 @@ struct _QtDemuxStream
 
   /* fragmented */
   gboolean parsed_trex;
+  guint32 def_sample_description_index; /* index is 1-based */
   guint32 def_sample_duration;
   guint32 def_sample_size;
   guint32 def_sample_flags;
@@ -583,6 +612,10 @@ static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
     const gchar * id);
 static void qtdemux_gst_structure_free (GstStructure * gststructure);
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+static void gst_tag_register_spherical_tags (void);
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
 static void
 gst_qtdemux_class_init (GstQTDemuxClass * klass)
 {
@@ -604,6 +637,10 @@ gst_qtdemux_class_init (GstQTDemuxClass * klass)
 
   gst_tag_register_musicbrainz_tags ();
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+  gst_tag_register_spherical_tags ();
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
   gst_element_class_add_static_pad_template (gstelement_class,
       &gst_qtdemux_sink_template);
   gst_element_class_add_static_pad_template (gstelement_class,
@@ -665,6 +702,33 @@ gst_qtdemux_init (GstQTDemux * qtdemux)
   gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
   qtdemux->flowcombiner = gst_flow_combiner_new ();
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+  qtdemux->spherical_metadata = (QtDemuxSphericalMetadata *)
+      malloc (sizeof (QtDemuxSphericalMetadata));
+
+  if (qtdemux->spherical_metadata) {
+    qtdemux->spherical_metadata->is_spherical = FALSE;
+    qtdemux->spherical_metadata->is_stitched = FALSE;
+    qtdemux->spherical_metadata->stitching_software = NULL;
+    qtdemux->spherical_metadata->projection_type = NULL;
+    qtdemux->spherical_metadata->stereo_mode = NULL;
+    qtdemux->spherical_metadata->source_count = 0;
+    qtdemux->spherical_metadata->init_view_heading = 0;
+    qtdemux->spherical_metadata->init_view_pitch = 0;
+    qtdemux->spherical_metadata->init_view_roll = 0;
+    qtdemux->spherical_metadata->timestamp = 0;
+    qtdemux->spherical_metadata->full_pano_width_pixels = 0;
+    qtdemux->spherical_metadata->full_pano_height_pixels = 0;
+    qtdemux->spherical_metadata->cropped_area_image_width = 0;
+    qtdemux->spherical_metadata->cropped_area_image_height = 0;
+    qtdemux->spherical_metadata->cropped_area_left = 0;
+    qtdemux->spherical_metadata->cropped_area_top = 0;
+    qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_UNKNOWN;
+    qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_UNKNOWN;
+    qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_UNKNOWN;
+  }
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
   GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
 }
 
@@ -673,6 +737,20 @@ gst_qtdemux_dispose (GObject * object)
 {
   GstQTDemux *qtdemux = GST_QTDEMUX (object);
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+  if (qtdemux->spherical_metadata) {
+    if (qtdemux->spherical_metadata->stitching_software)
+      free(qtdemux->spherical_metadata->stitching_software);
+    if (qtdemux->spherical_metadata->projection_type)
+      free(qtdemux->spherical_metadata->projection_type);
+    if (qtdemux->spherical_metadata->stereo_mode)
+      free(qtdemux->spherical_metadata->stereo_mode);
+
+    free(qtdemux->spherical_metadata);
+    qtdemux->spherical_metadata = NULL;
+  }
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
   if (qtdemux->adapter) {
     g_object_unref (G_OBJECT (qtdemux->adapter));
     qtdemux->adapter = NULL;
@@ -986,6 +1064,12 @@ gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
           stream->stream_tags);
       gst_pad_push_event (stream->pad,
           gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+      /* post message qtdemux tag (for early recive application) */
+      gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
+            gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
+                  gst_tag_list_copy (stream->stream_tags)));
+#endif
     }
 
     if (G_UNLIKELY (stream->send_global_tags)) {
@@ -1627,6 +1711,7 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
   GstSegment seeksegment;
   guint32 seqnum = 0;
   GstEvent *flush_event;
+  gboolean ret;
 
   if (event) {
     GST_DEBUG_OBJECT (qtdemux, "doing seek with event");
@@ -1673,13 +1758,19 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
   if (event) {
     /* configure the segment with the seek variables */
     GST_DEBUG_OBJECT (qtdemux, "configuring seek");
-    gst_segment_do_seek (&seeksegment, rate, format, flags,
-        cur_type, cur, stop_type, stop, &update);
+    if (!gst_segment_do_seek (&seeksegment, rate, format, flags,
+            cur_type, cur, stop_type, stop, &update)) {
+      ret = FALSE;
+      GST_ERROR_OBJECT (qtdemux, "inconsistent seek values, doing nothing");
+    } else {
+      /* now do the seek */
+      ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
+    }
+  } else {
+    /* now do the seek */
+    ret = gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
   }
 
-  /* now do the seek, this actually never returns FALSE */
-  gst_qtdemux_perform_seek (qtdemux, &seeksegment, seqnum, flags);
-
   /* prepare for streaming again */
   if (flush) {
     flush_event = gst_event_new_flush_stop (TRUE);
@@ -1707,7 +1798,7 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
 
   GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
 
-  return TRUE;
+  return ret;
 
   /* ERRORS */
 no_format:
@@ -1963,6 +2054,10 @@ gst_qtdemux_setcaps (GstQTDemux * demux, GstCaps * caps)
         stream = _create_stream ();
         demux->streams[demux->n_streams] = stream;
         demux->n_streams = 1;
+        /* mss has no stsd/stsd entry, use id 0 as default */
+        stream->stsd_entries_length = 1;
+        stream->stsd_sample_description_id = stream->cur_stsd_entry_index = 0;
+        stream->stsd_entries = g_new0 (QtDemuxStreamStsdEntry, 1);
       } else {
         stream = demux->streams[0];
       }
@@ -2536,6 +2631,7 @@ gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
     gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
     gst_flow_combiner_remove_pad (qtdemux->flowcombiner, stream->pad);
   }
+  g_free (stream->stsd_entries);
   g_free (stream);
 }
 
@@ -2618,6 +2714,482 @@ qtdemux_handle_xmp_taglist (GstQTDemux * qtdemux, GstTagList * taglist,
   }
 }
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+static void
+_get_int_value_from_xml_string (GstQTDemux * qtdemux,
+    const char *xml_str, const char *param_name, int *value)
+{
+  char *value_start, *value_end, *endptr;
+  const short value_length_max = 12;
+  char init_view_ret[12];
+  int value_length = 0;
+  int i = 0;
+
+  value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
+
+  if (!value_start) {
+    GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
+        param_name);
+    return;
+  }
+
+  value_start += strlen (param_name);
+  while ((value_start[0] == ' ') || (value_start[0] == '\t'))
+    value_start++;
+
+  value_end = strchr (value_start, '<');
+  if (!value_end) {
+    GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
+    return;
+  }
+
+  value_length = value_end - value_start;
+  while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
+          || (value_start[value_length - 1] == '\t')))
+    value_length--;
+
+  if (value_start[i] == '+' || value_start[i] == '-')
+    i++;
+  while (i < value_length) {
+    if (value_start[i] < '0' || value_start[i] > '9') {
+      GST_ERROR_OBJECT (qtdemux,
+          "error: incorrect value, integer was expected\n");
+      return;
+    }
+    i++;
+  }
+
+  if (value_length >= value_length_max || value_length < 1) {
+    GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
+    return;
+  }
+
+  strncpy (init_view_ret, value_start, value_length_max);
+  init_view_ret[value_length] = '\0';
+
+  *value = strtol (init_view_ret, &endptr, 10);
+  if (endptr == init_view_ret) {
+    GST_ERROR_OBJECT (qtdemux, "error: no digits were found\n");
+    return;
+  }
+
+  return;
+}
+
+static void
+_get_string_value_from_xml_string (GstQTDemux * qtdemux,
+    const char *xml_str, const char *param_name, char **value)
+{
+  char *value_start, *value_end;
+  const short value_length_max = 256;
+  int value_length = 0;
+
+  value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
+
+  if (!value_start) {
+    GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
+        param_name);
+    return;
+  }
+
+  value_start += strlen (param_name);
+  while ((value_start[0] == ' ') || (value_start[0] == '\t'))
+    value_start++;
+
+  value_end = strchr (value_start, '<');
+  if (!value_end) {
+    GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
+    return;
+  }
+
+  value_length = value_end - value_start;
+  while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
+          || (value_start[value_length - 1] == '\t')))
+    value_length--;
+
+  if (value_length >= value_length_max || value_length < 1) {
+    GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
+    return;
+  }
+
+  *value = (char *) malloc (value_length);
+
+  if (*value == NULL) {
+    GST_ERROR_OBJECT (qtdemux, "error: malloc failed\n");
+    return;
+  }
+
+  strncpy (*value, value_start, value_length);
+
+  return;
+}
+
+static void
+_get_bool_value_from_xml_string (GstQTDemux * qtdemux,
+    const char *xml_str, const char *param_name, gboolean * value)
+{
+  char *value_start, *value_end;
+  int value_length = 0;
+
+  value_start = (xml_str && param_name) ? strstr (xml_str, param_name) : NULL;
+
+  if (!value_start) {
+    GST_WARNING_OBJECT (qtdemux, "error: parameter does not exist: %s\n",
+        param_name);
+    return;
+  }
+
+  value_start += strlen (param_name);
+  while ((value_start[0] == ' ') || (value_start[0] == '\t'))
+    value_start++;
+
+  value_end = strchr (value_start, '<');
+  if (!value_end) {
+    GST_ERROR_OBJECT (qtdemux, "error: incorrect XML\n");
+    return;
+  }
+
+  value_length = value_end - value_start;
+  while ((value_length >= 1) && ((value_start[value_length - 1] == ' ')
+          || (value_start[value_length - 1] == '\t')))
+    value_length--;
+
+  if (value_length < 1) {
+    GST_ERROR_OBJECT (qtdemux, "error: empty XML value or incorrect range\n");
+    return;
+  }
+
+  *value = strstr (value_start, "true") ? TRUE : FALSE;
+
+  return;
+}
+
+static void
+_parse_spatial_video_metadata_from_xml_string (GstQTDemux * qtdemux, const char *xmlStr)
+{
+  const char is_spherical_str[] = "<GSpherical:Spherical>";
+  const char is_stitched_str[] = "<GSpherical:Stitched>";
+  const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
+  const char projection_type_str[] = "<GSpherical:ProjectionType>";
+  const char stereo_mode_str[] = "<GSpherical:StereoMode>";
+  const char source_count_str[] = "<GSpherical:SourceCount>";
+  const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
+  const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
+  const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
+  const char timestamp_str[] = "<GSpherical:Timestamp>";
+  const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
+  const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
+  const char cropped_area_image_width_str[] =
+      "<GSpherical:CroppedAreaImageWidthPixels>";
+  const char cropped_area_image_height_str[] =
+      "<GSpherical:CroppedAreaImageHeightPixels>";
+  const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
+  const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
+
+  QtDemuxSphericalMetadata * spherical_metadata = qtdemux->spherical_metadata;
+
+  _get_bool_value_from_xml_string (qtdemux, xmlStr, is_spherical_str,
+      (gboolean *) & spherical_metadata->is_spherical);
+  _get_bool_value_from_xml_string (qtdemux, xmlStr, is_stitched_str,
+      (gboolean *) & spherical_metadata->is_stitched);
+
+  if (spherical_metadata->is_spherical && spherical_metadata->is_stitched) {
+    _get_string_value_from_xml_string (qtdemux, xmlStr,
+        stitching_software_str, &spherical_metadata->stitching_software);
+    _get_string_value_from_xml_string (qtdemux, xmlStr,
+        projection_type_str, &spherical_metadata->projection_type);
+    _get_string_value_from_xml_string (qtdemux, xmlStr, stereo_mode_str,
+        &spherical_metadata->stereo_mode);
+    _get_int_value_from_xml_string (qtdemux, xmlStr, source_count_str,
+        &spherical_metadata->source_count);
+    _get_int_value_from_xml_string (qtdemux, xmlStr,
+        init_view_heading_str, &spherical_metadata->init_view_heading);
+    _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_pitch_str,
+        &spherical_metadata->init_view_pitch);
+    _get_int_value_from_xml_string (qtdemux, xmlStr, init_view_roll_str,
+        &spherical_metadata->init_view_roll);
+    _get_int_value_from_xml_string (qtdemux, xmlStr, timestamp_str,
+        &spherical_metadata->timestamp);
+    _get_int_value_from_xml_string (qtdemux, xmlStr, full_pano_width_str,
+        &spherical_metadata->full_pano_width_pixels);
+    _get_int_value_from_xml_string (qtdemux, xmlStr,
+        full_pano_height_str, &spherical_metadata->full_pano_height_pixels);
+    _get_int_value_from_xml_string (qtdemux, xmlStr,
+        cropped_area_image_width_str,
+        &spherical_metadata->cropped_area_image_width);
+    _get_int_value_from_xml_string (qtdemux, xmlStr,
+        cropped_area_image_height_str,
+        &spherical_metadata->cropped_area_image_height);
+    _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_left_str,
+        &spherical_metadata->cropped_area_left);
+    _get_int_value_from_xml_string (qtdemux, xmlStr, cropped_area_top_str,
+        &spherical_metadata->cropped_area_top);
+  }
+
+  return;
+}
+
+static void
+gst_tag_register_spherical_tags (void) {
+  gst_tag_register ("is_spherical", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-spherical"),
+      _("Flag indicating if the video is a spherical video"),
+      NULL);
+  gst_tag_register ("is_stitched", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-stitched"),
+      _("Flag indicating if the video is stitched"),
+      NULL);
+  gst_tag_register ("stitching_software", GST_TAG_FLAG_META,
+      G_TYPE_STRING,
+      _("tag-stitching-software"),
+      _("Software used to stitch the spherical video"),
+      NULL);
+  gst_tag_register ("projection_type", GST_TAG_FLAG_META,
+      G_TYPE_STRING,
+      _("tag-projection-type"),
+      _("Projection type used in the video frames"),
+      NULL);
+  gst_tag_register ("stereo_mode", GST_TAG_FLAG_META,
+      G_TYPE_STRING,
+      _("tag-stereo-mode"),
+      _("Description of stereoscopic 3D layout"),
+      NULL);
+  gst_tag_register ("source_count", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-source-count"),
+      _("Number of cameras used to create the spherical video"),
+      NULL);
+  gst_tag_register ("init_view_heading", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-init-view-heading"),
+      _("The heading angle of the initial view in degrees"),
+      NULL);
+  gst_tag_register ("init_view_pitch", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-init-view-pitch"),
+      _("The pitch angle of the initial view in degrees"),
+      NULL);
+  gst_tag_register ("init_view_roll", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-init-view-roll"),
+      _("The roll angle of the initial view in degrees"),
+      NULL);
+  gst_tag_register ("timestamp", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-timestamp"),
+      _("Epoch timestamp of when the first frame in the video was recorded"),
+      NULL);
+  gst_tag_register ("full_pano_width_pixels", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-full-pano-width"),
+      _("Width of the encoded video frame in pixels"),
+      NULL);
+  gst_tag_register ("full_pano_height_pixels", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-full-pano-height"),
+      _("Height of the encoded video frame in pixels"),
+      NULL);
+  gst_tag_register ("cropped_area_image_width", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-cropped-area-image-width"),
+      _("Width of the video frame to display (e.g. cropping)"),
+      NULL);
+  gst_tag_register ("cropped_area_image_height", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-cropped-area-image-height"),
+      _("Height of the video frame to display (e.g. cropping)"),
+      NULL);
+  gst_tag_register ("cropped_area_left", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-cropped-area-left"),
+      _("Column where the left edge of the image was cropped from the"
+          " full sized panorama"),
+      NULL);
+  gst_tag_register ("cropped_area_top", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-cropped-area-top"),
+      _("Row where the top edge of the image was cropped from the"
+          " full sized panorama"),
+      NULL);
+  gst_tag_register ("ambisonic_type", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-ambisonic-type"),
+      _("Specifies the type of ambisonic audio represented"),
+      NULL);
+  gst_tag_register ("ambisonic_format", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-ambisonic-format"),
+      _("Specifies the ambisonic audio format"),
+      NULL);
+  gst_tag_register ("ambisonic_order", GST_TAG_FLAG_META,
+      G_TYPE_INT,
+      _("tag-ambisonic-order"),
+      _("Specifies the ambisonic audio channel order"),
+      NULL);
+
+  return;
+}
+
+static void
+_send_spherical_metadata_msg_to_bus (GstQTDemux * qtdemux)
+{
+  GstTagList *taglist;
+  QtDemuxSphericalMetadata *spherical_metadata = qtdemux->spherical_metadata;
+
+  GST_DEBUG_OBJECT (qtdemux, "is_spherical = %d",
+      spherical_metadata->is_spherical);
+  GST_DEBUG_OBJECT (qtdemux, "is_stitched = %d",
+      spherical_metadata->is_stitched);
+  GST_DEBUG_OBJECT (qtdemux, "stitching_software = %s",
+      spherical_metadata->stitching_software);
+  GST_DEBUG_OBJECT (qtdemux, "projection_type = %s",
+      spherical_metadata->projection_type);
+  GST_DEBUG_OBJECT (qtdemux, "stereo_mode = %s",
+      spherical_metadata->stereo_mode);
+  GST_DEBUG_OBJECT (qtdemux, "source_count %d",
+      spherical_metadata->source_count);
+  GST_DEBUG_OBJECT (qtdemux, "init_view_heading = %d",
+      spherical_metadata->init_view_heading);
+  GST_DEBUG_OBJECT (qtdemux, "init_view_pitch = %d",
+      spherical_metadata->init_view_pitch);
+  GST_DEBUG_OBJECT (qtdemux, "init_view_roll = %d",
+      spherical_metadata->init_view_roll);
+  GST_DEBUG_OBJECT (qtdemux, "timestamp = %d", spherical_metadata->timestamp);
+  GST_DEBUG_OBJECT (qtdemux, "full_pano_width_pixels = %d",
+      spherical_metadata->full_pano_width_pixels);
+  GST_DEBUG_OBJECT (qtdemux, "full_pano_height_pixels = %d",
+      spherical_metadata->full_pano_height_pixels);
+  GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_width = %d",
+      spherical_metadata->cropped_area_image_width);
+  GST_DEBUG_OBJECT (qtdemux, "cropped_area_image_height = %d",
+      spherical_metadata->cropped_area_image_height);
+  GST_DEBUG_OBJECT (qtdemux, "cropped_area_left = %d",
+      spherical_metadata->cropped_area_left);
+  GST_DEBUG_OBJECT (qtdemux, "cropped_area_top = %d",
+      spherical_metadata->cropped_area_top);
+  GST_DEBUG_OBJECT (qtdemux, "ambisonic_type = %d",
+      spherical_metadata->ambisonic_type);
+  GST_DEBUG_OBJECT (qtdemux, "ambisonic_order = %d",
+      spherical_metadata->ambisonic_order);
+  GST_DEBUG_OBJECT (qtdemux, "ambisonic_format = %d",
+      spherical_metadata->ambisonic_format);
+
+  taglist = gst_tag_list_new_empty ();
+  gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
+      "is_spherical", spherical_metadata->is_spherical,
+      "is_stitched", spherical_metadata->is_stitched,
+      "source_count", spherical_metadata->source_count,
+      "init_view_heading", spherical_metadata->init_view_heading,
+      "init_view_pitch", spherical_metadata->init_view_pitch,
+      "init_view_roll", spherical_metadata->init_view_roll,
+      "timestamp", spherical_metadata->timestamp,
+      "full_pano_width_pixels", spherical_metadata->full_pano_width_pixels,
+      "full_pano_height_pixels", spherical_metadata->full_pano_height_pixels,
+      "cropped_area_image_width", spherical_metadata->cropped_area_image_width,
+      "cropped_area_image_height", spherical_metadata->cropped_area_image_height,
+      "cropped_area_left", spherical_metadata->cropped_area_left,
+      "cropped_area_top", spherical_metadata->cropped_area_top,
+      "ambisonic_type", spherical_metadata->ambisonic_type,
+      "ambisonic_format", spherical_metadata->ambisonic_format,
+      "ambisonic_order", spherical_metadata->ambisonic_order,
+      NULL);
+
+  if (spherical_metadata->stitching_software)
+    gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
+        "stitching_software", spherical_metadata->stitching_software,
+        NULL);
+  if (spherical_metadata->projection_type)
+    gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
+        "projection_type", spherical_metadata->projection_type,
+        NULL);
+  if (spherical_metadata->stereo_mode)
+    gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
+        "stereo_mode", spherical_metadata->stereo_mode,
+        NULL);
+
+  gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
+          gst_message_new_tag (GST_OBJECT_CAST (qtdemux),
+                  gst_tag_list_copy (taglist)));
+
+  gst_tag_list_unref(taglist);
+
+  return;
+}
+
+static void
+qtdemux_parse_SA3D (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
+{
+  guint offset = 0;
+
+  guint8 version = 0;
+  guint8 ambisonic_type  = 0;
+  guint32 ambisonic_order = 0;
+  guint8 ambisonic_channel_ordering = 0;
+  guint8 ambisonic_normalization = 0;
+  guint32 num_channels = 0;
+  guint32 channel_map[49] = { 0 };      /* Up to 6th order */
+
+  int i;
+
+  GST_DEBUG_OBJECT (qtdemux, "qtdemux_parse_SA3D");
+
+  qtdemux->header_size += length;
+  offset = (QT_UINT32 (buffer) == 0) ? 16 : 8;
+
+  if (length <= offset + 16) {
+    GST_DEBUG_OBJECT (qtdemux, "SA3D atom is too short, skipping");
+    return;
+  }
+
+  version = QT_UINT8 (buffer + offset);
+  ambisonic_type = QT_UINT8 (buffer + offset + 1);
+  ambisonic_order = QT_UINT32 (buffer + offset + 2);
+  ambisonic_channel_ordering = QT_UINT8 (buffer + offset + 6);
+  ambisonic_normalization = QT_UINT8 (buffer + offset + 7);
+  num_channels = QT_UINT32 (buffer + offset + 8);
+  for (i = 0; i < num_channels; ++i)
+    channel_map[i] = QT_UINT32 (buffer + offset + 12 + i * 4);
+
+  GST_DEBUG_OBJECT (qtdemux, "version: %d", version);
+  GST_DEBUG_OBJECT (qtdemux, "ambisonic_type: %d", ambisonic_type);
+  GST_DEBUG_OBJECT (qtdemux, "ambisonic_order: %d", ambisonic_order);
+  GST_DEBUG_OBJECT (qtdemux, "ambisonic_channel_ordering: %d",
+      ambisonic_channel_ordering);
+  GST_DEBUG_OBJECT (qtdemux, "ambisonic_normalization: %d",
+      ambisonic_normalization);
+  GST_DEBUG_OBJECT (qtdemux, "num_channels: %d", num_channels);
+  for (i = 0; i < num_channels; ++i)
+    GST_DEBUG_OBJECT (qtdemux, "channel_map: %d", channel_map[i]);
+
+  if (version == RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
+    if (ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
+      qtdemux->spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_PERIPHONIC;
+
+    if (ambisonic_order == RFC_AMBISONIC_ORDER_FOA) {
+      if (num_channels == 4) {
+        qtdemux->spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_FOA;
+
+        if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN)
+            && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D)
+            && (channel_map[0] == 0) && (channel_map[1] == 1)
+            && (channel_map[2] == 2) && (channel_map[3] == 3))
+          qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMBIX;
+
+        if ((ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA)
+            && (ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA)
+            && (channel_map[0] == 0) && (channel_map[1] == 3)
+            && (channel_map[2] == 1) && (channel_map[3] == 2))
+          qtdemux->spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMB;
+      }
+    }
+  }
+
+  return;
+}
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
 static void
 qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
     guint offset)
@@ -2804,6 +3376,13 @@ qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
     0xa2, 0x44, 0x6c, 0x42, 0x7c, 0x64, 0x8d, 0xf4
   };
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+  static const guint8 spherical_uuid[] = {
+    0xff, 0xcc, 0x82, 0x63, 0xf8, 0x55, 0x4a, 0x93,
+    0x88, 0x14, 0x58, 0x7a, 0x02, 0x52, 0x1f, 0xdd
+  };
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
   guint offset;
 
   /* counts as header data */
@@ -2816,6 +3395,21 @@ qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
     return;
   }
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+  if (memcmp (buffer + offset, spherical_uuid, 16) == 0) {
+    const char *contents;
+
+    GST_DEBUG_OBJECT (qtdemux, "spherical uuid was found");
+    contents = (char *) (buffer + offset + 16);
+    GST_DEBUG_OBJECT (qtdemux, "contents: %s\n", contents);
+
+    if (qtdemux->spherical_metadata)
+      _parse_spatial_video_metadata_from_xml_string (qtdemux, contents);
+
+    return;
+  }
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
   if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
     GstBuffer *buf;
     GstTagList *taglist;
@@ -2949,7 +3543,7 @@ qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
       trex = qtdemux_tree_get_child_by_type_full (mvex, FOURCC_trex,
           &trex_data);
       while (trex) {
-        guint32 id = 0, dur = 0, size = 0, flags = 0, dummy = 0;
+        guint32 id = 0, sdi = 0, dur = 0, size = 0, flags = 0;
 
         /* skip version/flags */
         if (!gst_byte_reader_skip (&trex_data, 4))
@@ -2958,8 +3552,7 @@ qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
           goto next;
         if (id != stream->track_id)
           goto next;
-        /* sample description index; ignore */
-        if (!gst_byte_reader_get_uint32_be (&trex_data, &dummy))
+        if (!gst_byte_reader_get_uint32_be (&trex_data, &sdi))
           goto next;
         if (!gst_byte_reader_get_uint32_be (&trex_data, &dur))
           goto next;
@@ -2973,6 +3566,7 @@ qtdemux_parse_trex (GstQTDemux * qtdemux, QtDemuxStream * stream,
             dur, size, flags);
 
         stream->parsed_trex = TRUE;
+        stream->def_sample_description_index = sdi;
         stream->def_sample_duration = dur;
         stream->def_sample_size = size;
         stream->def_sample_flags = flags;
@@ -3425,10 +4019,20 @@ qtdemux_parse_tfhd (GstQTDemux * qtdemux, GstByteReader * tfhd,
   qtdemux_parse_trex (qtdemux, *stream,
       default_sample_duration, default_sample_size, default_sample_flags);
 
-  /* FIXME: Handle TF_SAMPLE_DESCRIPTION_INDEX properly */
-  if (flags & TF_SAMPLE_DESCRIPTION_INDEX)
-    if (!gst_byte_reader_skip (tfhd, 4))
+  (*stream)->stsd_sample_description_id =
+      (*stream)->def_sample_description_index - 1;
+
+  if (flags & TF_SAMPLE_DESCRIPTION_INDEX) {
+    guint32 sample_description_index;
+    if (!gst_byte_reader_get_uint32_be (tfhd, &sample_description_index))
       goto invalid_track;
+    (*stream)->stsd_sample_description_id = sample_description_index - 1;
+  }
+
+  if (qtdemux->mss_mode) {
+    /* mss has no stsd entry */
+    (*stream)->stsd_sample_description_id = 0;
+  }
 
   if (flags & TF_DEFAULT_SAMPLE_DURATION)
     if (!gst_byte_reader_get_uint32_be (tfhd, default_sample_duration))
@@ -3919,7 +4523,8 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
 
       GST_DEBUG_OBJECT (qtdemux, "decode time %" G_GINT64_FORMAT
           " (%" GST_TIME_FORMAT ")", decode_time,
-          GST_TIME_ARGS (QTSTREAMTIME_TO_GSTTIME (stream, decode_time)));
+          GST_TIME_ARGS (stream ? QTSTREAMTIME_TO_GSTTIME (stream,
+                  decode_time) : GST_CLOCK_TIME_NONE));
 
       /* Discard the fragment buffer timestamp info to avoid using it.
        * Rely on tfdt instead as it is more accurate than the timestamp
@@ -4423,6 +5028,10 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
 beach:
   if (ret == GST_FLOW_EOS && (qtdemux->got_moov || qtdemux->media_caps)) {
     /* digested all data, show what we have */
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+    if (qtdemux->spherical_metadata)
+      _send_spherical_metadata_msg_to_bus (qtdemux);
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
     qtdemux_prepare_streams (qtdemux);
     ret = qtdemux_expose_streams (qtdemux);
 
@@ -6322,10 +6931,12 @@ gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
         }
       }
       if (!is_gap_input) {
+        GST_DEBUG_OBJECT (demux, "Resetting, actual DISCONT");
         /* Reset state if it's a real discont */
         demux->neededbytes = 16;
         demux->state = QTDEMUX_STATE_INITIAL;
         demux->offset = GST_BUFFER_OFFSET (inbuf);
+        gst_adapter_clear (demux->adapter);
       }
     }
     /* Reverse fragmented playback, need to flush all we have before
@@ -6882,6 +7493,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
 
             /* skip this data, stream is EOS */
             gst_adapter_flush (demux->adapter, demux->neededbytes);
+            demux->offset += demux->neededbytes;
 
             /* check if all streams are eos */
             ret = GST_FLOW_EOS;
@@ -6891,11 +7503,6 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
                 break;
               }
             }
-
-            if (ret == GST_FLOW_EOS) {
-              GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
-              goto eos;
-            }
           } else {
             GstBuffer *outbuf;
 
@@ -6926,6 +7533,13 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
         GST_LOG_OBJECT (demux, "offset is now %" G_GUINT64_FORMAT,
             demux->offset);
 
+
+        if (ret == GST_FLOW_EOS) {
+          GST_DEBUG_OBJECT (demux, "All streams are EOS, signal upstream");
+          demux->neededbytes = -1;
+          goto eos;
+        }
+
         if ((demux->neededbytes = next_entry_size (demux)) == -1) {
           if (demux->fragmented) {
             GST_DEBUG_OBJECT (demux, "(temporarily) out of fragmented samples");
@@ -7505,6 +8119,13 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
         qtdemux_parse_container (qtdemux, node, buffer + 36, end);
         break;
       }
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+      case FOURCC_SA3D:
+      {
+        qtdemux_parse_SA3D (qtdemux, buffer, end - buffer);
+        break;
+      }
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
       default:
         if (!strcmp (type->name, "unknown"))
           GST_MEMDUMP ("Unknown tag", buffer + 4, end - buffer - 4);
@@ -7964,8 +8585,16 @@ gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
 
   GST_DEBUG_OBJECT (stream->pad, "Changing stsd index from '%u' to '%u'",
       stream->cur_stsd_entry_index, stream->stsd_sample_description_id);
-  stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
-  stream->new_caps = TRUE;
+  if (G_UNLIKELY (stream->stsd_sample_description_id >=
+          stream->stsd_entries_length)) {
+    GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
+        (_("This file is invalid and cannot be played.")),
+        ("New sample description id is out of bounds (%d >= %d)",
+            stream->stsd_sample_description_id, stream->stsd_entries_length));
+  } else {
+    stream->cur_stsd_entry_index = stream->stsd_sample_description_id;
+    stream->new_caps = TRUE;
+  }
 }
 
 static gboolean
@@ -10599,7 +11228,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
           case FOURCC_WRLE:
           {
             gst_caps_set_simple (entry->caps,
-                "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 82),
+                "depth", G_TYPE_INT, QT_UINT16 (stsd_entry_data + offset + 66),
                 NULL);
             break;
           }