Coverage Report

Created: 2025-07-11 07:16

/src/vlc/modules/demux/mp4/meta.c
Line
Count
Source (jump to first uncovered line)
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
#ifdef HAVE_CONFIG_H
21
# include "config.h"
22
#endif
23
24
#include "libmp4.h"
25
#include "../../meta_engine/ID3Genres.h"  /* for ATOM_gnre */
26
#include "languages.h"
27
28
#include <vlc_meta.h>
29
#include <vlc_charset.h>
30
31
#include "meta.h"
32
33
#include "../../meta_engine/ID3Tag.h"
34
#include "../../meta_engine/ID3Meta.h"
35
36
#include <assert.h>
37
38
static const struct
39
{
40
    const uint32_t xa9_type;
41
    const vlc_meta_type_t meta_type;
42
} xa9typetometa[] = {
43
    { ATOM_0x40PRM, vlc_meta_EncodedBy }, /* Adobe Premiere */
44
    { ATOM_0x40PRQ, vlc_meta_EncodedBy }, /* Adobe Qt */
45
    { ATOM_0xa9nam, vlc_meta_Title }, /* Full name */
46
    { ATOM_0xa9aut, vlc_meta_Artist },
47
    { ATOM_0xa9ART, vlc_meta_Artist },
48
    { ATOM_0xa9cpy, vlc_meta_Copyright },
49
    { ATOM_0xa9day, vlc_meta_Date }, /* Creation Date */
50
    { ATOM_0xa9des, vlc_meta_Description }, /* Description */
51
    { ATOM_0xa9gen, vlc_meta_Genre }, /* Genre */
52
    { ATOM_0xa9alb, vlc_meta_Album }, /* Album */
53
    { ATOM_0xa9trk, vlc_meta_TrackNumber }, /* Track */
54
    { ATOM_0xa9cmt, vlc_meta_Description }, /* Comment */
55
    { ATOM_0xa9url, vlc_meta_URL }, /* URL */
56
    { ATOM_0xa9too, vlc_meta_EncodedBy }, /* Encoder Tool */
57
    { ATOM_0xa9enc, vlc_meta_EncodedBy }, /* Encoded By */
58
    { ATOM_0xa9pub, vlc_meta_Publisher },
59
    { ATOM_0xa9dir, vlc_meta_Director },
60
    { ATOM_desc,    vlc_meta_Description },
61
    { ATOM_MCPS,    vlc_meta_EncodedBy }, /* Cleaner Pro */
62
    { ATOM_aART,    vlc_meta_AlbumArtist },
63
};
64
65
static const struct
66
{
67
    const uint32_t xa9_type;
68
    const char metadata[26];
69
} xa9typetoextrameta[] = {
70
    { ATOM_0xa9wrt, N_("Writer") },
71
    { ATOM_0xa9com, N_("Composer") },
72
    { ATOM_0xa9prd, N_("Producer") },
73
    { ATOM_0xa9inf, N_("Information") },
74
    { ATOM_0xa9dis, N_("Disclaimer") },
75
    { ATOM_0xa9req, N_("Requirements") },
76
    { ATOM_0xa9fmt, N_("Original Format") },
77
    { ATOM_0xa9dsa, N_("Display Source As") },
78
    { ATOM_0xa9hst, N_("Host Computer") },
79
    { ATOM_0xa9prf, N_("Performers") },
80
    { ATOM_0xa9ope, N_("Original Performer") },
81
    { ATOM_0xa9src, N_("Providers Source Content") },
82
    { ATOM_0xa9wrn, N_("Warning") },
83
    { ATOM_0xa9swr, N_("Software") },
84
    { ATOM_0xa9lyr, N_("Lyrics") },
85
    { ATOM_0xa9mak, N_("Record Company") },
86
    { ATOM_0xa9mod, N_("Model") },
87
    { ATOM_0xa9PRD, N_("Product") },
88
    { ATOM_0xa9grp, N_("Grouping") },
89
    { ATOM_0xa9gen, N_("Genre") },
90
    { ATOM_0xa9st3, N_("Sub-Title") },
91
    { ATOM_0xa9arg, N_("Arranger") },
92
    { ATOM_0xa9ard, N_("Art Director") },
93
    { ATOM_0xa9cak, N_("Copyright Acknowledgement") },
94
    { ATOM_0xa9con, N_("Conductor") },
95
    { ATOM_0xa9des, N_("Song Description") },
96
    { ATOM_0xa9lnt, N_("Liner Notes") },
97
    { ATOM_0xa9phg, N_("Phonogram Rights") },
98
    { ATOM_0xa9pub, N_("Publisher") },
99
    { ATOM_0xa9sne, N_("Sound Engineer") },
100
    { ATOM_0xa9sol, N_("Soloist") },
101
    { ATOM_0xa9thx, N_("Thanks") },
102
    { ATOM_0xa9xpd, N_("Executive Producer") },
103
    { ATOM_aART,    N_("Album Artist") },
104
    { ATOM_flvr,    N_("Encoding Params") },
105
    { ATOM_vndr,    N_("Vendor") },
106
    { ATOM_xid_,    N_("Catalog Number") },
107
    { ATOM_gshh,    "YouTube Host" },
108
    { ATOM_gspm,    "YouTube Ping Message" },
109
    { ATOM_gspu,    "YouTube Ping Url" },
110
    { ATOM_gssd,    "YouTube Source Data" },
111
    { ATOM_gsst,    "YouTube Start Time" },
112
    { ATOM_gstd,    "YouTube Track Duration" },
113
    { ATOM_purl,    "Podcast URL" },
114
};
115
116
static const struct
117
{
118
    const char *psz_naming;
119
    const vlc_meta_type_t meta_type;
120
} com_apple_quicktime_tometa[] = {
121
    { "displayname",     vlc_meta_NowPlaying },
122
    { "software",        vlc_meta_EncodedBy },
123
    { "Encoded_With",    vlc_meta_EncodedBy },
124
    { "album",           vlc_meta_Album },
125
    { "artist",          vlc_meta_Artist },
126
    { "comment",         vlc_meta_Description },
127
    { "description",     vlc_meta_Description },
128
    { "copyright",       vlc_meta_Copyright },
129
    { "creationdate",    vlc_meta_Date },
130
    { "director",        vlc_meta_Director },
131
    { "genre",           vlc_meta_Genre },
132
    { "publisher",       vlc_meta_Publisher },
133
};
134
135
static const struct
136
{
137
    const char *psz_naming;
138
    const char *psz_metadata;
139
} com_apple_quicktime_toextrameta[] = {
140
    { "information",     N_("Information") },
141
    { "keywords",        N_("Keywords") },
142
    { "make",            N_("Vendor") },
143
};
144
145
inline static char * StringConvert( const MP4_Box_data_data_t *p_data )
146
250
{
147
250
    if ( !p_data || !p_data->i_blob )
148
1
        return NULL;
149
150
249
    switch( p_data->e_wellknowntype )
151
249
    {
152
238
    case DATA_WKT_UTF8:
153
238
    case DATA_WKT_UTF8_SORT:
154
238
        return FromCharset( "UTF-8", p_data->p_blob, p_data->i_blob );
155
1
    case DATA_WKT_UTF16:
156
2
    case DATA_WKT_UTF16_SORT:
157
2
        return FromCharset( "UTF-16BE", p_data->p_blob, p_data->i_blob );
158
2
    case DATA_WKT_SJIS:
159
2
        return FromCharset( "SHIFT-JIS", p_data->p_blob, p_data->i_blob );
160
7
    default:
161
7
        return NULL;
162
249
    }
163
249
}
164
165
static char * ExtractString( const MP4_Box_t *p_box )
166
623
{
167
623
    if ( p_box->i_type == ATOM_data )
168
0
        return StringConvert( p_box->data.p_data );
169
170
623
    const MP4_Box_t *p_data = MP4_BoxGet( p_box, "data" );
171
623
    if ( p_data )
172
215
        return StringConvert( BOXDATA(p_data) );
173
408
    else if ( p_box->data.p_binary && p_box->data.p_binary->p_blob )
174
236
    {
175
236
        char *psz_utf = strndup( p_box->data.p_binary->p_blob,
176
236
                                 p_box->data.p_binary->i_blob );
177
236
        if (likely( psz_utf ))
178
236
            EnsureUTF8( psz_utf );
179
236
        return psz_utf;
180
236
    }
181
172
    else
182
172
        return NULL;
183
623
}
184
185
static bool AppleNameToMeta( char const* name,
186
    vlc_meta_type_t const** meta_type, char const** meta_key )
187
47
{
188
47
    *meta_type = NULL;
189
47
    *meta_key = NULL;
190
191
611
    for( size_t i = 0; *meta_type == NULL &&
192
611
                       i < ARRAY_SIZE( com_apple_quicktime_tometa ); ++i )
193
564
    {
194
564
        if( !strcmp( name, com_apple_quicktime_tometa[i].psz_naming ) )
195
0
            *meta_type = &com_apple_quicktime_tometa[i].meta_type;
196
564
    }
197
198
188
    for( size_t i = 0; *meta_key == NULL &&
199
188
                       i < ARRAY_SIZE( com_apple_quicktime_toextrameta ); ++i )
200
141
    {
201
141
        if( !strcmp( name, com_apple_quicktime_toextrameta[i].psz_naming ) )
202
0
            *meta_key = com_apple_quicktime_toextrameta[i].psz_metadata;
203
141
    }
204
205
47
    return *meta_type || *meta_key;
206
47
}
207
208
static bool AtomXA9ToMeta( uint32_t i_type,
209
    vlc_meta_type_t const** meta_type, char const** meta_key )
210
2.53k
{
211
2.53k
    *meta_type = NULL;
212
2.53k
    *meta_key = NULL;
213
214
47.1k
    for( size_t i = 0; !*meta_type && i < ARRAY_SIZE( xa9typetometa ); ++i )
215
44.6k
        if( xa9typetometa[i].xa9_type == i_type )
216
792
            *meta_type = &xa9typetometa[i].meta_type;
217
218
92.5k
    for( size_t i = 0; !*meta_key && i < ARRAY_SIZE( xa9typetoextrameta ); ++i )
219
89.9k
        if( xa9typetoextrameta[i].xa9_type == i_type )
220
608
            *meta_key = xa9typetoextrameta[i].metadata;
221
222
2.53k
    return *meta_type || *meta_key;
223
2.53k
}
224
225
static bool SetMeta( vlc_meta_t* p_meta, int i_type, char const* name, const MP4_Box_t* p_box )
226
1.24k
{
227
1.24k
    vlc_meta_type_t const* type;
228
1.24k
    char const* key;
229
230
1.24k
    if( name != NULL)
231
47
    {
232
47
        if ( !AppleNameToMeta( name, &type, &key ) )
233
47
            return false;
234
47
    }
235
1.19k
    else
236
1.19k
    {
237
1.19k
        if ( !AtomXA9ToMeta( i_type, &type, &key ) )
238
575
            return false;
239
1.19k
    }
240
241
623
    char* psz_utf = ExtractString( p_box );
242
243
623
    if( psz_utf )
244
446
    {
245
446
        if( type ) vlc_meta_Set( p_meta, *type, psz_utf );
246
135
        else       vlc_meta_SetExtra( p_meta, key, psz_utf );
247
248
446
        free( psz_utf );
249
446
    }
250
251
623
    return true;
252
1.24k
}
253
254
static int ExtractIntlStrings( vlc_meta_t *p_meta, const MP4_Box_t *p_box )
255
1.33k
{
256
1.33k
    if( MP4_BoxGet( p_box, "data" ) )
257
0
        return false;
258
259
1.33k
    vlc_meta_type_t const* meta_type;
260
1.33k
    char const* meta_key;
261
262
1.33k
    if( AtomXA9ToMeta( p_box->i_type, &meta_type, &meta_key ) == false )
263
573
        return false;
264
265
763
    if( p_box->p_father == NULL              ||
266
763
        p_box->p_father->i_type != ATOM_udta ||
267
763
        p_box->data.p_binary == NULL         ||
268
763
        p_box->data.p_binary->p_blob == NULL )
269
172
    {
270
172
        return false;
271
172
    }
272
273
591
    vlc_meta_t* p_meta_intl = vlc_meta_New();
274
275
591
    if( unlikely( !p_meta_intl ) )
276
0
        return false;
277
278
591
    char const* p_peek = p_box->data.p_binary->p_blob;
279
591
    uint64_t i_read = p_box->data.p_binary->i_blob;
280
281
2.12k
    while( i_read >= 4 )
282
1.78k
    {
283
1.78k
        uint16_t i_len = GetWBE( p_peek );
284
1.78k
        uint16_t i_lang = GetWBE( p_peek + 2 );
285
1.78k
        p_peek += 4;
286
1.78k
        i_read -= 4;
287
288
1.78k
        if( i_len > i_read )
289
247
            break;
290
291
1.53k
        char charset[15] = "MACINTOSH//";
292
293
1.53k
        decodeQtLanguageCode( i_lang, charset+11, &(bool){0} );
294
295
1.53k
        if( i_lang >= 0x400 && i_lang != 0x7fff )
296
549
        {
297
549
            strcpy( charset, i_len < 2 || memcmp( p_peek, "\xFE\xFF", 2 )
298
549
                ? "UTF-8" : "UTF-16BE" );
299
549
        }
300
301
1.53k
        char* data = FromCharset( charset, p_peek, i_len );
302
1.53k
        if( data )
303
1.50k
        {
304
1.50k
            if( meta_type )
305
119
            {
306
119
                vlc_meta_Set( p_meta_intl, *meta_type, data );
307
308
119
                meta_key = vlc_meta_TypeToLocalizedString( *meta_type );
309
119
                meta_type = NULL;
310
119
            }
311
1.38k
            else
312
1.38k
            {
313
1.38k
                char* key;
314
1.38k
                if( asprintf( &key, "%s (%s)", meta_key, charset+11 ) != -1 )
315
1.38k
                {
316
1.38k
                    vlc_meta_SetExtra( p_meta_intl, key, data );
317
1.38k
                    free( key );
318
1.38k
                }
319
1.38k
            }
320
1.50k
            free( data );
321
1.50k
        }
322
323
1.53k
        p_peek += i_len;
324
1.53k
        i_read -= i_len;
325
1.53k
    }
326
327
591
    if( i_read == 0 )
328
355
        vlc_meta_Merge( p_meta, p_meta_intl );
329
330
591
    vlc_meta_Delete( p_meta_intl );
331
591
    return i_read == 0;
332
591
}
333
334
static void ParseTriplet( const MP4_Box_t *p_box,
335
                          const char *psz_ns,
336
                          const char *psz_key,
337
                          void (*pf_callback)(const char *psz_key,
338
                                              const MP4_Box_data_data_t *, void *),
339
                          void *p_priv )
340
78
{
341
78
    size_t i_ns = psz_ns ? strlen(psz_ns) : 0;
342
78
    size_t i_key = psz_key ? strlen(psz_key) : 0;
343
78
    const MP4_Box_t *p_mean = MP4_BoxGet( p_box, "mean" );
344
78
    const MP4_Box_t *p_name = MP4_BoxGet( p_box, "name" );
345
78
    const MP4_Box_t *p_data = MP4_BoxGet( p_box, "data" );
346
78
    if( !p_mean || p_mean->data.p_binary->i_blob < 4 + i_ns ||
347
78
        !p_name || p_name->data.p_binary->i_blob < 4 + i_key ||
348
78
        !p_data || !BOXDATA(p_data) )
349
7
        return;
350
351
71
    if( p_name->data.p_binary->i_blob - 4 != i_ns &&
352
71
        strncmp( &((const char*)p_mean->data.p_binary->p_blob)[4], psz_ns, i_ns ) )
353
11
        return;
354
355
60
    if( psz_key != NULL &&
356
60
        p_name->data.p_binary->i_blob - 4 != i_key &&
357
60
        strncmp( &((const char*)p_name->data.p_binary->p_blob)[4], psz_key, i_key ) )
358
0
            return;
359
360
60
    char *psz_name = strndup( &((const char*)p_name->data.p_binary->p_blob)[4],
361
60
                              p_name->data.p_binary->i_blob - 4 );
362
60
    if( psz_name )
363
60
        pf_callback( psz_name, p_data->data.p_data, p_priv );
364
60
    free( psz_name );
365
60
}
366
367
struct itunprivcallbackctx
368
{
369
    vlc_meta_t *p_meta;
370
    qt_itunes_callback ituncb;
371
    void *priv;
372
};
373
374
static void iTUNTripletCallback( const char *psz_key,
375
                                 const MP4_Box_data_data_t *p_data, void *priv )
376
60
{
377
60
    struct itunprivcallbackctx *ctx = priv;
378
379
60
    if( !strcmp(psz_key, "iTunSMPB") )
380
26
    {
381
26
        struct qt_itunes_triplet_data data = {.type = iTunSMPB };
382
26
        char *psz_val = StringConvert( p_data );
383
26
        if( psz_val && sscanf(psz_val, "%*"PRIX32" %"PRIX32" %"PRIX32" %"PRIX64,
384
20
                                       &data.SMPB.delay, &data.SMPB.padding,
385
20
                                       &data.SMPB.original_samplescount) == 3 )
386
16
            ctx->ituncb( &data, ctx->priv );
387
26
        free( psz_val );
388
26
    }
389
34
    else if( !strcmp(psz_key, "iTunNORM") )
390
9
    {
391
9
        struct qt_itunes_triplet_data data = {.type = iTunNORM };
392
9
        char *psz_val = StringConvert( p_data );
393
9
        uint32_t values[4];
394
9
        if( psz_val && sscanf(psz_val, "%"PRIX32" %"PRIX32" %*"PRIX32" %*"PRIX32
395
8
                              "%*"PRIX32" %*"PRIX32" %"PRIX32" %"PRIX32,
396
8
                              &values[0], &values[1], &values[2], &values[3] ) == 4 )
397
7
        {
398
7
            values[0] = __MAX(values[0], values[1]); /* left / right volume */
399
7
            if( values[0] )
400
7
                data.NORM.volume_adjust = -10.0 * log10( values[0] / 1000.0 );
401
402
7
            values[2] = __MAX(values[2], values[3]); /* left / right peak */
403
7
            if( values[2] )
404
6
                data.NORM.peak = values[2] / 32768.0;
405
7
            if( values[0] && values[2] )
406
6
                ctx->ituncb( &data, ctx->priv );
407
7
        }
408
9
        free( psz_val );
409
9
    }
410
25
    else if( !strcmp(psz_key, "Encoding Params"))
411
12
    {
412
12
        struct qt_itunes_triplet_data data = {.type = iTunEncodingParams,
413
12
                                               .EncodingParams = { 0 } };
414
61
        for( size_t i=0; i<p_data->i_blob; i += 8 )
415
49
        {
416
49
            const char *id = (const char*) &p_data->p_blob[i];
417
49
            if( !strncmp(id, "brat", 4) )
418
7
                data.EncodingParams.target_bitrate = GetDWBE(&p_data->p_blob[i+4]);
419
42
            else if( !strncmp(id, "vbrq", 4) )
420
2
                data.EncodingParams.target_quality = GetDWBE(&p_data->p_blob[i+4]);
421
49
        }
422
12
        ctx->ituncb( &data, ctx->priv );
423
12
    }
424
13
    else if( !strncasecmp(psz_key, "REPLAYGAIN_", 11) )
425
0
    {
426
0
        char *psz_val = StringConvert( p_data );
427
0
        if( psz_val )
428
0
            vlc_meta_SetExtra( ctx->p_meta, psz_key, psz_val );
429
0
        free( psz_val );
430
0
    }
431
60
}
432
433
static void SetupmdirMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_box, void *priv )
434
1.63k
{
435
1.63k
    const MP4_Box_t *p_data = MP4_BoxGet( p_box, "data" );
436
437
1.63k
    if( p_data == NULL || !BOXDATA(p_data) )
438
1.33k
    {
439
1.33k
        if( ExtractIntlStrings( p_meta, p_box ) )
440
355
            return;
441
442
981
        SetMeta( p_meta, p_box->i_type, NULL, p_box );
443
981
        return;
444
1.33k
    }
445
446
    /* XXX Becarefull p_udta can have box that are not 0xa9xx */
447
299
    switch( p_box->i_type )
448
299
    {
449
0
    case ATOM_atID:
450
0
    {
451
0
        if ( BOXDATA(p_data)->i_blob >= 4 &&
452
0
             BOXDATA(p_data)->e_wellknowntype == DATA_WKT_BE_SIGNED )
453
0
        {
454
0
            char psz_utf[11];
455
0
            snprintf( psz_utf, sizeof( psz_utf ), "%"PRId32,
456
0
                      GetDWBE(BOXDATA(p_data)->p_blob) );
457
0
            vlc_meta_SetExtra( p_meta, "iTunes Account ID", psz_utf );
458
0
        }
459
0
        break;
460
0
    }
461
0
    case ATOM_cnID:
462
0
    {
463
0
        if ( BOXDATA(p_data)->i_blob >= 4 &&
464
0
             BOXDATA(p_data)->e_wellknowntype == DATA_WKT_BE_SIGNED )
465
0
        {
466
0
            char psz_utf[11];
467
0
            snprintf( psz_utf, sizeof( psz_utf ), "%"PRId32,
468
0
                      GetDWBE(BOXDATA(p_data)->p_blob) );
469
0
            vlc_meta_SetExtra( p_meta, "iTunes Catalog ID", psz_utf );
470
0
        }
471
0
        break;
472
0
    }
473
1
    case ATOM_disk:
474
1
    {
475
1
        if ( BOXDATA(p_data)->i_blob >= 6 &&
476
1
             BOXDATA(p_data)->e_wellknowntype == DATA_WKT_RESERVED )
477
1
        {
478
1
            char psz_number[6];
479
1
            snprintf( psz_number, sizeof( psz_number ), "%"PRIu16, GetWBE(&BOXDATA(p_data)->p_blob[2]) );
480
1
            vlc_meta_Set( p_meta, vlc_meta_DiscNumber, psz_number );
481
1
            snprintf( psz_number, sizeof( psz_number ), "%"PRIu16, GetWBE(&BOXDATA(p_data)->p_blob[4]) );
482
1
            vlc_meta_Set( p_meta, vlc_meta_DiscTotal, psz_number );
483
1
        }
484
1
        break;
485
0
    }
486
1
    case ATOM_gnre:
487
1
    {
488
1
        if ( BOXDATA(p_data)->i_blob >= 2 &&
489
1
             BOXDATA(p_data)->e_wellknowntype == DATA_WKT_RESERVED )
490
1
        {
491
1
            const uint16_t i_genre = GetWBE(BOXDATA(p_data)->p_blob);
492
1
            if( i_genre && i_genre <= ID3_GENRES_COUNT )
493
1
                vlc_meta_SetGenre( p_meta, ID3_ppsz_genres[i_genre - 1] );
494
1
        }
495
1
        break;
496
0
    }
497
0
    case ATOM_rtng:
498
0
    {
499
0
        if ( BOXDATA(p_data)->i_blob >= 1 )
500
0
        {
501
0
            const char *psz_rating;
502
0
            switch( *BOXDATA(p_data)->p_blob )
503
0
            {
504
0
            case 0x4:
505
0
                psz_rating = N_("Explicit");
506
0
                break;
507
0
            case 0x2:
508
0
                psz_rating = N_("Clean");
509
0
                break;
510
0
            default:
511
0
            case 0x0:
512
0
                psz_rating = N_("None");
513
0
                break;
514
0
            }
515
0
            vlc_meta_SetExtra( p_meta, N_("Rating"), psz_rating );
516
0
        }
517
0
        break;
518
0
    }
519
2
    case ATOM_trkn:
520
2
    {
521
2
        if ( BOXDATA(p_data)->i_blob >= 4 &&
522
2
             BOXDATA(p_data)->e_wellknowntype == DATA_WKT_RESERVED )
523
1
        {
524
1
            char psz_trck[6];
525
1
            snprintf( psz_trck, sizeof( psz_trck ), "%"PRIu16, GetWBE(&BOXDATA(p_data)->p_blob[2]) );
526
1
            vlc_meta_SetTrackNum( p_meta, psz_trck );
527
1
            if( BOXDATA(p_data)->i_blob >= 8 && GetWBE(&BOXDATA(p_data)->p_blob[4]) )
528
1
            {
529
1
                snprintf( psz_trck, sizeof( psz_trck ), "%"PRIu16, GetWBE(&BOXDATA(p_data)->p_blob[4]) );
530
1
                vlc_meta_Set( p_meta, vlc_meta_TrackTotal, psz_trck );
531
1
            }
532
1
        }
533
2
        break;
534
0
    }
535
78
    case ATOM_ITUN:
536
78
        ParseTriplet( p_box, "com.apple.iTunes", NULL, iTUNTripletCallback, priv );
537
78
        break;
538
217
    default:
539
217
        SetMeta( p_meta, p_box->i_type, NULL, p_box );
540
217
        break;
541
299
    }
542
299
}
543
544
static void SetupmdtaMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_box, const MP4_Box_t *p_keys )
545
149
{
546
149
    if ( !p_keys || !BOXDATA(p_keys) || BOXDATA(p_keys)->i_entry_count == 0 )
547
39
        return;
548
110
    if ( !p_box->i_index || p_box->i_index > BOXDATA(p_keys)->i_entry_count )
549
21
        return;
550
551
89
    const char *psz_naming = BOXDATA(p_keys)->p_entries[p_box->i_index - 1].psz_value;
552
89
    const uint32_t i_namespace = BOXDATA(p_keys)->p_entries[p_box->i_index - 1].i_namespace;
553
554
89
    if( i_namespace == HANDLER_mdta )
555
87
    {
556
87
        if ( !strncmp( "com.apple.quicktime.", psz_naming, 20 ) )
557
47
            SetMeta( p_meta, 0, psz_naming + 20, p_box );
558
87
    }
559
2
    else if ( i_namespace == ATOM_udta )
560
0
    {
561
        /* Regular atom inside... could that be even more complex ??? */
562
0
        char *psz_utf = ExtractString( p_box );
563
0
        if ( psz_utf )
564
0
        {
565
0
            if ( strlen(psz_utf) == 4 )
566
0
            {
567
0
                SetMeta( p_meta,
568
0
                         VLC_FOURCC(psz_utf[0],psz_utf[1],psz_utf[2],psz_utf[3]),
569
0
                         NULL, p_box );
570
0
            }
571
0
            free( psz_utf );
572
0
        }
573
0
    }
574
89
}
575
576
static int ID3TAG_Parse_Handler( uint32_t i_tag, const uint8_t *p_payload,
577
                                 size_t i_payload, void *p_priv )
578
0
{
579
0
    vlc_meta_t *p_meta = (vlc_meta_t *) p_priv;
580
581
0
    (void) ID3HandleTag( p_payload, i_payload, i_tag, p_meta, NULL );
582
583
0
    return VLC_SUCCESS;
584
0
}
585
586
static void SetupID3v2Meta( vlc_meta_t *p_meta, const MP4_Box_t *p_box )
587
0
{
588
0
    const MP4_Box_t *p_binary = MP4_BoxGet( p_box, "ID32" );
589
0
    if( p_binary == NULL || !BOXDATA(p_binary) || BOXDATA(p_binary)->i_blob < 6 + 20 + 1 )
590
0
        return;
591
592
    /* ID3v2 in 3GPP / ETSI TS 126 244 8.3, Header size 4 + 2 */
593
0
    ID3TAG_Parse( &((uint8_t *)BOXDATA(p_binary)->p_blob)[6], BOXDATA(p_binary)->i_blob - 6,
594
0
                  ID3TAG_Parse_Handler, p_meta );
595
0
}
596
597
void SetupMeta( vlc_meta_t *p_meta, const MP4_Box_t *p_udta,
598
                qt_itunes_callback ituncb, void *priv )
599
1.16k
{
600
1.16k
    uint32_t i_handler = 0;
601
1.16k
    if ( p_udta->p_father )
602
1.16k
        i_handler = p_udta->i_handler;
603
604
2.94k
    for( const MP4_Box_t *p_box = p_udta->p_first; p_box; p_box = p_box->p_next )
605
1.78k
    {
606
1.78k
        switch( i_handler )
607
1.78k
        {
608
149
            case HANDLER_mdta:
609
149
            {
610
149
                const MP4_Box_t *p_keys = MP4_BoxGet( p_udta->p_father, "keys" );
611
149
                SetupmdtaMeta( p_meta, p_box, p_keys );
612
149
                break;
613
0
            }
614
615
0
            case HANDLER_ID32:
616
0
                SetupID3v2Meta( p_meta, p_box );
617
0
                break;
618
619
430
            case HANDLER_mdir:
620
1.63k
            default:
621
1.63k
            {
622
1.63k
                struct itunprivcallbackctx ctx = { .priv = priv, .p_meta = p_meta, .ituncb = ituncb };
623
1.63k
                SetupmdirMeta( p_meta, p_box, &ctx );
624
1.63k
                break;
625
430
            }
626
1.78k
        }
627
1.78k
    }
628
1.16k
}