uint32_t channel_map[49]; /* Up to 6th order */
} __attribute__((aligned(1), packed)) MMFILE_MP4A_SA3D_TAGBOX;
+typedef struct _mmfile_proj_v2_box {
+ uint16_t proj_box_id;
+ uint8_t proj_box_size;
+ uint16_t proj_type_box_id;
+ uint8_t proj_type_box_size;
+ uint8_t proj_type_box_value; /* only equi (=1) or cubemap (=2) currently */
+ uint16_t proj_priv_box_id;
+ uint8_t proj_priv_box_size;
+} __attribute__((aligned(1), packed)) MMFILE_WEBM_PROJ_V2_BOX;
+
+typedef struct _mmfile_equi_proj_v2_box {
+ unsigned char proj_priv_box_name[4]; /* "equi" */
+ uint32_t equi_projection_bounds_top;
+ uint32_t equi_projection_bounds_bottom;
+ uint32_t equi_projection_bounds_left;
+ uint32_t equi_projection_bounds_right;
+} __attribute__((aligned(1), packed)) MMFILE_WEBM_EQUI_PROJ_V2_BOX;
+
+typedef struct _mmfile_cbmp_proj_v2_box {
+ unsigned char proj_priv_box_name[4]; /* "cbmp" */
+ uint32_t cbmp_projection_layout;
+ uint32_t cbmp_projection_padding;
+} __attribute__((aligned(1), packed)) MMFILE_WEBM_CBMP_PROJ_V2_BOX;
+
+typedef struct _mmfile_pose_element_v2_box {
+ uint16_t pose_yaw_element_id;
+ uint8_t pose_yaw_element_size;
+ float pose_yaw_element_value;
+ uint16_t pose_pitch_element_id;
+ uint8_t pose_pitch_element_size;
+ float pose_pitch_element_value;
+ uint16_t pose_roll_element_id;
+ uint8_t pose_roll_element_size;
+ float pose_roll_element_value;
+} __attribute__((aligned(1), packed)) MMFILE_WEBM_POSE_ELEMENT_V2_BOX;
+
+
typedef enum {
PROJECTION_TYPE_RECT = 0, /* Rectangular, Google's RFC value */
PROJECTION_TYPE_EQUI = 1, /* Equirectangular, Google's RFC value */
PROJECTION_TYPE_UNKNOWN = INVALID_UINT_VALUE,
} SPHERICAL_VIDEO2_PROJECTION_TYPE;
+enum spherical_video_metadata_elements_ids_le {
+ PROJ_BOX_ID = 0x7076,
+ PROJ_TYPE_BOX_ID = 0x7176,
+ PROJ_PRIV_BOX_ID = 0x7276,
+ POSE_YAW_ELEMENT_ID = 0x7376,
+ POSE_PITCH_ELEMENT_ID = 0x7476,
+ POSE_ROLL_ELEMENT_ID = 0x7576
+};
+
#define MMFILE_MP4_BASIC_BOX_HEADER_LEN 8
#define MMFILE_MP4_MOVIE_HEADER_BOX_LEN 96
#define MMFILE_MP4_HDLR_BOX_LEN 24
return data;
}
-static int ParseSpatialVideoMetadataFromXMLString(const char *xmlStr, MMFileFormatContext *formatContext)
+int ParseSpatialVideoMetadataFromXMLString(const char *xmlStr, MMFileFormatContext *formatContext)
{
const char is_spherical_str[] = "<GSpherical:Spherical>";
const char is_stitched_str[] = "<GSpherical:Stitched>";
}
#define BIG_CONTENT_BOX_SIZE_LEN 8
+
+int MMFileUtilGetMetaDataFromMKV(MMFileFormatContext *formatContext)
+{
+ MMFileIOHandle *fp = NULL;
+ int probe_size = 10000;
+ unsigned char *buffer = NULL;
+ int ret = 0;
+ int i;
+
+ MMFILE_WEBM_PROJ_V2_BOX v2box = { 0, };
+ MMFILE_WEBM_EQUI_PROJ_V2_BOX equi = { 0, };
+ MMFILE_WEBM_CBMP_PROJ_V2_BOX cbmp = { 0, };
+ MMFILE_WEBM_POSE_ELEMENT_V2_BOX pose = { 0, };
+
+ ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
+ if (ret == MMFILE_UTIL_FAIL) {
+ debug_error(DEBUG, "error: mmfile_open\n");
+ goto exit;
+ }
+
+ long long file_size = mmfile_seek(fp, 0, SEEK_END);
+ if (file_size == MMFILE_UTIL_FAIL) {
+ debug_error(DEBUG, "mmfile operation failed\n");
+ goto exit;
+ }
+
+ probe_size = (file_size > probe_size) ? probe_size : file_size;
+ buffer = (unsigned char *)malloc(probe_size * sizeof(unsigned char));
+ if (!buffer) {
+ debug_error(DEBUG, "malloc failed\n");
+ goto exit;
+ }
+
+ ret = mmfile_seek(fp, 0, SEEK_SET);
+ if (ret == MMFILE_UTIL_FAIL) {
+ debug_error(DEBUG, "mmfile operation failed\n");
+ goto exit;
+ }
+
+ ret = mmfile_read(fp, buffer, probe_size * sizeof(unsigned char));
+ if (ret == MMFILE_UTIL_FAIL) {
+ debug_error(DEBUG, "mmfile operation failed\n");
+ goto exit;
+ }
+
+ /* FIXME (m.alieksieie): It's better to use some EBML parser here*/
+ for (i = 0; i + 3 < probe_size; ++i) {
+ if (*(unsigned int *)(buffer + i) == FOURCC('e', 'q', 'u', 'i') ||
+ *(unsigned int *)(buffer + i) == FOURCC('c', 'b', 'm', 'p')) {
+ debug_msg(DEBUG, "projection data found at offset %d bytes\n", i);
+ break;
+ }
+ }
+
+ if (i + 3 == probe_size) {
+ debug_msg(DEBUG, "projection info wasn't found\n");
+ ret = MMFILE_UTIL_SUCCESS;
+ goto exit;
+ }
+
+ if ((i - (int)sizeof(MMFILE_WEBM_PROJ_V2_BOX)) < 0) {
+ debug_error(DEBUG, "error: invalid supposed projection info location\n");
+ ret = MMFILE_UTIL_FAIL;
+ goto exit;
+ }
+
+ ret = mmfile_seek(fp, i - sizeof(MMFILE_WEBM_PROJ_V2_BOX), SEEK_SET);
+ if (ret == MMFILE_UTIL_FAIL) {
+ debug_error(DEBUG, "error: failed to seek to the supposed projection info location\n");
+ goto exit;
+ }
+
+ ret = mmfile_read(fp, (unsigned char *)&v2box, sizeof(MMFILE_WEBM_PROJ_V2_BOX));
+ if (ret == MMFILE_UTIL_FAIL) {
+ debug_error(DEBUG, "mmfile operation failed\n");
+ goto exit;
+ }
+
+ if (v2box.proj_type_box_value == PROJECTION_TYPE_EQUI) {
+ debug_msg(DEBUG, "Equirectangular projection is used\n");
+
+ ret = mmfile_read(fp, (unsigned char *)&equi, sizeof(MMFILE_WEBM_EQUI_PROJ_V2_BOX));
+ if (ret == MMFILE_UTIL_FAIL) {
+ debug_error(DEBUG, "error: failed to read equirectangular element\n");
+ goto exit;
+ }
+ if (strncmp((char *)equi.proj_priv_box_name, "equi", 4) == 0) {
+ formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_top);
+ formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_bottom);
+ formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_left);
+ formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_right);
+ } else {
+ debug_error(DEBUG, "error: failed to read equirectangular element\n");
+ ret = MMFILE_UTIL_SUCCESS;
+ goto exit;
+ }
+ }
+ if (v2box.proj_type_box_value == PROJECTION_TYPE_CBMP) {
+ debug_msg(DEBUG, "Cubemap projection is used\n");
+
+ ret = mmfile_read(fp, (unsigned char *)&cbmp, sizeof(MMFILE_WEBM_CBMP_PROJ_V2_BOX));
+ if (ret == MMFILE_UTIL_FAIL) {
+ debug_error(DEBUG, "error: failed to read cubemap element\n");
+ goto exit;
+ }
+ if (strncmp((char *)cbmp.proj_priv_box_name, "cbmp", 4) == 0) {
+ formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_layout);
+ formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_padding);
+ } else {
+ debug_error(DEBUG, "error: failed to read cubemap element\n");
+ ret = MMFILE_UTIL_FAIL;
+ goto exit;
+ }
+ }
+
+ ret = mmfile_read(fp, (unsigned char *)&pose, sizeof(MMFILE_WEBM_POSE_ELEMENT_V2_BOX));
+ if (ret == MMFILE_UTIL_FAIL) {
+ debug_error(DEBUG, "error: failed to read pose info\n");
+ goto exit;
+ }
+
+ if (pose.pose_yaw_element_id == POSE_YAW_ELEMENT_ID) {
+ formatContext->poseYawV2 = (uint)mmfile_io_be_float32(pose.pose_yaw_element_value);
+ } else {
+ debug_error(DEBUG, "error: failed to pose yaw element\n");
+ ret = MMFILE_UTIL_FAIL;
+ goto exit;
+ }
+ if (pose.pose_pitch_element_id == POSE_PITCH_ELEMENT_ID) {
+ formatContext->posePitchV2 = (uint)mmfile_io_be_float32(pose.pose_pitch_element_value);
+ } else {
+ debug_error(DEBUG, "error: failed to pose pitch element\n");
+ ret = MMFILE_UTIL_FAIL;
+ goto exit;
+ }
+ if (pose.pose_roll_element_id == POSE_ROLL_ELEMENT_ID) {
+ formatContext->poseRollV2 = (uint)mmfile_io_be_float32(pose.pose_roll_element_value);
+ } else {
+ debug_error(DEBUG, "error: failed to pose roll element\n");
+ ret = MMFILE_UTIL_FAIL;
+ goto exit;
+ }
+
+exit:
+ if (fp)
+ mmfile_close(fp);
+
+ if (buffer)
+ free(buffer);
+
+ return ret;
+}
+
EXPORT_API int MMFileUtilGetMetaDataFromMP4(MMFileFormatContext *formatContext)
{
MMFileIOHandle *fp = NULL;