4 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: Haejeong Kim <backto.kim@samsung.com>
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
31 #include "mm_file_debug.h"
32 #include "mm_file_utils.h"
34 #define ENABLE_ITUNES_META /*All itunes metadata extracted by ffmpeg. see mov_read_udta_string() but Some cover art not support. */
35 #define INVALID_UINT_VALUE 0xFFFFFFFF
36 #define INVALID_UINT8_VALUE 0xFF
38 #define ID3TAG_V110_TRACK_NUM_DIGIT 5
40 typedef struct _mmfilemp4basicboxheader {
43 long long start_offset; /*huge file*/
44 } MMFILE_MP4_BASIC_BOX_HEADER;
46 typedef struct _mmfilemp4ftpybox {
47 unsigned int major_brand;
48 unsigned short major_version;
49 unsigned short minor_version;
50 unsigned int *compatiable_brands;
54 eMMFILE_3GP_TAG_TITLE = 0x01,
55 eMMFILE_3GP_TAG_CAPTION = 0x02,
56 eMMFILE_3GP_TAG_COPYRIGHT = 0x03,
57 eMMFILE_3GP_TAG_PERFORMER = 0x04,
58 eMMFILE_3GP_TAG_AUTHOR = 0x05,
59 eMMFILE_3GP_TAG_GENRE = 0x06,
60 } eMMFILE_3GP_TEXT_TAG;
62 typedef struct _mmfile3gptagtextbox {
63 unsigned char version;
64 unsigned char flag[3];
65 unsigned char language[2];
67 } MMFILE_3GP_TEXT_TAGBOX;
69 typedef struct _mmfile3gptagyearbox {
70 unsigned char version;
71 unsigned char flag[3];
73 } MMFILE_3GP_YEAR_TAGBOX;
75 typedef struct _mmfile3gptagalbumbox {
76 unsigned char version;
77 unsigned char flag[3];
78 unsigned char language[2];
79 unsigned char *albumtile;
80 unsigned char trackNumber;
81 } MMFILE_3GP_ALBUM_TAGBOX;
83 typedef struct _mmfile3gpratingbox {
84 unsigned char version;
85 unsigned char flag[3];
86 unsigned int ratingEntity;
87 unsigned int ratingCriteria;
88 unsigned char language[2];
89 unsigned char *ratingInfo;
90 } MMFILE_3GP_RATING_TAGBOX;
92 typedef struct _mmfile3gpclsfbox {
93 unsigned char version;
94 unsigned char flag[3];
95 unsigned int classificationEntity;
96 unsigned int classificationTable;
97 unsigned char language[2];
98 unsigned char *classificationInfo;
99 } MMFILE_3GP_CLASSIFICATION_TAGBOX;
101 typedef struct _mmfile3gphdlrbox {
102 unsigned int version;
103 unsigned int pre_defined;
104 unsigned int handler_type;
105 unsigned int reserved[3];
106 unsigned char *boxname;
107 } MMFILE_3GP_HANDLER_BOX;
109 typedef struct _mmfileidv2box {
110 unsigned char version;
111 unsigned char flag[3];
112 unsigned char language[2];
113 unsigned char *id3v2Data;
114 } MMFILE_3GP_ID3V2_BOX;
116 typedef struct _mmfilelocibox {
117 unsigned char version;
118 unsigned char flag[3];
119 unsigned char language[2];
125 unsigned char *astronomical_body;
126 unsigned char *additional_notes;
127 } MMFILE_3GP_LOCATION_TAGBOX;
129 typedef struct _mmfilesmtabox {
132 unsigned char saut[4];
134 } MMFILE_M4A_SMTA_TAGBOX;
136 typedef struct _mmfilesa3dbox {
138 uint8_t ambisonic_type;
139 uint32_t ambisonic_order;
140 uint8_t ambisonic_channel_ordering;
141 uint8_t ambisonic_normalization;
142 uint32_t num_channels;
143 uint32_t channel_map[49]; /* Up to 6th order */
144 } __attribute__((aligned(1), packed)) MMFILE_MP4A_SA3D_TAGBOX;
146 typedef struct _mmfile_proj_v2_box {
147 uint16_t proj_box_id;
148 uint8_t proj_box_size;
149 uint16_t proj_type_box_id;
150 uint8_t proj_type_box_size;
151 uint8_t proj_type_box_value; /* only equi (=1) or cubemap (=2) currently */
152 uint16_t proj_priv_box_id;
153 uint8_t proj_priv_box_size;
154 } __attribute__((aligned(1), packed)) MMFILE_WEBM_PROJ_V2_BOX;
156 typedef struct _mmfile_equi_proj_v2_box {
157 unsigned char proj_priv_box_name[4]; /* "equi" */
158 uint32_t equi_projection_bounds_top;
159 uint32_t equi_projection_bounds_bottom;
160 uint32_t equi_projection_bounds_left;
161 uint32_t equi_projection_bounds_right;
162 } __attribute__((aligned(1), packed)) MMFILE_WEBM_EQUI_PROJ_V2_BOX;
164 typedef struct _mmfile_cbmp_proj_v2_box {
165 unsigned char proj_priv_box_name[4]; /* "cbmp" */
166 uint32_t cbmp_projection_layout;
167 uint32_t cbmp_projection_padding;
168 } __attribute__((aligned(1), packed)) MMFILE_WEBM_CBMP_PROJ_V2_BOX;
170 typedef struct _mmfile_pose_element_v2_box {
171 uint16_t pose_yaw_element_id;
172 uint8_t pose_yaw_element_size;
173 float pose_yaw_element_value;
174 uint16_t pose_pitch_element_id;
175 uint8_t pose_pitch_element_size;
176 float pose_pitch_element_value;
177 uint16_t pose_roll_element_id;
178 uint8_t pose_roll_element_size;
179 float pose_roll_element_value;
180 } __attribute__((aligned(1), packed)) MMFILE_WEBM_POSE_ELEMENT_V2_BOX;
184 PROJECTION_TYPE_RECT = 0, /* Rectangular, Google's RFC value */
185 PROJECTION_TYPE_EQUI = 1, /* Equirectangular, Google's RFC value */
186 PROJECTION_TYPE_CBMP = 2, /* Cubemap, Google's RFC value */
187 PROJECTION_TYPE_MESH = 3, /* Mesh, Google's RFC value, unsupported */
188 PROJECTION_TYPE_UNKNOWN = INVALID_UINT_VALUE,
189 } SPHERICAL_VIDEO2_PROJECTION_TYPE;
191 enum spherical_video_metadata_elements_ids_le {
192 PROJ_BOX_ID = 0x7076,
193 PROJ_TYPE_BOX_ID = 0x7176,
194 PROJ_PRIV_BOX_ID = 0x7276,
195 POSE_YAW_ELEMENT_ID = 0x7376,
196 POSE_PITCH_ELEMENT_ID = 0x7476,
197 POSE_ROLL_ELEMENT_ID = 0x7576
200 #define MMFILE_MP4_BASIC_BOX_HEADER_LEN 8
201 #define MMFILE_MP4_MOVIE_HEADER_BOX_LEN 96
202 #define MMFILE_MP4_HDLR_BOX_LEN 24
203 #define MMFILE_MP4_STSZ_BOX_LEN 20
204 #define MMFILE_MP4_MP4VBOX_LEN 80
205 #define MMFILE_MP4_MP4ABOX_LEN 28
206 #define MMFILE_3GP_TEXT_TAGBOX_LEN 6
207 #define MMFILE_3GP_YEAR_TAGBOX_LEN 6
208 #define MMFILE_3GP_ALBUM_TAGBOX_LEN 6
209 #define MMFILE_3GP_RATING_TAGBOX_LEN 14
210 #define MMFILE_3GP_CLASS_TAGBOX_LEN 14
211 #define MMFILE_3GP_HANDLER_BOX_LEN 24
212 #define MMFILE_3GP_ID3V2_BOX_LEN 6
213 #define MMFILE_SYNC_LYRIC_INFO_MIN_LEN 5
216 #define FOURCC(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
218 #define GENRE_COUNT 149
220 static const char * const MpegAudio_Genre[GENRE_COUNT] = {"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal",
221 "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial",
222 "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
223 "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise",
224 "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic",
225 "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
226 "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes",
227 "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock",
228 "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass",
229 "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic",
230 "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove",
231 "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
232 "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore",
233 "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian", "Heavy Metal", "Black Metal", "Crossover",
234 "Contemporary", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", "Synthpop", "Unknown"
237 static unsigned char gTagJPEGHeader[] = {0xff, 0xd8, 0xff };
238 static unsigned char gTagPNGHeader[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
240 typedef struct _mmfileid3taginfo {
242 unsigned int int_name;
244 } MMFILE_ID3TAG_INFO;
246 #define ID3TAG_NUM_V22 16
247 #define ID3TAG_NUM_V23 21
249 static MMFILE_ID3TAG_INFO tag_info_v22[ID3TAG_NUM_V22] = {
250 {"TT2", 21170, AV_ID3TAG_TITLE},
251 {"TP1", 21041, AV_ID3TAG_ARTIST},
252 {"TP2", 21042, AV_ID3TAG_ALBUM_ARTIST},
253 {"TP3", 21043, AV_ID3TAG_CONDUCTOR},
254 {"TAL", 20588, AV_ID3TAG_ALBUM},
255 {"TYE", 21349, AV_ID3TAG_YEAR},
256 {"COM", 3629, AV_ID3TAG_COMMENT},
257 {"TCO", 20655, AV_ID3TAG_GENRE},
258 {"TRK", 21131, AV_ID3TAG_TRACKNUM},
259 {"PIC", 16739, AV_ID3TAG_PICTURE},
260 {"TCR", 20658, AV_ID3TAG_COPYRIGHT},
261 {"TCM", 20653, AV_ID3TAG_COMPOSER},
262 {"TRD", 21124, AV_ID3TAG_RECDATE},
265 static MMFILE_ID3TAG_INFO tag_info_v23[ID3TAG_NUM_V23] = {
266 {"TIT2", 665266, AV_ID3TAG_TITLE},
267 {"TPE1", 671953, AV_ID3TAG_ARTIST},
268 {"TPE2", 671954, AV_ID3TAG_ALBUM_ARTIST},
269 {"TPE3", 671955, AV_ID3TAG_CONDUCTOR},
270 {"TALB", 656834, AV_ID3TAG_ALBUM},
271 {"TYER", 681202, AV_ID3TAG_YEAR},
272 {"COMM", 114157, AV_ID3TAG_COMMENT},
273 {"TCON", 658990, AV_ID3TAG_GENRE},
274 {"TRCK", 673963, AV_ID3TAG_TRACKNUM},
275 {"APIC", 49507, AV_ID3TAG_PICTURE},
276 {"TCOP", 658992, AV_ID3TAG_COPYRIGHT},
277 {"TCOM", 658989, AV_ID3TAG_COMPOSER},
278 {"TRDA", 660097, AV_ID3TAG_RECDATE}, /*Recdate for 2.3*/
279 {"USLT", 708052, AV_ID3TAG_UNSYNCLYRICS},
280 {"SYLT", 648660, AV_ID3TAG_SYNCLYRICS},
281 {"TIT1", 665265, AV_ID3TAG_CONTENT_GROUP}, /*Content Group for 2.4*/
282 {"TDRC", 660099, AV_ID3TAG_RECDATE}, /*Recdate for 2.4*/
285 static int g_junk_counter_limit = 0;
287 static int GetStringFromTextTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header, eMMFILE_3GP_TEXT_TAG eTag)
289 int ret = MMFILE_UTIL_FAIL; /*fail*/
290 MMFILE_3GP_TEXT_TAGBOX texttag = {0, };
293 char *temp_text = NULL;
295 if (!formatContext || !fp || !basic_header) {
296 debug_error(DEBUG, "invalid param");
297 return MMFILE_UTIL_FAIL;
300 textBytes = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_TEXT_TAGBOX_LEN;
302 readed = mmfile_read(fp, (unsigned char *)&texttag, MMFILE_3GP_TEXT_TAGBOX_LEN);
303 if (readed != MMFILE_3GP_TEXT_TAGBOX_LEN) {
304 debug_error(DEBUG, "read text tag header fail");
305 ret = MMFILE_UTIL_FAIL;
309 if (textBytes <= 1) { /* there exist only 00(null) */
310 debug_error(DEBUG, "Text is NULL");
314 texttag.text = g_malloc0(textBytes);
316 readed = mmfile_read(fp, (unsigned char *)texttag.text, textBytes);
317 if (readed != textBytes) {
318 debug_error(DEBUG, "read text fail");
319 ret = MMFILE_UTIL_FAIL;
324 if ((texttag.text[0] == 0xFE) && (texttag.text[1] == 0xFF)) {
325 /* this char is UTF-16 */
326 temp_text = mmfile_convert_to_utf8((const char *)&texttag.text[2], readed - 2, MMFILE_CODESET_UTF16);
328 temp_text = g_strdup((const char *)texttag.text);
332 case eMMFILE_3GP_TAG_TITLE: {
333 if (!formatContext->title) {
334 formatContext->title = g_strdup(temp_text);
338 case eMMFILE_3GP_TAG_CAPTION: {
339 if (!formatContext->description) {
340 formatContext->description = g_strdup(temp_text);
344 case eMMFILE_3GP_TAG_COPYRIGHT: {
345 if (!formatContext->copyright) {
346 formatContext->copyright = g_strdup(temp_text);
350 case eMMFILE_3GP_TAG_PERFORMER: {
351 if (!formatContext->artist) {
352 formatContext->artist = g_strdup(temp_text);
356 case eMMFILE_3GP_TAG_AUTHOR: {
357 if (!formatContext->author) {
358 formatContext->author = g_strdup(temp_text);
362 case eMMFILE_3GP_TAG_GENRE: {
363 if (!formatContext->genre) {
364 formatContext->genre = g_strdup(temp_text);
369 debug_warning(DEBUG, "Not supported Text Tag type[%d]", eTag);
374 mmfile_free(texttag.text);
376 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
378 mmfile_free(temp_text);
380 return MMFILE_UTIL_SUCCESS;
383 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
384 mmfile_free(texttag.text);
389 static int GetYearFromYearTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
391 #define MAX_YEAR_BUFFER 10
393 MMFILE_3GP_YEAR_TAGBOX yearbox = {0, };
394 char temp_year[MAX_YEAR_BUFFER] = {0, };
396 if (!formatContext || !fp || !basic_header) {
397 debug_error(DEBUG, "invalid param");
398 return MMFILE_UTIL_FAIL;
401 readed = mmfile_read(fp, (unsigned char *)&yearbox, MMFILE_3GP_YEAR_TAGBOX_LEN);
402 if (readed != MMFILE_3GP_YEAR_TAGBOX_LEN) {
403 debug_error(DEBUG, "read yeartag header fail");
407 if (!formatContext->year) {
408 yearbox.year = mmfile_io_be_int16(yearbox.year);
409 snprintf(temp_year, MAX_YEAR_BUFFER, "%d", yearbox.year);
410 temp_year[MAX_YEAR_BUFFER - 1] = '\0';
411 formatContext->year = g_strdup((const char *)temp_year);
414 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
415 return MMFILE_UTIL_SUCCESS;
418 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
420 return MMFILE_UTIL_FAIL;
423 static int GetAlbumFromAlbumTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
425 int albumTitleLen = 0;
426 char *temp_text = NULL;
429 MMFILE_3GP_ALBUM_TAGBOX albumbox = {0, };
431 if (!formatContext || !fp || !basic_header) {
432 debug_error(DEBUG, "invalid param");
433 return MMFILE_UTIL_FAIL;
436 readed = mmfile_read(fp, (unsigned char *)&albumbox, MMFILE_3GP_ALBUM_TAGBOX_LEN);
437 if (readed != MMFILE_3GP_ALBUM_TAGBOX_LEN) {
438 debug_error(DEBUG, "read albumtag header fail");
442 albumTitleLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_ALBUM_TAGBOX_LEN - 1; /* 1: track number */
443 if (albumTitleLen > 1) { /* there exist only 00(null) */
444 debug_msg(RELEASE, "albumTitleLen=%d", albumTitleLen);
446 albumbox.albumtile = g_malloc0(albumTitleLen + 1); /* 1: for null char */
448 readed = mmfile_read(fp, (unsigned char *)albumbox.albumtile, albumTitleLen);
449 if (readed != albumTitleLen) {
450 debug_error(DEBUG, "read album title fail");
454 if (albumbox.albumtile[albumTitleLen - 1] == '\0') { /* there exist track number */
458 readed = mmfile_read(fp, (unsigned char *)&(albumbox.albumtile[albumTitleLen]), 1);
460 debug_error(DEBUG, "read album title fail");
463 albumbox.albumtile[albumTitleLen] = '\0';
467 if ((albumbox.albumtile[0] == 0xFE) && (albumbox.albumtile[1] == 0xFF)) {
468 /* this char is UTF-16 */
469 temp_text = mmfile_convert_to_utf8((const char *)&albumbox.albumtile[2], readed - 2, MMFILE_CODESET_UTF16);
471 temp_text = g_strdup((const char *)albumbox.albumtile);
474 if (!formatContext->album)
475 formatContext->album = temp_text;
477 mmfile_free(temp_text);
479 debug_msg(RELEASE, "formatContext->album=%s, strlen=%zu", formatContext->album, strlen(formatContext->album));
483 readed = mmfile_read(fp, (unsigned char *)&albumbox.trackNumber, 1);
485 debug_error(DEBUG, "read track number fail");
489 if (formatContext->tagTrackNum == 0) {
490 char tracknum[10] = {0, };
491 snprintf(tracknum, 10, "%d", albumbox.trackNumber);
493 formatContext->tagTrackNum = g_strdup((const char *)tracknum);
497 mmfile_free(albumbox.albumtile);
498 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
500 return MMFILE_UTIL_SUCCESS;
503 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
504 mmfile_free(albumbox.albumtile);
506 return MMFILE_UTIL_FAIL;
509 static int GetRatingFromRatingTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
512 int ratinginfoLen = 0;
513 char *temp_text = NULL;
515 MMFILE_3GP_RATING_TAGBOX ratingTag = {0, };
517 if (!formatContext || !fp || !basic_header) {
518 debug_error(DEBUG, "invalid param");
519 return MMFILE_UTIL_FAIL;
522 readed = mmfile_read(fp, (unsigned char *)&ratingTag, MMFILE_3GP_RATING_TAGBOX_LEN);
523 if (readed != MMFILE_3GP_RATING_TAGBOX_LEN) {
524 debug_error(DEBUG, "read rating tag header fail");
528 ratinginfoLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_RATING_TAGBOX_LEN;
530 if (ratinginfoLen == 1) {
531 debug_error(DEBUG, "Rating Text is NULL");
535 ratingTag.ratingInfo = g_malloc0(ratinginfoLen);
537 readed = mmfile_read(fp, (unsigned char *)ratingTag.ratingInfo, ratinginfoLen);
538 if (readed != ratinginfoLen) {
539 debug_error(DEBUG, "read rating info string fail");
544 if ((ratingTag.ratingInfo[0] == 0xFE) && (ratingTag.ratingInfo[1] == 0xFF)) {
545 /* this char is UTF-16 */
546 temp_text = mmfile_convert_to_utf8((const char *)&ratingTag.ratingInfo[2], readed - 2, MMFILE_CODESET_UTF16);
548 temp_text = g_strdup((const char *)ratingTag.ratingInfo);
551 if (!formatContext->rating) {
552 formatContext->rating = temp_text;
554 mmfile_free(temp_text);
557 mmfile_free(ratingTag.ratingInfo);
558 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
560 return MMFILE_UTIL_SUCCESS;
563 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
564 mmfile_free(ratingTag.ratingInfo);
566 return MMFILE_UTIL_FAIL;
570 static int GetClassficationFromClsfTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
572 int classinfoLen = 0;
574 char *temp_text = NULL;
575 MMFILE_3GP_CLASSIFICATION_TAGBOX classTag = {0, };
577 if (!formatContext || !fp || !basic_header) {
578 debug_error(DEBUG, "invalid param");
579 return MMFILE_UTIL_FAIL;
582 readed = mmfile_read(fp, (unsigned char *)&classTag, MMFILE_3GP_CLASS_TAGBOX_LEN);
583 if (readed != MMFILE_3GP_CLASS_TAGBOX_LEN) {
584 debug_error(DEBUG, "read classification tag header fail");
589 classinfoLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_CLASS_TAGBOX_LEN;
590 if (classinfoLen == 1) {
591 debug_error(DEBUG, "Classification Text is NULL");
595 classTag.classificationInfo = g_malloc0(classinfoLen);
597 readed = mmfile_read(fp, (unsigned char *)classTag.classificationInfo, classinfoLen);
598 if (readed != classinfoLen) {
599 debug_error(DEBUG, "read class info string fail");
604 if ((classTag.classificationInfo[0] == 0xFE) && (classTag.classificationInfo[1] == 0xFF)) {
605 /* this char is UTF-16 */
606 temp_text = mmfile_convert_to_utf8((const char *)&classTag.classificationInfo[2], readed - 2, MMFILE_CODESET_UTF16);
608 temp_text = g_strdup((const char *)classTag.classificationInfo);
611 if (!formatContext->classification) {
612 formatContext->classification = temp_text;
614 mmfile_free(temp_text);
617 mmfile_free(classTag.classificationInfo);
618 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
620 return MMFILE_UTIL_SUCCESS;
623 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
624 mmfile_free(classTag.classificationInfo);
626 return MMFILE_UTIL_FAIL;
630 * The Location Information box
631 * --------------------+-------------------+-----------------------------------+------
632 * Field Type Details Value
633 * --------------------+-------------------+-----------------------------------+------
634 * BoxHeader.Size Unsigned int(32)
635 * BoxHeader.Type Unsigned int(32) 'loci'
636 * BoxHeader.Version Unsigned int(8) 0
637 * BoxHeader.Flags Bit(24) 0
639 * Language Unsigned int(5)[3] Packed ISO-639-2/T language code
640 * Name String Text of place name
641 * Role Unsigned int(8) Non-negative value indicating role
643 * Longitude Unsigned int(32) Fixed-point value of the longitude
644 * Latitude Unsigned int(32) Fixed-point value of the latitude
645 * Altitude Unsigned int(32) Fixed-point value of the Altitude
646 * Astronomical_body String Text of astronomical body
647 * Additional_notes String Text of additional location-related
649 * --------------------+-------------------+-----------------------------------+------
651 static int _get_char_position(unsigned char *src, char ch, int max)
654 for (i = 0; i < max; i++) {
655 if (*(src + i) == ch)
662 static int GetLocationFromLociTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
665 MMFILE_3GP_LOCATION_TAGBOX lociTag = {0, };
668 unsigned char *buffer = NULL;
669 unsigned char *p = NULL;
671 unsigned int name_sz = 0;
672 unsigned int astro_sz = 0;
674 int ilong, ilati, ialti;
675 float flong, flati, falti;
678 if (!formatContext || !fp || !basic_header) {
679 debug_error(DEBUG, "invalid param");
680 return MMFILE_UTIL_FAIL;
683 readed = mmfile_read(fp, (unsigned char *)&lociTag, 6); /*6 = version + flag + pad + language */
685 debug_error(DEBUG, "read location tag header fail");
689 /*buffer len = name + role + ... + additional notes length */
690 bufferLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - 6;
692 debug_error(DEBUG, "too small buffer");
696 buffer = g_malloc0(bufferLen);
698 readed = mmfile_read(fp, (unsigned char *)buffer, bufferLen);
699 if (readed != bufferLen) {
700 debug_error(DEBUG, "read location tag fail");
706 pos = _get_char_position(p, '\0', readed - (1 + 4 + 4 + 4 + 2));
708 if (p[0] == 0xFE && p[1] == 0xFF) {
709 lociTag.name = (unsigned char *)mmfile_convert_to_utf8((const char *)(p + 2), pos - 2, MMFILE_CODESET_UTF16);
711 lociTag.name = (unsigned char *)g_strdup((const char *)p);
723 debug_msg(RELEASE, "long: 0x%02X 0x%02X 0x%02X 0x%02X ", *(p + 0), *(p + 1), *(p + 2), *(p + 3));
724 debug_msg(RELEASE, "lati: 0x%02X 0x%02X 0x%02X 0x%02X ", *(p + 4), *(p + 5), *(p + 6), *(p + 7));
725 debug_msg(RELEASE, "alti: 0x%02X 0x%02X 0x%02X 0x%02X ", *(p + 8), *(p + 9), *(p + 10), *(p + 11));
727 ilong = mmfile_io_be_uint32(*(unsigned int *)p);
728 ilati = mmfile_io_be_uint32(*(unsigned int *)(p + 4));
729 ialti = mmfile_io_be_uint32(*(unsigned int *)(p + 8));
731 flong = (float)ilong / (1 << 16);
732 flati = (float)ilati / (1 << 16);
733 falti = (float)ialti / (1 << 16);
736 lociTag.longitude = flong;
738 lociTag.latitude = flati;
740 lociTag.altitude = falti;
744 /*astronomical body*/
745 pos = _get_char_position(p, '\0', readed - (name_sz + 1 + 4 + 4 + 4 + 1));
747 if (p[0] == 0xFE && p[1] == 0xFF) {
748 lociTag.astronomical_body = (unsigned char *)mmfile_convert_to_utf8((const char *)(p + 2), pos - 2, MMFILE_CODESET_UTF16);
750 lociTag.astronomical_body = (unsigned char *)g_strdup((const char *)p);
759 pos = _get_char_position(p, '\0', readed - (name_sz + 1 + 4 + 4 + 4 + astro_sz));
761 if (p[0] == 0xFE && p[1] == 0xFF) {
762 lociTag.additional_notes = (unsigned char *)mmfile_convert_to_utf8((const char *)(p + 2), pos - 2, MMFILE_CODESET_UTF16);
764 lociTag.additional_notes = (unsigned char *)g_strdup((const char *)p);
770 debug_msg(RELEASE, "** Location Information **");
771 debug_msg(RELEASE, "Name : %s", lociTag.name);
772 debug_msg(RELEASE, "Role : %d (0: shooting, 1: real, 2: fictional, other: reserved)", lociTag.role);
773 debug_msg(RELEASE, "Longitude : %16.16f", lociTag.longitude);
774 debug_msg(RELEASE, "Latitude : %16.16f", lociTag.latitude);
775 debug_msg(RELEASE, "Altitude : %16.16f", lociTag.altitude);
776 debug_msg(RELEASE, "Astronomical body: %s", lociTag.astronomical_body);
777 debug_msg(RELEASE, "Additional notes : %s", lociTag.additional_notes);
779 formatContext->longitude = lociTag.longitude;
780 formatContext->latitude = lociTag.latitude;
781 formatContext->altitude = lociTag.altitude;
784 mmfile_free(lociTag.name);
785 mmfile_free(lociTag.astronomical_body);
786 mmfile_free(lociTag.additional_notes);
788 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
789 return MMFILE_UTIL_SUCCESS;
792 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
794 mmfile_free(lociTag.name);
795 mmfile_free(lociTag.astronomical_body);
796 mmfile_free(lociTag.additional_notes);
798 return MMFILE_UTIL_FAIL;
801 static int GetSAUTInfoFromSMTATagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
803 MMFILE_M4A_SMTA_TAGBOX smtaTag = {0, };
806 if (!formatContext || !fp || !basic_header) {
807 debug_error(DEBUG, "invalid param");
808 return MMFILE_UTIL_FAIL;
811 readed = mmfile_read(fp, (unsigned char *)&smtaTag, sizeof(MMFILE_M4A_SMTA_TAGBOX));
812 if (readed != sizeof(MMFILE_M4A_SMTA_TAGBOX)) {
813 debug_error(DEBUG, "read smta tag header fail");
817 smtaTag.length = mmfile_io_be_uint32(smtaTag.length);
818 smtaTag.value = mmfile_io_be_uint32(smtaTag.value);
820 debug_msg(RELEASE, "Len : 0x%x", smtaTag.length);
821 debug_msg(RELEASE, "Saut : 0x%x 0x%x 0x%x 0x%x", smtaTag.saut[0], smtaTag.saut[1], smtaTag.saut[2], smtaTag.saut[3]);
822 debug_msg(RELEASE, "Value : 0x%x", smtaTag.value);
833 7: 360 slowmotion mode
834 8: 180 slowmotion mode
836 if (smtaTag.saut[0] == 's'
837 && smtaTag.saut[1] == 'a'
838 && smtaTag.saut[2] == 'u'
839 && smtaTag.saut[3] == 't') {
840 if (smtaTag.value == 0x01) {
841 debug_msg(RELEASE, "This has saut tag and valid value");
842 formatContext->smta = 1;
843 } else if (smtaTag.value == 0x02) {
844 debug_msg(RELEASE, "This has saut tag and valid value");
845 formatContext->smta = 2;
847 debug_error(DEBUG, "This has saut tag but invalid value");
851 debug_error(DEBUG, "This hasn't saut tag and valid value");
855 return MMFILE_UTIL_SUCCESS;
858 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
860 return MMFILE_UTIL_FAIL;
863 static int GetSA3DInfoFromMP4ATagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
865 if (!formatContext || !fp || !basic_header) {
866 debug_error(DEBUG, "invalid param");
867 return MMFILE_UTIL_FAIL;
870 unsigned char *buffer;
872 bool is_SA3D_present = false;
874 MMFILE_MP4A_SA3D_TAGBOX sa3dTag = {0, };
876 formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_UNKNOWN;
877 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_UNKNOWN;
878 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_UNKNOWN;
880 mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
882 buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
884 debug_error(DEBUG, "calloc failed ");
888 readed = mmfile_read(fp, buffer, basic_header->size);
889 if (readed != (int)basic_header->size) {
890 debug_error(DEBUG, "read mp4a box failed");
894 for (i = 0; i + 3 < basic_header->size; ++i)
895 if (buffer[i] == 'S' && buffer[i + 1] == 'A' && buffer[i + 2] == '3' && buffer[i + 3] == 'D') {
896 debug_warning(DEBUG, "SA3D data found at offset %d bytes", i);
897 is_SA3D_present = true;
901 if (!is_SA3D_present) {
902 debug_warning(DEBUG, "No SA3D box found");
906 mmfile_seek(fp, basic_header->start_offset + i + 4, SEEK_SET);
908 readed = mmfile_read(fp, (unsigned char *)&sa3dTag, sizeof(MMFILE_MP4A_SA3D_TAGBOX));
909 if (readed != sizeof(MMFILE_MP4A_SA3D_TAGBOX)) {
910 debug_error(DEBUG, "read SA3D tag header fail");
914 sa3dTag.ambisonic_order = mmfile_io_be_uint32(sa3dTag.ambisonic_order);
915 sa3dTag.num_channels = mmfile_io_be_uint32(sa3dTag.num_channels);
916 for (i = 0; i < sa3dTag.num_channels; ++i)
917 sa3dTag.channel_map[i] = mmfile_io_be_uint32(sa3dTag.channel_map[i]);
919 debug_msg(RELEASE, "sa3dTag.version = %d", sa3dTag.version);
920 debug_msg(RELEASE, "sa3dTag.ambisonic_type = %d", sa3dTag.ambisonic_type);
921 debug_msg(RELEASE, "sa3dTag.ambisonic_order = %d", sa3dTag.ambisonic_order);
922 debug_msg(RELEASE, "sa3dTag.ambisonic_channel_ordering = %d", sa3dTag.ambisonic_channel_ordering);
923 debug_msg(RELEASE, "sa3dTag.ambisonic_normalization = %d", sa3dTag.ambisonic_normalization);
924 debug_msg(RELEASE, "sa3dTag.num_channels = %d", sa3dTag.num_channels);
925 for (i = 0; i < sa3dTag.num_channels; ++i)
926 debug_msg(RELEASE, "sa3dTag.channel_map[%d] = %d", i, sa3dTag.channel_map[i]);
928 if (sa3dTag.version != RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
929 debug_error(DEBUG, "SA3D tag box version is unsupported");
932 if (sa3dTag.ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
933 formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_PERIPHONIC;
935 switch (sa3dTag.ambisonic_order) {
936 case MMFILE_AMBISONIC_ORDER_FOA: {
937 if (sa3dTag.num_channels == 4) {
938 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_FOA;
940 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
941 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
942 (sa3dTag.channel_map[0] == 0) &&
943 (sa3dTag.channel_map[1] == 1) &&
944 (sa3dTag.channel_map[2] == 2) &&
945 (sa3dTag.channel_map[3] == 3))
946 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
948 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
949 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
950 (sa3dTag.channel_map[0] == 0) &&
951 (sa3dTag.channel_map[1] == 3) &&
952 (sa3dTag.channel_map[2] == 1) &&
953 (sa3dTag.channel_map[3] == 2))
954 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
956 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond");
962 case MMFILE_AMBISONIC_ORDER_SOA: {
963 if (sa3dTag.num_channels == 9) {
964 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_SOA;
966 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
967 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
968 (sa3dTag.channel_map[0] == 0) &&
969 (sa3dTag.channel_map[1] == 1) &&
970 (sa3dTag.channel_map[2] == 2) &&
971 (sa3dTag.channel_map[3] == 3) &&
972 (sa3dTag.channel_map[4] == 4) &&
973 (sa3dTag.channel_map[5] == 5) &&
974 (sa3dTag.channel_map[6] == 6) &&
975 (sa3dTag.channel_map[7] == 7) &&
976 (sa3dTag.channel_map[8] == 8))
977 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
979 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
980 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
981 (sa3dTag.channel_map[0] == 0) &&
982 (sa3dTag.channel_map[1] == 3) &&
983 (sa3dTag.channel_map[2] == 1) &&
984 (sa3dTag.channel_map[3] == 2) &&
985 (sa3dTag.channel_map[4] == 6) &&
986 (sa3dTag.channel_map[5] == 7) &&
987 (sa3dTag.channel_map[6] == 5) &&
988 (sa3dTag.channel_map[7] == 8) &&
989 (sa3dTag.channel_map[8] == 4))
990 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
992 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond");
999 case MMFILE_AMBISONIC_ORDER_TOA: {
1000 if (sa3dTag.num_channels == 16) {
1001 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_TOA;
1003 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
1004 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
1005 (sa3dTag.channel_map[0] == 0) &&
1006 (sa3dTag.channel_map[1] == 1) &&
1007 (sa3dTag.channel_map[2] == 2) &&
1008 (sa3dTag.channel_map[3] == 3) &&
1009 (sa3dTag.channel_map[4] == 4) &&
1010 (sa3dTag.channel_map[5] == 5) &&
1011 (sa3dTag.channel_map[6] == 6) &&
1012 (sa3dTag.channel_map[7] == 7) &&
1013 (sa3dTag.channel_map[8] == 8) &&
1014 (sa3dTag.channel_map[9] == 9) &&
1015 (sa3dTag.channel_map[10] == 10) &&
1016 (sa3dTag.channel_map[11] == 11) &&
1017 (sa3dTag.channel_map[12] == 12) &&
1018 (sa3dTag.channel_map[13] == 13) &&
1019 (sa3dTag.channel_map[14] == 14) &&
1020 (sa3dTag.channel_map[15] == 15))
1021 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
1023 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
1024 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
1025 (sa3dTag.channel_map[0] == 0) &&
1026 (sa3dTag.channel_map[1] == 3) &&
1027 (sa3dTag.channel_map[2] == 1) &&
1028 (sa3dTag.channel_map[3] == 2) &&
1029 (sa3dTag.channel_map[4] == 6) &&
1030 (sa3dTag.channel_map[5] == 7) &&
1031 (sa3dTag.channel_map[6] == 5) &&
1032 (sa3dTag.channel_map[7] == 8) &&
1033 (sa3dTag.channel_map[8] == 4) &&
1034 (sa3dTag.channel_map[9] == 12) &&
1035 (sa3dTag.channel_map[10] == 13) &&
1036 (sa3dTag.channel_map[11] == 11) &&
1037 (sa3dTag.channel_map[12] == 14) &&
1038 (sa3dTag.channel_map[13] == 10) &&
1039 (sa3dTag.channel_map[14] == 15) &&
1040 (sa3dTag.channel_map[15] == 9))
1041 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
1043 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond");
1051 debug_warning(DEBUG, "Ambisonic order or format is not supported: ambix or amb formats up to 3rd order were expected.");
1057 debug_msg(RELEASE, "formatContext->ambisonic_type = %d", formatContext->ambisonicType);
1058 debug_msg(RELEASE, "formatContext->ambisonic_order = %d", formatContext->ambisonicOrder);
1059 debug_msg(RELEASE, "formatContext->ambisonic_format = %d", formatContext->ambisonicFormat);
1062 return MMFILE_UTIL_SUCCESS;
1065 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1067 return MMFILE_UTIL_FAIL;
1070 static int ParseSt3dData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset)
1072 uint8_t stereo_mode = INVALID_UINT8_VALUE;
1073 unsigned int readed = 0;
1075 mmfile_seek(fp, start_offset, SEEK_SET);
1077 readed = mmfile_read(fp, (unsigned char *)&stereo_mode, sizeof(uint8_t));
1078 if (readed != sizeof(uint8_t)) {
1079 debug_error(DEBUG, "read st3d tag header fail");
1080 return MMFILE_UTIL_FAIL;
1083 formatContext->stereoModeV2 = stereo_mode;
1085 return MMFILE_UTIL_SUCCESS;
1088 static int ParseSvhdData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset, unsigned int box_size)
1090 unsigned int readed = 0;
1092 formatContext->metadataSourceV2 = (char *)calloc(1, box_size);
1093 if (!formatContext->metadataSourceV2) {
1094 debug_error(DEBUG, "Calloc failed");
1095 return MMFILE_UTIL_FAIL;
1098 mmfile_seek(fp, start_offset, SEEK_SET);
1099 readed = mmfile_read(fp, (unsigned char *)formatContext->metadataSourceV2, box_size);
1100 if (readed != box_size) {
1101 debug_error(DEBUG, "read svhd tag header fail");
1102 if (formatContext->metadataSourceV2)
1103 free(formatContext->metadataSourceV2);
1104 return MMFILE_UTIL_FAIL;
1107 return MMFILE_UTIL_SUCCESS;
1110 static int ParseProjData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset)
1112 unsigned int readed = 0;
1113 typedef struct proj_box_data_s {
1117 } __attribute__((aligned(1), packed)) proj_box_data;
1119 typedef struct prhd_box_data_s {
1120 uint32_t projection_pose_yaw;
1121 uint32_t projection_pose_pitch;
1122 uint32_t projection_pose_roll;
1125 typedef struct equi_box_data_s {
1126 uint32_t projection_bounds_top;
1127 uint32_t projection_bounds_bottom;
1128 uint32_t projection_bounds_left;
1129 uint32_t projection_bounds_right;
1132 typedef struct cbmp_box_data_s {
1138 proj_box_data proj_data;
1139 proj_data.proj_type = INVALID_UINT_VALUE;
1141 prhd_box_data prhd_data;
1142 prhd_data.projection_pose_yaw = INVALID_UINT_VALUE;
1143 prhd_data.projection_pose_pitch = INVALID_UINT_VALUE;
1144 prhd_data.projection_pose_roll = INVALID_UINT_VALUE;
1146 equi_box_data equi_data;
1147 equi_data.projection_bounds_top = INVALID_UINT_VALUE;
1148 equi_data.projection_bounds_bottom = INVALID_UINT_VALUE;
1149 equi_data.projection_bounds_left = INVALID_UINT_VALUE;
1150 equi_data.projection_bounds_right = INVALID_UINT_VALUE;
1152 cbmp_box_data cbmp_data;
1153 cbmp_data.layout = INVALID_UINT_VALUE;
1154 cbmp_data.padding = INVALID_UINT_VALUE;
1156 mmfile_seek(fp, start_offset, SEEK_SET);
1158 readed = mmfile_read(fp, (unsigned char *)&proj_data, sizeof(proj_box_data));
1159 if (readed != sizeof(proj_box_data)) {
1160 debug_error(DEBUG, "read of proj box failed");
1161 return MMFILE_UTIL_FAIL;
1164 formatContext->projTypeV2 = mmfile_io_be_uint32(proj_data.proj_type);
1166 debug_error(DEBUG, "formatContext->projTypeV2 = %d", formatContext->projTypeV2);
1167 debug_error(DEBUG, "proj_data.version = %d", proj_data.version);
1168 debug_error(DEBUG, "proj_data.flags = %d", ((uint32_t)proj_data.flags[0] << 16) +
1169 ((uint32_t)proj_data.flags[1] << 8) + (uint32_t)proj_data.flags[2]);
1171 mmfile_seek(fp, sizeof(proj_box_data), SEEK_CUR);
1172 readed = mmfile_read(fp, (unsigned char *)&prhd_data, sizeof(prhd_box_data));
1173 if (readed != sizeof(prhd_box_data)) {
1174 debug_error(DEBUG, "read of prhd box failed");
1175 return MMFILE_UTIL_FAIL;
1178 formatContext->poseYawV2 = mmfile_io_be_uint32(prhd_data.projection_pose_yaw);
1179 formatContext->posePitchV2 = mmfile_io_be_uint32(prhd_data.projection_pose_pitch);
1180 formatContext->poseRollV2 = mmfile_io_be_uint32(prhd_data.projection_pose_roll);
1182 debug_error(DEBUG, "formatContext->poseYawV2 = %d", formatContext->poseYawV2);
1183 debug_error(DEBUG, "formatContext->posePitchV2 = %d", formatContext->posePitchV2);
1184 debug_error(DEBUG, "formatContext->poseRollV2 = %d", formatContext->poseRollV2);
1186 if (formatContext->projTypeV2 == PROJECTION_TYPE_EQUI) {
1187 debug_msg(RELEASE, "Projection type is Equirectangular");
1188 mmfile_seek(fp, 8, SEEK_CUR); /* 8 = 4 (for size) + 4 (fourcc) */
1189 readed = mmfile_read(fp, (unsigned char *)&equi_data, sizeof(equi_box_data));
1190 if (readed != sizeof(equi_box_data)) {
1191 debug_error(DEBUG, "read of equi box failed");
1192 return MMFILE_UTIL_FAIL;
1195 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi_data.projection_bounds_top);
1196 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi_data.projection_bounds_bottom);
1197 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi_data.projection_bounds_left);
1198 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi_data.projection_bounds_right);
1200 debug_error(DEBUG, "formatContext->equiBoundsTopV2 = %d", formatContext->equiBoundsTopV2);
1201 debug_error(DEBUG, "formatContext->equiBoundsBottomV2 = %d", formatContext->equiBoundsBottomV2);
1202 debug_error(DEBUG, "formatContext->equiBoundsLeftV2 = %d", formatContext->equiBoundsLeftV2);
1203 debug_error(DEBUG, "formatContext->equiBoundsRightV2 = %d", formatContext->equiBoundsRightV2);
1205 } else if (formatContext->projTypeV2 == PROJECTION_TYPE_CBMP) {
1206 debug_msg(RELEASE, "Projection type is Cubemap");
1207 mmfile_seek(fp, 8, SEEK_CUR); /* 8 = 4 (for size) + 4 (fourcc) */
1208 readed = mmfile_read(fp, (unsigned char *)&cbmp_data, sizeof(cbmp_box_data));
1209 if (readed != sizeof(cbmp_box_data)) {
1210 debug_error(DEBUG, "read of cbmp box failed");
1211 return MMFILE_UTIL_FAIL;
1214 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp_data.layout);
1215 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp_data.padding);
1217 debug_error(DEBUG, "formatContext->cbmpLayoutV2 = %d", formatContext->cbmpLayoutV2);
1218 debug_error(DEBUG, "formatContext->cbmpPaddingV2 = %d", formatContext->cbmpPaddingV2);
1221 debug_msg(RELEASE, "Projection type is %d (unknown)", proj_data.proj_type);
1222 return MMFILE_UTIL_FAIL;
1225 return MMFILE_UTIL_SUCCESS;
1228 static int GetVideoV2MetadataFromAvc1TagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1230 if (!formatContext || !fp || !basic_header) {
1231 debug_error(DEBUG, "invalid param");
1232 return MMFILE_UTIL_FAIL;
1235 unsigned char *buffer;
1239 formatContext->stereoModeV2 = INVALID_UINT_VALUE;
1240 formatContext->metadataSourceV2 = NULL;
1241 formatContext->projTypeV2 = INVALID_UINT_VALUE;
1242 formatContext->poseYawV2 = INVALID_UINT_VALUE;
1243 formatContext->posePitchV2 = INVALID_UINT_VALUE;
1244 formatContext->poseRollV2 = INVALID_UINT_VALUE;
1245 formatContext->cbmpLayoutV2 = INVALID_UINT_VALUE;
1246 formatContext->cbmpPaddingV2 = INVALID_UINT_VALUE;
1247 formatContext->equiBoundsTopV2 = INVALID_UINT_VALUE;
1248 formatContext->equiBoundsBottomV2 = INVALID_UINT_VALUE;
1249 formatContext->equiBoundsLeftV2 = INVALID_UINT_VALUE;
1250 formatContext->equiBoundsRightV2 = INVALID_UINT_VALUE;
1252 mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
1254 buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
1256 debug_error(DEBUG, "calloc failed ");
1260 readed = mmfile_read(fp, buffer, basic_header->size);
1261 if (readed != (int)basic_header->size) {
1262 debug_error(DEBUG, "read st3d box failed");
1266 for (i = 0; i + 3 < basic_header->size; ++i) {
1267 if ((buffer[i] == 's' && buffer[i + 1] == 't' && buffer[i + 2] == '3' && buffer[i + 3] == 'd') && (formatContext->stereoModeV2 == INVALID_UINT_VALUE)) {
1268 debug_warning(DEBUG, "st3d data found at offset %lld", basic_header->start_offset + i);
1269 ParseSt3dData(formatContext, fp, basic_header->start_offset + i + 4);
1270 debug_msg(RELEASE, "formatContext->stereoModeV2 = %d", formatContext->stereoModeV2);
1272 if (buffer[i] == 's' && buffer[i + 1] == 'v' && buffer[i + 2] == '3' && buffer[i + 3] == 'd') {
1273 debug_warning(DEBUG, "sv3d data found at offset %lld", basic_header->start_offset + i);
1274 formatContext->isSpherical = true;
1276 if (buffer[i] == 's' && buffer[i + 1] == 'v' && buffer[i + 2] == 'h' && buffer[i + 3] == 'd') {
1277 debug_warning(DEBUG, "svhd data found at offset %lld", basic_header->start_offset + i);
1278 ParseSvhdData(formatContext, fp, basic_header->start_offset + i + 4, mmfile_io_be_uint32(*((uint32_t*)(buffer - 4 + i))));
1279 debug_msg(RELEASE, "formatContext->metadataSourceV2 = %s (length = %zu)", formatContext->metadataSourceV2, strlen(formatContext->metadataSourceV2));
1281 if (buffer[i] == 'p' && buffer[i + 1] == 'r' && buffer[i + 2] == 'o' && buffer[i + 3] == 'j') {
1282 debug_warning(DEBUG, "proj data found at offset %lld", basic_header->start_offset + i);
1283 ParseProjData(formatContext, fp, basic_header->start_offset + i + 4);
1287 return MMFILE_UTIL_SUCCESS;
1290 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1292 return MMFILE_UTIL_FAIL;
1295 static int GetValueFromCDISTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1297 unsigned int value = 0;
1300 if (!formatContext || !fp || !basic_header) {
1301 debug_error(DEBUG, "invalid param");
1302 return MMFILE_UTIL_FAIL;
1305 readed = mmfile_read(fp, (unsigned char *)&value, sizeof(unsigned int));
1306 if (readed != sizeof(unsigned int)) {
1307 debug_error(DEBUG, "read cdis tag header fail");
1311 value = mmfile_io_be_uint32(value);
1313 debug_msg(RELEASE, "Value : 0x%x", value);
1315 if (value == 0x01) {
1316 debug_msg(RELEASE, "This has cdis tag and valid value");
1317 formatContext->cdis = 1;
1319 debug_error(DEBUG, "This has cdis tag and but invalid value");
1323 return MMFILE_UTIL_SUCCESS;
1326 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1328 return MMFILE_UTIL_FAIL;
1331 static int GetTagFromMetaBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1334 MMFILE_MP4_BASIC_BOX_HEADER hdlrBoxHeader = {0, };
1335 MMFILE_MP4_BASIC_BOX_HEADER id3v2BoxHeader = {0, };
1336 MMFILE_3GP_ID3V2_BOX id3v2Box = {0, };
1337 AvFileContentInfo tagInfo = {0, };
1338 unsigned char tagVersion = 0;
1339 bool versionCheck = false;
1341 unsigned int meta_version = 0;
1342 MMFILE_3GP_HANDLER_BOX hdlrBox = {0, };
1343 unsigned int encSize = 0;
1345 #ifdef ENABLE_ITUNES_META /* We don't support itunes meta now. so this is not defined yet */
1346 int iTunes_meta = 0;
1350 readed = mmfile_read(fp, (unsigned char *)&meta_version, 4);
1352 debug_error(DEBUG, "read meta box version");
1357 readed = mmfile_read(fp, (unsigned char *)&hdlrBoxHeader, MMFILE_MP4_BASIC_BOX_HEADER_LEN);
1358 if (readed != MMFILE_MP4_BASIC_BOX_HEADER_LEN) {
1359 debug_error(DEBUG, "read hdlr box header");
1363 if (hdlrBoxHeader.type != FOURCC('h', 'd', 'l', 'r')) {
1364 debug_warning(DEBUG, "meta type is not hdlr");
1368 hdlrBoxHeader.size = mmfile_io_be_uint32(hdlrBoxHeader.size);
1369 hdlrBoxHeader.type = mmfile_io_le_uint32(hdlrBoxHeader.type);
1371 readed = mmfile_read(fp, (unsigned char *)&hdlrBox, MMFILE_3GP_HANDLER_BOX_LEN);
1372 if (readed != MMFILE_3GP_HANDLER_BOX_LEN) {
1373 debug_error(DEBUG, "read hdlr box");
1377 hdlrBox.handler_type = mmfile_io_le_uint32(hdlrBox.handler_type);
1380 * check tag type (ID3v2 or iTunes)
1382 if (hdlrBox.handler_type == FOURCC('I', 'D', '3', '2')) {
1383 debug_msg(RELEASE, "ID3v2 tag detected.");
1386 #ifdef ENABLE_ITUNES_META
1389 } else if (hdlrBox.handler_type == FOURCC('m', 'd', 'i', 'r') &&
1390 mmfile_io_le_uint32(hdlrBox.reserved[0]) == FOURCC('a', 'p', 'p', 'l')) {
1392 debug_msg(RELEASE, "Apple iTunes tag detected by mdir.");
1394 #ifdef ENABLE_ITUNES_META
1398 debug_warning(DEBUG, "unknown meta type. 4CC:[%c%c%c%c]", ((char *)&hdlrBox.handler_type)[0],
1399 ((char *)&hdlrBox.handler_type)[1],
1400 ((char *)&hdlrBox.handler_type)[2],
1401 ((char *)&hdlrBox.handler_type)[3]);
1402 /*goto exception; */
1405 #ifdef ENABLE_ITUNES_META
1406 if (!id3_meta && !iTunes_meta) {
1408 APPLE meta data for iTunes reader = 'mdir.' so if handler type is 'mdir', this content may has itunes meta.
1409 most of contents has 'mdir' + 'appl'. but some contents just has 'mdir'
1410 but 'ilst' is meta for iTunes. so find 'ilst' is more correct to check if this contents has iTunes meta or not.*/
1412 const char *ilst_box = "ilst";
1413 int buf_size = strlen(ilst_box);
1415 unsigned char read_buf[buf_size + 1];
1416 memset(read_buf, 0x00, buf_size + 1);
1419 mmfile_seek(fp, hdlrBoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_HANDLER_BOX_LEN + 4, SEEK_CUR); /*+4 is hdlr size field */
1421 readed = mmfile_read(fp, read_buf, buf_size); /* to find 'ilst' */
1422 if (readed != buf_size) {
1423 debug_error(DEBUG, "read fail [%d]", readed);
1427 if (read_buf[0] == 'i' && read_buf[1] == 'l' && read_buf[2] == 's' && read_buf[3] == 't') {
1428 debug_msg(RELEASE, "Apple iTunes tag detected by ilst.");
1434 #ifdef ENABLE_ITUNES_META
1437 * iTunes (Cover[?ovr] & Track[trkn] only extract!) + Genre/Artist : Added 2010.10.27,28
1445 #define _ITUNES_READ_BUF_SZ 20
1446 #define _ITUNES_TRACK_NUM_SZ 4
1447 #define _ITUNES_GENRE_NUM_SZ 4
1448 #define _ITUNES_COVER_TYPE_JPEG 13
1449 #define _ITUNES_COVER_TYPE_PNG 14
1451 unsigned char read_buf[_ITUNES_READ_BUF_SZ];
1453 int cover_sz = 0, cover_type = 0, cover_found = 0;
1454 /* int track_found = 0; */ /* , track_num = 0; */
1455 /* int genre_found = 0; */ /* , genre_index = 0; */
1456 /* int artist_found = 0; */ /* , artist_sz = 0; */
1457 int limit = basic_header->size - hdlrBoxHeader.size;
1458 long long cover_offset = 0; /*, track_offset =0, genre_offset = 0, artist_offset = 0; */
1460 /* 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++) { */
1461 for (i = 0; (i < limit) && (cover_found == 0) ; i++) {
1462 readed = mmfile_read(fp, read_buf, _ITUNES_READ_BUF_SZ);
1463 if (readed != _ITUNES_READ_BUF_SZ)
1466 /*ffmpeg extract artist, tracknum, genre and cover image. see mov_read_udta_string().
1467 but ffmpeg does not support strange cover image.
1468 only support covr type 0xd(JPEG), 0xe(PNG), 0x1b(BMP). but we support other type*/
1471 * Artist : Added 2010.10.28
1473 if (artist_found == 0 &&
1474 read_buf[0] == 0xa9 && read_buf[1] == 'A' && read_buf[2] == 'R' && read_buf[3] == 'T' &&
1475 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1478 artist_offset = mmfile_tell(fp);
1479 artist_sz = mmfile_io_be_uint32(*(int *)(read_buf + 4)) - 16; /* atom len(4)+data(4)+atom verion(1)+flag(3)+null(4) = 16 */
1481 debug_msg(RELEASE, "----------------------------------- artist found, offset=[%lld], size=[%d]", artist_offset, artist_sz);
1487 if (track_found == 0 &&
1488 read_buf[0] == 't' && read_buf[1] == 'r' && read_buf[2] == 'k' && read_buf[3] == 'n' &&
1489 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1492 track_offset = mmfile_tell(fp);
1494 debug_msg(RELEASE, "----------------------------------- Track found, offset=[%lld]", track_offset);
1498 * Genre : Added 2010.10.27
1500 /*ffmpeg extract genre but only (0xa9,'g','e','n'). see mov_read_udta_string()*/
1501 if (genre_found == 0 &&
1502 read_buf[0] == 'g' && read_buf[1] == 'n' && read_buf[2] == 'r' && read_buf[3] == 'e' &&
1503 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1506 genre_offset = mmfile_tell(fp);
1508 debug_msg(RELEASE, "----------------------------------- genre found, offset=[%lld]", genre_offset);
1516 if (cover_found == 0 &&
1517 read_buf[0] == 'c' && read_buf[1] == 'o' && read_buf[2] == 'v' && read_buf[3] == 'r' &&
1518 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1521 cover_sz = mmfile_io_be_uint32(*(int *)(read_buf + 4)) - 12;
1522 cover_type = mmfile_io_be_uint32(*(int *)(read_buf + 12));
1524 cover_offset = mmfile_tell(fp);
1526 debug_msg(RELEASE, "----------------------------------- cover_found found, offset=[%lld]", cover_offset);
1529 mmfile_seek(fp, -(_ITUNES_READ_BUF_SZ - 1), SEEK_CUR); /*FIXME: poor search*/
1532 /*ffmpeg extract artist, tracknum, except cover image. see mov_read_udta_string()*/
1535 if (artist_sz > 0) {
1536 mmfile_seek(fp, artist_offset, SEEK_SET);
1538 if (formatContext->artist) {
1539 debug_msg(RELEASE, "----------------------------------- previous artist was [%s] ", formatContext->artist);
1540 free(formatContext->artist);
1543 debug_msg(RELEASE, "----------------------------------- new artist will be allocated with size (len+1) [%d] ", artist_sz + 1);
1544 formatContext->artist = g_malloc0(artist_sz + 1);
1546 if (formatContext->artist) {
1547 readed = mmfile_read(fp, (unsigned char *)formatContext->artist, artist_sz);
1548 formatContext->artist[artist_sz] = '\0';
1550 debug_msg(RELEASE, "----------------------------------- new artist is [%s] ", formatContext->artist);
1552 if (readed != artist_sz) {
1553 debug_error(DEBUG, "failed to read. ret = %d, in = %d", readed, artist_sz);
1554 mmfile_free(formatContext->artist);
1561 mmfile_seek(fp, track_offset, SEEK_SET);
1562 readed = mmfile_read(fp, read_buf, _ITUNES_TRACK_NUM_SZ);
1563 if (readed != _ITUNES_TRACK_NUM_SZ) {
1564 debug_error(DEBUG, "failed to read. ret = %d, in = %d", readed, _ITUNES_TRACK_NUM_SZ);
1566 track_num = mmfile_io_be_uint32(*(int *)read_buf);
1567 if (!formatContext->tagTrackNum) {
1568 memset(read_buf, 0x00, _ITUNES_READ_BUF_SZ);
1569 snprintf((char *)read_buf, sizeof(read_buf), "%d", track_num);
1570 formatContext->tagTrackNum = g_strdup((const char *)read_buf);
1576 mmfile_seek(fp, genre_offset, SEEK_SET);
1577 readed = mmfile_read(fp, read_buf, _ITUNES_GENRE_NUM_SZ);
1578 if (readed != _ITUNES_GENRE_NUM_SZ) {
1579 debug_error(DEBUG, "failed to read. ret = %d, in = %d", readed, _ITUNES_GENRE_NUM_SZ);
1581 genre_index = mmfile_io_be_uint16(*(int *)read_buf);
1582 debug_msg(RELEASE, "genre index=[%d] ", genre_index);
1584 if (genre_index > 0 && genre_index < GENRE_COUNT) {
1585 if (!formatContext->genre) {
1586 memset(read_buf, 0x00, _ITUNES_READ_BUF_SZ);
1587 snprintf((char *)read_buf, sizeof(read_buf), "%s", MpegAudio_Genre[genre_index - 1]);
1588 debug_msg(RELEASE, "genre string=[%s] ", read_buf);
1589 formatContext->genre = g_strdup((const char *)read_buf);
1597 1) below spec is in "iTunes Package Asset Specification 4.3" published by apple.
1598 Music Cover Art Image Profile
1599 - TIFF with ".tif" extension (32-bit uncompressed), JPEG with ".jpg" extension (quality unconstrained), or PNG with ".png" extension
1600 - RGB (screen standard)
1601 - Minimum size of 600 x 600 pixels
1602 - Images must be at least 72 dpi
1604 2)I found below info from google.
1605 cover image flag : JPEG (13, 0xd), PNG (14, 0xe)
1607 3)So, FIXME when cover image format is tif!
1611 mmfile_seek(fp, cover_offset, SEEK_SET);
1613 formatContext->artwork = g_malloc0(cover_sz);
1614 formatContext->artworkSize = cover_sz;
1615 if (cover_type == _ITUNES_COVER_TYPE_JPEG) {
1616 formatContext->artworkMime = g_strdup("image/jpeg");
1617 } else if (cover_type == _ITUNES_COVER_TYPE_PNG) {
1618 formatContext->artworkMime = g_strdup("image/png");
1619 /*} else if (cover_type == _ITUNES_COVER_TYPE_TIF) {
1620 formatContext->artworkMime = g_strdup("image/tif");*/
1622 debug_warning(DEBUG, "Not proper cover image type, but set to jpeg. cover_type[%d]", cover_type);
1623 formatContext->artworkMime = g_strdup("image/jpeg");
1626 if (formatContext->artwork) {
1627 readed = mmfile_read(fp, formatContext->artwork, cover_sz);
1628 if (readed != cover_sz) {
1629 debug_error(DEBUG, "failed to read. ret = %d, in = %d", readed, cover_sz);
1630 mmfile_free(formatContext->artwork);
1631 formatContext->artworkSize = 0;
1632 mmfile_free(formatContext->artworkMime);
1638 /*reset seek position*/
1639 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1641 return MMFILE_UTIL_SUCCESS;
1649 /* skip hdlr box name */
1650 mmfile_seek(fp, hdlrBoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_HANDLER_BOX_LEN, SEEK_CUR);
1653 readed = mmfile_read(fp, (unsigned char *)&id3v2BoxHeader, MMFILE_MP4_BASIC_BOX_HEADER_LEN);
1654 if (readed != MMFILE_MP4_BASIC_BOX_HEADER_LEN) {
1655 debug_error(DEBUG, "read id3v2 box header");
1659 id3v2BoxHeader.size = mmfile_io_be_uint32(id3v2BoxHeader.size);
1660 id3v2BoxHeader.type = mmfile_io_le_uint32(id3v2BoxHeader.type);
1662 if (id3v2BoxHeader.type != FOURCC('I', 'D', '3', '2')) {
1663 debug_warning(DEBUG, "meta type is not id3v2");
1667 readed = mmfile_read(fp, (unsigned char *)&id3v2Box, MMFILE_3GP_ID3V2_BOX_LEN);
1668 if (readed != MMFILE_3GP_ID3V2_BOX_LEN) {
1669 debug_error(DEBUG, "read id3v2 box");
1673 id3v2Len = id3v2BoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_ID3V2_BOX_LEN;
1675 id3v2Box.id3v2Data = g_malloc0(id3v2Len);
1677 readed = mmfile_read(fp, (unsigned char *)id3v2Box.id3v2Data, id3v2Len);
1678 if (readed != id3v2Len) {
1679 debug_error(DEBUG, "read id3tag data error");
1684 if (!IS_ID3V2_TAG(id3v2Box.id3v2Data)) {
1685 debug_error(DEBUG, "it is not id3tag");
1689 if (id3v2Box.id3v2Data[3] == 0xFF || id3v2Box.id3v2Data[4] == 0xFF ||
1690 id3v2Box.id3v2Data[6] >= 0x80 || id3v2Box.id3v2Data[7] >= 0x80 ||
1691 id3v2Box.id3v2Data[8] >= 0x80 || id3v2Box.id3v2Data[9] >= 0x80) {
1692 debug_error(DEBUG, "it is not valid id3tag");
1696 tagVersion = id3v2Box.id3v2Data[3];
1697 if (tagVersion > 4) {
1698 debug_error(DEBUG, "tag version is too high");
1702 encSize = mmfile_io_le_uint32((unsigned int)&id3v2Box.id3v2Data[6]);
1703 tagInfo.tagV2Info.tagLen = MP3_TAGv2_HEADER_LEN;
1704 tagInfo.tagV2Info.tagLen += (((encSize & 0x0000007F) >> 0) | ((encSize & 0x00007F00) >> 1) | ((encSize & 0x007F0000) >> 2) | ((encSize & 0x7F000000) >> 3));
1705 tagInfo.tagV2Info.tagVersion = tagVersion;
1706 tagInfo.fileLen = id3v2Len;
1708 /* set id3v2 data to formatContext */
1709 switch (tagVersion) {
1711 versionCheck = mm_file_id3tag_parse_v222(&tagInfo, id3v2Box.id3v2Data, formatContext->extract_artwork);
1714 versionCheck = mm_file_id3tag_parse_v223(&tagInfo, id3v2Box.id3v2Data, formatContext->extract_artwork);
1717 versionCheck = mm_file_id3tag_parse_v224(&tagInfo, id3v2Box.id3v2Data, formatContext->extract_artwork);
1721 debug_error(DEBUG, "tag vesion is not support");
1722 versionCheck = false;
1726 if (versionCheck == false) {
1727 debug_error(DEBUG, "tag parsing is fail");
1731 formatContext->title = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_TITLE].value);
1732 formatContext->artist = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_ARTIST].value);
1733 formatContext->copyright = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_COPYRIGHT].value);
1734 formatContext->comment = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_COMMENT].value);
1735 formatContext->album = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_ALBUM].value);
1736 formatContext->album_artist = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_ALBUM_ARTIST].value);
1737 formatContext->year = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_YEAR].value);
1738 formatContext->genre = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_GENRE].value);
1739 formatContext->tagTrackNum = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_TRACKNUM].value);
1740 formatContext->composer = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_COMPOSER].value);
1741 formatContext->classification = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_CONTENT_GROUP].value);
1742 formatContext->conductor = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_CONDUCTOR].value);
1744 if (tagInfo.imageInfo.pImageBuf && tagInfo.imageInfo.imageLen > 0) {
1745 formatContext->artworkSize = tagInfo.imageInfo.imageLen;
1746 formatContext->artwork = g_memdup2(tagInfo.imageInfo.pImageBuf, tagInfo.imageInfo.imageLen);
1749 mm_file_free_AvFileContentInfo(&tagInfo);
1750 mmfile_free(id3v2Box.id3v2Data);
1752 /*reset seek position*/
1753 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1755 return MMFILE_UTIL_SUCCESS;
1761 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1762 mmfile_free(id3v2Box.id3v2Data);
1763 mm_file_free_AvFileContentInfo(&tagInfo);
1765 return MMFILE_UTIL_FAIL;
1768 int mm_file_get_int_value_from_xml_string(const char* xml_str, const char* param_name, int* value)
1770 char *value_start, *value_end, *endptr;
1771 const short value_length_max = 12;
1772 char init_view_ret[value_length_max];
1773 int value_length = 0;
1775 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1776 debug_error(DEBUG, "error: incorrect or non-existing parameter");
1777 return MMFILE_UTIL_FAIL;
1780 value_start = strstr(xml_str, param_name) + strlen(param_name);
1781 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
1784 value_end = strchr(value_start, '<');
1786 debug_error(DEBUG, "error: incorrect XML");
1787 return MMFILE_UTIL_FAIL;
1790 value_length = value_end - value_start;
1791 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1795 if (value_start[i] == '+' || value_start[i] == '-')
1797 while (i < value_length) {
1798 if (value_start[i] < '0' || value_start[i] > '9') {
1799 debug_error(DEBUG, "error: incorrect value, integer was expected");
1800 return MMFILE_UTIL_FAIL;
1805 if (value_length >= value_length_max || value_length < 1) {
1806 debug_error(DEBUG, "error: empty XML value or incorrect range");
1807 return MMFILE_UTIL_FAIL;
1810 memset(init_view_ret, 0x00, value_length_max);
1811 if (g_strlcpy(init_view_ret, value_start, value_length_max) >= value_length_max) {
1812 debug_error(DEBUG, "error: truncation occurred");
1813 return MMFILE_UTIL_FAIL;
1817 *value = strtol(init_view_ret, &endptr, 10);
1818 if (endptr == init_view_ret) {
1819 debug_error(DEBUG, "error: no digits were found");
1820 return MMFILE_UTIL_FAIL;
1823 return MMFILE_UTIL_SUCCESS;
1826 int mm_file_get_string_value_from_xml_string(const char* xml_str, const char* param_name, char** value)
1828 char *value_start, *value_end;
1829 const short value_length_max = 256;
1830 int value_length = 0;
1832 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1833 debug_error(DEBUG, "error: incorrect or non-existing parameter");
1834 return MMFILE_UTIL_FAIL;
1837 value_start = strstr(xml_str, param_name) + strlen(param_name);
1838 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
1841 value_end = strchr(value_start, '<');
1843 debug_error(DEBUG, "error: incorrect XML");
1844 return MMFILE_UTIL_FAIL;
1847 value_length = value_end - value_start;
1848 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1851 if (value_length >= value_length_max || value_length < 1) {
1852 debug_error(DEBUG, "error: empty XML value or incorrect range");
1853 return MMFILE_UTIL_FAIL;
1856 *value = (char*)calloc(value_length, sizeof(char));
1857 if (*value == NULL) {
1858 debug_error(DEBUG, "error: calloc failed");
1859 return MMFILE_UTIL_FAIL;
1861 strncpy(*value, value_start, value_length);
1863 return MMFILE_UTIL_SUCCESS;
1866 int mm_file_get_bool_value_from_xml_string(const char* xml_str, const char* param_name, bool* value)
1868 char *value_start = NULL;
1869 char *value_end = NULL;
1870 int value_length = 0;
1872 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1873 debug_error(DEBUG, "error: incorrect or non-existing parameter");
1874 return MMFILE_UTIL_FAIL;
1877 value_start = strstr(xml_str, param_name) + strlen(param_name);
1878 while ((value_start != NULL) && ((value_start[0] == ' ') || (value_start[0] == '\t')))
1881 value_end = strchr(value_start, '<');
1882 if (value_end == NULL) {
1883 debug_error(DEBUG, "error: incorrect XML.");
1884 return MMFILE_UTIL_FAIL;
1887 value_length = value_end - value_start;
1888 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1891 if (value_length < 1) {
1892 debug_error(DEBUG, "error: empty XML value or incorrect range");
1893 return MMFILE_UTIL_FAIL;
1896 *value = strstr(value_start, "true") ? true : false;
1898 return MMFILE_UTIL_SUCCESS;
1901 int ParseSpatialVideoMetadataFromXMLString(const char *xmlStr, MMFileFormatContext *formatContext)
1903 const char is_spherical_str[] = "<GSpherical:Spherical>";
1904 const char is_stitched_str[] = "<GSpherical:Stitched>";
1905 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
1906 const char projection_type_str[] = "<GSpherical:ProjectionType>";
1907 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
1908 const char source_count_str[] = "<GSpherical:SourceCount>";
1909 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
1910 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
1911 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
1912 const char timestamp_str[] = "<GSpherical:Timestamp>";
1913 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
1914 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
1915 const char cropped_area_image_width_str[] = "<GSpherical:CroppedAreaImageWidthPixels>";
1916 const char cropped_area_image_height_str[] = "<GSpherical:CroppedAreaImageHeightPixels>";
1917 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
1918 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
1920 mm_file_get_bool_value_from_xml_string(xmlStr, is_spherical_str, (bool*)&formatContext->isSpherical);
1921 mm_file_get_bool_value_from_xml_string(xmlStr, is_stitched_str, (bool*)&formatContext->isStitched);
1923 debug_msg(RELEASE, "isSpherical = %d", formatContext->isSpherical);
1924 debug_msg(RELEASE, "isStitched = %d", formatContext->isStitched);
1926 if (formatContext->isSpherical && formatContext->isStitched) {
1927 mm_file_get_string_value_from_xml_string(xmlStr, stitching_software_str, &formatContext->stitchingSoftware);
1928 mm_file_get_string_value_from_xml_string(xmlStr, projection_type_str, &formatContext->projectionType);
1929 mm_file_get_string_value_from_xml_string(xmlStr, stereo_mode_str, &formatContext->stereoMode);
1930 mm_file_get_int_value_from_xml_string(xmlStr, source_count_str, &formatContext->sourceCount);
1931 mm_file_get_int_value_from_xml_string(xmlStr, init_view_heading_str, &formatContext->initViewHeading);
1932 mm_file_get_int_value_from_xml_string(xmlStr, init_view_pitch_str, &formatContext->initViewPitch);
1933 mm_file_get_int_value_from_xml_string(xmlStr, init_view_roll_str, &formatContext->initViewRoll);
1934 mm_file_get_int_value_from_xml_string(xmlStr, timestamp_str, &formatContext->timestamp);
1935 mm_file_get_int_value_from_xml_string(xmlStr, full_pano_width_str, &formatContext->fullPanoWidth);
1936 mm_file_get_int_value_from_xml_string(xmlStr, full_pano_height_str, &formatContext->fullPanoHeight);
1937 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_width_str, &formatContext->croppedAreaImageWidth);
1938 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_height_str, &formatContext->croppedAreaImageHeight);
1939 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_left_str, &formatContext->croppedAreaLeft);
1940 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_top_str, &formatContext->croppedAreaTop);
1942 debug_msg(RELEASE, "stitchingSoftware = %s", formatContext->stitchingSoftware);
1943 debug_msg(RELEASE, "projectionType = %s", formatContext->projectionType);
1944 debug_msg(RELEASE, "stereoMode = %s", formatContext->stereoMode);
1945 debug_msg(RELEASE, "sourceCount %d", formatContext->sourceCount);
1946 debug_msg(RELEASE, "initViewHeading = %d", formatContext->initViewHeading);
1947 debug_msg(RELEASE, "initViewPitch = %d", formatContext->initViewPitch);
1948 debug_msg(RELEASE, "initViewRoll = %d", formatContext->initViewRoll);
1949 debug_msg(RELEASE, "timestamp = %d", formatContext->timestamp);
1950 debug_msg(RELEASE, "fullPanoWidthPixels = %d", formatContext->fullPanoWidth);
1951 debug_msg(RELEASE, "fullPanoHeightPixels = %d", formatContext->fullPanoHeight);
1952 debug_msg(RELEASE, "croppedAreaImageWidth = %d", formatContext->croppedAreaImageWidth);
1953 debug_msg(RELEASE, "croppedAreaImageHeight = %d", formatContext->croppedAreaImageHeight);
1954 debug_msg(RELEASE, "croppedAreaLeft = %d", formatContext->croppedAreaLeft);
1955 debug_msg(RELEASE, "croppedAreaTop = %d", formatContext->croppedAreaTop);
1958 return MMFILE_UTIL_SUCCESS;
1961 #define BIG_CONTENT_BOX_SIZE_LEN 8
1963 int MMFileUtilGetMetaDataFromMKV(MMFileFormatContext *formatContext)
1965 MMFileIOHandle *fp = NULL;
1966 int probe_size = 10000;
1967 unsigned char *buffer = NULL;
1970 long long file_size = 0;
1972 MMFILE_WEBM_PROJ_V2_BOX v2box = { 0, };
1973 MMFILE_WEBM_EQUI_PROJ_V2_BOX equi = { 0, };
1974 MMFILE_WEBM_CBMP_PROJ_V2_BOX cbmp = { 0, };
1975 MMFILE_WEBM_POSE_ELEMENT_V2_BOX pose = { 0, };
1977 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
1978 if (ret == MMFILE_UTIL_FAIL) {
1979 debug_error(DEBUG, "error: mmfile_open");
1983 file_size = mmfile_seek(fp, 0, SEEK_END);
1984 if (file_size == MMFILE_UTIL_FAIL) {
1985 debug_error(DEBUG, "mmfile operation failed");
1989 probe_size = (file_size > probe_size) ? probe_size : file_size;
1990 buffer = (unsigned char *)malloc(probe_size * sizeof(unsigned char));
1992 debug_error(DEBUG, "malloc failed");
1996 ret = mmfile_seek(fp, 0, SEEK_SET);
1997 if (ret == MMFILE_UTIL_FAIL) {
1998 debug_error(DEBUG, "mmfile operation failed");
2002 ret = mmfile_read(fp, buffer, probe_size * sizeof(unsigned char));
2003 if (ret == MMFILE_UTIL_FAIL) {
2004 debug_error(DEBUG, "mmfile operation failed");
2008 /* FIXME (m.alieksieie): It's better to use some EBML parser here*/
2009 for (i = 0; i + 3 < probe_size; ++i) {
2010 if (*(unsigned int *)(buffer + i) == FOURCC('e', 'q', 'u', 'i') ||
2011 *(unsigned int *)(buffer + i) == FOURCC('c', 'b', 'm', 'p')) {
2012 debug_msg(DEBUG, "projection data found at offset %d bytes", i);
2017 if (i + 3 == probe_size) {
2018 debug_msg(DEBUG, "projection info wasn't found");
2019 ret = MMFILE_UTIL_SUCCESS;
2023 if ((i - (int)sizeof(MMFILE_WEBM_PROJ_V2_BOX)) < 0) {
2024 debug_error(DEBUG, "error: invalid supposed projection info location");
2025 ret = MMFILE_UTIL_FAIL;
2029 ret = mmfile_seek(fp, i - sizeof(MMFILE_WEBM_PROJ_V2_BOX), SEEK_SET);
2030 if (ret == MMFILE_UTIL_FAIL) {
2031 debug_error(DEBUG, "error: failed to seek to the supposed projection info location");
2035 ret = mmfile_read(fp, (unsigned char *)&v2box, sizeof(MMFILE_WEBM_PROJ_V2_BOX));
2036 if (ret == MMFILE_UTIL_FAIL) {
2037 debug_error(DEBUG, "mmfile operation failed");
2041 if (v2box.proj_type_box_value == PROJECTION_TYPE_EQUI) {
2042 debug_msg(DEBUG, "Equirectangular projection is used");
2044 ret = mmfile_read(fp, (unsigned char *)&equi, sizeof(MMFILE_WEBM_EQUI_PROJ_V2_BOX));
2045 if (ret == MMFILE_UTIL_FAIL) {
2046 debug_error(DEBUG, "error: failed to read equirectangular element");
2049 if (strncmp((char *)equi.proj_priv_box_name, "equi", 4) == 0) {
2050 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_top);
2051 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_bottom);
2052 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_left);
2053 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_right);
2055 debug_error(DEBUG, "error: failed to read equirectangular element");
2056 ret = MMFILE_UTIL_SUCCESS;
2060 if (v2box.proj_type_box_value == PROJECTION_TYPE_CBMP) {
2061 debug_msg(DEBUG, "Cubemap projection is used");
2063 ret = mmfile_read(fp, (unsigned char *)&cbmp, sizeof(MMFILE_WEBM_CBMP_PROJ_V2_BOX));
2064 if (ret == MMFILE_UTIL_FAIL) {
2065 debug_error(DEBUG, "error: failed to read cubemap element");
2068 if (strncmp((char *)cbmp.proj_priv_box_name, "cbmp", 4) == 0) {
2069 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_layout);
2070 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_padding);
2072 debug_error(DEBUG, "error: failed to read cubemap element");
2073 ret = MMFILE_UTIL_FAIL;
2078 ret = mmfile_read(fp, (unsigned char *)&pose, sizeof(MMFILE_WEBM_POSE_ELEMENT_V2_BOX));
2079 if (ret == MMFILE_UTIL_FAIL) {
2080 debug_error(DEBUG, "error: failed to read pose info");
2084 if (pose.pose_yaw_element_id == POSE_YAW_ELEMENT_ID) {
2085 formatContext->poseYawV2 = (uint)mmfile_io_be_float32(pose.pose_yaw_element_value);
2087 debug_error(DEBUG, "error: failed to pose yaw element");
2088 ret = MMFILE_UTIL_FAIL;
2091 if (pose.pose_pitch_element_id == POSE_PITCH_ELEMENT_ID) {
2092 formatContext->posePitchV2 = (uint)mmfile_io_be_float32(pose.pose_pitch_element_value);
2094 debug_error(DEBUG, "error: failed to pose pitch element");
2095 ret = MMFILE_UTIL_FAIL;
2098 if (pose.pose_roll_element_id == POSE_ROLL_ELEMENT_ID) {
2099 formatContext->poseRollV2 = (uint)mmfile_io_be_float32(pose.pose_roll_element_value);
2101 debug_error(DEBUG, "error: failed to pose roll element");
2102 ret = MMFILE_UTIL_FAIL;
2115 int MMFileUtilGetMetaDataFromMP4(MMFileFormatContext *formatContext)
2117 MMFileIOHandle *fp = NULL;
2120 unsigned long long chunk_size = 0;
2121 long long moov_end = 0;
2122 MMFILE_MP4_BASIC_BOX_HEADER basic_header = {0, };
2123 int junk_counter = 0;
2125 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
2126 if (ret == MMFILE_UTIL_FAIL) {
2127 debug_error(DEBUG, "error: mmfile_open");
2131 basic_header.start_offset = mmfile_tell(fp);
2133 if (g_junk_counter_limit == 0)
2134 g_junk_counter_limit = mmfile_get_int_from_ini(MMFILE_INI_JUNKCNTLIMIT, MMFILE_DEFAULT_JUNKCNTLIMIT);
2136 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)) {
2137 basic_header.size = mmfile_io_be_uint32(basic_header.size);
2138 basic_header.type = mmfile_io_le_uint32(basic_header.type);
2140 if (basic_header.size == 0) {
2141 debug_warning(DEBUG, "header is invalid.");
2142 basic_header.size = readed;
2143 basic_header.type = 0;
2144 chunk_size = basic_header.size;
2146 if ((moov_end != 0) && (moov_end < basic_header.start_offset)) {
2147 debug_msg(DEBUG, "found junk data but moov data already was extracted, so junk counter will be increase: %d", junk_counter);
2150 /* stop the loop for junk case. */
2151 if ((g_junk_counter_limit > 0) && (junk_counter > g_junk_counter_limit)) {
2152 debug_msg(DEBUG, "stop the loop by junk-data checker");
2153 ret = MMFILE_UTIL_FAIL;
2157 } else if (basic_header.size == 1) {
2159 unsigned char temp[BIG_CONTENT_BOX_SIZE_LEN] = {0, };
2160 unsigned long long size = 0;
2162 mmfile_read(fp, (unsigned char *)&temp, BIG_CONTENT_BOX_SIZE_LEN);
2164 for (i = 0; i < BIG_CONTENT_BOX_SIZE_LEN; i++)
2165 size |= (unsigned long long)temp[i] << (BIG_CONTENT_BOX_SIZE_LEN - 1 - i) * BIG_CONTENT_BOX_SIZE_LEN;
2169 chunk_size = basic_header.size;
2173 switch (basic_header.type) {
2174 case FOURCC('m', 'o', 'o', 'v'): {
2175 debug_msg(RELEASE, "MPEG4: [moov] SIZE: [%lld]Byte", chunk_size);
2176 moov_end = basic_header.start_offset + chunk_size;
2179 case FOURCC('u', 'd', 't', 'a'): {
2180 debug_msg(RELEASE, "MPEG4: [udat] SIZE: [%lld]Byte", chunk_size);
2183 /*/////////////////////////////////////////////////////////////// */
2184 /* Extracting Tag Data // */
2185 /*/////////////////////////////////////////////////////////////// */
2186 case FOURCC('t', 'i', 't', 'l'): {
2187 debug_msg(RELEASE, "MPEG4: [titl] SIZE: [%lld]Byte", chunk_size);
2188 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_TITLE);
2191 case FOURCC('d', 's', 'c', 'p'): {
2192 debug_msg(RELEASE, "MPEG4: [dscp] SIZE: [%lld]Byte", chunk_size);
2193 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_CAPTION);
2196 case FOURCC('c', 'p', 'r', 't'): {
2197 debug_msg(RELEASE, "MPEG4: [cprt] SIZE: [%lld]Byte", chunk_size);
2198 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_COPYRIGHT);
2201 case FOURCC('p', 'e', 'r', 'f'): {
2202 debug_msg(RELEASE, "MPEG4: [perf] SIZE: [%lld]Byte", chunk_size);
2203 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_PERFORMER);
2206 case FOURCC('a', 'u', 't', 'h'): {
2207 debug_msg(RELEASE, "MPEG4: [auth] SIZE: [%lld]Byte", chunk_size);
2208 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_AUTHOR);
2211 case FOURCC('g', 'n', 'r', 'e'): {
2212 debug_msg(RELEASE, "MPEG4: [gnre] SIZE: [%lld]Byte", chunk_size);
2213 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_GENRE);
2216 case FOURCC('a', 'l', 'b', 'm'): {
2217 debug_msg(RELEASE, "MPEG4: [albm] SIZE: [%lld]Byte", chunk_size);
2218 GetAlbumFromAlbumTagBox(formatContext, fp, &basic_header);
2221 case FOURCC('y', 'r', 'r', 'c'): {
2222 debug_msg(RELEASE, "MPEG4: [yrrc] SIZE: [%lld]Byte", chunk_size);
2223 GetYearFromYearTagBox(formatContext, fp, &basic_header);
2226 case FOURCC('r', 't', 'n', 'g'): {
2227 debug_msg(RELEASE, "MPEG4: [rtng] SIZE: [%lld]Byte", chunk_size);
2228 GetRatingFromRatingTagBox(formatContext, fp, &basic_header); /* not use */
2231 case FOURCC('c', 'l', 's', 'f'): {
2232 debug_msg(RELEASE, "MPEG4: [clsf] SIZE: [%lld]Byte", chunk_size);
2233 GetClassficationFromClsfTagBox(formatContext, fp, &basic_header);
2236 case FOURCC('k', 'y', 'w', 'd'): {
2237 debug_msg(RELEASE, "MPEG4: [kywd] SIZE: [%lld]Byte", chunk_size);
2238 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2241 case FOURCC('l', 'o', 'c', 'i'): {
2242 debug_msg(RELEASE, "MPEG4: [loci] SIZE: [%lld]Byte", chunk_size);
2243 GetLocationFromLociTagBox(formatContext, fp, &basic_header);
2246 /* Check smta in user data field (moov) to be compatible with android */
2247 case FOURCC('s', 'm', 't', 'a'): {
2248 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte", chunk_size);
2249 GetSAUTInfoFromSMTATagBox(formatContext, fp, &basic_header);
2252 /* Check cdis in user data field (moov) to be compatible with android */
2253 case FOURCC('c', 'd', 'i', 's'): {
2254 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte", chunk_size);
2255 GetValueFromCDISTagBox(formatContext, fp, &basic_header);
2258 /*/////////////////////////////////////////////////////////////// */
2259 /* Extracting ID3 Tag Data // */
2260 /*/////////////////////////////////////////////////////////////// */
2261 case FOURCC('m', 'e', 't', 'a'): {
2262 debug_msg(RELEASE, "MPEG4: [meta] SIZE: [%lld]Byte", chunk_size);
2263 GetTagFromMetaBox(formatContext, fp, &basic_header);
2267 case FOURCC('t', 'r', 'a', 'k'): {
2268 debug_msg(RELEASE, "MPEG4: [trak] SIZE: [%lld]Byte", chunk_size);
2271 case FOURCC('u', 'u', 'i', 'd'): {
2272 unsigned long uuid[4] = {0, };
2274 debug_msg(RELEASE, "MPEG4: [uuid] SIZE: [%lld]Byte", chunk_size);
2276 mmfile_read(fp, (unsigned char *)uuid, sizeof(uuid));
2278 if (mmfile_io_be_uint32(uuid[0]) == 0xffcc8263
2279 && mmfile_io_be_uint32(uuid[1]) == 0xf8554a93
2280 && mmfile_io_be_uint32(uuid[2]) == 0x8814587a
2281 && mmfile_io_be_uint32(uuid[3]) == 0x02521fdd) {
2283 str = (char *)malloc(basic_header.size);
2286 memset(str, 0, basic_header.size);
2287 mmfile_read(fp, (unsigned char *)str, basic_header.size);
2289 /* The block is superseded */
2290 if (strstr(str, "<GSpherical:Spherical>true</GSpherical:Spherical>"))
2291 formatContext->is_360 = 1;
2293 formatContext->is_360 = 0;
2294 /* Image can be stitched even if it is not spherical */
2295 if (formatContext->is_360 == 1) {
2296 if (strstr(str, "<GSpherical:Stitched>true</GSpherical:Stitched>"))
2297 formatContext->stitched = MMFILE_360_STITCHED;
2299 formatContext->stitched = MMFILE_360_NON_STITCHED;
2301 /* Image can be stitched or non-stitched. Usage of some 3rd value is superfluous */
2302 formatContext->stitched = MMFILE_360_NONE;
2306 debug_msg(RELEASE, "Extracting tags from UUID XML string %s", str);
2308 ParseSpatialVideoMetadataFromXMLString(str, formatContext);
2311 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2315 case FOURCC('m', 'd', 'i', 'a'): {
2316 debug_msg(RELEASE, "MPEG4: [mdia] SIZE: [%lld]Byte", chunk_size);
2319 case FOURCC('m', 'i', 'n', 'f'): {
2320 debug_msg(RELEASE, "MPEG4: [minf] SIZE: [%lld]Byte", chunk_size);
2323 case FOURCC('s', 't', 'b', 'l'): {
2324 debug_msg(RELEASE, "MPEG4: [stbl] SIZE: [%lld]Byte", chunk_size);
2327 case FOURCC('s', 't', 's', 'd'): {
2328 debug_msg(RELEASE, "MPEG4: [stsd] SIZE: [%lld]Byte", chunk_size);
2331 case FOURCC('m', 'p', '4', 'a'): {
2332 debug_msg(RELEASE, "MPEG4: [mp4a] SIZE: [%lld]Byte", chunk_size);
2333 GetSA3DInfoFromMP4ATagBox(formatContext, fp, &basic_header);
2334 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2337 case FOURCC('a', 'v', 'c', '1'): {
2338 debug_msg(RELEASE, "MPEG4: [avc1] SIZE: [%lld]Byte (offset: %lld)", chunk_size, basic_header.start_offset);
2339 GetVideoV2MetadataFromAvc1TagBox(formatContext, fp, &basic_header);
2343 debug_msg(RELEASE, "4CC: Not Support [%c%c%c%c]. So skip it. Size [%lld Byte]",
2344 ((char *)&basic_header.type)[0], ((char *)&basic_header.type)[1],
2345 ((char *)&basic_header.type)[2], ((char *)&basic_header.type)[3], chunk_size);
2346 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2351 if (ret == MMFILE_UTIL_FAIL) {
2352 debug_error(DEBUG, "mmfile operation is error");
2357 long long new_pos = mmfile_tell(fp);
2359 if ((moov_end == 0) && (new_pos <= basic_header.start_offset)) {
2360 debug_error(DEBUG, "Wrong position");
2361 ret = MMFILE_UTIL_FAIL;
2364 basic_header.start_offset = new_pos;
2373 static bool __get_genre_num(const char *buffer, int *si)
2375 int len = strlen(buffer);
2376 char *tmp_genre = NULL;
2378 /* Genre format: (###), ### */
2379 if (len > 6 || len == 0)
2384 if (!g_ascii_isdigit(buffer[0]))
2387 if (!g_ascii_isdigit(buffer[1]))
2391 /* Remove bind () if exist */
2392 tmp_genre = g_strdup(buffer);
2393 if (g_str_has_prefix(tmp_genre, "(") && g_str_has_suffix(tmp_genre, ")"))
2396 *si = atoi(tmp_genre);
2402 static bool make_characterset_array(char ***charset_array)
2404 char *locale = MMFileUtilGetLocale();
2406 *charset_array = calloc(AV_ID3V2_MAX, sizeof(char *));
2408 if (*charset_array == NULL) {
2409 debug_error(DEBUG, "calloc failed ");
2415 if (locale != NULL) {
2416 (*charset_array)[AV_ID3V2_ISO_8859] = strdup(locale);
2418 debug_error(DEBUG, "get locale failed");
2419 (*charset_array)[AV_ID3V2_ISO_8859] = NULL;
2422 (*charset_array)[AV_ID3V2_UTF16] = strdup("UCS2");
2423 (*charset_array)[AV_ID3V2_UTF16_BE] = strdup("UTF16-BE");
2424 (*charset_array)[AV_ID3V2_UTF8] = strdup(MMFILE_CODESET_UTF8);
2429 static bool release_characterset_array(char **charset_array)
2433 for (i = 0; i < AV_ID3V2_MAX; i++) {
2434 if (charset_array[i] != NULL) {
2435 free(charset_array[i]);
2436 charset_array[i] = NULL;
2440 if (charset_array != NULL) {
2441 free(charset_array);
2442 charset_array = NULL;
2448 static void init_content_info(AvFileContentInfo *pInfo)
2452 for(i = 0; i < AV_ID3TAG_MAX; i++)
2453 pInfo->tagInfo[i].value = NULL;
2455 pInfo->imageInfo.bURLInfo = false;
2456 pInfo->imageInfo.pImageBuf = NULL;
2457 pInfo->imageInfo.imageLen = 0;
2460 static void __id3tag_skip_newline(unsigned char *pTagVal, int *nTagLen, int *offset)
2462 /* skip newline in text encoding of ID3 tag frame */
2463 while ((NEWLINE_OF_UTF16(pTagVal + *offset) || NEWLINE_OF_UTF16_R(pTagVal + *offset)) && *nTagLen > 4) {
2469 static int __id3tag_get_text_encoding_v222(unsigned char *pTagVal, int offset)
2471 if (pTagVal[offset - 1] == 0x00)
2472 return AV_ID3V2_ISO_8859;
2474 return AV_ID3V2_UTF16;
2477 static int __id3tag_get_text_encoding_v223(unsigned char *pTagVal, int *npTagLen, int nTextEnc, int *offset)
2479 if ((IS_ENCODEDBY_UTF16(pTagVal + *offset) || IS_ENCODEDBY_UTF16_R(pTagVal + *offset)) && *npTagLen > 2) {
2480 __id3tag_skip_newline(pTagVal, npTagLen, offset);
2482 if (IS_ENCODEDBY_UTF16(pTagVal + *offset) && (*npTagLen > 2)) {
2485 return AV_ID3V2_UTF16;
2486 } else if (IS_ENCODEDBY_UTF16_R(pTagVal + *offset) && (*npTagLen > 2)) {
2489 return AV_ID3V2_UTF16_BE;
2490 } else if (IS_ENCODEDBY_UTF16(pTagVal + *offset + 1) && (*npTagLen > 3)) {
2493 return AV_ID3V2_UTF16;
2494 } else if (IS_ENCODEDBY_UTF16_R(pTagVal + *offset + 1) && (*npTagLen > 3)) {
2497 return AV_ID3V2_UTF16_BE;
2499 debug_msg(RELEASE, "id3tag never get here!!");
2500 return nTextEnc; /* default bypass */
2503 while ((pTagVal[*offset] < 0x20) && (*offset < *npTagLen)) { /* text string encoded by ISO-8859-1 */
2507 return AV_ID3V2_ISO_8859;
2511 static int __id3tag_get_text_encoding_v224(unsigned char *pTagVal, int *npTagLen, int nTextEnc, int *offset)
2513 if (nTextEnc == AV_ID3V2_UTF16 || nTextEnc == AV_ID3V2_UTF16_BE) {
2514 __id3tag_skip_newline(pTagVal, npTagLen, offset);
2516 if ((IS_ENCODEDBY_UTF16(pTagVal + *offset) || IS_ENCODEDBY_UTF16_R(pTagVal + *offset)) && *npTagLen > 2) {
2519 return AV_ID3V2_UTF16;
2521 debug_msg(RELEASE, "id3tag never get here!!");
2522 return nTextEnc; /* default bypass */
2524 } else if (nTextEnc == AV_ID3V2_UTF8) {
2525 while (pTagVal[*offset] < 0x20 && (*offset < *npTagLen)) { /* text string encoded by UTF-8 */
2529 return AV_ID3V2_UTF8;
2531 while (pTagVal[*offset] < 0x20 && (*offset < *npTagLen)) { /* text string encoded by ISO-8859-1 */
2535 return AV_ID3V2_ISO_8859;
2539 static void __id3tag_parse_SYLT(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int textEncodingType, int offset)
2543 int copy_start_pos = offset;
2544 AvSynclyricsInfo *synclyrics_info = NULL;
2545 GList *synclyrics_info_list = NULL;
2547 if (nTagLen < MMFILE_SYNC_LYRIC_INFO_MIN_LEN) {
2548 debug_msg(RELEASE, "failed to get Synchronised lyrics Info realCpyFramNum(%d)", nTagLen);
2552 if ((textEncodingType == AV_ID3V2_UTF16) || (textEncodingType == AV_ID3V2_UTF16_BE)) {
2553 debug_warning(DEBUG, "[%d] not implemented", textEncodingType);
2557 for (idx = 0; idx < nTagLen; idx++) {
2558 if (pTagVal[offset + idx] == 0x00) {
2559 synclyrics_info = g_new0(AvSynclyricsInfo, 1);
2560 if (textEncodingType == AV_ID3V2_UTF8)
2561 synclyrics_info->lyric_info = g_memdup2(pTagVal + copy_start_pos, copy_len + 1);
2563 synclyrics_info->lyric_info = mmfile_convert_to_utf8((const char *)&pTagVal[copy_start_pos], copy_len, pCharSet);
2565 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];
2567 copy_start_pos = offset + idx + 1;
2568 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);
2570 synclyrics_info_list = g_list_append(synclyrics_info_list, synclyrics_info);
2575 pInfo->pSyncLyrics = synclyrics_info_list;
2578 static bool __id3tag_parse_PIC_format(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2580 unsigned int idx = 0;
2582 /* get the mime type of Attached PICture, it is text string */
2584 if (pTagVal[*offset] == '\0') {
2585 debug_msg(RELEASE, "The picture format of PIC is not included");
2589 /* init ext variable */
2590 memset(pInfo->imageInfo.imageExt, 0, sizeof(pInfo->imageInfo.imageExt));
2592 while ((idx < MP3_ID3_IMAGE_EXT_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2593 pInfo->imageInfo.imageExt[idx] = pTagVal[idx];
2602 static bool __id3tag_parse_APIC_mimetype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2604 unsigned int idx = 0;
2605 const char *MIME_PRFIX = "image/";
2607 /* get the mime type of Attached PICture, it is text string */
2609 if (pTagVal[*offset] == '\0') {
2610 pInfo->imageInfo.imgMimetypeLen = 0;
2611 debug_msg(RELEASE, "The MIME type of APIC is not included");
2615 /* init mimetype variable */
2616 memset(pInfo->imageInfo.imageMIMEType, 0, sizeof(pInfo->imageInfo.imageMIMEType));
2618 while ((idx < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2619 pInfo->imageInfo.imageMIMEType[idx] = pTagVal[idx];
2622 pInfo->imageInfo.imgMimetypeLen = idx;
2626 if (strncmp(pInfo->imageInfo.imageMIMEType, MIME_PRFIX, strlen(MIME_PRFIX)) != 0) {
2627 pInfo->imageInfo.imgMimetypeLen = 0;
2628 debug_error(DEBUG, "MIME type(%s) is not image", pInfo->imageInfo.imageMIMEType);
2632 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2633 debug_msg(RELEASE, "pTagVal[offset](%d) mimetype is not NULL terminated! realCpyFrameNum - offset(%d)",
2634 pTagVal[*offset], nTagLen - *offset);
2638 (*offset)++;/* end of MIME('\0', 1byte) */
2639 debug_msg(RELEASE, "after scaning Mime type offset(%d) value!", *offset);
2644 static void __id3tag_parse_APIC_pictype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2646 /* get the picture type of Attached PICture, it is 1byte(0xff) */
2648 if (pTagVal[*offset] < MP3_ID3V2_PICTURE_TYPE_MAX)
2649 pInfo->imageInfo.pictureType = pTagVal[*offset];
2651 debug_msg(RELEASE, "APIC image has invalid picture type(0x%x)", pTagVal[*offset]);
2653 (*offset)++;/* PictureType(1byte) */
2654 debug_msg(RELEASE, "after scaning PictureType(%d) offset(%d) value!", pInfo->imageInfo.pictureType, *offset);
2657 static void __id3tag_parse_APIC_desc(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int *offset)
2659 /* get the description of Attached PICture, it is text string */
2662 unsigned int tag_len = 0;
2663 char *tmp_desc = NULL;
2665 if (pTagVal[*offset] == 0x0) {
2666 debug_msg(RELEASE, "The description of APIC is not included!!!");
2671 if (pTagVal[*offset + idx] == '\0') {
2672 if (nTagLen < (*offset + idx)) {
2673 debug_error(DEBUG, "End of APIC Tag %d %d %d", nTagLen, *offset, idx);
2676 /* check end of image description */
2677 if ((pTagVal[*offset + idx + 1] == gTagJPEGHeader[0]) ||
2678 (pTagVal[*offset + idx + 1] == gTagPNGHeader[0])) {
2679 debug_msg(RELEASE, "length of description (%d)", idx);
2686 tag_len = idx + 1; /* length of description + '\0' */
2688 tmp_desc = g_memdup2(pTagVal + *offset, tag_len);
2690 /* convert description */
2691 pInfo->imageInfo.imageDescription = mmfile_convert_to_utf8(tmp_desc, tag_len, pCharSet);
2692 mmfile_free(tmp_desc);
2693 debug_msg(RELEASE, "new_desc %s", pInfo->imageInfo.imageDescription);
2696 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2697 debug_msg(RELEASE, "pTagVal[offset](%d) description is not NULL terminated! realCpyFrameNum - offset(%d)",
2698 pTagVal[*offset], nTagLen - *offset);
2701 (*offset)++; /* end of desceription(1byte) */
2704 static void __id3tag_parse_APIC_picture(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2706 /* get the picture of Attached PICture, it is binary data */
2708 /* some content has useless '\0' in front of picture data */
2709 while (pTagVal[*offset] == '\0') {
2713 debug_msg(RELEASE, "after scaning APIC description offset(%d) value!", *offset);
2715 if (nTagLen <= *offset) {
2716 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - offset(%d)", nTagLen, *offset);
2720 pInfo->imageInfo.imageLen = nTagLen - *offset;
2721 pInfo->imageInfo.pImageBuf = g_memdup2(pTagVal + *offset, pInfo->imageInfo.imageLen);
2723 /* if mimetype is "-->", image date has an URL */
2724 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
2725 pInfo->imageInfo.bURLInfo = true;
2728 static bool _mm_file_id3tag_parse_PIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2730 /* current position to read pTagVal */
2733 debug_fenter(RELEASE);
2735 if (!__id3tag_parse_PIC_format(pInfo, pTagVal, &offset)) {
2736 debug_msg(RELEASE, "PIC is not valid");
2740 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2741 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2742 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2744 debug_fleave(RELEASE);
2749 static bool _mm_file_id3tag_parse_APIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2753 debug_fenter(RELEASE);
2755 if (!__id3tag_parse_APIC_mimetype(pInfo, pTagVal, nTagLen, &offset)) {
2756 debug_msg(RELEASE, "APIC is not valid");
2760 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2761 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2762 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2764 debug_fleave(RELEASE);
2769 static char * __id3tag_get_v110(const char *buf, ssize_t len, const char *locale)
2771 char *tmp_str = NULL;
2773 mm_file_retv_if_fails(buf, NULL);
2774 mm_file_retv_if_fails(locale, NULL);
2776 tmp_str = mmfile_convert_to_utf8(buf, len, locale);
2778 /* ID3v1 tag need check length or space. */
2779 if (tmp_str && (strlen(tmp_str) == 0 || g_ascii_isspace(tmp_str[0])))
2780 mmfile_free(tmp_str);
2785 bool mm_file_id3tag_parse_v110(AvFileContentInfo *pInfo, unsigned char *buffer)
2787 const char *locale = MMFileUtilGetLocale();
2789 debug_msg(RELEASE, "ID3tag v110--------------------------------------------------------------");
2791 if (!pInfo->tagInfo[AV_ID3TAG_TITLE].value) {
2792 pInfo->tagInfo[AV_ID3TAG_TITLE].value = __id3tag_get_v110((const char *)&buffer[3], MP3_ID3_TITLE_LENGTH, locale);
2794 debug_msg(RELEASE, "pInfo->pTitle returned =(%s)", pInfo->tagInfo[AV_ID3TAG_TITLE].value);
2797 if (!pInfo->tagInfo[AV_ID3TAG_ARTIST].value) {
2798 pInfo->tagInfo[AV_ID3TAG_ARTIST].value = __id3tag_get_v110((const char *)&buffer[33], MP3_ID3_ARTIST_LENGTH, locale);
2800 debug_msg(RELEASE, "pInfo->pArtist returned =(%s)", pInfo->tagInfo[AV_ID3TAG_ARTIST].value);
2803 if (!pInfo->tagInfo[AV_ID3TAG_ALBUM].value) {
2804 pInfo->tagInfo[AV_ID3TAG_ALBUM].value = __id3tag_get_v110((const char *)&buffer[63], MP3_ID3_ALBUM_LENGTH, locale);
2806 debug_msg(RELEASE, "pInfo->pAlbum returned =(%s)", pInfo->tagInfo[AV_ID3TAG_ALBUM].value);
2809 if (!pInfo->tagInfo[AV_ID3TAG_YEAR].value) {
2810 pInfo->tagInfo[AV_ID3TAG_YEAR].value = __id3tag_get_v110((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, locale);
2812 debug_msg(RELEASE, "pInfo->pYear returned =(%s)", pInfo->tagInfo[AV_ID3TAG_YEAR].value);
2815 if (!pInfo->tagInfo[AV_ID3TAG_COMMENT].value) {
2816 pInfo->tagInfo[AV_ID3TAG_COMMENT].value = __id3tag_get_v110((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, locale);
2818 debug_msg(RELEASE, "pInfo->pComment returned =(%s)", pInfo->tagInfo[AV_ID3TAG_COMMENT].value);
2821 if (!pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value) {
2822 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value = g_malloc0(ID3TAG_V110_TRACK_NUM_DIGIT);
2823 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value[ID3TAG_V110_TRACK_NUM_DIGIT - 1] = 0;
2824 snprintf(pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value, ID3TAG_V110_TRACK_NUM_DIGIT, "%04d", (int)buffer[126]);
2826 debug_msg(RELEASE, "pInfo->pTrackNum returned =(%s)", pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value);
2829 /*ID3V2 genre is stored in pInfo->tagInfo[AV_ID3TAG_GENRE].value */
2830 /*pInfo->genre is used when ID3V2 genre is invalid, or empty. */
2831 pInfo->genre = buffer[127];
2832 debug_msg(RELEASE, "pInfo->genre returned genre number (%d)", pInfo->genre);
2837 static AvID3TagList __get_tag_info_v222(const unsigned char *tag)
2843 n += tag[i++] - 'A' + 1;
2846 n += tag[2]; //num, char mixted
2848 for (i = 0; i < ID3TAG_NUM_V22; i++) {
2849 if (n == tag_info_v22[i].int_name) {
2850 return tag_info_v22[i].tag;
2854 debug_msg(RELEASE, "(%s) This Frame ID currently not Supports!!", tag);
2856 return AV_ID3TAG_MAX;
2859 static AvID3TagList __get_tag_info_v223(const unsigned char *tag)
2865 n += tag[i++] - 'A' + 1;
2868 n += tag[3]; //num, char mixted
2870 for (i = 0; i < ID3TAG_NUM_V23; i++) {
2871 if (n == tag_info_v23[i].int_name) {
2872 return tag_info_v23[i].tag;
2876 debug_msg(RELEASE, "(%s) This Frame ID currently not Supports!!", tag);
2878 return AV_ID3TAG_MAX;
2882 bool mm_file_id3tag_parse_v222(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
2884 int curPos = MP3_TAGv2_HEADER_LEN;
2888 char **charset_array = NULL;
2889 AvID3TagList tag_id = AV_ID3TAG_MAX;
2891 if (pInfo->tagV2Info.tagLen <= 0)
2894 make_characterset_array(&charset_array);
2895 init_content_info(pInfo);
2897 debug_msg(RELEASE, "ID3tag v222--------------------------------------------------------------");
2899 while (curPos + MP3_TAGv2_22_TXT_HEADER_LEN < pInfo->tagV2Info.tagLen) {
2900 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) || !g_ascii_isalnum(buffer[curPos + 2]))
2903 tag_id = __get_tag_info_v222(&buffer[curPos]);
2904 frameLen = buffer[curPos + 3] << 16 | buffer[curPos + 4] << 8 | buffer[curPos + 5];
2905 curPos += MP3_TAGv2_22_TXT_HEADER_LEN;
2906 if (curPos + frameLen > pInfo->tagV2Info.tagLen)
2912 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value)
2915 if (buffer[curPos] == 0x00) {
2917 encType = AV_ID3V2_ISO_8859;
2918 } else if (buffer[curPos] == 0x01) {
2920 encType = AV_ID3V2_UTF16;
2923 /*in order to deliver valid string to MP */
2924 while ((buffer[curPos + encOffset] < 0x20) && (encOffset < frameLen))
2927 if (frameLen == encOffset) {
2928 debug_warning(DEBUG, "warning: invalid frame length %d %d", frameLen, encOffset);
2932 curPos += encOffset;
2933 frameLen -= encOffset;
2936 case AV_ID3TAG_COMMENT:
2938 https://id3.org/id3v2-00
2940 Frame size $xx xx xx
2943 Short content description <textstring> $00 (00)
2944 The actual text <textstring>
2946 if (frameLen <= 4) {
2947 debug_msg(RELEASE, "Too small to parse frameLen(%d)", frameLen);
2951 if (buffer[curPos + 4] > 0x20 && (buffer[curPos + 3] == 0x00 || buffer[curPos + 3] == 0x01)) {
2952 encType = __id3tag_get_text_encoding_v222(&buffer[curPos], 4);
2953 /*skip language data! */
2956 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
2958 debug_msg(RELEASE, "Failed to get tag: frameLen(%d)", frameLen);
2963 case AV_ID3TAG_PICTURE:
2964 if (extract_artwork)
2965 _mm_file_id3tag_parse_PIC(pInfo, &buffer[curPos], frameLen, charset_array[encType]);
2969 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
2973 if (pInfo->tagInfo[tag_id].value)
2974 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
2981 release_characterset_array(charset_array);
2986 static void __get_v223_encoding_info(const unsigned char *buffer,
2993 if (!buffer || !offset || !type)
2996 if (IS_ENCODEDBY_UTF16(buffer)) {
2998 *type = AV_ID3V2_UTF16;
3001 if (IS_ENCODEDBY_UTF16_R(buffer)) {
3003 *type = AV_ID3V2_UTF16_BE;
3006 if (IS_ENCODEDBY_UTF16(buffer + 1)) {
3008 *type = AV_ID3V2_UTF16;
3011 if (IS_ENCODEDBY_UTF16_R(buffer + 1)) {
3013 *type = AV_ID3V2_UTF16_BE;
3017 if (buffer[0] == 0x00) {
3020 while ((buffer[_offset] < 0x20) && (_offset < length))
3024 *type = AV_ID3V2_ISO_8859;
3027 bool mm_file_id3tag_parse_v223(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
3029 int extendedHeaderLen = 0;
3031 int curPos = MP3_TAGv2_HEADER_LEN;
3033 unsigned int encType = 0;
3034 char **charset_array = NULL;
3035 AvID3TagList tag_id = AV_ID3TAG_MAX;
3036 char *lang_info = NULL;
3038 if (pInfo->tagV2Info.tagLen <= 0)
3041 make_characterset_array(&charset_array);
3042 init_content_info(pInfo);
3044 debug_msg(RELEASE, "ID3tag v223--------------------------------------------------------------");
3045 if (buffer[5] & 0x40) {
3046 /* if extended header exists, skip it*/
3047 extendedHeaderLen = buffer[10] << 21 | buffer[11] << 14 | buffer[12] << 7 | buffer[13];
3048 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d", extendedHeaderLen);
3049 if (extendedHeaderLen > pInfo->tagV2Info.tagLen - MP3_TAGv2_HEADER_LEN) {
3050 debug_error(DEBUG, "extended header too long.");
3052 curPos += extendedHeaderLen;
3057 while (curPos + MP3_TAGv2_23_TXT_HEADER_LEN < pInfo->tagV2Info.tagLen) {
3058 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) ||
3059 !g_ascii_isalnum(buffer[curPos + 2]) || !g_ascii_isalnum(buffer[curPos + 3]))
3062 tag_id = __get_tag_info_v223(&buffer[curPos]);
3063 frameLen = buffer[curPos + 4] << 24 | buffer[curPos + 5] << 16 | buffer[curPos + 6] << 8 | buffer[curPos + 7];
3064 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3065 if (curPos + frameLen > pInfo->tagV2Info.tagLen)
3071 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value)
3074 __get_v223_encoding_info(buffer + curPos, frameLen, &encOffset, &encType);
3075 if (frameLen <= encOffset) {
3076 debug_warning(DEBUG, "warning: invalid frame length %d %d", frameLen, encOffset);
3080 curPos += encOffset;
3081 frameLen -= encOffset;
3084 if (encType != AV_ID3V2_UTF16 && encType != AV_ID3V2_UTF16_BE) {
3085 if (tag_id != AV_ID3TAG_COMMENT && tag_id != AV_ID3TAG_UNSYNCLYRICS && tag_id != AV_ID3TAG_SYNCLYRICS) {
3086 debug_msg(RELEASE, "get the new text encoding type");
3087 encType = buffer[curPos - 1];
3091 if (encType > AV_ID3V2_MAX) {
3092 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%u], TAG ID[%d]", encType, tag_id);
3097 case AV_ID3TAG_COMMENT:
3098 if (frameLen <= 3) {
3099 debug_msg(RELEASE, "Description info too small to parse frameLen(%d)", frameLen);
3105 if (buffer[curPos] != 0x00 && buffer[curPos] != 0xFF && buffer[curPos] != 0xFE) {
3106 debug_msg(RELEASE, "failed to get Comment: frameLen(%d)", frameLen);
3109 encType = __id3tag_get_text_encoding_v223(&buffer[curPos], &frameLen, encType, &encOffset);
3110 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3111 curPos += encOffset;
3112 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3115 case AV_ID3TAG_SYNCLYRICS:
3116 if (frameLen <= 5) {
3117 debug_msg(RELEASE, "Synchronised lyrics too small to parse frameLen(%d)", frameLen);
3123 if (buffer[curPos] != 0x00 && buffer[curPos] != 0xFF && buffer[curPos] != 0xFE) {
3124 debug_msg(RELEASE, "failed to get Synchronised lyrics Info curPos(%d), frameLen(%d)", curPos, frameLen);
3126 encType = __id3tag_get_text_encoding_v223(&buffer[curPos], &frameLen, encType, &encOffset);
3127 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3128 curPos += encOffset;
3129 __id3tag_parse_SYLT(pInfo, buffer, frameLen, charset_array[encType], encType, curPos);
3132 case AV_ID3TAG_UNSYNCLYRICS:
3133 lang_info = strndup((const char *)&buffer[curPos], 3);
3135 if (frameLen <= 3) {
3136 debug_msg(RELEASE, "Unsynchronised lyrics too small to parse frameLen(%d)", frameLen);
3137 mmfile_free(lang_info);
3143 if (buffer[curPos] != 0x00 && buffer[curPos] != 0xFF && buffer[curPos] != 0xFE) {
3144 debug_msg(RELEASE, "failed to get Unsynchronised lyrics Info curPos(%d), frameLen(%d)", curPos, frameLen);
3146 encType = __id3tag_get_text_encoding_v223(&buffer[curPos], &frameLen, encType, &encOffset);
3148 char *char_set = NULL;
3150 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3152 if (encType == AV_ID3V2_ISO_8859) {
3153 if (lang_info != NULL && !g_ascii_strcasecmp(lang_info, "KOR")) {
3154 char_set = strdup("EUC-KR");
3156 char_set = mmfile_get_charset((const char *)&buffer[curPos]);
3158 mmfile_free(lang_info);
3161 curPos += encOffset;
3164 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3166 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, char_set);
3167 mmfile_free(char_set);
3169 mmfile_free(lang_info);
3172 case AV_ID3TAG_PICTURE:
3173 if (extract_artwork)
3174 _mm_file_id3tag_parse_APIC(pInfo, &buffer[curPos], frameLen, charset_array[encType]);
3178 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3182 if (pInfo->tagInfo[tag_id].value)
3183 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
3190 release_characterset_array(charset_array);
3195 static void __get_v224_encoding_info(const unsigned char *buffer,
3202 if (!buffer || !offset || !type)
3205 /*in case of UTF 16 encoding */
3206 /*buffer+(position-length) data should '0x01' but in order to expansion, we don't accurately check the value. */
3207 if (IS_ENCODEDBY_UTF16(buffer)) {
3209 *type = AV_ID3V2_UTF16;
3213 if (IS_ENCODEDBY_UTF16_R(buffer)) {
3215 *type = AV_ID3V2_UTF16_BE;
3219 if (IS_ENCODEDBY_UTF16(buffer + 1)) {
3221 *type = AV_ID3V2_UTF16;
3224 if (IS_ENCODEDBY_UTF16_R(buffer + 1)) {
3226 *type = AV_ID3V2_UTF16_BE;
3230 /*in case of UTF-16 BE encoding */
3231 if (buffer[0] == 0x02) {
3233 while ((buffer[_offset] == '\0') && (_offset < length))
3234 _offset++;/*null skip! */
3236 *type = AV_ID3V2_UTF16_BE;
3240 /*in case of UTF8 encoding */
3241 if (buffer[0] == 0x03) {
3243 while ((buffer[_offset] == '\0') && (_offset < length))
3244 _offset++;/*null skip! */
3246 *type = AV_ID3V2_UTF8;
3249 /*in case of ISO-8859-1 encoding */
3250 /*buffer+(position-length) data should 0x00 but in order to expansion, we don't accurately check the value. */
3252 while ((buffer[_offset] < 0x20) && (_offset < length))
3253 _offset++;/*less than 0x20 value skip! */
3255 *type = AV_ID3V2_ISO_8859;
3258 bool mm_file_id3tag_parse_v224(AvFileContentInfo *pInfo, unsigned char *buffer, bool extract_artwork)
3260 int extendedHeaderLen = 0;
3262 int curPos = MP3_TAGv2_HEADER_LEN;
3264 unsigned int encType = 0;
3265 char **charset_array = NULL;
3266 AvID3TagList tag_id = AV_ID3TAG_MAX;
3268 if (pInfo->tagV2Info.tagLen <= 0)
3271 make_characterset_array(&charset_array);
3272 init_content_info(pInfo);
3274 debug_msg(RELEASE, "ID3tag v224--------------------------------------------------------------");
3275 if (buffer[5] & 0x40) {
3276 /* if extended header exists, skip it*/
3277 extendedHeaderLen = buffer[10] << 21 | buffer[11] << 14 | buffer[12] << 7 | buffer[13];
3278 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d", extendedHeaderLen);
3279 if (extendedHeaderLen > pInfo->tagV2Info.tagLen - MP3_TAGv2_HEADER_LEN) {
3280 debug_error(DEBUG, "extended header too long.");
3282 curPos += extendedHeaderLen;
3286 while (curPos + MP3_TAGv2_23_TXT_HEADER_LEN < pInfo->tagV2Info.tagLen) {
3287 if (!g_ascii_isalnum(buffer[curPos]) || !g_ascii_isalnum(buffer[curPos + 1]) ||
3288 !g_ascii_isalnum(buffer[curPos + 2]) || !g_ascii_isalnum(buffer[curPos + 3]))
3291 tag_id = __get_tag_info_v223(&buffer[curPos]);
3292 frameLen = buffer[curPos + 4] << 21 | buffer[curPos + 5] << 14 | buffer[curPos + 6] << 7 | buffer[curPos + 7];
3293 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3294 if (curPos + frameLen > pInfo->tagV2Info.tagLen)
3300 if (tag_id == AV_ID3TAG_MAX || pInfo->tagInfo[tag_id].value)
3303 __get_v224_encoding_info(buffer + curPos, frameLen, &encOffset, &encType);
3304 if (frameLen <= encOffset) {
3305 debug_warning(DEBUG, "warning: invalid frame length %d %d", frameLen, encOffset);
3309 curPos += encOffset;
3310 frameLen -= encOffset;
3313 if (encType != AV_ID3V2_UTF16 && encType != AV_ID3V2_UTF16_BE) {
3314 if (tag_id != AV_ID3TAG_COMMENT && tag_id != AV_ID3TAG_UNSYNCLYRICS && tag_id != AV_ID3TAG_SYNCLYRICS) {
3315 debug_msg(RELEASE, "get the new text encoding type");
3316 encType = buffer[curPos - 1];
3320 if (encType > AV_ID3V2_MAX) {
3321 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%u], TAG ID[%d]", encType, tag_id);
3326 case AV_ID3TAG_COMMENT:
3328 case AV_ID3TAG_UNSYNCLYRICS:
3329 if (frameLen <= 3) {
3330 debug_msg(RELEASE, "Description info too small to parse frameLen(%d)", frameLen);
3336 encType = __id3tag_get_text_encoding_v224(&buffer[curPos], &frameLen, encType, &encOffset);
3337 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3338 curPos += encOffset;
3339 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3342 case AV_ID3TAG_SYNCLYRICS:
3343 if (frameLen <= 5) {
3344 debug_msg(RELEASE, "Synchronised lyrics too small to parse frameLen(%d)", frameLen);
3350 encType = __id3tag_get_text_encoding_v224(&buffer[curPos], &frameLen, encType, &encOffset);
3351 debug_msg(RELEASE, "encOffset(%d) encType(%u), frameLen(%d)", encOffset, encType, frameLen);
3352 curPos += encOffset;
3353 __id3tag_parse_SYLT(pInfo, buffer, frameLen, charset_array[encType], encType, curPos);
3356 case AV_ID3TAG_PICTURE:
3357 if (extract_artwork)
3358 _mm_file_id3tag_parse_APIC(pInfo, &buffer[curPos], frameLen, charset_array[encType]);
3362 pInfo->tagInfo[tag_id].value = mmfile_convert_to_utf8((const char *)&buffer[curPos], frameLen, charset_array[encType]);
3366 if (pInfo->tagInfo[tag_id].value)
3367 debug_msg(RELEASE, "[%d] returned = (%s)", tag_id, pInfo->tagInfo[tag_id].value);
3374 release_characterset_array(charset_array);
3380 void mm_file_id3tag_restore_content_info(AvFileContentInfo *pInfo)
3384 /* for Genre Info */
3385 if (pInfo->tagInfo[AV_ID3TAG_GENRE].value) {
3387 if (!__get_genre_num(pInfo->tagInfo[AV_ID3TAG_GENRE].value, &genre_id)) {
3388 debug_log(RELEASE, "genre information is not integer [%s]", pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3392 /* If integer, check genre code. */
3393 /* If out of range, set UNKNOWN */
3394 if (genre_id < 0 || genre_id >= GENRE_COUNT)
3395 genre_id = GENRE_COUNT - 1;
3397 debug_msg(RELEASE, "genre information is integer [%d]", genre_id);
3399 g_free(pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3400 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(MpegAudio_Genre[genre_id]);
3402 /* No genre in ID3V2.. So check V1 */
3403 if (pInfo->bV1tagFound == true) {
3404 debug_msg(RELEASE, "Genre: %d", pInfo->genre);
3406 /* If out of range, set UNKNOWN */
3407 if (pInfo->genre > GENRE_COUNT - 1)
3408 pInfo->genre = GENRE_COUNT - 1;
3410 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(MpegAudio_Genre[pInfo->genre]);
3412 debug_msg(RELEASE, "Genre was not Found.");
3417 static void __free_synclyrics(gpointer data)
3419 AvSynclyricsInfo *info = (AvSynclyricsInfo *) data;
3424 mmfile_free(info->lyric_info);
3429 void mm_file_free_synclyrics_list(GList *synclyrics_list)
3431 if (!synclyrics_list)
3434 g_list_free_full(synclyrics_list, __free_synclyrics);