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 typedef struct _mmfilemp4basicboxheader {
42 long long start_offset; /*huge file*/
43 } MMFILE_MP4_BASIC_BOX_HEADER;
45 typedef struct _mmfilemp4ftpybox {
46 unsigned int major_brand;
47 unsigned short major_version;
48 unsigned short minor_version;
49 unsigned int *compatiable_brands;
53 eMMFILE_3GP_TAG_TITLE = 0x01,
54 eMMFILE_3GP_TAG_CAPTION = 0x02,
55 eMMFILE_3GP_TAG_COPYRIGHT = 0x03,
56 eMMFILE_3GP_TAG_PERFORMER = 0x04,
57 eMMFILE_3GP_TAG_AUTHOR = 0x05,
58 eMMFILE_3GP_TAG_GENRE = 0x06,
59 } eMMFILE_3GP_TEXT_TAG;
61 typedef struct _mmfile3gptagtextbox {
62 unsigned char version;
63 unsigned char flag[3];
64 unsigned char language[2];
66 } MMFILE_3GP_TEXT_TAGBOX;
68 typedef struct _mmfile3gptagyearbox {
69 unsigned char version;
70 unsigned char flag[3];
72 } MMFILE_3GP_YEAR_TAGBOX;
74 typedef struct _mmfile3gptagalbumbox {
75 unsigned char version;
76 unsigned char flag[3];
77 unsigned char language[2];
78 unsigned char *albumtile;
79 unsigned char trackNumber;
80 } MMFILE_3GP_ALBUM_TAGBOX;
82 typedef struct _mmfile3gpratingbox {
83 unsigned char version;
84 unsigned char flag[3];
85 unsigned int ratingEntity;
86 unsigned int ratingCriteria;
87 unsigned char language[2];
88 unsigned char *ratingInfo;
89 } MMFILE_3GP_RATING_TAGBOX;
91 typedef struct _mmfile3gpclsfbox {
92 unsigned char version;
93 unsigned char flag[3];
94 unsigned int classificationEntity;
95 unsigned int classificationTable;
96 unsigned char language[2];
97 unsigned char *classificationInfo;
98 } MMFILE_3GP_CLASSIFICATION_TAGBOX;
100 typedef struct _mmfile3gphdlrbox {
101 unsigned int version;
102 unsigned int pre_defined;
103 unsigned int handler_type;
104 unsigned int reserved[3];
105 unsigned char *boxname;
106 } MMFILE_3GP_HANDLER_BOX;
108 typedef struct _mmfileidv2box {
109 unsigned char version;
110 unsigned char flag[3];
111 unsigned char language[2];
112 unsigned char *id3v2Data;
113 } MMFILE_3GP_ID3V2_BOX;
115 typedef struct _mmfilelocibox {
116 unsigned char version;
117 unsigned char flag[3];
118 unsigned char language[2];
124 unsigned char *astronomical_body;
125 unsigned char *additional_notes;
126 } MMFILE_3GP_LOCATION_TAGBOX;
128 typedef struct _mmfilesmtabox {
131 unsigned char saut[4];
133 } MMFILE_M4A_SMTA_TAGBOX;
135 typedef struct _mmfilesa3dbox {
137 uint8_t ambisonic_type;
138 uint32_t ambisonic_order;
139 uint8_t ambisonic_channel_ordering;
140 uint8_t ambisonic_normalization;
141 uint32_t num_channels;
142 uint32_t channel_map[49]; /* Up to 6th order */
143 } __attribute__((aligned(1), packed)) MMFILE_MP4A_SA3D_TAGBOX;
145 typedef struct _mmfile_proj_v2_box {
146 uint16_t proj_box_id;
147 uint8_t proj_box_size;
148 uint16_t proj_type_box_id;
149 uint8_t proj_type_box_size;
150 uint8_t proj_type_box_value; /* only equi (=1) or cubemap (=2) currently */
151 uint16_t proj_priv_box_id;
152 uint8_t proj_priv_box_size;
153 } __attribute__((aligned(1), packed)) MMFILE_WEBM_PROJ_V2_BOX;
155 typedef struct _mmfile_equi_proj_v2_box {
156 unsigned char proj_priv_box_name[4]; /* "equi" */
157 uint32_t equi_projection_bounds_top;
158 uint32_t equi_projection_bounds_bottom;
159 uint32_t equi_projection_bounds_left;
160 uint32_t equi_projection_bounds_right;
161 } __attribute__((aligned(1), packed)) MMFILE_WEBM_EQUI_PROJ_V2_BOX;
163 typedef struct _mmfile_cbmp_proj_v2_box {
164 unsigned char proj_priv_box_name[4]; /* "cbmp" */
165 uint32_t cbmp_projection_layout;
166 uint32_t cbmp_projection_padding;
167 } __attribute__((aligned(1), packed)) MMFILE_WEBM_CBMP_PROJ_V2_BOX;
169 typedef struct _mmfile_pose_element_v2_box {
170 uint16_t pose_yaw_element_id;
171 uint8_t pose_yaw_element_size;
172 float pose_yaw_element_value;
173 uint16_t pose_pitch_element_id;
174 uint8_t pose_pitch_element_size;
175 float pose_pitch_element_value;
176 uint16_t pose_roll_element_id;
177 uint8_t pose_roll_element_size;
178 float pose_roll_element_value;
179 } __attribute__((aligned(1), packed)) MMFILE_WEBM_POSE_ELEMENT_V2_BOX;
183 PROJECTION_TYPE_RECT = 0, /* Rectangular, Google's RFC value */
184 PROJECTION_TYPE_EQUI = 1, /* Equirectangular, Google's RFC value */
185 PROJECTION_TYPE_CBMP = 2, /* Cubemap, Google's RFC value */
186 PROJECTION_TYPE_MESH = 3, /* Mesh, Google's RFC value, unsupported */
187 PROJECTION_TYPE_UNKNOWN = INVALID_UINT_VALUE,
188 } SPHERICAL_VIDEO2_PROJECTION_TYPE;
190 enum spherical_video_metadata_elements_ids_le {
191 PROJ_BOX_ID = 0x7076,
192 PROJ_TYPE_BOX_ID = 0x7176,
193 PROJ_PRIV_BOX_ID = 0x7276,
194 POSE_YAW_ELEMENT_ID = 0x7376,
195 POSE_PITCH_ELEMENT_ID = 0x7476,
196 POSE_ROLL_ELEMENT_ID = 0x7576
199 #define MMFILE_MP4_BASIC_BOX_HEADER_LEN 8
200 #define MMFILE_MP4_MOVIE_HEADER_BOX_LEN 96
201 #define MMFILE_MP4_HDLR_BOX_LEN 24
202 #define MMFILE_MP4_STSZ_BOX_LEN 20
203 #define MMFILE_MP4_MP4VBOX_LEN 80
204 #define MMFILE_MP4_MP4ABOX_LEN 28
205 #define MMFILE_3GP_TEXT_TAGBOX_LEN 6
206 #define MMFILE_3GP_YEAR_TAGBOX_LEN 6
207 #define MMFILE_3GP_ALBUM_TAGBOX_LEN 6
208 #define MMFILE_3GP_RATING_TAGBOX_LEN 14
209 #define MMFILE_3GP_CLASS_TAGBOX_LEN 14
210 #define MMFILE_3GP_HANDLER_BOX_LEN 24
211 #define MMFILE_3GP_ID3V2_BOX_LEN 6
212 #define MMFILE_SYNC_LYRIC_INFO_MIN_LEN 5
215 #define FOURCC(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
217 /*#define MIN(a, b) (((a) < (b)) ? (a):(b))*/
219 #define GENRE_COUNT 149
221 static const char *MpegAudio_Genre[GENRE_COUNT] = {"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal",
222 "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial",
223 "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
224 "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise",
225 "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic",
226 "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
227 "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes",
228 "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock",
229 "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass",
230 "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic",
231 "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove",
232 "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
233 "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore",
234 "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian", "Heavy Metal", "Black Metal", "Crossover",
235 "Contemporary", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", "Synthpop", "Unknown"
238 static unsigned char gTagJPEGHeader[] = {0xff, 0xd8, 0xff };
239 static unsigned char gTagPNGHeader[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
241 static int GetJunkCounterLimit(void);
243 static int GetStringFromTextTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header, eMMFILE_3GP_TEXT_TAG eTag)
245 int ret = MMFILE_UTIL_FAIL; /*fail*/
246 MMFILE_3GP_TEXT_TAGBOX texttag = {0, };
249 char *temp_text = NULL;
251 if (!formatContext || !fp || !basic_header) {
252 debug_error(DEBUG, "invalid param\n");
253 return MMFILE_UTIL_FAIL;
256 textBytes = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_TEXT_TAGBOX_LEN;
258 readed = mmfile_read(fp, (unsigned char *)&texttag, MMFILE_3GP_TEXT_TAGBOX_LEN);
259 if (readed != MMFILE_3GP_TEXT_TAGBOX_LEN) {
260 debug_error(DEBUG, "read text tag header fail\n");
261 ret = MMFILE_UTIL_FAIL;
265 if (textBytes <= 1) { /* there exist only 00(null) */
266 debug_error(DEBUG, "Text is NULL\n");
270 texttag.text = mmfile_malloc(textBytes);
272 debug_error(DEBUG, "malloc fail for text box\n");
273 ret = MMFILE_UTIL_FAIL;
277 readed = mmfile_read(fp, (unsigned char *)texttag.text, textBytes);
278 if (readed != textBytes) {
279 debug_error(DEBUG, "read text fail\n");
280 ret = MMFILE_UTIL_FAIL;
285 if ((texttag.text[0] == 0xFE) && (texttag.text[1] == 0xFF)) {
286 /* this char is UTF-16 */
287 unsigned int bytes_written = 0;
288 temp_text = mmfile_string_convert((const char *)&texttag.text[2], readed - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
290 temp_text = mmfile_strdup((const char *)texttag.text);
294 case eMMFILE_3GP_TAG_TITLE: {
295 if (!formatContext->title) {
296 formatContext->title = g_strdup(temp_text);
300 case eMMFILE_3GP_TAG_CAPTION: {
301 if (!formatContext->description) {
302 formatContext->description = g_strdup(temp_text);
306 case eMMFILE_3GP_TAG_COPYRIGHT: {
307 if (!formatContext->copyright) {
308 formatContext->copyright = g_strdup(temp_text);
312 case eMMFILE_3GP_TAG_PERFORMER: {
313 if (!formatContext->artist) {
314 formatContext->artist = g_strdup(temp_text);
318 case eMMFILE_3GP_TAG_AUTHOR: {
319 if (!formatContext->author) {
320 formatContext->author = g_strdup(temp_text);
324 case eMMFILE_3GP_TAG_GENRE: {
325 if (!formatContext->genre) {
326 formatContext->genre = g_strdup(temp_text);
331 debug_warning(DEBUG, "Not supported Text Tag type[%d]\n", eTag);
336 mmfile_free(texttag.text);
338 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
340 mmfile_free(temp_text);
342 return MMFILE_UTIL_SUCCESS;
345 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
346 mmfile_free(texttag.text);
351 static int GetYearFromYearTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
353 #define MAX_YEAR_BUFFER 10
355 MMFILE_3GP_YEAR_TAGBOX yearbox = {0, };
356 char temp_year[MAX_YEAR_BUFFER] = {0, };
358 if (!formatContext || !fp || !basic_header) {
359 debug_error(DEBUG, "invalid param\n");
360 return MMFILE_UTIL_FAIL;
363 readed = mmfile_read(fp, (unsigned char *)&yearbox, MMFILE_3GP_YEAR_TAGBOX_LEN);
364 if (readed != MMFILE_3GP_YEAR_TAGBOX_LEN) {
365 debug_error(DEBUG, "read yeartag header fail\n");
369 if (!formatContext->year) {
370 yearbox.year = mmfile_io_be_int16(yearbox.year);
371 snprintf(temp_year, MAX_YEAR_BUFFER, "%d", yearbox.year);
372 temp_year[MAX_YEAR_BUFFER - 1] = '\0';
373 formatContext->year = mmfile_strdup((const char *)temp_year);
376 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
377 return MMFILE_UTIL_SUCCESS;
380 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
382 return MMFILE_UTIL_FAIL;
385 static int GetAlbumFromAlbumTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
387 int albumTitleLen = 0;
388 char *temp_text = NULL;
391 MMFILE_3GP_ALBUM_TAGBOX albumbox = {0, };
393 if (!formatContext || !fp || !basic_header) {
394 debug_error(DEBUG, "invalid param\n");
395 return MMFILE_UTIL_FAIL;
398 readed = mmfile_read(fp, (unsigned char *)&albumbox, MMFILE_3GP_ALBUM_TAGBOX_LEN);
399 if (readed != MMFILE_3GP_ALBUM_TAGBOX_LEN) {
400 debug_error(DEBUG, "read albumtag header fail\n");
404 albumTitleLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_ALBUM_TAGBOX_LEN - 1; /* 1: track number */
405 if (albumTitleLen > 1) { /* there exist only 00(null) */
406 debug_msg(RELEASE, "albumTitleLen=%d\n", albumTitleLen);
408 albumbox.albumtile = mmfile_malloc(albumTitleLen + 1); /* 1: for null char */
409 if (!albumbox.albumtile) {
410 debug_error(DEBUG, "malloc fail for album title text\n");
414 readed = mmfile_read(fp, (unsigned char *)albumbox.albumtile, albumTitleLen);
415 if (readed != albumTitleLen) {
416 debug_error(DEBUG, "read album title fail\n");
420 if (albumbox.albumtile[albumTitleLen - 1] == '\0') { /* there exist track number */
424 readed = mmfile_read(fp, (unsigned char *)&(albumbox.albumtile[albumTitleLen]), 1);
426 debug_error(DEBUG, "read album title fail\n");
429 albumbox.albumtile[albumTitleLen] = '\0';
433 if ((albumbox.albumtile[0] == 0xFE) && (albumbox.albumtile[1] == 0xFF)) {
434 /* this char is UTF-16 */
435 unsigned int bytes_written = 0;
436 temp_text = mmfile_string_convert((const char *)&albumbox.albumtile[2], readed - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
438 temp_text = mmfile_strdup((const char *)albumbox.albumtile);
441 if (!formatContext->album)
442 formatContext->album = temp_text;
444 mmfile_free(temp_text);
446 debug_msg(RELEASE, "formatContext->album=%s, strlen=%zu\n", formatContext->album, strlen(formatContext->album));
450 readed = mmfile_read(fp, (unsigned char *)&albumbox.trackNumber, 1);
452 debug_error(DEBUG, "read track number fail\n");
456 if (formatContext->tagTrackNum == 0) {
457 char tracknum[10] = {0, };
458 snprintf(tracknum, 10, "%d", albumbox.trackNumber);
460 formatContext->tagTrackNum = mmfile_strdup((const char *)tracknum);
464 mmfile_free(albumbox.albumtile);
465 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
467 return MMFILE_UTIL_SUCCESS;
470 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
471 mmfile_free(albumbox.albumtile);
473 return MMFILE_UTIL_FAIL;
476 static int GetRatingFromRatingTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
479 int ratinginfoLen = 0;
480 char *temp_text = NULL;
482 MMFILE_3GP_RATING_TAGBOX ratingTag = {0, };
484 if (!formatContext || !fp || !basic_header) {
485 debug_error(DEBUG, "invalid param\n");
486 return MMFILE_UTIL_FAIL;
489 readed = mmfile_read(fp, (unsigned char *)&ratingTag, MMFILE_3GP_RATING_TAGBOX_LEN);
490 if (readed != MMFILE_3GP_RATING_TAGBOX_LEN) {
491 debug_error(DEBUG, "read rating tag header fail\n");
495 ratinginfoLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_RATING_TAGBOX_LEN;
497 if (ratinginfoLen == 1) {
498 debug_error(DEBUG, "Rating Text is NULL\n");
502 ratingTag.ratingInfo = mmfile_malloc(ratinginfoLen);
503 if (!ratingTag.ratingInfo) {
504 debug_error(DEBUG, "rating info error\n");
508 readed = mmfile_read(fp, (unsigned char *)ratingTag.ratingInfo, ratinginfoLen);
509 if (readed != ratinginfoLen) {
510 debug_error(DEBUG, "read rating info string fail\n");
515 if ((ratingTag.ratingInfo[0] == 0xFE) && (ratingTag.ratingInfo[1] == 0xFF)) {
516 /* this char is UTF-16 */
517 unsigned int bytes_written = 0;
518 temp_text = mmfile_string_convert((const char *)&ratingTag.ratingInfo[2], readed - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
520 temp_text = mmfile_strdup((const char *)ratingTag.ratingInfo);
523 if (!formatContext->rating) {
524 formatContext->rating = temp_text;
526 mmfile_free(temp_text);
529 mmfile_free(ratingTag.ratingInfo);
530 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
532 return MMFILE_UTIL_SUCCESS;
535 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
536 mmfile_free(ratingTag.ratingInfo);
538 return MMFILE_UTIL_FAIL;
542 static int GetClassficationFromClsfTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
544 int classinfoLen = 0;
546 char *temp_text = NULL;
547 MMFILE_3GP_CLASSIFICATION_TAGBOX classTag = {0, };
549 if (!formatContext || !fp || !basic_header) {
550 debug_error(DEBUG, "invalid param\n");
551 return MMFILE_UTIL_FAIL;
554 readed = mmfile_read(fp, (unsigned char *)&classTag, MMFILE_3GP_CLASS_TAGBOX_LEN);
555 if (readed != MMFILE_3GP_CLASS_TAGBOX_LEN) {
556 debug_error(DEBUG, "read classification tag header fail\n");
561 classinfoLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_CLASS_TAGBOX_LEN;
562 if (classinfoLen == 1) {
563 debug_error(DEBUG, "Classification Text is NULL\n");
567 classTag.classificationInfo = mmfile_malloc(classinfoLen);
568 if (!classTag.classificationInfo) {
569 debug_error(DEBUG, "class info error\n");
573 readed = mmfile_read(fp, (unsigned char *)classTag.classificationInfo, classinfoLen);
574 if (readed != classinfoLen) {
575 debug_error(DEBUG, "read class info string fail\n");
580 if ((classTag.classificationInfo[0] == 0xFE) && (classTag.classificationInfo[1] == 0xFF)) {
581 /* this char is UTF-16 */
582 unsigned int bytes_written = 0;
583 temp_text = mmfile_string_convert((const char *)&classTag.classificationInfo[2], readed - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
585 temp_text = mmfile_strdup((const char *)classTag.classificationInfo);
588 if (!formatContext->classification) {
589 formatContext->classification = temp_text;
591 mmfile_free(temp_text);
594 mmfile_free(classTag.classificationInfo);
595 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
597 return MMFILE_UTIL_SUCCESS;
600 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
601 mmfile_free(classTag.classificationInfo);
603 return MMFILE_UTIL_FAIL;
607 * The Location Information box
608 * --------------------+-------------------+-----------------------------------+------
609 * Field Type Details Value
610 * --------------------+-------------------+-----------------------------------+------
611 * BoxHeader.Size Unsigned int(32)
612 * BoxHeader.Type Unsigned int(32) 'loci'
613 * BoxHeader.Version Unsigned int(8) 0
614 * BoxHeader.Flags Bit(24) 0
616 * Language Unsigned int(5)[3] Packed ISO-639-2/T language code
617 * Name String Text of place name
618 * Role Unsigned int(8) Non-negative value indicating role
620 * Longitude Unsigned int(32) Fixed-point value of the longitude
621 * Latitude Unsigned int(32) Fixed-point value of the latitude
622 * Altitude Unsigned int(32) Fixed-point value of the Altitude
623 * Astronomical_body String Text of astronomical body
624 * Additional_notes String Text of additional location-related
626 * --------------------+-------------------+-----------------------------------+------
628 static int _get_char_position(unsigned char *src, char ch, int max)
631 for (i = 0; i < max; i++) {
632 if (*(src + i) == ch)
639 static int GetLocationFromLociTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
642 MMFILE_3GP_LOCATION_TAGBOX lociTag = {0, };
645 unsigned char *buffer = NULL;
646 unsigned char *p = NULL;
648 unsigned int bytes_written = 0;
649 unsigned int name_sz = 0;
650 unsigned int astro_sz = 0;
652 int ilong, ilati, ialti;
653 float flong, flati, falti;
656 if (!formatContext || !fp || !basic_header) {
657 debug_error(DEBUG, "invalid param\n");
658 return MMFILE_UTIL_FAIL;
661 readed = mmfile_read(fp, (unsigned char *)&lociTag, 6); /*6 = version + flag + pad + language */
663 debug_error(DEBUG, "read location tag header fail\n");
667 /*buffer len = name + role + ... + additional notes length */
668 bufferLen = basic_header->size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - 6;
670 debug_error(DEBUG, "too small buffer\n");
674 buffer = mmfile_malloc(bufferLen);
676 debug_error(DEBUG, "buffer malloc error\n");
680 readed = mmfile_read(fp, (unsigned char *)buffer, bufferLen);
681 if (readed != bufferLen) {
682 debug_error(DEBUG, "read location tag fail\n");
688 pos = _get_char_position(p, '\0', readed - (1 + 4 + 4 + 4 + 2));
690 if (p[0] == 0xFE && p[1] == 0xFF) {
691 lociTag.name = (unsigned char *)mmfile_string_convert((const char *)(p + 2), pos - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
693 lociTag.name = (unsigned char *)mmfile_strdup((const char *)p);
705 debug_msg(RELEASE, "long: 0x%02X 0x%02X 0x%02X 0x%02X \n", *(p + 0), *(p + 1), *(p + 2), *(p + 3));
706 debug_msg(RELEASE, "lati: 0x%02X 0x%02X 0x%02X 0x%02X \n", *(p + 4), *(p + 5), *(p + 6), *(p + 7));
707 debug_msg(RELEASE, "alti: 0x%02X 0x%02X 0x%02X 0x%02X \n", *(p + 8), *(p + 9), *(p + 10), *(p + 11));
709 ilong = mmfile_io_be_uint32(*(unsigned int *)p);
710 ilati = mmfile_io_be_uint32(*(unsigned int *)(p + 4));
711 ialti = mmfile_io_be_uint32(*(unsigned int *)(p + 8));
713 flong = (float)ilong / (1 << 16);
714 flati = (float)ilati / (1 << 16);
715 falti = (float)ialti / (1 << 16);
718 lociTag.longitude = flong;
720 lociTag.latitude = flati;
722 lociTag.altitude = falti;
726 /*astronomical body*/
727 pos = _get_char_position(p, '\0', readed - (name_sz + 1 + 4 + 4 + 4 + 1));
729 if (p[0] == 0xFE && p[1] == 0xFF) {
730 lociTag.astronomical_body = (unsigned char *)mmfile_string_convert((const char *)(p + 2), pos - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
732 lociTag.astronomical_body = (unsigned char *)mmfile_strdup((const char *)p);
741 pos = _get_char_position(p, '\0', readed - (name_sz + 1 + 4 + 4 + 4 + astro_sz));
743 if (p[0] == 0xFE && p[1] == 0xFF) {
744 lociTag.additional_notes = (unsigned char *)mmfile_string_convert((const char *)(p + 2), pos - 2, "UTF-8", "UTF-16", NULL, (unsigned int *)&bytes_written);
746 lociTag.additional_notes = (unsigned char *)mmfile_strdup((const char *)p);
752 debug_msg(RELEASE, "** Location Information **\n");
753 debug_msg(RELEASE, "Name : %s\n", lociTag.name);
754 debug_msg(RELEASE, "Role : %d (0: shooting, 1: real, 2: fictional, other: reserved)\n", lociTag.role);
755 debug_msg(RELEASE, "Longitude : %16.16f\n", lociTag.longitude);
756 debug_msg(RELEASE, "Latitude : %16.16f\n", lociTag.latitude);
757 debug_msg(RELEASE, "Altitude : %16.16f\n", lociTag.altitude);
758 debug_msg(RELEASE, "Astronomical body: %s\n", lociTag.astronomical_body);
759 debug_msg(RELEASE, "Additional notes : %s\n", lociTag.additional_notes);
761 formatContext->longitude = lociTag.longitude;
762 formatContext->latitude = lociTag.latitude;
763 formatContext->altitude = lociTag.altitude;
766 mmfile_free(lociTag.name);
767 mmfile_free(lociTag.astronomical_body);
768 mmfile_free(lociTag.additional_notes);
770 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
771 return MMFILE_UTIL_SUCCESS;
774 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
776 mmfile_free(lociTag.name);
777 mmfile_free(lociTag.astronomical_body);
778 mmfile_free(lociTag.additional_notes);
780 return MMFILE_UTIL_FAIL;
783 static int GetSAUTInfoFromSMTATagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
785 MMFILE_M4A_SMTA_TAGBOX smtaTag = {0, };
788 if (!formatContext || !fp || !basic_header) {
789 debug_error(DEBUG, "invalid param\n");
790 return MMFILE_UTIL_FAIL;
793 readed = mmfile_read(fp, (unsigned char *)&smtaTag, sizeof(MMFILE_M4A_SMTA_TAGBOX));
794 if (readed != sizeof(MMFILE_M4A_SMTA_TAGBOX)) {
795 debug_error(DEBUG, "read smta tag header fail\n");
799 smtaTag.length = mmfile_io_be_uint32(smtaTag.length);
800 smtaTag.value = mmfile_io_be_uint32(smtaTag.value);
802 debug_msg(RELEASE, "Len : 0x%x", smtaTag.length);
803 debug_msg(RELEASE, "Saut : 0x%x 0x%x 0x%x 0x%x", smtaTag.saut[0], smtaTag.saut[1], smtaTag.saut[2], smtaTag.saut[3]);
804 debug_msg(RELEASE, "Value : 0x%x", smtaTag.value);
806 if (smtaTag.saut[0] == 's'
807 && smtaTag.saut[1] == 'a'
808 && smtaTag.saut[2] == 'u'
809 && smtaTag.saut[3] == 't') {
810 if (smtaTag.value == 0x01) {
811 debug_msg(RELEASE, "This has saut tag and valid value");
812 formatContext->smta = 1;
814 debug_error(DEBUG, "This has saut tag and but invalid value");
818 debug_error(DEBUG, "This hasn't saut tag and valid value");
822 return MMFILE_UTIL_SUCCESS;
825 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
827 return MMFILE_UTIL_FAIL;
830 static int GetSA3DInfoFromMP4ATagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
832 if (!formatContext || !fp || !basic_header) {
833 debug_error(DEBUG, "invalid param\n");
834 return MMFILE_UTIL_FAIL;
837 unsigned char *buffer;
839 bool is_SA3D_present = false;
841 MMFILE_MP4A_SA3D_TAGBOX sa3dTag = {0, };
843 formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_UNKNOWN;
844 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_UNKNOWN;
845 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_UNKNOWN;
847 mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
849 buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
851 debug_error(DEBUG, "calloc failed ");
855 readed = mmfile_read(fp, buffer, basic_header->size);
856 if (readed != (int)basic_header->size) {
857 debug_error(DEBUG, "read mp4a box failed\n");
861 for (i = 0; i + 3 < basic_header->size; ++i)
862 if (buffer[i] == 'S' && buffer[i + 1] == 'A' && buffer[i + 2] == '3' && buffer[i + 3] == 'D') {
863 debug_warning(DEBUG, "SA3D data found at offset %d bytes\n", i);
864 is_SA3D_present = true;
868 if (!is_SA3D_present) {
869 debug_warning(DEBUG, "No SA3D box found");
873 mmfile_seek(fp, basic_header->start_offset + i + 4, SEEK_SET);
875 readed = mmfile_read(fp, (unsigned char *)&sa3dTag, sizeof(MMFILE_MP4A_SA3D_TAGBOX));
876 if (readed != sizeof(MMFILE_MP4A_SA3D_TAGBOX)) {
877 debug_error(DEBUG, "read SA3D tag header fail\n");
881 sa3dTag.ambisonic_order = mmfile_io_be_uint32(sa3dTag.ambisonic_order);
882 sa3dTag.num_channels = mmfile_io_be_uint32(sa3dTag.num_channels);
883 for (i = 0; i < sa3dTag.num_channels; ++i)
884 sa3dTag.channel_map[i] = mmfile_io_be_uint32(sa3dTag.channel_map[i]);
886 debug_msg(RELEASE, "sa3dTag.version = %d", sa3dTag.version);
887 debug_msg(RELEASE, "sa3dTag.ambisonic_type = %d", sa3dTag.ambisonic_type);
888 debug_msg(RELEASE, "sa3dTag.ambisonic_order = %d", sa3dTag.ambisonic_order);
889 debug_msg(RELEASE, "sa3dTag.ambisonic_channel_ordering = %d", sa3dTag.ambisonic_channel_ordering);
890 debug_msg(RELEASE, "sa3dTag.ambisonic_normalization = %d", sa3dTag.ambisonic_normalization);
891 debug_msg(RELEASE, "sa3dTag.num_channels = %d", sa3dTag.num_channels);
892 for (i = 0; i < sa3dTag.num_channels; ++i)
893 debug_msg(RELEASE, "sa3dTag.channel_map[%d] = %d", i, sa3dTag.channel_map[i]);
895 if (sa3dTag.version != RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
896 debug_error(DEBUG, "SA3D tag box version is unsupported\n");
899 if (sa3dTag.ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
900 formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_PERIPHONIC;
902 switch (sa3dTag.ambisonic_order) {
903 case MMFILE_AMBISONIC_ORDER_FOA: {
904 if (sa3dTag.num_channels == 4) {
905 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_FOA;
907 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
908 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
909 (sa3dTag.channel_map[0] == 0) &&
910 (sa3dTag.channel_map[1] == 1) &&
911 (sa3dTag.channel_map[2] == 2) &&
912 (sa3dTag.channel_map[3] == 3))
913 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
915 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
916 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
917 (sa3dTag.channel_map[0] == 0) &&
918 (sa3dTag.channel_map[1] == 3) &&
919 (sa3dTag.channel_map[2] == 1) &&
920 (sa3dTag.channel_map[3] == 2))
921 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
923 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond\n");
929 case MMFILE_AMBISONIC_ORDER_SOA: {
930 if (sa3dTag.num_channels == 9) {
931 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_SOA;
933 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
934 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
935 (sa3dTag.channel_map[0] == 0) &&
936 (sa3dTag.channel_map[1] == 1) &&
937 (sa3dTag.channel_map[2] == 2) &&
938 (sa3dTag.channel_map[3] == 3) &&
939 (sa3dTag.channel_map[4] == 4) &&
940 (sa3dTag.channel_map[5] == 5) &&
941 (sa3dTag.channel_map[6] == 6) &&
942 (sa3dTag.channel_map[7] == 7) &&
943 (sa3dTag.channel_map[8] == 8))
944 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
946 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
947 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
948 (sa3dTag.channel_map[0] == 0) &&
949 (sa3dTag.channel_map[1] == 3) &&
950 (sa3dTag.channel_map[2] == 1) &&
951 (sa3dTag.channel_map[3] == 2) &&
952 (sa3dTag.channel_map[4] == 6) &&
953 (sa3dTag.channel_map[5] == 7) &&
954 (sa3dTag.channel_map[6] == 5) &&
955 (sa3dTag.channel_map[7] == 8) &&
956 (sa3dTag.channel_map[8] == 4))
957 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
959 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond\n");
966 case MMFILE_AMBISONIC_ORDER_TOA: {
967 if (sa3dTag.num_channels == 16) {
968 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_TOA;
970 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
971 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
972 (sa3dTag.channel_map[0] == 0) &&
973 (sa3dTag.channel_map[1] == 1) &&
974 (sa3dTag.channel_map[2] == 2) &&
975 (sa3dTag.channel_map[3] == 3) &&
976 (sa3dTag.channel_map[4] == 4) &&
977 (sa3dTag.channel_map[5] == 5) &&
978 (sa3dTag.channel_map[6] == 6) &&
979 (sa3dTag.channel_map[7] == 7) &&
980 (sa3dTag.channel_map[8] == 8) &&
981 (sa3dTag.channel_map[9] == 9) &&
982 (sa3dTag.channel_map[10] == 10) &&
983 (sa3dTag.channel_map[11] == 11) &&
984 (sa3dTag.channel_map[12] == 12) &&
985 (sa3dTag.channel_map[13] == 13) &&
986 (sa3dTag.channel_map[14] == 14) &&
987 (sa3dTag.channel_map[15] == 15))
988 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
990 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
991 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
992 (sa3dTag.channel_map[0] == 0) &&
993 (sa3dTag.channel_map[1] == 3) &&
994 (sa3dTag.channel_map[2] == 1) &&
995 (sa3dTag.channel_map[3] == 2) &&
996 (sa3dTag.channel_map[4] == 6) &&
997 (sa3dTag.channel_map[5] == 7) &&
998 (sa3dTag.channel_map[6] == 5) &&
999 (sa3dTag.channel_map[7] == 8) &&
1000 (sa3dTag.channel_map[8] == 4) &&
1001 (sa3dTag.channel_map[9] == 12) &&
1002 (sa3dTag.channel_map[10] == 13) &&
1003 (sa3dTag.channel_map[11] == 11) &&
1004 (sa3dTag.channel_map[12] == 14) &&
1005 (sa3dTag.channel_map[13] == 10) &&
1006 (sa3dTag.channel_map[14] == 15) &&
1007 (sa3dTag.channel_map[15] == 9))
1008 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
1010 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond\n");
1018 debug_warning(DEBUG, "Ambisonic order or format is not supported: ambix or amb formats up to 3rd order were expected.\n");
1024 debug_msg(RELEASE, "formatContext->ambisonic_type = %d", formatContext->ambisonicType);
1025 debug_msg(RELEASE, "formatContext->ambisonic_order = %d", formatContext->ambisonicOrder);
1026 debug_msg(RELEASE, "formatContext->ambisonic_format = %d", formatContext->ambisonicFormat);
1029 return MMFILE_UTIL_SUCCESS;
1032 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1034 return MMFILE_UTIL_FAIL;
1037 static int ParseSt3dData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset)
1039 uint8_t stereo_mode = INVALID_UINT8_VALUE;
1040 unsigned int readed = 0;
1042 mmfile_seek(fp, start_offset, SEEK_SET);
1044 readed = mmfile_read(fp, (unsigned char *)&stereo_mode, sizeof(uint8_t));
1045 if (readed != sizeof(uint8_t)) {
1046 debug_error(DEBUG, "read st3d tag header fail\n");
1047 return MMFILE_UTIL_FAIL;
1050 formatContext->stereoModeV2 = stereo_mode;
1052 return MMFILE_UTIL_SUCCESS;
1055 static int ParseSvhdData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset, unsigned int box_size)
1057 unsigned int readed = 0;
1059 formatContext->metadataSourceV2 = (char *)calloc(1, box_size);
1060 if (!formatContext->metadataSourceV2) {
1061 debug_error(DEBUG, "Calloc failed");
1062 return MMFILE_UTIL_FAIL;
1065 mmfile_seek(fp, start_offset, SEEK_SET);
1066 readed = mmfile_read(fp, (unsigned char *)formatContext->metadataSourceV2, box_size);
1067 if (readed != box_size) {
1068 debug_error(DEBUG, "read svhd tag header fail\n");
1069 if (formatContext->metadataSourceV2)
1070 free(formatContext->metadataSourceV2);
1071 return MMFILE_UTIL_FAIL;
1074 return MMFILE_UTIL_SUCCESS;
1077 static int ParseProjData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset)
1079 unsigned int readed = 0;
1080 typedef struct proj_box_data_s {
1084 } __attribute__((aligned(1), packed)) proj_box_data;
1086 typedef struct prhd_box_data_s {
1087 uint32_t projection_pose_yaw;
1088 uint32_t projection_pose_pitch;
1089 uint32_t projection_pose_roll;
1092 typedef struct equi_box_data_s {
1093 uint32_t projection_bounds_top;
1094 uint32_t projection_bounds_bottom;
1095 uint32_t projection_bounds_left;
1096 uint32_t projection_bounds_right;
1099 typedef struct cbmp_box_data_s {
1105 proj_box_data proj_data;
1106 proj_data.proj_type = INVALID_UINT_VALUE;
1108 prhd_box_data prhd_data;
1109 prhd_data.projection_pose_yaw = INVALID_UINT_VALUE;
1110 prhd_data.projection_pose_pitch = INVALID_UINT_VALUE;
1111 prhd_data.projection_pose_roll = INVALID_UINT_VALUE;
1113 equi_box_data equi_data;
1114 equi_data.projection_bounds_top = INVALID_UINT_VALUE;
1115 equi_data.projection_bounds_bottom = INVALID_UINT_VALUE;
1116 equi_data.projection_bounds_left = INVALID_UINT_VALUE;
1117 equi_data.projection_bounds_right = INVALID_UINT_VALUE;
1119 cbmp_box_data cbmp_data;
1120 cbmp_data.layout = INVALID_UINT_VALUE;
1121 cbmp_data.padding = INVALID_UINT_VALUE;
1123 mmfile_seek(fp, start_offset, SEEK_SET);
1125 readed = mmfile_read(fp, (unsigned char *)&proj_data, sizeof(proj_box_data));
1126 if (readed != sizeof(proj_box_data)) {
1127 debug_error(DEBUG, "read of proj box failed\n");
1128 return MMFILE_UTIL_FAIL;
1131 formatContext->projTypeV2 = mmfile_io_be_uint32(proj_data.proj_type);
1133 debug_error(DEBUG, "formatContext->projTypeV2 = %d\n", formatContext->projTypeV2);
1134 debug_error(DEBUG, "proj_data.version = %d\n", proj_data.version);
1135 debug_error(DEBUG, "proj_data.flags = %d\n", ((uint32_t)proj_data.flags[0] << 16) +
1136 ((uint32_t)proj_data.flags[1] << 8) + (uint32_t)proj_data.flags[2]);
1138 mmfile_seek(fp, sizeof(proj_box_data), SEEK_CUR);
1139 readed = mmfile_read(fp, (unsigned char *)&prhd_data, sizeof(prhd_box_data));
1140 if (readed != sizeof(prhd_box_data)) {
1141 debug_error(DEBUG, "read of prhd box failed\n");
1142 return MMFILE_UTIL_FAIL;
1145 formatContext->poseYawV2 = mmfile_io_be_uint32(prhd_data.projection_pose_yaw);
1146 formatContext->posePitchV2 = mmfile_io_be_uint32(prhd_data.projection_pose_pitch);
1147 formatContext->poseRollV2 = mmfile_io_be_uint32(prhd_data.projection_pose_roll);
1149 debug_error(DEBUG, "formatContext->poseYawV2 = %d\n", formatContext->poseYawV2);
1150 debug_error(DEBUG, "formatContext->posePitchV2 = %d\n", formatContext->posePitchV2);
1151 debug_error(DEBUG, "formatContext->poseRollV2 = %d\n", formatContext->poseRollV2);
1153 if (formatContext->projTypeV2 == PROJECTION_TYPE_EQUI) {
1154 debug_msg(RELEASE, "Projection type is Equirectangular");
1155 mmfile_seek(fp, 8, SEEK_CUR); /* 8 = 4 (for size) + 4 (fourcc) */
1156 readed = mmfile_read(fp, (unsigned char *)&equi_data, sizeof(equi_box_data));
1157 if (readed != sizeof(equi_box_data)) {
1158 debug_error(DEBUG, "read of equi box failed\n");
1159 return MMFILE_UTIL_FAIL;
1162 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi_data.projection_bounds_top);
1163 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi_data.projection_bounds_bottom);
1164 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi_data.projection_bounds_left);
1165 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi_data.projection_bounds_right);
1167 debug_error(DEBUG, "formatContext->equiBoundsTopV2 = %d\n", formatContext->equiBoundsTopV2);
1168 debug_error(DEBUG, "formatContext->equiBoundsBottomV2 = %d\n", formatContext->equiBoundsBottomV2);
1169 debug_error(DEBUG, "formatContext->equiBoundsLeftV2 = %d\n", formatContext->equiBoundsLeftV2);
1170 debug_error(DEBUG, "formatContext->equiBoundsRightV2 = %d\n", formatContext->equiBoundsRightV2);
1172 } else if (formatContext->projTypeV2 == PROJECTION_TYPE_CBMP) {
1173 debug_msg(RELEASE, "Projection type is Cubemap");
1174 mmfile_seek(fp, 8, SEEK_CUR); /* 8 = 4 (for size) + 4 (fourcc) */
1175 readed = mmfile_read(fp, (unsigned char *)&cbmp_data, sizeof(cbmp_box_data));
1176 if (readed != sizeof(cbmp_box_data)) {
1177 debug_error(DEBUG, "read of cbmp box failed\n");
1178 return MMFILE_UTIL_FAIL;
1181 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp_data.layout);
1182 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp_data.padding);
1184 debug_error(DEBUG, "formatContext->cbmpLayoutV2 = %d\n", formatContext->cbmpLayoutV2);
1185 debug_error(DEBUG, "formatContext->cbmpPaddingV2 = %d\n", formatContext->cbmpPaddingV2);
1188 debug_msg(RELEASE, "Projection type is %d (unknown)", proj_data.proj_type);
1189 return MMFILE_UTIL_FAIL;
1192 return MMFILE_UTIL_SUCCESS;
1195 static int GetVideoV2MetadataFromAvc1TagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1197 if (!formatContext || !fp || !basic_header) {
1198 debug_error(DEBUG, "invalid param\n");
1199 return MMFILE_UTIL_FAIL;
1202 unsigned char *buffer;
1206 formatContext->stereoModeV2 = INVALID_UINT_VALUE;
1207 formatContext->metadataSourceV2 = NULL;
1208 formatContext->projTypeV2 = INVALID_UINT_VALUE;
1209 formatContext->poseYawV2 = INVALID_UINT_VALUE;
1210 formatContext->posePitchV2 = INVALID_UINT_VALUE;
1211 formatContext->poseRollV2 = INVALID_UINT_VALUE;
1212 formatContext->cbmpLayoutV2 = INVALID_UINT_VALUE;
1213 formatContext->cbmpPaddingV2 = INVALID_UINT_VALUE;
1214 formatContext->equiBoundsTopV2 = INVALID_UINT_VALUE;
1215 formatContext->equiBoundsBottomV2 = INVALID_UINT_VALUE;
1216 formatContext->equiBoundsLeftV2 = INVALID_UINT_VALUE;
1217 formatContext->equiBoundsRightV2 = INVALID_UINT_VALUE;
1219 mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
1221 buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
1223 debug_error(DEBUG, "calloc failed ");
1227 readed = mmfile_read(fp, buffer, basic_header->size);
1228 if (readed != (int)basic_header->size) {
1229 debug_error(DEBUG, "read st3d box failed\n");
1233 for (i = 0; i + 3 < basic_header->size; ++i) {
1234 if ((buffer[i] == 's' && buffer[i + 1] == 't' && buffer[i + 2] == '3' && buffer[i + 3] == 'd') && (formatContext->stereoModeV2 == INVALID_UINT_VALUE)) {
1235 debug_warning(DEBUG, "st3d data found at offset %lld\n", basic_header->start_offset + i);
1236 ParseSt3dData(formatContext, fp, basic_header->start_offset + i + 4);
1237 debug_msg(RELEASE, "formatContext->stereoModeV2 = %d", formatContext->stereoModeV2);
1239 if (buffer[i] == 's' && buffer[i + 1] == 'v' && buffer[i + 2] == '3' && buffer[i + 3] == 'd') {
1240 debug_warning(DEBUG, "sv3d data found at offset %lld\n", basic_header->start_offset + i);
1241 formatContext->isSpherical = true;
1243 if (buffer[i] == 's' && buffer[i + 1] == 'v' && buffer[i + 2] == 'h' && buffer[i + 3] == 'd') {
1244 debug_warning(DEBUG, "svhd data found at offset %lld\n", basic_header->start_offset + i);
1245 ParseSvhdData(formatContext, fp, basic_header->start_offset + i + 4, mmfile_io_be_uint32(*((uint32_t*)(buffer - 4 + i))));
1246 debug_msg(RELEASE, "formatContext->metadataSourceV2 = %s (length = %zu)", formatContext->metadataSourceV2, strlen(formatContext->metadataSourceV2));
1248 if (buffer[i] == 'p' && buffer[i + 1] == 'r' && buffer[i + 2] == 'o' && buffer[i + 3] == 'j') {
1249 debug_warning(DEBUG, "proj data found at offset %lld\n", basic_header->start_offset + i);
1250 ParseProjData(formatContext, fp, basic_header->start_offset + i + 4);
1254 return MMFILE_UTIL_SUCCESS;
1257 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1259 return MMFILE_UTIL_FAIL;
1262 static int GetValueFromCDISTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1264 unsigned int value = 0;
1267 if (!formatContext || !fp || !basic_header) {
1268 debug_error(DEBUG, "invalid param\n");
1269 return MMFILE_UTIL_FAIL;
1272 readed = mmfile_read(fp, (unsigned char *)&value, sizeof(unsigned int));
1273 if (readed != sizeof(unsigned int)) {
1274 debug_error(DEBUG, "read cdis tag header fail\n");
1278 value = mmfile_io_be_uint32(value);
1280 debug_msg(RELEASE, "Value : 0x%x", value);
1282 if (value == 0x01) {
1283 debug_msg(RELEASE, "This has cdis tag and valid value");
1284 formatContext->cdis = 1;
1286 debug_error(DEBUG, "This has cdis tag and but invalid value");
1290 return MMFILE_UTIL_SUCCESS;
1293 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1295 return MMFILE_UTIL_FAIL;
1298 static int GetTagFromMetaBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1301 MMFILE_MP4_BASIC_BOX_HEADER hdlrBoxHeader = {0, };
1302 MMFILE_MP4_BASIC_BOX_HEADER id3v2BoxHeader = {0, };
1303 MMFILE_3GP_ID3V2_BOX id3v2Box = {0, };
1304 AvFileContentInfo tagInfo = {0, };
1305 unsigned char tagVersion = 0;
1306 bool versionCheck = false;
1308 unsigned int meta_version = 0;
1309 MMFILE_3GP_HANDLER_BOX hdlrBox = {0, };
1310 unsigned int encSize = 0;
1312 #ifdef ENABLE_ITUNES_META /* We don't support itunes meta now. so this is not defined yet */
1313 int iTunes_meta = 0;
1317 readed = mmfile_read(fp, (unsigned char *)&meta_version, 4);
1319 debug_error(DEBUG, "read meta box version\n");
1324 readed = mmfile_read(fp, (unsigned char *)&hdlrBoxHeader, MMFILE_MP4_BASIC_BOX_HEADER_LEN);
1325 if (readed != MMFILE_MP4_BASIC_BOX_HEADER_LEN) {
1326 debug_error(DEBUG, "read hdlr box header\n");
1330 if (hdlrBoxHeader.type != FOURCC('h', 'd', 'l', 'r')) {
1331 debug_warning(DEBUG, "meta type is not hdlr\n");
1335 hdlrBoxHeader.size = mmfile_io_be_uint32(hdlrBoxHeader.size);
1336 hdlrBoxHeader.type = mmfile_io_le_uint32(hdlrBoxHeader.type);
1338 readed = mmfile_read(fp, (unsigned char *)&hdlrBox, MMFILE_3GP_HANDLER_BOX_LEN);
1339 if (readed != MMFILE_3GP_HANDLER_BOX_LEN) {
1340 debug_error(DEBUG, "read hdlr box\n");
1344 hdlrBox.handler_type = mmfile_io_le_uint32(hdlrBox.handler_type);
1347 * check tag type (ID3v2 or iTunes)
1349 if (hdlrBox.handler_type == FOURCC('I', 'D', '3', '2')) {
1350 debug_msg(RELEASE, "ID3v2 tag detected.\n");
1353 #ifdef ENABLE_ITUNES_META
1356 } else if (hdlrBox.handler_type == FOURCC('m', 'd', 'i', 'r') &&
1357 mmfile_io_le_uint32(hdlrBox.reserved[0]) == FOURCC('a', 'p', 'p', 'l')) {
1359 debug_msg(RELEASE, "Apple iTunes tag detected by mdir.\n");
1361 #ifdef ENABLE_ITUNES_META
1365 debug_warning(DEBUG, "unknown meta type. 4CC:[%c%c%c%c]\n", ((char *)&hdlrBox.handler_type)[0],
1366 ((char *)&hdlrBox.handler_type)[1],
1367 ((char *)&hdlrBox.handler_type)[2],
1368 ((char *)&hdlrBox.handler_type)[3]);
1369 /*goto exception; */
1372 #ifdef ENABLE_ITUNES_META
1373 if (!id3_meta && !iTunes_meta) {
1375 APPLE meta data for iTunes reader = 'mdir.' so if handler type is 'mdir', this content may has itunes meta.
1376 most of contents has 'mdir' + 'appl'. but some contents just has 'mdir'
1377 but 'ilst' is meta for iTunes. so find 'ilst' is more correct to check if this contents has iTunes meta or not.*/
1379 const char *ilst_box = "ilst";
1380 int buf_size = strlen(ilst_box);
1382 unsigned char read_buf[buf_size + 1];
1383 memset(read_buf, 0x00, buf_size + 1);
1386 mmfile_seek(fp, hdlrBoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_HANDLER_BOX_LEN + 4, SEEK_CUR); /*+4 is hdlr size field */
1388 readed = mmfile_read(fp, read_buf, buf_size); /* to find 'ilst' */
1389 if (readed != buf_size) {
1390 debug_error(DEBUG, "read fail [%d]\n", readed);
1394 if (read_buf[0] == 'i' && read_buf[1] == 'l' && read_buf[2] == 's' && read_buf[3] == 't') {
1395 debug_msg(RELEASE, "Apple iTunes tag detected by ilst.\n");
1401 #ifdef ENABLE_ITUNES_META
1404 * iTunes (Cover[?ovr] & Track[trkn] only extract!) + Genre/Artist : Added 2010.10.27,28
1412 #define _ITUNES_READ_BUF_SZ 20
1413 #define _ITUNES_TRACK_NUM_SZ 4
1414 #define _ITUNES_GENRE_NUM_SZ 4
1415 #define _ITUNES_COVER_TYPE_JPEG 13
1416 #define _ITUNES_COVER_TYPE_PNG 14
1418 unsigned char read_buf[_ITUNES_READ_BUF_SZ];
1420 int cover_sz = 0, cover_type = 0, cover_found = 0;
1421 /* int track_found = 0; */ /* , track_num = 0; */
1422 /* int genre_found = 0; */ /* , genre_index = 0; */
1423 /* int artist_found = 0; */ /* , artist_sz = 0; */
1424 int limit = basic_header->size - hdlrBoxHeader.size;
1425 long long cover_offset = 0; /*, track_offset =0, genre_offset = 0, artist_offset = 0; */
1427 /* 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++) { */
1428 for (i = 0; (i < limit) && (cover_found == 0) ; i++) {
1429 readed = mmfile_read(fp, read_buf, _ITUNES_READ_BUF_SZ);
1430 if (readed != _ITUNES_READ_BUF_SZ)
1433 /*ffmpeg extract artist, tracknum, genre and cover image. see mov_read_udta_string().
1434 but ffmpeg does not support strange cover image.
1435 only support covr type 0xd(JPEG), 0xe(PNG), 0x1b(BMP). but we support other type*/
1438 * Artist : Added 2010.10.28
1440 if (artist_found == 0 &&
1441 read_buf[0] == 0xa9 && read_buf[1] == 'A' && read_buf[2] == 'R' && read_buf[3] == 'T' &&
1442 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1445 artist_offset = mmfile_tell(fp);
1446 artist_sz = mmfile_io_be_uint32(*(int *)(read_buf + 4)) - 16; /* atom len(4)+data(4)+atom verion(1)+flag(3)+null(4) = 16 */
1448 debug_msg(RELEASE, "----------------------------------- artist found, offset=[%lld], size=[%d]\n", artist_offset, artist_sz);
1454 if (track_found == 0 &&
1455 read_buf[0] == 't' && read_buf[1] == 'r' && read_buf[2] == 'k' && read_buf[3] == 'n' &&
1456 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1459 track_offset = mmfile_tell(fp);
1461 debug_msg(RELEASE, "----------------------------------- Track found, offset=[%lld]\n", track_offset);
1465 * Genre : Added 2010.10.27
1467 /*ffmpeg extract genre but only (0xa9,'g','e','n'). see mov_read_udta_string()*/
1468 if (genre_found == 0 &&
1469 read_buf[0] == 'g' && read_buf[1] == 'n' && read_buf[2] == 'r' && read_buf[3] == 'e' &&
1470 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1473 genre_offset = mmfile_tell(fp);
1475 debug_msg(RELEASE, "----------------------------------- genre found, offset=[%lld]\n", genre_offset);
1483 if (cover_found == 0 &&
1484 read_buf[0] == 'c' && read_buf[1] == 'o' && read_buf[2] == 'v' && read_buf[3] == 'r' &&
1485 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1488 cover_sz = mmfile_io_be_uint32(*(int *)(read_buf + 4)) - 12;
1489 cover_type = mmfile_io_be_uint32(*(int *)(read_buf + 12));
1491 cover_offset = mmfile_tell(fp);
1493 debug_msg(RELEASE, "----------------------------------- cover_found found, offset=[%lld]\n", cover_offset);
1496 mmfile_seek(fp, -(_ITUNES_READ_BUF_SZ - 1), SEEK_CUR); /*FIXME: poor search*/
1499 /*ffmpeg extract artist, tracknum, excep cover image. see mov_read_udta_string()*/
1502 if (artist_sz > 0) {
1503 mmfile_seek(fp, artist_offset, SEEK_SET);
1505 if (formatContext->artist) {
1506 debug_msg(RELEASE, "----------------------------------- previous artist was [%s] \n", formatContext->artist);
1507 free(formatContext->artist);
1510 debug_msg(RELEASE, "----------------------------------- new artist will be allocated with size (len+1) [%d] \n", artist_sz + 1);
1511 formatContext->artist = mmfile_malloc(artist_sz + 1);
1513 if (formatContext->artist) {
1514 readed = mmfile_read(fp, (unsigned char *)formatContext->artist, artist_sz);
1515 formatContext->artist[artist_sz] = '\0';
1517 debug_msg(RELEASE, "----------------------------------- new artist is [%s] \n", formatContext->artist);
1519 if (readed != artist_sz) {
1520 debug_error(DEBUG, "failed to read. ret = %d, in = %d\n", readed, artist_sz);
1521 mmfile_free(formatContext->artist);
1528 mmfile_seek(fp, track_offset, SEEK_SET);
1529 readed = mmfile_read(fp, read_buf, _ITUNES_TRACK_NUM_SZ);
1530 if (readed != _ITUNES_TRACK_NUM_SZ) {
1531 debug_error(DEBUG, "failed to read. ret = %d, in = %d\n", readed, _ITUNES_TRACK_NUM_SZ);
1533 track_num = mmfile_io_be_uint32(*(int *)read_buf);
1534 if (!formatContext->tagTrackNum) {
1535 memset(read_buf, 0x00, _ITUNES_READ_BUF_SZ);
1536 snprintf((char *)read_buf, sizeof(read_buf), "%d", track_num);
1537 formatContext->tagTrackNum = mmfile_strdup((const char *)read_buf);
1543 mmfile_seek(fp, genre_offset, SEEK_SET);
1544 readed = mmfile_read(fp, read_buf, _ITUNES_GENRE_NUM_SZ);
1545 if (readed != _ITUNES_GENRE_NUM_SZ) {
1546 debug_error(DEBUG, "failed to read. ret = %d, in = %d\n", readed, _ITUNES_GENRE_NUM_SZ);
1548 genre_index = mmfile_io_be_uint16(*(int *)read_buf);
1549 debug_msg(RELEASE, "genre index=[%d] \n", genre_index);
1551 if (genre_index > 0 && genre_index < GENRE_COUNT) {
1552 if (!formatContext->genre) {
1553 memset(read_buf, 0x00, _ITUNES_READ_BUF_SZ);
1554 snprintf((char *)read_buf, sizeof(read_buf), "%s", MpegAudio_Genre[genre_index - 1]);
1555 debug_msg(RELEASE, "genre string=[%s] \n", read_buf);
1556 formatContext->genre = mmfile_strdup((const char *)read_buf);
1564 1) below spec is in "iTunes Package Asset Specification 4.3" published by apple.
1565 Music Cover Art Image Profile
1566 - TIFF with ".tif" extension (32-bit uncompressed), JPEG with ".jpg" extension (quality unconstrained), or PNG with ".png" extension
1567 - RGB (screen standard)
1568 - Minimum size of 600 x 600 pixels
1569 - Images must be at least 72 dpi
1571 2)I found below info from google.
1572 cover image flag : JPEG (13, 0xd), PNG (14, 0xe)
1574 3)So, FIXME when cover image format is tif!
1578 mmfile_seek(fp, cover_offset, SEEK_SET);
1580 formatContext->artwork = mmfile_malloc(cover_sz);
1581 formatContext->artworkSize = cover_sz;
1582 if (cover_type == _ITUNES_COVER_TYPE_JPEG) {
1583 formatContext->artworkMime = mmfile_strdup("image/jpeg");
1584 } else if (cover_type == _ITUNES_COVER_TYPE_PNG) {
1585 formatContext->artworkMime = mmfile_strdup("image/png");
1586 /*} else if(cover_type == _ITUNES_COVER_TYPE_TIF) {
1587 formatContext->artworkMime = mmfile_strdup("image/tif");*/
1589 debug_warning(DEBUG, "Not proper cover image type, but set to jpeg. cover_type[%d]", cover_type);
1590 formatContext->artworkMime = mmfile_strdup("image/jpeg");
1593 if (formatContext->artwork) {
1594 readed = mmfile_read(fp, formatContext->artwork, cover_sz);
1595 if (readed != cover_sz) {
1596 debug_error(DEBUG, "failed to read. ret = %d, in = %d\n", readed, cover_sz);
1597 mmfile_free(formatContext->artwork);
1598 formatContext->artworkSize = 0;
1599 mmfile_free(formatContext->artworkMime);
1605 /*reset seek position*/
1606 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1608 return MMFILE_UTIL_SUCCESS;
1616 /* skip hdlr box name */
1617 mmfile_seek(fp, hdlrBoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_HANDLER_BOX_LEN, SEEK_CUR);
1620 readed = mmfile_read(fp, (unsigned char *)&id3v2BoxHeader, MMFILE_MP4_BASIC_BOX_HEADER_LEN);
1621 if (readed != MMFILE_MP4_BASIC_BOX_HEADER_LEN) {
1622 debug_error(DEBUG, "read id3v2 box header\n");
1626 id3v2BoxHeader.size = mmfile_io_be_uint32(id3v2BoxHeader.size);
1627 id3v2BoxHeader.type = mmfile_io_le_uint32(id3v2BoxHeader.type);
1629 if (id3v2BoxHeader.type != FOURCC('I', 'D', '3', '2')) {
1630 debug_warning(DEBUG, "meta type is not id3v2\n");
1634 readed = mmfile_read(fp, (unsigned char *)&id3v2Box, MMFILE_3GP_ID3V2_BOX_LEN);
1635 if (readed != MMFILE_3GP_ID3V2_BOX_LEN) {
1636 debug_error(DEBUG, "read id3v2 box\n");
1640 id3v2Len = id3v2BoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_ID3V2_BOX_LEN;
1642 id3v2Box.id3v2Data = mmfile_malloc(id3v2Len);
1643 if (!id3v2Box.id3v2Data) {
1644 debug_error(DEBUG, "malloc id3tag data error\n");
1648 readed = mmfile_read(fp, (unsigned char *)id3v2Box.id3v2Data, id3v2Len);
1649 if (readed != id3v2Len) {
1650 debug_error(DEBUG, "read id3tag data error\n");
1655 if (!IS_ID3V2_TAG(id3v2Box.id3v2Data)) {
1656 debug_error(DEBUG, "it is not id3tag\n");
1660 if (id3v2Box.id3v2Data[3] == 0xFF || id3v2Box.id3v2Data[4] == 0xFF ||
1661 id3v2Box.id3v2Data[6] >= 0x80 || id3v2Box.id3v2Data[7] >= 0x80 ||
1662 id3v2Box.id3v2Data[8] >= 0x80 || id3v2Box.id3v2Data[9] >= 0x80) {
1663 debug_error(DEBUG, "it is not valid id3tag\n");
1667 tagVersion = id3v2Box.id3v2Data[3];
1668 if (tagVersion > 4) {
1669 debug_error(DEBUG, "tag vesion is too high\n");
1673 encSize = mmfile_io_le_uint32((unsigned int)&id3v2Box.id3v2Data[6]);
1674 tagInfo.tagV2Info.tagLen = MP3_TAGv2_HEADER_LEN;
1675 tagInfo.tagV2Info.tagLen += (((encSize & 0x0000007F) >> 0) | ((encSize & 0x00007F00) >> 1) | ((encSize & 0x007F0000) >> 2) | ((encSize & 0x7F000000) >> 3));
1676 tagInfo.tagV2Info.tagVersion = tagVersion;
1677 tagInfo.fileLen = id3v2Len;
1679 /* set id3v2 data to formatContext */
1680 switch (tagVersion) {
1682 versionCheck = mm_file_id3tag_parse_v222(&tagInfo, id3v2Box.id3v2Data);
1686 versionCheck = mm_file_id3tag_parse_v223(&tagInfo, id3v2Box.id3v2Data);
1690 versionCheck = mm_file_id3tag_parse_v224(&tagInfo, id3v2Box.id3v2Data);
1695 debug_error(DEBUG, "tag vesion is not support\n");
1696 versionCheck = false;
1701 if (versionCheck == false) {
1702 debug_error(DEBUG, "tag parsing is fail\n");
1706 if (!formatContext->title) formatContext->title = mmfile_strdup((const char *)tagInfo.pTitle);
1707 if (!formatContext->artist) formatContext->artist = mmfile_strdup((const char *)tagInfo.pArtist);
1708 if (!formatContext->author) formatContext->author = mmfile_strdup((const char *)tagInfo.pAuthor);
1709 if (!formatContext->copyright) formatContext->copyright = mmfile_strdup((const char *)tagInfo.pCopyright);
1710 if (!formatContext->comment) formatContext->comment = mmfile_strdup((const char *)tagInfo.pComment);
1711 if (!formatContext->album) formatContext->album = mmfile_strdup((const char *)tagInfo.pAlbum);
1712 if (!formatContext->album_artist) formatContext->album_artist = mmfile_strdup((const char *)tagInfo.pAlbum_Artist);
1713 if (!formatContext->year) formatContext->year = mmfile_strdup((const char *)tagInfo.pYear);
1714 if (!formatContext->genre) formatContext->genre = mmfile_strdup((const char *)tagInfo.pGenre);
1715 if (!formatContext->tagTrackNum) formatContext->tagTrackNum = mmfile_strdup((const char *)tagInfo.pTrackNum);
1716 if (!formatContext->composer) formatContext->composer = mmfile_strdup((const char *)tagInfo.pComposer);
1717 if (!formatContext->classification) formatContext->classification = mmfile_strdup((const char *)tagInfo.pContentGroup);
1718 if (!formatContext->conductor) formatContext->conductor = mmfile_strdup((const char *)tagInfo.pConductor);
1720 formatContext->artwork = mmfile_malloc(tagInfo.imageInfo.imageLen);
1721 if ((tagInfo.imageInfo.imageLen > 0) && formatContext->artwork) {
1722 formatContext->artworkSize = tagInfo.imageInfo.imageLen;
1723 memcpy(formatContext->artwork, tagInfo.imageInfo.pImageBuf, tagInfo.imageInfo.imageLen);
1726 mm_file_free_AvFileContentInfo(&tagInfo);
1727 mmfile_free(id3v2Box.id3v2Data);
1729 /*reset seek position*/
1730 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1732 return MMFILE_UTIL_SUCCESS;
1738 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1739 mmfile_free(id3v2Box.id3v2Data);
1740 mm_file_free_AvFileContentInfo(&tagInfo);
1742 return MMFILE_UTIL_FAIL;
1745 int mm_file_get_int_value_from_xml_string(const char* xml_str, const char* param_name, int* value)
1747 char *value_start, *value_end, *endptr;
1748 const short value_length_max = 12;
1749 char init_view_ret[value_length_max];
1750 int value_length = 0;
1752 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1753 debug_error(DEBUG, "error: incorrect or non-existing parameter\n");
1754 return MMFILE_UTIL_FAIL;
1757 value_start = strstr(xml_str, param_name) + strlen(param_name);
1758 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
1761 value_end = strchr(value_start, '<');
1763 debug_error(DEBUG, "error: incorrect XML\n");
1764 return MMFILE_UTIL_FAIL;
1767 value_length = value_end - value_start;
1768 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1772 if (value_start[i] == '+' || value_start[i] == '-')
1774 while (i < value_length) {
1775 if (value_start[i] < '0' || value_start[i] > '9') {
1776 debug_error(DEBUG, "error: incorrect value, integer was expected\n");
1777 return MMFILE_UTIL_FAIL;
1782 if (value_length >= value_length_max || value_length < 1) {
1783 debug_error(DEBUG, "error: empty XML value or incorrect range\n");
1784 return MMFILE_UTIL_FAIL;
1787 memset(init_view_ret, 0x00, sizeof(init_view_ret));
1788 SAFE_STRLCPY(init_view_ret, value_start, sizeof(init_view_ret));
1790 *value = strtol(init_view_ret, &endptr, 10);
1791 if (endptr == init_view_ret) {
1792 debug_error(DEBUG, "error: no digits were found\n");
1793 return MMFILE_UTIL_FAIL;
1796 return MMFILE_UTIL_SUCCESS;
1799 int mm_file_get_string_value_from_xml_string(const char* xml_str, const char* param_name, char** value)
1801 char *value_start, *value_end;
1802 const short value_length_max = 256;
1803 int value_length = 0;
1805 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1806 debug_error(DEBUG, "error: incorrect or non-existing parameter\n");
1807 return MMFILE_UTIL_FAIL;
1810 value_start = strstr(xml_str, param_name) + strlen(param_name);
1811 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
1814 value_end = strchr(value_start, '<');
1816 debug_error(DEBUG, "error: incorrect XML\n");
1817 return MMFILE_UTIL_FAIL;
1820 value_length = value_end - value_start;
1821 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
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 *value = (char*)calloc(value_length, sizeof(char));
1830 if (*value == NULL) {
1831 debug_error(DEBUG, "error: calloc failed\n");
1832 return MMFILE_UTIL_FAIL;
1834 strncpy(*value, value_start, value_length);
1836 return MMFILE_UTIL_SUCCESS;
1839 int mm_file_get_bool_value_from_xml_string(const char* xml_str, const char* param_name, bool* value)
1841 char *value_start = NULL;
1842 char *value_end = NULL;
1843 int value_length = 0;
1845 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1846 debug_error(DEBUG, "error: incorrect or non-existing parameter\n");
1847 return MMFILE_UTIL_FAIL;
1850 value_start = strstr(xml_str, param_name) + strlen(param_name);
1851 while ((value_start != NULL) && ((value_start[0] == ' ') || (value_start[0] == '\t')))
1854 value_end = strchr(value_start, '<');
1855 if (value_end == NULL) {
1856 debug_error(DEBUG, "error: incorrect XML.");
1857 return MMFILE_UTIL_FAIL;
1860 value_length = value_end - value_start;
1861 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1864 if (value_length < 1) {
1865 debug_error(DEBUG, "error: empty XML value or incorrect range\n");
1866 return MMFILE_UTIL_FAIL;
1869 *value = strstr(value_start, "true") ? true : false;
1871 return MMFILE_UTIL_SUCCESS;
1874 static int g_junk_counter_limit = 0;
1875 static int GetJunkCounterLimit(void)
1877 dictionary *dict = NULL;
1880 dict = iniparser_load(MM_FILE_INI_PATH);
1882 debug_error(DEBUG, "%s load failed", MM_FILE_INI_PATH);
1886 data = iniparser_getint(dict, "mm-file-config:junk_counter_limit", 0);
1887 debug_msg(DEBUG, "mm-file-config:junk_counter_limit= %u", data);
1889 iniparser_freedict(dict);
1894 int ParseSpatialVideoMetadataFromXMLString(const char *xmlStr, MMFileFormatContext *formatContext)
1896 const char is_spherical_str[] = "<GSpherical:Spherical>";
1897 const char is_stitched_str[] = "<GSpherical:Stitched>";
1898 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
1899 const char projection_type_str[] = "<GSpherical:ProjectionType>";
1900 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
1901 const char source_count_str[] = "<GSpherical:SourceCount>";
1902 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
1903 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
1904 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
1905 const char timestamp_str[] = "<GSpherical:Timestamp>";
1906 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
1907 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
1908 const char cropped_area_image_width_str[] = "<GSpherical:CroppedAreaImageWidthPixels>";
1909 const char cropped_area_image_height_str[] = "<GSpherical:CroppedAreaImageHeightPixels>";
1910 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
1911 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
1913 mm_file_get_bool_value_from_xml_string(xmlStr, is_spherical_str, (bool*)&formatContext->isSpherical);
1914 mm_file_get_bool_value_from_xml_string(xmlStr, is_stitched_str, (bool*)&formatContext->isStitched);
1916 debug_msg(RELEASE, "isSpherical = %d", formatContext->isSpherical);
1917 debug_msg(RELEASE, "isStitched = %d", formatContext->isStitched);
1919 if (formatContext->isSpherical && formatContext->isStitched) {
1920 mm_file_get_string_value_from_xml_string(xmlStr, stitching_software_str, &formatContext->stitchingSoftware);
1921 mm_file_get_string_value_from_xml_string(xmlStr, projection_type_str, &formatContext->projectionType);
1922 mm_file_get_string_value_from_xml_string(xmlStr, stereo_mode_str, &formatContext->stereoMode);
1923 mm_file_get_int_value_from_xml_string(xmlStr, source_count_str, &formatContext->sourceCount);
1924 mm_file_get_int_value_from_xml_string(xmlStr, init_view_heading_str, &formatContext->initViewHeading);
1925 mm_file_get_int_value_from_xml_string(xmlStr, init_view_pitch_str, &formatContext->initViewPitch);
1926 mm_file_get_int_value_from_xml_string(xmlStr, init_view_roll_str, &formatContext->initViewRoll);
1927 mm_file_get_int_value_from_xml_string(xmlStr, timestamp_str, &formatContext->timestamp);
1928 mm_file_get_int_value_from_xml_string(xmlStr, full_pano_width_str, &formatContext->fullPanoWidth);
1929 mm_file_get_int_value_from_xml_string(xmlStr, full_pano_height_str, &formatContext->fullPanoHeight);
1930 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_width_str, &formatContext->croppedAreaImageWidth);
1931 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_height_str, &formatContext->croppedAreaImageHeight);
1932 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_left_str, &formatContext->croppedAreaLeft);
1933 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_top_str, &formatContext->croppedAreaTop);
1935 debug_msg(RELEASE, "stitchingSoftware = %s", formatContext->stitchingSoftware);
1936 debug_msg(RELEASE, "projectionType = %s", formatContext->projectionType);
1937 debug_msg(RELEASE, "stereoMode = %s", formatContext->stereoMode);
1938 debug_msg(RELEASE, "sourceCount %d", formatContext->sourceCount);
1939 debug_msg(RELEASE, "initViewHeading = %d", formatContext->initViewHeading);
1940 debug_msg(RELEASE, "initViewPitch = %d", formatContext->initViewPitch);
1941 debug_msg(RELEASE, "initViewRoll = %d", formatContext->initViewRoll);
1942 debug_msg(RELEASE, "timestamp = %d", formatContext->timestamp);
1943 debug_msg(RELEASE, "fullPanoWidthPixels = %d", formatContext->fullPanoWidth);
1944 debug_msg(RELEASE, "fullPanoHeightPixels = %d", formatContext->fullPanoHeight);
1945 debug_msg(RELEASE, "croppedAreaImageWidth = %d", formatContext->croppedAreaImageWidth);
1946 debug_msg(RELEASE, "croppedAreaImageHeight = %d", formatContext->croppedAreaImageHeight);
1947 debug_msg(RELEASE, "croppedAreaLeft = %d", formatContext->croppedAreaLeft);
1948 debug_msg(RELEASE, "croppedAreaTop = %d", formatContext->croppedAreaTop);
1951 return MMFILE_UTIL_SUCCESS;
1954 #define BIG_CONTENT_BOX_SIZE_LEN 8
1956 int MMFileUtilGetMetaDataFromMKV(MMFileFormatContext *formatContext)
1958 MMFileIOHandle *fp = NULL;
1959 int probe_size = 10000;
1960 unsigned char *buffer = NULL;
1963 long long file_size = 0;
1965 MMFILE_WEBM_PROJ_V2_BOX v2box = { 0, };
1966 MMFILE_WEBM_EQUI_PROJ_V2_BOX equi = { 0, };
1967 MMFILE_WEBM_CBMP_PROJ_V2_BOX cbmp = { 0, };
1968 MMFILE_WEBM_POSE_ELEMENT_V2_BOX pose = { 0, };
1970 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
1971 if (ret == MMFILE_UTIL_FAIL) {
1972 debug_error(DEBUG, "error: mmfile_open\n");
1976 file_size = mmfile_seek(fp, 0, SEEK_END);
1977 if (file_size == MMFILE_UTIL_FAIL) {
1978 debug_error(DEBUG, "mmfile operation failed\n");
1982 probe_size = (file_size > probe_size) ? probe_size : file_size;
1983 buffer = (unsigned char *)malloc(probe_size * sizeof(unsigned char));
1985 debug_error(DEBUG, "malloc failed\n");
1989 ret = mmfile_seek(fp, 0, SEEK_SET);
1990 if (ret == MMFILE_UTIL_FAIL) {
1991 debug_error(DEBUG, "mmfile operation failed\n");
1995 ret = mmfile_read(fp, buffer, probe_size * sizeof(unsigned char));
1996 if (ret == MMFILE_UTIL_FAIL) {
1997 debug_error(DEBUG, "mmfile operation failed\n");
2001 /* FIXME (m.alieksieie): It's better to use some EBML parser here*/
2002 for (i = 0; i + 3 < probe_size; ++i) {
2003 if (*(unsigned int *)(buffer + i) == FOURCC('e', 'q', 'u', 'i') ||
2004 *(unsigned int *)(buffer + i) == FOURCC('c', 'b', 'm', 'p')) {
2005 debug_msg(DEBUG, "projection data found at offset %d bytes\n", i);
2010 if (i + 3 == probe_size) {
2011 debug_msg(DEBUG, "projection info wasn't found\n");
2012 ret = MMFILE_UTIL_SUCCESS;
2016 if ((i - (int)sizeof(MMFILE_WEBM_PROJ_V2_BOX)) < 0) {
2017 debug_error(DEBUG, "error: invalid supposed projection info location\n");
2018 ret = MMFILE_UTIL_FAIL;
2022 ret = mmfile_seek(fp, i - sizeof(MMFILE_WEBM_PROJ_V2_BOX), SEEK_SET);
2023 if (ret == MMFILE_UTIL_FAIL) {
2024 debug_error(DEBUG, "error: failed to seek to the supposed projection info location\n");
2028 ret = mmfile_read(fp, (unsigned char *)&v2box, sizeof(MMFILE_WEBM_PROJ_V2_BOX));
2029 if (ret == MMFILE_UTIL_FAIL) {
2030 debug_error(DEBUG, "mmfile operation failed\n");
2034 if (v2box.proj_type_box_value == PROJECTION_TYPE_EQUI) {
2035 debug_msg(DEBUG, "Equirectangular projection is used\n");
2037 ret = mmfile_read(fp, (unsigned char *)&equi, sizeof(MMFILE_WEBM_EQUI_PROJ_V2_BOX));
2038 if (ret == MMFILE_UTIL_FAIL) {
2039 debug_error(DEBUG, "error: failed to read equirectangular element\n");
2042 if (strncmp((char *)equi.proj_priv_box_name, "equi", 4) == 0) {
2043 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_top);
2044 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_bottom);
2045 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_left);
2046 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_right);
2048 debug_error(DEBUG, "error: failed to read equirectangular element\n");
2049 ret = MMFILE_UTIL_SUCCESS;
2053 if (v2box.proj_type_box_value == PROJECTION_TYPE_CBMP) {
2054 debug_msg(DEBUG, "Cubemap projection is used\n");
2056 ret = mmfile_read(fp, (unsigned char *)&cbmp, sizeof(MMFILE_WEBM_CBMP_PROJ_V2_BOX));
2057 if (ret == MMFILE_UTIL_FAIL) {
2058 debug_error(DEBUG, "error: failed to read cubemap element\n");
2061 if (strncmp((char *)cbmp.proj_priv_box_name, "cbmp", 4) == 0) {
2062 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_layout);
2063 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_padding);
2065 debug_error(DEBUG, "error: failed to read cubemap element\n");
2066 ret = MMFILE_UTIL_FAIL;
2071 ret = mmfile_read(fp, (unsigned char *)&pose, sizeof(MMFILE_WEBM_POSE_ELEMENT_V2_BOX));
2072 if (ret == MMFILE_UTIL_FAIL) {
2073 debug_error(DEBUG, "error: failed to read pose info\n");
2077 if (pose.pose_yaw_element_id == POSE_YAW_ELEMENT_ID) {
2078 formatContext->poseYawV2 = (uint)mmfile_io_be_float32(pose.pose_yaw_element_value);
2080 debug_error(DEBUG, "error: failed to pose yaw element\n");
2081 ret = MMFILE_UTIL_FAIL;
2084 if (pose.pose_pitch_element_id == POSE_PITCH_ELEMENT_ID) {
2085 formatContext->posePitchV2 = (uint)mmfile_io_be_float32(pose.pose_pitch_element_value);
2087 debug_error(DEBUG, "error: failed to pose pitch element\n");
2088 ret = MMFILE_UTIL_FAIL;
2091 if (pose.pose_roll_element_id == POSE_ROLL_ELEMENT_ID) {
2092 formatContext->poseRollV2 = (uint)mmfile_io_be_float32(pose.pose_roll_element_value);
2094 debug_error(DEBUG, "error: failed to pose roll element\n");
2095 ret = MMFILE_UTIL_FAIL;
2108 EXPORT_API int MMFileUtilGetMetaDataFromMP4(MMFileFormatContext *formatContext)
2110 MMFileIOHandle *fp = NULL;
2113 unsigned long long chunk_size = 0;
2114 long long moov_end = 0;
2115 MMFILE_MP4_BASIC_BOX_HEADER basic_header = {0, };
2116 int junk_counter = 0;
2118 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
2119 if (ret == MMFILE_UTIL_FAIL) {
2120 debug_error(DEBUG, "error: mmfile_open\n");
2124 basic_header.start_offset = mmfile_tell(fp);
2126 if (g_junk_counter_limit == 0)
2127 g_junk_counter_limit = GetJunkCounterLimit();
2129 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)) {
2130 basic_header.size = mmfile_io_be_uint32(basic_header.size);
2131 basic_header.type = mmfile_io_le_uint32(basic_header.type);
2133 if (basic_header.size == 0) {
2134 debug_warning(DEBUG, "header is invalid.\n");
2135 basic_header.size = readed;
2136 basic_header.type = 0;
2137 chunk_size = basic_header.size;
2139 if ((moov_end != 0) && (moov_end < basic_header.start_offset)) {
2140 debug_msg(DEBUG, "found junk data but moov data already was extracted, so junk counter will be increase: %d", junk_counter);
2143 /* stop the loop for junk case. */
2144 if ((g_junk_counter_limit > 0) && (junk_counter > g_junk_counter_limit)) {
2145 debug_msg(DEBUG, "stop the loop by junk-data checker");
2146 ret = MMFILE_UTIL_FAIL;
2150 } else if (basic_header.size == 1) {
2152 unsigned char temp[BIG_CONTENT_BOX_SIZE_LEN] = {0, };
2153 unsigned long long size = 0;
2155 mmfile_read(fp, (unsigned char *)&temp, BIG_CONTENT_BOX_SIZE_LEN);
2157 for (i = 0; i < BIG_CONTENT_BOX_SIZE_LEN; i++)
2158 size |= (unsigned long long)temp[i] << (BIG_CONTENT_BOX_SIZE_LEN - 1 - i) * BIG_CONTENT_BOX_SIZE_LEN;
2162 chunk_size = basic_header.size;
2166 switch (basic_header.type) {
2167 case FOURCC('m', 'o', 'o', 'v'): {
2168 debug_msg(RELEASE, "MPEG4: [moov] SIZE: [%lld]Byte\n", chunk_size);
2169 moov_end = basic_header.start_offset + chunk_size;
2172 case FOURCC('u', 'd', 't', 'a'): {
2173 debug_msg(RELEASE, "MPEG4: [udat] SIZE: [%lld]Byte\n", chunk_size);
2176 /*/////////////////////////////////////////////////////////////// */
2177 /* Extracting Tag Data // */
2178 /*/////////////////////////////////////////////////////////////// */
2179 case FOURCC('t', 'i', 't', 'l'): {
2180 debug_msg(RELEASE, "MPEG4: [titl] SIZE: [%lld]Byte\n", chunk_size);
2181 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_TITLE);
2184 case FOURCC('d', 's', 'c', 'p'): {
2185 debug_msg(RELEASE, "MPEG4: [dscp] SIZE: [%lld]Byte\n", chunk_size);
2186 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_CAPTION);
2189 case FOURCC('c', 'p', 'r', 't'): {
2190 debug_msg(RELEASE, "MPEG4: [cprt] SIZE: [%lld]Byte\n", chunk_size);
2191 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_COPYRIGHT);
2194 case FOURCC('p', 'e', 'r', 'f'): {
2195 debug_msg(RELEASE, "MPEG4: [perf] SIZE: [%lld]Byte\n", chunk_size);
2196 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_PERFORMER);
2199 case FOURCC('a', 'u', 't', 'h'): {
2200 debug_msg(RELEASE, "MPEG4: [auth] SIZE: [%lld]Byte\n", chunk_size);
2201 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_AUTHOR);
2204 case FOURCC('g', 'n', 'r', 'e'): {
2205 debug_msg(RELEASE, "MPEG4: [gnre] SIZE: [%lld]Byte\n", chunk_size);
2206 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_GENRE);
2209 case FOURCC('a', 'l', 'b', 'm'): {
2210 debug_msg(RELEASE, "MPEG4: [albm] SIZE: [%lld]Byte\n", chunk_size);
2211 GetAlbumFromAlbumTagBox(formatContext, fp, &basic_header);
2214 case FOURCC('y', 'r', 'r', 'c'): {
2215 debug_msg(RELEASE, "MPEG4: [yrrc] SIZE: [%lld]Byte\n", chunk_size);
2216 GetYearFromYearTagBox(formatContext, fp, &basic_header);
2219 case FOURCC('r', 't', 'n', 'g'): {
2220 debug_msg(RELEASE, "MPEG4: [rtng] SIZE: [%lld]Byte\n", chunk_size);
2221 GetRatingFromRatingTagBox(formatContext, fp, &basic_header); /* not use */
2224 case FOURCC('c', 'l', 's', 'f'): {
2225 debug_msg(RELEASE, "MPEG4: [clsf] SIZE: [%lld]Byte\n", chunk_size);
2226 GetClassficationFromClsfTagBox(formatContext, fp, &basic_header);
2229 case FOURCC('k', 'y', 'w', 'd'): {
2230 debug_msg(RELEASE, "MPEG4: [kywd] SIZE: [%lld]Byte\n", chunk_size);
2231 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2234 case FOURCC('l', 'o', 'c', 'i'): {
2235 debug_msg(RELEASE, "MPEG4: [loci] SIZE: [%lld]Byte\n", chunk_size);
2236 GetLocationFromLociTagBox(formatContext, fp, &basic_header);
2239 /* Check smta in user data field (moov) to be compatible with android */
2240 case FOURCC('s', 'm', 't', 'a'): {
2241 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte\n", chunk_size);
2242 GetSAUTInfoFromSMTATagBox(formatContext, fp, &basic_header);
2245 /* Check cdis in user data field (moov) to be compatible with android */
2246 case FOURCC('c', 'd', 'i', 's'): {
2247 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte\n", chunk_size);
2248 GetValueFromCDISTagBox(formatContext, fp, &basic_header);
2251 /*/////////////////////////////////////////////////////////////// */
2252 /* Extracting ID3 Tag Data // */
2253 /*/////////////////////////////////////////////////////////////// */
2254 case FOURCC('m', 'e', 't', 'a'): {
2255 debug_msg(RELEASE, "MPEG4: [meta] SIZE: [%lld]Byte\n", chunk_size);
2256 GetTagFromMetaBox(formatContext, fp, &basic_header);
2260 case FOURCC('t', 'r', 'a', 'k'): {
2261 debug_msg(RELEASE, "MPEG4: [trak] SIZE: [%lld]Byte\n", chunk_size);
2264 case FOURCC('u', 'u', 'i', 'd'): {
2265 unsigned long uuid[4] = {0, };
2267 debug_msg(RELEASE, "MPEG4: [uuid] SIZE: [%lld]Byte\n", chunk_size);
2269 mmfile_read(fp, (unsigned char *)uuid, sizeof(uuid));
2271 if (mmfile_io_be_uint32(uuid[0]) == 0xffcc8263
2272 && mmfile_io_be_uint32(uuid[1]) == 0xf8554a93
2273 && mmfile_io_be_uint32(uuid[2]) == 0x8814587a
2274 && mmfile_io_be_uint32(uuid[3]) == 0x02521fdd) {
2276 str = (char *)malloc(basic_header.size);
2279 memset(str, 0, basic_header.size);
2280 mmfile_read(fp, (unsigned char *)str, basic_header.size);
2282 /* The block is superseded */
2283 if (strstr(str, "<GSpherical:Spherical>true</GSpherical:Spherical>"))
2284 formatContext->is_360 = 1;
2286 formatContext->is_360 = 0;
2287 /* Image can be stitched even if it is not spherical */
2288 if (formatContext->is_360 == 1) {
2289 if (strstr(str, "<GSpherical:Stitched>true</GSpherical:Stitched>"))
2290 formatContext->stitched = MMFILE_360_STITCHED;
2292 formatContext->stitched = MMFILE_360_NON_STITCHED;
2294 /* Image can be stitched or non-stitched. Usage of some 3rd value is superfluous */
2295 formatContext->stitched = MMFILE_360_NONE;
2299 debug_msg(RELEASE, "Extracting tags from UUID XML string %s\n", str);
2301 ParseSpatialVideoMetadataFromXMLString(str, formatContext);
2304 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2308 case FOURCC('m', 'd', 'i', 'a'): {
2309 debug_msg(RELEASE, "MPEG4: [mdia] SIZE: [%lld]Byte\n", chunk_size);
2312 case FOURCC('m', 'i', 'n', 'f'): {
2313 debug_msg(RELEASE, "MPEG4: [minf] SIZE: [%lld]Byte\n", chunk_size);
2316 case FOURCC('s', 't', 'b', 'l'): {
2317 debug_msg(RELEASE, "MPEG4: [stbl] SIZE: [%lld]Byte\n", chunk_size);
2320 case FOURCC('s', 't', 's', 'd'): {
2321 debug_msg(RELEASE, "MPEG4: [stsd] SIZE: [%lld]Byte\n", chunk_size);
2324 case FOURCC('m', 'p', '4', 'a'): {
2325 debug_msg(RELEASE, "MPEG4: [mp4a] SIZE: [%lld]Byte\n", chunk_size);
2326 GetSA3DInfoFromMP4ATagBox(formatContext, fp, &basic_header);
2327 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2330 case FOURCC('a', 'v', 'c', '1'): {
2331 debug_msg(RELEASE, "MPEG4: [avc1] SIZE: [%lld]Byte (offset: %lld)\n", chunk_size, basic_header.start_offset);
2332 GetVideoV2MetadataFromAvc1TagBox(formatContext, fp, &basic_header);
2336 debug_msg(RELEASE, "4CC: Not Support [%c%c%c%c]. So skip it. Size [%lld Byte]\n",
2337 ((char *)&basic_header.type)[0], ((char *)&basic_header.type)[1],
2338 ((char *)&basic_header.type)[2], ((char *)&basic_header.type)[3], chunk_size);
2339 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2344 if (ret == MMFILE_UTIL_FAIL) {
2345 debug_error(DEBUG, "mmfile operation is error\n");
2350 long long new_pos = mmfile_tell(fp);
2352 if ((moov_end == 0) && (new_pos <= basic_header.start_offset)) {
2353 debug_error(DEBUG, "Wrong position");
2354 ret = MMFILE_UTIL_FAIL;
2357 basic_header.start_offset = new_pos;
2366 static char *get_string(const char *buf, int buf_size, int *bytes_written)
2370 char str[512] = {0, };
2373 for (i = 0; i < buf_size; i++) {
2377 if ((q - str) >= (int)sizeof(str) - 1)
2383 if (strlen(str) > 0) {
2384 *bytes_written = strlen(str);
2392 static bool is_numeric(const char *buf, int buf_size)
2397 for (idx = 0; idx < buf_size; idx++) {
2398 if (isdigit((int)buf[idx])) {
2409 char *rtrimN(char *pStr)
2412 pos = strlen(pStr) - 1;
2413 for (; pos >= 0; pos--) {
2414 if (pStr[pos] == 0x20) {
2421 return strdup(pStr);
2424 bool safe_atoi(char *buffer, int *si)
2430 const long sl = strtol(buffer, &end, 10);
2432 if (end == buffer) {
2433 debug_error(RELEASE, "not a decimal number");
2435 } else if ('\0' != *end) {
2436 debug_error(RELEASE, "extra characters at end of input: %s", end);
2438 } else if ((LONG_MIN == sl || LONG_MAX == sl) && (ERANGE == errno)) {
2439 debug_error(RELEASE, "out of range of type long");
2441 } else if (sl > INT_MAX) {
2442 debug_error(RELEASE, "greater than INT_MAX");
2444 } else if (sl < INT_MIN) {
2445 debug_error(RELEASE, "less than INT_MIN");
2453 static bool make_characterset_array(char ***charset_array)
2455 char *locale = MMFileUtilGetLocale();
2457 *charset_array = calloc(AV_ID3V2_MAX, sizeof(char *));
2459 if (*charset_array == NULL) {
2460 debug_error(DEBUG, "calloc failed ");
2466 if (locale != NULL) {
2467 (*charset_array)[AV_ID3V2_ISO_8859] = strdup(locale);
2469 debug_error(DEBUG, "get locale failed");
2470 (*charset_array)[AV_ID3V2_ISO_8859] = NULL;
2473 (*charset_array)[AV_ID3V2_UTF16] = strdup("UCS2");
2474 (*charset_array)[AV_ID3V2_UTF16_BE] = strdup("UTF16-BE");
2475 (*charset_array)[AV_ID3V2_UTF8] = strdup("UTF-8");
2480 static bool release_characterset_array(char **charset_array)
2484 for (i = 0; i < AV_ID3V2_MAX; i++) {
2485 if (charset_array[i] != NULL) {
2486 free(charset_array[i]);
2487 charset_array[i] = NULL;
2491 if (charset_array != NULL) {
2492 free(charset_array);
2493 charset_array = NULL;
2499 static void init_content_info(AvFileContentInfo *pInfo)
2501 pInfo->tagV2Info.bTitleMarked = false;
2502 pInfo->tagV2Info.bArtistMarked = false;
2503 pInfo->tagV2Info.bAlbumMarked = false;
2504 pInfo->tagV2Info.bAlbum_ArtistMarked = false;
2505 pInfo->tagV2Info.bYearMarked = false;
2506 pInfo->tagV2Info.bDescriptionMarked = false;
2507 pInfo->tagV2Info.bGenreMarked = false;
2508 pInfo->tagV2Info.bTrackNumMarked = false;
2509 pInfo->tagV2Info.bEncByMarked = false;
2510 pInfo->tagV2Info.bURLMarked = false;
2511 pInfo->tagV2Info.bCopyRightMarked = false;
2512 pInfo->tagV2Info.bOriginArtistMarked = false;
2513 pInfo->tagV2Info.bComposerMarked = false;
2514 pInfo->tagV2Info.bImageMarked = false;
2516 pInfo->tagV2Info.bRecDateMarked = false;
2517 pInfo->tagV2Info.bContentGroupMarked = false;
2519 pInfo->tagV2Info.bUnsyncLyricsMarked = false;
2520 pInfo->tagV2Info.bSyncLyricsMarked = false;
2521 pInfo->tagV2Info.bConductorMarked = false;
2522 pInfo->tagV2Info.bGenreUTF16 = false;
2524 pInfo->imageInfo.bURLInfo = false;
2525 pInfo->imageInfo.pImageBuf = NULL;
2526 pInfo->imageInfo.imageLen = 0;
2530 bool mm_file_id3tag_parse_v110(AvFileContentInfo *pInfo, unsigned char *buffer)
2532 const char *locale = MMFileUtilGetLocale();
2533 char *pFullStr = NULL;
2535 debug_msg(RELEASE, "ID3tag v110--------------------------------------------------------------\n");
2537 if (pInfo->tagV2Info.bTitleMarked == false) {
2538 pFullStr = mmfile_string_convert((const char *)&buffer[3], MP3_ID3_TITLE_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->titleLen);
2539 if (pFullStr != NULL) {
2540 pInfo->pTitle = rtrimN(pFullStr);
2544 debug_msg(RELEASE, "pInfo->pTitle returned =(%s), pInfo->titleLen(%d)\n", pInfo->pTitle, pInfo->titleLen);
2547 if (pInfo->tagV2Info.bArtistMarked == false) {
2548 pFullStr = mmfile_string_convert((const char *)&buffer[33], MP3_ID3_ARTIST_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->artistLen);
2549 if (pFullStr != NULL) {
2550 pInfo->pArtist = rtrimN(pFullStr);
2554 debug_msg(RELEASE, "pInfo->pArtist returned =(%s), pInfo->artistLen(%d)\n", pInfo->pArtist, pInfo->artistLen);
2557 if (pInfo->tagV2Info.bAlbumMarked == false) {
2558 pFullStr = mmfile_string_convert((const char *)&buffer[63], MP3_ID3_ALBUM_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->albumLen);
2559 if (pFullStr != NULL) {
2560 pInfo->pAlbum = rtrimN(pFullStr);
2564 debug_msg(RELEASE, "pInfo->pAlbum returned =(%s), pInfo->albumLen(%d)\n", pInfo->pAlbum, pInfo->albumLen);
2567 if (pInfo->tagV2Info.bYearMarked == false) {
2569 pInfo->pYear = mmfile_string_convert((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->yearLen);
2571 debug_msg(RELEASE, "pInfo->pYear returned =(%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
2573 if (pInfo->pYear == NULL) { /*Use same logic with ffmpeg*/
2574 pInfo->pYear = get_string((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, (int *)&pInfo->yearLen);
2575 debug_msg(RELEASE, "pInfo->pYear returned =(%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
2579 if (pInfo->tagV2Info.bDescriptionMarked == false) {
2580 pInfo->pComment = mmfile_string_convert((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->commentLen);
2581 debug_msg(RELEASE, "pInfo->pComment returned =(%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
2583 if (pInfo->pComment == NULL) { /*Use same logic with ffmpeg*/
2584 pInfo->pComment = get_string((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, (int *)&pInfo->commentLen);
2585 debug_msg(RELEASE, "pInfo->pComment returned =(%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
2589 if (pInfo->tagV2Info.bTrackNumMarked == false) {
2590 pInfo->pTrackNum = mmfile_malloc(5);
2591 if (pInfo->pTrackNum != NULL) {
2592 pInfo->pTrackNum[4] = 0;
2593 snprintf(pInfo->pTrackNum, 4, "%04d", (int)buffer[126]);
2594 pInfo->tracknumLen = strlen(pInfo->pTrackNum);
2596 debug_msg(RELEASE, "pInfo->pTrackNum returned =(%s), pInfo->tracknumLen(%d)\n", pInfo->pTrackNum, pInfo->tracknumLen);
2600 if (pInfo->tagV2Info.bGenreMarked == false) {
2601 pInfo->genre = buffer[127];
2602 debug_msg(RELEASE, "pInfo->genre returned genre number (%d)\n", pInfo->genre);
2609 bool mm_file_id3tag_parse_v222(AvFileContentInfo *pInfo, unsigned char *buffer)
2611 unsigned long taglen = 0;
2612 unsigned long needToloopv2taglen;
2613 unsigned long oneFrameLen = 0;
2614 unsigned long v2numOfFrames = 0;
2615 unsigned long curPos = 0;
2617 unsigned char *pExtContent = NULL;
2618 unsigned long purelyFramelen = 0;
2619 unsigned int encodingOffSet = 0;
2620 int inx = 0, realCpyFrameNum = 0,
2621 /*checkImgMimeTypeMax = 0, */checkImgExtMax = 0,
2622 imgstartOffset = 0, tmp = 0;
2624 int textEncodingType = 0;
2626 char **charset_array = NULL;
2628 make_characterset_array(&charset_array);
2630 init_content_info(pInfo);
2632 taglen = pInfo->tagV2Info.tagLen;
2633 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
2634 curPos = MP3_TAGv2_HEADER_LEN;
2636 debug_msg(RELEASE, "ID3tag v222--------------------------------------------------------------\n");
2638 if (needToloopv2taglen - MP3_TAGv2_22_TXT_HEADER_LEN > MP3_TAGv2_22_TXT_HEADER_LEN) {
2640 while (needToloopv2taglen > MP3_TAGv2_22_TXT_HEADER_LEN) {
2641 if ((buffer[curPos] < '0' || buffer[curPos] > 'Z') || (buffer[curPos + 1] < '0' || buffer[curPos + 1] > 'Z')
2642 || (buffer[curPos + 2] < '0' || buffer[curPos + 2] > 'Z'))
2645 memcpy(CompTmp, &buffer[curPos], 3);
2648 oneFrameLen = MP3_TAGv2_22_TXT_HEADER_LEN;
2649 oneFrameLen += (unsigned long)buffer[3 + curPos] << 16 | (unsigned long)buffer[4 + curPos] << 8
2650 | (unsigned long)buffer[5 + curPos];
2651 if (oneFrameLen > taglen - curPos)
2653 purelyFramelen = oneFrameLen - MP3_TAGv2_22_TXT_HEADER_LEN;
2654 curPos += MP3_TAGv2_22_TXT_HEADER_LEN;
2656 if (oneFrameLen > MP3_TAGv2_22_TXT_HEADER_LEN && purelyFramelen <= taglen - curPos) {
2657 curPos += purelyFramelen;
2659 if (buffer[curPos - purelyFramelen] == 0x00) {
2661 textEncodingType = AV_ID3V2_ISO_8859;
2662 } else if (buffer[curPos - purelyFramelen] == 0x01) {
2664 textEncodingType = AV_ID3V2_UTF16;
2667 /*in order to deliver valid string to MP */
2668 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen))
2671 if (encodingOffSet < purelyFramelen) {
2672 realCpyFrameNum = purelyFramelen - encodingOffSet;
2673 pExtContent = mmfile_malloc(realCpyFrameNum + 3);
2675 if (pExtContent == NULL) {
2676 debug_error(DEBUG, "out of memory for pExtContent\n");
2680 memset(pExtContent, '\0', realCpyFrameNum + 3);
2682 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
2684 if (realCpyFrameNum > 0) {
2685 if (strncmp((char *)CompTmp, "TT2", 3) == 0 && pInfo->tagV2Info.bTitleMarked == false) {
2686 pInfo->pTitle = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->titleLen);
2687 debug_msg(RELEASE, "pInfo->pTitle returned = (%s), pInfo->titleLen(%d)\n", pInfo->pTitle, pInfo->titleLen);
2688 pInfo->tagV2Info.bTitleMarked = true;
2689 } else if (strncmp((char *)CompTmp, "TP1", 3) == 0 && pInfo->tagV2Info.bArtistMarked == false) {
2690 pInfo->pArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->artistLen);
2691 debug_msg(RELEASE, "pInfo->pArtist returned = (%s), pInfo->artistLen(%d)\n", pInfo->pArtist, pInfo->artistLen);
2692 pInfo->tagV2Info.bArtistMarked = true;
2693 } else if (strncmp((char *)CompTmp, "TP2", 3) == 0 && pInfo->tagV2Info.bAlbum_ArtistMarked == false) {
2694 pInfo->pAlbum_Artist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->album_artistLen);
2695 debug_msg(RELEASE, "pInfo->pAlbum_Artist returned = (%s), pInfo->album_artistLen(%d)\n", pInfo->pAlbum_Artist, pInfo->album_artistLen);
2696 pInfo->tagV2Info.bAlbum_ArtistMarked = true;
2697 } else if (strncmp((char *)CompTmp, "TP3", 3) == 0 && pInfo->tagV2Info.bConductorMarked == false) {
2698 pInfo->pConductor = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->conductorLen);
2699 debug_msg(RELEASE, "pInfo->pConductor returned = (%s), pInfo->conductorLen(%d)\n", pInfo->pConductor, pInfo->conductorLen);
2700 pInfo->tagV2Info.bConductorMarked = true;
2701 } else if (strncmp((char *)CompTmp, "TAL", 3) == 0 && pInfo->tagV2Info.bAlbumMarked == false) {
2702 pInfo->pAlbum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->albumLen);
2703 debug_msg(RELEASE, "pInfo->pAlbum returned = (%s), pInfo->albumLen(%d)\n", pInfo->pAlbum, pInfo->albumLen);
2704 pInfo->tagV2Info.bAlbumMarked = true;
2705 } else if (strncmp((char *)CompTmp, "TYE", 3) == 0 && pInfo->tagV2Info.bYearMarked == false) {
2706 pInfo->pYear = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->yearLen);
2707 debug_msg(RELEASE, "pInfo->pYear returned = (%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
2708 pInfo->tagV2Info.bYearMarked = true;
2709 } else if (strncmp((char *)CompTmp, "COM", 3) == 0 && pInfo->tagV2Info.bDescriptionMarked == false) {
2710 /*skip language data! */
2711 if (realCpyFrameNum > 4) {
2712 realCpyFrameNum -= 4;
2715 /*pExtContent[tmp+1] value should't have encoding value */
2716 if (pExtContent[tmp] > 0x20 && (pExtContent[tmp - 1] == 0x00 || pExtContent[tmp - 1] == 0x01)) {
2717 if (pExtContent[tmp - 1] == 0x00)
2718 textEncodingType = AV_ID3V2_ISO_8859;
2720 textEncodingType = AV_ID3V2_UTF16;
2722 pInfo->pComment = mmfile_string_convert((char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->commentLen);
2723 debug_msg(RELEASE, "pInfo->pComment returned = (%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
2724 pInfo->tagV2Info.bDescriptionMarked = true;
2726 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: failed to get Comment Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
2729 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
2733 } else if (strncmp((char *)CompTmp, "TCO", 3) == 0 && pInfo->tagV2Info.bGenreMarked == false) {
2734 pInfo->pGenre = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->genreLen);
2735 debug_msg(RELEASE, "pInfo->pGenre returned = (%s), pInfo->genreLen(%d)\n", pInfo->pGenre, pInfo->genreLen);
2737 if ((pInfo->pGenre != NULL) && (pInfo->genreLen > 0)) {
2741 ret = is_numeric(pInfo->pGenre, pInfo->genreLen);
2744 ret = safe_atoi(pInfo->pGenre, &int_genre);
2746 debug_msg(RELEASE, "genre information is inteager [%d]\n", int_genre);
2748 /*Change int to string */
2749 if ((0 <= int_genre) && (int_genre < GENRE_COUNT - 1)) {
2750 /*save genreinfo like "(123)". mm_file_id3tag_restore_content_info convert it to string*/
2751 char tmp_genre[6] = {0, }; /*ex. "(123)+NULL"*/
2752 int tmp_genre_len = 0;
2754 memset(tmp_genre, 0, 6);
2755 snprintf(tmp_genre, sizeof(tmp_genre), "(%d)", int_genre);
2757 tmp_genre_len = strlen(tmp_genre);
2758 if (tmp_genre_len > 0) {
2759 mmfile_free(pInfo->pGenre);
2760 pInfo->pGenre = mmfile_malloc(sizeof(char) * (tmp_genre_len + 1));
2761 if (pInfo->pGenre) {
2762 SAFE_STRLCPY(pInfo->pGenre, tmp_genre, tmp_genre_len + 1);
2767 debug_error(RELEASE, "genre information is wrong inteager [%s]\n", pInfo->pGenre);
2772 pInfo->tagV2Info.bGenreMarked = true;
2773 } else if (strncmp((char *)CompTmp, "TRK", 3) == 0 && pInfo->tagV2Info.bTrackNumMarked == false) {
2774 pInfo->pTrackNum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tracknumLen);
2775 debug_msg(RELEASE, "pInfo->pTrackNum returned = (%s), pInfo->tracknumLen(%d)\n", pInfo->pTrackNum, pInfo->tracknumLen);
2776 pInfo->tagV2Info.bTrackNumMarked = true;
2777 } else if (strncmp((char *)CompTmp, "TEN", 3) == 0 && pInfo->tagV2Info.bEncByMarked == false) {
2778 pInfo->pEncBy = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->encbyLen);
2779 debug_msg(RELEASE, "pInfo->pEncBy returned = (%s), pInfo->encbyLen(%d)\n", pInfo->pEncBy, pInfo->encbyLen);
2780 pInfo->tagV2Info.bEncByMarked = true;
2781 } else if (strncmp((char *)CompTmp, "WXX", 3) == 0 && pInfo->tagV2Info.bURLMarked == false) {
2782 if (realCpyFrameNum > 4) {
2783 /*skip language data! */
2784 realCpyFrameNum -= 4;
2787 /*pExtContent[tmp+1] value should't have null value */
2788 if (pExtContent[tmp] > 0x20 && (pExtContent[tmp - 1] == 0x00 || pExtContent[tmp - 1] == 0x01)) {
2789 if (pExtContent[tmp - 1] == 0x00)
2790 textEncodingType = AV_ID3V2_ISO_8859;
2792 textEncodingType = AV_ID3V2_UTF16;
2794 pInfo->pURL = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->urlLen);
2795 debug_msg(RELEASE, "pInfo->pURL returned = (%s), pInfo->urlLen(%d)\n", pInfo->pURL, pInfo->urlLen);
2796 pInfo->tagV2Info.bURLMarked = true;
2798 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: failed to get URL Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
2801 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: URL info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
2804 } else if (strncmp((char *)CompTmp, "TCR", 3) == 0 && pInfo->tagV2Info.bCopyRightMarked == false) {
2805 pInfo->pCopyright = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->copyrightLen);
2806 debug_msg(RELEASE, "pInfo->pCopyright returned = (%s), pInfo->copyrightLen(%d)\n", pInfo->pCopyright, pInfo->copyrightLen);
2807 pInfo->tagV2Info.bCopyRightMarked = true;
2808 } else if (strncmp((char *)CompTmp, "TOA", 3) == 0 && pInfo->tagV2Info.bOriginArtistMarked == false) {
2809 pInfo->pOriginArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->originartistLen);
2810 debug_msg(RELEASE, "pInfo->pOriginArtist returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pOriginArtist, pInfo->originartistLen);
2811 pInfo->tagV2Info.bOriginArtistMarked = true;
2812 } else if (strncmp((char *)CompTmp, "TCM", 3) == 0 && pInfo->tagV2Info.bComposerMarked == false) {
2813 pInfo->pComposer = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->composerLen);
2814 debug_msg(RELEASE, "pInfo->pComposer returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pComposer, pInfo->composerLen);
2815 pInfo->tagV2Info.bComposerMarked = true;
2816 } else if (strncmp((char *)CompTmp, "TRD", 3) == 0 && pInfo->tagV2Info.bRecDateMarked == false) {
2817 pInfo->pRecDate = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->recdateLen);
2818 debug_msg(RELEASE, "pInfo->pRecDate returned = (%s), pInfo->recdateLen(%d)\n", pInfo->pRecDate, pInfo->recdateLen);
2819 pInfo->tagV2Info.bRecDateMarked = true;
2820 } else if (strncmp((char *)CompTmp, "PIC", 3) == 0 && pInfo->tagV2Info.bImageMarked == false && realCpyFrameNum <= 2000000) {
2821 if (pExtContent[0] != 0) {
2822 for (inx = 0; inx < MP3_ID3_IMAGE_EXT_MAX_LENGTH; inx++)
2823 pInfo->imageInfo.imageExt[inx] = '\0';/*ini mimetype variable */
2825 while ((checkImgExtMax < MP3_ID3_IMAGE_EXT_MAX_LENGTH - 1) && pExtContent[checkImgExtMax] != '\0') {
2826 pInfo->imageInfo.imageExt[checkImgExtMax] = pExtContent[checkImgExtMax];
2830 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: PIC image's not included to image Extention\n");
2833 imgstartOffset += checkImgExtMax;
2835 if (pExtContent[imgstartOffset] < AV_ID3V2_PICTURE_TYPE_MAX) {
2836 pInfo->imageInfo.pictureType = pExtContent[imgstartOffset];
2838 imgstartOffset++;/*PictureType(1byte) */
2840 if (pExtContent[imgstartOffset] != 0x0) {
2843 int new_dis_len = 0;
2844 char *tmp_desc = NULL;
2847 if (pExtContent[imgstartOffset + cur_pos] == '\0') {
2848 if (realCpyFrameNum < imgstartOffset + cur_pos) {
2849 debug_error(DEBUG, "End of APIC Tag %d %d %d\n", realCpyFrameNum, imgstartOffset, cur_pos);
2852 /*check end of image description*/
2853 if ((pExtContent[imgstartOffset + cur_pos + 1] == gTagJPEGHeader[0]) ||
2854 (pExtContent[imgstartOffset + cur_pos + 1] == gTagPNGHeader[0])) {
2855 debug_msg(RELEASE, "length of description (%d)", cur_pos);
2862 dis_len = cur_pos + 1;
2864 tmp_desc = mmfile_malloc(sizeof(char) * dis_len);
2866 if (tmp_desc != NULL) {
2867 memcpy(tmp_desc, pExtContent + imgstartOffset, dis_len);
2869 /*convert description*/
2870 pInfo->imageInfo.imageDescription = mmfile_string_convert(tmp_desc, dis_len, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&new_dis_len);
2871 mmfile_free(tmp_desc);
2872 debug_msg(RELEASE, "new_desc %s(%d)\n", pInfo->imageInfo.imageDescription, new_dis_len);
2873 pInfo->imageInfo.imgDesLen = new_dis_len; /**/
2876 imgstartOffset += cur_pos;
2878 pInfo->imageInfo.imgDesLen = 0;
2881 if ((pExtContent[imgstartOffset] == '\0') && (realCpyFrameNum - imgstartOffset > 0)) {
2882 imgstartOffset++; /* endofDesceriptionType(1byte) */
2884 while (pExtContent[imgstartOffset] == '\0') { /*some content has useless '\0' in front of picture data */
2888 debug_msg(RELEASE, "after scaning imgDescription imgstartOffset(%d) value!\n", imgstartOffset);
2890 if (realCpyFrameNum - imgstartOffset > 0) {
2891 pInfo->imageInfo.imageLen = realCpyFrameNum - imgstartOffset;
2892 pInfo->imageInfo.pImageBuf = mmfile_malloc(pInfo->imageInfo.imageLen + 1);
2894 if (pInfo->imageInfo.pImageBuf != NULL) {
2895 memcpy(pInfo->imageInfo.pImageBuf, pExtContent + imgstartOffset, pInfo->imageInfo.imageLen);
2896 pInfo->imageInfo.pImageBuf[pInfo->imageInfo.imageLen] = 0;
2899 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
2900 pInfo->imageInfo.bURLInfo = true; /*if mimetype is "-->", image date has an URL */
2902 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - imgstartOffset(%d)\n", realCpyFrameNum, imgstartOffset);
2906 /*checkImgMimeTypeMax = 0;*/
2910 pInfo->tagV2Info.bImageMarked = true;
2917 curPos += purelyFramelen;
2918 if (purelyFramelen != 0)
2919 needToloopv2taglen = MP3_TAGv2_22_TXT_HEADER_LEN;
2922 mmfile_free(pExtContent);
2923 memset(CompTmp, 0, 4);
2924 if (curPos < taglen) {
2925 needToloopv2taglen -= oneFrameLen;
2928 needToloopv2taglen = MP3_TAGv2_22_TXT_HEADER_LEN;
2931 realCpyFrameNum = 0;
2932 textEncodingType = 0;
2938 release_characterset_array(charset_array);
2948 bool mm_file_id3tag_parse_v223(AvFileContentInfo *pInfo, unsigned char *buffer)
2950 unsigned long taglen = 0;
2951 unsigned long needToloopv2taglen;
2952 unsigned long oneFrameLen = 0;
2953 unsigned long v2numOfFrames = 0;
2954 unsigned long curPos = 0;
2956 unsigned char *pExtContent = NULL;
2957 unsigned long purelyFramelen = 0;
2958 unsigned int encodingOffSet = 0;
2959 int inx = 0, realCpyFrameNum = 0, checkImgMimeTypeMax = 0, imgstartOffset = 0, tmp = 0;
2960 unsigned int textEncodingType = 0;
2961 char **charset_array = NULL;
2962 const char *MIME_PRFIX = "image/";
2964 make_characterset_array(&charset_array);
2966 init_content_info(pInfo);
2968 taglen = pInfo->tagV2Info.tagLen;
2969 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
2970 curPos = MP3_TAGv2_HEADER_LEN;
2972 debug_msg(RELEASE, "ID3tag v223--------------------------------------------------------------\n");
2974 /* check Extended Header */
2975 if (buffer[5] & 0x40) {
2976 /* if extended header exists, skip it*/
2977 int extendedHeaderLen = (unsigned long)buffer[10] << 21 | (unsigned long)buffer[11] << 14 | (unsigned long)buffer[12] << 7 | (unsigned long)buffer[13];
2979 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d\n", extendedHeaderLen);
2981 if (extendedHeaderLen > (int)(taglen - curPos)) {
2982 debug_error(DEBUG, "extended header too long.\n");
2984 curPos += extendedHeaderLen;
2989 if (needToloopv2taglen - MP3_TAGv2_23_TXT_HEADER_LEN > MP3_TAGv2_23_TXT_HEADER_LEN) {
2991 while (needToloopv2taglen > MP3_TAGv2_23_TXT_HEADER_LEN) {
2992 if ((buffer[curPos] < '0' || buffer[curPos] > 'Z') || (buffer[curPos + 1] < '0' || buffer[curPos + 1] > 'Z')
2993 || (buffer[curPos + 2] < '0' || buffer[curPos + 2] > 'Z') || (buffer[curPos + 3] < '0' || buffer[curPos + 3] > 'Z'))
2996 memcpy(CompTmp, &buffer[curPos], 4);
2999 oneFrameLen = MP3_TAGv2_23_TXT_HEADER_LEN;
3000 oneFrameLen += (unsigned long)buffer[4 + curPos] << 24 | (unsigned long)buffer[5 + curPos] << 16
3001 | (unsigned long)buffer[6 + curPos] << 8 | (unsigned long)buffer[7 + curPos];
3003 debug_msg(RELEASE, "----------------------------------------------------------------------------------------------------\n");
3005 if (oneFrameLen > taglen - curPos)
3008 purelyFramelen = oneFrameLen - MP3_TAGv2_23_TXT_HEADER_LEN;
3009 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3011 if (oneFrameLen > MP3_TAGv2_23_TXT_HEADER_LEN && purelyFramelen <= taglen - curPos) {
3012 curPos += purelyFramelen;
3014 if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen))) {
3016 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3017 textEncodingType = AV_ID3V2_UTF16;
3018 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen))) {
3020 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3021 textEncodingType = AV_ID3V2_UTF16_BE;
3022 } else if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen + 1))) {
3024 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3025 textEncodingType = AV_ID3V2_UTF16;
3026 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen + 1))) {
3028 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3029 textEncodingType = AV_ID3V2_UTF16_BE;
3031 if (buffer[curPos - purelyFramelen + encodingOffSet] == 0x00) {
3032 debug_msg(RELEASE, "encodingOffset will be set to 1\n");
3035 debug_msg(RELEASE, "Finding encodingOffset\n");
3037 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen)) /* text string encoded by ISO-8859-1 */
3040 textEncodingType = AV_ID3V2_ISO_8859;
3041 debug_msg(RELEASE, "this text string(%s) encoded by ISO-8859-1 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3044 mmfile_free(pExtContent);
3046 if (encodingOffSet < purelyFramelen) {
3047 realCpyFrameNum = purelyFramelen - encodingOffSet;
3048 pExtContent = mmfile_malloc(realCpyFrameNum + 3);
3050 if (pExtContent == NULL) {
3051 debug_msg(DEBUG, "pExtContent malloc failed\n");
3055 memset(pExtContent, '\0', realCpyFrameNum + 3);
3057 if (textEncodingType != AV_ID3V2_UTF16 && textEncodingType != AV_ID3V2_UTF16_BE) {
3058 if (CompTmp[0] == 'T' || (strcmp(CompTmp, "APIC") == 0)) {
3059 debug_msg(RELEASE, "get the new text ecoding type\n");
3060 textEncodingType = buffer[curPos - purelyFramelen + encodingOffSet - 1];
3064 if (textEncodingType > AV_ID3V2_MAX) {
3065 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%d], FRAME[%s]\n", textEncodingType, (char *)CompTmp);
3069 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
3070 if (realCpyFrameNum > 0) {
3071 if (strncmp((char *)CompTmp, "TIT2", 4) == 0 && pInfo->tagV2Info.bTitleMarked == false) {
3072 pInfo->pTitle = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->titleLen);
3073 debug_msg(RELEASE, "pInfo->pTitle returned = (%s), pInfo->titleLen(%d)\n", pInfo->pTitle, pInfo->titleLen);
3074 pInfo->tagV2Info.bTitleMarked = true;
3076 } else if (strncmp((char *)CompTmp, "TPE1", 4) == 0 && pInfo->tagV2Info.bArtistMarked == false) {
3077 pInfo->pArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->artistLen);
3078 debug_msg(RELEASE, "pInfo->pArtist returned = (%s), pInfo->artistLen(%d)\n", pInfo->pArtist, pInfo->artistLen);
3079 pInfo->tagV2Info.bArtistMarked = true;
3080 } else if (strncmp((char *)CompTmp, "TPE2", 4) == 0 && pInfo->tagV2Info.bAlbum_ArtistMarked == false) {
3081 pInfo->pAlbum_Artist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->album_artistLen);
3082 debug_msg(RELEASE, "pInfo->pAlbum_Artist returned = (%s), pInfo->album_artistLen(%d)\n", pInfo->pAlbum_Artist, pInfo->album_artistLen);
3083 pInfo->tagV2Info.bAlbum_ArtistMarked = true;
3084 } else if (strncmp((char *)CompTmp, "TPE3", 4) == 0 && pInfo->tagV2Info.bConductorMarked == false) {
3085 pInfo->pConductor = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->conductorLen);
3086 debug_msg(RELEASE, "pInfo->pConductor returned = (%s), pInfo->conductorLen(%d)\n", pInfo->pConductor, pInfo->conductorLen);
3087 pInfo->tagV2Info.bConductorMarked = true;
3088 } else if (strncmp((char *)CompTmp, "TALB", 4) == 0 && pInfo->tagV2Info.bAlbumMarked == false) {
3089 pInfo->pAlbum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->albumLen);
3090 debug_msg(RELEASE, "pInfo->pAlbum returned = (%s), pInfo->albumLen(%d)\n", pInfo->pAlbum, pInfo->albumLen);
3091 pInfo->tagV2Info.bAlbumMarked = true;
3092 } else if (strncmp((char *)CompTmp, "TYER", 4) == 0 && pInfo->tagV2Info.bYearMarked == false) {
3093 pInfo->pYear = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->yearLen);
3094 debug_msg(RELEASE, "pInfo->pYear returned = (%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
3095 pInfo->tagV2Info.bYearMarked = true;
3096 } else if (strncmp((char *)CompTmp, "COMM", 4) == 0 && pInfo->tagV2Info.bDescriptionMarked == false) {
3097 if (realCpyFrameNum > 3) {
3098 realCpyFrameNum -= 3;
3101 /*pExtContent[tmp+1] value should't have encoding value */
3102 if (pExtContent[tmp] == 0x00 || pExtContent[tmp] == 0xFF || pExtContent[tmp] == 0xFE) {
3103 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3104 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3105 realCpyFrameNum -= 4;
3109 if (IS_ENCODEDBY_UTF16(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3110 realCpyFrameNum -= 2;
3112 textEncodingType = AV_ID3V2_UTF16;
3113 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3114 realCpyFrameNum -= 2;
3116 textEncodingType = AV_ID3V2_UTF16_BE;
3117 } else if (IS_ENCODEDBY_UTF16(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3118 realCpyFrameNum -= 3;
3120 textEncodingType = AV_ID3V2_UTF16;
3121 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3122 realCpyFrameNum -= 3;
3124 textEncodingType = AV_ID3V2_UTF16_BE;
3126 debug_msg(RELEASE, "pInfo->pComment Never Get Here!!\n");
3129 while ((pExtContent[tmp] < 0x20) && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3133 textEncodingType = AV_ID3V2_ISO_8859;
3136 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3137 pInfo->pComment = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->commentLen);
3139 debug_msg(RELEASE, "failed to get Comment Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3140 pInfo->commentLen = 0;
3143 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3144 pInfo->commentLen = 0;
3148 debug_msg(RELEASE, "pInfo->pComment returned = (%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
3149 pInfo->tagV2Info.bDescriptionMarked = true;
3150 } else if (strncmp((char *)CompTmp, "SYLT", 4) == 0 && pInfo->tagV2Info.bSyncLyricsMarked == false) {
3153 int copy_start_pos = tmp;
3154 AvSynclyricsInfo *synclyrics_info = NULL;
3155 GList *synclyrics_info_list = NULL;
3157 if (realCpyFrameNum > 5) {
3158 realCpyFrameNum -= 5;
3161 /*pExtContent[tmp+1] value should't have encoding value */
3162 if (pExtContent[tmp] == 0x00 || pExtContent[tmp] == 0xFF || pExtContent[tmp] == 0xFE) {
3163 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3164 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3165 realCpyFrameNum -= 4;
3169 if (IS_ENCODEDBY_UTF16(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3170 realCpyFrameNum -= 2;
3172 textEncodingType = AV_ID3V2_UTF16;
3173 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3174 realCpyFrameNum -= 2;
3176 textEncodingType = AV_ID3V2_UTF16_BE;
3177 } else if (IS_ENCODEDBY_UTF16(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3178 realCpyFrameNum -= 3;
3180 textEncodingType = AV_ID3V2_UTF16;
3181 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3182 realCpyFrameNum -= 3;
3184 textEncodingType = AV_ID3V2_UTF16_BE;
3186 debug_msg(RELEASE, "pInfo->pSyncLyrics Never Get Here!!\n");
3189 while ((pExtContent[tmp] < 0x20) && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3193 textEncodingType = AV_ID3V2_ISO_8859;
3196 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3198 if (realCpyFrameNum < MMFILE_SYNC_LYRIC_INFO_MIN_LEN) {
3199 debug_msg(RELEASE, "failed to get Synchronised lyrics Info realCpyFramNum(%d)\n", realCpyFrameNum);
3200 pInfo->syncLyricsNum = 0;
3202 if (textEncodingType == AV_ID3V2_UTF16) {
3203 debug_warning(DEBUG, "[AV_ID3V2_UTF16] not implemented\n");
3204 } else if (textEncodingType == AV_ID3V2_UTF16_BE) {
3205 debug_warning(DEBUG, "[AV_ID3V2_UTF16_BE] not implemented\n");
3207 for (idx = 0; idx < realCpyFrameNum; idx++) {
3208 if (pExtContent[tmp + idx] == 0x00) {
3209 synclyrics_info = (AvSynclyricsInfo *)malloc(sizeof(AvSynclyricsInfo));
3211 if (synclyrics_info != NULL) {
3212 if (textEncodingType == AV_ID3V2_UTF8) {
3213 synclyrics_info->lyric_info = mmfile_malloc(copy_len + 1);
3214 if (synclyrics_info->lyric_info != NULL) {
3215 memset(synclyrics_info->lyric_info, 0, copy_len + 1);
3216 memcpy(synclyrics_info->lyric_info, pExtContent + copy_start_pos, copy_len);
3217 synclyrics_info->lyric_info[copy_len] = '\0';
3220 synclyrics_info->lyric_info = mmfile_string_convert((const char *)&pExtContent[copy_start_pos], copy_len, "UTF-8", charset_array[AV_ID3V2_ISO_8859], NULL, NULL);
3223 synclyrics_info->time_info = (unsigned long)pExtContent[tmp + idx + 1] << 24 | (unsigned long)pExtContent[tmp + idx + 2] << 16 | (unsigned long)pExtContent[tmp + idx + 3] << 8 | (unsigned long)pExtContent[tmp + idx + 4];
3225 copy_start_pos = tmp + idx + 1;
3226 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);
3228 synclyrics_info_list = g_list_append(synclyrics_info_list, synclyrics_info);
3233 pInfo->pSyncLyrics = synclyrics_info_list;
3234 pInfo->syncLyricsNum = g_list_length(pInfo->pSyncLyrics);
3238 debug_msg(RELEASE, "failed to get Synchronised lyrics Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3239 pInfo->syncLyricsNum = 0;
3242 debug_msg(RELEASE, "Synchronised lyrics too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3243 pInfo->syncLyricsNum = 0;
3247 //debug_msg(RELEASE, "pInfo->pSyncLyrics returned = (%s), pInfo->syncLyricsNum(%d)\n", pInfo->pSyncLyrics, pInfo->syncLyricsNum);
3248 debug_msg(RELEASE, "pInfo->syncLyricsNum(%d)\n", pInfo->syncLyricsNum);
3249 pInfo->tagV2Info.bSyncLyricsMarked = true;
3250 } else if (strncmp((char *)CompTmp, "USLT", 4) == 0 && pInfo->tagV2Info.bUnsyncLyricsMarked == false) {
3251 char *lang_info = strndup((char *)pExtContent, 3);
3253 if (realCpyFrameNum > 3) {
3254 realCpyFrameNum -= 3;
3257 /*find start of lyrics */
3259 if (pExtContent[tmp] == 0x00) {
3260 if (pExtContent[tmp + 1] == 0x00) {
3261 realCpyFrameNum -= 2;
3271 /*pExtContent[tmp+1] value should't have encoding value */
3272 debug_msg(RELEASE, "tpExtContent[%d] %x\n", tmp, pExtContent[tmp]);
3274 if (pExtContent[tmp] == 0x00 || pExtContent[tmp] == 0xFF || pExtContent[tmp] == 0xFE) {
3275 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3276 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3277 realCpyFrameNum -= 4;
3281 if (IS_ENCODEDBY_UTF16(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3282 realCpyFrameNum -= 2;
3284 textEncodingType = AV_ID3V2_UTF16;
3285 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3286 realCpyFrameNum -= 2;
3288 textEncodingType = AV_ID3V2_UTF16_BE;
3289 } else if (IS_ENCODEDBY_UTF16(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3290 realCpyFrameNum -= 3;
3292 textEncodingType = AV_ID3V2_UTF16;
3293 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3294 realCpyFrameNum -= 3;
3296 textEncodingType = AV_ID3V2_UTF16_BE;
3298 debug_msg(RELEASE, "pInfo->pUnsyncLyrics Never Get Here!!\n");
3301 while ((pExtContent[tmp] < 0x20) && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3305 textEncodingType = AV_ID3V2_ISO_8859;
3308 char *char_set = NULL;
3310 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3312 if (textEncodingType == AV_ID3V2_ISO_8859) {
3313 if (lang_info != NULL && !strcasecmp(lang_info, "KOR")) {
3314 char_set = strdup("EUC-KR");
3316 char_set = mmfile_get_charset((const char *)&pExtContent[tmp]);
3318 mmfile_free(lang_info);
3321 if (char_set == NULL) {
3322 pInfo->pUnsyncLyrics = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->unsynclyricsLen);
3324 pInfo->pUnsyncLyrics = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", char_set, NULL, (unsigned int *)&pInfo->unsynclyricsLen);
3325 mmfile_free(char_set);
3328 debug_msg(RELEASE, "failed to get Unsynchronised lyrics Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3329 pInfo->unsynclyricsLen = 0;
3332 debug_msg(RELEASE, "Unsynchronised lyrics too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3333 pInfo->unsynclyricsLen = 0;
3337 debug_msg(RELEASE, "pInfo->pUnsyncLyrics returned = (%s), pInfo->unsynclyricsLen(%d)\n", pInfo->pUnsyncLyrics, pInfo->unsynclyricsLen);
3338 pInfo->tagV2Info.bUnsyncLyricsMarked = true;
3339 mmfile_free(lang_info);
3340 } else if (strncmp((char *)CompTmp, "TCON", 4) == 0 && pInfo->tagV2Info.bGenreMarked == false) {
3341 pInfo->pGenre = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->genreLen);
3342 debug_msg(RELEASE, "pInfo->pGenre returned = (%s), pInfo->genreLen(%d)\n", pInfo->pGenre, pInfo->genreLen);
3344 if ((pInfo->pGenre != NULL) && (pInfo->genreLen > 0)) {
3348 ret = is_numeric(pInfo->pGenre, pInfo->genreLen);
3351 ret = safe_atoi(pInfo->pGenre, &int_genre);
3353 debug_msg(RELEASE, "genre information is inteager [%d]\n", int_genre);
3355 /*Change int to string */
3356 if ((0 <= int_genre) && (int_genre < GENRE_COUNT - 1)) {
3357 /*save genreinfo like "(123)". mm_file_id3tag_restore_content_info convert it to string*/
3358 char tmp_genre[6] = {0, }; /*ex. "(123)+NULL"*/
3359 int tmp_genre_len = 0;
3361 memset(tmp_genre, 0, 6);
3362 snprintf(tmp_genre, sizeof(tmp_genre), "(%d)", int_genre);
3364 tmp_genre_len = strlen(tmp_genre);
3365 if (tmp_genre_len > 0) {
3366 mmfile_free(pInfo->pGenre);
3367 pInfo->pGenre = mmfile_malloc(sizeof(char) * (tmp_genre_len + 1));
3368 if (pInfo->pGenre) {
3369 SAFE_STRLCPY(pInfo->pGenre, tmp_genre, tmp_genre_len + 1);
3374 debug_msg(RELEASE, "genre information is wrong inteager [%s]\n", pInfo->pGenre);
3379 pInfo->tagV2Info.bGenreMarked = true;
3380 } else if (strncmp((char *)CompTmp, "TRCK", 4) == 0 && pInfo->tagV2Info.bTrackNumMarked == false) {
3381 pInfo->pTrackNum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tracknumLen);
3382 debug_msg(RELEASE, "pInfo->pTrackNum returned = (%s), pInfo->tracknumLen(%d)\n", pInfo->pTrackNum, pInfo->tracknumLen);
3383 pInfo->tagV2Info.bTrackNumMarked = true;
3384 } else if (strncmp((char *)CompTmp, "TENC", 4) == 0 && pInfo->tagV2Info.bEncByMarked == false) {
3385 pInfo->pEncBy = mmfile_string_convert((char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->encbyLen);
3386 debug_msg(RELEASE, "pInfo->pEncBy returned = (%s), pInfo->encbyLen(%d)\n", pInfo->pEncBy, pInfo->encbyLen);
3387 pInfo->tagV2Info.bEncByMarked = true;
3388 } else if (strncmp((char *)CompTmp, "WXXX", 4) == 0 && pInfo->tagV2Info.bURLMarked == false) {
3389 pInfo->pURL = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->urlLen);
3390 debug_msg(RELEASE, "pInfo->pURL returned = (%s), pInfo->urlLen(%d)\n", pInfo->pURL, pInfo->urlLen);
3391 pInfo->tagV2Info.bURLMarked = true;
3392 } else if (strncmp((char *)CompTmp, "TCOP", 4) == 0 && pInfo->tagV2Info.bCopyRightMarked == false) {
3393 pInfo->pCopyright = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->copyrightLen);
3394 debug_msg(RELEASE, "pInfo->pCopyright returned = (%s), pInfo->copyrightLen(%d)\n", pInfo->pCopyright, pInfo->copyrightLen);
3395 pInfo->tagV2Info.bCopyRightMarked = true;
3396 } else if (strncmp((char *)CompTmp, "TOPE", 4) == 0 && pInfo->tagV2Info.bOriginArtistMarked == false) {
3397 pInfo->pOriginArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->originartistLen);
3398 debug_msg(RELEASE, "pInfo->pOriginArtist returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pOriginArtist, pInfo->originartistLen);
3399 pInfo->tagV2Info.bOriginArtistMarked = true;
3400 } else if (strncmp((char *)CompTmp, "TCOM", 4) == 0 && pInfo->tagV2Info.bComposerMarked == false) {
3401 pInfo->pComposer = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->composerLen);
3402 debug_msg(RELEASE, "pInfo->pComposer returned = (%s), pInfo->composerLen(%d)\n", pInfo->pComposer, pInfo->composerLen);
3403 pInfo->tagV2Info.bComposerMarked = true;
3404 } else if (strncmp((char *)CompTmp, "TRDA", 4) == 0 && pInfo->tagV2Info.bRecDateMarked == false) {
3405 pInfo->pRecDate = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->recdateLen);
3406 debug_msg(RELEASE, "pInfo->pRecDate returned = (%s), pInfo->recdateLen(%d)\n", pInfo->pRecDate, pInfo->recdateLen);
3407 pInfo->tagV2Info.bRecDateMarked = true;
3408 } else if (strncmp((char *)CompTmp, "APIC", 4) == 0 && pInfo->tagV2Info.bImageMarked == false && realCpyFrameNum <= 2000000) {
3409 debug_msg(DEBUG, "text encoding %d \n", textEncodingType);
3411 if (pExtContent[0] != '\0') {
3412 for (inx = 0; inx < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1; inx++)
3413 pInfo->imageInfo.imageMIMEType[inx] = '\0';/*ini mimetype variable */
3415 while ((checkImgMimeTypeMax < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1) && pExtContent[checkImgMimeTypeMax] != '\0') {
3416 pInfo->imageInfo.imageMIMEType[checkImgMimeTypeMax] = pExtContent[checkImgMimeTypeMax];
3417 checkImgMimeTypeMax++;
3419 pInfo->imageInfo.imgMimetypeLen = checkImgMimeTypeMax;
3421 pInfo->imageInfo.imgMimetypeLen = 0;
3422 debug_msg(RELEASE, "APIC image's not included to MIME type\n");
3425 imgstartOffset += checkImgMimeTypeMax;
3427 if (strncmp(pInfo->imageInfo.imageMIMEType, MIME_PRFIX, strlen(MIME_PRFIX)) != 0) {
3428 pInfo->imageInfo.imgMimetypeLen = 0;
3429 debug_error(DEBUG, "APIC NOT VALID");
3433 if ((pExtContent[imgstartOffset] == '\0') && (realCpyFrameNum - imgstartOffset > 0)) {
3434 imgstartOffset++;/*endofMIME(1byte) */
3435 debug_msg(RELEASE, "after scaning Mime type imgstartOffset(%d) value!\n", imgstartOffset);
3437 if (pExtContent[imgstartOffset] < AV_ID3V2_PICTURE_TYPE_MAX) {
3438 pInfo->imageInfo.pictureType = pExtContent[imgstartOffset];
3440 debug_msg(RELEASE, "APIC image has invalid picture type(0x%x)\n", pExtContent[imgstartOffset]);
3442 imgstartOffset++;/*PictureType(1byte) */
3443 debug_msg(RELEASE, "after scaning PictureType imgstartOffset(%d) value!\n", imgstartOffset);
3445 if (pExtContent[imgstartOffset] != 0x0) {
3448 int new_dis_len = 0;
3449 char *tmp_desc = NULL;
3452 if (pExtContent[imgstartOffset + cur_pos] == '\0') {
3453 if (realCpyFrameNum < imgstartOffset + cur_pos) {
3454 debug_error(DEBUG, "End of APIC Tag %d %d %d\n", realCpyFrameNum, imgstartOffset, cur_pos);
3457 /*check end of image description*/
3458 if ((pExtContent[imgstartOffset + cur_pos + 1] == gTagJPEGHeader[0]) ||
3459 (pExtContent[imgstartOffset + cur_pos + 1] == gTagPNGHeader[0])) {
3460 debug_msg(RELEASE, "length of description (%d)", cur_pos);
3467 dis_len = cur_pos + 1;
3469 tmp_desc = mmfile_malloc(sizeof(char) * dis_len);
3470 if (tmp_desc != NULL) {
3471 memcpy(tmp_desc, pExtContent + imgstartOffset, dis_len);
3473 /*convert description*/
3474 pInfo->imageInfo.imageDescription = mmfile_string_convert(tmp_desc, dis_len, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&new_dis_len);
3475 debug_msg(RELEASE, "new_desc %s(%d)\n", pInfo->imageInfo.imageDescription, new_dis_len);
3476 mmfile_free(tmp_desc);
3478 pInfo->imageInfo.imgDesLen = new_dis_len; /**/
3481 imgstartOffset += cur_pos;
3483 pInfo->imageInfo.imgDesLen = 0;
3484 debug_msg(RELEASE, "APIC image's not included to Description!!!\n");
3487 if ((pExtContent[imgstartOffset] == '\0') && (realCpyFrameNum - imgstartOffset > 0)) {
3488 imgstartOffset++; /* endofDesceriptionType(1byte) */
3490 while (pExtContent[imgstartOffset] == '\0') { /*some content has useless '\0' in front of picture data */
3494 debug_msg(RELEASE, "after scaning imgDescription imgstartOffset(%d) value!\n", imgstartOffset);
3496 if (realCpyFrameNum - imgstartOffset > 0) {
3497 pInfo->imageInfo.imageLen = realCpyFrameNum - imgstartOffset;
3498 pInfo->imageInfo.pImageBuf = mmfile_malloc(pInfo->imageInfo.imageLen + 1);
3500 if (pInfo->imageInfo.pImageBuf != NULL) {
3501 memcpy(pInfo->imageInfo.pImageBuf, pExtContent + imgstartOffset, pInfo->imageInfo.imageLen);
3502 pInfo->imageInfo.pImageBuf[pInfo->imageInfo.imageLen] = 0;
3505 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
3506 pInfo->imageInfo.bURLInfo = true; /*if mimetype is "-->", image date has an URL */
3508 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - imgstartOffset(%d)\n", realCpyFrameNum, imgstartOffset);
3510 debug_msg(RELEASE, "pInfo->imageInfo.imageLen(%d), imgstartOffset(%d)!\n", pInfo->imageInfo.imageLen, imgstartOffset);
3512 debug_msg(RELEASE, "pExtContent[imgstartOffset](%d) value should setted NULL value for end of description! realCpyFrameNum - imgstartOffset(%d)\n",
3513 pExtContent[imgstartOffset], realCpyFrameNum - imgstartOffset);
3516 debug_msg(RELEASE, "pExtContent[imgstartOffset](%d) value should setted NULL value for end of mimetype! realCpyFrameNum - imgstartOffset(%d)\n",
3517 pExtContent[imgstartOffset], realCpyFrameNum - imgstartOffset);
3520 checkImgMimeTypeMax = 0;
3523 pInfo->tagV2Info.bImageMarked = true;
3526 debug_msg(RELEASE, "CompTmp(%s) This Frame ID currently not Supports!!\n", CompTmp);
3531 debug_msg(RELEASE, "All of the pExtContent Values are NULL\n");
3534 curPos += purelyFramelen;
3535 if (purelyFramelen != 0)
3536 needToloopv2taglen = MP3_TAGv2_23_TXT_HEADER_LEN;
3538 debug_msg(RELEASE, "This Frame's size is Zero! purelyFramelen(%lu)\n", purelyFramelen);
3541 mmfile_free(pExtContent);
3542 memset(CompTmp, 0, 4);
3544 if (curPos < taglen) {
3545 needToloopv2taglen -= oneFrameLen;
3548 needToloopv2taglen = MP3_TAGv2_23_TXT_HEADER_LEN;
3551 realCpyFrameNum = 0;
3552 textEncodingType = 0;
3558 release_characterset_array(charset_array);
3568 bool mm_file_id3tag_parse_v224(AvFileContentInfo *pInfo, unsigned char *buffer)
3570 unsigned long taglen = 0;
3571 unsigned long needToloopv2taglen;
3572 unsigned long oneFrameLen = 0;
3573 unsigned long v2numOfFrames = 0;
3574 unsigned long curPos = 0;
3576 unsigned char *pExtContent = NULL;
3577 unsigned long purelyFramelen = 0;
3578 unsigned int encodingOffSet = 0;
3579 int inx = 0, realCpyFrameNum = 0, checkImgMimeTypeMax = 0, imgstartOffset = 0, tmp = 0;
3580 unsigned int textEncodingType = 0;
3581 char **charset_array = NULL;
3582 const char *MIME_PRFIX = "image/";
3584 make_characterset_array(&charset_array);
3586 init_content_info(pInfo);
3588 taglen = pInfo->tagV2Info.tagLen;
3589 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
3590 curPos = MP3_TAGv2_HEADER_LEN;
3592 debug_msg(RELEASE, "ID3tag v224--------------------------------------------------------------\n");
3594 /* check Extended Header */
3595 if (buffer[5] & 0x40) {
3596 /* if extended header exists, skip it*/
3597 int extendedHeaderLen = (unsigned long)buffer[10] << 21 | (unsigned long)buffer[11] << 14 | (unsigned long)buffer[12] << 7 | (unsigned long)buffer[13];
3599 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d\n", extendedHeaderLen);
3601 if (extendedHeaderLen > (int)(taglen - curPos)) {
3602 debug_error(DEBUG, "extended header too long.\n");
3604 curPos += extendedHeaderLen;
3608 if (needToloopv2taglen - MP3_TAGv2_23_TXT_HEADER_LEN > MP3_TAGv2_23_TXT_HEADER_LEN) {
3610 while (needToloopv2taglen > MP3_TAGv2_23_TXT_HEADER_LEN) {
3611 if ((buffer[curPos] < '0' || buffer[curPos] > 'Z') || (buffer[curPos + 1] < '0' || buffer[curPos + 1] > 'Z')
3612 || (buffer[curPos + 2] < '0' || buffer[curPos + 2] > 'Z') || (buffer[curPos + 3] < '0' || buffer[curPos + 3] > 'Z'))
3615 memcpy(CompTmp, &buffer[curPos], 4);
3618 oneFrameLen = MP3_TAGv2_23_TXT_HEADER_LEN;
3619 oneFrameLen += (unsigned long)buffer[4 + curPos] << 21 | (unsigned long)buffer[5 + curPos] << 14
3620 | (unsigned long)buffer[6 + curPos] << 7 | (unsigned long)buffer[7 + curPos];
3621 if (oneFrameLen > taglen - curPos)
3624 purelyFramelen = oneFrameLen - MP3_TAGv2_23_TXT_HEADER_LEN;
3625 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3627 debug_msg(RELEASE, "-----------------------------------------------------------------------------------\n");
3629 if (oneFrameLen > MP3_TAGv2_23_TXT_HEADER_LEN && purelyFramelen <= taglen - curPos) {
3630 curPos += purelyFramelen;
3632 /*in case of UTF 16 encoding */
3633 /*buffer+(curPos-purelyFramelen) data should '0x01' but in order to expansion, we don't accurately check the value. */
3634 if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen))) {
3636 textEncodingType = AV_ID3V2_UTF16;
3637 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen))) {
3639 textEncodingType = AV_ID3V2_UTF16_BE;
3640 } else if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen + 1))) {
3642 textEncodingType = AV_ID3V2_UTF16;
3643 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen + 1))) {
3645 textEncodingType = AV_ID3V2_UTF16_BE;
3647 /*in case of UTF-16 BE encoding */
3648 if (buffer[curPos - purelyFramelen] == 0x02) {
3650 while ((buffer[curPos - purelyFramelen + encodingOffSet] == '\0') && (encodingOffSet < purelyFramelen))
3651 encodingOffSet++;/*null skip! */
3652 textEncodingType = AV_ID3V2_UTF16_BE;
3654 /*in case of UTF8 encoding */
3655 else if (buffer[curPos - purelyFramelen] == 0x03) {
3657 while ((buffer[curPos - purelyFramelen + encodingOffSet] == '\0') && (encodingOffSet < purelyFramelen))
3658 encodingOffSet++;/*null skip! */
3659 textEncodingType = AV_ID3V2_UTF8;
3661 /*in case of ISO-8859-1 encoding */
3663 /*buffer+(curPos-purelyFramelen) data should 0x00 but in order to expansion, we don't accurately check the value. */
3665 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen))
3666 encodingOffSet++;/*less than 0x20 value skip! */
3667 textEncodingType = AV_ID3V2_ISO_8859;
3671 mmfile_free(pExtContent);
3673 if (encodingOffSet < purelyFramelen) {
3674 realCpyFrameNum = purelyFramelen - encodingOffSet;
3675 pExtContent = mmfile_malloc(realCpyFrameNum + 3);
3677 if (pExtContent == NULL) {
3678 debug_error(DEBUG, "out of memoryu for id3tag parse\n");
3682 memset(pExtContent, '\0', realCpyFrameNum + 3);
3684 if (textEncodingType != AV_ID3V2_UTF16 && textEncodingType != AV_ID3V2_UTF16_BE) {
3685 if (CompTmp[0] == 'T' || (strcmp(CompTmp, "APIC") == 0)) {
3686 debug_msg(RELEASE, "get the new text ecoding type\n");
3687 textEncodingType = buffer[curPos - purelyFramelen + encodingOffSet - 1];
3691 if (textEncodingType > AV_ID3V2_MAX) {
3692 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%d], FRAME[%s]\n", textEncodingType, (char *)CompTmp);
3696 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
3698 if (realCpyFrameNum > 0) {
3699 if (strncmp((char *)CompTmp, "TIT2", 4) == 0 && pInfo->tagV2Info.bTitleMarked == false) {
3700 if (textEncodingType == AV_ID3V2_UTF8) {
3701 pInfo->pTitle = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3702 if (pInfo->pTitle) {
3703 memcpy(pInfo->pTitle, pExtContent, realCpyFrameNum);
3704 pInfo->pTitle[realCpyFrameNum] = '\0';
3705 /*string copy with '\0'*/
3706 pInfo->titleLen = realCpyFrameNum;
3707 _STRNCPY_EX(pInfo->pTitle, pExtContent, pInfo->titleLen);
3710 pInfo->pTitle = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->titleLen);
3713 debug_msg(RELEASE, "pInfo->pTitle returned = (%s), pInfo->titleLen(%d)\n", pInfo->pTitle, pInfo->titleLen);
3714 pInfo->tagV2Info.bTitleMarked = true;
3716 } else if (strncmp((char *)CompTmp, "TPE1", 4) == 0 && pInfo->tagV2Info.bArtistMarked == false) {
3717 if (textEncodingType == AV_ID3V2_UTF8) {
3718 pInfo->pArtist = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3719 if (pInfo->pArtist) {
3720 memcpy(pInfo->pArtist, pExtContent, realCpyFrameNum);
3721 pInfo->pArtist[realCpyFrameNum] = '\0';
3722 /*string copy with '\0'*/
3723 pInfo->artistLen = realCpyFrameNum;
3724 _STRNCPY_EX(pInfo->pArtist, pExtContent, pInfo->artistLen);
3727 pInfo->pArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->artistLen);
3731 debug_msg(RELEASE, "pInfo->pArtist returned = (%s), pInfo->artistLen(%d)\n", pInfo->pArtist, pInfo->artistLen);
3732 pInfo->tagV2Info.bArtistMarked = true;
3733 } else if (strncmp((char *)CompTmp, "TPE2", 4) == 0 && pInfo->tagV2Info.bAlbum_ArtistMarked == false) {
3734 if (textEncodingType == AV_ID3V2_UTF8) {
3735 pInfo->pAlbum_Artist = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3736 if (pInfo->pAlbum_Artist) {
3737 memcpy(pInfo->pAlbum_Artist, pExtContent, realCpyFrameNum);
3738 pInfo->pAlbum_Artist[realCpyFrameNum] = '\0';
3739 /*string copy with '\0'*/
3740 pInfo->album_artistLen = realCpyFrameNum;
3741 _STRNCPY_EX(pInfo->pAlbum_Artist, pExtContent, pInfo->album_artistLen);
3744 pInfo->pAlbum_Artist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->album_artistLen);
3747 debug_msg(RELEASE, "pInfo->pAlbum_Artist returned = (%s), pInfo->album_artistLen(%d)\n", pInfo->pAlbum_Artist, pInfo->album_artistLen);
3748 pInfo->tagV2Info.bAlbum_ArtistMarked = true;
3749 } else if (strncmp((char *)CompTmp, "TPE3", 4) == 0 && pInfo->tagV2Info.bConductorMarked == false) {
3750 if (textEncodingType == AV_ID3V2_UTF8) {
3751 pInfo->pConductor = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3752 if (pInfo->pConductor) {
3753 memcpy(pInfo->pConductor, pExtContent, realCpyFrameNum);
3754 pInfo->pConductor[realCpyFrameNum] = '\0';
3755 /*string copy with '\0'*/
3756 pInfo->conductorLen = realCpyFrameNum;
3757 _STRNCPY_EX(pInfo->pConductor, pExtContent, pInfo->conductorLen);
3760 pInfo->pConductor = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->conductorLen);
3763 debug_msg(RELEASE, "pInfo->pConductor returned = (%s), pInfo->conductorLen(%d)\n", pInfo->pConductor, pInfo->conductorLen);
3764 pInfo->tagV2Info.bConductorMarked = true;
3765 } else if (strncmp((char *)CompTmp, "TALB", 4) == 0 && pInfo->tagV2Info.bAlbumMarked == false) {
3766 if (textEncodingType == AV_ID3V2_UTF8) {
3767 pInfo->pAlbum = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3768 if (pInfo->pAlbum) {
3769 memcpy(pInfo->pAlbum, pExtContent, realCpyFrameNum);
3770 pInfo->pAlbum[realCpyFrameNum] = '\0';
3771 /*string copy with '\0'*/
3772 pInfo->albumLen = realCpyFrameNum;
3773 _STRNCPY_EX(pInfo->pAlbum, pExtContent, pInfo->albumLen);
3776 pInfo->pAlbum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->albumLen);
3779 debug_msg(RELEASE, "pInfo->pAlbum returned = (%s), pInfo->albumLen(%d)\n", pInfo->pAlbum, pInfo->albumLen);
3780 pInfo->tagV2Info.bAlbumMarked = true;
3781 } else if (strncmp((char *)CompTmp, "TYER", 4) == 0 && pInfo->tagV2Info.bYearMarked == false) { /*TODO. TYER is replaced by the TDRC. but many files use TYER in v2.4 */
3782 if (textEncodingType == AV_ID3V2_UTF8) {
3783 pInfo->pYear = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3785 memcpy(pInfo->pYear, pExtContent, realCpyFrameNum);
3786 pInfo->pYear[realCpyFrameNum] = '\0';
3787 /*string copy with '\0'*/
3788 pInfo->yearLen = realCpyFrameNum;
3789 _STRNCPY_EX(pInfo->pYear, pExtContent, pInfo->yearLen);
3792 pInfo->pYear = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->yearLen);
3795 debug_msg(RELEASE, "pInfo->pYear returned = (%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
3796 pInfo->tagV2Info.bYearMarked = true;
3797 } else if (strncmp((char *)CompTmp, "COMM", 4) == 0 && pInfo->tagV2Info.bDescriptionMarked == false) {
3798 if (realCpyFrameNum > 3) {
3799 realCpyFrameNum -= 3;
3802 if (textEncodingType == AV_ID3V2_UTF16 || textEncodingType == AV_ID3V2_UTF16_BE) {
3803 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3804 realCpyFrameNum -= 4;
3808 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3809 realCpyFrameNum -= 2;
3811 textEncodingType = AV_ID3V2_UTF16;
3813 debug_msg(RELEASE, "pInfo->pComment Never Get Here!!\n");
3815 } else if (textEncodingType == AV_ID3V2_UTF8) {
3816 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3820 textEncodingType = AV_ID3V2_UTF8;
3822 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3826 textEncodingType = AV_ID3V2_ISO_8859;
3829 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3831 if (textEncodingType == AV_ID3V2_UTF8) {
3832 pInfo->pComment = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3833 if (pInfo->pComment) {
3834 memset(pInfo->pComment, 0, (realCpyFrameNum + 2));
3835 memcpy(pInfo->pComment, pExtContent + tmp, realCpyFrameNum);
3836 pInfo->pComment[realCpyFrameNum] = '\0';
3837 /*string copy with '\0'*/
3838 pInfo->commentLen = realCpyFrameNum;
3839 _STRNCPY_EX(pInfo->pComment, pExtContent, pInfo->commentLen);
3842 pInfo->pComment = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->commentLen);
3845 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3850 debug_msg(RELEASE, "pInfo->pComment returned = (%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
3851 pInfo->tagV2Info.bDescriptionMarked = true;
3852 } else if (strncmp((char *)CompTmp, "SYLT", 4) == 0 && pInfo->tagV2Info.bSyncLyricsMarked == false) {
3855 int copy_start_pos = tmp;
3856 AvSynclyricsInfo *synclyrics_info = NULL;
3857 GList *synclyrics_info_list = NULL;
3859 if (realCpyFrameNum > 5) {
3860 realCpyFrameNum -= 5;
3863 if (textEncodingType == AV_ID3V2_UTF16 || textEncodingType == AV_ID3V2_UTF16_BE) {
3864 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3865 realCpyFrameNum -= 4;
3869 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3870 realCpyFrameNum -= 2;
3872 textEncodingType = AV_ID3V2_UTF16;
3874 debug_msg(RELEASE, "pInfo->pSyncLyrics Never Get Here!!\n");
3876 } else if (textEncodingType == AV_ID3V2_UTF8) {
3877 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3881 textEncodingType = AV_ID3V2_UTF8;
3883 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3887 textEncodingType = AV_ID3V2_ISO_8859;
3890 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3892 if (realCpyFrameNum < MMFILE_SYNC_LYRIC_INFO_MIN_LEN) {
3893 debug_msg(RELEASE, "failed to get Synchronised lyrics Info realCpyFramNum(%d)\n", realCpyFrameNum);
3894 pInfo->syncLyricsNum = 0;
3896 if (textEncodingType == AV_ID3V2_UTF16) {
3897 debug_warning(DEBUG, "[AV_ID3V2_UTF16] not implemented\n");
3898 } else if (textEncodingType == AV_ID3V2_UTF16_BE) {
3899 debug_warning(DEBUG, "[AV_ID3V2_UTF16_BE] not implemented\n");
3901 for (idx = 0; idx < realCpyFrameNum; idx++) {
3902 if (pExtContent[tmp + idx] == 0x00) {
3903 synclyrics_info = (AvSynclyricsInfo *)mmfile_malloc(sizeof(AvSynclyricsInfo));
3904 if (synclyrics_info) {
3905 if (textEncodingType == AV_ID3V2_UTF8) {
3906 synclyrics_info->lyric_info = mmfile_malloc(copy_len + 1);
3907 if (synclyrics_info->lyric_info) {
3908 memset(synclyrics_info->lyric_info, 0, copy_len + 1);
3909 memcpy(synclyrics_info->lyric_info, pExtContent + copy_start_pos, copy_len);
3910 synclyrics_info->lyric_info[copy_len] = '\0';
3913 synclyrics_info->lyric_info = mmfile_string_convert((const char *)&pExtContent[copy_start_pos], copy_len, "UTF-8", charset_array[textEncodingType], NULL, NULL);
3916 synclyrics_info->time_info = (unsigned long)pExtContent[tmp + idx + 1] << 24 | (unsigned long)pExtContent[tmp + idx + 2] << 16 | (unsigned long)pExtContent[tmp + idx + 3] << 8 | (unsigned long)pExtContent[tmp + idx + 4];
3918 copy_start_pos = tmp + idx + 1;
3919 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);
3921 synclyrics_info_list = g_list_append(synclyrics_info_list, synclyrics_info);
3926 pInfo->pSyncLyrics = synclyrics_info_list;
3927 pInfo->syncLyricsNum = g_list_length(pInfo->pSyncLyrics);
3931 debug_msg(RELEASE, "SyncLyrics info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3935 pInfo->tagV2Info.bSyncLyricsMarked = true;
3936 } else if (strncmp((char *)CompTmp, "USLT", 4) == 0 && pInfo->tagV2Info.bUnsyncLyricsMarked == false) {
3937 if (realCpyFrameNum > 3) {
3938 realCpyFrameNum -= 3;
3941 if (textEncodingType == AV_ID3V2_UTF16 || textEncodingType == AV_ID3V2_UTF16_BE) {
3942 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3943 realCpyFrameNum -= 4;
3947 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3948 realCpyFrameNum -= 2;
3950 textEncodingType = AV_ID3V2_UTF16;
3952 debug_msg(RELEASE, "pInfo->pUnsyncLyrics Never Get Here!!\n");
3954 } else if (textEncodingType == AV_ID3V2_UTF8) {
3955 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3959 textEncodingType = AV_ID3V2_UTF8;
3961 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3965 textEncodingType = AV_ID3V2_ISO_8859;
3968 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3970 if (textEncodingType == AV_ID3V2_UTF8) {
3971 pInfo->pUnsyncLyrics = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3973 if (pInfo->pUnsyncLyrics != NULL) {
3974 memset(pInfo->pUnsyncLyrics, 0, (realCpyFrameNum + 2));
3975 memcpy(pInfo->pUnsyncLyrics, pExtContent + tmp, realCpyFrameNum);
3976 pInfo->pUnsyncLyrics[realCpyFrameNum] = '\0';
3977 /*string copy with '\0'*/
3978 pInfo->unsynclyricsLen = realCpyFrameNum;
3979 _STRNCPY_EX(pInfo->pUnsyncLyrics, pExtContent, pInfo->unsynclyricsLen);
3981 debug_error(DEBUG, "out of memoryu for SyncLyrics\n");
3984 pInfo->pUnsyncLyrics = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->unsynclyricsLen);
3987 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3992 debug_msg(RELEASE, "pInfo->pUnsyncLyrics returned = (%s), pInfo->unsynclyricsLen(%d)\n", pInfo->pUnsyncLyrics, pInfo->unsynclyricsLen);
3993 pInfo->tagV2Info.bDescriptionMarked = true;
3994 } else if (strncmp((char *)CompTmp, "TCON", 4) == 0 && pInfo->tagV2Info.bGenreMarked == false) {
3995 if (textEncodingType == AV_ID3V2_UTF8) {
3996 pInfo->pGenre = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3997 if (pInfo->pGenre) {
3998 memcpy(pInfo->pGenre, pExtContent, realCpyFrameNum);
3999 pInfo->pGenre[realCpyFrameNum] = '\0';
4000 /*string copy with '\0'*/
4001 pInfo->genreLen = realCpyFrameNum;
4002 _STRNCPY_EX(pInfo->pGenre, pExtContent, pInfo->genreLen);
4005 pInfo->pGenre = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->genreLen);
4008 debug_msg(RELEASE, "pInfo->pGenre returned = (%s), pInfo->genreLen(%d)\n", pInfo->pGenre, pInfo->genreLen);
4010 if ((pInfo->pGenre != NULL) && (pInfo->genreLen > 0)) {
4014 ret = is_numeric(pInfo->pGenre, pInfo->genreLen);
4017 ret = safe_atoi(pInfo->pGenre, &int_genre);
4019 debug_msg(RELEASE, "genre information is inteager [%d]\n", int_genre);
4021 /*Change int to string */
4022 if ((0 <= int_genre) && (int_genre < GENRE_COUNT - 1)) {
4023 /*save genreinfo like "(123)". mm_file_id3tag_restore_content_info convert it to string*/
4024 char tmp_genre[6] = {0, }; /*ex. "(123)+NULL"*/
4025 int tmp_genre_len = 0;
4027 memset(tmp_genre, 0, 6);
4028 snprintf(tmp_genre, sizeof(tmp_genre), "(%d)", int_genre);
4030 tmp_genre_len = strlen(tmp_genre);
4031 if (tmp_genre_len > 0) {
4032 mmfile_free(pInfo->pGenre);
4033 pInfo->pGenre = mmfile_malloc(sizeof(char) * (tmp_genre_len + 1));
4034 if (pInfo->pGenre) {
4035 SAFE_STRLCPY(pInfo->pGenre, tmp_genre, tmp_genre_len + 1);
4040 debug_msg(RELEASE, "genre information is wrong inteager [%s]\n", pInfo->pGenre);
4045 pInfo->tagV2Info.bGenreMarked = true;
4046 } else if (strncmp((char *)CompTmp, "TRCK", 4) == 0 && pInfo->tagV2Info.bTrackNumMarked == false) {
4047 if (textEncodingType == AV_ID3V2_UTF8) {
4048 pInfo->pTrackNum = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4049 if (pInfo->pTrackNum != NULL) {
4050 memcpy(pInfo->pTrackNum, pExtContent, realCpyFrameNum);
4051 pInfo->pTrackNum[realCpyFrameNum] = '\0';
4052 /*string copy with '\0'*/
4053 pInfo->tracknumLen = realCpyFrameNum;
4054 _STRNCPY_EX(pInfo->pTrackNum, pExtContent, pInfo->tracknumLen);
4057 pInfo->pTrackNum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tracknumLen);
4060 debug_msg(RELEASE, "pInfo->pTrackNum returned = (%s), pInfo->tracknumLen(%d)\n", pInfo->pTrackNum, pInfo->tracknumLen);
4061 pInfo->tagV2Info.bTrackNumMarked = true;
4062 } else if (strncmp((char *)CompTmp, "TENC", 4) == 0 && pInfo->tagV2Info.bEncByMarked == false) {
4063 if (textEncodingType == AV_ID3V2_UTF8) {
4064 pInfo->pEncBy = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4065 if (pInfo->pEncBy != NULL) {
4066 memcpy(pInfo->pEncBy, pExtContent, realCpyFrameNum);
4067 pInfo->pEncBy[realCpyFrameNum] = '\0';
4068 /*string copy with '\0'*/
4069 pInfo->encbyLen = realCpyFrameNum;
4070 _STRNCPY_EX(pInfo->pEncBy, pExtContent, pInfo->encbyLen);
4073 pInfo->pEncBy = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->encbyLen);
4076 debug_msg(RELEASE, "pInfo->pEncBy returned = (%s), pInfo->encbyLen(%d)\n", pInfo->pEncBy, pInfo->encbyLen);
4077 pInfo->tagV2Info.bEncByMarked = true;
4078 } else if (strncmp((char *)CompTmp, "WXXX", 4) == 0 && pInfo->tagV2Info.bURLMarked == false) {
4079 if (textEncodingType == AV_ID3V2_UTF8) {
4080 pInfo->pURL = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4081 if (pInfo->pURL != NULL) {
4082 memcpy(pInfo->pURL, pExtContent, realCpyFrameNum);
4083 pInfo->pURL[realCpyFrameNum] = '\0';
4084 /*string copy with '\0'*/
4085 pInfo->urlLen = realCpyFrameNum;
4086 _STRNCPY_EX(pInfo->pURL, pExtContent, pInfo->urlLen);
4089 pInfo->pURL = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->urlLen);
4092 debug_msg(RELEASE, "pInfo->pURL returned = (%s), pInfo->urlLen(%d)\n", pInfo->pURL, pInfo->urlLen);
4093 pInfo->tagV2Info.bURLMarked = true;
4094 } else if (strncmp((char *)CompTmp, "TCOP", 4) == 0 && pInfo->tagV2Info.bCopyRightMarked == false) {
4095 if (textEncodingType == AV_ID3V2_UTF8) {
4096 pInfo->pCopyright = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4097 if (pInfo->pCopyright != NULL) {
4098 memcpy(pInfo->pCopyright, pExtContent, realCpyFrameNum);
4099 pInfo->pCopyright[realCpyFrameNum] = '\0';
4100 /*string copy with '\0'*/
4101 pInfo->copyrightLen = realCpyFrameNum;
4102 _STRNCPY_EX(pInfo->pCopyright, pExtContent, pInfo->copyrightLen);
4105 pInfo->pCopyright = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->copyrightLen);
4108 debug_msg(RELEASE, "pInfo->pCopyright returned = (%s), pInfo->copyrightLen(%d)\n", pInfo->pCopyright, pInfo->copyrightLen);
4109 pInfo->tagV2Info.bCopyRightMarked = true;
4110 } else if (strncmp((char *)CompTmp, "TOPE", 4) == 0 && pInfo->tagV2Info.bOriginArtistMarked == false) {
4111 if (textEncodingType == AV_ID3V2_UTF8) {
4112 pInfo->pOriginArtist = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4113 if (pInfo->pOriginArtist != NULL) {
4114 memcpy(pInfo->pOriginArtist, pExtContent, realCpyFrameNum);
4115 pInfo->pOriginArtist[realCpyFrameNum] = '\0';
4116 /*string copy with '\0'*/
4117 pInfo->originartistLen = realCpyFrameNum;
4118 _STRNCPY_EX(pInfo->pOriginArtist, pExtContent, pInfo->originartistLen);
4121 pInfo->pOriginArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->originartistLen);
4124 debug_msg(RELEASE, "pInfo->pOriginArtist returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pOriginArtist, pInfo->originartistLen);
4125 pInfo->tagV2Info.bOriginArtistMarked = true;
4126 } else if (strncmp((char *)CompTmp, "TCOM", 4) == 0 && pInfo->tagV2Info.bComposerMarked == false) {
4127 if (textEncodingType == AV_ID3V2_UTF8) {
4128 pInfo->pComposer = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4129 if (pInfo->pComposer != NULL) {
4130 memcpy(pInfo->pComposer, pExtContent, realCpyFrameNum);
4131 pInfo->pComposer[realCpyFrameNum] = '\0';
4132 /*string copy with '\0'*/
4133 pInfo->composerLen = realCpyFrameNum;
4134 _STRNCPY_EX(pInfo->pComposer, pExtContent, pInfo->composerLen);
4137 pInfo->pComposer = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->composerLen);
4140 debug_msg(RELEASE, "pInfo->pComposer returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pComposer, pInfo->composerLen);
4141 pInfo->tagV2Info.bComposerMarked = true;
4142 } else if (strncmp((char *)CompTmp, "TDRC", 4) == 0 && pInfo->tagV2Info.bRecDateMarked == false) { /*TYER(year) and TRDA are replaced by the TDRC */
4143 if (textEncodingType == AV_ID3V2_UTF8) {
4144 pInfo->pRecDate = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4145 if (pInfo->pRecDate != NULL) {
4146 memcpy(pInfo->pRecDate, pExtContent, realCpyFrameNum);
4147 pInfo->pRecDate[realCpyFrameNum] = '\0';
4148 /*string copy with '\0'*/
4149 pInfo->recdateLen = realCpyFrameNum;
4150 _STRNCPY_EX(pInfo->pRecDate, pExtContent, pInfo->recdateLen);
4153 pInfo->pRecDate = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->recdateLen);
4156 debug_msg(RELEASE, "pInfo->pRecDate returned = (%s), pInfo->recdateLen(%d)\n", pInfo->pRecDate, pInfo->recdateLen);
4157 pInfo->tagV2Info.bRecDateMarked = true;
4158 } else if (strncmp((char *)CompTmp, "TIT1", 4) == 0 && pInfo->tagV2Info.bContentGroupMarked == false) {
4159 if (textEncodingType == AV_ID3V2_UTF8) {
4160 pInfo->pContentGroup = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4161 if (pInfo->pContentGroup != NULL) {
4162 memcpy(pInfo->pContentGroup, pExtContent, realCpyFrameNum);
4163 pInfo->pContentGroup[realCpyFrameNum] = '\0';
4164 /*string copy with '\0'*/
4165 pInfo->contentGroupLen = realCpyFrameNum;
4166 _STRNCPY_EX(pInfo->pContentGroup, pExtContent, pInfo->contentGroupLen);
4169 pInfo->pContentGroup = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->contentGroupLen);
4172 debug_msg(RELEASE, "pInfo->pContentGroup returned = (%s), pInfo->contentGroupLen(%d)\n", pInfo->pContentGroup, pInfo->contentGroupLen);
4173 pInfo->tagV2Info.bContentGroupMarked = true;
4174 } else if (strncmp((char *)CompTmp, "APIC", 4) == 0 && pInfo->tagV2Info.bImageMarked == false && realCpyFrameNum <= 2000000) {
4175 if (pExtContent[0] != '\0') {
4176 for (inx = 0; inx < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1; inx++)
4177 pInfo->imageInfo.imageMIMEType[inx] = '\0';/*ini mimetype variable */
4179 while ((checkImgMimeTypeMax < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1) && pExtContent[checkImgMimeTypeMax] != '\0') {
4180 pInfo->imageInfo.imageMIMEType[checkImgMimeTypeMax] = pExtContent[checkImgMimeTypeMax];
4181 checkImgMimeTypeMax++;
4183 pInfo->imageInfo.imgMimetypeLen = checkImgMimeTypeMax;
4185 pInfo->imageInfo.imgMimetypeLen = 0;
4188 imgstartOffset += checkImgMimeTypeMax;
4190 if (strncmp(pInfo->imageInfo.imageMIMEType, MIME_PRFIX, strlen(MIME_PRFIX)) != 0) {
4191 pInfo->imageInfo.imgMimetypeLen = 0;
4192 debug_error(DEBUG, "APIC NOT VALID");
4196 if ((pExtContent[imgstartOffset] == '\0') && (realCpyFrameNum - imgstartOffset > 0)) {
4197 imgstartOffset++;/*endofMIME(1byte) */
4199 if (pExtContent[imgstartOffset] < AV_ID3V2_PICTURE_TYPE_MAX) {
4200 pInfo->imageInfo.pictureType = pExtContent[imgstartOffset];
4202 imgstartOffset++;/*PictureType(1byte) */
4204 if (pExtContent[imgstartOffset] != 0x0) {
4207 int new_dis_len = 0;
4208 char *tmp_desc = NULL;
4211 if (pExtContent[imgstartOffset + cur_pos] == '\0') {
4212 if (realCpyFrameNum < imgstartOffset + cur_pos) {
4213 debug_error(DEBUG, "End of APIC Tag %d %d %d\n", realCpyFrameNum, imgstartOffset, cur_pos);
4216 /*check end of image description*/
4217 if ((pExtContent[imgstartOffset + cur_pos + 1] == gTagJPEGHeader[0]) ||
4218 (pExtContent[imgstartOffset + cur_pos + 1] == gTagPNGHeader[0])) {
4219 debug_msg(RELEASE, "length of description (%d)", cur_pos);
4226 dis_len = cur_pos + 1;
4228 tmp_desc = mmfile_malloc(sizeof(char) * dis_len);
4230 if (tmp_desc != NULL) {
4231 memcpy(tmp_desc, pExtContent + imgstartOffset, dis_len);
4232 debug_msg(DEBUG, "tmp_desc %s\n", tmp_desc);
4234 /*convert description*/
4235 pInfo->imageInfo.imageDescription = mmfile_string_convert(tmp_desc, dis_len, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&new_dis_len);
4236 debug_msg(DEBUG, "new_desc %s(%d)\n", pInfo->imageInfo.imageDescription, new_dis_len);
4237 mmfile_free(tmp_desc);
4239 pInfo->imageInfo.imgDesLen = new_dis_len; /**/
4242 imgstartOffset += cur_pos;
4244 pInfo->imageInfo.imgDesLen = 0;
4247 if ((pExtContent[imgstartOffset] == '\0') && (realCpyFrameNum - imgstartOffset > 0)) {
4248 imgstartOffset++; /* endofDesceriptionType(1byte) */
4250 while (pExtContent[imgstartOffset] == '\0') { /*some content has useless '\0' in front of picture data */
4254 debug_msg(RELEASE, "after scaning imgDescription imgstartOffset(%d) value!\n", imgstartOffset);
4256 if (realCpyFrameNum - imgstartOffset > 0) {
4257 pInfo->imageInfo.imageLen = realCpyFrameNum - imgstartOffset;
4258 pInfo->imageInfo.pImageBuf = mmfile_malloc(pInfo->imageInfo.imageLen + 1);
4260 if (pInfo->imageInfo.pImageBuf != NULL) {
4261 memcpy(pInfo->imageInfo.pImageBuf, pExtContent + imgstartOffset, pInfo->imageInfo.imageLen);
4262 pInfo->imageInfo.pImageBuf[pInfo->imageInfo.imageLen] = 0;
4265 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
4266 pInfo->imageInfo.bURLInfo = true; /*if mimetype is "-->", image date has an URL */
4268 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - imgstartOffset(%d)\n", realCpyFrameNum, imgstartOffset);
4273 checkImgMimeTypeMax = 0;
4276 pInfo->tagV2Info.bImageMarked = true;
4278 debug_msg(RELEASE, "CompTmp(%s) This Frame ID currently not Supports!!\n", CompTmp);
4283 debug_msg(RELEASE, "mmf_file_id3tag_parse_v224: All of the pExtContent Values are NULL\n");
4287 curPos += purelyFramelen;
4288 if (purelyFramelen != 0)
4289 needToloopv2taglen = MP3_TAGv2_23_TXT_HEADER_LEN;
4292 mmfile_free(pExtContent);
4293 memset(CompTmp, 0, 4);
4294 if (curPos < taglen) {
4295 needToloopv2taglen -= oneFrameLen;
4298 needToloopv2taglen = MP3_TAGv2_23_TXT_HEADER_LEN;
4302 realCpyFrameNum = 0;
4303 textEncodingType = 0;
4309 release_characterset_array(charset_array);
4319 void mm_file_id3tag_restore_content_info(AvFileContentInfo *pInfo)
4321 char *mpegAudioGenre = NULL/*, *tmpGenreForV1Tag = NULL*/;
4322 bool bAdditionGenre = false /*, bMpegAudioFrame = false*/;
4323 int mpegAudioFileLen = 0, idv2IntGenre = 148/*, tmpinx = 0, tmpinx2=0*/;
4324 unsigned char genre = pInfo->genre;
4326 /* for Genre Info */
4327 if (pInfo->tagV2Info.bGenreMarked == false) {
4328 if (pInfo->bV1tagFound == true) {
4329 debug_msg(RELEASE, "Genre: %d\n", genre);
4334 if (MpegAudio_Genre[genre] != NULL) {
4335 pInfo->genreLen = strlen(MpegAudio_Genre[genre]);
4336 if (pInfo->genreLen > 0) {
4337 /* Give space for NULL character. Hence added "+1" */
4338 pInfo->pGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen + 1));
4339 if (pInfo->pGenre) {
4340 SAFE_STRLCPY(pInfo->pGenre, MpegAudio_Genre[genre], pInfo->genreLen + 1);
4345 debug_msg(RELEASE, "Genre was not Found.\n");
4347 } else if (pInfo->tagV2Info.bGenreMarked == true) {
4348 if (pInfo->genreLen && pInfo->tagV2Info.bGenreUTF16) {
4349 pInfo->pGenre[pInfo->genreLen + 1] = '\0';
4350 mpegAudioGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen * AV_WM_LOCALCODE_SIZE_MAX + 1));
4352 debug_msg(RELEASE, "pInfo->genreLen size is Zero Or not UTF16 code! genreLen[%d] genre[%s]\n", pInfo->genreLen, pInfo->pGenre);
4354 if (pInfo->pGenre) {
4355 pInfo->genreLen = strlen(pInfo->pGenre);
4356 mpegAudioGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen + 1));
4357 if (mpegAudioGenre != NULL) {
4358 SAFE_STRLCPY(mpegAudioGenre, pInfo->pGenre, pInfo->genreLen + 1);
4361 pInfo->genreLen = 0;
4365 mmfile_free(pInfo->pGenre);
4368 if (mpegAudioGenre != NULL) {
4371 * (XXX) XXX is 0 - 148
4373 pInfo->genreLen = strlen(mpegAudioGenre);
4374 if (pInfo->genreLen >= 3 &&
4375 mpegAudioGenre[0] == '(' && mpegAudioGenre[pInfo->genreLen - 1] == ')') {
4376 bAdditionGenre = true;
4377 for (mpegAudioFileLen = 1; mpegAudioFileLen <= pInfo->genreLen - 2; mpegAudioFileLen++) {
4378 if (mpegAudioGenre[mpegAudioFileLen] < '0' || mpegAudioGenre[mpegAudioFileLen] > '9') {
4379 bAdditionGenre = false;
4385 if (bAdditionGenre == true) {
4386 idv2IntGenre = atoi(mpegAudioGenre + 1);
4388 if (idv2IntGenre > 147 || idv2IntGenre < 0)
4391 if (MpegAudio_Genre[idv2IntGenre] != NULL) {
4392 pInfo->genreLen = strlen(MpegAudio_Genre[idv2IntGenre]);
4393 if (pInfo->genreLen > 0) {
4394 /* Give space for NULL character. Hence added "+1" */
4395 pInfo->pGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen + 1));
4396 if (pInfo->pGenre) {
4397 SAFE_STRLCPY(pInfo->pGenre, MpegAudio_Genre[idv2IntGenre], pInfo->genreLen + 1);
4401 debug_msg(RELEASE, "pInfo->pGenre = %s\n", pInfo->pGenre);
4402 } else if (bAdditionGenre == false && pInfo->genreLen > 0) {
4407 /* Give space for NULL character. Hence added "+1" */
4408 pInfo->pGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen + 1));
4409 if (pInfo->pGenre) {
4410 SAFE_STRLCPY(pInfo->pGenre, mpegAudioGenre, pInfo->genreLen + 1);
4412 debug_msg(RELEASE, "pInfo->pGenre = %s, pInfo->genreLen = %d\n", pInfo->pGenre, pInfo->genreLen);
4414 debug_msg(RELEASE, "Failed to \"(...)\" value to genre = %s\n", pInfo->pGenre);
4417 debug_msg(RELEASE, "mpegAudioGenre = %s\n", mpegAudioGenre);
4419 mmfile_free(mpegAudioGenre);
4422 debug_msg(RELEASE, "Neither ID3 v1 nor v2 info doesn't have Genre Info.\n");
4427 void mm_file_free_synclyrics_list(GList *synclyrics_list)
4431 AvSynclyricsInfo *synclyrics_info = NULL;
4433 if (synclyrics_list == NULL) {
4437 list_len = g_list_length(synclyrics_list);
4438 for (idx = 0; idx < list_len; idx++) {
4439 synclyrics_info = g_list_nth_data(synclyrics_list, idx);
4441 if (synclyrics_info != NULL) {
4442 mmfile_free(synclyrics_info->lyric_info);
4443 mmfile_free(synclyrics_info);
4447 if (synclyrics_list != NULL) {
4448 g_list_free(synclyrics_list);
4449 synclyrics_list = NULL;