Add support for Spherical Video and Ambisonics Audio Metadata 50/123150/10
authorMykola Alieksieiev <m.alieksieie@samsung.com>
Tue, 4 Apr 2017 17:14:29 +0000 (20:14 +0300)
committerMykola Alieksieiev <m.alieksieie@samsung.com>
Mon, 3 Jul 2017 06:04:22 +0000 (09:04 +0300)
* Spherical Video Metadata V1 in mp4 files
* Spatial audio metadata extraction from SA3D atom nested in mp4a atom
* Supported different ambisonics types, orders and formats

Change-Id: I0768ef0e8f6875c47d1b58b35e4cbea68dacaf52
Signed-off-by: Mykola Alieksieiev <m.alieksieie@samsung.com>
formats/ffmpeg/mm_file_formats.c
include/mm_file.h
include/mm_file_formats.h
mm_file.c
tests/mm_file_test.c
utils/mm_file_util_tag.c

index 93dfa39..b2e9c2b 100755 (executable)
@@ -142,6 +142,9 @@ static int _CleanupFrameContext(MMFileFormatContext *formatContext, bool clean_a
                if (formatContext->conductor)                   mmfile_free(formatContext->conductor);
                if (formatContext->unsyncLyrics)                mmfile_free(formatContext->unsyncLyrics);
                if (formatContext->rotate)                              mmfile_free(formatContext->rotate);
+               if (formatContext->stereoMode)                  mmfile_free(formatContext->stereoMode);
+               if (formatContext->stitchingSoftware)   mmfile_free(formatContext->stitchingSoftware);
+               if (formatContext->projectionType)              mmfile_free(formatContext->projectionType);
 
                if (clean_all)  /*syncLyrics has to be freed in mm_file_destroy_tag_attrs() except abnormal status */
                        if (formatContext->syncLyrics)                  mm_file_free_synclyrics_list(formatContext->syncLyrics);
index 9067824..7afa290 100755 (executable)
@@ -58,6 +58,46 @@ typedef enum {
        MMFILE_360_MASK = 0x00001111,
 } MMFILE_360_TYPE;
 
+#define RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED 0
+#define RFC_AMBISONIC_TYPE_PERIPHONIC 0
+#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 */
+
+typedef enum {
+       MMFILE_AMBISONIC_TYPE_UNKNOWN = 0,
+       MMFILE_AMBISONIC_TYPE_PERIPHONIC = 1,           /**< To comply with Google's Spatial Audio RFC*/
+       MMFILE_AMBISONIC_TYPE_NON_PERIPHONIC = 2,
+} MMFILE_AMBISONIC_TYPE;
+
+typedef enum {
+       MMFILE_AMBISONIC_FORMAT_UNKNOWN = 0,
+       MMFILE_AMBISONIC_FORMAT_AMBIX = 1,              /**< AMBIX (Channel sequence: ACN, Normalization: SN3D) */
+       MMFILE_AMBISONIC_FORMAT_AMB = 2,                /**< .AMB, Tetraproc (Channel sequence: FuMa, Normalization: FuMa) */
+       MMFILE_AMBISONIC_FORMAT_UA = 3,                 /**< Universal Ambisonics (Channel sequence: SID, Normalization: N3D) */
+} MMFILE_AMBISONIC_FORMAT;
+
+typedef enum {
+       MMFILE_AMBISONIC_ORDER_UNKNOWN = 0,
+       MMFILE_AMBISONIC_ORDER_FOA = 1,                 /**< First order Ambisonics */
+       MMFILE_AMBISONIC_ORDER_SOA = 2,                 /**< Second order Ambisonics */
+       MMFILE_AMBISONIC_ORDER_TOA = 3,                 /**< Third order Ambisonics */
+} MMFILE_AMBISONIC_ORDER;
+
+typedef enum {
+       MMFILE_AMBISONIC_CHANNEL_ORDERING_UNKNOWN = 0,
+       MMFILE_AMBISONIC_CHANNEL_ORDERING_ACN = 1,              /**< Ambisonic Channel Number (ACN) system */
+       MMFILE_AMBISONIC_CHANNEL_ORDERING_FUMA = 2,             /**< Furse-Malham ordering*/
+       MMFILE_AMBISONIC_CHANNEL_ORDERING_SID = 3,              /**< Single Index Designation ordering */
+} MMFILE_AMBISONIC_CHANNEL_ORDERING;
+
+typedef enum {
+       MMFILE_AMBISONIC_NORMALIZATION_UNKNOWN = 0,
+       MMFILE_AMBISONIC_NORMALIZATION_SN3D = 1,        /**< Schmidt semi-normalization */
+       MMFILE_AMBISONIC_NORMALIZATION_FUMA = 2,        /**< Furse-Malham MaxN normalization*/
+       MMFILE_AMBISONIC_NORMALIZATION_N3D = 3,         /**< Full 3D normalization  */
+} MMFILE_AMBISONIC_NORMALIZATION;
 
 /**
  * content attributes.
@@ -109,8 +149,25 @@ typedef enum {
 #define        MM_FILE_TAG_ROTATE                      "tag-rotate"                    /**< Rotate(Orientation) Information*/
 #define        MM_FILE_TAG_CDIS                        "tag-cdis"                              /**< CDIS in User Data Information*/
 #define        MM_FILE_TAG_SMTA                        "tag-smta"                              /**< SMTA in User Data Information*/
-#define        MM_FILE_TAG_360                         "tag-360"                       /**< 360 content in User Data Information*/
-#define MM_FILE_TAG_STITCHED_INFO      "tag-stitched_info"
+#define        MM_FILE_TAG_SPHERICAL                                                   "tag-spherical"                                 /**< Flag indicating if the video is a spherical video*/
+#define MM_FILE_TAG_SPHERICAL_STITCHED                                 "tag-stitched"                                  /**< Flag indicating if the video is stitched*/
+#define MM_FILE_TAG_SPHERICAL_STITCHING_SOFTWARE               "tag-stitching-software"                /**< Software used to stitch the spherical video*/
+#define MM_FILE_TAG_SPHERICAL_PROJECTION_TYPE                  "tag-projection-type"                   /**< Projection type used in the video frames*/
+#define MM_FILE_TAG_SPHERICAL_STEREO_MODE                              "tag-stereo-mode"                               /**< Description of stereoscopic 3D layout*/
+#define MM_FILE_TAG_SPHERICAL_SOURCE_COUNT                             "tag-source-count"                              /**< Number of cameras used to create the spherical video*/
+#define        MM_FILE_TAG_SPHERICAL_INIT_VIEW_HEADING                 "tag-init-view-heading"                 /**< The heading angle of the initial view in degrees.*/
+#define        MM_FILE_TAG_SPHERICAL_INIT_VIEW_PITCH                   "tag-init-view-pitch"                   /**< The pitch angle of the initial view in degrees*/
+#define        MM_FILE_TAG_SPHERICAL_INIT_VIEW_ROLL                    "tag-init-view-roll"                    /**< The roll angle of the initial view in degrees*/
+#define        MM_FILE_TAG_SPHERICAL_TIMESTAMP                                 "tag-timestamp"                                 /**< Epoch timestamp of when the first frame in the video was recorded*/
+#define        MM_FILE_TAG_SPHERICAL_FULL_PANO_WIDTH                   "tag-full-pano-width"                   /**< Width of the encoded video frame in pixels*/
+#define        MM_FILE_TAG_SPHERICAL_FULL_PANO_HEIGHT                  "tag-full-pano-height"                  /**< Height of the encoded video frame in pixels*/
+#define        MM_FILE_TAG_SPHERICAL_CROPPED_AREA_IMAGE_WIDTH  "tag-cropped-area-image-width"  /**< Width of the video frame to display (e.g. cropping)*/
+#define        MM_FILE_TAG_SPHERICAL_CROPPED_AREA_IMAGE_HEIGHT "tag-cropped-area-image-height" /**< Height of the video frame to display (e.g. cropping)*/
+#define        MM_FILE_TAG_SPHERICAL_CROPPED_AREA_LEFT                 "tag-cropped-area-left"                 /**< Column where the left edge of the image was cropped from the full sized panorama*/
+#define        MM_FILE_TAG_SPHERICAL_CROPPED_AREA_TOP                  "tag-cropped-area-top"                  /**< Row where the top edge of the image was cropped from the full sized panorama*/
+#define        MM_FILE_TAG_AMBISONIC_TYPE                      "tag-ambisonic-type"            /**< Ambisonics type in User Data Information*/
+#define        MM_FILE_TAG_AMBISONIC_FORMAT            "tag-ambisonic-format"          /**< Ambisonics format in User Data Information*/
+#define        MM_FILE_TAG_AMBISONIC_ORDER                     "tag-ambisonic-order"           /**< Ambisonics order in User Data Information*/
 
 
 /**
index 68c673d..9efc3ab 100755 (executable)
@@ -26,6 +26,7 @@
 extern "C" {
 #endif
 #include <glib.h>
+#include "mm_file.h"
 
 #define MAXSTREAMS              20
 #define MMFILE_VIDEO_STREAM     0
@@ -150,8 +151,25 @@ struct _MMFileFormatContext {
        int syncLyricsNum;
        int cdis;
        int smta;
-       int is_360;
-       int stitched_info;
+       int isSpherical;
+       int isStitched;
+       char *stitchingSoftware;
+       char *projectionType;
+       char *stereoMode;
+       int sourceCount;
+       int initViewHeading;
+       int initViewPitch;
+       int initViewRoll;
+       int timestamp;
+       int fullPanoWidth;
+       int fullPanoHeight;
+       int croppedAreaImageWidth;
+       int croppedAreaImageHeight;
+       int croppedAreaLeft;
+       int croppedAreaTop;
+       MMFILE_AMBISONIC_TYPE ambisonicType;
+       MMFILE_AMBISONIC_FORMAT ambisonicFormat;
+       MMFILE_AMBISONIC_ORDER ambisonicOrder;
 
        /* private data */
        void *privateFormatData;
index 0879710..98812e3 100755 (executable)
--- a/mm_file.c
+++ b/mm_file.c
@@ -124,8 +124,25 @@ static mmf_attrs_construct_info_t g_tag_attrs[] = {
        {(char *)"tag-rotate",                  MMF_VALUE_TYPE_STRING,  MM_ATTRS_FLAG_RW, (void *)NULL},
        {(char *)"tag-cdis",                    MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
        {(char *)"tag-smta",                    MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
-       {(char *)"tag-360",                     MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
-       {(char *)"tag-stitched_info",   MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-spherical",                                       MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-stitched",                                        MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-stitching-software",                      MMF_VALUE_TYPE_STRING,  MM_ATTRS_FLAG_RW, (void *)NULL},
+       {(char *)"tag-projection-type",                         MMF_VALUE_TYPE_STRING,  MM_ATTRS_FLAG_RW, (void *)NULL},
+       {(char *)"tag-stereo-mode",                                     MMF_VALUE_TYPE_STRING,  MM_ATTRS_FLAG_RW, (void *)NULL},
+       {(char *)"tag-source-count",                            MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-init-view-heading",                       MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-init-view-pitch",                         MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-init-view-roll",                          MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-timestamp",                                       MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-full-pano-width",                         MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-full-pano-height",                        MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-cropped-area-image-width",        MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-cropped-area-image-height",       MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-cropped-area-left",                       MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-cropped-area-top",                        MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-ambisonic-type",                          MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-ambisonic-format",                        MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
+       {(char *)"tag-ambisonic-order",                         MMF_VALUE_TYPE_INT,             MM_ATTRS_FLAG_RW, (void *)0},
 };
 
 static mmf_attrs_construct_info_t g_content_attrs[] = {
@@ -308,8 +325,26 @@ _info_set_attr_media(mmf_attrs_t *attrs, MMFileFormatContext *formatContext)
                mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_CDIS, formatContext->cdis);
                mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SMTA, formatContext->smta);
 
-               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_360, formatContext->is_360);
-               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_STITCHED_INFO, formatContext->stitched_info);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL, formatContext->isSpherical);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL_STITCHED, formatContext->isStitched);
+               if (formatContext->stitchingSoftware)   mm_attrs_set_string_by_name(hattrs, MM_FILE_TAG_SPHERICAL_STITCHING_SOFTWARE, formatContext->stitchingSoftware);
+               if (formatContext->projectionType)              mm_attrs_set_string_by_name(hattrs, MM_FILE_TAG_SPHERICAL_PROJECTION_TYPE, formatContext->projectionType);
+               if (formatContext->stereoMode)                  mm_attrs_set_string_by_name(hattrs, MM_FILE_TAG_SPHERICAL_STEREO_MODE, formatContext->stereoMode);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL_SOURCE_COUNT, formatContext->sourceCount);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL_INIT_VIEW_HEADING, formatContext->initViewHeading);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL_INIT_VIEW_PITCH, formatContext->initViewPitch);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL_INIT_VIEW_ROLL, formatContext->initViewRoll);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL_TIMESTAMP, formatContext->timestamp);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL_FULL_PANO_WIDTH, formatContext->fullPanoWidth);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL_FULL_PANO_HEIGHT, formatContext->fullPanoHeight);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL_CROPPED_AREA_IMAGE_WIDTH, formatContext->croppedAreaImageWidth);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL_CROPPED_AREA_IMAGE_HEIGHT, formatContext->croppedAreaImageHeight);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL_CROPPED_AREA_LEFT, formatContext->croppedAreaLeft);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_SPHERICAL_CROPPED_AREA_TOP, formatContext->croppedAreaTop);
+
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_AMBISONIC_TYPE, formatContext->ambisonicType);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_AMBISONIC_FORMAT, formatContext->ambisonicFormat);
+               mm_attrs_set_int_by_name(hattrs, MM_FILE_TAG_AMBISONIC_ORDER, formatContext->ambisonicOrder);
 
                if ((formatContext->syncLyricsNum > 0) && (formatContext->syncLyrics))
                        mm_attrs_set_data_by_name(hattrs, MM_FILE_TAG_SYNCLYRICS, formatContext->syncLyrics, formatContext->syncLyricsNum);
index 4b7c325..b39104e 100755 (executable)
@@ -31,6 +31,7 @@
 
 #include <mm_file.h>
 #include "mm_file_traverse.h"
+#include "mm_file_debug.h"
 
 #define MM_TIME_CHECK_START \
        { FILE *msg_tmp_fp = fopen("time_check.txt", "a+"); struct timeval start, finish; gettimeofday(&start, NULL);
@@ -80,7 +81,26 @@ typedef struct _TagContext {
        mmfile_value_t unsynclyrics;
        mmfile_value_t synclyrics_size;
        mmfile_value_t rotate;                  /*string */
-       mmfile_value_t stitched_info;
+       mmfile_value_t is_spherical;
+       mmfile_value_t is_stitched;
+       mmfile_value_t stitching_software;      /*string */
+       mmfile_value_t projection_type;         /*string */
+       mmfile_value_t stereo_mode;                     /*string */
+       mmfile_value_t source_count;
+       mmfile_value_t init_view_heading;
+       mmfile_value_t init_view_pitch;
+       mmfile_value_t init_view_roll;
+       mmfile_value_t timestamp;
+       mmfile_value_t full_pano_width;
+       mmfile_value_t full_pano_height;
+       mmfile_value_t cropped_area_image_width;
+       mmfile_value_t cropped_area_image_height;
+       mmfile_value_t cropped_area_left;
+       mmfile_value_t cropped_area_top;
+       mmfile_value_t ambisonic_type;
+       mmfile_value_t ambisonic_format;
+       mmfile_value_t ambisonic_order;
+
 } TagContext_t;
 
 typedef struct _ContentContext {
@@ -346,7 +366,25 @@ static int mmfile_get_file_infomation(void *data, void *user_data, bool file_tes
                                                MM_FILE_TAG_UNSYNCLYRICS, &ctag.unsynclyrics.value.s_val, &ctag.unsynclyrics.len,
                                                MM_FILE_TAG_SYNCLYRICS_NUM, &ctag.synclyrics_size.value.i_val,
                                                MM_FILE_TAG_ROTATE, &ctag.rotate.value.s_val, &ctag.rotate.len,
-                                               MM_FILE_TAG_STITCHED_INFO, &ctag.stitched_info.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL, &ctag.is_spherical.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL_STITCHED, &ctag.is_stitched.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL_STITCHING_SOFTWARE, &ctag.stitching_software.value.s_val, &ctag.stitching_software.len,
+                                               MM_FILE_TAG_SPHERICAL_PROJECTION_TYPE, &ctag.projection_type.value.s_val, &ctag.projection_type.len,
+                                               MM_FILE_TAG_SPHERICAL_STEREO_MODE, &ctag.stereo_mode.value.s_val, &ctag.stereo_mode.len,
+                                               MM_FILE_TAG_SPHERICAL_SOURCE_COUNT, &ctag.source_count.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL_INIT_VIEW_HEADING, &ctag.init_view_heading.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL_INIT_VIEW_PITCH, &ctag.init_view_pitch.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL_INIT_VIEW_ROLL, &ctag.init_view_roll.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL_TIMESTAMP, &ctag.timestamp.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL_FULL_PANO_WIDTH, &ctag.full_pano_width.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL_FULL_PANO_HEIGHT, &ctag.full_pano_height.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL_CROPPED_AREA_IMAGE_WIDTH, &ctag.cropped_area_image_width.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL_CROPPED_AREA_IMAGE_HEIGHT, &ctag.cropped_area_image_height.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL_CROPPED_AREA_LEFT, &ctag.cropped_area_left.value.i_val,
+                                               MM_FILE_TAG_SPHERICAL_CROPPED_AREA_TOP, &ctag.cropped_area_top.value.i_val,
+                                               MM_FILE_TAG_AMBISONIC_TYPE, &ctag.ambisonic_type.value.i_val,
+                                               MM_FILE_TAG_AMBISONIC_FORMAT, &ctag.ambisonic_format.value.i_val,
+                                               MM_FILE_TAG_AMBISONIC_ORDER, &ctag.ambisonic_order.value.i_val,
                                                NULL);
                if (ret != FILEINFO_ERROR_NONE &&  err_attr_name) {
                        printf("failed to get %s attrs\n", err_attr_name);
@@ -388,7 +426,32 @@ static int mmfile_get_file_infomation(void *data, void *user_data, bool file_tes
                printf("# unsynclyrics: [%s]\n", ctag.unsynclyrics.value.s_val);
                printf("# synclyrics size: [%d]\n", ctag.synclyrics_size.value.i_val);
                printf("# rotate: [%s]\n", ctag.rotate.value.s_val);
-               printf("# stitched_info: [%d]\n", ctag.stitched_info.value.i_val);
+               printf("# is_spherical: [%d]\n", ctag.is_spherical.value.i_val);
+               printf("# is_stitched: [%d]\n", ctag.is_stitched.value.i_val);
+
+               if (ctag.is_spherical.value.i_val > 0 && ctag.is_stitched.value.i_val > 0) {
+                       printf("# stitching_software: [%s]\n", ctag.stitching_software.value.s_val);
+                       printf("# projection_type: [%s]\n", ctag.projection_type.value.s_val);
+                       printf("# stereo_mode [%s]\n", ctag.stereo_mode.value.s_val);
+                       printf("# source_count: [%d]\n", ctag.source_count.value.i_val);
+                       printf("# init_view_heading: [%d]\n", ctag.init_view_heading.value.i_val);
+                       printf("# init_view_pitch: [%d]\n", ctag.init_view_pitch.value.i_val);
+                       printf("# init_view_roll: [%d]\n", ctag.init_view_roll.value.i_val);
+                       printf("# time_stamp: [%d]\n", ctag.timestamp.value.i_val);
+                       printf("# full_pano_width: [%d]\n", ctag.full_pano_width.value.i_val);
+                       printf("# full_pano_height: [%d]\n", ctag.full_pano_height.value.i_val);
+                       printf("# cropped_area_image_width: [%d]\n", ctag.cropped_area_image_width.value.i_val);
+                       printf("# cropped_area_image_height: [%d]\n", ctag.cropped_area_image_height.value.i_val);
+                       printf("# cropped_area_left: [%d]\n", ctag.cropped_area_left.value.i_val);
+                       printf("# cropped_area_top: [%d]\n", ctag.cropped_area_top.value.i_val);
+               }
+
+               printf("# ambisonic_type: [%d]\n", ctag.ambisonic_type.value.i_val);
+
+               if (ctag.ambisonic_type.value.i_val > 0) {
+                       printf("# ambisonic_format: [%d]\n", ctag.ambisonic_format.value.i_val);
+                       printf("# ambisonic_order: [%d]\n", ctag.ambisonic_order.value.i_val);
+               }
 
                if (ctag.synclyrics_size.value.i_val > 0) {
                        int idx = 0;
index 89c6aca..01f0eb7 100755 (executable)
@@ -26,6 +26,7 @@
 #include <vconf.h>
 #include <glib.h>
 #include <iniparser.h>
+#include <stdint.h>
 
 #include "mm_file.h"
 #include "mm_file_debug.h"
@@ -129,6 +130,16 @@ typedef struct _mmfilesmtabox {
        unsigned int value;
 } MMFILE_M4A_SMTA_TAGBOX;
 
+typedef struct _mmfilesa3dbox {
+       uint8_t version;
+       uint8_t ambisonic_type;
+       uint32_t ambisonic_order;
+       uint8_t ambisonic_channel_ordering;
+       uint8_t ambisonic_normalization;
+       uint32_t num_channels;
+       uint32_t channel_map[49]; /* Up to 6th order */
+} __attribute__((aligned(1), packed)) MMFILE_MP4A_SA3D_TAGBOX;
+
 #define MMFILE_MP4_BASIC_BOX_HEADER_LEN 8
 #define MMFILE_MP4_MOVIE_HEADER_BOX_LEN 96
 #define MMFILE_MP4_HDLR_BOX_LEN         24
@@ -758,6 +769,213 @@ exception:
        return MMFILE_UTIL_FAIL;
 }
 
+static int GetSA3DInfoFromMP4ATagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
+{
+       if (!formatContext || !fp || !basic_header) {
+               debug_error(DEBUG, "invalid param\n");
+               return MMFILE_UTIL_FAIL;
+       }
+
+       unsigned char *buffer;
+       int readed = 0;
+       bool is_SA3D_present = false;
+       unsigned int i = 0;
+       MMFILE_MP4A_SA3D_TAGBOX sa3dTag = {0, };
+
+       formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_UNKNOWN;
+       formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_UNKNOWN;
+       formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_UNKNOWN;
+
+       mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
+
+       buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
+       if (!buffer) {
+               debug_error(DEBUG, "calloc failed ");
+               goto exception;
+       }
+
+       readed = mmfile_read(fp, buffer, basic_header->size);
+       if (readed != (int)basic_header->size) {
+               debug_error(DEBUG, "read mp4a box failed\n");
+               goto exception;
+       }
+
+       for (i = 0; i + 3 < basic_header->size; ++i)
+               if (buffer[i] == 'S' && buffer[i + 1] == 'A' && buffer[i + 2] == '3' && buffer[i + 3] == 'D') {
+                       debug_warning(DEBUG, "SA3D data found at offset %d bytes\n", i);
+                       is_SA3D_present = true;
+                       break;
+               }
+
+       if (!is_SA3D_present) {
+               debug_warning(DEBUG, "No SA3D box found");
+               goto exception;
+       }
+
+       mmfile_seek(fp, basic_header->start_offset + i + 4, SEEK_SET);
+
+       readed = mmfile_read(fp, (unsigned char *)&sa3dTag, sizeof(MMFILE_MP4A_SA3D_TAGBOX));
+       if (readed != sizeof(MMFILE_MP4A_SA3D_TAGBOX)) {
+               debug_error(DEBUG, "read SA3D tag header fail\n");
+               goto exception;
+       }
+
+       sa3dTag.ambisonic_order = mmfile_io_be_uint32(sa3dTag.ambisonic_order);
+       sa3dTag.num_channels = mmfile_io_be_uint32(sa3dTag.num_channels);
+       for (i = 0; i < sa3dTag.num_channels; ++i)
+               sa3dTag.channel_map[i] = mmfile_io_be_uint32(sa3dTag.channel_map[i]);
+
+       debug_msg(RELEASE, "sa3dTag.version = %d", sa3dTag.version);
+       debug_msg(RELEASE, "sa3dTag.ambisonic_type = %d", sa3dTag.ambisonic_type);
+       debug_msg(RELEASE, "sa3dTag.ambisonic_order = %d", sa3dTag.ambisonic_order);
+       debug_msg(RELEASE, "sa3dTag.ambisonic_channel_ordering = %d", sa3dTag.ambisonic_channel_ordering);
+       debug_msg(RELEASE, "sa3dTag.ambisonic_normalization = %d", sa3dTag.ambisonic_normalization);
+       debug_msg(RELEASE, "sa3dTag.num_channels = %d", sa3dTag.num_channels);
+       for (i = 0; i < sa3dTag.num_channels; ++i)
+               debug_msg(RELEASE, "sa3dTag.channel_map[%d] = %d", i, sa3dTag.channel_map[i]);
+
+       if (sa3dTag.version != RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
+               debug_error(DEBUG, "SA3D tag box version is unsupported\n");
+               goto exception;
+       } else {
+               if (sa3dTag.ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
+                       formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_PERIPHONIC;
+
+               switch (sa3dTag.ambisonic_order) {
+                       case MMFILE_AMBISONIC_ORDER_FOA: {
+                                       if (sa3dTag.num_channels == 4) {
+                                               formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_FOA;
+
+                                               if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
+                                                               (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
+                                                               (sa3dTag.channel_map[0] == 0) &&
+                                                               (sa3dTag.channel_map[1] == 1) &&
+                                                               (sa3dTag.channel_map[2] == 2) &&
+                                                               (sa3dTag.channel_map[3] == 3))
+                                                       formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
+
+                                               if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
+                                                               (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
+                                                               (sa3dTag.channel_map[0] == 0) &&
+                                                               (sa3dTag.channel_map[1] == 3) &&
+                                                               (sa3dTag.channel_map[2] == 1) &&
+                                                               (sa3dTag.channel_map[3] == 2))
+                                                       formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
+                                       } else {
+                                               debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond\n");
+                                               goto exception;
+                                       }
+
+                                       break;
+                               }
+                       case MMFILE_AMBISONIC_ORDER_SOA: {
+                                       if (sa3dTag.num_channels == 9) {
+                                               formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_SOA;
+
+                                               if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
+                                                               (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
+                                                               (sa3dTag.channel_map[0] == 0) &&
+                                                               (sa3dTag.channel_map[1] == 1) &&
+                                                               (sa3dTag.channel_map[2] == 2) &&
+                                                               (sa3dTag.channel_map[3] == 3) &&
+                                                               (sa3dTag.channel_map[4] == 4) &&
+                                                               (sa3dTag.channel_map[5] == 5) &&
+                                                               (sa3dTag.channel_map[6] == 6) &&
+                                                               (sa3dTag.channel_map[7] == 7) &&
+                                                               (sa3dTag.channel_map[8] == 8))
+                                                       formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
+
+                                               if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
+                                                               (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
+                                                               (sa3dTag.channel_map[0] == 0) &&
+                                                               (sa3dTag.channel_map[1] == 3) &&
+                                                               (sa3dTag.channel_map[2] == 1) &&
+                                                               (sa3dTag.channel_map[3] == 2) &&
+                                                               (sa3dTag.channel_map[4] == 6) &&
+                                                               (sa3dTag.channel_map[5] == 7) &&
+                                                               (sa3dTag.channel_map[6] == 5) &&
+                                                               (sa3dTag.channel_map[7] == 8) &&
+                                                               (sa3dTag.channel_map[4] == 4))
+                                                       formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
+                                       } else {
+                                               debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond\n");
+                                               goto exception;
+                                       }
+
+                                       break;
+                               }
+
+                       case MMFILE_AMBISONIC_ORDER_TOA: {
+                                       if (sa3dTag.num_channels == 16) {
+                                               formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_TOA;
+
+                                               if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
+                                                               (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
+                                                               (sa3dTag.channel_map[0] == 0) &&
+                                                               (sa3dTag.channel_map[1] == 1) &&
+                                                               (sa3dTag.channel_map[2] == 2) &&
+                                                               (sa3dTag.channel_map[3] == 3) &&
+                                                               (sa3dTag.channel_map[4] == 4) &&
+                                                               (sa3dTag.channel_map[5] == 5) &&
+                                                               (sa3dTag.channel_map[6] == 6) &&
+                                                               (sa3dTag.channel_map[7] == 7) &&
+                                                               (sa3dTag.channel_map[8] == 8) &&
+                                                               (sa3dTag.channel_map[9] == 9) &&
+                                                               (sa3dTag.channel_map[10] == 10) &&
+                                                               (sa3dTag.channel_map[11] == 11) &&
+                                                               (sa3dTag.channel_map[12] == 12) &&
+                                                               (sa3dTag.channel_map[13] == 13) &&
+                                                               (sa3dTag.channel_map[14] == 14) &&
+                                                               (sa3dTag.channel_map[15] == 15))
+                                                       formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
+
+                                               if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
+                                                               (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
+                                                               (sa3dTag.channel_map[0] == 0) &&
+                                                               (sa3dTag.channel_map[1] == 3) &&
+                                                               (sa3dTag.channel_map[2] == 1) &&
+                                                               (sa3dTag.channel_map[3] == 2) &&
+                                                               (sa3dTag.channel_map[4] == 6) &&
+                                                               (sa3dTag.channel_map[5] == 7) &&
+                                                               (sa3dTag.channel_map[6] == 5) &&
+                                                               (sa3dTag.channel_map[7] == 8) &&
+                                                               (sa3dTag.channel_map[8] == 4) &&
+                                                               (sa3dTag.channel_map[9] == 12) &&
+                                                               (sa3dTag.channel_map[10] == 13) &&
+                                                               (sa3dTag.channel_map[11] == 11) &&
+                                                               (sa3dTag.channel_map[12] == 14) &&
+                                                               (sa3dTag.channel_map[13] == 10) &&
+                                                               (sa3dTag.channel_map[14] == 15) &&
+                                                               (sa3dTag.channel_map[15] == 9))
+                                                       formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
+                                       } else {
+                                               debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond\n");
+                                               goto exception;
+                                       }
+
+                                       break;
+                               }
+
+                       default: {
+                                       debug_warning(DEBUG, "Ambisonic order or format is not supported: ambix or amb formats up to 3rd order were expected.\n");
+                                       goto exception;
+                                       break;
+                               }
+               }
+
+               debug_msg(RELEASE, "formatContext->ambisonic_type = %d", formatContext->ambisonicType);
+               debug_msg(RELEASE, "formatContext->ambisonic_order = %d", formatContext->ambisonicOrder);
+               debug_msg(RELEASE, "formatContext->ambisonic_format = %d", formatContext->ambisonicFormat);
+       }
+
+       return MMFILE_UTIL_SUCCESS;
+
+exception:
+       mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
+
+       return MMFILE_UTIL_FAIL;
+}
+
 static int GetValueFromCDISTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
 {
        unsigned int value = 0;
@@ -1241,6 +1459,134 @@ exception:
        return MMFILE_UTIL_FAIL;
 }
 
+int mm_file_get_int_value_from_xml_string(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[value_length_max];
+       int value_length = 0;
+
+       if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
+               debug_error(DEBUG, "error: incorrect or non-existing parameter\n");
+               return MMFILE_UTIL_FAIL;
+       }
+
+       value_start = strstr(xml_str, param_name) + strlen(param_name);
+       while ((value_start[0] == ' ') || (value_start[0] == '\t'))
+               value_start++;
+
+       value_end = strchr(value_start, '<');
+       if (!value_end) {
+               debug_error(DEBUG, "error: incorrect XML\n");
+               return MMFILE_UTIL_FAIL;
+       }
+
+       value_length = value_end - value_start;
+       while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
+               value_length--;
+
+       int i = 0;
+       if (value_start[i] == '+' || value_start[i] == '-')
+                       i++;
+       while (i < value_length) {
+               if (value_start[i] < '0' || value_start[i] > '9') {
+                       debug_error(DEBUG, "error: incorrect value, integer was expected\n");
+                       return MMFILE_UTIL_FAIL;
+               }
+               i++;
+       }
+
+       if (value_length >= value_length_max || value_length < 1) {
+               debug_error(DEBUG, "error: empty XML value or incorrect range\n");
+               return MMFILE_UTIL_FAIL;
+       }
+
+       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) {
+               debug_error(DEBUG, "error: no digits were found\n");
+               return MMFILE_UTIL_FAIL;
+       }
+
+       return MMFILE_UTIL_SUCCESS;
+}
+
+int mm_file_get_string_value_from_xml_string(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;
+
+       if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
+               debug_error(DEBUG, "error: incorrect or non-existing parameter\n");
+               return MMFILE_UTIL_FAIL;
+       }
+
+       value_start = strstr(xml_str, param_name) + strlen(param_name);
+       while ((value_start[0] == ' ') || (value_start[0] == '\t'))
+               value_start++;
+
+       value_end = strchr(value_start, '<');
+       if (!value_end) {
+               debug_error(DEBUG, "error: incorrect XML\n");
+               return MMFILE_UTIL_FAIL;
+       }
+
+       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) {
+               debug_error(DEBUG, "error: empty XML value or incorrect range\n");
+               return MMFILE_UTIL_FAIL;
+       }
+
+       *value=(char*)calloc(value_length, sizeof(char));
+       if (*value == NULL) {
+               debug_error(DEBUG, "error: calloc failed\n");
+               return MMFILE_UTIL_FAIL;
+       }
+       strncpy(*value, value_start, value_length);
+
+       return MMFILE_UTIL_SUCCESS;
+}
+
+int mm_file_get_bool_value_from_xml_string(const char* xml_str, const char* param_name, bool* value)
+{
+       char *value_start, *value_end;
+       int value_length = 0;
+
+       if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
+               debug_error(DEBUG, "error: incorrect or non-existing parameter\n");
+               return MMFILE_UTIL_FAIL;
+       }
+
+       value_start = strstr(xml_str, param_name) + strlen(param_name);
+       while ((value_start[0] == ' ') || (value_start[0] == '\t'))
+               value_start++;
+
+       value_end = strchr(value_start, '<');
+       if (!value_end) {
+               debug_error(DEBUG, "error: incorrect XML\n");
+               return MMFILE_UTIL_FAIL;
+       }
+
+       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) {
+               debug_error(DEBUG, "error: empty XML value or incorrect range\n");
+               return MMFILE_UTIL_FAIL;
+       }
+
+       *value= strstr(value_start, "true") ? true : false;
+
+       return MMFILE_UTIL_SUCCESS;
+}
+
 static int g_junk_counter_limit = 0;
 static int GetJunkCounterLimit(void)
 {
@@ -1261,6 +1607,66 @@ static int GetJunkCounterLimit(void)
        return data;
 }
 
+static int ParseSpatialVideoMetadataFromXMLString(const char *xmlStr, MMFileFormatContext *formatContext)
+{
+       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>";
+
+       mm_file_get_bool_value_from_xml_string(xmlStr, is_spherical_str,(bool*)&formatContext->isSpherical);
+       mm_file_get_bool_value_from_xml_string(xmlStr, is_stitched_str, (bool*)&formatContext->isStitched);
+
+       debug_msg(RELEASE, "isSpherical = %d", formatContext->isSpherical);
+       debug_msg(RELEASE, "isStitched = %d", formatContext->isStitched);
+
+       if (formatContext->isSpherical && formatContext->isStitched) {
+               mm_file_get_string_value_from_xml_string(xmlStr, stitching_software_str, &formatContext->stitchingSoftware);
+               mm_file_get_string_value_from_xml_string(xmlStr, projection_type_str, &formatContext->projectionType);
+               mm_file_get_string_value_from_xml_string(xmlStr, stereo_mode_str, &formatContext->stereoMode);
+               mm_file_get_int_value_from_xml_string(xmlStr, source_count_str, &formatContext->sourceCount);
+               mm_file_get_int_value_from_xml_string(xmlStr, init_view_heading_str, &formatContext->initViewHeading);
+               mm_file_get_int_value_from_xml_string(xmlStr, init_view_pitch_str, &formatContext->initViewPitch);
+               mm_file_get_int_value_from_xml_string(xmlStr, init_view_roll_str, &formatContext->initViewRoll);
+               mm_file_get_int_value_from_xml_string(xmlStr, timestamp_str, &formatContext->timestamp);
+               mm_file_get_int_value_from_xml_string(xmlStr, full_pano_width_str, &formatContext->fullPanoWidth);
+               mm_file_get_int_value_from_xml_string(xmlStr, full_pano_height_str, &formatContext->fullPanoHeight);
+               mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_width_str, &formatContext->croppedAreaImageWidth);
+               mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_height_str, &formatContext->croppedAreaImageHeight);
+               mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_left_str, &formatContext->croppedAreaLeft);
+               mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_top_str, &formatContext->croppedAreaTop);
+
+               debug_msg(RELEASE, "stitchingSoftware = %s", formatContext->stitchingSoftware);
+               debug_msg(RELEASE, "projectionType = %s", formatContext->projectionType);
+               debug_msg(RELEASE, "stereoMode = %s", formatContext->stereoMode);
+               debug_msg(RELEASE, "sourceCount %d", formatContext->sourceCount);
+               debug_msg(RELEASE, "initViewHeading = %d", formatContext->initViewHeading);
+               debug_msg(RELEASE, "initViewPitch = %d", formatContext->initViewPitch);
+               debug_msg(RELEASE, "initViewRoll = %d", formatContext->initViewRoll);
+               debug_msg(RELEASE, "timestamp = %d", formatContext->timestamp);
+               debug_msg(RELEASE, "fullPanoWidthPixels = %d", formatContext->fullPanoWidth);
+               debug_msg(RELEASE, "fullPanoHeightPixels = %d", formatContext->fullPanoHeight);
+               debug_msg(RELEASE, "croppedAreaImageWidth = %d", formatContext->croppedAreaImageWidth);
+               debug_msg(RELEASE, "croppedAreaImageHeight = %d", formatContext->croppedAreaImageHeight);
+               debug_msg(RELEASE, "croppedAreaLeft = %d", formatContext->croppedAreaLeft);
+               debug_msg(RELEASE, "croppedAreaTop = %d", formatContext->croppedAreaTop);
+       }
+
+       return MMFILE_UTIL_SUCCESS;
+}
+
 #define BIG_CONTENT_BOX_SIZE_LEN 8
 EXPORT_API int MMFileUtilGetMetaDataFromMP4(MMFileFormatContext *formatContext)
 {
@@ -1437,26 +1843,55 @@ EXPORT_API int MMFileUtilGetMetaDataFromMP4(MMFileFormatContext *formatContext)
                                                if(str != NULL) {
                                                        memset(str, 0, basic_header.size);
                                                        mmfile_read(fp, (unsigned char *)str, basic_header.size);
-
+#if 0
+/* The block is superseded */
                                                        if(strstr(str, "<GSpherical:Spherical>true</GSpherical:Spherical>"))
                                                                formatContext->is_360 = 1;
                                                        else
                                                                formatContext->is_360 = 0;
-
+/* Image can be stitched even if it is not spherical */
                                                        if(formatContext->is_360 == 1) {
                                                                if (strstr(str, "<GSpherical:Stitched>true</GSpherical:Stitched>"))
-                                                                       formatContext->stitched_info = MMFILE_360_STITCHED;
+                                                                       formatContext->stitched = MMFILE_360_STITCHED;
                                                                else
-                                                                       formatContext->stitched_info = MMFILE_360_NON_STITCHED;
+                                                                       formatContext->stitched = MMFILE_360_NON_STITCHED;
                                                        } else {
-                                                               formatContext->stitched_info = MMFILE_360_NONE;
+/* Image can be stitched or non-stitched. Usage of some 3rd value is superfluous */
+                                                               formatContext->stitched = MMFILE_360_NONE;
                                                        }
+#endif
+
+                                                       debug_msg(RELEASE, "Extracting tags from UUID XML string %s\n", str);
+
+                                                       ParseSpatialVideoMetadataFromXMLString(str, formatContext);
                                                }
                                        }
                                        ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
 
                                        break;
                                }
+                       case FOURCC('m', 'd', 'i', 'a'): {
+                                       debug_msg(RELEASE, "MPEG4: [mdia] SIZE: [%lld]Byte\n", chunk_size);
+                                       break;
+                               }
+                       case FOURCC('m', 'i', 'n', 'f'): {
+                                       debug_msg(RELEASE, "MPEG4: [minf] SIZE: [%lld]Byte\n", chunk_size);
+                                       break;
+                               }
+                       case FOURCC('s', 't', 'b', 'l'): {
+                                       debug_msg(RELEASE, "MPEG4: [stbl] SIZE: [%lld]Byte\n", chunk_size);
+                                       break;
+                               }
+                       case FOURCC('s', 't', 's', 'd'): {
+                                       debug_msg(RELEASE, "MPEG4: [stsd] SIZE: [%lld]Byte\n", chunk_size);
+                                       break;
+                               }
+                       case FOURCC('m', 'p', '4', 'a'): {
+                                       debug_msg(RELEASE, "MPEG4: [mp4a] SIZE: [%lld]Byte\n", chunk_size);
+                                       GetSA3DInfoFromMP4ATagBox(formatContext, fp, &basic_header);
+                                       ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
+                                       break;
+                               }
                        default: {
                                        debug_msg(RELEASE, "4CC: Not Support [%c%c%c%c]. So skip it. Size [%lld Byte]\n",
                                                                ((char *)&basic_header.type)[0], ((char *)&basic_header.type)[1],