demux: libmp4: fix regression in trun reading (fix #19170)
[vlc.git] / modules / demux / mp4 / meta.c
blob9464c842dede60a705bcf42cde9c62e6fc4cc550
1 /*****************************************************************************
2 * meta.c: mp4 meta handling
3 *****************************************************************************
4 * Copyright (C) 2001-2004, 2010, 2014 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
24 #include "mp4.h"
25 #include "../meta_engine/ID3Genres.h" /* for ATOM_gnre */
26 #include "languages.h"
28 #include <vlc_meta.h>
29 #include <vlc_charset.h>
31 #include "../meta_engine/ID3Tag.h"
32 #include "../meta_engine/ID3Meta.h"
34 #include <assert.h>
36 static const struct
38 const uint32_t xa9_type;
39 const vlc_meta_type_t meta_type;
40 } xa9typetometa[] = {
41 { ATOM_0x40PRM, vlc_meta_EncodedBy }, /* Adobe Premiere */
42 { ATOM_0x40PRQ, vlc_meta_EncodedBy }, /* Adobe Qt */
43 { ATOM_0xa9nam, vlc_meta_Title }, /* Full name */
44 { ATOM_0xa9aut, vlc_meta_Artist },
45 { ATOM_0xa9ART, vlc_meta_Artist },
46 { ATOM_0xa9cpy, vlc_meta_Copyright },
47 { ATOM_0xa9day, vlc_meta_Date }, /* Creation Date */
48 { ATOM_0xa9des, vlc_meta_Description }, /* Description */
49 { ATOM_0xa9gen, vlc_meta_Genre }, /* Genre */
50 { ATOM_0xa9alb, vlc_meta_Album }, /* Album */
51 { ATOM_0xa9trk, vlc_meta_TrackNumber }, /* Track */
52 { ATOM_0xa9cmt, vlc_meta_Description }, /* Comment */
53 { ATOM_0xa9url, vlc_meta_URL }, /* URL */
54 { ATOM_0xa9too, vlc_meta_EncodedBy }, /* Encoder Tool */
55 { ATOM_0xa9enc, vlc_meta_EncodedBy }, /* Encoded By */
56 { ATOM_0xa9pub, vlc_meta_Publisher },
57 { ATOM_0xa9dir, vlc_meta_Director },
58 { ATOM_desc, vlc_meta_Description },
59 { ATOM_MCPS, vlc_meta_EncodedBy }, /* Cleaner Pro */
60 { ATOM_aART, vlc_meta_AlbumArtist },
63 static const struct
65 const uint32_t xa9_type;
66 const char metadata[25];
67 } xa9typetoextrameta[] = {
68 { ATOM_0xa9wrt, N_("Writer") },
69 { ATOM_0xa9com, N_("Composer") },
70 { ATOM_0xa9prd, N_("Producer") },
71 { ATOM_0xa9inf, N_("Information") },
72 { ATOM_0xa9dis, N_("Disclaimer") },
73 { ATOM_0xa9req, N_("Requirements") },
74 { ATOM_0xa9fmt, N_("Original Format") },
75 { ATOM_0xa9dsa, N_("Display Source As") },
76 { ATOM_0xa9hst, N_("Host Computer") },
77 { ATOM_0xa9prf, N_("Performers") },
78 { ATOM_0xa9ope, N_("Original Performer") },
79 { ATOM_0xa9src, N_("Providers Source Content") },
80 { ATOM_0xa9wrn, N_("Warning") },
81 { ATOM_0xa9swr, N_("Software") },
82 { ATOM_0xa9lyr, N_("Lyrics") },
83 { ATOM_0xa9mak, N_("Record Company") },
84 { ATOM_0xa9mod, N_("Model") },
85 { ATOM_0xa9PRD, N_("Product") },
86 { ATOM_0xa9grp, N_("Grouping") },
87 { ATOM_0xa9gen, N_("Genre") },
88 { ATOM_0xa9st3, N_("Sub-Title") },
89 { ATOM_0xa9arg, N_("Arranger") },
90 { ATOM_0xa9ard, N_("Art Director") },
91 { ATOM_0xa9cak, N_("Copyright Acknowledgement") },
92 { ATOM_0xa9con, N_("Conductor") },
93 { ATOM_0xa9des, N_("Song Description") },
94 { ATOM_0xa9lnt, N_("Liner Notes") },
95 { ATOM_0xa9phg, N_("Phonogram Rights") },
96 { ATOM_0xa9pub, N_("Publisher") },
97 { ATOM_0xa9sne, N_("Sound Engineer") },
98 { ATOM_0xa9sol, N_("Soloist") },
99 { ATOM_0xa9thx, N_("Thanks") },
100 { ATOM_0xa9xpd, N_("Executive Producer") },
101 { ATOM_aART, N_("Album Artist") },
102 { ATOM_flvr, N_("Encoding Params") },
103 { ATOM_vndr, N_("Vendor") },
104 { ATOM_xid_, N_("Catalog Number") },
105 { ATOM_gshh, "YouTube Host" },
106 { ATOM_gspm, "YouTube Ping Message" },
107 { ATOM_gspu, "YouTube Ping Url" },
108 { ATOM_gssd, "YouTube Source Data" },
109 { ATOM_gsst, "YouTube Start Time" },
110 { ATOM_gstd, "YouTube Track Duration" },
113 static const struct
115 const char *psz_naming;
116 const vlc_meta_type_t meta_type;
117 } com_apple_quicktime_tometa[] = {
118 { "displayname", vlc_meta_NowPlaying },
119 { "software", vlc_meta_EncodedBy },
120 { "Encoded_With", vlc_meta_EncodedBy },
121 { "album", vlc_meta_Album },
122 { "artist", vlc_meta_Artist },
123 { "comment", vlc_meta_Description },
124 { "description", vlc_meta_Description },
125 { "copyright", vlc_meta_Copyright },
126 { "creationdate", vlc_meta_Date },
127 { "director", vlc_meta_Director },
128 { "genre", vlc_meta_Genre },
129 { "publisher", vlc_meta_Publisher },
132 static const struct
134 const char *psz_naming;
135 const char *psz_metadata;
136 } com_apple_quicktime_toextrameta[] = {
137 { "information", N_("Information") },
138 { "keywords", N_("Keywords") },
139 { "make", N_("Vendor") },
142 inline static char * StringConvert( const MP4_Box_data_data_t *p_data )
144 if ( !p_data || !p_data->i_blob )
145 return NULL;
147 switch( p_data->e_wellknowntype )
149 case DATA_WKT_UTF8:
150 case DATA_WKT_UTF8_SORT:
151 return FromCharset( "UTF-8", p_data->p_blob, p_data->i_blob );
152 case DATA_WKT_UTF16:
153 case DATA_WKT_UTF16_SORT:
154 return FromCharset( "UTF-16BE", p_data->p_blob, p_data->i_blob );
155 case DATA_WKT_SJIS:
156 return FromCharset( "SHIFT-JIS", p_data->p_blob, p_data->i_blob );
157 default:
158 return NULL;
162 static char * ExtractString( MP4_Box_t *p_box )
164 if ( p_box->i_type == ATOM_data )
165 return StringConvert( p_box->data.p_data );
167 MP4_Box_t *p_data = MP4_BoxGet( p_box, "data" );
168 if ( p_data )
169 return StringConvert( BOXDATA(p_data) );
170 else if ( p_box->data.p_binary && p_box->data.p_binary->p_blob )
172 char *psz_utf = strndup( p_box->data.p_binary->p_blob,
173 p_box->data.p_binary->i_blob );
174 if (likely( psz_utf ))
175 EnsureUTF8( psz_utf );
176 return psz_utf;
178 else
179 return NULL;
182 static bool AppleNameToMeta( char const* name,
183 vlc_meta_type_t const** meta_type, char const** meta_key )
185 *meta_type = NULL;
186 *meta_key = NULL;
188 for( size_t i = 0; *meta_type == NULL &&
189 i < ARRAY_SIZE( com_apple_quicktime_tometa ); ++i )
191 if( !strcmp( name, com_apple_quicktime_tometa[i].psz_naming ) )
192 *meta_type = &com_apple_quicktime_tometa[i].meta_type;
195 for( size_t i = 0; *meta_key == NULL &&
196 i < ARRAY_SIZE( com_apple_quicktime_toextrameta ); ++i )
198 if( !strcmp( name, com_apple_quicktime_toextrameta[i].psz_naming ) )
199 *meta_key = com_apple_quicktime_toextrameta[i].psz_metadata;
202 return *meta_type || *meta_key;
205 static bool AtomXA9ToMeta( uint32_t i_type,
206 vlc_meta_type_t const** meta_type, char const** meta_key )
208 *meta_type = NULL;
209 *meta_key = NULL;
211 for( size_t i = 0; !*meta_type && i < ARRAY_SIZE( xa9typetometa ); ++i )
212 if( xa9typetometa[i].xa9_type == i_type )
213 *meta_type = &xa9typetometa[i].meta_type;
215 for( size_t i = 0; !*meta_key && i < ARRAY_SIZE( xa9typetoextrameta ); ++i )
216 if( xa9typetoextrameta[i].xa9_type == i_type )
217 *meta_key = xa9typetoextrameta[i].metadata;
219 return *meta_type || *meta_key;
222 static bool SetMeta( vlc_meta_t* p_meta, int i_type, char const* name, MP4_Box_t* p_box )
224 vlc_meta_type_t const* type;
225 char const* key;
227 if( ( name != NULL && !AppleNameToMeta( name, &type, &key ) ) ||
228 ( name == NULL && !AtomXA9ToMeta( i_type, &type, &key ) ) )
230 return false;
233 char* psz_utf = ExtractString( p_box );
235 if( psz_utf )
237 if( type ) vlc_meta_Set( p_meta, *type, psz_utf );
238 else vlc_meta_AddExtra( p_meta, key, psz_utf );
240 free( psz_utf );
243 return true;
246 static int ExtractIntlStrings( vlc_meta_t *p_meta, MP4_Box_t *p_box )
248 if( MP4_BoxGet( p_box, "data" ) )
249 return false;
251 vlc_meta_type_t const* meta_type;
252 char const* meta_key;
254 if( AtomXA9ToMeta( p_box->i_type, &meta_type, &meta_key ) == false )
255 return false;
257 if( p_box->p_father == NULL ||
258 p_box->p_father->i_type != ATOM_udta ||
259 p_box->data.p_binary == NULL ||
260 p_box->data.p_binary->p_blob == NULL )
262 return false;
265 vlc_meta_t* p_meta_intl = vlc_meta_New();
267 if( unlikely( !p_meta_intl ) )
268 return false;
270 char const* p_peek = p_box->data.p_binary->p_blob;
271 uint64_t i_read = p_box->data.p_binary->i_blob;
273 while( i_read >= 4 )
275 uint16_t i_len, i_lang;
277 MP4_GET2BYTES( i_len );
278 MP4_GET2BYTES( i_lang );
280 if( i_len > i_read )
281 break;
283 char charset[15] = "MACINTOSH//";
285 decodeQtLanguageCode( i_lang, charset+11, &(bool){0} );
287 if( i_lang >= 0x400 && i_lang != 0x7fff )
289 strcpy( charset, i_len < 2 || memcmp( p_peek, "\xFE\xFF", 2 )
290 ? "UTF-8" : "UTF-16BE" );
293 char* data = FromCharset( charset, p_peek, i_len );
294 if( data )
296 if( meta_type )
298 vlc_meta_Set( p_meta_intl, *meta_type, data );
300 meta_key = vlc_meta_TypeToLocalizedString( *meta_type );
301 meta_type = NULL;
303 else
305 char* key;
306 if( asprintf( &key, "%s (%s)", meta_key, charset+11 ) != -1 )
308 vlc_meta_AddExtra( p_meta_intl, key, data );
309 free( key );
312 free( data );
315 p_peek += i_len;
316 i_read -= i_len;
319 if( i_read == 0 )
320 vlc_meta_Merge( p_meta, p_meta_intl );
322 vlc_meta_Delete( p_meta_intl );
323 return i_read == 0;
326 static void ExtractItunesInfoTriplets( vlc_meta_t *p_meta, MP4_Box_t *p_box )
328 if( p_box->i_type != ATOM_ITUN )
329 return;
330 MP4_Box_t *p_mean = MP4_BoxGet( p_box, "mean" );
331 MP4_Box_t *p_name = MP4_BoxGet( p_box, "name" );
332 MP4_Box_t *p_data = MP4_BoxGet( p_box, "data" );
333 if( !p_mean || p_mean->data.p_binary->i_blob < 4 + 16 ||
334 !p_name || p_name->data.p_binary->i_blob < 5 ||
335 !p_data || !BOXDATA(p_data) )
336 return;
338 if( !strncmp( &((char*)p_mean->data.p_binary->p_blob)[4], "com.apple.iTunes",
339 p_mean->data.p_binary->i_blob - 4 ) )
341 char *psz_name = strndup( &((char*)p_name->data.p_binary->p_blob)[4],
342 p_name->data.p_binary->i_blob - 4 );
343 char *psz_value = ExtractString( p_data );
344 if( psz_name && psz_value )
345 vlc_meta_AddExtra( p_meta, psz_name, psz_value );
346 free( psz_name );
347 free( psz_value );
351 static void SetupmdirMeta( vlc_meta_t *p_meta, MP4_Box_t *p_box )
353 const MP4_Box_t *p_data = MP4_BoxGet( p_box, "data" );
355 if( p_data == NULL || !BOXDATA(p_data) )
357 if( ExtractIntlStrings( p_meta, p_box ) )
358 return;
360 SetMeta( p_meta, p_box->i_type, NULL, p_box );
361 return;
364 /* XXX Becarefull p_udta can have box that are not 0xa9xx */
365 switch( p_box->i_type )
367 case ATOM_atID:
369 if ( BOXDATA(p_data)->i_blob >= 4 &&
370 BOXDATA(p_data)->e_wellknowntype == DATA_WKT_BE_SIGNED )
372 char psz_utf[11];
373 snprintf( psz_utf, sizeof( psz_utf ), "%"PRId32,
374 GetDWBE(BOXDATA(p_data)->p_blob) );
375 vlc_meta_AddExtra( p_meta, "iTunes Account ID", psz_utf );
377 break;
379 case ATOM_cnID:
381 if ( BOXDATA(p_data)->i_blob >= 4 &&
382 BOXDATA(p_data)->e_wellknowntype == DATA_WKT_BE_SIGNED )
384 char psz_utf[11];
385 snprintf( psz_utf, sizeof( psz_utf ), "%"PRId32,
386 GetDWBE(BOXDATA(p_data)->p_blob) );
387 vlc_meta_AddExtra( p_meta, "iTunes Catalog ID", psz_utf );
389 break;
391 case ATOM_disk:
393 if ( BOXDATA(p_data)->i_blob >= 6 &&
394 BOXDATA(p_data)->e_wellknowntype == DATA_WKT_RESERVED )
396 char psz_number[5];
397 snprintf( psz_number, sizeof( psz_number ), "%"PRIu16, GetWBE(&BOXDATA(p_data)->p_blob[2]) );
398 vlc_meta_Set( p_meta, vlc_meta_DiscNumber, psz_number );
399 snprintf( psz_number, sizeof( psz_number ), "%"PRIu16, GetWBE(&BOXDATA(p_data)->p_blob[4]) );
400 vlc_meta_Set( p_meta, vlc_meta_DiscTotal, psz_number );
402 break;
404 case ATOM_gnre:
406 if ( BOXDATA(p_data)->i_blob >= 2 &&
407 BOXDATA(p_data)->e_wellknowntype == DATA_WKT_RESERVED )
409 const uint16_t i_genre = GetWBE(BOXDATA(p_data)->p_blob);
410 if( i_genre && i_genre <= ID3_GENRES_COUNT )
411 vlc_meta_SetGenre( p_meta, ID3_ppsz_genres[i_genre - 1] );
413 break;
415 case ATOM_rtng:
417 if ( BOXDATA(p_data)->i_blob >= 1 )
419 const char *psz_rating;
420 switch( *BOXDATA(p_data)->p_blob )
422 case 0x4:
423 psz_rating = N_("Explicit");
424 break;
425 case 0x2:
426 psz_rating = N_("Clean");
427 break;
428 default:
429 case 0x0:
430 psz_rating = N_("None");
431 break;
433 vlc_meta_AddExtra( p_meta, N_("Rating"), psz_rating );
435 break;
437 case ATOM_trkn:
439 if ( BOXDATA(p_data)->i_blob >= 4 &&
440 BOXDATA(p_data)->e_wellknowntype == DATA_WKT_RESERVED )
442 char psz_trck[6];
443 snprintf( psz_trck, sizeof( psz_trck ), "%"PRIu16, GetWBE(&BOXDATA(p_data)->p_blob[2]) );
444 vlc_meta_SetTrackNum( p_meta, psz_trck );
445 if( BOXDATA(p_data)->i_blob >= 8 && GetWBE(&BOXDATA(p_data)->p_blob[4]) )
447 snprintf( psz_trck, sizeof( psz_trck ), "%"PRIu16, GetWBE(&BOXDATA(p_data)->p_blob[4]) );
448 vlc_meta_Set( p_meta, vlc_meta_TrackTotal, psz_trck );
451 break;
453 case ATOM_ITUN:
454 ExtractItunesInfoTriplets( p_meta, p_box );
455 break;
456 default:
457 SetMeta( p_meta, p_box->i_type, NULL, p_box );
458 break;
462 static void SetupmdtaMeta( vlc_meta_t *p_meta, MP4_Box_t *p_box, MP4_Box_t *p_keys )
464 if ( !p_keys || !BOXDATA(p_keys) || BOXDATA(p_keys)->i_entry_count == 0 )
465 return;
466 if ( !p_box->i_index || p_box->i_index > BOXDATA(p_keys)->i_entry_count )
467 return;
469 const char *psz_naming = BOXDATA(p_keys)->p_entries[p_box->i_index - 1].psz_value;
470 const uint32_t i_namespace = BOXDATA(p_keys)->p_entries[p_box->i_index - 1].i_namespace;
472 if( i_namespace == HANDLER_mdta )
474 if ( !strncmp( "com.apple.quicktime.", psz_naming, 20 ) )
475 SetMeta( p_meta, 0, psz_naming + 20, p_box );
477 else if ( i_namespace == ATOM_udta )
479 /* Regular atom inside... could that be even more complex ??? */
480 char *psz_utf = ExtractString( p_box );
481 if ( psz_utf )
483 if ( strlen(psz_utf) == 4 )
485 SetMeta( p_meta,
486 VLC_FOURCC(psz_utf[0],psz_utf[1],psz_utf[2],psz_utf[3]),
487 NULL, p_box );
489 free( psz_utf );
494 static int ID3TAG_Parse_Handler( uint32_t i_tag, const uint8_t *p_payload,
495 size_t i_payload, void *p_priv )
497 vlc_meta_t *p_meta = (vlc_meta_t *) p_priv;
499 (void) ID3HandleTag( p_payload, i_payload, i_tag, p_meta, NULL );
501 return VLC_SUCCESS;
504 static void SetupID3v2Meta( vlc_meta_t *p_meta, MP4_Box_t *p_box )
506 const MP4_Box_t *p_binary = MP4_BoxGet( p_box, "ID32" );
507 if( p_binary == NULL || !BOXDATA(p_binary) || BOXDATA(p_binary)->i_blob < 6 + 20 + 1 )
508 return;
510 /* ID3v2 in 3GPP / ETSI TS 126 244 8.3, Header size 4 + 2 */
511 ID3TAG_Parse( &((uint8_t *)BOXDATA(p_binary)->p_blob)[6], BOXDATA(p_binary)->i_blob - 6,
512 ID3TAG_Parse_Handler, p_meta );
515 void SetupMeta( vlc_meta_t *p_meta, MP4_Box_t *p_udta )
517 uint32_t i_handler = 0;
518 if ( p_udta->p_father )
519 i_handler = p_udta->i_handler;
521 for( MP4_Box_t *p_box = p_udta->p_first; p_box; p_box = p_box->p_next )
523 switch( i_handler )
525 case HANDLER_mdta:
527 MP4_Box_t *p_keys = MP4_BoxGet( p_udta->p_father, "keys" );
528 SetupmdtaMeta( p_meta, p_box, p_keys );
529 break;
532 case HANDLER_ID32:
533 SetupID3v2Meta( p_meta, p_box );
534 break;
536 case HANDLER_mdir:
537 default:
538 SetupmdirMeta( p_meta, p_box );
539 break;