Extract spherical video and spatial audio metadata and send it to the bus 78/152978/7 accepted/tizen/unified/20171031.055723 submit/tizen/20171030.055657
authorMykola Alieksieiev <m.alieksieie@samsung.com>
Wed, 27 Sep 2017 11:51:28 +0000 (14:51 +0300)
committerMykola Alieksieiev <m.alieksieie@samsung.com>
Thu, 28 Sep 2017 09:40:14 +0000 (12:40 +0300)
Change-Id: I0126c6afa12e5843587030fd5ec236f6ba2c507b
Signed-off-by: Mykola Alieksieiev <m.alieksieie@samsung.com>
gst/isomp4/fourcc.h
gst/isomp4/qtdemux.c
gst/isomp4/qtdemux_types.c
gst/isomp4/qtdemux_types.h

index fa991a5..1dd6c62 100644 (file)
@@ -346,6 +346,11 @@ G_BEGIN_DECLS
 #define FOURCC_tenc     GST_MAKE_FOURCC('t','e','n','c')
 #define FOURCC_cenc     GST_MAKE_FOURCC('c','e','n','c')
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+/* Spatial Audio */
+#define FOURCC_SA3D     GST_MAKE_FOURCC('S','A','3','D')
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
 G_END_DECLS
 
 #endif /* __FOURCC_H__ */
index 9aac858..49a924e 100644 (file)
@@ -78,7 +78,7 @@
 #include <gst/math-compat.h>
 
 #ifdef HAVE_ZLIB
-# include <zlib.h>
+#include <zlib.h>
 #endif
 
 /* max. size considered 'sane' for non-mdat atoms */
@@ -120,6 +120,35 @@ 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;
+};
+
+QtDemuxSphericalMetadata *spherical_metadata;
+#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))
@@ -530,6 +559,10 @@ static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
 static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
     const gchar * id);
 
+#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)
 {
@@ -551,6 +584,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_pad_template (gstelement_class,
       gst_static_pad_template_get (&gst_qtdemux_sink_template));
   gst_element_class_add_pad_template (gstelement_class,
@@ -607,6 +644,33 @@ gst_qtdemux_init (GstQTDemux * qtdemux)
   gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
   qtdemux->flowcombiner = gst_flow_combiner_new ();
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+  spherical_metadata = (QtDemuxSphericalMetadata *)
+      malloc (sizeof (QtDemuxSphericalMetadata));
+
+  if (spherical_metadata) {
+    spherical_metadata->is_spherical = FALSE;
+    spherical_metadata->is_stitched = FALSE;
+    spherical_metadata->stitching_software = NULL;
+    spherical_metadata->projection_type = NULL;
+    spherical_metadata->stereo_mode = NULL;
+    spherical_metadata->source_count = 0;
+    spherical_metadata->init_view_heading = 0;
+    spherical_metadata->init_view_pitch = 0;
+    spherical_metadata->init_view_roll = 0;
+    spherical_metadata->timestamp = 0;
+    spherical_metadata->full_pano_width_pixels = 0;
+    spherical_metadata->full_pano_height_pixels = 0;
+    spherical_metadata->cropped_area_image_width = 0;
+    spherical_metadata->cropped_area_image_height = 0;
+    spherical_metadata->cropped_area_left = 0;
+    spherical_metadata->cropped_area_top = 0;
+    spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_UNKNOWN;
+    spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_TYPE_UNKNOWN;
+    spherical_metadata->ambisonic_order = QTDEMUX_AMBISONIC_ORDER_UNKNOWN;
+  }
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
   GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
 }
 
@@ -615,6 +679,17 @@ gst_qtdemux_dispose (GObject * object)
 {
   GstQTDemux *qtdemux = GST_QTDEMUX (object);
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+  if (spherical_metadata->stitching_software)
+    free(spherical_metadata->stitching_software);
+  if (spherical_metadata->projection_type)
+    free(spherical_metadata->projection_type);
+  if (spherical_metadata->stereo_mode)
+    free(spherical_metadata->stereo_mode);
+  if (spherical_metadata)
+    free(spherical_metadata);
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
   if (qtdemux->adapter) {
     g_object_unref (G_OBJECT (qtdemux->adapter));
     qtdemux->adapter = NULL;
@@ -2388,6 +2463,481 @@ 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, QtDemuxSphericalMetadata * spherical_metadata)
+{
+  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>";
+
+  _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_message_to_bus (GstQTDemux * qtdemux,
+    QtDemuxSphericalMetadata * spherical_metadata)
+{
+  GstTagList *taglist;
+
+  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)
+      spherical_metadata->ambisonic_type = QTDEMUX_AMBISONIC_TYPE_PERIPHONIC;
+
+    if (ambisonic_order == RFC_AMBISONIC_ORDER_FOA) {
+      if (num_channels == 4) {
+        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))
+          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))
+          spherical_metadata->ambisonic_format = QTDEMUX_AMBISONIC_FORMAT_AMB;
+      }
+    }
+  }
+
+  return;
+}
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
 static void
 qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
 {
@@ -2400,6 +2950,14 @@ qtdemux_parse_uuid (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
     0xd0, 0x8a, 0x4f, 0x18, 0x10, 0xf3, 0x4a, 0x82,
     0xb6, 0xc8, 0x32, 0xd8, 0xab, 0xa1, 0x83, 0xd3
   };
+
+#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 */
@@ -2412,6 +2970,22 @@ 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 (spherical_metadata)
+      _parse_spatial_video_metadata_from_xml_string (qtdemux, contents,
+          spherical_metadata);
+
+    return;
+  }
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
   if (memcmp (buffer + offset, xmp_uuid, 16) == 0) {
     GstBuffer *buf;
     GstTagList *taglist;
@@ -3910,6 +4484,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 (spherical_metadata)
+      _send_message_to_bus (qtdemux, spherical_metadata);
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
     qtdemux_prepare_streams (qtdemux);
     ret = qtdemux_expose_streams (qtdemux);
 
@@ -6584,6 +7162,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);
@@ -12431,6 +13016,7 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
 
   switch (fourcc) {
     case GST_MAKE_FOURCC ('N', 'O', 'N', 'E'):
+      /* fall-through */
     case GST_MAKE_FOURCC ('r', 'a', 'w', ' '):
       /* 8-bit audio is unsigned */
       if (depth == 8)
index 8e8189d..114bfd3 100644 (file)
@@ -201,6 +201,9 @@ static const QtNodeType qt_node_types[] = {
   {FOURCC_schi, "scheme information", QT_FLAG_CONTAINER},
   {FOURCC_pssh, "protection system specific header", 0},
   {FOURCC_tenc, "track encryption", 0},
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+  {FOURCC_SA3D, "spatial sound", 0,},
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
   {0, "unknown", 0,},
 };
 
index 43ef77c..441e3b0 100644 (file)
@@ -43,6 +43,16 @@ typedef struct _QtNodeType QtNodeType;
 #define QT_FOURCC(a)  (GST_READ_UINT32_LE(a))
 #define QT_UINT64(a)  ((((guint64)QT_UINT32(a))<<32)|QT_UINT32(((guint8 *)a)+4))
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+#define RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED 0
+#define RFC_AMBISONIC_TYPE_PERIPHONIC 0
+#define RFC_AMBISONIC_ORDER_FOA 1
+#define RFC_AMBISONIC_CHANNEL_ORDERING_ACN 0
+#define RFC_AMBISONIC_CHANNEL_ORDERING_FUMA 1  /* FIXME: Currently value is not defined in Spatial Audio RFC */
+#define RFC_AMBISONIC_NORMALIZATION_SN3D 0
+#define RFC_AMBISONIC_NORMALIZATION_FUMA 1             /* FIXME: Currently value is not defined in Spatial Audio RFC */
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
 typedef enum {
   QT_FLAG_NONE      = (0),
   QT_FLAG_CONTAINER = (1 << 0)
@@ -76,6 +86,47 @@ enum TrFlags
   TR_COMPOSITION_TIME_OFFSETS = 0x000800    /* sample-composition-time-offsets-presents */
 };
 
+#ifdef TIZEN_FEATURE_QTDEMUX_MODIFICATION
+typedef enum
+{
+  QTDEMUX_AMBISONIC_TYPE_UNKNOWN = 0,
+  QTDEMUX_AMBISONIC_TYPE_PERIPHONIC = 1,            /* To comply with Google's Spatial Audio RFC */
+  QTDEMUX_AMBISONIC_TYPE_NON_PERIPHONIC = 2,
+} QTDEMUX_AMBISONIC_TYPE;
+
+typedef enum
+{
+  QTDEMUX_AMBISONIC_FORMAT_UNKNOWN = 0,
+  QTDEMUX_AMBISONIC_FORMAT_AMBIX = 1,               /* AMBIX (Channel sequence: ACN, Normalization: SN3D) */
+  QTDEMUX_AMBISONIC_FORMAT_AMB = 2,                 /* .AMB, Tetraproc (Channel sequence: FuMa, Normalization: FuMa) */
+  QTDEMUX_AMBISONIC_FORMAT_UA = 3,                  /* Universal Ambisonics (Channel sequence: SID, Normalization: N3D) */
+} QTDEMUX_AMBISONIC_FORMAT;
+
+typedef enum
+{
+  QTDEMUX_AMBISONIC_ORDER_UNKNOWN = 0,
+  QTDEMUX_AMBISONIC_ORDER_FOA = 1,                  /* First order Ambisonics */
+  QTDEMUX_AMBISONIC_ORDER_TOA = 3,                  /* Third order Ambisonics */
+  QTDEMUX_AMBISONIC_ORDER_HOA,                      /* Higher order Ambisonics */
+} QTDEMUX_AMBISONIC_ORDER;
+
+typedef enum
+{
+  QTDEMUX_AMBISONIC_CHANNEL_ORDERING_UNKNOWN = 0,
+  QTDEMUX_AMBISONIC_CHANNEL_ORDERING_ACN = 1,       /* Ambisonic Channel Number (ACN) system */
+  QTDEMUX_AMBISONIC_CHANNEL_ORDERING_FUMA = 2,      /* Furse-Malham ordering */
+  QTDEMUX_AMBISONIC_CHANNEL_ORDERING_SID = 3,       /* Single Index Designation ordering */
+} QTDEMUX_AMBISONIC_CHANNEL_ORDERING;
+
+typedef enum
+{
+  QTDEMUX_AMBISONIC_NORMALIZATION_UNKNOWN = 0,
+  QTDEMUX_AMBISONIC_NORMALIZATION_SN3D = 1,         /* Schmidt semi-normalization */
+  QTDEMUX_AMBISONIC_NORMALIZATION_FUMA = 2,         /* Furse-Malham MaxN normalization */
+  QTDEMUX_AMBISONIC_NORMALIZATION_N3D = 3,          /* Full 3D normalization */
+} QTDEMUX_AMBISONIC_NORMALIZATION;
+#endif /* TIZEN_FEATURE_QTDEMUX_MODIFICATION */
+
 const QtNodeType *qtdemux_type_get (guint32 fourcc);
 
 G_END_DECLS