Coverage Report

Created: 2023-06-07 07:26

/src/vlc/modules/demux/mp4/attachments.c
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * attachments.c : MP4 attachments handling
3
 *****************************************************************************
4
 * Copyright (C) 2001-2015 VLC authors and VideoLAN
5
 *               2019 VideoLabs
6
 *
7
 * This program is free software; you can redistribute it and/or modify it
8
 * under the terms of the GNU Lesser General Public License as published by
9
 * the Free Software Foundation; either version 2.1 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with this program; if not, write to the Free Software Foundation,
19
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20
 *****************************************************************************/
21
#ifdef HAVE_CONFIG_H
22
# include "config.h"
23
#endif
24
25
#include <vlc_common.h>
26
#include <vlc_input.h>
27
28
#include "libmp4.h"
29
#include "attachments.h"
30
#include <limits.h>
31
32
static const char *psz_meta_roots[] = { "/moov/udta/meta/ilst",
33
                                        "/moov/meta/ilst",
34
                                        "/moov/udta/meta",
35
                                        "/moov/udta",
36
                                        "/meta/ilst",
37
                                        "/udta" };
38
39
const MP4_Box_t *MP4_GetMetaRoot( const MP4_Box_t *p_root, const char **ppsz_path )
40
7
{
41
40
    for( size_t i = 0; i < ARRAY_SIZE(psz_meta_roots); i++ )
42
36
    {
43
36
        MP4_Box_t *p_udta = MP4_BoxGet( p_root, psz_meta_roots[i] );
44
36
        if ( p_udta )
45
3
        {
46
3
            *ppsz_path = psz_meta_roots[i];
47
3
            return p_udta;
48
3
        }
49
36
    }
50
4
    return NULL;
51
7
}
52
53
enum detectedImageType
54
{
55
    IMAGE_TYPE_UNKNOWN = DATA_WKT_RESERVED,
56
    IMAGE_TYPE_JPEG = DATA_WKT_JPEG,
57
    IMAGE_TYPE_PNG = DATA_WKT_PNG,
58
    IMAGE_TYPE_BMP = DATA_WKT_BMP,
59
};
60
61
static const struct
62
{
63
    enum detectedImageType type;
64
    const char *mime;
65
} rg_imageTypeToMime[] = {
66
    { IMAGE_TYPE_JPEG,  "image/jpeg" },
67
    { IMAGE_TYPE_PNG,   "image/png" },
68
    { IMAGE_TYPE_BMP,   "image/bmp" },
69
};
70
71
static const char * getMimeType( enum detectedImageType type )
72
0
{
73
0
    for( size_t i=0; i<ARRAY_SIZE(rg_imageTypeToMime); i++ )
74
0
    {
75
0
        if( rg_imageTypeToMime[i].type == type )
76
0
            return rg_imageTypeToMime[i].mime;
77
0
    }
78
0
    return NULL;
79
0
}
80
81
static enum detectedImageType wellKnownToimageType( int e_wellknowntype )
82
0
{
83
0
    switch( e_wellknowntype )
84
0
    {
85
0
        case DATA_WKT_PNG:
86
0
        case DATA_WKT_JPEG:
87
0
        case DATA_WKT_BMP:
88
0
            return e_wellknowntype;
89
0
        default:
90
0
            return IMAGE_TYPE_UNKNOWN;
91
0
    }
92
0
}
93
94
static enum detectedImageType probeImageType( const uint8_t *p_data, size_t i_data )
95
0
{
96
0
    if( i_data > 4 )
97
0
    {
98
0
        if( !memcmp( p_data, "\xFF\xD8\xFF", 3 ) )
99
0
            return IMAGE_TYPE_JPEG;
100
0
        else if( !memcmp( p_data, ".PNG", 4 ) )
101
0
            return IMAGE_TYPE_PNG;
102
0
    }
103
0
    return IMAGE_TYPE_UNKNOWN;
104
0
}
105
106
static const MP4_Box_t * GetValidCovrMeta( const MP4_Box_t *p_data,
107
                                           unsigned *pi_index,
108
                                           const void **ctx )
109
3
{
110
3
    for( ; p_data; p_data = p_data->p_next )
111
0
    {
112
0
        if( p_data->i_type != ATOM_data || p_data == *ctx )
113
0
            continue;
114
0
        (*pi_index)++;
115
0
        if ( !BOXDATA(p_data) ||
116
0
             wellKnownToimageType(
117
0
                 BOXDATA(p_data)->e_wellknowntype ) == IMAGE_TYPE_UNKNOWN )
118
0
            continue;
119
0
        *ctx = p_data;
120
0
        return p_data;
121
0
    }
122
3
    return NULL;
123
3
}
124
125
static const MP4_Box_t * GetValidPnotMeta( const MP4_Box_t *p_pnot,
126
                                           unsigned *pi_index,
127
                                           const void **ctx )
128
0
{
129
0
    for( ; p_pnot; p_pnot = p_pnot->p_next )
130
0
    {
131
0
        if( p_pnot->i_type != ATOM_pnot || p_pnot == *ctx )
132
0
            continue;
133
0
        (*pi_index)++;
134
0
        if( BOXDATA(p_pnot)->i_type != ATOM_PICT &&
135
0
            BOXDATA(p_pnot)->i_type != ATOM_pict )
136
0
            continue;
137
0
        *ctx = p_pnot;
138
0
        return p_pnot;
139
0
    }
140
0
    return NULL;
141
0
}
142
143
static const MP4_Box_t * GetValidThumMeta( const MP4_Box_t *p_thum,
144
                                           unsigned *pi_index,
145
                                           const void **ctx )
146
0
{
147
0
    for( ; p_thum; p_thum = p_thum->p_next )
148
0
    {
149
0
        if( p_thum->i_type != ATOM_thum || p_thum == *ctx )
150
0
            continue;
151
0
        (*pi_index)++;
152
0
        if ( !p_thum->data.p_binary ||
153
0
                probeImageType( p_thum->data.p_binary->p_blob,
154
0
                                p_thum->data.p_binary->i_blob )
155
0
                                        == IMAGE_TYPE_UNKNOWN )
156
0
            continue;
157
0
        *ctx = p_thum;
158
0
        return p_thum;
159
0
    }
160
0
    return NULL;
161
0
}
162
163
int MP4_GetCoverMetaURI( const MP4_Box_t *p_root,
164
                         const MP4_Box_t *p_metaroot,
165
                         const char *psz_metapath,
166
                         vlc_meta_t *p_meta )
167
7
{
168
7
    bool b_attachment_set = false;
169
170
7
    if( !p_meta )
171
0
        return VLC_EGENERIC;
172
173
7
    if ( p_metaroot )
174
3
    {
175
3
        const MP4_Box_t *p_data = MP4_BoxGet( p_metaroot, "covr/data" );
176
3
        unsigned i_index = 0;
177
3
        const void *ctx = NULL;
178
3
        if( (p_data = GetValidCovrMeta( p_data, &i_index, &ctx )) )
179
0
        {
180
0
            char *psz_attachment;
181
0
            if ( -1 != asprintf( &psz_attachment,
182
0
                                 "attachment://%s/covr/data[%u]",
183
0
                                 psz_metapath, i_index - 1 ) )
184
0
            {
185
0
                vlc_meta_SetArtURL( p_meta, psz_attachment );
186
0
                b_attachment_set = true;
187
0
                free( psz_attachment );
188
0
            }
189
0
        }
190
3
    }
191
192
7
    const MP4_Box_t *p_pnot;
193
7
    if ( !b_attachment_set && (p_pnot = MP4_BoxGet( p_root, "pnot" )) )
194
0
    {
195
0
        unsigned i_index = 0;
196
0
        const void *ctx = NULL;
197
0
        if( (p_pnot = GetValidPnotMeta( p_pnot, &i_index, &ctx )) )
198
0
        {
199
0
            char *psz_attachment;
200
0
            if ( -1 != asprintf( &psz_attachment,
201
0
                                 "attachment://pnot[%u]", i_index - 1 ) )
202
0
            {
203
0
                vlc_meta_SetArtURL( p_meta, psz_attachment );
204
0
                b_attachment_set = true;
205
0
                free( psz_attachment );
206
0
            }
207
0
        }
208
0
    }
209
210
211
7
    const MP4_Box_t *p_thum;
212
7
    if( !b_attachment_set && (p_thum = MP4_BoxGet( p_root, "thum" )) )
213
0
    {
214
0
        unsigned i_index = 0;
215
0
        const void *ctx = NULL;
216
0
        if( (p_thum = GetValidThumMeta( p_thum, &i_index, &ctx )) )
217
0
        {
218
0
            char *psz_attachment;
219
0
            if ( -1 != asprintf( &psz_attachment,
220
0
                                 "attachment://thum[%u]",
221
0
                                 i_index - 1 ) )
222
0
            {
223
0
                vlc_meta_SetArtURL( p_meta, psz_attachment );
224
0
                b_attachment_set = true;
225
0
                free( psz_attachment );
226
0
            }
227
0
        }
228
0
    }
229
230
7
    if( !b_attachment_set )
231
7
        return VLC_EGENERIC;
232
233
0
    return VLC_SUCCESS;
234
7
}
235
236
size_t MP4_GetAttachments( const MP4_Box_t *p_root, input_attachment_t ***ppp_attach )
237
0
{
238
0
    const MP4_Box_t *p_metaroot = NULL;
239
0
    const char *psz_metarootpath;
240
0
    size_t i_count = 0;
241
0
    input_attachment_t **pp_attach = NULL;
242
0
    *ppp_attach = NULL;
243
244
    /* Count MAX number of total attachments */
245
0
    p_metaroot = MP4_GetMetaRoot( p_root, &psz_metarootpath );
246
0
    if( p_metaroot )
247
0
    {
248
0
        unsigned i_covercount = MP4_BoxCount( p_metaroot, "covr/data" );
249
0
        if( unlikely(i_covercount > INT_MAX - i_count) )
250
0
            return 0;
251
0
        i_count += i_covercount;
252
0
    }
253
254
0
    unsigned i_pictcount = MP4_BoxCount( p_root, "pnot" );
255
0
    if( unlikely(i_pictcount > INT_MAX - i_count) )
256
0
        return 0;
257
0
    i_count += i_pictcount;
258
259
0
    unsigned i_thmb_count = MP4_BoxCount( p_root, "thum" );
260
0
    if( unlikely(i_thmb_count > INT_MAX - i_count) )
261
0
        return 0;
262
0
    i_count += i_thmb_count;
263
264
0
    if ( i_count == 0 )
265
0
        return 0;
266
267
0
    pp_attach = vlc_alloc( i_count, sizeof(input_attachment_t*) );
268
0
    if( !(pp_attach) )
269
0
        return 0;
270
271
    /* Create and add valid attachments */
272
0
    i_count = 0;
273
274
    /* First add cover attachments */
275
0
    if ( p_metaroot )
276
0
    {
277
0
        const MP4_Box_t *p_data = MP4_BoxGet( p_metaroot, "covr/data" );
278
0
        unsigned i_index = 0;
279
0
        const void *ctx = NULL;
280
0
        while( (p_data = GetValidCovrMeta( p_data, &i_index, &ctx )) )
281
0
        {
282
0
            char *psz_filename;
283
284
0
            enum detectedImageType type =
285
0
                    wellKnownToimageType( BOXDATA(p_data)->e_wellknowntype );
286
0
            const char *psz_mime = getMimeType( type );
287
288
0
            if ( asprintf( &psz_filename, "%s/covr/data[%u]",
289
0
                           psz_metarootpath,
290
0
                           i_index - 1 ) > -1 )
291
0
            {
292
0
                input_attachment_t *p_attach =
293
0
                    vlc_input_attachment_New(
294
0
                            psz_filename,
295
0
                            psz_mime,
296
0
                            "Cover picture",
297
0
                            BOXDATA(p_data)->p_blob,
298
0
                            BOXDATA(p_data)->i_blob );
299
0
                free( psz_filename );
300
0
                if( p_attach )
301
0
                    pp_attach[i_count++] = p_attach;
302
0
            }
303
0
        }
304
0
    }
305
306
    /* Then quickdraw pict ones */
307
0
    const MP4_Box_t *p_pnot = MP4_BoxGet( p_root, "pnot" );
308
0
    if( p_pnot )
309
0
    {
310
0
        unsigned i_index = 0;
311
0
        const void *ctx = NULL;
312
0
        while( (p_pnot = GetValidPnotMeta( p_pnot, &i_index, &ctx )) )
313
0
        {
314
0
            char *psz_location;
315
0
            if ( asprintf( &psz_location, "pnot[%u]", i_index - 1 ) > -1 )
316
0
            {
317
0
                char rgz_path[14];
318
0
                snprintf( rgz_path, 14,
319
0
                         "/%4.4s[%u]",
320
0
                         (const char *) &p_pnot->data.p_pnot->i_type,
321
0
                         p_pnot->data.p_pnot->i_index - 1 );
322
0
                const MP4_Box_t *p_pict = MP4_BoxGet( p_root, rgz_path );
323
0
                if( p_pict )
324
0
                {
325
0
                    input_attachment_t *p_attach =
326
0
                            vlc_input_attachment_New(
327
0
                                psz_location,
328
0
                                "image/x-pict",
329
0
                                "Quickdraw image",
330
0
                                p_pict->data.p_binary->p_blob,
331
0
                                p_pict->data.p_binary->i_blob );
332
0
                    free( psz_location );
333
0
                    if( p_attach )
334
0
                        pp_attach[i_count++] = p_attach;
335
0
                }
336
0
            }
337
0
        }
338
0
    }
339
340
341
    /* Then other thumbnails */
342
0
    const MP4_Box_t *p_thum = MP4_BoxGet( p_root, "thum" );
343
0
    if( p_thum )
344
0
    {
345
0
        unsigned i_index = 0;
346
0
        const void *ctx = NULL;
347
0
        while( (p_thum = GetValidThumMeta( p_thum, &i_index, &ctx )) )
348
0
        {
349
0
            char *psz_location;
350
0
            enum detectedImageType type =
351
0
                    probeImageType( p_thum->data.p_binary->p_blob,
352
0
                                    p_thum->data.p_binary->i_blob );
353
0
            const char *psz_mime = getMimeType( type );
354
0
            if ( asprintf( &psz_location, "thum[%u]", i_index - 1 ) > -1 )
355
0
            {
356
0
                input_attachment_t *p_attach =
357
0
                        vlc_input_attachment_New(
358
0
                            psz_location,
359
0
                            psz_mime,
360
0
                            "Cover picture",
361
0
                            p_thum->data.p_binary->p_blob,
362
0
                            p_thum->data.p_binary->i_blob );
363
0
                free( psz_location );
364
0
                if( p_attach )
365
0
                    pp_attach[i_count++] = p_attach;
366
0
            }
367
0
        }
368
0
    }
369
370
    /* errors in adding attachments */
371
0
    if ( i_count == 0 )
372
0
    {
373
0
        free( pp_attach );
374
0
        return 0;
375
0
    }
376
377
0
    *ppp_attach = pp_attach;
378
379
0
    return i_count;
380
0
}