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);
815 7: 360 slowmotion mode
816 8: 180 slowmotion mode
818 if (smtaTag.saut[0] == 's'
819 && smtaTag.saut[1] == 'a'
820 && smtaTag.saut[2] == 'u'
821 && smtaTag.saut[3] == 't') {
822 if (smtaTag.value == 0x01) {
823 debug_msg(RELEASE, "This has saut tag and valid value");
824 formatContext->smta = 1;
825 } else if (smtaTag.value == 0x02) {
826 debug_msg(RELEASE, "This has saut tag and valid value");
827 formatContext->smta = 2;
829 debug_error(DEBUG, "This has saut tag but invalid value");
833 debug_error(DEBUG, "This hasn't saut tag and valid value");
837 return MMFILE_UTIL_SUCCESS;
840 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
842 return MMFILE_UTIL_FAIL;
845 static int GetSA3DInfoFromMP4ATagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
847 if (!formatContext || !fp || !basic_header) {
848 debug_error(DEBUG, "invalid param\n");
849 return MMFILE_UTIL_FAIL;
852 unsigned char *buffer;
854 bool is_SA3D_present = false;
856 MMFILE_MP4A_SA3D_TAGBOX sa3dTag = {0, };
858 formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_UNKNOWN;
859 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_UNKNOWN;
860 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_UNKNOWN;
862 mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
864 buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
866 debug_error(DEBUG, "calloc failed ");
870 readed = mmfile_read(fp, buffer, basic_header->size);
871 if (readed != (int)basic_header->size) {
872 debug_error(DEBUG, "read mp4a box failed\n");
876 for (i = 0; i + 3 < basic_header->size; ++i)
877 if (buffer[i] == 'S' && buffer[i + 1] == 'A' && buffer[i + 2] == '3' && buffer[i + 3] == 'D') {
878 debug_warning(DEBUG, "SA3D data found at offset %d bytes\n", i);
879 is_SA3D_present = true;
883 if (!is_SA3D_present) {
884 debug_warning(DEBUG, "No SA3D box found");
888 mmfile_seek(fp, basic_header->start_offset + i + 4, SEEK_SET);
890 readed = mmfile_read(fp, (unsigned char *)&sa3dTag, sizeof(MMFILE_MP4A_SA3D_TAGBOX));
891 if (readed != sizeof(MMFILE_MP4A_SA3D_TAGBOX)) {
892 debug_error(DEBUG, "read SA3D tag header fail\n");
896 sa3dTag.ambisonic_order = mmfile_io_be_uint32(sa3dTag.ambisonic_order);
897 sa3dTag.num_channels = mmfile_io_be_uint32(sa3dTag.num_channels);
898 for (i = 0; i < sa3dTag.num_channels; ++i)
899 sa3dTag.channel_map[i] = mmfile_io_be_uint32(sa3dTag.channel_map[i]);
901 debug_msg(RELEASE, "sa3dTag.version = %d", sa3dTag.version);
902 debug_msg(RELEASE, "sa3dTag.ambisonic_type = %d", sa3dTag.ambisonic_type);
903 debug_msg(RELEASE, "sa3dTag.ambisonic_order = %d", sa3dTag.ambisonic_order);
904 debug_msg(RELEASE, "sa3dTag.ambisonic_channel_ordering = %d", sa3dTag.ambisonic_channel_ordering);
905 debug_msg(RELEASE, "sa3dTag.ambisonic_normalization = %d", sa3dTag.ambisonic_normalization);
906 debug_msg(RELEASE, "sa3dTag.num_channels = %d", sa3dTag.num_channels);
907 for (i = 0; i < sa3dTag.num_channels; ++i)
908 debug_msg(RELEASE, "sa3dTag.channel_map[%d] = %d", i, sa3dTag.channel_map[i]);
910 if (sa3dTag.version != RFC_AMBISONIC_SA3DBOX_VERSION_SUPPORTED) {
911 debug_error(DEBUG, "SA3D tag box version is unsupported\n");
914 if (sa3dTag.ambisonic_type == RFC_AMBISONIC_TYPE_PERIPHONIC)
915 formatContext->ambisonicType = MMFILE_AMBISONIC_TYPE_PERIPHONIC;
917 switch (sa3dTag.ambisonic_order) {
918 case MMFILE_AMBISONIC_ORDER_FOA: {
919 if (sa3dTag.num_channels == 4) {
920 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_FOA;
922 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
923 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
924 (sa3dTag.channel_map[0] == 0) &&
925 (sa3dTag.channel_map[1] == 1) &&
926 (sa3dTag.channel_map[2] == 2) &&
927 (sa3dTag.channel_map[3] == 3))
928 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
930 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
931 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
932 (sa3dTag.channel_map[0] == 0) &&
933 (sa3dTag.channel_map[1] == 3) &&
934 (sa3dTag.channel_map[2] == 1) &&
935 (sa3dTag.channel_map[3] == 2))
936 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
938 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond\n");
944 case MMFILE_AMBISONIC_ORDER_SOA: {
945 if (sa3dTag.num_channels == 9) {
946 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_SOA;
948 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
949 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
950 (sa3dTag.channel_map[0] == 0) &&
951 (sa3dTag.channel_map[1] == 1) &&
952 (sa3dTag.channel_map[2] == 2) &&
953 (sa3dTag.channel_map[3] == 3) &&
954 (sa3dTag.channel_map[4] == 4) &&
955 (sa3dTag.channel_map[5] == 5) &&
956 (sa3dTag.channel_map[6] == 6) &&
957 (sa3dTag.channel_map[7] == 7) &&
958 (sa3dTag.channel_map[8] == 8))
959 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
961 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
962 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
963 (sa3dTag.channel_map[0] == 0) &&
964 (sa3dTag.channel_map[1] == 3) &&
965 (sa3dTag.channel_map[2] == 1) &&
966 (sa3dTag.channel_map[3] == 2) &&
967 (sa3dTag.channel_map[4] == 6) &&
968 (sa3dTag.channel_map[5] == 7) &&
969 (sa3dTag.channel_map[6] == 5) &&
970 (sa3dTag.channel_map[7] == 8) &&
971 (sa3dTag.channel_map[8] == 4))
972 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
974 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond\n");
981 case MMFILE_AMBISONIC_ORDER_TOA: {
982 if (sa3dTag.num_channels == 16) {
983 formatContext->ambisonicOrder = MMFILE_AMBISONIC_ORDER_TOA;
985 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_ACN) &&
986 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_SN3D) &&
987 (sa3dTag.channel_map[0] == 0) &&
988 (sa3dTag.channel_map[1] == 1) &&
989 (sa3dTag.channel_map[2] == 2) &&
990 (sa3dTag.channel_map[3] == 3) &&
991 (sa3dTag.channel_map[4] == 4) &&
992 (sa3dTag.channel_map[5] == 5) &&
993 (sa3dTag.channel_map[6] == 6) &&
994 (sa3dTag.channel_map[7] == 7) &&
995 (sa3dTag.channel_map[8] == 8) &&
996 (sa3dTag.channel_map[9] == 9) &&
997 (sa3dTag.channel_map[10] == 10) &&
998 (sa3dTag.channel_map[11] == 11) &&
999 (sa3dTag.channel_map[12] == 12) &&
1000 (sa3dTag.channel_map[13] == 13) &&
1001 (sa3dTag.channel_map[14] == 14) &&
1002 (sa3dTag.channel_map[15] == 15))
1003 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMBIX;
1005 if ((sa3dTag.ambisonic_channel_ordering == RFC_AMBISONIC_CHANNEL_ORDERING_FUMA) &&
1006 (sa3dTag.ambisonic_normalization == RFC_AMBISONIC_NORMALIZATION_FUMA) &&
1007 (sa3dTag.channel_map[0] == 0) &&
1008 (sa3dTag.channel_map[1] == 3) &&
1009 (sa3dTag.channel_map[2] == 1) &&
1010 (sa3dTag.channel_map[3] == 2) &&
1011 (sa3dTag.channel_map[4] == 6) &&
1012 (sa3dTag.channel_map[5] == 7) &&
1013 (sa3dTag.channel_map[6] == 5) &&
1014 (sa3dTag.channel_map[7] == 8) &&
1015 (sa3dTag.channel_map[8] == 4) &&
1016 (sa3dTag.channel_map[9] == 12) &&
1017 (sa3dTag.channel_map[10] == 13) &&
1018 (sa3dTag.channel_map[11] == 11) &&
1019 (sa3dTag.channel_map[12] == 14) &&
1020 (sa3dTag.channel_map[13] == 10) &&
1021 (sa3dTag.channel_map[14] == 15) &&
1022 (sa3dTag.channel_map[15] == 9))
1023 formatContext->ambisonicFormat = MMFILE_AMBISONIC_FORMAT_AMB;
1025 debug_error(DEBUG, "Incorrect metadata: ambisonic order and channels number do not correspond\n");
1033 debug_warning(DEBUG, "Ambisonic order or format is not supported: ambix or amb formats up to 3rd order were expected.\n");
1039 debug_msg(RELEASE, "formatContext->ambisonic_type = %d", formatContext->ambisonicType);
1040 debug_msg(RELEASE, "formatContext->ambisonic_order = %d", formatContext->ambisonicOrder);
1041 debug_msg(RELEASE, "formatContext->ambisonic_format = %d", formatContext->ambisonicFormat);
1044 return MMFILE_UTIL_SUCCESS;
1047 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1049 return MMFILE_UTIL_FAIL;
1052 static int ParseSt3dData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset)
1054 uint8_t stereo_mode = INVALID_UINT8_VALUE;
1055 unsigned int readed = 0;
1057 mmfile_seek(fp, start_offset, SEEK_SET);
1059 readed = mmfile_read(fp, (unsigned char *)&stereo_mode, sizeof(uint8_t));
1060 if (readed != sizeof(uint8_t)) {
1061 debug_error(DEBUG, "read st3d tag header fail\n");
1062 return MMFILE_UTIL_FAIL;
1065 formatContext->stereoModeV2 = stereo_mode;
1067 return MMFILE_UTIL_SUCCESS;
1070 static int ParseSvhdData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset, unsigned int box_size)
1072 unsigned int readed = 0;
1074 formatContext->metadataSourceV2 = (char *)calloc(1, box_size);
1075 if (!formatContext->metadataSourceV2) {
1076 debug_error(DEBUG, "Calloc failed");
1077 return MMFILE_UTIL_FAIL;
1080 mmfile_seek(fp, start_offset, SEEK_SET);
1081 readed = mmfile_read(fp, (unsigned char *)formatContext->metadataSourceV2, box_size);
1082 if (readed != box_size) {
1083 debug_error(DEBUG, "read svhd tag header fail\n");
1084 if (formatContext->metadataSourceV2)
1085 free(formatContext->metadataSourceV2);
1086 return MMFILE_UTIL_FAIL;
1089 return MMFILE_UTIL_SUCCESS;
1092 static int ParseProjData(MMFileFormatContext *formatContext, MMFileIOHandle *fp, long long start_offset)
1094 unsigned int readed = 0;
1095 typedef struct proj_box_data_s {
1099 } __attribute__((aligned(1), packed)) proj_box_data;
1101 typedef struct prhd_box_data_s {
1102 uint32_t projection_pose_yaw;
1103 uint32_t projection_pose_pitch;
1104 uint32_t projection_pose_roll;
1107 typedef struct equi_box_data_s {
1108 uint32_t projection_bounds_top;
1109 uint32_t projection_bounds_bottom;
1110 uint32_t projection_bounds_left;
1111 uint32_t projection_bounds_right;
1114 typedef struct cbmp_box_data_s {
1120 proj_box_data proj_data;
1121 proj_data.proj_type = INVALID_UINT_VALUE;
1123 prhd_box_data prhd_data;
1124 prhd_data.projection_pose_yaw = INVALID_UINT_VALUE;
1125 prhd_data.projection_pose_pitch = INVALID_UINT_VALUE;
1126 prhd_data.projection_pose_roll = INVALID_UINT_VALUE;
1128 equi_box_data equi_data;
1129 equi_data.projection_bounds_top = INVALID_UINT_VALUE;
1130 equi_data.projection_bounds_bottom = INVALID_UINT_VALUE;
1131 equi_data.projection_bounds_left = INVALID_UINT_VALUE;
1132 equi_data.projection_bounds_right = INVALID_UINT_VALUE;
1134 cbmp_box_data cbmp_data;
1135 cbmp_data.layout = INVALID_UINT_VALUE;
1136 cbmp_data.padding = INVALID_UINT_VALUE;
1138 mmfile_seek(fp, start_offset, SEEK_SET);
1140 readed = mmfile_read(fp, (unsigned char *)&proj_data, sizeof(proj_box_data));
1141 if (readed != sizeof(proj_box_data)) {
1142 debug_error(DEBUG, "read of proj box failed\n");
1143 return MMFILE_UTIL_FAIL;
1146 formatContext->projTypeV2 = mmfile_io_be_uint32(proj_data.proj_type);
1148 debug_error(DEBUG, "formatContext->projTypeV2 = %d\n", formatContext->projTypeV2);
1149 debug_error(DEBUG, "proj_data.version = %d\n", proj_data.version);
1150 debug_error(DEBUG, "proj_data.flags = %d\n", ((uint32_t)proj_data.flags[0] << 16) +
1151 ((uint32_t)proj_data.flags[1] << 8) + (uint32_t)proj_data.flags[2]);
1153 mmfile_seek(fp, sizeof(proj_box_data), SEEK_CUR);
1154 readed = mmfile_read(fp, (unsigned char *)&prhd_data, sizeof(prhd_box_data));
1155 if (readed != sizeof(prhd_box_data)) {
1156 debug_error(DEBUG, "read of prhd box failed\n");
1157 return MMFILE_UTIL_FAIL;
1160 formatContext->poseYawV2 = mmfile_io_be_uint32(prhd_data.projection_pose_yaw);
1161 formatContext->posePitchV2 = mmfile_io_be_uint32(prhd_data.projection_pose_pitch);
1162 formatContext->poseRollV2 = mmfile_io_be_uint32(prhd_data.projection_pose_roll);
1164 debug_error(DEBUG, "formatContext->poseYawV2 = %d\n", formatContext->poseYawV2);
1165 debug_error(DEBUG, "formatContext->posePitchV2 = %d\n", formatContext->posePitchV2);
1166 debug_error(DEBUG, "formatContext->poseRollV2 = %d\n", formatContext->poseRollV2);
1168 if (formatContext->projTypeV2 == PROJECTION_TYPE_EQUI) {
1169 debug_msg(RELEASE, "Projection type is Equirectangular");
1170 mmfile_seek(fp, 8, SEEK_CUR); /* 8 = 4 (for size) + 4 (fourcc) */
1171 readed = mmfile_read(fp, (unsigned char *)&equi_data, sizeof(equi_box_data));
1172 if (readed != sizeof(equi_box_data)) {
1173 debug_error(DEBUG, "read of equi box failed\n");
1174 return MMFILE_UTIL_FAIL;
1177 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi_data.projection_bounds_top);
1178 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi_data.projection_bounds_bottom);
1179 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi_data.projection_bounds_left);
1180 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi_data.projection_bounds_right);
1182 debug_error(DEBUG, "formatContext->equiBoundsTopV2 = %d\n", formatContext->equiBoundsTopV2);
1183 debug_error(DEBUG, "formatContext->equiBoundsBottomV2 = %d\n", formatContext->equiBoundsBottomV2);
1184 debug_error(DEBUG, "formatContext->equiBoundsLeftV2 = %d\n", formatContext->equiBoundsLeftV2);
1185 debug_error(DEBUG, "formatContext->equiBoundsRightV2 = %d\n", formatContext->equiBoundsRightV2);
1187 } else if (formatContext->projTypeV2 == PROJECTION_TYPE_CBMP) {
1188 debug_msg(RELEASE, "Projection type is Cubemap");
1189 mmfile_seek(fp, 8, SEEK_CUR); /* 8 = 4 (for size) + 4 (fourcc) */
1190 readed = mmfile_read(fp, (unsigned char *)&cbmp_data, sizeof(cbmp_box_data));
1191 if (readed != sizeof(cbmp_box_data)) {
1192 debug_error(DEBUG, "read of cbmp box failed\n");
1193 return MMFILE_UTIL_FAIL;
1196 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp_data.layout);
1197 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp_data.padding);
1199 debug_error(DEBUG, "formatContext->cbmpLayoutV2 = %d\n", formatContext->cbmpLayoutV2);
1200 debug_error(DEBUG, "formatContext->cbmpPaddingV2 = %d\n", formatContext->cbmpPaddingV2);
1203 debug_msg(RELEASE, "Projection type is %d (unknown)", proj_data.proj_type);
1204 return MMFILE_UTIL_FAIL;
1207 return MMFILE_UTIL_SUCCESS;
1210 static int GetVideoV2MetadataFromAvc1TagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1212 if (!formatContext || !fp || !basic_header) {
1213 debug_error(DEBUG, "invalid param\n");
1214 return MMFILE_UTIL_FAIL;
1217 unsigned char *buffer;
1221 formatContext->stereoModeV2 = INVALID_UINT_VALUE;
1222 formatContext->metadataSourceV2 = NULL;
1223 formatContext->projTypeV2 = INVALID_UINT_VALUE;
1224 formatContext->poseYawV2 = INVALID_UINT_VALUE;
1225 formatContext->posePitchV2 = INVALID_UINT_VALUE;
1226 formatContext->poseRollV2 = INVALID_UINT_VALUE;
1227 formatContext->cbmpLayoutV2 = INVALID_UINT_VALUE;
1228 formatContext->cbmpPaddingV2 = INVALID_UINT_VALUE;
1229 formatContext->equiBoundsTopV2 = INVALID_UINT_VALUE;
1230 formatContext->equiBoundsBottomV2 = INVALID_UINT_VALUE;
1231 formatContext->equiBoundsLeftV2 = INVALID_UINT_VALUE;
1232 formatContext->equiBoundsRightV2 = INVALID_UINT_VALUE;
1234 mmfile_seek(fp, basic_header->start_offset, SEEK_SET);
1236 buffer = calloc(basic_header->size + 1, sizeof(unsigned char));
1238 debug_error(DEBUG, "calloc failed ");
1242 readed = mmfile_read(fp, buffer, basic_header->size);
1243 if (readed != (int)basic_header->size) {
1244 debug_error(DEBUG, "read st3d box failed\n");
1248 for (i = 0; i + 3 < basic_header->size; ++i) {
1249 if ((buffer[i] == 's' && buffer[i + 1] == 't' && buffer[i + 2] == '3' && buffer[i + 3] == 'd') && (formatContext->stereoModeV2 == INVALID_UINT_VALUE)) {
1250 debug_warning(DEBUG, "st3d data found at offset %lld\n", basic_header->start_offset + i);
1251 ParseSt3dData(formatContext, fp, basic_header->start_offset + i + 4);
1252 debug_msg(RELEASE, "formatContext->stereoModeV2 = %d", formatContext->stereoModeV2);
1254 if (buffer[i] == 's' && buffer[i + 1] == 'v' && buffer[i + 2] == '3' && buffer[i + 3] == 'd') {
1255 debug_warning(DEBUG, "sv3d data found at offset %lld\n", basic_header->start_offset + i);
1256 formatContext->isSpherical = true;
1258 if (buffer[i] == 's' && buffer[i + 1] == 'v' && buffer[i + 2] == 'h' && buffer[i + 3] == 'd') {
1259 debug_warning(DEBUG, "svhd data found at offset %lld\n", basic_header->start_offset + i);
1260 ParseSvhdData(formatContext, fp, basic_header->start_offset + i + 4, mmfile_io_be_uint32(*((uint32_t*)(buffer - 4 + i))));
1261 debug_msg(RELEASE, "formatContext->metadataSourceV2 = %s (length = %zu)", formatContext->metadataSourceV2, strlen(formatContext->metadataSourceV2));
1263 if (buffer[i] == 'p' && buffer[i + 1] == 'r' && buffer[i + 2] == 'o' && buffer[i + 3] == 'j') {
1264 debug_warning(DEBUG, "proj data found at offset %lld\n", basic_header->start_offset + i);
1265 ParseProjData(formatContext, fp, basic_header->start_offset + i + 4);
1269 return MMFILE_UTIL_SUCCESS;
1272 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1274 return MMFILE_UTIL_FAIL;
1277 static int GetValueFromCDISTagBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1279 unsigned int value = 0;
1282 if (!formatContext || !fp || !basic_header) {
1283 debug_error(DEBUG, "invalid param\n");
1284 return MMFILE_UTIL_FAIL;
1287 readed = mmfile_read(fp, (unsigned char *)&value, sizeof(unsigned int));
1288 if (readed != sizeof(unsigned int)) {
1289 debug_error(DEBUG, "read cdis tag header fail\n");
1293 value = mmfile_io_be_uint32(value);
1295 debug_msg(RELEASE, "Value : 0x%x", value);
1297 if (value == 0x01) {
1298 debug_msg(RELEASE, "This has cdis tag and valid value");
1299 formatContext->cdis = 1;
1301 debug_error(DEBUG, "This has cdis tag and but invalid value");
1305 return MMFILE_UTIL_SUCCESS;
1308 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1310 return MMFILE_UTIL_FAIL;
1313 static int GetTagFromMetaBox(MMFileFormatContext *formatContext, MMFileIOHandle *fp, MMFILE_MP4_BASIC_BOX_HEADER *basic_header)
1316 MMFILE_MP4_BASIC_BOX_HEADER hdlrBoxHeader = {0, };
1317 MMFILE_MP4_BASIC_BOX_HEADER id3v2BoxHeader = {0, };
1318 MMFILE_3GP_ID3V2_BOX id3v2Box = {0, };
1319 AvFileContentInfo tagInfo = {0, };
1320 unsigned char tagVersion = 0;
1321 bool versionCheck = false;
1323 unsigned int meta_version = 0;
1324 MMFILE_3GP_HANDLER_BOX hdlrBox = {0, };
1325 unsigned int encSize = 0;
1327 #ifdef ENABLE_ITUNES_META /* We don't support itunes meta now. so this is not defined yet */
1328 int iTunes_meta = 0;
1332 readed = mmfile_read(fp, (unsigned char *)&meta_version, 4);
1334 debug_error(DEBUG, "read meta box version\n");
1339 readed = mmfile_read(fp, (unsigned char *)&hdlrBoxHeader, MMFILE_MP4_BASIC_BOX_HEADER_LEN);
1340 if (readed != MMFILE_MP4_BASIC_BOX_HEADER_LEN) {
1341 debug_error(DEBUG, "read hdlr box header\n");
1345 if (hdlrBoxHeader.type != FOURCC('h', 'd', 'l', 'r')) {
1346 debug_warning(DEBUG, "meta type is not hdlr\n");
1350 hdlrBoxHeader.size = mmfile_io_be_uint32(hdlrBoxHeader.size);
1351 hdlrBoxHeader.type = mmfile_io_le_uint32(hdlrBoxHeader.type);
1353 readed = mmfile_read(fp, (unsigned char *)&hdlrBox, MMFILE_3GP_HANDLER_BOX_LEN);
1354 if (readed != MMFILE_3GP_HANDLER_BOX_LEN) {
1355 debug_error(DEBUG, "read hdlr box\n");
1359 hdlrBox.handler_type = mmfile_io_le_uint32(hdlrBox.handler_type);
1362 * check tag type (ID3v2 or iTunes)
1364 if (hdlrBox.handler_type == FOURCC('I', 'D', '3', '2')) {
1365 debug_msg(RELEASE, "ID3v2 tag detected.\n");
1368 #ifdef ENABLE_ITUNES_META
1371 } else if (hdlrBox.handler_type == FOURCC('m', 'd', 'i', 'r') &&
1372 mmfile_io_le_uint32(hdlrBox.reserved[0]) == FOURCC('a', 'p', 'p', 'l')) {
1374 debug_msg(RELEASE, "Apple iTunes tag detected by mdir.\n");
1376 #ifdef ENABLE_ITUNES_META
1380 debug_warning(DEBUG, "unknown meta type. 4CC:[%c%c%c%c]\n", ((char *)&hdlrBox.handler_type)[0],
1381 ((char *)&hdlrBox.handler_type)[1],
1382 ((char *)&hdlrBox.handler_type)[2],
1383 ((char *)&hdlrBox.handler_type)[3]);
1384 /*goto exception; */
1387 #ifdef ENABLE_ITUNES_META
1388 if (!id3_meta && !iTunes_meta) {
1390 APPLE meta data for iTunes reader = 'mdir.' so if handler type is 'mdir', this content may has itunes meta.
1391 most of contents has 'mdir' + 'appl'. but some contents just has 'mdir'
1392 but 'ilst' is meta for iTunes. so find 'ilst' is more correct to check if this contents has iTunes meta or not.*/
1394 const char *ilst_box = "ilst";
1395 int buf_size = strlen(ilst_box);
1397 unsigned char read_buf[buf_size + 1];
1398 memset(read_buf, 0x00, buf_size + 1);
1401 mmfile_seek(fp, hdlrBoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_HANDLER_BOX_LEN + 4, SEEK_CUR); /*+4 is hdlr size field */
1403 readed = mmfile_read(fp, read_buf, buf_size); /* to find 'ilst' */
1404 if (readed != buf_size) {
1405 debug_error(DEBUG, "read fail [%d]\n", readed);
1409 if (read_buf[0] == 'i' && read_buf[1] == 'l' && read_buf[2] == 's' && read_buf[3] == 't') {
1410 debug_msg(RELEASE, "Apple iTunes tag detected by ilst.\n");
1416 #ifdef ENABLE_ITUNES_META
1419 * iTunes (Cover[?ovr] & Track[trkn] only extract!) + Genre/Artist : Added 2010.10.27,28
1427 #define _ITUNES_READ_BUF_SZ 20
1428 #define _ITUNES_TRACK_NUM_SZ 4
1429 #define _ITUNES_GENRE_NUM_SZ 4
1430 #define _ITUNES_COVER_TYPE_JPEG 13
1431 #define _ITUNES_COVER_TYPE_PNG 14
1433 unsigned char read_buf[_ITUNES_READ_BUF_SZ];
1435 int cover_sz = 0, cover_type = 0, cover_found = 0;
1436 /* int track_found = 0; */ /* , track_num = 0; */
1437 /* int genre_found = 0; */ /* , genre_index = 0; */
1438 /* int artist_found = 0; */ /* , artist_sz = 0; */
1439 int limit = basic_header->size - hdlrBoxHeader.size;
1440 long long cover_offset = 0; /*, track_offset =0, genre_offset = 0, artist_offset = 0; */
1442 /* 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++) { */
1443 for (i = 0; (i < limit) && (cover_found == 0) ; i++) {
1444 readed = mmfile_read(fp, read_buf, _ITUNES_READ_BUF_SZ);
1445 if (readed != _ITUNES_READ_BUF_SZ)
1448 /*ffmpeg extract artist, tracknum, genre and cover image. see mov_read_udta_string().
1449 but ffmpeg does not support strange cover image.
1450 only support covr type 0xd(JPEG), 0xe(PNG), 0x1b(BMP). but we support other type*/
1453 * Artist : Added 2010.10.28
1455 if (artist_found == 0 &&
1456 read_buf[0] == 0xa9 && read_buf[1] == 'A' && read_buf[2] == 'R' && read_buf[3] == 'T' &&
1457 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1460 artist_offset = mmfile_tell(fp);
1461 artist_sz = mmfile_io_be_uint32(*(int *)(read_buf + 4)) - 16; /* atom len(4)+data(4)+atom verion(1)+flag(3)+null(4) = 16 */
1463 debug_msg(RELEASE, "----------------------------------- artist found, offset=[%lld], size=[%d]\n", artist_offset, artist_sz);
1469 if (track_found == 0 &&
1470 read_buf[0] == 't' && read_buf[1] == 'r' && read_buf[2] == 'k' && read_buf[3] == 'n' &&
1471 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1474 track_offset = mmfile_tell(fp);
1476 debug_msg(RELEASE, "----------------------------------- Track found, offset=[%lld]\n", track_offset);
1480 * Genre : Added 2010.10.27
1482 /*ffmpeg extract genre but only (0xa9,'g','e','n'). see mov_read_udta_string()*/
1483 if (genre_found == 0 &&
1484 read_buf[0] == 'g' && read_buf[1] == 'n' && read_buf[2] == 'r' && read_buf[3] == 'e' &&
1485 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1488 genre_offset = mmfile_tell(fp);
1490 debug_msg(RELEASE, "----------------------------------- genre found, offset=[%lld]\n", genre_offset);
1498 if (cover_found == 0 &&
1499 read_buf[0] == 'c' && read_buf[1] == 'o' && read_buf[2] == 'v' && read_buf[3] == 'r' &&
1500 read_buf[8] == 'd' && read_buf[9] == 'a' && read_buf[10] == 't' && read_buf[11] == 'a') {
1503 cover_sz = mmfile_io_be_uint32(*(int *)(read_buf + 4)) - 12;
1504 cover_type = mmfile_io_be_uint32(*(int *)(read_buf + 12));
1506 cover_offset = mmfile_tell(fp);
1508 debug_msg(RELEASE, "----------------------------------- cover_found found, offset=[%lld]\n", cover_offset);
1511 mmfile_seek(fp, -(_ITUNES_READ_BUF_SZ - 1), SEEK_CUR); /*FIXME: poor search*/
1514 /*ffmpeg extract artist, tracknum, excep cover image. see mov_read_udta_string()*/
1517 if (artist_sz > 0) {
1518 mmfile_seek(fp, artist_offset, SEEK_SET);
1520 if (formatContext->artist) {
1521 debug_msg(RELEASE, "----------------------------------- previous artist was [%s] \n", formatContext->artist);
1522 free(formatContext->artist);
1525 debug_msg(RELEASE, "----------------------------------- new artist will be allocated with size (len+1) [%d] \n", artist_sz + 1);
1526 formatContext->artist = mmfile_malloc(artist_sz + 1);
1528 if (formatContext->artist) {
1529 readed = mmfile_read(fp, (unsigned char *)formatContext->artist, artist_sz);
1530 formatContext->artist[artist_sz] = '\0';
1532 debug_msg(RELEASE, "----------------------------------- new artist is [%s] \n", formatContext->artist);
1534 if (readed != artist_sz) {
1535 debug_error(DEBUG, "failed to read. ret = %d, in = %d\n", readed, artist_sz);
1536 mmfile_free(formatContext->artist);
1543 mmfile_seek(fp, track_offset, SEEK_SET);
1544 readed = mmfile_read(fp, read_buf, _ITUNES_TRACK_NUM_SZ);
1545 if (readed != _ITUNES_TRACK_NUM_SZ) {
1546 debug_error(DEBUG, "failed to read. ret = %d, in = %d\n", readed, _ITUNES_TRACK_NUM_SZ);
1548 track_num = mmfile_io_be_uint32(*(int *)read_buf);
1549 if (!formatContext->tagTrackNum) {
1550 memset(read_buf, 0x00, _ITUNES_READ_BUF_SZ);
1551 snprintf((char *)read_buf, sizeof(read_buf), "%d", track_num);
1552 formatContext->tagTrackNum = mmfile_strdup((const char *)read_buf);
1558 mmfile_seek(fp, genre_offset, SEEK_SET);
1559 readed = mmfile_read(fp, read_buf, _ITUNES_GENRE_NUM_SZ);
1560 if (readed != _ITUNES_GENRE_NUM_SZ) {
1561 debug_error(DEBUG, "failed to read. ret = %d, in = %d\n", readed, _ITUNES_GENRE_NUM_SZ);
1563 genre_index = mmfile_io_be_uint16(*(int *)read_buf);
1564 debug_msg(RELEASE, "genre index=[%d] \n", genre_index);
1566 if (genre_index > 0 && genre_index < GENRE_COUNT) {
1567 if (!formatContext->genre) {
1568 memset(read_buf, 0x00, _ITUNES_READ_BUF_SZ);
1569 snprintf((char *)read_buf, sizeof(read_buf), "%s", MpegAudio_Genre[genre_index - 1]);
1570 debug_msg(RELEASE, "genre string=[%s] \n", read_buf);
1571 formatContext->genre = mmfile_strdup((const char *)read_buf);
1579 1) below spec is in "iTunes Package Asset Specification 4.3" published by apple.
1580 Music Cover Art Image Profile
1581 - TIFF with ".tif" extension (32-bit uncompressed), JPEG with ".jpg" extension (quality unconstrained), or PNG with ".png" extension
1582 - RGB (screen standard)
1583 - Minimum size of 600 x 600 pixels
1584 - Images must be at least 72 dpi
1586 2)I found below info from google.
1587 cover image flag : JPEG (13, 0xd), PNG (14, 0xe)
1589 3)So, FIXME when cover image format is tif!
1593 mmfile_seek(fp, cover_offset, SEEK_SET);
1595 formatContext->artwork = mmfile_malloc(cover_sz);
1596 formatContext->artworkSize = cover_sz;
1597 if (cover_type == _ITUNES_COVER_TYPE_JPEG) {
1598 formatContext->artworkMime = mmfile_strdup("image/jpeg");
1599 } else if (cover_type == _ITUNES_COVER_TYPE_PNG) {
1600 formatContext->artworkMime = mmfile_strdup("image/png");
1601 /*} else if (cover_type == _ITUNES_COVER_TYPE_TIF) {
1602 formatContext->artworkMime = mmfile_strdup("image/tif");*/
1604 debug_warning(DEBUG, "Not proper cover image type, but set to jpeg. cover_type[%d]", cover_type);
1605 formatContext->artworkMime = mmfile_strdup("image/jpeg");
1608 if (formatContext->artwork) {
1609 readed = mmfile_read(fp, formatContext->artwork, cover_sz);
1610 if (readed != cover_sz) {
1611 debug_error(DEBUG, "failed to read. ret = %d, in = %d\n", readed, cover_sz);
1612 mmfile_free(formatContext->artwork);
1613 formatContext->artworkSize = 0;
1614 mmfile_free(formatContext->artworkMime);
1620 /*reset seek position*/
1621 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1623 return MMFILE_UTIL_SUCCESS;
1631 /* skip hdlr box name */
1632 mmfile_seek(fp, hdlrBoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_HANDLER_BOX_LEN, SEEK_CUR);
1635 readed = mmfile_read(fp, (unsigned char *)&id3v2BoxHeader, MMFILE_MP4_BASIC_BOX_HEADER_LEN);
1636 if (readed != MMFILE_MP4_BASIC_BOX_HEADER_LEN) {
1637 debug_error(DEBUG, "read id3v2 box header\n");
1641 id3v2BoxHeader.size = mmfile_io_be_uint32(id3v2BoxHeader.size);
1642 id3v2BoxHeader.type = mmfile_io_le_uint32(id3v2BoxHeader.type);
1644 if (id3v2BoxHeader.type != FOURCC('I', 'D', '3', '2')) {
1645 debug_warning(DEBUG, "meta type is not id3v2\n");
1649 readed = mmfile_read(fp, (unsigned char *)&id3v2Box, MMFILE_3GP_ID3V2_BOX_LEN);
1650 if (readed != MMFILE_3GP_ID3V2_BOX_LEN) {
1651 debug_error(DEBUG, "read id3v2 box\n");
1655 id3v2Len = id3v2BoxHeader.size - MMFILE_MP4_BASIC_BOX_HEADER_LEN - MMFILE_3GP_ID3V2_BOX_LEN;
1657 id3v2Box.id3v2Data = mmfile_malloc(id3v2Len);
1658 if (!id3v2Box.id3v2Data) {
1659 debug_error(DEBUG, "malloc id3tag data error\n");
1663 readed = mmfile_read(fp, (unsigned char *)id3v2Box.id3v2Data, id3v2Len);
1664 if (readed != id3v2Len) {
1665 debug_error(DEBUG, "read id3tag data error\n");
1670 if (!IS_ID3V2_TAG(id3v2Box.id3v2Data)) {
1671 debug_error(DEBUG, "it is not id3tag\n");
1675 if (id3v2Box.id3v2Data[3] == 0xFF || id3v2Box.id3v2Data[4] == 0xFF ||
1676 id3v2Box.id3v2Data[6] >= 0x80 || id3v2Box.id3v2Data[7] >= 0x80 ||
1677 id3v2Box.id3v2Data[8] >= 0x80 || id3v2Box.id3v2Data[9] >= 0x80) {
1678 debug_error(DEBUG, "it is not valid id3tag\n");
1682 tagVersion = id3v2Box.id3v2Data[3];
1683 if (tagVersion > 4) {
1684 debug_error(DEBUG, "tag vesion is too high\n");
1688 encSize = mmfile_io_le_uint32((unsigned int)&id3v2Box.id3v2Data[6]);
1689 tagInfo.tagV2Info.tagLen = MP3_TAGv2_HEADER_LEN;
1690 tagInfo.tagV2Info.tagLen += (((encSize & 0x0000007F) >> 0) | ((encSize & 0x00007F00) >> 1) | ((encSize & 0x007F0000) >> 2) | ((encSize & 0x7F000000) >> 3));
1691 tagInfo.tagV2Info.tagVersion = tagVersion;
1692 tagInfo.fileLen = id3v2Len;
1694 /* set id3v2 data to formatContext */
1695 switch (tagVersion) {
1697 versionCheck = mm_file_id3tag_parse_v222(&tagInfo, id3v2Box.id3v2Data);
1701 versionCheck = mm_file_id3tag_parse_v223(&tagInfo, id3v2Box.id3v2Data);
1705 versionCheck = mm_file_id3tag_parse_v224(&tagInfo, id3v2Box.id3v2Data);
1710 debug_error(DEBUG, "tag vesion is not support\n");
1711 versionCheck = false;
1716 if (versionCheck == false) {
1717 debug_error(DEBUG, "tag parsing is fail\n");
1721 formatContext->title = mmfile_strdup((const char *)tagInfo.pTitle);
1722 formatContext->artist = mmfile_strdup((const char *)tagInfo.pArtist);
1723 formatContext->author = mmfile_strdup((const char *)tagInfo.pAuthor);
1724 formatContext->copyright = mmfile_strdup((const char *)tagInfo.pCopyright);
1725 formatContext->comment = mmfile_strdup((const char *)tagInfo.pComment);
1726 formatContext->album = mmfile_strdup((const char *)tagInfo.pAlbum);
1727 formatContext->album_artist = mmfile_strdup((const char *)tagInfo.pAlbum_Artist);
1728 formatContext->year = mmfile_strdup((const char *)tagInfo.pYear);
1729 formatContext->genre = mmfile_strdup((const char *)tagInfo.pGenre);
1730 formatContext->tagTrackNum = mmfile_strdup((const char *)tagInfo.pTrackNum);
1731 formatContext->composer = mmfile_strdup((const char *)tagInfo.pComposer);
1732 formatContext->classification = mmfile_strdup((const char *)tagInfo.pContentGroup);
1733 formatContext->conductor = mmfile_strdup((const char *)tagInfo.pConductor);
1735 formatContext->artwork = mmfile_malloc(tagInfo.imageInfo.imageLen);
1736 if ((tagInfo.imageInfo.imageLen > 0) && formatContext->artwork) {
1737 formatContext->artworkSize = tagInfo.imageInfo.imageLen;
1738 memcpy(formatContext->artwork, tagInfo.imageInfo.pImageBuf, tagInfo.imageInfo.imageLen);
1741 mm_file_free_AvFileContentInfo(&tagInfo);
1742 mmfile_free(id3v2Box.id3v2Data);
1744 /*reset seek position*/
1745 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1747 return MMFILE_UTIL_SUCCESS;
1753 mmfile_seek(fp, basic_header->start_offset + basic_header->size, SEEK_SET);
1754 mmfile_free(id3v2Box.id3v2Data);
1755 mm_file_free_AvFileContentInfo(&tagInfo);
1757 return MMFILE_UTIL_FAIL;
1760 int mm_file_get_int_value_from_xml_string(const char* xml_str, const char* param_name, int* value)
1762 char *value_start, *value_end, *endptr;
1763 const short value_length_max = 12;
1764 char init_view_ret[value_length_max];
1765 int value_length = 0;
1767 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1768 debug_error(DEBUG, "error: incorrect or non-existing parameter\n");
1769 return MMFILE_UTIL_FAIL;
1772 value_start = strstr(xml_str, param_name) + strlen(param_name);
1773 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
1776 value_end = strchr(value_start, '<');
1778 debug_error(DEBUG, "error: incorrect XML\n");
1779 return MMFILE_UTIL_FAIL;
1782 value_length = value_end - value_start;
1783 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1787 if (value_start[i] == '+' || value_start[i] == '-')
1789 while (i < value_length) {
1790 if (value_start[i] < '0' || value_start[i] > '9') {
1791 debug_error(DEBUG, "error: incorrect value, integer was expected\n");
1792 return MMFILE_UTIL_FAIL;
1797 if (value_length >= value_length_max || value_length < 1) {
1798 debug_error(DEBUG, "error: empty XML value or incorrect range\n");
1799 return MMFILE_UTIL_FAIL;
1802 memset(init_view_ret, 0x00, sizeof(init_view_ret));
1803 SAFE_STRLCPY(init_view_ret, value_start, sizeof(init_view_ret));
1805 *value = strtol(init_view_ret, &endptr, 10);
1806 if (endptr == init_view_ret) {
1807 debug_error(DEBUG, "error: no digits were found\n");
1808 return MMFILE_UTIL_FAIL;
1811 return MMFILE_UTIL_SUCCESS;
1814 int mm_file_get_string_value_from_xml_string(const char* xml_str, const char* param_name, char** value)
1816 char *value_start, *value_end;
1817 const short value_length_max = 256;
1818 int value_length = 0;
1820 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1821 debug_error(DEBUG, "error: incorrect or non-existing parameter\n");
1822 return MMFILE_UTIL_FAIL;
1825 value_start = strstr(xml_str, param_name) + strlen(param_name);
1826 while ((value_start[0] == ' ') || (value_start[0] == '\t'))
1829 value_end = strchr(value_start, '<');
1831 debug_error(DEBUG, "error: incorrect XML\n");
1832 return MMFILE_UTIL_FAIL;
1835 value_length = value_end - value_start;
1836 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1839 if (value_length >= value_length_max || value_length < 1) {
1840 debug_error(DEBUG, "error: empty XML value or incorrect range\n");
1841 return MMFILE_UTIL_FAIL;
1844 *value = (char*)calloc(value_length, sizeof(char));
1845 if (*value == NULL) {
1846 debug_error(DEBUG, "error: calloc failed\n");
1847 return MMFILE_UTIL_FAIL;
1849 strncpy(*value, value_start, value_length);
1851 return MMFILE_UTIL_SUCCESS;
1854 int mm_file_get_bool_value_from_xml_string(const char* xml_str, const char* param_name, bool* value)
1856 char *value_start = NULL;
1857 char *value_end = NULL;
1858 int value_length = 0;
1860 if (!xml_str || !param_name || !strstr(xml_str, param_name)) {
1861 debug_error(DEBUG, "error: incorrect or non-existing parameter\n");
1862 return MMFILE_UTIL_FAIL;
1865 value_start = strstr(xml_str, param_name) + strlen(param_name);
1866 while ((value_start != NULL) && ((value_start[0] == ' ') || (value_start[0] == '\t')))
1869 value_end = strchr(value_start, '<');
1870 if (value_end == NULL) {
1871 debug_error(DEBUG, "error: incorrect XML.");
1872 return MMFILE_UTIL_FAIL;
1875 value_length = value_end - value_start;
1876 while ((value_length >= 1) && ((value_start[value_length - 1] == ' ') || (value_start[value_length - 1] == '\t')))
1879 if (value_length < 1) {
1880 debug_error(DEBUG, "error: empty XML value or incorrect range\n");
1881 return MMFILE_UTIL_FAIL;
1884 *value = strstr(value_start, "true") ? true : false;
1886 return MMFILE_UTIL_SUCCESS;
1889 static int g_junk_counter_limit = 0;
1890 static int GetJunkCounterLimit(void)
1892 dictionary *dict = NULL;
1895 dict = iniparser_load(MM_FILE_INI_PATH);
1897 debug_error(DEBUG, "%s load failed", MM_FILE_INI_PATH);
1901 data = iniparser_getint(dict, "mm-file-config:junk_counter_limit", 0);
1902 debug_msg(DEBUG, "mm-file-config:junk_counter_limit= %u", data);
1904 iniparser_freedict(dict);
1909 int ParseSpatialVideoMetadataFromXMLString(const char *xmlStr, MMFileFormatContext *formatContext)
1911 const char is_spherical_str[] = "<GSpherical:Spherical>";
1912 const char is_stitched_str[] = "<GSpherical:Stitched>";
1913 const char stitching_software_str[] = "<GSpherical:StitchingSoftware>";
1914 const char projection_type_str[] = "<GSpherical:ProjectionType>";
1915 const char stereo_mode_str[] = "<GSpherical:StereoMode>";
1916 const char source_count_str[] = "<GSpherical:SourceCount>";
1917 const char init_view_heading_str[] = "<GSpherical:InitialViewHeadingDegrees>";
1918 const char init_view_pitch_str[] = "<GSpherical:InitialViewPitchDegrees>";
1919 const char init_view_roll_str[] = "<GSpherical:InitialViewRollDegrees>";
1920 const char timestamp_str[] = "<GSpherical:Timestamp>";
1921 const char full_pano_width_str[] = "<GSpherical:FullPanoWidthPixels>";
1922 const char full_pano_height_str[] = "<GSpherical:FullPanoHeightPixels>";
1923 const char cropped_area_image_width_str[] = "<GSpherical:CroppedAreaImageWidthPixels>";
1924 const char cropped_area_image_height_str[] = "<GSpherical:CroppedAreaImageHeightPixels>";
1925 const char cropped_area_left_str[] = "<GSpherical:CroppedAreaLeftPixels>";
1926 const char cropped_area_top_str[] = "<GSpherical:CroppedAreaTopPixels>";
1928 mm_file_get_bool_value_from_xml_string(xmlStr, is_spherical_str, (bool*)&formatContext->isSpherical);
1929 mm_file_get_bool_value_from_xml_string(xmlStr, is_stitched_str, (bool*)&formatContext->isStitched);
1931 debug_msg(RELEASE, "isSpherical = %d", formatContext->isSpherical);
1932 debug_msg(RELEASE, "isStitched = %d", formatContext->isStitched);
1934 if (formatContext->isSpherical && formatContext->isStitched) {
1935 mm_file_get_string_value_from_xml_string(xmlStr, stitching_software_str, &formatContext->stitchingSoftware);
1936 mm_file_get_string_value_from_xml_string(xmlStr, projection_type_str, &formatContext->projectionType);
1937 mm_file_get_string_value_from_xml_string(xmlStr, stereo_mode_str, &formatContext->stereoMode);
1938 mm_file_get_int_value_from_xml_string(xmlStr, source_count_str, &formatContext->sourceCount);
1939 mm_file_get_int_value_from_xml_string(xmlStr, init_view_heading_str, &formatContext->initViewHeading);
1940 mm_file_get_int_value_from_xml_string(xmlStr, init_view_pitch_str, &formatContext->initViewPitch);
1941 mm_file_get_int_value_from_xml_string(xmlStr, init_view_roll_str, &formatContext->initViewRoll);
1942 mm_file_get_int_value_from_xml_string(xmlStr, timestamp_str, &formatContext->timestamp);
1943 mm_file_get_int_value_from_xml_string(xmlStr, full_pano_width_str, &formatContext->fullPanoWidth);
1944 mm_file_get_int_value_from_xml_string(xmlStr, full_pano_height_str, &formatContext->fullPanoHeight);
1945 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_width_str, &formatContext->croppedAreaImageWidth);
1946 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_image_height_str, &formatContext->croppedAreaImageHeight);
1947 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_left_str, &formatContext->croppedAreaLeft);
1948 mm_file_get_int_value_from_xml_string(xmlStr, cropped_area_top_str, &formatContext->croppedAreaTop);
1950 debug_msg(RELEASE, "stitchingSoftware = %s", formatContext->stitchingSoftware);
1951 debug_msg(RELEASE, "projectionType = %s", formatContext->projectionType);
1952 debug_msg(RELEASE, "stereoMode = %s", formatContext->stereoMode);
1953 debug_msg(RELEASE, "sourceCount %d", formatContext->sourceCount);
1954 debug_msg(RELEASE, "initViewHeading = %d", formatContext->initViewHeading);
1955 debug_msg(RELEASE, "initViewPitch = %d", formatContext->initViewPitch);
1956 debug_msg(RELEASE, "initViewRoll = %d", formatContext->initViewRoll);
1957 debug_msg(RELEASE, "timestamp = %d", formatContext->timestamp);
1958 debug_msg(RELEASE, "fullPanoWidthPixels = %d", formatContext->fullPanoWidth);
1959 debug_msg(RELEASE, "fullPanoHeightPixels = %d", formatContext->fullPanoHeight);
1960 debug_msg(RELEASE, "croppedAreaImageWidth = %d", formatContext->croppedAreaImageWidth);
1961 debug_msg(RELEASE, "croppedAreaImageHeight = %d", formatContext->croppedAreaImageHeight);
1962 debug_msg(RELEASE, "croppedAreaLeft = %d", formatContext->croppedAreaLeft);
1963 debug_msg(RELEASE, "croppedAreaTop = %d", formatContext->croppedAreaTop);
1966 return MMFILE_UTIL_SUCCESS;
1969 #define BIG_CONTENT_BOX_SIZE_LEN 8
1971 int MMFileUtilGetMetaDataFromMKV(MMFileFormatContext *formatContext)
1973 MMFileIOHandle *fp = NULL;
1974 int probe_size = 10000;
1975 unsigned char *buffer = NULL;
1978 long long file_size = 0;
1980 MMFILE_WEBM_PROJ_V2_BOX v2box = { 0, };
1981 MMFILE_WEBM_EQUI_PROJ_V2_BOX equi = { 0, };
1982 MMFILE_WEBM_CBMP_PROJ_V2_BOX cbmp = { 0, };
1983 MMFILE_WEBM_POSE_ELEMENT_V2_BOX pose = { 0, };
1985 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
1986 if (ret == MMFILE_UTIL_FAIL) {
1987 debug_error(DEBUG, "error: mmfile_open\n");
1991 file_size = mmfile_seek(fp, 0, SEEK_END);
1992 if (file_size == MMFILE_UTIL_FAIL) {
1993 debug_error(DEBUG, "mmfile operation failed\n");
1997 probe_size = (file_size > probe_size) ? probe_size : file_size;
1998 buffer = (unsigned char *)malloc(probe_size * sizeof(unsigned char));
2000 debug_error(DEBUG, "malloc failed\n");
2004 ret = mmfile_seek(fp, 0, SEEK_SET);
2005 if (ret == MMFILE_UTIL_FAIL) {
2006 debug_error(DEBUG, "mmfile operation failed\n");
2010 ret = mmfile_read(fp, buffer, probe_size * sizeof(unsigned char));
2011 if (ret == MMFILE_UTIL_FAIL) {
2012 debug_error(DEBUG, "mmfile operation failed\n");
2016 /* FIXME (m.alieksieie): It's better to use some EBML parser here*/
2017 for (i = 0; i + 3 < probe_size; ++i) {
2018 if (*(unsigned int *)(buffer + i) == FOURCC('e', 'q', 'u', 'i') ||
2019 *(unsigned int *)(buffer + i) == FOURCC('c', 'b', 'm', 'p')) {
2020 debug_msg(DEBUG, "projection data found at offset %d bytes\n", i);
2025 if (i + 3 == probe_size) {
2026 debug_msg(DEBUG, "projection info wasn't found\n");
2027 ret = MMFILE_UTIL_SUCCESS;
2031 if ((i - (int)sizeof(MMFILE_WEBM_PROJ_V2_BOX)) < 0) {
2032 debug_error(DEBUG, "error: invalid supposed projection info location\n");
2033 ret = MMFILE_UTIL_FAIL;
2037 ret = mmfile_seek(fp, i - sizeof(MMFILE_WEBM_PROJ_V2_BOX), SEEK_SET);
2038 if (ret == MMFILE_UTIL_FAIL) {
2039 debug_error(DEBUG, "error: failed to seek to the supposed projection info location\n");
2043 ret = mmfile_read(fp, (unsigned char *)&v2box, sizeof(MMFILE_WEBM_PROJ_V2_BOX));
2044 if (ret == MMFILE_UTIL_FAIL) {
2045 debug_error(DEBUG, "mmfile operation failed\n");
2049 if (v2box.proj_type_box_value == PROJECTION_TYPE_EQUI) {
2050 debug_msg(DEBUG, "Equirectangular projection is used\n");
2052 ret = mmfile_read(fp, (unsigned char *)&equi, sizeof(MMFILE_WEBM_EQUI_PROJ_V2_BOX));
2053 if (ret == MMFILE_UTIL_FAIL) {
2054 debug_error(DEBUG, "error: failed to read equirectangular element\n");
2057 if (strncmp((char *)equi.proj_priv_box_name, "equi", 4) == 0) {
2058 formatContext->equiBoundsTopV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_top);
2059 formatContext->equiBoundsBottomV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_bottom);
2060 formatContext->equiBoundsLeftV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_left);
2061 formatContext->equiBoundsRightV2 = mmfile_io_be_uint32(equi.equi_projection_bounds_right);
2063 debug_error(DEBUG, "error: failed to read equirectangular element\n");
2064 ret = MMFILE_UTIL_SUCCESS;
2068 if (v2box.proj_type_box_value == PROJECTION_TYPE_CBMP) {
2069 debug_msg(DEBUG, "Cubemap projection is used\n");
2071 ret = mmfile_read(fp, (unsigned char *)&cbmp, sizeof(MMFILE_WEBM_CBMP_PROJ_V2_BOX));
2072 if (ret == MMFILE_UTIL_FAIL) {
2073 debug_error(DEBUG, "error: failed to read cubemap element\n");
2076 if (strncmp((char *)cbmp.proj_priv_box_name, "cbmp", 4) == 0) {
2077 formatContext->cbmpLayoutV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_layout);
2078 formatContext->cbmpPaddingV2 = mmfile_io_be_uint32(cbmp.cbmp_projection_padding);
2080 debug_error(DEBUG, "error: failed to read cubemap element\n");
2081 ret = MMFILE_UTIL_FAIL;
2086 ret = mmfile_read(fp, (unsigned char *)&pose, sizeof(MMFILE_WEBM_POSE_ELEMENT_V2_BOX));
2087 if (ret == MMFILE_UTIL_FAIL) {
2088 debug_error(DEBUG, "error: failed to read pose info\n");
2092 if (pose.pose_yaw_element_id == POSE_YAW_ELEMENT_ID) {
2093 formatContext->poseYawV2 = (uint)mmfile_io_be_float32(pose.pose_yaw_element_value);
2095 debug_error(DEBUG, "error: failed to pose yaw element\n");
2096 ret = MMFILE_UTIL_FAIL;
2099 if (pose.pose_pitch_element_id == POSE_PITCH_ELEMENT_ID) {
2100 formatContext->posePitchV2 = (uint)mmfile_io_be_float32(pose.pose_pitch_element_value);
2102 debug_error(DEBUG, "error: failed to pose pitch element\n");
2103 ret = MMFILE_UTIL_FAIL;
2106 if (pose.pose_roll_element_id == POSE_ROLL_ELEMENT_ID) {
2107 formatContext->poseRollV2 = (uint)mmfile_io_be_float32(pose.pose_roll_element_value);
2109 debug_error(DEBUG, "error: failed to pose roll element\n");
2110 ret = MMFILE_UTIL_FAIL;
2123 EXPORT_API int MMFileUtilGetMetaDataFromMP4(MMFileFormatContext *formatContext)
2125 MMFileIOHandle *fp = NULL;
2128 unsigned long long chunk_size = 0;
2129 long long moov_end = 0;
2130 MMFILE_MP4_BASIC_BOX_HEADER basic_header = {0, };
2131 int junk_counter = 0;
2133 ret = mmfile_open(&fp, formatContext->uriFileName, MMFILE_RDONLY);
2134 if (ret == MMFILE_UTIL_FAIL) {
2135 debug_error(DEBUG, "error: mmfile_open\n");
2139 basic_header.start_offset = mmfile_tell(fp);
2141 if (g_junk_counter_limit == 0)
2142 g_junk_counter_limit = GetJunkCounterLimit();
2144 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)) {
2145 basic_header.size = mmfile_io_be_uint32(basic_header.size);
2146 basic_header.type = mmfile_io_le_uint32(basic_header.type);
2148 if (basic_header.size == 0) {
2149 debug_warning(DEBUG, "header is invalid.\n");
2150 basic_header.size = readed;
2151 basic_header.type = 0;
2152 chunk_size = basic_header.size;
2154 if ((moov_end != 0) && (moov_end < basic_header.start_offset)) {
2155 debug_msg(DEBUG, "found junk data but moov data already was extracted, so junk counter will be increase: %d", junk_counter);
2158 /* stop the loop for junk case. */
2159 if ((g_junk_counter_limit > 0) && (junk_counter > g_junk_counter_limit)) {
2160 debug_msg(DEBUG, "stop the loop by junk-data checker");
2161 ret = MMFILE_UTIL_FAIL;
2165 } else if (basic_header.size == 1) {
2167 unsigned char temp[BIG_CONTENT_BOX_SIZE_LEN] = {0, };
2168 unsigned long long size = 0;
2170 mmfile_read(fp, (unsigned char *)&temp, BIG_CONTENT_BOX_SIZE_LEN);
2172 for (i = 0; i < BIG_CONTENT_BOX_SIZE_LEN; i++)
2173 size |= (unsigned long long)temp[i] << (BIG_CONTENT_BOX_SIZE_LEN - 1 - i) * BIG_CONTENT_BOX_SIZE_LEN;
2177 chunk_size = basic_header.size;
2181 switch (basic_header.type) {
2182 case FOURCC('m', 'o', 'o', 'v'): {
2183 debug_msg(RELEASE, "MPEG4: [moov] SIZE: [%lld]Byte\n", chunk_size);
2184 moov_end = basic_header.start_offset + chunk_size;
2187 case FOURCC('u', 'd', 't', 'a'): {
2188 debug_msg(RELEASE, "MPEG4: [udat] SIZE: [%lld]Byte\n", chunk_size);
2191 /*/////////////////////////////////////////////////////////////// */
2192 /* Extracting Tag Data // */
2193 /*/////////////////////////////////////////////////////////////// */
2194 case FOURCC('t', 'i', 't', 'l'): {
2195 debug_msg(RELEASE, "MPEG4: [titl] SIZE: [%lld]Byte\n", chunk_size);
2196 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_TITLE);
2199 case FOURCC('d', 's', 'c', 'p'): {
2200 debug_msg(RELEASE, "MPEG4: [dscp] SIZE: [%lld]Byte\n", chunk_size);
2201 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_CAPTION);
2204 case FOURCC('c', 'p', 'r', 't'): {
2205 debug_msg(RELEASE, "MPEG4: [cprt] SIZE: [%lld]Byte\n", chunk_size);
2206 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_COPYRIGHT);
2209 case FOURCC('p', 'e', 'r', 'f'): {
2210 debug_msg(RELEASE, "MPEG4: [perf] SIZE: [%lld]Byte\n", chunk_size);
2211 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_PERFORMER);
2214 case FOURCC('a', 'u', 't', 'h'): {
2215 debug_msg(RELEASE, "MPEG4: [auth] SIZE: [%lld]Byte\n", chunk_size);
2216 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_AUTHOR);
2219 case FOURCC('g', 'n', 'r', 'e'): {
2220 debug_msg(RELEASE, "MPEG4: [gnre] SIZE: [%lld]Byte\n", chunk_size);
2221 GetStringFromTextTagBox(formatContext, fp, &basic_header, eMMFILE_3GP_TAG_GENRE);
2224 case FOURCC('a', 'l', 'b', 'm'): {
2225 debug_msg(RELEASE, "MPEG4: [albm] SIZE: [%lld]Byte\n", chunk_size);
2226 GetAlbumFromAlbumTagBox(formatContext, fp, &basic_header);
2229 case FOURCC('y', 'r', 'r', 'c'): {
2230 debug_msg(RELEASE, "MPEG4: [yrrc] SIZE: [%lld]Byte\n", chunk_size);
2231 GetYearFromYearTagBox(formatContext, fp, &basic_header);
2234 case FOURCC('r', 't', 'n', 'g'): {
2235 debug_msg(RELEASE, "MPEG4: [rtng] SIZE: [%lld]Byte\n", chunk_size);
2236 GetRatingFromRatingTagBox(formatContext, fp, &basic_header); /* not use */
2239 case FOURCC('c', 'l', 's', 'f'): {
2240 debug_msg(RELEASE, "MPEG4: [clsf] SIZE: [%lld]Byte\n", chunk_size);
2241 GetClassficationFromClsfTagBox(formatContext, fp, &basic_header);
2244 case FOURCC('k', 'y', 'w', 'd'): {
2245 debug_msg(RELEASE, "MPEG4: [kywd] SIZE: [%lld]Byte\n", chunk_size);
2246 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2249 case FOURCC('l', 'o', 'c', 'i'): {
2250 debug_msg(RELEASE, "MPEG4: [loci] SIZE: [%lld]Byte\n", chunk_size);
2251 GetLocationFromLociTagBox(formatContext, fp, &basic_header);
2254 /* Check smta in user data field (moov) to be compatible with android */
2255 case FOURCC('s', 'm', 't', 'a'): {
2256 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte\n", chunk_size);
2257 GetSAUTInfoFromSMTATagBox(formatContext, fp, &basic_header);
2260 /* Check cdis in user data field (moov) to be compatible with android */
2261 case FOURCC('c', 'd', 'i', 's'): {
2262 debug_msg(RELEASE, "MPEG4: [smta] SIZE: [%lld]Byte\n", chunk_size);
2263 GetValueFromCDISTagBox(formatContext, fp, &basic_header);
2266 /*/////////////////////////////////////////////////////////////// */
2267 /* Extracting ID3 Tag Data // */
2268 /*/////////////////////////////////////////////////////////////// */
2269 case FOURCC('m', 'e', 't', 'a'): {
2270 debug_msg(RELEASE, "MPEG4: [meta] SIZE: [%lld]Byte\n", chunk_size);
2271 GetTagFromMetaBox(formatContext, fp, &basic_header);
2275 case FOURCC('t', 'r', 'a', 'k'): {
2276 debug_msg(RELEASE, "MPEG4: [trak] SIZE: [%lld]Byte\n", chunk_size);
2279 case FOURCC('u', 'u', 'i', 'd'): {
2280 unsigned long uuid[4] = {0, };
2282 debug_msg(RELEASE, "MPEG4: [uuid] SIZE: [%lld]Byte\n", chunk_size);
2284 mmfile_read(fp, (unsigned char *)uuid, sizeof(uuid));
2286 if (mmfile_io_be_uint32(uuid[0]) == 0xffcc8263
2287 && mmfile_io_be_uint32(uuid[1]) == 0xf8554a93
2288 && mmfile_io_be_uint32(uuid[2]) == 0x8814587a
2289 && mmfile_io_be_uint32(uuid[3]) == 0x02521fdd) {
2291 str = (char *)malloc(basic_header.size);
2294 memset(str, 0, basic_header.size);
2295 mmfile_read(fp, (unsigned char *)str, basic_header.size);
2297 /* The block is superseded */
2298 if (strstr(str, "<GSpherical:Spherical>true</GSpherical:Spherical>"))
2299 formatContext->is_360 = 1;
2301 formatContext->is_360 = 0;
2302 /* Image can be stitched even if it is not spherical */
2303 if (formatContext->is_360 == 1) {
2304 if (strstr(str, "<GSpherical:Stitched>true</GSpherical:Stitched>"))
2305 formatContext->stitched = MMFILE_360_STITCHED;
2307 formatContext->stitched = MMFILE_360_NON_STITCHED;
2309 /* Image can be stitched or non-stitched. Usage of some 3rd value is superfluous */
2310 formatContext->stitched = MMFILE_360_NONE;
2314 debug_msg(RELEASE, "Extracting tags from UUID XML string %s\n", str);
2316 ParseSpatialVideoMetadataFromXMLString(str, formatContext);
2319 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2323 case FOURCC('m', 'd', 'i', 'a'): {
2324 debug_msg(RELEASE, "MPEG4: [mdia] SIZE: [%lld]Byte\n", chunk_size);
2327 case FOURCC('m', 'i', 'n', 'f'): {
2328 debug_msg(RELEASE, "MPEG4: [minf] SIZE: [%lld]Byte\n", chunk_size);
2331 case FOURCC('s', 't', 'b', 'l'): {
2332 debug_msg(RELEASE, "MPEG4: [stbl] SIZE: [%lld]Byte\n", chunk_size);
2335 case FOURCC('s', 't', 's', 'd'): {
2336 debug_msg(RELEASE, "MPEG4: [stsd] SIZE: [%lld]Byte\n", chunk_size);
2339 case FOURCC('m', 'p', '4', 'a'): {
2340 debug_msg(RELEASE, "MPEG4: [mp4a] SIZE: [%lld]Byte\n", chunk_size);
2341 GetSA3DInfoFromMP4ATagBox(formatContext, fp, &basic_header);
2342 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2345 case FOURCC('a', 'v', 'c', '1'): {
2346 debug_msg(RELEASE, "MPEG4: [avc1] SIZE: [%lld]Byte (offset: %lld)\n", chunk_size, basic_header.start_offset);
2347 GetVideoV2MetadataFromAvc1TagBox(formatContext, fp, &basic_header);
2351 debug_msg(RELEASE, "4CC: Not Support [%c%c%c%c]. So skip it. Size [%lld Byte]\n",
2352 ((char *)&basic_header.type)[0], ((char *)&basic_header.type)[1],
2353 ((char *)&basic_header.type)[2], ((char *)&basic_header.type)[3], chunk_size);
2354 ret = mmfile_seek(fp, basic_header.start_offset + chunk_size, SEEK_SET);
2359 if (ret == MMFILE_UTIL_FAIL) {
2360 debug_error(DEBUG, "mmfile operation is error\n");
2365 long long new_pos = mmfile_tell(fp);
2367 if ((moov_end == 0) && (new_pos <= basic_header.start_offset)) {
2368 debug_error(DEBUG, "Wrong position");
2369 ret = MMFILE_UTIL_FAIL;
2372 basic_header.start_offset = new_pos;
2381 static char *get_string(const char *buf, int buf_size, int *bytes_written)
2385 char str[512] = {0, };
2388 for (i = 0; i < buf_size; i++) {
2392 if ((q - str) >= (int)sizeof(str) - 1)
2398 if (strlen(str) > 0) {
2399 *bytes_written = strlen(str);
2407 char *rtrimN(char *pStr)
2410 pos = strlen(pStr) - 1;
2411 for (; pos >= 0; pos--) {
2412 if (pStr[pos] == 0x20) {
2419 return strdup(pStr);
2422 bool safe_atoi(char *buffer, int *si)
2428 const long sl = strtol(buffer, &end, 10);
2430 if (end == buffer) {
2431 debug_error(RELEASE, "not a decimal number");
2433 } else if ('\0' != *end) {
2434 debug_error(RELEASE, "extra characters at end of input: %s", end);
2436 } else if ((LONG_MIN == sl || LONG_MAX == sl) && (ERANGE == errno)) {
2437 debug_error(RELEASE, "out of range of type long");
2439 } else if (sl > INT_MAX) {
2440 debug_error(RELEASE, "greater than INT_MAX");
2442 } else if (sl < INT_MIN) {
2443 debug_error(RELEASE, "less than INT_MIN");
2451 static bool make_characterset_array(char ***charset_array)
2453 char *locale = MMFileUtilGetLocale();
2455 *charset_array = calloc(AV_ID3V2_MAX, sizeof(char *));
2457 if (*charset_array == NULL) {
2458 debug_error(DEBUG, "calloc failed ");
2464 if (locale != NULL) {
2465 (*charset_array)[AV_ID3V2_ISO_8859] = strdup(locale);
2467 debug_error(DEBUG, "get locale failed");
2468 (*charset_array)[AV_ID3V2_ISO_8859] = NULL;
2471 (*charset_array)[AV_ID3V2_UTF16] = strdup("UCS2");
2472 (*charset_array)[AV_ID3V2_UTF16_BE] = strdup("UTF16-BE");
2473 (*charset_array)[AV_ID3V2_UTF8] = strdup("UTF-8");
2478 static bool release_characterset_array(char **charset_array)
2482 for (i = 0; i < AV_ID3V2_MAX; i++) {
2483 if (charset_array[i] != NULL) {
2484 free(charset_array[i]);
2485 charset_array[i] = NULL;
2489 if (charset_array != NULL) {
2490 free(charset_array);
2491 charset_array = NULL;
2497 static void init_content_info(AvFileContentInfo *pInfo)
2499 pInfo->tagV2Info.bTitleMarked = false;
2500 pInfo->tagV2Info.bArtistMarked = false;
2501 pInfo->tagV2Info.bAlbumMarked = false;
2502 pInfo->tagV2Info.bAlbum_ArtistMarked = false;
2503 pInfo->tagV2Info.bYearMarked = false;
2504 pInfo->tagV2Info.bDescriptionMarked = false;
2505 pInfo->tagV2Info.bGenreMarked = false;
2506 pInfo->tagV2Info.bTrackNumMarked = false;
2507 pInfo->tagV2Info.bEncByMarked = false;
2508 pInfo->tagV2Info.bURLMarked = false;
2509 pInfo->tagV2Info.bCopyRightMarked = false;
2510 pInfo->tagV2Info.bOriginArtistMarked = false;
2511 pInfo->tagV2Info.bComposerMarked = false;
2512 pInfo->tagV2Info.bImageMarked = false;
2514 pInfo->tagV2Info.bRecDateMarked = false;
2515 pInfo->tagV2Info.bContentGroupMarked = false;
2517 pInfo->tagV2Info.bUnsyncLyricsMarked = false;
2518 pInfo->tagV2Info.bSyncLyricsMarked = false;
2519 pInfo->tagV2Info.bConductorMarked = false;
2520 pInfo->tagV2Info.bGenreUTF16 = false;
2522 pInfo->imageInfo.bURLInfo = false;
2523 pInfo->imageInfo.pImageBuf = NULL;
2524 pInfo->imageInfo.imageLen = 0;
2527 static void _mm_file_id3tag_add_bracket_at_genre(char **genre, int genre_len)
2530 char tmp_genre[6] = {0, };
2531 int tmp_genre_len = 0;
2533 if (!genre || !(*genre) || genre_len <= 0)
2536 if (!safe_atoi(*genre, &int_genre)) {
2537 debug_log(RELEASE, "genre information is not integer [%s]", *genre);
2541 debug_msg(RELEASE, "genre information is integer [%d]", int_genre);
2543 /* if the value is not kinds of genre, exit */
2544 if (int_genre < 0 || int_genre >= GENRE_COUNT)
2547 /* Change int to string with bracket like "(123)"
2548 * mm_file_id3tag_restore_content_info convert it to string
2550 memset(tmp_genre, 0, sizeof(tmp_genre));
2551 snprintf(tmp_genre, sizeof(tmp_genre), "(%d)", int_genre);
2553 tmp_genre_len = strlen(tmp_genre);
2554 if (tmp_genre_len > 0) {
2555 *genre = mmfile_realloc(*genre, sizeof(char) * (tmp_genre_len + 1));
2557 SAFE_STRLCPY(*genre, tmp_genre, tmp_genre_len + 1);
2561 static bool __id3tag_parse_PIC_format(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2563 unsigned int idx = 0;
2565 /* get the mime type of Attached PICture, it is text string */
2567 if (pTagVal[*offset] == '\0') {
2568 debug_msg(RELEASE, "The picture format of PIC is not included\n");
2572 /* init ext variable */
2573 memset(pInfo->imageInfo.imageExt, 0, sizeof(pInfo->imageInfo.imageExt));
2575 while ((idx < MP3_ID3_IMAGE_EXT_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2576 pInfo->imageInfo.imageExt[idx] = pTagVal[idx];
2585 static bool __id3tag_parse_APIC_mimetype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2587 unsigned int idx = 0;
2588 const char *MIME_PRFIX = "image/";
2590 /* get the mime type of Attached PICture, it is text string */
2592 if (pTagVal[*offset] == '\0') {
2593 pInfo->imageInfo.imgMimetypeLen = 0;
2594 debug_msg(RELEASE, "The MIME type of APIC is not included\n");
2598 /* init mimetype variable */
2599 memset(pInfo->imageInfo.imageMIMEType, 0, sizeof(pInfo->imageInfo.imageMIMEType));
2601 while ((idx < MP3_ID3_IMAGE_MIME_TYPE_MAX_LENGTH - 1) && (pTagVal[idx] != '\0')) {
2602 pInfo->imageInfo.imageMIMEType[idx] = pTagVal[idx];
2605 pInfo->imageInfo.imgMimetypeLen = idx;
2609 if (strncmp(pInfo->imageInfo.imageMIMEType, MIME_PRFIX, strlen(MIME_PRFIX)) != 0) {
2610 pInfo->imageInfo.imgMimetypeLen = 0;
2611 debug_error(DEBUG, "MIME type(%s) is not image", pInfo->imageInfo.imageMIMEType);
2615 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2616 debug_msg(RELEASE, "pTagVal[offset](%d) mimetype is not NULL terminated! realCpyFrameNum - offset(%d)\n",
2617 pTagVal[*offset], nTagLen - *offset);
2621 (*offset)++;/* end of MIME('\0', 1byte) */
2622 debug_msg(RELEASE, "after scaning Mime type offset(%d) value!\n", *offset);
2627 static void __id3tag_parse_APIC_pictype(AvFileContentInfo *pInfo, unsigned char *pTagVal, int *offset)
2629 /* get the picture type of Attached PICture, it is 1byte(0xff) */
2631 if (pTagVal[*offset] < AV_ID3V2_PICTURE_TYPE_MAX)
2632 pInfo->imageInfo.pictureType = pTagVal[*offset];
2634 debug_msg(RELEASE, "APIC image has invalid picture type(0x%x)\n", pTagVal[*offset]);
2636 (*offset)++;/* PictureType(1byte) */
2637 debug_msg(RELEASE, "after scaning PictureType(%d) offset(%d) value!\n", pInfo->imageInfo.pictureType, *offset);
2640 static void __id3tag_parse_APIC_desc(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet, int *offset)
2642 /* get the description of Attached PICture, it is text string */
2645 unsigned int tag_len = 0;
2646 unsigned int desc_len = 0;
2647 char *tmp_desc = NULL;
2649 if (pTagVal[*offset] == 0x0) {
2650 pInfo->imageInfo.imgDesLen = 0;
2651 debug_msg(RELEASE, "The description of APIC is not included!!!\n");
2656 if (pTagVal[*offset + idx] == '\0') {
2657 if (nTagLen < (*offset + idx)) {
2658 debug_error(DEBUG, "End of APIC Tag %d %d %d\n", nTagLen, *offset, idx);
2661 /* check end of image description */
2662 if ((pTagVal[*offset + idx + 1] == gTagJPEGHeader[0]) ||
2663 (pTagVal[*offset + idx + 1] == gTagPNGHeader[0])) {
2664 debug_msg(RELEASE, "length of description (%d)", idx);
2671 tag_len = idx + 1; /* length of description + '\0' */
2673 tmp_desc = mmfile_calloc(1, sizeof(char) * tag_len);
2675 memcpy(tmp_desc, pTagVal + *offset, tag_len);
2677 /* convert description */
2678 pInfo->imageInfo.imageDescription = mmfile_string_convert(tmp_desc, tag_len, "UTF-8", pCharSet, NULL, &desc_len);
2679 pInfo->imageInfo.imgDesLen = (int)desc_len;
2680 mmfile_free(tmp_desc);
2681 debug_msg(RELEASE, "new_desc %s(%d)\n", pInfo->imageInfo.imageDescription, pInfo->imageInfo.imgDesLen);
2685 if ((pTagVal[*offset] != '\0') || (nTagLen <= *offset)) {
2686 debug_msg(RELEASE, "pTagVal[offset](%d) description is not NULL terminated! realCpyFrameNum - offset(%d)\n",
2687 pTagVal[*offset], nTagLen - *offset);
2690 (*offset)++; /* end of desceription(1byte) */
2693 static void __id3tag_parse_APIC_picture(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, int *offset)
2695 /* get the picture of Attached PICture, it is binary data */
2697 /* some content has useless '\0' in front of picture data */
2698 while (pTagVal[*offset] == '\0') {
2702 debug_msg(RELEASE, "after scaning APIC description offset(%d) value!\n", *offset);
2704 if (nTagLen <= *offset) {
2705 debug_msg(RELEASE, "No APIC image!! realCpyFrameNum(%d) - offset(%d)\n", nTagLen, *offset);
2709 pInfo->imageInfo.imageLen = nTagLen - *offset;
2710 pInfo->imageInfo.pImageBuf = mmfile_malloc(pInfo->imageInfo.imageLen + 1);
2712 if (pInfo->imageInfo.pImageBuf) {
2713 memcpy(pInfo->imageInfo.pImageBuf, pTagVal + *offset, pInfo->imageInfo.imageLen);
2714 pInfo->imageInfo.pImageBuf[pInfo->imageInfo.imageLen] = 0;
2717 /* if mimetype is "-->", image date has an URL */
2718 if (IS_INCLUDE_URL(pInfo->imageInfo.imageMIMEType))
2719 pInfo->imageInfo.bURLInfo = true;
2722 static void _mm_file_id3tag_parse_PIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2724 /* current position to read pTagVal */
2727 debug_fenter(RELEASE);
2729 if (!__id3tag_parse_PIC_format(pInfo, pTagVal, &offset))
2730 debug_msg(RELEASE, "PIC is not valid\n");
2732 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2733 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2734 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2736 debug_fleave(RELEASE);
2739 static bool _mm_file_id3tag_parse_APIC(AvFileContentInfo *pInfo, unsigned char *pTagVal, int nTagLen, const char *pCharSet)
2743 debug_fenter(RELEASE);
2745 if (!__id3tag_parse_APIC_mimetype(pInfo, pTagVal, nTagLen, &offset)) {
2746 debug_msg(RELEASE, "APIC is not valid\n");
2750 __id3tag_parse_APIC_pictype(pInfo, pTagVal, &offset);
2751 __id3tag_parse_APIC_desc(pInfo, pTagVal, nTagLen, pCharSet, &offset);
2752 __id3tag_parse_APIC_picture(pInfo, pTagVal, nTagLen, &offset);
2754 debug_fleave(RELEASE);
2759 static char *_mm_file_string_convert_v224(const char *strTag, int nTagLen, const int nEncodingType, const char *strCharSet, int *npStrLen)
2761 char *new_tag = NULL;
2765 if (nEncodingType == AV_ID3V2_UTF8) {
2766 new_tag = g_strdup(strTag);
2770 *npStrLen = strlen(new_tag);
2772 new_tag = mmfile_string_convert((const char *)strTag, nTagLen, "UTF-8", strCharSet, NULL, (unsigned int *)npStrLen);
2779 bool mm_file_id3tag_parse_v110(AvFileContentInfo *pInfo, unsigned char *buffer)
2781 const char *locale = MMFileUtilGetLocale();
2782 char *pFullStr = NULL;
2784 debug_msg(RELEASE, "ID3tag v110--------------------------------------------------------------\n");
2786 if (pInfo->tagV2Info.bTitleMarked == false) {
2787 pFullStr = mmfile_string_convert((const char *)&buffer[3], MP3_ID3_TITLE_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->titleLen);
2788 if (pFullStr != NULL) {
2789 pInfo->pTitle = rtrimN(pFullStr);
2793 debug_msg(RELEASE, "pInfo->pTitle returned =(%s), pInfo->titleLen(%d)\n", pInfo->pTitle, pInfo->titleLen);
2796 if (pInfo->tagV2Info.bArtistMarked == false) {
2797 pFullStr = mmfile_string_convert((const char *)&buffer[33], MP3_ID3_ARTIST_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->artistLen);
2798 if (pFullStr != NULL) {
2799 pInfo->pArtist = rtrimN(pFullStr);
2803 debug_msg(RELEASE, "pInfo->pArtist returned =(%s), pInfo->artistLen(%d)\n", pInfo->pArtist, pInfo->artistLen);
2806 if (pInfo->tagV2Info.bAlbumMarked == false) {
2807 pFullStr = mmfile_string_convert((const char *)&buffer[63], MP3_ID3_ALBUM_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->albumLen);
2808 if (pFullStr != NULL) {
2809 pInfo->pAlbum = rtrimN(pFullStr);
2813 debug_msg(RELEASE, "pInfo->pAlbum returned =(%s), pInfo->albumLen(%d)\n", pInfo->pAlbum, pInfo->albumLen);
2816 if (pInfo->tagV2Info.bYearMarked == false) {
2818 pInfo->pYear = mmfile_string_convert((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->yearLen);
2820 debug_msg(RELEASE, "pInfo->pYear returned =(%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
2822 if (pInfo->pYear == NULL) { /*Use same logic with ffmpeg*/
2823 pInfo->pYear = get_string((const char *)&buffer[93], MP3_ID3_YEAR_LENGTH, (int *)&pInfo->yearLen);
2824 debug_msg(RELEASE, "pInfo->pYear returned =(%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
2828 if (pInfo->tagV2Info.bDescriptionMarked == false) {
2829 pInfo->pComment = mmfile_string_convert((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, "UTF-8", locale, NULL, (unsigned int *)&pInfo->commentLen);
2830 debug_msg(RELEASE, "pInfo->pComment returned =(%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
2832 if (pInfo->pComment == NULL) { /*Use same logic with ffmpeg*/
2833 pInfo->pComment = get_string((const char *)&buffer[97], MP3_ID3_DESCRIPTION_LENGTH, (int *)&pInfo->commentLen);
2834 debug_msg(RELEASE, "pInfo->pComment returned =(%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
2838 if (pInfo->tagV2Info.bTrackNumMarked == false) {
2839 pInfo->pTrackNum = mmfile_malloc(5);
2840 if (pInfo->pTrackNum != NULL) {
2841 pInfo->pTrackNum[4] = 0;
2842 snprintf(pInfo->pTrackNum, 4, "%04d", (int)buffer[126]);
2843 pInfo->tracknumLen = strlen(pInfo->pTrackNum);
2845 debug_msg(RELEASE, "pInfo->pTrackNum returned =(%s), pInfo->tracknumLen(%d)\n", pInfo->pTrackNum, pInfo->tracknumLen);
2849 if (pInfo->tagV2Info.bGenreMarked == false) {
2850 pInfo->genre = buffer[127];
2851 debug_msg(RELEASE, "pInfo->genre returned genre number (%d)\n", pInfo->genre);
2858 bool mm_file_id3tag_parse_v222(AvFileContentInfo *pInfo, unsigned char *buffer)
2860 unsigned long taglen = 0;
2861 unsigned long needToloopv2taglen;
2862 unsigned long oneFrameLen = 0;
2863 unsigned long v2numOfFrames = 0;
2864 unsigned long curPos = 0;
2866 unsigned char *pExtContent = NULL;
2867 unsigned long purelyFramelen = 0;
2868 unsigned int encodingOffSet = 0;
2869 int realCpyFrameNum = 0, tmp = 0;
2871 int textEncodingType = 0;
2873 char **charset_array = NULL;
2875 make_characterset_array(&charset_array);
2877 init_content_info(pInfo);
2879 taglen = pInfo->tagV2Info.tagLen;
2880 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
2881 curPos = MP3_TAGv2_HEADER_LEN;
2883 debug_msg(RELEASE, "ID3tag v222--------------------------------------------------------------\n");
2885 if (needToloopv2taglen - MP3_TAGv2_22_TXT_HEADER_LEN > MP3_TAGv2_22_TXT_HEADER_LEN) {
2887 while (needToloopv2taglen > MP3_TAGv2_22_TXT_HEADER_LEN) {
2888 if ((buffer[curPos] < '0' || buffer[curPos] > 'Z') || (buffer[curPos + 1] < '0' || buffer[curPos + 1] > 'Z')
2889 || (buffer[curPos + 2] < '0' || buffer[curPos + 2] > 'Z'))
2892 memcpy(CompTmp, &buffer[curPos], 3);
2895 oneFrameLen = MP3_TAGv2_22_TXT_HEADER_LEN;
2896 oneFrameLen += (unsigned long)buffer[3 + curPos] << 16 | (unsigned long)buffer[4 + curPos] << 8
2897 | (unsigned long)buffer[5 + curPos];
2898 if (oneFrameLen > taglen - curPos)
2900 purelyFramelen = oneFrameLen - MP3_TAGv2_22_TXT_HEADER_LEN;
2901 curPos += MP3_TAGv2_22_TXT_HEADER_LEN;
2903 if (oneFrameLen > MP3_TAGv2_22_TXT_HEADER_LEN && purelyFramelen <= taglen - curPos) {
2904 curPos += purelyFramelen;
2906 if (buffer[curPos - purelyFramelen] == 0x00) {
2908 textEncodingType = AV_ID3V2_ISO_8859;
2909 } else if (buffer[curPos - purelyFramelen] == 0x01) {
2911 textEncodingType = AV_ID3V2_UTF16;
2914 /*in order to deliver valid string to MP */
2915 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen))
2918 if (encodingOffSet < purelyFramelen) {
2919 realCpyFrameNum = purelyFramelen - encodingOffSet;
2920 mmfile_free(pExtContent);
2921 pExtContent = mmfile_malloc(realCpyFrameNum + 3);
2923 if (pExtContent == NULL) {
2924 debug_error(DEBUG, "out of memory for pExtContent\n");
2928 memset(pExtContent, '\0', realCpyFrameNum + 3);
2930 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
2932 if (realCpyFrameNum > 0) {
2933 if (strncmp((char *)CompTmp, "TT2", 3) == 0 && pInfo->tagV2Info.bTitleMarked == false) {
2934 pInfo->pTitle = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->titleLen);
2935 debug_msg(RELEASE, "pInfo->pTitle returned = (%s), pInfo->titleLen(%d)\n", pInfo->pTitle, pInfo->titleLen);
2936 pInfo->tagV2Info.bTitleMarked = true;
2937 } else if (strncmp((char *)CompTmp, "TP1", 3) == 0 && pInfo->tagV2Info.bArtistMarked == false) {
2938 pInfo->pArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->artistLen);
2939 debug_msg(RELEASE, "pInfo->pArtist returned = (%s), pInfo->artistLen(%d)\n", pInfo->pArtist, pInfo->artistLen);
2940 pInfo->tagV2Info.bArtistMarked = true;
2941 } else if (strncmp((char *)CompTmp, "TP2", 3) == 0 && pInfo->tagV2Info.bAlbum_ArtistMarked == false) {
2942 pInfo->pAlbum_Artist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->album_artistLen);
2943 debug_msg(RELEASE, "pInfo->pAlbum_Artist returned = (%s), pInfo->album_artistLen(%d)\n", pInfo->pAlbum_Artist, pInfo->album_artistLen);
2944 pInfo->tagV2Info.bAlbum_ArtistMarked = true;
2945 } else if (strncmp((char *)CompTmp, "TP3", 3) == 0 && pInfo->tagV2Info.bConductorMarked == false) {
2946 pInfo->pConductor = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->conductorLen);
2947 debug_msg(RELEASE, "pInfo->pConductor returned = (%s), pInfo->conductorLen(%d)\n", pInfo->pConductor, pInfo->conductorLen);
2948 pInfo->tagV2Info.bConductorMarked = true;
2949 } else if (strncmp((char *)CompTmp, "TAL", 3) == 0 && pInfo->tagV2Info.bAlbumMarked == false) {
2950 pInfo->pAlbum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->albumLen);
2951 debug_msg(RELEASE, "pInfo->pAlbum returned = (%s), pInfo->albumLen(%d)\n", pInfo->pAlbum, pInfo->albumLen);
2952 pInfo->tagV2Info.bAlbumMarked = true;
2953 } else if (strncmp((char *)CompTmp, "TYE", 3) == 0 && pInfo->tagV2Info.bYearMarked == false) {
2954 pInfo->pYear = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->yearLen);
2955 debug_msg(RELEASE, "pInfo->pYear returned = (%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
2956 pInfo->tagV2Info.bYearMarked = true;
2957 } else if (strncmp((char *)CompTmp, "COM", 3) == 0 && pInfo->tagV2Info.bDescriptionMarked == false) {
2958 /*skip language data! */
2959 if (realCpyFrameNum > 4) {
2960 realCpyFrameNum -= 4;
2963 /*pExtContent[tmp+1] value should't have encoding value */
2964 if (pExtContent[tmp] > 0x20 && (pExtContent[tmp - 1] == 0x00 || pExtContent[tmp - 1] == 0x01)) {
2965 if (pExtContent[tmp - 1] == 0x00)
2966 textEncodingType = AV_ID3V2_ISO_8859;
2968 textEncodingType = AV_ID3V2_UTF16;
2970 pInfo->pComment = mmfile_string_convert((char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->commentLen);
2971 debug_msg(RELEASE, "pInfo->pComment returned = (%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
2972 pInfo->tagV2Info.bDescriptionMarked = true;
2974 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: failed to get Comment Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
2977 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
2981 } else if (strncmp((char *)CompTmp, "TCO", 3) == 0 && pInfo->tagV2Info.bGenreMarked == false) {
2982 pInfo->pGenre = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->genreLen);
2983 debug_msg(RELEASE, "pInfo->pGenre returned = (%s), pInfo->genreLen(%d)\n", pInfo->pGenre, pInfo->genreLen);
2984 _mm_file_id3tag_add_bracket_at_genre(&pInfo->pGenre, pInfo->genreLen);
2985 pInfo->tagV2Info.bGenreMarked = true;
2986 } else if (strncmp((char *)CompTmp, "TRK", 3) == 0 && pInfo->tagV2Info.bTrackNumMarked == false) {
2987 pInfo->pTrackNum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tracknumLen);
2988 debug_msg(RELEASE, "pInfo->pTrackNum returned = (%s), pInfo->tracknumLen(%d)\n", pInfo->pTrackNum, pInfo->tracknumLen);
2989 pInfo->tagV2Info.bTrackNumMarked = true;
2990 } else if (strncmp((char *)CompTmp, "TEN", 3) == 0 && pInfo->tagV2Info.bEncByMarked == false) {
2991 pInfo->pEncBy = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->encbyLen);
2992 debug_msg(RELEASE, "pInfo->pEncBy returned = (%s), pInfo->encbyLen(%d)\n", pInfo->pEncBy, pInfo->encbyLen);
2993 pInfo->tagV2Info.bEncByMarked = true;
2994 } else if (strncmp((char *)CompTmp, "WXX", 3) == 0 && pInfo->tagV2Info.bURLMarked == false) {
2995 if (realCpyFrameNum > 4) {
2996 /*skip language data! */
2997 realCpyFrameNum -= 4;
3000 /*pExtContent[tmp+1] value should't have null value */
3001 if (pExtContent[tmp] > 0x20 && (pExtContent[tmp - 1] == 0x00 || pExtContent[tmp - 1] == 0x01)) {
3002 if (pExtContent[tmp - 1] == 0x00)
3003 textEncodingType = AV_ID3V2_ISO_8859;
3005 textEncodingType = AV_ID3V2_UTF16;
3007 pInfo->pURL = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->urlLen);
3008 debug_msg(RELEASE, "pInfo->pURL returned = (%s), pInfo->urlLen(%d)\n", pInfo->pURL, pInfo->urlLen);
3009 pInfo->tagV2Info.bURLMarked = true;
3011 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: failed to get URL Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3014 debug_msg(RELEASE, "mmf_file_id3tag_parse_v222: URL info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3017 } else if (strncmp((char *)CompTmp, "TCR", 3) == 0 && pInfo->tagV2Info.bCopyRightMarked == false) {
3018 pInfo->pCopyright = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->copyrightLen);
3019 debug_msg(RELEASE, "pInfo->pCopyright returned = (%s), pInfo->copyrightLen(%d)\n", pInfo->pCopyright, pInfo->copyrightLen);
3020 pInfo->tagV2Info.bCopyRightMarked = true;
3021 } else if (strncmp((char *)CompTmp, "TOA", 3) == 0 && pInfo->tagV2Info.bOriginArtistMarked == false) {
3022 pInfo->pOriginArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->originartistLen);
3023 debug_msg(RELEASE, "pInfo->pOriginArtist returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pOriginArtist, pInfo->originartistLen);
3024 pInfo->tagV2Info.bOriginArtistMarked = true;
3025 } else if (strncmp((char *)CompTmp, "TCM", 3) == 0 && pInfo->tagV2Info.bComposerMarked == false) {
3026 pInfo->pComposer = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->composerLen);
3027 debug_msg(RELEASE, "pInfo->pComposer returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pComposer, pInfo->composerLen);
3028 pInfo->tagV2Info.bComposerMarked = true;
3029 } else if (strncmp((char *)CompTmp, "TRD", 3) == 0 && pInfo->tagV2Info.bRecDateMarked == false) {
3030 pInfo->pRecDate = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->recdateLen);
3031 debug_msg(RELEASE, "pInfo->pRecDate returned = (%s), pInfo->recdateLen(%d)\n", pInfo->pRecDate, pInfo->recdateLen);
3032 pInfo->tagV2Info.bRecDateMarked = true;
3033 } else if (strncmp((char *)CompTmp, "PIC", 3) == 0 && pInfo->tagV2Info.bImageMarked == false && realCpyFrameNum <= 2000000) {
3034 debug_msg(DEBUG, "text encoding %d \n", textEncodingType);
3035 _mm_file_id3tag_parse_PIC(pInfo, pExtContent, realCpyFrameNum, (const char*)charset_array[textEncodingType]);
3036 pInfo->tagV2Info.bImageMarked = true;
3042 curPos += purelyFramelen;
3043 if (purelyFramelen != 0)
3044 needToloopv2taglen = MP3_TAGv2_22_TXT_HEADER_LEN;
3047 mmfile_free(pExtContent);
3048 memset(CompTmp, 0, 4);
3049 if (curPos < taglen) {
3050 needToloopv2taglen -= oneFrameLen;
3053 needToloopv2taglen = MP3_TAGv2_22_TXT_HEADER_LEN;
3056 realCpyFrameNum = 0;
3057 textEncodingType = 0;
3063 release_characterset_array(charset_array);
3073 bool mm_file_id3tag_parse_v223(AvFileContentInfo *pInfo, unsigned char *buffer)
3075 unsigned long taglen = 0;
3076 unsigned long needToloopv2taglen;
3077 unsigned long oneFrameLen = 0;
3078 unsigned long v2numOfFrames = 0;
3079 unsigned long curPos = 0;
3081 unsigned char *pExtContent = NULL;
3082 unsigned long purelyFramelen = 0;
3083 unsigned int encodingOffSet = 0;
3084 int realCpyFrameNum = 0, tmp = 0;
3085 unsigned int textEncodingType = 0;
3086 char **charset_array = NULL;
3088 make_characterset_array(&charset_array);
3090 init_content_info(pInfo);
3092 taglen = pInfo->tagV2Info.tagLen;
3093 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
3094 curPos = MP3_TAGv2_HEADER_LEN;
3096 debug_msg(RELEASE, "ID3tag v223--------------------------------------------------------------\n");
3098 /* check Extended Header */
3099 if (buffer[5] & 0x40) {
3100 /* if extended header exists, skip it*/
3101 int extendedHeaderLen = (unsigned long)buffer[10] << 21 | (unsigned long)buffer[11] << 14 | (unsigned long)buffer[12] << 7 | (unsigned long)buffer[13];
3103 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d\n", extendedHeaderLen);
3105 if (extendedHeaderLen > (int)(taglen - curPos)) {
3106 debug_error(DEBUG, "extended header too long.\n");
3108 curPos += extendedHeaderLen;
3113 if (needToloopv2taglen - MP3_TAGv2_23_TXT_HEADER_LEN > MP3_TAGv2_23_TXT_HEADER_LEN) {
3115 while (needToloopv2taglen > MP3_TAGv2_23_TXT_HEADER_LEN) {
3116 if ((buffer[curPos] < '0' || buffer[curPos] > 'Z') || (buffer[curPos + 1] < '0' || buffer[curPos + 1] > 'Z')
3117 || (buffer[curPos + 2] < '0' || buffer[curPos + 2] > 'Z') || (buffer[curPos + 3] < '0' || buffer[curPos + 3] > 'Z'))
3120 memcpy(CompTmp, &buffer[curPos], 4);
3123 oneFrameLen = MP3_TAGv2_23_TXT_HEADER_LEN;
3124 oneFrameLen += (unsigned long)buffer[4 + curPos] << 24 | (unsigned long)buffer[5 + curPos] << 16
3125 | (unsigned long)buffer[6 + curPos] << 8 | (unsigned long)buffer[7 + curPos];
3127 debug_msg(RELEASE, "----------------------------------------------------------------------------------------------------\n");
3129 if (oneFrameLen > taglen - curPos)
3132 purelyFramelen = oneFrameLen - MP3_TAGv2_23_TXT_HEADER_LEN;
3133 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3135 if (oneFrameLen > MP3_TAGv2_23_TXT_HEADER_LEN && purelyFramelen <= taglen - curPos) {
3136 curPos += purelyFramelen;
3138 if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen))) {
3140 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3141 textEncodingType = AV_ID3V2_UTF16;
3142 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen))) {
3144 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3145 textEncodingType = AV_ID3V2_UTF16_BE;
3146 } else if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen + 1))) {
3148 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3149 textEncodingType = AV_ID3V2_UTF16;
3150 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen + 1))) {
3152 debug_msg(RELEASE, "this text string(%s) encoded by UTF16 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3153 textEncodingType = AV_ID3V2_UTF16_BE;
3155 if (buffer[curPos - purelyFramelen + encodingOffSet] == 0x00) {
3156 debug_msg(RELEASE, "encodingOffset will be set to 1\n");
3159 debug_msg(RELEASE, "Finding encodingOffset\n");
3161 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen)) /* text string encoded by ISO-8859-1 */
3164 textEncodingType = AV_ID3V2_ISO_8859;
3165 debug_msg(RELEASE, "this text string(%s) encoded by ISO-8859-1 encodingOffSet(%d)\n", CompTmp, encodingOffSet);
3168 mmfile_free(pExtContent);
3170 if (encodingOffSet < purelyFramelen) {
3171 realCpyFrameNum = purelyFramelen - encodingOffSet;
3172 pExtContent = mmfile_malloc(realCpyFrameNum + 3);
3174 if (pExtContent == NULL) {
3175 debug_msg(DEBUG, "pExtContent malloc failed\n");
3179 memset(pExtContent, '\0', realCpyFrameNum + 3);
3181 if (textEncodingType != AV_ID3V2_UTF16 && textEncodingType != AV_ID3V2_UTF16_BE) {
3182 if (CompTmp[0] == 'T' || (strcmp(CompTmp, "APIC") == 0)) {
3183 debug_msg(RELEASE, "get the new text ecoding type\n");
3184 textEncodingType = buffer[curPos - purelyFramelen + encodingOffSet - 1];
3188 if (textEncodingType > AV_ID3V2_MAX) {
3189 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%d], FRAME[%s]\n", textEncodingType, (char *)CompTmp);
3193 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
3194 if (realCpyFrameNum > 0) {
3195 if (strncmp((char *)CompTmp, "TIT2", 4) == 0 && pInfo->tagV2Info.bTitleMarked == false) {
3196 pInfo->pTitle = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->titleLen);
3197 debug_msg(RELEASE, "pInfo->pTitle returned = (%s), pInfo->titleLen(%d)\n", pInfo->pTitle, pInfo->titleLen);
3198 pInfo->tagV2Info.bTitleMarked = true;
3200 } else if (strncmp((char *)CompTmp, "TPE1", 4) == 0 && pInfo->tagV2Info.bArtistMarked == false) {
3201 pInfo->pArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->artistLen);
3202 debug_msg(RELEASE, "pInfo->pArtist returned = (%s), pInfo->artistLen(%d)\n", pInfo->pArtist, pInfo->artistLen);
3203 pInfo->tagV2Info.bArtistMarked = true;
3204 } else if (strncmp((char *)CompTmp, "TPE2", 4) == 0 && pInfo->tagV2Info.bAlbum_ArtistMarked == false) {
3205 pInfo->pAlbum_Artist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->album_artistLen);
3206 debug_msg(RELEASE, "pInfo->pAlbum_Artist returned = (%s), pInfo->album_artistLen(%d)\n", pInfo->pAlbum_Artist, pInfo->album_artistLen);
3207 pInfo->tagV2Info.bAlbum_ArtistMarked = true;
3208 } else if (strncmp((char *)CompTmp, "TPE3", 4) == 0 && pInfo->tagV2Info.bConductorMarked == false) {
3209 pInfo->pConductor = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->conductorLen);
3210 debug_msg(RELEASE, "pInfo->pConductor returned = (%s), pInfo->conductorLen(%d)\n", pInfo->pConductor, pInfo->conductorLen);
3211 pInfo->tagV2Info.bConductorMarked = true;
3212 } else if (strncmp((char *)CompTmp, "TALB", 4) == 0 && pInfo->tagV2Info.bAlbumMarked == false) {
3213 pInfo->pAlbum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->albumLen);
3214 debug_msg(RELEASE, "pInfo->pAlbum returned = (%s), pInfo->albumLen(%d)\n", pInfo->pAlbum, pInfo->albumLen);
3215 pInfo->tagV2Info.bAlbumMarked = true;
3216 } else if (strncmp((char *)CompTmp, "TYER", 4) == 0 && pInfo->tagV2Info.bYearMarked == false) {
3217 pInfo->pYear = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->yearLen);
3218 debug_msg(RELEASE, "pInfo->pYear returned = (%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
3219 pInfo->tagV2Info.bYearMarked = true;
3220 } else if (strncmp((char *)CompTmp, "COMM", 4) == 0 && pInfo->tagV2Info.bDescriptionMarked == false) {
3221 if (realCpyFrameNum > 3) {
3222 realCpyFrameNum -= 3;
3225 /*pExtContent[tmp+1] value should't have encoding value */
3226 if (pExtContent[tmp] == 0x00 || pExtContent[tmp] == 0xFF || pExtContent[tmp] == 0xFE) {
3227 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3228 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3229 realCpyFrameNum -= 4;
3233 if (IS_ENCODEDBY_UTF16(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3234 realCpyFrameNum -= 2;
3236 textEncodingType = AV_ID3V2_UTF16;
3237 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3238 realCpyFrameNum -= 2;
3240 textEncodingType = AV_ID3V2_UTF16_BE;
3241 } else if (IS_ENCODEDBY_UTF16(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3242 realCpyFrameNum -= 3;
3244 textEncodingType = AV_ID3V2_UTF16;
3245 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3246 realCpyFrameNum -= 3;
3248 textEncodingType = AV_ID3V2_UTF16_BE;
3250 debug_msg(RELEASE, "pInfo->pComment Never Get Here!!\n");
3253 while ((pExtContent[tmp] < 0x20) && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3257 textEncodingType = AV_ID3V2_ISO_8859;
3260 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3261 pInfo->pComment = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->commentLen);
3263 debug_msg(RELEASE, "failed to get Comment Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3264 pInfo->commentLen = 0;
3267 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3268 pInfo->commentLen = 0;
3272 debug_msg(RELEASE, "pInfo->pComment returned = (%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
3273 pInfo->tagV2Info.bDescriptionMarked = true;
3274 } else if (strncmp((char *)CompTmp, "SYLT", 4) == 0 && pInfo->tagV2Info.bSyncLyricsMarked == false) {
3277 int copy_start_pos = tmp;
3278 AvSynclyricsInfo *synclyrics_info = NULL;
3279 GList *synclyrics_info_list = NULL;
3281 if (realCpyFrameNum > 5) {
3282 realCpyFrameNum -= 5;
3285 /*pExtContent[tmp+1] value should't have encoding value */
3286 if (pExtContent[tmp] == 0x00 || pExtContent[tmp] == 0xFF || pExtContent[tmp] == 0xFE) {
3287 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3288 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3289 realCpyFrameNum -= 4;
3293 if (IS_ENCODEDBY_UTF16(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3294 realCpyFrameNum -= 2;
3296 textEncodingType = AV_ID3V2_UTF16;
3297 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3298 realCpyFrameNum -= 2;
3300 textEncodingType = AV_ID3V2_UTF16_BE;
3301 } else if (IS_ENCODEDBY_UTF16(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3302 realCpyFrameNum -= 3;
3304 textEncodingType = AV_ID3V2_UTF16;
3305 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3306 realCpyFrameNum -= 3;
3308 textEncodingType = AV_ID3V2_UTF16_BE;
3310 debug_msg(RELEASE, "pInfo->pSyncLyrics Never Get Here!!\n");
3313 while ((pExtContent[tmp] < 0x20) && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3317 textEncodingType = AV_ID3V2_ISO_8859;
3320 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3322 if (realCpyFrameNum < MMFILE_SYNC_LYRIC_INFO_MIN_LEN) {
3323 debug_msg(RELEASE, "failed to get Synchronised lyrics Info realCpyFramNum(%d)\n", realCpyFrameNum);
3324 pInfo->syncLyricsNum = 0;
3326 if (textEncodingType == AV_ID3V2_UTF16) {
3327 debug_warning(DEBUG, "[AV_ID3V2_UTF16] not implemented\n");
3328 } else if (textEncodingType == AV_ID3V2_UTF16_BE) {
3329 debug_warning(DEBUG, "[AV_ID3V2_UTF16_BE] not implemented\n");
3331 for (idx = 0; idx < realCpyFrameNum; idx++) {
3332 if (pExtContent[tmp + idx] == 0x00) {
3333 synclyrics_info = (AvSynclyricsInfo *)malloc(sizeof(AvSynclyricsInfo));
3335 if (synclyrics_info != NULL) {
3336 if (textEncodingType == AV_ID3V2_UTF8) {
3337 synclyrics_info->lyric_info = mmfile_malloc(copy_len + 1);
3338 if (synclyrics_info->lyric_info != NULL) {
3339 memset(synclyrics_info->lyric_info, 0, copy_len + 1);
3340 memcpy(synclyrics_info->lyric_info, pExtContent + copy_start_pos, copy_len);
3341 synclyrics_info->lyric_info[copy_len] = '\0';
3344 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);
3347 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];
3349 copy_start_pos = tmp + idx + 1;
3350 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);
3352 synclyrics_info_list = g_list_append(synclyrics_info_list, synclyrics_info);
3357 pInfo->pSyncLyrics = synclyrics_info_list;
3358 pInfo->syncLyricsNum = g_list_length(pInfo->pSyncLyrics);
3362 debug_msg(RELEASE, "failed to get Synchronised lyrics Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3363 pInfo->syncLyricsNum = 0;
3366 debug_msg(RELEASE, "Synchronised lyrics too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3367 pInfo->syncLyricsNum = 0;
3371 //debug_msg(RELEASE, "pInfo->pSyncLyrics returned = (%s), pInfo->syncLyricsNum(%d)\n", pInfo->pSyncLyrics, pInfo->syncLyricsNum);
3372 debug_msg(RELEASE, "pInfo->syncLyricsNum(%d)\n", pInfo->syncLyricsNum);
3373 pInfo->tagV2Info.bSyncLyricsMarked = true;
3374 } else if (strncmp((char *)CompTmp, "USLT", 4) == 0 && pInfo->tagV2Info.bUnsyncLyricsMarked == false) {
3375 char *lang_info = strndup((char *)pExtContent, 3);
3377 if (realCpyFrameNum > 3) {
3378 realCpyFrameNum -= 3;
3381 /*find start of lyrics */
3383 if (pExtContent[tmp] == 0x00) {
3384 if (pExtContent[tmp + 1] == 0x00) {
3385 realCpyFrameNum -= 2;
3395 /*pExtContent[tmp+1] value should't have encoding value */
3396 debug_msg(RELEASE, "tpExtContent[%d] %x\n", tmp, pExtContent[tmp]);
3398 if (pExtContent[tmp] == 0x00 || pExtContent[tmp] == 0xFF || pExtContent[tmp] == 0xFE) {
3399 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3400 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3401 realCpyFrameNum -= 4;
3405 if (IS_ENCODEDBY_UTF16(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3406 realCpyFrameNum -= 2;
3408 textEncodingType = AV_ID3V2_UTF16;
3409 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp) && (realCpyFrameNum > 2)) {
3410 realCpyFrameNum -= 2;
3412 textEncodingType = AV_ID3V2_UTF16_BE;
3413 } else if (IS_ENCODEDBY_UTF16(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3414 realCpyFrameNum -= 3;
3416 textEncodingType = AV_ID3V2_UTF16;
3417 } else if (IS_ENCODEDBY_UTF16_R(pExtContent + tmp + 1) && (realCpyFrameNum > 3)) {
3418 realCpyFrameNum -= 3;
3420 textEncodingType = AV_ID3V2_UTF16_BE;
3422 debug_msg(RELEASE, "pInfo->pUnsyncLyrics Never Get Here!!\n");
3425 while ((pExtContent[tmp] < 0x20) && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3429 textEncodingType = AV_ID3V2_ISO_8859;
3432 char *char_set = NULL;
3434 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3436 if (textEncodingType == AV_ID3V2_ISO_8859) {
3437 if (lang_info != NULL && !g_ascii_strcasecmp(lang_info, "KOR")) {
3438 char_set = strdup("EUC-KR");
3440 char_set = mmfile_get_charset((const char *)&pExtContent[tmp]);
3442 mmfile_free(lang_info);
3445 if (char_set == NULL) {
3446 pInfo->pUnsyncLyrics = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->unsynclyricsLen);
3448 pInfo->pUnsyncLyrics = mmfile_string_convert((const char *)&pExtContent[tmp], realCpyFrameNum, "UTF-8", char_set, NULL, (unsigned int *)&pInfo->unsynclyricsLen);
3449 mmfile_free(char_set);
3452 debug_msg(RELEASE, "failed to get Unsynchronised lyrics Info tmp(%d), purelyFramelen - encodingOffSet(%lu)\n", tmp, purelyFramelen - encodingOffSet);
3453 pInfo->unsynclyricsLen = 0;
3456 debug_msg(RELEASE, "Unsynchronised lyrics too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3457 pInfo->unsynclyricsLen = 0;
3461 debug_msg(RELEASE, "pInfo->pUnsyncLyrics returned = (%s), pInfo->unsynclyricsLen(%d)\n", pInfo->pUnsyncLyrics, pInfo->unsynclyricsLen);
3462 pInfo->tagV2Info.bUnsyncLyricsMarked = true;
3463 mmfile_free(lang_info);
3464 } else if (strncmp((char *)CompTmp, "TCON", 4) == 0 && pInfo->tagV2Info.bGenreMarked == false) {
3465 pInfo->pGenre = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->genreLen);
3466 debug_msg(RELEASE, "pInfo->pGenre returned = (%s), pInfo->genreLen(%d)\n", pInfo->pGenre, pInfo->genreLen);
3467 _mm_file_id3tag_add_bracket_at_genre(&pInfo->pGenre, pInfo->genreLen);
3468 pInfo->tagV2Info.bGenreMarked = true;
3469 } else if (strncmp((char *)CompTmp, "TRCK", 4) == 0 && pInfo->tagV2Info.bTrackNumMarked == false) {
3470 pInfo->pTrackNum = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->tracknumLen);
3471 debug_msg(RELEASE, "pInfo->pTrackNum returned = (%s), pInfo->tracknumLen(%d)\n", pInfo->pTrackNum, pInfo->tracknumLen);
3472 pInfo->tagV2Info.bTrackNumMarked = true;
3473 } else if (strncmp((char *)CompTmp, "TPOS", 4) == 0 && pInfo->tagV2Info.bPartOfASetMarked == false) {
3474 pInfo->pPartOfASet = mmfile_string_convert((const char*)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int*)&pInfo->partofsetLen);
3475 debug_msg(RELEASE, "pInfo->pPartOfASet returned = (%s), pInfo->partofsetLen(%d)\n", pInfo->pPartOfASet, pInfo->partofsetLen);
3476 pInfo->tagV2Info.bPartOfASetMarked = true;
3477 } else if (strncmp((char *)CompTmp, "TENC", 4) == 0 && pInfo->tagV2Info.bEncByMarked == false) {
3478 pInfo->pEncBy = mmfile_string_convert((char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->encbyLen);
3479 debug_msg(RELEASE, "pInfo->pEncBy returned = (%s), pInfo->encbyLen(%d)\n", pInfo->pEncBy, pInfo->encbyLen);
3480 pInfo->tagV2Info.bEncByMarked = true;
3481 } else if (strncmp((char *)CompTmp, "WXXX", 4) == 0 && pInfo->tagV2Info.bURLMarked == false) {
3482 pInfo->pURL = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->urlLen);
3483 debug_msg(RELEASE, "pInfo->pURL returned = (%s), pInfo->urlLen(%d)\n", pInfo->pURL, pInfo->urlLen);
3484 pInfo->tagV2Info.bURLMarked = true;
3485 } else if (strncmp((char *)CompTmp, "TCOP", 4) == 0 && pInfo->tagV2Info.bCopyRightMarked == false) {
3486 pInfo->pCopyright = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->copyrightLen);
3487 debug_msg(RELEASE, "pInfo->pCopyright returned = (%s), pInfo->copyrightLen(%d)\n", pInfo->pCopyright, pInfo->copyrightLen);
3488 pInfo->tagV2Info.bCopyRightMarked = true;
3489 } else if (strncmp((char *)CompTmp, "TOPE", 4) == 0 && pInfo->tagV2Info.bOriginArtistMarked == false) {
3490 pInfo->pOriginArtist = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->originartistLen);
3491 debug_msg(RELEASE, "pInfo->pOriginArtist returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pOriginArtist, pInfo->originartistLen);
3492 pInfo->tagV2Info.bOriginArtistMarked = true;
3493 } else if (strncmp((char *)CompTmp, "TCOM", 4) == 0 && pInfo->tagV2Info.bComposerMarked == false) {
3494 pInfo->pComposer = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->composerLen);
3495 debug_msg(RELEASE, "pInfo->pComposer returned = (%s), pInfo->composerLen(%d)\n", pInfo->pComposer, pInfo->composerLen);
3496 pInfo->tagV2Info.bComposerMarked = true;
3497 } else if (strncmp((char *)CompTmp, "TRDA", 4) == 0 && pInfo->tagV2Info.bRecDateMarked == false) {
3498 pInfo->pRecDate = mmfile_string_convert((const char *)pExtContent, realCpyFrameNum, "UTF-8", charset_array[textEncodingType], NULL, (unsigned int *)&pInfo->recdateLen);
3499 debug_msg(RELEASE, "pInfo->pRecDate returned = (%s), pInfo->recdateLen(%d)\n", pInfo->pRecDate, pInfo->recdateLen);
3500 pInfo->tagV2Info.bRecDateMarked = true;
3501 } else if (strncmp((char *)CompTmp, "APIC", 4) == 0 && pInfo->tagV2Info.bImageMarked == false && realCpyFrameNum <= 2000000) {
3502 debug_msg(DEBUG, "text encoding %d \n", textEncodingType);
3503 if (!_mm_file_id3tag_parse_APIC(pInfo, pExtContent, realCpyFrameNum, (const char*)charset_array[textEncodingType]))
3505 pInfo->tagV2Info.bImageMarked = true;
3507 debug_msg(RELEASE, "CompTmp(%s) This Frame ID currently not Supports!!\n", CompTmp);
3512 debug_msg(RELEASE, "All of the pExtContent Values are NULL\n");
3515 curPos += purelyFramelen;
3516 if (purelyFramelen != 0)
3517 needToloopv2taglen = MP3_TAGv2_23_TXT_HEADER_LEN;
3519 debug_msg(RELEASE, "This Frame's size is Zero! purelyFramelen(%lu)\n", purelyFramelen);
3522 mmfile_free(pExtContent);
3523 memset(CompTmp, 0, 4);
3525 if (curPos < taglen) {
3526 needToloopv2taglen -= oneFrameLen;
3529 needToloopv2taglen = MP3_TAGv2_23_TXT_HEADER_LEN;
3532 realCpyFrameNum = 0;
3533 textEncodingType = 0;
3539 release_characterset_array(charset_array);
3549 bool mm_file_id3tag_parse_v224(AvFileContentInfo *pInfo, unsigned char *buffer)
3551 unsigned long taglen = 0;
3552 unsigned long needToloopv2taglen;
3553 unsigned long oneFrameLen = 0;
3554 unsigned long v2numOfFrames = 0;
3555 unsigned long curPos = 0;
3557 unsigned char *pExtContent = NULL;
3558 unsigned long purelyFramelen = 0;
3559 unsigned int encodingOffSet = 0;
3560 int realCpyFrameNum = 0, tmp = 0;
3561 unsigned int textEncodingType = 0;
3562 char **charset_array = NULL;
3564 make_characterset_array(&charset_array);
3566 init_content_info(pInfo);
3568 taglen = pInfo->tagV2Info.tagLen;
3569 needToloopv2taglen = taglen - MP3_TAGv2_HEADER_LEN;
3570 curPos = MP3_TAGv2_HEADER_LEN;
3572 debug_msg(RELEASE, "ID3tag v224--------------------------------------------------------------\n");
3574 /* check Extended Header */
3575 if (buffer[5] & 0x40) {
3576 /* if extended header exists, skip it*/
3577 int extendedHeaderLen = (unsigned long)buffer[10] << 21 | (unsigned long)buffer[11] << 14 | (unsigned long)buffer[12] << 7 | (unsigned long)buffer[13];
3579 debug_msg(RELEASE, "--------------- extendedHeaderLen = %d\n", extendedHeaderLen);
3581 if (extendedHeaderLen > (int)(taglen - curPos)) {
3582 debug_error(DEBUG, "extended header too long.\n");
3584 curPos += extendedHeaderLen;
3588 if (needToloopv2taglen - MP3_TAGv2_23_TXT_HEADER_LEN > MP3_TAGv2_23_TXT_HEADER_LEN) {
3590 while (needToloopv2taglen > MP3_TAGv2_23_TXT_HEADER_LEN) {
3591 if ((buffer[curPos] < '0' || buffer[curPos] > 'Z') || (buffer[curPos + 1] < '0' || buffer[curPos + 1] > 'Z')
3592 || (buffer[curPos + 2] < '0' || buffer[curPos + 2] > 'Z') || (buffer[curPos + 3] < '0' || buffer[curPos + 3] > 'Z'))
3595 memcpy(CompTmp, &buffer[curPos], 4);
3598 oneFrameLen = MP3_TAGv2_23_TXT_HEADER_LEN;
3599 oneFrameLen += (unsigned long)buffer[4 + curPos] << 21 | (unsigned long)buffer[5 + curPos] << 14
3600 | (unsigned long)buffer[6 + curPos] << 7 | (unsigned long)buffer[7 + curPos];
3601 if (oneFrameLen > taglen - curPos)
3604 purelyFramelen = oneFrameLen - MP3_TAGv2_23_TXT_HEADER_LEN;
3605 curPos += MP3_TAGv2_23_TXT_HEADER_LEN;
3607 debug_msg(RELEASE, "-----------------------------------------------------------------------------------\n");
3609 if (oneFrameLen > MP3_TAGv2_23_TXT_HEADER_LEN && purelyFramelen <= taglen - curPos) {
3610 curPos += purelyFramelen;
3612 /*in case of UTF 16 encoding */
3613 /*buffer+(curPos-purelyFramelen) data should '0x01' but in order to expansion, we don't accurately check the value. */
3614 if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen))) {
3616 textEncodingType = AV_ID3V2_UTF16;
3617 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen))) {
3619 textEncodingType = AV_ID3V2_UTF16_BE;
3620 } else if (IS_ENCODEDBY_UTF16(buffer + (curPos - purelyFramelen + 1))) {
3622 textEncodingType = AV_ID3V2_UTF16;
3623 } else if (IS_ENCODEDBY_UTF16_R(buffer + (curPos - purelyFramelen + 1))) {
3625 textEncodingType = AV_ID3V2_UTF16_BE;
3627 /*in case of UTF-16 BE encoding */
3628 if (buffer[curPos - purelyFramelen] == 0x02) {
3630 while ((buffer[curPos - purelyFramelen + encodingOffSet] == '\0') && (encodingOffSet < purelyFramelen))
3631 encodingOffSet++;/*null skip! */
3632 textEncodingType = AV_ID3V2_UTF16_BE;
3634 /*in case of UTF8 encoding */
3635 else if (buffer[curPos - purelyFramelen] == 0x03) {
3637 while ((buffer[curPos - purelyFramelen + encodingOffSet] == '\0') && (encodingOffSet < purelyFramelen))
3638 encodingOffSet++;/*null skip! */
3639 textEncodingType = AV_ID3V2_UTF8;
3641 /*in case of ISO-8859-1 encoding */
3643 /*buffer+(curPos-purelyFramelen) data should 0x00 but in order to expansion, we don't accurately check the value. */
3645 while ((buffer[curPos - purelyFramelen + encodingOffSet] < 0x20) && (encodingOffSet < purelyFramelen))
3646 encodingOffSet++;/*less than 0x20 value skip! */
3647 textEncodingType = AV_ID3V2_ISO_8859;
3651 mmfile_free(pExtContent);
3653 if (encodingOffSet < purelyFramelen) {
3654 realCpyFrameNum = purelyFramelen - encodingOffSet;
3655 pExtContent = mmfile_malloc(realCpyFrameNum + 3);
3657 if (pExtContent == NULL) {
3658 debug_error(DEBUG, "out of memoryu for id3tag parse\n");
3662 memset(pExtContent, '\0', realCpyFrameNum + 3);
3664 if (textEncodingType != AV_ID3V2_UTF16 && textEncodingType != AV_ID3V2_UTF16_BE) {
3665 if (CompTmp[0] == 'T' || (strcmp(CompTmp, "APIC") == 0)) {
3666 debug_msg(RELEASE, "get the new text ecoding type\n");
3667 textEncodingType = buffer[curPos - purelyFramelen + encodingOffSet - 1];
3671 if (textEncodingType > AV_ID3V2_MAX) {
3672 debug_msg(DEBUG, "WRONG ENCOIDNG TYPE [%d], FRAME[%s]\n", textEncodingType, (char *)CompTmp);
3676 memcpy(pExtContent, &buffer[curPos - purelyFramelen + encodingOffSet], purelyFramelen - encodingOffSet);
3678 if (realCpyFrameNum > 0) {
3679 if (strncmp((char *)CompTmp, "TIT2", 4) == 0 && pInfo->tagV2Info.bTitleMarked == false) {
3680 pInfo->pTitle = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->titleLen);
3681 debug_msg(RELEASE, "pInfo->pTitle returned = (%s), pInfo->titleLen(%d)\n", pInfo->pTitle, pInfo->titleLen);
3682 pInfo->tagV2Info.bTitleMarked = true;
3684 } else if (strncmp((char *)CompTmp, "TPE1", 4) == 0 && pInfo->tagV2Info.bArtistMarked == false) {
3685 pInfo->pArtist = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->artistLen);
3686 debug_msg(RELEASE, "pInfo->pArtist returned = (%s), pInfo->artistLen(%d)\n", pInfo->pArtist, pInfo->artistLen);
3687 pInfo->tagV2Info.bArtistMarked = true;
3688 } else if (strncmp((char *)CompTmp, "TPE2", 4) == 0 && pInfo->tagV2Info.bAlbum_ArtistMarked == false) {
3689 pInfo->pAlbum_Artist = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->album_artistLen);
3690 debug_msg(RELEASE, "pInfo->pAlbum_Artist returned = (%s), pInfo->album_artistLen(%d)\n", pInfo->pAlbum_Artist, pInfo->album_artistLen);
3691 pInfo->tagV2Info.bAlbum_ArtistMarked = true;
3692 } else if (strncmp((char *)CompTmp, "TPE3", 4) == 0 && pInfo->tagV2Info.bConductorMarked == false) {
3693 pInfo->pConductor = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->conductorLen);
3694 debug_msg(RELEASE, "pInfo->pConductor returned = (%s), pInfo->conductorLen(%d)\n", pInfo->pConductor, pInfo->conductorLen);
3695 pInfo->tagV2Info.bConductorMarked = true;
3696 } else if (strncmp((char *)CompTmp, "TALB", 4) == 0 && pInfo->tagV2Info.bAlbumMarked == false) {
3697 pInfo->pAlbum = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->albumLen);
3698 debug_msg(RELEASE, "pInfo->pAlbum returned = (%s), pInfo->albumLen(%d)\n", pInfo->pAlbum, pInfo->albumLen);
3699 pInfo->tagV2Info.bAlbumMarked = true;
3700 } 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 */
3701 pInfo->pYear = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->yearLen);
3702 debug_msg(RELEASE, "pInfo->pYear returned = (%s), pInfo->yearLen(%d)\n", pInfo->pYear, pInfo->yearLen);
3703 pInfo->tagV2Info.bYearMarked = true;
3704 } else if (strncmp((char *)CompTmp, "COMM", 4) == 0 && pInfo->tagV2Info.bDescriptionMarked == false) {
3705 if (realCpyFrameNum > 3) {
3706 realCpyFrameNum -= 3;
3709 if (textEncodingType == AV_ID3V2_UTF16 || textEncodingType == AV_ID3V2_UTF16_BE) {
3710 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3711 realCpyFrameNum -= 4;
3715 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3716 realCpyFrameNum -= 2;
3718 textEncodingType = AV_ID3V2_UTF16;
3720 debug_msg(RELEASE, "pInfo->pComment Never Get Here!!\n");
3722 } else if (textEncodingType == AV_ID3V2_UTF8) {
3723 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3727 textEncodingType = AV_ID3V2_UTF8;
3729 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3733 textEncodingType = AV_ID3V2_ISO_8859;
3736 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3738 pInfo->pComment = _mm_file_string_convert_v224((const char *)&pExtContent[tmp], realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->commentLen);
3740 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3745 debug_msg(RELEASE, "pInfo->pComment returned = (%s), pInfo->commentLen(%d)\n", pInfo->pComment, pInfo->commentLen);
3746 pInfo->tagV2Info.bDescriptionMarked = true;
3747 } else if (strncmp((char *)CompTmp, "SYLT", 4) == 0 && pInfo->tagV2Info.bSyncLyricsMarked == false) {
3750 int copy_start_pos = tmp;
3751 AvSynclyricsInfo *synclyrics_info = NULL;
3752 GList *synclyrics_info_list = NULL;
3754 if (realCpyFrameNum > 5) {
3755 realCpyFrameNum -= 5;
3758 if (textEncodingType == AV_ID3V2_UTF16 || textEncodingType == AV_ID3V2_UTF16_BE) {
3759 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3760 realCpyFrameNum -= 4;
3764 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3765 realCpyFrameNum -= 2;
3767 textEncodingType = AV_ID3V2_UTF16;
3769 debug_msg(RELEASE, "pInfo->pSyncLyrics Never Get Here!!\n");
3771 } else if (textEncodingType == AV_ID3V2_UTF8) {
3772 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3776 textEncodingType = AV_ID3V2_UTF8;
3778 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3782 textEncodingType = AV_ID3V2_ISO_8859;
3785 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3787 if (realCpyFrameNum < MMFILE_SYNC_LYRIC_INFO_MIN_LEN) {
3788 debug_msg(RELEASE, "failed to get Synchronised lyrics Info realCpyFramNum(%d)\n", realCpyFrameNum);
3789 pInfo->syncLyricsNum = 0;
3791 if (textEncodingType == AV_ID3V2_UTF16) {
3792 debug_warning(DEBUG, "[AV_ID3V2_UTF16] not implemented\n");
3793 } else if (textEncodingType == AV_ID3V2_UTF16_BE) {
3794 debug_warning(DEBUG, "[AV_ID3V2_UTF16_BE] not implemented\n");
3796 for (idx = 0; idx < realCpyFrameNum; idx++) {
3797 if (pExtContent[tmp + idx] == 0x00) {
3798 synclyrics_info = (AvSynclyricsInfo *)mmfile_malloc(sizeof(AvSynclyricsInfo));
3799 if (synclyrics_info) {
3800 if (textEncodingType == AV_ID3V2_UTF8) {
3801 synclyrics_info->lyric_info = mmfile_malloc(copy_len + 1);
3802 if (synclyrics_info->lyric_info) {
3803 memset(synclyrics_info->lyric_info, 0, copy_len + 1);
3804 memcpy(synclyrics_info->lyric_info, pExtContent + copy_start_pos, copy_len);
3805 synclyrics_info->lyric_info[copy_len] = '\0';
3808 synclyrics_info->lyric_info = mmfile_string_convert((const char *)&pExtContent[copy_start_pos], copy_len, "UTF-8", charset_array[textEncodingType], NULL, NULL);
3811 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];
3813 copy_start_pos = tmp + idx + 1;
3814 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);
3816 synclyrics_info_list = g_list_append(synclyrics_info_list, synclyrics_info);
3821 pInfo->pSyncLyrics = synclyrics_info_list;
3822 pInfo->syncLyricsNum = g_list_length(pInfo->pSyncLyrics);
3826 debug_msg(RELEASE, "SyncLyrics info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3830 pInfo->tagV2Info.bSyncLyricsMarked = true;
3831 } else if (strncmp((char *)CompTmp, "USLT", 4) == 0 && pInfo->tagV2Info.bUnsyncLyricsMarked == false) {
3832 if (realCpyFrameNum > 3) {
3833 realCpyFrameNum -= 3;
3836 if (textEncodingType == AV_ID3V2_UTF16 || textEncodingType == AV_ID3V2_UTF16_BE) {
3837 while ((NEWLINE_OF_UTF16(pExtContent + tmp) || NEWLINE_OF_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 4) {
3838 realCpyFrameNum -= 4;
3842 if ((IS_ENCODEDBY_UTF16(pExtContent + tmp) || IS_ENCODEDBY_UTF16_R(pExtContent + tmp)) && realCpyFrameNum > 2) {
3843 realCpyFrameNum -= 2;
3845 textEncodingType = AV_ID3V2_UTF16;
3847 debug_msg(RELEASE, "pInfo->pUnsyncLyrics Never Get Here!!\n");
3849 } else if (textEncodingType == AV_ID3V2_UTF8) {
3850 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3854 textEncodingType = AV_ID3V2_UTF8;
3856 while (pExtContent[tmp] < 0x20 && (tmp < realCpyFrameNum)) { /* text string encoded by ISO-8859-1 */
3860 textEncodingType = AV_ID3V2_ISO_8859;
3863 debug_msg(RELEASE, "tmp(%d) textEncodingType(%d), realCpyFrameNum(%d)\n", tmp, textEncodingType, realCpyFrameNum);
3865 pInfo->pUnsyncLyrics = _mm_file_string_convert_v224((const char *)&pExtContent[tmp], realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->unsynclyricsLen);
3867 debug_msg(RELEASE, "Description info too small to parse realCpyFrameNum(%d)\n", realCpyFrameNum);
3872 debug_msg(RELEASE, "pInfo->pUnsyncLyrics returned = (%s), pInfo->unsynclyricsLen(%d)\n", pInfo->pUnsyncLyrics, pInfo->unsynclyricsLen);
3873 pInfo->tagV2Info.bDescriptionMarked = true;
3874 } else if (strncmp((char *)CompTmp, "TCON", 4) == 0 && pInfo->tagV2Info.bGenreMarked == false) {
3875 pInfo->pGenre = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->genreLen);
3876 debug_msg(RELEASE, "pInfo->pGenre returned = (%s), pInfo->genreLen(%d)\n", pInfo->pGenre, pInfo->genreLen);
3877 _mm_file_id3tag_add_bracket_at_genre(&pInfo->pGenre, pInfo->genreLen);
3878 pInfo->tagV2Info.bGenreMarked = true;
3879 } else if (strncmp((char *)CompTmp, "TRCK", 4) == 0 && pInfo->tagV2Info.bTrackNumMarked == false) {
3880 pInfo->pTrackNum = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->tracknumLen);
3881 debug_msg(RELEASE, "pInfo->pTrackNum returned = (%s), pInfo->tracknumLen(%d)\n", pInfo->pTrackNum, pInfo->tracknumLen);
3882 pInfo->tagV2Info.bTrackNumMarked = true;
3883 } else if (strncmp((char *)CompTmp, "TPOS", 4) == 0 && pInfo->tagV2Info.bPartOfASetMarked == false) {
3884 pInfo->pPartOfASet = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->partofsetLen);
3885 debug_msg(RELEASE, "pInfo->pPartOfASet returned = (%s), pInfo->partofsetLen(%d)\n", pInfo->pPartOfASet, pInfo->partofsetLen);
3886 pInfo->tagV2Info.bPartOfASetMarked = true;
3887 } else if (strncmp((char *)CompTmp, "TENC", 4) == 0 && pInfo->tagV2Info.bEncByMarked == false) {
3888 pInfo->pEncBy = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->encbyLen);
3889 debug_msg(RELEASE, "pInfo->pEncBy returned = (%s), pInfo->encbyLen(%d)\n", pInfo->pEncBy, pInfo->encbyLen);
3890 pInfo->tagV2Info.bEncByMarked = true;
3891 } else if (strncmp((char *)CompTmp, "WXXX", 4) == 0 && pInfo->tagV2Info.bURLMarked == false) {
3892 pInfo->pURL = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->urlLen);
3893 debug_msg(RELEASE, "pInfo->pURL returned = (%s), pInfo->urlLen(%d)\n", pInfo->pURL, pInfo->urlLen);
3894 pInfo->tagV2Info.bURLMarked = true;
3895 } else if (strncmp((char *)CompTmp, "TCOP", 4) == 0 && pInfo->tagV2Info.bCopyRightMarked == false) {
3896 pInfo->pCopyright = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->copyrightLen);
3897 debug_msg(RELEASE, "pInfo->pCopyright returned = (%s), pInfo->copyrightLen(%d)\n", pInfo->pCopyright, pInfo->copyrightLen);
3898 pInfo->tagV2Info.bCopyRightMarked = true;
3899 } else if (strncmp((char *)CompTmp, "TOPE", 4) == 0 && pInfo->tagV2Info.bOriginArtistMarked == false) {
3900 pInfo->pOriginArtist = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->originartistLen);
3901 debug_msg(RELEASE, "pInfo->pOriginArtist returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pOriginArtist, pInfo->originartistLen);
3902 pInfo->tagV2Info.bOriginArtistMarked = true;
3903 } else if (strncmp((char *)CompTmp, "TCOM", 4) == 0 && pInfo->tagV2Info.bComposerMarked == false) {
3904 pInfo->pComposer = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->composerLen);
3905 debug_msg(RELEASE, "pInfo->pComposer returned = (%s), pInfo->originartistLen(%d)\n", pInfo->pComposer, pInfo->composerLen);
3906 pInfo->tagV2Info.bComposerMarked = true;
3907 } else if (strncmp((char *)CompTmp, "TDRC", 4) == 0 && pInfo->tagV2Info.bRecDateMarked == false) { /*TYER(year) and TRDA are replaced by the TDRC */
3908 pInfo->pRecDate = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->recdateLen);
3909 debug_msg(RELEASE, "pInfo->pRecDate returned = (%s), pInfo->recdateLen(%d)\n", pInfo->pRecDate, pInfo->recdateLen);
3910 pInfo->tagV2Info.bRecDateMarked = true;
3911 } else if (strncmp((char *)CompTmp, "TIT1", 4) == 0 && pInfo->tagV2Info.bContentGroupMarked == false) {
3912 pInfo->pContentGroup = _mm_file_string_convert_v224((const char *)pExtContent, realCpyFrameNum, textEncodingType, charset_array[textEncodingType], &pInfo->contentGroupLen);
3913 debug_msg(RELEASE, "pInfo->pContentGroup returned = (%s), pInfo->contentGroupLen(%d)\n", pInfo->pContentGroup, pInfo->contentGroupLen);
3914 pInfo->tagV2Info.bContentGroupMarked = true;
3915 } else if (strncmp((char *)CompTmp, "APIC", 4) == 0 && pInfo->tagV2Info.bImageMarked == false && realCpyFrameNum <= 2000000) {
3916 debug_msg(DEBUG, "text encoding %d \n", textEncodingType);
3917 if (!_mm_file_id3tag_parse_APIC(pInfo, pExtContent, realCpyFrameNum, (const char*)charset_array[textEncodingType]))
3919 pInfo->tagV2Info.bImageMarked = true;
3921 debug_msg(RELEASE, "CompTmp(%s) This Frame ID currently not Supports!!\n", CompTmp);
3926 debug_msg(RELEASE, "mmf_file_id3tag_parse_v224: All of the pExtContent Values are NULL\n");
3930 curPos += purelyFramelen;
3931 if (purelyFramelen != 0)
3932 needToloopv2taglen = MP3_TAGv2_23_TXT_HEADER_LEN;
3935 mmfile_free(pExtContent);
3936 memset(CompTmp, 0, 4);
3937 if (curPos < taglen) {
3938 needToloopv2taglen -= oneFrameLen;
3941 needToloopv2taglen = MP3_TAGv2_23_TXT_HEADER_LEN;
3945 realCpyFrameNum = 0;
3946 textEncodingType = 0;
3952 release_characterset_array(charset_array);
3962 void mm_file_id3tag_restore_content_info(AvFileContentInfo *pInfo)
3964 char *mpegAudioGenre = NULL/*, *tmpGenreForV1Tag = NULL*/;
3965 bool bAdditionGenre = false /*, bMpegAudioFrame = false*/;
3966 int mpegAudioFileLen = 0, idv2IntGenre = 148/*, tmpinx = 0, tmpinx2=0*/;
3967 unsigned char genre = pInfo->genre;
3969 /* for Genre Info */
3970 if (pInfo->tagV2Info.bGenreMarked == false) {
3971 if (pInfo->bV1tagFound == true) {
3972 debug_msg(RELEASE, "Genre: %d\n", genre);
3977 if (MpegAudio_Genre[genre] != NULL) {
3978 pInfo->genreLen = strlen(MpegAudio_Genre[genre]);
3979 if (pInfo->genreLen > 0) {
3980 /* Give space for NULL character. Hence added "+1" */
3981 pInfo->pGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen + 1));
3982 if (pInfo->pGenre) {
3983 SAFE_STRLCPY(pInfo->pGenre, MpegAudio_Genre[genre], pInfo->genreLen + 1);
3988 debug_msg(RELEASE, "Genre was not Found.\n");
3990 } else if (pInfo->tagV2Info.bGenreMarked == true) {
3991 if (pInfo->genreLen && pInfo->tagV2Info.bGenreUTF16) {
3992 pInfo->pGenre[pInfo->genreLen + 1] = '\0';
3993 mpegAudioGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen * AV_WM_LOCALCODE_SIZE_MAX + 1));
3995 debug_msg(RELEASE, "pInfo->genreLen size is Zero Or not UTF16 code! genreLen[%d] genre[%s]\n", pInfo->genreLen, pInfo->pGenre);
3997 if (pInfo->pGenre) {
3998 pInfo->genreLen = strlen(pInfo->pGenre);
3999 mpegAudioGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen + 1));
4000 if (mpegAudioGenre != NULL) {
4001 SAFE_STRLCPY(mpegAudioGenre, pInfo->pGenre, pInfo->genreLen + 1);
4004 pInfo->genreLen = 0;
4008 mmfile_free(pInfo->pGenre);
4011 if (mpegAudioGenre != NULL) {
4014 * (XXX) XXX is 0 - 148
4016 pInfo->genreLen = strlen(mpegAudioGenre);
4017 if (pInfo->genreLen >= 3 &&
4018 mpegAudioGenre[0] == '(' && mpegAudioGenre[pInfo->genreLen - 1] == ')') {
4019 bAdditionGenre = true;
4020 for (mpegAudioFileLen = 1; mpegAudioFileLen <= pInfo->genreLen - 2; mpegAudioFileLen++) {
4021 if (mpegAudioGenre[mpegAudioFileLen] < '0' || mpegAudioGenre[mpegAudioFileLen] > '9') {
4022 bAdditionGenre = false;
4028 if (bAdditionGenre == true) {
4029 idv2IntGenre = atoi(mpegAudioGenre + 1);
4031 if (idv2IntGenre > 147 || idv2IntGenre < 0)
4034 if (MpegAudio_Genre[idv2IntGenre] != NULL) {
4035 pInfo->genreLen = strlen(MpegAudio_Genre[idv2IntGenre]);
4036 if (pInfo->genreLen > 0) {
4037 /* Give space for NULL character. Hence added "+1" */
4038 pInfo->pGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen + 1));
4039 if (pInfo->pGenre) {
4040 SAFE_STRLCPY(pInfo->pGenre, MpegAudio_Genre[idv2IntGenre], pInfo->genreLen + 1);
4044 debug_msg(RELEASE, "pInfo->pGenre = %s\n", pInfo->pGenre);
4045 } else if (bAdditionGenre == false && pInfo->genreLen > 0) {
4050 /* Give space for NULL character. Hence added "+1" */
4051 pInfo->pGenre = mmfile_malloc(sizeof(char) * (pInfo->genreLen + 1));
4052 if (pInfo->pGenre) {
4053 SAFE_STRLCPY(pInfo->pGenre, mpegAudioGenre, pInfo->genreLen + 1);
4055 debug_msg(RELEASE, "pInfo->pGenre = %s, pInfo->genreLen = %d\n", pInfo->pGenre, pInfo->genreLen);
4057 debug_msg(RELEASE, "Failed to \"(...)\" value to genre = %s\n", pInfo->pGenre);
4060 debug_msg(RELEASE, "mpegAudioGenre = %s\n", mpegAudioGenre);
4062 mmfile_free(mpegAudioGenre);
4065 debug_msg(RELEASE, "Neither ID3 v1 nor v2 info doesn't have Genre Info.\n");
4070 void mm_file_free_synclyrics_list(GList *synclyrics_list)
4074 AvSynclyricsInfo *synclyrics_info = NULL;
4076 if (synclyrics_list == NULL) {
4080 list_len = g_list_length(synclyrics_list);
4081 for (idx = 0; idx < list_len; idx++) {
4082 synclyrics_info = g_list_nth_data(synclyrics_list, idx);
4084 if (synclyrics_info != NULL) {
4085 mmfile_free(synclyrics_info->lyric_info);
4086 mmfile_free(synclyrics_info);
4090 if (synclyrics_list != NULL) {
4091 g_list_free(synclyrics_list);
4092 synclyrics_list = NULL;