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 {"TEN", 20718, AV_ID3TAG_ENCBY},
261 {"WXX", 24408, AV_ID3TAG_URL},
262 {"TCR", 20658, AV_ID3TAG_COPYRIGHT},
263 {"TOA", 21025, AV_ID3TAG_ORIGIN_ARTIST},
264 {"TCM", 20653, AV_ID3TAG_COMPOSER},
265 {"TRD", 21124, AV_ID3TAG_RECDATE},
268 static MMFILE_ID3TAG_INFO tag_info_v23[ID3TAG_NUM_V23] = {
269 {"TIT2", 665266, AV_ID3TAG_TITLE},
270 {"TPE1", 671953, AV_ID3TAG_ARTIST},
271 {"TPE2", 671954, AV_ID3TAG_ALBUM_ARTIST},
272 {"TPE3", 671955, AV_ID3TAG_CONDUCTOR},
273 {"TALB", 656834, AV_ID3TAG_ALBUM},
274 {"TYER", 681202, AV_ID3TAG_YEAR},
275 {"COMM", 114157, AV_ID3TAG_COMMENT},
276 {"TCON", 658990, AV_ID3TAG_GENRE},
277 {"TRCK", 673963, AV_ID3TAG_TRACKNUM},
278 {"APIC", 49507, AV_ID3TAG_PICTURE},
279 {"TENC", 660995, AV_ID3TAG_ENCBY},
280 {"WXXX", 779096, AV_ID3TAG_URL},
281 {"TCOP", 658992, AV_ID3TAG_COPYRIGHT},
282 {"TOPE", 671301, AV_ID3TAG_ORIGIN_ARTIST},
283 {"TCOM", 658989, AV_ID3TAG_COMPOSER},
284 {"TRDA", 660097, AV_ID3TAG_RECDATE}, /*Recdate for 2.3*/
285 {"USLT", 708052, AV_ID3TAG_UNSYNCLYRICS},
286 {"SYLT", 648660, AV_ID3TAG_SYNCLYRICS},
287 {"TPOS", 672307, AV_ID3TAG_PART_OF_SET},
288 {"TIT1", 665265, AV_ID3TAG_CONTENT_GROUP}, /*Content Group for 2.4*/
289 {"TDRC", 660099, AV_ID3TAG_RECDATE}, /*Recdate for 2.4*/
292 static int g_junk_counter_limit = 0;
294 static int GetStringFromTextTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header, eMMFILE_3GP_TEXT_TAG eTag)
296 int ret = MMFILE_UTIL_FAIL; /*fail*/
297 MMFILE_3GP_TEXT_TAGBOX texttag = {0, };
300 char *temp_text = NULL;
302 if (!formatContext || !fp || !basic_header) {
303 debug_error(DEBUG, "invalid param");
304 return MMFILE_UTIL_FAIL;
307 textBytes = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_TEXT_TAGBOX_LEN;
309 readed = mmfile_read(fp, (unsigned char *)&texttag, MMFILE_3GP_TEXT_TAGBOX_LEN);
310 if (readed != MMFILE_3GP_TEXT_TAGBOX_LEN) {
311 debug_error(DEBUG, "read text tag header fail");
312 ret = MMFILE_UTIL_FAIL;
316 if (textBytes <= 1) { /* there exist only 00(null) */
317 debug_error(DEBUG, "Text is NULL");
321 texttag.text = g_malloc0(textBytes);
323 readed = mmfile_read(fp, (unsigned char *)texttag.text, textBytes);
324 if (readed != textBytes) {
325 debug_error(DEBUG, "read text fail");
326 ret = MMFILE_UTIL_FAIL;
331 if ((texttag.text[0] == 0xFE) && (texttag.text[1] == 0xFF)) {
332 /* this char is UTF-16 */
333 temp_text = mmfile_convert_to_utf8((const char *)&texttag.text[2], readed - 2, MMFILE_CODESET_UTF16);
335 temp_text = g_strdup((const char *)texttag.text);
339 case eMMFILE_3GP_TAG_TITLE: {
340 if (!formatContext->title) {
341 formatContext->title = g_strdup(temp_text);
345 case eMMFILE_3GP_TAG_CAPTION: {
346 if (!formatContext->description) {
347 formatContext->description = g_strdup(temp_text);
351 case eMMFILE_3GP_TAG_COPYRIGHT: {
352 if (!formatContext->copyright) {
353 formatContext->copyright = g_strdup(temp_text);
357 case eMMFILE_3GP_TAG_PERFORMER: {
358 if (!formatContext->artist) {
359 formatContext->artist = g_strdup(temp_text);
363 case eMMFILE_3GP_TAG_AUTHOR: {
364 if (!formatContext->author) {
365 formatContext->author = g_strdup(temp_text);
369 case eMMFILE_3GP_TAG_GENRE: {
370 if (!formatContext->genre) {
371 formatContext->genre = g_strdup(temp_text);
376 debug_warning(DEBUG, "Not supported Text Tag type[%d]", eTag);
381 mmfile_free(texttag.text);
383 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
385 mmfile_free(temp_text);
387 return MMFILE_UTIL_SUCCESS;
390 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
391 mmfile_free(texttag.text);
396 static int GetYearFromYearTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
398 #define MAX_YEAR_BUFFER 10
400 MMFILE_3GP_YEAR_TAGBOX yearbox = {0, };
401 char temp_year[MAX_YEAR_BUFFER] = {0, };
403 if (!formatContext || !fp || !basic_header) {
404 debug_error(DEBUG, "invalid param");
405 return MMFILE_UTIL_FAIL;
408 readed = mmfile_read(fp, (unsigned char *)&yearbox, MMFILE_3GP_YEAR_TAGBOX_LEN);
409 if (readed != MMFILE_3GP_YEAR_TAGBOX_LEN) {
410 debug_error(DEBUG, "read yeartag header fail");
414 if (!formatContext->year) {
415 yearbox.year = mmfile_io_be_int16(yearbox.year);
416 snprintf(temp_year, MAX_YEAR_BUFFER, "%d", yearbox.year);
417 temp_year[MAX_YEAR_BUFFER - 1] = '\0';
418 formatContext->year = g_strdup((const char *)temp_year);
421 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
422 return MMFILE_UTIL_SUCCESS;
425 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
427 return MMFILE_UTIL_FAIL;
430 static int GetAlbumFromAlbumTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
432 int albumTitleLen = 0;
433 char *temp_text = NULL;
436 MMFILE_3GP_ALBUM_TAGBOX albumbox = {0, };
438 if (!formatContext || !fp || !basic_header) {
439 debug_error(DEBUG, "invalid param");
440 return MMFILE_UTIL_FAIL;
443 readed = mmfile_read(fp, (unsigned char *)&albumbox, MMFILE_3GP_ALBUM_TAGBOX_LEN);
444 if (readed != MMFILE_3GP_ALBUM_TAGBOX_LEN) {
445 debug_error(DEBUG, "read albumtag header fail");
449 albumTitleLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_ALBUM_TAGBOX_LEN - 1; /* 1: track number */
450 if (albumTitleLen > 1) { /* there exist only 00(null) */
451 debug_msg(RELEASE, "albumTitleLen=%d", albumTitleLen);
453 albumbox.albumtile = g_malloc0(albumTitleLen + 1); /* 1: for null char */
455 readed = mmfile_read(fp, (unsigned char *)albumbox.albumtile, albumTitleLen);
456 if (readed != albumTitleLen) {
457 debug_error(DEBUG, "read album title fail");
461 if (albumbox.albumtile[albumTitleLen - 1] == '\0') { /* there exist track number */
465 readed = mmfile_read(fp, (unsigned char *)&(albumbox.albumtile[albumTitleLen]), 1);
467 debug_error(DEBUG, "read album title fail");
470 albumbox.albumtile[albumTitleLen] = '\0';
474 if ((albumbox.albumtile[0] == 0xFE) && (albumbox.albumtile[1] == 0xFF)) {
475 /* this char is UTF-16 */
476 temp_text = mmfile_convert_to_utf8((const char *)&albumbox.albumtile[2], readed - 2, MMFILE_CODESET_UTF16);
478 temp_text = g_strdup((const char *)albumbox.albumtile);
481 if (!formatContext->album)
482 formatContext->album = temp_text;
484 mmfile_free(temp_text);
486 debug_msg(RELEASE, "formatContext->album=%s, strlen=%zu", formatContext->album, strlen(formatContext->album));
490 readed = mmfile_read(fp, (unsigned char *)&albumbox.trackNumber, 1);
492 debug_error(DEBUG, "read track number fail");
496 if (formatContext->tagTrackNum == 0) {
497 char tracknum[10] = {0, };
498 snprintf(tracknum, 10, "%d", albumbox.trackNumber);
500 formatContext->tagTrackNum = g_strdup((const char *)tracknum);
504 mmfile_free(albumbox.albumtile);
505 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
507 return MMFILE_UTIL_SUCCESS;
510 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
511 mmfile_free(albumbox.albumtile);
513 return MMFILE_UTIL_FAIL;
516 static int GetRatingFromRatingTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
519 int ratinginfoLen = 0;
520 char *temp_text = NULL;
522 MMFILE_3GP_RATING_TAGBOX ratingTag = {0, };
524 if (!formatContext || !fp || !basic_header) {
525 debug_error(DEBUG, "invalid param");
526 return MMFILE_UTIL_FAIL;
529 readed = mmfile_read(fp, (unsigned char *)&ratingTag, MMFILE_3GP_RATING_TAGBOX_LEN);
530 if (readed != MMFILE_3GP_RATING_TAGBOX_LEN) {
531 debug_error(DEBUG, "read rating tag header fail");
535 ratinginfoLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_RATING_TAGBOX_LEN;
537 if (ratinginfoLen == 1) {
538 debug_error(DEBUG, "Rating Text is NULL");
542 ratingTag.ratingInfo = g_malloc0(ratinginfoLen);
544 readed = mmfile_read(fp, (unsigned char *)ratingTag.ratingInfo, ratinginfoLen);
545 if (readed != ratinginfoLen) {
546 debug_error(DEBUG, "read rating info string fail");
551 if ((ratingTag.ratingInfo[0] == 0xFE) && (ratingTag.ratingInfo[1] == 0xFF)) {
552 /* this char is UTF-16 */
553 temp_text = mmfile_convert_to_utf8((const char *)&ratingTag.ratingInfo[2], readed - 2, MMFILE_CODESET_UTF16);
555 temp_text = g_strdup((const char *)ratingTag.ratingInfo);
558 if (!formatContext->rating) {
559 formatContext->rating = temp_text;
561 mmfile_free(temp_text);
564 mmfile_free(ratingTag.ratingInfo);
565 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
567 return MMFILE_UTIL_SUCCESS;
570 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
571 mmfile_free(ratingTag.ratingInfo);
573 return MMFILE_UTIL_FAIL;
577 static int GetClassficationFromClsfTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
579 int classinfoLen = 0;
581 char *temp_text = NULL;
582 MMFILE_3GP_CLASSIFICATION_TAGBOX classTag = {0, };
584 if (!formatContext || !fp || !basic_header) {
585 debug_error(DEBUG, "invalid param");
586 return MMFILE_UTIL_FAIL;
589 readed = mmfile_read(fp, (unsigned char *)&classTag, MMFILE_3GP_CLASS_TAGBOX_LEN);
590 if (readed != MMFILE_3GP_CLASS_TAGBOX_LEN) {
591 debug_error(DEBUG, "read classification tag header fail");
596 classinfoLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_CLASS_TAGBOX_LEN;
597 if (classinfoLen == 1) {
598 debug_error(DEBUG, "Classification Text is NULL");
602 classTag.classificationInfo = g_malloc0(classinfoLen);
604 readed = mmfile_read(fp, (unsigned char *)classTag.classificationInfo, classinfoLen);
605 if (readed != classinfoLen) {
606 debug_error(DEBUG, "read class info string fail");
611 if ((classTag.classificationInfo[0] == 0xFE) && (classTag.classificationInfo[1] == 0xFF)) {
612 /* this char is UTF-16 */
613 temp_text = mmfile_convert_to_utf8((const char *)&classTag.classificationInfo[2], readed - 2, MMFILE_CODESET_UTF16);
615 temp_text = g_strdup((const char *)classTag.classificationInfo);
618 if (!formatContext->classification) {
619 formatContext->classification = temp_text;
621 mmfile_free(temp_text);
624 mmfile_free(classTag.classificationInfo);
625 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
627 return MMFILE_UTIL_SUCCESS;
630 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
631 mmfile_free(classTag.classificationInfo);
633 return MMFILE_UTIL_FAIL;
637 * The Location Information box
638 * --------------------+-------------------+-----------------------------------+------
639 * Field Type Details Value
640 * --------------------+-------------------+-----------------------------------+------
641 * BoxHeader.Size Unsigned int(32)
642 * BoxHeader.Type Unsigned int(32) 'loci'
643 * BoxHeader.Version Unsigned int(8) 0
644 * BoxHeader.Flags Bit(24) 0
646 * Language Unsigned int(5)[3] Packed ISO-639-2/T language code
647 * Name String Text of place name
648 * Role Unsigned int(8) Non-negative value indicating role
650 * Longitude Unsigned int(32) Fixed-point value of the longitude
651 * Latitude Unsigned int(32) Fixed-point value of the latitude
652 * Altitude Unsigned int(32) Fixed-point value of the Altitude
653 * Astronomical_body String Text of astronomical body
654 * Additional_notes String Text of additional location-related
656 * --------------------+-------------------+-----------------------------------+------
658 static int _get_char_position(unsigned char *src, char ch, int max)
661 for (i = 0; i < max; i++) {
662 if (*(src + i) == ch)
669 static int GetLocationFromLociTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
672 MMFILE_3GP_LOCATION_TAGBOX lociTag = {0, };
675 unsigned char *buffer = NULL;
676 unsigned char *p = NULL;
678 unsigned int name_sz = 0;
679 unsigned int astro_sz = 0;
681 int ilong, ilati, ialti;
682 float flong, flati, falti;
685 if (!formatContext || !fp || !basic_header) {
686 debug_error(DEBUG, "invalid param");
687 return MMFILE_UTIL_FAIL;
690 readed = mmfile_read(fp, (unsigned char *)&lociTag, 6); /*6 = version + flag + pad + language */
692 debug_error(DEBUG, "read location tag header fail");
696 /*buffer len = name + role + ... + additional notes length */
697 bufferLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - 6;
699 debug_error(DEBUG, "too small buffer");
703 buffer = g_malloc0(bufferLen);
705 readed = mmfile_read(fp, (unsigned char *)buffer, bufferLen);
706 if (readed != bufferLen) {
707 debug_error(DEBUG, "read location tag fail");
713 pos = _get_char_position(p, '\0', readed - (1 + 4 + 4 + 4 + 2));
715 if (p[0] == 0xFE && p[1] == 0xFF) {
716 lociTag.name = (unsigned char *)mmfile_convert_to_utf8((const char *)(p + 2), pos - 2, MMFILE_CODESET_UTF16);
718 lociTag.name = (unsigned char *)g_strdup((const char *)p);
730 debug_msg(RELEASE, "long: 0x%02X 0x%02X 0x%02X 0x%02X ", *(p + 0), *(p + 1), *(p + 2), *(p + 3));
731 debug_msg(RELEASE, "lati: 0x%02X 0x%02X 0x%02X 0x%02X ", *(p + 4), *(p + 5), *(p + 6), *(p + 7));
732 debug_msg(RELEASE, "alti: 0x%02X 0x%02X 0x%02X 0x%02X ", *(p + 8), *(p + 9), *(p + 10), *(p + 11));
734 ilong = mmfile_io_be_uint32(*(unsigned int *)p);
735 ilati = mmfile_io_be_uint32(*(unsigned int *)(p + 4));
736 ialti = mmfile_io_be_uint32(*(unsigned int *)(p + 8));
738 flong = (float)ilong / (1 << 16);
739 flati = (float)ilati / (1 << 16);
740 falti = (float)ialti / (1 << 16);
743 lociTag.longitude = flong;
745 lociTag.latitude = flati;
747 lociTag.altitude = falti;
751 /*astronomical body*/
752 pos = _get_char_position(p, '\0', readed - (name_sz + 1 + 4 + 4 + 4 + 1));
754 if (p[0] == 0xFE && p[1] == 0xFF) {
755 lociTag.astronomical_body = (unsigned char *)mmfile_convert_to_utf8((const char *)(p + 2), pos - 2, MMFILE_CODESET_UTF16);
757 lociTag.astronomical_body = (unsigned char *)g_strdup((const char *)p);
766 pos = _get_char_position(p, '\0', readed - (name_sz + 1 + 4 + 4 + 4 + astro_sz));
768 if (p[0] == 0xFE && p[1] == 0xFF) {
769 lociTag.additional_notes = (unsigned char *)mmfile_convert_to_utf8((const char *)(p + 2), pos - 2, MMFILE_CODESET_UTF16);
771 lociTag.additional_notes = (unsigned char *)g_strdup((const char *)p);
777 debug_msg(RELEASE, "** Location Information **");
778 debug_msg(RELEASE, "Name : %s", lociTag.name);
779 debug_msg(RELEASE, "Role : %d (0: shooting, 1: real, 2: fictional, other: reserved)", lociTag.role);
780 debug_msg(RELEASE, "Longitude : %16.16f", lociTag.longitude);
781 debug_msg(RELEASE, "Latitude : %16.16f", lociTag.latitude);
782 debug_msg(RELEASE, "Altitude : %16.16f", lociTag.altitude);
783 debug_msg(RELEASE, "Astronomical body: %s", lociTag.astronomical_body);
784 debug_msg(RELEASE, "Additional notes : %s", lociTag.additional_notes);
786 formatContext->longitude = lociTag.longitude;
787 formatContext->latitude = lociTag.latitude;
788 formatContext->altitude = lociTag.altitude;
791 mmfile_free(lociTag.name);
792 mmfile_free(lociTag.astronomical_body);
793 mmfile_free(lociTag.additional_notes);
795 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
796 return MMFILE_UTIL_SUCCESS;
799 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
801 mmfile_free(lociTag.name);
802 mmfile_free(lociTag.astronomical_body);
803 mmfile_free(lociTag.additional_notes);
805 return MMFILE_UTIL_FAIL;
808 static int GetSAUTInfoFromSMTATagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
810 MMFILE_M4A_SMTA_TAGBOX smtaTag = {0, };
813 if (!formatContext || !fp || !basic_header) {
814 debug_error(DEBUG, "invalid param");
815 return MMFILE_UTIL_FAIL;
818 readed = mmfile_read(fp, (unsigned char *)&smtaTag, sizeof(MMFILE_M4A_SMTA_TAGBOX));
819 if (readed != sizeof(MMFILE_M4A_SMTA_TAGBOX)) {
820 debug_error(DEBUG, "read smta tag header fail");
824 smtaTag.length = mmfile_io_be_uint32(smtaTag.length);
825 smtaTag.value = mmfile_io_be_uint32(smtaTag.value);
827 debug_msg(RELEASE, "Len : 0x%x", smtaTag.length);
828 debug_msg(RELEASE, "Saut : 0x%x 0x%x 0x%x 0x%x", smtaTag.saut[0], smtaTag.saut[1], smtaTag.saut[2], smtaTag.saut[3]);
829 debug_msg(RELEASE, "Value : 0x%x", smtaTag.value);
840 7: 360 slowmotion mode
841 8: 180 slowmotion mode
843 if (smtaTag.saut[0] == 's'
844 && smtaTag.saut[1] == 'a'
845 && smtaTag.saut[2] == 'u'
846 && smtaTag.saut[3] == 't') {
847 if (smtaTag.value == 0x01) {
848 debug_msg(RELEASE, "This has saut tag and valid value");
849 formatContext->smta = 1;
850 } else if (smtaTag.value == 0x02) {
851 debug_msg(RELEASE, "This has saut tag and valid value");
852 formatContext->smta = 2;
854 debug_error(DEBUG, "This has saut tag but invalid value");
858 debug_error(DEBUG, "This hasn't saut tag and valid value");
862 return MMFILE_UTIL_SUCCESS;
865 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
867 return MMFILE_UTIL_FAIL;
870 static int GetSA3DInfoFromMP4ATagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
872 if (!formatContext || !fp || !basic_header) {
873 debug_error(DEBUG, "invalid param");
874 return MMFILE_UTIL_FAIL;
877 unsigned char *buffer;
879 bool is_SA3D_present = false;
881 MMFILE_MP4A_SA3D_TAGBOX sa3dTag = {0, };
883 formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_UNKNOWN;
884 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_UNKNOWN;
885 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_UNKNOWN;
887 mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
889 buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
891 debug_error(DEBUG, "calloc failed ");
895 readed = mmfile_read(fp, buffer, basic_header->size);
896 if (readed != (int)basic_header->size) {
897 debug_error(DEBUG, "read mp4a box failed");
901 for (i = 0; i + 3 < basic_header->size; ++i)
902 if (buffer[i] == 'S' && buffer[i + 1] == 'A' && buffer[i + 2] == '3' && buffer[i + 3] == 'D') {
903 debug_warning(DEBUG, "SA3D data found at offset %d bytes", i);
904 is_SA3D_present = true;
908 if (!is_SA3D_present) {
909 debug_warning(DEBUG, "No SA3D box found");
913 mmfile_seek(fp, basic_header->start_offset + i + 4, SEEK_SET);
915 readed = mmfile_read(fp, (unsigned char *)&sa3dTag, sizeof(MMFILE_MP4A_SA3D_TAGBOX));
916 if (readed != sizeof(MMFILE_MP4A_SA3D_TAGBOX)) {
917 debug_error(DEBUG, "read SA3D tag header fail");
921 sa3dTag.ambisonic_order = mmfile_io_be_uint32(sa3dTag.ambisonic_order);
922 sa3dTag.num_channels = mmfile_io_be_uint32(sa3dTag.num_channels);
923 for (i = 0; i < sa3dTag.num_channels; ++i)
924 sa3dTag.channel_map[i] = mmfile_io_be_uint32(sa3dTag.channel_map[i]);
926 debug_msg(RELEASE, "sa3dTag.version = %d", sa3dTag.version);
927 debug_msg(RELEASE, "sa3dTag.ambisonic_type = %d", sa3dTag.ambisonic_type);
928 debug_msg(RELEASE, "sa3dTag.ambisonic_order = %d", sa3dTag.ambisonic_order);
929 debug_msg(RELEASE, "sa3dTag.ambisonic_channel_ordering = %d", sa3dTag.ambisonic_channel_ordering);
930 debug_msg(RELEASE, "sa3dTag.ambisonic_normalization = %d", sa3dTag.ambisonic_normalization);
931 debug_msg(RELEASE, "sa3dTag.num_channels = %d", sa3dTag.num_channels);
932 for (i = 0; i < sa3dTag.num_channels; ++i)
933 debug_msg(RELEASE, "sa3dTag.channel_map[%d] = %d", i, sa3dTag.channel_map[i]);
935 if (sa3dTag.version != RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
936 debug_error(DEBUG, "SA3D tag box version is unsupported");
939 if (sa3dTag.ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
940 formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_PERIPHONIC;
942 switch (sa3dTag.ambisonic_order) {
943 case MMFILE_AMBISONIC_ORDER_FOA: {
944 if (sa3dTag.num_channels == 4) {
945 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_FOA;
947 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
948 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
949 (sa3dTag.channel_map[0] == 0) &&
950 (sa3dTag.channel_map[1] == 1) &&
951 (sa3dTag.channel_map[2] == 2) &&
952 (sa3dTag.channel_map[3] == 3))
953 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
955 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
956 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
957 (sa3dTag.channel_map[0] == 0) &&
958 (sa3dTag.channel_map[1] == 3) &&
959 (sa3dTag.channel_map[2] == 1) &&
960 (sa3dTag.channel_map[3] == 2))
961 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
963 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond");
969 case MMFILE_AMBISONIC_ORDER_SOA: {
970 if (sa3dTag.num_channels == 9) {
971 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_SOA;
973 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
974 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
975 (sa3dTag.channel_map[0] == 0) &&
976 (sa3dTag.channel_map[1] == 1) &&
977 (sa3dTag.channel_map[2] == 2) &&
978 (sa3dTag.channel_map[3] == 3) &&
979 (sa3dTag.channel_map[4] == 4) &&
980 (sa3dTag.channel_map[5] == 5) &&
981 (sa3dTag.channel_map[6] == 6) &&
982 (sa3dTag.channel_map[7] == 7) &&
983 (sa3dTag.channel_map[8] == 8))
984 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
986 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
987 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
988 (sa3dTag.channel_map[0] == 0) &&
989 (sa3dTag.channel_map[1] == 3) &&
990 (sa3dTag.channel_map[2] == 1) &&
991 (sa3dTag.channel_map[3] == 2) &&
992 (sa3dTag.channel_map[4] == 6) &&
993 (sa3dTag.channel_map[5] == 7) &&
994 (sa3dTag.channel_map[6] == 5) &&
995 (sa3dTag.channel_map[7] == 8) &&
996 (sa3dTag.channel_map[8] == 4))
997 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
999 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond");
1006 case MMFILE_AMBISONIC_ORDER_TOA: {
1007 if (sa3dTag.num_channels == 16) {
1008 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_TOA;
1010 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
1011 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
1012 (sa3dTag.channel_map[0] == 0) &&
1013 (sa3dTag.channel_map[1] == 1) &&
1014 (sa3dTag.channel_map[2] == 2) &&
1015 (sa3dTag.channel_map[3] == 3) &&
1016 (sa3dTag.channel_map[4] == 4) &&
1017 (sa3dTag.channel_map[5] == 5) &&
1018 (sa3dTag.channel_map[6] == 6) &&
1019 (sa3dTag.channel_map[7] == 7) &&
1020 (sa3dTag.channel_map[8] == 8) &&
1021 (sa3dTag.channel_map[9] == 9) &&
1022 (sa3dTag.channel_map[10] == 10) &&
1023 (sa3dTag.channel_map[11] == 11) &&
1024 (sa3dTag.channel_map[12] == 12) &&
1025 (sa3dTag.channel_map[13] == 13) &&
1026 (sa3dTag.channel_map[14] == 14) &&
1027 (sa3dTag.channel_map[15] == 15))
1028 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
1030 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
1031 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
1032 (sa3dTag.channel_map[0] == 0) &&
1033 (sa3dTag.channel_map[1] == 3) &&
1034 (sa3dTag.channel_map[2] == 1) &&
1035 (sa3dTag.channel_map[3] == 2) &&
1036 (sa3dTag.channel_map[4] == 6) &&
1037 (sa3dTag.channel_map[5] == 7) &&
1038 (sa3dTag.channel_map[6] == 5) &&
1039 (sa3dTag.channel_map[7] == 8) &&
1040 (sa3dTag.channel_map[8] == 4) &&
1041 (sa3dTag.channel_map[9] == 12) &&
1042 (sa3dTag.channel_map[10] == 13) &&
1043 (sa3dTag.channel_map[11] == 11) &&
1044 (sa3dTag.channel_map[12] == 14) &&
1045 (sa3dTag.channel_map[13] == 10) &&
1046 (sa3dTag.channel_map[14] == 15) &&
1047 (sa3dTag.channel_map[15] == 9))
1048 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
1050 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond");
1058 debug_warning(DEBUG, "Ambisonic order or format is not supported: ambix or amb formats up to 3rd order were expected.");
1064 debug_msg(RELEASE, "formatContext->ambisonic_type = %d", formatContext->ambisonicType);
1065 debug_msg(RELEASE, "formatContext->ambisonic_order = %d", formatContext->ambisonicOrder);
1066 debug_msg(RELEASE, "formatContext->ambisonic_format = %d", formatContext->ambisonicFormat);
1069 return MMFILE_UTIL_SUCCESS;
1072 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1074 return MMFILE_UTIL_FAIL;
1077 static int ParseSt3dData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset)
1079 uint8_t stereo_mode = INVALID_UINT8_VALUE;
1080 unsigned int readed = 0;
1082 mmfile_seek(fp, start_offset, SEEK_SET);
1084 readed = mmfile_read(fp, (unsigned char *)&stereo_mode, sizeof(uint8_t));
1085 if (readed != sizeof(uint8_t)) {
1086 debug_error(DEBUG, "read st3d tag header fail");
1087 return MMFILE_UTIL_FAIL;
1090 formatContext->stereoModeV2 = stereo_mode;
1092 return MMFILE_UTIL_SUCCESS;
1095 static int ParseSvhdData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset, unsigned int box_size)
1097 unsigned int readed = 0;
1099 formatContext->metadataSourceV2 = (char *)calloc(1, box_size);
1100 if (!formatContext->metadataSourceV2) {
1101 debug_error(DEBUG, "Calloc failed");
1102 return MMFILE_UTIL_FAIL;
1105 mmfile_seek(fp, start_offset, SEEK_SET);
1106 readed = mmfile_read(fp, (unsigned char *)formatContext->metadataSourceV2, box_size);
1107 if (readed != box_size) {
1108 debug_error(DEBUG, "read svhd tag header fail");
1109 if (formatContext->metadataSourceV2)
1110 free(formatContext->metadataSourceV2);
1111 return MMFILE_UTIL_FAIL;
1114 return MMFILE_UTIL_SUCCESS;
1117 static int ParseProjData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset)
1119 unsigned int readed = 0;
1120 typedef struct proj_box_data_s {
1124 } __attribute__((aligned(1), packed)) proj_box_data;
1126 typedef struct prhd_box_data_s {
1127 uint32_t projection_pose_yaw;
1128 uint32_t projection_pose_pitch;
1129 uint32_t projection_pose_roll;
1132 typedef struct equi_box_data_s {
1133 uint32_t projection_bounds_top;
1134 uint32_t projection_bounds_bottom;
1135 uint32_t projection_bounds_left;
1136 uint32_t projection_bounds_right;
1139 typedef struct cbmp_box_data_s {
1145 proj_box_data proj_data;
1146 proj_data.proj_type = INVALID_UINT_VALUE;
1148 prhd_box_data prhd_data;
1149 prhd_data.projection_pose_yaw = INVALID_UINT_VALUE;
1150 prhd_data.projection_pose_pitch = INVALID_UINT_VALUE;
1151 prhd_data.projection_pose_roll = INVALID_UINT_VALUE;
1153 equi_box_data equi_data;
1154 equi_data.projection_bounds_top = INVALID_UINT_VALUE;
1155 equi_data.projection_bounds_bottom = INVALID_UINT_VALUE;
1156 equi_data.projection_bounds_left = INVALID_UINT_VALUE;
1157 equi_data.projection_bounds_right = INVALID_UINT_VALUE;
1159 cbmp_box_data cbmp_data;
1160 cbmp_data.layout = INVALID_UINT_VALUE;
1161 cbmp_data.padding = INVALID_UINT_VALUE;
1163 mmfile_seek(fp, start_offset, SEEK_SET);
1165 readed = mmfile_read(fp, (unsigned char *)&proj_data, sizeof(proj_box_data));
1166 if (readed != sizeof(proj_box_data)) {
1167 debug_error(DEBUG, "read of proj box failed");
1168 return MMFILE_UTIL_FAIL;
1171 formatContext->projTypeV2 = mmfile_io_be_uint32(proj_data.proj_type);
1173 debug_error(DEBUG, "formatContext->projTypeV2 = %d", formatContext->projTypeV2);
1174 debug_error(DEBUG, "proj_data.version = %d", proj_data.version);
1175 debug_error(DEBUG, "proj_data.flags = %d", ((uint32_t)proj_data.flags[0] << 16) +
1176 ((uint32_t)proj_data.flags[1] << 8) + (uint32_t)proj_data.flags[2]);
1178 mmfile_seek(fp, sizeof(proj_box_data), SEEK_CUR);
1179 readed = mmfile_read(fp, (unsigned char *)&prhd_data, sizeof(prhd_box_data));
1180 if (readed != sizeof(prhd_box_data)) {
1181 debug_error(DEBUG, "read of prhd box failed");
1182 return MMFILE_UTIL_FAIL;
1185 formatContext->poseYawV2 = mmfile_io_be_uint32(prhd_data.projection_pose_yaw);
1186 formatContext->posePitchV2 = mmfile_io_be_uint32(prhd_data.projection_pose_pitch);
1187 formatContext->poseRollV2 = mmfile_io_be_uint32(prhd_data.projection_pose_roll);
1189 debug_error(DEBUG, "formatContext->poseYawV2 = %d", formatContext->poseYawV2);
1190 debug_error(DEBUG, "formatContext->posePitchV2 = %d", formatContext->posePitchV2);
1191 debug_error(DEBUG, "formatContext->poseRollV2 = %d", formatContext->poseRollV2);
1193 if (formatContext->projTypeV2 == PROJECTION_TYPE_EQUI) {
1194 debug_msg(RELEASE, "Projection type is Equirectangular");
1195 mmfile_seek(fp, 8, SEEK_CUR); /* 8 = 4 (for size) + 4 (fourcc) */
1196 readed = mmfile_read(fp, (unsigned char *)&equi_data, sizeof(equi_box_data));
1197 if (readed != sizeof(equi_box_data)) {
1198 debug_error(DEBUG, "read of equi box failed");
1199 return MMFILE_UTIL_FAIL;
1202 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi_data.projection_bounds_top);
1203 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi_data.projection_bounds_bottom);
1204 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi_data.projection_bounds_left);
1205 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi_data.projection_bounds_right);
1207 debug_error(DEBUG, "formatContext->equiBoundsTopV2 = %d", formatContext->equiBoundsTopV2);
1208 debug_error(DEBUG, "formatContext->equiBoundsBottomV2 = %d", formatContext->equiBoundsBottomV2);
1209 debug_error(DEBUG, "formatContext->equiBoundsLeftV2 = %d", formatContext->equiBoundsLeftV2);
1210 debug_error(DEBUG, "formatContext->equiBoundsRightV2 = %d", formatContext->equiBoundsRightV2);
1212 } else if (formatContext->projTypeV2 == PROJECTION_TYPE_CBMP) {
1213 debug_msg(RELEASE, "Projection type is Cubemap");
1214 mmfile_seek(fp, 8, SEEK_CUR); /* 8 = 4 (for size) + 4 (fourcc) */
1215 readed = mmfile_read(fp, (unsigned char *)&cbmp_data, sizeof(cbmp_box_data));
1216 if (readed != sizeof(cbmp_box_data)) {
1217 debug_error(DEBUG, "read of cbmp box failed");
1218 return MMFILE_UTIL_FAIL;
1221 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp_data.layout);
1222 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp_data.padding);
1224 debug_error(DEBUG, "formatContext->cbmpLayoutV2 = %d", formatContext->cbmpLayoutV2);
1225 debug_error(DEBUG, "formatContext->cbmpPaddingV2 = %d", formatContext->cbmpPaddingV2);
1228 debug_msg(RELEASE, "Projection type is %d (unknown)", proj_data.proj_type);
1229 return MMFILE_UTIL_FAIL;
1232 return MMFILE_UTIL_SUCCESS;
1235 static int GetVideoV2MetadataFromAvc1TagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1237 if (!formatContext || !fp || !basic_header) {
1238 debug_error(DEBUG, "invalid param");
1239 return MMFILE_UTIL_FAIL;
1242 unsigned char *buffer;
1246 formatContext->stereoModeV2 = INVALID_UINT_VALUE;
1247 formatContext->metadataSourceV2 = NULL;
1248 formatContext->projTypeV2 = INVALID_UINT_VALUE;
1249 formatContext->poseYawV2 = INVALID_UINT_VALUE;
1250 formatContext->posePitchV2 = INVALID_UINT_VALUE;
1251 formatContext->poseRollV2 = INVALID_UINT_VALUE;
1252 formatContext->cbmpLayoutV2 = INVALID_UINT_VALUE;
1253 formatContext->cbmpPaddingV2 = INVALID_UINT_VALUE;
1254 formatContext->equiBoundsTopV2 = INVALID_UINT_VALUE;
1255 formatContext->equiBoundsBottomV2 = INVALID_UINT_VALUE;
1256 formatContext->equiBoundsLeftV2 = INVALID_UINT_VALUE;
1257 formatContext->equiBoundsRightV2 = INVALID_UINT_VALUE;
1259 mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
1261 buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
1263 debug_error(DEBUG, "calloc failed ");
1267 readed = mmfile_read(fp, buffer, basic_header->size);
1268 if (readed != (int)basic_header->size) {
1269 debug_error(DEBUG, "read st3d box failed");
1273 for (i = 0; i + 3 < basic_header->size; ++i) {
1274 if ((buffer[i] == 's' && buffer[i + 1] == 't' && buffer[i + 2] == '3' && buffer[i + 3] == 'd') && (formatContext->stereoModeV2 == INVALID_UINT_VALUE)) {
1275 debug_warning(DEBUG, "st3d data found at offset %lld", basic_header->start_offset + i);
1276 ParseSt3dData(formatContext, fp, basic_header->start_offset + i + 4);
1277 debug_msg(RELEASE, "formatContext->stereoModeV2 = %d", formatContext->stereoModeV2);
1279 if (buffer[i] == 's' && buffer[i + 1] == 'v' && buffer[i + 2] == '3' && buffer[i + 3] == 'd') {
1280 debug_warning(DEBUG, "sv3d data found at offset %lld", basic_header->start_offset + i);
1281 formatContext->isSpherical = true;
1283 if (buffer[i] == 's' && buffer[i + 1] == 'v' && buffer[i + 2] == 'h' && buffer[i + 3] == 'd') {
1284 debug_warning(DEBUG, "svhd data found at offset %lld", basic_header->start_offset + i);
1285 ParseSvhdData(formatContext, fp, basic_header->start_offset + i + 4, mmfile_io_be_uint32(*((uint32_t*)(buffer - 4 + i))));
1286 debug_msg(RELEASE, "formatContext->metadataSourceV2 = %s (length = %zu)", formatContext->metadataSourceV2, strlen(formatContext->metadataSourceV2));
1288 if (buffer[i] == 'p' && buffer[i + 1] == 'r' && buffer[i + 2] == 'o' && buffer[i + 3] == 'j') {
1289 debug_warning(DEBUG, "proj data found at offset %lld", basic_header->start_offset + i);
1290 ParseProjData(formatContext, fp, basic_header->start_offset + i + 4);
1294 return MMFILE_UTIL_SUCCESS;
1297 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1299 return MMFILE_UTIL_FAIL;
1302 static int GetValueFromCDISTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1304 unsigned int value = 0;
1307 if (!formatContext || !fp || !basic_header) {
1308 debug_error(DEBUG, "invalid param");
1309 return MMFILE_UTIL_FAIL;
1312 readed = mmfile_read(fp, (unsigned char *)&value, sizeof(unsigned int));
1313 if (readed != sizeof(unsigned int)) {
1314 debug_error(DEBUG, "read cdis tag header fail");
1318 value = mmfile_io_be_uint32(value);
1320 debug_msg(RELEASE, "Value : 0x%x", value);
1322 if (value == 0x01) {
1323 debug_msg(RELEASE, "This has cdis tag and valid value");
1324 formatContext->cdis = 1;
1326 debug_error(DEBUG, "This has cdis tag and but invalid value");
1330 return MMFILE_UTIL_SUCCESS;
1333 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1335 return MMFILE_UTIL_FAIL;
1338 static int GetTagFromMetaBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1341 MMFILE_MP4_BASIC_BOX_HEADER hdlrBoxHeader = {0, };
1342 MMFILE_MP4_BASIC_BOX_HEADER id3v2BoxHeader = {0, };
1343 MMFILE_3GP_ID3V2_BOX id3v2Box = {0, };
1344 AvFileContentInfo tagInfo = {0, };
1345 unsigned char tagVersion = 0;
1346 bool versionCheck = false;
1348 unsigned int meta_version = 0;
1349 MMFILE_3GP_HANDLER_BOX hdlrBox = {0, };
1350 unsigned int encSize = 0;
1352 #ifdef ENABLE_ITUNES_META /* We don't support itunes meta now. so this is not defined yet */
1353 int iTunes_meta = 0;
1357 readed = mmfile_read(fp, (unsigned char *)&meta_version, 4);
1359 debug_error(DEBUG, "read meta box version");
1364 readed = mmfile_read(fp, (unsigned char *)&hdlrBoxHeader, MMFILE_MP4_BASIC_BOX_HEADER_LEN);
1365 if (readed != MMFILE_MP4_BASIC_BOX_HEADER_LEN) {
1366 debug_error(DEBUG, "read hdlr box header");
1370 if (hdlrBoxHeader.type != FOURCC('h', 'd', 'l', 'r')) {
1371 debug_warning(DEBUG, "meta type is not hdlr");
1375 hdlrBoxHeader.size = mmfile_io_be_uint32(hdlrBoxHeader.size);
1376 hdlrBoxHeader.type = mmfile_io_le_uint32(hdlrBoxHeader.type);
1378 readed = mmfile_read(fp, (unsigned char *)&hdlrBox, MMFILE_3GP_HANDLER_BOX_LEN);
1379 if (readed != MMFILE_3GP_HANDLER_BOX_LEN) {
1380 debug_error(DEBUG, "read hdlr box");
1384 hdlrBox.handler_type = mmfile_io_le_uint32(hdlrBox.handler_type);
1387 * check tag type (ID3v2 or iTunes)
1389 if (hdlrBox.handler_type == FOURCC('I', 'D', '3', '2')) {
1390 debug_msg(RELEASE, "ID3v2 tag detected.");
1393 #ifdef ENABLE_ITUNES_META
1396 } else if (hdlrBox.handler_type == FOURCC('m', 'd', 'i', 'r') &&
1397 mmfile_io_le_uint32(hdlrBox.reserved[0]) == FOURCC('a', 'p', 'p', 'l')) {
1399 debug_msg(RELEASE, "Apple iTunes tag detected by mdir.");
1401 #ifdef ENABLE_ITUNES_META
1405 debug_warning(DEBUG, "unknown meta type. 4CC:[%c%c%c%c]", ((char *)&hdlrBox.handler_type)[0],
1406 ((char *)&hdlrBox.handler_type)[1],
1407 ((char *)&hdlrBox.handler_type)[2],
1408 ((char *)&hdlrBox.handler_type)[3]);
1409 /*goto exception; */
1412 #ifdef ENABLE_ITUNES_META
1413 if (!id3_meta && !iTunes_meta) {
1415 APPLE meta data for iTunes reader = 'mdir.' so if handler type is 'mdir', this content may has itunes meta.
1416 most of contents has 'mdir' + 'appl'. but some contents just has 'mdir'
1417 but 'ilst' is meta for iTunes. so find 'ilst' is more correct to check if this contents has iTunes meta or not.*/
1419 const char *ilst_box = "ilst";
1420 int buf_size = strlen(ilst_box);
1422 unsigned char read_buf[buf_size + 1];
1423 memset(read_buf, 0x00, buf_size + 1);
1426 mmfile_seek(fp, hdlrBoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_HANDLER_BOX_LEN + 4, SEEK_CUR); /*+4 is hdlr size field */
1428 readed = mmfile_read(fp, read_buf, buf_size); /* to find 'ilst' */
1429 if (readed != buf_size) {
1430 debug_error(DEBUG, "read fail [%d]", readed);
1434 if (read_buf[0] == 'i' && read_buf[1] == 'l' && read_buf[2] == 's' && read_buf[3] == 't') {
1435 debug_msg(RELEASE, "Apple iTunes tag detected by ilst.");
1441 #ifdef ENABLE_ITUNES_META
1444 * iTunes (Cover[?ovr] & Track[trkn] only extract!) + Genre/Artist : Added 2010.10.27,28
1452 #define _ITUNES_READ_BUF_SZ 20
1453 #define _ITUNES_TRACK_NUM_SZ 4
1454 #define _ITUNES_GENRE_NUM_SZ 4
1455 #define _ITUNES_COVER_TYPE_JPEG 13
1456 #define _ITUNES_COVER_TYPE_PNG 14
1458 unsigned char read_buf[_ITUNES_READ_BUF_SZ];
1460 int cover_sz = 0, cover_type = 0, cover_found = 0;
1461 /* int track_found = 0; */ /* , track_num = 0; */
1462 /* int genre_found = 0; */ /* , genre_index = 0; */
1463 /* int artist_found = 0; */ /* , artist_sz = 0; */
1464 int limit = basic_header->size - hdlrBoxHeader.size;
1465 long long cover_offset = 0; /*, track_offset =0, genre_offset = 0, artist_offset = 0; */
1467 /* 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++) { */
1468 for (i = 0; (i < limit) && (cover_found == 0) ; i++) {
1469 readed = mmfile_read(fp, read_buf, _ITUNES_READ_BUF_SZ);
1470 if (readed != _ITUNES_READ_BUF_SZ)
1473 /*ffmpeg extract artist, tracknum, genre and cover image. see mov_read_udta_string().
1474 but ffmpeg does not support strange cover image.
1475 only support covr type 0xd(JPEG), 0xe(PNG), 0x1b(BMP). but we support other type*/
1478 * Artist : Added 2010.10.28
1480 if (artist_found == 0 &&
1481 read_buf[0] == 0xa9 && read_buf[1] == 'A' && read_buf[2] == 'R' && read_buf[3] == 'T' &&
1482 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1485 artist_offset = mmfile_tell(fp);
1486 artist_sz = mmfile_io_be_uint32(*(int *)(read_buf + 4)) - 16; /* atom len(4)+data(4)+atom verion(1)+flag(3)+null(4) = 16 */
1488 debug_msg(RELEASE, "----------------------------------- artist found, offset=[%lld], size=[%d]", artist_offset, artist_sz);
1494 if (track_found == 0 &&
1495 read_buf[0] == 't' && read_buf[1] == 'r' && read_buf[2] == 'k' && read_buf[3] == 'n' &&
1496 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1499 track_offset = mmfile_tell(fp);
1501 debug_msg(RELEASE, "----------------------------------- Track found, offset=[%lld]", track_offset);
1505 * Genre : Added 2010.10.27
1507 /*ffmpeg extract genre but only (0xa9,'g','e','n'). see mov_read_udta_string()*/
1508 if (genre_found == 0 &&
1509 read_buf[0] == 'g' && read_buf[1] == 'n' && read_buf[2] == 'r' && read_buf[3] == 'e' &&
1510 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1513 genre_offset = mmfile_tell(fp);
1515 debug_msg(RELEASE, "----------------------------------- genre found, offset=[%lld]", genre_offset);
1523 if (cover_found == 0 &&
1524 read_buf[0] == 'c' && read_buf[1] == 'o' && read_buf[2] == 'v' && read_buf[3] == 'r' &&
1525 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1528 cover_sz = mmfile_io_be_uint32(*(int *)(read_buf + 4)) - 12;
1529 cover_type = mmfile_io_be_uint32(*(int *)(read_buf + 12));
1531 cover_offset = mmfile_tell(fp);
1533 debug_msg(RELEASE, "----------------------------------- cover_found found, offset=[%lld]", cover_offset);
1536 mmfile_seek(fp, -(_ITUNES_READ_BUF_SZ - 1), SEEK_CUR); /*FIXME: poor search*/
1539 /*ffmpeg extract artist, tracknum, except cover image. see mov_read_udta_string()*/
1542 if (artist_sz > 0) {
1543 mmfile_seek(fp, artist_offset, SEEK_SET);
1545 if (formatContext->artist) {
1546 debug_msg(RELEASE, "----------------------------------- previous artist was [%s] ", formatContext->artist);
1547 free(formatContext->artist);
1550 debug_msg(RELEASE, "----------------------------------- new artist will be allocated with size (len+1) [%d] ", artist_sz + 1);
1551 formatContext->artist = g_malloc0(artist_sz + 1);
1553 if (formatContext->artist) {
1554 readed = mmfile_read(fp, (unsigned char *)formatContext->artist, artist_sz);
1555 formatContext->artist[artist_sz] = '\0';
1557 debug_msg(RELEASE, "----------------------------------- new artist is [%s] ", formatContext->artist);
1559 if (readed != artist_sz) {
1560 debug_error(DEBUG, "failed to read. ret = %d, in = %d", readed, artist_sz);
1561 mmfile_free(formatContext->artist);
1568 mmfile_seek(fp, track_offset, SEEK_SET);
1569 readed = mmfile_read(fp, read_buf, _ITUNES_TRACK_NUM_SZ);
1570 if (readed != _ITUNES_TRACK_NUM_SZ) {
1571 debug_error(DEBUG, "failed to read. ret = %d, in = %d", readed, _ITUNES_TRACK_NUM_SZ);
1573 track_num = mmfile_io_be_uint32(*(int *)read_buf);
1574 if (!formatContext->tagTrackNum) {
1575 memset(read_buf, 0x00, _ITUNES_READ_BUF_SZ);
1576 snprintf((char *)read_buf, sizeof(read_buf), "%d", track_num);
1577 formatContext->tagTrackNum = g_strdup((const char *)read_buf);
1583 mmfile_seek(fp, genre_offset, SEEK_SET);
1584 readed = mmfile_read(fp, read_buf, _ITUNES_GENRE_NUM_SZ);
1585 if (readed != _ITUNES_GENRE_NUM_SZ) {
1586 debug_error(DEBUG, "failed to read. ret = %d, in = %d", readed, _ITUNES_GENRE_NUM_SZ);
1588 genre_index = mmfile_io_be_uint16(*(int *)read_buf);
1589 debug_msg(RELEASE, "genre index=[%d] ", genre_index);
1591 if (genre_index > 0 && genre_index < GENRE_COUNT) {
1592 if (!formatContext->genre) {
1593 memset(read_buf, 0x00, _ITUNES_READ_BUF_SZ);
1594 snprintf((char *)read_buf, sizeof(read_buf), "%s", MpegAudio_Genre[genre_index - 1]);
1595 debug_msg(RELEASE, "genre string=[%s] ", read_buf);
1596 formatContext->genre = g_strdup((const char *)read_buf);
1604 1) below spec is in "iTunes Package Asset Specification 4.3" published by apple.
1605 Music Cover Art Image Profile
1606 - TIFF with ".tif" extension (32-bit uncompressed), JPEG with ".jpg" extension (quality unconstrained), or PNG with ".png" extension
1607 - RGB (screen standard)
1608 - Minimum size of 600 x 600 pixels
1609 - Images must be at least 72 dpi
1611 2)I found below info from google.
1612 cover image flag : JPEG (13, 0xd), PNG (14, 0xe)
1614 3)So, FIXME when cover image format is tif!
1618 mmfile_seek(fp, cover_offset, SEEK_SET);
1620 formatContext->artwork = g_malloc0(cover_sz);
1621 formatContext->artworkSize = cover_sz;
1622 if (cover_type == _ITUNES_COVER_TYPE_JPEG) {
1623 formatContext->artworkMime = g_strdup("image/jpeg");
1624 } else if (cover_type == _ITUNES_COVER_TYPE_PNG) {
1625 formatContext->artworkMime = g_strdup("image/png");
1626 /*} else if (cover_type == _ITUNES_COVER_TYPE_TIF) {
1627 formatContext->artworkMime = g_strdup("image/tif");*/
1629 debug_warning(DEBUG, "Not proper cover image type, but set to jpeg. cover_type[%d]", cover_type);
1630 formatContext->artworkMime = g_strdup("image/jpeg");
1633 if (formatContext->artwork) {
1634 readed = mmfile_read(fp, formatContext->artwork, cover_sz);
1635 if (readed != cover_sz) {
1636 debug_error(DEBUG, "failed to read. ret = %d, in = %d", readed, cover_sz);
1637 mmfile_free(formatContext->artwork);
1638 formatContext->artworkSize = 0;
1639 mmfile_free(formatContext->artworkMime);
1645 /*reset seek position*/
1646 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1648 return MMFILE_UTIL_SUCCESS;
1656 /* skip hdlr box name */
1657 mmfile_seek(fp, hdlrBoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_HANDLER_BOX_LEN, SEEK_CUR);
1660 readed = mmfile_read(fp, (unsigned char *)&id3v2BoxHeader, MMFILE_MP4_BASIC_BOX_HEADER_LEN);
1661 if (readed != MMFILE_MP4_BASIC_BOX_HEADER_LEN) {
1662 debug_error(DEBUG, "read id3v2 box header");
1666 id3v2BoxHeader.size = mmfile_io_be_uint32(id3v2BoxHeader.size);
1667 id3v2BoxHeader.type = mmfile_io_le_uint32(id3v2BoxHeader.type);
1669 if (id3v2BoxHeader.type != FOURCC('I', 'D', '3', '2')) {
1670 debug_warning(DEBUG, "meta type is not id3v2");
1674 readed = mmfile_read(fp, (unsigned char *)&id3v2Box, MMFILE_3GP_ID3V2_BOX_LEN);
1675 if (readed != MMFILE_3GP_ID3V2_BOX_LEN) {
1676 debug_error(DEBUG, "read id3v2 box");
1680 id3v2Len = id3v2BoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_ID3V2_BOX_LEN;
1682 id3v2Box.id3v2Data = g_malloc0(id3v2Len);
1684 readed = mmfile_read(fp, (unsigned char *)id3v2Box.id3v2Data, id3v2Len);
1685 if (readed != id3v2Len) {
1686 debug_error(DEBUG, "read id3tag data error");
1691 if (!IS_ID3V2_TAG(id3v2Box.id3v2Data)) {
1692 debug_error(DEBUG, "it is not id3tag");
1696 if (id3v2Box.id3v2Data[3] == 0xFF || id3v2Box.id3v2Data[4] == 0xFF ||
1697 id3v2Box.id3v2Data[6] >= 0x80 || id3v2Box.id3v2Data[7] >= 0x80 ||
1698 id3v2Box.id3v2Data[8] >= 0x80 || id3v2Box.id3v2Data[9] >= 0x80) {
1699 debug_error(DEBUG, "it is not valid id3tag");
1703 tagVersion = id3v2Box.id3v2Data[3];
1704 if (tagVersion > 4) {
1705 debug_error(DEBUG, "tag version is too high");
1709 encSize = mmfile_io_le_uint32((unsigned int)&id3v2Box.id3v2Data[6]);
1710 tagInfo.tagV2Info.tagLen = MP3_TAGv2_HEADER_LEN;
1711 tagInfo.tagV2Info.tagLen += (((encSize & 0x0000007F) >> 0) | ((encSize & 0x00007F00) >> 1) | ((encSize & 0x007F0000) >> 2) | ((encSize & 0x7F000000) >> 3));
1712 tagInfo.tagV2Info.tagVersion = tagVersion;
1713 tagInfo.fileLen = id3v2Len;
1715 /* set id3v2 data to formatContext */
1716 switch (tagVersion) {
1718 versionCheck = mm_file_id3tag_parse_v222(&tagInfo, id3v2Box.id3v2Data, formatContext->extract_artwork);
1721 versionCheck = mm_file_id3tag_parse_v223(&tagInfo, id3v2Box.id3v2Data, formatContext->extract_artwork);
1724 versionCheck = mm_file_id3tag_parse_v224(&tagInfo, id3v2Box.id3v2Data, formatContext->extract_artwork);
1728 debug_error(DEBUG, "tag vesion is not support");
1729 versionCheck = false;
1733 if (versionCheck == false) {
1734 debug_error(DEBUG, "tag parsing is fail");
1738 formatContext->title = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_TITLE].value);
1739 formatContext->artist = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_ARTIST].value);
1740 formatContext->copyright = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_COPYRIGHT].value);
1741 formatContext->comment = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_COMMENT].value);
1742 formatContext->album = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_ALBUM].value);
1743 formatContext->album_artist = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_ALBUM_ARTIST].value);
1744 formatContext->year = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_YEAR].value);
1745 formatContext->genre = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_GENRE].value);
1746 formatContext->tagTrackNum = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_TRACKNUM].value);
1747 formatContext->composer = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_COMPOSER].value);
1748 formatContext->classification = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_CONTENT_GROUP].value);
1749 formatContext->conductor = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_CONDUCTOR].value);
1751 if (tagInfo.imageInfo.pImageBuf && tagInfo.imageInfo.imageLen > 0) {
1752 formatContext->artworkSize = tagInfo.imageInfo.imageLen;
1753 formatContext->artwork = g_memdup2(tagInfo.imageInfo.pImageBuf, tagInfo.imageInfo.imageLen);
1756 mm_file_free_AvFileContentInfo(&tagInfo);
1757 mmfile_free(id3v2Box.id3v2Data);
1759 /*reset seek position*/
1760 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1762 return MMFILE_UTIL_SUCCESS;
1768 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1769 mmfile_free(id3v2Box.id3v2Data);
1770 mm_file_free_AvFileContentInfo(&tagInfo);
1772 return MMFILE_UTIL_FAIL;
1775 int mm_file_get_int_value_from_xml_string(const char* xml_str, const char* param_name, int* value)
1777 char *value_start, *value_end, *endptr;
1778 const short value_length_max = 12;
1779 char init_view_ret[value_length_max];
1780 int value_length = 0;
1782 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1783 debug_error(DEBUG, "error: incorrect or non-existing parameter");
1784 return MMFILE_UTIL_FAIL;
1787 value_start = strstr(xml_str, param_name) + strlen(param_name);
1788 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
1791 value_end = strchr(value_start, '<');
1793 debug_error(DEBUG, "error: incorrect XML");
1794 return MMFILE_UTIL_FAIL;
1797 value_length = value_end - value_start;
1798 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1802 if (value_start[i] == '+' || value_start[i] == '-')
1804 while (i < value_length) {
1805 if (value_start[i] < '0' || value_start[i] > '9') {
1806 debug_error(DEBUG, "error: incorrect value, integer was expected");
1807 return MMFILE_UTIL_FAIL;
1812 if (value_length >= value_length_max || value_length < 1) {
1813 debug_error(DEBUG, "error: empty XML value or incorrect range");
1814 return MMFILE_UTIL_FAIL;
1817 memset(init_view_ret, 0x00, sizeof(init_view_ret));
1818 SAFE_STRLCPY(init_view_ret, value_start, sizeof(init_view_ret));
1820 *value = strtol(init_view_ret, &endptr, 10);
1821 if (endptr == init_view_ret) {
1822 debug_error(DEBUG, "error: no digits were found");
1823 return MMFILE_UTIL_FAIL;
1826 return MMFILE_UTIL_SUCCESS;
1829 int mm_file_get_string_value_from_xml_string(const char* xml_str, const char* param_name, char** value)
1831 char *value_start, *value_end;
1832 const short value_length_max = 256;
1833 int value_length = 0;
1835 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1836 debug_error(DEBUG, "error: incorrect or non-existing parameter");
1837 return MMFILE_UTIL_FAIL;
1840 value_start = strstr(xml_str, param_name) + strlen(param_name);
1841 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
1844 value_end = strchr(value_start, '<');
1846 debug_error(DEBUG, "error: incorrect XML");
1847 return MMFILE_UTIL_FAIL;
1850 value_length = value_end - value_start;
1851 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1854 if (value_length >= value_length_max || value_length < 1) {
1855 debug_error(DEBUG, "error: empty XML value or incorrect range");
1856 return MMFILE_UTIL_FAIL;
1859 *value = (char*)calloc(value_length, sizeof(char));
1860 if (*value == NULL) {
1861 debug_error(DEBUG, "error: calloc failed");
1862 return MMFILE_UTIL_FAIL;
1864 strncpy(*value, value_start, value_length);
1866 return MMFILE_UTIL_SUCCESS;
1869 int mm_file_get_bool_value_from_xml_string(const char* xml_str, const char* param_name, bool* value)
1871 char *value_start = NULL;
1872 char *value_end = NULL;
1873 int value_length = 0;
1875 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1876 debug_error(DEBUG, "error: incorrect or non-existing parameter");
1877 return MMFILE_UTIL_FAIL;
1880 value_start = strstr(xml_str, param_name) + strlen(param_name);
1881 while ((value_start != NULL) && ((value_start[0] == ' ') || (value_start[0] == '\t')))
1884 value_end = strchr(value_start, '<');
1885 if (value_end == NULL) {
1886 debug_error(DEBUG, "error: incorrect XML.");
1887 return MMFILE_UTIL_FAIL;
1890 value_length = value_end - value_start;
1891 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1894 if (value_length < 1) {
1895 debug_error(DEBUG, "error: empty XML value or incorrect range");
1896 return MMFILE_UTIL_FAIL;
1899 *value = strstr(value_start, "true") ? true : false;
1901 return MMFILE_UTIL_SUCCESS;
1904 int ParseSpatialVideoMetadataFromXMLString(const char *xmlStr, MMFileFormatContext *formatContext)
1906 const char is_spherical_str[] = "<GSpherical:Spherical>";
1907 const char is_stitched_str[] = "<GSpherical:Stitched>";
1908 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
1909 const char projection_type_str[] = "<GSpherical:ProjectionType>";
1910 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
1911 const char source_count_str[] = "<GSpherical:SourceCount>";
1912 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
1913 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
1914 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
1915 const char timestamp_str[] = "<GSpherical:Timestamp>";
1916 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
1917 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
1918 const char cropped_area_image_width_str[] = "<GSpherical:CroppedAreaImageWidthPixels>";
1919 const char cropped_area_image_height_str[] = "<GSpherical:CroppedAreaImageHeightPixels>";
1920 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
1921 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
1923 mm_file_get_bool_value_from_xml_string(xmlStr, is_spherical_str, (bool*)&formatContext->isSpherical);
1924 mm_file_get_bool_value_from_xml_string(xmlStr, is_stitched_str, (bool*)&formatContext->isStitched);
1926 debug_msg(RELEASE, "isSpherical = %d", formatContext->isSpherical);
1927 debug_msg(RELEASE, "isStitched = %d", formatContext->isStitched);
1929 if (formatContext->isSpherical && formatContext->isStitched) {
1930 mm_file_get_string_value_from_xml_string(xmlStr, stitching_software_str, &formatContext->stitchingSoftware);
1931 mm_file_get_string_value_from_xml_string(xmlStr, projection_type_str, &formatContext->projectionType);
1932 mm_file_get_string_value_from_xml_string(xmlStr, stereo_mode_str, &formatContext->stereoMode);
1933 mm_file_get_int_value_from_xml_string(xmlStr, source_count_str, &formatContext->sourceCount);
1934 mm_file_get_int_value_from_xml_string(xmlStr, init_view_heading_str, &formatContext->initViewHeading);
1935 mm_file_get_int_value_from_xml_string(xmlStr, init_view_pitch_str, &formatContext->initViewPitch);
1936 mm_file_get_int_value_from_xml_string(xmlStr, init_view_roll_str, &formatContext->initViewRoll);
1937 mm_file_get_int_value_from_xml_string(xmlStr, timestamp_str, &formatContext->timestamp);
1938 mm_file_get_int_value_from_xml_string(xmlStr, full_pano_width_str, &formatContext->fullPanoWidth);
1939 mm_file_get_int_value_from_xml_string(xmlStr, full_pano_height_str, &formatContext->fullPanoHeight);
1940 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_width_str, &formatContext->croppedAreaImageWidth);
1941 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_height_str, &formatContext->croppedAreaImageHeight);
1942 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_left_str, &formatContext->croppedAreaLeft);
1943 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_top_str, &formatContext->croppedAreaTop);
1945 debug_msg(RELEASE, "stitchingSoftware = %s", formatContext->stitchingSoftware);
1946 debug_msg(RELEASE, "projectionType = %s", formatContext->projectionType);
1947 debug_msg(RELEASE, "stereoMode = %s", formatContext->stereoMode);
1948 debug_msg(RELEASE, "sourceCount %d", formatContext->sourceCount);
1949 debug_msg(RELEASE, "initViewHeading = %d", formatContext->initViewHeading);
1950 debug_msg(RELEASE, "initViewPitch = %d", formatContext->initViewPitch);
1951 debug_msg(RELEASE, "initViewRoll = %d", formatContext->initViewRoll);
1952 debug_msg(RELEASE, "timestamp = %d", formatContext->timestamp);
1953 debug_msg(RELEASE, "fullPanoWidthPixels = %d", formatContext->fullPanoWidth);
1954 debug_msg(RELEASE, "fullPanoHeightPixels = %d", formatContext->fullPanoHeight);
1955 debug_msg(RELEASE, "croppedAreaImageWidth = %d", formatContext->croppedAreaImageWidth);
1956 debug_msg(RELEASE, "croppedAreaImageHeight = %d", formatContext->croppedAreaImageHeight);
1957 debug_msg(RELEASE, "croppedAreaLeft = %d", formatContext->croppedAreaLeft);
1958 debug_msg(RELEASE, "croppedAreaTop = %d", formatContext->croppedAreaTop);
1961 return MMFILE_UTIL_SUCCESS;
1964 #define BIG_CONTENT_BOX_SIZE_LEN 8
1966 int MMFileUtilGetMetaDataFromMKV(MMFileFormatContext *formatContext)
1968 MMFileIOHandle *fp = NULL;
1969 int probe_size = 10000;
1970 unsigned char *buffer = NULL;
1973 long long file_size = 0;
1975 MMFILE_WEBM_PROJ_V2_BOX v2box = { 0, };
1976 MMFILE_WEBM_EQUI_PROJ_V2_BOX equi = { 0, };
1977 MMFILE_WEBM_CBMP_PROJ_V2_BOX cbmp = { 0, };
1978 MMFILE_WEBM_POSE_ELEMENT_V2_BOX pose = { 0, };
1980 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
1981 if (ret == MMFILE_UTIL_FAIL) {
1982 debug_error(DEBUG, "error: mmfile_open");
1986 file_size = mmfile_seek(fp, 0, SEEK_END);
1987 if (file_size == MMFILE_UTIL_FAIL) {
1988 debug_error(DEBUG, "mmfile operation failed");
1992 probe_size = (file_size > probe_size) ? probe_size : file_size;
1993 buffer = (unsigned char *)malloc(probe_size * sizeof(unsigned char));
1995 debug_error(DEBUG, "malloc failed");
1999 ret = mmfile_seek(fp, 0, SEEK_SET);
2000 if (ret == MMFILE_UTIL_FAIL) {
2001 debug_error(DEBUG, "mmfile operation failed");
2005 ret = mmfile_read(fp, buffer, probe_size * sizeof(unsigned char));
2006 if (ret == MMFILE_UTIL_FAIL) {
2007 debug_error(DEBUG, "mmfile operation failed");
2011 /* FIXME (m.alieksieie): It's better to use some EBML parser here*/
2012 for (i = 0; i + 3 < probe_size; ++i) {
2013 if (*(unsigned int *)(buffer + i) == FOURCC('e', 'q', 'u', 'i') ||
2014 *(unsigned int *)(buffer + i) == FOURCC('c', 'b', 'm', 'p')) {
2015 debug_msg(DEBUG, "projection data found at offset %d bytes", i);
2020 if (i + 3 == probe_size) {
2021 debug_msg(DEBUG, "projection info wasn't found");
2022 ret = MMFILE_UTIL_SUCCESS;
2026 if ((i - (int)sizeof(MMFILE_WEBM_PROJ_V2_BOX)) < 0) {
2027 debug_error(DEBUG, "error: invalid supposed projection info location");
2028 ret = MMFILE_UTIL_FAIL;
2032 ret = mmfile_seek(fp, i - sizeof(MMFILE_WEBM_PROJ_V2_BOX), SEEK_SET);
2033 if (ret == MMFILE_UTIL_FAIL) {
2034 debug_error(DEBUG, "error: failed to seek to the supposed projection info location");
2038 ret = mmfile_read(fp, (unsigned char *)&v2box, sizeof(MMFILE_WEBM_PROJ_V2_BOX));
2039 if (ret == MMFILE_UTIL_FAIL) {
2040 debug_error(DEBUG, "mmfile operation failed");
2044 if (v2box.proj_type_box_value == PROJECTION_TYPE_EQUI) {
2045 debug_msg(DEBUG, "Equirectangular projection is used");
2047 ret = mmfile_read(fp, (unsigned char *)&equi, sizeof(MMFILE_WEBM_EQUI_PROJ_V2_BOX));
2048 if (ret == MMFILE_UTIL_FAIL) {
2049 debug_error(DEBUG, "error: failed to read equirectangular element");
2052 if (strncmp((char *)equi.proj_priv_box_name, "equi", 4) == 0) {
2053 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_top);
2054 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_bottom);
2055 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_left);
2056 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_right);
2058 debug_error(DEBUG, "error: failed to read equirectangular element");
2059 ret = MMFILE_UTIL_SUCCESS;
2063 if (v2box.proj_type_box_value == PROJECTION_TYPE_CBMP) {
2064 debug_msg(DEBUG, "Cubemap projection is used");
2066 ret = mmfile_read(fp, (unsigned char *)&cbmp, sizeof(MMFILE_WEBM_CBMP_PROJ_V2_BOX));
2067 if (ret == MMFILE_UTIL_FAIL) {
2068 debug_error(DEBUG, "error: failed to read cubemap element");
2071 if (strncmp((char *)cbmp.proj_priv_box_name, "cbmp", 4) == 0) {
2072 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_layout);
2073 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_padding);
2075 debug_error(DEBUG, "error: failed to read cubemap element");
2076 ret = MMFILE_UTIL_FAIL;
2081 ret = mmfile_read(fp, (unsigned char *)&pose, sizeof(MMFILE_WEBM_POSE_ELEMENT_V2_BOX));
2082 if (ret == MMFILE_UTIL_FAIL) {
2083 debug_error(DEBUG, "error: failed to read pose info");
2087 if (pose.pose_yaw_element_id == POSE_YAW_ELEMENT_ID) {
2088 formatContext->poseYawV2 = (uint)mmfile_io_be_float32(pose.pose_yaw_element_value);
2090 debug_error(DEBUG, "error: failed to pose yaw element");
2091 ret = MMFILE_UTIL_FAIL;
2094 if (pose.pose_pitch_element_id == POSE_PITCH_ELEMENT_ID) {
2095 formatContext->posePitchV2 = (uint)mmfile_io_be_float32(pose.pose_pitch_element_value);
2097 debug_error(DEBUG, "error: failed to pose pitch element");
2098 ret = MMFILE_UTIL_FAIL;
2101 if (pose.pose_roll_element_id == POSE_ROLL_ELEMENT_ID) {
2102 formatContext->poseRollV2 = (uint)mmfile_io_be_float32(pose.pose_roll_element_value);
2104 debug_error(DEBUG, "error: failed to pose roll element");
2105 ret = MMFILE_UTIL_FAIL;
2118 int MMFileUtilGetMetaDataFromMP4(MMFileFormatContext *formatContext)
2120 MMFileIOHandle *fp = NULL;
2123 unsigned long long chunk_size = 0;
2124 long long moov_end = 0;
2125 MMFILE_MP4_BASIC_BOX_HEADER basic_header = {0, };
2126 int junk_counter = 0;
2128 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
2129 if (ret == MMFILE_UTIL_FAIL) {
2130 debug_error(DEBUG, "error: mmfile_open");
2134 basic_header.start_offset = mmfile_tell(fp);
2136 if (g_junk_counter_limit == 0)
2137 g_junk_counter_limit = mmfile_get_int_from_ini(MMFILE_INI_JUNKCNTLIMIT, MMFILE_DEFAULT_JUNKCNTLIMIT);
2139 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)) {
2140 basic_header.size = mmfile_io_be_uint32(basic_header.size);
2141 basic_header.type = mmfile_io_le_uint32(basic_header.type);
2143 if (basic_header.size == 0) {
2144 debug_warning(DEBUG, "header is invalid.");
2145 basic_header.size = readed;
2146 basic_header.type = 0;
2147 chunk_size = basic_header.size;
2149 if ((moov_end != 0) && (moov_end < basic_header.start_offset)) {
2150 debug_msg(DEBUG, "found junk data but moov data already was extracted, so junk counter will be increase: %d", junk_counter);
2153 /* stop the loop for junk case. */
2154 if ((g_junk_counter_limit > 0) && (junk_counter > g_junk_counter_limit)) {
2155 debug_msg(DEBUG, "stop the loop by junk-data checker");
2156 ret = MMFILE_UTIL_FAIL;
2160 } else if (basic_header.size == 1) {
2162 unsigned char temp[BIG_CONTENT_BOX_SIZE_LEN] = {0, };
2163 unsigned long long size = 0;
2165 mmfile_read(fp, (unsigned char *)&temp, BIG_CONTENT_BOX_SIZE_LEN);
2167 for (i = 0; i < BIG_CONTENT_BOX_SIZE_LEN; i++)
2168 size |= (unsigned long long)temp[i] << (BIG_CONTENT_BOX_SIZE_LEN - 1 - i) * BIG_CONTENT_BOX_SIZE_LEN;
2172 chunk_size = basic_header.size;
2176 switch (basic_header.type) {
2177 case FOURCC('m', 'o', 'o', 'v'): {
2178 debug_msg(RELEASE, "MPEG4: [moov] SIZE: [%lld]Byte", chunk_size);
2179 moov_end = basic_header.start_offset + chunk_size;
2182 case FOURCC('u', 'd', 't', 'a'): {
2183 debug_msg(RELEASE, "MPEG4: [udat] SIZE: [%lld]Byte", chunk_size);
2186 /*/////////////////////////////////////////////////////////////// */
2187 /* Extracting Tag Data // */
2188 /*/////////////////////////////////////////////////////////////// */
2189 case FOURCC('t', 'i', 't', 'l'): {
2190 debug_msg(RELEASE, "MPEG4: [titl] SIZE: [%lld]Byte", chunk_size);
2191 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_TITLE);
2194 case FOURCC('d', 's', 'c', 'p'): {
2195 debug_msg(RELEASE, "MPEG4: [dscp] SIZE: [%lld]Byte", chunk_size);
2196 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_CAPTION);
2199 case FOURCC('c', 'p', 'r', 't'): {
2200 debug_msg(RELEASE, "MPEG4: [cprt] SIZE: [%lld]Byte", chunk_size);
2201 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_COPYRIGHT);
2204 case FOURCC('p', 'e', 'r', 'f'): {
2205 debug_msg(RELEASE, "MPEG4: [perf] SIZE: [%lld]Byte", chunk_size);
2206 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_PERFORMER);
2209 case FOURCC('a', 'u', 't', 'h'): {
2210 debug_msg(RELEASE, "MPEG4: [auth] SIZE: [%lld]Byte", chunk_size);
2211 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_AUTHOR);
2214 case FOURCC('g', 'n', 'r', 'e'): {
2215 debug_msg(RELEASE, "MPEG4: [gnre] SIZE: [%lld]Byte", chunk_size);
2216 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_GENRE);
2219 case FOURCC('a', 'l', 'b', 'm'): {
2220 debug_msg(RELEASE, "MPEG4: [albm] SIZE: [%lld]Byte", chunk_size);
2221 GetAlbumFromAlbumTagBox(formatContext, fp, &basic_header);
2224 case FOURCC('y', 'r', 'r', 'c'): {
2225 debug_msg(RELEASE, "MPEG4: [yrrc] SIZE: [%lld]Byte", chunk_size);
2226 GetYearFromYearTagBox(formatContext, fp, &basic_header);
2229 case FOURCC('r', 't', 'n', 'g'): {
2230 debug_msg(RELEASE, "MPEG4: [rtng] SIZE: [%lld]Byte", chunk_size);
2231 GetRatingFromRatingTagBox(formatContext, fp, &basic_header); /* not use */
2234 case FOURCC('c', 'l', 's', 'f'): {
2235 debug_msg(RELEASE, "MPEG4: [clsf] SIZE: [%lld]Byte", chunk_size);
2236 GetClassficationFromClsfTagBox(formatContext, fp, &basic_header);
2239 case FOURCC('k', 'y', 'w', 'd'): {
2240 debug_msg(RELEASE, "MPEG4: [kywd] SIZE: [%lld]Byte", chunk_size);
2241 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2244 case FOURCC('l', 'o', 'c', 'i'): {
2245 debug_msg(RELEASE, "MPEG4: [loci] SIZE: [%lld]Byte", chunk_size);
2246 GetLocationFromLociTagBox(formatContext, fp, &basic_header);
2249 /* Check smta in user data field (moov) to be compatible with android */
2250 case FOURCC('s', 'm', 't', 'a'): {
2251 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte", chunk_size);
2252 GetSAUTInfoFromSMTATagBox(formatContext, fp, &basic_header);
2255 /* Check cdis in user data field (moov) to be compatible with android */
2256 case FOURCC('c', 'd', 'i', 's'): {
2257 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte", chunk_size);
2258 GetValueFromCDISTagBox(formatContext, fp, &basic_header);
2261 /*/////////////////////////////////////////////////////////////// */
2262 /* Extracting ID3 Tag Data // */
2263 /*/////////////////////////////////////////////////////////////// */
2264 case FOURCC('m', 'e', 't', 'a'): {
2265 debug_msg(RELEASE, "MPEG4: [meta] SIZE: [%lld]Byte", chunk_size);
2266 GetTagFromMetaBox(formatContext, fp, &basic_header);
2270 case FOURCC('t', 'r', 'a', 'k'): {
2271 debug_msg(RELEASE, "MPEG4: [trak] SIZE: [%lld]Byte", chunk_size);
2274 case FOURCC('u', 'u', 'i', 'd'): {
2275 unsigned long uuid[4] = {0, };
2277 debug_msg(RELEASE, "MPEG4: [uuid] SIZE: [%lld]Byte", chunk_size);
2279 mmfile_read(fp, (unsigned char *)uuid, sizeof(uuid));
2281 if (mmfile_io_be_uint32(uuid[0]) == 0xffcc8263
2282 && mmfile_io_be_uint32(uuid[1]) == 0xf8554a93
2283 && mmfile_io_be_uint32(uuid[2]) == 0x8814587a
2284 && mmfile_io_be_uint32(uuid[3]) == 0x02521fdd) {
2286 str = (char *)malloc(basic_header.size);
2289 memset(str, 0, basic_header.size);
2290 mmfile_read(fp, (unsigned char *)str, basic_header.size);
2292 /* The block is superseded */
2293 if (strstr(str, "<GSpherical:Spherical>true</GSpherical:Spherical>"))
2294 formatContext->is_360 = 1;
2296 formatContext->is_360 = 0;
2297 /* Image can be stitched even if it is not spherical */
2298 if (formatContext->is_360 == 1) {
2299 if (strstr(str, "<GSpherical:Stitched>true</GSpherical:Stitched>"))
2300 formatContext->stitched = MMFILE_360_STITCHED;
2302 formatContext->stitched = MMFILE_360_NON_STITCHED;
2304 /* Image can be stitched or non-stitched. Usage of some 3rd value is superfluous */
2305 formatContext->stitched = MMFILE_360_NONE;
2309 debug_msg(RELEASE, "Extracting tags from UUID XML string %s", str);
2311 ParseSpatialVideoMetadataFromXMLString(str, formatContext);
2314 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2318 case FOURCC('m', 'd', 'i', 'a'): {
2319 debug_msg(RELEASE, "MPEG4: [mdia] SIZE: [%lld]Byte", chunk_size);
2322 case FOURCC('m', 'i', 'n', 'f'): {
2323 debug_msg(RELEASE, "MPEG4: [minf] SIZE: [%lld]Byte", chunk_size);
2326 case FOURCC('s', 't', 'b', 'l'): {
2327 debug_msg(RELEASE, "MPEG4: [stbl] SIZE: [%lld]Byte", chunk_size);
2330 case FOURCC('s', 't', 's', 'd'): {
2331 debug_msg(RELEASE, "MPEG4: [stsd] SIZE: [%lld]Byte", chunk_size);
2334 case FOURCC('m', 'p', '4', 'a'): {
2335 debug_msg(RELEASE, "MPEG4: [mp4a] SIZE: [%lld]Byte", chunk_size);
2336 GetSA3DInfoFromMP4ATagBox(formatContext, fp, &basic_header);
2337 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2340 case FOURCC('a', 'v', 'c', '1'): {
2341 debug_msg(RELEASE, "MPEG4: [avc1] SIZE: [%lld]Byte (offset: %lld)", chunk_size, basic_header.start_offset);
2342 GetVideoV2MetadataFromAvc1TagBox(formatContext, fp, &basic_header);
2346 debug_msg(RELEASE, "4CC: Not Support [%c%c%c%c]. So skip it. Size [%lld Byte]",
2347 ((char *)&basic_header.type)[0], ((char *)&basic_header.type)[1],
2348 ((char *)&basic_header.type)[2], ((char *)&basic_header.type)[3], chunk_size);
2349 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2354 if (ret == MMFILE_UTIL_FAIL) {
2355 debug_error(DEBUG, "mmfile operation is error");
2360 long long new_pos = mmfile_tell(fp);
2362 if ((moov_end == 0) && (new_pos <= basic_header.start_offset)) {
2363 debug_error(DEBUG, "Wrong position");
2364 ret = MMFILE_UTIL_FAIL;
2367 basic_header.start_offset = new_pos;
2376 static bool __get_genre_num(const char *buffer, int *si)
2378 int len = strlen(buffer);
2379 char *tmp_genre = NULL;
2381 /* Genre format: (###), ### */
2382 if (len > 6 || len == 0)
2387 if (!g_ascii_isdigit(buffer[0]))
2390 if (!g_ascii_isdigit(buffer[1]))
2394 /* Remove bind () if exist */
2395 tmp_genre = g_strdup(buffer);
2396 if (g_str_has_prefix(tmp_genre, "(") && g_str_has_suffix(tmp_genre, ")"))
2399 *si = atoi(tmp_genre);
2405 static bool make_characterset_array(char ***charset_array)
2407 char *locale = MMFileUtilGetLocale();
2409 *charset_array = calloc(AV_ID3V2_MAX, sizeof(char *));
2411 if (*charset_array == NULL) {
2412 debug_error(DEBUG, "calloc failed ");
2418 if (locale != NULL) {
2419 (*charset_array)[AV_ID3V2_ISO_8859] = strdup(locale);
2421 debug_error(DEBUG, "get locale failed");
2422 (*charset_array)[AV_ID3V2_ISO_8859] = NULL;
2425 (*charset_array)[AV_ID3V2_UTF16] = strdup("UCS2");
2426 (*charset_array)[AV_ID3V2_UTF16_BE] = strdup("UTF16-BE");
2427 (*charset_array)[AV_ID3V2_UTF8] = strdup(MMFILE_CODESET_UTF8);
2432 static bool release_characterset_array(char **charset_array)
2436 for (i = 0; i < AV_ID3V2_MAX; i++) {
2437 if (charset_array[i] != NULL) {
2438 free(charset_array[i]);
2439 charset_array[i] = NULL;
2443 if (charset_array != NULL) {
2444 free(charset_array);
2445 charset_array = NULL;
2451 static void init_content_info(AvFileContentInfo *pInfo)
2455 for(i = 0; i < AV_ID3TAG_MAX; i++)
2456 pInfo->tagInfo[i].value = NULL;
2458 pInfo->imageInfo.bURLInfo = false;
2459 pInfo->imageInfo.pImageBuf = NULL;
2460 pInfo->imageInfo.imageLen = 0;
2463 static void __id3tag_skip_newline(unsigned char *pTagVal, int *nTagLen, int *offset)
2465 /* skip newline in text encoding of ID3 tag frame */
2466 while ((NEWLINE_OF_UTF16(pTagVal + *offset) || NEWLINE_OF_UTF16_R(pTagVal + *offset)) && *nTagLen > 4) {
2472 static int __id3tag_get_text_encoding_v222(unsigned char *pTagVal, int offset)
2474 if (pTagVal[offset - 1] == 0x00)
2475 return AV_ID3V2_ISO_8859;
2477 return AV_ID3V2_UTF16;
2480 static int __id3tag_get_text_encoding_v223(unsigned char *pTagVal, int *npTagLen, int nTextEnc, int *offset)
2482 if ((IS_ENCODEDBY_UTF16(pTagVal + *offset) || IS_ENCODEDBY_UTF16_R(pTagVal + *offset)) && *npTagLen > 2) {
2483 __id3tag_skip_newline(pTagVal, npTagLen, offset);
2485 if (IS_ENCODEDBY_UTF16(pTagVal + *offset) && (*npTagLen > 2)) {
2488 return AV_ID3V2_UTF16;
2489 } else if (IS_ENCODEDBY_UTF16_R(pTagVal + *offset) && (*npTagLen > 2)) {
2492 return AV_ID3V2_UTF16_BE;
2493 } else if (IS_ENCODEDBY_UTF16(pTagVal + *offset + 1) && (*npTagLen > 3)) {
2496 return AV_ID3V2_UTF16;
2497 } else if (IS_ENCODEDBY_UTF16_R(pTagVal + *offset + 1) && (*npTagLen > 3)) {
2500 return AV_ID3V2_UTF16_BE;
2502 debug_msg(RELEASE, "id3tag never get here!!");
2503 return nTextEnc; /* default bypass */
2506 while ((pTagVal[*offset] < 0x20) && (*offset < *npTagLen)) { /* text string encoded by ISO-8859-1 */
2510 return AV_ID3V2_ISO_8859;
2514 static int __id3tag_get_text_encoding_v224(unsigned char *pTagVal, int *npTagLen, int nTextEnc, int *offset)
2516 if (nTextEnc == AV_ID3V2_UTF16 || nTextEnc == AV_ID3V2_UTF16_BE) {
2517 __id3tag_skip_newline(pTagVal, npTagLen, offset);
2519 if ((IS_ENCODEDBY_UTF16(pTagVal + *offset) || IS_ENCODEDBY_UTF16_R(pTagVal + *offset)) && *npTagLen > 2) {
2522 return AV_ID3V2_UTF16;
2524 debug_msg(RELEASE, "id3tag never get here!!");
2525 return nTextEnc; /* default bypass */
2527 } else if (nTextEnc == AV_ID3V2_UTF8) {
2528 while (pTagVal[*offset] < 0x20 && (*offset < *npTagLen)) { /* text string encoded by UTF-8 */
2532 return AV_ID3V2_UTF8;
2534 while (pTagVal[*offset] < 0x20 && (*offset < *npTagLen)) { /* text string encoded by ISO-8859-1 */
2538 return AV_ID3V2_ISO_8859;
2542 static void __id3tag_parse_SYLT(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int textEncodingType, int offset)
2546 int copy_start_pos = offset;
2547 AvSynclyricsInfo *synclyrics_info = NULL;
2548 GList *synclyrics_info_list = NULL;
2550 if (nTagLen < MMFILE_SYNC_LYRIC_INFO_MIN_LEN) {
2551 debug_msg(RELEASE, "failed to get Synchronised lyrics Info realCpyFramNum(%d)", nTagLen);
2555 if ((textEncodingType == AV_ID3V2_UTF16) || (textEncodingType == AV_ID3V2_UTF16_BE)) {
2556 debug_warning(DEBUG, "[%d] not implemented", textEncodingType);
2560 for (idx = 0; idx < nTagLen; idx++) {
2561 if (pTagVal[offset + idx] == 0x00) {
2562 synclyrics_info = g_new0(AvSynclyricsInfo, 1);
2563 if (textEncodingType == AV_ID3V2_UTF8)
2564 synclyrics_info->lyric_info = g_memdup2(pTagVal + copy_start_pos, copy_len + 1);
2566 synclyrics_info->lyric_info = mmfile_convert_to_utf8((const char *)&pTagVal[copy_start_pos], copy_len, pCharSet);
2568 synclyrics_info->time_info = (unsigned long)pTagVal[offset + idx + 1] << 24 | (unsigned long)pTagVal[offset + idx + 2] << 16 | (unsigned long)pTagVal[offset + idx + 3] << 8 | (unsigned long)pTagVal[offset + idx + 4];
2570 copy_start_pos = offset + idx + 1;
2571 debug_msg(RELEASE, "[%lu][%s] idx[%d], copy_len[%d] copy_start_pos[%d]", synclyrics_info->time_info, synclyrics_info->lyric_info, idx, copy_len, copy_start_pos);
2573 synclyrics_info_list = g_list_append(synclyrics_info_list, synclyrics_info);
2578 pInfo->pSyncLyrics = synclyrics_info_list;
2581 static bool __id3tag_parse_PIC_format(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2583 unsigned int idx = 0;
2585 /* get the mime type of Attached PICture, it is text string */
2587 if (pTagVal[*offset] == '\0') {
2588 debug_msg(RELEASE, "The picture format of PIC is not included");
2592 /* init ext variable */
2593 memset(pInfo->imageInfo.imageExt, 0, sizeof(pInfo->imageInfo.imageExt));
2595 while ((idx < MP3_ID3_IMAGE_EXT_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2596 pInfo->imageInfo.imageExt[idx] = pTagVal[idx];
2605 static bool __id3tag_parse_APIC_mimetype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2607 unsigned int idx = 0;
2608 const char *MIME_PRFIX = "image/";
2610 /* get the mime type of Attached PICture, it is text string */
2612 if (pTagVal[*offset] == '\0') {
2613 pInfo->imageInfo.imgMimetypeLen = 0;
2614 debug_msg(RELEASE, "The MIME type of APIC is not included");
2618 /* init mimetype variable */
2619 memset(pInfo->imageInfo.imageMIMEType, 0, sizeof(pInfo->imageInfo.imageMIMEType));
2621 while ((idx < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2622 pInfo->imageInfo.imageMIMEType[idx] = pTagVal[idx];
2625 pInfo->imageInfo.imgMimetypeLen = idx;
2629 if (strncmp(pInfo->imageInfo.imageMIMEType, MIME_PRFIX, strlen(MIME_PRFIX)) != 0) {
2630 pInfo->imageInfo.imgMimetypeLen = 0;
2631 debug_error(DEBUG, "MIME type(%s) is not image", pInfo->imageInfo.imageMIMEType);
2635 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2636 debug_msg(RELEASE, "pTagVal[offset](%d) mimetype is not NULL terminated! realCpyFrameNum - offset(%d)",
2637 pTagVal[*offset], nTagLen - *offset);
2641 (*offset)++;/* end of MIME('\0', 1byte) */
2642 debug_msg(RELEASE, "after scaning Mime type offset(%d) value!", *offset);
2647 static void __id3tag_parse_APIC_pictype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2649 /* get the picture type of Attached PICture, it is 1byte(0xff) */
2651 if (pTagVal[*offset] < MP3_ID3V2_PICTURE_TYPE_MAX)
2652 pInfo->imageInfo.pictureType = pTagVal[*offset];
2654 debug_msg(RELEASE, "APIC image has invalid picture type(0x%x)", pTagVal[*offset]);
2656 (*offset)++;/* PictureType(1byte) */
2657 debug_msg(RELEASE, "after scaning PictureType(%d) offset(%d) value!", pInfo->imageInfo.pictureType, *offset);
2660 static void __id3tag_parse_APIC_desc(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int *offset)
2662 /* get the description of Attached PICture, it is text string */
2665 unsigned int tag_len = 0;
2666 char *tmp_desc = NULL;
2668 if (pTagVal[*offset] == 0x0) {
2669 debug_msg(RELEASE, "The description of APIC is not included!!!");
2674 if (pTagVal[*offset + idx] == '\0') {
2675 if (nTagLen < (*offset + idx)) {
2676 debug_error(DEBUG, "End of APIC Tag %d %d %d", nTagLen, *offset, idx);
2679 /* check end of image description */
2680 if ((pTagVal[*offset + idx + 1] == gTagJPEGHeader[0]) ||
2681 (pTagVal[*offset + idx + 1] == gTagPNGHeader[0])) {
2682 debug_msg(RELEASE, "length of description (%d)", idx);
2689 tag_len = idx + 1; /* length of description + '\0' */
2691 tmp_desc = g_memdup2(pTagVal + *offset, tag_len);
2693 /* convert description */
2694 pInfo->imageInfo.imageDescription = mmfile_convert_to_utf8(tmp_desc, tag_len, pCharSet);
2695 mmfile_free(tmp_desc);
2696 debug_msg(RELEASE, "new_desc %s", pInfo->imageInfo.imageDescription);
2699 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2700 debug_msg(RELEASE, "pTagVal[offset](%d) description is not NULL terminated! realCpyFrameNum - offset(%d)",
2701 pTagVal[*offset], nTagLen - *offset);
2704 (*offset)++; /* end of desceription(1byte) */
2707 static void __id3tag_parse_APIC_picture(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2709 /* get the picture of Attached PICture, it is binary data */
2711 /* some content has useless '\0' in front of picture data */
2712 while (pTagVal[*offset] == '\0') {
2716 debug_msg(RELEASE, "after scaning APIC description offset(%d) value!", *offset);
2718 if (nTagLen <= *offset) {
2719 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - offset(%d)", nTagLen, *offset);
2723 pInfo->imageInfo.imageLen = nTagLen - *offset;
2724 pInfo->imageInfo.pImageBuf = g_memdup2(pTagVal + *offset, pInfo->imageInfo.imageLen);
2726 /* if mimetype is "-->", image date has an URL */
2727 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
2728 pInfo->imageInfo.bURLInfo = true;
2731 static bool _mm_file_id3tag_parse_PIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2733 /* current position to read pTagVal */
2736 debug_fenter(RELEASE);
2738 if (!__id3tag_parse_PIC_format(pInfo, pTagVal, &offset)) {
2739 debug_msg(RELEASE, "PIC is not valid");
2743 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2744 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2745 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2747 debug_fleave(RELEASE);
2752 static bool _mm_file_id3tag_parse_APIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2756 debug_fenter(RELEASE);
2758 if (!__id3tag_parse_APIC_mimetype(pInfo, pTagVal, nTagLen, &offset)) {
2759 debug_msg(RELEASE, "APIC is not valid");
2763 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2764 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2765 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2767 debug_fleave(RELEASE);
2772 static char * __id3tag_get_v110(const char *buf, ssize_t len, const char *locale)
2774 char *tmp_str = NULL;
2776 mm_file_retv_if_fails(buf, NULL);
2777 mm_file_retv_if_fails(locale, NULL);
2779 tmp_str = mmfile_convert_to_utf8(buf, len, locale);
2781 /* ID3v1 tag need check length or space. */
2782 if (tmp_str && (strlen(tmp_str) == 0 || g_ascii_isspace(tmp_str[0])))
2783 mmfile_free(tmp_str);
2788 bool mm_file_id3tag_parse_v110(AvFileContentInfo *pInfo, unsigned char *buffer)
2790 const char *locale = MMFileUtilGetLocale();
2792 debug_msg(RELEASE, "ID3tag v110--------------------------------------------------------------");
2794 if (!pInfo->tagInfo[AV_ID3TAG_TITLE].value) {
2795 pInfo->tagInfo[AV_ID3TAG_TITLE].value = __id3tag_get_v110((const char *)&buffer[3], MP3_ID3_TITLE_LENGTH, locale);
2797 debug_msg(RELEASE, "pInfo->pTitle returned =(%s)", pInfo->tagInfo[AV_ID3TAG_TITLE].value);
2800 if (!pInfo->tagInfo[AV_ID3TAG_ARTIST].value) {
2801 pInfo->tagInfo[AV_ID3TAG_ARTIST].value = __id3tag_get_v110((const char *)&buffer[33], MP3_ID3_ARTIST_LENGTH, locale);
2803 debug_msg(RELEASE, "pInfo->pArtist returned =(%s)", pInfo->tagInfo[AV_ID3TAG_ARTIST].value);
2806 if (!pInfo->tagInfo[AV_ID3TAG_ALBUM].value) {
2807 pInfo->tagInfo[AV_ID3TAG_ALBUM].value = __id3tag_get_v110((const char *)&buffer[63], MP3_ID3_ALBUM_LENGTH, locale);
2809 debug_msg(RELEASE, "pInfo->pAlbum returned =(%s)", pInfo->tagInfo[AV_ID3TAG_ALBUM].value);
2812 if (!pInfo->tagInfo[AV_ID3TAG_YEAR].value) {
2813 pInfo->tagInfo[AV_ID3TAG_YEAR].value = __id3tag_get_v110((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, locale);
2815 debug_msg(RELEASE, "pInfo->pYear returned =(%s)", pInfo->tagInfo[AV_ID3TAG_YEAR].value);
2818 if (!pInfo->tagInfo[AV_ID3TAG_COMMENT].value) {
2819 pInfo->tagInfo[AV_ID3TAG_COMMENT].value = __id3tag_get_v110((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, locale);
2821 debug_msg(RELEASE, "pInfo->pComment returned =(%s)", pInfo->tagInfo[AV_ID3TAG_COMMENT].value);
2824 if (!pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value) {
2825 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value = g_malloc0(ID3TAG_V110_TRACK_NUM_DIGIT);
2826 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value[ID3TAG_V110_TRACK_NUM_DIGIT - 1] = 0;
2827 snprintf(pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value, ID3TAG_V110_TRACK_NUM_DIGIT, "%04d", (int)buffer[126]);
2829 debug_msg(RELEASE, "pInfo->pTrackNum returned =(%s)", pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value);
2832 /*ID3V2 genre is stored in pInfo->tagInfo[AV_ID3TAG_GENRE].value */
2833 /*pInfo->genre is used when ID3V2 genre is invalid, or empty. */
2834 pInfo->genre = buffer[127];
2835 debug_msg(RELEASE, "pInfo->genre returned genre number (%d)", pInfo->genre);
2840 static AvID3TagList __get_tag_info_v222(const char *tag)
2846 n += tag[i++] - 'A' + 1;
2849 n += tag[2]; //num, char mixted
2851 for (i = 0; i < ID3TAG_NUM_V22; i++) {
2852 if (n == tag_info_v22[i].int_name) {
2853 return tag_info_v22[i].tag;
2857 debug_msg(RELEASE, "(%s) This Frame ID currently not Supports!!", tag);
2859 return AV_ID3TAG_MAX;
2862 static AvID3TagList __get_tag_info_v223(const char *tag)
2868 n += tag[i++] - 'A' + 1;
2871 n += tag[3]; //num, char mixted
2873 for (i = 0; i < ID3TAG_NUM_V23; i++) {
2874 if (n == tag_info_v23[i].int_name) {
2875 return tag_info_v23[i].tag;
2879 debug_msg(RELEASE, "(%s) This Frame ID currently not Supports!!", tag);
2881 return AV_ID3TAG_MAX;
2885 bool mm_file_id3tag_parse_v222(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
2887 unsigned long taglen = 0;
2888 unsigned long needToloopv2taglen;
2889 unsigned long oneFrameLen = 0;
2890 unsigned long curPos = 0;
2892 unsigned char *pExtContent = NULL;
2893 unsigned long purelyFramelen = 0;
2894 unsigned int encodingOffSet = 0;
2895 int realCpyFrameNum = 0;
2896 int textEncodingType = 0;
2897 char **charset_array = NULL;
2898 AvID3TagList tag_id = AV_ID3TAG_MAX;
2900 make_characterset_array(&charset_array);
2902 init_content_info(pInfo);
2904 taglen = pInfo->tagV2Info.tagLen;
2905 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
2906 curPos = MP3_TAGv2_HEADER_LEN;
2908 debug_msg(RELEASE, "ID3tag v222--------------------------------------------------------------");
2910 while (needToloopv2taglen > MP3_TAGv2_22_TXT_HEADER_LEN) {
2911 if (!g_ascii_isalnum(buffer[curPos]) ||
2912 !g_ascii_isalnum(buffer[curPos + 1]) ||
2913 !g_ascii_isalnum(buffer[curPos + 2]))
2916 memcpy(CompTmp, &buffer[curPos], 3);
2919 oneFrameLen = MP3_TAGv2_22_TXT_HEADER_LEN;
2920 oneFrameLen += (unsigned long)buffer[3 + curPos] << 16 | (unsigned long)buffer[4 + curPos] << 8
2921 | (unsigned long)buffer[5 + curPos];
2923 if (oneFrameLen > taglen - curPos)
2926 purelyFramelen = oneFrameLen - MP3_TAGv2_22_TXT_HEADER_LEN;
2927 curPos += oneFrameLen;
2929 tag_id = __get_tag_info_v222(CompTmp);
2930 if (tag_id != AV_ID3TAG_MAX && !pInfo->tagInfo[tag_id].value && purelyFramelen > 0) {
2931 if (buffer[curPos - purelyFramelen] == 0x00) {
2933 textEncodingType = AV_ID3V2_ISO_8859;
2934 } else if (buffer[curPos - purelyFramelen] == 0x01) {
2936 textEncodingType = AV_ID3V2_UTF16;
2939 /*in order to deliver valid string to MP */
2940 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen))
2943 if (purelyFramelen <= encodingOffSet) {
2944 debug_warning(DEBUG, "warning: invalid frame length %lu %u", purelyFramelen, encodingOffSet);
2948 realCpyFrameNum = purelyFramelen - encodingOffSet;
2949 mmfile_free(pExtContent);
2950 pExtContent = g_malloc0(realCpyFrameNum + 3);
2952 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
2955 case AV_ID3TAG_COMMENT:
2958 if (realCpyFrameNum <= 4) {
2959 debug_msg(RELEASE, "Too small to parse realCpyFrameNum(%d)", realCpyFrameNum);
2963 /*skip language data! */
2964 realCpyFrameNum -= 4;
2966 if (pExtContent[4] > 0x20 && (pExtContent[3] == 0x00 || pExtContent[3] == 0x01)) {
2967 textEncodingType = __id3tag_get_text_encoding_v222(pExtContent, 4);
2968 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)pExtContent, realCpyFrameNum, charset_array[textEncodingType]);
2970 debug_msg(RELEASE, "Failed to get tag: purelyFramelen - encodingOffSet(%lu)", purelyFramelen - encodingOffSet);
2975 case AV_ID3TAG_PICTURE:
2976 if (extract_artwork)
2977 _mm_file_id3tag_parse_PIC(pInfo, pExtContent, realCpyFrameNum, (const char*)charset_array[textEncodingType]);
2981 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)pExtContent, realCpyFrameNum, charset_array[textEncodingType]);
2985 if (pInfo->tagInfo[tag_id].value)
2986 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
2989 mmfile_free(pExtContent);
2990 memset(CompTmp, 0, 4);
2992 if (curPos >= taglen)
2995 needToloopv2taglen -= oneFrameLen;
2999 realCpyFrameNum = 0;
3000 textEncodingType = 0;
3005 release_characterset_array(charset_array);
3007 return (taglen > 0);
3010 static void __get_v223_encoding_info(const unsigned char *buffer,
3011 unsigned long position,
3012 unsigned long length,
3013 unsigned int *offset,
3016 unsigned int _offset = 0;
3018 if (!buffer || !offset || !type || (position < length))
3021 if (IS_ENCODEDBY_UTF16(buffer + (position - length))) {
3023 *type = AV_ID3V2_UTF16;
3026 if (IS_ENCODEDBY_UTF16_R(buffer + (position - length))) {
3028 *type = AV_ID3V2_UTF16_BE;
3031 if (IS_ENCODEDBY_UTF16(buffer + (position - length + 1))) {
3033 *type = AV_ID3V2_UTF16;
3036 if (IS_ENCODEDBY_UTF16_R(buffer + (position - length + 1))) {
3038 *type = AV_ID3V2_UTF16_BE;
3042 if (buffer[position - length] == 0x00) {
3045 while ((buffer[position - length + _offset] < 0x20) && (_offset < length))
3049 *type = AV_ID3V2_ISO_8859;
3052 bool mm_file_id3tag_parse_v223(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
3054 unsigned long taglen = 0;
3055 unsigned long needToloopv2taglen;
3056 unsigned long oneFrameLen = 0;
3057 unsigned long curPos = 0;
3059 unsigned char *pExtContent = NULL;
3060 unsigned long purelyFramelen = 0;
3061 unsigned int encodingOffSet = 0;
3062 int realCpyFrameNum = 0, tmp = 0;
3063 unsigned int textEncodingType = 0;
3064 char **charset_array = NULL;
3065 AvID3TagList tag_id = AV_ID3TAG_MAX;
3066 char *lang_info = NULL;
3068 make_characterset_array(&charset_array);
3070 init_content_info(pInfo);
3072 taglen = pInfo->tagV2Info.tagLen;
3073 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
3074 curPos = MP3_TAGv2_HEADER_LEN;
3076 debug_msg(RELEASE, "ID3tag v223--------------------------------------------------------------");
3078 /* check Extended Header */
3079 if (buffer[5] & 0x40) {
3080 /* if extended header exists, skip it*/
3081 int extendedHeaderLen = (unsigned long)buffer[10] << 21 | (unsigned long)buffer[11] << 14 | (unsigned long)buffer[12] << 7 | (unsigned long)buffer[13];
3083 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d", extendedHeaderLen);
3085 if (extendedHeaderLen > (int)(taglen - curPos)) {
3086 debug_error(DEBUG, "extended header too long.");
3088 curPos += extendedHeaderLen;
3093 while (needToloopv2taglen > MP3_TAGv2_23_TXT_HEADER_LEN) {
3094 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) ||
3095 !g_ascii_isalnum(buffer[curPos + 2]) || !g_ascii_isalnum(buffer[curPos + 3]))
3098 memcpy(CompTmp, &buffer[curPos], 4);
3101 oneFrameLen = MP3_TAGv2_23_TXT_HEADER_LEN;
3102 oneFrameLen += (unsigned long)buffer[4 + curPos] << 24 | (unsigned long)buffer[5 + curPos] << 16
3103 | (unsigned long)buffer[6 + curPos] << 8 | (unsigned long)buffer[7 + curPos];
3105 debug_msg(RELEASE, "----------------------------------------------------------------------------------------------------");
3107 if (oneFrameLen > taglen - curPos)
3110 purelyFramelen = oneFrameLen - MP3_TAGv2_23_TXT_HEADER_LEN;
3111 curPos += oneFrameLen;
3113 tag_id = __get_tag_info_v223(CompTmp);
3114 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value || purelyFramelen == 0)
3117 __get_v223_encoding_info(buffer, curPos, purelyFramelen, &encodingOffSet, &textEncodingType);
3118 if (purelyFramelen <= encodingOffSet) {
3119 debug_warning(DEBUG, "warning: invalid frame length %lu %u", purelyFramelen, encodingOffSet);
3123 mmfile_free(pExtContent);
3124 realCpyFrameNum = purelyFramelen - encodingOffSet;
3125 pExtContent = g_malloc0(realCpyFrameNum + 3);
3127 if (textEncodingType != AV_ID3V2_UTF16 && textEncodingType != AV_ID3V2_UTF16_BE) {
3128 if (CompTmp[0] == 'T' || (strcmp(CompTmp, "APIC") == 0)) {
3129 debug_msg(RELEASE, "get the new text encoding type");
3130 textEncodingType = buffer[curPos - purelyFramelen + encodingOffSet - 1];
3134 if (textEncodingType > AV_ID3V2_MAX) {
3135 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%d], FRAME[%s]", textEncodingType, (char *)CompTmp);
3139 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
3141 case AV_ID3TAG_COMMENT:
3142 if (realCpyFrameNum <= 3) {
3143 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)", realCpyFrameNum);
3146 realCpyFrameNum -= 3;
3149 /*pExtContent[tmp+1] value should't have encoding value */
3150 if (pExtContent[tmp] != 0x00 && pExtContent[tmp] != 0xFF && pExtContent[tmp] != 0xFE) {
3151 debug_msg(RELEASE, "failed to get Comment: tmp(%d), purelyFramelen - encodingOffSet(%lu)", tmp, purelyFramelen - encodingOffSet);
3154 textEncodingType = __id3tag_get_text_encoding_v223(pExtContent, &realCpyFrameNum, textEncodingType, &tmp);
3155 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)", tmp, textEncodingType, realCpyFrameNum);
3156 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&pExtContent[tmp], realCpyFrameNum, charset_array[textEncodingType]);
3159 case AV_ID3TAG_SYNCLYRICS:
3160 if (realCpyFrameNum <= 5) {
3161 debug_msg(RELEASE, "Synchronised lyrics too small to parse realCpyFrameNum(%d)", realCpyFrameNum);
3164 realCpyFrameNum -= 5;
3167 /*pExtContent[tmp+1] value should't have encoding value */
3168 if (pExtContent[tmp] != 0x00 && pExtContent[tmp] != 0xFF && pExtContent[tmp] != 0xFE) {
3169 debug_msg(RELEASE, "failed to get Synchronised lyrics Info tmp(%d), purelyFramelen - encodingOffSet(%lu)", tmp, purelyFramelen - encodingOffSet);
3171 textEncodingType = __id3tag_get_text_encoding_v223(pExtContent, &realCpyFrameNum, textEncodingType, &tmp);
3172 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)", tmp, textEncodingType, realCpyFrameNum);
3173 __id3tag_parse_SYLT(pInfo, pExtContent, realCpyFrameNum, charset_array[textEncodingType], textEncodingType, tmp);
3176 case AV_ID3TAG_UNSYNCLYRICS:
3177 lang_info = strndup((char *)pExtContent, 3);
3179 if (realCpyFrameNum <= 3) {
3180 debug_msg(RELEASE, "Unsynchronised lyrics too small to parse realCpyFrameNum(%d)", realCpyFrameNum);
3183 realCpyFrameNum -= 3;
3186 /*find start of lyrics */
3188 if (pExtContent[tmp] == 0x00) {
3189 if (pExtContent[tmp + 1] == 0x00) {
3190 realCpyFrameNum -= 2;
3200 /*pExtContent[tmp+1] value should't have encoding value */
3201 debug_msg(RELEASE, "tpExtContent[%d] %x", tmp, pExtContent[tmp]);
3202 if (pExtContent[tmp] != 0x00 && pExtContent[tmp] != 0xFF && pExtContent[tmp] != 0xFE) {
3203 debug_msg(RELEASE, "failed to get Unsynchronised lyrics Info tmp(%d), purelyFramelen - encodingOffSet(%lu)", tmp, purelyFramelen - encodingOffSet);
3205 textEncodingType = __id3tag_get_text_encoding_v223(pExtContent, &realCpyFrameNum, textEncodingType, &tmp);
3207 char *char_set = NULL;
3209 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)", tmp, textEncodingType, realCpyFrameNum);
3211 if (textEncodingType == AV_ID3V2_ISO_8859) {
3212 if (lang_info != NULL && !g_ascii_strcasecmp(lang_info, "KOR")) {
3213 char_set = strdup("EUC-KR");
3215 char_set = mmfile_get_charset((const char *)&pExtContent[tmp]);
3217 mmfile_free(lang_info);
3220 if (char_set == NULL) {
3221 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&pExtContent[tmp], realCpyFrameNum, charset_array[textEncodingType]);
3223 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&pExtContent[tmp], realCpyFrameNum, char_set);
3224 mmfile_free(char_set);
3226 mmfile_free(lang_info);
3229 case AV_ID3TAG_PICTURE:
3230 if (extract_artwork)
3231 _mm_file_id3tag_parse_APIC(pInfo, (unsigned char *)pExtContent, realCpyFrameNum, charset_array[textEncodingType]);
3235 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)pExtContent, realCpyFrameNum, charset_array[textEncodingType]);
3239 if (pInfo->tagInfo[tag_id].value)
3240 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
3242 mmfile_free(pExtContent);
3243 memset(CompTmp, 0, 4);
3245 if (curPos >= taglen)
3248 needToloopv2taglen -= oneFrameLen;
3252 realCpyFrameNum = 0;
3253 textEncodingType = 0;
3257 release_characterset_array(charset_array);
3259 return (taglen > 0);
3262 static void __get_v224_encoding_info(const unsigned char *buffer,
3263 unsigned long position,
3264 unsigned long length,
3265 unsigned int *offset,
3268 unsigned int _offset = 0;
3270 if (!buffer || !offset || !type || (position < length))
3273 /*in case of UTF 16 encoding */
3274 /*buffer+(position-length) data should '0x01' but in order to expansion, we don't accurately check the value. */
3275 if (IS_ENCODEDBY_UTF16(buffer + (position - length))) {
3277 *type = AV_ID3V2_UTF16;
3281 if (IS_ENCODEDBY_UTF16_R(buffer + (position - length))) {
3283 *type = AV_ID3V2_UTF16_BE;
3287 if (IS_ENCODEDBY_UTF16(buffer + (position - length + 1))) {
3289 *type = AV_ID3V2_UTF16;
3292 if (IS_ENCODEDBY_UTF16_R(buffer + (position - length + 1))) {
3294 *type = AV_ID3V2_UTF16_BE;
3298 /*in case of UTF-16 BE encoding */
3299 if (buffer[position - length] == 0x02) {
3301 while ((buffer[position - length + _offset] == '\0') && (_offset < length))
3302 _offset++;/*null skip! */
3304 *type = AV_ID3V2_UTF16_BE;
3308 /*in case of UTF8 encoding */
3309 if (buffer[position - length] == 0x03) {
3311 while ((buffer[position - length + _offset] == '\0') && (_offset < length))
3312 _offset++;/*null skip! */
3314 *type = AV_ID3V2_UTF8;
3317 /*in case of ISO-8859-1 encoding */
3318 /*buffer+(position-length) data should 0x00 but in order to expansion, we don't accurately check the value. */
3320 while ((buffer[position - length + _offset] < 0x20) && (_offset < length))
3321 _offset++;/*less than 0x20 value skip! */
3323 *type = AV_ID3V2_ISO_8859;
3326 bool mm_file_id3tag_parse_v224(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
3328 unsigned long taglen = 0;
3329 unsigned long needToloopv2taglen;
3330 unsigned long oneFrameLen = 0;
3331 unsigned long curPos = 0;
3333 unsigned char *pExtContent = NULL;
3334 unsigned long purelyFramelen = 0;
3335 unsigned int encodingOffSet = 0;
3336 int realCpyFrameNum = 0, tmp = 0;
3337 unsigned int textEncodingType = 0;
3338 char **charset_array = NULL;
3339 AvID3TagList tag_id = AV_ID3TAG_MAX;
3341 make_characterset_array(&charset_array);
3343 init_content_info(pInfo);
3345 taglen = pInfo->tagV2Info.tagLen;
3346 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
3347 curPos = MP3_TAGv2_HEADER_LEN;
3349 debug_msg(RELEASE, "ID3tag v224--------------------------------------------------------------");
3351 /* check Extended Header */
3352 if (buffer[5] & 0x40) {
3353 /* if extended header exists, skip it*/
3354 int extendedHeaderLen = (unsigned long)buffer[10] << 21 | (unsigned long)buffer[11] << 14 | (unsigned long)buffer[12] << 7 | (unsigned long)buffer[13];
3356 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d", extendedHeaderLen);
3358 if (extendedHeaderLen > (int)(taglen - curPos)) {
3359 debug_error(DEBUG, "extended header too long.");
3361 curPos += extendedHeaderLen;
3365 while (needToloopv2taglen > MP3_TAGv2_23_TXT_HEADER_LEN) {
3366 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) ||
3367 !g_ascii_isalnum(buffer[curPos + 2]) || !g_ascii_isalnum(buffer[curPos + 3]))
3370 memcpy(CompTmp, &buffer[curPos], 4);
3373 oneFrameLen = MP3_TAGv2_23_TXT_HEADER_LEN;
3374 oneFrameLen += (unsigned long)buffer[4 + curPos] << 21 | (unsigned long)buffer[5 + curPos] << 14
3375 | (unsigned long)buffer[6 + curPos] << 7 | (unsigned long)buffer[7 + curPos];
3376 if (oneFrameLen > taglen - curPos)
3379 purelyFramelen = oneFrameLen - MP3_TAGv2_23_TXT_HEADER_LEN;
3380 curPos += oneFrameLen;
3382 debug_msg(RELEASE, "-----------------------------------------------------------------------------------");
3384 tag_id = __get_tag_info_v223(CompTmp);
3385 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value || purelyFramelen == 0)
3388 __get_v224_encoding_info(buffer, curPos, purelyFramelen, &encodingOffSet, &textEncodingType);
3389 if (purelyFramelen <= encodingOffSet) {
3390 debug_warning(DEBUG, "warning: invalid frame length %lu %u", purelyFramelen, encodingOffSet);
3394 mmfile_free(pExtContent);
3395 realCpyFrameNum = purelyFramelen - encodingOffSet;
3396 pExtContent = g_malloc0(realCpyFrameNum + 3);
3398 if (textEncodingType != AV_ID3V2_UTF16 && textEncodingType != AV_ID3V2_UTF16_BE) {
3399 if (CompTmp[0] == 'T' || (strcmp(CompTmp, "APIC") == 0)) {
3400 debug_msg(RELEASE, "get the new text encoding type");
3401 textEncodingType = buffer[curPos - purelyFramelen + encodingOffSet - 1];
3405 if (textEncodingType > AV_ID3V2_MAX) {
3406 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%d], FRAME[%s]", textEncodingType, (char *)CompTmp);
3410 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
3413 case AV_ID3TAG_COMMENT:
3414 if (realCpyFrameNum <= 3) {
3415 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)", realCpyFrameNum);
3419 realCpyFrameNum -= 3;
3422 textEncodingType = __id3tag_get_text_encoding_v224(pExtContent, &realCpyFrameNum, textEncodingType, &tmp);
3423 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)", tmp, textEncodingType, realCpyFrameNum);
3425 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&pExtContent[tmp], realCpyFrameNum, charset_array[textEncodingType]);
3428 case AV_ID3TAG_SYNCLYRICS:
3429 if (realCpyFrameNum <= 5) {
3430 debug_msg(RELEASE, "SyncLyrics info too small to parse realCpyFrameNum(%d)", realCpyFrameNum);
3433 realCpyFrameNum -= 5;
3436 textEncodingType = __id3tag_get_text_encoding_v224(pExtContent, &realCpyFrameNum, textEncodingType, &tmp);
3437 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)", tmp, textEncodingType, realCpyFrameNum);
3439 __id3tag_parse_SYLT(pInfo, pExtContent, realCpyFrameNum, charset_array[textEncodingType], textEncodingType, tmp);
3442 case AV_ID3TAG_UNSYNCLYRICS:
3443 if (realCpyFrameNum <= 3) {
3444 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)", realCpyFrameNum);
3447 realCpyFrameNum -= 3;
3450 textEncodingType = __id3tag_get_text_encoding_v224(pExtContent, &realCpyFrameNum, textEncodingType, &tmp);
3451 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)", tmp, textEncodingType, realCpyFrameNum);
3453 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&pExtContent[tmp], realCpyFrameNum, charset_array[textEncodingType]);
3456 case AV_ID3TAG_PICTURE:
3457 if (extract_artwork)
3458 _mm_file_id3tag_parse_APIC(pInfo, (unsigned char *)pExtContent, realCpyFrameNum, charset_array[textEncodingType]);
3462 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)pExtContent, realCpyFrameNum, charset_array[textEncodingType]);
3466 if (pInfo->tagInfo[tag_id].value)
3467 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
3470 mmfile_free(pExtContent);
3471 memset(CompTmp, 0, 4);
3473 if (curPos >= taglen)
3476 needToloopv2taglen -= oneFrameLen;
3480 realCpyFrameNum = 0;
3481 textEncodingType = 0;
3485 release_characterset_array(charset_array);
3487 return (taglen > 0);
3491 void mm_file_id3tag_restore_content_info(AvFileContentInfo *pInfo)
3495 /* for Genre Info */
3496 if (pInfo->tagInfo[AV_ID3TAG_GENRE].value) {
3498 if (!__get_genre_num(pInfo->tagInfo[AV_ID3TAG_GENRE].value, &genre_id)) {
3499 debug_log(RELEASE, "genre information is not integer [%s]", pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3503 /* If integer, check genre code. */
3504 /* If out of range, set UNKNOWN */
3505 if (genre_id < 0 || genre_id >= GENRE_COUNT)
3506 genre_id = GENRE_COUNT - 1;
3508 debug_msg(RELEASE, "genre information is integer [%d]", genre_id);
3510 g_free(pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3511 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(MpegAudio_Genre[genre_id]);
3513 /* No genre in ID3V2.. So check V1 */
3514 if (pInfo->bV1tagFound == true) {
3515 debug_msg(RELEASE, "Genre: %d", pInfo->genre);
3517 /* If out of range, set UNKNOWN */
3518 if (pInfo->genre > GENRE_COUNT - 1)
3519 pInfo->genre = GENRE_COUNT - 1;
3521 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(MpegAudio_Genre[pInfo->genre]);
3523 debug_msg(RELEASE, "Genre was not Found.");
3528 static void __free_synclyrics(gpointer data)
3530 AvSynclyricsInfo *info = (AvSynclyricsInfo *) data;
3535 mmfile_free(info->lyric_info);
3540 void mm_file_free_synclyrics_list(GList *synclyrics_list)
3542 if (!synclyrics_list)
3545 g_list_free_full(synclyrics_list, __free_synclyrics);