]> git.sesse.net Git - vlc/blob - modules/demux/mp4/meta.c
demux: mp4: remove special handler for trkn atom
[vlc] / modules / demux / mp4 / meta.c
1 /*****************************************************************************
2  * meta.c: mp4 meta handling
3  *****************************************************************************
4  * Copyright (C) 2001-2004, 2010, 2014 VLC authors and VideoLAN
5  *
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.
10  *
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.
15  *
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
21 #include "mp4.h"
22
23 #include "id3genres.h"                             /* for ATOM_gnre */
24
25 #include <vlc_meta.h>
26 #include <vlc_charset.h>
27
28 #include <assert.h>
29
30 static const struct
31 {
32     const uint32_t xa9_type;
33     const vlc_meta_type_t meta_type;
34 } xa9typetometa[] = {
35     { ATOM_0xa9nam, vlc_meta_Title }, /* Full name */
36     { ATOM_0xa9aut, vlc_meta_Artist },
37     { ATOM_0xa9ART, vlc_meta_Artist },
38     { ATOM_0xa9cpy, vlc_meta_Copyright },
39     { ATOM_0xa9day, vlc_meta_Date }, /* Creation Date */
40     { ATOM_0xa9des, vlc_meta_Description }, /* Description */
41     { ATOM_0xa9gen, vlc_meta_Genre }, /* Genre */
42     { ATOM_0xa9alb, vlc_meta_Album }, /* Album */
43     { ATOM_0xa9trk, vlc_meta_TrackNumber }, /* Track */
44     { ATOM_0xa9cmt, vlc_meta_Description }, /* Comment */
45     { ATOM_0xa9url, vlc_meta_URL }, /* URL */
46     { ATOM_0xa9too, vlc_meta_EncodedBy }, /* Encoder Tool */
47     { ATOM_0xa9enc, vlc_meta_EncodedBy }, /* Encoded By */
48     { ATOM_0xa9pub, vlc_meta_Publisher },
49     { ATOM_0xa9dir, vlc_meta_Director },
50     { 0, 0 },
51 };
52
53 static const struct
54 {
55     const uint32_t xa9_type;
56     const char metadata[25];
57 } xa9typetoextrameta[] = {
58     { ATOM_0xa9wrt, N_("Writer") },
59     { ATOM_0xa9com, N_("Composer") },
60     { ATOM_0xa9prd, N_("Producer") },
61     { ATOM_0xa9inf, N_("Information") },
62     { ATOM_0xa9dis, N_("Disclaimer") },
63     { ATOM_0xa9req, N_("Requirements") },
64     { ATOM_0xa9fmt, N_("Original Format") },
65     { ATOM_0xa9dsa, N_("Display Source As") },
66     { ATOM_0xa9hst, N_("Host Computer") },
67     { ATOM_0xa9prf, N_("Performers") },
68     { ATOM_0xa9ope, N_("Original Performer") },
69     { ATOM_0xa9src, N_("Providers Source Content") },
70     { ATOM_0xa9wrn, N_("Warning") },
71     { ATOM_0xa9swr, N_("Software") },
72     { ATOM_0xa9lyr, N_("Lyrics") },
73     { ATOM_0xa9mak, N_("Record Company") },
74     { ATOM_0xa9mod, N_("Model") },
75     { ATOM_0xa9PRD, N_("Product") },
76     { ATOM_0xa9grp, N_("Grouping") },
77     { ATOM_0xa9gen, N_("Genre") },
78     { ATOM_0xa9st3, N_("Sub-Title") },
79     { ATOM_0xa9arg, N_("Arranger") },
80     { ATOM_0xa9ard, N_("Art Director") },
81     { ATOM_0xa9cak, N_("Copyright Acknowledgement") },
82     { ATOM_0xa9con, N_("Conductor") },
83     { ATOM_0xa9des, N_("Song Description") },
84     { ATOM_0xa9lnt, N_("Liner Notes") },
85     { ATOM_0xa9phg, N_("Phonogram Rights") },
86     { ATOM_0xa9pub, N_("Publisher") },
87     { ATOM_0xa9sne, N_("Sound Engineer") },
88     { ATOM_0xa9sol, N_("Soloist") },
89     { ATOM_0xa9thx, N_("Thanks") },
90     { ATOM_0xa9xpd, N_("Executive Producer") },
91     { ATOM_vndr,    N_("Vendor") },
92     { 0, "" },
93 };
94
95 static const struct
96 {
97     const char *psz_naming;
98     const vlc_meta_type_t meta_type;
99 } com_apple_quicktime_tometa[] = {
100     { "displayname",     vlc_meta_NowPlaying },
101     { "software",        vlc_meta_EncodedBy },
102     { "Encoded_With",    vlc_meta_EncodedBy },
103     { "album",           vlc_meta_Album },
104     { "artist",          vlc_meta_Artist },
105     { "comment",         vlc_meta_Description },
106     { "description",     vlc_meta_Description },
107     { "copyright",       vlc_meta_Copyright },
108     { "creationdate",    vlc_meta_Date },
109     { "director",        vlc_meta_Director },
110     { "genre",           vlc_meta_Genre },
111     { "publisher",       vlc_meta_Publisher },
112     { NULL,              0 },
113 };
114
115 static const struct
116 {
117     const char *psz_naming;
118     const char *psz_metadata;
119 } com_apple_quicktime_toextrameta[] = {
120     { "information",     N_("Information") },
121     { "keywords",        N_("Keywords") },
122     { "make",            N_("Vendor") },
123     { NULL,              NULL },
124 };
125
126 inline static char * StringConvert( const MP4_Box_data_data_t *p_data )
127 {
128     if ( !p_data || !p_data->i_blob )
129         return NULL;
130
131     switch( p_data->e_wellknowntype )
132     {
133     case DATA_WKT_UTF8:
134     case DATA_WKT_UTF8_SORT:
135         return FromCharset( "UTF-8", p_data->p_blob, p_data->i_blob );
136     case DATA_WKT_UTF16:
137     case DATA_WKT_UTF16_SORT:
138         return FromCharset( "UTF-16BE", p_data->p_blob, p_data->i_blob );
139     case DATA_WKT_SJIS:
140         return FromCharset( "SHIFT-JIS", p_data->p_blob, p_data->i_blob );
141     default:
142         return NULL;
143     }
144 }
145
146 static char * ExtractString( MP4_Box_t *p_box )
147 {
148     if ( p_box->i_type == ATOM_data )
149         return StringConvert( p_box->data.p_data );
150
151     MP4_Box_t *p_data = MP4_BoxGet( p_box, "data" );
152     if ( p_data )
153         return StringConvert( BOXDATA(p_data) );
154     else if ( p_box->data.p_string && p_box->data.p_string->psz_text )
155     {
156         char *psz_utf = strdup( p_box->data.p_string->psz_text );
157         if (likely( psz_utf ))
158             EnsureUTF8( psz_utf );
159         return psz_utf;
160     }
161     else
162         return NULL;
163 }
164
165 static bool MatchXA9Type( vlc_meta_t *p_meta, uint32_t i_type, MP4_Box_t *p_box )
166 {
167     bool b_matched = false;
168
169     for( unsigned i = 0; !b_matched && xa9typetometa[i].xa9_type; i++ )
170     {
171         if( i_type == xa9typetometa[i].xa9_type )
172         {
173             b_matched = true;
174             char *psz_utf = ExtractString( p_box );
175             if( psz_utf )
176             {
177                  vlc_meta_Set( p_meta, xa9typetometa[i].meta_type, psz_utf );
178                  free( psz_utf );
179             }
180             break;
181         }
182     }
183
184     for( unsigned i = 0; !b_matched && xa9typetoextrameta[i].xa9_type; i++ )
185     {
186         if( i_type == xa9typetoextrameta[i].xa9_type )
187         {
188             b_matched = true;
189             char *psz_utf = ExtractString( p_box );
190             if( psz_utf )
191             {
192                  vlc_meta_AddExtra( p_meta, _(xa9typetoextrameta[i].metadata), psz_utf );
193                  free( psz_utf );
194             }
195             break;
196         }
197     }
198
199     return b_matched;
200 }
201
202 static bool Matchcom_apple_quicktime( vlc_meta_t *p_meta, const char *psz_naming, MP4_Box_t *p_box )
203 {
204     bool b_matched = false;
205
206     for( unsigned i = 0; !b_matched && com_apple_quicktime_tometa[i].psz_naming; i++ )
207     {
208         if( !strcmp( psz_naming, com_apple_quicktime_tometa[i].psz_naming ) )
209         {
210             b_matched = true;
211             char *psz_utf = ExtractString( p_box );
212             if( psz_utf )
213             {
214                  vlc_meta_Set( p_meta, com_apple_quicktime_tometa[i].meta_type, psz_utf );
215                  free( psz_utf );
216             }
217             break;
218         }
219     }
220
221     for( unsigned i = 0; !b_matched && com_apple_quicktime_toextrameta[i].psz_naming; i++ )
222     {
223         if( !strcmp( psz_naming, com_apple_quicktime_toextrameta[i].psz_naming ) )
224         {
225             b_matched = true;
226             char *psz_utf = ExtractString( p_box );
227             if( psz_utf )
228             {
229                  vlc_meta_AddExtra( p_meta, _(com_apple_quicktime_toextrameta[i].psz_metadata), psz_utf );
230                  free( psz_utf );
231             }
232             break;
233         }
234     }
235
236     return b_matched;
237 }
238
239 static void SetupmdirMeta( vlc_meta_t *p_meta, MP4_Box_t *p_box )
240 {
241     bool b_matched = true;
242     /* XXX Becarefull p_udta can have box that are not 0xa9xx */
243     switch( p_box->i_type )
244     {
245     case ATOM_gnre:
246         if( p_box->data.p_gnre && p_box->data.p_gnre->i_genre <= NUM_GENRES )
247             vlc_meta_SetGenre( p_meta, ppsz_genres[p_box->data.p_gnre->i_genre - 1] );
248         break;
249     case ATOM_trkn:
250     {
251         const MP4_Box_t *p_data = MP4_BoxGet( p_box, "data" );
252         if ( p_data && BOXDATA(p_data) && BOXDATA(p_data)->i_blob >= 4 &&
253              BOXDATA(p_data)->e_wellknowntype == DATA_WKT_RESERVED )
254         {
255             char psz_trck[6];
256             snprintf( psz_trck, sizeof( psz_trck ), "%"PRIu16, GetWBE(&BOXDATA(p_data)->p_blob[2]) );
257             vlc_meta_SetTrackNum( p_meta, psz_trck );
258             if( BOXDATA(p_data)->i_blob >= 8 )
259             {
260                 snprintf( psz_trck, sizeof( psz_trck ), "%"PRIu16, GetWBE(&BOXDATA(p_data)->p_blob[4]) );
261                 vlc_meta_Set( p_meta, vlc_meta_TrackTotal, psz_trck );
262             }
263         }
264         break;
265     }
266
267     default:
268         b_matched = false;
269         break;
270     }
271
272     if ( !b_matched )
273         MatchXA9Type( p_meta, p_box->i_type, p_box );
274 }
275
276 static void SetupmdtaMeta( vlc_meta_t *p_meta, MP4_Box_t *p_box, MP4_Box_t *p_keys )
277 {
278     if ( !p_keys || !BOXDATA(p_keys) || BOXDATA(p_keys)->i_entry_count == 0 )
279         return;
280     if ( !p_box->i_index || p_box->i_index > BOXDATA(p_keys)->i_entry_count )
281         return;
282
283     const char *psz_naming = BOXDATA(p_keys)->p_entries[p_box->i_index - 1].psz_value;
284     const uint32_t i_namespace = BOXDATA(p_keys)->p_entries[p_box->i_index - 1].i_namespace;
285
286     if( i_namespace == HANDLER_mdta )
287     {
288         if ( !strncmp( "com.apple.quicktime.", psz_naming, 20 ) )
289         {
290             Matchcom_apple_quicktime( p_meta, psz_naming + 20, p_box );
291         }
292     }
293     else if ( i_namespace == ATOM_udta )
294     {
295         /* Regular atom inside... could that be even more complex ??? */
296         char *psz_utf = ExtractString( p_box );
297         if ( psz_utf )
298         {
299             if ( strlen(psz_utf) == 4 )
300             {
301                 MatchXA9Type( p_meta,
302                               VLC_FOURCC(psz_utf[0],psz_utf[1],psz_utf[2],psz_utf[3]),
303                               p_box );
304             }
305             free( psz_utf );
306         }
307     }
308 }
309
310 void SetupMeta( vlc_meta_t *p_meta, MP4_Box_t *p_udta )
311 {
312     uint32_t i_handler = 0;
313     if ( p_udta->p_father )
314         i_handler = p_udta->i_handler;
315
316     for( MP4_Box_t *p_box = p_udta->p_first; p_box; p_box = p_box->p_next )
317     {
318         switch( i_handler )
319         {
320             case HANDLER_mdta:
321             {
322                 MP4_Box_t *p_keys = MP4_BoxGet( p_udta->p_father, "keys" );
323                 SetupmdtaMeta( p_meta, p_box, p_keys );
324                 break;
325             }
326
327             case HANDLER_mdir:
328             default:
329                 SetupmdirMeta( p_meta, p_box );
330                 break;
331         }
332     }
333 }
334