/*****************************************************************************
* ts_psi.c: Transport Stream input module for VLC.
*****************************************************************************
* Copyright (C) 2004-2016 VLC authors and VideoLAN
*
* 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 General Public License
* along with this program. If not, see .
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include
#ifndef _DVBPSI_DVBPSI_H_
# include
#endif
#include
#include
#include
#include
#include
#include
#include "ts_streams.h"
#include "ts_psi.h"
#include "ts_pid.h"
#include "ts_streams_private.h"
#include "ts.h"
#include "ts_strings.h"
#include "timestamps.h"
#include "../../codec/jpeg2000.h"
#include "../../codec/opus_header.h"
#include "../../packetizer/dts_header.h"
#include "sections.h"
#include "ts_sl.h"
#include "ts_scte.h"
#include "ts_psip.h"
#include "ts_si.h"
#include "ts_metadata.h"
#include "../access/dtv/en50221_capmt.h"
#include
static void PIDFillFormat( demux_t *, ts_stream_t *p_pes, int i_stream_type, ts_transport_type_t * );
static void PMTCallBack( void *data, dvbpsi_pmt_t *p_dvbpsipmt );
static ts_standards_e ProbePMTStandard( const dvbpsi_pmt_t *p_dvbpsipmt );
static int PATCheck( demux_t *p_demux, dvbpsi_pat_t *p_pat )
{
/* Some Dreambox streams have all PMT set to same pid */
int i_prev_pid = -1;
for( dvbpsi_pat_program_t * p_program = p_pat->p_first_program;
p_program != NULL;
p_program = p_program->p_next )
{
if( p_program->i_pid == i_prev_pid )
{
msg_Warn( p_demux, "PAT check failed: duplicate program pid %d", i_prev_pid );
return VLC_EGENERIC;
}
i_prev_pid = p_program->i_pid;
}
return VLC_SUCCESS;
}
static void PATCallBack( void *data, dvbpsi_pat_t *p_dvbpsipat )
{
demux_t *p_demux = data;
demux_sys_t *p_sys = p_demux->p_sys;
dvbpsi_pat_program_t *p_program;
ts_pid_t *patpid = GetPID(p_sys, 0);
ts_pat_t *p_pat = GetPID(p_sys, 0)->u.p_pat;
patpid->i_flags |= FLAG_SEEN;
msg_Dbg( p_demux, "PATCallBack called" );
if(unlikely( GetPID(p_sys, 0)->type != TYPE_PAT ))
{
msg_Warn( p_demux, "PATCallBack called on invalid pid" );
return;
}
/* check versioning changes */
if( !p_pat->b_generated )
{
/* override hotfixes */
if( ( p_pat->i_version != -1 && p_dvbpsipat->i_version == p_pat->i_version ) ||
( p_pat->i_ts_id != -1 && p_dvbpsipat->i_ts_id != p_pat->i_ts_id ) )
{
dvbpsi_pat_delete( p_dvbpsipat );
return;
}
}
else msg_Warn( p_demux, "Replacing generated PAT with one received from stream" );
/* check content */
if( !p_dvbpsipat->b_current_next || p_sys->b_user_pmt ||
PATCheck( p_demux, p_dvbpsipat ) )
{
dvbpsi_pat_delete( p_dvbpsipat );
return;
}
msg_Dbg( p_demux, "new PAT ts_id=%d version=%d current_next=%d",
p_dvbpsipat->i_ts_id, p_dvbpsipat->i_version, p_dvbpsipat->b_current_next );
/* Save old programs array */
DECL_ARRAY(ts_pid_t *) old_pmt_rm;
old_pmt_rm.i_alloc = p_pat->programs.i_alloc;
old_pmt_rm.i_size = p_pat->programs.i_size;
old_pmt_rm.p_elems = p_pat->programs.p_elems;
ARRAY_INIT(p_pat->programs);
bool b_force_reselect = false;
if( p_sys->programs.i_size && p_sys->seltype == PROGRAM_AUTO_DEFAULT )
{
/* If the program was set by default selection, we'll need to repick */
b_force_reselect = true;
for( p_program = p_dvbpsipat->p_first_program; p_program != NULL;
p_program = p_program->p_next )
{
if( p_sys->programs.p_elems[0] == p_program->i_number )
{
b_force_reselect = false;
break;
}
}
if( b_force_reselect )
ARRAY_RESET( p_sys->programs );
}
/* now create programs */
for( p_program = p_dvbpsipat->p_first_program; p_program != NULL;
p_program = p_program->p_next )
{
msg_Dbg( p_demux, " * number=%d pid=%d", p_program->i_number,
p_program->i_pid );
if( p_program->i_number == 0 )
continue;
ts_pid_t *pmtpid = GetPID(p_sys, p_program->i_pid);
bool b_existing = (pmtpid->type == TYPE_PMT);
/* create or temporary incref pid */
if( !PIDSetup( p_demux, TYPE_PMT, pmtpid, patpid ) )
{
msg_Warn( p_demux, " * number=%d pid=%d (ignored)", p_program->i_number,
p_program->i_pid );
continue;
}
if( !b_existing || pmtpid->u.p_pmt->i_number != p_program->i_number )
{
if( b_existing && pmtpid->u.p_pmt->i_number != p_program->i_number )
dvbpsi_pmt_detach(pmtpid->u.p_pmt->handle);
if( !dvbpsi_pmt_attach( pmtpid->u.p_pmt->handle, p_program->i_number, PMTCallBack, p_demux ) )
msg_Err( p_demux, "PATCallback failed attaching PMTCallback to program %d",
p_program->i_number );
}
pmtpid->u.p_pmt->i_number = p_program->i_number;
ARRAY_APPEND( p_pat->programs, pmtpid );
/* Now select PID at access level */
if( p_sys->programs.i_size == 0 ||
ProgramIsSelected( p_sys, p_program->i_number ) )
{
if( p_sys->programs.i_size == 0 )
{
msg_Dbg( p_demux, "temporary receiving program %d", p_program->i_number );
p_sys->b_default_selection = true;
ARRAY_APPEND( p_sys->programs, p_program->i_number );
}
SetPIDFilter( p_sys, pmtpid, true );
if ( p_sys->es_creation == DELAY_ES )
p_sys->es_creation = CREATE_ES;
}
}
p_pat->i_version = p_dvbpsipat->i_version;
p_pat->i_ts_id = p_dvbpsipat->i_ts_id;
p_pat->b_generated = false;
for(int i=0; iprograms.i_size )
{
es_out_Control( p_demux->out, ES_OUT_SET_GROUP, p_sys->programs.p_elems[0] );
}
dvbpsi_pat_delete( p_dvbpsipat );
}
#define PMT_DESC_PREFIX " * PMT descriptor: "
#define PMT_DESC_INDENT " : "
static void ParsePMTRegistrations( demux_t *p_demux, const dvbpsi_descriptor_t *p_firstdr,
ts_pmt_t *p_pmt, ts_pmt_registration_type_t *p_registration_type )
{
demux_sys_t *p_sys = p_demux->p_sys;
ts_pmt_registration_type_t registration_type = *p_registration_type;
int i_arib_score_flags = 0; /* Descriptors can be repeated */
for( const dvbpsi_descriptor_t *p_dr = p_firstdr; p_dr != NULL; p_dr = p_dr->p_next )
{
/* general descriptors handling < 0x40 and scoring */
if( p_dr->i_tag < 0x40 )
{
msg_Dbg( p_demux, PMT_DESC_PREFIX "%s (0x%x)",
ISO13818_1_Get_Descriptor_Description(p_dr->i_tag), p_dr->i_tag );
}
switch(p_dr->i_tag)
{
case 0x05: /* Registration Descriptor */
{
if( p_dr->i_length != 4 )
{
msg_Warn( p_demux, PMT_DESC_INDENT "invalid registration descriptor" );
break;
}
static const struct
{
const char rgs[4];
const ts_pmt_registration_type_t reg;
} regs[] = {
{ { 'H', 'D', 'M', 'V' }, TS_PMT_REGISTRATION_BLURAY },
{ { 'H', 'D', 'P', 'R' }, TS_PMT_REGISTRATION_BLURAY },
{ { 'G', 'A', '9', '4' }, TS_PMT_REGISTRATION_ATSC },
};
for( unsigned i=0; ip_data, 4 ) )
{
registration_type = regs[i].reg;
msg_Dbg( p_demux, PMT_DESC_INDENT "%4.4s registration", p_dr->p_data );
break;
}
}
}
break;
case 0x09:
{
dvbpsi_ca_dr_t *p_cadr = dvbpsi_DecodeCADr( (dvbpsi_descriptor_t *) p_dr );
msg_Dbg( p_demux, PMT_DESC_INDENT "CA System ID 0x%x", p_cadr->i_ca_system_id );
i_arib_score_flags |= (p_cadr->i_ca_system_id == 0x05);
}
break;
case 0x1d: /* We have found an IOD descriptor */
p_pmt->iod = IODNew( VLC_OBJECT(p_demux), p_dr->i_length, p_dr->p_data );
break;
case 0xC1:
i_arib_score_flags |= 1 << 2;
break;
case 0xF6:
i_arib_score_flags |= 1 << 1;
break;
default:
break;
}
}
if ( p_sys->standard == TS_STANDARD_AUTO &&
registration_type == TS_PMT_REGISTRATION_NONE &&
i_arib_score_flags == 0x07 ) //0b111
{
registration_type = TS_PMT_REGISTRATION_ARIB;
}
*p_registration_type = registration_type;
}
static void ParsePMTPrivateRegistrations( demux_t *p_demux, const dvbpsi_descriptor_t *p_firstdr,
ts_pmt_t *p_pmt, ts_standards_e i_standard )
{
VLC_UNUSED(p_pmt);
/* Now process private descriptors >= 0x40 */
for( const dvbpsi_descriptor_t *p_dr = p_firstdr; p_dr != NULL; p_dr = p_dr->p_next )
{
if( p_dr->i_tag < 0x40 )
continue;
switch( i_standard )
{
case TS_STANDARD_ARIB:
{
const char *psz_desc = ARIB_B10_Get_PMT_Descriptor_Description( p_dr->i_tag );
if( psz_desc )
msg_Dbg( p_demux, PMT_DESC_PREFIX "%s (0x%x)", psz_desc, p_dr->i_tag );
else
msg_Dbg( p_demux, PMT_DESC_PREFIX "Unknown Private (0x%x)", p_dr->i_tag );
}
break;
case TS_STANDARD_DVB:
case TS_STANDARD_AUTO:
{
if( p_dr->i_tag == 0x88 )
{
/* EACEM Simulcast HD Logical channels ordering */
/* TODO: apply visibility flags */
msg_Dbg( p_demux, PMT_DESC_PREFIX "EACEM Simulcast HD" );
break;
}
}
/* fallthrough */
default:
msg_Dbg( p_demux, PMT_DESC_PREFIX "Unknown Private (0x%x)", p_dr->i_tag );
break;
}
}
}
/*****************************************************************************
* PMT callback and helpers
*****************************************************************************/
static dvbpsi_descriptor_t *PMTEsFindDescriptor( const dvbpsi_pmt_es_t *p_es,
int i_tag )
{
dvbpsi_descriptor_t *p_dr = p_es->p_first_descriptor;;
while( p_dr && ( p_dr->i_tag != i_tag ) )
p_dr = p_dr->p_next;
return p_dr;
}
static bool PMTEsHasRegistration( demux_t *p_demux,
const dvbpsi_pmt_es_t *p_es,
const char *psz_tag )
{
dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_es, 0x05 );
if( !p_dr )
return false;
if( p_dr->i_length < 4 )
{
msg_Warn( p_demux, "invalid Registration Descriptor" );
return false;
}
assert( strlen(psz_tag) == 4 );
return !memcmp( p_dr->p_data, psz_tag, 4 );
}
static bool PMTEsHasComponentTagBetween( const dvbpsi_pmt_es_t *p_es,
uint8_t i_low, uint8_t i_high )
{
dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_es, 0x52 );
if( !p_dr )
return false;
dvbpsi_stream_identifier_dr_t *p_si = dvbpsi_DecodeStreamIdentifierDr( p_dr );
if( !p_si )
return false;
return p_si->i_component_tag >= i_low && p_si->i_component_tag <= i_high;
}
static ts_standards_e ProbePMTStandard( const dvbpsi_pmt_t *p_dvbpsipmt )
{
dvbpsi_pmt_es_t *p_dvbpsies;
for( p_dvbpsies = p_dvbpsipmt->p_first_es; p_dvbpsies; p_dvbpsies = p_dvbpsies->p_next )
{
if( p_dvbpsies->i_type == 0x06 )
{
/* Probe for ARIB subtitles */
dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0xFD );
if( p_dr && p_dr->i_length >= 2 )
{
const uint16_t i_data_component_id = GetWBE(p_dr->p_data);
if( ( i_data_component_id == 0x0008 &&
PMTEsHasComponentTagBetween( p_dvbpsies, 0x30, 0x37 ) ) ||
( i_data_component_id == 0x0012 &&
PMTEsHasComponentTagBetween( p_dvbpsies, 0x87, 0x88 ) ) )
return TS_STANDARD_ARIB;
}
}
}
return TS_STANDARD_AUTO;
}
static void SetupAudioExtendedDescriptors( demux_t *p_demux, ts_es_t *p_es,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
if( p_demux->p_sys->standard == TS_STANDARD_AUTO ||
p_demux->p_sys->standard == TS_STANDARD_DVB )
{
const dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x7F );
if( p_dr && p_dr->i_length > 1 && p_dr->p_data[0] == 0x06 /* Tag extension */ )
{
static const char *editorial_classification_coding[] = {
N_("Main audio"),
N_("Audio description for the visually impaired"),
N_("Clean audio for the hearing impaired"),
N_("Spoken subtitles for the visually impaired"),
};
uint8_t i_audio_type = (p_dr->p_data[1] & 0x7F) >> 2;
if( i_audio_type < ARRAY_SIZE(editorial_classification_coding) )
{
free( p_es->fmt.psz_description );
p_es->fmt.psz_description = strdup(editorial_classification_coding[i_audio_type]);
}
if( i_audio_type == 0x00 /* Main Audio */ )
p_es->fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 1;
if( (p_dr->p_data[1] & 0x80) == 0x00 ) /* Split mixed */
p_es->fmt.i_priority = ES_PRIORITY_NOT_DEFAULTABLE;
if( (p_dr->p_data[1] & 0x01) && p_dr->i_length >= 5 )
{
free( p_es->fmt.psz_language );
p_es->fmt.psz_language = malloc( 4 );
if( p_es->fmt.psz_language )
{
memcpy( p_es->fmt.psz_language, &p_dr->p_data[2], 3 );
p_es->fmt.psz_language[3] = 0;
msg_Dbg( p_demux, " found language: %s", p_es->fmt.psz_language );
}
}
}
}
}
static void SetupISO14496Descriptors( demux_t *p_demux, ts_stream_t *p_pes,
const ts_pmt_t *p_pmt, const dvbpsi_pmt_es_t *p_dvbpsies )
{
const dvbpsi_descriptor_t *p_dr = p_dvbpsies->p_first_descriptor;
ts_es_t *p_es = p_pes->p_es;
while( p_dr )
{
uint8_t i_length = p_dr->i_length;
switch( p_dr->i_tag )
{
case 0x1f: /* FMC Descriptor */
while( i_length >= 2 /* see below */ && !p_es->i_sl_es_id )
{
p_es->i_sl_es_id = ( p_dr->p_data[0] << 8 ) | p_dr->p_data[1];
/* FIXME: map all ids and flexmux channels */
/* Handle broken streams with 2 byte 0x1F descriptors
* see samples/A-codecs/AAC/freetv_aac_latm.txt */
if( i_length == 2 )
break;
i_length -= 3;
msg_Dbg( p_demux, " - found FMC_descriptor mapping es_id=%"PRIu16, p_es->i_sl_es_id );
}
break;
case 0x1e: /* SL Descriptor */
if( i_length == 2 )
{
p_es->i_sl_es_id = ( p_dr->p_data[0] << 8 ) | p_dr->p_data[1];
msg_Dbg( p_demux, " - found SL_descriptor mapping es_id=%"PRIu16, p_es->i_sl_es_id );
if( p_dvbpsies->i_type == 0x12 ) /* SL AU pes stream */
{
if( !p_pes->p_proc )
p_pes->p_proc = SL_stream_processor_New( p_pes );
}
else if( p_dvbpsies->i_type == 0x13 ) /* IOD / SL sections */
{
ts_sections_processor_Add( p_demux,
&p_pes->p_sections_proc, 0x05, 0x00,
SLPackets_Section_Handler, p_pes );
}
p_pes->b_always_receive = true;
}
break;
default:
break;
}
p_dr = p_dr->p_next;
}
if( p_es->i_sl_es_id )
{
const es_mpeg4_descriptor_t *p_mpeg4desc = GetMPEG4DescByEsId( p_pmt, p_es->i_sl_es_id );
if( p_mpeg4desc && p_mpeg4desc->b_ok )
{
if( !SetupISO14496LogicalStream( p_demux, &p_mpeg4desc->dec_descr, &p_es->fmt ) )
msg_Dbg( p_demux, " - IOD not yet available for es_id=%"PRIu16, p_es->i_sl_es_id );
}
}
else
{
switch( p_dvbpsies->i_type )
{
/* non fatal, set by packetizer */
case 0x0f: /* ADTS */
case 0x11: /* LOAS */
msg_Dbg( p_demux, " - SL/FMC descriptor not found/matched" );
break;
default:
msg_Err( p_demux, " - SL/FMC descriptor not found/matched" );
break;
}
}
}
static void SetupMetadataDescriptors( demux_t *p_demux, ts_stream_t *p_stream, const dvbpsi_pmt_es_t *p_dvbpsies )
{
ts_es_t *p_es = p_stream->p_es;
const dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x26 );
if( p_dr && p_dr->i_length >= 13 )
{
/* app format 0xFFFF
* metadata_application_format_identifier ID3\x20
* i_metadata_format 0xFF
* metadata_format_identifier ID3\x20 */
if( !memcmp( p_dr->p_data, "\xFF\xFFID3 \xFFID3 ", 11 ) &&
(p_dr->p_data[12] & 0xF0) == 0x00 )
{
p_es->metadata.i_format = VLC_FOURCC('I', 'D', '3', ' ');
p_es->metadata.i_service_id = p_dr->p_data[11];
msg_Dbg( p_demux, " - found Metadata_descriptor type ID3 with service_id=0x%"PRIx8,
p_dr->p_data[11] );
if( !p_stream->p_proc )
p_stream->p_proc = Metadata_stream_processor_New( p_stream, p_demux->out );
}
}
}
static void SetupAVCDescriptors( demux_t *p_demux, ts_es_t *p_es, const dvbpsi_pmt_es_t *p_dvbpsies )
{
const dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x28 );
if( p_dr && p_dr->i_length >= 4 )
{
p_es->fmt.i_profile = p_dr->p_data[0];
p_es->fmt.i_level = p_dr->p_data[2];
msg_Dbg( p_demux, " - found AVC_video_descriptor profile=0x%"PRIx8" level=0x%"PRIx8,
p_es->fmt.i_profile, p_es->fmt.i_level );
}
}
static void SetupJ2KDescriptors( demux_t *p_demux, ts_es_t *p_es, const dvbpsi_pmt_es_t *p_dvbpsies )
{
const dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x32 );
if( p_dr && p_dr->i_length >= 24 )
{
es_format_Change( &p_es->fmt, VIDEO_ES, VLC_CODEC_JPEG2000 );
p_es->fmt.i_profile = p_dr->p_data[0];
p_es->fmt.i_level = p_dr->p_data[1];
p_es->fmt.video.i_width = GetDWBE(&p_dr->p_data[2]);
p_es->fmt.video.i_height = GetDWBE(&p_dr->p_data[6]);
p_es->fmt.video.i_frame_rate_base = GetWBE(&p_dr->p_data[18]);
p_es->fmt.video.i_frame_rate = GetWBE(&p_dr->p_data[20]);
j2k_fill_color_profile( p_dr->p_data[21],
&p_es->fmt.video.primaries,
&p_es->fmt.video.transfer,
&p_es->fmt.video.space );
p_es->b_interlaced = p_dr->p_data[23] & 0x40;
if( p_dr->i_length > 24 )
{
p_es->fmt.p_extra = malloc(p_dr->i_length - 24);
if( p_es->fmt.p_extra )
p_es->fmt.i_extra = p_dr->i_length - 24;
}
msg_Dbg( p_demux, " - found J2K_video_descriptor profile=0x%"PRIx8" level=0x%"PRIx8,
p_es->fmt.i_profile, p_es->fmt.i_level );
}
}
typedef struct
{
int i_type;
int i_magazine;
int i_page;
char p_iso639[3];
} ts_teletext_page_t;
static const char *const ppsz_teletext_type[] = {
"",
N_("Teletext"),
N_("Teletext subtitles"),
N_("Teletext: additional information"),
N_("Teletext: program schedule"),
N_("Teletext subtitles: hearing impaired")
};
static void PMTSetupEsTeletext( demux_t *p_demux, ts_stream_t *p_pes,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
es_format_t *p_fmt = &p_pes->p_es->fmt;
ts_teletext_page_t p_page[2 * 64 + 20];
unsigned i_page = 0;
dvbpsi_descriptor_t *p_dr;
/* Gather pages information */
for( unsigned i_tag_idx = 0; i_tag_idx < 2; i_tag_idx++ )
{
p_dr = PMTEsFindDescriptor( p_dvbpsies, i_tag_idx == 0 ? 0x46 : 0x56 );
if( !p_dr )
continue;
dvbpsi_teletext_dr_t *p_sub = dvbpsi_DecodeTeletextDr( p_dr );
if( !p_sub )
continue;
for( int i = 0; i < p_sub->i_pages_number; i++ )
{
const dvbpsi_teletextpage_t *p_src = &p_sub->p_pages[i];
if( p_src->i_teletext_type >= 0x06 )
continue;
assert( i_page < sizeof(p_page)/sizeof(*p_page) );
ts_teletext_page_t *p_dst = &p_page[i_page++];
p_dst->i_type = p_src->i_teletext_type;
p_dst->i_magazine = p_src->i_teletext_magazine_number
? p_src->i_teletext_magazine_number : 8;
p_dst->i_page = p_src->i_teletext_page_number;
memcpy( p_dst->p_iso639, p_src->i_iso6392_language_code, 3 );
}
}
p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x59 );
if( p_dr )
{
dvbpsi_subtitling_dr_t *p_sub = dvbpsi_DecodeSubtitlingDr( p_dr );
for( int i = 0; p_sub && i < p_sub->i_subtitles_number; i++ )
{
dvbpsi_subtitle_t *p_src = &p_sub->p_subtitle[i];
if( p_src->i_subtitling_type < 0x01 || p_src->i_subtitling_type > 0x03 )
continue;
assert( i_page < sizeof(p_page)/sizeof(*p_page) );
ts_teletext_page_t *p_dst = &p_page[i_page++];
switch( p_src->i_subtitling_type )
{
case 0x01:
p_dst->i_type = 0x02;
break;
default:
p_dst->i_type = 0x03;
break;
}
/* FIXME check if it is the right split */
p_dst->i_magazine = (p_src->i_composition_page_id >> 8)
? (p_src->i_composition_page_id >> 8) : 8;
p_dst->i_page = p_src->i_composition_page_id & 0xff;
memcpy( p_dst->p_iso639, p_src->i_iso6392_language_code, 3 );
}
}
/* */
es_format_Change(p_fmt, SPU_ES, VLC_CODEC_TELETEXT );
if( !p_demux->p_sys->b_split_es || i_page <= 0 )
{
p_fmt->subs.teletext.i_magazine = -1;
p_fmt->subs.teletext.i_page = 0;
p_fmt->psz_description = strdup( vlc_gettext(ppsz_teletext_type[1]) );
p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x46 );
if( !p_dr )
p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x56 );
if( !p_demux->p_sys->b_split_es && p_dr && p_dr->i_length > 0 )
{
/* Descriptor pass-through */
p_fmt->p_extra = malloc( p_dr->i_length );
if( p_fmt->p_extra )
{
p_fmt->i_extra = p_dr->i_length;
memcpy( p_fmt->p_extra, p_dr->p_data, p_dr->i_length );
}
}
}
else
{
for( unsigned i = 0; i < i_page; i++ )
{
ts_es_t *p_page_es;
/* */
if( i == 0 )
{
p_page_es = p_pes->p_es;
}
else
{
p_page_es = ts_es_New( p_pes->p_es->p_program );
if( !p_page_es )
break;
es_format_Copy( &p_page_es->fmt, p_fmt );
free( p_page_es->fmt.psz_language );
free( p_page_es->fmt.psz_description );
p_page_es->fmt.psz_language = NULL;
p_page_es->fmt.psz_description = NULL;
ts_stream_Add_es( p_pes, p_page_es, true );
}
/* */
const ts_teletext_page_t *p = &p_page[i];
p_page_es->fmt.i_priority = (p->i_type == 0x02 || p->i_type == 0x05) ?
ES_PRIORITY_SELECTABLE_MIN : ES_PRIORITY_NOT_DEFAULTABLE;
p_page_es->fmt.psz_language = strndup( p->p_iso639, 3 );
p_page_es->fmt.psz_description = strdup(vlc_gettext(ppsz_teletext_type[p->i_type]));
p_page_es->fmt.subs.teletext.i_magazine = p->i_magazine;
p_page_es->fmt.subs.teletext.i_page = p->i_page;
msg_Dbg( p_demux,
" * ttxt type=%s lan=%s page=%d%02x",
p_page_es->fmt.psz_description,
p_page_es->fmt.psz_language,
p->i_magazine, p->i_page );
}
}
}
static void PMTSetupEsDvbSubtitle( demux_t *p_demux, ts_stream_t *p_pes,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
es_format_t *p_fmt = &p_pes->p_es->fmt;
es_format_Change( p_fmt, SPU_ES, VLC_CODEC_DVBS );
dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x59 );
int i_page = 0;
dvbpsi_subtitling_dr_t *p_sub = dvbpsi_DecodeSubtitlingDr( p_dr );
for( int i = 0; p_sub && i < p_sub->i_subtitles_number; i++ )
{
const int i_type = p_sub->p_subtitle[i].i_subtitling_type;
if( ( i_type >= 0x10 && i_type <= 0x14 ) ||
( i_type >= 0x20 && i_type <= 0x24 ) )
i_page++;
}
if( !p_demux->p_sys->b_split_es || i_page <= 0 )
{
p_fmt->subs.dvb.i_id = -1;
p_fmt->psz_description = strdup( _("DVB subtitles") );
if( !p_demux->p_sys->b_split_es && p_dr && p_dr->i_length > 0 )
{
/* Descriptor pass-through */
p_fmt->p_extra = malloc( p_dr->i_length );
if( p_fmt->p_extra )
{
p_fmt->i_extra = p_dr->i_length;
memcpy( p_fmt->p_extra, p_dr->p_data, p_dr->i_length );
}
}
}
else
{
for( int i = 0; i < p_sub->i_subtitles_number; i++ )
{
ts_es_t *p_subs_es;
/* */
if( i == 0 )
{
p_subs_es = p_pes->p_es;
}
else
{
p_subs_es = ts_es_New( p_pes->p_es->p_program );
if( !p_subs_es )
break;
es_format_Copy( &p_subs_es->fmt, p_fmt );
free( p_subs_es->fmt.psz_language );
free( p_subs_es->fmt.psz_description );
p_subs_es->fmt.psz_language = NULL;
p_subs_es->fmt.psz_description = NULL;
ts_stream_Add_es( p_pes, p_subs_es, true );
}
/* */
const dvbpsi_subtitle_t *p = &p_sub->p_subtitle[i];
p_subs_es->fmt.psz_language = strndup( (char *)p->i_iso6392_language_code, 3 );
switch( p->i_subtitling_type )
{
case 0x10: /* unspec. */
case 0x11: /* 4:3 */
case 0x12: /* 16:9 */
case 0x13: /* 2.21:1 */
case 0x14: /* HD monitor */
p_subs_es->fmt.psz_description = strdup( _("DVB subtitles") );
break;
case 0x20: /* Hearing impaired unspec. */
case 0x21: /* h.i. 4:3 */
case 0x22: /* h.i. 16:9 */
case 0x23: /* h.i. 2.21:1 */
case 0x24: /* h.i. HD monitor */
p_subs_es->fmt.psz_description = strdup( _("DVB subtitles: hearing impaired") );
break;
default:
break;
}
/* Hack, FIXME */
p_subs_es->fmt.subs.dvb.i_id = ( p->i_composition_page_id << 0 ) |
( p->i_ancillary_page_id << 16 );
}
}
}
static int vlc_ceil_log2( const unsigned int val )
{
int n = 31 - clz(val);
if ((1U << n) != val)
n++;
return n;
}
static void OpusSetup(demux_t *demux, uint8_t *p, size_t len, es_format_t *p_fmt)
{
OpusHeader h;
opus_header_init(&h);
/* default mapping */
static const unsigned char map[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
memcpy(h.stream_map, map, sizeof(map));
int csc, mapping;
int channels = 0;
int stream_count = 0;
int ccc = p[1]; // channel_config_code
if (ccc <= 8) {
channels = ccc;
if (channels)
mapping = channels > 2;
else {
mapping = 255;
channels = 2; // dual mono
}
static const uint8_t p_csc[8] = { 0, 1, 1, 2, 2, 2, 3, 3 };
csc = p_csc[channels - 1];
stream_count = channels - csc;
static const uint8_t maps[6][7] = {
{ 2,1 },
{ 1,2,3 },
{ 4,1,2,3 },
{ 4,1,2,3,5 },
{ 4,1,2,3,5,6 },
{ 6,1,2,3,4,5,7 },
};
if (channels > 2)
memcpy(&h.stream_map[1], maps[channels-3], channels - 1);
} else if (ccc == 0x81) {
if (len < 4)
goto explicit_config_too_short;
channels = p[2];
mapping = p[3];
csc = 0;
if (mapping) {
bs_t s;
bs_init(&s, &p[4], len - 4);
stream_count = 1;
if (channels) {
int bits = vlc_ceil_log2(channels);
if (s.i_left < bits)
goto explicit_config_too_short;
stream_count = bs_read(&s, bits) + 1;
bits = vlc_ceil_log2(stream_count + 1);
if (s.i_left < bits)
goto explicit_config_too_short;
csc = bs_read(&s, bits);
}
int channel_bits = vlc_ceil_log2(stream_count + csc + 1);
if (s.i_left < channels * channel_bits)
goto explicit_config_too_short;
unsigned char silence = (1U << (stream_count + csc + 1)) - 1;
for (int i = 0; i < channels; i++) {
unsigned char m = bs_read(&s, channel_bits);
if (m == silence)
m = 0xff;
h.stream_map[i] = m;
}
}
} else if (ccc >= 0x80 && ccc <= 0x88) {
channels = ccc - 0x80;
if (channels)
mapping = 1;
else {
mapping = 255;
channels = 2; // dual mono
}
csc = 0;
stream_count = channels;
} else {
msg_Err(demux, "Opus channel configuration 0x%.2x is reserved", ccc);
}
if (!channels) {
msg_Err(demux, "Opus channel configuration 0x%.2x not supported yet", p[1]);
opus_header_clean(&h);
return;
}
opus_prepare_header(channels, 0, &h);
h.preskip = 0;
h.input_sample_rate = 48000;
h.nb_coupled = csc;
h.nb_streams = channels - csc;
h.channel_mapping = mapping;
if (h.channels) {
uint8_t *p_extra = NULL;
int i_extra = 0;
opus_write_header(&p_extra, &i_extra, &h, NULL /* FIXME */);
if (p_extra) {
es_format_Change(p_fmt, AUDIO_ES, VLC_CODEC_OPUS);
p_fmt->p_extra = p_extra;
p_fmt->i_extra = i_extra;
p_fmt->audio.i_channels = h.channels;
p_fmt->audio.i_rate = 48000;
}
}
opus_header_clean(&h);
return;
explicit_config_too_short:
opus_header_clean(&h);
msg_Err(demux, "Opus descriptor too short");
}
static void PMTSetupEs0x02( ts_es_t *p_es,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x02 );
if( p_dr )
{
/* sample: wcbs.ts */
const dvbpsi_vstream_dr_t *p_vdr = dvbpsi_DecodeVStreamDr( p_dr );
if( p_vdr )
{
if( p_vdr->i_frame_rate_code > 1 && p_vdr->i_frame_rate_code < 9 &&
!p_vdr->b_multiple_frame_rate )
{
static const int code_to_frame_rate[8][2] =
{
{ 24000, 1001 }, { 24, 1 }, { 25, 1 }, { 30000, 1001 },
{ 30, 1 }, { 50, 1 }, { 60000, 1001 }, { 60, 1 },
};
p_es->fmt.video.i_frame_rate = code_to_frame_rate[p_vdr->i_frame_rate_code - 1][0];
p_es->fmt.video.i_frame_rate_base = code_to_frame_rate[p_vdr->i_frame_rate_code - 1][1];
}
if( !p_vdr->b_mpeg2 && p_es->fmt.i_codec == VLC_CODEC_MPGV )
p_es->fmt.i_original_fourcc = VLC_CODEC_MP1V;
}
}
/* MPEG2_stereoscopic_video_format_descriptor */
p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x34 );
if( p_dr && p_dr->i_length > 0 && (p_dr->p_data[0] & 0x80) )
{
video_multiview_mode_t mode;
switch( p_dr->p_data[0] & 0x7F )
{
case 0x03:
mode = MULTIVIEW_STEREO_SBS; break;
case 0x04:
mode = MULTIVIEW_STEREO_TB; break;
case 0x08:
default:
mode = MULTIVIEW_2D; break;
}
p_es->fmt.video.multiview_mode = mode;
}
}
static void PMTSetupEs0x05PrivateData( demux_t *p_demux, ts_es_t *p_es,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
VLC_UNUSED(p_es);
if( p_demux->p_sys->standard == TS_STANDARD_DVB ||
p_demux->p_sys->standard == TS_STANDARD_AUTO )
{
dvbpsi_descriptor_t *p_ait_dr = PMTEsFindDescriptor( p_dvbpsies, 0x6F );
if( p_ait_dr )
{
uint8_t *p_data = p_ait_dr->p_data;
for( uint8_t i_data = p_ait_dr->i_length; i_data >= 3; i_data -= 3, p_data += 3 )
{
uint16_t i_app_type = ((p_data[0] & 0x7F) << 8) | p_data[1];
msg_Dbg( p_demux, " - Application type 0x%"PRIx16" version %"PRIu8,
i_app_type, p_data[2] & 0x1F);
}
}
}
}
static void PMTSetupEs0x06( demux_t *p_demux, ts_stream_t *p_pes,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
es_format_t *p_fmt = &p_pes->p_es->fmt;
dvbpsi_descriptor_t *p_subs_dr = PMTEsFindDescriptor( p_dvbpsies, 0x59 );
dvbpsi_descriptor_t *desc;
if( PMTEsHasRegistration( p_demux, p_dvbpsies, "EAC3" ) ||
PMTEsFindDescriptor( p_dvbpsies, 0x7a ) )
{
/* DVB with stream_type 0x06 (ETS EN 300 468) */
es_format_Change( p_fmt, AUDIO_ES, VLC_CODEC_EAC3 );
}
else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "AC-3" ) ||
PMTEsFindDescriptor( p_dvbpsies, 0x6a ) ||
PMTEsFindDescriptor( p_dvbpsies, 0x81 ) ) /* AC-3 channel (also in EAC3) */
{
es_format_Change( p_fmt, AUDIO_ES, VLC_CODEC_A52 );
}
else if( (desc = PMTEsFindDescriptor( p_dvbpsies, 0x7f ) ) &&
desc->i_length >= 2 && desc->p_data[0] == 0x80 &&
PMTEsHasRegistration(p_demux, p_dvbpsies, "Opus"))
{
OpusSetup(p_demux, desc->p_data, desc->i_length, p_fmt);
}
else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "DTS1" ) || /* 512 Bpf */
PMTEsHasRegistration( p_demux, p_dvbpsies, "DTS2" ) || /* 1024 Bpf */
PMTEsHasRegistration( p_demux, p_dvbpsies, "DTS3" ) || /* 2048 Bpf */
PMTEsFindDescriptor( p_dvbpsies, 0x73 ) )
{
/*registration descriptor(ETSI TS 101 154 Annex F)*/
es_format_Change( p_fmt, AUDIO_ES, VLC_CODEC_DTS );
}
else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "BSSD" ) && !p_subs_dr )
{
/* BSSD is AES3 DATA, but could also be subtitles
* we need to check for secondary descriptor then s*/
es_format_Change( p_fmt, AUDIO_ES, VLC_CODEC_302M );
p_fmt->b_packetized = true;
}
else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "HEVC" ) )
{
es_format_Change( p_fmt, VIDEO_ES, VLC_CODEC_HEVC );
}
else if ( p_demux->p_sys->standard == TS_STANDARD_ARIB )
{
/* Lookup our data component descriptor first ARIB STD B10 6.4 */
dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0xFD );
/* and check that it maps to something ARIB STD B14 Table 5.1/5.2 */
if ( p_dr && p_dr->i_length >= 2 )
{
/* See STD-B10 Annex J, table J-1 mappings */
const uint16_t i_data_component_id = GetWBE(p_dr->p_data);
if( i_data_component_id == 0x0008 &&
PMTEsHasComponentTagBetween( p_dvbpsies, 0x30, 0x37 ) )
{
es_format_Change( p_fmt, SPU_ES, VLC_CODEC_ARIB_A );
p_fmt->psz_language = strndup ( "jpn", 3 );
p_fmt->psz_description = strdup( _("ARIB subtitles") );
}
else if( i_data_component_id == 0x0012 &&
PMTEsHasComponentTagBetween( p_dvbpsies, 0x87, 0x88 ) )
{
es_format_Change( p_fmt, SPU_ES, VLC_CODEC_ARIB_C );
p_fmt->psz_language = strndup ( "jpn", 3 );
p_fmt->psz_description = strdup( _("ARIB subtitles") );
}
}
}
else
{
/* Subtitle/Teletext/VBI fallbacks */
dvbpsi_subtitling_dr_t *p_sub;
if( p_subs_dr && ( p_sub = dvbpsi_DecodeSubtitlingDr( p_subs_dr ) ) )
{
for( int i = 0; i < p_sub->i_subtitles_number; i++ )
{
if( p_fmt->i_cat != UNKNOWN_ES )
break;
switch( p_sub->p_subtitle[i].i_subtitling_type )
{
case 0x01: /* EBU Teletext subtitles */
case 0x02: /* Associated EBU Teletext */
case 0x03: /* VBI data */
PMTSetupEsTeletext( p_demux, p_pes, p_dvbpsies );
break;
case 0x10: /* DVB Subtitle (normal) with no monitor AR critical */
case 0x11: /* ... on 4:3 AR monitor */
case 0x12: /* ... on 16:9 AR monitor */
case 0x13: /* ... on 2.21:1 AR monitor */
case 0x14: /* ... for display on a high definition monitor */
case 0x20: /* DVB Subtitle (impaired) with no monitor AR critical */
case 0x21: /* ... on 4:3 AR monitor */
case 0x22: /* ... on 16:9 AR monitor */
case 0x23: /* ... on 2.21:1 AR monitor */
case 0x24: /* ... for display on a high definition monitor */
PMTSetupEsDvbSubtitle( p_demux, p_pes, p_dvbpsies );
break;
default:
msg_Err( p_demux, "Unrecognized DVB subtitle type (0x%x)",
p_sub->p_subtitle[i].i_subtitling_type );
break;
}
}
}
if( p_fmt->i_cat == UNKNOWN_ES &&
( PMTEsFindDescriptor( p_dvbpsies, 0x45 ) || /* VBI Data descriptor */
PMTEsFindDescriptor( p_dvbpsies, 0x46 ) || /* VBI Teletext descriptor */
PMTEsFindDescriptor( p_dvbpsies, 0x56 ) ) ) /* EBU Teletext descriptor */
{
/* Teletext/VBI */
PMTSetupEsTeletext( p_demux, p_pes, p_dvbpsies );
}
}
/* FIXME is it useful ? */
if( PMTEsFindDescriptor( p_dvbpsies, 0x52 ) )
{
dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x52 );
dvbpsi_stream_identifier_dr_t *p_si = dvbpsi_DecodeStreamIdentifierDr( p_dr );
msg_Dbg( p_demux, " * Stream Component Identifier: %d", p_si->i_component_tag );
}
}
static void PMTSetupEs0xEA( demux_t *p_demux, ts_es_t *p_es,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
/* Registration Descriptor */
if( !PMTEsHasRegistration( p_demux, p_dvbpsies, "VC-1" ) )
{
msg_Err( p_demux, "Registration descriptor not found or invalid" );
return;
}
/* registration descriptor for VC-1 (SMPTE rp227) */
es_format_Change( &p_es->fmt, VIDEO_ES, VLC_CODEC_VC1 );
/* XXX With Simple and Main profile the SEQUENCE
* header is modified: video width and height are
* inserted just after the start code as 2 int16_t
* The packetizer will take care of that. */
}
static void PMTSetupEs0xD1( demux_t *p_demux, ts_es_t *p_es,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
/* Registration Descriptor */
if( !PMTEsHasRegistration( p_demux, p_dvbpsies, "drac" ) )
{
msg_Err( p_demux, "Registration descriptor not found or invalid" );
return;
}
/* registration descriptor for Dirac
* (backwards compatible with VC-2 (SMPTE Sxxxx:2008)) */
es_format_Change( &p_es->fmt, VIDEO_ES, VLC_CODEC_DIRAC );
}
static void PMTSetupEs0xA0( demux_t *p_demux, ts_es_t *p_es,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
/* MSCODEC sent by vlc */
dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0xa0 );
if( !p_dr || p_dr->i_length < 10 )
{
msg_Warn( p_demux,
"private MSCODEC (vlc) without bih private descriptor" );
return;
}
es_format_t *p_fmt = &p_es->fmt;
es_format_Change( &p_es->fmt, VIDEO_ES,
VLC_FOURCC( p_dr->p_data[0], p_dr->p_data[1],
p_dr->p_data[2], p_dr->p_data[3] ) );
p_fmt->video.i_width = GetWBE( &p_dr->p_data[4] );
p_fmt->video.i_height = GetWBE( &p_dr->p_data[6] );
p_fmt->video.i_visible_width = p_fmt->video.i_width;
p_fmt->video.i_visible_height = p_fmt->video.i_height;
p_fmt->i_extra = GetWBE( &p_dr->p_data[8] );
if( p_fmt->i_extra > 0 )
{
p_fmt->p_extra = malloc( p_fmt->i_extra );
if( p_fmt->p_extra )
memcpy( p_fmt->p_extra, &p_dr->p_data[10],
__MIN( p_fmt->i_extra, p_dr->i_length - 10 ) );
else
p_fmt->i_extra = 0;
}
/* For such stream we will gather them ourself and don't launch a
* packetizer.
* Yes it's ugly but it's the only way to have DIV3 working */
p_fmt->b_packetized = true;
}
static void PMTSetupEs0x83( const dvbpsi_pmt_t *p_pmt, ts_es_t *p_es, int i_pid )
{
/* WiDi broadcasts without registration on PMT 0x1, PCR 0x1000 and
* with audio track pid being 0x1100..0x11FF */
if ( p_pmt->i_program_number == 0x1 &&
p_pmt->i_pcr_pid == 0x1000 &&
( i_pid >> 8 ) == 0x11 )
{
/* Not enough ? might contain 0x83 private descriptor, 2 bytes 0x473F */
es_format_Change( &p_es->fmt, AUDIO_ES, VLC_CODEC_WIDI_LPCM );
}
else
es_format_Change( &p_es->fmt, AUDIO_ES, VLC_CODEC_DVD_LPCM );
}
static bool PMTSetupEsHDMV( demux_t *p_demux, ts_es_t *p_es,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
es_format_t *p_fmt = &p_es->fmt;
/* Blu-Ray mapping */
switch( p_dvbpsies->i_type )
{
case 0x80:
es_format_Change( p_fmt, AUDIO_ES, VLC_CODEC_BD_LPCM );
break;
case 0x81:
es_format_Change( p_fmt, AUDIO_ES, VLC_CODEC_A52 );
break;
case 0x85: /* DTS-HD High resolution audio */
case 0x86: /* DTS-HD Master audio */
es_format_Change( p_fmt, AUDIO_ES, VLC_CODEC_DTS );
p_fmt->i_profile = PROFILE_DTS_HD;
break;
case 0x82:
case 0xA2: /* Secondary DTS audio */
es_format_Change( p_fmt, AUDIO_ES, VLC_CODEC_DTS );
break;
case 0x83: /* TrueHD AC3 */
es_format_Change( p_fmt, AUDIO_ES, VLC_CODEC_TRUEHD );
break;
case 0x84: /* E-AC3 */
case 0xA1: /* Secondary E-AC3 */
es_format_Change( p_fmt, AUDIO_ES, VLC_CODEC_EAC3 );
break;
case 0x90: /* Presentation graphics */
es_format_Change( p_fmt, SPU_ES, VLC_CODEC_BD_PG );
break;
case 0x91: /* Interactive graphics */
es_format_Change( p_fmt, UNKNOWN_ES, 0 ); /* prevent regular ES handler */
break;
case 0x92: /* Subtitle */
es_format_Change( p_fmt, SPU_ES, VLC_CODEC_BD_TEXT );
break;
case 0xEA:
es_format_Change( p_fmt, VIDEO_ES, VLC_CODEC_VC1 );
break;
default:
msg_Info( p_demux, "HDMV registration not implemented for pid 0x%x type 0x%x",
p_dvbpsies->i_pid, p_dvbpsies->i_type );
return false;
}
return true;
}
static bool PMTSetupEsRegistration( demux_t *p_demux, ts_es_t *p_es,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
static const struct
{
char psz_tag[5];
enum es_format_category_e i_cat;
vlc_fourcc_t i_codec;
} p_regs[] = {
{ "AC-3", AUDIO_ES, VLC_CODEC_A52 },
{ "EAC3", AUDIO_ES, VLC_CODEC_EAC3 },
{ "DTS1", AUDIO_ES, VLC_CODEC_DTS },
{ "DTS2", AUDIO_ES, VLC_CODEC_DTS },
{ "DTS3", AUDIO_ES, VLC_CODEC_DTS },
{ "BSSD", AUDIO_ES, VLC_CODEC_302M },
{ "VC-1", VIDEO_ES, VLC_CODEC_VC1 },
{ "drac", VIDEO_ES, VLC_CODEC_DIRAC },
{ "", UNKNOWN_ES, 0 }
};
es_format_t *p_fmt = &p_es->fmt;
for( int i = 0; p_regs[i].i_cat != UNKNOWN_ES; i++ )
{
if( PMTEsHasRegistration( p_demux, p_dvbpsies, p_regs[i].psz_tag ) )
{
es_format_Change( p_fmt, p_regs[i].i_cat, p_regs[i].i_codec );
/* System A AC3 extension, see ATSC A/52 Annex G.2 */
if ( p_regs[i].i_codec == VLC_CODEC_A52 && p_dvbpsies->i_type == 0x87 )
p_fmt->i_codec = VLC_CODEC_EAC3;
return true;
}
}
return false;
}
static char *GetIso639AudioTypeDesc( uint8_t type )
{
static const char *audio_type[] = {
/* "Main audio", */
N_("clean effects"),
N_("hearing impaired"),
N_("visual impaired commentary"),
};
if ( type == 0 || type >= ARRAY_SIZE(audio_type) )
return NULL;
return strdup( audio_type[ --type ] );
}
static void PMTParseEsIso639( demux_t *p_demux, ts_es_t *p_es,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
/* get language descriptor */
dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x0a );
if( !p_dr )
return;
dvbpsi_iso639_dr_t *p_decoded = dvbpsi_DecodeISO639Dr( p_dr );
if( !p_decoded )
{
msg_Err( p_demux, " Failed to decode a ISO 639 descriptor" );
return;
}
p_es->fmt.psz_language = malloc( 4 );
if( p_es->fmt.psz_language )
{
memcpy( p_es->fmt.psz_language, p_decoded->code[0].iso_639_code, 3 );
p_es->fmt.psz_language[3] = 0;
msg_Dbg( p_demux, " found language: %s", p_es->fmt.psz_language);
}
uint8_t type = p_decoded->code[0].i_audio_type;
p_es->fmt.psz_description = GetIso639AudioTypeDesc( type );
if (type == 0x00) /* Undefined */
p_es->fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 1; // prioritize normal audio tracks
p_es->fmt.i_extra_languages = p_decoded->i_code_count-1;
if( p_es->fmt.i_extra_languages > 0 )
p_es->fmt.p_extra_languages =
malloc( sizeof(*p_es->fmt.p_extra_languages) *
p_es->fmt.i_extra_languages );
if( p_es->fmt.p_extra_languages )
{
for( unsigned i = 0; i < p_es->fmt.i_extra_languages; i++ )
{
extra_languages_t *p_lang = &p_es->fmt.p_extra_languages[i];
if( (p_lang->psz_language = malloc(4)) )
{
memcpy( p_lang->psz_language, p_decoded->code[i+1].iso_639_code, 3 );
p_lang->psz_language[3] = '\0';
}
p_lang->psz_description = GetIso639AudioTypeDesc( p_decoded->code[i].i_audio_type );
}
}
}
static void PIDFillFormat( demux_t *p_demux, ts_stream_t *p_pes,
int i_stream_type, ts_transport_type_t *p_datatype )
{
es_format_t *fmt = &p_pes->p_es->fmt;
switch( i_stream_type )
{
case 0x01: /* MPEG-1 video */
es_format_Change( fmt, VIDEO_ES, VLC_CODEC_MPGV );
fmt->i_original_fourcc = VLC_CODEC_MP1V;
break;
case 0x02: /* MPEG-2 video */
case 0x80: /* MPEG-2 MOTO video */
es_format_Change( fmt, VIDEO_ES, VLC_CODEC_MPGV );
break;
case 0x03: /* MPEG-1 audio */
case 0x04: /* MPEG-2 audio */
es_format_Change( fmt, AUDIO_ES, VLC_CODEC_MPGA );
break;
case 0x0f: /* ISO/IEC 13818-7 Audio with ADTS transport syntax */
es_format_Change( fmt, AUDIO_ES, VLC_CODEC_MP4A );
fmt->i_original_fourcc = VLC_FOURCC('A','D','T','S');
break;
case 0x10: /* MPEG4 (video) */
es_format_Change( fmt, VIDEO_ES, VLC_CODEC_MP4V );
break;
case 0x11: /* MPEG4 (audio) LATM */
es_format_Change( fmt, AUDIO_ES, VLC_CODEC_MP4A );
/* https://trac.ffmpeg.org/ticket/320
* Haivision's Makito encoder violates mapping by putting ADTS
instead of LOAS, we can only hint that's not raw AAC */
fmt->i_original_fourcc = VLC_FOURCC('H','E','A','D');
break;
case 0x1B: /* H264 <- check transport syntax/needed descriptor */
es_format_Change( fmt, VIDEO_ES, VLC_CODEC_H264 );
break;
case 0x1C: /* ISO/IEC 14496-3 Audio, without using any additional
transport syntax, such as DST, ALS and SLS */
es_format_Change( fmt, AUDIO_ES, VLC_CODEC_MP4A );
break;
case 0x24: /* HEVC */
es_format_Change( fmt, VIDEO_ES, VLC_CODEC_HEVC );
break;
case 0x42: /* CAVS (Chinese AVS) */
es_format_Change( fmt, VIDEO_ES, VLC_CODEC_CAVS );
break;
case 0x81: /* A52 (audio) */
es_format_Change( fmt, AUDIO_ES, VLC_CODEC_A52 );
break;
case 0x82: /* SCTE-27 (sub) */
es_format_Change( fmt, SPU_ES, VLC_CODEC_SCTE_27 );
*p_datatype = TS_TRANSPORT_SECTIONS;
ts_sections_processor_Add( p_demux, &p_pes->p_sections_proc, 0xC6, 0x00,
SCTE27_Section_Callback, p_pes );
break;
case 0x84: /* SDDS (audio) */
es_format_Change( fmt, AUDIO_ES, VLC_CODEC_SDDS );
break;
case 0x85: /* DTS (audio) FIXME: HDMV Only ? */
es_format_Change( fmt, AUDIO_ES, VLC_CODEC_DTS );
break;
case 0x87: /* E-AC3, ATSC */
es_format_Change( fmt, AUDIO_ES, VLC_CODEC_EAC3 );
break;
case 0x8a: /* DTS (audio) */
es_format_Change( fmt, AUDIO_ES, VLC_CODEC_DTS );
break;
case 0x91: /* A52 vls (audio) */
es_format_Change( fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', 'b' ) );
break;
case 0x92: /* DVD_SPU vls (sub) */
es_format_Change( fmt, SPU_ES, VLC_FOURCC( 's', 'p', 'u', 'b' ) );
break;
case 0x94: /* SDDS (audio) */
es_format_Change( fmt, AUDIO_ES, VLC_FOURCC( 's', 'd', 'd', 'b' ) );
break;
case 0xa0: /* MSCODEC vlc (video) (fixed later) */
es_format_Change( fmt, UNKNOWN_ES, 0 );
break;
case 0x06: /* PES_PRIVATE (fixed later) */
case 0x12: /* MPEG-4 generic (sub/scene/...) (fixed later) */
case 0xEA: /* Privately managed ES (VC-1) (fixed later */
default:
es_format_Change( fmt, UNKNOWN_ES, 0 );
break;
}
}
static void FillPESFromDvbpsiES( demux_t *p_demux,
const dvbpsi_pmt_t *p_dvbpsipmt,
const dvbpsi_pmt_es_t *p_dvbpsies,
ts_pmt_registration_type_t registration_type,
const ts_pmt_t *p_pmt,
ts_stream_t *p_pes )
{
ts_transport_type_t type_change = TS_TRANSPORT_PES;
PIDFillFormat( p_demux, p_pes, p_dvbpsies->i_type, &type_change );
p_pes->i_stream_type = p_dvbpsies->i_type;
bool b_registration_applied = false;
if ( p_dvbpsies->i_type >= 0x80 ) /* non standard, extensions */
{
if ( registration_type == TS_PMT_REGISTRATION_BLURAY )
{
if (( b_registration_applied = PMTSetupEsHDMV( p_demux, p_pes->p_es, p_dvbpsies ) ))
msg_Dbg( p_demux, " + HDMV registration applied to pid %d type 0x%x",
p_dvbpsies->i_pid, p_dvbpsies->i_type );
}
else
{
if (( b_registration_applied = PMTSetupEsRegistration( p_demux, p_pes->p_es, p_dvbpsies ) ))
msg_Dbg( p_demux, " + registration applied to pid %d type 0x%x",
p_dvbpsies->i_pid, p_dvbpsies->i_type );
}
}
if ( !b_registration_applied )
{
p_pes->transport = type_change; /* Only change type if registration has not changed meaning */
switch( p_dvbpsies->i_type )
{
case 0x02:
PMTSetupEs0x02( p_pes->p_es, p_dvbpsies );
break;
case 0x05: /* Private data in sections */
p_pes->transport = TS_TRANSPORT_SECTIONS;
PMTSetupEs0x05PrivateData( p_demux, p_pes->p_es, p_dvbpsies );
break;
case 0x06:
/* Handle PES private data */
PMTSetupEs0x06( p_demux, p_pes, p_dvbpsies );
break;
case 0x0a: /* DSM-CC */
case 0x0b:
case 0x0c:
case 0x0d:
p_pes->transport = TS_TRANSPORT_IGNORE;
break;
/* All other private or reserved types */
case 0x13: /* SL in sections */
p_pes->transport = TS_TRANSPORT_SECTIONS;
/* fallthrough */
case 0x0f:
case 0x10:
case 0x11:
case 0x12:
SetupISO14496Descriptors( p_demux, p_pes, p_pmt, p_dvbpsies );
break;
case 0x15:
SetupMetadataDescriptors( p_demux, p_pes, p_dvbpsies );
break;
case 0x1b:
SetupAVCDescriptors( p_demux, p_pes->p_es, p_dvbpsies );
break;
case 0x21:
SetupJ2KDescriptors( p_demux, p_pes->p_es, p_dvbpsies );
break;
case 0x83:
/* LPCM (audio) */
PMTSetupEs0x83( p_dvbpsipmt, p_pes->p_es, p_dvbpsies->i_pid );
break;
case 0xa0:
PMTSetupEs0xA0( p_demux, p_pes->p_es, p_dvbpsies );
break;
case 0xd1:
PMTSetupEs0xD1( p_demux, p_pes->p_es, p_dvbpsies );
break;
case 0xEA:
PMTSetupEs0xEA( p_demux, p_pes->p_es, p_dvbpsies );
default:
break;
}
}
if( p_pes->p_es->fmt.i_cat == AUDIO_ES ||
( p_pes->p_es->fmt.i_cat == SPU_ES &&
p_pes->p_es->fmt.i_codec != VLC_CODEC_DVBS &&
p_pes->p_es->fmt.i_codec != VLC_CODEC_TELETEXT ) )
{
PMTParseEsIso639( p_demux, p_pes->p_es, p_dvbpsies );
}
if( p_pes->p_es->fmt.i_cat == AUDIO_ES )
{
SetupAudioExtendedDescriptors( p_demux, p_pes->p_es, p_dvbpsies );
}
/* Disable dolbyvision */
if ( registration_type == TS_PMT_REGISTRATION_BLURAY &&
p_dvbpsies->i_pid == 0x1015 &&
PMTEsHasRegistration( p_demux, p_dvbpsies, "HDMV" ) )
p_pes->p_es->fmt.i_priority = ES_PRIORITY_NOT_DEFAULTABLE;
if ( registration_type == TS_PMT_REGISTRATION_BLURAY )
{
/*
* 0x1011 primary video
* 0x1100- 0x111f primary audio
* 0x1a00- 0x1a1f secondary audio
* 0x1b00- 0x1b1f secondary video */
/* Disable dolbyvision */
if ( p_dvbpsies->i_pid == 0x1015 &&
PMTEsHasRegistration( p_demux, p_dvbpsies, "HDMV" ) )
{
p_pes->p_es->fmt.i_priority = ES_PRIORITY_NOT_DEFAULTABLE;
}
else if( (p_dvbpsies->i_pid >= 0x1a00 && p_dvbpsies->i_pid <= 0x1a1f) ||
(p_dvbpsies->i_pid >= 0x1b00 && p_dvbpsies->i_pid <= 0x1b1f) )
{
/* We might just want to prio, but it will trigger multiple videos es */
p_pes->p_es->fmt.i_priority = ES_PRIORITY_NOT_DEFAULTABLE;
}
else
{
p_pes->p_es->fmt.i_priority = 0xFFFF - (p_dvbpsies->i_pid & 0xFFFF) +
ES_PRIORITY_SELECTABLE_MIN;
}
}
/* PES packets usually contain truncated frames */
p_pes->p_es->fmt.b_packetized = false;
/* Set Groups / ID */
p_pes->p_es->fmt.i_group = p_dvbpsipmt->i_program_number;
if( p_demux->p_sys->b_es_id_pid )
p_pes->p_es->fmt.i_id = p_dvbpsies->i_pid;
}
static en50221_capmt_info_t * CreateCAPMTInfo( const dvbpsi_pmt_t *p_pmt )
{
en50221_capmt_info_t *p_en = en50221_capmt_New( p_pmt->i_version,
p_pmt->i_program_number );
if( unlikely(p_en == NULL) )
return p_en;
for( const dvbpsi_descriptor_t *p_dr = p_pmt->p_first_descriptor;
p_dr; p_dr = p_dr->p_next )
{
if( p_dr->i_tag == 0x09 )
en50221_capmt_AddCADescriptor( p_en, p_dr->p_data, p_dr->i_length );
}
for( const dvbpsi_pmt_es_t *p_es = p_pmt->p_first_es;
p_es; p_es = p_es->p_next )
{
en50221_capmt_es_info_t *p_enes = en50221_capmt_EsAdd( p_en,
p_es->i_type,
p_es->i_pid );
if( likely(p_enes) )
{
for( const dvbpsi_descriptor_t *p_dr = p_es->p_first_descriptor;
p_dr; p_dr = p_dr->p_next )
{
if( p_dr->i_tag == 0x09 )
en50221_capmt_AddESCADescriptor( p_enes, p_dr->p_data, p_dr->i_length );
}
}
}
return p_en;
}
static void PMTCallBack( void *data, dvbpsi_pmt_t *p_dvbpsipmt )
{
demux_t *p_demux = data;
demux_sys_t *p_sys = p_demux->p_sys;
ts_pid_t *pmtpid = NULL;
ts_pmt_t *p_pmt = NULL;
msg_Dbg( p_demux, "PMTCallBack called for program %d", p_dvbpsipmt->i_program_number );
if (unlikely(GetPID(p_sys, 0)->type != TYPE_PAT))
{
assert(GetPID(p_sys, 0)->type == TYPE_PAT);
dvbpsi_pmt_delete(p_dvbpsipmt);
}
const ts_pat_t *p_pat = GetPID(p_sys, 0)->u.p_pat;
/* First find this PMT declared in PAT */
for( int i = 0; !pmtpid && i < p_pat->programs.i_size; i++ )
{
const int i_pmt_prgnumber = p_pat->programs.p_elems[i]->u.p_pmt->i_number;
if( i_pmt_prgnumber != TS_USER_PMT_NUMBER &&
i_pmt_prgnumber == p_dvbpsipmt->i_program_number )
{
pmtpid = p_pat->programs.p_elems[i];
assert(pmtpid->type == TYPE_PMT);
p_pmt = pmtpid->u.p_pmt;
}
}
if( pmtpid == NULL )
{
msg_Warn( p_demux, "unreferenced program (broken stream)" );
dvbpsi_pmt_delete(p_dvbpsipmt);
return;
}
pmtpid->i_flags |= FLAG_SEEN;
if( p_pmt->i_version != -1 &&
( !p_dvbpsipmt->b_current_next || p_pmt->i_version == p_dvbpsipmt->i_version ) )
{
dvbpsi_pmt_delete( p_dvbpsipmt );
return;
}
/* Save old es array */
DECL_ARRAY(ts_pid_t *) pid_to_decref;
pid_to_decref.i_alloc = p_pmt->e_streams.i_alloc;
pid_to_decref.i_size = p_pmt->e_streams.i_size;
pid_to_decref.p_elems = p_pmt->e_streams.p_elems;
if( p_pmt->p_atsc_si_basepid )
ARRAY_APPEND( pid_to_decref, p_pmt->p_atsc_si_basepid );
if( p_pmt->p_si_sdt_pid )
ARRAY_APPEND( pid_to_decref, p_pmt->p_si_sdt_pid );
ARRAY_INIT(p_pmt->e_streams);
if( p_pmt->iod )
{
ODFree( p_pmt->iod );
p_pmt->iod = NULL;
}
msg_Dbg( p_demux, "new PMT program number=%d version=%d pid_pcr=%d",
p_dvbpsipmt->i_program_number, p_dvbpsipmt->i_version, p_dvbpsipmt->i_pcr_pid );
p_pmt->i_pid_pcr = p_dvbpsipmt->i_pcr_pid;
p_pmt->i_version = p_dvbpsipmt->i_version;
if( ProgramIsSelected( p_sys, p_pmt->i_number ) )
SetPIDFilter( p_sys, GetPID(p_sys, p_pmt->i_pid_pcr), true ); /* Set demux filter */
/* Parse PMT descriptors */
ts_pmt_registration_type_t registration_type = TS_PMT_REGISTRATION_NONE;
ParsePMTRegistrations( p_demux, p_dvbpsipmt->p_first_descriptor, p_pmt, ®istration_type );
if( p_sys->standard == TS_STANDARD_AUTO )
{
switch( registration_type )
{
case TS_PMT_REGISTRATION_BLURAY:
TsChangeStandard( p_sys, TS_STANDARD_MPEG );
break;
case TS_PMT_REGISTRATION_ARIB:
TsChangeStandard( p_sys, TS_STANDARD_ARIB );
break;
case TS_PMT_REGISTRATION_ATSC:
TsChangeStandard( p_sys, TS_STANDARD_ATSC );
break;
default:
if(SEEN(GetPID(p_sys, ATSC_BASE_PID)))
{
TsChangeStandard( p_sys, TS_STANDARD_ATSC );
}
else
{
/* Probe using ES */
p_sys->standard = ProbePMTStandard( p_dvbpsipmt );
}
break;
}
}
/* Private descriptors depends on standard */
ParsePMTPrivateRegistrations( p_demux, p_dvbpsipmt->p_first_descriptor, p_pmt, p_sys->standard );
dvbpsi_pmt_es_t *p_dvbpsies;
for( p_dvbpsies = p_dvbpsipmt->p_first_es; p_dvbpsies != NULL; p_dvbpsies = p_dvbpsies->p_next )
{
ts_pid_t *pespid = GetPID(p_sys, p_dvbpsies->i_pid);
if ( pespid->type != TYPE_STREAM && pespid->type != TYPE_FREE )
{
msg_Warn( p_demux, " * PMT wants to create PES on pid %d used by non PES", pespid->i_pid );
continue;
}
char const * psz_typedesc = ISO13818_1_Get_StreamType_Description( p_dvbpsies->i_type );
msg_Dbg( p_demux, " * pid=%d type=0x%x %s",
p_dvbpsies->i_pid, p_dvbpsies->i_type, psz_typedesc );
/* PMT element/component descriptors */
for( dvbpsi_descriptor_t *p_dr = p_dvbpsies->p_first_descriptor;
p_dr != NULL; p_dr = p_dr->p_next )
{
const char *psz_desc = NULL;
if( registration_type == TS_PMT_REGISTRATION_ARIB )
psz_desc = ARIB_B10_Get_PMT_Descriptor_Description( p_dr->i_tag );
if( psz_desc )
msg_Dbg( p_demux, " - ES descriptor %s 0x%x", psz_desc, p_dr->i_tag );
else
msg_Dbg( p_demux, " - ES descriptor tag 0x%x", p_dr->i_tag );
}
const bool b_pid_inuse = ( pespid->type == TYPE_STREAM );
ts_stream_t *p_pes;
if ( !PIDSetup( p_demux, TYPE_STREAM, pespid, pmtpid ) )
{
msg_Warn( p_demux, " * pid=%d type=0x%x %s (skipped)",
p_dvbpsies->i_pid, p_dvbpsies->i_type, psz_typedesc );
continue;
}
else
{
if( b_pid_inuse ) /* pes will point to a temp */
{
p_pes = ts_stream_New( p_demux, p_pmt );
if( !p_pes )
{
PIDRelease( p_demux, pespid );
continue;
}
}
else /* pes will point to the new one allocated from PIDSetup */
{
p_pes = pespid->u.p_stream;
}
}
/* Add pid to the list of used ones in pmt */
ARRAY_APPEND( p_pmt->e_streams, pespid );
pespid->i_flags |= SEEN(GetPID(p_sys, p_dvbpsies->i_pid));
/* Fill p_pes es and add extra es if any */
FillPESFromDvbpsiES( p_demux, p_dvbpsipmt, p_dvbpsies,
registration_type, p_pmt, p_pes );
/* Set description and debug */
if( p_pes->p_es->fmt.i_cat == UNKNOWN_ES )
{
msg_Dbg( p_demux, " => pid %d content is *unknown*",
p_dvbpsies->i_pid );
p_pes->p_es->fmt.psz_description = strdup( psz_typedesc );
}
else
{
msg_Dbg( p_demux, " => pid %d has now es fcc=%4.4s",
p_dvbpsies->i_pid, (char*)&p_pes->p_es->fmt.i_codec );
}
dvbpsi_descriptor_t *p_dr = PMTEsFindDescriptor( p_dvbpsies, 0x09 );
if( p_dr && p_dr->i_length >= 2 )
{
msg_Dbg( p_demux, " - ES descriptor : CA (0x9) SysID 0x%x",
(p_dr->p_data[0] << 8) | p_dr->p_data[1] );
}
const bool b_create_es = (p_pes->p_es->fmt.i_cat != UNKNOWN_ES);
/* Now check and merge */
if( b_pid_inuse ) /* We need to compare to the existing pes/es */
{
ts_es_t *p_existing_es = ts_stream_Find_es( pespid->u.p_stream, p_pmt );
if( p_existing_es )
{
const es_format_t *ofmt = &p_existing_es->fmt;
const es_format_t *nfmt = &p_pes->p_es->fmt;
/* Check if we can avoid restarting that ES */
bool b_canreuse = es_format_IsSimilar( ofmt, nfmt );
/* Check codecs extra */
b_canreuse = b_canreuse &&
(
ofmt->i_extra == nfmt->i_extra &&
( ofmt->i_extra == 0 ||
memcmp( ofmt->p_extra, nfmt->p_extra, nfmt->i_extra ) == 0 )
);
/* Tracks must have same language */
b_canreuse = b_canreuse &&
(
( !!ofmt->psz_language == !!nfmt->psz_language ) &&
( ofmt->psz_language == NULL ||
!strcmp( ofmt->psz_language, nfmt->psz_language ) )
);
/* Check is we have any subtitles */
b_canreuse = b_canreuse &&
( ts_Count_es( p_pes->p_es->p_extraes, false, NULL ) ==
ts_Count_es( p_existing_es->p_extraes, false, NULL )
);
if( b_canreuse )
{
/* Just keep using previous es */
ts_stream_Del( p_demux, p_pes );
}
else
{
ts_es_t *p_new = ts_stream_Extract_es( p_pes, p_pmt );
ts_es_t *p_old = ts_stream_Extract_es( pespid->u.p_stream, p_pmt );
ts_stream_Add_es( pespid->u.p_stream, p_new, false );
assert(p_old == p_existing_es);
assert(ts_Count_es(p_pes->p_es, false, NULL) == 0);
ts_stream_Add_es( p_pes, p_old, false );
ts_stream_Del( p_demux, p_pes );
}
}
else /* There was no es for that program on that pid, merge in */
{
assert(ts_Count_es(pespid->u.p_stream->p_es, false, NULL)); /* Used by another program */
ts_es_t *p_new = ts_stream_Extract_es( p_pes, p_pmt );
assert( p_new );
ts_stream_Add_es( pespid->u.p_stream, p_new, false );
ts_stream_Del( p_demux, p_pes );
}
}
/* Nothing to do, pes is now just set */
if( b_create_es )
AddAndCreateES( p_demux, pespid, false );
}
/* Set CAM descrambling */
if( ProgramIsSelected( p_sys, p_pmt->i_number ) )
{
en50221_capmt_info_t *p_en = CreateCAPMTInfo( p_dvbpsipmt );
if( p_en )
{
/* DTV/CAM takes ownership of en50221_capmt_info_t on success */
if( vlc_stream_Control( p_sys->stream, STREAM_SET_PRIVATE_ID_CA, p_en ) != VLC_SUCCESS )
{
en50221_capmt_Delete( p_en );
if ( p_sys->standard == TS_STANDARD_ARIB && !p_sys->arib.b25stream )
{
p_sys->arib.b25stream = vlc_stream_FilterNew( p_demux->s, "aribcam" );
p_sys->stream = ( p_sys->arib.b25stream ) ? p_sys->arib.b25stream : p_demux->s;
}
}
}
}
/* Add arbitrary PID from here */
if ( p_sys->standard == TS_STANDARD_ATSC )
{
ts_pid_t *atsc_base_pid = GetPID(p_sys, ATSC_BASE_PID);
if ( PIDSetup( p_demux, TYPE_PSIP, atsc_base_pid, pmtpid ) )
{
ts_psip_t *p_psip = atsc_base_pid->u.p_psip;
if( !ATSC_Attach_Dvbpsi_Base_Decoders( p_psip->handle, atsc_base_pid ) )
{
msg_Err( p_demux, "dvbpsi_atsc_AttachMGT/STT failed for program %d",
p_pmt->i_number );
PIDRelease( p_demux, atsc_base_pid );
}
else
{
p_pmt->p_atsc_si_basepid = atsc_base_pid;
SetPIDFilter( p_demux->p_sys, atsc_base_pid, true );
msg_Dbg( p_demux, " * pid=%d listening for MGT/STT", atsc_base_pid->i_pid );
/* Set up EAS spu es */
if( p_pmt->e_streams.i_size )
{
ts_es_t *p_eas_es = ts_es_New( p_pmt );
if( likely(p_eas_es) )
{
es_format_Change( &p_eas_es->fmt, SPU_ES, VLC_CODEC_SCTE_18 );
p_eas_es->fmt.i_id = ATSC_BASE_PID;
p_eas_es->fmt.i_group = p_pmt->i_number;
p_eas_es->fmt.psz_description = strdup(SCTE18_DESCRIPTION);
if( p_psip->p_eas_es )
{
ts_es_t *p_next = p_psip->p_eas_es->p_next;
p_psip->p_eas_es->p_next = p_eas_es;
p_eas_es->p_next = p_next;
}
else
{
p_psip->p_eas_es = p_eas_es;
}
msg_Dbg( p_demux, " * pid=%d listening for EAS events", ATSC_BASE_PID );
}
}
}
}
else if( atsc_base_pid->type != TYPE_FREE )
{
msg_Err( p_demux, "can't attach PSIP table handlers"
"on already in use ATSC base pid %d", ATSC_BASE_PID );
}
}
else if( p_sys->standard != TS_STANDARD_MPEG && p_sys->standard != TS_STANDARD_TDMB )
{
ts_pid_t *p_sdt_pid = ts_pid_Get( &p_sys->pids, TS_SI_SDT_PID );
if ( PIDSetup( p_demux, TYPE_SI, p_sdt_pid, pmtpid ) ) /* Create or incref SDT */
{
if( !ts_attach_SI_Tables_Decoders( p_sdt_pid ) )
{
msg_Err( p_demux, "Can't attach SI table decoders from program %d",
p_pmt->i_number );
PIDRelease( p_demux, p_sdt_pid );
}
else
{
p_pmt->p_si_sdt_pid = p_sdt_pid;
SetPIDFilter( p_demux->p_sys, p_sdt_pid, true );
msg_Dbg( p_demux, " * pid=%d listening for SDT", p_sdt_pid->i_pid );
}
}
else if( p_sdt_pid->type != TYPE_FREE )
{
msg_Err( p_demux, "can't attach SI SDT table handler"
"on already in used pid %d (Not DVB ?)", p_sdt_pid->i_pid );
}
}
/* Decref or clean now unused es */
for( int i = 0; i < pid_to_decref.i_size; i++ )
PIDRelease( p_demux, pid_to_decref.p_elems[i] );
ARRAY_RESET( pid_to_decref );
if( !p_sys->b_trust_pcr )
{
int i_cand = FindPCRCandidate( p_pmt );
p_pmt->i_pid_pcr = i_cand;
p_pmt->pcr.b_disable = true;
msg_Warn( p_demux, "PCR not trusted for program %d, set up workaround using pid %d",
p_pmt->i_number, i_cand );
}
UpdatePESFilters( p_demux, p_demux->p_sys->seltype == PROGRAM_ALL );
/* Probe Boundaries */
if( p_sys->b_canfastseek && p_pmt->i_last_dts == -1 )
{
p_pmt->i_last_dts = 0;
ProbeStart( p_demux, p_pmt->i_number );
ProbeEnd( p_demux, p_pmt->i_number );
}
dvbpsi_pmt_delete( p_dvbpsipmt );
}
int UserPmt( demux_t *p_demux, const char *psz_fmt )
{
demux_sys_t *p_sys = p_demux->p_sys;
char *psz_dup = strdup( psz_fmt );
char *psz = psz_dup;
int i_number;
if( !psz_dup )
return VLC_ENOMEM;
/* Parse PID */
unsigned long i_pid = strtoul( psz, &psz, 0 );
if( i_pid < 2 || i_pid >= 8192 )
goto error;
/* Parse optional program number */
i_number = 0;
if( *psz == ':' )
i_number = strtol( &psz[1], &psz, 0 );
/* */
ts_pid_t *pmtpid = GetPID(p_sys, i_pid);
msg_Dbg( p_demux, "user pmt specified (pid=%lu,number=%d)", i_pid, i_number );
if ( !PIDSetup( p_demux, TYPE_PMT, pmtpid, GetPID(p_sys, 0) ) )
goto error;
/* Dummy PMT */
ts_pmt_t *p_pmt = pmtpid->u.p_pmt;
p_pmt->i_number = i_number != 0 ? i_number : TS_USER_PMT_NUMBER;
if( !dvbpsi_pmt_attach( p_pmt->handle,
((i_number != TS_USER_PMT_NUMBER ? i_number : 1)),
PMTCallBack, p_demux ) )
{
PIDRelease( p_demux, pmtpid );
goto error;
}
ARRAY_APPEND( GetPID(p_sys, 0)->u.p_pat->programs, pmtpid );
ARRAY_APPEND( p_sys->programs, pmtpid->i_pid );
p_sys->b_default_selection = true;
psz = strchr( psz, '=' );
if( psz )
psz++;
while( psz && *psz )
{
char *psz_next = strchr( psz, ',' );
if( psz_next )
*psz_next++ = '\0';
i_pid = strtoul( psz, &psz, 0 );
if( *psz != ':' || i_pid < 2 || i_pid >= 8192 )
goto next;
char *psz_opt = &psz[1];
if( !strcmp( psz_opt, "pcr" ) )
{
p_pmt->i_pid_pcr = i_pid;
}
else if( GetPID(p_sys, i_pid)->type == TYPE_FREE )
{
ts_pid_t *pid = GetPID(p_sys, i_pid);
char *psz_arg = strchr( psz_opt, '=' );
if( psz_arg )
*psz_arg++ = '\0';
if ( !PIDSetup( p_demux, TYPE_STREAM, pid, pmtpid ) )
continue;
ARRAY_APPEND( p_pmt->e_streams, pid );
if( p_pmt->i_pid_pcr <= 0 )
p_pmt->i_pid_pcr = i_pid;
es_format_t *fmt = &pid->u.p_stream->p_es->fmt;
if( psz_arg && strlen( psz_arg ) == 4 )
{
const vlc_fourcc_t i_codec = VLC_FOURCC( psz_arg[0], psz_arg[1],
psz_arg[2], psz_arg[3] );
int i_cat = UNKNOWN_ES;
if( !strcmp( psz_opt, "video" ) )
i_cat = VIDEO_ES;
else if( !strcmp( psz_opt, "audio" ) )
i_cat = AUDIO_ES;
else if( !strcmp( psz_opt, "spu" ) )
i_cat = SPU_ES;
es_format_Change( fmt, i_cat, i_codec );
fmt->b_packetized = false;
}
else
{
const int i_stream_type = strtol( psz_opt, NULL, 0 );
PIDFillFormat( p_demux, pid->u.p_stream, i_stream_type, &pid->u.p_stream->transport );
}
fmt->i_group = i_number;
if( p_sys->b_es_id_pid )
fmt->i_id = i_pid;
if( fmt->i_cat != UNKNOWN_ES )
{
msg_Dbg( p_demux, " * es pid=%lu fcc=%4.4s", i_pid,
(char*)&fmt->i_codec );
pid->u.p_stream->p_es->id = es_out_Add( p_demux->out, fmt );
p_sys->i_pmt_es++;
}
}
next:
psz = psz_next;
}
p_sys->b_user_pmt = true;
free( psz_dup );
return VLC_SUCCESS;
error:
free( psz_dup );
return VLC_EGENERIC;
}
bool ts_psi_PAT_Attach( ts_pid_t *patpid, void *cbdata )
{
if( unlikely(patpid->type != TYPE_PAT || patpid->i_pid != TS_PSI_PAT_PID) )
return false;
return dvbpsi_pat_attach( patpid->u.p_pat->handle, PATCallBack, cbdata );
}
void ts_psi_Packet_Push( ts_pid_t *p_pid, const uint8_t *p_pktbuffer )
{
if( p_pid->type == TYPE_PAT )
dvbpsi_packet_push( p_pid->u.p_pat->handle, (uint8_t *) p_pktbuffer );
else if( p_pid->type == TYPE_PMT )
dvbpsi_packet_push( p_pid->u.p_pmt->handle, (uint8_t *) p_pktbuffer );
}