/*****************************************************************************
* essetup.h: es setup from stsd and extensions parsing
*****************************************************************************
* Copyright (C) 2001-2004, 2010, 2014 VLC authors and VideoLAN
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
* 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 <stdbit.h>
#include "mp4.h"
#include "avci.h"
#include "../xiph.h"
#include "../../packetizer/iso_color_tables.h"
#include "../../packetizer/mpeg4systems.h"
#include "qt_palette.h"
#include <vlc_demux.h>
#include <vlc_aout.h>
#include <assert.h>
static void CopyExtradata( const uint8_t *p_extra, size_t i_extra,
es_format_t *fmt )
{
if( i_extra > 0 && !fmt->i_extra )
{
fmt->p_extra = malloc( i_extra );
if( fmt->p_extra )
{
fmt->i_extra = i_extra;
memcpy( fmt->p_extra, p_extra, i_extra );
}
}
}
static uint32_t GetSampleType( demux_t *p_demux, const MP4_Box_t *p_sample )
{
const MP4_Box_t *p_frma;
if( ( p_frma = MP4_BoxGet( p_sample, "sinf/frma" ) ) && BOXDATA(p_frma) )
{
msg_Warn( p_demux, "Original Format Box: %4.4s", (char *)&BOXDATA(p_frma)->i_type );
return BOXDATA(p_frma)->i_type;
}
return p_sample->i_type;
}
static void SetupGlobalExtensions( const MP4_Box_t *p_sample,
es_format_t *p_fmt )
{
if( !p_fmt->i_bitrate )
{
const MP4_Box_t *p_btrt = MP4_BoxGet( p_sample, "btrt" );
if( p_btrt && BOXDATA(p_btrt) )
{
p_fmt->i_bitrate = BOXDATA(p_btrt)->i_avg_bitrate;
}
}
const MP4_Box_t *p_glbl = MP4_BoxGet( p_sample, "glbl" );
if( p_glbl && p_glbl->data.p_binary && p_glbl->data.p_binary->p_blob )
{
CopyExtradata( p_glbl->data.p_binary->p_blob,
p_glbl->data.p_binary->i_blob, p_fmt );
}
}
static void SetupESDS( demux_t *p_demux, const mp4_track_t *p_track,
const MP4_descriptor_decoder_config_t *p_decconfig,
es_format_t *p_fmt )
{
/* First update information based on i_objectTypeIndication */
switch( p_decconfig->i_objectProfileIndication )
{
/* Private ID */
case( 0xe0 ): /* NeroDigital: dvd subs */
if( p_fmt->i_cat == SPU_ES )
{
p_fmt->i_codec = VLC_CODEC_SPU;
if( p_track->i_width > 0 )
p_fmt->subs.spu.i_original_frame_width = p_track->i_width;
if( p_track->i_height > 0 )
p_fmt->subs.spu.i_original_frame_height = p_track->i_height;
}
break;
case( 0xe1 ): /* QCelp for 3gp */
if( p_fmt->i_cat == AUDIO_ES )
{
p_fmt->i_codec = VLC_CODEC_QCELP;
}
break;
/* Fallback */
default:
if( MPEG4_get_codec_by_ObjectType( p_decconfig->i_objectProfileIndication,
p_decconfig->p_decoder_specific_info,
p_decconfig->i_decoder_specific_info_len,
&p_fmt->i_codec,
&p_fmt->i_profile ) )
break;
/* Unknown entry, but don't touch i_fourcc */
msg_Warn( p_demux,
"unknown objectProfileIndication(0x%x) (Track[ID 0x%x])",
p_decconfig->i_objectProfileIndication,
p_track->i_track_ID );
return;
}
p_fmt->i_original_fourcc = 0; /* so we don't have MP4A as original fourcc */
p_fmt->i_bitrate = p_decconfig->i_avg_bitrate;
CopyExtradata( p_decconfig->p_decoder_specific_info,
p_decconfig->i_decoder_specific_info_len,
p_fmt );
if( p_fmt->i_codec == VLC_CODEC_SPU &&
p_fmt->i_extra >= sizeof(p_fmt->subs.spu.palette) )
{
for (size_t i = 0; i < ARRAY_SIZE(p_fmt->subs.spu.palette); i++)
{
p_fmt->subs.spu.palette[i] = GetDWBE((char*)p_fmt->p_extra + i * 4);
}
p_fmt->subs.spu.b_palette = true;
}
}
static int SetupRTPReceptionHintTrack( demux_t *p_demux, const mp4_track_t *p_track,
const MP4_Box_t *p_sample, es_format_t *p_fmt,
track_config_t *params )
{
const uint32_t i_sample_type = GetSampleType( p_demux, p_sample );
p_fmt->i_original_fourcc = i_sample_type;
const MP4_Box_t *p_sdp = MP4_BoxGet( p_track->p_track, "udta/hnti/sdp " );
if( !p_sdp || !BOXDATA(p_sdp)->psz_text )
{
msg_Err(p_demux, "Required 'sdp '-box not found");
return 0;
}
char *strtok_state;
char * pch = strtok_r(BOXDATA(p_sdp)->psz_text, " =\n", &strtok_state); /* media entry */
if( pch && pch[0] != 'm' )
{
msg_Err(p_demux, "No Media entry found in SDP:%s", pch);
return 0;
}
if( !( pch = strtok_r(NULL, " =\n", &strtok_state) ) ) /* media type */
return 0;
/* media type has already been checked */
msg_Dbg(p_demux, "sdp: media type:%s", pch);
if( !( pch = strtok_r(NULL, " =\n", &strtok_state) ) ) /* port */
return 0;
msg_Dbg(p_demux, "sdp: port:%s", pch);
if( !( pch = strtok_r(NULL, " =\n", &strtok_state) ) ) /* protocol */
return 0;
msg_Dbg(p_demux, "sdp: protocol:%s", pch);
if( !( pch = strtok_r(NULL, " =\n", &strtok_state) ) ) /* fmt */
return 0;
bool codec_set = false;
/* process rtp types until we get an attribute field or end of sdp */
while( pch && pch[0] != 'a' )
{
int rtp_payload = atoi(pch);
msg_Dbg(p_demux, "sdp: payload type:%d", rtp_payload);
if( !codec_set )
{
/* Payload types 34 and under have a set type and can be identified here */
switch( rtp_payload )
{
case 3:
p_fmt->i_codec = VLC_CODEC_GSM;
codec_set = true;
break;
default:
break;
}
}
pch = strtok_r(NULL, " =\n", &strtok_state); /* attribute or additional payload type */
if( !pch && !codec_set )
return 0;
}
while( pch && pch[0] == 'a' )
{
if( !( pch = strtok_r(NULL, " :=\n", &strtok_state) ) ) /* attribute type */
return 0;
msg_Dbg(p_demux, "sdp: attribute type:%s", pch);
if( !strcmp(pch, "rtpmap") )
{
if( !( pch = strtok_r(NULL, " :=\n", &strtok_state) ) ) /* payload type */
return 0;
msg_Dbg(p_demux, "sdp: payload type:%s", pch);
if( !(pch = strtok_r(NULL, " /:=\n", &strtok_state) ) ) /* encoding name */
return 0;
msg_Dbg(p_demux, "sdp: encoding name:%s", pch);
/* Simply adding codec recognition should work for most codecs */
/* Codecs using slices need their picture constructed from sample */
if( !strcmp(pch, "H264") )
{
p_fmt->i_codec = VLC_CODEC_H264;
/* ******* sending AnnexB ! */
p_fmt->b_packetized = false;
}
else if( !strcmp(pch, "GSM") )
{
p_fmt->i_codec = VLC_CODEC_GSM;
}
else if( !strcmp(pch, "Speex") )
{
p_fmt->i_codec = VLC_CODEC_SPEEX;
}
else if( !codec_set )
{
msg_Err(p_demux, "Support for codec contained in RTP \
Reception Hint Track RTP stream has not been added");
return 0;
}
if( !( pch = strtok_r(NULL, " :=\n", &strtok_state) ) ) /* clock rate */
return 0;
int clock_rate = atoi(pch);
msg_Dbg(p_demux, "sdp clock rate:%d", clock_rate);
if( p_fmt->i_cat == AUDIO_ES )
p_fmt->audio.i_rate = clock_rate;
}
pch = strtok_r(NULL, " =\n", &strtok_state); /* next attribute */
}
const MP4_Box_t *p_tims = MP4_BoxGet(p_sample, "tims");
if( p_tims && BOXDATA(p_tims) && BOXDATA(p_tims)->i_timescale )
{
params->i_timescale_override = BOXDATA(p_tims)->i_timescale;
}
else
{
msg_Warn(p_demux, "Missing mandatory box tims");
return 0;
}
#if 0
const MP4_Box_t *p_tssy = MP4_BoxGet(p_sample, "tssy");
if( p_tssy && BOXDATA(p_tssy) )
{
/* take the 2 last bits which indicate the synchronization mode */
params->sync_mode = (RTP_timstamp_synchronization_t)
BOXDATA(p_tssy)->i_reserved_timestamp_sync & 0x03;
}
const MP4_Box_t *p_tsro = MP4_BoxGet(p_sample, "tsro");
if( p_tsro && BOXDATA(p_tsro) )
params->i_tsro_offset = BOXDATA(p_tsro)->i_offset;
else
msg_Dbg(p_demux, "No tsro box present");
msg_Dbg(p_demux, "setting tsro: %" PRId32, p_track->i_tsro_offset);
#endif
return 1;
}
int SetupVideoES( demux_t *p_demux, const mp4_track_t *p_track, const MP4_Box_t *p_sample,
es_format_t *p_fmt, track_config_t *p_cfg )
{
track_config_t trackparams, *params = &trackparams;
const MP4_Box_data_sample_vide_t *p_vide = p_sample->data.p_sample_vide;
if(!p_vide)
return 0;
const uint32_t i_sample_type = GetSampleType( p_demux, p_sample );
p_fmt->video.i_width = p_vide->i_width;
p_fmt->video.i_height = p_vide->i_height;
/* fall on display size */
if( p_fmt->video.i_width <= 0 )
p_fmt->video.i_width = p_track->i_width;
if( p_fmt->video.i_height <= 0 )
p_fmt->video.i_height = p_track->i_height;
/* Find out apect ratio from display size */
if( p_track->i_width > 0 && p_track->i_height > 0 &&
/* Work-around buggy muxed files */
p_vide->i_width != p_track->i_width )
{
p_fmt->video.i_sar_num = p_track->i_width * p_fmt->video.i_height;
p_fmt->video.i_sar_den = p_track->i_height * p_fmt->video.i_width;
}
/* Support for cropping (eg. in H263 files) */
p_fmt->video.i_visible_width = p_fmt->video.i_width;
p_fmt->video.i_visible_height = p_fmt->video.i_height;
/* Rotation */
switch( (int)p_track->f_rotation ) {
case 90:
p_fmt->video.orientation = ORIENT_ROTATED_90;
break;
case 180:
if (p_track->i_flip == 1) {
p_fmt->video.orientation = ORIENT_VFLIPPED;
} else {
p_fmt->video.orientation = ORIENT_ROTATED_180;
}
break;
case 270:
p_fmt->video.orientation = ORIENT_ROTATED_270;
break;
}
/* Flip, unless already flipped */
if (p_track->i_flip == 1 && (int)p_track->f_rotation != 180) {
video_transform_t transform = (video_transform_t)p_fmt->video.orientation;
/* Flip first then rotate */
p_fmt->video.orientation = ORIENT_HFLIPPED;
video_format_TransformBy(&p_fmt->video, transform);
}
/* Set 360 video mode */
p_fmt->video.projection_mode = PROJECTION_MODE_RECTANGULAR;
const MP4_Box_t *p_uuid = MP4_BoxGet( p_track->p_track, "uuid" );
for( ; p_uuid; p_uuid = p_uuid->p_next)
{
if( p_uuid->i_type == ATOM_uuid
&& !CmpUUID( &p_uuid->i_uuid, &XML360BoxUUID )
&& p_uuid->data.p_360 )
{
p_fmt->video.projection_mode = p_uuid->data.p_360->i_projection_mode;
switch (p_uuid->data.p_360->e_stereo_mode)
{
case XML360_STEREOSCOPIC_TOP_BOTTOM:
p_fmt->video.multiview_mode = MULTIVIEW_STEREO_TB;
break;
case XML360_STEREOSCOPIC_LEFT_RIGHT:
p_fmt->video.multiview_mode = MULTIVIEW_STEREO_SBS;
break;
default:
p_fmt->video.multiview_mode = MULTIVIEW_2D;
break;
}
}
}
const MP4_Box_t *p_st3d = MP4_BoxGet( p_sample, "st3d" );
if (p_st3d && BOXDATA(p_st3d))
{
switch( BOXDATA(p_st3d)->i_stereo_mode )
{
case ST3D_MONOSCOPIC:
p_fmt->video.multiview_mode = MULTIVIEW_2D;
break;
case ST3D_STEREOSCOPIC_TOP_BOTTOM:
p_fmt->video.multiview_mode = MULTIVIEW_STEREO_TB;
break;
case ST3D_STEREOSCOPIC_LEFT_RIGHT:
p_fmt->video.multiview_mode = MULTIVIEW_STEREO_SBS;
break;
default:
msg_Warn( p_demux, "Unknown stereo mode %d", BOXDATA(p_st3d)->i_stereo_mode );
break;
}
}
else
{
for( p_uuid = MP4_BoxGet( p_sample, "uuid" ); p_uuid;
p_uuid = p_uuid->p_next )
{
if( p_uuid->i_type == ATOM_uuid &&
!CmpUUID( &p_uuid->i_uuid, &PS3DDSBoxUUID ) &&
p_uuid->data.p_binary &&
p_uuid->data.p_binary->i_blob == 4 &&
!memcmp( p_uuid->data.p_binary->p_blob, "\x82\x81\x10\x02", 4 ) )
{
p_fmt->video.multiview_mode = MULTIVIEW_STEREO_FRAME;
break;
}
}
}
const MP4_Box_t *p_prhd = MP4_BoxGet( p_sample, "sv3d/proj/prhd" );
if (p_prhd && BOXDATA(p_prhd))
{
vlc_viewpoint_from_euler(&p_fmt->video.pose,
BOXDATA(p_prhd)->f_pose_yaw_degrees,
BOXDATA(p_prhd)->f_pose_pitch_degrees,
BOXDATA(p_prhd)->f_pose_roll_degrees);
}
const MP4_Box_t *p_equi = MP4_BoxGet( p_sample, "sv3d/proj/equi" );
const MP4_Box_t *p_cbmp = MP4_BoxGet( p_sample, "sv3d/proj/cbmp" );
if (p_equi && BOXDATA(p_equi))
p_fmt->video.projection_mode = PROJECTION_MODE_EQUIRECTANGULAR;
else if (p_cbmp && BOXDATA(p_cbmp))
p_fmt->video.projection_mode = PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD;
/* It's a little ugly but .. there are special cases */
switch( i_sample_type )
{
case( VLC_FOURCC( 's', '2', '6', '3' ) ):
p_fmt->i_codec = VLC_CODEC_H263;
break;
case VLC_FOURCC('y','v','1','2'):
p_fmt->i_codec = VLC_CODEC_YV12;
break;
case VLC_FOURCC('y','u','v','2'):
p_fmt->i_codec = VLC_CODEC_YUV2;
break;
case VLC_FOURCC('A','B','G','R'):
p_fmt->i_codec = VLC_CODEC_ARGB;
break;
case VLC_FOURCC('2','4','B','G'):
p_fmt->i_codec = VLC_CODEC_BGR24;
break;
case VLC_FOURCC('r','a','w',' '):
switch( p_vide->i_depth ) {
case 16:
p_fmt->i_codec = VLC_CODEC_RGB555BE;
break;
case 24:
p_fmt->i_codec = VLC_CODEC_RGB24;
break;
case 32:
p_fmt->i_codec = VLC_CODEC_ARGB;
break;
case 32 + 8:
p_fmt->i_codec = VLC_CODEC_GREY;
break;
default:
msg_Dbg( p_demux, "Unrecognized raw video format (depth = %" PRIu16 ")",
p_vide->i_depth );
p_fmt->i_codec = i_sample_type;
break;
}
break;
case( VLC_FOURCC( 'r', 'r', 't', 'p' ) ): /* RTP Reception Hint Track */
{
if( !SetupRTPReceptionHintTrack( p_demux, p_track, p_sample, p_fmt, params ) )
p_fmt->i_codec = i_sample_type;
break;
}
default:
p_fmt->i_codec = i_sample_type;
break;
}
/* Read extensions */
const MP4_Box_t *p_clap = MP4_BoxGet( p_sample, "clap" );
if( p_clap && BOXDATA(p_clap) &&
BOXDATA(p_clap)->i_width + BOXDATA(p_clap)->i_x_offset <= p_fmt->video.i_width &&
BOXDATA(p_clap)->i_height + BOXDATA(p_clap)->i_y_offset <= p_fmt->video.i_height )
{
p_fmt->video.i_visible_width = BOXDATA(p_clap)->i_width;
p_fmt->video.i_visible_height = BOXDATA(p_clap)->i_height;
p_fmt->video.i_x_offset = BOXDATA(p_clap)->i_x_offset;
p_fmt->video.i_y_offset = BOXDATA(p_clap)->i_y_offset;
}
/* Set up A/R from extension atom */
const MP4_Box_t *p_pasp = MP4_BoxGet( p_sample, "pasp" );
if( p_pasp && BOXDATA(p_pasp) && BOXDATA(p_pasp)->i_horizontal_spacing > 0 &&
BOXDATA(p_pasp)->i_vertical_spacing > 0 )
{
p_fmt->video.i_sar_num = BOXDATA(p_pasp)->i_horizontal_spacing;
p_fmt->video.i_sar_den = BOXDATA(p_pasp)->i_vertical_spacing;
}
const MP4_Box_t *p_fiel = MP4_BoxGet( p_sample, "fiel" );
if( p_fiel && BOXDATA(p_fiel) )
{
p_cfg->i_block_flags = BOXDATA(p_fiel)->i_flags;
}
const MP4_Box_t *p_colr = MP4_BoxGet( p_sample, "colr" );
if ( p_colr != NULL )
{
if ( BOXDATA(p_colr)->i_type == VLC_FOURCC( 'n', 'c', 'l', 'c' ) ||
BOXDATA(p_colr)->i_type == VLC_FOURCC( 'n', 'c', 'l', 'x' ) )
{
p_fmt->video.primaries =
iso_23001_8_cp_to_vlc_primaries( BOXDATA( p_colr )->nclc.i_primary_idx );
p_fmt->video.transfer =
iso_23001_8_tc_to_vlc_xfer( BOXDATA( p_colr )->nclc.i_transfer_function_idx );
p_fmt->video.space =
iso_23001_8_mc_to_vlc_coeffs( BOXDATA( p_colr )->nclc.i_matrix_idx );
if ( BOXDATA(p_colr)->i_type == VLC_FOURCC( 'n', 'c', 'l', 'x' ) &&
(BOXDATA(p_colr)->nclc.i_full_range >> 7) != 0 )
p_fmt->video.color_range = COLOR_RANGE_FULL;
else
p_fmt->video.color_range = COLOR_RANGE_LIMITED;
}
}
const MP4_Box_t *p_dvcC = MP4_BoxGet( p_sample, "dvcC" );
if( !p_dvcC )
p_dvcC = MP4_BoxGet( p_sample, "dvvC" );
if( !p_dvcC )
p_dvcC = MP4_BoxGet( p_sample, "dvwC" );
if( p_dvcC && BOXDATA(p_dvcC) )
{
const MP4_Box_data_dvcC_t *p_data = BOXDATA( p_dvcC );
p_fmt->video.dovi.version_major = p_data->i_version_major;
p_fmt->video.dovi.version_minor = p_data->i_version_minor;
p_fmt->video.dovi.profile = p_data->i_profile;
p_fmt->video.dovi.level = p_data->i_level;
p_fmt->video.dovi.rpu_present = p_data->i_rpu_present;
p_fmt->video.dovi.bl_present = p_data->i_bl_present;
p_fmt->video.dovi.el_present = p_data->i_el_present;
}
SetupGlobalExtensions( p_sample, p_fmt );
/* now see if esds is present and if so create a data packet
with decoder_specific_info */
MP4_Box_t *p_esds = MP4_BoxGet( p_sample, "esds" );
if ( p_esds && BOXDATA(p_esds) && BOXDATA(p_esds)->es_descriptor.p_decConfigDescr )
{
assert(i_sample_type == ATOM_mp4v);
SetupESDS( p_demux, p_track, BOXDATA(p_esds)->es_descriptor.p_decConfigDescr, p_fmt );
}
else switch( i_sample_type )
{
/* qt decoder, send the complete chunk */
case VLC_FOURCC ('h', 'd', 'v', '1'): // HDV 720p30
case VLC_FOURCC ('h', 'd', 'v', '2'): // HDV 1080i60
case VLC_FOURCC ('h', 'd', 'v', '3'): // HDV 1080i50
case VLC_FOURCC ('h', 'd', 'v', '5'): // HDV 720p25
case VLC_FOURCC ('m', 'x', '5', 'n'): // MPEG2 IMX NTSC 525/60 50mb/s produced by FCP
case VLC_FOURCC ('m', 'x', '5', 'p'): // MPEG2 IMX PAL 625/60 50mb/s produced by FCP
case VLC_FOURCC ('m', 'x', '4', 'n'): // MPEG2 IMX NTSC 525/60 40mb/s produced by FCP
case VLC_FOURCC ('m', 'x', '4', 'p'): // MPEG2 IMX PAL 625/60 40mb/s produced by FCP
case VLC_FOURCC ('m', 'x', '3', 'n'): // MPEG2 IMX NTSC 525/60 30mb/s produced by FCP
case VLC_FOURCC ('m', 'x', '3', 'p'): // MPEG2 IMX PAL 625/50 30mb/s produced by FCP
case VLC_FOURCC ('x', 'd', 'v', '2'): // XDCAM HD 1080i60
case VLC_FOURCC ('A', 'V', 'm', 'p'): // AVID IMX PAL
p_fmt->i_codec = VLC_CODEC_MPGV;
break;
/* qt decoder, send the complete chunk */
case VLC_CODEC_SVQ1:
case VLC_CODEC_SVQ3:
case VLC_FOURCC( 'V', 'P', '3', '1' ):
case VLC_FOURCC( '3', 'I', 'V', '1' ):
case VLC_FOURCC( 'Z', 'y', 'G', 'o' ):
{
CopyExtradata( p_sample->data.p_sample_vide->p_qt_image_description,
p_sample->data.p_sample_vide->i_qt_image_description,
p_fmt );
break;
}
case VLC_FOURCC( 'A', 'V', 'j', '2' ):
p_fmt->i_codec = VLC_CODEC_JPEG2000;
/* final decoded resolution stored in ARES w, h, nbfields to group
* but since avcodec can't tell... */
break;
case VLC_FOURCC('j', 'p', 'e', 'g'):
p_fmt->i_codec = VLC_CODEC_MJPG;
break;
case VLC_CODEC_FFV1:
{
MP4_Box_t *p_binary = MP4_BoxGet( p_sample, "glbl" );
if( p_binary && BOXDATA(p_binary) )
{
CopyExtradata( BOXDATA(p_binary)->p_blob,
BOXDATA(p_binary)->i_blob,
p_fmt );
}
break;
}
case VLC_FOURCC( 'v', 'c', '-', '1' ):
{
MP4_Box_t *p_dvc1 = MP4_BoxGet( p_sample, "dvc1" );
if( p_dvc1 && BOXDATA(p_dvc1) )
{
CopyExtradata( BOXDATA(p_dvc1)->p_vc1,
BOXDATA(p_dvc1)->i_vc1,
p_fmt );
}
else
{
msg_Err( p_demux, "missing dvc1" );
}
break;
}
case ATOM_av01:
{
static_assert(ATOM_av01 == VLC_CODEC_AV1, "VLC_CODEC_AV1 != ATOM_av01");
MP4_Box_t *p_av1C = MP4_BoxGet( p_sample, "av1C" );
if( p_av1C && BOXDATA(p_av1C) )
{
p_fmt->i_profile = BOXDATA(p_av1C)->i_profile;
p_fmt->i_level = BOXDATA(p_av1C)->i_level;
CopyExtradata( BOXDATA(p_av1C)->p_av1C,
BOXDATA(p_av1C)->i_av1C,
p_fmt );
if (p_fmt->i_extra <= 4)
p_fmt->b_packetized = false; // force full extradata by the packetizer
}
break;
}
case VLC_FOURCC( 'v', 'v', 'c', '1' ):
{
MP4_Box_t *p_vvcC = MP4_BoxGet( p_sample, "vvcC" );
if( p_vvcC && p_vvcC->data.p_binary &&
p_vvcC->data.p_binary->i_blob > 4 )
{
CopyExtradata( ((uint8_t *)p_vvcC->data.p_binary->p_blob) + 4,
p_vvcC->data.p_binary->i_blob - 4,
p_fmt );
}
break;
}
case ATOM_apv1:
{
const MP4_Box_t *p_binary = MP4_BoxGet( p_sample, "apvC" );
if( p_binary && p_binary->data.p_binary->i_blob > 4 &&
GetDWBE(p_binary->data.p_binary->p_blob) == 0 ) /* fullbox header */
{
size_t i_extra = p_binary->data.p_binary->i_blob - 4;
uint8_t *p_extra = malloc(i_extra);
if( likely( p_extra ) )
{
p_fmt->i_extra = i_extra;
p_fmt->p_extra = p_extra;
memcpy( p_extra, ((uint8_t *)p_binary->data.p_binary->p_blob) + 4, i_extra);
}
}
break;
}
/* avc1: send avcC (h264 without annexe B, ie without start code)*/
case VLC_FOURCC( 'a', 'v', 'c', '3' ):
case VLC_FOURCC( 'a', 'v', 'c', '1' ):
case VLC_FOURCC( 'd', 'v', 'a', '1' ): /* DolbyVision */
case VLC_FOURCC( 'd', 'v', 'a', 'v' ): /* DolbyVision */
{
MP4_Box_t *p_avcC = MP4_BoxGet( p_sample, "avcC" );
if( p_avcC && BOXDATA(p_avcC) )
{
p_fmt->i_profile = BOXDATA(p_avcC)->i_profile;
p_fmt->i_level = BOXDATA(p_avcC)->i_level;
CopyExtradata( BOXDATA(p_avcC)->p_avcC,
BOXDATA(p_avcC)->i_avcC,
p_fmt );
}
else
{
msg_Err( p_demux, "missing avcC" );
}
break;
}
case VLC_FOURCC( 'h', 'v', 'c', '1' ):
case VLC_FOURCC( 'h', 'e', 'v', '1' ):
case VLC_FOURCC( 'd', 'v', 'h', 'e' ): /* DolbyVision */
case VLC_FOURCC( 'd', 'v', 'h', '1' ): /* DolbyVision */
{
MP4_Box_t *p_hvcC = MP4_BoxGet( p_sample, "hvcC" );
/* Handle DV fourcc collision at demux level */
if( i_sample_type == VLC_FOURCC( 'd', 'v', 'h', '1' ) )
p_fmt->i_codec = VLC_FOURCC( 'd', 'v', 'h', 'e' );
if( p_hvcC && p_hvcC->data.p_binary )
{
CopyExtradata( p_hvcC->data.p_binary->p_blob,
p_hvcC->data.p_binary->i_blob,
p_fmt );
}
else
{
msg_Err( p_demux, "missing hvcC" );
}
break;
}
case ATOM_vp08:
case ATOM_vp09:
case ATOM_vp10:
{
const MP4_Box_t *p_vpcC = MP4_BoxGet( p_sample, "vpcC" );
if( p_vpcC && BOXDATA(p_vpcC) )
{
const MP4_Box_data_vpcC_t *p_data = BOXDATA(p_vpcC);
if( i_sample_type == ATOM_vp10 )
p_fmt->i_codec = VLC_CODEC_VP10;
else if( i_sample_type == ATOM_vp09 )
p_fmt->i_codec = VLC_CODEC_VP9;
else
p_fmt->i_codec = VLC_CODEC_VP8;
p_fmt->i_profile = p_data->i_profile;
p_fmt->i_level = p_data->i_level;
if( p_data->i_version == 0 ) /* old deprecated */
{
const uint8_t colorspacesmapping[] =
{
COLOR_SPACE_UNDEF,
COLOR_SPACE_BT601,
COLOR_SPACE_BT709,
COLOR_SPACE_SMPTE_170,
COLOR_SPACE_SMPTE_240,
COLOR_SPACE_BT2020,
COLOR_SPACE_BT2020,
COLOR_SPACE_SRGB,
};
if( p_data->i_color_primaries < ARRAY_SIZE(colorspacesmapping) )
p_fmt->video.space = colorspacesmapping[p_data->i_color_primaries];
if( p_data->i_xfer_function == 0 )
p_fmt->video.transfer = TRANSFER_FUNC_BT709;
else if ( p_data->i_xfer_function == 1 )
p_fmt->video.transfer = TRANSFER_FUNC_SMPTE_ST2084;
}
else
{
p_fmt->video.primaries =
iso_23001_8_cp_to_vlc_primaries( p_data->i_color_primaries );
p_fmt->video.transfer =
iso_23001_8_tc_to_vlc_xfer( p_data->i_xfer_function );
p_fmt->video.space =
iso_23001_8_mc_to_vlc_coeffs( p_data->i_matrix_coeffs );
}
p_fmt->video.color_range = p_data->i_fullrange ? COLOR_RANGE_FULL : COLOR_RANGE_LIMITED;
if (p_fmt->i_profile == -1 && p_fmt->i_level == -1)
// HACK: keep the bits per sample in i_level
p_fmt->i_level = p_data->i_bit_depth;
CopyExtradata( p_data->p_codec_init_data,
p_data->i_codec_init_datasize,
p_fmt );
const MP4_Box_t *p_SmDm = MP4_BoxGet( p_sample, "SmDm" );
if( !p_SmDm )
p_SmDm = MP4_BoxGet( p_sample, "mdcv" );
if( p_SmDm && BOXDATA(p_SmDm) )
{
memcpy( p_fmt->video.mastering.primaries,
BOXDATA(p_SmDm)->primaries, sizeof(p_fmt->video.mastering.primaries) );
memcpy( p_fmt->video.mastering.white_point,
BOXDATA(p_SmDm)->white_point, sizeof(p_fmt->video.mastering.white_point) );
p_fmt->video.mastering.max_luminance = BOXDATA(p_SmDm)->i_luminanceMax;
p_fmt->video.mastering.min_luminance = BOXDATA(p_SmDm)->i_luminanceMin;
}
const MP4_Box_t *p_CoLL = MP4_BoxGet( p_sample, "CoLL" );
if( !p_CoLL )
p_CoLL = MP4_BoxGet( p_sample, "clli" );
if( p_CoLL && BOXDATA(p_CoLL) )
{
p_fmt->video.lighting.MaxCLL = BOXDATA(p_CoLL)->i_maxCLL;
p_fmt->video.lighting.MaxFALL = BOXDATA(p_CoLL)->i_maxFALL;
}
}
}
break;
case ATOM_WMV3:
p_cfg->p_asf = MP4_BoxGet( p_sample, "ASF " );
/* fallthrough */
case ATOM_H264:
case VLC_FOURCC('W','V','C','1'):
{
MP4_Box_t *p_strf = MP4_BoxGet( p_sample, "strf" );
if ( p_strf && BOXDATA(p_strf) )
{
p_fmt->video.i_width = BOXDATA(p_strf)->bmiHeader.biWidth;
p_fmt->video.i_visible_width = p_fmt->video.i_width;
p_fmt->video.i_height = BOXDATA(p_strf)->bmiHeader.biHeight;
p_fmt->video.i_visible_height =p_fmt->video.i_height;
p_fmt->video.i_x_offset = 0;
p_fmt->video.i_y_offset = 0;
CopyExtradata( BOXDATA(p_strf)->p_extra,
BOXDATA(p_strf)->i_extra,
p_fmt );
}
/* Mostly bogus muxs with old codecs
* see 71c4cc66456facb59cd0eef1626be1be1befeb39 */
p_cfg->b_ignore_implicit_pts = true;
break;
}
case VLC_FOURCC( 'a', 'i', '5', 'p' ):
case VLC_FOURCC( 'a', 'i', '5', 'q' ):
case VLC_FOURCC( 'a', 'i', '5', '2' ):
case VLC_FOURCC( 'a', 'i', '5', '3' ):
case VLC_FOURCC( 'a', 'i', '5', '5' ):
case VLC_FOURCC( 'a', 'i', '5', '6' ):
case VLC_FOURCC( 'a', 'i', '1', 'p' ):
case VLC_FOURCC( 'a', 'i', '1', 'q' ):
case VLC_FOURCC( 'a', 'i', '1', '2' ):
case VLC_FOURCC( 'a', 'i', '1', '3' ):
case VLC_FOURCC( 'a', 'i', '1', '5' ):
case VLC_FOURCC( 'a', 'i', '1', '6' ):
{
if( !p_fmt->i_extra && p_fmt->video.i_width < UINT16_MAX &&
p_fiel && BOXDATA(p_fiel) )
{
p_fmt->p_extra =
AVCi_create_AnnexB( p_fmt->video.i_width,
!!BOXDATA(p_fiel)->i_flags, &p_fmt->i_extra );
}
break;
}
case VLC_FOURCC('s','m','c',' '):
case VLC_FOURCC('8','B','P','S'):
{
if( p_sample->data.p_sample_vide->p_palette )
{
p_fmt->video.p_palette = malloc(sizeof(video_palette_t));
if( p_fmt->video.p_palette )
*p_fmt->video.p_palette = *p_sample->data.p_sample_vide->p_palette;
}
else if(qt_palette_depth_has_default(p_sample->data.p_sample_vide->i_depth))
{
p_fmt->video.p_palette = qt_make_palette(p_sample->data.p_sample_vide->i_depth);
}
break;
}
default:
msg_Dbg( p_demux, "Unrecognized FourCC %4.4s", (char *)&i_sample_type );
break;
}
/* Codec like QTRLE will need to provide depth. */
if (p_fmt->i_profile == -1 && p_fmt->i_level == -1 &&
p_fmt->video.i_chroma == 0)
p_fmt->i_level = p_vide->i_depth;
return 1;
}
static bool SetupAudioFromWaveFormatEx( const MP4_Box_t *p_WMA2, es_format_t *p_fmt )
{
if( p_WMA2 && BOXDATA(p_WMA2) )
{
wf_tag_to_fourcc(BOXDATA(p_WMA2)->Format.wFormatTag, &p_fmt->i_codec, NULL);
p_fmt->audio.i_channels = BOXDATA(p_WMA2)->Format.nChannels;
p_fmt->audio.i_rate = BOXDATA(p_WMA2)->Format.nSamplesPerSec;
p_fmt->i_bitrate = BOXDATA(p_WMA2)->Format.nAvgBytesPerSec * 8;
p_fmt->audio.i_blockalign = BOXDATA(p_WMA2)->Format.nBlockAlign;
p_fmt->audio.i_bitspersample = BOXDATA(p_WMA2)->Format.wBitsPerSample;
CopyExtradata( BOXDATA(p_WMA2)->p_extra,
BOXDATA(p_WMA2)->i_extra,
p_fmt );
return true;
}
return false;
}
int SetupAudioES( demux_t *p_demux, const mp4_track_t *p_track,
const MP4_Box_t *p_sample, es_format_t *p_fmt,
track_config_t *p_cfg )
{
const MP4_Box_data_sample_soun_t *p_soun = p_sample->data.p_sample_soun;
if(!p_soun)
return 0;
const uint32_t i_sample_type = GetSampleType( p_demux, p_sample );
p_fmt->i_original_fourcc = i_sample_type;
p_fmt->audio.i_channels = p_soun->i_channelcount;
p_fmt->audio.i_rate = p_soun->i_sampleratehi;
if( p_soun->i_qt_version == 0 ) /* otherwise defaults to meaningless 16 */
{
p_fmt->audio.i_bitspersample = p_soun->i_samplesize;
p_fmt->i_bitrate = (unsigned) p_soun->i_channelcount * p_soun->i_sampleratehi *
p_soun->i_samplesize;
}
/* Endianness atom */
const MP4_Box_t *p_enda = MP4_BoxGet( p_sample, "wave/enda" );
if( !p_enda )
p_enda = MP4_BoxGet( p_sample, "enda" );
/* It's a little ugly but .. there are special cases */
switch( i_sample_type )
{
case( VLC_FOURCC( 'r', 'r', 't', 'p' ) ): /* RTP Reception Hint Track */
{
if( !SetupRTPReceptionHintTrack( p_demux, p_track, p_sample, p_fmt, p_cfg ) )
return 0;
break;
}
case ATOM_agsm: /* Apple gsm 33 bytes != MS GSM (agsm fourcc, 65 bytes) */
p_fmt->i_codec = VLC_CODEC_GSM;
break;
case( VLC_FOURCC( '.', 'm', 'p', '3' ) ):
case( VLC_FOURCC( 'm', 's', 0x00, 0x55 ) ):
{
p_fmt->i_codec = VLC_CODEC_MP3;
p_fmt->b_packetized = false;
break;
}
case ATOM_XiVs:
{
const MP4_Box_t *p_vCtH = MP4_BoxGet( p_sample, "wave/vCtH" ); /* kCookieTypeVorbisHeader */
const MP4_Box_t *p_vCtd = MP4_BoxGet( p_sample, "wave/vCt#" ); /* kCookieTypeVorbisComments */
const MP4_Box_t *p_vCtC = MP4_BoxGet( p_sample, "wave/vCtC" ); /* kCookieTypeVorbisCodebooks */
if( p_vCtH && p_vCtH->data.p_binary &&
p_vCtd && p_vCtd->data.p_binary &&
p_vCtC && p_vCtC->data.p_binary )
{
size_t headers_sizes[3] = {
p_vCtH->data.p_binary->i_blob,
p_vCtd->data.p_binary->i_blob,
p_vCtC->data.p_binary->i_blob
};
const void * headers[3] = {
p_vCtH->data.p_binary->p_blob,
p_vCtd->data.p_binary->p_blob,
p_vCtC->data.p_binary->p_blob
};
if( xiph_PackHeaders( &p_fmt->i_extra, &p_fmt->p_extra,
headers_sizes, headers, 3 ) == VLC_SUCCESS )
{
p_fmt->i_codec = VLC_CODEC_VORBIS;
p_fmt->b_packetized = false;
}
}
break;
}
case ATOM_XiFL:
{
const MP4_Box_t *p_fCtS = MP4_BoxGet( p_sample, "wave/fCtS" ); /* kCookieTypeFLACStreaminfo */
if( p_fCtS && p_fCtS->data.p_binary )
{
size_t i_extra = 8 + p_fCtS->data.p_binary->i_blob;
uint8_t *p_extra = malloc(i_extra);
if( p_extra )
{
p_fmt->i_extra = i_extra;
p_fmt->p_extra = p_extra;
memcpy( p_extra, "fLaC", 4 );
SetDWBE( &p_extra[4], p_fCtS->data.p_binary->i_blob ); /* want the lowest 24bits */
p_extra[4] = 0x80; /* 0x80 Last metablock | 0x00 StreamInfo */
memcpy( &p_extra[8], p_fCtS->data.p_binary->p_blob, p_fCtS->data.p_binary->i_blob );
p_fmt->i_codec = VLC_CODEC_FLAC;
p_fmt->b_packetized = false;
}
}
break;
}
case ATOM_fLaC:
{
const MP4_Box_t *p_dfLa = MP4_BoxGet( p_sample, "dfLa" );
if( p_dfLa && p_dfLa->data.p_binary->i_blob > 4 &&
GetDWBE(p_dfLa->data.p_binary->p_blob) == 0 ) /* fullbox header, avoids creating dedicated parser */
{
size_t i_extra = p_dfLa->data.p_binary->i_blob;
uint8_t *p_extra = malloc(i_extra);
if( likely( p_extra ) )
{
p_fmt->i_extra = i_extra;
p_fmt->p_extra = p_extra;
memcpy( p_extra, p_dfLa->data.p_binary->p_blob, p_dfLa->data.p_binary->i_blob);
memcpy( p_extra, "fLaC", 4 );
p_fmt->i_codec = VLC_CODEC_FLAC;
}
}
break;
}
case ATOM_Opus:
{
const MP4_Box_t *p_dOps = MP4_BoxGet( p_sample, "dOps" );
if( p_dOps && p_dOps->data.p_binary->i_blob > 10 )
{
size_t i_src = p_dOps->data.p_binary->i_blob;
const uint8_t *p_src = p_dOps->data.p_binary->p_blob;
if(p_src[0] != 0x00 || (SIZE_MAX - p_dOps->data.p_binary->i_blob < 26))
break;
size_t i_dst = 2 + 8 + p_dOps->data.p_binary->i_blob + 8 + 8;
uint8_t *p_dst = malloc(i_dst);
if( likely( p_dst ) )
{
p_dst[0] = 0x01;
p_dst[1] = 8 + i_src;
memcpy(&p_dst[2], "OpusHead", 8);
memcpy(&p_dst[10], p_src, i_src);
p_dst[10] = 0x01; // set version != ISOBMFF mapping
SetWLE(&p_dst[12], GetWBE(&p_dst[12])); // swap endianness for PreSkip
SetDWLE(&p_dst[14], GetDWBE(&p_dst[14])); // swap endianness for InputSampleRate
SetWLE(&p_dst[18], GetWBE(&p_dst[18])); // swap endianness for OutputGain
memcpy(&p_dst[10 + i_src], "OpusTags\x00\x00\x00\x00\x00\x00\x00", 16);
p_fmt->i_extra = i_dst;
p_fmt->p_extra = p_dst;
p_fmt->i_codec = VLC_CODEC_OPUS;
}
}
break;
}
case( ATOM_eac3 ):
{
p_fmt->i_codec = VLC_CODEC_EAC3;
/* TS 102.366. F6 The values of the ChannelCount and SampleSize fields
* within the EC3SampleEntry Box shall be ignored. */
p_fmt->audio.i_channels = 0;
p_fmt->audio.i_bitspersample = 0;
const MP4_Box_t *p_dec3 = MP4_BoxGet( p_sample, "dec3" );
if( p_dec3 && BOXDATA(p_dec3) )
{
p_fmt->i_bitrate = BOXDATA(p_dec3)->i_data_rate * 1000;
}
break;
}
case( ATOM_AC3 ):
case( ATOM_ac3 ):
{
p_fmt->i_codec = VLC_CODEC_A52;
/* TS 102.366. F3 The values of the ChannelCount and SampleSize fields
* within the AC3SampleEntry Box shall be ignored */
p_fmt->audio.i_channels = 0;
p_fmt->audio.i_bitspersample = 0;
MP4_Box_t *p_dac3 = MP4_BoxGet( p_sample, "dac3" );
if( p_dac3 && BOXDATA(p_dac3) )
{
static const int pi_bitrate[] = {
32, 40, 48, 56,
64, 80, 96, 112,
128, 160, 192, 224,
256, 320, 384, 448,
512, 576, 640,
};
p_fmt->i_bitrate = 0;
if( BOXDATA(p_dac3)->i_bitrate_code < sizeof(pi_bitrate)/sizeof(*pi_bitrate) )
{
p_fmt->i_bitrate = pi_bitrate[BOXDATA(p_dac3)->i_bitrate_code] * 1000;
if (pi_bitrate[BOXDATA(p_dac3)->i_bitrate_code] == 640
&& BOXDATA(p_dac3)->i_acmod == 7
&& BOXDATA(p_dac3)->i_lfeon == 1)
{
/* DD+ can be an optional codec, and is deployed as an
* extension to a "core" AC-3 5.1 640kbit/s audiotrack.
* In that case, the AC-3 track might have an EAC3
* extension, therefore trigger the A52 packetizer to
* detect it (this is needed for aout passhthrough
* configuration). */
p_fmt->b_packetized = false;
}
}
}
break;
}
case VLC_FOURCC( 'm', 'l', 'p', 'a' ):
/* spec violation: 32 bits rate instead of fixed point */
p_fmt->i_codec = VLC_CODEC_TRUEHD;
p_fmt->audio.i_rate = ((unsigned int)p_soun->i_sampleratehi << 16) | p_soun->i_sampleratelo;
break;
case ATOM_dtsc: /* DTS */
{
p_fmt->i_codec = VLC_CODEC_DTS;
p_fmt->i_profile = PROFILE_DTS;
break;
}
case ATOM_dtse: /* DTS LBR */
{
p_fmt->i_codec = VLC_CODEC_DTS;
p_fmt->i_profile = PROFILE_DTS_EXPRESS;
break;
}
case ATOM_dtsh: /* DTS‐HD audio formats */
case ATOM_dtsl: /* DTS‐HD Lossless formats */
{
p_fmt->i_codec = VLC_CODEC_DTS;
p_fmt->i_profile = PROFILE_DTS_HD;
break;
}
case VLC_FOURCC( 't', 'w', 'o', 's' ):
p_fmt->i_codec = VLC_CODEC_S16B;
p_fmt->i_original_fourcc = i_sample_type;
p_fmt->audio.i_bitspersample = 16;
break;
case VLC_FOURCC( 's', 'o', 'w', 't' ):
p_fmt->i_codec = VLC_CODEC_S16L;
p_fmt->i_original_fourcc = i_sample_type;
p_fmt->audio.i_bitspersample = 16;
break;
case 0x0000000:
case( VLC_FOURCC( 'r', 'a', 'w', ' ' ) ):
case( VLC_FOURCC( 'N', 'O', 'N', 'E' ) ):
{
if( (p_soun->i_samplesize+7)/8 == 1 )
{
p_fmt->i_codec = VLC_CODEC_U8;
p_fmt->audio.i_bitspersample = 8;
}
else
{
p_fmt->i_codec = VLC_CODEC_S16B;
p_fmt->audio.i_bitspersample = 16;
}
p_fmt->i_original_fourcc = p_fmt->i_codec;
/* Buggy files workaround */
if( p_track->i_timescale != p_soun->i_sampleratehi && p_soun->i_qt_version == 0 )
{
msg_Warn( p_demux, "i_timescale (%"PRId32") != i_sampleratehi "
"(%"PRIu16"), making both equal (report any problem).",
p_track->i_timescale, p_soun->i_sampleratehi );
if( p_soun->i_sampleratehi != 0 )
p_cfg->i_timescale_override = p_soun->i_sampleratehi;
else
p_fmt->audio.i_rate = p_track->i_timescale;
}
break;
}
case ATOM_ipcm:
case ATOM_fpcm:
{
const MP4_Box_t *p_pcmC = MP4_BoxGet( p_sample, "pcmC" );
if( p_pcmC )
{
const vlc_fourcc_t lookup = i_sample_type +
BOXDATA(p_pcmC)->i_sample_size +
(BOXDATA(p_pcmC)->i_format_flags & 0x01);
const vlc_fourcc_t lookuptable[10][2] =
{
{ ATOM_fpcm + 32 + 0, VLC_CODEC_F32B },
{ ATOM_fpcm + 32 + 1, VLC_CODEC_F32L },
{ ATOM_fpcm + 64 + 0, VLC_CODEC_F64B },
{ ATOM_fpcm + 64 + 1, VLC_CODEC_F64L },
{ ATOM_ipcm + 16 + 0, VLC_CODEC_S16B },
{ ATOM_ipcm + 16 + 1, VLC_CODEC_S16L },
{ ATOM_ipcm + 24 + 0, VLC_CODEC_S24B },
{ ATOM_ipcm + 24 + 1, VLC_CODEC_S24L },
{ ATOM_ipcm + 32 + 0, VLC_CODEC_S32B },
{ ATOM_ipcm + 32 + 1, VLC_CODEC_S32L },
};
for( size_t i = 0; i<10; i++ )
if( lookuptable[i][0] == lookup )
p_fmt->i_codec = lookuptable[i][1];
}
break;
}
case ATOM_in24:
p_fmt->i_original_fourcc =
p_fmt->i_codec = p_enda && BOXDATA(p_enda)->i_little_endian == 1 ?
VLC_CODEC_S24L : VLC_CODEC_S24B;
break;
case ATOM_in32:
p_fmt->i_original_fourcc =
p_fmt->i_codec = p_enda && BOXDATA(p_enda)->i_little_endian == 1 ?
VLC_CODEC_S32L : VLC_CODEC_S32B;
break;
case ATOM_fl32:
p_fmt->i_original_fourcc =
p_fmt->i_codec = p_enda && BOXDATA(p_enda)->i_little_endian == 1 ?
VLC_CODEC_F32L : VLC_CODEC_F32B;
break;
case ATOM_fl64:
p_fmt->i_original_fourcc =
p_fmt->i_codec = p_enda && BOXDATA(p_enda)->i_little_endian == 1 ?
VLC_CODEC_F64L : VLC_CODEC_F64B;
break;
case VLC_CODEC_DVD_LPCM:
{
if( p_soun->i_qt_version == 2 )
{
/* Flags:
* 0x01: IsFloat
* 0x02: IsBigEndian
* 0x04: IsSigned
*/
static const struct {
unsigned i_flags;
unsigned i_mask;
unsigned i_bits;
vlc_fourcc_t i_codec;
} p_formats[] = {
{ 0x01, 0x03, 32, VLC_CODEC_F32L },
{ 0x01, 0x03, 64, VLC_CODEC_F64L },
{ 0x01|0x02, 0x03, 32, VLC_CODEC_F32B },
{ 0x01|0x02, 0x03, 64, VLC_CODEC_F64B },
{ 0x00, 0x05, 8, VLC_CODEC_U8 },
{ 0x00| 0x04, 0x05, 8, VLC_CODEC_S8 },
{ 0x00, 0x07, 16, VLC_CODEC_U16L },
{ 0x00|0x02, 0x07, 16, VLC_CODEC_U16B },
{ 0x00 |0x04, 0x07, 16, VLC_CODEC_S16L },
{ 0x00|0x02|0x04, 0x07, 16, VLC_CODEC_S16B },
{ 0x00, 0x07, 24, VLC_CODEC_U24L },
{ 0x00|0x02, 0x07, 24, VLC_CODEC_U24B },
{ 0x00 |0x04, 0x07, 24, VLC_CODEC_S24L },
{ 0x00|0x02|0x04, 0x07, 24, VLC_CODEC_S24B },
{ 0x00, 0x07, 32, VLC_CODEC_U32L },
{ 0x00|0x02, 0x07, 32, VLC_CODEC_U32B },
{ 0x00 |0x04, 0x07, 32, VLC_CODEC_S32L },
{ 0x00|0x02|0x04, 0x07, 32, VLC_CODEC_S32B },
{0, 0, 0, 0}
};
for( int i = 0; p_formats[i].i_codec; i++ )
{
if( p_formats[i].i_bits == p_soun->i_constbitsperchannel &&
(p_soun->i_formatflags & p_formats[i].i_mask) == p_formats[i].i_flags )
{
p_fmt->i_codec = p_formats[i].i_codec;
p_fmt->audio.i_bitspersample = p_soun->i_constbitsperchannel;
p_fmt->audio.i_blockalign =
p_soun->i_channelcount * p_soun->i_constbitsperchannel / 8;
p_cfg->i_sample_size_override = p_fmt->audio.i_blockalign;
break;
}
}
}
break;
}
default:
p_fmt->i_codec = i_sample_type;
break;
}
/* Process extensions */
/* Lookup for then channels extension */
const MP4_Box_t *p_chan = MP4_BoxGet( p_sample, "chan" );
if ( p_chan )
{
uint16_t i_vlc_mapping = 0;
uint8_t i_channels = 0;
const uint32_t *p_rg_chans_order = NULL;
if ( CoreAudio_Layout_to_vlc( &BOXDATA(p_chan)->layout,
&i_vlc_mapping, &i_channels,
&p_rg_chans_order ) != VLC_SUCCESS )
{
msg_Warn( p_demux, "discarding chan mapping" );
}
else if( i_vlc_mapping )
{
const unsigned i_bps = aout_BitsPerSample( p_fmt->i_codec );
/* Uncompressed audio */
if( i_bps )
p_cfg->i_chans_to_reorder =
aout_CheckChannelReorder( p_rg_chans_order, NULL,
i_vlc_mapping,
p_cfg->rgi_chans_reordering );
/* we can only set bitmap for VLC mapping or [re]mapped pcm audio
* as vlc can't enumerate channels for compressed content */
if( i_bps )
{
p_fmt->audio.i_channels = stdc_count_ones(i_vlc_mapping);
p_fmt->audio.i_physical_channels = i_vlc_mapping;
}
}
}
const MP4_Box_t *p_srat = MP4_BoxGet( p_sample, "srat" );
if ( p_srat )
p_fmt->audio.i_rate = BOXDATA(p_srat)->i_sample_rate;
SetupGlobalExtensions( p_sample, p_fmt );
/* now see if esds is present and if so create a data packet
with decoder_specific_info */
MP4_Box_t *p_esds = MP4_BoxGet( p_sample, "esds" );
if ( !p_esds ) p_esds = MP4_BoxGet( p_sample, "wave/esds" );
if ( p_esds && BOXDATA(p_esds) && BOXDATA(p_esds)->es_descriptor.p_decConfigDescr )
{
if( i_sample_type != ATOM_mp4a )
{
msg_Warn( p_demux, "Unexpected ESDS for %4.4s", (char *)&i_sample_type );
}
SetupESDS( p_demux, p_track, BOXDATA(p_esds)->es_descriptor.p_decConfigDescr, p_fmt );
}
else switch( i_sample_type )
{
case VLC_CODEC_AMR_NB:
p_fmt->audio.i_rate = 8000;
break;
case VLC_CODEC_AMR_WB:
p_fmt->audio.i_rate = 16000;
break;
case VLC_CODEC_QDMC:
case VLC_CODEC_QDM2:
case VLC_CODEC_ALAC:
{
CopyExtradata( p_sample->data.p_sample_soun->p_qt_description,
p_sample->data.p_sample_soun->i_qt_description,
p_fmt );
if( p_fmt->i_extra == 56 && i_sample_type == VLC_CODEC_ALAC )
{
p_fmt->audio.i_channels = *((uint8_t*)p_fmt->p_extra + 41);
p_fmt->audio.i_rate = GetDWBE((uint8_t*)p_fmt->p_extra + 52);
}
break;
}
case VLC_CODEC_ADPCM_MS:
case VLC_CODEC_ADPCM_IMA_WAV:
case VLC_CODEC_QCELP:
{
p_fmt->audio.i_blockalign = p_sample->data.p_sample_soun->i_bytes_per_frame;
break;
}
case ATOM_WMA2:
{
if( SetupAudioFromWaveFormatEx( MP4_BoxGet( p_sample, "wave/WMA2" ), p_fmt ) )
{
p_cfg->p_asf = MP4_BoxGet( p_sample, "wave/ASF " );
}
else
{
msg_Err( p_demux, "missing WMA2 %4.4s", (char*) &p_sample->p_father->i_type );
}
break;
}
case ATOM_wma: /* isml wmapro */
{
if( !SetupAudioFromWaveFormatEx( MP4_BoxGet( p_sample, "wfex" ), p_fmt ) )
msg_Err( p_demux, "missing wfex for wma" );
break;
}
default:
if(p_fmt->i_codec == 0)
msg_Dbg( p_demux, "Unrecognized FourCC %4.4s", (char *)&i_sample_type );
break;
}
/* Ambisonics */
const MP4_Box_t *p_SA3D = MP4_BoxGet(p_sample, "SA3D");
if (p_SA3D && BOXDATA(p_SA3D))
p_fmt->audio.channel_type = AUDIO_CHANNEL_TYPE_AMBISONICS;
/* Sanity check for raw audio */
if( aout_BitsPerSample(p_fmt->i_codec) && p_soun->i_channelcount == 0 )
return 0;
return 1;
}
int SetupSpuES( demux_t *p_demux, const mp4_track_t *p_track,
const MP4_Box_t *p_sample, es_format_t *p_fmt,
track_config_t *p_cfg )
{
const uint32_t i_sample_type = GetSampleType( p_demux, p_sample );
/* It's a little ugly but .. there are special cases */
switch( i_sample_type )
{
case VLC_FOURCC('s','t','p','p'):
p_fmt->i_codec = VLC_CODEC_TTML;
break;
case ATOM_wvtt:
p_fmt->i_codec = VLC_CODEC_WEBVTT;
break;
case ATOM_c608: /* EIA608 closed captions */
p_fmt->i_codec = VLC_CODEC_CEA608;
p_fmt->subs.cc.i_reorder_depth = -1;
break;
case ATOM_c708: /* EIA708 closed captions */
p_fmt->i_codec = VLC_CODEC_CEA708;
p_fmt->subs.cc.i_reorder_depth = -1;
break;
case( VLC_FOURCC( 't', 'e', 'x', 't' ) ):
case( VLC_FOURCC( 't', 'x', '3', 'g' ) ):
{
const MP4_Box_data_sample_generic_t *p_text = p_sample->data.p_sample_gen;
if(!p_text)
return 0;
if( i_sample_type == VLC_FOURCC( 't', 'e', 'x', 't' ) )
p_fmt->i_codec = VLC_CODEC_QTXT;
else
p_fmt->i_codec = VLC_CODEC_TX3G;
if( p_text->i_data > 4 && GetDWBE(p_text->p_data) & 0xC0000000 )
{
p_fmt->i_priority = ES_PRIORITY_SELECTABLE_MIN + 1;
p_fmt->subs.b_forced = true;
p_fmt->psz_description = strdup( _("Forced caption") );
}
CopyExtradata( p_text->p_data, p_text->i_data, p_fmt );
/* FIXME UTF-8 doesn't work here ? */
if( p_track->b_mac_encoding )
p_fmt->subs.psz_encoding = strdup( "MAC" );
else
p_fmt->subs.psz_encoding = strdup( "UTF-8" );
break;
}
default:
p_fmt->i_codec = i_sample_type;
break;
}
SetupGlobalExtensions( p_sample, p_fmt );
/* now see if esds is present and if so create a data packet
with decoder_specific_info */
MP4_Box_t *p_esds = MP4_BoxGet( p_sample, "esds" );
if ( p_esds && BOXDATA(p_esds) && BOXDATA(p_esds)->es_descriptor.p_decConfigDescr )
{
SetupESDS( p_demux, p_track, BOXDATA(p_esds)->es_descriptor.p_decConfigDescr, p_fmt );
}
return 1;
}
¤ Dauer der Verarbeitung: 0.29 Sekunden
¤
*© Formatika GbR, Deutschland
|
|