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 - 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 /*/////////////////////////////////////////////////////////////// */
2261 /* Extracting ID3 Tag Data // */
2262 /*/////////////////////////////////////////////////////////////// */
2263 case FOURCC('m', 'e', 't', 'a'): {
2264 debug_msg(RELEASE, "MPEG4: [meta] SIZE: [%lld]Byte", chunk_size);
2265 GetTagFromMetaBox(formatContext, fp, &basic_header);
2269 case FOURCC('t', 'r', 'a', 'k'): {
2270 debug_msg(RELEASE, "MPEG4: [trak] SIZE: [%lld]Byte", chunk_size);
2273 case FOURCC('u', 'u', 'i', 'd'): {
2274 unsigned long uuid[4] = {0, };
2276 debug_msg(RELEASE, "MPEG4: [uuid] SIZE: [%lld]Byte", chunk_size);
2278 mmfile_read(fp, (unsigned char *)uuid, sizeof(uuid));
2280 if (mmfile_io_be_uint32(uuid[0]) == 0xffcc8263
2281 && mmfile_io_be_uint32(uuid[1]) == 0xf8554a93
2282 && mmfile_io_be_uint32(uuid[2]) == 0x8814587a
2283 && mmfile_io_be_uint32(uuid[3]) == 0x02521fdd) {
2285 str = (char *)malloc(basic_header.size);
2288 memset(str, 0, basic_header.size);
2289 mmfile_read(fp, (unsigned char *)str, basic_header.size);
2291 /* The block is superseded */
2292 if (strstr(str, "<GSpherical:Spherical>true</GSpherical:Spherical>"))
2293 formatContext->is_360 = 1;
2295 formatContext->is_360 = 0;
2296 /* Image can be stitched even if it is not spherical */
2297 if (formatContext->is_360 == 1) {
2298 if (strstr(str, "<GSpherical:Stitched>true</GSpherical:Stitched>"))
2299 formatContext->stitched = MMFILE_360_STITCHED;
2301 formatContext->stitched = MMFILE_360_NON_STITCHED;
2303 /* Image can be stitched or non-stitched. Usage of some 3rd value is superfluous */
2304 formatContext->stitched = MMFILE_360_NONE;
2308 debug_msg(RELEASE, "Extracting tags from UUID XML string %s", str);
2310 ParseSpatialVideoMetadataFromXMLString(str, formatContext);
2313 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2317 case FOURCC('m', 'd', 'i', 'a'): {
2318 debug_msg(RELEASE, "MPEG4: [mdia] SIZE: [%lld]Byte", chunk_size);
2321 case FOURCC('m', 'i', 'n', 'f'): {
2322 debug_msg(RELEASE, "MPEG4: [minf] SIZE: [%lld]Byte", chunk_size);
2325 case FOURCC('s', 't', 'b', 'l'): {
2326 debug_msg(RELEASE, "MPEG4: [stbl] SIZE: [%lld]Byte", chunk_size);
2329 case FOURCC('s', 't', 's', 'd'): {
2330 debug_msg(RELEASE, "MPEG4: [stsd] SIZE: [%lld]Byte", chunk_size);
2333 case FOURCC('m', 'p', '4', 'a'): {
2334 debug_msg(RELEASE, "MPEG4: [mp4a] SIZE: [%lld]Byte", chunk_size);
2335 GetSA3DInfoFromMP4ATagBox(formatContext, fp, &basic_header);
2336 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2339 case FOURCC('a', 'v', 'c', '1'): {
2340 debug_msg(RELEASE, "MPEG4: [avc1] SIZE: [%lld]Byte (offset: %lld)", chunk_size, basic_header.start_offset);
2341 GetVideoV2MetadataFromAvc1TagBox(formatContext, fp, &basic_header);
2345 debug_msg(RELEASE, "4CC: Not Support [%c%c%c%c]. So skip it. Size [%lld Byte]",
2346 ((char *)&basic_header.type)[0], ((char *)&basic_header.type)[1],
2347 ((char *)&basic_header.type)[2], ((char *)&basic_header.type)[3], chunk_size);
2348 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2353 if (ret == MMFILE_UTIL_FAIL) {
2354 debug_error(DEBUG, "mmfile operation is error");
2359 long long new_pos = mmfile_tell(fp);
2361 if ((moov_end == 0) && (new_pos <= basic_header.start_offset)) {
2362 debug_error(DEBUG, "Wrong position");
2363 ret = MMFILE_UTIL_FAIL;
2366 basic_header.start_offset = new_pos;
2375 static bool __get_genre_num(const char *buffer, int *si)
2377 int len = strlen(buffer);
2378 char *tmp_genre = NULL;
2380 /* Genre format: (###), ### */
2381 if (len > 6 || len == 0)
2386 if (!g_ascii_isdigit(buffer[0]))
2389 if (!g_ascii_isdigit(buffer[1]))
2393 /* Remove bind () if exist */
2394 tmp_genre = g_strdup(buffer);
2395 if (g_str_has_prefix(tmp_genre, "(") && g_str_has_suffix(tmp_genre, ")"))
2398 *si = atoi(tmp_genre);
2404 static bool make_characterset_array(char ***charset_array)
2406 char *locale = MMFileUtilGetLocale();
2408 *charset_array = calloc(AV_ID3V2_MAX, sizeof(char *));
2410 if (*charset_array == NULL) {
2411 debug_error(DEBUG, "calloc failed ");
2417 if (locale != NULL) {
2418 (*charset_array)[AV_ID3V2_ISO_8859] = strdup(locale);
2420 debug_error(DEBUG, "get locale failed");
2421 (*charset_array)[AV_ID3V2_ISO_8859] = NULL;
2424 (*charset_array)[AV_ID3V2_UTF16] = strdup("UCS2");
2425 (*charset_array)[AV_ID3V2_UTF16_BE] = strdup("UTF16-BE");
2426 (*charset_array)[AV_ID3V2_UTF8] = strdup(MMFILE_CODESET_UTF8);
2431 static bool release_characterset_array(char **charset_array)
2438 for (i = 0; i < AV_ID3V2_MAX; i++) {
2439 if (charset_array[i] != NULL) {
2440 free(charset_array[i]);
2441 charset_array[i] = NULL;
2445 free(charset_array);
2446 charset_array = NULL;
2451 static void init_content_info(AvFileContentInfo *pInfo)
2455 for(i = 0; i < AV_ID3TAG_MAX; i++)
2456 pInfo->tagInfo[i].value = NULL;
2458 pInfo->imageInfo.bURLInfo = false;
2459 pInfo->imageInfo.pImageBuf = NULL;
2460 pInfo->imageInfo.imageLen = 0;
2463 static void __id3tag_skip_newline(unsigned char *pTagVal, int *nTagLen, int *offset)
2465 /* skip newline in text encoding of ID3 tag frame */
2466 while ((NEWLINE_OF_UTF16(pTagVal + *offset) || NEWLINE_OF_UTF16_R(pTagVal + *offset)) && *nTagLen > 4) {
2472 static int __id3tag_get_text_encoding_v222(unsigned char *pTagVal, int offset)
2474 if (pTagVal[offset - 1] == 0x00)
2475 return AV_ID3V2_ISO_8859;
2477 return AV_ID3V2_UTF16;
2480 static int __id3tag_get_text_encoding_v223(unsigned char *pTagVal, int *npTagLen, int nTextEnc, int *offset)
2482 if ((IS_ENCODEDBY_UTF16(pTagVal + *offset) || IS_ENCODEDBY_UTF16_R(pTagVal + *offset)) && *npTagLen > 2) {
2483 __id3tag_skip_newline(pTagVal, npTagLen, offset);
2485 if (IS_ENCODEDBY_UTF16(pTagVal + *offset) && (*npTagLen > 2)) {
2488 return AV_ID3V2_UTF16;
2489 } else if (IS_ENCODEDBY_UTF16_R(pTagVal + *offset) && (*npTagLen > 2)) {
2492 return AV_ID3V2_UTF16_BE;
2493 } else if (IS_ENCODEDBY_UTF16(pTagVal + *offset + 1) && (*npTagLen > 3)) {
2496 return AV_ID3V2_UTF16;
2497 } else if (IS_ENCODEDBY_UTF16_R(pTagVal + *offset + 1) && (*npTagLen > 3)) {
2500 return AV_ID3V2_UTF16_BE;
2502 debug_msg(RELEASE, "id3tag never get here!!");
2503 return nTextEnc; /* default bypass */
2506 while ((pTagVal[*offset] < 0x20) && (*offset < *npTagLen)) { /* text string encoded by ISO-8859-1 */
2510 return AV_ID3V2_ISO_8859;
2514 static int __id3tag_get_text_encoding_v224(unsigned char *pTagVal, int *npTagLen, int nTextEnc, int *offset)
2516 if (nTextEnc == AV_ID3V2_UTF16 || nTextEnc == AV_ID3V2_UTF16_BE) {
2517 __id3tag_skip_newline(pTagVal, npTagLen, offset);
2519 if ((IS_ENCODEDBY_UTF16(pTagVal + *offset) || IS_ENCODEDBY_UTF16_R(pTagVal + *offset)) && *npTagLen > 2) {
2522 return AV_ID3V2_UTF16;
2524 debug_msg(RELEASE, "id3tag never get here!!");
2525 return nTextEnc; /* default bypass */
2527 } else if (nTextEnc == AV_ID3V2_UTF8) {
2528 while (pTagVal[*offset] < 0x20 && (*offset < *npTagLen)) { /* text string encoded by UTF-8 */
2532 return AV_ID3V2_UTF8;
2534 while (pTagVal[*offset] < 0x20 && (*offset < *npTagLen)) { /* text string encoded by ISO-8859-1 */
2538 return AV_ID3V2_ISO_8859;
2542 static void __id3tag_parse_SYLT(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int textEncodingType, int offset)
2546 int copy_start_pos = offset;
2547 AvSynclyricsInfo *synclyrics_info = NULL;
2548 GList *synclyrics_info_list = NULL;
2550 if (nTagLen < MMFILE_SYNC_LYRIC_INFO_MIN_LEN) {
2551 debug_msg(RELEASE, "failed to get Synchronised lyrics Info realCpyFramNum(%d)", nTagLen);
2555 if ((textEncodingType == AV_ID3V2_UTF16) || (textEncodingType == AV_ID3V2_UTF16_BE)) {
2556 debug_warning(DEBUG, "[%d] not implemented", textEncodingType);
2560 for (idx = 0; idx < nTagLen; idx++) {
2561 if (pTagVal[offset + idx] == 0x00) {
2562 synclyrics_info = g_new0(AvSynclyricsInfo, 1);
2563 if (textEncodingType == AV_ID3V2_UTF8)
2564 synclyrics_info->lyric_info = g_memdup2(pTagVal + copy_start_pos, copy_len + 1);
2566 synclyrics_info->lyric_info = mmfile_convert_to_utf8((const char *)&pTagVal[copy_start_pos], copy_len, pCharSet);
2568 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];
2570 copy_start_pos = offset + idx + 1;
2571 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);
2573 synclyrics_info_list = g_list_append(synclyrics_info_list, synclyrics_info);
2578 pInfo->pSyncLyrics = synclyrics_info_list;
2581 static bool __id3tag_parse_PIC_format(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2583 unsigned int idx = 0;
2585 /* get the mime type of Attached PICture, it is text string */
2587 if (pTagVal[*offset] == '\0') {
2588 debug_msg(RELEASE, "The picture format of PIC is not included");
2592 /* init ext variable */
2593 memset(pInfo->imageInfo.imageExt, 0, sizeof(pInfo->imageInfo.imageExt));
2595 while ((idx < MP3_ID3_IMAGE_EXT_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2596 pInfo->imageInfo.imageExt[idx] = pTagVal[idx];
2605 static bool __id3tag_parse_APIC_mimetype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2607 unsigned int idx = 0;
2608 const char *MIME_PRFIX = "image/";
2610 /* get the mime type of Attached PICture, it is text string */
2612 if (pTagVal[*offset] == '\0') {
2613 pInfo->imageInfo.imgMimetypeLen = 0;
2614 debug_msg(RELEASE, "The MIME type of APIC is not included");
2618 /* init mimetype variable */
2619 memset(pInfo->imageInfo.imageMIMEType, 0, sizeof(pInfo->imageInfo.imageMIMEType));
2621 while ((idx < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2622 pInfo->imageInfo.imageMIMEType[idx] = pTagVal[idx];
2625 pInfo->imageInfo.imgMimetypeLen = idx;
2629 if (strncmp(pInfo->imageInfo.imageMIMEType, MIME_PRFIX, strlen(MIME_PRFIX)) != 0) {
2630 pInfo->imageInfo.imgMimetypeLen = 0;
2631 debug_error(DEBUG, "MIME type(%s) is not image", pInfo->imageInfo.imageMIMEType);
2635 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2636 debug_msg(RELEASE, "pTagVal[offset](%d) mimetype is not NULL terminated! realCpyFrameNum - offset(%d)",
2637 pTagVal[*offset], nTagLen - *offset);
2641 (*offset)++;/* end of MIME('\0', 1byte) */
2642 debug_msg(RELEASE, "after scaning Mime type offset(%d) value!", *offset);
2647 static void __id3tag_parse_APIC_pictype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2649 /* get the picture type of Attached PICture, it is 1byte(0xff) */
2651 if (pTagVal[*offset] < MP3_ID3V2_PICTURE_TYPE_MAX)
2652 pInfo->imageInfo.pictureType = pTagVal[*offset];
2654 debug_msg(RELEASE, "APIC image has invalid picture type(0x%x)", pTagVal[*offset]);
2656 (*offset)++;/* PictureType(1byte) */
2657 debug_msg(RELEASE, "after scaning PictureType(%d) offset(%d) value!", pInfo->imageInfo.pictureType, *offset);
2660 static void __id3tag_parse_APIC_desc(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int *offset)
2662 /* get the description of Attached PICture, it is text string */
2665 unsigned int tag_len = 0;
2666 char *tmp_desc = NULL;
2668 if (pTagVal[*offset] == 0x0) {
2669 debug_msg(RELEASE, "The description of APIC is not included!!!");
2674 if (pTagVal[*offset + idx] == '\0') {
2675 if (nTagLen < (*offset + idx)) {
2676 debug_error(DEBUG, "End of APIC Tag %d %d %d", nTagLen, *offset, idx);
2679 /* check end of image description */
2680 if ((pTagVal[*offset + idx + 1] == gTagJPEGHeader[0]) ||
2681 (pTagVal[*offset + idx + 1] == gTagPNGHeader[0])) {
2682 debug_msg(RELEASE, "length of description (%d)", idx);
2689 tag_len = idx + 1; /* length of description + '\0' */
2691 tmp_desc = g_memdup2(pTagVal + *offset, tag_len);
2693 /* convert description */
2694 pInfo->imageInfo.imageDescription = mmfile_convert_to_utf8(tmp_desc, tag_len, pCharSet);
2695 mmfile_free(tmp_desc);
2696 debug_msg(RELEASE, "new_desc %s", pInfo->imageInfo.imageDescription);
2699 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2700 debug_msg(RELEASE, "pTagVal[offset](%d) description is not NULL terminated! realCpyFrameNum - offset(%d)",
2701 pTagVal[*offset], nTagLen - *offset);
2704 (*offset)++; /* end of desceription(1byte) */
2707 static void __id3tag_parse_APIC_picture(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2709 /* get the picture of Attached PICture, it is binary data */
2711 /* some content has useless '\0' in front of picture data */
2712 while (pTagVal[*offset] == '\0') {
2716 debug_msg(RELEASE, "after scaning APIC description offset(%d) value!", *offset);
2718 if (nTagLen <= *offset) {
2719 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - offset(%d)", nTagLen, *offset);
2723 pInfo->imageInfo.imageLen = nTagLen - *offset;
2724 pInfo->imageInfo.pImageBuf = g_memdup2(pTagVal + *offset, pInfo->imageInfo.imageLen);
2726 /* if mimetype is "-->", image date has an URL */
2727 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
2728 pInfo->imageInfo.bURLInfo = true;
2731 static bool _mm_file_id3tag_parse_PIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2733 /* current position to read pTagVal */
2736 debug_fenter(RELEASE);
2738 if (!__id3tag_parse_PIC_format(pInfo, pTagVal, &offset)) {
2739 debug_msg(RELEASE, "PIC is not valid");
2743 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2744 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2745 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2747 debug_fleave(RELEASE);
2752 static bool _mm_file_id3tag_parse_APIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2756 debug_fenter(RELEASE);
2758 if (!__id3tag_parse_APIC_mimetype(pInfo, pTagVal, nTagLen, &offset)) {
2759 debug_msg(RELEASE, "APIC is not valid");
2763 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2764 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2765 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2767 debug_fleave(RELEASE);
2772 static char * __id3tag_get_v110(const char *buf, ssize_t len, const char *locale)
2774 char *tmp_str = NULL;
2776 mm_file_retv_if_fails(buf, NULL);
2777 mm_file_retv_if_fails(locale, NULL);
2779 tmp_str = mmfile_convert_to_utf8(buf, len, locale);
2781 /* ID3v1 tag need check length or space. */
2782 if (tmp_str && (strlen(tmp_str) == 0 || g_ascii_isspace(tmp_str[0])))
2783 mmfile_free(tmp_str);
2788 bool mm_file_id3tag_parse_v110(AvFileContentInfo *pInfo, unsigned char *buffer)
2790 const char *locale = MMFileUtilGetLocale();
2792 debug_msg(RELEASE, "ID3tag v110--------------------------------------------------------------");
2794 if (!pInfo->tagInfo[AV_ID3TAG_TITLE].value) {
2795 pInfo->tagInfo[AV_ID3TAG_TITLE].value = __id3tag_get_v110((const char *)&buffer[3], MP3_ID3_TITLE_LENGTH, locale);
2797 debug_msg(RELEASE, "pInfo->pTitle returned =(%s)", pInfo->tagInfo[AV_ID3TAG_TITLE].value);
2800 if (!pInfo->tagInfo[AV_ID3TAG_ARTIST].value) {
2801 pInfo->tagInfo[AV_ID3TAG_ARTIST].value = __id3tag_get_v110((const char *)&buffer[33], MP3_ID3_ARTIST_LENGTH, locale);
2803 debug_msg(RELEASE, "pInfo->pArtist returned =(%s)", pInfo->tagInfo[AV_ID3TAG_ARTIST].value);
2806 if (!pInfo->tagInfo[AV_ID3TAG_ALBUM].value) {
2807 pInfo->tagInfo[AV_ID3TAG_ALBUM].value = __id3tag_get_v110((const char *)&buffer[63], MP3_ID3_ALBUM_LENGTH, locale);
2809 debug_msg(RELEASE, "pInfo->pAlbum returned =(%s)", pInfo->tagInfo[AV_ID3TAG_ALBUM].value);
2812 if (!pInfo->tagInfo[AV_ID3TAG_YEAR].value) {
2813 pInfo->tagInfo[AV_ID3TAG_YEAR].value = __id3tag_get_v110((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, locale);
2815 debug_msg(RELEASE, "pInfo->pYear returned =(%s)", pInfo->tagInfo[AV_ID3TAG_YEAR].value);
2818 if (!pInfo->tagInfo[AV_ID3TAG_COMMENT].value) {
2819 pInfo->tagInfo[AV_ID3TAG_COMMENT].value = __id3tag_get_v110((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, locale);
2821 debug_msg(RELEASE, "pInfo->pComment returned =(%s)", pInfo->tagInfo[AV_ID3TAG_COMMENT].value);
2824 if (!pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value) {
2825 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value = g_malloc0(ID3TAG_V110_TRACK_NUM_DIGIT);
2826 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value[ID3TAG_V110_TRACK_NUM_DIGIT - 1] = 0;
2827 snprintf(pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value, ID3TAG_V110_TRACK_NUM_DIGIT, "%04d", (int)buffer[126]);
2829 debug_msg(RELEASE, "pInfo->pTrackNum returned =(%s)", pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value);
2832 /*ID3V2 genre is stored in pInfo->tagInfo[AV_ID3TAG_GENRE].value */
2833 /*pInfo->genre is used when ID3V2 genre is invalid, or empty. */
2834 pInfo->genre = buffer[127];
2835 debug_msg(RELEASE, "pInfo->genre returned genre number (%d)", pInfo->genre);
2840 static AvID3TagList __get_tag_info_v222(const unsigned char *tag)
2846 n += tag[i++] - 'A' + 1;
2849 n += tag[2]; //num, char mixted
2851 for (i = 0; i < ID3TAG_NUM_V22; i++) {
2852 if (n == tag_info_v22[i].int_name) {
2853 return tag_info_v22[i].tag;
2857 debug_msg(RELEASE, "(%s) This Frame ID currently not Supports!!", tag);
2859 return AV_ID3TAG_MAX;
2862 static AvID3TagList __get_tag_info_v223(const unsigned char *tag)
2868 n += tag[i++] - 'A' + 1;
2871 n += tag[3]; //num, char mixted
2873 for (i = 0; i < ID3TAG_NUM_V23; i++) {
2874 if (n == tag_info_v23[i].int_name) {
2875 return tag_info_v23[i].tag;
2879 debug_msg(RELEASE, "(%s) This Frame ID currently not Supports!!", tag);
2881 return AV_ID3TAG_MAX;
2885 bool mm_file_id3tag_parse_v222(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
2887 int curPos = MP3_TAGv2_HEADER_LEN;
2891 char **charset_array = NULL;
2892 AvID3TagList tag_id = AV_ID3TAG_MAX;
2894 if (pInfo->tagV2Info.tagLen <= 0)
2897 make_characterset_array(&charset_array);
2898 init_content_info(pInfo);
2900 debug_msg(RELEASE, "ID3tag v222--------------------------------------------------------------");
2902 while (curPos + MP3_TAGv2_22_TXT_HEADER_LEN < pInfo->tagV2Info.tagLen) {
2903 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) || !g_ascii_isalnum(buffer[curPos + 2]))
2906 tag_id = __get_tag_info_v222(&buffer[curPos]);
2907 frameLen = buffer[curPos + 3] << 16 | buffer[curPos + 4] << 8 | buffer[curPos + 5];
2908 curPos += MP3_TAGv2_22_TXT_HEADER_LEN;
2909 if (curPos + frameLen > pInfo->tagV2Info.tagLen)
2915 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value)
2918 if (buffer[curPos] == 0x00) {
2920 encType = AV_ID3V2_ISO_8859;
2921 } else if (buffer[curPos] == 0x01) {
2923 encType = AV_ID3V2_UTF16;
2926 /*in order to deliver valid string to MP */
2927 while ((buffer[curPos + encOffset] < 0x20) && (encOffset < frameLen))
2930 if (frameLen == encOffset) {
2931 debug_warning(DEBUG, "warning: invalid frame length %d %d", frameLen, encOffset);
2935 curPos += encOffset;
2936 frameLen -= encOffset;
2939 case AV_ID3TAG_COMMENT:
2941 https://id3.org/id3v2-00
2943 Frame size $xx xx xx
2946 Short content description <textstring> $00 (00)
2947 The actual text <textstring>
2949 if (frameLen <= 4) {
2950 debug_msg(RELEASE, "Too small to parse frameLen(%d)", frameLen);
2954 if (buffer[curPos + 4] > 0x20 && (buffer[curPos + 3] == 0x00 || buffer[curPos + 3] == 0x01)) {
2955 encType = __id3tag_get_text_encoding_v222(&buffer[curPos], 4);
2956 /*skip language data! */
2959 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
2961 debug_msg(RELEASE, "Failed to get tag: frameLen(%d)", frameLen);
2966 case AV_ID3TAG_PICTURE:
2967 if (extract_artwork)
2968 _mm_file_id3tag_parse_PIC(pInfo, &buffer[curPos], frameLen, charset_array[encType]);
2972 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
2976 if (pInfo->tagInfo[tag_id].value)
2977 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
2984 release_characterset_array(charset_array);
2989 static void __get_v223_encoding_info(const unsigned char *buffer,
2996 if (!buffer || !offset || !type)
2999 if (IS_ENCODEDBY_UTF16(buffer)) {
3001 *type = AV_ID3V2_UTF16;
3004 if (IS_ENCODEDBY_UTF16_R(buffer)) {
3006 *type = AV_ID3V2_UTF16_BE;
3009 if (IS_ENCODEDBY_UTF16(buffer + 1)) {
3011 *type = AV_ID3V2_UTF16;
3014 if (IS_ENCODEDBY_UTF16_R(buffer + 1)) {
3016 *type = AV_ID3V2_UTF16_BE;
3020 if (buffer[0] == 0x00) {
3023 while ((buffer[_offset] < 0x20) && (_offset < length))
3027 *type = AV_ID3V2_ISO_8859;
3030 bool mm_file_id3tag_parse_v223(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
3032 int extendedHeaderLen = 0;
3034 int curPos = MP3_TAGv2_HEADER_LEN;
3036 unsigned int encType = 0;
3037 char **charset_array = NULL;
3038 AvID3TagList tag_id = AV_ID3TAG_MAX;
3039 char *lang_info = NULL;
3041 if (pInfo->tagV2Info.tagLen <= 0)
3044 make_characterset_array(&charset_array);
3045 init_content_info(pInfo);
3047 debug_msg(RELEASE, "ID3tag v223--------------------------------------------------------------");
3048 if (buffer[5] & 0x40) {
3049 /* if extended header exists, skip it*/
3050 extendedHeaderLen = buffer[10] << 21 | buffer[11] << 14 | buffer[12] << 7 | buffer[13];
3051 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d", extendedHeaderLen);
3052 if (extendedHeaderLen > pInfo->tagV2Info.tagLen - MP3_TAGv2_HEADER_LEN) {
3053 debug_error(DEBUG, "extended header too long.");
3055 curPos += extendedHeaderLen;
3060 while (curPos + MP3_TAGv2_23_TXT_HEADER_LEN < pInfo->tagV2Info.tagLen) {
3061 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) ||
3062 !g_ascii_isalnum(buffer[curPos + 2]) || !g_ascii_isalnum(buffer[curPos + 3]))
3065 tag_id = __get_tag_info_v223(&buffer[curPos]);
3066 frameLen = buffer[curPos + 4] << 24 | buffer[curPos + 5] << 16 | buffer[curPos + 6] << 8 | buffer[curPos + 7];
3067 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3068 if (curPos + frameLen > pInfo->tagV2Info.tagLen)
3074 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value)
3077 __get_v223_encoding_info(buffer + curPos, frameLen, &encOffset, &encType);
3078 if (frameLen <= encOffset) {
3079 debug_warning(DEBUG, "warning: invalid frame length %d %d", frameLen, encOffset);
3083 curPos += encOffset;
3084 frameLen -= encOffset;
3087 if (encType != AV_ID3V2_UTF16 && encType != AV_ID3V2_UTF16_BE) {
3088 if (tag_id != AV_ID3TAG_COMMENT && tag_id != AV_ID3TAG_UNSYNCLYRICS && tag_id != AV_ID3TAG_SYNCLYRICS) {
3089 debug_msg(RELEASE, "get the new text encoding type");
3090 encType = buffer[curPos - 1];
3094 if (encType > AV_ID3V2_MAX) {
3095 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%u], TAG ID[%d]", encType, tag_id);
3100 case AV_ID3TAG_COMMENT:
3101 if (frameLen <= 3) {
3102 debug_msg(RELEASE, "Description info too small to parse frameLen(%d)", frameLen);
3108 if (buffer[curPos] != 0x00 && buffer[curPos] != 0xFF && buffer[curPos] != 0xFE) {
3109 debug_msg(RELEASE, "failed to get Comment: frameLen(%d)", frameLen);
3112 encType = __id3tag_get_text_encoding_v223(&buffer[curPos], &frameLen, encType, &encOffset);
3113 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3114 curPos += encOffset;
3115 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3118 case AV_ID3TAG_SYNCLYRICS:
3119 if (frameLen <= 5) {
3120 debug_msg(RELEASE, "Synchronised lyrics 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 Synchronised lyrics Info curPos(%d), frameLen(%d)", curPos, frameLen);
3129 encType = __id3tag_get_text_encoding_v223(&buffer[curPos], &frameLen, encType, &encOffset);
3130 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3131 curPos += encOffset;
3132 __id3tag_parse_SYLT(pInfo, buffer, frameLen, charset_array[encType], encType, curPos);
3135 case AV_ID3TAG_UNSYNCLYRICS:
3136 lang_info = strndup((const char *)&buffer[curPos], 3);
3138 if (frameLen <= 3) {
3139 debug_msg(RELEASE, "Unsynchronised lyrics too small to parse frameLen(%d)", frameLen);
3140 mmfile_free(lang_info);
3146 if (buffer[curPos] != 0x00 && buffer[curPos] != 0xFF && buffer[curPos] != 0xFE) {
3147 debug_msg(RELEASE, "failed to get Unsynchronised lyrics Info curPos(%d), frameLen(%d)", curPos, frameLen);
3149 encType = __id3tag_get_text_encoding_v223(&buffer[curPos], &frameLen, encType, &encOffset);
3151 char *char_set = NULL;
3153 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3155 if (encType == AV_ID3V2_ISO_8859) {
3156 if (lang_info != NULL && !g_ascii_strcasecmp(lang_info, "KOR")) {
3157 char_set = strdup("EUC-KR");
3159 char_set = mmfile_get_charset((const char *)&buffer[curPos]);
3161 mmfile_free(lang_info);
3164 curPos += encOffset;
3167 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3169 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, char_set);
3170 mmfile_free(char_set);
3172 mmfile_free(lang_info);
3175 case AV_ID3TAG_PICTURE:
3176 if (extract_artwork)
3177 _mm_file_id3tag_parse_APIC(pInfo, &buffer[curPos], frameLen, charset_array[encType]);
3181 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3185 if (pInfo->tagInfo[tag_id].value)
3186 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
3193 release_characterset_array(charset_array);
3198 static void __get_v224_encoding_info(const unsigned char *buffer,
3205 if (!buffer || !offset || !type)
3208 /*in case of UTF 16 encoding */
3209 /*buffer+(position-length) data should '0x01' but in order to expansion, we don't accurately check the value. */
3210 if (IS_ENCODEDBY_UTF16(buffer)) {
3212 *type = AV_ID3V2_UTF16;
3216 if (IS_ENCODEDBY_UTF16_R(buffer)) {
3218 *type = AV_ID3V2_UTF16_BE;
3222 if (IS_ENCODEDBY_UTF16(buffer + 1)) {
3224 *type = AV_ID3V2_UTF16;
3227 if (IS_ENCODEDBY_UTF16_R(buffer + 1)) {
3229 *type = AV_ID3V2_UTF16_BE;
3233 /*in case of UTF-16 BE encoding */
3234 if (buffer[0] == 0x02) {
3236 while ((buffer[_offset] == '\0') && (_offset < length))
3237 _offset++;/*null skip! */
3239 *type = AV_ID3V2_UTF16_BE;
3243 /*in case of UTF8 encoding */
3244 if (buffer[0] == 0x03) {
3246 while ((buffer[_offset] == '\0') && (_offset < length))
3247 _offset++;/*null skip! */
3249 *type = AV_ID3V2_UTF8;
3252 /*in case of ISO-8859-1 encoding */
3253 /*buffer+(position-length) data should 0x00 but in order to expansion, we don't accurately check the value. */
3255 while ((buffer[_offset] < 0x20) && (_offset < length))
3256 _offset++;/*less than 0x20 value skip! */
3258 *type = AV_ID3V2_ISO_8859;
3261 bool mm_file_id3tag_parse_v224(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
3263 int extendedHeaderLen = 0;
3265 int curPos = MP3_TAGv2_HEADER_LEN;
3267 unsigned int encType = 0;
3268 char **charset_array = NULL;
3269 AvID3TagList tag_id = AV_ID3TAG_MAX;
3271 if (pInfo->tagV2Info.tagLen <= 0)
3274 make_characterset_array(&charset_array);
3275 init_content_info(pInfo);
3277 debug_msg(RELEASE, "ID3tag v224--------------------------------------------------------------");
3278 if (buffer[5] & 0x40) {
3279 /* if extended header exists, skip it*/
3280 extendedHeaderLen = buffer[10] << 21 | buffer[11] << 14 | buffer[12] << 7 | buffer[13];
3281 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d", extendedHeaderLen);
3282 if (extendedHeaderLen > pInfo->tagV2Info.tagLen - MP3_TAGv2_HEADER_LEN) {
3283 debug_error(DEBUG, "extended header too long.");
3285 curPos += extendedHeaderLen;
3289 while (curPos + MP3_TAGv2_23_TXT_HEADER_LEN < pInfo->tagV2Info.tagLen) {
3290 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) ||
3291 !g_ascii_isalnum(buffer[curPos + 2]) || !g_ascii_isalnum(buffer[curPos + 3]))
3294 tag_id = __get_tag_info_v223(&buffer[curPos]);
3295 frameLen = buffer[curPos + 4] << 21 | buffer[curPos + 5] << 14 | buffer[curPos + 6] << 7 | buffer[curPos + 7];
3296 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3297 if (curPos + frameLen > pInfo->tagV2Info.tagLen)
3303 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value)
3306 __get_v224_encoding_info(buffer + curPos, frameLen, &encOffset, &encType);
3307 if (frameLen <= encOffset) {
3308 debug_warning(DEBUG, "warning: invalid frame length %d %d", frameLen, encOffset);
3312 curPos += encOffset;
3313 frameLen -= encOffset;
3316 if (encType != AV_ID3V2_UTF16 && encType != AV_ID3V2_UTF16_BE) {
3317 if (tag_id != AV_ID3TAG_COMMENT && tag_id != AV_ID3TAG_UNSYNCLYRICS && tag_id != AV_ID3TAG_SYNCLYRICS) {
3318 debug_msg(RELEASE, "get the new text encoding type");
3319 encType = buffer[curPos - 1];
3323 if (encType > AV_ID3V2_MAX) {
3324 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%u], TAG ID[%d]", encType, tag_id);
3329 case AV_ID3TAG_COMMENT:
3331 case AV_ID3TAG_UNSYNCLYRICS:
3332 if (frameLen <= 3) {
3333 debug_msg(RELEASE, "Description info too small to parse frameLen(%d)", frameLen);
3339 encType = __id3tag_get_text_encoding_v224(&buffer[curPos], &frameLen, encType, &encOffset);
3340 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3341 curPos += encOffset;
3342 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3345 case AV_ID3TAG_SYNCLYRICS:
3346 if (frameLen <= 5) {
3347 debug_msg(RELEASE, "Synchronised lyrics too small to parse frameLen(%d)", frameLen);
3353 encType = __id3tag_get_text_encoding_v224(&buffer[curPos], &frameLen, encType, &encOffset);
3354 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3355 curPos += encOffset;
3356 __id3tag_parse_SYLT(pInfo, buffer, frameLen, charset_array[encType], encType, curPos);
3359 case AV_ID3TAG_PICTURE:
3360 if (extract_artwork)
3361 _mm_file_id3tag_parse_APIC(pInfo, &buffer[curPos], frameLen, charset_array[encType]);
3365 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3369 if (pInfo->tagInfo[tag_id].value)
3370 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
3377 release_characterset_array(charset_array);
3383 void mm_file_id3tag_restore_content_info(AvFileContentInfo *pInfo)
3387 /* for Genre Info */
3388 if (pInfo->tagInfo[AV_ID3TAG_GENRE].value) {
3390 if (!__get_genre_num(pInfo->tagInfo[AV_ID3TAG_GENRE].value, &genre_id)) {
3391 debug_msg(RELEASE, "genre information is not integer [%s]", pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3395 /* If integer, check genre code. */
3396 /* If out of range, set UNKNOWN */
3397 if (genre_id < 0 || genre_id >= GENRE_COUNT)
3398 genre_id = GENRE_COUNT - 1;
3400 debug_msg(RELEASE, "genre information is integer [%d]", genre_id);
3402 g_free(pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3403 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(MpegAudio_Genre[genre_id]);
3405 /* No genre in ID3V2.. So check V1 */
3406 if (pInfo->bV1tagFound == true) {
3407 debug_msg(RELEASE, "Genre: %d", pInfo->genre);
3409 /* If out of range, set UNKNOWN */
3410 if (pInfo->genre > GENRE_COUNT - 1)
3411 pInfo->genre = GENRE_COUNT - 1;
3413 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(MpegAudio_Genre[pInfo->genre]);
3415 debug_msg(RELEASE, "Genre was not Found.");
3420 static void __free_synclyrics(gpointer data)
3422 AvSynclyricsInfo *info = (AvSynclyricsInfo *) data;
3427 mmfile_free(info->lyric_info);
3432 void mm_file_free_synclyrics_list(GList *synclyrics_list)
3434 if (!synclyrics_list)
3437 g_list_free_full(synclyrics_list, __free_synclyrics);