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;
1964 MMFILE_WEBM_PROJ_V2_BOX v2box = { 0, };
1965 MMFILE_WEBM_EQUI_PROJ_V2_BOX equi = { 0, };
1966 MMFILE_WEBM_CBMP_PROJ_V2_BOX cbmp = { 0, };
1967 MMFILE_WEBM_POSE_ELEMENT_V2_BOX pose = { 0, };
1969 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
1970 if (ret == MMFILE_UTIL_FAIL) {
1971 debug_error(DEBUG, "error: mmfile_open\n");
1975 long long file_size = mmfile_seek(fp, 0, SEEK_END);
1976 if (file_size == MMFILE_UTIL_FAIL) {
1977 debug_error(DEBUG, "mmfile operation failed\n");
1981 probe_size = (file_size > probe_size) ? probe_size : file_size;
1982 buffer = (unsigned char *)malloc(probe_size * sizeof(unsigned char));
1984 debug_error(DEBUG, "malloc failed\n");
1988 ret = mmfile_seek(fp, 0, SEEK_SET);
1989 if (ret == MMFILE_UTIL_FAIL) {
1990 debug_error(DEBUG, "mmfile operation failed\n");
1994 ret = mmfile_read(fp, buffer, probe_size * sizeof(unsigned char));
1995 if (ret == MMFILE_UTIL_FAIL) {
1996 debug_error(DEBUG, "mmfile operation failed\n");
2000 /* FIXME (m.alieksieie): It's better to use some EBML parser here*/
2001 for (i = 0; i + 3 < probe_size; ++i) {
2002 if (*(unsigned int *)(buffer + i) == FOURCC('e', 'q', 'u', 'i') ||
2003 *(unsigned int *)(buffer + i) == FOURCC('c', 'b', 'm', 'p')) {
2004 debug_msg(DEBUG, "projection data found at offset %d bytes\n", i);
2009 if (i + 3 == probe_size) {
2010 debug_msg(DEBUG, "projection info wasn't found\n");
2011 ret = MMFILE_UTIL_SUCCESS;
2015 if ((i - (int)sizeof(MMFILE_WEBM_PROJ_V2_BOX)) < 0) {
2016 debug_error(DEBUG, "error: invalid supposed projection info location\n");
2017 ret = MMFILE_UTIL_FAIL;
2021 ret = mmfile_seek(fp, i - sizeof(MMFILE_WEBM_PROJ_V2_BOX), SEEK_SET);
2022 if (ret == MMFILE_UTIL_FAIL) {
2023 debug_error(DEBUG, "error: failed to seek to the supposed projection info location\n");
2027 ret = mmfile_read(fp, (unsigned char *)&v2box, sizeof(MMFILE_WEBM_PROJ_V2_BOX));
2028 if (ret == MMFILE_UTIL_FAIL) {
2029 debug_error(DEBUG, "mmfile operation failed\n");
2033 if (v2box.proj_type_box_value == PROJECTION_TYPE_EQUI) {
2034 debug_msg(DEBUG, "Equirectangular projection is used\n");
2036 ret = mmfile_read(fp, (unsigned char *)&equi, sizeof(MMFILE_WEBM_EQUI_PROJ_V2_BOX));
2037 if (ret == MMFILE_UTIL_FAIL) {
2038 debug_error(DEBUG, "error: failed to read equirectangular element\n");
2041 if (strncmp((char *)equi.proj_priv_box_name, "equi", 4) == 0) {
2042 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_top);
2043 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_bottom);
2044 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_left);
2045 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_right);
2047 debug_error(DEBUG, "error: failed to read equirectangular element\n");
2048 ret = MMFILE_UTIL_SUCCESS;
2052 if (v2box.proj_type_box_value == PROJECTION_TYPE_CBMP) {
2053 debug_msg(DEBUG, "Cubemap projection is used\n");
2055 ret = mmfile_read(fp, (unsigned char *)&cbmp, sizeof(MMFILE_WEBM_CBMP_PROJ_V2_BOX));
2056 if (ret == MMFILE_UTIL_FAIL) {
2057 debug_error(DEBUG, "error: failed to read cubemap element\n");
2060 if (strncmp((char *)cbmp.proj_priv_box_name, "cbmp", 4) == 0) {
2061 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_layout);
2062 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_padding);
2064 debug_error(DEBUG, "error: failed to read cubemap element\n");
2065 ret = MMFILE_UTIL_FAIL;
2070 ret = mmfile_read(fp, (unsigned char *)&pose, sizeof(MMFILE_WEBM_POSE_ELEMENT_V2_BOX));
2071 if (ret == MMFILE_UTIL_FAIL) {
2072 debug_error(DEBUG, "error: failed to read pose info\n");
2076 if (pose.pose_yaw_element_id == POSE_YAW_ELEMENT_ID) {
2077 formatContext->poseYawV2 = (uint)mmfile_io_be_float32(pose.pose_yaw_element_value);
2079 debug_error(DEBUG, "error: failed to pose yaw element\n");
2080 ret = MMFILE_UTIL_FAIL;
2083 if (pose.pose_pitch_element_id == POSE_PITCH_ELEMENT_ID) {
2084 formatContext->posePitchV2 = (uint)mmfile_io_be_float32(pose.pose_pitch_element_value);
2086 debug_error(DEBUG, "error: failed to pose pitch element\n");
2087 ret = MMFILE_UTIL_FAIL;
2090 if (pose.pose_roll_element_id == POSE_ROLL_ELEMENT_ID) {
2091 formatContext->poseRollV2 = (uint)mmfile_io_be_float32(pose.pose_roll_element_value);
2093 debug_error(DEBUG, "error: failed to pose roll element\n");
2094 ret = MMFILE_UTIL_FAIL;
2107 EXPORT_API int MMFileUtilGetMetaDataFromMP4(MMFileFormatContext *formatContext)
2109 MMFileIOHandle *fp = NULL;
2112 unsigned long long chunk_size = 0;
2113 long long moov_end = 0;
2114 MMFILE_MP4_BASIC_BOX_HEADER basic_header = {0, };
2115 int junk_counter = 0;
2117 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
2118 if (ret == MMFILE_UTIL_FAIL) {
2119 debug_error(DEBUG, "error: mmfile_open\n");
2123 basic_header.start_offset = mmfile_tell(fp);
2125 if (g_junk_counter_limit == 0)
2126 g_junk_counter_limit = GetJunkCounterLimit();
2128 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)) {
2129 basic_header.size = mmfile_io_be_uint32(basic_header.size);
2130 basic_header.type = mmfile_io_le_uint32(basic_header.type);
2132 if (basic_header.size == 0) {
2133 debug_warning(DEBUG, "header is invalid.\n");
2134 basic_header.size = readed;
2135 basic_header.type = 0;
2136 chunk_size = basic_header.size;
2138 if ((moov_end != 0) && (moov_end < basic_header.start_offset)) {
2139 debug_msg(DEBUG, "found junk data but moov data already was extracted, so junk counter will be increase: %d", junk_counter);
2142 /* stop the loop for junk case. */
2143 if ((g_junk_counter_limit > 0) && (junk_counter > g_junk_counter_limit)) {
2144 debug_msg(DEBUG, "stop the loop by junk-data checker");
2145 ret = MMFILE_UTIL_FAIL;
2149 } else if (basic_header.size == 1) {
2151 unsigned char temp[BIG_CONTENT_BOX_SIZE_LEN] = {0, };
2152 unsigned long long size = 0;
2154 mmfile_read(fp, (unsigned char *)&temp, BIG_CONTENT_BOX_SIZE_LEN);
2156 for (i = 0; i < BIG_CONTENT_BOX_SIZE_LEN; i++)
2157 size |= (unsigned long long)temp[i] << (BIG_CONTENT_BOX_SIZE_LEN - 1 - i) * BIG_CONTENT_BOX_SIZE_LEN;
2161 chunk_size = basic_header.size;
2165 switch (basic_header.type) {
2166 case FOURCC('m', 'o', 'o', 'v'): {
2167 debug_msg(RELEASE, "MPEG4: [moov] SIZE: [%lld]Byte\n", chunk_size);
2168 moov_end = basic_header.start_offset + chunk_size;
2171 case FOURCC('u', 'd', 't', 'a'): {
2172 debug_msg(RELEASE, "MPEG4: [udat] SIZE: [%lld]Byte\n", chunk_size);
2175 /*/////////////////////////////////////////////////////////////// */
2176 /* Extracting Tag Data // */
2177 /*/////////////////////////////////////////////////////////////// */
2178 case FOURCC('t', 'i', 't', 'l'): {
2179 debug_msg(RELEASE, "MPEG4: [titl] SIZE: [%lld]Byte\n", chunk_size);
2180 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_TITLE);
2183 case FOURCC('d', 's', 'c', 'p'): {
2184 debug_msg(RELEASE, "MPEG4: [dscp] SIZE: [%lld]Byte\n", chunk_size);
2185 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_CAPTION);
2188 case FOURCC('c', 'p', 'r', 't'): {
2189 debug_msg(RELEASE, "MPEG4: [cprt] SIZE: [%lld]Byte\n", chunk_size);
2190 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_COPYRIGHT);
2193 case FOURCC('p', 'e', 'r', 'f'): {
2194 debug_msg(RELEASE, "MPEG4: [perf] SIZE: [%lld]Byte\n", chunk_size);
2195 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_PERFORMER);
2198 case FOURCC('a', 'u', 't', 'h'): {
2199 debug_msg(RELEASE, "MPEG4: [auth] SIZE: [%lld]Byte\n", chunk_size);
2200 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_AUTHOR);
2203 case FOURCC('g', 'n', 'r', 'e'): {
2204 debug_msg(RELEASE, "MPEG4: [gnre] SIZE: [%lld]Byte\n", chunk_size);
2205 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_GENRE);
2208 case FOURCC('a', 'l', 'b', 'm'): {
2209 debug_msg(RELEASE, "MPEG4: [albm] SIZE: [%lld]Byte\n", chunk_size);
2210 GetAlbumFromAlbumTagBox(formatContext, fp, &basic_header);
2213 case FOURCC('y', 'r', 'r', 'c'): {
2214 debug_msg(RELEASE, "MPEG4: [yrrc] SIZE: [%lld]Byte\n", chunk_size);
2215 GetYearFromYearTagBox(formatContext, fp, &basic_header);
2218 case FOURCC('r', 't', 'n', 'g'): {
2219 debug_msg(RELEASE, "MPEG4: [rtng] SIZE: [%lld]Byte\n", chunk_size);
2220 GetRatingFromRatingTagBox(formatContext, fp, &basic_header); /* not use */
2223 case FOURCC('c', 'l', 's', 'f'): {
2224 debug_msg(RELEASE, "MPEG4: [clsf] SIZE: [%lld]Byte\n", chunk_size);
2225 GetClassficationFromClsfTagBox(formatContext, fp, &basic_header);
2228 case FOURCC('k', 'y', 'w', 'd'): {
2229 debug_msg(RELEASE, "MPEG4: [kywd] SIZE: [%lld]Byte\n", chunk_size);
2230 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2233 case FOURCC('l', 'o', 'c', 'i'): {
2234 debug_msg(RELEASE, "MPEG4: [loci] SIZE: [%lld]Byte\n", chunk_size);
2235 GetLocationFromLociTagBox(formatContext, fp, &basic_header);
2238 /* Check smta in user data field (moov) to be compatible with android */
2239 case FOURCC('s', 'm', 't', 'a'): {
2240 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte\n", chunk_size);
2241 GetSAUTInfoFromSMTATagBox(formatContext, fp, &basic_header);
2244 /* Check cdis in user data field (moov) to be compatible with android */
2245 case FOURCC('c', 'd', 'i', 's'): {
2246 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte\n", chunk_size);
2247 GetValueFromCDISTagBox(formatContext, fp, &basic_header);
2250 /*/////////////////////////////////////////////////////////////// */
2251 /* Extracting ID3 Tag Data // */
2252 /*/////////////////////////////////////////////////////////////// */
2253 case FOURCC('m', 'e', 't', 'a'): {
2254 debug_msg(RELEASE, "MPEG4: [meta] SIZE: [%lld]Byte\n", chunk_size);
2255 GetTagFromMetaBox(formatContext, fp, &basic_header);
2259 case FOURCC('t', 'r', 'a', 'k'): {
2260 debug_msg(RELEASE, "MPEG4: [trak] SIZE: [%lld]Byte\n", chunk_size);
2263 case FOURCC('u', 'u', 'i', 'd'): {
2264 unsigned long uuid[4] = {0, };
2266 debug_msg(RELEASE, "MPEG4: [uuid] SIZE: [%lld]Byte\n", chunk_size);
2268 mmfile_read(fp, (unsigned char *)uuid, sizeof(uuid));
2270 if (mmfile_io_be_uint32(uuid[0]) == 0xffcc8263
2271 && mmfile_io_be_uint32(uuid[1]) == 0xf8554a93
2272 && mmfile_io_be_uint32(uuid[2]) == 0x8814587a
2273 && mmfile_io_be_uint32(uuid[3]) == 0x02521fdd) {
2275 str = (char *)malloc(basic_header.size);
2278 memset(str, 0, basic_header.size);
2279 mmfile_read(fp, (unsigned char *)str, basic_header.size);
2281 /* The block is superseded */
2282 if (strstr(str, "<GSpherical:Spherical>true</GSpherical:Spherical>"))
2283 formatContext->is_360 = 1;
2285 formatContext->is_360 = 0;
2286 /* Image can be stitched even if it is not spherical */
2287 if (formatContext->is_360 == 1) {
2288 if (strstr(str, "<GSpherical:Stitched>true</GSpherical:Stitched>"))
2289 formatContext->stitched = MMFILE_360_STITCHED;
2291 formatContext->stitched = MMFILE_360_NON_STITCHED;
2293 /* Image can be stitched or non-stitched. Usage of some 3rd value is superfluous */
2294 formatContext->stitched = MMFILE_360_NONE;
2298 debug_msg(RELEASE, "Extracting tags from UUID XML string %s\n", str);
2300 ParseSpatialVideoMetadataFromXMLString(str, formatContext);
2303 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2307 case FOURCC('m', 'd', 'i', 'a'): {
2308 debug_msg(RELEASE, "MPEG4: [mdia] SIZE: [%lld]Byte\n", chunk_size);
2311 case FOURCC('m', 'i', 'n', 'f'): {
2312 debug_msg(RELEASE, "MPEG4: [minf] SIZE: [%lld]Byte\n", chunk_size);
2315 case FOURCC('s', 't', 'b', 'l'): {
2316 debug_msg(RELEASE, "MPEG4: [stbl] SIZE: [%lld]Byte\n", chunk_size);
2319 case FOURCC('s', 't', 's', 'd'): {
2320 debug_msg(RELEASE, "MPEG4: [stsd] SIZE: [%lld]Byte\n", chunk_size);
2323 case FOURCC('m', 'p', '4', 'a'): {
2324 debug_msg(RELEASE, "MPEG4: [mp4a] SIZE: [%lld]Byte\n", chunk_size);
2325 GetSA3DInfoFromMP4ATagBox(formatContext, fp, &basic_header);
2326 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2329 case FOURCC('a', 'v', 'c', '1'): {
2330 debug_msg(RELEASE, "MPEG4: [avc1] SIZE: [%lld]Byte (offset: %lld)\n", chunk_size, basic_header.start_offset);
2331 GetVideoV2MetadataFromAvc1TagBox(formatContext, fp, &basic_header);
2335 debug_msg(RELEASE, "4CC: Not Support [%c%c%c%c]. So skip it. Size [%lld Byte]\n",
2336 ((char *)&basic_header.type)[0], ((char *)&basic_header.type)[1],
2337 ((char *)&basic_header.type)[2], ((char *)&basic_header.type)[3], chunk_size);
2338 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2343 if (ret == MMFILE_UTIL_FAIL) {
2344 debug_error(DEBUG, "mmfile operation is error\n");
2349 long long new_pos = mmfile_tell(fp);
2351 if ((moov_end == 0) && (new_pos <= basic_header.start_offset)) {
2352 debug_error(DEBUG, "Wrong position");
2353 ret = MMFILE_UTIL_FAIL;
2356 basic_header.start_offset = new_pos;
2365 static char *get_string(const char *buf, int buf_size, int *bytes_written)
2369 char str[512] = {0, };
2372 for (i = 0; i < buf_size; i++) {
2376 if ((q - str) >= (int)sizeof(str) - 1)
2382 if (strlen(str) > 0) {
2383 *bytes_written = strlen(str);
2391 static bool is_numeric(const char *buf, int buf_size)
2396 for (idx = 0; idx < buf_size; idx++) {
2397 if (isdigit((int)buf[idx])) {
2408 char *rtrimN(char *pStr)
2411 pos = strlen(pStr) - 1;
2412 for (; pos >= 0; pos--) {
2413 if (pStr[pos] == 0x20) {
2420 return strdup(pStr);
2423 bool safe_atoi(char *buffer, int *si)
2429 const long sl = strtol(buffer, &end, 10);
2431 if (end == buffer) {
2432 debug_error(RELEASE, "not a decimal number");
2434 } else if ('\0' != *end) {
2435 debug_error(RELEASE, "extra characters at end of input: %s", end);
2437 } else if ((LONG_MIN == sl || LONG_MAX == sl) && (ERANGE == errno)) {
2438 debug_error(RELEASE, "out of range of type long");
2440 } else if (sl > INT_MAX) {
2441 debug_error(RELEASE, "greater than INT_MAX");
2443 } else if (sl < INT_MIN) {
2444 debug_error(RELEASE, "less than INT_MIN");
2452 static bool make_characterset_array(char ***charset_array)
2454 char *locale = MMFileUtilGetLocale();
2456 *charset_array = calloc(AV_ID3V2_MAX, sizeof(char *));
2458 if (*charset_array == NULL) {
2459 debug_error(DEBUG, "calloc failed ");
2465 if (locale != NULL) {
2466 (*charset_array)[AV_ID3V2_ISO_8859] = strdup(locale);
2468 debug_error(DEBUG, "get locale failed");
2469 (*charset_array)[AV_ID3V2_ISO_8859] = NULL;
2472 (*charset_array)[AV_ID3V2_UTF16] = strdup("UCS2");
2473 (*charset_array)[AV_ID3V2_UTF16_BE] = strdup("UTF16-BE");
2474 (*charset_array)[AV_ID3V2_UTF8] = strdup("UTF-8");
2479 static bool release_characterset_array(char **charset_array)
2483 for (i = 0; i < AV_ID3V2_MAX; i++) {
2484 if (charset_array[i] != NULL) {
2485 free(charset_array[i]);
2486 charset_array[i] = NULL;
2490 if (charset_array != NULL) {
2491 free(charset_array);
2492 charset_array = NULL;
2498 static void init_content_info(AvFileContentInfo *pInfo)
2500 pInfo->tagV2Info.bTitleMarked = false;
2501 pInfo->tagV2Info.bArtistMarked = false;
2502 pInfo->tagV2Info.bAlbumMarked = false;
2503 pInfo->tagV2Info.bAlbum_ArtistMarked = false;
2504 pInfo->tagV2Info.bYearMarked = false;
2505 pInfo->tagV2Info.bDescriptionMarked = false;
2506 pInfo->tagV2Info.bGenreMarked = false;
2507 pInfo->tagV2Info.bTrackNumMarked = false;
2508 pInfo->tagV2Info.bEncByMarked = false;
2509 pInfo->tagV2Info.bURLMarked = false;
2510 pInfo->tagV2Info.bCopyRightMarked = false;
2511 pInfo->tagV2Info.bOriginArtistMarked = false;
2512 pInfo->tagV2Info.bComposerMarked = false;
2513 pInfo->tagV2Info.bImageMarked = false;
2515 pInfo->tagV2Info.bRecDateMarked = false;
2516 pInfo->tagV2Info.bContentGroupMarked = false;
2518 pInfo->tagV2Info.bUnsyncLyricsMarked = false;
2519 pInfo->tagV2Info.bSyncLyricsMarked = false;
2520 pInfo->tagV2Info.bConductorMarked = false;
2521 pInfo->tagV2Info.bGenreUTF16 = false;
2523 pInfo->imageInfo.bURLInfo = false;
2524 pInfo->imageInfo.pImageBuf = NULL;
2525 pInfo->imageInfo.imageLen = 0;
2529 bool mm_file_id3tag_parse_v110(AvFileContentInfo *pInfo, unsigned char *buffer)
2531 const char *locale = MMFileUtilGetLocale();
2532 char *pFullStr = NULL;
2534 debug_msg(RELEASE, "ID3tag v110--------------------------------------------------------------\n");
2536 if (pInfo->tagV2Info.bTitleMarked == false) {
2537 pFullStr = mmfile_string_convert((const char *)&buffer[3], MP3_ID3_TITLE_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->titleLen);
2538 if (pFullStr != NULL) {
2539 pInfo->pTitle = rtrimN(pFullStr);
2543 debug_msg(RELEASE, "pInfo->pTitle returned =(%s), pInfo->titleLen(%d)\n", pInfo->pTitle, pInfo->titleLen);
2546 if (pInfo->tagV2Info.bArtistMarked == false) {
2547 pFullStr = mmfile_string_convert((const char *)&buffer[33], MP3_ID3_ARTIST_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->artistLen);
2548 if (pFullStr != NULL) {
2549 pInfo->pArtist = rtrimN(pFullStr);
2553 debug_msg(RELEASE, "pInfo->pArtist returned =(%s), pInfo->artistLen(%d)\n", pInfo->pArtist, pInfo->artistLen);
2556 if (pInfo->tagV2Info.bAlbumMarked == false) {
2557 pFullStr = mmfile_string_convert((const char *)&buffer[63], MP3_ID3_ALBUM_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->albumLen);
2558 if (pFullStr != NULL) {
2559 pInfo->pAlbum = rtrimN(pFullStr);
2563 debug_msg(RELEASE, "pInfo->pAlbum returned =(%s), pInfo->albumLen(%d)\n", pInfo->pAlbum, pInfo->albumLen);
2566 if (pInfo->tagV2Info.bYearMarked == false) {
2568 pInfo->pYear = mmfile_string_convert((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->yearLen);
2570 debug_msg(RELEASE, "pInfo->pYear returned =(%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
2572 if (pInfo->pYear == NULL) { /*Use same logic with ffmpeg*/
2573 pInfo->pYear = get_string((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, (int *)&pInfo->yearLen);
2574 debug_msg(RELEASE, "pInfo->pYear returned =(%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
2578 if (pInfo->tagV2Info.bDescriptionMarked == false) {
2579 pInfo->pComment = mmfile_string_convert((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->commentLen);
2580 debug_msg(RELEASE, "pInfo->pComment returned =(%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
2582 if (pInfo->pComment == NULL) { /*Use same logic with ffmpeg*/
2583 pInfo->pComment = get_string((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, (int *)&pInfo->commentLen);
2584 debug_msg(RELEASE, "pInfo->pComment returned =(%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
2588 if (pInfo->tagV2Info.bTrackNumMarked == false) {
2589 pInfo->pTrackNum = mmfile_malloc(5);
2590 if (pInfo->pTrackNum != NULL) {
2591 pInfo->pTrackNum[4] = 0;
2592 snprintf(pInfo->pTrackNum, 4, "%04d", (int)buffer[126]);
2593 pInfo->tracknumLen = strlen(pInfo->pTrackNum);
2595 debug_msg(RELEASE, "pInfo->pTrackNum returned =(%s), pInfo->tracknumLen(%d)\n", pInfo->pTrackNum, pInfo->tracknumLen);
2599 if (pInfo->tagV2Info.bGenreMarked == false) {
2600 pInfo->genre = buffer[127];
2601 debug_msg(RELEASE, "pInfo->genre returned genre number (%d)\n", pInfo->genre);
2608 bool mm_file_id3tag_parse_v222(AvFileContentInfo *pInfo, unsigned char *buffer)
2610 unsigned long taglen = 0;
2611 unsigned long needToloopv2taglen;
2612 unsigned long oneFrameLen = 0;
2613 unsigned long v2numOfFrames = 0;
2614 unsigned long curPos = 0;
2616 unsigned char *pExtContent = NULL;
2617 unsigned long purelyFramelen = 0;
2618 unsigned int encodingOffSet = 0;
2619 int inx = 0, realCpyFrameNum = 0,
2620 /*checkImgMimeTypeMax = 0, */checkImgExtMax = 0,
2621 imgstartOffset = 0, tmp = 0;
2623 int textEncodingType = 0;
2625 char **charset_array = NULL;
2627 make_characterset_array(&charset_array);
2629 init_content_info(pInfo);
2631 taglen = pInfo->tagV2Info.tagLen;
2632 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
2633 curPos = MP3_TAGv2_HEADER_LEN;
2635 debug_msg(RELEASE, "ID3tag v222--------------------------------------------------------------\n");
2637 if (needToloopv2taglen - MP3_TAGv2_22_TXT_HEADER_LEN > MP3_TAGv2_22_TXT_HEADER_LEN) {
2639 while (needToloopv2taglen > MP3_TAGv2_22_TXT_HEADER_LEN) {
2640 if ((buffer[curPos] < '0' || buffer[curPos] > 'Z') || (buffer[curPos + 1] < '0' || buffer[curPos + 1] > 'Z')
2641 || (buffer[curPos + 2] < '0' || buffer[curPos + 2] > 'Z'))
2644 memcpy(CompTmp, &buffer[curPos], 3);
2647 oneFrameLen = MP3_TAGv2_22_TXT_HEADER_LEN;
2648 oneFrameLen += (unsigned long)buffer[3 + curPos] << 16 | (unsigned long)buffer[4 + curPos] << 8
2649 | (unsigned long)buffer[5 + curPos];
2650 if (oneFrameLen > taglen - curPos)
2652 purelyFramelen = oneFrameLen - MP3_TAGv2_22_TXT_HEADER_LEN;
2653 curPos += MP3_TAGv2_22_TXT_HEADER_LEN;
2655 if (oneFrameLen > MP3_TAGv2_22_TXT_HEADER_LEN && purelyFramelen <= taglen - curPos) {
2656 curPos += purelyFramelen;
2658 if (buffer[curPos - purelyFramelen] == 0x00) {
2660 textEncodingType = AV_ID3V2_ISO_8859;
2661 } else if (buffer[curPos - purelyFramelen] == 0x01) {
2663 textEncodingType = AV_ID3V2_UTF16;
2666 /*in order to deliver valid string to MP */
2667 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen))
2670 if (encodingOffSet < purelyFramelen) {
2671 realCpyFrameNum = purelyFramelen - encodingOffSet;
2672 pExtContent = mmfile_malloc(realCpyFrameNum + 3);
2674 if (pExtContent == NULL) {
2675 debug_error(DEBUG, "out of memory for pExtContent\n");
2679 memset(pExtContent, '\0', realCpyFrameNum + 3);
2681 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
2683 if (realCpyFrameNum > 0) {
2684 if (strncmp((char *)CompTmp, "TT2", 3) == 0 && pInfo->tagV2Info.bTitleMarked == false) {
2685 pInfo->pTitle = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->titleLen);
2686 debug_msg(RELEASE, "pInfo->pTitle returned = (%s), pInfo->titleLen(%d)\n", pInfo->pTitle, pInfo->titleLen);
2687 pInfo->tagV2Info.bTitleMarked = true;
2688 } else if (strncmp((char *)CompTmp, "TP1", 3) == 0 && pInfo->tagV2Info.bArtistMarked == false) {
2689 pInfo->pArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->artistLen);
2690 debug_msg(RELEASE, "pInfo->pArtist returned = (%s), pInfo->artistLen(%d)\n", pInfo->pArtist, pInfo->artistLen);
2691 pInfo->tagV2Info.bArtistMarked = true;
2692 } else if (strncmp((char *)CompTmp, "TP2", 3) == 0 && pInfo->tagV2Info.bAlbum_ArtistMarked == false) {
2693 pInfo->pAlbum_Artist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->album_artistLen);
2694 debug_msg(RELEASE, "pInfo->pAlbum_Artist returned = (%s), pInfo->album_artistLen(%d)\n", pInfo->pAlbum_Artist, pInfo->album_artistLen);
2695 pInfo->tagV2Info.bAlbum_ArtistMarked = true;
2696 } else if (strncmp((char *)CompTmp, "TP3", 3) == 0 && pInfo->tagV2Info.bConductorMarked == false) {
2697 pInfo->pConductor = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->conductorLen);
2698 debug_msg(RELEASE, "pInfo->pConductor returned = (%s), pInfo->conductorLen(%d)\n", pInfo->pConductor, pInfo->conductorLen);
2699 pInfo->tagV2Info.bConductorMarked = true;
2700 } else if (strncmp((char *)CompTmp, "TAL", 3) == 0 && pInfo->tagV2Info.bAlbumMarked == false) {
2701 pInfo->pAlbum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->albumLen);
2702 debug_msg(RELEASE, "pInfo->pAlbum returned = (%s), pInfo->albumLen(%d)\n", pInfo->pAlbum, pInfo->albumLen);
2703 pInfo->tagV2Info.bAlbumMarked = true;
2704 } else if (strncmp((char *)CompTmp, "TYE", 3) == 0 && pInfo->tagV2Info.bYearMarked == false) {
2705 pInfo->pYear = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->yearLen);
2706 debug_msg(RELEASE, "pInfo->pYear returned = (%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
2707 pInfo->tagV2Info.bYearMarked = true;
2708 } else if (strncmp((char *)CompTmp, "COM", 3) == 0 && pInfo->tagV2Info.bDescriptionMarked == false) {
2709 /*skip language data! */
2710 if (realCpyFrameNum > 4) {
2711 realCpyFrameNum -= 4;
2714 /*pExtContent[tmp+1] value should't have encoding value */
2715 if (pExtContent[tmp] > 0x20 && (pExtContent[tmp - 1] == 0x00 || pExtContent[tmp - 1] == 0x01)) {
2716 if (pExtContent[tmp - 1] == 0x00)
2717 textEncodingType = AV_ID3V2_ISO_8859;
2719 textEncodingType = AV_ID3V2_UTF16;
2721 pInfo->pComment = mmfile_string_convert((char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->commentLen);
2722 debug_msg(RELEASE, "pInfo->pComment returned = (%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
2723 pInfo->tagV2Info.bDescriptionMarked = true;
2725 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: failed to get Comment Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
2728 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
2732 } else if (strncmp((char *)CompTmp, "TCO", 3) == 0 && pInfo->tagV2Info.bGenreMarked == false) {
2733 pInfo->pGenre = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->genreLen);
2734 debug_msg(RELEASE, "pInfo->pGenre returned = (%s), pInfo->genreLen(%d)\n", pInfo->pGenre, pInfo->genreLen);
2736 if ((pInfo->pGenre != NULL) && (pInfo->genreLen > 0)) {
2740 ret = is_numeric(pInfo->pGenre, pInfo->genreLen);
2743 ret = safe_atoi(pInfo->pGenre, &int_genre);
2745 debug_msg(RELEASE, "genre information is inteager [%d]\n", int_genre);
2747 /*Change int to string */
2748 if ((0 <= int_genre) && (int_genre < GENRE_COUNT - 1)) {
2749 /*save genreinfo like "(123)". mm_file_id3tag_restore_content_info convert it to string*/
2750 char tmp_genre[6] = {0, }; /*ex. "(123)+NULL"*/
2751 int tmp_genre_len = 0;
2753 memset(tmp_genre, 0, 6);
2754 snprintf(tmp_genre, sizeof(tmp_genre), "(%d)", int_genre);
2756 tmp_genre_len = strlen(tmp_genre);
2757 if (tmp_genre_len > 0) {
2758 mmfile_free(pInfo->pGenre);
2759 pInfo->pGenre = mmfile_malloc(sizeof(char) * (tmp_genre_len + 1));
2760 if (pInfo->pGenre) {
2761 SAFE_STRLCPY(pInfo->pGenre, tmp_genre, tmp_genre_len + 1);
2766 debug_error(RELEASE, "genre information is wrong inteager [%s]\n", pInfo->pGenre);
2771 pInfo->tagV2Info.bGenreMarked = true;
2772 } else if (strncmp((char *)CompTmp, "TRK", 3) == 0 && pInfo->tagV2Info.bTrackNumMarked == false) {
2773 pInfo->pTrackNum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tracknumLen);
2774 debug_msg(RELEASE, "pInfo->pTrackNum returned = (%s), pInfo->tracknumLen(%d)\n", pInfo->pTrackNum, pInfo->tracknumLen);
2775 pInfo->tagV2Info.bTrackNumMarked = true;
2776 } else if (strncmp((char *)CompTmp, "TEN", 3) == 0 && pInfo->tagV2Info.bEncByMarked == false) {
2777 pInfo->pEncBy = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->encbyLen);
2778 debug_msg(RELEASE, "pInfo->pEncBy returned = (%s), pInfo->encbyLen(%d)\n", pInfo->pEncBy, pInfo->encbyLen);
2779 pInfo->tagV2Info.bEncByMarked = true;
2780 } else if (strncmp((char *)CompTmp, "WXX", 3) == 0 && pInfo->tagV2Info.bURLMarked == false) {
2781 if (realCpyFrameNum > 4) {
2782 /*skip language data! */
2783 realCpyFrameNum -= 4;
2786 /*pExtContent[tmp+1] value should't have null value */
2787 if (pExtContent[tmp] > 0x20 && (pExtContent[tmp - 1] == 0x00 || pExtContent[tmp - 1] == 0x01)) {
2788 if (pExtContent[tmp - 1] == 0x00)
2789 textEncodingType = AV_ID3V2_ISO_8859;
2791 textEncodingType = AV_ID3V2_UTF16;
2793 pInfo->pURL = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->urlLen);
2794 debug_msg(RELEASE, "pInfo->pURL returned = (%s), pInfo->urlLen(%d)\n", pInfo->pURL, pInfo->urlLen);
2795 pInfo->tagV2Info.bURLMarked = true;
2797 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: failed to get URL Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
2800 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: URL info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
2803 } else if (strncmp((char *)CompTmp, "TCR", 3) == 0 && pInfo->tagV2Info.bCopyRightMarked == false) {
2804 pInfo->pCopyright = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->copyrightLen);
2805 debug_msg(RELEASE, "pInfo->pCopyright returned = (%s), pInfo->copyrightLen(%d)\n", pInfo->pCopyright, pInfo->copyrightLen);
2806 pInfo->tagV2Info.bCopyRightMarked = true;
2807 } else if (strncmp((char *)CompTmp, "TOA", 3) == 0 && pInfo->tagV2Info.bOriginArtistMarked == false) {
2808 pInfo->pOriginArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->originartistLen);
2809 debug_msg(RELEASE, "pInfo->pOriginArtist returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pOriginArtist, pInfo->originartistLen);
2810 pInfo->tagV2Info.bOriginArtistMarked = true;
2811 } else if (strncmp((char *)CompTmp, "TCM", 3) == 0 && pInfo->tagV2Info.bComposerMarked == false) {
2812 pInfo->pComposer = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->composerLen);
2813 debug_msg(RELEASE, "pInfo->pComposer returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pComposer, pInfo->composerLen);
2814 pInfo->tagV2Info.bComposerMarked = true;
2815 } else if (strncmp((char *)CompTmp, "TRD", 3) == 0 && pInfo->tagV2Info.bRecDateMarked == false) {
2816 pInfo->pRecDate = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->recdateLen);
2817 debug_msg(RELEASE, "pInfo->pRecDate returned = (%s), pInfo->recdateLen(%d)\n", pInfo->pRecDate, pInfo->recdateLen);
2818 pInfo->tagV2Info.bRecDateMarked = true;
2819 } else if (strncmp((char *)CompTmp, "PIC", 3) == 0 && pInfo->tagV2Info.bImageMarked == false && realCpyFrameNum <= 2000000) {
2820 if (pExtContent[0] != 0) {
2821 for (inx = 0; inx < MP3_ID3_IMAGE_EXT_MAX_LENGTH; inx++)
2822 pInfo->imageInfo.imageExt[inx] = '\0';/*ini mimetype variable */
2824 while ((checkImgExtMax < MP3_ID3_IMAGE_EXT_MAX_LENGTH - 1) && pExtContent[checkImgExtMax] != '\0') {
2825 pInfo->imageInfo.imageExt[checkImgExtMax] = pExtContent[checkImgExtMax];
2829 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: PIC image's not included to image Extention\n");
2832 imgstartOffset += checkImgExtMax;
2834 if (pExtContent[imgstartOffset] < AV_ID3V2_PICTURE_TYPE_MAX) {
2835 pInfo->imageInfo.pictureType = pExtContent[imgstartOffset];
2837 imgstartOffset++;/*PictureType(1byte) */
2839 if (pExtContent[imgstartOffset] != 0x0) {
2842 int new_dis_len = 0;
2843 char *tmp_desc = NULL;
2846 if (pExtContent[imgstartOffset + cur_pos] == '\0') {
2847 if (realCpyFrameNum < imgstartOffset + cur_pos) {
2848 debug_error(DEBUG, "End of APIC Tag %d %d %d\n", realCpyFrameNum, imgstartOffset, cur_pos);
2851 /*check end of image description*/
2852 if ((pExtContent[imgstartOffset + cur_pos + 1] == gTagJPEGHeader[0]) ||
2853 (pExtContent[imgstartOffset + cur_pos + 1] == gTagPNGHeader[0])) {
2854 debug_msg(RELEASE, "length of description (%d)", cur_pos);
2861 dis_len = cur_pos + 1;
2863 tmp_desc = mmfile_malloc(sizeof(char) * dis_len);
2865 if (tmp_desc != NULL) {
2866 memcpy(tmp_desc, pExtContent + imgstartOffset, dis_len);
2868 /*convert description*/
2869 pInfo->imageInfo.imageDescription = mmfile_string_convert(tmp_desc, dis_len, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&new_dis_len);
2870 mmfile_free(tmp_desc);
2871 debug_msg(RELEASE, "new_desc %s(%d)\n", pInfo->imageInfo.imageDescription, new_dis_len);
2872 pInfo->imageInfo.imgDesLen = new_dis_len; /**/
2875 imgstartOffset += cur_pos;
2877 pInfo->imageInfo.imgDesLen = 0;
2880 if ((pExtContent[imgstartOffset] == '\0') && (realCpyFrameNum - imgstartOffset > 0)) {
2881 imgstartOffset++; /* endofDesceriptionType(1byte) */
2883 while (pExtContent[imgstartOffset] == '\0') { /*some content has useless '\0' in front of picture data */
2887 debug_msg(RELEASE, "after scaning imgDescription imgstartOffset(%d) value!\n", imgstartOffset);
2889 if (realCpyFrameNum - imgstartOffset > 0) {
2890 pInfo->imageInfo.imageLen = realCpyFrameNum - imgstartOffset;
2891 pInfo->imageInfo.pImageBuf = mmfile_malloc(pInfo->imageInfo.imageLen + 1);
2893 if (pInfo->imageInfo.pImageBuf != NULL) {
2894 memcpy(pInfo->imageInfo.pImageBuf, pExtContent + imgstartOffset, pInfo->imageInfo.imageLen);
2895 pInfo->imageInfo.pImageBuf[pInfo->imageInfo.imageLen] = 0;
2898 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
2899 pInfo->imageInfo.bURLInfo = true; /*if mimetype is "-->", image date has an URL */
2901 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - imgstartOffset(%d)\n", realCpyFrameNum, imgstartOffset);
2905 /*checkImgMimeTypeMax = 0;*/
2909 pInfo->tagV2Info.bImageMarked = true;
2916 curPos += purelyFramelen;
2917 if (purelyFramelen != 0)
2918 needToloopv2taglen = MP3_TAGv2_22_TXT_HEADER_LEN;
2921 mmfile_free(pExtContent);
2922 memset(CompTmp, 0, 4);
2923 if (curPos < taglen) {
2924 needToloopv2taglen -= oneFrameLen;
2927 needToloopv2taglen = MP3_TAGv2_22_TXT_HEADER_LEN;
2930 realCpyFrameNum = 0;
2931 textEncodingType = 0;
2937 release_characterset_array(charset_array);
2947 bool mm_file_id3tag_parse_v223(AvFileContentInfo *pInfo, unsigned char *buffer)
2949 unsigned long taglen = 0;
2950 unsigned long needToloopv2taglen;
2951 unsigned long oneFrameLen = 0;
2952 unsigned long v2numOfFrames = 0;
2953 unsigned long curPos = 0;
2955 unsigned char *pExtContent = NULL;
2956 unsigned long purelyFramelen = 0;
2957 unsigned int encodingOffSet = 0;
2958 int inx = 0, realCpyFrameNum = 0, checkImgMimeTypeMax = 0, imgstartOffset = 0, tmp = 0;
2959 unsigned int textEncodingType = 0;
2960 char **charset_array = NULL;
2961 const char *MIME_PRFIX = "image/";
2963 make_characterset_array(&charset_array);
2965 init_content_info(pInfo);
2967 taglen = pInfo->tagV2Info.tagLen;
2968 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
2969 curPos = MP3_TAGv2_HEADER_LEN;
2971 debug_msg(RELEASE, "ID3tag v223--------------------------------------------------------------\n");
2973 /* check Extended Header */
2974 if (buffer[5] & 0x40) {
2975 /* if extended header exists, skip it*/
2976 int extendedHeaderLen = (unsigned long)buffer[10] << 21 | (unsigned long)buffer[11] << 14 | (unsigned long)buffer[12] << 7 | (unsigned long)buffer[13];
2978 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d\n", extendedHeaderLen);
2980 if (extendedHeaderLen > (int)(taglen - curPos)) {
2981 debug_error(DEBUG, "extended header too long.\n");
2983 curPos += extendedHeaderLen;
2988 if (needToloopv2taglen - MP3_TAGv2_23_TXT_HEADER_LEN > MP3_TAGv2_23_TXT_HEADER_LEN) {
2990 while (needToloopv2taglen > MP3_TAGv2_23_TXT_HEADER_LEN) {
2991 if ((buffer[curPos] < '0' || buffer[curPos] > 'Z') || (buffer[curPos + 1] < '0' || buffer[curPos + 1] > 'Z')
2992 || (buffer[curPos + 2] < '0' || buffer[curPos + 2] > 'Z') || (buffer[curPos + 3] < '0' || buffer[curPos + 3] > 'Z'))
2995 memcpy(CompTmp, &buffer[curPos], 4);
2998 oneFrameLen = MP3_TAGv2_23_TXT_HEADER_LEN;
2999 oneFrameLen += (unsigned long)buffer[4 + curPos] << 24 | (unsigned long)buffer[5 + curPos] << 16
3000 | (unsigned long)buffer[6 + curPos] << 8 | (unsigned long)buffer[7 + curPos];
3002 debug_msg(RELEASE, "----------------------------------------------------------------------------------------------------\n");
3004 if (oneFrameLen > taglen - curPos)
3007 purelyFramelen = oneFrameLen - MP3_TAGv2_23_TXT_HEADER_LEN;
3008 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3010 if (oneFrameLen > MP3_TAGv2_23_TXT_HEADER_LEN && purelyFramelen <= taglen - curPos) {
3011 curPos += purelyFramelen;
3013 if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen))) {
3015 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3016 textEncodingType = AV_ID3V2_UTF16;
3017 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen))) {
3019 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3020 textEncodingType = AV_ID3V2_UTF16_BE;
3021 } else if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen + 1))) {
3023 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3024 textEncodingType = AV_ID3V2_UTF16;
3025 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen + 1))) {
3027 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3028 textEncodingType = AV_ID3V2_UTF16_BE;
3030 if (buffer[curPos - purelyFramelen + encodingOffSet] == 0x00) {
3031 debug_msg(RELEASE, "encodingOffset will be set to 1\n");
3034 debug_msg(RELEASE, "Finding encodingOffset\n");
3036 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen)) /* text string encoded by ISO-8859-1 */
3039 textEncodingType = AV_ID3V2_ISO_8859;
3040 debug_msg(RELEASE, "this text string(%s) encoded by ISO-8859-1 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3043 mmfile_free(pExtContent);
3045 if (encodingOffSet < purelyFramelen) {
3046 realCpyFrameNum = purelyFramelen - encodingOffSet;
3047 pExtContent = mmfile_malloc(realCpyFrameNum + 3);
3049 if (pExtContent == NULL) {
3050 debug_msg(DEBUG, "pExtContent malloc failed\n");
3054 memset(pExtContent, '\0', realCpyFrameNum + 3);
3056 if (textEncodingType != AV_ID3V2_UTF16 && textEncodingType != AV_ID3V2_UTF16_BE) {
3057 if (CompTmp[0] == 'T' || (strcmp(CompTmp, "APIC") == 0)) {
3058 debug_msg(RELEASE, "get the new text ecoding type\n");
3059 textEncodingType = buffer[curPos - purelyFramelen + encodingOffSet - 1];
3063 if (textEncodingType > AV_ID3V2_MAX) {
3064 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%d], FRAME[%s]\n", textEncodingType, (char *)CompTmp);
3068 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
3069 if (realCpyFrameNum > 0) {
3070 if (strncmp((char *)CompTmp, "TIT2", 4) == 0 && pInfo->tagV2Info.bTitleMarked == false) {
3071 pInfo->pTitle = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->titleLen);
3072 debug_msg(RELEASE, "pInfo->pTitle returned = (%s), pInfo->titleLen(%d)\n", pInfo->pTitle, pInfo->titleLen);
3073 pInfo->tagV2Info.bTitleMarked = true;
3075 } else if (strncmp((char *)CompTmp, "TPE1", 4) == 0 && pInfo->tagV2Info.bArtistMarked == false) {
3076 pInfo->pArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->artistLen);
3077 debug_msg(RELEASE, "pInfo->pArtist returned = (%s), pInfo->artistLen(%d)\n", pInfo->pArtist, pInfo->artistLen);
3078 pInfo->tagV2Info.bArtistMarked = true;
3079 } else if (strncmp((char *)CompTmp, "TPE2", 4) == 0 && pInfo->tagV2Info.bAlbum_ArtistMarked == false) {
3080 pInfo->pAlbum_Artist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->album_artistLen);
3081 debug_msg(RELEASE, "pInfo->pAlbum_Artist returned = (%s), pInfo->album_artistLen(%d)\n", pInfo->pAlbum_Artist, pInfo->album_artistLen);
3082 pInfo->tagV2Info.bAlbum_ArtistMarked = true;
3083 } else if (strncmp((char *)CompTmp, "TPE3", 4) == 0 && pInfo->tagV2Info.bConductorMarked == false) {
3084 pInfo->pConductor = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->conductorLen);
3085 debug_msg(RELEASE, "pInfo->pConductor returned = (%s), pInfo->conductorLen(%d)\n", pInfo->pConductor, pInfo->conductorLen);
3086 pInfo->tagV2Info.bConductorMarked = true;
3087 } else if (strncmp((char *)CompTmp, "TALB", 4) == 0 && pInfo->tagV2Info.bAlbumMarked == false) {
3088 pInfo->pAlbum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->albumLen);
3089 debug_msg(RELEASE, "pInfo->pAlbum returned = (%s), pInfo->albumLen(%d)\n", pInfo->pAlbum, pInfo->albumLen);
3090 pInfo->tagV2Info.bAlbumMarked = true;
3091 } else if (strncmp((char *)CompTmp, "TYER", 4) == 0 && pInfo->tagV2Info.bYearMarked == false) {
3092 pInfo->pYear = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->yearLen);
3093 debug_msg(RELEASE, "pInfo->pYear returned = (%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
3094 pInfo->tagV2Info.bYearMarked = true;
3095 } else if (strncmp((char *)CompTmp, "COMM", 4) == 0 && pInfo->tagV2Info.bDescriptionMarked == false) {
3096 if (realCpyFrameNum > 3) {
3097 realCpyFrameNum -= 3;
3100 /*pExtContent[tmp+1] value should't have encoding value */
3101 if (pExtContent[tmp] == 0x00 || pExtContent[tmp] == 0xFF || pExtContent[tmp] == 0xFE) {
3102 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3103 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3104 realCpyFrameNum -= 4;
3108 if (IS_ENCODEDBY_UTF16(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3109 realCpyFrameNum -= 2;
3111 textEncodingType = AV_ID3V2_UTF16;
3112 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3113 realCpyFrameNum -= 2;
3115 textEncodingType = AV_ID3V2_UTF16_BE;
3116 } else if (IS_ENCODEDBY_UTF16(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3117 realCpyFrameNum -= 3;
3119 textEncodingType = AV_ID3V2_UTF16;
3120 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3121 realCpyFrameNum -= 3;
3123 textEncodingType = AV_ID3V2_UTF16_BE;
3125 debug_msg(RELEASE, "pInfo->pComment Never Get Here!!\n");
3128 while ((pExtContent[tmp] < 0x20) && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3132 textEncodingType = AV_ID3V2_ISO_8859;
3135 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3136 pInfo->pComment = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->commentLen);
3138 debug_msg(RELEASE, "failed to get Comment Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3139 pInfo->commentLen = 0;
3142 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3143 pInfo->commentLen = 0;
3147 debug_msg(RELEASE, "pInfo->pComment returned = (%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
3148 pInfo->tagV2Info.bDescriptionMarked = true;
3149 } else if (strncmp((char *)CompTmp, "SYLT", 4) == 0 && pInfo->tagV2Info.bSyncLyricsMarked == false) {
3152 int copy_start_pos = tmp;
3153 AvSynclyricsInfo *synclyrics_info = NULL;
3154 GList *synclyrics_info_list = NULL;
3156 if (realCpyFrameNum > 5) {
3157 realCpyFrameNum -= 5;
3160 /*pExtContent[tmp+1] value should't have encoding value */
3161 if (pExtContent[tmp] == 0x00 || pExtContent[tmp] == 0xFF || pExtContent[tmp] == 0xFE) {
3162 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3163 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3164 realCpyFrameNum -= 4;
3168 if (IS_ENCODEDBY_UTF16(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3169 realCpyFrameNum -= 2;
3171 textEncodingType = AV_ID3V2_UTF16;
3172 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3173 realCpyFrameNum -= 2;
3175 textEncodingType = AV_ID3V2_UTF16_BE;
3176 } else if (IS_ENCODEDBY_UTF16(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3177 realCpyFrameNum -= 3;
3179 textEncodingType = AV_ID3V2_UTF16;
3180 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3181 realCpyFrameNum -= 3;
3183 textEncodingType = AV_ID3V2_UTF16_BE;
3185 debug_msg(RELEASE, "pInfo->pSyncLyrics Never Get Here!!\n");
3188 while ((pExtContent[tmp] < 0x20) && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3192 textEncodingType = AV_ID3V2_ISO_8859;
3195 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3197 if (realCpyFrameNum < MMFILE_SYNC_LYRIC_INFO_MIN_LEN) {
3198 debug_msg(RELEASE, "failed to get Synchronised lyrics Info realCpyFramNum(%d)\n", realCpyFrameNum);
3199 pInfo->syncLyricsNum = 0;
3201 if (textEncodingType == AV_ID3V2_UTF16) {
3202 debug_warning(DEBUG, "[AV_ID3V2_UTF16] not implemented\n");
3203 } else if (textEncodingType == AV_ID3V2_UTF16_BE) {
3204 debug_warning(DEBUG, "[AV_ID3V2_UTF16_BE] not implemented\n");
3206 for (idx = 0; idx < realCpyFrameNum; idx++) {
3207 if (pExtContent[tmp + idx] == 0x00) {
3208 synclyrics_info = (AvSynclyricsInfo *)malloc(sizeof(AvSynclyricsInfo));
3210 if (synclyrics_info != NULL) {
3211 if (textEncodingType == AV_ID3V2_UTF8) {
3212 synclyrics_info->lyric_info = mmfile_malloc(copy_len + 1);
3213 if (synclyrics_info->lyric_info != NULL) {
3214 memset(synclyrics_info->lyric_info, 0, copy_len + 1);
3215 memcpy(synclyrics_info->lyric_info, pExtContent + copy_start_pos, copy_len);
3216 synclyrics_info->lyric_info[copy_len] = '\0';
3219 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);
3222 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];
3224 copy_start_pos = tmp + idx + 1;
3225 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);
3227 synclyrics_info_list = g_list_append(synclyrics_info_list, synclyrics_info);
3232 pInfo->pSyncLyrics = synclyrics_info_list;
3233 pInfo->syncLyricsNum = g_list_length(pInfo->pSyncLyrics);
3237 debug_msg(RELEASE, "failed to get Synchronised lyrics Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3238 pInfo->syncLyricsNum = 0;
3241 debug_msg(RELEASE, "Synchronised lyrics too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3242 pInfo->syncLyricsNum = 0;
3246 //debug_msg(RELEASE, "pInfo->pSyncLyrics returned = (%s), pInfo->syncLyricsNum(%d)\n", pInfo->pSyncLyrics, pInfo->syncLyricsNum);
3247 debug_msg(RELEASE, "pInfo->syncLyricsNum(%d)\n", pInfo->syncLyricsNum);
3248 pInfo->tagV2Info.bSyncLyricsMarked = true;
3249 } else if (strncmp((char *)CompTmp, "USLT", 4) == 0 && pInfo->tagV2Info.bUnsyncLyricsMarked == false) {
3250 char *lang_info = strndup((char *)pExtContent, 3);
3252 if (realCpyFrameNum > 3) {
3253 realCpyFrameNum -= 3;
3256 /*find start of lyrics */
3258 if (pExtContent[tmp] == 0x00) {
3259 if (pExtContent[tmp + 1] == 0x00) {
3260 realCpyFrameNum -= 2;
3270 /*pExtContent[tmp+1] value should't have encoding value */
3271 debug_msg(RELEASE, "tpExtContent[%d] %x\n", tmp, pExtContent[tmp]);
3273 if (pExtContent[tmp] == 0x00 || pExtContent[tmp] == 0xFF || pExtContent[tmp] == 0xFE) {
3274 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3275 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3276 realCpyFrameNum -= 4;
3280 if (IS_ENCODEDBY_UTF16(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3281 realCpyFrameNum -= 2;
3283 textEncodingType = AV_ID3V2_UTF16;
3284 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3285 realCpyFrameNum -= 2;
3287 textEncodingType = AV_ID3V2_UTF16_BE;
3288 } else if (IS_ENCODEDBY_UTF16(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3289 realCpyFrameNum -= 3;
3291 textEncodingType = AV_ID3V2_UTF16;
3292 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3293 realCpyFrameNum -= 3;
3295 textEncodingType = AV_ID3V2_UTF16_BE;
3297 debug_msg(RELEASE, "pInfo->pUnsyncLyrics Never Get Here!!\n");
3300 while ((pExtContent[tmp] < 0x20) && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3304 textEncodingType = AV_ID3V2_ISO_8859;
3307 char *char_set = NULL;
3309 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3311 if (textEncodingType == AV_ID3V2_ISO_8859) {
3312 if (lang_info != NULL && !strcasecmp(lang_info, "KOR")) {
3313 char_set = strdup("EUC-KR");
3315 char_set = mmfile_get_charset((const char *)&pExtContent[tmp]);
3317 mmfile_free(lang_info);
3320 if (char_set == NULL) {
3321 pInfo->pUnsyncLyrics = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->unsynclyricsLen);
3323 pInfo->pUnsyncLyrics = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", char_set, NULL, (unsigned int *)&pInfo->unsynclyricsLen);
3324 mmfile_free(char_set);
3327 debug_msg(RELEASE, "failed to get Unsynchronised lyrics Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3328 pInfo->unsynclyricsLen = 0;
3331 debug_msg(RELEASE, "Unsynchronised lyrics too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3332 pInfo->unsynclyricsLen = 0;
3336 debug_msg(RELEASE, "pInfo->pUnsyncLyrics returned = (%s), pInfo->unsynclyricsLen(%d)\n", pInfo->pUnsyncLyrics, pInfo->unsynclyricsLen);
3337 pInfo->tagV2Info.bUnsyncLyricsMarked = true;
3338 mmfile_free(lang_info);
3339 } else if (strncmp((char *)CompTmp, "TCON", 4) == 0 && pInfo->tagV2Info.bGenreMarked == false) {
3340 pInfo->pGenre = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->genreLen);
3341 debug_msg(RELEASE, "pInfo->pGenre returned = (%s), pInfo->genreLen(%d)\n", pInfo->pGenre, pInfo->genreLen);
3343 if ((pInfo->pGenre != NULL) && (pInfo->genreLen > 0)) {
3347 ret = is_numeric(pInfo->pGenre, pInfo->genreLen);
3350 ret = safe_atoi(pInfo->pGenre, &int_genre);
3352 debug_msg(RELEASE, "genre information is inteager [%d]\n", int_genre);
3354 /*Change int to string */
3355 if ((0 <= int_genre) && (int_genre < GENRE_COUNT - 1)) {
3356 /*save genreinfo like "(123)". mm_file_id3tag_restore_content_info convert it to string*/
3357 char tmp_genre[6] = {0, }; /*ex. "(123)+NULL"*/
3358 int tmp_genre_len = 0;
3360 memset(tmp_genre, 0, 6);
3361 snprintf(tmp_genre, sizeof(tmp_genre), "(%d)", int_genre);
3363 tmp_genre_len = strlen(tmp_genre);
3364 if (tmp_genre_len > 0) {
3365 mmfile_free(pInfo->pGenre);
3366 pInfo->pGenre = mmfile_malloc(sizeof(char) * (tmp_genre_len + 1));
3367 if (pInfo->pGenre) {
3368 SAFE_STRLCPY(pInfo->pGenre, tmp_genre, tmp_genre_len + 1);
3373 debug_msg(RELEASE, "genre information is wrong inteager [%s]\n", pInfo->pGenre);
3378 pInfo->tagV2Info.bGenreMarked = true;
3379 } else if (strncmp((char *)CompTmp, "TRCK", 4) == 0 && pInfo->tagV2Info.bTrackNumMarked == false) {
3380 pInfo->pTrackNum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tracknumLen);
3381 debug_msg(RELEASE, "pInfo->pTrackNum returned = (%s), pInfo->tracknumLen(%d)\n", pInfo->pTrackNum, pInfo->tracknumLen);
3382 pInfo->tagV2Info.bTrackNumMarked = true;
3383 } else if (strncmp((char *)CompTmp, "TENC", 4) == 0 && pInfo->tagV2Info.bEncByMarked == false) {
3384 pInfo->pEncBy = mmfile_string_convert((char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->encbyLen);
3385 debug_msg(RELEASE, "pInfo->pEncBy returned = (%s), pInfo->encbyLen(%d)\n", pInfo->pEncBy, pInfo->encbyLen);
3386 pInfo->tagV2Info.bEncByMarked = true;
3387 } else if (strncmp((char *)CompTmp, "WXXX", 4) == 0 && pInfo->tagV2Info.bURLMarked == false) {
3388 pInfo->pURL = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->urlLen);
3389 debug_msg(RELEASE, "pInfo->pURL returned = (%s), pInfo->urlLen(%d)\n", pInfo->pURL, pInfo->urlLen);
3390 pInfo->tagV2Info.bURLMarked = true;
3391 } else if (strncmp((char *)CompTmp, "TCOP", 4) == 0 && pInfo->tagV2Info.bCopyRightMarked == false) {
3392 pInfo->pCopyright = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->copyrightLen);
3393 debug_msg(RELEASE, "pInfo->pCopyright returned = (%s), pInfo->copyrightLen(%d)\n", pInfo->pCopyright, pInfo->copyrightLen);
3394 pInfo->tagV2Info.bCopyRightMarked = true;
3395 } else if (strncmp((char *)CompTmp, "TOPE", 4) == 0 && pInfo->tagV2Info.bOriginArtistMarked == false) {
3396 pInfo->pOriginArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->originartistLen);
3397 debug_msg(RELEASE, "pInfo->pOriginArtist returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pOriginArtist, pInfo->originartistLen);
3398 pInfo->tagV2Info.bOriginArtistMarked = true;
3399 } else if (strncmp((char *)CompTmp, "TCOM", 4) == 0 && pInfo->tagV2Info.bComposerMarked == false) {
3400 pInfo->pComposer = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->composerLen);
3401 debug_msg(RELEASE, "pInfo->pComposer returned = (%s), pInfo->composerLen(%d)\n", pInfo->pComposer, pInfo->composerLen);
3402 pInfo->tagV2Info.bComposerMarked = true;
3403 } else if (strncmp((char *)CompTmp, "TRDA", 4) == 0 && pInfo->tagV2Info.bRecDateMarked == false) {
3404 pInfo->pRecDate = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->recdateLen);
3405 debug_msg(RELEASE, "pInfo->pRecDate returned = (%s), pInfo->recdateLen(%d)\n", pInfo->pRecDate, pInfo->recdateLen);
3406 pInfo->tagV2Info.bRecDateMarked = true;
3407 } else if (strncmp((char *)CompTmp, "APIC", 4) == 0 && pInfo->tagV2Info.bImageMarked == false && realCpyFrameNum <= 2000000) {
3408 debug_msg(DEBUG, "text encoding %d \n", textEncodingType);
3410 if (pExtContent[0] != '\0') {
3411 for (inx = 0; inx < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1; inx++)
3412 pInfo->imageInfo.imageMIMEType[inx] = '\0';/*ini mimetype variable */
3414 while ((checkImgMimeTypeMax < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1) && pExtContent[checkImgMimeTypeMax] != '\0') {
3415 pInfo->imageInfo.imageMIMEType[checkImgMimeTypeMax] = pExtContent[checkImgMimeTypeMax];
3416 checkImgMimeTypeMax++;
3418 pInfo->imageInfo.imgMimetypeLen = checkImgMimeTypeMax;
3420 pInfo->imageInfo.imgMimetypeLen = 0;
3421 debug_msg(RELEASE, "APIC image's not included to MIME type\n");
3424 imgstartOffset += checkImgMimeTypeMax;
3426 if (strncmp(pInfo->imageInfo.imageMIMEType, MIME_PRFIX, strlen(MIME_PRFIX)) != 0) {
3427 pInfo->imageInfo.imgMimetypeLen = 0;
3428 debug_error(DEBUG, "APIC NOT VALID");
3432 if ((pExtContent[imgstartOffset] == '\0') && (realCpyFrameNum - imgstartOffset > 0)) {
3433 imgstartOffset++;/*endofMIME(1byte) */
3434 debug_msg(RELEASE, "after scaning Mime type imgstartOffset(%d) value!\n", imgstartOffset);
3436 if (pExtContent[imgstartOffset] < AV_ID3V2_PICTURE_TYPE_MAX) {
3437 pInfo->imageInfo.pictureType = pExtContent[imgstartOffset];
3439 debug_msg(RELEASE, "APIC image has invalid picture type(0x%x)\n", pExtContent[imgstartOffset]);
3441 imgstartOffset++;/*PictureType(1byte) */
3442 debug_msg(RELEASE, "after scaning PictureType imgstartOffset(%d) value!\n", imgstartOffset);
3444 if (pExtContent[imgstartOffset] != 0x0) {
3447 int new_dis_len = 0;
3448 char *tmp_desc = NULL;
3451 if (pExtContent[imgstartOffset + cur_pos] == '\0') {
3452 if (realCpyFrameNum < imgstartOffset + cur_pos) {
3453 debug_error(DEBUG, "End of APIC Tag %d %d %d\n", realCpyFrameNum, imgstartOffset, cur_pos);
3456 /*check end of image description*/
3457 if ((pExtContent[imgstartOffset + cur_pos + 1] == gTagJPEGHeader[0]) ||
3458 (pExtContent[imgstartOffset + cur_pos + 1] == gTagPNGHeader[0])) {
3459 debug_msg(RELEASE, "length of description (%d)", cur_pos);
3466 dis_len = cur_pos + 1;
3468 tmp_desc = mmfile_malloc(sizeof(char) * dis_len);
3469 if (tmp_desc != NULL) {
3470 memcpy(tmp_desc, pExtContent + imgstartOffset, dis_len);
3472 /*convert description*/
3473 pInfo->imageInfo.imageDescription = mmfile_string_convert(tmp_desc, dis_len, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&new_dis_len);
3474 debug_msg(RELEASE, "new_desc %s(%d)\n", pInfo->imageInfo.imageDescription, new_dis_len);
3475 mmfile_free(tmp_desc);
3477 pInfo->imageInfo.imgDesLen = new_dis_len; /**/
3480 imgstartOffset += cur_pos;
3482 pInfo->imageInfo.imgDesLen = 0;
3483 debug_msg(RELEASE, "APIC image's not included to Description!!!\n");
3486 if ((pExtContent[imgstartOffset] == '\0') && (realCpyFrameNum - imgstartOffset > 0)) {
3487 imgstartOffset++; /* endofDesceriptionType(1byte) */
3489 while (pExtContent[imgstartOffset] == '\0') { /*some content has useless '\0' in front of picture data */
3493 debug_msg(RELEASE, "after scaning imgDescription imgstartOffset(%d) value!\n", imgstartOffset);
3495 if (realCpyFrameNum - imgstartOffset > 0) {
3496 pInfo->imageInfo.imageLen = realCpyFrameNum - imgstartOffset;
3497 pInfo->imageInfo.pImageBuf = mmfile_malloc(pInfo->imageInfo.imageLen + 1);
3499 if (pInfo->imageInfo.pImageBuf != NULL) {
3500 memcpy(pInfo->imageInfo.pImageBuf, pExtContent + imgstartOffset, pInfo->imageInfo.imageLen);
3501 pInfo->imageInfo.pImageBuf[pInfo->imageInfo.imageLen] = 0;
3504 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
3505 pInfo->imageInfo.bURLInfo = true; /*if mimetype is "-->", image date has an URL */
3507 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - imgstartOffset(%d)\n", realCpyFrameNum, imgstartOffset);
3509 debug_msg(RELEASE, "pInfo->imageInfo.imageLen(%d), imgstartOffset(%d)!\n", pInfo->imageInfo.imageLen, imgstartOffset);
3511 debug_msg(RELEASE, "pExtContent[imgstartOffset](%d) value should setted NULL value for end of description! realCpyFrameNum - imgstartOffset(%d)\n",
3512 pExtContent[imgstartOffset], realCpyFrameNum - imgstartOffset);
3515 debug_msg(RELEASE, "pExtContent[imgstartOffset](%d) value should setted NULL value for end of mimetype! realCpyFrameNum - imgstartOffset(%d)\n",
3516 pExtContent[imgstartOffset], realCpyFrameNum - imgstartOffset);
3519 checkImgMimeTypeMax = 0;
3522 pInfo->tagV2Info.bImageMarked = true;
3525 debug_msg(RELEASE, "CompTmp(%s) This Frame ID currently not Supports!!\n", CompTmp);
3530 debug_msg(RELEASE, "All of the pExtContent Values are NULL\n");
3533 curPos += purelyFramelen;
3534 if (purelyFramelen != 0)
3535 needToloopv2taglen = MP3_TAGv2_23_TXT_HEADER_LEN;
3537 debug_msg(RELEASE, "This Frame's size is Zero! purelyFramelen(%lu)\n", purelyFramelen);
3540 mmfile_free(pExtContent);
3541 memset(CompTmp, 0, 4);
3543 if (curPos < taglen) {
3544 needToloopv2taglen -= oneFrameLen;
3547 needToloopv2taglen = MP3_TAGv2_23_TXT_HEADER_LEN;
3550 realCpyFrameNum = 0;
3551 textEncodingType = 0;
3557 release_characterset_array(charset_array);
3567 bool mm_file_id3tag_parse_v224(AvFileContentInfo *pInfo, unsigned char *buffer)
3569 unsigned long taglen = 0;
3570 unsigned long needToloopv2taglen;
3571 unsigned long oneFrameLen = 0;
3572 unsigned long v2numOfFrames = 0;
3573 unsigned long curPos = 0;
3575 unsigned char *pExtContent = NULL;
3576 unsigned long purelyFramelen = 0;
3577 unsigned int encodingOffSet = 0;
3578 int inx = 0, realCpyFrameNum = 0, checkImgMimeTypeMax = 0, imgstartOffset = 0, tmp = 0;
3579 unsigned int textEncodingType = 0;
3580 char **charset_array = NULL;
3581 const char *MIME_PRFIX = "image/";
3583 make_characterset_array(&charset_array);
3585 init_content_info(pInfo);
3587 taglen = pInfo->tagV2Info.tagLen;
3588 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
3589 curPos = MP3_TAGv2_HEADER_LEN;
3591 debug_msg(RELEASE, "ID3tag v224--------------------------------------------------------------\n");
3593 /* check Extended Header */
3594 if (buffer[5] & 0x40) {
3595 /* if extended header exists, skip it*/
3596 int extendedHeaderLen = (unsigned long)buffer[10] << 21 | (unsigned long)buffer[11] << 14 | (unsigned long)buffer[12] << 7 | (unsigned long)buffer[13];
3598 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d\n", extendedHeaderLen);
3600 if (extendedHeaderLen > (int)(taglen - curPos)) {
3601 debug_error(DEBUG, "extended header too long.\n");
3603 curPos += extendedHeaderLen;
3607 if (needToloopv2taglen - MP3_TAGv2_23_TXT_HEADER_LEN > MP3_TAGv2_23_TXT_HEADER_LEN) {
3609 while (needToloopv2taglen > MP3_TAGv2_23_TXT_HEADER_LEN) {
3610 if ((buffer[curPos] < '0' || buffer[curPos] > 'Z') || (buffer[curPos + 1] < '0' || buffer[curPos + 1] > 'Z')
3611 || (buffer[curPos + 2] < '0' || buffer[curPos + 2] > 'Z') || (buffer[curPos + 3] < '0' || buffer[curPos + 3] > 'Z'))
3614 memcpy(CompTmp, &buffer[curPos], 4);
3617 oneFrameLen = MP3_TAGv2_23_TXT_HEADER_LEN;
3618 oneFrameLen += (unsigned long)buffer[4 + curPos] << 21 | (unsigned long)buffer[5 + curPos] << 14
3619 | (unsigned long)buffer[6 + curPos] << 7 | (unsigned long)buffer[7 + curPos];
3620 if (oneFrameLen > taglen - curPos)
3623 purelyFramelen = oneFrameLen - MP3_TAGv2_23_TXT_HEADER_LEN;
3624 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3626 debug_msg(RELEASE, "-----------------------------------------------------------------------------------\n");
3628 if (oneFrameLen > MP3_TAGv2_23_TXT_HEADER_LEN && purelyFramelen <= taglen - curPos) {
3629 curPos += purelyFramelen;
3631 /*in case of UTF 16 encoding */
3632 /*buffer+(curPos-purelyFramelen) data should '0x01' but in order to expansion, we don't accurately check the value. */
3633 if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen))) {
3635 textEncodingType = AV_ID3V2_UTF16;
3636 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen))) {
3638 textEncodingType = AV_ID3V2_UTF16_BE;
3639 } else if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen + 1))) {
3641 textEncodingType = AV_ID3V2_UTF16;
3642 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen + 1))) {
3644 textEncodingType = AV_ID3V2_UTF16_BE;
3646 /*in case of UTF-16 BE encoding */
3647 if (buffer[curPos - purelyFramelen] == 0x02) {
3649 while ((buffer[curPos - purelyFramelen + encodingOffSet] == '\0') && (encodingOffSet < purelyFramelen))
3650 encodingOffSet++;/*null skip! */
3651 textEncodingType = AV_ID3V2_UTF16_BE;
3653 /*in case of UTF8 encoding */
3654 else if (buffer[curPos - purelyFramelen] == 0x03) {
3656 while ((buffer[curPos - purelyFramelen + encodingOffSet] == '\0') && (encodingOffSet < purelyFramelen))
3657 encodingOffSet++;/*null skip! */
3658 textEncodingType = AV_ID3V2_UTF8;
3660 /*in case of ISO-8859-1 encoding */
3662 /*buffer+(curPos-purelyFramelen) data should 0x00 but in order to expansion, we don't accurately check the value. */
3664 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen))
3665 encodingOffSet++;/*less than 0x20 value skip! */
3666 textEncodingType = AV_ID3V2_ISO_8859;
3670 mmfile_free(pExtContent);
3672 if (encodingOffSet < purelyFramelen) {
3673 realCpyFrameNum = purelyFramelen - encodingOffSet;
3674 pExtContent = mmfile_malloc(realCpyFrameNum + 3);
3676 if (pExtContent == NULL) {
3677 debug_error(DEBUG, "out of memoryu for id3tag parse\n");
3681 memset(pExtContent, '\0', realCpyFrameNum + 3);
3683 if (textEncodingType != AV_ID3V2_UTF16 && textEncodingType != AV_ID3V2_UTF16_BE) {
3684 if (CompTmp[0] == 'T' || (strcmp(CompTmp, "APIC") == 0)) {
3685 debug_msg(RELEASE, "get the new text ecoding type\n");
3686 textEncodingType = buffer[curPos - purelyFramelen + encodingOffSet - 1];
3690 if (textEncodingType > AV_ID3V2_MAX) {
3691 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%d], FRAME[%s]\n", textEncodingType, (char *)CompTmp);
3695 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
3697 if (realCpyFrameNum > 0) {
3698 if (strncmp((char *)CompTmp, "TIT2", 4) == 0 && pInfo->tagV2Info.bTitleMarked == false) {
3699 if (textEncodingType == AV_ID3V2_UTF8) {
3700 pInfo->pTitle = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3701 if (pInfo->pTitle) {
3702 memcpy(pInfo->pTitle, pExtContent, realCpyFrameNum);
3703 pInfo->pTitle[realCpyFrameNum] = '\0';
3704 /*string copy with '\0'*/
3705 pInfo->titleLen = realCpyFrameNum;
3706 _STRNCPY_EX(pInfo->pTitle, pExtContent, pInfo->titleLen);
3709 pInfo->pTitle = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->titleLen);
3712 debug_msg(RELEASE, "pInfo->pTitle returned = (%s), pInfo->titleLen(%d)\n", pInfo->pTitle, pInfo->titleLen);
3713 pInfo->tagV2Info.bTitleMarked = true;
3715 } else if (strncmp((char *)CompTmp, "TPE1", 4) == 0 && pInfo->tagV2Info.bArtistMarked == false) {
3716 if (textEncodingType == AV_ID3V2_UTF8) {
3717 pInfo->pArtist = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3718 if (pInfo->pArtist) {
3719 memcpy(pInfo->pArtist, pExtContent, realCpyFrameNum);
3720 pInfo->pArtist[realCpyFrameNum] = '\0';
3721 /*string copy with '\0'*/
3722 pInfo->artistLen = realCpyFrameNum;
3723 _STRNCPY_EX(pInfo->pArtist, pExtContent, pInfo->artistLen);
3726 pInfo->pArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->artistLen);
3730 debug_msg(RELEASE, "pInfo->pArtist returned = (%s), pInfo->artistLen(%d)\n", pInfo->pArtist, pInfo->artistLen);
3731 pInfo->tagV2Info.bArtistMarked = true;
3732 } else if (strncmp((char *)CompTmp, "TPE2", 4) == 0 && pInfo->tagV2Info.bAlbum_ArtistMarked == false) {
3733 if (textEncodingType == AV_ID3V2_UTF8) {
3734 pInfo->pAlbum_Artist = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3735 if (pInfo->pAlbum_Artist) {
3736 memcpy(pInfo->pAlbum_Artist, pExtContent, realCpyFrameNum);
3737 pInfo->pAlbum_Artist[realCpyFrameNum] = '\0';
3738 /*string copy with '\0'*/
3739 pInfo->album_artistLen = realCpyFrameNum;
3740 _STRNCPY_EX(pInfo->pAlbum_Artist, pExtContent, pInfo->album_artistLen);
3743 pInfo->pAlbum_Artist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->album_artistLen);
3746 debug_msg(RELEASE, "pInfo->pAlbum_Artist returned = (%s), pInfo->album_artistLen(%d)\n", pInfo->pAlbum_Artist, pInfo->album_artistLen);
3747 pInfo->tagV2Info.bAlbum_ArtistMarked = true;
3748 } else if (strncmp((char *)CompTmp, "TPE3", 4) == 0 && pInfo->tagV2Info.bConductorMarked == false) {
3749 if (textEncodingType == AV_ID3V2_UTF8) {
3750 pInfo->pConductor = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3751 if (pInfo->pConductor) {
3752 memcpy(pInfo->pConductor, pExtContent, realCpyFrameNum);
3753 pInfo->pConductor[realCpyFrameNum] = '\0';
3754 /*string copy with '\0'*/
3755 pInfo->conductorLen = realCpyFrameNum;
3756 _STRNCPY_EX(pInfo->pConductor, pExtContent, pInfo->conductorLen);
3759 pInfo->pConductor = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->conductorLen);
3762 debug_msg(RELEASE, "pInfo->pConductor returned = (%s), pInfo->conductorLen(%d)\n", pInfo->pConductor, pInfo->conductorLen);
3763 pInfo->tagV2Info.bConductorMarked = true;
3764 } else if (strncmp((char *)CompTmp, "TALB", 4) == 0 && pInfo->tagV2Info.bAlbumMarked == false) {
3765 if (textEncodingType == AV_ID3V2_UTF8) {
3766 pInfo->pAlbum = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3767 if (pInfo->pAlbum) {
3768 memcpy(pInfo->pAlbum, pExtContent, realCpyFrameNum);
3769 pInfo->pAlbum[realCpyFrameNum] = '\0';
3770 /*string copy with '\0'*/
3771 pInfo->albumLen = realCpyFrameNum;
3772 _STRNCPY_EX(pInfo->pAlbum, pExtContent, pInfo->albumLen);
3775 pInfo->pAlbum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->albumLen);
3778 debug_msg(RELEASE, "pInfo->pAlbum returned = (%s), pInfo->albumLen(%d)\n", pInfo->pAlbum, pInfo->albumLen);
3779 pInfo->tagV2Info.bAlbumMarked = true;
3780 } 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 */
3781 if (textEncodingType == AV_ID3V2_UTF8) {
3782 pInfo->pYear = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3784 memcpy(pInfo->pYear, pExtContent, realCpyFrameNum);
3785 pInfo->pYear[realCpyFrameNum] = '\0';
3786 /*string copy with '\0'*/
3787 pInfo->yearLen = realCpyFrameNum;
3788 _STRNCPY_EX(pInfo->pYear, pExtContent, pInfo->yearLen);
3791 pInfo->pYear = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->yearLen);
3794 debug_msg(RELEASE, "pInfo->pYear returned = (%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
3795 pInfo->tagV2Info.bYearMarked = true;
3796 } else if (strncmp((char *)CompTmp, "COMM", 4) == 0 && pInfo->tagV2Info.bDescriptionMarked == false) {
3797 if (realCpyFrameNum > 3) {
3798 realCpyFrameNum -= 3;
3801 if (textEncodingType == AV_ID3V2_UTF16 || textEncodingType == AV_ID3V2_UTF16_BE) {
3802 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3803 realCpyFrameNum -= 4;
3807 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3808 realCpyFrameNum -= 2;
3810 textEncodingType = AV_ID3V2_UTF16;
3812 debug_msg(RELEASE, "pInfo->pComment Never Get Here!!\n");
3814 } else if (textEncodingType == AV_ID3V2_UTF8) {
3815 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3819 textEncodingType = AV_ID3V2_UTF8;
3821 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3825 textEncodingType = AV_ID3V2_ISO_8859;
3828 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3830 if (textEncodingType == AV_ID3V2_UTF8) {
3831 pInfo->pComment = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3832 if (pInfo->pComment) {
3833 memset(pInfo->pComment, 0, (realCpyFrameNum + 2));
3834 memcpy(pInfo->pComment, pExtContent + tmp, realCpyFrameNum);
3835 pInfo->pComment[realCpyFrameNum] = '\0';
3836 /*string copy with '\0'*/
3837 pInfo->commentLen = realCpyFrameNum;
3838 _STRNCPY_EX(pInfo->pComment, pExtContent, pInfo->commentLen);
3841 pInfo->pComment = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->commentLen);
3844 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3849 debug_msg(RELEASE, "pInfo->pComment returned = (%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
3850 pInfo->tagV2Info.bDescriptionMarked = true;
3851 } else if (strncmp((char *)CompTmp, "SYLT", 4) == 0 && pInfo->tagV2Info.bSyncLyricsMarked == false) {
3854 int copy_start_pos = tmp;
3855 AvSynclyricsInfo *synclyrics_info = NULL;
3856 GList *synclyrics_info_list = NULL;
3858 if (realCpyFrameNum > 5) {
3859 realCpyFrameNum -= 5;
3862 if (textEncodingType == AV_ID3V2_UTF16 || textEncodingType == AV_ID3V2_UTF16_BE) {
3863 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3864 realCpyFrameNum -= 4;
3868 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3869 realCpyFrameNum -= 2;
3871 textEncodingType = AV_ID3V2_UTF16;
3873 debug_msg(RELEASE, "pInfo->pSyncLyrics Never Get Here!!\n");
3875 } else if (textEncodingType == AV_ID3V2_UTF8) {
3876 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3880 textEncodingType = AV_ID3V2_UTF8;
3882 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3886 textEncodingType = AV_ID3V2_ISO_8859;
3889 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3891 if (realCpyFrameNum < MMFILE_SYNC_LYRIC_INFO_MIN_LEN) {
3892 debug_msg(RELEASE, "failed to get Synchronised lyrics Info realCpyFramNum(%d)\n", realCpyFrameNum);
3893 pInfo->syncLyricsNum = 0;
3895 if (textEncodingType == AV_ID3V2_UTF16) {
3896 debug_warning(DEBUG, "[AV_ID3V2_UTF16] not implemented\n");
3897 } else if (textEncodingType == AV_ID3V2_UTF16_BE) {
3898 debug_warning(DEBUG, "[AV_ID3V2_UTF16_BE] not implemented\n");
3900 for (idx = 0; idx < realCpyFrameNum; idx++) {
3901 if (pExtContent[tmp + idx] == 0x00) {
3902 synclyrics_info = (AvSynclyricsInfo *)mmfile_malloc(sizeof(AvSynclyricsInfo));
3903 if (synclyrics_info) {
3904 if (textEncodingType == AV_ID3V2_UTF8) {
3905 synclyrics_info->lyric_info = mmfile_malloc(copy_len + 1);
3906 if (synclyrics_info->lyric_info) {
3907 memset(synclyrics_info->lyric_info, 0, copy_len + 1);
3908 memcpy(synclyrics_info->lyric_info, pExtContent + copy_start_pos, copy_len);
3909 synclyrics_info->lyric_info[copy_len] = '\0';
3912 synclyrics_info->lyric_info = mmfile_string_convert((const char *)&pExtContent[copy_start_pos], copy_len, "UTF-8", charset_array[textEncodingType], NULL, NULL);
3915 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];
3917 copy_start_pos = tmp + idx + 1;
3918 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);
3920 synclyrics_info_list = g_list_append(synclyrics_info_list, synclyrics_info);
3925 pInfo->pSyncLyrics = synclyrics_info_list;
3926 pInfo->syncLyricsNum = g_list_length(pInfo->pSyncLyrics);
3930 debug_msg(RELEASE, "SyncLyrics info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3934 pInfo->tagV2Info.bSyncLyricsMarked = true;
3935 } else if (strncmp((char *)CompTmp, "USLT", 4) == 0 && pInfo->tagV2Info.bUnsyncLyricsMarked == false) {
3936 if (realCpyFrameNum > 3) {
3937 realCpyFrameNum -= 3;
3940 if (textEncodingType == AV_ID3V2_UTF16 || textEncodingType == AV_ID3V2_UTF16_BE) {
3941 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3942 realCpyFrameNum -= 4;
3946 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3947 realCpyFrameNum -= 2;
3949 textEncodingType = AV_ID3V2_UTF16;
3951 debug_msg(RELEASE, "pInfo->pUnsyncLyrics Never Get Here!!\n");
3953 } else if (textEncodingType == AV_ID3V2_UTF8) {
3954 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3958 textEncodingType = AV_ID3V2_UTF8;
3960 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3964 textEncodingType = AV_ID3V2_ISO_8859;
3967 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3969 if (textEncodingType == AV_ID3V2_UTF8) {
3970 pInfo->pUnsyncLyrics = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3972 if (pInfo->pUnsyncLyrics != NULL) {
3973 memset(pInfo->pUnsyncLyrics, 0, (realCpyFrameNum + 2));
3974 memcpy(pInfo->pUnsyncLyrics, pExtContent + tmp, realCpyFrameNum);
3975 pInfo->pUnsyncLyrics[realCpyFrameNum] = '\0';
3976 /*string copy with '\0'*/
3977 pInfo->unsynclyricsLen = realCpyFrameNum;
3978 _STRNCPY_EX(pInfo->pUnsyncLyrics, pExtContent, pInfo->unsynclyricsLen);
3980 debug_error(DEBUG, "out of memoryu for SyncLyrics\n");
3983 pInfo->pUnsyncLyrics = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->unsynclyricsLen);
3986 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3991 debug_msg(RELEASE, "pInfo->pUnsyncLyrics returned = (%s), pInfo->unsynclyricsLen(%d)\n", pInfo->pUnsyncLyrics, pInfo->unsynclyricsLen);
3992 pInfo->tagV2Info.bDescriptionMarked = true;
3993 } else if (strncmp((char *)CompTmp, "TCON", 4) == 0 && pInfo->tagV2Info.bGenreMarked == false) {
3994 if (textEncodingType == AV_ID3V2_UTF8) {
3995 pInfo->pGenre = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
3996 if (pInfo->pGenre) {
3997 memcpy(pInfo->pGenre, pExtContent, realCpyFrameNum);
3998 pInfo->pGenre[realCpyFrameNum] = '\0';
3999 /*string copy with '\0'*/
4000 pInfo->genreLen = realCpyFrameNum;
4001 _STRNCPY_EX(pInfo->pGenre, pExtContent, pInfo->genreLen);
4004 pInfo->pGenre = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->genreLen);
4007 debug_msg(RELEASE, "pInfo->pGenre returned = (%s), pInfo->genreLen(%d)\n", pInfo->pGenre, pInfo->genreLen);
4009 if ((pInfo->pGenre != NULL) && (pInfo->genreLen > 0)) {
4013 ret = is_numeric(pInfo->pGenre, pInfo->genreLen);
4016 ret = safe_atoi(pInfo->pGenre, &int_genre);
4018 debug_msg(RELEASE, "genre information is inteager [%d]\n", int_genre);
4020 /*Change int to string */
4021 if ((0 <= int_genre) && (int_genre < GENRE_COUNT - 1)) {
4022 /*save genreinfo like "(123)". mm_file_id3tag_restore_content_info convert it to string*/
4023 char tmp_genre[6] = {0, }; /*ex. "(123)+NULL"*/
4024 int tmp_genre_len = 0;
4026 memset(tmp_genre, 0, 6);
4027 snprintf(tmp_genre, sizeof(tmp_genre), "(%d)", int_genre);
4029 tmp_genre_len = strlen(tmp_genre);
4030 if (tmp_genre_len > 0) {
4031 mmfile_free(pInfo->pGenre);
4032 pInfo->pGenre = mmfile_malloc(sizeof(char) * (tmp_genre_len + 1));
4033 if (pInfo->pGenre) {
4034 SAFE_STRLCPY(pInfo->pGenre, tmp_genre, tmp_genre_len + 1);
4039 debug_msg(RELEASE, "genre information is wrong inteager [%s]\n", pInfo->pGenre);
4044 pInfo->tagV2Info.bGenreMarked = true;
4045 } else if (strncmp((char *)CompTmp, "TRCK", 4) == 0 && pInfo->tagV2Info.bTrackNumMarked == false) {
4046 if (textEncodingType == AV_ID3V2_UTF8) {
4047 pInfo->pTrackNum = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4048 if (pInfo->pTrackNum != NULL) {
4049 memcpy(pInfo->pTrackNum, pExtContent, realCpyFrameNum);
4050 pInfo->pTrackNum[realCpyFrameNum] = '\0';
4051 /*string copy with '\0'*/
4052 pInfo->tracknumLen = realCpyFrameNum;
4053 _STRNCPY_EX(pInfo->pTrackNum, pExtContent, pInfo->tracknumLen);
4056 pInfo->pTrackNum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tracknumLen);
4059 debug_msg(RELEASE, "pInfo->pTrackNum returned = (%s), pInfo->tracknumLen(%d)\n", pInfo->pTrackNum, pInfo->tracknumLen);
4060 pInfo->tagV2Info.bTrackNumMarked = true;
4061 } else if (strncmp((char *)CompTmp, "TENC", 4) == 0 && pInfo->tagV2Info.bEncByMarked == false) {
4062 if (textEncodingType == AV_ID3V2_UTF8) {
4063 pInfo->pEncBy = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4064 if (pInfo->pEncBy != NULL) {
4065 memcpy(pInfo->pEncBy, pExtContent, realCpyFrameNum);
4066 pInfo->pEncBy[realCpyFrameNum] = '\0';
4067 /*string copy with '\0'*/
4068 pInfo->encbyLen = realCpyFrameNum;
4069 _STRNCPY_EX(pInfo->pEncBy, pExtContent, pInfo->encbyLen);
4072 pInfo->pEncBy = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->encbyLen);
4075 debug_msg(RELEASE, "pInfo->pEncBy returned = (%s), pInfo->encbyLen(%d)\n", pInfo->pEncBy, pInfo->encbyLen);
4076 pInfo->tagV2Info.bEncByMarked = true;
4077 } else if (strncmp((char *)CompTmp, "WXXX", 4) == 0 && pInfo->tagV2Info.bURLMarked == false) {
4078 if (textEncodingType == AV_ID3V2_UTF8) {
4079 pInfo->pURL = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4080 if (pInfo->pURL != NULL) {
4081 memcpy(pInfo->pURL, pExtContent, realCpyFrameNum);
4082 pInfo->pURL[realCpyFrameNum] = '\0';
4083 /*string copy with '\0'*/
4084 pInfo->urlLen = realCpyFrameNum;
4085 _STRNCPY_EX(pInfo->pURL, pExtContent, pInfo->urlLen);
4088 pInfo->pURL = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->urlLen);
4091 debug_msg(RELEASE, "pInfo->pURL returned = (%s), pInfo->urlLen(%d)\n", pInfo->pURL, pInfo->urlLen);
4092 pInfo->tagV2Info.bURLMarked = true;
4093 } else if (strncmp((char *)CompTmp, "TCOP", 4) == 0 && pInfo->tagV2Info.bCopyRightMarked == false) {
4094 if (textEncodingType == AV_ID3V2_UTF8) {
4095 pInfo->pCopyright = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4096 if (pInfo->pCopyright != NULL) {
4097 memcpy(pInfo->pCopyright, pExtContent, realCpyFrameNum);
4098 pInfo->pCopyright[realCpyFrameNum] = '\0';
4099 /*string copy with '\0'*/
4100 pInfo->copyrightLen = realCpyFrameNum;
4101 _STRNCPY_EX(pInfo->pCopyright, pExtContent, pInfo->copyrightLen);
4104 pInfo->pCopyright = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->copyrightLen);
4107 debug_msg(RELEASE, "pInfo->pCopyright returned = (%s), pInfo->copyrightLen(%d)\n", pInfo->pCopyright, pInfo->copyrightLen);
4108 pInfo->tagV2Info.bCopyRightMarked = true;
4109 } else if (strncmp((char *)CompTmp, "TOPE", 4) == 0 && pInfo->tagV2Info.bOriginArtistMarked == false) {
4110 if (textEncodingType == AV_ID3V2_UTF8) {
4111 pInfo->pOriginArtist = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4112 if (pInfo->pOriginArtist != NULL) {
4113 memcpy(pInfo->pOriginArtist, pExtContent, realCpyFrameNum);
4114 pInfo->pOriginArtist[realCpyFrameNum] = '\0';
4115 /*string copy with '\0'*/
4116 pInfo->originartistLen = realCpyFrameNum;
4117 _STRNCPY_EX(pInfo->pOriginArtist, pExtContent, pInfo->originartistLen);
4120 pInfo->pOriginArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->originartistLen);
4123 debug_msg(RELEASE, "pInfo->pOriginArtist returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pOriginArtist, pInfo->originartistLen);
4124 pInfo->tagV2Info.bOriginArtistMarked = true;
4125 } else if (strncmp((char *)CompTmp, "TCOM", 4) == 0 && pInfo->tagV2Info.bComposerMarked == false) {
4126 if (textEncodingType == AV_ID3V2_UTF8) {
4127 pInfo->pComposer = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4128 if (pInfo->pComposer != NULL) {
4129 memcpy(pInfo->pComposer, pExtContent, realCpyFrameNum);
4130 pInfo->pComposer[realCpyFrameNum] = '\0';
4131 /*string copy with '\0'*/
4132 pInfo->composerLen = realCpyFrameNum;
4133 _STRNCPY_EX(pInfo->pComposer, pExtContent, pInfo->composerLen);
4136 pInfo->pComposer = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->composerLen);
4139 debug_msg(RELEASE, "pInfo->pComposer returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pComposer, pInfo->composerLen);
4140 pInfo->tagV2Info.bComposerMarked = true;
4141 } else if (strncmp((char *)CompTmp, "TDRC", 4) == 0 && pInfo->tagV2Info.bRecDateMarked == false) { /*TYER(year) and TRDA are replaced by the TDRC */
4142 if (textEncodingType == AV_ID3V2_UTF8) {
4143 pInfo->pRecDate = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4144 if (pInfo->pRecDate != NULL) {
4145 memcpy(pInfo->pRecDate, pExtContent, realCpyFrameNum);
4146 pInfo->pRecDate[realCpyFrameNum] = '\0';
4147 /*string copy with '\0'*/
4148 pInfo->recdateLen = realCpyFrameNum;
4149 _STRNCPY_EX(pInfo->pRecDate, pExtContent, pInfo->recdateLen);
4152 pInfo->pRecDate = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->recdateLen);
4155 debug_msg(RELEASE, "pInfo->pRecDate returned = (%s), pInfo->recdateLen(%d)\n", pInfo->pRecDate, pInfo->recdateLen);
4156 pInfo->tagV2Info.bRecDateMarked = true;
4157 } else if (strncmp((char *)CompTmp, "TIT1", 4) == 0 && pInfo->tagV2Info.bContentGroupMarked == false) {
4158 if (textEncodingType == AV_ID3V2_UTF8) {
4159 pInfo->pContentGroup = mmfile_malloc(realCpyFrameNum + 2); /*Ignore NULL char for UTF16 */
4160 if (pInfo->pContentGroup != NULL) {
4161 memcpy(pInfo->pContentGroup, pExtContent, realCpyFrameNum);
4162 pInfo->pContentGroup[realCpyFrameNum] = '\0';
4163 /*string copy with '\0'*/
4164 pInfo->contentGroupLen = realCpyFrameNum;
4165 _STRNCPY_EX(pInfo->pContentGroup, pExtContent, pInfo->contentGroupLen);
4168 pInfo->pContentGroup = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->contentGroupLen);
4171 debug_msg(RELEASE, "pInfo->pContentGroup returned = (%s), pInfo->contentGroupLen(%d)\n", pInfo->pContentGroup, pInfo->contentGroupLen);
4172 pInfo->tagV2Info.bContentGroupMarked = true;
4173 } else if (strncmp((char *)CompTmp, "APIC", 4) == 0 && pInfo->tagV2Info.bImageMarked == false && realCpyFrameNum <= 2000000) {
4174 if (pExtContent[0] != '\0') {
4175 for (inx = 0; inx < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1; inx++)
4176 pInfo->imageInfo.imageMIMEType[inx] = '\0';/*ini mimetype variable */
4178 while ((checkImgMimeTypeMax < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1) && pExtContent[checkImgMimeTypeMax] != '\0') {
4179 pInfo->imageInfo.imageMIMEType[checkImgMimeTypeMax] = pExtContent[checkImgMimeTypeMax];
4180 checkImgMimeTypeMax++;
4182 pInfo->imageInfo.imgMimetypeLen = checkImgMimeTypeMax;
4184 pInfo->imageInfo.imgMimetypeLen = 0;
4187 imgstartOffset += checkImgMimeTypeMax;
4189 if (strncmp(pInfo->imageInfo.imageMIMEType, MIME_PRFIX, strlen(MIME_PRFIX)) != 0) {
4190 pInfo->imageInfo.imgMimetypeLen = 0;
4191 debug_error(DEBUG, "APIC NOT VALID");
4195 if ((pExtContent[imgstartOffset] == '\0') && (realCpyFrameNum - imgstartOffset > 0)) {
4196 imgstartOffset++;/*endofMIME(1byte) */
4198 if (pExtContent[imgstartOffset] < AV_ID3V2_PICTURE_TYPE_MAX) {
4199 pInfo->imageInfo.pictureType = pExtContent[imgstartOffset];
4201 imgstartOffset++;/*PictureType(1byte) */
4203 if (pExtContent[imgstartOffset] != 0x0) {
4206 int new_dis_len = 0;
4207 char *tmp_desc = NULL;
4210 if (pExtContent[imgstartOffset + cur_pos] == '\0') {
4211 if (realCpyFrameNum < imgstartOffset + cur_pos) {
4212 debug_error(DEBUG, "End of APIC Tag %d %d %d\n", realCpyFrameNum, imgstartOffset, cur_pos);
4215 /*check end of image description*/
4216 if ((pExtContent[imgstartOffset + cur_pos + 1] == gTagJPEGHeader[0]) ||
4217 (pExtContent[imgstartOffset + cur_pos + 1] == gTagPNGHeader[0])) {
4218 debug_msg(RELEASE, "length of description (%d)", cur_pos);
4225 dis_len = cur_pos + 1;
4227 tmp_desc = mmfile_malloc(sizeof(char) * dis_len);
4229 if (tmp_desc != NULL) {
4230 memcpy(tmp_desc, pExtContent + imgstartOffset, dis_len);
4231 debug_msg(DEBUG, "tmp_desc %s\n", tmp_desc);
4233 /*convert description*/
4234 pInfo->imageInfo.imageDescription = mmfile_string_convert(tmp_desc, dis_len, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&new_dis_len);
4235 debug_msg(DEBUG, "new_desc %s(%d)\n", pInfo->imageInfo.imageDescription, new_dis_len);
4236 mmfile_free(tmp_desc);
4238 pInfo->imageInfo.imgDesLen = new_dis_len; /**/
4241 imgstartOffset += cur_pos;
4243 pInfo->imageInfo.imgDesLen = 0;
4246 if ((pExtContent[imgstartOffset] == '\0') && (realCpyFrameNum - imgstartOffset > 0)) {
4247 imgstartOffset++; /* endofDesceriptionType(1byte) */
4249 while (pExtContent[imgstartOffset] == '\0') { /*some content has useless '\0' in front of picture data */
4253 debug_msg(RELEASE, "after scaning imgDescription imgstartOffset(%d) value!\n", imgstartOffset);
4255 if (realCpyFrameNum - imgstartOffset > 0) {
4256 pInfo->imageInfo.imageLen = realCpyFrameNum - imgstartOffset;
4257 pInfo->imageInfo.pImageBuf = mmfile_malloc(pInfo->imageInfo.imageLen + 1);
4259 if (pInfo->imageInfo.pImageBuf != NULL) {
4260 memcpy(pInfo->imageInfo.pImageBuf, pExtContent + imgstartOffset, pInfo->imageInfo.imageLen);
4261 pInfo->imageInfo.pImageBuf[pInfo->imageInfo.imageLen] = 0;
4264 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
4265 pInfo->imageInfo.bURLInfo = true; /*if mimetype is "-->", image date has an URL */
4267 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - imgstartOffset(%d)\n", realCpyFrameNum, imgstartOffset);
4272 checkImgMimeTypeMax = 0;
4275 pInfo->tagV2Info.bImageMarked = true;
4277 debug_msg(RELEASE, "CompTmp(%s) This Frame ID currently not Supports!!\n", CompTmp);
4282 debug_msg(RELEASE, "mmf_file_id3tag_parse_v224: All of the pExtContent Values are NULL\n");
4286 curPos += purelyFramelen;
4287 if (purelyFramelen != 0)
4288 needToloopv2taglen = MP3_TAGv2_23_TXT_HEADER_LEN;
4291 mmfile_free(pExtContent);
4292 memset(CompTmp, 0, 4);
4293 if (curPos < taglen) {
4294 needToloopv2taglen -= oneFrameLen;
4297 needToloopv2taglen = MP3_TAGv2_23_TXT_HEADER_LEN;
4301 realCpyFrameNum = 0;
4302 textEncodingType = 0;
4308 release_characterset_array(charset_array);
4318 void mm_file_id3tag_restore_content_info(AvFileContentInfo *pInfo)
4320 char *mpegAudioGenre = NULL/*, *tmpGenreForV1Tag = NULL*/;
4321 bool bAdditionGenre = false /*, bMpegAudioFrame = false*/;
4322 int mpegAudioFileLen = 0, idv2IntGenre = 148/*, tmpinx = 0, tmpinx2=0*/;
4323 unsigned char genre = pInfo->genre;
4325 /* for Genre Info */
4326 if (pInfo->tagV2Info.bGenreMarked == false) {
4327 if (pInfo->bV1tagFound == true) {
4328 debug_msg(RELEASE, "Genre: %d\n", genre);
4333 if (MpegAudio_Genre[genre] != NULL) {
4334 pInfo->genreLen = strlen(MpegAudio_Genre[genre]);
4335 if (pInfo->genreLen > 0) {
4336 /* Give space for NULL character. Hence added "+1" */
4337 pInfo->pGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen + 1));
4338 if (pInfo->pGenre) {
4339 SAFE_STRLCPY(pInfo->pGenre, MpegAudio_Genre[genre], pInfo->genreLen + 1);
4344 debug_msg(RELEASE, "Genre was not Found.\n");
4346 } else if (pInfo->tagV2Info.bGenreMarked == true) {
4347 if (pInfo->genreLen && pInfo->tagV2Info.bGenreUTF16) {
4348 pInfo->pGenre[pInfo->genreLen + 1] = '\0';
4349 mpegAudioGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen * AV_WM_LOCALCODE_SIZE_MAX + 1));
4351 debug_msg(RELEASE, "pInfo->genreLen size is Zero Or not UTF16 code! genreLen[%d] genre[%s]\n", pInfo->genreLen, pInfo->pGenre);
4353 if (pInfo->pGenre) {
4354 pInfo->genreLen = strlen(pInfo->pGenre);
4355 mpegAudioGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen + 1));
4356 if (mpegAudioGenre != NULL) {
4357 SAFE_STRLCPY(mpegAudioGenre, pInfo->pGenre, pInfo->genreLen + 1);
4360 pInfo->genreLen = 0;
4364 mmfile_free(pInfo->pGenre);
4367 if (mpegAudioGenre != NULL) {
4370 * (XXX) XXX is 0 - 148
4372 pInfo->genreLen = strlen(mpegAudioGenre);
4373 if (pInfo->genreLen >= 3 &&
4374 mpegAudioGenre[0] == '(' && mpegAudioGenre[pInfo->genreLen - 1] == ')') {
4375 bAdditionGenre = true;
4376 for (mpegAudioFileLen = 1; mpegAudioFileLen <= pInfo->genreLen - 2; mpegAudioFileLen++) {
4377 if (mpegAudioGenre[mpegAudioFileLen] < '0' || mpegAudioGenre[mpegAudioFileLen] > '9') {
4378 bAdditionGenre = false;
4384 if (bAdditionGenre == true) {
4385 idv2IntGenre = atoi(mpegAudioGenre + 1);
4387 if (idv2IntGenre > 147 || idv2IntGenre < 0)
4390 if (MpegAudio_Genre[idv2IntGenre] != NULL) {
4391 pInfo->genreLen = strlen(MpegAudio_Genre[idv2IntGenre]);
4392 if (pInfo->genreLen > 0) {
4393 /* Give space for NULL character. Hence added "+1" */
4394 pInfo->pGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen + 1));
4395 if (pInfo->pGenre) {
4396 SAFE_STRLCPY(pInfo->pGenre, MpegAudio_Genre[idv2IntGenre], pInfo->genreLen + 1);
4400 debug_msg(RELEASE, "pInfo->pGenre = %s\n", pInfo->pGenre);
4401 } else if (bAdditionGenre == false && pInfo->genreLen > 0) {
4406 /* Give space for NULL character. Hence added "+1" */
4407 pInfo->pGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen + 1));
4408 if (pInfo->pGenre) {
4409 SAFE_STRLCPY(pInfo->pGenre, mpegAudioGenre, pInfo->genreLen + 1);
4411 debug_msg(RELEASE, "pInfo->pGenre = %s, pInfo->genreLen = %d\n", pInfo->pGenre, pInfo->genreLen);
4413 debug_msg(RELEASE, "Failed to \"(...)\" value to genre = %s\n", pInfo->pGenre);
4416 debug_msg(RELEASE, "mpegAudioGenre = %s\n", mpegAudioGenre);
4418 mmfile_free(mpegAudioGenre);
4421 debug_msg(RELEASE, "Neither ID3 v1 nor v2 info doesn't have Genre Info.\n");
4426 void mm_file_free_synclyrics_list(GList *synclyrics_list)
4430 AvSynclyricsInfo *synclyrics_info = NULL;
4432 if (synclyrics_list == NULL) {
4436 list_len = g_list_length(synclyrics_list);
4437 for (idx = 0; idx < list_len; idx++) {
4438 synclyrics_info = g_list_nth_data(synclyrics_list, idx);
4440 if (synclyrics_info != NULL) {
4441 mmfile_free(synclyrics_info->lyric_info);
4442 mmfile_free(synclyrics_info);
4446 if (synclyrics_list != NULL) {
4447 g_list_free(synclyrics_list);
4448 synclyrics_list = NULL;