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 int 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 %d 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 - (int)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);
2131 if (g_junk_counter_limit == 0)
2132 g_junk_counter_limit = mmfile_get_int_from_ini(MMFILE_INI_JUNKCNTLIMIT, MMFILE_DEFAULT_JUNKCNTLIMIT);
2134 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)) {
2135 basic_header.size = mmfile_io_be_uint32(basic_header.size);
2136 basic_header.type = mmfile_io_le_uint32(basic_header.type);
2138 if (basic_header.size == 0) {
2139 debug_warning(DEBUG, "header is invalid.");
2140 basic_header.size = readed;
2141 basic_header.type = 0;
2142 chunk_size = basic_header.size;
2144 if ((moov_end != 0) && (moov_end < basic_header.start_offset)) {
2145 debug_msg(DEBUG, "found junk data but moov data already was extracted, so junk counter will be increase: %d", junk_counter);
2148 /* stop the loop for junk case. */
2149 if ((g_junk_counter_limit > 0) && (junk_counter > g_junk_counter_limit)) {
2150 debug_msg(DEBUG, "stop the loop by junk-data checker");
2151 ret = MMFILE_UTIL_FAIL;
2155 } else if (basic_header.size == 1) {
2157 unsigned char temp[BIG_CONTENT_BOX_SIZE_LEN] = {0, };
2158 unsigned long long size = 0;
2160 mmfile_read(fp, (unsigned char *)&temp, BIG_CONTENT_BOX_SIZE_LEN);
2162 for (i = 0; i < BIG_CONTENT_BOX_SIZE_LEN; i++)
2163 size |= (unsigned long long)temp[i] << (BIG_CONTENT_BOX_SIZE_LEN - 1 - i) * BIG_CONTENT_BOX_SIZE_LEN;
2167 chunk_size = basic_header.size;
2171 switch (basic_header.type) {
2172 case FOURCC('m', 'o', 'o', 'v'): {
2173 debug_msg(RELEASE, "MPEG4: [moov] SIZE: [%lld]Byte", chunk_size);
2174 moov_end = basic_header.start_offset + chunk_size;
2177 case FOURCC('u', 'd', 't', 'a'): {
2178 debug_msg(RELEASE, "MPEG4: [udat] SIZE: [%lld]Byte", chunk_size);
2181 /*/////////////////////////////////////////////////////////////// */
2182 /* Extracting Tag Data // */
2183 /*/////////////////////////////////////////////////////////////// */
2184 case FOURCC('t', 'i', 't', 'l'): {
2185 debug_msg(RELEASE, "MPEG4: [titl] SIZE: [%lld]Byte", chunk_size);
2186 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_TITLE);
2189 case FOURCC('d', 's', 'c', 'p'): {
2190 debug_msg(RELEASE, "MPEG4: [dscp] SIZE: [%lld]Byte", chunk_size);
2191 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_CAPTION);
2194 case FOURCC('c', 'p', 'r', 't'): {
2195 debug_msg(RELEASE, "MPEG4: [cprt] SIZE: [%lld]Byte", chunk_size);
2196 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_COPYRIGHT);
2199 case FOURCC('p', 'e', 'r', 'f'): {
2200 debug_msg(RELEASE, "MPEG4: [perf] SIZE: [%lld]Byte", chunk_size);
2201 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_PERFORMER);
2204 case FOURCC('a', 'u', 't', 'h'): {
2205 debug_msg(RELEASE, "MPEG4: [auth] SIZE: [%lld]Byte", chunk_size);
2206 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_AUTHOR);
2209 case FOURCC('g', 'n', 'r', 'e'): {
2210 debug_msg(RELEASE, "MPEG4: [gnre] SIZE: [%lld]Byte", chunk_size);
2211 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_GENRE);
2214 case FOURCC('a', 'l', 'b', 'm'): {
2215 debug_msg(RELEASE, "MPEG4: [albm] SIZE: [%lld]Byte", chunk_size);
2216 GetAlbumFromAlbumTagBox(formatContext, fp, &basic_header);
2219 case FOURCC('y', 'r', 'r', 'c'): {
2220 debug_msg(RELEASE, "MPEG4: [yrrc] SIZE: [%lld]Byte", chunk_size);
2221 GetYearFromYearTagBox(formatContext, fp, &basic_header);
2224 case FOURCC('r', 't', 'n', 'g'): {
2225 debug_msg(RELEASE, "MPEG4: [rtng] SIZE: [%lld]Byte", chunk_size);
2226 GetRatingFromRatingTagBox(formatContext, fp, &basic_header); /* not use */
2229 case FOURCC('c', 'l', 's', 'f'): {
2230 debug_msg(RELEASE, "MPEG4: [clsf] SIZE: [%lld]Byte", chunk_size);
2231 GetClassficationFromClsfTagBox(formatContext, fp, &basic_header);
2234 case FOURCC('k', 'y', 'w', 'd'): {
2235 debug_msg(RELEASE, "MPEG4: [kywd] SIZE: [%lld]Byte", chunk_size);
2236 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2239 case FOURCC('l', 'o', 'c', 'i'): {
2240 debug_msg(RELEASE, "MPEG4: [loci] SIZE: [%lld]Byte", chunk_size);
2241 GetLocationFromLociTagBox(formatContext, fp, &basic_header);
2244 /* Check smta in user data field (moov) to be compatible with android */
2245 case FOURCC('s', 'm', 't', 'a'): {
2246 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte", chunk_size);
2247 GetSAUTInfoFromSMTATagBox(formatContext, fp, &basic_header);
2250 /* Check cdis in user data field (moov) to be compatible with android */
2251 case FOURCC('c', 'd', 'i', 's'): {
2252 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte", chunk_size);
2253 GetValueFromCDISTagBox(formatContext, fp, &basic_header);
2256 /*/////////////////////////////////////////////////////////////// */
2257 /* Extracting ID3 Tag Data // */
2258 /*/////////////////////////////////////////////////////////////// */
2259 case FOURCC('m', 'e', 't', 'a'): {
2260 debug_msg(RELEASE, "MPEG4: [meta] SIZE: [%lld]Byte", chunk_size);
2261 GetTagFromMetaBox(formatContext, fp, &basic_header);
2265 case FOURCC('t', 'r', 'a', 'k'): {
2266 debug_msg(RELEASE, "MPEG4: [trak] SIZE: [%lld]Byte", chunk_size);
2269 case FOURCC('u', 'u', 'i', 'd'): {
2270 unsigned long uuid[4] = {0, };
2272 debug_msg(RELEASE, "MPEG4: [uuid] SIZE: [%lld]Byte", chunk_size);
2274 mmfile_read(fp, (unsigned char *)uuid, sizeof(uuid));
2276 if (mmfile_io_be_uint32(uuid[0]) == 0xffcc8263
2277 && mmfile_io_be_uint32(uuid[1]) == 0xf8554a93
2278 && mmfile_io_be_uint32(uuid[2]) == 0x8814587a
2279 && mmfile_io_be_uint32(uuid[3]) == 0x02521fdd) {
2281 str = (char *)malloc(basic_header.size);
2284 memset(str, 0, basic_header.size);
2285 mmfile_read(fp, (unsigned char *)str, basic_header.size);
2287 /* The block is superseded */
2288 if (strstr(str, "<GSpherical:Spherical>true</GSpherical:Spherical>"))
2289 formatContext->is_360 = 1;
2291 formatContext->is_360 = 0;
2292 /* Image can be stitched even if it is not spherical */
2293 if (formatContext->is_360 == 1) {
2294 if (strstr(str, "<GSpherical:Stitched>true</GSpherical:Stitched>"))
2295 formatContext->stitched = MMFILE_360_STITCHED;
2297 formatContext->stitched = MMFILE_360_NON_STITCHED;
2299 /* Image can be stitched or non-stitched. Usage of some 3rd value is superfluous */
2300 formatContext->stitched = MMFILE_360_NONE;
2304 debug_msg(RELEASE, "Extracting tags from UUID XML string %s", str);
2306 ParseSpatialVideoMetadataFromXMLString(str, formatContext);
2309 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2313 case FOURCC('m', 'd', 'i', 'a'): {
2314 debug_msg(RELEASE, "MPEG4: [mdia] SIZE: [%lld]Byte", chunk_size);
2317 case FOURCC('m', 'i', 'n', 'f'): {
2318 debug_msg(RELEASE, "MPEG4: [minf] SIZE: [%lld]Byte", chunk_size);
2321 case FOURCC('s', 't', 'b', 'l'): {
2322 debug_msg(RELEASE, "MPEG4: [stbl] SIZE: [%lld]Byte", chunk_size);
2325 case FOURCC('s', 't', 's', 'd'): {
2326 debug_msg(RELEASE, "MPEG4: [stsd] SIZE: [%lld]Byte", chunk_size);
2329 case FOURCC('m', 'p', '4', 'a'): {
2330 debug_msg(RELEASE, "MPEG4: [mp4a] SIZE: [%lld]Byte", chunk_size);
2331 GetSA3DInfoFromMP4ATagBox(formatContext, fp, &basic_header);
2332 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2335 case FOURCC('a', 'v', 'c', '1'): {
2336 debug_msg(RELEASE, "MPEG4: [avc1] SIZE: [%lld]Byte (offset: %lld)", chunk_size, basic_header.start_offset);
2337 GetVideoV2MetadataFromAvc1TagBox(formatContext, fp, &basic_header);
2341 debug_msg(RELEASE, "4CC: Not Support [%c%c%c%c]. So skip it. Size [%lld Byte]",
2342 ((char *)&basic_header.type)[0], ((char *)&basic_header.type)[1],
2343 ((char *)&basic_header.type)[2], ((char *)&basic_header.type)[3], chunk_size);
2344 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2349 if (ret == MMFILE_UTIL_FAIL) {
2350 debug_error(DEBUG, "mmfile operation is error");
2355 long long new_pos = mmfile_tell(fp);
2357 if ((moov_end == 0) && (new_pos <= basic_header.start_offset)) {
2358 debug_error(DEBUG, "Wrong position");
2359 ret = MMFILE_UTIL_FAIL;
2362 basic_header.start_offset = new_pos;
2371 static bool __get_genre_num(const char *buffer, int *si)
2373 int len = strlen(buffer);
2374 char *tmp_genre = NULL;
2376 /* Genre format: (###), ### */
2377 if (len > 6 || len == 0)
2382 if (!g_ascii_isdigit(buffer[0]))
2385 if (!g_ascii_isdigit(buffer[1]))
2389 /* Remove bind () if exist */
2390 tmp_genre = g_strdup(buffer);
2391 if (g_str_has_prefix(tmp_genre, "(") && g_str_has_suffix(tmp_genre, ")"))
2394 *si = atoi(tmp_genre);
2400 static bool make_characterset_array(char ***charset_array)
2402 char *locale = MMFileUtilGetLocale();
2404 *charset_array = calloc(AV_ID3V2_MAX, sizeof(char *));
2406 if (*charset_array == NULL) {
2407 debug_error(DEBUG, "calloc failed ");
2413 if (locale != NULL) {
2414 (*charset_array)[AV_ID3V2_ISO_8859] = strdup(locale);
2416 debug_error(DEBUG, "get locale failed");
2417 (*charset_array)[AV_ID3V2_ISO_8859] = NULL;
2420 (*charset_array)[AV_ID3V2_UTF16] = strdup("UCS2");
2421 (*charset_array)[AV_ID3V2_UTF16_BE] = strdup("UTF16-BE");
2422 (*charset_array)[AV_ID3V2_UTF8] = strdup(MMFILE_CODESET_UTF8);
2427 static bool release_characterset_array(char **charset_array)
2431 for (i = 0; i < AV_ID3V2_MAX; i++) {
2432 if (charset_array[i] != NULL) {
2433 free(charset_array[i]);
2434 charset_array[i] = NULL;
2438 if (charset_array != NULL) {
2439 free(charset_array);
2440 charset_array = NULL;
2446 static void init_content_info(AvFileContentInfo *pInfo)
2450 for(i = 0; i < AV_ID3TAG_MAX; i++)
2451 pInfo->tagInfo[i].value = NULL;
2453 pInfo->imageInfo.bURLInfo = false;
2454 pInfo->imageInfo.pImageBuf = NULL;
2455 pInfo->imageInfo.imageLen = 0;
2458 static void __id3tag_skip_newline(unsigned char *pTagVal, int *nTagLen, int *offset)
2460 /* skip newline in text encoding of ID3 tag frame */
2461 while ((NEWLINE_OF_UTF16(pTagVal + *offset) || NEWLINE_OF_UTF16_R(pTagVal + *offset)) && *nTagLen > 4) {
2467 static int __id3tag_get_text_encoding_v222(unsigned char *pTagVal, int offset)
2469 if (pTagVal[offset - 1] == 0x00)
2470 return AV_ID3V2_ISO_8859;
2472 return AV_ID3V2_UTF16;
2475 static int __id3tag_get_text_encoding_v223(unsigned char *pTagVal, int *npTagLen, int nTextEnc, int *offset)
2477 if ((IS_ENCODEDBY_UTF16(pTagVal + *offset) || IS_ENCODEDBY_UTF16_R(pTagVal + *offset)) && *npTagLen > 2) {
2478 __id3tag_skip_newline(pTagVal, npTagLen, offset);
2480 if (IS_ENCODEDBY_UTF16(pTagVal + *offset) && (*npTagLen > 2)) {
2483 return AV_ID3V2_UTF16;
2484 } else if (IS_ENCODEDBY_UTF16_R(pTagVal + *offset) && (*npTagLen > 2)) {
2487 return AV_ID3V2_UTF16_BE;
2488 } else if (IS_ENCODEDBY_UTF16(pTagVal + *offset + 1) && (*npTagLen > 3)) {
2491 return AV_ID3V2_UTF16;
2492 } else if (IS_ENCODEDBY_UTF16_R(pTagVal + *offset + 1) && (*npTagLen > 3)) {
2495 return AV_ID3V2_UTF16_BE;
2497 debug_msg(RELEASE, "id3tag never get here!!");
2498 return nTextEnc; /* default bypass */
2501 while ((pTagVal[*offset] < 0x20) && (*offset < *npTagLen)) { /* text string encoded by ISO-8859-1 */
2505 return AV_ID3V2_ISO_8859;
2509 static int __id3tag_get_text_encoding_v224(unsigned char *pTagVal, int *npTagLen, int nTextEnc, int *offset)
2511 if (nTextEnc == AV_ID3V2_UTF16 || nTextEnc == AV_ID3V2_UTF16_BE) {
2512 __id3tag_skip_newline(pTagVal, npTagLen, offset);
2514 if ((IS_ENCODEDBY_UTF16(pTagVal + *offset) || IS_ENCODEDBY_UTF16_R(pTagVal + *offset)) && *npTagLen > 2) {
2517 return AV_ID3V2_UTF16;
2519 debug_msg(RELEASE, "id3tag never get here!!");
2520 return nTextEnc; /* default bypass */
2522 } else if (nTextEnc == AV_ID3V2_UTF8) {
2523 while (pTagVal[*offset] < 0x20 && (*offset < *npTagLen)) { /* text string encoded by UTF-8 */
2527 return AV_ID3V2_UTF8;
2529 while (pTagVal[*offset] < 0x20 && (*offset < *npTagLen)) { /* text string encoded by ISO-8859-1 */
2533 return AV_ID3V2_ISO_8859;
2537 static void __id3tag_parse_SYLT(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int textEncodingType, int offset)
2541 int copy_start_pos = offset;
2542 AvSynclyricsInfo *synclyrics_info = NULL;
2543 GList *synclyrics_info_list = NULL;
2545 if (nTagLen < MMFILE_SYNC_LYRIC_INFO_MIN_LEN) {
2546 debug_msg(RELEASE, "failed to get Synchronised lyrics Info realCpyFramNum(%d)", nTagLen);
2550 if ((textEncodingType == AV_ID3V2_UTF16) || (textEncodingType == AV_ID3V2_UTF16_BE)) {
2551 debug_warning(DEBUG, "[%d] not implemented", textEncodingType);
2555 for (idx = 0; idx < nTagLen; idx++) {
2556 if (pTagVal[offset + idx] == 0x00) {
2557 synclyrics_info = g_new0(AvSynclyricsInfo, 1);
2558 if (textEncodingType == AV_ID3V2_UTF8)
2559 synclyrics_info->lyric_info = g_memdup2(pTagVal + copy_start_pos, copy_len + 1);
2561 synclyrics_info->lyric_info = mmfile_convert_to_utf8((const char *)&pTagVal[copy_start_pos], copy_len, pCharSet);
2563 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];
2565 copy_start_pos = offset + idx + 1;
2566 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);
2568 synclyrics_info_list = g_list_append(synclyrics_info_list, synclyrics_info);
2573 pInfo->pSyncLyrics = synclyrics_info_list;
2576 static bool __id3tag_parse_PIC_format(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2578 unsigned int idx = 0;
2580 /* get the mime type of Attached PICture, it is text string */
2582 if (pTagVal[*offset] == '\0') {
2583 debug_msg(RELEASE, "The picture format of PIC is not included");
2587 /* init ext variable */
2588 memset(pInfo->imageInfo.imageExt, 0, sizeof(pInfo->imageInfo.imageExt));
2590 while ((idx < MP3_ID3_IMAGE_EXT_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2591 pInfo->imageInfo.imageExt[idx] = pTagVal[idx];
2600 static bool __id3tag_parse_APIC_mimetype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2602 unsigned int idx = 0;
2603 const char *MIME_PRFIX = "image/";
2605 /* get the mime type of Attached PICture, it is text string */
2607 if (pTagVal[*offset] == '\0') {
2608 pInfo->imageInfo.imgMimetypeLen = 0;
2609 debug_msg(RELEASE, "The MIME type of APIC is not included");
2613 /* init mimetype variable */
2614 memset(pInfo->imageInfo.imageMIMEType, 0, sizeof(pInfo->imageInfo.imageMIMEType));
2616 while ((idx < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2617 pInfo->imageInfo.imageMIMEType[idx] = pTagVal[idx];
2620 pInfo->imageInfo.imgMimetypeLen = idx;
2624 if (strncmp(pInfo->imageInfo.imageMIMEType, MIME_PRFIX, strlen(MIME_PRFIX)) != 0) {
2625 pInfo->imageInfo.imgMimetypeLen = 0;
2626 debug_error(DEBUG, "MIME type(%s) is not image", pInfo->imageInfo.imageMIMEType);
2630 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2631 debug_msg(RELEASE, "pTagVal[offset](%d) mimetype is not NULL terminated! realCpyFrameNum - offset(%d)",
2632 pTagVal[*offset], nTagLen - *offset);
2636 (*offset)++;/* end of MIME('\0', 1byte) */
2637 debug_msg(RELEASE, "after scaning Mime type offset(%d) value!", *offset);
2642 static void __id3tag_parse_APIC_pictype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2644 /* get the picture type of Attached PICture, it is 1byte(0xff) */
2646 if (pTagVal[*offset] < MP3_ID3V2_PICTURE_TYPE_MAX)
2647 pInfo->imageInfo.pictureType = pTagVal[*offset];
2649 debug_msg(RELEASE, "APIC image has invalid picture type(0x%x)", pTagVal[*offset]);
2651 (*offset)++;/* PictureType(1byte) */
2652 debug_msg(RELEASE, "after scaning PictureType(%d) offset(%d) value!", pInfo->imageInfo.pictureType, *offset);
2655 static void __id3tag_parse_APIC_desc(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int *offset)
2657 /* get the description of Attached PICture, it is text string */
2660 unsigned int tag_len = 0;
2661 char *tmp_desc = NULL;
2663 if (pTagVal[*offset] == 0x0) {
2664 debug_msg(RELEASE, "The description of APIC is not included!!!");
2669 if (pTagVal[*offset + idx] == '\0') {
2670 if (nTagLen < (*offset + idx)) {
2671 debug_error(DEBUG, "End of APIC Tag %d %d %d", nTagLen, *offset, idx);
2674 /* check end of image description */
2675 if ((pTagVal[*offset + idx + 1] == gTagJPEGHeader[0]) ||
2676 (pTagVal[*offset + idx + 1] == gTagPNGHeader[0])) {
2677 debug_msg(RELEASE, "length of description (%d)", idx);
2684 tag_len = idx + 1; /* length of description + '\0' */
2686 tmp_desc = g_memdup2(pTagVal + *offset, tag_len);
2688 /* convert description */
2689 pInfo->imageInfo.imageDescription = mmfile_convert_to_utf8(tmp_desc, tag_len, pCharSet);
2690 mmfile_free(tmp_desc);
2691 debug_msg(RELEASE, "new_desc %s", pInfo->imageInfo.imageDescription);
2694 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2695 debug_msg(RELEASE, "pTagVal[offset](%d) description is not NULL terminated! realCpyFrameNum - offset(%d)",
2696 pTagVal[*offset], nTagLen - *offset);
2699 (*offset)++; /* end of desceription(1byte) */
2702 static void __id3tag_parse_APIC_picture(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2704 /* get the picture of Attached PICture, it is binary data */
2706 /* some content has useless '\0' in front of picture data */
2707 while (pTagVal[*offset] == '\0') {
2711 debug_msg(RELEASE, "after scaning APIC description offset(%d) value!", *offset);
2713 if (nTagLen <= *offset) {
2714 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - offset(%d)", nTagLen, *offset);
2718 pInfo->imageInfo.imageLen = nTagLen - *offset;
2719 pInfo->imageInfo.pImageBuf = g_memdup2(pTagVal + *offset, pInfo->imageInfo.imageLen);
2721 /* if mimetype is "-->", image date has an URL */
2722 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
2723 pInfo->imageInfo.bURLInfo = true;
2726 static bool _mm_file_id3tag_parse_PIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2728 /* current position to read pTagVal */
2731 debug_fenter(RELEASE);
2733 if (!__id3tag_parse_PIC_format(pInfo, pTagVal, &offset)) {
2734 debug_msg(RELEASE, "PIC is not valid");
2738 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2739 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2740 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2742 debug_fleave(RELEASE);
2747 static bool _mm_file_id3tag_parse_APIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2751 debug_fenter(RELEASE);
2753 if (!__id3tag_parse_APIC_mimetype(pInfo, pTagVal, nTagLen, &offset)) {
2754 debug_msg(RELEASE, "APIC is not valid");
2758 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2759 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2760 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2762 debug_fleave(RELEASE);
2767 static char * __id3tag_get_v110(const char *buf, ssize_t len, const char *locale)
2769 char *tmp_str = NULL;
2771 mm_file_retv_if_fails(buf, NULL);
2772 mm_file_retv_if_fails(locale, NULL);
2774 tmp_str = mmfile_convert_to_utf8(buf, len, locale);
2776 /* ID3v1 tag need check length or space. */
2777 if (tmp_str && (strlen(tmp_str) == 0 || g_ascii_isspace(tmp_str[0])))
2778 mmfile_free(tmp_str);
2783 bool mm_file_id3tag_parse_v110(AvFileContentInfo *pInfo, unsigned char *buffer)
2785 const char *locale = MMFileUtilGetLocale();
2787 debug_msg(RELEASE, "ID3tag v110--------------------------------------------------------------");
2789 if (!pInfo->tagInfo[AV_ID3TAG_TITLE].value) {
2790 pInfo->tagInfo[AV_ID3TAG_TITLE].value = __id3tag_get_v110((const char *)&buffer[3], MP3_ID3_TITLE_LENGTH, locale);
2792 debug_msg(RELEASE, "pInfo->pTitle returned =(%s)", pInfo->tagInfo[AV_ID3TAG_TITLE].value);
2795 if (!pInfo->tagInfo[AV_ID3TAG_ARTIST].value) {
2796 pInfo->tagInfo[AV_ID3TAG_ARTIST].value = __id3tag_get_v110((const char *)&buffer[33], MP3_ID3_ARTIST_LENGTH, locale);
2798 debug_msg(RELEASE, "pInfo->pArtist returned =(%s)", pInfo->tagInfo[AV_ID3TAG_ARTIST].value);
2801 if (!pInfo->tagInfo[AV_ID3TAG_ALBUM].value) {
2802 pInfo->tagInfo[AV_ID3TAG_ALBUM].value = __id3tag_get_v110((const char *)&buffer[63], MP3_ID3_ALBUM_LENGTH, locale);
2804 debug_msg(RELEASE, "pInfo->pAlbum returned =(%s)", pInfo->tagInfo[AV_ID3TAG_ALBUM].value);
2807 if (!pInfo->tagInfo[AV_ID3TAG_YEAR].value) {
2808 pInfo->tagInfo[AV_ID3TAG_YEAR].value = __id3tag_get_v110((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, locale);
2810 debug_msg(RELEASE, "pInfo->pYear returned =(%s)", pInfo->tagInfo[AV_ID3TAG_YEAR].value);
2813 if (!pInfo->tagInfo[AV_ID3TAG_COMMENT].value) {
2814 pInfo->tagInfo[AV_ID3TAG_COMMENT].value = __id3tag_get_v110((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, locale);
2816 debug_msg(RELEASE, "pInfo->pComment returned =(%s)", pInfo->tagInfo[AV_ID3TAG_COMMENT].value);
2819 if (!pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value) {
2820 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value = g_malloc0(ID3TAG_V110_TRACK_NUM_DIGIT);
2821 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value[ID3TAG_V110_TRACK_NUM_DIGIT - 1] = 0;
2822 snprintf(pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value, ID3TAG_V110_TRACK_NUM_DIGIT, "%04d", (int)buffer[126]);
2824 debug_msg(RELEASE, "pInfo->pTrackNum returned =(%s)", pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value);
2827 /*ID3V2 genre is stored in pInfo->tagInfo[AV_ID3TAG_GENRE].value */
2828 /*pInfo->genre is used when ID3V2 genre is invalid, or empty. */
2829 pInfo->genre = buffer[127];
2830 debug_msg(RELEASE, "pInfo->genre returned genre number (%d)", pInfo->genre);
2835 static AvID3TagList __get_tag_info_v222(const unsigned char *tag)
2841 n += tag[i++] - 'A' + 1;
2844 n += tag[2]; //num, char mixted
2846 for (i = 0; i < ID3TAG_NUM_V22; i++) {
2847 if (n == tag_info_v22[i].int_name) {
2848 return tag_info_v22[i].tag;
2852 debug_msg(RELEASE, "(%s) This Frame ID currently not Supports!!", tag);
2854 return AV_ID3TAG_MAX;
2857 static AvID3TagList __get_tag_info_v223(const unsigned char *tag)
2863 n += tag[i++] - 'A' + 1;
2866 n += tag[3]; //num, char mixted
2868 for (i = 0; i < ID3TAG_NUM_V23; i++) {
2869 if (n == tag_info_v23[i].int_name) {
2870 return tag_info_v23[i].tag;
2874 debug_msg(RELEASE, "(%s) This Frame ID currently not Supports!!", tag);
2876 return AV_ID3TAG_MAX;
2880 bool mm_file_id3tag_parse_v222(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
2882 int curPos = MP3_TAGv2_HEADER_LEN;
2886 char **charset_array = NULL;
2887 AvID3TagList tag_id = AV_ID3TAG_MAX;
2889 if (pInfo->tagV2Info.tagLen <= 0)
2892 make_characterset_array(&charset_array);
2893 init_content_info(pInfo);
2895 debug_msg(RELEASE, "ID3tag v222--------------------------------------------------------------");
2897 while (curPos + MP3_TAGv2_22_TXT_HEADER_LEN < pInfo->tagV2Info.tagLen) {
2898 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) || !g_ascii_isalnum(buffer[curPos + 2]))
2901 tag_id = __get_tag_info_v222(&buffer[curPos]);
2902 frameLen = buffer[curPos + 3] << 16 | buffer[curPos + 4] << 8 | buffer[curPos + 5];
2903 curPos += MP3_TAGv2_22_TXT_HEADER_LEN;
2904 if (curPos + frameLen > pInfo->tagV2Info.tagLen)
2910 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value)
2913 if (buffer[curPos] == 0x00) {
2915 encType = AV_ID3V2_ISO_8859;
2916 } else if (buffer[curPos] == 0x01) {
2918 encType = AV_ID3V2_UTF16;
2921 /*in order to deliver valid string to MP */
2922 while ((buffer[curPos + encOffset] < 0x20) && (encOffset < frameLen))
2925 if (frameLen == encOffset) {
2926 debug_warning(DEBUG, "warning: invalid frame length %d %d", frameLen, encOffset);
2930 curPos += encOffset;
2931 frameLen -= encOffset;
2934 case AV_ID3TAG_COMMENT:
2936 https://id3.org/id3v2-00
2938 Frame size $xx xx xx
2941 Short content description <textstring> $00 (00)
2942 The actual text <textstring>
2944 if (frameLen <= 4) {
2945 debug_msg(RELEASE, "Too small to parse frameLen(%d)", frameLen);
2949 if (buffer[curPos + 4] > 0x20 && (buffer[curPos + 3] == 0x00 || buffer[curPos + 3] == 0x01)) {
2950 encType = __id3tag_get_text_encoding_v222(&buffer[curPos], 4);
2951 /*skip language data! */
2954 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
2956 debug_msg(RELEASE, "Failed to get tag: frameLen(%d)", frameLen);
2961 case AV_ID3TAG_PICTURE:
2962 if (extract_artwork)
2963 _mm_file_id3tag_parse_PIC(pInfo, &buffer[curPos], frameLen, charset_array[encType]);
2967 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
2971 if (pInfo->tagInfo[tag_id].value)
2972 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
2979 release_characterset_array(charset_array);
2984 static void __get_v223_encoding_info(const unsigned char *buffer,
2991 if (!buffer || !offset || !type)
2994 if (IS_ENCODEDBY_UTF16(buffer)) {
2996 *type = AV_ID3V2_UTF16;
2999 if (IS_ENCODEDBY_UTF16_R(buffer)) {
3001 *type = AV_ID3V2_UTF16_BE;
3004 if (IS_ENCODEDBY_UTF16(buffer + 1)) {
3006 *type = AV_ID3V2_UTF16;
3009 if (IS_ENCODEDBY_UTF16_R(buffer + 1)) {
3011 *type = AV_ID3V2_UTF16_BE;
3015 if (buffer[0] == 0x00) {
3018 while ((buffer[_offset] < 0x20) && (_offset < length))
3022 *type = AV_ID3V2_ISO_8859;
3025 bool mm_file_id3tag_parse_v223(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
3027 int extendedHeaderLen = 0;
3029 int curPos = MP3_TAGv2_HEADER_LEN;
3031 unsigned int encType = 0;
3032 char **charset_array = NULL;
3033 AvID3TagList tag_id = AV_ID3TAG_MAX;
3034 char *lang_info = NULL;
3036 if (pInfo->tagV2Info.tagLen <= 0)
3039 make_characterset_array(&charset_array);
3040 init_content_info(pInfo);
3042 debug_msg(RELEASE, "ID3tag v223--------------------------------------------------------------");
3043 if (buffer[5] & 0x40) {
3044 /* if extended header exists, skip it*/
3045 extendedHeaderLen = buffer[10] << 21 | buffer[11] << 14 | buffer[12] << 7 | buffer[13];
3046 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d", extendedHeaderLen);
3047 if (extendedHeaderLen > pInfo->tagV2Info.tagLen - MP3_TAGv2_HEADER_LEN) {
3048 debug_error(DEBUG, "extended header too long.");
3050 curPos += extendedHeaderLen;
3055 while (curPos + MP3_TAGv2_23_TXT_HEADER_LEN < pInfo->tagV2Info.tagLen) {
3056 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) ||
3057 !g_ascii_isalnum(buffer[curPos + 2]) || !g_ascii_isalnum(buffer[curPos + 3]))
3060 tag_id = __get_tag_info_v223(&buffer[curPos]);
3061 frameLen = buffer[curPos + 4] << 24 | buffer[curPos + 5] << 16 | buffer[curPos + 6] << 8 | buffer[curPos + 7];
3062 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3063 if (curPos + frameLen > pInfo->tagV2Info.tagLen)
3069 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value)
3072 __get_v223_encoding_info(buffer + curPos, frameLen, &encOffset, &encType);
3073 if (frameLen <= encOffset) {
3074 debug_warning(DEBUG, "warning: invalid frame length %d %d", frameLen, encOffset);
3078 curPos += encOffset;
3079 frameLen -= encOffset;
3082 if (encType != AV_ID3V2_UTF16 && encType != AV_ID3V2_UTF16_BE) {
3083 if (tag_id != AV_ID3TAG_COMMENT && tag_id != AV_ID3TAG_UNSYNCLYRICS && tag_id != AV_ID3TAG_SYNCLYRICS) {
3084 debug_msg(RELEASE, "get the new text encoding type");
3085 encType = buffer[curPos - 1];
3089 if (encType > AV_ID3V2_MAX) {
3090 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%u], TAG ID[%d]", encType, tag_id);
3095 case AV_ID3TAG_COMMENT:
3096 if (frameLen <= 3) {
3097 debug_msg(RELEASE, "Description info too small to parse frameLen(%d)", frameLen);
3103 if (buffer[curPos] != 0x00 && buffer[curPos] != 0xFF && buffer[curPos] != 0xFE) {
3104 debug_msg(RELEASE, "failed to get Comment: frameLen(%d)", frameLen);
3107 encType = __id3tag_get_text_encoding_v223(&buffer[curPos], &frameLen, encType, &encOffset);
3108 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3109 curPos += encOffset;
3110 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3113 case AV_ID3TAG_SYNCLYRICS:
3114 if (frameLen <= 5) {
3115 debug_msg(RELEASE, "Synchronised lyrics too small to parse frameLen(%d)", frameLen);
3121 if (buffer[curPos] != 0x00 && buffer[curPos] != 0xFF && buffer[curPos] != 0xFE) {
3122 debug_msg(RELEASE, "failed to get Synchronised lyrics Info curPos(%d), frameLen(%d)", curPos, frameLen);
3124 encType = __id3tag_get_text_encoding_v223(&buffer[curPos], &frameLen, encType, &encOffset);
3125 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3126 curPos += encOffset;
3127 __id3tag_parse_SYLT(pInfo, buffer, frameLen, charset_array[encType], encType, curPos);
3130 case AV_ID3TAG_UNSYNCLYRICS:
3131 lang_info = strndup((const char *)&buffer[curPos], 3);
3133 if (frameLen <= 3) {
3134 debug_msg(RELEASE, "Unsynchronised lyrics too small to parse frameLen(%d)", frameLen);
3135 mmfile_free(lang_info);
3141 if (buffer[curPos] != 0x00 && buffer[curPos] != 0xFF && buffer[curPos] != 0xFE) {
3142 debug_msg(RELEASE, "failed to get Unsynchronised lyrics Info curPos(%d), frameLen(%d)", curPos, frameLen);
3144 encType = __id3tag_get_text_encoding_v223(&buffer[curPos], &frameLen, encType, &encOffset);
3146 char *char_set = NULL;
3148 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3150 if (encType == AV_ID3V2_ISO_8859) {
3151 if (lang_info != NULL && !g_ascii_strcasecmp(lang_info, "KOR")) {
3152 char_set = strdup("EUC-KR");
3154 char_set = mmfile_get_charset((const char *)&buffer[curPos]);
3156 mmfile_free(lang_info);
3159 curPos += encOffset;
3162 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3164 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, char_set);
3165 mmfile_free(char_set);
3167 mmfile_free(lang_info);
3170 case AV_ID3TAG_PICTURE:
3171 if (extract_artwork)
3172 _mm_file_id3tag_parse_APIC(pInfo, &buffer[curPos], frameLen, charset_array[encType]);
3176 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3180 if (pInfo->tagInfo[tag_id].value)
3181 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
3188 release_characterset_array(charset_array);
3193 static void __get_v224_encoding_info(const unsigned char *buffer,
3200 if (!buffer || !offset || !type)
3203 /*in case of UTF 16 encoding */
3204 /*buffer+(position-length) data should '0x01' but in order to expansion, we don't accurately check the value. */
3205 if (IS_ENCODEDBY_UTF16(buffer)) {
3207 *type = AV_ID3V2_UTF16;
3211 if (IS_ENCODEDBY_UTF16_R(buffer)) {
3213 *type = AV_ID3V2_UTF16_BE;
3217 if (IS_ENCODEDBY_UTF16(buffer + 1)) {
3219 *type = AV_ID3V2_UTF16;
3222 if (IS_ENCODEDBY_UTF16_R(buffer + 1)) {
3224 *type = AV_ID3V2_UTF16_BE;
3228 /*in case of UTF-16 BE encoding */
3229 if (buffer[0] == 0x02) {
3231 while ((buffer[_offset] == '\0') && (_offset < length))
3232 _offset++;/*null skip! */
3234 *type = AV_ID3V2_UTF16_BE;
3238 /*in case of UTF8 encoding */
3239 if (buffer[0] == 0x03) {
3241 while ((buffer[_offset] == '\0') && (_offset < length))
3242 _offset++;/*null skip! */
3244 *type = AV_ID3V2_UTF8;
3247 /*in case of ISO-8859-1 encoding */
3248 /*buffer+(position-length) data should 0x00 but in order to expansion, we don't accurately check the value. */
3250 while ((buffer[_offset] < 0x20) && (_offset < length))
3251 _offset++;/*less than 0x20 value skip! */
3253 *type = AV_ID3V2_ISO_8859;
3256 bool mm_file_id3tag_parse_v224(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
3258 int extendedHeaderLen = 0;
3260 int curPos = MP3_TAGv2_HEADER_LEN;
3262 unsigned int encType = 0;
3263 char **charset_array = NULL;
3264 AvID3TagList tag_id = AV_ID3TAG_MAX;
3266 if (pInfo->tagV2Info.tagLen <= 0)
3269 make_characterset_array(&charset_array);
3270 init_content_info(pInfo);
3272 debug_msg(RELEASE, "ID3tag v224--------------------------------------------------------------");
3273 if (buffer[5] & 0x40) {
3274 /* if extended header exists, skip it*/
3275 extendedHeaderLen = buffer[10] << 21 | buffer[11] << 14 | buffer[12] << 7 | buffer[13];
3276 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d", extendedHeaderLen);
3277 if (extendedHeaderLen > pInfo->tagV2Info.tagLen - MP3_TAGv2_HEADER_LEN) {
3278 debug_error(DEBUG, "extended header too long.");
3280 curPos += extendedHeaderLen;
3284 while (curPos + MP3_TAGv2_23_TXT_HEADER_LEN < pInfo->tagV2Info.tagLen) {
3285 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) ||
3286 !g_ascii_isalnum(buffer[curPos + 2]) || !g_ascii_isalnum(buffer[curPos + 3]))
3289 tag_id = __get_tag_info_v223(&buffer[curPos]);
3290 frameLen = buffer[curPos + 4] << 21 | buffer[curPos + 5] << 14 | buffer[curPos + 6] << 7 | buffer[curPos + 7];
3291 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3292 if (curPos + frameLen > pInfo->tagV2Info.tagLen)
3298 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value)
3301 __get_v224_encoding_info(buffer + curPos, frameLen, &encOffset, &encType);
3302 if (frameLen <= encOffset) {
3303 debug_warning(DEBUG, "warning: invalid frame length %d %d", frameLen, encOffset);
3307 curPos += encOffset;
3308 frameLen -= encOffset;
3311 if (encType != AV_ID3V2_UTF16 && encType != AV_ID3V2_UTF16_BE) {
3312 if (tag_id != AV_ID3TAG_COMMENT && tag_id != AV_ID3TAG_UNSYNCLYRICS && tag_id != AV_ID3TAG_SYNCLYRICS) {
3313 debug_msg(RELEASE, "get the new text encoding type");
3314 encType = buffer[curPos - 1];
3318 if (encType > AV_ID3V2_MAX) {
3319 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%u], TAG ID[%d]", encType, tag_id);
3324 case AV_ID3TAG_COMMENT:
3326 case AV_ID3TAG_UNSYNCLYRICS:
3327 if (frameLen <= 3) {
3328 debug_msg(RELEASE, "Description info too small to parse frameLen(%d)", frameLen);
3334 encType = __id3tag_get_text_encoding_v224(&buffer[curPos], &frameLen, encType, &encOffset);
3335 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3336 curPos += encOffset;
3337 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3340 case AV_ID3TAG_SYNCLYRICS:
3341 if (frameLen <= 5) {
3342 debug_msg(RELEASE, "Synchronised lyrics too small to parse frameLen(%d)", frameLen);
3348 encType = __id3tag_get_text_encoding_v224(&buffer[curPos], &frameLen, encType, &encOffset);
3349 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3350 curPos += encOffset;
3351 __id3tag_parse_SYLT(pInfo, buffer, frameLen, charset_array[encType], encType, curPos);
3354 case AV_ID3TAG_PICTURE:
3355 if (extract_artwork)
3356 _mm_file_id3tag_parse_APIC(pInfo, &buffer[curPos], frameLen, charset_array[encType]);
3360 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3364 if (pInfo->tagInfo[tag_id].value)
3365 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
3372 release_characterset_array(charset_array);
3378 void mm_file_id3tag_restore_content_info(AvFileContentInfo *pInfo)
3382 /* for Genre Info */
3383 if (pInfo->tagInfo[AV_ID3TAG_GENRE].value) {
3385 if (!__get_genre_num(pInfo->tagInfo[AV_ID3TAG_GENRE].value, &genre_id)) {
3386 debug_log(RELEASE, "genre information is not integer [%s]", pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3390 /* If integer, check genre code. */
3391 /* If out of range, set UNKNOWN */
3392 if (genre_id < 0 || genre_id >= GENRE_COUNT)
3393 genre_id = GENRE_COUNT - 1;
3395 debug_msg(RELEASE, "genre information is integer [%d]", genre_id);
3397 g_free(pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3398 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(MpegAudio_Genre[genre_id]);
3400 /* No genre in ID3V2.. So check V1 */
3401 if (pInfo->bV1tagFound == true) {
3402 debug_msg(RELEASE, "Genre: %d", pInfo->genre);
3404 /* If out of range, set UNKNOWN */
3405 if (pInfo->genre > GENRE_COUNT - 1)
3406 pInfo->genre = GENRE_COUNT - 1;
3408 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(MpegAudio_Genre[pInfo->genre]);
3410 debug_msg(RELEASE, "Genre was not Found.");
3415 static void __free_synclyrics(gpointer data)
3417 AvSynclyricsInfo *info = (AvSynclyricsInfo *) data;
3422 mmfile_free(info->lyric_info);
3427 void mm_file_free_synclyrics_list(GList *synclyrics_list)
3429 if (!synclyrics_list)
3432 g_list_free_full(synclyrics_list, __free_synclyrics);