/*****************************************************************************
* attachments.c : MP4 attachments handling
*****************************************************************************
* Copyright (C) 2001-2015 VLC authors and VideoLAN
* 2019 VideoLabs
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_input.h>
#include "libmp4.h"
#include "attachments.h"
#include <limits.h>
static const char *psz_meta_roots[] = { "/moov/udta/meta/ilst",
"/moov/meta/ilst",
"/moov/udta/meta",
"/moov/udta",
"/meta/ilst",
"/udta" };
const MP4_Box_t *MP4_GetMetaRoot( const MP4_Box_t *p_root, const char **ppsz_path )
{
for( size_t i = 0; i < ARRAY_SIZE(psz_meta_roots); i++ )
{
MP4_Box_t *p_udta = MP4_BoxGet( p_root, psz_meta_roots[i] );
if ( p_udta )
{
*ppsz_path = psz_meta_roots[i];
return p_udta;
}
}
return NULL;
}
enum detectedImageType
{
IMAGE_TYPE_UNKNOWN = DATA_WKT_RESERVED,
IMAGE_TYPE_JPEG = DATA_WKT_JPEG,
IMAGE_TYPE_PNG = DATA_WKT_PNG,
IMAGE_TYPE_BMP = DATA_WKT_BMP,
};
static const struct
{
enum detectedImageType type;
const char *mime;
} rg_imageTypeToMime[] = {
{ IMAGE_TYPE_JPEG, "image/jpeg" },
{ IMAGE_TYPE_PNG, "image/png" },
{ IMAGE_TYPE_BMP, "image/bmp" },
};
static const char * getMimeType( enum detectedImageType type )
{
for( size_t i=0; i<ARRAY_SIZE(rg_imageTypeToMime); i++ )
{
if( rg_imageTypeToMime[i].type == type )
return rg_imageTypeToMime[i].mime;
}
return NULL;
}
static enum detectedImageType wellKnownToimageType( int e_wellknowntype )
{
switch( e_wellknowntype )
{
case DATA_WKT_PNG:
case DATA_WKT_JPEG:
case DATA_WKT_BMP:
return e_wellknowntype;
default:
return IMAGE_TYPE_UNKNOWN;
}
}
static enum detectedImageType probeImageType( const uint8_t *p_data, size_t i_data )
{
if( i_data > 4 )
{
if( !memcmp( p_data, "\xFF\xD8\xFF", 3 ) )
return IMAGE_TYPE_JPEG;
else if( !memcmp( p_data, ".PNG", 4 ) )
return IMAGE_TYPE_PNG;
}
return IMAGE_TYPE_UNKNOWN;
}
static const MP4_Box_t * GetValidCovrMeta( const MP4_Box_t *p_data,
unsigned *pi_index,
const void **ctx )
{
for( ; p_data; p_data = p_data->p_next )
{
if( p_data->i_type != ATOM_data || p_data == *ctx )
continue;
(*pi_index)++;
if ( !BOXDATA(p_data) ||
wellKnownToimageType(
BOXDATA(p_data)->e_wellknowntype ) == IMAGE_TYPE_UNKNOWN )
continue;
*ctx = p_data;
return p_data;
}
return NULL;
}
static const MP4_Box_t * GetValidPnotMeta( const MP4_Box_t *p_pnot,
unsigned *pi_index,
const void **ctx )
{
for( ; p_pnot; p_pnot = p_pnot->p_next )
{
if( p_pnot->i_type != ATOM_pnot || p_pnot == *ctx )
continue;
(*pi_index)++;
if( BOXDATA(p_pnot)->i_type != ATOM_PICT &&
BOXDATA(p_pnot)->i_type != ATOM_pict )
continue;
*ctx = p_pnot;
return p_pnot;
}
return NULL;
}
static const MP4_Box_t * GetValidThumMeta( const MP4_Box_t *p_thum,
unsigned *pi_index,
const void **ctx )
{
for( ; p_thum; p_thum = p_thum->p_next )
{
if( p_thum->i_type != ATOM_thum || p_thum == *ctx )
continue;
(*pi_index)++;
if ( !p_thum->data.p_binary ||
probeImageType( p_thum->data.p_binary->p_blob,
p_thum->data.p_binary->i_blob )
== IMAGE_TYPE_UNKNOWN )
continue;
*ctx = p_thum;
return p_thum;
}
return NULL;
}
int MP4_GetCoverMetaURI( const MP4_Box_t *p_root,
const MP4_Box_t *p_metaroot,
const char *psz_metapath,
vlc_meta_t *p_meta )
{
bool b_attachment_set = false;
if( !p_meta )
return VLC_EGENERIC;
if ( p_metaroot )
{
const MP4_Box_t *p_data = MP4_BoxGet( p_metaroot, "covr/data" );
unsigned i_index = 0;
const void *ctx = NULL;
if( (p_data = GetValidCovrMeta( p_data, &i_index, &ctx )) )
{
char *psz_attachment;
if ( -1 != asprintf( &psz_attachment,
"attachment://%s/covr/data[%u]",
psz_metapath, i_index - 1 ) )
{
vlc_meta_SetArtURL( p_meta, psz_attachment );
b_attachment_set = true;
free( psz_attachment );
}
}
}
const MP4_Box_t *p_pnot;
if ( !b_attachment_set && (p_pnot = MP4_BoxGet( p_root, "pnot" )) )
{
unsigned i_index = 0;
const void *ctx = NULL;
if( (p_pnot = GetValidPnotMeta( p_pnot, &i_index, &ctx )) )
{
char *psz_attachment;
if ( -1 != asprintf( &psz_attachment,
"attachment://pnot[%u]", i_index - 1 ) )
{
vlc_meta_SetArtURL( p_meta, psz_attachment );
b_attachment_set = true;
free( psz_attachment );
}
}
}
const MP4_Box_t *p_thum;
if( !b_attachment_set && (p_thum = MP4_BoxGet( p_root, "thum" )) )
{
unsigned i_index = 0;
const void *ctx = NULL;
if( (p_thum = GetValidThumMeta( p_thum, &i_index, &ctx )) )
{
char *psz_attachment;
if ( -1 != asprintf( &psz_attachment,
"attachment://thum[%u]",
i_index - 1 ) )
{
vlc_meta_SetArtURL( p_meta, psz_attachment );
b_attachment_set = true;
free( psz_attachment );
}
}
}
if( !b_attachment_set )
return VLC_EGENERIC;
return VLC_SUCCESS;
}
size_t MP4_GetAttachments( const MP4_Box_t *p_root, input_attachment_t ***ppp_attach )
{
const MP4_Box_t *p_metaroot = NULL;
const char *psz_metarootpath;
size_t i_count = 0;
input_attachment_t **pp_attach = NULL;
*ppp_attach = NULL;
/* Count MAX number of total attachments */
p_metaroot = MP4_GetMetaRoot( p_root, &psz_metarootpath );
if( p_metaroot )
{
unsigned i_covercount = MP4_BoxCount( p_metaroot, "covr/data" );
if( unlikely(i_covercount > INT_MAX - i_count) )
return 0;
i_count += i_covercount;
}
unsigned i_pictcount = MP4_BoxCount( p_root, "pnot" );
if( unlikely(i_pictcount > INT_MAX - i_count) )
return 0;
i_count += i_pictcount;
unsigned i_thmb_count = MP4_BoxCount( p_root, "thum" );
if( unlikely(i_thmb_count > INT_MAX - i_count) )
return 0;
i_count += i_thmb_count;
if ( i_count == 0 )
return 0;
pp_attach = vlc_alloc( i_count, sizeof(input_attachment_t*) );
if( !(pp_attach) )
return 0;
/* Create and add valid attachments */
i_count = 0;
/* First add cover attachments */
if ( p_metaroot )
{
const MP4_Box_t *p_data = MP4_BoxGet( p_metaroot, "covr/data" );
unsigned i_index = 0;
const void *ctx = NULL;
while( (p_data = GetValidCovrMeta( p_data, &i_index, &ctx )) )
{
char *psz_filename;
enum detectedImageType type =
wellKnownToimageType( BOXDATA(p_data)->e_wellknowntype );
const char *psz_mime = getMimeType( type );
if ( asprintf( &psz_filename, "%s/covr/data[%u]",
psz_metarootpath,
i_index - 1 ) > -1 )
{
input_attachment_t *p_attach =
vlc_input_attachment_New(
psz_filename,
psz_mime,
"Cover picture",
BOXDATA(p_data)->p_blob,
BOXDATA(p_data)->i_blob );
free( psz_filename );
if( p_attach )
pp_attach[i_count++] = p_attach;
}
}
}
/* Then quickdraw pict ones */
const MP4_Box_t *p_pnot = MP4_BoxGet( p_root, "pnot" );
if( p_pnot )
{
unsigned i_index = 0;
const void *ctx = NULL;
while( (p_pnot = GetValidPnotMeta( p_pnot, &i_index, &ctx )) )
{
char *psz_location;
if ( asprintf( &psz_location, "pnot[%u]", i_index - 1 ) > -1 )
{
const MP4_Box_t *p_pict = MP4_BoxGetVa( p_root, "/%4.4s[%u]",
(const char *) &p_pnot->data.p_pnot->i_type,
p_pnot->data.p_pnot->i_index - 1 );
if( p_pict )
{
input_attachment_t *p_attach =
vlc_input_attachment_New(
psz_location,
"image/x-pict",
"Quickdraw image",
p_pict->data.p_binary->p_blob,
p_pict->data.p_binary->i_blob );
free( psz_location );
if( p_attach )
pp_attach[i_count++] = p_attach;
}
}
}
}
/* Then other thumbnails */
const MP4_Box_t *p_thum = MP4_BoxGet( p_root, "thum" );
if( p_thum )
{
unsigned i_index = 0;
const void *ctx = NULL;
while( (p_thum = GetValidThumMeta( p_thum, &i_index, &ctx )) )
{
char *psz_location;
enum detectedImageType type =
probeImageType( p_thum->data.p_binary->p_blob,
p_thum->data.p_binary->i_blob );
const char *psz_mime = getMimeType( type );
if ( asprintf( &psz_location, "thum[%u]", i_index - 1 ) > -1 )
{
input_attachment_t *p_attach =
vlc_input_attachment_New(
psz_location,
psz_mime,
"Cover picture",
p_thum->data.p_binary->p_blob,
p_thum->data.p_binary->i_blob );
free( psz_location );
if( p_attach )
pp_attach[i_count++] = p_attach;
}
}
}
/* errors in adding attachments */
if ( i_count == 0 )
{
free( pp_attach );
return 0;
}
*ppp_attach = pp_attach;
return i_count;
}
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland
|
|