4 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: Haejeong Kim <backto.kim@samsung.com>
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
31 #include "mm_file_debug.h"
32 #include "mm_file_utils.h"
34 #define ENABLE_ITUNES_META /*All itunes metadata extracted by ffmpeg. see mov_read_udta_string() but Some cover art not support. */
35 #define INVALID_UINT_VALUE 0xFFFFFFFF
36 #define INVALID_UINT8_VALUE 0xFF
38 #define ID3TAG_V110_TRACK_NUM_DIGIT 5
40 typedef struct _mmfilemp4basicboxheader {
43 long long start_offset; /*huge file*/
44 } MMFILE_MP4_BASIC_BOX_HEADER;
46 typedef struct _mmfilemp4ftpybox {
47 unsigned int major_brand;
48 unsigned short major_version;
49 unsigned short minor_version;
50 unsigned int *compatiable_brands;
54 eMMFILE_3GP_TAG_TITLE = 0x01,
55 eMMFILE_3GP_TAG_CAPTION = 0x02,
56 eMMFILE_3GP_TAG_COPYRIGHT = 0x03,
57 eMMFILE_3GP_TAG_PERFORMER = 0x04,
58 eMMFILE_3GP_TAG_AUTHOR = 0x05,
59 eMMFILE_3GP_TAG_GENRE = 0x06,
60 } eMMFILE_3GP_TEXT_TAG;
62 typedef struct _mmfile3gptagtextbox {
63 unsigned char version;
64 unsigned char flag[3];
65 unsigned char language[2];
67 } MMFILE_3GP_TEXT_TAGBOX;
69 typedef struct _mmfile3gptagyearbox {
70 unsigned char version;
71 unsigned char flag[3];
73 } MMFILE_3GP_YEAR_TAGBOX;
75 typedef struct _mmfile3gptagalbumbox {
76 unsigned char version;
77 unsigned char flag[3];
78 unsigned char language[2];
79 unsigned char *albumtile;
80 unsigned char trackNumber;
81 } MMFILE_3GP_ALBUM_TAGBOX;
83 typedef struct _mmfile3gpratingbox {
84 unsigned char version;
85 unsigned char flag[3];
86 unsigned int ratingEntity;
87 unsigned int ratingCriteria;
88 unsigned char language[2];
89 unsigned char *ratingInfo;
90 } MMFILE_3GP_RATING_TAGBOX;
92 typedef struct _mmfile3gpclsfbox {
93 unsigned char version;
94 unsigned char flag[3];
95 unsigned int classificationEntity;
96 unsigned int classificationTable;
97 unsigned char language[2];
98 unsigned char *classificationInfo;
99 } MMFILE_3GP_CLASSIFICATION_TAGBOX;
101 typedef struct _mmfile3gphdlrbox {
102 unsigned int version;
103 unsigned int pre_defined;
104 unsigned int handler_type;
105 unsigned int reserved[3];
106 unsigned char *boxname;
107 } MMFILE_3GP_HANDLER_BOX;
109 typedef struct _mmfileidv2box {
110 unsigned char version;
111 unsigned char flag[3];
112 unsigned char language[2];
113 unsigned char *id3v2Data;
114 } MMFILE_3GP_ID3V2_BOX;
116 typedef struct _mmfilelocibox {
117 unsigned char version;
118 unsigned char flag[3];
119 unsigned char language[2];
125 unsigned char *astronomical_body;
126 unsigned char *additional_notes;
127 } MMFILE_3GP_LOCATION_TAGBOX;
129 typedef struct _mmfilesmtabox {
132 unsigned char saut[4];
134 } MMFILE_M4A_SMTA_TAGBOX;
136 typedef struct _mmfilesa3dbox {
138 uint8_t ambisonic_type;
139 uint32_t ambisonic_order;
140 uint8_t ambisonic_channel_ordering;
141 uint8_t ambisonic_normalization;
142 uint32_t num_channels;
143 uint32_t channel_map[49]; /* Up to 6th order */
144 } __attribute__((aligned(1), packed)) MMFILE_MP4A_SA3D_TAGBOX;
146 typedef struct _mmfile_proj_v2_box {
147 uint16_t proj_box_id;
148 uint8_t proj_box_size;
149 uint16_t proj_type_box_id;
150 uint8_t proj_type_box_size;
151 uint8_t proj_type_box_value; /* only equi (=1) or cubemap (=2) currently */
152 uint16_t proj_priv_box_id;
153 uint8_t proj_priv_box_size;
154 } __attribute__((aligned(1), packed)) MMFILE_WEBM_PROJ_V2_BOX;
156 typedef struct _mmfile_equi_proj_v2_box {
157 unsigned char proj_priv_box_name[4]; /* "equi" */
158 uint32_t equi_projection_bounds_top;
159 uint32_t equi_projection_bounds_bottom;
160 uint32_t equi_projection_bounds_left;
161 uint32_t equi_projection_bounds_right;
162 } __attribute__((aligned(1), packed)) MMFILE_WEBM_EQUI_PROJ_V2_BOX;
164 typedef struct _mmfile_cbmp_proj_v2_box {
165 unsigned char proj_priv_box_name[4]; /* "cbmp" */
166 uint32_t cbmp_projection_layout;
167 uint32_t cbmp_projection_padding;
168 } __attribute__((aligned(1), packed)) MMFILE_WEBM_CBMP_PROJ_V2_BOX;
170 typedef struct _mmfile_pose_element_v2_box {
171 uint16_t pose_yaw_element_id;
172 uint8_t pose_yaw_element_size;
173 float pose_yaw_element_value;
174 uint16_t pose_pitch_element_id;
175 uint8_t pose_pitch_element_size;
176 float pose_pitch_element_value;
177 uint16_t pose_roll_element_id;
178 uint8_t pose_roll_element_size;
179 float pose_roll_element_value;
180 } __attribute__((aligned(1), packed)) MMFILE_WEBM_POSE_ELEMENT_V2_BOX;
184 PROJECTION_TYPE_RECT = 0, /* Rectangular, Google's RFC value */
185 PROJECTION_TYPE_EQUI = 1, /* Equirectangular, Google's RFC value */
186 PROJECTION_TYPE_CBMP = 2, /* Cubemap, Google's RFC value */
187 PROJECTION_TYPE_MESH = 3, /* Mesh, Google's RFC value, unsupported */
188 PROJECTION_TYPE_UNKNOWN = INVALID_UINT_VALUE,
189 } SPHERICAL_VIDEO2_PROJECTION_TYPE;
191 enum spherical_video_metadata_elements_ids_le {
192 PROJ_BOX_ID = 0x7076,
193 PROJ_TYPE_BOX_ID = 0x7176,
194 PROJ_PRIV_BOX_ID = 0x7276,
195 POSE_YAW_ELEMENT_ID = 0x7376,
196 POSE_PITCH_ELEMENT_ID = 0x7476,
197 POSE_ROLL_ELEMENT_ID = 0x7576
200 #define MMFILE_MP4_BASIC_BOX_HEADER_LEN 8
201 #define MMFILE_MP4_MOVIE_HEADER_BOX_LEN 96
202 #define MMFILE_MP4_HDLR_BOX_LEN 24
203 #define MMFILE_MP4_STSZ_BOX_LEN 20
204 #define MMFILE_MP4_MP4VBOX_LEN 80
205 #define MMFILE_MP4_MP4ABOX_LEN 28
206 #define MMFILE_3GP_TEXT_TAGBOX_LEN 6
207 #define MMFILE_3GP_YEAR_TAGBOX_LEN 6
208 #define MMFILE_3GP_ALBUM_TAGBOX_LEN 6
209 #define MMFILE_3GP_RATING_TAGBOX_LEN 14
210 #define MMFILE_3GP_CLASS_TAGBOX_LEN 14
211 #define MMFILE_3GP_HANDLER_BOX_LEN 24
212 #define MMFILE_3GP_ID3V2_BOX_LEN 6
213 #define MMFILE_SYNC_LYRIC_INFO_MIN_LEN 5
216 #define FOURCC(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
218 #define GENRE_COUNT 149
220 static const char * const MpegAudio_Genre[GENRE_COUNT] = {"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal",
221 "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial",
222 "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
223 "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise",
224 "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic",
225 "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
226 "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes",
227 "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock",
228 "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass",
229 "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic",
230 "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove",
231 "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
232 "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore",
233 "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian", "Heavy Metal", "Black Metal", "Crossover",
234 "Contemporary", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", "Synthpop", "Unknown"
237 static unsigned char gTagJPEGHeader[] = {0xff, 0xd8, 0xff };
238 static unsigned char gTagPNGHeader[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
240 typedef struct _mmfileid3taginfo {
242 unsigned int int_name;
244 } MMFILE_ID3TAG_INFO;
246 #define ID3TAG_NUM_V22 16
247 #define ID3TAG_NUM_V23 21
249 static MMFILE_ID3TAG_INFO tag_info_v22[ID3TAG_NUM_V22] = {
250 {"TT2", 21170, AV_ID3TAG_TITLE},
251 {"TP1", 21041, AV_ID3TAG_ARTIST},
252 {"TP2", 21042, AV_ID3TAG_ALBUM_ARTIST},
253 {"TP3", 21043, AV_ID3TAG_CONDUCTOR},
254 {"TAL", 20588, AV_ID3TAG_ALBUM},
255 {"TYE", 21349, AV_ID3TAG_YEAR},
256 {"COM", 3629, AV_ID3TAG_COMMENT},
257 {"TCO", 20655, AV_ID3TAG_GENRE},
258 {"TRK", 21131, AV_ID3TAG_TRACKNUM},
259 {"PIC", 16739, AV_ID3TAG_PICTURE},
260 {"TCR", 20658, AV_ID3TAG_COPYRIGHT},
261 {"TCM", 20653, AV_ID3TAG_COMPOSER},
262 {"TRD", 21124, AV_ID3TAG_RECDATE},
265 static MMFILE_ID3TAG_INFO tag_info_v23[ID3TAG_NUM_V23] = {
266 {"TIT2", 665266, AV_ID3TAG_TITLE},
267 {"TPE1", 671953, AV_ID3TAG_ARTIST},
268 {"TPE2", 671954, AV_ID3TAG_ALBUM_ARTIST},
269 {"TPE3", 671955, AV_ID3TAG_CONDUCTOR},
270 {"TALB", 656834, AV_ID3TAG_ALBUM},
271 {"TYER", 681202, AV_ID3TAG_YEAR},
272 {"COMM", 114157, AV_ID3TAG_COMMENT},
273 {"TCON", 658990, AV_ID3TAG_GENRE},
274 {"TRCK", 673963, AV_ID3TAG_TRACKNUM},
275 {"APIC", 49507, AV_ID3TAG_PICTURE},
276 {"TCOP", 658992, AV_ID3TAG_COPYRIGHT},
277 {"TCOM", 658989, AV_ID3TAG_COMPOSER},
278 {"TRDA", 660097, AV_ID3TAG_RECDATE}, /*Recdate for 2.3*/
279 {"USLT", 708052, AV_ID3TAG_UNSYNCLYRICS},
280 {"SYLT", 648660, AV_ID3TAG_SYNCLYRICS},
281 {"TIT1", 665265, AV_ID3TAG_CONTENT_GROUP}, /*Content Group for 2.4*/
282 {"TDRC", 660099, AV_ID3TAG_RECDATE}, /*Recdate for 2.4*/
285 static int g_junk_counter_limit = 0;
287 static int GetStringFromTextTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header, eMMFILE_3GP_TEXT_TAG eTag)
289 int ret = MMFILE_UTIL_FAIL; /*fail*/
290 MMFILE_3GP_TEXT_TAGBOX texttag = {0, };
293 char *temp_text = NULL;
295 if (!formatContext || !fp || !basic_header) {
296 debug_error(DEBUG, "invalid param");
297 return MMFILE_UTIL_FAIL;
300 textBytes = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_TEXT_TAGBOX_LEN;
302 readed = mmfile_read(fp, (unsigned char *)&texttag, MMFILE_3GP_TEXT_TAGBOX_LEN);
303 if (readed != MMFILE_3GP_TEXT_TAGBOX_LEN) {
304 debug_error(DEBUG, "read text tag header fail");
305 ret = MMFILE_UTIL_FAIL;
309 if (textBytes <= 1) { /* there exist only 00(null) */
310 debug_error(DEBUG, "Text is NULL");
314 texttag.text = g_malloc0(textBytes);
316 readed = mmfile_read(fp, (unsigned char *)texttag.text, textBytes);
317 if (readed != textBytes) {
318 debug_error(DEBUG, "read text fail");
319 ret = MMFILE_UTIL_FAIL;
324 if ((texttag.text[0] == 0xFE) && (texttag.text[1] == 0xFF)) {
325 /* this char is UTF-16 */
326 temp_text = mmfile_convert_to_utf8((const char *)&texttag.text[2], readed - 2, MMFILE_CODESET_UTF16);
328 temp_text = g_strdup((const char *)texttag.text);
332 case eMMFILE_3GP_TAG_TITLE: {
333 if (!formatContext->title) {
334 formatContext->title = g_strdup(temp_text);
338 case eMMFILE_3GP_TAG_CAPTION: {
339 if (!formatContext->description) {
340 formatContext->description = g_strdup(temp_text);
344 case eMMFILE_3GP_TAG_COPYRIGHT: {
345 if (!formatContext->copyright) {
346 formatContext->copyright = g_strdup(temp_text);
350 case eMMFILE_3GP_TAG_PERFORMER: {
351 if (!formatContext->artist) {
352 formatContext->artist = g_strdup(temp_text);
356 case eMMFILE_3GP_TAG_AUTHOR: {
357 if (!formatContext->author) {
358 formatContext->author = g_strdup(temp_text);
362 case eMMFILE_3GP_TAG_GENRE: {
363 if (!formatContext->genre) {
364 formatContext->genre = g_strdup(temp_text);
369 debug_warning(DEBUG, "Not supported Text Tag type[%d]", eTag);
374 mmfile_free(texttag.text);
376 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
378 mmfile_free(temp_text);
380 return MMFILE_UTIL_SUCCESS;
383 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
384 mmfile_free(texttag.text);
389 static int GetYearFromYearTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
391 #define MAX_YEAR_BUFFER 10
393 MMFILE_3GP_YEAR_TAGBOX yearbox = {0, };
394 char temp_year[MAX_YEAR_BUFFER] = {0, };
396 if (!formatContext || !fp || !basic_header) {
397 debug_error(DEBUG, "invalid param");
398 return MMFILE_UTIL_FAIL;
401 readed = mmfile_read(fp, (unsigned char *)&yearbox, MMFILE_3GP_YEAR_TAGBOX_LEN);
402 if (readed != MMFILE_3GP_YEAR_TAGBOX_LEN) {
403 debug_error(DEBUG, "read yeartag header fail");
407 if (!formatContext->year) {
408 yearbox.year = mmfile_io_be_int16(yearbox.year);
409 snprintf(temp_year, MAX_YEAR_BUFFER, "%d", yearbox.year);
410 temp_year[MAX_YEAR_BUFFER - 1] = '\0';
411 formatContext->year = g_strdup((const char *)temp_year);
414 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
415 return MMFILE_UTIL_SUCCESS;
418 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
420 return MMFILE_UTIL_FAIL;
423 static int GetAlbumFromAlbumTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
425 int albumTitleLen = 0;
426 char *temp_text = NULL;
429 MMFILE_3GP_ALBUM_TAGBOX albumbox = {0, };
431 if (!formatContext || !fp || !basic_header) {
432 debug_error(DEBUG, "invalid param");
433 return MMFILE_UTIL_FAIL;
436 readed = mmfile_read(fp, (unsigned char *)&albumbox, MMFILE_3GP_ALBUM_TAGBOX_LEN);
437 if (readed != MMFILE_3GP_ALBUM_TAGBOX_LEN) {
438 debug_error(DEBUG, "read albumtag header fail");
442 albumTitleLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_ALBUM_TAGBOX_LEN - 1; /* 1: track number */
443 if (albumTitleLen > 1) { /* there exist only 00(null) */
444 debug_msg(RELEASE, "albumTitleLen=%d", albumTitleLen);
446 albumbox.albumtile = g_malloc0(albumTitleLen + 1); /* 1: for null char */
448 readed = mmfile_read(fp, (unsigned char *)albumbox.albumtile, albumTitleLen);
449 if (readed != albumTitleLen) {
450 debug_error(DEBUG, "read album title fail");
454 if (albumbox.albumtile[albumTitleLen - 1] == '\0') { /* there exist track number */
458 readed = mmfile_read(fp, (unsigned char *)&(albumbox.albumtile[albumTitleLen]), 1);
460 debug_error(DEBUG, "read album title fail");
463 albumbox.albumtile[albumTitleLen] = '\0';
467 if ((albumbox.albumtile[0] == 0xFE) && (albumbox.albumtile[1] == 0xFF)) {
468 /* this char is UTF-16 */
469 temp_text = mmfile_convert_to_utf8((const char *)&albumbox.albumtile[2], readed - 2, MMFILE_CODESET_UTF16);
471 temp_text = g_strdup((const char *)albumbox.albumtile);
474 if (!formatContext->album)
475 formatContext->album = temp_text;
477 mmfile_free(temp_text);
479 debug_msg(RELEASE, "formatContext->album=%s, strlen=%zu", formatContext->album, strlen(formatContext->album));
483 readed = mmfile_read(fp, (unsigned char *)&albumbox.trackNumber, 1);
485 debug_error(DEBUG, "read track number fail");
489 if (formatContext->tagTrackNum == 0) {
490 char tracknum[10] = {0, };
491 snprintf(tracknum, 10, "%d", albumbox.trackNumber);
493 formatContext->tagTrackNum = g_strdup((const char *)tracknum);
497 mmfile_free(albumbox.albumtile);
498 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
500 return MMFILE_UTIL_SUCCESS;
503 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
504 mmfile_free(albumbox.albumtile);
506 return MMFILE_UTIL_FAIL;
509 static int GetRatingFromRatingTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
512 int ratinginfoLen = 0;
513 char *temp_text = NULL;
515 MMFILE_3GP_RATING_TAGBOX ratingTag = {0, };
517 if (!formatContext || !fp || !basic_header) {
518 debug_error(DEBUG, "invalid param");
519 return MMFILE_UTIL_FAIL;
522 readed = mmfile_read(fp, (unsigned char *)&ratingTag, MMFILE_3GP_RATING_TAGBOX_LEN);
523 if (readed != MMFILE_3GP_RATING_TAGBOX_LEN) {
524 debug_error(DEBUG, "read rating tag header fail");
528 ratinginfoLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_RATING_TAGBOX_LEN;
530 if (ratinginfoLen == 1) {
531 debug_error(DEBUG, "Rating Text is NULL");
535 ratingTag.ratingInfo = g_malloc0(ratinginfoLen);
537 readed = mmfile_read(fp, (unsigned char *)ratingTag.ratingInfo, ratinginfoLen);
538 if (readed != ratinginfoLen) {
539 debug_error(DEBUG, "read rating info string fail");
544 if ((ratingTag.ratingInfo[0] == 0xFE) && (ratingTag.ratingInfo[1] == 0xFF)) {
545 /* this char is UTF-16 */
546 temp_text = mmfile_convert_to_utf8((const char *)&ratingTag.ratingInfo[2], readed - 2, MMFILE_CODESET_UTF16);
548 temp_text = g_strdup((const char *)ratingTag.ratingInfo);
551 if (!formatContext->rating) {
552 formatContext->rating = temp_text;
554 mmfile_free(temp_text);
557 mmfile_free(ratingTag.ratingInfo);
558 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
560 return MMFILE_UTIL_SUCCESS;
563 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
564 mmfile_free(ratingTag.ratingInfo);
566 return MMFILE_UTIL_FAIL;
570 static int GetClassficationFromClsfTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
572 int classinfoLen = 0;
574 char *temp_text = NULL;
575 MMFILE_3GP_CLASSIFICATION_TAGBOX classTag = {0, };
577 if (!formatContext || !fp || !basic_header) {
578 debug_error(DEBUG, "invalid param");
579 return MMFILE_UTIL_FAIL;
582 readed = mmfile_read(fp, (unsigned char *)&classTag, MMFILE_3GP_CLASS_TAGBOX_LEN);
583 if (readed != MMFILE_3GP_CLASS_TAGBOX_LEN) {
584 debug_error(DEBUG, "read classification tag header fail");
589 classinfoLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_CLASS_TAGBOX_LEN;
590 if (classinfoLen == 1) {
591 debug_error(DEBUG, "Classification Text is NULL");
595 classTag.classificationInfo = g_malloc0(classinfoLen);
597 readed = mmfile_read(fp, (unsigned char *)classTag.classificationInfo, classinfoLen);
598 if (readed != classinfoLen) {
599 debug_error(DEBUG, "read class info string fail");
604 if ((classTag.classificationInfo[0] == 0xFE) && (classTag.classificationInfo[1] == 0xFF)) {
605 /* this char is UTF-16 */
606 temp_text = mmfile_convert_to_utf8((const char *)&classTag.classificationInfo[2], readed - 2, MMFILE_CODESET_UTF16);
608 temp_text = g_strdup((const char *)classTag.classificationInfo);
611 if (!formatContext->classification) {
612 formatContext->classification = temp_text;
614 mmfile_free(temp_text);
617 mmfile_free(classTag.classificationInfo);
618 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
620 return MMFILE_UTIL_SUCCESS;
623 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
624 mmfile_free(classTag.classificationInfo);
626 return MMFILE_UTIL_FAIL;
630 * The Location Information box
631 * --------------------+-------------------+-----------------------------------+------
632 * Field Type Details Value
633 * --------------------+-------------------+-----------------------------------+------
634 * BoxHeader.Size Unsigned int(32)
635 * BoxHeader.Type Unsigned int(32) 'loci'
636 * BoxHeader.Version Unsigned int(8) 0
637 * BoxHeader.Flags Bit(24) 0
639 * Language Unsigned int(5)[3] Packed ISO-639-2/T language code
640 * Name String Text of place name
641 * Role Unsigned int(8) Non-negative value indicating role
643 * Longitude Unsigned int(32) Fixed-point value of the longitude
644 * Latitude Unsigned int(32) Fixed-point value of the latitude
645 * Altitude Unsigned int(32) Fixed-point value of the Altitude
646 * Astronomical_body String Text of astronomical body
647 * Additional_notes String Text of additional location-related
649 * --------------------+-------------------+-----------------------------------+------
651 static int _get_char_position(unsigned char *src, char ch, int max)
654 for (i = 0; i < max; i++) {
655 if (*(src + i) == ch)
662 static int GetLocationFromLociTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
665 MMFILE_3GP_LOCATION_TAGBOX lociTag = {0, };
668 unsigned char *buffer = NULL;
669 unsigned char *p = NULL;
671 unsigned int name_sz = 0;
672 unsigned int astro_sz = 0;
674 int ilong, ilati, ialti;
675 float flong, flati, falti;
678 if (!formatContext || !fp || !basic_header) {
679 debug_error(DEBUG, "invalid param");
680 return MMFILE_UTIL_FAIL;
683 readed = mmfile_read(fp, (unsigned char *)&lociTag, 6); /*6 = version + flag + pad + language */
685 debug_error(DEBUG, "read location tag header fail");
689 /*buffer len = name + role + ... + additional notes length */
690 bufferLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - 6;
692 debug_error(DEBUG, "too small buffer");
696 buffer = g_malloc0(bufferLen);
698 readed = mmfile_read(fp, (unsigned char *)buffer, bufferLen);
699 if (readed != bufferLen) {
700 debug_error(DEBUG, "read location tag fail");
706 pos = _get_char_position(p, '\0', readed - (1 + 4 + 4 + 4 + 2));
708 if (p[0] == 0xFE && p[1] == 0xFF) {
709 lociTag.name = (unsigned char *)mmfile_convert_to_utf8((const char *)(p + 2), pos - 2, MMFILE_CODESET_UTF16);
711 lociTag.name = (unsigned char *)g_strdup((const char *)p);
723 debug_msg(RELEASE, "long: 0x%02X 0x%02X 0x%02X 0x%02X ", *(p + 0), *(p + 1), *(p + 2), *(p + 3));
724 debug_msg(RELEASE, "lati: 0x%02X 0x%02X 0x%02X 0x%02X ", *(p + 4), *(p + 5), *(p + 6), *(p + 7));
725 debug_msg(RELEASE, "alti: 0x%02X 0x%02X 0x%02X 0x%02X ", *(p + 8), *(p + 9), *(p + 10), *(p + 11));
727 ilong = mmfile_io_be_uint32(*(unsigned int *)p);
728 ilati = mmfile_io_be_uint32(*(unsigned int *)(p + 4));
729 ialti = mmfile_io_be_uint32(*(unsigned int *)(p + 8));
731 flong = (float)ilong / (1 << 16);
732 flati = (float)ilati / (1 << 16);
733 falti = (float)ialti / (1 << 16);
736 lociTag.longitude = flong;
738 lociTag.latitude = flati;
740 lociTag.altitude = falti;
744 /*astronomical body*/
745 pos = _get_char_position(p, '\0', readed - (name_sz + 1 + 4 + 4 + 4 + 1));
747 if (p[0] == 0xFE && p[1] == 0xFF) {
748 lociTag.astronomical_body = (unsigned char *)mmfile_convert_to_utf8((const char *)(p + 2), pos - 2, MMFILE_CODESET_UTF16);
750 lociTag.astronomical_body = (unsigned char *)g_strdup((const char *)p);
759 pos = _get_char_position(p, '\0', readed - (name_sz + 1 + 4 + 4 + 4 + astro_sz));
761 if (p[0] == 0xFE && p[1] == 0xFF) {
762 lociTag.additional_notes = (unsigned char *)mmfile_convert_to_utf8((const char *)(p + 2), pos - 2, MMFILE_CODESET_UTF16);
764 lociTag.additional_notes = (unsigned char *)g_strdup((const char *)p);
770 debug_msg(RELEASE, "** Location Information **");
771 debug_msg(RELEASE, "Name : %s", lociTag.name);
772 debug_msg(RELEASE, "Role : %d (0: shooting, 1: real, 2: fictional, other: reserved)", lociTag.role);
773 debug_msg(RELEASE, "Longitude : %16.16f", lociTag.longitude);
774 debug_msg(RELEASE, "Latitude : %16.16f", lociTag.latitude);
775 debug_msg(RELEASE, "Altitude : %16.16f", lociTag.altitude);
776 debug_msg(RELEASE, "Astronomical body: %s", lociTag.astronomical_body);
777 debug_msg(RELEASE, "Additional notes : %s", lociTag.additional_notes);
779 formatContext->longitude = lociTag.longitude;
780 formatContext->latitude = lociTag.latitude;
781 formatContext->altitude = lociTag.altitude;
784 mmfile_free(lociTag.name);
785 mmfile_free(lociTag.astronomical_body);
786 mmfile_free(lociTag.additional_notes);
788 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
789 return MMFILE_UTIL_SUCCESS;
792 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
794 mmfile_free(lociTag.name);
795 mmfile_free(lociTag.astronomical_body);
796 mmfile_free(lociTag.additional_notes);
798 return MMFILE_UTIL_FAIL;
801 static int GetSAUTInfoFromSMTATagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
803 MMFILE_M4A_SMTA_TAGBOX smtaTag = {0, };
806 if (!formatContext || !fp || !basic_header) {
807 debug_error(DEBUG, "invalid param");
808 return MMFILE_UTIL_FAIL;
811 readed = mmfile_read(fp, (unsigned char *)&smtaTag, sizeof(MMFILE_M4A_SMTA_TAGBOX));
812 if (readed != sizeof(MMFILE_M4A_SMTA_TAGBOX)) {
813 debug_error(DEBUG, "read smta tag header fail");
817 smtaTag.length = mmfile_io_be_uint32(smtaTag.length);
818 smtaTag.value = mmfile_io_be_uint32(smtaTag.value);
820 debug_msg(RELEASE, "Len : 0x%x", smtaTag.length);
821 debug_msg(RELEASE, "Saut : 0x%x 0x%x 0x%x 0x%x", smtaTag.saut[0], smtaTag.saut[1], smtaTag.saut[2], smtaTag.saut[3]);
822 debug_msg(RELEASE, "Value : 0x%x", smtaTag.value);
833 7: 360 slowmotion mode
834 8: 180 slowmotion mode
836 if (smtaTag.saut[0] == 's'
837 && smtaTag.saut[1] == 'a'
838 && smtaTag.saut[2] == 'u'
839 && smtaTag.saut[3] == 't') {
840 if (smtaTag.value == 0x01) {
841 debug_msg(RELEASE, "This has saut tag and valid value");
842 formatContext->smta = 1;
843 } else if (smtaTag.value == 0x02) {
844 debug_msg(RELEASE, "This has saut tag and valid value");
845 formatContext->smta = 2;
847 debug_error(DEBUG, "This has saut tag but invalid value");
851 debug_error(DEBUG, "This hasn't saut tag and valid value");
855 return MMFILE_UTIL_SUCCESS;
858 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
860 return MMFILE_UTIL_FAIL;
863 static int GetSA3DInfoFromMP4ATagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
865 if (!formatContext || !fp || !basic_header) {
866 debug_error(DEBUG, "invalid param");
867 return MMFILE_UTIL_FAIL;
870 unsigned char *buffer;
872 bool is_SA3D_present = false;
874 MMFILE_MP4A_SA3D_TAGBOX sa3dTag = {0, };
876 formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_UNKNOWN;
877 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_UNKNOWN;
878 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_UNKNOWN;
880 mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
882 buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
884 debug_error(DEBUG, "calloc failed ");
888 readed = mmfile_read(fp, buffer, basic_header->size);
889 if (readed != (int)basic_header->size) {
890 debug_error(DEBUG, "read mp4a box failed");
894 for (i = 0; i + 3 < basic_header->size; ++i)
895 if (buffer[i] == 'S' && buffer[i + 1] == 'A' && buffer[i + 2] == '3' && buffer[i + 3] == 'D') {
896 debug_warning(DEBUG, "SA3D data found at offset %d bytes", i);
897 is_SA3D_present = true;
901 if (!is_SA3D_present) {
902 debug_warning(DEBUG, "No SA3D box found");
906 mmfile_seek(fp, basic_header->start_offset + i + 4, SEEK_SET);
908 readed = mmfile_read(fp, (unsigned char *)&sa3dTag, sizeof(MMFILE_MP4A_SA3D_TAGBOX));
909 if (readed != sizeof(MMFILE_MP4A_SA3D_TAGBOX)) {
910 debug_error(DEBUG, "read SA3D tag header fail");
914 sa3dTag.ambisonic_order = mmfile_io_be_uint32(sa3dTag.ambisonic_order);
915 sa3dTag.num_channels = mmfile_io_be_uint32(sa3dTag.num_channels);
916 for (i = 0; i < sa3dTag.num_channels; ++i)
917 sa3dTag.channel_map[i] = mmfile_io_be_uint32(sa3dTag.channel_map[i]);
919 debug_msg(RELEASE, "sa3dTag.version = %d", sa3dTag.version);
920 debug_msg(RELEASE, "sa3dTag.ambisonic_type = %d", sa3dTag.ambisonic_type);
921 debug_msg(RELEASE, "sa3dTag.ambisonic_order = %d", sa3dTag.ambisonic_order);
922 debug_msg(RELEASE, "sa3dTag.ambisonic_channel_ordering = %d", sa3dTag.ambisonic_channel_ordering);
923 debug_msg(RELEASE, "sa3dTag.ambisonic_normalization = %d", sa3dTag.ambisonic_normalization);
924 debug_msg(RELEASE, "sa3dTag.num_channels = %d", sa3dTag.num_channels);
925 for (i = 0; i < sa3dTag.num_channels; ++i)
926 debug_msg(RELEASE, "sa3dTag.channel_map[%d] = %d", i, sa3dTag.channel_map[i]);
928 if (sa3dTag.version != RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
929 debug_error(DEBUG, "SA3D tag box version is unsupported");
932 if (sa3dTag.ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
933 formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_PERIPHONIC;
935 switch (sa3dTag.ambisonic_order) {
936 case MMFILE_AMBISONIC_ORDER_FOA: {
937 if (sa3dTag.num_channels == 4) {
938 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_FOA;
940 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
941 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
942 (sa3dTag.channel_map[0] == 0) &&
943 (sa3dTag.channel_map[1] == 1) &&
944 (sa3dTag.channel_map[2] == 2) &&
945 (sa3dTag.channel_map[3] == 3))
946 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
948 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
949 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
950 (sa3dTag.channel_map[0] == 0) &&
951 (sa3dTag.channel_map[1] == 3) &&
952 (sa3dTag.channel_map[2] == 1) &&
953 (sa3dTag.channel_map[3] == 2))
954 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
956 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond");
962 case MMFILE_AMBISONIC_ORDER_SOA: {
963 if (sa3dTag.num_channels == 9) {
964 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_SOA;
966 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
967 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
968 (sa3dTag.channel_map[0] == 0) &&
969 (sa3dTag.channel_map[1] == 1) &&
970 (sa3dTag.channel_map[2] == 2) &&
971 (sa3dTag.channel_map[3] == 3) &&
972 (sa3dTag.channel_map[4] == 4) &&
973 (sa3dTag.channel_map[5] == 5) &&
974 (sa3dTag.channel_map[6] == 6) &&
975 (sa3dTag.channel_map[7] == 7) &&
976 (sa3dTag.channel_map[8] == 8))
977 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
979 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
980 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
981 (sa3dTag.channel_map[0] == 0) &&
982 (sa3dTag.channel_map[1] == 3) &&
983 (sa3dTag.channel_map[2] == 1) &&
984 (sa3dTag.channel_map[3] == 2) &&
985 (sa3dTag.channel_map[4] == 6) &&
986 (sa3dTag.channel_map[5] == 7) &&
987 (sa3dTag.channel_map[6] == 5) &&
988 (sa3dTag.channel_map[7] == 8) &&
989 (sa3dTag.channel_map[8] == 4))
990 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
992 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond");
999 case MMFILE_AMBISONIC_ORDER_TOA: {
1000 if (sa3dTag.num_channels == 16) {
1001 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_TOA;
1003 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
1004 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
1005 (sa3dTag.channel_map[0] == 0) &&
1006 (sa3dTag.channel_map[1] == 1) &&
1007 (sa3dTag.channel_map[2] == 2) &&
1008 (sa3dTag.channel_map[3] == 3) &&
1009 (sa3dTag.channel_map[4] == 4) &&
1010 (sa3dTag.channel_map[5] == 5) &&
1011 (sa3dTag.channel_map[6] == 6) &&
1012 (sa3dTag.channel_map[7] == 7) &&
1013 (sa3dTag.channel_map[8] == 8) &&
1014 (sa3dTag.channel_map[9] == 9) &&
1015 (sa3dTag.channel_map[10] == 10) &&
1016 (sa3dTag.channel_map[11] == 11) &&
1017 (sa3dTag.channel_map[12] == 12) &&
1018 (sa3dTag.channel_map[13] == 13) &&
1019 (sa3dTag.channel_map[14] == 14) &&
1020 (sa3dTag.channel_map[15] == 15))
1021 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
1023 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
1024 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
1025 (sa3dTag.channel_map[0] == 0) &&
1026 (sa3dTag.channel_map[1] == 3) &&
1027 (sa3dTag.channel_map[2] == 1) &&
1028 (sa3dTag.channel_map[3] == 2) &&
1029 (sa3dTag.channel_map[4] == 6) &&
1030 (sa3dTag.channel_map[5] == 7) &&
1031 (sa3dTag.channel_map[6] == 5) &&
1032 (sa3dTag.channel_map[7] == 8) &&
1033 (sa3dTag.channel_map[8] == 4) &&
1034 (sa3dTag.channel_map[9] == 12) &&
1035 (sa3dTag.channel_map[10] == 13) &&
1036 (sa3dTag.channel_map[11] == 11) &&
1037 (sa3dTag.channel_map[12] == 14) &&
1038 (sa3dTag.channel_map[13] == 10) &&
1039 (sa3dTag.channel_map[14] == 15) &&
1040 (sa3dTag.channel_map[15] == 9))
1041 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
1043 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond");
1051 debug_warning(DEBUG, "Ambisonic order or format is not supported: ambix or amb formats up to 3rd order were expected.");
1057 debug_msg(RELEASE, "formatContext->ambisonic_type = %d", formatContext->ambisonicType);
1058 debug_msg(RELEASE, "formatContext->ambisonic_order = %d", formatContext->ambisonicOrder);
1059 debug_msg(RELEASE, "formatContext->ambisonic_format = %d", formatContext->ambisonicFormat);
1062 return MMFILE_UTIL_SUCCESS;
1065 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1067 return MMFILE_UTIL_FAIL;
1070 static int ParseSt3dData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset)
1072 uint8_t stereo_mode = INVALID_UINT8_VALUE;
1073 unsigned int readed = 0;
1075 mmfile_seek(fp, start_offset, SEEK_SET);
1077 readed = mmfile_read(fp, (unsigned char *)&stereo_mode, sizeof(uint8_t));
1078 if (readed != sizeof(uint8_t)) {
1079 debug_error(DEBUG, "read st3d tag header fail");
1080 return MMFILE_UTIL_FAIL;
1083 formatContext->stereoModeV2 = stereo_mode;
1085 return MMFILE_UTIL_SUCCESS;
1088 static int ParseSvhdData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset, unsigned int box_size)
1090 unsigned int readed = 0;
1092 formatContext->metadataSourceV2 = (char *)calloc(1, box_size);
1093 if (!formatContext->metadataSourceV2) {
1094 debug_error(DEBUG, "Calloc failed");
1095 return MMFILE_UTIL_FAIL;
1098 mmfile_seek(fp, start_offset, SEEK_SET);
1099 readed = mmfile_read(fp, (unsigned char *)formatContext->metadataSourceV2, box_size);
1100 if (readed != box_size) {
1101 debug_error(DEBUG, "read svhd tag header fail");
1102 if (formatContext->metadataSourceV2)
1103 free(formatContext->metadataSourceV2);
1104 return MMFILE_UTIL_FAIL;
1107 return MMFILE_UTIL_SUCCESS;
1110 static int ParseProjData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset)
1112 unsigned int readed = 0;
1113 typedef struct proj_box_data_s {
1117 } __attribute__((aligned(1), packed)) proj_box_data;
1119 typedef struct prhd_box_data_s {
1120 uint32_t projection_pose_yaw;
1121 uint32_t projection_pose_pitch;
1122 uint32_t projection_pose_roll;
1125 typedef struct equi_box_data_s {
1126 uint32_t projection_bounds_top;
1127 uint32_t projection_bounds_bottom;
1128 uint32_t projection_bounds_left;
1129 uint32_t projection_bounds_right;
1132 typedef struct cbmp_box_data_s {
1138 proj_box_data proj_data;
1139 proj_data.proj_type = INVALID_UINT_VALUE;
1141 prhd_box_data prhd_data;
1142 prhd_data.projection_pose_yaw = INVALID_UINT_VALUE;
1143 prhd_data.projection_pose_pitch = INVALID_UINT_VALUE;
1144 prhd_data.projection_pose_roll = INVALID_UINT_VALUE;
1146 equi_box_data equi_data;
1147 equi_data.projection_bounds_top = INVALID_UINT_VALUE;
1148 equi_data.projection_bounds_bottom = INVALID_UINT_VALUE;
1149 equi_data.projection_bounds_left = INVALID_UINT_VALUE;
1150 equi_data.projection_bounds_right = INVALID_UINT_VALUE;
1152 cbmp_box_data cbmp_data;
1153 cbmp_data.layout = INVALID_UINT_VALUE;
1154 cbmp_data.padding = INVALID_UINT_VALUE;
1156 mmfile_seek(fp, start_offset, SEEK_SET);
1158 readed = mmfile_read(fp, (unsigned char *)&proj_data, sizeof(proj_box_data));
1159 if (readed != sizeof(proj_box_data)) {
1160 debug_error(DEBUG, "read of proj box failed");
1161 return MMFILE_UTIL_FAIL;
1164 formatContext->projTypeV2 = mmfile_io_be_uint32(proj_data.proj_type);
1166 debug_error(DEBUG, "formatContext->projTypeV2 = %d", formatContext->projTypeV2);
1167 debug_error(DEBUG, "proj_data.version = %d", proj_data.version);
1168 debug_error(DEBUG, "proj_data.flags = %d", ((uint32_t)proj_data.flags[0] << 16) +
1169 ((uint32_t)proj_data.flags[1] << 8) + (uint32_t)proj_data.flags[2]);
1171 mmfile_seek(fp, sizeof(proj_box_data), SEEK_CUR);
1172 readed = mmfile_read(fp, (unsigned char *)&prhd_data, sizeof(prhd_box_data));
1173 if (readed != sizeof(prhd_box_data)) {
1174 debug_error(DEBUG, "read of prhd box failed");
1175 return MMFILE_UTIL_FAIL;
1178 formatContext->poseYawV2 = mmfile_io_be_uint32(prhd_data.projection_pose_yaw);
1179 formatContext->posePitchV2 = mmfile_io_be_uint32(prhd_data.projection_pose_pitch);
1180 formatContext->poseRollV2 = mmfile_io_be_uint32(prhd_data.projection_pose_roll);
1182 debug_error(DEBUG, "formatContext->poseYawV2 = %d", formatContext->poseYawV2);
1183 debug_error(DEBUG, "formatContext->posePitchV2 = %d", formatContext->posePitchV2);
1184 debug_error(DEBUG, "formatContext->poseRollV2 = %d", formatContext->poseRollV2);
1186 if (formatContext->projTypeV2 == PROJECTION_TYPE_EQUI) {
1187 debug_msg(RELEASE, "Projection type is Equirectangular");
1188 mmfile_seek(fp, 8, SEEK_CUR); /* 8 = 4 (for size) + 4 (fourcc) */
1189 readed = mmfile_read(fp, (unsigned char *)&equi_data, sizeof(equi_box_data));
1190 if (readed != sizeof(equi_box_data)) {
1191 debug_error(DEBUG, "read of equi box failed");
1192 return MMFILE_UTIL_FAIL;
1195 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi_data.projection_bounds_top);
1196 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi_data.projection_bounds_bottom);
1197 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi_data.projection_bounds_left);
1198 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi_data.projection_bounds_right);
1200 debug_error(DEBUG, "formatContext->equiBoundsTopV2 = %d", formatContext->equiBoundsTopV2);
1201 debug_error(DEBUG, "formatContext->equiBoundsBottomV2 = %d", formatContext->equiBoundsBottomV2);
1202 debug_error(DEBUG, "formatContext->equiBoundsLeftV2 = %d", formatContext->equiBoundsLeftV2);
1203 debug_error(DEBUG, "formatContext->equiBoundsRightV2 = %d", formatContext->equiBoundsRightV2);
1205 } else if (formatContext->projTypeV2 == PROJECTION_TYPE_CBMP) {
1206 debug_msg(RELEASE, "Projection type is Cubemap");
1207 mmfile_seek(fp, 8, SEEK_CUR); /* 8 = 4 (for size) + 4 (fourcc) */
1208 readed = mmfile_read(fp, (unsigned char *)&cbmp_data, sizeof(cbmp_box_data));
1209 if (readed != sizeof(cbmp_box_data)) {
1210 debug_error(DEBUG, "read of cbmp box failed");
1211 return MMFILE_UTIL_FAIL;
1214 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp_data.layout);
1215 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp_data.padding);
1217 debug_error(DEBUG, "formatContext->cbmpLayoutV2 = %d", formatContext->cbmpLayoutV2);
1218 debug_error(DEBUG, "formatContext->cbmpPaddingV2 = %d", formatContext->cbmpPaddingV2);
1221 debug_msg(RELEASE, "Projection type is %d (unknown)", proj_data.proj_type);
1222 return MMFILE_UTIL_FAIL;
1225 return MMFILE_UTIL_SUCCESS;
1228 static int GetVideoV2MetadataFromAvc1TagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1230 if (!formatContext || !fp || !basic_header) {
1231 debug_error(DEBUG, "invalid param");
1232 return MMFILE_UTIL_FAIL;
1235 unsigned char *buffer;
1239 formatContext->stereoModeV2 = INVALID_UINT_VALUE;
1240 formatContext->metadataSourceV2 = NULL;
1241 formatContext->projTypeV2 = INVALID_UINT_VALUE;
1242 formatContext->poseYawV2 = INVALID_UINT_VALUE;
1243 formatContext->posePitchV2 = INVALID_UINT_VALUE;
1244 formatContext->poseRollV2 = INVALID_UINT_VALUE;
1245 formatContext->cbmpLayoutV2 = INVALID_UINT_VALUE;
1246 formatContext->cbmpPaddingV2 = INVALID_UINT_VALUE;
1247 formatContext->equiBoundsTopV2 = INVALID_UINT_VALUE;
1248 formatContext->equiBoundsBottomV2 = INVALID_UINT_VALUE;
1249 formatContext->equiBoundsLeftV2 = INVALID_UINT_VALUE;
1250 formatContext->equiBoundsRightV2 = INVALID_UINT_VALUE;
1252 mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
1254 buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
1256 debug_error(DEBUG, "calloc failed ");
1260 readed = mmfile_read(fp, buffer, basic_header->size);
1261 if (readed != (int)basic_header->size) {
1262 debug_error(DEBUG, "read st3d box failed");
1266 for (i = 0; i + 3 < basic_header->size; ++i) {
1267 if ((buffer[i] == 's' && buffer[i + 1] == 't' && buffer[i + 2] == '3' && buffer[i + 3] == 'd') && (formatContext->stereoModeV2 == INVALID_UINT_VALUE)) {
1268 debug_warning(DEBUG, "st3d data found at offset %lld", basic_header->start_offset + i);
1269 ParseSt3dData(formatContext, fp, basic_header->start_offset + i + 4);
1270 debug_msg(RELEASE, "formatContext->stereoModeV2 = %d", formatContext->stereoModeV2);
1272 if (buffer[i] == 's' && buffer[i + 1] == 'v' && buffer[i + 2] == '3' && buffer[i + 3] == 'd') {
1273 debug_warning(DEBUG, "sv3d data found at offset %lld", basic_header->start_offset + i);
1274 formatContext->isSpherical = true;
1276 if (buffer[i] == 's' && buffer[i + 1] == 'v' && buffer[i + 2] == 'h' && buffer[i + 3] == 'd') {
1277 debug_warning(DEBUG, "svhd data found at offset %lld", basic_header->start_offset + i);
1278 ParseSvhdData(formatContext, fp, basic_header->start_offset + i + 4, mmfile_io_be_uint32(*((uint32_t*)(buffer - 4 + i))));
1279 debug_msg(RELEASE, "formatContext->metadataSourceV2 = %s (length = %zu)", formatContext->metadataSourceV2, strlen(formatContext->metadataSourceV2));
1281 if (buffer[i] == 'p' && buffer[i + 1] == 'r' && buffer[i + 2] == 'o' && buffer[i + 3] == 'j') {
1282 debug_warning(DEBUG, "proj data found at offset %lld", basic_header->start_offset + i);
1283 ParseProjData(formatContext, fp, basic_header->start_offset + i + 4);
1287 return MMFILE_UTIL_SUCCESS;
1290 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1292 return MMFILE_UTIL_FAIL;
1295 static int GetValueFromCDISTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1297 unsigned int value = 0;
1300 if (!formatContext || !fp || !basic_header) {
1301 debug_error(DEBUG, "invalid param");
1302 return MMFILE_UTIL_FAIL;
1305 readed = mmfile_read(fp, (unsigned char *)&value, sizeof(unsigned int));
1306 if (readed != sizeof(unsigned int)) {
1307 debug_error(DEBUG, "read cdis tag header fail");
1311 value = mmfile_io_be_uint32(value);
1313 debug_msg(RELEASE, "Value : 0x%x", value);
1315 if (value == 0x01) {
1316 debug_msg(RELEASE, "This has cdis tag and valid value");
1317 formatContext->cdis = 1;
1319 debug_error(DEBUG, "This has cdis tag and but invalid value");
1323 return MMFILE_UTIL_SUCCESS;
1326 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1328 return MMFILE_UTIL_FAIL;
1331 static int GetTagFromMetaBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1334 MMFILE_MP4_BASIC_BOX_HEADER hdlrBoxHeader = {0, };
1335 MMFILE_MP4_BASIC_BOX_HEADER id3v2BoxHeader = {0, };
1336 MMFILE_3GP_ID3V2_BOX id3v2Box = {0, };
1337 AvFileContentInfo tagInfo = {0, };
1338 unsigned char tagVersion = 0;
1339 bool versionCheck = false;
1341 unsigned int meta_version = 0;
1342 MMFILE_3GP_HANDLER_BOX hdlrBox = {0, };
1344 #ifdef ENABLE_ITUNES_META /* We don't support itunes meta now. so this is not defined yet */
1345 int iTunes_meta = 0;
1349 readed = mmfile_read(fp, (unsigned char *)&meta_version, 4);
1351 debug_error(DEBUG, "read meta box version");
1356 readed = mmfile_read(fp, (unsigned char *)&hdlrBoxHeader, MMFILE_MP4_BASIC_BOX_HEADER_LEN);
1357 if (readed != MMFILE_MP4_BASIC_BOX_HEADER_LEN) {
1358 debug_error(DEBUG, "read hdlr box header");
1362 if (hdlrBoxHeader.type != FOURCC('h', 'd', 'l', 'r')) {
1363 debug_warning(DEBUG, "meta type is not hdlr");
1367 hdlrBoxHeader.size = mmfile_io_be_uint32(hdlrBoxHeader.size);
1368 hdlrBoxHeader.type = mmfile_io_le_uint32(hdlrBoxHeader.type);
1370 readed = mmfile_read(fp, (unsigned char *)&hdlrBox, MMFILE_3GP_HANDLER_BOX_LEN);
1371 if (readed != MMFILE_3GP_HANDLER_BOX_LEN) {
1372 debug_error(DEBUG, "read hdlr box");
1376 hdlrBox.handler_type = mmfile_io_le_uint32(hdlrBox.handler_type);
1379 * check tag type (ID3v2 or iTunes)
1381 if (hdlrBox.handler_type == FOURCC('I', 'D', '3', '2')) {
1382 debug_msg(RELEASE, "ID3v2 tag detected.");
1385 #ifdef ENABLE_ITUNES_META
1388 } else if (hdlrBox.handler_type == FOURCC('m', 'd', 'i', 'r') &&
1389 mmfile_io_le_uint32(hdlrBox.reserved[0]) == FOURCC('a', 'p', 'p', 'l')) {
1391 debug_msg(RELEASE, "Apple iTunes tag detected by mdir.");
1393 #ifdef ENABLE_ITUNES_META
1397 debug_warning(DEBUG, "unknown meta type. 4CC:[%c%c%c%c]", ((char *)&hdlrBox.handler_type)[0],
1398 ((char *)&hdlrBox.handler_type)[1],
1399 ((char *)&hdlrBox.handler_type)[2],
1400 ((char *)&hdlrBox.handler_type)[3]);
1401 /*goto exception; */
1404 #ifdef ENABLE_ITUNES_META
1405 if (!id3_meta && !iTunes_meta) {
1407 APPLE meta data for iTunes reader = 'mdir.' so if handler type is 'mdir', this content may has itunes meta.
1408 most of contents has 'mdir' + 'appl'. but some contents just has 'mdir'
1409 but 'ilst' is meta for iTunes. so find 'ilst' is more correct to check if this contents has iTunes meta or not.*/
1411 const char *ilst_box = "ilst";
1412 int buf_size = strlen(ilst_box);
1414 unsigned char read_buf[buf_size + 1];
1415 memset(read_buf, 0x00, buf_size + 1);
1418 mmfile_seek(fp, hdlrBoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_HANDLER_BOX_LEN + 4, SEEK_CUR); /*+4 is hdlr size field */
1420 readed = mmfile_read(fp, read_buf, buf_size); /* to find 'ilst' */
1421 if (readed != buf_size) {
1422 debug_error(DEBUG, "read fail [%d]", readed);
1426 if (read_buf[0] == 'i' && read_buf[1] == 'l' && read_buf[2] == 's' && read_buf[3] == 't') {
1427 debug_msg(RELEASE, "Apple iTunes tag detected by ilst.");
1433 #ifdef ENABLE_ITUNES_META
1436 * iTunes (Cover[?ovr] & Track[trkn] only extract!) + Genre/Artist : Added 2010.10.27,28
1444 #define _ITUNES_READ_BUF_SZ 20
1445 #define _ITUNES_TRACK_NUM_SZ 4
1446 #define _ITUNES_GENRE_NUM_SZ 4
1447 #define _ITUNES_COVER_TYPE_JPEG 13
1448 #define _ITUNES_COVER_TYPE_PNG 14
1450 unsigned char read_buf[_ITUNES_READ_BUF_SZ];
1452 int cover_sz = 0, cover_type = 0, cover_found = 0;
1453 /* int track_found = 0; */ /* , track_num = 0; */
1454 /* int genre_found = 0; */ /* , genre_index = 0; */
1455 /* int artist_found = 0; */ /* , artist_sz = 0; */
1456 int limit = basic_header->size - hdlrBoxHeader.size;
1457 long long cover_offset = 0; /*, track_offset =0, genre_offset = 0, artist_offset = 0; */
1459 /* for (i = 0, cover_found = 0, track_found = 0, genre_found = 0, artist_found = 0; i < limit && (cover_found == 0 || track_found == 0 || genre_found == 0 || artist_found == 0) ; i++) { */
1460 for (i = 0; (i < limit) && (cover_found == 0) ; i++) {
1461 readed = mmfile_read(fp, read_buf, _ITUNES_READ_BUF_SZ);
1462 if (readed != _ITUNES_READ_BUF_SZ)
1465 /*ffmpeg extract artist, tracknum, genre and cover image. see mov_read_udta_string().
1466 but ffmpeg does not support strange cover image.
1467 only support covr type 0xd(JPEG), 0xe(PNG), 0x1b(BMP). but we support other type*/
1470 * Artist : Added 2010.10.28
1472 if (artist_found == 0 &&
1473 read_buf[0] == 0xa9 && read_buf[1] == 'A' && read_buf[2] == 'R' && read_buf[3] == 'T' &&
1474 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1477 artist_offset = mmfile_tell(fp);
1478 artist_sz = mmfile_io_be_uint32(*(int *)(read_buf + 4)) - 16; /* atom len(4)+data(4)+atom verion(1)+flag(3)+null(4) = 16 */
1480 debug_msg(RELEASE, "----------------------------------- artist found, offset=[%lld], size=[%d]", artist_offset, artist_sz);
1486 if (track_found == 0 &&
1487 read_buf[0] == 't' && read_buf[1] == 'r' && read_buf[2] == 'k' && read_buf[3] == 'n' &&
1488 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1491 track_offset = mmfile_tell(fp);
1493 debug_msg(RELEASE, "----------------------------------- Track found, offset=[%lld]", track_offset);
1497 * Genre : Added 2010.10.27
1499 /*ffmpeg extract genre but only (0xa9,'g','e','n'). see mov_read_udta_string()*/
1500 if (genre_found == 0 &&
1501 read_buf[0] == 'g' && read_buf[1] == 'n' && read_buf[2] == 'r' && read_buf[3] == 'e' &&
1502 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1505 genre_offset = mmfile_tell(fp);
1507 debug_msg(RELEASE, "----------------------------------- genre found, offset=[%lld]", genre_offset);
1515 if (cover_found == 0 &&
1516 read_buf[0] == 'c' && read_buf[1] == 'o' && read_buf[2] == 'v' && read_buf[3] == 'r' &&
1517 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1520 cover_sz = mmfile_io_be_uint32(*(int *)(read_buf + 4)) - 12;
1521 cover_type = mmfile_io_be_uint32(*(int *)(read_buf + 12));
1523 cover_offset = mmfile_tell(fp);
1525 debug_msg(RELEASE, "----------------------------------- cover_found found, offset=[%lld]", cover_offset);
1528 mmfile_seek(fp, -(_ITUNES_READ_BUF_SZ - 1), SEEK_CUR); /*FIXME: poor search*/
1531 /*ffmpeg extract artist, tracknum, except cover image. see mov_read_udta_string()*/
1534 if (artist_sz > 0) {
1535 mmfile_seek(fp, artist_offset, SEEK_SET);
1537 if (formatContext->artist) {
1538 debug_msg(RELEASE, "----------------------------------- previous artist was [%s] ", formatContext->artist);
1539 free(formatContext->artist);
1542 debug_msg(RELEASE, "----------------------------------- new artist will be allocated with size (len+1) [%d] ", artist_sz + 1);
1543 formatContext->artist = g_malloc0(artist_sz + 1);
1545 if (formatContext->artist) {
1546 readed = mmfile_read(fp, (unsigned char *)formatContext->artist, artist_sz);
1547 formatContext->artist[artist_sz] = '\0';
1549 debug_msg(RELEASE, "----------------------------------- new artist is [%s] ", formatContext->artist);
1551 if (readed != artist_sz) {
1552 debug_error(DEBUG, "failed to read. ret = %d, in = %d", readed, artist_sz);
1553 mmfile_free(formatContext->artist);
1560 mmfile_seek(fp, track_offset, SEEK_SET);
1561 readed = mmfile_read(fp, read_buf, _ITUNES_TRACK_NUM_SZ);
1562 if (readed != _ITUNES_TRACK_NUM_SZ) {
1563 debug_error(DEBUG, "failed to read. ret = %d, in = %d", readed, _ITUNES_TRACK_NUM_SZ);
1565 track_num = mmfile_io_be_uint32(*(int *)read_buf);
1566 if (!formatContext->tagTrackNum) {
1567 memset(read_buf, 0x00, _ITUNES_READ_BUF_SZ);
1568 snprintf((char *)read_buf, sizeof(read_buf), "%d", track_num);
1569 formatContext->tagTrackNum = g_strdup((const char *)read_buf);
1575 mmfile_seek(fp, genre_offset, SEEK_SET);
1576 readed = mmfile_read(fp, read_buf, _ITUNES_GENRE_NUM_SZ);
1577 if (readed != _ITUNES_GENRE_NUM_SZ) {
1578 debug_error(DEBUG, "failed to read. ret = %d, in = %d", readed, _ITUNES_GENRE_NUM_SZ);
1580 genre_index = mmfile_io_be_uint16(*(int *)read_buf);
1581 debug_msg(RELEASE, "genre index=[%d] ", genre_index);
1583 if (genre_index > 0 && genre_index < GENRE_COUNT) {
1584 if (!formatContext->genre) {
1585 memset(read_buf, 0x00, _ITUNES_READ_BUF_SZ);
1586 snprintf((char *)read_buf, sizeof(read_buf), "%s", MpegAudio_Genre[genre_index - 1]);
1587 debug_msg(RELEASE, "genre string=[%s] ", read_buf);
1588 formatContext->genre = g_strdup((const char *)read_buf);
1596 1) below spec is in "iTunes Package Asset Specification 4.3" published by apple.
1597 Music Cover Art Image Profile
1598 - TIFF with ".tif" extension (32-bit uncompressed), JPEG with ".jpg" extension (quality unconstrained), or PNG with ".png" extension
1599 - RGB (screen standard)
1600 - Minimum size of 600 x 600 pixels
1601 - Images must be at least 72 dpi
1603 2)I found below info from google.
1604 cover image flag : JPEG (13, 0xd), PNG (14, 0xe)
1606 3)So, FIXME when cover image format is tif!
1610 mmfile_seek(fp, cover_offset, SEEK_SET);
1612 formatContext->artwork = g_malloc0(cover_sz);
1613 formatContext->artworkSize = cover_sz;
1614 if (cover_type == _ITUNES_COVER_TYPE_JPEG) {
1615 formatContext->artworkMime = g_strdup("image/jpeg");
1616 } else if (cover_type == _ITUNES_COVER_TYPE_PNG) {
1617 formatContext->artworkMime = g_strdup("image/png");
1618 /*} else if (cover_type == _ITUNES_COVER_TYPE_TIF) {
1619 formatContext->artworkMime = g_strdup("image/tif");*/
1621 debug_warning(DEBUG, "Not proper cover image type, but set to jpeg. cover_type[%d]", cover_type);
1622 formatContext->artworkMime = g_strdup("image/jpeg");
1625 if (formatContext->artwork) {
1626 readed = mmfile_read(fp, formatContext->artwork, cover_sz);
1627 if (readed != cover_sz) {
1628 debug_error(DEBUG, "failed to read. ret = %d, in = %d", readed, cover_sz);
1629 mmfile_free(formatContext->artwork);
1630 formatContext->artworkSize = 0;
1631 mmfile_free(formatContext->artworkMime);
1637 /*reset seek position*/
1638 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1640 return MMFILE_UTIL_SUCCESS;
1648 /* skip hdlr box name */
1649 mmfile_seek(fp, hdlrBoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_HANDLER_BOX_LEN, SEEK_CUR);
1652 readed = mmfile_read(fp, (unsigned char *)&id3v2BoxHeader, MMFILE_MP4_BASIC_BOX_HEADER_LEN);
1653 if (readed != MMFILE_MP4_BASIC_BOX_HEADER_LEN) {
1654 debug_error(DEBUG, "read id3v2 box header");
1658 id3v2BoxHeader.size = mmfile_io_be_uint32(id3v2BoxHeader.size);
1659 id3v2BoxHeader.type = mmfile_io_le_uint32(id3v2BoxHeader.type);
1661 if (id3v2BoxHeader.type != FOURCC('I', 'D', '3', '2')) {
1662 debug_warning(DEBUG, "meta type is not id3v2");
1666 readed = mmfile_read(fp, (unsigned char *)&id3v2Box, MMFILE_3GP_ID3V2_BOX_LEN);
1667 if (readed != MMFILE_3GP_ID3V2_BOX_LEN) {
1668 debug_error(DEBUG, "read id3v2 box");
1672 id3v2Len = id3v2BoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_ID3V2_BOX_LEN;
1674 id3v2Box.id3v2Data = g_malloc0(id3v2Len);
1676 readed = mmfile_read(fp, (unsigned char *)id3v2Box.id3v2Data, id3v2Len);
1677 if (readed != id3v2Len) {
1678 debug_error(DEBUG, "read id3tag data error");
1683 if (!IS_ID3V2_TAG(id3v2Box.id3v2Data)) {
1684 debug_error(DEBUG, "it is not id3tag");
1688 if (id3v2Box.id3v2Data[3] == 0xFF || id3v2Box.id3v2Data[4] == 0xFF ||
1689 id3v2Box.id3v2Data[6] >= 0x80 || id3v2Box.id3v2Data[7] >= 0x80 ||
1690 id3v2Box.id3v2Data[8] >= 0x80 || id3v2Box.id3v2Data[9] >= 0x80) {
1691 debug_error(DEBUG, "it is not valid id3tag");
1695 tagVersion = id3v2Box.id3v2Data[3];
1696 if (tagVersion > 4) {
1697 debug_error(DEBUG, "tag version is too high");
1701 tagInfo.tagV2Info.tagLen = MP3_TAGv2_HEADER_LEN;
1702 tagInfo.tagV2Info.tagLen += (((id3v2Box.id3v2Data[6] & 0x7F) << 21) | ((id3v2Box.id3v2Data[7] & 0x7F) << 14) | ((id3v2Box.id3v2Data[8] & 0x7F) << 7) | ((id3v2Box.id3v2Data[9] & 0x7F)));
1703 tagInfo.tagV2Info.tagVersion = tagVersion;
1704 tagInfo.fileLen = id3v2Len;
1706 /* set id3v2 data to formatContext */
1707 switch (tagVersion) {
1709 versionCheck = mm_file_id3tag_parse_v222(&tagInfo, id3v2Box.id3v2Data, formatContext->extract_artwork);
1712 versionCheck = mm_file_id3tag_parse_v223(&tagInfo, id3v2Box.id3v2Data, formatContext->extract_artwork);
1715 versionCheck = mm_file_id3tag_parse_v224(&tagInfo, id3v2Box.id3v2Data, formatContext->extract_artwork);
1719 debug_error(DEBUG, "tag vesion is not support");
1720 versionCheck = false;
1724 if (versionCheck == false) {
1725 debug_error(DEBUG, "tag parsing is fail");
1729 formatContext->title = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_TITLE].value);
1730 formatContext->artist = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_ARTIST].value);
1731 formatContext->copyright = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_COPYRIGHT].value);
1732 formatContext->comment = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_COMMENT].value);
1733 formatContext->album = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_ALBUM].value);
1734 formatContext->album_artist = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_ALBUM_ARTIST].value);
1735 formatContext->year = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_YEAR].value);
1736 formatContext->genre = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_GENRE].value);
1737 formatContext->tagTrackNum = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_TRACKNUM].value);
1738 formatContext->composer = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_COMPOSER].value);
1739 formatContext->classification = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_CONTENT_GROUP].value);
1740 formatContext->conductor = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_CONDUCTOR].value);
1742 if (tagInfo.imageInfo.pImageBuf && tagInfo.imageInfo.imageLen > 0) {
1743 formatContext->artworkSize = tagInfo.imageInfo.imageLen;
1744 formatContext->artwork = g_memdup2(tagInfo.imageInfo.pImageBuf, tagInfo.imageInfo.imageLen);
1747 mm_file_free_AvFileContentInfo(&tagInfo);
1748 mmfile_free(id3v2Box.id3v2Data);
1750 /*reset seek position*/
1751 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1753 return MMFILE_UTIL_SUCCESS;
1759 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1760 mmfile_free(id3v2Box.id3v2Data);
1761 mm_file_free_AvFileContentInfo(&tagInfo);
1763 return MMFILE_UTIL_FAIL;
1766 int mm_file_get_int_value_from_xml_string(const char* xml_str, const char* param_name, int* value)
1768 char *value_start, *value_end, *endptr;
1769 const short value_length_max = 12;
1770 char init_view_ret[value_length_max];
1771 int value_length = 0;
1773 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1774 debug_error(DEBUG, "error: incorrect or non-existing parameter");
1775 return MMFILE_UTIL_FAIL;
1778 value_start = strstr(xml_str, param_name) + strlen(param_name);
1779 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
1782 value_end = strchr(value_start, '<');
1784 debug_error(DEBUG, "error: incorrect XML");
1785 return MMFILE_UTIL_FAIL;
1788 value_length = value_end - value_start;
1789 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1793 if (value_start[i] == '+' || value_start[i] == '-')
1795 while (i < value_length) {
1796 if (value_start[i] < '0' || value_start[i] > '9') {
1797 debug_error(DEBUG, "error: incorrect value, integer was expected");
1798 return MMFILE_UTIL_FAIL;
1803 if (value_length >= value_length_max || value_length < 1) {
1804 debug_error(DEBUG, "error: empty XML value or incorrect range");
1805 return MMFILE_UTIL_FAIL;
1808 memset(init_view_ret, 0x00, value_length_max);
1809 if (g_strlcpy(init_view_ret, value_start, value_length_max) >= value_length_max) {
1810 debug_error(DEBUG, "error: truncation occurred");
1811 return MMFILE_UTIL_FAIL;
1815 *value = strtol(init_view_ret, &endptr, 10);
1816 if (endptr == init_view_ret) {
1817 debug_error(DEBUG, "error: no digits were found");
1818 return MMFILE_UTIL_FAIL;
1821 return MMFILE_UTIL_SUCCESS;
1824 int mm_file_get_string_value_from_xml_string(const char* xml_str, const char* param_name, char** value)
1826 char *value_start, *value_end;
1827 const short value_length_max = 256;
1828 int value_length = 0;
1830 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1831 debug_error(DEBUG, "error: incorrect or non-existing parameter");
1832 return MMFILE_UTIL_FAIL;
1835 value_start = strstr(xml_str, param_name) + strlen(param_name);
1836 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
1839 value_end = strchr(value_start, '<');
1841 debug_error(DEBUG, "error: incorrect XML");
1842 return MMFILE_UTIL_FAIL;
1845 value_length = value_end - value_start;
1846 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1849 if (value_length >= value_length_max || value_length < 1) {
1850 debug_error(DEBUG, "error: empty XML value or incorrect range");
1851 return MMFILE_UTIL_FAIL;
1854 *value = (char*)calloc(value_length, sizeof(char));
1855 if (*value == NULL) {
1856 debug_error(DEBUG, "error: calloc failed");
1857 return MMFILE_UTIL_FAIL;
1859 strncpy(*value, value_start, value_length);
1861 return MMFILE_UTIL_SUCCESS;
1864 int mm_file_get_bool_value_from_xml_string(const char* xml_str, const char* param_name, bool* value)
1866 char *value_start = NULL;
1867 char *value_end = NULL;
1868 int value_length = 0;
1870 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1871 debug_error(DEBUG, "error: incorrect or non-existing parameter");
1872 return MMFILE_UTIL_FAIL;
1875 value_start = strstr(xml_str, param_name) + strlen(param_name);
1876 while ((value_start != NULL) && ((value_start[0] == ' ') || (value_start[0] == '\t')))
1879 value_end = strchr(value_start, '<');
1880 if (value_end == NULL) {
1881 debug_error(DEBUG, "error: incorrect XML.");
1882 return MMFILE_UTIL_FAIL;
1885 value_length = value_end - value_start;
1886 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1889 if (value_length < 1) {
1890 debug_error(DEBUG, "error: empty XML value or incorrect range");
1891 return MMFILE_UTIL_FAIL;
1894 *value = strstr(value_start, "true") ? true : false;
1896 return MMFILE_UTIL_SUCCESS;
1899 int ParseSpatialVideoMetadataFromXMLString(const char *xmlStr, MMFileFormatContext *formatContext)
1901 const char is_spherical_str[] = "<GSpherical:Spherical>";
1902 const char is_stitched_str[] = "<GSpherical:Stitched>";
1903 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
1904 const char projection_type_str[] = "<GSpherical:ProjectionType>";
1905 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
1906 const char source_count_str[] = "<GSpherical:SourceCount>";
1907 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
1908 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
1909 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
1910 const char timestamp_str[] = "<GSpherical:Timestamp>";
1911 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
1912 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
1913 const char cropped_area_image_width_str[] = "<GSpherical:CroppedAreaImageWidthPixels>";
1914 const char cropped_area_image_height_str[] = "<GSpherical:CroppedAreaImageHeightPixels>";
1915 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
1916 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
1918 mm_file_get_bool_value_from_xml_string(xmlStr, is_spherical_str, (bool*)&formatContext->isSpherical);
1919 mm_file_get_bool_value_from_xml_string(xmlStr, is_stitched_str, (bool*)&formatContext->isStitched);
1921 debug_msg(RELEASE, "isSpherical = %d", formatContext->isSpherical);
1922 debug_msg(RELEASE, "isStitched = %d", formatContext->isStitched);
1924 if (formatContext->isSpherical && formatContext->isStitched) {
1925 mm_file_get_string_value_from_xml_string(xmlStr, stitching_software_str, &formatContext->stitchingSoftware);
1926 mm_file_get_string_value_from_xml_string(xmlStr, projection_type_str, &formatContext->projectionType);
1927 mm_file_get_string_value_from_xml_string(xmlStr, stereo_mode_str, &formatContext->stereoMode);
1928 mm_file_get_int_value_from_xml_string(xmlStr, source_count_str, &formatContext->sourceCount);
1929 mm_file_get_int_value_from_xml_string(xmlStr, init_view_heading_str, &formatContext->initViewHeading);
1930 mm_file_get_int_value_from_xml_string(xmlStr, init_view_pitch_str, &formatContext->initViewPitch);
1931 mm_file_get_int_value_from_xml_string(xmlStr, init_view_roll_str, &formatContext->initViewRoll);
1932 mm_file_get_int_value_from_xml_string(xmlStr, timestamp_str, &formatContext->timestamp);
1933 mm_file_get_int_value_from_xml_string(xmlStr, full_pano_width_str, &formatContext->fullPanoWidth);
1934 mm_file_get_int_value_from_xml_string(xmlStr, full_pano_height_str, &formatContext->fullPanoHeight);
1935 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_width_str, &formatContext->croppedAreaImageWidth);
1936 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_height_str, &formatContext->croppedAreaImageHeight);
1937 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_left_str, &formatContext->croppedAreaLeft);
1938 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_top_str, &formatContext->croppedAreaTop);
1940 debug_msg(RELEASE, "stitchingSoftware = %s", formatContext->stitchingSoftware);
1941 debug_msg(RELEASE, "projectionType = %s", formatContext->projectionType);
1942 debug_msg(RELEASE, "stereoMode = %s", formatContext->stereoMode);
1943 debug_msg(RELEASE, "sourceCount %d", formatContext->sourceCount);
1944 debug_msg(RELEASE, "initViewHeading = %d", formatContext->initViewHeading);
1945 debug_msg(RELEASE, "initViewPitch = %d", formatContext->initViewPitch);
1946 debug_msg(RELEASE, "initViewRoll = %d", formatContext->initViewRoll);
1947 debug_msg(RELEASE, "timestamp = %d", formatContext->timestamp);
1948 debug_msg(RELEASE, "fullPanoWidthPixels = %d", formatContext->fullPanoWidth);
1949 debug_msg(RELEASE, "fullPanoHeightPixels = %d", formatContext->fullPanoHeight);
1950 debug_msg(RELEASE, "croppedAreaImageWidth = %d", formatContext->croppedAreaImageWidth);
1951 debug_msg(RELEASE, "croppedAreaImageHeight = %d", formatContext->croppedAreaImageHeight);
1952 debug_msg(RELEASE, "croppedAreaLeft = %d", formatContext->croppedAreaLeft);
1953 debug_msg(RELEASE, "croppedAreaTop = %d", formatContext->croppedAreaTop);
1956 return MMFILE_UTIL_SUCCESS;
1959 #define BIG_CONTENT_BOX_SIZE_LEN 8
1961 int MMFileUtilGetMetaDataFromMKV(MMFileFormatContext *formatContext)
1963 MMFileIOHandle *fp = NULL;
1964 long long probe_size = 10000;
1965 unsigned char *buffer = NULL;
1968 long long file_size = 0;
1970 MMFILE_WEBM_PROJ_V2_BOX v2box = { 0, };
1971 MMFILE_WEBM_EQUI_PROJ_V2_BOX equi = { 0, };
1972 MMFILE_WEBM_CBMP_PROJ_V2_BOX cbmp = { 0, };
1973 MMFILE_WEBM_POSE_ELEMENT_V2_BOX pose = { 0, };
1975 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
1976 if (ret == MMFILE_UTIL_FAIL) {
1977 debug_error(DEBUG, "error: mmfile_open");
1981 file_size = mmfile_seek(fp, 0, SEEK_END);
1982 if (file_size == MMFILE_UTIL_FAIL) {
1983 debug_error(DEBUG, "mmfile operation failed");
1987 probe_size = (file_size > probe_size) ? probe_size : file_size;
1988 buffer = (unsigned char *)malloc(probe_size * sizeof(unsigned char));
1990 debug_error(DEBUG, "malloc failed");
1994 ret = mmfile_seek(fp, 0, SEEK_SET);
1995 if (ret == MMFILE_UTIL_FAIL) {
1996 debug_error(DEBUG, "mmfile operation failed");
2000 ret = mmfile_read(fp, buffer, probe_size * sizeof(unsigned char));
2001 if (ret == MMFILE_UTIL_FAIL) {
2002 debug_error(DEBUG, "mmfile operation failed");
2006 /* FIXME (m.alieksieie): It's better to use some EBML parser here*/
2007 for (i = 0; i + 3 < probe_size; ++i) {
2008 if (*(unsigned int *)(buffer + i) == FOURCC('e', 'q', 'u', 'i') ||
2009 *(unsigned int *)(buffer + i) == FOURCC('c', 'b', 'm', 'p')) {
2010 debug_msg(DEBUG, "projection data found at offset %lld bytes", i);
2015 if (i + 3 == probe_size) {
2016 debug_msg(DEBUG, "projection info wasn't found");
2017 ret = MMFILE_UTIL_SUCCESS;
2021 if ((i - (long long)sizeof(MMFILE_WEBM_PROJ_V2_BOX)) < 0) {
2022 debug_error(DEBUG, "error: invalid supposed projection info location");
2023 ret = MMFILE_UTIL_FAIL;
2027 ret = mmfile_seek(fp, i - sizeof(MMFILE_WEBM_PROJ_V2_BOX), SEEK_SET);
2028 if (ret == MMFILE_UTIL_FAIL) {
2029 debug_error(DEBUG, "error: failed to seek to the supposed projection info location");
2033 ret = mmfile_read(fp, (unsigned char *)&v2box, sizeof(MMFILE_WEBM_PROJ_V2_BOX));
2034 if (ret == MMFILE_UTIL_FAIL) {
2035 debug_error(DEBUG, "mmfile operation failed");
2039 if (v2box.proj_type_box_value == PROJECTION_TYPE_EQUI) {
2040 debug_msg(DEBUG, "Equirectangular projection is used");
2042 ret = mmfile_read(fp, (unsigned char *)&equi, sizeof(MMFILE_WEBM_EQUI_PROJ_V2_BOX));
2043 if (ret == MMFILE_UTIL_FAIL) {
2044 debug_error(DEBUG, "error: failed to read equirectangular element");
2047 if (strncmp((char *)equi.proj_priv_box_name, "equi", 4) == 0) {
2048 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_top);
2049 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_bottom);
2050 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_left);
2051 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_right);
2053 debug_error(DEBUG, "error: failed to read equirectangular element");
2054 ret = MMFILE_UTIL_SUCCESS;
2058 if (v2box.proj_type_box_value == PROJECTION_TYPE_CBMP) {
2059 debug_msg(DEBUG, "Cubemap projection is used");
2061 ret = mmfile_read(fp, (unsigned char *)&cbmp, sizeof(MMFILE_WEBM_CBMP_PROJ_V2_BOX));
2062 if (ret == MMFILE_UTIL_FAIL) {
2063 debug_error(DEBUG, "error: failed to read cubemap element");
2066 if (strncmp((char *)cbmp.proj_priv_box_name, "cbmp", 4) == 0) {
2067 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_layout);
2068 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_padding);
2070 debug_error(DEBUG, "error: failed to read cubemap element");
2071 ret = MMFILE_UTIL_FAIL;
2076 ret = mmfile_read(fp, (unsigned char *)&pose, sizeof(MMFILE_WEBM_POSE_ELEMENT_V2_BOX));
2077 if (ret == MMFILE_UTIL_FAIL) {
2078 debug_error(DEBUG, "error: failed to read pose info");
2082 if (pose.pose_yaw_element_id == POSE_YAW_ELEMENT_ID) {
2083 formatContext->poseYawV2 = (uint)mmfile_io_be_float32(pose.pose_yaw_element_value);
2085 debug_error(DEBUG, "error: failed to pose yaw element");
2086 ret = MMFILE_UTIL_FAIL;
2089 if (pose.pose_pitch_element_id == POSE_PITCH_ELEMENT_ID) {
2090 formatContext->posePitchV2 = (uint)mmfile_io_be_float32(pose.pose_pitch_element_value);
2092 debug_error(DEBUG, "error: failed to pose pitch element");
2093 ret = MMFILE_UTIL_FAIL;
2096 if (pose.pose_roll_element_id == POSE_ROLL_ELEMENT_ID) {
2097 formatContext->poseRollV2 = (uint)mmfile_io_be_float32(pose.pose_roll_element_value);
2099 debug_error(DEBUG, "error: failed to pose roll element");
2100 ret = MMFILE_UTIL_FAIL;
2113 int MMFileUtilGetMetaDataFromMP4(MMFileFormatContext *formatContext)
2115 MMFileIOHandle *fp = NULL;
2118 unsigned long long chunk_size = 0;
2119 long long moov_end = 0;
2120 MMFILE_MP4_BASIC_BOX_HEADER basic_header = {0, };
2121 int junk_counter = 0;
2123 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
2124 if (ret == MMFILE_UTIL_FAIL) {
2125 debug_error(DEBUG, "error: mmfile_open");
2129 basic_header.start_offset = mmfile_tell(fp);
2130 if (basic_header.start_offset < 0) {
2131 debug_error(DEBUG, "error: mmfile_tell");
2135 if (g_junk_counter_limit == 0)
2136 g_junk_counter_limit = mmfile_get_int_from_ini(MMFILE_INI_JUNKCNTLIMIT, MMFILE_DEFAULT_JUNKCNTLIMIT);
2138 while ((ret != MMFILE_UTIL_FAIL) && ((readed = mmfile_read(fp, (unsigned char *)&basic_header, MMFILE_MP4_BASIC_BOX_HEADER_LEN)) == MMFILE_MP4_BASIC_BOX_HEADER_LEN)) {
2139 basic_header.size = mmfile_io_be_uint32(basic_header.size);
2140 basic_header.type = mmfile_io_le_uint32(basic_header.type);
2142 if (basic_header.size == 0) {
2143 debug_warning(DEBUG, "header is invalid.");
2144 basic_header.size = readed;
2145 basic_header.type = 0;
2146 chunk_size = basic_header.size;
2148 if ((moov_end != 0) && (moov_end < basic_header.start_offset)) {
2149 debug_msg(DEBUG, "found junk data but moov data already was extracted, so junk counter will be increase: %d", junk_counter);
2152 /* stop the loop for junk case. */
2153 if ((g_junk_counter_limit > 0) && (junk_counter > g_junk_counter_limit)) {
2154 debug_msg(DEBUG, "stop the loop by junk-data checker");
2155 ret = MMFILE_UTIL_FAIL;
2159 } else if (basic_header.size == 1) {
2161 unsigned char temp[BIG_CONTENT_BOX_SIZE_LEN] = {0, };
2162 unsigned long long size = 0;
2164 mmfile_read(fp, (unsigned char *)&temp, BIG_CONTENT_BOX_SIZE_LEN);
2166 for (i = 0; i < BIG_CONTENT_BOX_SIZE_LEN; i++)
2167 size |= (unsigned long long)temp[i] << (BIG_CONTENT_BOX_SIZE_LEN - 1 - i) * BIG_CONTENT_BOX_SIZE_LEN;
2171 chunk_size = basic_header.size;
2175 switch (basic_header.type) {
2176 case FOURCC('m', 'o', 'o', 'v'): {
2177 debug_msg(RELEASE, "MPEG4: [moov] SIZE: [%lld]Byte", chunk_size);
2178 moov_end = basic_header.start_offset + chunk_size;
2181 case FOURCC('u', 'd', 't', 'a'): {
2182 debug_msg(RELEASE, "MPEG4: [udat] SIZE: [%lld]Byte", chunk_size);
2185 /*/////////////////////////////////////////////////////////////// */
2186 /* Extracting Tag Data // */
2187 /*/////////////////////////////////////////////////////////////// */
2188 case FOURCC('t', 'i', 't', 'l'): {
2189 debug_msg(RELEASE, "MPEG4: [titl] SIZE: [%lld]Byte", chunk_size);
2190 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_TITLE);
2193 case FOURCC('d', 's', 'c', 'p'): {
2194 debug_msg(RELEASE, "MPEG4: [dscp] SIZE: [%lld]Byte", chunk_size);
2195 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_CAPTION);
2198 case FOURCC('c', 'p', 'r', 't'): {
2199 debug_msg(RELEASE, "MPEG4: [cprt] SIZE: [%lld]Byte", chunk_size);
2200 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_COPYRIGHT);
2203 case FOURCC('p', 'e', 'r', 'f'): {
2204 debug_msg(RELEASE, "MPEG4: [perf] SIZE: [%lld]Byte", chunk_size);
2205 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_PERFORMER);
2208 case FOURCC('a', 'u', 't', 'h'): {
2209 debug_msg(RELEASE, "MPEG4: [auth] SIZE: [%lld]Byte", chunk_size);
2210 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_AUTHOR);
2213 case FOURCC('g', 'n', 'r', 'e'): {
2214 debug_msg(RELEASE, "MPEG4: [gnre] SIZE: [%lld]Byte", chunk_size);
2215 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_GENRE);
2218 case FOURCC('a', 'l', 'b', 'm'): {
2219 debug_msg(RELEASE, "MPEG4: [albm] SIZE: [%lld]Byte", chunk_size);
2220 GetAlbumFromAlbumTagBox(formatContext, fp, &basic_header);
2223 case FOURCC('y', 'r', 'r', 'c'): {
2224 debug_msg(RELEASE, "MPEG4: [yrrc] SIZE: [%lld]Byte", chunk_size);
2225 GetYearFromYearTagBox(formatContext, fp, &basic_header);
2228 case FOURCC('r', 't', 'n', 'g'): {
2229 debug_msg(RELEASE, "MPEG4: [rtng] SIZE: [%lld]Byte", chunk_size);
2230 GetRatingFromRatingTagBox(formatContext, fp, &basic_header); /* not use */
2233 case FOURCC('c', 'l', 's', 'f'): {
2234 debug_msg(RELEASE, "MPEG4: [clsf] SIZE: [%lld]Byte", chunk_size);
2235 GetClassficationFromClsfTagBox(formatContext, fp, &basic_header);
2238 case FOURCC('k', 'y', 'w', 'd'): {
2239 debug_msg(RELEASE, "MPEG4: [kywd] SIZE: [%lld]Byte", chunk_size);
2240 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2243 case FOURCC('l', 'o', 'c', 'i'): {
2244 debug_msg(RELEASE, "MPEG4: [loci] SIZE: [%lld]Byte", chunk_size);
2245 GetLocationFromLociTagBox(formatContext, fp, &basic_header);
2248 /* Check smta in user data field (moov) to be compatible with android */
2249 case FOURCC('s', 'm', 't', 'a'): {
2250 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte", chunk_size);
2251 GetSAUTInfoFromSMTATagBox(formatContext, fp, &basic_header);
2254 /* Check cdis in user data field (moov) to be compatible with android */
2255 case FOURCC('c', 'd', 'i', 's'): {
2256 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte", chunk_size);
2257 GetValueFromCDISTagBox(formatContext, fp, &basic_header);
2260 case FOURCC('m', '3', '6', '0'): {
2261 debug_msg(RELEASE, "MPEG4: [m360] SIZE: [%lld]Byte", chunk_size);
2262 unsigned long mode[2] = {0, };
2265 mmfile_read(fp, (unsigned char *)mode, sizeof(mode));
2267 temp = mmfile_io_be_uint32(mode[1]);
2269 formatContext->isSpherical = false;
2270 formatContext->isStitched = 0;
2272 formatContext->isSpherical = true;
2273 formatContext->isStitched = temp * MMFILE_360_NON_STITCHED;
2278 /*/////////////////////////////////////////////////////////////// */
2279 /* Extracting ID3 Tag Data // */
2280 /*/////////////////////////////////////////////////////////////// */
2281 case FOURCC('m', 'e', 't', 'a'): {
2282 debug_msg(RELEASE, "MPEG4: [meta] SIZE: [%lld]Byte", chunk_size);
2283 GetTagFromMetaBox(formatContext, fp, &basic_header);
2287 case FOURCC('t', 'r', 'a', 'k'): {
2288 debug_msg(RELEASE, "MPEG4: [trak] SIZE: [%lld]Byte", chunk_size);
2291 case FOURCC('u', 'u', 'i', 'd'): {
2292 unsigned long uuid[4] = {0, };
2294 debug_msg(RELEASE, "MPEG4: [uuid] SIZE: [%lld]Byte", chunk_size);
2296 mmfile_read(fp, (unsigned char *)uuid, sizeof(uuid));
2298 if (mmfile_io_be_uint32(uuid[0]) == 0xffcc8263
2299 && mmfile_io_be_uint32(uuid[1]) == 0xf8554a93
2300 && mmfile_io_be_uint32(uuid[2]) == 0x8814587a
2301 && mmfile_io_be_uint32(uuid[3]) == 0x02521fdd) {
2303 str = (char *)malloc(basic_header.size);
2306 memset(str, 0, basic_header.size);
2307 mmfile_read(fp, (unsigned char *)str, basic_header.size);
2309 /* The block is superseded */
2310 if (strstr(str, "<GSpherical:Spherical>true</GSpherical:Spherical>"))
2311 formatContext->is_360 = 1;
2313 formatContext->is_360 = 0;
2314 /* Image can be stitched even if it is not spherical */
2315 if (formatContext->is_360 == 1) {
2316 if (strstr(str, "<GSpherical:Stitched>true</GSpherical:Stitched>"))
2317 formatContext->stitched = MMFILE_360_STITCHED;
2319 formatContext->stitched = MMFILE_360_NON_STITCHED;
2321 /* Image can be stitched or non-stitched. Usage of some 3rd value is superfluous */
2322 formatContext->stitched = MMFILE_360_NONE;
2326 debug_msg(RELEASE, "Extracting tags from UUID XML string %s", str);
2328 ParseSpatialVideoMetadataFromXMLString(str, formatContext);
2331 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2335 case FOURCC('m', 'd', 'i', 'a'): {
2336 debug_msg(RELEASE, "MPEG4: [mdia] SIZE: [%lld]Byte", chunk_size);
2339 case FOURCC('m', 'i', 'n', 'f'): {
2340 debug_msg(RELEASE, "MPEG4: [minf] SIZE: [%lld]Byte", chunk_size);
2343 case FOURCC('s', 't', 'b', 'l'): {
2344 debug_msg(RELEASE, "MPEG4: [stbl] SIZE: [%lld]Byte", chunk_size);
2347 case FOURCC('s', 't', 's', 'd'): {
2348 debug_msg(RELEASE, "MPEG4: [stsd] SIZE: [%lld]Byte", chunk_size);
2351 case FOURCC('m', 'p', '4', 'a'): {
2352 debug_msg(RELEASE, "MPEG4: [mp4a] SIZE: [%lld]Byte", chunk_size);
2353 GetSA3DInfoFromMP4ATagBox(formatContext, fp, &basic_header);
2354 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2357 case FOURCC('a', 'v', 'c', '1'): {
2358 debug_msg(RELEASE, "MPEG4: [avc1] SIZE: [%lld]Byte (offset: %lld)", chunk_size, basic_header.start_offset);
2359 GetVideoV2MetadataFromAvc1TagBox(formatContext, fp, &basic_header);
2363 debug_msg(RELEASE, "4CC: Not Support [%c%c%c%c]. So skip it. Size [%lld Byte]",
2364 ((char *)&basic_header.type)[0], ((char *)&basic_header.type)[1],
2365 ((char *)&basic_header.type)[2], ((char *)&basic_header.type)[3], chunk_size);
2366 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2371 if (ret == MMFILE_UTIL_FAIL) {
2372 debug_error(DEBUG, "mmfile operation is error");
2377 long long new_pos = mmfile_tell(fp);
2379 if ((moov_end == 0) && (new_pos <= basic_header.start_offset)) {
2380 debug_error(DEBUG, "Wrong position");
2381 ret = MMFILE_UTIL_FAIL;
2384 basic_header.start_offset = new_pos;
2393 static bool __get_genre_num(const char *buffer, int *si)
2395 int len = strlen(buffer);
2396 char *tmp_genre = NULL;
2398 /* Genre format: (###), ### */
2399 if (len > 6 || len == 0)
2404 if (!g_ascii_isdigit(buffer[0]))
2407 if (!g_ascii_isdigit(buffer[1]))
2411 /* Remove bind () if exist */
2412 tmp_genre = g_strdup(buffer);
2413 if (g_str_has_prefix(tmp_genre, "(") && g_str_has_suffix(tmp_genre, ")"))
2416 *si = atoi(tmp_genre);
2422 static bool make_characterset_array(char ***charset_array)
2424 char *locale = MMFileUtilGetLocale();
2426 *charset_array = calloc(AV_ID3V2_MAX, sizeof(char *));
2428 if (*charset_array == NULL) {
2429 debug_error(DEBUG, "calloc failed ");
2435 if (locale != NULL) {
2436 (*charset_array)[AV_ID3V2_ISO_8859] = strdup(locale);
2438 debug_error(DEBUG, "get locale failed");
2439 (*charset_array)[AV_ID3V2_ISO_8859] = NULL;
2442 (*charset_array)[AV_ID3V2_UTF16] = strdup("UCS2");
2443 (*charset_array)[AV_ID3V2_UTF16_BE] = strdup("UTF16-BE");
2444 (*charset_array)[AV_ID3V2_UTF8] = strdup(MMFILE_CODESET_UTF8);
2449 static bool release_characterset_array(char **charset_array)
2456 for (i = 0; i < AV_ID3V2_MAX; i++) {
2457 if (charset_array[i] != NULL) {
2458 free(charset_array[i]);
2459 charset_array[i] = NULL;
2463 free(charset_array);
2464 charset_array = NULL;
2469 static void init_content_info(AvFileContentInfo *pInfo)
2473 for(i = 0; i < AV_ID3TAG_MAX; i++)
2474 pInfo->tagInfo[i].value = NULL;
2476 pInfo->imageInfo.bURLInfo = false;
2477 pInfo->imageInfo.pImageBuf = NULL;
2478 pInfo->imageInfo.imageLen = 0;
2481 static void __id3tag_skip_newline(unsigned char *pTagVal, int *nTagLen, int *offset)
2483 /* skip newline in text encoding of ID3 tag frame */
2484 while ((NEWLINE_OF_UTF16(pTagVal + *offset) || NEWLINE_OF_UTF16_R(pTagVal + *offset)) && *nTagLen > 4) {
2490 static int __id3tag_get_text_encoding_v222(unsigned char *pTagVal, int offset)
2492 if (pTagVal[offset - 1] == 0x00)
2493 return AV_ID3V2_ISO_8859;
2495 return AV_ID3V2_UTF16;
2498 static int __id3tag_get_text_encoding_v223(unsigned char *pTagVal, int *npTagLen, int nTextEnc, int *offset)
2500 if ((IS_ENCODEDBY_UTF16(pTagVal + *offset) || IS_ENCODEDBY_UTF16_R(pTagVal + *offset)) && *npTagLen > 2) {
2501 __id3tag_skip_newline(pTagVal, npTagLen, offset);
2503 if (IS_ENCODEDBY_UTF16(pTagVal + *offset) && (*npTagLen > 2)) {
2506 return AV_ID3V2_UTF16;
2507 } else if (IS_ENCODEDBY_UTF16_R(pTagVal + *offset) && (*npTagLen > 2)) {
2510 return AV_ID3V2_UTF16_BE;
2511 } else if (IS_ENCODEDBY_UTF16(pTagVal + *offset + 1) && (*npTagLen > 3)) {
2514 return AV_ID3V2_UTF16;
2515 } else if (IS_ENCODEDBY_UTF16_R(pTagVal + *offset + 1) && (*npTagLen > 3)) {
2518 return AV_ID3V2_UTF16_BE;
2520 debug_msg(RELEASE, "id3tag never get here!!");
2521 return nTextEnc; /* default bypass */
2524 while ((pTagVal[*offset] < 0x20) && (*offset < *npTagLen)) { /* text string encoded by ISO-8859-1 */
2528 return AV_ID3V2_ISO_8859;
2532 static int __id3tag_get_text_encoding_v224(unsigned char *pTagVal, int *npTagLen, int nTextEnc, int *offset)
2534 if (nTextEnc == AV_ID3V2_UTF16 || nTextEnc == AV_ID3V2_UTF16_BE) {
2535 __id3tag_skip_newline(pTagVal, npTagLen, offset);
2537 if ((IS_ENCODEDBY_UTF16(pTagVal + *offset) || IS_ENCODEDBY_UTF16_R(pTagVal + *offset)) && *npTagLen > 2) {
2540 return AV_ID3V2_UTF16;
2542 debug_msg(RELEASE, "id3tag never get here!!");
2543 return nTextEnc; /* default bypass */
2545 } else if (nTextEnc == AV_ID3V2_UTF8) {
2546 while (pTagVal[*offset] < 0x20 && (*offset < *npTagLen)) { /* text string encoded by UTF-8 */
2550 return AV_ID3V2_UTF8;
2552 while (pTagVal[*offset] < 0x20 && (*offset < *npTagLen)) { /* text string encoded by ISO-8859-1 */
2556 return AV_ID3V2_ISO_8859;
2560 static void __id3tag_parse_SYLT(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int textEncodingType, int offset)
2564 int copy_start_pos = offset;
2565 AvSynclyricsInfo *synclyrics_info = NULL;
2566 GList *synclyrics_info_list = NULL;
2568 if (nTagLen < MMFILE_SYNC_LYRIC_INFO_MIN_LEN) {
2569 debug_msg(RELEASE, "failed to get Synchronised lyrics Info realCpyFramNum(%d)", nTagLen);
2573 if ((textEncodingType == AV_ID3V2_UTF16) || (textEncodingType == AV_ID3V2_UTF16_BE)) {
2574 debug_warning(DEBUG, "[%d] not implemented", textEncodingType);
2578 for (idx = 0; idx < nTagLen; idx++) {
2579 if (pTagVal[offset + idx] == 0x00) {
2580 synclyrics_info = g_new0(AvSynclyricsInfo, 1);
2581 if (textEncodingType == AV_ID3V2_UTF8)
2582 synclyrics_info->lyric_info = g_memdup2(pTagVal + copy_start_pos, copy_len + 1);
2584 synclyrics_info->lyric_info = mmfile_convert_to_utf8((const char *)&pTagVal[copy_start_pos], copy_len, pCharSet);
2586 synclyrics_info->time_info = (unsigned long)pTagVal[offset + idx + 1] << 24 | (unsigned long)pTagVal[offset + idx + 2] << 16 | (unsigned long)pTagVal[offset + idx + 3] << 8 | (unsigned long)pTagVal[offset + idx + 4];
2588 copy_start_pos = offset + idx + 1;
2589 debug_msg(RELEASE, "[%lu][%s] idx[%d], copy_len[%d] copy_start_pos[%d]", synclyrics_info->time_info, synclyrics_info->lyric_info, idx, copy_len, copy_start_pos);
2591 synclyrics_info_list = g_list_append(synclyrics_info_list, synclyrics_info);
2596 pInfo->pSyncLyrics = synclyrics_info_list;
2599 static bool __id3tag_parse_PIC_format(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2601 unsigned int idx = 0;
2603 /* get the mime type of Attached PICture, it is text string */
2605 if (pTagVal[*offset] == '\0') {
2606 debug_msg(RELEASE, "The picture format of PIC is not included");
2610 /* init ext variable */
2611 memset(pInfo->imageInfo.imageExt, 0, sizeof(pInfo->imageInfo.imageExt));
2613 while ((idx < MP3_ID3_IMAGE_EXT_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2614 pInfo->imageInfo.imageExt[idx] = pTagVal[idx];
2623 static bool __id3tag_parse_APIC_mimetype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2625 unsigned int idx = 0;
2626 const char *MIME_PRFIX = "image/";
2628 /* get the mime type of Attached PICture, it is text string */
2630 if (pTagVal[*offset] == '\0') {
2631 pInfo->imageInfo.imgMimetypeLen = 0;
2632 debug_msg(RELEASE, "The MIME type of APIC is not included");
2636 /* init mimetype variable */
2637 memset(pInfo->imageInfo.imageMIMEType, 0, sizeof(pInfo->imageInfo.imageMIMEType));
2639 while ((idx < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2640 pInfo->imageInfo.imageMIMEType[idx] = pTagVal[idx];
2643 pInfo->imageInfo.imgMimetypeLen = idx;
2647 if (strncmp(pInfo->imageInfo.imageMIMEType, MIME_PRFIX, strlen(MIME_PRFIX)) != 0) {
2648 pInfo->imageInfo.imgMimetypeLen = 0;
2649 debug_error(DEBUG, "MIME type(%s) is not image", pInfo->imageInfo.imageMIMEType);
2653 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2654 debug_msg(RELEASE, "pTagVal[offset](%d) mimetype is not NULL terminated! realCpyFrameNum - offset(%d)",
2655 pTagVal[*offset], nTagLen - *offset);
2659 (*offset)++;/* end of MIME('\0', 1byte) */
2660 debug_msg(RELEASE, "after scaning Mime type offset(%d) value!", *offset);
2665 static void __id3tag_parse_APIC_pictype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2667 /* get the picture type of Attached PICture, it is 1byte(0xff) */
2669 if (pTagVal[*offset] < MP3_ID3V2_PICTURE_TYPE_MAX)
2670 pInfo->imageInfo.pictureType = pTagVal[*offset];
2672 debug_msg(RELEASE, "APIC image has invalid picture type(0x%x)", pTagVal[*offset]);
2674 (*offset)++;/* PictureType(1byte) */
2675 debug_msg(RELEASE, "after scaning PictureType(%d) offset(%d) value!", pInfo->imageInfo.pictureType, *offset);
2678 static void __id3tag_parse_APIC_desc(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int *offset)
2680 /* get the description of Attached PICture, it is text string */
2683 unsigned int tag_len = 0;
2684 char *tmp_desc = NULL;
2686 if (pTagVal[*offset] == 0x0) {
2687 debug_msg(RELEASE, "The description of APIC is not included!!!");
2692 if (pTagVal[*offset + idx] == '\0') {
2693 if (nTagLen < (*offset + idx)) {
2694 debug_error(DEBUG, "End of APIC Tag %d %d %d", nTagLen, *offset, idx);
2697 /* check end of image description */
2698 if ((pTagVal[*offset + idx + 1] == gTagJPEGHeader[0]) ||
2699 (pTagVal[*offset + idx + 1] == gTagPNGHeader[0])) {
2700 debug_msg(RELEASE, "length of description (%d)", idx);
2707 tag_len = idx + 1; /* length of description + '\0' */
2709 tmp_desc = g_memdup2(pTagVal + *offset, tag_len);
2711 /* convert description */
2712 pInfo->imageInfo.imageDescription = mmfile_convert_to_utf8(tmp_desc, tag_len, pCharSet);
2713 mmfile_free(tmp_desc);
2714 debug_msg(RELEASE, "new_desc %s", pInfo->imageInfo.imageDescription);
2717 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2718 debug_msg(RELEASE, "pTagVal[offset](%d) description is not NULL terminated! realCpyFrameNum - offset(%d)",
2719 pTagVal[*offset], nTagLen - *offset);
2722 (*offset)++; /* end of desceription(1byte) */
2725 static void __id3tag_parse_APIC_picture(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2727 /* get the picture of Attached PICture, it is binary data */
2729 /* some content has useless '\0' in front of picture data */
2730 while (pTagVal[*offset] == '\0') {
2734 debug_msg(RELEASE, "after scaning APIC description offset(%d) value!", *offset);
2736 if (nTagLen <= *offset) {
2737 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - offset(%d)", nTagLen, *offset);
2741 pInfo->imageInfo.imageLen = nTagLen - *offset;
2742 pInfo->imageInfo.pImageBuf = g_memdup2(pTagVal + *offset, pInfo->imageInfo.imageLen);
2744 /* if mimetype is "-->", image date has an URL */
2745 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
2746 pInfo->imageInfo.bURLInfo = true;
2749 static bool _mm_file_id3tag_parse_PIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2751 /* current position to read pTagVal */
2754 debug_fenter(RELEASE);
2756 if (!__id3tag_parse_PIC_format(pInfo, pTagVal, &offset)) {
2757 debug_msg(RELEASE, "PIC is not valid");
2761 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2762 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2763 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2765 debug_fleave(RELEASE);
2770 static bool _mm_file_id3tag_parse_APIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2774 debug_fenter(RELEASE);
2776 if (!__id3tag_parse_APIC_mimetype(pInfo, pTagVal, nTagLen, &offset)) {
2777 debug_msg(RELEASE, "APIC is not valid");
2781 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2782 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2783 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2785 debug_fleave(RELEASE);
2790 static char * __id3tag_get_v110(const char *buf, ssize_t len, const char *locale)
2792 char *tmp_str = NULL;
2794 mm_file_retv_if_fails(buf, NULL);
2795 mm_file_retv_if_fails(locale, NULL);
2797 tmp_str = mmfile_convert_to_utf8(buf, len, locale);
2799 /* ID3v1 tag need check length or space. */
2800 if (tmp_str && (strlen(tmp_str) == 0 || g_ascii_isspace(tmp_str[0])))
2801 mmfile_free(tmp_str);
2806 bool mm_file_id3tag_parse_v110(AvFileContentInfo *pInfo, unsigned char *buffer)
2808 const char *locale = MMFileUtilGetLocale();
2810 debug_msg(RELEASE, "ID3tag v110--------------------------------------------------------------");
2812 if (!pInfo->tagInfo[AV_ID3TAG_TITLE].value) {
2813 pInfo->tagInfo[AV_ID3TAG_TITLE].value = __id3tag_get_v110((const char *)&buffer[3], MP3_ID3_TITLE_LENGTH, locale);
2815 debug_msg(RELEASE, "pInfo->pTitle returned =(%s)", pInfo->tagInfo[AV_ID3TAG_TITLE].value);
2818 if (!pInfo->tagInfo[AV_ID3TAG_ARTIST].value) {
2819 pInfo->tagInfo[AV_ID3TAG_ARTIST].value = __id3tag_get_v110((const char *)&buffer[33], MP3_ID3_ARTIST_LENGTH, locale);
2821 debug_msg(RELEASE, "pInfo->pArtist returned =(%s)", pInfo->tagInfo[AV_ID3TAG_ARTIST].value);
2824 if (!pInfo->tagInfo[AV_ID3TAG_ALBUM].value) {
2825 pInfo->tagInfo[AV_ID3TAG_ALBUM].value = __id3tag_get_v110((const char *)&buffer[63], MP3_ID3_ALBUM_LENGTH, locale);
2827 debug_msg(RELEASE, "pInfo->pAlbum returned =(%s)", pInfo->tagInfo[AV_ID3TAG_ALBUM].value);
2830 if (!pInfo->tagInfo[AV_ID3TAG_YEAR].value) {
2831 pInfo->tagInfo[AV_ID3TAG_YEAR].value = __id3tag_get_v110((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, locale);
2833 debug_msg(RELEASE, "pInfo->pYear returned =(%s)", pInfo->tagInfo[AV_ID3TAG_YEAR].value);
2836 if (!pInfo->tagInfo[AV_ID3TAG_COMMENT].value) {
2837 pInfo->tagInfo[AV_ID3TAG_COMMENT].value = __id3tag_get_v110((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, locale);
2839 debug_msg(RELEASE, "pInfo->pComment returned =(%s)", pInfo->tagInfo[AV_ID3TAG_COMMENT].value);
2842 if (!pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value) {
2843 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value = g_malloc0(ID3TAG_V110_TRACK_NUM_DIGIT);
2844 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value[ID3TAG_V110_TRACK_NUM_DIGIT - 1] = 0;
2845 snprintf(pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value, ID3TAG_V110_TRACK_NUM_DIGIT, "%04d", (int)buffer[126]);
2847 debug_msg(RELEASE, "pInfo->pTrackNum returned =(%s)", pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value);
2850 /*ID3V2 genre is stored in pInfo->tagInfo[AV_ID3TAG_GENRE].value */
2851 /*pInfo->genre is used when ID3V2 genre is invalid, or empty. */
2852 pInfo->genre = buffer[127];
2853 debug_msg(RELEASE, "pInfo->genre returned genre number (%d)", pInfo->genre);
2858 static AvID3TagList __get_tag_info_v222(const unsigned char *tag)
2864 n += tag[i++] - 'A' + 1;
2867 n += tag[2]; //num, char mixted
2869 for (i = 0; i < ID3TAG_NUM_V22; i++) {
2870 if (n == tag_info_v22[i].int_name) {
2871 return tag_info_v22[i].tag;
2875 debug_msg(RELEASE, "(%s) This Frame ID currently not Supports!!", tag);
2877 return AV_ID3TAG_MAX;
2880 static AvID3TagList __get_tag_info_v223(const unsigned char *tag)
2886 n += tag[i++] - 'A' + 1;
2889 n += tag[3]; //num, char mixted
2891 for (i = 0; i < ID3TAG_NUM_V23; i++) {
2892 if (n == tag_info_v23[i].int_name) {
2893 return tag_info_v23[i].tag;
2897 debug_msg(RELEASE, "(%s) This Frame ID currently not Supports!!", tag);
2899 return AV_ID3TAG_MAX;
2903 bool mm_file_id3tag_parse_v222(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
2905 int curPos = MP3_TAGv2_HEADER_LEN;
2909 char **charset_array = NULL;
2910 AvID3TagList tag_id = AV_ID3TAG_MAX;
2912 if (pInfo->tagV2Info.tagLen <= 0)
2915 make_characterset_array(&charset_array);
2916 init_content_info(pInfo);
2918 debug_msg(RELEASE, "ID3tag v222--------------------------------------------------------------");
2920 while (curPos + MP3_TAGv2_22_TXT_HEADER_LEN < pInfo->tagV2Info.tagLen) {
2921 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) || !g_ascii_isalnum(buffer[curPos + 2]))
2924 tag_id = __get_tag_info_v222(&buffer[curPos]);
2925 frameLen = buffer[curPos + 3] << 16 | buffer[curPos + 4] << 8 | buffer[curPos + 5];
2926 curPos += MP3_TAGv2_22_TXT_HEADER_LEN;
2927 if (curPos + frameLen > pInfo->tagV2Info.tagLen)
2933 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value)
2936 if (buffer[curPos] == 0x00) {
2938 encType = AV_ID3V2_ISO_8859;
2939 } else if (buffer[curPos] == 0x01) {
2941 encType = AV_ID3V2_UTF16;
2944 /*in order to deliver valid string to MP */
2945 while ((buffer[curPos + encOffset] < 0x20) && (encOffset < frameLen))
2948 if (frameLen == encOffset) {
2949 debug_warning(DEBUG, "warning: invalid frame length %d %d", frameLen, encOffset);
2953 curPos += encOffset;
2954 frameLen -= encOffset;
2957 case AV_ID3TAG_COMMENT:
2959 https://id3.org/id3v2-00
2961 Frame size $xx xx xx
2964 Short content description <textstring> $00 (00)
2965 The actual text <textstring>
2967 if (frameLen <= 4) {
2968 debug_msg(RELEASE, "Too small to parse frameLen(%d)", frameLen);
2972 if (buffer[curPos + 4] > 0x20 && (buffer[curPos + 3] == 0x00 || buffer[curPos + 3] == 0x01)) {
2973 encType = __id3tag_get_text_encoding_v222(&buffer[curPos], 4);
2974 /*skip language data! */
2977 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
2979 debug_msg(RELEASE, "Failed to get tag: frameLen(%d)", frameLen);
2984 case AV_ID3TAG_PICTURE:
2985 if (extract_artwork)
2986 _mm_file_id3tag_parse_PIC(pInfo, &buffer[curPos], frameLen, charset_array[encType]);
2990 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
2994 if (pInfo->tagInfo[tag_id].value)
2995 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
3002 release_characterset_array(charset_array);
3007 static void __get_v223_encoding_info(const unsigned char *buffer,
3014 if (!buffer || !offset || !type)
3017 if (IS_ENCODEDBY_UTF16(buffer)) {
3019 *type = AV_ID3V2_UTF16;
3022 if (IS_ENCODEDBY_UTF16_R(buffer)) {
3024 *type = AV_ID3V2_UTF16_BE;
3027 if (IS_ENCODEDBY_UTF16(buffer + 1)) {
3029 *type = AV_ID3V2_UTF16;
3032 if (IS_ENCODEDBY_UTF16_R(buffer + 1)) {
3034 *type = AV_ID3V2_UTF16_BE;
3038 if (buffer[0] == 0x00) {
3041 while ((buffer[_offset] < 0x20) && (_offset < length))
3045 *type = AV_ID3V2_ISO_8859;
3048 bool mm_file_id3tag_parse_v223(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
3050 int extendedHeaderLen = 0;
3052 int curPos = MP3_TAGv2_HEADER_LEN;
3054 unsigned int encType = 0;
3055 char **charset_array = NULL;
3056 AvID3TagList tag_id = AV_ID3TAG_MAX;
3057 char *lang_info = NULL;
3059 if (pInfo->tagV2Info.tagLen <= 0)
3062 make_characterset_array(&charset_array);
3063 init_content_info(pInfo);
3065 debug_msg(RELEASE, "ID3tag v223--------------------------------------------------------------");
3066 if (buffer[5] & 0x40) {
3067 /* if extended header exists, skip it*/
3068 extendedHeaderLen = buffer[10] << 21 | buffer[11] << 14 | buffer[12] << 7 | buffer[13];
3069 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d", extendedHeaderLen);
3070 if (extendedHeaderLen > pInfo->tagV2Info.tagLen - MP3_TAGv2_HEADER_LEN) {
3071 debug_error(DEBUG, "extended header too long.");
3073 curPos += extendedHeaderLen;
3078 while (curPos + MP3_TAGv2_23_TXT_HEADER_LEN < pInfo->tagV2Info.tagLen) {
3079 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) ||
3080 !g_ascii_isalnum(buffer[curPos + 2]) || !g_ascii_isalnum(buffer[curPos + 3]))
3083 tag_id = __get_tag_info_v223(&buffer[curPos]);
3084 frameLen = buffer[curPos + 4] << 24 | buffer[curPos + 5] << 16 | buffer[curPos + 6] << 8 | buffer[curPos + 7];
3085 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3086 if (curPos + frameLen > pInfo->tagV2Info.tagLen)
3092 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value)
3095 __get_v223_encoding_info(buffer + curPos, frameLen, &encOffset, &encType);
3096 if (frameLen <= encOffset) {
3097 debug_warning(DEBUG, "warning: invalid frame length %d %d", frameLen, encOffset);
3101 curPos += encOffset;
3102 frameLen -= encOffset;
3105 if (encType != AV_ID3V2_UTF16 && encType != AV_ID3V2_UTF16_BE) {
3106 if (tag_id != AV_ID3TAG_COMMENT && tag_id != AV_ID3TAG_UNSYNCLYRICS && tag_id != AV_ID3TAG_SYNCLYRICS) {
3107 debug_msg(RELEASE, "get the new text encoding type");
3108 encType = buffer[curPos - 1];
3112 if (encType > AV_ID3V2_MAX) {
3113 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%u], TAG ID[%d]", encType, tag_id);
3118 case AV_ID3TAG_COMMENT:
3119 if (frameLen <= 3) {
3120 debug_msg(RELEASE, "Description info too small to parse frameLen(%d)", frameLen);
3126 if (buffer[curPos] != 0x00 && buffer[curPos] != 0xFF && buffer[curPos] != 0xFE) {
3127 debug_msg(RELEASE, "failed to get Comment: frameLen(%d)", frameLen);
3130 encType = __id3tag_get_text_encoding_v223(&buffer[curPos], &frameLen, encType, &encOffset);
3131 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3132 curPos += encOffset;
3133 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3136 case AV_ID3TAG_SYNCLYRICS:
3137 if (frameLen <= 5) {
3138 debug_msg(RELEASE, "Synchronised lyrics too small to parse frameLen(%d)", frameLen);
3144 if (buffer[curPos] != 0x00 && buffer[curPos] != 0xFF && buffer[curPos] != 0xFE) {
3145 debug_msg(RELEASE, "failed to get Synchronised lyrics Info curPos(%d), frameLen(%d)", curPos, frameLen);
3147 encType = __id3tag_get_text_encoding_v223(&buffer[curPos], &frameLen, encType, &encOffset);
3148 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3149 curPos += encOffset;
3150 __id3tag_parse_SYLT(pInfo, buffer, frameLen, charset_array[encType], encType, curPos);
3153 case AV_ID3TAG_UNSYNCLYRICS:
3154 lang_info = strndup((const char *)&buffer[curPos], 3);
3156 if (frameLen <= 3) {
3157 debug_msg(RELEASE, "Unsynchronised lyrics too small to parse frameLen(%d)", frameLen);
3158 mmfile_free(lang_info);
3164 if (buffer[curPos] != 0x00 && buffer[curPos] != 0xFF && buffer[curPos] != 0xFE) {
3165 debug_msg(RELEASE, "failed to get Unsynchronised lyrics Info curPos(%d), frameLen(%d)", curPos, frameLen);
3167 encType = __id3tag_get_text_encoding_v223(&buffer[curPos], &frameLen, encType, &encOffset);
3169 char *char_set = NULL;
3171 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3173 if (encType == AV_ID3V2_ISO_8859) {
3174 if (lang_info != NULL && !g_ascii_strcasecmp(lang_info, "KOR")) {
3175 char_set = strdup("EUC-KR");
3177 char_set = mmfile_get_charset((const char *)&buffer[curPos]);
3179 mmfile_free(lang_info);
3182 curPos += encOffset;
3185 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3187 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, char_set);
3188 mmfile_free(char_set);
3190 mmfile_free(lang_info);
3193 case AV_ID3TAG_PICTURE:
3194 if (extract_artwork)
3195 _mm_file_id3tag_parse_APIC(pInfo, &buffer[curPos], frameLen, charset_array[encType]);
3199 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3203 if (pInfo->tagInfo[tag_id].value)
3204 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
3211 release_characterset_array(charset_array);
3216 static void __get_v224_encoding_info(const unsigned char *buffer,
3223 if (!buffer || !offset || !type)
3226 /*in case of UTF 16 encoding */
3227 /*buffer+(position-length) data should '0x01' but in order to expansion, we don't accurately check the value. */
3228 if (IS_ENCODEDBY_UTF16(buffer)) {
3230 *type = AV_ID3V2_UTF16;
3234 if (IS_ENCODEDBY_UTF16_R(buffer)) {
3236 *type = AV_ID3V2_UTF16_BE;
3240 if (IS_ENCODEDBY_UTF16(buffer + 1)) {
3242 *type = AV_ID3V2_UTF16;
3245 if (IS_ENCODEDBY_UTF16_R(buffer + 1)) {
3247 *type = AV_ID3V2_UTF16_BE;
3251 /*in case of UTF-16 BE encoding */
3252 if (buffer[0] == 0x02) {
3254 while ((buffer[_offset] == '\0') && (_offset < length))
3255 _offset++;/*null skip! */
3257 *type = AV_ID3V2_UTF16_BE;
3261 /*in case of UTF8 encoding */
3262 if (buffer[0] == 0x03) {
3264 while ((buffer[_offset] == '\0') && (_offset < length))
3265 _offset++;/*null skip! */
3267 *type = AV_ID3V2_UTF8;
3270 /*in case of ISO-8859-1 encoding */
3271 /*buffer+(position-length) data should 0x00 but in order to expansion, we don't accurately check the value. */
3273 while ((buffer[_offset] < 0x20) && (_offset < length))
3274 _offset++;/*less than 0x20 value skip! */
3276 *type = AV_ID3V2_ISO_8859;
3279 bool mm_file_id3tag_parse_v224(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
3281 int extendedHeaderLen = 0;
3283 int curPos = MP3_TAGv2_HEADER_LEN;
3285 unsigned int encType = 0;
3286 char **charset_array = NULL;
3287 AvID3TagList tag_id = AV_ID3TAG_MAX;
3289 if (pInfo->tagV2Info.tagLen <= 0)
3292 make_characterset_array(&charset_array);
3293 init_content_info(pInfo);
3295 debug_msg(RELEASE, "ID3tag v224--------------------------------------------------------------");
3296 if (buffer[5] & 0x40) {
3297 /* if extended header exists, skip it*/
3298 extendedHeaderLen = buffer[10] << 21 | buffer[11] << 14 | buffer[12] << 7 | buffer[13];
3299 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d", extendedHeaderLen);
3300 if (extendedHeaderLen > pInfo->tagV2Info.tagLen - MP3_TAGv2_HEADER_LEN) {
3301 debug_error(DEBUG, "extended header too long.");
3303 curPos += extendedHeaderLen;
3307 while (curPos + MP3_TAGv2_23_TXT_HEADER_LEN < pInfo->tagV2Info.tagLen) {
3308 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) ||
3309 !g_ascii_isalnum(buffer[curPos + 2]) || !g_ascii_isalnum(buffer[curPos + 3]))
3312 tag_id = __get_tag_info_v223(&buffer[curPos]);
3313 frameLen = buffer[curPos + 4] << 21 | buffer[curPos + 5] << 14 | buffer[curPos + 6] << 7 | buffer[curPos + 7];
3314 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3315 if (curPos + frameLen > pInfo->tagV2Info.tagLen)
3321 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value)
3324 __get_v224_encoding_info(buffer + curPos, frameLen, &encOffset, &encType);
3325 if (frameLen <= encOffset) {
3326 debug_warning(DEBUG, "warning: invalid frame length %d %d", frameLen, encOffset);
3330 curPos += encOffset;
3331 frameLen -= encOffset;
3334 if (encType != AV_ID3V2_UTF16 && encType != AV_ID3V2_UTF16_BE) {
3335 if (tag_id != AV_ID3TAG_COMMENT && tag_id != AV_ID3TAG_UNSYNCLYRICS && tag_id != AV_ID3TAG_SYNCLYRICS) {
3336 debug_msg(RELEASE, "get the new text encoding type");
3337 encType = buffer[curPos - 1];
3341 if (encType > AV_ID3V2_MAX) {
3342 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%u], TAG ID[%d]", encType, tag_id);
3347 case AV_ID3TAG_COMMENT:
3349 case AV_ID3TAG_UNSYNCLYRICS:
3350 if (frameLen <= 3) {
3351 debug_msg(RELEASE, "Description info too small to parse frameLen(%d)", frameLen);
3357 encType = __id3tag_get_text_encoding_v224(&buffer[curPos], &frameLen, encType, &encOffset);
3358 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3359 curPos += encOffset;
3360 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3363 case AV_ID3TAG_SYNCLYRICS:
3364 if (frameLen <= 5) {
3365 debug_msg(RELEASE, "Synchronised lyrics too small to parse frameLen(%d)", frameLen);
3371 encType = __id3tag_get_text_encoding_v224(&buffer[curPos], &frameLen, encType, &encOffset);
3372 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3373 curPos += encOffset;
3374 __id3tag_parse_SYLT(pInfo, buffer, frameLen, charset_array[encType], encType, curPos);
3377 case AV_ID3TAG_PICTURE:
3378 if (extract_artwork)
3379 _mm_file_id3tag_parse_APIC(pInfo, &buffer[curPos], frameLen, charset_array[encType]);
3383 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3387 if (pInfo->tagInfo[tag_id].value)
3388 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
3395 release_characterset_array(charset_array);
3401 void mm_file_id3tag_restore_content_info(AvFileContentInfo *pInfo)
3405 /* for Genre Info */
3406 if (pInfo->tagInfo[AV_ID3TAG_GENRE].value) {
3408 if (!__get_genre_num(pInfo->tagInfo[AV_ID3TAG_GENRE].value, &genre_id)) {
3409 debug_msg(RELEASE, "genre information is not integer [%s]", pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3413 /* If integer, check genre code. */
3414 /* If out of range, set UNKNOWN */
3415 if (genre_id < 0 || genre_id >= GENRE_COUNT)
3416 genre_id = GENRE_COUNT - 1;
3418 debug_msg(RELEASE, "genre information is integer [%d]", genre_id);
3420 g_free(pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3421 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(MpegAudio_Genre[genre_id]);
3423 /* No genre in ID3V2.. So check V1 */
3424 if (pInfo->bV1tagFound == true) {
3425 debug_msg(RELEASE, "Genre: %d", pInfo->genre);
3427 /* If out of range, set UNKNOWN */
3428 if (pInfo->genre > GENRE_COUNT - 1)
3429 pInfo->genre = GENRE_COUNT - 1;
3431 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(MpegAudio_Genre[pInfo->genre]);
3433 debug_msg(RELEASE, "Genre was not Found.");
3438 static void __free_synclyrics(gpointer data)
3440 AvSynclyricsInfo *info = (AvSynclyricsInfo *) data;
3445 mmfile_free(info->lyric_info);
3450 void mm_file_free_synclyrics_list(GList *synclyrics_list)
3452 if (!synclyrics_list)
3455 g_list_free_full(synclyrics_list, __free_synclyrics);