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.
28 #include <iniparser.h>
32 #include "mm_file_debug.h"
33 #include "mm_file_utils.h"
35 #define ENABLE_ITUNES_META /*All itunes metadata extracted by ffmpeg. see mov_read_udta_string() but Some cover art not support. */
36 #define INVALID_UINT_VALUE 0xFFFFFFFF
37 #define INVALID_UINT8_VALUE 0xFF
39 #define ID3TAG_V110_TRACK_NUM_DIGIT 5
41 typedef struct _mmfilemp4basicboxheader {
44 long long start_offset; /*huge file*/
45 } MMFILE_MP4_BASIC_BOX_HEADER;
47 typedef struct _mmfilemp4ftpybox {
48 unsigned int major_brand;
49 unsigned short major_version;
50 unsigned short minor_version;
51 unsigned int *compatiable_brands;
55 eMMFILE_3GP_TAG_TITLE = 0x01,
56 eMMFILE_3GP_TAG_CAPTION = 0x02,
57 eMMFILE_3GP_TAG_COPYRIGHT = 0x03,
58 eMMFILE_3GP_TAG_PERFORMER = 0x04,
59 eMMFILE_3GP_TAG_AUTHOR = 0x05,
60 eMMFILE_3GP_TAG_GENRE = 0x06,
61 } eMMFILE_3GP_TEXT_TAG;
63 typedef struct _mmfile3gptagtextbox {
64 unsigned char version;
65 unsigned char flag[3];
66 unsigned char language[2];
68 } MMFILE_3GP_TEXT_TAGBOX;
70 typedef struct _mmfile3gptagyearbox {
71 unsigned char version;
72 unsigned char flag[3];
74 } MMFILE_3GP_YEAR_TAGBOX;
76 typedef struct _mmfile3gptagalbumbox {
77 unsigned char version;
78 unsigned char flag[3];
79 unsigned char language[2];
80 unsigned char *albumtile;
81 unsigned char trackNumber;
82 } MMFILE_3GP_ALBUM_TAGBOX;
84 typedef struct _mmfile3gpratingbox {
85 unsigned char version;
86 unsigned char flag[3];
87 unsigned int ratingEntity;
88 unsigned int ratingCriteria;
89 unsigned char language[2];
90 unsigned char *ratingInfo;
91 } MMFILE_3GP_RATING_TAGBOX;
93 typedef struct _mmfile3gpclsfbox {
94 unsigned char version;
95 unsigned char flag[3];
96 unsigned int classificationEntity;
97 unsigned int classificationTable;
98 unsigned char language[2];
99 unsigned char *classificationInfo;
100 } MMFILE_3GP_CLASSIFICATION_TAGBOX;
102 typedef struct _mmfile3gphdlrbox {
103 unsigned int version;
104 unsigned int pre_defined;
105 unsigned int handler_type;
106 unsigned int reserved[3];
107 unsigned char *boxname;
108 } MMFILE_3GP_HANDLER_BOX;
110 typedef struct _mmfileidv2box {
111 unsigned char version;
112 unsigned char flag[3];
113 unsigned char language[2];
114 unsigned char *id3v2Data;
115 } MMFILE_3GP_ID3V2_BOX;
117 typedef struct _mmfilelocibox {
118 unsigned char version;
119 unsigned char flag[3];
120 unsigned char language[2];
126 unsigned char *astronomical_body;
127 unsigned char *additional_notes;
128 } MMFILE_3GP_LOCATION_TAGBOX;
130 typedef struct _mmfilesmtabox {
133 unsigned char saut[4];
135 } MMFILE_M4A_SMTA_TAGBOX;
137 typedef struct _mmfilesa3dbox {
139 uint8_t ambisonic_type;
140 uint32_t ambisonic_order;
141 uint8_t ambisonic_channel_ordering;
142 uint8_t ambisonic_normalization;
143 uint32_t num_channels;
144 uint32_t channel_map[49]; /* Up to 6th order */
145 } __attribute__((aligned(1), packed)) MMFILE_MP4A_SA3D_TAGBOX;
147 typedef struct _mmfile_proj_v2_box {
148 uint16_t proj_box_id;
149 uint8_t proj_box_size;
150 uint16_t proj_type_box_id;
151 uint8_t proj_type_box_size;
152 uint8_t proj_type_box_value; /* only equi (=1) or cubemap (=2) currently */
153 uint16_t proj_priv_box_id;
154 uint8_t proj_priv_box_size;
155 } __attribute__((aligned(1), packed)) MMFILE_WEBM_PROJ_V2_BOX;
157 typedef struct _mmfile_equi_proj_v2_box {
158 unsigned char proj_priv_box_name[4]; /* "equi" */
159 uint32_t equi_projection_bounds_top;
160 uint32_t equi_projection_bounds_bottom;
161 uint32_t equi_projection_bounds_left;
162 uint32_t equi_projection_bounds_right;
163 } __attribute__((aligned(1), packed)) MMFILE_WEBM_EQUI_PROJ_V2_BOX;
165 typedef struct _mmfile_cbmp_proj_v2_box {
166 unsigned char proj_priv_box_name[4]; /* "cbmp" */
167 uint32_t cbmp_projection_layout;
168 uint32_t cbmp_projection_padding;
169 } __attribute__((aligned(1), packed)) MMFILE_WEBM_CBMP_PROJ_V2_BOX;
171 typedef struct _mmfile_pose_element_v2_box {
172 uint16_t pose_yaw_element_id;
173 uint8_t pose_yaw_element_size;
174 float pose_yaw_element_value;
175 uint16_t pose_pitch_element_id;
176 uint8_t pose_pitch_element_size;
177 float pose_pitch_element_value;
178 uint16_t pose_roll_element_id;
179 uint8_t pose_roll_element_size;
180 float pose_roll_element_value;
181 } __attribute__((aligned(1), packed)) MMFILE_WEBM_POSE_ELEMENT_V2_BOX;
185 PROJECTION_TYPE_RECT = 0, /* Rectangular, Google's RFC value */
186 PROJECTION_TYPE_EQUI = 1, /* Equirectangular, Google's RFC value */
187 PROJECTION_TYPE_CBMP = 2, /* Cubemap, Google's RFC value */
188 PROJECTION_TYPE_MESH = 3, /* Mesh, Google's RFC value, unsupported */
189 PROJECTION_TYPE_UNKNOWN = INVALID_UINT_VALUE,
190 } SPHERICAL_VIDEO2_PROJECTION_TYPE;
192 enum spherical_video_metadata_elements_ids_le {
193 PROJ_BOX_ID = 0x7076,
194 PROJ_TYPE_BOX_ID = 0x7176,
195 PROJ_PRIV_BOX_ID = 0x7276,
196 POSE_YAW_ELEMENT_ID = 0x7376,
197 POSE_PITCH_ELEMENT_ID = 0x7476,
198 POSE_ROLL_ELEMENT_ID = 0x7576
201 #define MMFILE_MP4_BASIC_BOX_HEADER_LEN 8
202 #define MMFILE_MP4_MOVIE_HEADER_BOX_LEN 96
203 #define MMFILE_MP4_HDLR_BOX_LEN 24
204 #define MMFILE_MP4_STSZ_BOX_LEN 20
205 #define MMFILE_MP4_MP4VBOX_LEN 80
206 #define MMFILE_MP4_MP4ABOX_LEN 28
207 #define MMFILE_3GP_TEXT_TAGBOX_LEN 6
208 #define MMFILE_3GP_YEAR_TAGBOX_LEN 6
209 #define MMFILE_3GP_ALBUM_TAGBOX_LEN 6
210 #define MMFILE_3GP_RATING_TAGBOX_LEN 14
211 #define MMFILE_3GP_CLASS_TAGBOX_LEN 14
212 #define MMFILE_3GP_HANDLER_BOX_LEN 24
213 #define MMFILE_3GP_ID3V2_BOX_LEN 6
214 #define MMFILE_SYNC_LYRIC_INFO_MIN_LEN 5
217 #define FOURCC(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
219 /*#define MIN(a, b) (((a) < (b)) ? (a):(b))*/
221 #define GENRE_COUNT 149
223 static const char * const MpegAudio_Genre[GENRE_COUNT] = {"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal",
224 "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial",
225 "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
226 "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise",
227 "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic",
228 "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
229 "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes",
230 "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock",
231 "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass",
232 "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic",
233 "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove",
234 "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
235 "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore",
236 "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian", "Heavy Metal", "Black Metal", "Crossover",
237 "Contemporary", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", "Synthpop", "Unknown"
240 static unsigned char gTagJPEGHeader[] = {0xff, 0xd8, 0xff };
241 static unsigned char gTagPNGHeader[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
243 typedef struct _mmfileid3taginfo {
245 unsigned int int_name;
247 } MMFILE_ID3TAG_INFO;
249 #define ID3TAG_NUM_V22 16
250 #define ID3TAG_NUM_V23 21
252 MMFILE_ID3TAG_INFO tag_info_v22[ID3TAG_NUM_V22] = {
253 {"TT2", 21170, AV_ID3TAG_TITLE},
254 {"TP1", 21041, AV_ID3TAG_ARTIST},
255 {"TP2", 21042, AV_ID3TAG_ALBUM_ARTIST},
256 {"TP3", 21043, AV_ID3TAG_CONDUCTOR},
257 {"TAL", 20588, AV_ID3TAG_ALBUM},
258 {"TYE", 21349, AV_ID3TAG_YEAR},
259 {"COM", 3629, AV_ID3TAG_COMMENT},
260 {"TCO", 20655, AV_ID3TAG_GENRE},
261 {"TRK", 21131, AV_ID3TAG_TRACKNUM},
262 {"PIC", 16739, AV_ID3TAG_PICTURE},
263 {"TEN", 20718, AV_ID3TAG_ENCBY},
264 {"WXX", 24408, AV_ID3TAG_URL},
265 {"TCR", 20658, AV_ID3TAG_COPYRIGHT},
266 {"TOA", 21025, AV_ID3TAG_ORIGIN_ARTIST},
267 {"TCM", 20653, AV_ID3TAG_COMPOSER},
268 {"TRD", 21124, AV_ID3TAG_RECDATE},
271 MMFILE_ID3TAG_INFO tag_info_v23[ID3TAG_NUM_V23] = {
272 {"TIT2", 665266, AV_ID3TAG_TITLE},
273 {"TPE1", 671953, AV_ID3TAG_ARTIST},
274 {"TPE2", 671954, AV_ID3TAG_ALBUM_ARTIST},
275 {"TPE3", 671955, AV_ID3TAG_CONDUCTOR},
276 {"TALB", 656834, AV_ID3TAG_ALBUM},
277 {"TYER", 681202, AV_ID3TAG_YEAR},
278 {"COMM", 114157, AV_ID3TAG_COMMENT},
279 {"TCON", 658990, AV_ID3TAG_GENRE},
280 {"TRCK", 673963, AV_ID3TAG_TRACKNUM},
281 {"APIC", 49507, AV_ID3TAG_PICTURE},
282 {"TENC", 660995, AV_ID3TAG_ENCBY},
283 {"WXXX", 779096, AV_ID3TAG_URL},
284 {"TCOP", 658992, AV_ID3TAG_COPYRIGHT},
285 {"TOPE", 671301, AV_ID3TAG_ORIGIN_ARTIST},
286 {"TCOM", 658989, AV_ID3TAG_COMPOSER},
287 {"TRDA", 660097, AV_ID3TAG_RECDATE}, /*Recdate for 2.3*/
288 {"USLT", 708052, AV_ID3TAG_UNSYNCLYRICS},
289 {"SYLT", 648660, AV_ID3TAG_SYNCLYRICS},
290 {"TPOS", 672307, AV_ID3TAG_PART_OF_SET},
291 {"TIT1", 665265, AV_ID3TAG_CONTENT_GROUP}, /*Content Group for 2.4*/
292 {"TDRC", 660099, AV_ID3TAG_RECDATE}, /*Recdate for 2.4*/
295 static int GetJunkCounterLimit(void);
297 static int GetStringFromTextTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header, eMMFILE_3GP_TEXT_TAG eTag)
299 int ret = MMFILE_UTIL_FAIL; /*fail*/
300 MMFILE_3GP_TEXT_TAGBOX texttag = {0, };
303 char *temp_text = NULL;
305 if (!formatContext || !fp || !basic_header) {
306 debug_error(DEBUG, "invalid param\n");
307 return MMFILE_UTIL_FAIL;
310 textBytes = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_TEXT_TAGBOX_LEN;
312 readed = mmfile_read(fp, (unsigned char *)&texttag, MMFILE_3GP_TEXT_TAGBOX_LEN);
313 if (readed != MMFILE_3GP_TEXT_TAGBOX_LEN) {
314 debug_error(DEBUG, "read text tag header fail\n");
315 ret = MMFILE_UTIL_FAIL;
319 if (textBytes <= 1) { /* there exist only 00(null) */
320 debug_error(DEBUG, "Text is NULL\n");
324 texttag.text = g_malloc0(textBytes);
326 readed = mmfile_read(fp, (unsigned char *)texttag.text, textBytes);
327 if (readed != textBytes) {
328 debug_error(DEBUG, "read text fail\n");
329 ret = MMFILE_UTIL_FAIL;
334 if ((texttag.text[0] == 0xFE) && (texttag.text[1] == 0xFF)) {
335 /* this char is UTF-16 */
336 unsigned int bytes_written = 0;
337 temp_text = mmfile_string_convert((const char *)&texttag.text[2], readed - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
339 temp_text = g_strdup((const char *)texttag.text);
343 case eMMFILE_3GP_TAG_TITLE: {
344 if (!formatContext->title) {
345 formatContext->title = g_strdup(temp_text);
349 case eMMFILE_3GP_TAG_CAPTION: {
350 if (!formatContext->description) {
351 formatContext->description = g_strdup(temp_text);
355 case eMMFILE_3GP_TAG_COPYRIGHT: {
356 if (!formatContext->copyright) {
357 formatContext->copyright = g_strdup(temp_text);
361 case eMMFILE_3GP_TAG_PERFORMER: {
362 if (!formatContext->artist) {
363 formatContext->artist = g_strdup(temp_text);
367 case eMMFILE_3GP_TAG_AUTHOR: {
368 if (!formatContext->author) {
369 formatContext->author = g_strdup(temp_text);
373 case eMMFILE_3GP_TAG_GENRE: {
374 if (!formatContext->genre) {
375 formatContext->genre = g_strdup(temp_text);
380 debug_warning(DEBUG, "Not supported Text Tag type[%d]\n", eTag);
385 mmfile_free(texttag.text);
387 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
389 mmfile_free(temp_text);
391 return MMFILE_UTIL_SUCCESS;
394 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
395 mmfile_free(texttag.text);
400 static int GetYearFromYearTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
402 #define MAX_YEAR_BUFFER 10
404 MMFILE_3GP_YEAR_TAGBOX yearbox = {0, };
405 char temp_year[MAX_YEAR_BUFFER] = {0, };
407 if (!formatContext || !fp || !basic_header) {
408 debug_error(DEBUG, "invalid param\n");
409 return MMFILE_UTIL_FAIL;
412 readed = mmfile_read(fp, (unsigned char *)&yearbox, MMFILE_3GP_YEAR_TAGBOX_LEN);
413 if (readed != MMFILE_3GP_YEAR_TAGBOX_LEN) {
414 debug_error(DEBUG, "read yeartag header fail\n");
418 if (!formatContext->year) {
419 yearbox.year = mmfile_io_be_int16(yearbox.year);
420 snprintf(temp_year, MAX_YEAR_BUFFER, "%d", yearbox.year);
421 temp_year[MAX_YEAR_BUFFER - 1] = '\0';
422 formatContext->year = g_strdup((const char *)temp_year);
425 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
426 return MMFILE_UTIL_SUCCESS;
429 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
431 return MMFILE_UTIL_FAIL;
434 static int GetAlbumFromAlbumTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
436 int albumTitleLen = 0;
437 char *temp_text = NULL;
440 MMFILE_3GP_ALBUM_TAGBOX albumbox = {0, };
442 if (!formatContext || !fp || !basic_header) {
443 debug_error(DEBUG, "invalid param\n");
444 return MMFILE_UTIL_FAIL;
447 readed = mmfile_read(fp, (unsigned char *)&albumbox, MMFILE_3GP_ALBUM_TAGBOX_LEN);
448 if (readed != MMFILE_3GP_ALBUM_TAGBOX_LEN) {
449 debug_error(DEBUG, "read albumtag header fail\n");
453 albumTitleLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_ALBUM_TAGBOX_LEN - 1; /* 1: track number */
454 if (albumTitleLen > 1) { /* there exist only 00(null) */
455 debug_msg(RELEASE, "albumTitleLen=%d\n", albumTitleLen);
457 albumbox.albumtile = g_malloc0(albumTitleLen + 1); /* 1: for null char */
459 readed = mmfile_read(fp, (unsigned char *)albumbox.albumtile, albumTitleLen);
460 if (readed != albumTitleLen) {
461 debug_error(DEBUG, "read album title fail\n");
465 if (albumbox.albumtile[albumTitleLen - 1] == '\0') { /* there exist track number */
469 readed = mmfile_read(fp, (unsigned char *)&(albumbox.albumtile[albumTitleLen]), 1);
471 debug_error(DEBUG, "read album title fail\n");
474 albumbox.albumtile[albumTitleLen] = '\0';
478 if ((albumbox.albumtile[0] == 0xFE) && (albumbox.albumtile[1] == 0xFF)) {
479 /* this char is UTF-16 */
480 unsigned int bytes_written = 0;
481 temp_text = mmfile_string_convert((const char *)&albumbox.albumtile[2], readed - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
483 temp_text = g_strdup((const char *)albumbox.albumtile);
486 if (!formatContext->album)
487 formatContext->album = temp_text;
489 mmfile_free(temp_text);
491 debug_msg(RELEASE, "formatContext->album=%s, strlen=%zu\n", formatContext->album, strlen(formatContext->album));
495 readed = mmfile_read(fp, (unsigned char *)&albumbox.trackNumber, 1);
497 debug_error(DEBUG, "read track number fail\n");
501 if (formatContext->tagTrackNum == 0) {
502 char tracknum[10] = {0, };
503 snprintf(tracknum, 10, "%d", albumbox.trackNumber);
505 formatContext->tagTrackNum = g_strdup((const char *)tracknum);
509 mmfile_free(albumbox.albumtile);
510 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
512 return MMFILE_UTIL_SUCCESS;
515 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
516 mmfile_free(albumbox.albumtile);
518 return MMFILE_UTIL_FAIL;
521 static int GetRatingFromRatingTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
524 int ratinginfoLen = 0;
525 char *temp_text = NULL;
527 MMFILE_3GP_RATING_TAGBOX ratingTag = {0, };
529 if (!formatContext || !fp || !basic_header) {
530 debug_error(DEBUG, "invalid param\n");
531 return MMFILE_UTIL_FAIL;
534 readed = mmfile_read(fp, (unsigned char *)&ratingTag, MMFILE_3GP_RATING_TAGBOX_LEN);
535 if (readed != MMFILE_3GP_RATING_TAGBOX_LEN) {
536 debug_error(DEBUG, "read rating tag header fail\n");
540 ratinginfoLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_RATING_TAGBOX_LEN;
542 if (ratinginfoLen == 1) {
543 debug_error(DEBUG, "Rating Text is NULL\n");
547 ratingTag.ratingInfo = g_malloc0(ratinginfoLen);
549 readed = mmfile_read(fp, (unsigned char *)ratingTag.ratingInfo, ratinginfoLen);
550 if (readed != ratinginfoLen) {
551 debug_error(DEBUG, "read rating info string fail\n");
556 if ((ratingTag.ratingInfo[0] == 0xFE) && (ratingTag.ratingInfo[1] == 0xFF)) {
557 /* this char is UTF-16 */
558 unsigned int bytes_written = 0;
559 temp_text = mmfile_string_convert((const char *)&ratingTag.ratingInfo[2], readed - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
561 temp_text = g_strdup((const char *)ratingTag.ratingInfo);
564 if (!formatContext->rating) {
565 formatContext->rating = temp_text;
567 mmfile_free(temp_text);
570 mmfile_free(ratingTag.ratingInfo);
571 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
573 return MMFILE_UTIL_SUCCESS;
576 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
577 mmfile_free(ratingTag.ratingInfo);
579 return MMFILE_UTIL_FAIL;
583 static int GetClassficationFromClsfTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
585 int classinfoLen = 0;
587 char *temp_text = NULL;
588 MMFILE_3GP_CLASSIFICATION_TAGBOX classTag = {0, };
590 if (!formatContext || !fp || !basic_header) {
591 debug_error(DEBUG, "invalid param\n");
592 return MMFILE_UTIL_FAIL;
595 readed = mmfile_read(fp, (unsigned char *)&classTag, MMFILE_3GP_CLASS_TAGBOX_LEN);
596 if (readed != MMFILE_3GP_CLASS_TAGBOX_LEN) {
597 debug_error(DEBUG, "read classification tag header fail\n");
602 classinfoLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_CLASS_TAGBOX_LEN;
603 if (classinfoLen == 1) {
604 debug_error(DEBUG, "Classification Text is NULL\n");
608 classTag.classificationInfo = g_malloc0(classinfoLen);
610 readed = mmfile_read(fp, (unsigned char *)classTag.classificationInfo, classinfoLen);
611 if (readed != classinfoLen) {
612 debug_error(DEBUG, "read class info string fail\n");
617 if ((classTag.classificationInfo[0] == 0xFE) && (classTag.classificationInfo[1] == 0xFF)) {
618 /* this char is UTF-16 */
619 unsigned int bytes_written = 0;
620 temp_text = mmfile_string_convert((const char *)&classTag.classificationInfo[2], readed - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
622 temp_text = g_strdup((const char *)classTag.classificationInfo);
625 if (!formatContext->classification) {
626 formatContext->classification = temp_text;
628 mmfile_free(temp_text);
631 mmfile_free(classTag.classificationInfo);
632 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
634 return MMFILE_UTIL_SUCCESS;
637 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
638 mmfile_free(classTag.classificationInfo);
640 return MMFILE_UTIL_FAIL;
644 * The Location Information box
645 * --------------------+-------------------+-----------------------------------+------
646 * Field Type Details Value
647 * --------------------+-------------------+-----------------------------------+------
648 * BoxHeader.Size Unsigned int(32)
649 * BoxHeader.Type Unsigned int(32) 'loci'
650 * BoxHeader.Version Unsigned int(8) 0
651 * BoxHeader.Flags Bit(24) 0
653 * Language Unsigned int(5)[3] Packed ISO-639-2/T language code
654 * Name String Text of place name
655 * Role Unsigned int(8) Non-negative value indicating role
657 * Longitude Unsigned int(32) Fixed-point value of the longitude
658 * Latitude Unsigned int(32) Fixed-point value of the latitude
659 * Altitude Unsigned int(32) Fixed-point value of the Altitude
660 * Astronomical_body String Text of astronomical body
661 * Additional_notes String Text of additional location-related
663 * --------------------+-------------------+-----------------------------------+------
665 static int _get_char_position(unsigned char *src, char ch, int max)
668 for (i = 0; i < max; i++) {
669 if (*(src + i) == ch)
676 static int GetLocationFromLociTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
679 MMFILE_3GP_LOCATION_TAGBOX lociTag = {0, };
682 unsigned char *buffer = NULL;
683 unsigned char *p = NULL;
685 unsigned int bytes_written = 0;
686 unsigned int name_sz = 0;
687 unsigned int astro_sz = 0;
689 int ilong, ilati, ialti;
690 float flong, flati, falti;
693 if (!formatContext || !fp || !basic_header) {
694 debug_error(DEBUG, "invalid param\n");
695 return MMFILE_UTIL_FAIL;
698 readed = mmfile_read(fp, (unsigned char *)&lociTag, 6); /*6 = version + flag + pad + language */
700 debug_error(DEBUG, "read location tag header fail\n");
704 /*buffer len = name + role + ... + additional notes length */
705 bufferLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - 6;
707 debug_error(DEBUG, "too small buffer\n");
711 buffer = g_malloc0(bufferLen);
713 readed = mmfile_read(fp, (unsigned char *)buffer, bufferLen);
714 if (readed != bufferLen) {
715 debug_error(DEBUG, "read location tag fail\n");
721 pos = _get_char_position(p, '\0', readed - (1 + 4 + 4 + 4 + 2));
723 if (p[0] == 0xFE && p[1] == 0xFF) {
724 lociTag.name = (unsigned char *)mmfile_string_convert((const char *)(p + 2), pos - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
726 lociTag.name = (unsigned char *)g_strdup((const char *)p);
738 debug_msg(RELEASE, "long: 0x%02X 0x%02X 0x%02X 0x%02X \n", *(p + 0), *(p + 1), *(p + 2), *(p + 3));
739 debug_msg(RELEASE, "lati: 0x%02X 0x%02X 0x%02X 0x%02X \n", *(p + 4), *(p + 5), *(p + 6), *(p + 7));
740 debug_msg(RELEASE, "alti: 0x%02X 0x%02X 0x%02X 0x%02X \n", *(p + 8), *(p + 9), *(p + 10), *(p + 11));
742 ilong = mmfile_io_be_uint32(*(unsigned int *)p);
743 ilati = mmfile_io_be_uint32(*(unsigned int *)(p + 4));
744 ialti = mmfile_io_be_uint32(*(unsigned int *)(p + 8));
746 flong = (float)ilong / (1 << 16);
747 flati = (float)ilati / (1 << 16);
748 falti = (float)ialti / (1 << 16);
751 lociTag.longitude = flong;
753 lociTag.latitude = flati;
755 lociTag.altitude = falti;
759 /*astronomical body*/
760 pos = _get_char_position(p, '\0', readed - (name_sz + 1 + 4 + 4 + 4 + 1));
762 if (p[0] == 0xFE && p[1] == 0xFF) {
763 lociTag.astronomical_body = (unsigned char *)mmfile_string_convert((const char *)(p + 2), pos - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
765 lociTag.astronomical_body = (unsigned char *)g_strdup((const char *)p);
774 pos = _get_char_position(p, '\0', readed - (name_sz + 1 + 4 + 4 + 4 + astro_sz));
776 if (p[0] == 0xFE && p[1] == 0xFF) {
777 lociTag.additional_notes = (unsigned char *)mmfile_string_convert((const char *)(p + 2), pos - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
779 lociTag.additional_notes = (unsigned char *)g_strdup((const char *)p);
785 debug_msg(RELEASE, "** Location Information **\n");
786 debug_msg(RELEASE, "Name : %s\n", lociTag.name);
787 debug_msg(RELEASE, "Role : %d (0: shooting, 1: real, 2: fictional, other: reserved)\n", lociTag.role);
788 debug_msg(RELEASE, "Longitude : %16.16f\n", lociTag.longitude);
789 debug_msg(RELEASE, "Latitude : %16.16f\n", lociTag.latitude);
790 debug_msg(RELEASE, "Altitude : %16.16f\n", lociTag.altitude);
791 debug_msg(RELEASE, "Astronomical body: %s\n", lociTag.astronomical_body);
792 debug_msg(RELEASE, "Additional notes : %s\n", lociTag.additional_notes);
794 formatContext->longitude = lociTag.longitude;
795 formatContext->latitude = lociTag.latitude;
796 formatContext->altitude = lociTag.altitude;
799 mmfile_free(lociTag.name);
800 mmfile_free(lociTag.astronomical_body);
801 mmfile_free(lociTag.additional_notes);
803 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
804 return MMFILE_UTIL_SUCCESS;
807 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
809 mmfile_free(lociTag.name);
810 mmfile_free(lociTag.astronomical_body);
811 mmfile_free(lociTag.additional_notes);
813 return MMFILE_UTIL_FAIL;
816 static int GetSAUTInfoFromSMTATagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
818 MMFILE_M4A_SMTA_TAGBOX smtaTag = {0, };
821 if (!formatContext || !fp || !basic_header) {
822 debug_error(DEBUG, "invalid param\n");
823 return MMFILE_UTIL_FAIL;
826 readed = mmfile_read(fp, (unsigned char *)&smtaTag, sizeof(MMFILE_M4A_SMTA_TAGBOX));
827 if (readed != sizeof(MMFILE_M4A_SMTA_TAGBOX)) {
828 debug_error(DEBUG, "read smta tag header fail\n");
832 smtaTag.length = mmfile_io_be_uint32(smtaTag.length);
833 smtaTag.value = mmfile_io_be_uint32(smtaTag.value);
835 debug_msg(RELEASE, "Len : 0x%x", smtaTag.length);
836 debug_msg(RELEASE, "Saut : 0x%x 0x%x 0x%x 0x%x", smtaTag.saut[0], smtaTag.saut[1], smtaTag.saut[2], smtaTag.saut[3]);
837 debug_msg(RELEASE, "Value : 0x%x", smtaTag.value);
848 7: 360 slowmotion mode
849 8: 180 slowmotion mode
851 if (smtaTag.saut[0] == 's'
852 && smtaTag.saut[1] == 'a'
853 && smtaTag.saut[2] == 'u'
854 && smtaTag.saut[3] == 't') {
855 if (smtaTag.value == 0x01) {
856 debug_msg(RELEASE, "This has saut tag and valid value");
857 formatContext->smta = 1;
858 } else if (smtaTag.value == 0x02) {
859 debug_msg(RELEASE, "This has saut tag and valid value");
860 formatContext->smta = 2;
862 debug_error(DEBUG, "This has saut tag but invalid value");
866 debug_error(DEBUG, "This hasn't saut tag and valid value");
870 return MMFILE_UTIL_SUCCESS;
873 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
875 return MMFILE_UTIL_FAIL;
878 static int GetSA3DInfoFromMP4ATagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
880 if (!formatContext || !fp || !basic_header) {
881 debug_error(DEBUG, "invalid param\n");
882 return MMFILE_UTIL_FAIL;
885 unsigned char *buffer;
887 bool is_SA3D_present = false;
889 MMFILE_MP4A_SA3D_TAGBOX sa3dTag = {0, };
891 formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_UNKNOWN;
892 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_UNKNOWN;
893 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_UNKNOWN;
895 mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
897 buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
899 debug_error(DEBUG, "calloc failed ");
903 readed = mmfile_read(fp, buffer, basic_header->size);
904 if (readed != (int)basic_header->size) {
905 debug_error(DEBUG, "read mp4a box failed\n");
909 for (i = 0; i + 3 < basic_header->size; ++i)
910 if (buffer[i] == 'S' && buffer[i + 1] == 'A' && buffer[i + 2] == '3' && buffer[i + 3] == 'D') {
911 debug_warning(DEBUG, "SA3D data found at offset %d bytes\n", i);
912 is_SA3D_present = true;
916 if (!is_SA3D_present) {
917 debug_warning(DEBUG, "No SA3D box found");
921 mmfile_seek(fp, basic_header->start_offset + i + 4, SEEK_SET);
923 readed = mmfile_read(fp, (unsigned char *)&sa3dTag, sizeof(MMFILE_MP4A_SA3D_TAGBOX));
924 if (readed != sizeof(MMFILE_MP4A_SA3D_TAGBOX)) {
925 debug_error(DEBUG, "read SA3D tag header fail\n");
929 sa3dTag.ambisonic_order = mmfile_io_be_uint32(sa3dTag.ambisonic_order);
930 sa3dTag.num_channels = mmfile_io_be_uint32(sa3dTag.num_channels);
931 for (i = 0; i < sa3dTag.num_channels; ++i)
932 sa3dTag.channel_map[i] = mmfile_io_be_uint32(sa3dTag.channel_map[i]);
934 debug_msg(RELEASE, "sa3dTag.version = %d", sa3dTag.version);
935 debug_msg(RELEASE, "sa3dTag.ambisonic_type = %d", sa3dTag.ambisonic_type);
936 debug_msg(RELEASE, "sa3dTag.ambisonic_order = %d", sa3dTag.ambisonic_order);
937 debug_msg(RELEASE, "sa3dTag.ambisonic_channel_ordering = %d", sa3dTag.ambisonic_channel_ordering);
938 debug_msg(RELEASE, "sa3dTag.ambisonic_normalization = %d", sa3dTag.ambisonic_normalization);
939 debug_msg(RELEASE, "sa3dTag.num_channels = %d", sa3dTag.num_channels);
940 for (i = 0; i < sa3dTag.num_channels; ++i)
941 debug_msg(RELEASE, "sa3dTag.channel_map[%d] = %d", i, sa3dTag.channel_map[i]);
943 if (sa3dTag.version != RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
944 debug_error(DEBUG, "SA3D tag box version is unsupported\n");
947 if (sa3dTag.ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
948 formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_PERIPHONIC;
950 switch (sa3dTag.ambisonic_order) {
951 case MMFILE_AMBISONIC_ORDER_FOA: {
952 if (sa3dTag.num_channels == 4) {
953 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_FOA;
955 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
956 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
957 (sa3dTag.channel_map[0] == 0) &&
958 (sa3dTag.channel_map[1] == 1) &&
959 (sa3dTag.channel_map[2] == 2) &&
960 (sa3dTag.channel_map[3] == 3))
961 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
963 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
964 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
965 (sa3dTag.channel_map[0] == 0) &&
966 (sa3dTag.channel_map[1] == 3) &&
967 (sa3dTag.channel_map[2] == 1) &&
968 (sa3dTag.channel_map[3] == 2))
969 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
971 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond\n");
977 case MMFILE_AMBISONIC_ORDER_SOA: {
978 if (sa3dTag.num_channels == 9) {
979 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_SOA;
981 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
982 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
983 (sa3dTag.channel_map[0] == 0) &&
984 (sa3dTag.channel_map[1] == 1) &&
985 (sa3dTag.channel_map[2] == 2) &&
986 (sa3dTag.channel_map[3] == 3) &&
987 (sa3dTag.channel_map[4] == 4) &&
988 (sa3dTag.channel_map[5] == 5) &&
989 (sa3dTag.channel_map[6] == 6) &&
990 (sa3dTag.channel_map[7] == 7) &&
991 (sa3dTag.channel_map[8] == 8))
992 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
994 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
995 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
996 (sa3dTag.channel_map[0] == 0) &&
997 (sa3dTag.channel_map[1] == 3) &&
998 (sa3dTag.channel_map[2] == 1) &&
999 (sa3dTag.channel_map[3] == 2) &&
1000 (sa3dTag.channel_map[4] == 6) &&
1001 (sa3dTag.channel_map[5] == 7) &&
1002 (sa3dTag.channel_map[6] == 5) &&
1003 (sa3dTag.channel_map[7] == 8) &&
1004 (sa3dTag.channel_map[8] == 4))
1005 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
1007 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond\n");
1014 case MMFILE_AMBISONIC_ORDER_TOA: {
1015 if (sa3dTag.num_channels == 16) {
1016 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_TOA;
1018 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
1019 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
1020 (sa3dTag.channel_map[0] == 0) &&
1021 (sa3dTag.channel_map[1] == 1) &&
1022 (sa3dTag.channel_map[2] == 2) &&
1023 (sa3dTag.channel_map[3] == 3) &&
1024 (sa3dTag.channel_map[4] == 4) &&
1025 (sa3dTag.channel_map[5] == 5) &&
1026 (sa3dTag.channel_map[6] == 6) &&
1027 (sa3dTag.channel_map[7] == 7) &&
1028 (sa3dTag.channel_map[8] == 8) &&
1029 (sa3dTag.channel_map[9] == 9) &&
1030 (sa3dTag.channel_map[10] == 10) &&
1031 (sa3dTag.channel_map[11] == 11) &&
1032 (sa3dTag.channel_map[12] == 12) &&
1033 (sa3dTag.channel_map[13] == 13) &&
1034 (sa3dTag.channel_map[14] == 14) &&
1035 (sa3dTag.channel_map[15] == 15))
1036 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
1038 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
1039 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
1040 (sa3dTag.channel_map[0] == 0) &&
1041 (sa3dTag.channel_map[1] == 3) &&
1042 (sa3dTag.channel_map[2] == 1) &&
1043 (sa3dTag.channel_map[3] == 2) &&
1044 (sa3dTag.channel_map[4] == 6) &&
1045 (sa3dTag.channel_map[5] == 7) &&
1046 (sa3dTag.channel_map[6] == 5) &&
1047 (sa3dTag.channel_map[7] == 8) &&
1048 (sa3dTag.channel_map[8] == 4) &&
1049 (sa3dTag.channel_map[9] == 12) &&
1050 (sa3dTag.channel_map[10] == 13) &&
1051 (sa3dTag.channel_map[11] == 11) &&
1052 (sa3dTag.channel_map[12] == 14) &&
1053 (sa3dTag.channel_map[13] == 10) &&
1054 (sa3dTag.channel_map[14] == 15) &&
1055 (sa3dTag.channel_map[15] == 9))
1056 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
1058 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond\n");
1066 debug_warning(DEBUG, "Ambisonic order or format is not supported: ambix or amb formats up to 3rd order were expected.\n");
1072 debug_msg(RELEASE, "formatContext->ambisonic_type = %d", formatContext->ambisonicType);
1073 debug_msg(RELEASE, "formatContext->ambisonic_order = %d", formatContext->ambisonicOrder);
1074 debug_msg(RELEASE, "formatContext->ambisonic_format = %d", formatContext->ambisonicFormat);
1077 return MMFILE_UTIL_SUCCESS;
1080 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1082 return MMFILE_UTIL_FAIL;
1085 static int ParseSt3dData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset)
1087 uint8_t stereo_mode = INVALID_UINT8_VALUE;
1088 unsigned int readed = 0;
1090 mmfile_seek(fp, start_offset, SEEK_SET);
1092 readed = mmfile_read(fp, (unsigned char *)&stereo_mode, sizeof(uint8_t));
1093 if (readed != sizeof(uint8_t)) {
1094 debug_error(DEBUG, "read st3d tag header fail\n");
1095 return MMFILE_UTIL_FAIL;
1098 formatContext->stereoModeV2 = stereo_mode;
1100 return MMFILE_UTIL_SUCCESS;
1103 static int ParseSvhdData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset, unsigned int box_size)
1105 unsigned int readed = 0;
1107 formatContext->metadataSourceV2 = (char *)calloc(1, box_size);
1108 if (!formatContext->metadataSourceV2) {
1109 debug_error(DEBUG, "Calloc failed");
1110 return MMFILE_UTIL_FAIL;
1113 mmfile_seek(fp, start_offset, SEEK_SET);
1114 readed = mmfile_read(fp, (unsigned char *)formatContext->metadataSourceV2, box_size);
1115 if (readed != box_size) {
1116 debug_error(DEBUG, "read svhd tag header fail\n");
1117 if (formatContext->metadataSourceV2)
1118 free(formatContext->metadataSourceV2);
1119 return MMFILE_UTIL_FAIL;
1122 return MMFILE_UTIL_SUCCESS;
1125 static int ParseProjData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset)
1127 unsigned int readed = 0;
1128 typedef struct proj_box_data_s {
1132 } __attribute__((aligned(1), packed)) proj_box_data;
1134 typedef struct prhd_box_data_s {
1135 uint32_t projection_pose_yaw;
1136 uint32_t projection_pose_pitch;
1137 uint32_t projection_pose_roll;
1140 typedef struct equi_box_data_s {
1141 uint32_t projection_bounds_top;
1142 uint32_t projection_bounds_bottom;
1143 uint32_t projection_bounds_left;
1144 uint32_t projection_bounds_right;
1147 typedef struct cbmp_box_data_s {
1153 proj_box_data proj_data;
1154 proj_data.proj_type = INVALID_UINT_VALUE;
1156 prhd_box_data prhd_data;
1157 prhd_data.projection_pose_yaw = INVALID_UINT_VALUE;
1158 prhd_data.projection_pose_pitch = INVALID_UINT_VALUE;
1159 prhd_data.projection_pose_roll = INVALID_UINT_VALUE;
1161 equi_box_data equi_data;
1162 equi_data.projection_bounds_top = INVALID_UINT_VALUE;
1163 equi_data.projection_bounds_bottom = INVALID_UINT_VALUE;
1164 equi_data.projection_bounds_left = INVALID_UINT_VALUE;
1165 equi_data.projection_bounds_right = INVALID_UINT_VALUE;
1167 cbmp_box_data cbmp_data;
1168 cbmp_data.layout = INVALID_UINT_VALUE;
1169 cbmp_data.padding = INVALID_UINT_VALUE;
1171 mmfile_seek(fp, start_offset, SEEK_SET);
1173 readed = mmfile_read(fp, (unsigned char *)&proj_data, sizeof(proj_box_data));
1174 if (readed != sizeof(proj_box_data)) {
1175 debug_error(DEBUG, "read of proj box failed\n");
1176 return MMFILE_UTIL_FAIL;
1179 formatContext->projTypeV2 = mmfile_io_be_uint32(proj_data.proj_type);
1181 debug_error(DEBUG, "formatContext->projTypeV2 = %d\n", formatContext->projTypeV2);
1182 debug_error(DEBUG, "proj_data.version = %d\n", proj_data.version);
1183 debug_error(DEBUG, "proj_data.flags = %d\n", ((uint32_t)proj_data.flags[0] << 16) +
1184 ((uint32_t)proj_data.flags[1] << 8) + (uint32_t)proj_data.flags[2]);
1186 mmfile_seek(fp, sizeof(proj_box_data), SEEK_CUR);
1187 readed = mmfile_read(fp, (unsigned char *)&prhd_data, sizeof(prhd_box_data));
1188 if (readed != sizeof(prhd_box_data)) {
1189 debug_error(DEBUG, "read of prhd box failed\n");
1190 return MMFILE_UTIL_FAIL;
1193 formatContext->poseYawV2 = mmfile_io_be_uint32(prhd_data.projection_pose_yaw);
1194 formatContext->posePitchV2 = mmfile_io_be_uint32(prhd_data.projection_pose_pitch);
1195 formatContext->poseRollV2 = mmfile_io_be_uint32(prhd_data.projection_pose_roll);
1197 debug_error(DEBUG, "formatContext->poseYawV2 = %d\n", formatContext->poseYawV2);
1198 debug_error(DEBUG, "formatContext->posePitchV2 = %d\n", formatContext->posePitchV2);
1199 debug_error(DEBUG, "formatContext->poseRollV2 = %d\n", formatContext->poseRollV2);
1201 if (formatContext->projTypeV2 == PROJECTION_TYPE_EQUI) {
1202 debug_msg(RELEASE, "Projection type is Equirectangular");
1203 mmfile_seek(fp, 8, SEEK_CUR); /* 8 = 4 (for size) + 4 (fourcc) */
1204 readed = mmfile_read(fp, (unsigned char *)&equi_data, sizeof(equi_box_data));
1205 if (readed != sizeof(equi_box_data)) {
1206 debug_error(DEBUG, "read of equi box failed\n");
1207 return MMFILE_UTIL_FAIL;
1210 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi_data.projection_bounds_top);
1211 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi_data.projection_bounds_bottom);
1212 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi_data.projection_bounds_left);
1213 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi_data.projection_bounds_right);
1215 debug_error(DEBUG, "formatContext->equiBoundsTopV2 = %d\n", formatContext->equiBoundsTopV2);
1216 debug_error(DEBUG, "formatContext->equiBoundsBottomV2 = %d\n", formatContext->equiBoundsBottomV2);
1217 debug_error(DEBUG, "formatContext->equiBoundsLeftV2 = %d\n", formatContext->equiBoundsLeftV2);
1218 debug_error(DEBUG, "formatContext->equiBoundsRightV2 = %d\n", formatContext->equiBoundsRightV2);
1220 } else if (formatContext->projTypeV2 == PROJECTION_TYPE_CBMP) {
1221 debug_msg(RELEASE, "Projection type is Cubemap");
1222 mmfile_seek(fp, 8, SEEK_CUR); /* 8 = 4 (for size) + 4 (fourcc) */
1223 readed = mmfile_read(fp, (unsigned char *)&cbmp_data, sizeof(cbmp_box_data));
1224 if (readed != sizeof(cbmp_box_data)) {
1225 debug_error(DEBUG, "read of cbmp box failed\n");
1226 return MMFILE_UTIL_FAIL;
1229 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp_data.layout);
1230 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp_data.padding);
1232 debug_error(DEBUG, "formatContext->cbmpLayoutV2 = %d\n", formatContext->cbmpLayoutV2);
1233 debug_error(DEBUG, "formatContext->cbmpPaddingV2 = %d\n", formatContext->cbmpPaddingV2);
1236 debug_msg(RELEASE, "Projection type is %d (unknown)", proj_data.proj_type);
1237 return MMFILE_UTIL_FAIL;
1240 return MMFILE_UTIL_SUCCESS;
1243 static int GetVideoV2MetadataFromAvc1TagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1245 if (!formatContext || !fp || !basic_header) {
1246 debug_error(DEBUG, "invalid param\n");
1247 return MMFILE_UTIL_FAIL;
1250 unsigned char *buffer;
1254 formatContext->stereoModeV2 = INVALID_UINT_VALUE;
1255 formatContext->metadataSourceV2 = NULL;
1256 formatContext->projTypeV2 = INVALID_UINT_VALUE;
1257 formatContext->poseYawV2 = INVALID_UINT_VALUE;
1258 formatContext->posePitchV2 = INVALID_UINT_VALUE;
1259 formatContext->poseRollV2 = INVALID_UINT_VALUE;
1260 formatContext->cbmpLayoutV2 = INVALID_UINT_VALUE;
1261 formatContext->cbmpPaddingV2 = INVALID_UINT_VALUE;
1262 formatContext->equiBoundsTopV2 = INVALID_UINT_VALUE;
1263 formatContext->equiBoundsBottomV2 = INVALID_UINT_VALUE;
1264 formatContext->equiBoundsLeftV2 = INVALID_UINT_VALUE;
1265 formatContext->equiBoundsRightV2 = INVALID_UINT_VALUE;
1267 mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
1269 buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
1271 debug_error(DEBUG, "calloc failed ");
1275 readed = mmfile_read(fp, buffer, basic_header->size);
1276 if (readed != (int)basic_header->size) {
1277 debug_error(DEBUG, "read st3d box failed\n");
1281 for (i = 0; i + 3 < basic_header->size; ++i) {
1282 if ((buffer[i] == 's' && buffer[i + 1] == 't' && buffer[i + 2] == '3' && buffer[i + 3] == 'd') && (formatContext->stereoModeV2 == INVALID_UINT_VALUE)) {
1283 debug_warning(DEBUG, "st3d data found at offset %lld\n", basic_header->start_offset + i);
1284 ParseSt3dData(formatContext, fp, basic_header->start_offset + i + 4);
1285 debug_msg(RELEASE, "formatContext->stereoModeV2 = %d", formatContext->stereoModeV2);
1287 if (buffer[i] == 's' && buffer[i + 1] == 'v' && buffer[i + 2] == '3' && buffer[i + 3] == 'd') {
1288 debug_warning(DEBUG, "sv3d data found at offset %lld\n", basic_header->start_offset + i);
1289 formatContext->isSpherical = true;
1291 if (buffer[i] == 's' && buffer[i + 1] == 'v' && buffer[i + 2] == 'h' && buffer[i + 3] == 'd') {
1292 debug_warning(DEBUG, "svhd data found at offset %lld\n", basic_header->start_offset + i);
1293 ParseSvhdData(formatContext, fp, basic_header->start_offset + i + 4, mmfile_io_be_uint32(*((uint32_t*)(buffer - 4 + i))));
1294 debug_msg(RELEASE, "formatContext->metadataSourceV2 = %s (length = %zu)", formatContext->metadataSourceV2, strlen(formatContext->metadataSourceV2));
1296 if (buffer[i] == 'p' && buffer[i + 1] == 'r' && buffer[i + 2] == 'o' && buffer[i + 3] == 'j') {
1297 debug_warning(DEBUG, "proj data found at offset %lld\n", basic_header->start_offset + i);
1298 ParseProjData(formatContext, fp, basic_header->start_offset + i + 4);
1302 return MMFILE_UTIL_SUCCESS;
1305 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1307 return MMFILE_UTIL_FAIL;
1310 static int GetValueFromCDISTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1312 unsigned int value = 0;
1315 if (!formatContext || !fp || !basic_header) {
1316 debug_error(DEBUG, "invalid param\n");
1317 return MMFILE_UTIL_FAIL;
1320 readed = mmfile_read(fp, (unsigned char *)&value, sizeof(unsigned int));
1321 if (readed != sizeof(unsigned int)) {
1322 debug_error(DEBUG, "read cdis tag header fail\n");
1326 value = mmfile_io_be_uint32(value);
1328 debug_msg(RELEASE, "Value : 0x%x", value);
1330 if (value == 0x01) {
1331 debug_msg(RELEASE, "This has cdis tag and valid value");
1332 formatContext->cdis = 1;
1334 debug_error(DEBUG, "This has cdis tag and but invalid value");
1338 return MMFILE_UTIL_SUCCESS;
1341 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1343 return MMFILE_UTIL_FAIL;
1346 static int GetTagFromMetaBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1349 MMFILE_MP4_BASIC_BOX_HEADER hdlrBoxHeader = {0, };
1350 MMFILE_MP4_BASIC_BOX_HEADER id3v2BoxHeader = {0, };
1351 MMFILE_3GP_ID3V2_BOX id3v2Box = {0, };
1352 AvFileContentInfo tagInfo = {0, };
1353 unsigned char tagVersion = 0;
1354 bool versionCheck = false;
1356 unsigned int meta_version = 0;
1357 MMFILE_3GP_HANDLER_BOX hdlrBox = {0, };
1358 unsigned int encSize = 0;
1360 #ifdef ENABLE_ITUNES_META /* We don't support itunes meta now. so this is not defined yet */
1361 int iTunes_meta = 0;
1365 readed = mmfile_read(fp, (unsigned char *)&meta_version, 4);
1367 debug_error(DEBUG, "read meta box version\n");
1372 readed = mmfile_read(fp, (unsigned char *)&hdlrBoxHeader, MMFILE_MP4_BASIC_BOX_HEADER_LEN);
1373 if (readed != MMFILE_MP4_BASIC_BOX_HEADER_LEN) {
1374 debug_error(DEBUG, "read hdlr box header\n");
1378 if (hdlrBoxHeader.type != FOURCC('h', 'd', 'l', 'r')) {
1379 debug_warning(DEBUG, "meta type is not hdlr\n");
1383 hdlrBoxHeader.size = mmfile_io_be_uint32(hdlrBoxHeader.size);
1384 hdlrBoxHeader.type = mmfile_io_le_uint32(hdlrBoxHeader.type);
1386 readed = mmfile_read(fp, (unsigned char *)&hdlrBox, MMFILE_3GP_HANDLER_BOX_LEN);
1387 if (readed != MMFILE_3GP_HANDLER_BOX_LEN) {
1388 debug_error(DEBUG, "read hdlr box\n");
1392 hdlrBox.handler_type = mmfile_io_le_uint32(hdlrBox.handler_type);
1395 * check tag type (ID3v2 or iTunes)
1397 if (hdlrBox.handler_type == FOURCC('I', 'D', '3', '2')) {
1398 debug_msg(RELEASE, "ID3v2 tag detected.\n");
1401 #ifdef ENABLE_ITUNES_META
1404 } else if (hdlrBox.handler_type == FOURCC('m', 'd', 'i', 'r') &&
1405 mmfile_io_le_uint32(hdlrBox.reserved[0]) == FOURCC('a', 'p', 'p', 'l')) {
1407 debug_msg(RELEASE, "Apple iTunes tag detected by mdir.\n");
1409 #ifdef ENABLE_ITUNES_META
1413 debug_warning(DEBUG, "unknown meta type. 4CC:[%c%c%c%c]\n", ((char *)&hdlrBox.handler_type)[0],
1414 ((char *)&hdlrBox.handler_type)[1],
1415 ((char *)&hdlrBox.handler_type)[2],
1416 ((char *)&hdlrBox.handler_type)[3]);
1417 /*goto exception; */
1420 #ifdef ENABLE_ITUNES_META
1421 if (!id3_meta && !iTunes_meta) {
1423 APPLE meta data for iTunes reader = 'mdir.' so if handler type is 'mdir', this content may has itunes meta.
1424 most of contents has 'mdir' + 'appl'. but some contents just has 'mdir'
1425 but 'ilst' is meta for iTunes. so find 'ilst' is more correct to check if this contents has iTunes meta or not.*/
1427 const char *ilst_box = "ilst";
1428 int buf_size = strlen(ilst_box);
1430 unsigned char read_buf[buf_size + 1];
1431 memset(read_buf, 0x00, buf_size + 1);
1434 mmfile_seek(fp, hdlrBoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_HANDLER_BOX_LEN + 4, SEEK_CUR); /*+4 is hdlr size field */
1436 readed = mmfile_read(fp, read_buf, buf_size); /* to find 'ilst' */
1437 if (readed != buf_size) {
1438 debug_error(DEBUG, "read fail [%d]\n", readed);
1442 if (read_buf[0] == 'i' && read_buf[1] == 'l' && read_buf[2] == 's' && read_buf[3] == 't') {
1443 debug_msg(RELEASE, "Apple iTunes tag detected by ilst.\n");
1449 #ifdef ENABLE_ITUNES_META
1452 * iTunes (Cover[?ovr] & Track[trkn] only extract!) + Genre/Artist : Added 2010.10.27,28
1460 #define _ITUNES_READ_BUF_SZ 20
1461 #define _ITUNES_TRACK_NUM_SZ 4
1462 #define _ITUNES_GENRE_NUM_SZ 4
1463 #define _ITUNES_COVER_TYPE_JPEG 13
1464 #define _ITUNES_COVER_TYPE_PNG 14
1466 unsigned char read_buf[_ITUNES_READ_BUF_SZ];
1468 int cover_sz = 0, cover_type = 0, cover_found = 0;
1469 /* int track_found = 0; */ /* , track_num = 0; */
1470 /* int genre_found = 0; */ /* , genre_index = 0; */
1471 /* int artist_found = 0; */ /* , artist_sz = 0; */
1472 int limit = basic_header->size - hdlrBoxHeader.size;
1473 long long cover_offset = 0; /*, track_offset =0, genre_offset = 0, artist_offset = 0; */
1475 /* 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++) { */
1476 for (i = 0; (i < limit) && (cover_found == 0) ; i++) {
1477 readed = mmfile_read(fp, read_buf, _ITUNES_READ_BUF_SZ);
1478 if (readed != _ITUNES_READ_BUF_SZ)
1481 /*ffmpeg extract artist, tracknum, genre and cover image. see mov_read_udta_string().
1482 but ffmpeg does not support strange cover image.
1483 only support covr type 0xd(JPEG), 0xe(PNG), 0x1b(BMP). but we support other type*/
1486 * Artist : Added 2010.10.28
1488 if (artist_found == 0 &&
1489 read_buf[0] == 0xa9 && read_buf[1] == 'A' && read_buf[2] == 'R' && read_buf[3] == 'T' &&
1490 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1493 artist_offset = mmfile_tell(fp);
1494 artist_sz = mmfile_io_be_uint32(*(int *)(read_buf + 4)) - 16; /* atom len(4)+data(4)+atom verion(1)+flag(3)+null(4) = 16 */
1496 debug_msg(RELEASE, "----------------------------------- artist found, offset=[%lld], size=[%d]\n", artist_offset, artist_sz);
1502 if (track_found == 0 &&
1503 read_buf[0] == 't' && read_buf[1] == 'r' && read_buf[2] == 'k' && read_buf[3] == 'n' &&
1504 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1507 track_offset = mmfile_tell(fp);
1509 debug_msg(RELEASE, "----------------------------------- Track found, offset=[%lld]\n", track_offset);
1513 * Genre : Added 2010.10.27
1515 /*ffmpeg extract genre but only (0xa9,'g','e','n'). see mov_read_udta_string()*/
1516 if (genre_found == 0 &&
1517 read_buf[0] == 'g' && read_buf[1] == 'n' && read_buf[2] == 'r' && read_buf[3] == 'e' &&
1518 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1521 genre_offset = mmfile_tell(fp);
1523 debug_msg(RELEASE, "----------------------------------- genre found, offset=[%lld]\n", genre_offset);
1531 if (cover_found == 0 &&
1532 read_buf[0] == 'c' && read_buf[1] == 'o' && read_buf[2] == 'v' && read_buf[3] == 'r' &&
1533 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1536 cover_sz = mmfile_io_be_uint32(*(int *)(read_buf + 4)) - 12;
1537 cover_type = mmfile_io_be_uint32(*(int *)(read_buf + 12));
1539 cover_offset = mmfile_tell(fp);
1541 debug_msg(RELEASE, "----------------------------------- cover_found found, offset=[%lld]\n", cover_offset);
1544 mmfile_seek(fp, -(_ITUNES_READ_BUF_SZ - 1), SEEK_CUR); /*FIXME: poor search*/
1547 /*ffmpeg extract artist, tracknum, excep cover image. see mov_read_udta_string()*/
1550 if (artist_sz > 0) {
1551 mmfile_seek(fp, artist_offset, SEEK_SET);
1553 if (formatContext->artist) {
1554 debug_msg(RELEASE, "----------------------------------- previous artist was [%s] \n", formatContext->artist);
1555 free(formatContext->artist);
1558 debug_msg(RELEASE, "----------------------------------- new artist will be allocated with size (len+1) [%d] \n", artist_sz + 1);
1559 formatContext->artist = g_malloc0(artist_sz + 1);
1561 if (formatContext->artist) {
1562 readed = mmfile_read(fp, (unsigned char *)formatContext->artist, artist_sz);
1563 formatContext->artist[artist_sz] = '\0';
1565 debug_msg(RELEASE, "----------------------------------- new artist is [%s] \n", formatContext->artist);
1567 if (readed != artist_sz) {
1568 debug_error(DEBUG, "failed to read. ret = %d, in = %d\n", readed, artist_sz);
1569 mmfile_free(formatContext->artist);
1576 mmfile_seek(fp, track_offset, SEEK_SET);
1577 readed = mmfile_read(fp, read_buf, _ITUNES_TRACK_NUM_SZ);
1578 if (readed != _ITUNES_TRACK_NUM_SZ) {
1579 debug_error(DEBUG, "failed to read. ret = %d, in = %d\n", readed, _ITUNES_TRACK_NUM_SZ);
1581 track_num = mmfile_io_be_uint32(*(int *)read_buf);
1582 if (!formatContext->tagTrackNum) {
1583 memset(read_buf, 0x00, _ITUNES_READ_BUF_SZ);
1584 snprintf((char *)read_buf, sizeof(read_buf), "%d", track_num);
1585 formatContext->tagTrackNum = g_strdup((const char *)read_buf);
1591 mmfile_seek(fp, genre_offset, SEEK_SET);
1592 readed = mmfile_read(fp, read_buf, _ITUNES_GENRE_NUM_SZ);
1593 if (readed != _ITUNES_GENRE_NUM_SZ) {
1594 debug_error(DEBUG, "failed to read. ret = %d, in = %d\n", readed, _ITUNES_GENRE_NUM_SZ);
1596 genre_index = mmfile_io_be_uint16(*(int *)read_buf);
1597 debug_msg(RELEASE, "genre index=[%d] \n", genre_index);
1599 if (genre_index > 0 && genre_index < GENRE_COUNT) {
1600 if (!formatContext->genre) {
1601 memset(read_buf, 0x00, _ITUNES_READ_BUF_SZ);
1602 snprintf((char *)read_buf, sizeof(read_buf), "%s", MpegAudio_Genre[genre_index - 1]);
1603 debug_msg(RELEASE, "genre string=[%s] \n", read_buf);
1604 formatContext->genre = g_strdup((const char *)read_buf);
1612 1) below spec is in "iTunes Package Asset Specification 4.3" published by apple.
1613 Music Cover Art Image Profile
1614 - TIFF with ".tif" extension (32-bit uncompressed), JPEG with ".jpg" extension (quality unconstrained), or PNG with ".png" extension
1615 - RGB (screen standard)
1616 - Minimum size of 600 x 600 pixels
1617 - Images must be at least 72 dpi
1619 2)I found below info from google.
1620 cover image flag : JPEG (13, 0xd), PNG (14, 0xe)
1622 3)So, FIXME when cover image format is tif!
1626 mmfile_seek(fp, cover_offset, SEEK_SET);
1628 formatContext->artwork = g_malloc0(cover_sz);
1629 formatContext->artworkSize = cover_sz;
1630 if (cover_type == _ITUNES_COVER_TYPE_JPEG) {
1631 formatContext->artworkMime = g_strdup("image/jpeg");
1632 } else if (cover_type == _ITUNES_COVER_TYPE_PNG) {
1633 formatContext->artworkMime = g_strdup("image/png");
1634 /*} else if (cover_type == _ITUNES_COVER_TYPE_TIF) {
1635 formatContext->artworkMime = g_strdup("image/tif");*/
1637 debug_warning(DEBUG, "Not proper cover image type, but set to jpeg. cover_type[%d]", cover_type);
1638 formatContext->artworkMime = g_strdup("image/jpeg");
1641 if (formatContext->artwork) {
1642 readed = mmfile_read(fp, formatContext->artwork, cover_sz);
1643 if (readed != cover_sz) {
1644 debug_error(DEBUG, "failed to read. ret = %d, in = %d\n", readed, cover_sz);
1645 mmfile_free(formatContext->artwork);
1646 formatContext->artworkSize = 0;
1647 mmfile_free(formatContext->artworkMime);
1653 /*reset seek position*/
1654 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1656 return MMFILE_UTIL_SUCCESS;
1664 /* skip hdlr box name */
1665 mmfile_seek(fp, hdlrBoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_HANDLER_BOX_LEN, SEEK_CUR);
1668 readed = mmfile_read(fp, (unsigned char *)&id3v2BoxHeader, MMFILE_MP4_BASIC_BOX_HEADER_LEN);
1669 if (readed != MMFILE_MP4_BASIC_BOX_HEADER_LEN) {
1670 debug_error(DEBUG, "read id3v2 box header\n");
1674 id3v2BoxHeader.size = mmfile_io_be_uint32(id3v2BoxHeader.size);
1675 id3v2BoxHeader.type = mmfile_io_le_uint32(id3v2BoxHeader.type);
1677 if (id3v2BoxHeader.type != FOURCC('I', 'D', '3', '2')) {
1678 debug_warning(DEBUG, "meta type is not id3v2\n");
1682 readed = mmfile_read(fp, (unsigned char *)&id3v2Box, MMFILE_3GP_ID3V2_BOX_LEN);
1683 if (readed != MMFILE_3GP_ID3V2_BOX_LEN) {
1684 debug_error(DEBUG, "read id3v2 box\n");
1688 id3v2Len = id3v2BoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_ID3V2_BOX_LEN;
1690 id3v2Box.id3v2Data = g_malloc0(id3v2Len);
1692 readed = mmfile_read(fp, (unsigned char *)id3v2Box.id3v2Data, id3v2Len);
1693 if (readed != id3v2Len) {
1694 debug_error(DEBUG, "read id3tag data error\n");
1699 if (!IS_ID3V2_TAG(id3v2Box.id3v2Data)) {
1700 debug_error(DEBUG, "it is not id3tag\n");
1704 if (id3v2Box.id3v2Data[3] == 0xFF || id3v2Box.id3v2Data[4] == 0xFF ||
1705 id3v2Box.id3v2Data[6] >= 0x80 || id3v2Box.id3v2Data[7] >= 0x80 ||
1706 id3v2Box.id3v2Data[8] >= 0x80 || id3v2Box.id3v2Data[9] >= 0x80) {
1707 debug_error(DEBUG, "it is not valid id3tag\n");
1711 tagVersion = id3v2Box.id3v2Data[3];
1712 if (tagVersion > 4) {
1713 debug_error(DEBUG, "tag vesion is too high\n");
1717 encSize = mmfile_io_le_uint32((unsigned int)&id3v2Box.id3v2Data[6]);
1718 tagInfo.tagV2Info.tagLen = MP3_TAGv2_HEADER_LEN;
1719 tagInfo.tagV2Info.tagLen += (((encSize & 0x0000007F) >> 0) | ((encSize & 0x00007F00) >> 1) | ((encSize & 0x007F0000) >> 2) | ((encSize & 0x7F000000) >> 3));
1720 tagInfo.tagV2Info.tagVersion = tagVersion;
1721 tagInfo.fileLen = id3v2Len;
1723 /* set id3v2 data to formatContext */
1724 switch (tagVersion) {
1726 versionCheck = mm_file_id3tag_parse_v222(&tagInfo, id3v2Box.id3v2Data);
1730 versionCheck = mm_file_id3tag_parse_v223(&tagInfo, id3v2Box.id3v2Data);
1734 versionCheck = mm_file_id3tag_parse_v224(&tagInfo, id3v2Box.id3v2Data);
1739 debug_error(DEBUG, "tag vesion is not support\n");
1740 versionCheck = false;
1745 if (versionCheck == false) {
1746 debug_error(DEBUG, "tag parsing is fail\n");
1750 formatContext->title = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_TITLE].value);
1751 formatContext->artist = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_ARTIST].value);
1752 formatContext->copyright = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_COPYRIGHT].value);
1753 formatContext->comment = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_COMMENT].value);
1754 formatContext->album = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_ALBUM].value);
1755 formatContext->album_artist = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_ALBUM_ARTIST].value);
1756 formatContext->year = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_YEAR].value);
1757 formatContext->genre = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_GENRE].value);
1758 formatContext->tagTrackNum = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_TRACKNUM].value);
1759 formatContext->composer = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_COMPOSER].value);
1760 formatContext->classification = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_CONTENT_GROUP].value);
1761 formatContext->conductor = g_strdup((const char *)tagInfo.tagInfo[AV_ID3TAG_CONDUCTOR].value);
1763 if (tagInfo.imageInfo.pImageBuf && tagInfo.imageInfo.imageLen > 0) {
1764 formatContext->artworkSize = tagInfo.imageInfo.imageLen;
1765 formatContext->artwork = g_memdup(tagInfo.imageInfo.pImageBuf, tagInfo.imageInfo.imageLen);
1768 mm_file_free_AvFileContentInfo(&tagInfo);
1769 mmfile_free(id3v2Box.id3v2Data);
1771 /*reset seek position*/
1772 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1774 return MMFILE_UTIL_SUCCESS;
1780 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1781 mmfile_free(id3v2Box.id3v2Data);
1782 mm_file_free_AvFileContentInfo(&tagInfo);
1784 return MMFILE_UTIL_FAIL;
1787 int mm_file_get_int_value_from_xml_string(const char* xml_str, const char* param_name, int* value)
1789 char *value_start, *value_end, *endptr;
1790 const short value_length_max = 12;
1791 char init_view_ret[value_length_max];
1792 int value_length = 0;
1794 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1795 debug_error(DEBUG, "error: incorrect or non-existing parameter\n");
1796 return MMFILE_UTIL_FAIL;
1799 value_start = strstr(xml_str, param_name) + strlen(param_name);
1800 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
1803 value_end = strchr(value_start, '<');
1805 debug_error(DEBUG, "error: incorrect XML\n");
1806 return MMFILE_UTIL_FAIL;
1809 value_length = value_end - value_start;
1810 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1814 if (value_start[i] == '+' || value_start[i] == '-')
1816 while (i < value_length) {
1817 if (value_start[i] < '0' || value_start[i] > '9') {
1818 debug_error(DEBUG, "error: incorrect value, integer was expected\n");
1819 return MMFILE_UTIL_FAIL;
1824 if (value_length >= value_length_max || value_length < 1) {
1825 debug_error(DEBUG, "error: empty XML value or incorrect range\n");
1826 return MMFILE_UTIL_FAIL;
1829 memset(init_view_ret, 0x00, sizeof(init_view_ret));
1830 SAFE_STRLCPY(init_view_ret, value_start, sizeof(init_view_ret));
1832 *value = strtol(init_view_ret, &endptr, 10);
1833 if (endptr == init_view_ret) {
1834 debug_error(DEBUG, "error: no digits were found\n");
1835 return MMFILE_UTIL_FAIL;
1838 return MMFILE_UTIL_SUCCESS;
1841 int mm_file_get_string_value_from_xml_string(const char* xml_str, const char* param_name, char** value)
1843 char *value_start, *value_end;
1844 const short value_length_max = 256;
1845 int value_length = 0;
1847 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1848 debug_error(DEBUG, "error: incorrect or non-existing parameter\n");
1849 return MMFILE_UTIL_FAIL;
1852 value_start = strstr(xml_str, param_name) + strlen(param_name);
1853 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
1856 value_end = strchr(value_start, '<');
1858 debug_error(DEBUG, "error: incorrect XML\n");
1859 return MMFILE_UTIL_FAIL;
1862 value_length = value_end - value_start;
1863 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1866 if (value_length >= value_length_max || value_length < 1) {
1867 debug_error(DEBUG, "error: empty XML value or incorrect range\n");
1868 return MMFILE_UTIL_FAIL;
1871 *value = (char*)calloc(value_length, sizeof(char));
1872 if (*value == NULL) {
1873 debug_error(DEBUG, "error: calloc failed\n");
1874 return MMFILE_UTIL_FAIL;
1876 strncpy(*value, value_start, value_length);
1878 return MMFILE_UTIL_SUCCESS;
1881 int mm_file_get_bool_value_from_xml_string(const char* xml_str, const char* param_name, bool* value)
1883 char *value_start = NULL;
1884 char *value_end = NULL;
1885 int value_length = 0;
1887 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1888 debug_error(DEBUG, "error: incorrect or non-existing parameter\n");
1889 return MMFILE_UTIL_FAIL;
1892 value_start = strstr(xml_str, param_name) + strlen(param_name);
1893 while ((value_start != NULL) && ((value_start[0] == ' ') || (value_start[0] == '\t')))
1896 value_end = strchr(value_start, '<');
1897 if (value_end == NULL) {
1898 debug_error(DEBUG, "error: incorrect XML.");
1899 return MMFILE_UTIL_FAIL;
1902 value_length = value_end - value_start;
1903 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1906 if (value_length < 1) {
1907 debug_error(DEBUG, "error: empty XML value or incorrect range\n");
1908 return MMFILE_UTIL_FAIL;
1911 *value = strstr(value_start, "true") ? true : false;
1913 return MMFILE_UTIL_SUCCESS;
1916 static int g_junk_counter_limit = 0;
1917 static int GetJunkCounterLimit(void)
1919 dictionary *dict = NULL;
1922 dict = iniparser_load(MM_FILE_INI_PATH);
1924 debug_error(DEBUG, "%s load failed", MM_FILE_INI_PATH);
1928 data = iniparser_getint(dict, "mm-file-config:junk_counter_limit", 0);
1929 debug_msg(DEBUG, "mm-file-config:junk_counter_limit= %u", data);
1931 iniparser_freedict(dict);
1936 int ParseSpatialVideoMetadataFromXMLString(const char *xmlStr, MMFileFormatContext *formatContext)
1938 const char is_spherical_str[] = "<GSpherical:Spherical>";
1939 const char is_stitched_str[] = "<GSpherical:Stitched>";
1940 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
1941 const char projection_type_str[] = "<GSpherical:ProjectionType>";
1942 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
1943 const char source_count_str[] = "<GSpherical:SourceCount>";
1944 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
1945 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
1946 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
1947 const char timestamp_str[] = "<GSpherical:Timestamp>";
1948 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
1949 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
1950 const char cropped_area_image_width_str[] = "<GSpherical:CroppedAreaImageWidthPixels>";
1951 const char cropped_area_image_height_str[] = "<GSpherical:CroppedAreaImageHeightPixels>";
1952 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
1953 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
1955 mm_file_get_bool_value_from_xml_string(xmlStr, is_spherical_str, (bool*)&formatContext->isSpherical);
1956 mm_file_get_bool_value_from_xml_string(xmlStr, is_stitched_str, (bool*)&formatContext->isStitched);
1958 debug_msg(RELEASE, "isSpherical = %d", formatContext->isSpherical);
1959 debug_msg(RELEASE, "isStitched = %d", formatContext->isStitched);
1961 if (formatContext->isSpherical && formatContext->isStitched) {
1962 mm_file_get_string_value_from_xml_string(xmlStr, stitching_software_str, &formatContext->stitchingSoftware);
1963 mm_file_get_string_value_from_xml_string(xmlStr, projection_type_str, &formatContext->projectionType);
1964 mm_file_get_string_value_from_xml_string(xmlStr, stereo_mode_str, &formatContext->stereoMode);
1965 mm_file_get_int_value_from_xml_string(xmlStr, source_count_str, &formatContext->sourceCount);
1966 mm_file_get_int_value_from_xml_string(xmlStr, init_view_heading_str, &formatContext->initViewHeading);
1967 mm_file_get_int_value_from_xml_string(xmlStr, init_view_pitch_str, &formatContext->initViewPitch);
1968 mm_file_get_int_value_from_xml_string(xmlStr, init_view_roll_str, &formatContext->initViewRoll);
1969 mm_file_get_int_value_from_xml_string(xmlStr, timestamp_str, &formatContext->timestamp);
1970 mm_file_get_int_value_from_xml_string(xmlStr, full_pano_width_str, &formatContext->fullPanoWidth);
1971 mm_file_get_int_value_from_xml_string(xmlStr, full_pano_height_str, &formatContext->fullPanoHeight);
1972 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_width_str, &formatContext->croppedAreaImageWidth);
1973 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_height_str, &formatContext->croppedAreaImageHeight);
1974 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_left_str, &formatContext->croppedAreaLeft);
1975 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_top_str, &formatContext->croppedAreaTop);
1977 debug_msg(RELEASE, "stitchingSoftware = %s", formatContext->stitchingSoftware);
1978 debug_msg(RELEASE, "projectionType = %s", formatContext->projectionType);
1979 debug_msg(RELEASE, "stereoMode = %s", formatContext->stereoMode);
1980 debug_msg(RELEASE, "sourceCount %d", formatContext->sourceCount);
1981 debug_msg(RELEASE, "initViewHeading = %d", formatContext->initViewHeading);
1982 debug_msg(RELEASE, "initViewPitch = %d", formatContext->initViewPitch);
1983 debug_msg(RELEASE, "initViewRoll = %d", formatContext->initViewRoll);
1984 debug_msg(RELEASE, "timestamp = %d", formatContext->timestamp);
1985 debug_msg(RELEASE, "fullPanoWidthPixels = %d", formatContext->fullPanoWidth);
1986 debug_msg(RELEASE, "fullPanoHeightPixels = %d", formatContext->fullPanoHeight);
1987 debug_msg(RELEASE, "croppedAreaImageWidth = %d", formatContext->croppedAreaImageWidth);
1988 debug_msg(RELEASE, "croppedAreaImageHeight = %d", formatContext->croppedAreaImageHeight);
1989 debug_msg(RELEASE, "croppedAreaLeft = %d", formatContext->croppedAreaLeft);
1990 debug_msg(RELEASE, "croppedAreaTop = %d", formatContext->croppedAreaTop);
1993 return MMFILE_UTIL_SUCCESS;
1996 #define BIG_CONTENT_BOX_SIZE_LEN 8
1998 int MMFileUtilGetMetaDataFromMKV(MMFileFormatContext *formatContext)
2000 MMFileIOHandle *fp = NULL;
2001 int probe_size = 10000;
2002 unsigned char *buffer = NULL;
2005 long long file_size = 0;
2007 MMFILE_WEBM_PROJ_V2_BOX v2box = { 0, };
2008 MMFILE_WEBM_EQUI_PROJ_V2_BOX equi = { 0, };
2009 MMFILE_WEBM_CBMP_PROJ_V2_BOX cbmp = { 0, };
2010 MMFILE_WEBM_POSE_ELEMENT_V2_BOX pose = { 0, };
2012 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
2013 if (ret == MMFILE_UTIL_FAIL) {
2014 debug_error(DEBUG, "error: mmfile_open\n");
2018 file_size = mmfile_seek(fp, 0, SEEK_END);
2019 if (file_size == MMFILE_UTIL_FAIL) {
2020 debug_error(DEBUG, "mmfile operation failed\n");
2024 probe_size = (file_size > probe_size) ? probe_size : file_size;
2025 buffer = (unsigned char *)malloc(probe_size * sizeof(unsigned char));
2027 debug_error(DEBUG, "malloc failed\n");
2031 ret = mmfile_seek(fp, 0, SEEK_SET);
2032 if (ret == MMFILE_UTIL_FAIL) {
2033 debug_error(DEBUG, "mmfile operation failed\n");
2037 ret = mmfile_read(fp, buffer, probe_size * sizeof(unsigned char));
2038 if (ret == MMFILE_UTIL_FAIL) {
2039 debug_error(DEBUG, "mmfile operation failed\n");
2043 /* FIXME (m.alieksieie): It's better to use some EBML parser here*/
2044 for (i = 0; i + 3 < probe_size; ++i) {
2045 if (*(unsigned int *)(buffer + i) == FOURCC('e', 'q', 'u', 'i') ||
2046 *(unsigned int *)(buffer + i) == FOURCC('c', 'b', 'm', 'p')) {
2047 debug_msg(DEBUG, "projection data found at offset %d bytes\n", i);
2052 if (i + 3 == probe_size) {
2053 debug_msg(DEBUG, "projection info wasn't found\n");
2054 ret = MMFILE_UTIL_SUCCESS;
2058 if ((i - (int)sizeof(MMFILE_WEBM_PROJ_V2_BOX)) < 0) {
2059 debug_error(DEBUG, "error: invalid supposed projection info location\n");
2060 ret = MMFILE_UTIL_FAIL;
2064 ret = mmfile_seek(fp, i - sizeof(MMFILE_WEBM_PROJ_V2_BOX), SEEK_SET);
2065 if (ret == MMFILE_UTIL_FAIL) {
2066 debug_error(DEBUG, "error: failed to seek to the supposed projection info location\n");
2070 ret = mmfile_read(fp, (unsigned char *)&v2box, sizeof(MMFILE_WEBM_PROJ_V2_BOX));
2071 if (ret == MMFILE_UTIL_FAIL) {
2072 debug_error(DEBUG, "mmfile operation failed\n");
2076 if (v2box.proj_type_box_value == PROJECTION_TYPE_EQUI) {
2077 debug_msg(DEBUG, "Equirectangular projection is used\n");
2079 ret = mmfile_read(fp, (unsigned char *)&equi, sizeof(MMFILE_WEBM_EQUI_PROJ_V2_BOX));
2080 if (ret == MMFILE_UTIL_FAIL) {
2081 debug_error(DEBUG, "error: failed to read equirectangular element\n");
2084 if (strncmp((char *)equi.proj_priv_box_name, "equi", 4) == 0) {
2085 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_top);
2086 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_bottom);
2087 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_left);
2088 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_right);
2090 debug_error(DEBUG, "error: failed to read equirectangular element\n");
2091 ret = MMFILE_UTIL_SUCCESS;
2095 if (v2box.proj_type_box_value == PROJECTION_TYPE_CBMP) {
2096 debug_msg(DEBUG, "Cubemap projection is used\n");
2098 ret = mmfile_read(fp, (unsigned char *)&cbmp, sizeof(MMFILE_WEBM_CBMP_PROJ_V2_BOX));
2099 if (ret == MMFILE_UTIL_FAIL) {
2100 debug_error(DEBUG, "error: failed to read cubemap element\n");
2103 if (strncmp((char *)cbmp.proj_priv_box_name, "cbmp", 4) == 0) {
2104 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_layout);
2105 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_padding);
2107 debug_error(DEBUG, "error: failed to read cubemap element\n");
2108 ret = MMFILE_UTIL_FAIL;
2113 ret = mmfile_read(fp, (unsigned char *)&pose, sizeof(MMFILE_WEBM_POSE_ELEMENT_V2_BOX));
2114 if (ret == MMFILE_UTIL_FAIL) {
2115 debug_error(DEBUG, "error: failed to read pose info\n");
2119 if (pose.pose_yaw_element_id == POSE_YAW_ELEMENT_ID) {
2120 formatContext->poseYawV2 = (uint)mmfile_io_be_float32(pose.pose_yaw_element_value);
2122 debug_error(DEBUG, "error: failed to pose yaw element\n");
2123 ret = MMFILE_UTIL_FAIL;
2126 if (pose.pose_pitch_element_id == POSE_PITCH_ELEMENT_ID) {
2127 formatContext->posePitchV2 = (uint)mmfile_io_be_float32(pose.pose_pitch_element_value);
2129 debug_error(DEBUG, "error: failed to pose pitch element\n");
2130 ret = MMFILE_UTIL_FAIL;
2133 if (pose.pose_roll_element_id == POSE_ROLL_ELEMENT_ID) {
2134 formatContext->poseRollV2 = (uint)mmfile_io_be_float32(pose.pose_roll_element_value);
2136 debug_error(DEBUG, "error: failed to pose roll element\n");
2137 ret = MMFILE_UTIL_FAIL;
2150 int MMFileUtilGetMetaDataFromMP4(MMFileFormatContext *formatContext)
2152 MMFileIOHandle *fp = NULL;
2155 unsigned long long chunk_size = 0;
2156 long long moov_end = 0;
2157 MMFILE_MP4_BASIC_BOX_HEADER basic_header = {0, };
2158 int junk_counter = 0;
2160 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
2161 if (ret == MMFILE_UTIL_FAIL) {
2162 debug_error(DEBUG, "error: mmfile_open\n");
2166 basic_header.start_offset = mmfile_tell(fp);
2168 if (g_junk_counter_limit == 0)
2169 g_junk_counter_limit = GetJunkCounterLimit();
2171 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)) {
2172 basic_header.size = mmfile_io_be_uint32(basic_header.size);
2173 basic_header.type = mmfile_io_le_uint32(basic_header.type);
2175 if (basic_header.size == 0) {
2176 debug_warning(DEBUG, "header is invalid.\n");
2177 basic_header.size = readed;
2178 basic_header.type = 0;
2179 chunk_size = basic_header.size;
2181 if ((moov_end != 0) && (moov_end < basic_header.start_offset)) {
2182 debug_msg(DEBUG, "found junk data but moov data already was extracted, so junk counter will be increase: %d", junk_counter);
2185 /* stop the loop for junk case. */
2186 if ((g_junk_counter_limit > 0) && (junk_counter > g_junk_counter_limit)) {
2187 debug_msg(DEBUG, "stop the loop by junk-data checker");
2188 ret = MMFILE_UTIL_FAIL;
2192 } else if (basic_header.size == 1) {
2194 unsigned char temp[BIG_CONTENT_BOX_SIZE_LEN] = {0, };
2195 unsigned long long size = 0;
2197 mmfile_read(fp, (unsigned char *)&temp, BIG_CONTENT_BOX_SIZE_LEN);
2199 for (i = 0; i < BIG_CONTENT_BOX_SIZE_LEN; i++)
2200 size |= (unsigned long long)temp[i] << (BIG_CONTENT_BOX_SIZE_LEN - 1 - i) * BIG_CONTENT_BOX_SIZE_LEN;
2204 chunk_size = basic_header.size;
2208 switch (basic_header.type) {
2209 case FOURCC('m', 'o', 'o', 'v'): {
2210 debug_msg(RELEASE, "MPEG4: [moov] SIZE: [%lld]Byte\n", chunk_size);
2211 moov_end = basic_header.start_offset + chunk_size;
2214 case FOURCC('u', 'd', 't', 'a'): {
2215 debug_msg(RELEASE, "MPEG4: [udat] SIZE: [%lld]Byte\n", chunk_size);
2218 /*/////////////////////////////////////////////////////////////// */
2219 /* Extracting Tag Data // */
2220 /*/////////////////////////////////////////////////////////////// */
2221 case FOURCC('t', 'i', 't', 'l'): {
2222 debug_msg(RELEASE, "MPEG4: [titl] SIZE: [%lld]Byte\n", chunk_size);
2223 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_TITLE);
2226 case FOURCC('d', 's', 'c', 'p'): {
2227 debug_msg(RELEASE, "MPEG4: [dscp] SIZE: [%lld]Byte\n", chunk_size);
2228 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_CAPTION);
2231 case FOURCC('c', 'p', 'r', 't'): {
2232 debug_msg(RELEASE, "MPEG4: [cprt] SIZE: [%lld]Byte\n", chunk_size);
2233 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_COPYRIGHT);
2236 case FOURCC('p', 'e', 'r', 'f'): {
2237 debug_msg(RELEASE, "MPEG4: [perf] SIZE: [%lld]Byte\n", chunk_size);
2238 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_PERFORMER);
2241 case FOURCC('a', 'u', 't', 'h'): {
2242 debug_msg(RELEASE, "MPEG4: [auth] SIZE: [%lld]Byte\n", chunk_size);
2243 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_AUTHOR);
2246 case FOURCC('g', 'n', 'r', 'e'): {
2247 debug_msg(RELEASE, "MPEG4: [gnre] SIZE: [%lld]Byte\n", chunk_size);
2248 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_GENRE);
2251 case FOURCC('a', 'l', 'b', 'm'): {
2252 debug_msg(RELEASE, "MPEG4: [albm] SIZE: [%lld]Byte\n", chunk_size);
2253 GetAlbumFromAlbumTagBox(formatContext, fp, &basic_header);
2256 case FOURCC('y', 'r', 'r', 'c'): {
2257 debug_msg(RELEASE, "MPEG4: [yrrc] SIZE: [%lld]Byte\n", chunk_size);
2258 GetYearFromYearTagBox(formatContext, fp, &basic_header);
2261 case FOURCC('r', 't', 'n', 'g'): {
2262 debug_msg(RELEASE, "MPEG4: [rtng] SIZE: [%lld]Byte\n", chunk_size);
2263 GetRatingFromRatingTagBox(formatContext, fp, &basic_header); /* not use */
2266 case FOURCC('c', 'l', 's', 'f'): {
2267 debug_msg(RELEASE, "MPEG4: [clsf] SIZE: [%lld]Byte\n", chunk_size);
2268 GetClassficationFromClsfTagBox(formatContext, fp, &basic_header);
2271 case FOURCC('k', 'y', 'w', 'd'): {
2272 debug_msg(RELEASE, "MPEG4: [kywd] SIZE: [%lld]Byte\n", chunk_size);
2273 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2276 case FOURCC('l', 'o', 'c', 'i'): {
2277 debug_msg(RELEASE, "MPEG4: [loci] SIZE: [%lld]Byte\n", chunk_size);
2278 GetLocationFromLociTagBox(formatContext, fp, &basic_header);
2281 /* Check smta in user data field (moov) to be compatible with android */
2282 case FOURCC('s', 'm', 't', 'a'): {
2283 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte\n", chunk_size);
2284 GetSAUTInfoFromSMTATagBox(formatContext, fp, &basic_header);
2287 /* Check cdis in user data field (moov) to be compatible with android */
2288 case FOURCC('c', 'd', 'i', 's'): {
2289 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte\n", chunk_size);
2290 GetValueFromCDISTagBox(formatContext, fp, &basic_header);
2293 /*/////////////////////////////////////////////////////////////// */
2294 /* Extracting ID3 Tag Data // */
2295 /*/////////////////////////////////////////////////////////////// */
2296 case FOURCC('m', 'e', 't', 'a'): {
2297 debug_msg(RELEASE, "MPEG4: [meta] SIZE: [%lld]Byte\n", chunk_size);
2298 GetTagFromMetaBox(formatContext, fp, &basic_header);
2302 case FOURCC('t', 'r', 'a', 'k'): {
2303 debug_msg(RELEASE, "MPEG4: [trak] SIZE: [%lld]Byte\n", chunk_size);
2306 case FOURCC('u', 'u', 'i', 'd'): {
2307 unsigned long uuid[4] = {0, };
2309 debug_msg(RELEASE, "MPEG4: [uuid] SIZE: [%lld]Byte\n", chunk_size);
2311 mmfile_read(fp, (unsigned char *)uuid, sizeof(uuid));
2313 if (mmfile_io_be_uint32(uuid[0]) == 0xffcc8263
2314 && mmfile_io_be_uint32(uuid[1]) == 0xf8554a93
2315 && mmfile_io_be_uint32(uuid[2]) == 0x8814587a
2316 && mmfile_io_be_uint32(uuid[3]) == 0x02521fdd) {
2318 str = (char *)malloc(basic_header.size);
2321 memset(str, 0, basic_header.size);
2322 mmfile_read(fp, (unsigned char *)str, basic_header.size);
2324 /* The block is superseded */
2325 if (strstr(str, "<GSpherical:Spherical>true</GSpherical:Spherical>"))
2326 formatContext->is_360 = 1;
2328 formatContext->is_360 = 0;
2329 /* Image can be stitched even if it is not spherical */
2330 if (formatContext->is_360 == 1) {
2331 if (strstr(str, "<GSpherical:Stitched>true</GSpherical:Stitched>"))
2332 formatContext->stitched = MMFILE_360_STITCHED;
2334 formatContext->stitched = MMFILE_360_NON_STITCHED;
2336 /* Image can be stitched or non-stitched. Usage of some 3rd value is superfluous */
2337 formatContext->stitched = MMFILE_360_NONE;
2341 debug_msg(RELEASE, "Extracting tags from UUID XML string %s\n", str);
2343 ParseSpatialVideoMetadataFromXMLString(str, formatContext);
2346 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2350 case FOURCC('m', 'd', 'i', 'a'): {
2351 debug_msg(RELEASE, "MPEG4: [mdia] SIZE: [%lld]Byte\n", chunk_size);
2354 case FOURCC('m', 'i', 'n', 'f'): {
2355 debug_msg(RELEASE, "MPEG4: [minf] SIZE: [%lld]Byte\n", chunk_size);
2358 case FOURCC('s', 't', 'b', 'l'): {
2359 debug_msg(RELEASE, "MPEG4: [stbl] SIZE: [%lld]Byte\n", chunk_size);
2362 case FOURCC('s', 't', 's', 'd'): {
2363 debug_msg(RELEASE, "MPEG4: [stsd] SIZE: [%lld]Byte\n", chunk_size);
2366 case FOURCC('m', 'p', '4', 'a'): {
2367 debug_msg(RELEASE, "MPEG4: [mp4a] SIZE: [%lld]Byte\n", chunk_size);
2368 GetSA3DInfoFromMP4ATagBox(formatContext, fp, &basic_header);
2369 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2372 case FOURCC('a', 'v', 'c', '1'): {
2373 debug_msg(RELEASE, "MPEG4: [avc1] SIZE: [%lld]Byte (offset: %lld)\n", chunk_size, basic_header.start_offset);
2374 GetVideoV2MetadataFromAvc1TagBox(formatContext, fp, &basic_header);
2378 debug_msg(RELEASE, "4CC: Not Support [%c%c%c%c]. So skip it. Size [%lld Byte]\n",
2379 ((char *)&basic_header.type)[0], ((char *)&basic_header.type)[1],
2380 ((char *)&basic_header.type)[2], ((char *)&basic_header.type)[3], chunk_size);
2381 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2386 if (ret == MMFILE_UTIL_FAIL) {
2387 debug_error(DEBUG, "mmfile operation is error\n");
2392 long long new_pos = mmfile_tell(fp);
2394 if ((moov_end == 0) && (new_pos <= basic_header.start_offset)) {
2395 debug_error(DEBUG, "Wrong position");
2396 ret = MMFILE_UTIL_FAIL;
2399 basic_header.start_offset = new_pos;
2408 static char *get_string(const char *buf, int buf_size, int *bytes_written)
2412 char str[512] = {0, };
2415 for (i = 0; i < buf_size; i++) {
2419 if ((q - str) >= (int)sizeof(str) - 1)
2425 if (strlen(str) > 0) {
2426 *bytes_written = strlen(str);
2434 char *rtrimN(char *pStr)
2437 pos = strlen(pStr) - 1;
2438 for (; pos >= 0; pos--) {
2439 if (pStr[pos] == 0x20) {
2446 return strdup(pStr);
2449 bool safe_atoi(char *buffer, int *si)
2455 const long sl = strtol(buffer, &end, 10);
2457 if (end == buffer) {
2458 debug_error(RELEASE, "not a decimal number");
2460 } else if ('\0' != *end) {
2461 debug_error(RELEASE, "extra characters at end of input: %s", end);
2463 } else if ((LONG_MIN == sl || LONG_MAX == sl) && (ERANGE == errno)) {
2464 debug_error(RELEASE, "out of range of type long");
2466 } else if (sl > INT_MAX) {
2467 debug_error(RELEASE, "greater than INT_MAX");
2469 } else if (sl < INT_MIN) {
2470 debug_error(RELEASE, "less than INT_MIN");
2478 static bool make_characterset_array(char ***charset_array)
2480 char *locale = MMFileUtilGetLocale();
2482 *charset_array = calloc(AV_ID3V2_MAX, sizeof(char *));
2484 if (*charset_array == NULL) {
2485 debug_error(DEBUG, "calloc failed ");
2491 if (locale != NULL) {
2492 (*charset_array)[AV_ID3V2_ISO_8859] = strdup(locale);
2494 debug_error(DEBUG, "get locale failed");
2495 (*charset_array)[AV_ID3V2_ISO_8859] = NULL;
2498 (*charset_array)[AV_ID3V2_UTF16] = strdup("UCS2");
2499 (*charset_array)[AV_ID3V2_UTF16_BE] = strdup("UTF16-BE");
2500 (*charset_array)[AV_ID3V2_UTF8] = strdup("UTF-8");
2505 static bool release_characterset_array(char **charset_array)
2509 for (i = 0; i < AV_ID3V2_MAX; i++) {
2510 if (charset_array[i] != NULL) {
2511 free(charset_array[i]);
2512 charset_array[i] = NULL;
2516 if (charset_array != NULL) {
2517 free(charset_array);
2518 charset_array = NULL;
2524 static void init_content_info(AvFileContentInfo *pInfo)
2528 for(i = 0; i < AV_ID3TAG_MAX; i++) {
2529 pInfo->tagInfo[i].length = 0;
2530 pInfo->tagInfo[i].value = NULL;
2531 pInfo->tagInfo[i].marked = false;
2534 pInfo->imageInfo.bURLInfo = false;
2535 pInfo->imageInfo.pImageBuf = NULL;
2536 pInfo->imageInfo.imageLen = 0;
2539 static void _mm_file_id3tag_add_bracket_at_genre(char **genre, int genre_len)
2543 if (!genre || !(*genre) || genre_len <= 0)
2546 if (!safe_atoi(*genre, &int_genre)) {
2547 debug_log(RELEASE, "genre information is not integer [%s]", *genre);
2551 debug_msg(RELEASE, "genre information is integer [%d]", int_genre);
2553 /* if the value is not kinds of genre, exit */
2554 if (int_genre < 0 || int_genre >= GENRE_COUNT)
2557 /* Change int to string with bracket like "(123)"
2558 * mm_file_id3tag_restore_content_info convert it to string
2560 mmfile_free(*genre);
2561 *genre = g_strdup_printf("(%d)", int_genre);
2564 static void __id3tag_skip_newline(unsigned char *pTagVal, int *nTagLen, int *offset)
2566 /* skip newline in text encoding of ID3 tag frame */
2567 while ((NEWLINE_OF_UTF16(pTagVal + *offset) || NEWLINE_OF_UTF16_R(pTagVal + *offset)) && *nTagLen > 4) {
2573 static int __id3tag_get_text_encoding_v222(unsigned char *pTagVal, int offset)
2575 if (pTagVal[offset - 1] == 0x00)
2576 return AV_ID3V2_ISO_8859;
2578 return AV_ID3V2_UTF16;
2581 static int __id3tag_get_text_encoding_v223(unsigned char *pTagVal, int *npTagLen, int nTextEnc, int *offset)
2583 if ((IS_ENCODEDBY_UTF16(pTagVal + *offset) || IS_ENCODEDBY_UTF16_R(pTagVal + *offset)) && *npTagLen > 2) {
2584 __id3tag_skip_newline(pTagVal, npTagLen, offset);
2586 if (IS_ENCODEDBY_UTF16(pTagVal + *offset) && (*npTagLen > 2)) {
2589 return AV_ID3V2_UTF16;
2590 } else if (IS_ENCODEDBY_UTF16_R(pTagVal + *offset) && (*npTagLen > 2)) {
2593 return AV_ID3V2_UTF16_BE;
2594 } else if (IS_ENCODEDBY_UTF16(pTagVal + *offset + 1) && (*npTagLen > 3)) {
2597 return AV_ID3V2_UTF16;
2598 } else if (IS_ENCODEDBY_UTF16_R(pTagVal + *offset + 1) && (*npTagLen > 3)) {
2601 return AV_ID3V2_UTF16_BE;
2603 debug_msg(RELEASE, "id3tag never get here!!\n");
2604 return nTextEnc; /* default bypass */
2607 while ((pTagVal[*offset] < 0x20) && (*offset < *npTagLen)) { /* text string encoded by ISO-8859-1 */
2611 return AV_ID3V2_ISO_8859;
2615 static int __id3tag_get_text_encoding_v224(unsigned char *pTagVal, int *npTagLen, int nTextEnc, int *offset)
2617 if (nTextEnc == AV_ID3V2_UTF16 || nTextEnc == AV_ID3V2_UTF16_BE) {
2618 __id3tag_skip_newline(pTagVal, npTagLen, offset);
2620 if ((IS_ENCODEDBY_UTF16(pTagVal + *offset) || IS_ENCODEDBY_UTF16_R(pTagVal + *offset)) && *npTagLen > 2) {
2623 return AV_ID3V2_UTF16;
2625 debug_msg(RELEASE, "id3tag never get here!!\n");
2626 return nTextEnc; /* default bypass */
2628 } else if (nTextEnc == AV_ID3V2_UTF8) {
2629 while (pTagVal[*offset] < 0x20 && (*offset < *npTagLen)) { /* text string encoded by UTF-8 */
2633 return AV_ID3V2_UTF8;
2635 while (pTagVal[*offset] < 0x20 && (*offset < *npTagLen)) { /* text string encoded by ISO-8859-1 */
2639 return AV_ID3V2_ISO_8859;
2643 static void __id3tag_parse_SYLT(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int textEncodingType, int offset)
2647 int copy_start_pos = offset;
2648 AvSynclyricsInfo *synclyrics_info = NULL;
2649 GList *synclyrics_info_list = NULL;
2651 if (nTagLen < MMFILE_SYNC_LYRIC_INFO_MIN_LEN) {
2652 debug_msg(RELEASE, "failed to get Synchronised lyrics Info realCpyFramNum(%d)\n", nTagLen);
2653 pInfo->tagInfo[AV_ID3TAG_SYNCLYRICS].length = 0;
2657 if ((textEncodingType == AV_ID3V2_UTF16) || (textEncodingType == AV_ID3V2_UTF16_BE)) {
2658 debug_warning(DEBUG, "[%d] not implemented\n", textEncodingType);
2662 for (idx = 0; idx < nTagLen; idx++) {
2663 if (pTagVal[offset + idx] == 0x00) {
2664 synclyrics_info = g_new0(AvSynclyricsInfo, 1);
2665 if (textEncodingType == AV_ID3V2_UTF8)
2666 synclyrics_info->lyric_info = g_memdup(pTagVal + copy_start_pos, copy_len + 1);
2668 synclyrics_info->lyric_info = mmfile_string_convert((const char *)&pTagVal[copy_start_pos], copy_len, "UTF-8", pCharSet, NULL, NULL);
2670 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];
2672 copy_start_pos = offset + idx + 1;
2673 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);
2675 synclyrics_info_list = g_list_append(synclyrics_info_list, synclyrics_info);
2680 pInfo->pSyncLyrics = synclyrics_info_list;
2681 pInfo->tagInfo[AV_ID3TAG_SYNCLYRICS].length = g_list_length(pInfo->pSyncLyrics);
2684 static bool __id3tag_parse_PIC_format(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2686 unsigned int idx = 0;
2688 /* get the mime type of Attached PICture, it is text string */
2690 if (pTagVal[*offset] == '\0') {
2691 debug_msg(RELEASE, "The picture format of PIC is not included\n");
2695 /* init ext variable */
2696 memset(pInfo->imageInfo.imageExt, 0, sizeof(pInfo->imageInfo.imageExt));
2698 while ((idx < MP3_ID3_IMAGE_EXT_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2699 pInfo->imageInfo.imageExt[idx] = pTagVal[idx];
2708 static bool __id3tag_parse_APIC_mimetype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2710 unsigned int idx = 0;
2711 const char *MIME_PRFIX = "image/";
2713 /* get the mime type of Attached PICture, it is text string */
2715 if (pTagVal[*offset] == '\0') {
2716 pInfo->imageInfo.imgMimetypeLen = 0;
2717 debug_msg(RELEASE, "The MIME type of APIC is not included\n");
2721 /* init mimetype variable */
2722 memset(pInfo->imageInfo.imageMIMEType, 0, sizeof(pInfo->imageInfo.imageMIMEType));
2724 while ((idx < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2725 pInfo->imageInfo.imageMIMEType[idx] = pTagVal[idx];
2728 pInfo->imageInfo.imgMimetypeLen = idx;
2732 if (strncmp(pInfo->imageInfo.imageMIMEType, MIME_PRFIX, strlen(MIME_PRFIX)) != 0) {
2733 pInfo->imageInfo.imgMimetypeLen = 0;
2734 debug_error(DEBUG, "MIME type(%s) is not image", pInfo->imageInfo.imageMIMEType);
2738 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2739 debug_msg(RELEASE, "pTagVal[offset](%d) mimetype is not NULL terminated! realCpyFrameNum - offset(%d)\n",
2740 pTagVal[*offset], nTagLen - *offset);
2744 (*offset)++;/* end of MIME('\0', 1byte) */
2745 debug_msg(RELEASE, "after scaning Mime type offset(%d) value!\n", *offset);
2750 static void __id3tag_parse_APIC_pictype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2752 /* get the picture type of Attached PICture, it is 1byte(0xff) */
2754 if (pTagVal[*offset] < AV_ID3V2_PICTURE_TYPE_MAX)
2755 pInfo->imageInfo.pictureType = pTagVal[*offset];
2757 debug_msg(RELEASE, "APIC image has invalid picture type(0x%x)\n", pTagVal[*offset]);
2759 (*offset)++;/* PictureType(1byte) */
2760 debug_msg(RELEASE, "after scaning PictureType(%d) offset(%d) value!\n", pInfo->imageInfo.pictureType, *offset);
2763 static void __id3tag_parse_APIC_desc(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int *offset)
2765 /* get the description of Attached PICture, it is text string */
2768 unsigned int tag_len = 0;
2769 unsigned int desc_len = 0;
2770 char *tmp_desc = NULL;
2772 if (pTagVal[*offset] == 0x0) {
2773 pInfo->imageInfo.imgDesLen = 0;
2774 debug_msg(RELEASE, "The description of APIC is not included!!!\n");
2779 if (pTagVal[*offset + idx] == '\0') {
2780 if (nTagLen < (*offset + idx)) {
2781 debug_error(DEBUG, "End of APIC Tag %d %d %d\n", nTagLen, *offset, idx);
2784 /* check end of image description */
2785 if ((pTagVal[*offset + idx + 1] == gTagJPEGHeader[0]) ||
2786 (pTagVal[*offset + idx + 1] == gTagPNGHeader[0])) {
2787 debug_msg(RELEASE, "length of description (%d)", idx);
2794 tag_len = idx + 1; /* length of description + '\0' */
2796 tmp_desc = g_memdup(pTagVal + *offset, tag_len);
2798 /* convert description */
2799 pInfo->imageInfo.imageDescription = mmfile_string_convert(tmp_desc, tag_len, "UTF-8", pCharSet, NULL, &desc_len);
2800 pInfo->imageInfo.imgDesLen = (int)desc_len;
2801 mmfile_free(tmp_desc);
2802 debug_msg(RELEASE, "new_desc %s(%d)\n", pInfo->imageInfo.imageDescription, pInfo->imageInfo.imgDesLen);
2805 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2806 debug_msg(RELEASE, "pTagVal[offset](%d) description is not NULL terminated! realCpyFrameNum - offset(%d)\n",
2807 pTagVal[*offset], nTagLen - *offset);
2810 (*offset)++; /* end of desceription(1byte) */
2813 static void __id3tag_parse_APIC_picture(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2815 /* get the picture of Attached PICture, it is binary data */
2817 /* some content has useless '\0' in front of picture data */
2818 while (pTagVal[*offset] == '\0') {
2822 debug_msg(RELEASE, "after scaning APIC description offset(%d) value!\n", *offset);
2824 if (nTagLen <= *offset) {
2825 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - offset(%d)\n", nTagLen, *offset);
2829 pInfo->imageInfo.imageLen = nTagLen - *offset;
2830 pInfo->imageInfo.pImageBuf = g_memdup(pTagVal + *offset, pInfo->imageInfo.imageLen);
2832 /* if mimetype is "-->", image date has an URL */
2833 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
2834 pInfo->imageInfo.bURLInfo = true;
2837 static bool _mm_file_id3tag_parse_PIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2839 /* current position to read pTagVal */
2842 debug_fenter(RELEASE);
2844 if (!__id3tag_parse_PIC_format(pInfo, pTagVal, &offset)) {
2845 debug_msg(RELEASE, "PIC is not valid\n");
2849 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2850 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2851 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2853 debug_fleave(RELEASE);
2858 static bool _mm_file_id3tag_parse_APIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2862 debug_fenter(RELEASE);
2864 if (!__id3tag_parse_APIC_mimetype(pInfo, pTagVal, nTagLen, &offset)) {
2865 debug_msg(RELEASE, "APIC is not valid\n");
2869 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2870 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2871 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2873 debug_fleave(RELEASE);
2879 bool mm_file_id3tag_parse_v110(AvFileContentInfo *pInfo, unsigned char *buffer)
2881 const char *locale = MMFileUtilGetLocale();
2882 char *pFullStr = NULL;
2884 debug_msg(RELEASE, "ID3tag v110--------------------------------------------------------------\n");
2886 if (pInfo->tagInfo[AV_ID3TAG_TITLE].marked == false) {
2887 pFullStr = mmfile_string_convert((const char *)&buffer[3], MP3_ID3_TITLE_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->tagInfo[AV_ID3TAG_TITLE].length);
2888 if (pFullStr != NULL) {
2889 pInfo->tagInfo[AV_ID3TAG_TITLE].value = rtrimN(pFullStr);
2893 debug_msg(RELEASE, "pInfo->pTitle returned =(%s), pInfo->titleLen(%d)\n", pInfo->tagInfo[AV_ID3TAG_TITLE].value, pInfo->tagInfo[AV_ID3TAG_TITLE].length);
2896 if (pInfo->tagInfo[AV_ID3TAG_ARTIST].marked == false) {
2897 pFullStr = mmfile_string_convert((const char *)&buffer[33], MP3_ID3_ARTIST_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->tagInfo[AV_ID3TAG_ARTIST].length);
2898 if (pFullStr != NULL) {
2899 pInfo->tagInfo[AV_ID3TAG_ARTIST].value = rtrimN(pFullStr);
2903 debug_msg(RELEASE, "pInfo->pArtist returned =(%s), pInfo->artistLen(%d)\n", pInfo->tagInfo[AV_ID3TAG_ARTIST].value, pInfo->tagInfo[AV_ID3TAG_ARTIST].length);
2906 if (pInfo->tagInfo[AV_ID3TAG_ALBUM].marked == false) {
2907 pFullStr = mmfile_string_convert((const char *)&buffer[63], MP3_ID3_ALBUM_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->tagInfo[AV_ID3TAG_ALBUM].length);
2908 if (pFullStr != NULL) {
2909 pInfo->tagInfo[AV_ID3TAG_ALBUM].value = rtrimN(pFullStr);
2913 debug_msg(RELEASE, "pInfo->pAlbum returned =(%s), pInfo->albumLen(%d)\n", pInfo->tagInfo[AV_ID3TAG_ALBUM].value, pInfo->tagInfo[AV_ID3TAG_ALBUM].length);
2916 if (pInfo->tagInfo[AV_ID3TAG_YEAR].marked == false) {
2918 pInfo->tagInfo[AV_ID3TAG_YEAR].value = mmfile_string_convert((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->tagInfo[AV_ID3TAG_YEAR].length);
2920 debug_msg(RELEASE, "pInfo->pYear returned =(%s), pInfo->yearLen(%d)\n", pInfo->tagInfo[AV_ID3TAG_YEAR].value, pInfo->tagInfo[AV_ID3TAG_YEAR].length);
2922 if (pInfo->tagInfo[AV_ID3TAG_YEAR].value == NULL) { /*Use same logic with ffmpeg*/
2923 pInfo->tagInfo[AV_ID3TAG_YEAR].value = get_string((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, (int *)&pInfo->tagInfo[AV_ID3TAG_YEAR].length);
2924 debug_msg(RELEASE, "pInfo->pYear returned =(%s), pInfo->yearLen(%d)\n", pInfo->tagInfo[AV_ID3TAG_YEAR].value, pInfo->tagInfo[AV_ID3TAG_YEAR].length);
2928 if (pInfo->tagInfo[AV_ID3TAG_COMMENT].marked == false) {
2929 pInfo->tagInfo[AV_ID3TAG_COMMENT].value = mmfile_string_convert((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->tagInfo[AV_ID3TAG_COMMENT].length);
2930 debug_msg(RELEASE, "pInfo->pComment returned =(%s), pInfo->commentLen(%d)\n", pInfo->tagInfo[AV_ID3TAG_COMMENT].value, pInfo->tagInfo[AV_ID3TAG_COMMENT].length);
2932 if (pInfo->tagInfo[AV_ID3TAG_COMMENT].value == NULL) { /*Use same logic with ffmpeg*/
2933 pInfo->tagInfo[AV_ID3TAG_COMMENT].value = get_string((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, (int *)&pInfo->tagInfo[AV_ID3TAG_COMMENT]);
2934 debug_msg(RELEASE, "pInfo->pComment returned =(%s), pInfo->commentLen(%d)\n", pInfo->tagInfo[AV_ID3TAG_COMMENT].value, pInfo->tagInfo[AV_ID3TAG_COMMENT].length);
2938 if (pInfo->tagInfo[AV_ID3TAG_TRACKNUM].marked == false) {
2939 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value = g_malloc0(ID3TAG_V110_TRACK_NUM_DIGIT);
2940 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value[ID3TAG_V110_TRACK_NUM_DIGIT - 1] = 0;
2941 snprintf(pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value, ID3TAG_V110_TRACK_NUM_DIGIT, "%04d", (int)buffer[126]);
2942 pInfo->tagInfo[AV_ID3TAG_TRACKNUM].length = strlen(pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value);
2944 debug_msg(RELEASE, "pInfo->pTrackNum returned =(%s), pInfo->tracknumLen(%d)\n", pInfo->tagInfo[AV_ID3TAG_TRACKNUM].value, pInfo->tagInfo[AV_ID3TAG_TRACKNUM].length);
2947 if (pInfo->tagInfo[AV_ID3TAG_GENRE].marked == false) {
2948 pInfo->genre = buffer[127];
2949 debug_msg(RELEASE, "pInfo->genre returned genre number (%d)\n", pInfo->genre);
2955 static AvID3TagList __get_tag_info_v222(const char *tag)
2961 n += tag[i++] - 'A' + 1;
2964 n += tag[2]; //num, char mixted
2966 for (i = 0; i < ID3TAG_NUM_V22; i++) {
2967 if (n == tag_info_v22[i].int_name) {
2968 return tag_info_v22[i].tag;
2972 debug_msg(RELEASE, "(%s) This Frame ID currently not Supports!!\n", tag);
2974 return AV_ID3TAG_MAX;
2977 static AvID3TagList __get_tag_info_v223(const char *tag)
2983 n += tag[i++] - 'A' + 1;
2986 n += tag[3]; //num, char mixted
2988 for (i = 0; i < ID3TAG_NUM_V23; i++) {
2989 if (n == tag_info_v23[i].int_name) {
2990 return tag_info_v23[i].tag;
2994 debug_msg(RELEASE, "(%s) This Frame ID currently not Supports!!\n", tag);
2996 return AV_ID3TAG_MAX;
3000 bool mm_file_id3tag_parse_v222(AvFileContentInfo *pInfo, unsigned char *buffer)
3002 unsigned long taglen = 0;
3003 unsigned long needToloopv2taglen;
3004 unsigned long oneFrameLen = 0;
3005 unsigned long curPos = 0;
3007 unsigned char *pExtContent = NULL;
3008 unsigned long purelyFramelen = 0;
3009 unsigned int encodingOffSet = 0;
3010 int realCpyFrameNum = 0, tmp = 0;
3011 int textEncodingType = 0;
3012 char **charset_array = NULL;
3013 AvID3TagList tag_id = AV_ID3TAG_MAX;
3015 make_characterset_array(&charset_array);
3017 init_content_info(pInfo);
3019 taglen = pInfo->tagV2Info.tagLen;
3020 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
3021 curPos = MP3_TAGv2_HEADER_LEN;
3023 debug_msg(RELEASE, "ID3tag v222--------------------------------------------------------------\n");
3025 while (needToloopv2taglen > MP3_TAGv2_22_TXT_HEADER_LEN) {
3026 if ((buffer[curPos] < '0' || buffer[curPos] > 'Z') || (buffer[curPos + 1] < '0' || buffer[curPos + 1] > 'Z')
3027 || (buffer[curPos + 2] < '0' || buffer[curPos + 2] > 'Z'))
3030 memcpy(CompTmp, &buffer[curPos], 3);
3033 oneFrameLen = MP3_TAGv2_22_TXT_HEADER_LEN;
3034 oneFrameLen += (unsigned long)buffer[3 + curPos] << 16 | (unsigned long)buffer[4 + curPos] << 8
3035 | (unsigned long)buffer[5 + curPos];
3037 if (oneFrameLen > taglen - curPos)
3040 purelyFramelen = oneFrameLen - MP3_TAGv2_22_TXT_HEADER_LEN;
3041 curPos += oneFrameLen;
3043 tag_id = __get_tag_info_v222(CompTmp);
3044 if (tag_id != AV_ID3TAG_MAX && !pInfo->tagInfo[tag_id].marked && purelyFramelen > 0) {
3045 if (buffer[curPos - purelyFramelen] == 0x00) {
3047 textEncodingType = AV_ID3V2_ISO_8859;
3048 } else if (buffer[curPos - purelyFramelen] == 0x01) {
3050 textEncodingType = AV_ID3V2_UTF16;
3053 /*in order to deliver valid string to MP */
3054 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen))
3057 if (purelyFramelen - encodingOffSet <= 0) {
3058 debug_warning(DEBUG, "warning: wrong frame length\n");
3062 realCpyFrameNum = purelyFramelen - encodingOffSet;
3063 mmfile_free(pExtContent);
3064 pExtContent = g_malloc0(realCpyFrameNum + 3);
3066 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
3069 case AV_ID3TAG_COMMENT:
3070 /*skip language data! */
3071 if (realCpyFrameNum > 4) {
3072 realCpyFrameNum -= 4;
3075 /*pExtContent[tmp+1] value should't have encoding value */
3076 if (pExtContent[tmp] > 0x20 && (pExtContent[tmp - 1] == 0x00 || pExtContent[tmp - 1] == 0x01)) {
3077 textEncodingType = __id3tag_get_text_encoding_v222(pExtContent, tmp);
3078 pInfo->tagInfo[tag_id].value = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tagInfo[tag_id].length);
3079 pInfo->tagInfo[tag_id].marked = true;
3081 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: failed to get Comment Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3084 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3089 if (realCpyFrameNum > 4) {
3090 /*skip language data! */
3091 realCpyFrameNum -= 4;
3094 /*pExtContent[tmp+1] value should't have null value */
3095 if (pExtContent[tmp] > 0x20 && (pExtContent[tmp - 1] == 0x00 || pExtContent[tmp - 1] == 0x01)) {
3096 textEncodingType = __id3tag_get_text_encoding_v222(pExtContent, tmp);
3098 pInfo->tagInfo[tag_id].value = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tagInfo[tag_id].length);
3099 pInfo->tagInfo[tag_id].marked = true;
3101 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: failed to get URL Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3104 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: URL info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3109 case AV_ID3TAG_PICTURE:
3110 if ((realCpyFrameNum <= 2000000) && _mm_file_id3tag_parse_PIC(pInfo, pExtContent, realCpyFrameNum, (const char*)charset_array[textEncodingType]))
3111 pInfo->tagInfo[tag_id].marked = true;
3115 pInfo->tagInfo[tag_id].value = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tagInfo[tag_id].length);
3116 pInfo->tagInfo[tag_id].marked = true;
3118 if (tag_id == AV_ID3TAG_GENRE)
3119 _mm_file_id3tag_add_bracket_at_genre(&pInfo->tagInfo[tag_id].value, pInfo->tagInfo[tag_id].length);
3123 if (pInfo->tagInfo[tag_id].value)
3124 debug_msg(RELEASE, "[%d] returned = (%s), len = (%d)\n", tag_id, pInfo->tagInfo[tag_id].value, pInfo->tagInfo[tag_id].length);
3127 mmfile_free(pExtContent);
3128 memset(CompTmp, 0, 4);
3130 if (curPos >= taglen)
3133 needToloopv2taglen -= oneFrameLen;
3137 realCpyFrameNum = 0;
3138 textEncodingType = 0;
3143 release_characterset_array(charset_array);
3153 bool mm_file_id3tag_parse_v223(AvFileContentInfo *pInfo, unsigned char *buffer)
3155 unsigned long taglen = 0;
3156 unsigned long needToloopv2taglen;
3157 unsigned long oneFrameLen = 0;
3158 unsigned long curPos = 0;
3160 unsigned char *pExtContent = NULL;
3161 unsigned long purelyFramelen = 0;
3162 unsigned int encodingOffSet = 0;
3163 int realCpyFrameNum = 0, tmp = 0;
3164 unsigned int textEncodingType = 0;
3165 char **charset_array = NULL;
3166 AvID3TagList tag_id = AV_ID3TAG_MAX;
3167 char *lang_info = NULL;
3169 make_characterset_array(&charset_array);
3171 init_content_info(pInfo);
3173 taglen = pInfo->tagV2Info.tagLen;
3174 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
3175 curPos = MP3_TAGv2_HEADER_LEN;
3177 debug_msg(RELEASE, "ID3tag v223--------------------------------------------------------------\n");
3179 /* check Extended Header */
3180 if (buffer[5] & 0x40) {
3181 /* if extended header exists, skip it*/
3182 int extendedHeaderLen = (unsigned long)buffer[10] << 21 | (unsigned long)buffer[11] << 14 | (unsigned long)buffer[12] << 7 | (unsigned long)buffer[13];
3184 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d\n", extendedHeaderLen);
3186 if (extendedHeaderLen > (int)(taglen - curPos)) {
3187 debug_error(DEBUG, "extended header too long.\n");
3189 curPos += extendedHeaderLen;
3194 while (needToloopv2taglen > MP3_TAGv2_23_TXT_HEADER_LEN) {
3195 if ((buffer[curPos] < '0' || buffer[curPos] > 'Z') || (buffer[curPos + 1] < '0' || buffer[curPos + 1] > 'Z')
3196 || (buffer[curPos + 2] < '0' || buffer[curPos + 2] > 'Z') || (buffer[curPos + 3] < '0' || buffer[curPos + 3] > 'Z'))
3199 memcpy(CompTmp, &buffer[curPos], 4);
3202 oneFrameLen = MP3_TAGv2_23_TXT_HEADER_LEN;
3203 oneFrameLen += (unsigned long)buffer[4 + curPos] << 24 | (unsigned long)buffer[5 + curPos] << 16
3204 | (unsigned long)buffer[6 + curPos] << 8 | (unsigned long)buffer[7 + curPos];
3206 debug_msg(RELEASE, "----------------------------------------------------------------------------------------------------\n");
3208 if (oneFrameLen > taglen - curPos)
3211 purelyFramelen = oneFrameLen - MP3_TAGv2_23_TXT_HEADER_LEN;
3212 curPos += oneFrameLen;
3214 tag_id = __get_tag_info_v223(CompTmp);
3215 if (tag_id != AV_ID3TAG_MAX && !pInfo->tagInfo[tag_id].marked && purelyFramelen > 0) {
3216 if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen))) {
3218 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3219 textEncodingType = AV_ID3V2_UTF16;
3220 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen))) {
3222 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3223 textEncodingType = AV_ID3V2_UTF16_BE;
3224 } else if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen + 1))) {
3226 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3227 textEncodingType = AV_ID3V2_UTF16;
3228 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen + 1))) {
3230 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3231 textEncodingType = AV_ID3V2_UTF16_BE;
3233 if (buffer[curPos - purelyFramelen + encodingOffSet] == 0x00) {
3234 debug_msg(RELEASE, "encodingOffset will be set to 1\n");
3237 debug_msg(RELEASE, "Finding encodingOffset\n");
3239 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen)) /* text string encoded by ISO-8859-1 */
3242 textEncodingType = AV_ID3V2_ISO_8859;
3243 debug_msg(RELEASE, "this text string(%s) encoded by ISO-8859-1 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3246 mmfile_free(pExtContent);
3248 if (encodingOffSet < purelyFramelen) {
3249 realCpyFrameNum = purelyFramelen - encodingOffSet;
3250 pExtContent = g_malloc0(realCpyFrameNum + 3);
3252 if (textEncodingType != AV_ID3V2_UTF16 && textEncodingType != AV_ID3V2_UTF16_BE) {
3253 if (CompTmp[0] == 'T' || (strcmp(CompTmp, "APIC") == 0)) {
3254 debug_msg(RELEASE, "get the new text ecoding type\n");
3255 textEncodingType = buffer[curPos - purelyFramelen + encodingOffSet - 1];
3259 if (textEncodingType > AV_ID3V2_MAX) {
3260 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%d], FRAME[%s]\n", textEncodingType, (char *)CompTmp);
3264 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
3265 if (realCpyFrameNum > 0) {
3267 case AV_ID3TAG_COMMENT:
3268 if (realCpyFrameNum > 3) {
3269 realCpyFrameNum -= 3;
3272 /*pExtContent[tmp+1] value should't have encoding value */
3273 if (pExtContent[tmp] == 0x00 || pExtContent[tmp] == 0xFF || pExtContent[tmp] == 0xFE) {
3274 textEncodingType = __id3tag_get_text_encoding_v223(pExtContent, &realCpyFrameNum, textEncodingType, &tmp);
3276 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3277 pInfo->tagInfo[tag_id].value = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tagInfo[tag_id].length);
3278 pInfo->tagInfo[tag_id].marked= true;
3280 debug_msg(RELEASE, "failed to get Comment Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3283 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3287 case AV_ID3TAG_SYNCLYRICS:
3288 if (realCpyFrameNum > 5) {
3289 realCpyFrameNum -= 5;
3292 /*pExtContent[tmp+1] value should't have encoding value */
3293 if (pExtContent[tmp] == 0x00 || pExtContent[tmp] == 0xFF || pExtContent[tmp] == 0xFE) {
3294 textEncodingType = __id3tag_get_text_encoding_v223(pExtContent, &realCpyFrameNum, textEncodingType, &tmp);
3296 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3298 __id3tag_parse_SYLT(pInfo, pExtContent, realCpyFrameNum, charset_array[textEncodingType], textEncodingType, tmp);
3299 pInfo->tagInfo[tag_id].marked= true;
3301 debug_msg(RELEASE, "failed to get Synchronised lyrics Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3304 debug_msg(RELEASE, "Synchronised lyrics too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3308 case AV_ID3TAG_UNSYNCLYRICS:
3309 lang_info = strndup((char *)pExtContent, 3);
3311 if (realCpyFrameNum > 3) {
3312 realCpyFrameNum -= 3;
3315 /*find start of lyrics */
3317 if (pExtContent[tmp] == 0x00) {
3318 if (pExtContent[tmp + 1] == 0x00) {
3319 realCpyFrameNum -= 2;
3329 /*pExtContent[tmp+1] value should't have encoding value */
3330 debug_msg(RELEASE, "tpExtContent[%d] %x\n", tmp, pExtContent[tmp]);
3332 if (pExtContent[tmp] == 0x00 || pExtContent[tmp] == 0xFF || pExtContent[tmp] == 0xFE) {
3333 textEncodingType = __id3tag_get_text_encoding_v223(pExtContent, &realCpyFrameNum, textEncodingType, &tmp);
3335 char *char_set = NULL;
3337 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3339 if (textEncodingType == AV_ID3V2_ISO_8859) {
3340 if (lang_info != NULL && !g_ascii_strcasecmp(lang_info, "KOR")) {
3341 char_set = strdup("EUC-KR");
3343 char_set = mmfile_get_charset((const char *)&pExtContent[tmp]);
3345 mmfile_free(lang_info);
3348 if (char_set == NULL) {
3349 pInfo->tagInfo[tag_id].value = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tagInfo[tag_id].length);
3351 pInfo->tagInfo[tag_id].value = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", char_set, NULL, (unsigned int *)&pInfo->tagInfo[tag_id].length);
3352 mmfile_free(char_set);
3354 pInfo->tagInfo[tag_id].marked= true;
3356 debug_msg(RELEASE, "failed to get Unsynchronised lyrics Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3359 debug_msg(RELEASE, "Unsynchronised lyrics too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3361 mmfile_free(lang_info);
3364 case AV_ID3TAG_PICTURE:
3365 if ((realCpyFrameNum <= 2000000) && _mm_file_id3tag_parse_APIC(pInfo, (unsigned char *)pExtContent, realCpyFrameNum, charset_array[textEncodingType]))
3366 pInfo->tagInfo[tag_id].marked= true;
3370 pInfo->tagInfo[tag_id].value = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tagInfo[tag_id].length);
3371 pInfo->tagInfo[tag_id].marked= true;
3375 if (pInfo->tagInfo[tag_id].value)
3376 debug_msg(RELEASE, "[%d] returned = (%s), len = (%d)\n", tag_id, pInfo->tagInfo[tag_id].value, pInfo->tagInfo[tag_id].length);
3380 debug_msg(RELEASE, "All of the pExtContent Values are NULL\n");
3384 mmfile_free(pExtContent);
3385 memset(CompTmp, 0, 4);
3387 if (curPos >= taglen)
3390 needToloopv2taglen -= oneFrameLen;
3394 realCpyFrameNum = 0;
3395 textEncodingType = 0;
3400 release_characterset_array(charset_array);
3410 bool mm_file_id3tag_parse_v224(AvFileContentInfo *pInfo, unsigned char *buffer)
3412 unsigned long taglen = 0;
3413 unsigned long needToloopv2taglen;
3414 unsigned long oneFrameLen = 0;
3415 unsigned long curPos = 0;
3417 unsigned char *pExtContent = NULL;
3418 unsigned long purelyFramelen = 0;
3419 unsigned int encodingOffSet = 0;
3420 int realCpyFrameNum = 0, tmp = 0;
3421 unsigned int textEncodingType = 0;
3422 char **charset_array = NULL;
3423 AvID3TagList tag_id = AV_ID3TAG_MAX;
3425 make_characterset_array(&charset_array);
3427 init_content_info(pInfo);
3429 taglen = pInfo->tagV2Info.tagLen;
3430 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
3431 curPos = MP3_TAGv2_HEADER_LEN;
3433 debug_msg(RELEASE, "ID3tag v224--------------------------------------------------------------\n");
3435 /* check Extended Header */
3436 if (buffer[5] & 0x40) {
3437 /* if extended header exists, skip it*/
3438 int extendedHeaderLen = (unsigned long)buffer[10] << 21 | (unsigned long)buffer[11] << 14 | (unsigned long)buffer[12] << 7 | (unsigned long)buffer[13];
3440 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d\n", extendedHeaderLen);
3442 if (extendedHeaderLen > (int)(taglen - curPos)) {
3443 debug_error(DEBUG, "extended header too long.\n");
3445 curPos += extendedHeaderLen;
3449 while (needToloopv2taglen > MP3_TAGv2_23_TXT_HEADER_LEN) {
3450 if ((buffer[curPos] < '0' || buffer[curPos] > 'Z') || (buffer[curPos + 1] < '0' || buffer[curPos + 1] > 'Z')
3451 || (buffer[curPos + 2] < '0' || buffer[curPos + 2] > 'Z') || (buffer[curPos + 3] < '0' || buffer[curPos + 3] > 'Z'))
3454 memcpy(CompTmp, &buffer[curPos], 4);
3457 oneFrameLen = MP3_TAGv2_23_TXT_HEADER_LEN;
3458 oneFrameLen += (unsigned long)buffer[4 + curPos] << 21 | (unsigned long)buffer[5 + curPos] << 14
3459 | (unsigned long)buffer[6 + curPos] << 7 | (unsigned long)buffer[7 + curPos];
3460 if (oneFrameLen > taglen - curPos)
3463 purelyFramelen = oneFrameLen - MP3_TAGv2_23_TXT_HEADER_LEN;
3464 curPos += oneFrameLen;
3466 debug_msg(RELEASE, "-----------------------------------------------------------------------------------\n");
3468 tag_id = __get_tag_info_v223(CompTmp);
3469 if (tag_id != AV_ID3TAG_MAX && !pInfo->tagInfo[tag_id].marked && purelyFramelen > 0) {
3470 /*in case of UTF 16 encoding */
3471 /*buffer+(curPos-purelyFramelen) data should '0x01' but in order to expansion, we don't accurately check the value. */
3472 if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen))) {
3474 textEncodingType = AV_ID3V2_UTF16;
3475 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen))) {
3477 textEncodingType = AV_ID3V2_UTF16_BE;
3478 } else if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen + 1))) {
3480 textEncodingType = AV_ID3V2_UTF16;
3481 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen + 1))) {
3483 textEncodingType = AV_ID3V2_UTF16_BE;
3485 /*in case of UTF-16 BE encoding */
3486 if (buffer[curPos - purelyFramelen] == 0x02) {
3488 while ((buffer[curPos - purelyFramelen + encodingOffSet] == '\0') && (encodingOffSet < purelyFramelen))
3489 encodingOffSet++;/*null skip! */
3490 textEncodingType = AV_ID3V2_UTF16_BE;
3492 /*in case of UTF8 encoding */
3493 else if (buffer[curPos - purelyFramelen] == 0x03) {
3495 while ((buffer[curPos - purelyFramelen + encodingOffSet] == '\0') && (encodingOffSet < purelyFramelen))
3496 encodingOffSet++;/*null skip! */
3497 textEncodingType = AV_ID3V2_UTF8;
3499 /*in case of ISO-8859-1 encoding */
3501 /*buffer+(curPos-purelyFramelen) data should 0x00 but in order to expansion, we don't accurately check the value. */
3503 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen))
3504 encodingOffSet++;/*less than 0x20 value skip! */
3505 textEncodingType = AV_ID3V2_ISO_8859;
3509 mmfile_free(pExtContent);
3511 if (encodingOffSet < purelyFramelen) {
3512 realCpyFrameNum = purelyFramelen - encodingOffSet;
3513 pExtContent = g_malloc0(realCpyFrameNum + 3);
3515 if (textEncodingType != AV_ID3V2_UTF16 && textEncodingType != AV_ID3V2_UTF16_BE) {
3516 if (CompTmp[0] == 'T' || (strcmp(CompTmp, "APIC") == 0)) {
3517 debug_msg(RELEASE, "get the new text ecoding type\n");
3518 textEncodingType = buffer[curPos - purelyFramelen + encodingOffSet - 1];
3522 if (textEncodingType > AV_ID3V2_MAX) {
3523 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%d], FRAME[%s]\n", textEncodingType, (char *)CompTmp);
3527 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
3529 if (realCpyFrameNum > 0) {
3531 case AV_ID3TAG_COMMENT:
3532 if (realCpyFrameNum > 3) {
3533 realCpyFrameNum -= 3;
3536 textEncodingType = __id3tag_get_text_encoding_v224(pExtContent, &realCpyFrameNum, textEncodingType, &tmp);
3537 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3539 pInfo->tagInfo[tag_id].value = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tagInfo[tag_id].length);
3540 pInfo->tagInfo[tag_id].marked= true;
3542 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3546 case AV_ID3TAG_SYNCLYRICS:
3547 if (realCpyFrameNum > 5) {
3548 realCpyFrameNum -= 5;
3551 textEncodingType = __id3tag_get_text_encoding_v224(pExtContent, &realCpyFrameNum, textEncodingType, &tmp);
3552 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3554 __id3tag_parse_SYLT(pInfo, pExtContent, realCpyFrameNum, charset_array[textEncodingType], textEncodingType, tmp);
3555 pInfo->tagInfo[tag_id].marked= true;
3557 debug_msg(RELEASE, "SyncLyrics info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3561 case AV_ID3TAG_UNSYNCLYRICS:
3562 if (realCpyFrameNum > 3) {
3563 realCpyFrameNum -= 3;
3566 textEncodingType = __id3tag_get_text_encoding_v224(pExtContent, &realCpyFrameNum, textEncodingType, &tmp);
3567 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3569 pInfo->tagInfo[tag_id].value = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tagInfo[tag_id].length);
3570 pInfo->tagInfo[tag_id].marked= true;
3572 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3576 case AV_ID3TAG_PICTURE:
3577 if ((realCpyFrameNum <= 2000000) && _mm_file_id3tag_parse_APIC(pInfo, (unsigned char *)pExtContent, realCpyFrameNum, charset_array[textEncodingType]))
3578 pInfo->tagInfo[tag_id].marked= true;
3582 pInfo->tagInfo[tag_id].value = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tagInfo[tag_id].length);
3583 pInfo->tagInfo[tag_id].marked= true;
3587 if (pInfo->tagInfo[tag_id].value)
3588 debug_msg(RELEASE, "[%d] returned = (%s), len = (%d)\n", tag_id, pInfo->tagInfo[tag_id].value, pInfo->tagInfo[tag_id].length);
3592 debug_msg(RELEASE, "mmf_file_id3tag_parse_v224: All of the pExtContent Values are NULL\n");
3597 mmfile_free(pExtContent);
3598 memset(CompTmp, 0, 4);
3600 if (curPos >= taglen)
3603 needToloopv2taglen -= oneFrameLen;
3607 realCpyFrameNum = 0;
3608 textEncodingType = 0;
3613 release_characterset_array(charset_array);
3623 void mm_file_id3tag_restore_content_info(AvFileContentInfo *pInfo)
3625 char *mpegAudioGenre = NULL/*, *tmpGenreForV1Tag = NULL*/;
3626 bool bAdditionGenre = false /*, bMpegAudioFrame = false*/;
3627 int mpegAudioFileLen = 0, idv2IntGenre = GENRE_COUNT - 1/*, tmpinx = 0, tmpinx2=0*/;
3629 /* for Genre Info */
3630 if (pInfo->tagInfo[AV_ID3TAG_GENRE].marked == false) {
3631 if (pInfo->bV1tagFound == true) {
3632 debug_msg(RELEASE, "Genre: %d\n", pInfo->genre);
3634 if (pInfo->genre > GENRE_COUNT - 1)
3635 pInfo->genre = GENRE_COUNT - 1;
3637 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(MpegAudio_Genre[pInfo->genre]);
3638 if (pInfo->tagInfo[AV_ID3TAG_GENRE].value)
3639 pInfo->tagInfo[AV_ID3TAG_GENRE].length = strlen(pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3641 debug_error(RELEASE, "Genre: memory allocation failed.\n");
3643 debug_msg(RELEASE, "Genre was not Found.\n");
3646 debug_msg(RELEASE, "genre size is Zero Or not UTF16 code! genreLen[%d] genre[%s]\n", pInfo->tagInfo[AV_ID3TAG_GENRE].length, pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3648 if (pInfo->tagInfo[AV_ID3TAG_GENRE].value)
3649 mpegAudioGenre = g_strdup(pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3651 pInfo->tagInfo[AV_ID3TAG_GENRE].length = 0;
3653 mmfile_free(pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3656 if (!mpegAudioGenre)
3661 * (XXX) XXX is 0 - 148
3663 pInfo->tagInfo[AV_ID3TAG_GENRE].length = strlen(mpegAudioGenre);
3664 if (pInfo->tagInfo[AV_ID3TAG_GENRE].length >= 3 &&
3665 mpegAudioGenre[0] == '(' && mpegAudioGenre[pInfo->tagInfo[AV_ID3TAG_GENRE].length - 1] == ')') {
3666 bAdditionGenre = true;
3667 for (mpegAudioFileLen = 1; mpegAudioFileLen <= pInfo->tagInfo[AV_ID3TAG_GENRE].length - 2; mpegAudioFileLen++) {
3668 if (mpegAudioGenre[mpegAudioFileLen] < '0' || mpegAudioGenre[mpegAudioFileLen] > '9') {
3669 bAdditionGenre = false;
3675 if (bAdditionGenre == true) {
3676 idv2IntGenre = atoi(mpegAudioGenre + 1);
3678 if (idv2IntGenre > GENRE_COUNT - 1 || idv2IntGenre < 0)
3679 idv2IntGenre = GENRE_COUNT - 1;
3681 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(MpegAudio_Genre[idv2IntGenre]);
3682 if (pInfo->tagInfo[AV_ID3TAG_GENRE].value)
3683 pInfo->tagInfo[AV_ID3TAG_GENRE].length = strlen(pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3685 debug_error(RELEASE, "Genre: memory allocation failed.\n");
3687 debug_msg(RELEASE, "pInfo->tagInfo[AV_ID3TAG_GENRE].value = %s\n", pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3688 } else if (bAdditionGenre == false && pInfo->tagInfo[AV_ID3TAG_GENRE].length > 0) {
3693 /* Give space for NULL character. Hence added "+1" */
3694 pInfo->tagInfo[AV_ID3TAG_GENRE].value = g_strdup(mpegAudioGenre);
3695 if (pInfo->tagInfo[AV_ID3TAG_GENRE].value)
3696 pInfo->tagInfo[AV_ID3TAG_GENRE].length = strlen(pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3698 debug_error(RELEASE, "Genre: memory allocation failed.\n");
3700 debug_msg(RELEASE, "pInfo->tagInfo[AV_ID3TAG_GENRE].value = %s, pInfo->tagInfo[AV_ID3TAG_GENRE].length = %d\n", pInfo->tagInfo[AV_ID3TAG_GENRE].value, pInfo->tagInfo[AV_ID3TAG_GENRE].length);
3702 debug_msg(RELEASE, "Failed to \"(...)\" value to genre = %s\n", pInfo->tagInfo[AV_ID3TAG_GENRE].value);
3704 mmfile_free(mpegAudioGenre);
3709 void mm_file_free_synclyrics_list(GList *synclyrics_list)
3713 AvSynclyricsInfo *synclyrics_info = NULL;
3715 if (synclyrics_list == NULL) {
3719 list_len = g_list_length(synclyrics_list);
3720 for (idx = 0; idx < list_len; idx++) {
3721 synclyrics_info = g_list_nth_data(synclyrics_list, idx);
3723 if (synclyrics_info != NULL) {
3724 mmfile_free(synclyrics_info->lyric_info);
3725 mmfile_free(synclyrics_info);
3729 if (synclyrics_list != NULL) {
3730 g_list_free(synclyrics_list);
3731 synclyrics_list = NULL;