Commit efc1cee9 authored by Francois Cartegnie's avatar Francois Cartegnie

demux: ts: split the beast

parent fb98186a
......@@ -229,14 +229,27 @@ libplaylist_plugin_la_SOURCES = \
demux_LTLIBRARIES += libplaylist_plugin.la
libts_plugin_la_SOURCES = demux/mpeg/ts.c demux/mpeg/ts.h \
demux/mpeg/ts_pid.h demux/mpeg/ts_pid.c \
demux/mpeg/ts_psi.h demux/mpeg/ts_psi.c \
demux/mpeg/ts_psi_eit.h demux/mpeg/ts_psi_eit.c \
demux/mpeg/ts_streams.h demux/mpeg/ts_streams.c \
demux/mpeg/ts_scte.h demux/mpeg/ts_scte.c \
demux/mpeg/sections.c demux/mpeg/sections.h \
demux/mpeg/mpeg4_iod.c demux/mpeg/mpeg4_iod.h \
demux/mpeg/ts_strings.h demux/mpeg/pes.h \
mux/mpeg/csa.c mux/mpeg/dvbpsi_compat.h \
mux/mpeg/streams.h mux/mpeg/tables.c mux/mpeg/tables.h \
demux/mpeg/ts_sl.c demux/mpeg/ts_sl.h \
demux/mpeg/ts_hotfixes.c demux/mpeg/ts_hotfixes.h \
demux/mpeg/ts_strings.h \
demux/mpeg/pes.h \
demux/mpeg/timestamps.h \
demux/dvb-text.h \
demux/opus.h \
mux/mpeg/csa.c \
mux/mpeg/dvbpsi_compat.h \
mux/mpeg/streams.h \
mux/mpeg/tables.c mux/mpeg/tables.h \
mux/mpeg/tsutil.c mux/mpeg/tsutil.h \
codec/scte18.h \
demux/dvb-text.h codec/opus_header.c demux/opus.h
codec/opus_header.c
libts_plugin_la_CFLAGS = $(AM_CFLAGS) $(DVBPSI_CFLAGS)
libts_plugin_la_LIBADD = $(DVBPSI_LIBS) $(SOCKET_LIBS)
if HAVE_ARIBB24
......
......@@ -17,6 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_MPEG4_IOD_H
#define VLC_MPEG4_IOD_H
#define ES_DESCRIPTOR_COUNT 255
typedef enum
......@@ -61,7 +63,10 @@ typedef struct
mtime_t i_pts;
} sl_header_data;
typedef struct
typedef struct es_mpeg4_descriptor_t es_mpeg4_descriptor_t;
typedef struct decoder_config_descriptor_t decoder_config_descriptor_t;
struct decoder_config_descriptor_t
{
uint8_t i_objectTypeIndication;
uint8_t i_streamType;
......@@ -69,9 +74,9 @@ typedef struct
unsigned i_extra;
uint8_t *p_extra;
} decoder_config_descriptor_t;
};
typedef struct
struct es_mpeg4_descriptor_t
{
bool b_ok;
uint16_t i_es_id;
......@@ -81,7 +86,7 @@ typedef struct
decoder_config_descriptor_t dec_descr;
sl_config_descriptor_t sl_descr;
} es_mpeg4_descriptor_t;
};
typedef struct
{
......@@ -105,3 +110,4 @@ sl_header_data DecodeSLHeader( unsigned i_data, const uint8_t *p_data,
const sl_config_descriptor_t *sl );
void DecodeODCommand( vlc_object_t *p_object, od_descriptors_t *,
unsigned i_data, const uint8_t *p_data );
#endif
......@@ -17,12 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#define FROM_SCALE_NZ(x) ((x) * 100 / 9)
#define TO_SCALE_NZ(x) ((x) * 9 / 100)
#define FROM_SCALE(x) (VLC_TS_0 + FROM_SCALE_NZ(x))
#define TO_SCALE(x) TO_SCALE_NZ((x) - VLC_TS_0)
#ifndef VLC_MPEG_PES_H
#define VLC_MPEG_PES_H
static inline mtime_t ExtractPESTimestamp( const uint8_t *p_data )
{
......@@ -44,6 +40,7 @@ static inline mtime_t ExtractMPEG1PESTimestamp( const uint8_t *p_data )
(mtime_t)(p_data[4] >> 3);
}
inline
static int ParsePESHeader( vlc_object_t *p_object, const uint8_t *p_header, size_t i_header,
unsigned *pi_skip, mtime_t *pi_dts, mtime_t *pi_pts,
uint8_t *pi_stream_id )
......@@ -140,3 +137,5 @@ static int ParsePESHeader( vlc_object_t *p_object, const uint8_t *p_header, size
*pi_skip = i_skip;
return VLC_SUCCESS;
}
#endif
......@@ -24,6 +24,7 @@
#include <assert.h>
#include <vlc_demux.h>
#include <vlc_memory.h>
#include "timestamps.h"
/* 256-0xC0 for normal stream, 256 for 0xbd stream, 256 for 0xfd stream, 8 for 0xa0 AOB stream */
#define PS_TK_COUNT (256+256+256+8 - 0xc0)
......
......@@ -17,10 +17,16 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "sections.h"
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_block.h>
#include "ts_pid.h"
#include "sections.h"
typedef struct ts_sections_assembler_t
{
int8_t i_version;
......
......@@ -20,13 +20,6 @@
#ifndef TS_SECTIONS_H
#define TS_SECTIONS_H
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "ts.h"
#include <vlc_common.h>
typedef void(* ts_section_callback_t)( demux_t *, ts_pid_t *, block_t * );
typedef struct ts_sections_processor_t ts_sections_processor_t;
......
/*****************************************************************************
* timestamps.h: MPEG TS/PS Timestamps helpers
*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef VLC_MPEG_TIMESTAMPS_H
#define VLC_MPEG_TIMESTAMPS_H
#define FROM_SCALE_NZ(x) ((x) * 100 / 9)
#define TO_SCALE_NZ(x) ((x) * 9 / 100)
#define FROM_SCALE(x) (VLC_TS_0 + FROM_SCALE_NZ(x))
#define TO_SCALE(x) TO_SCALE_NZ((x) - VLC_TS_0)
static inline int64_t TimeStampWrapAround( int64_t i_first_pcr, int64_t i_time )
{
int64_t i_adjust = 0;
if( i_first_pcr > 0x0FFFFFFFF && i_time < 0x0FFFFFFFF )
i_adjust = 0x1FFFFFFFF;
return i_time + i_adjust;
}
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -20,6 +20,114 @@
#ifndef VLC_TS_H
#define VLC_TS_H
typedef struct ts_pid_t ts_pid_t;
#ifdef HAVE_ARIBB24
typedef struct arib_instance_t arib_instance_t;
#endif
typedef struct csa_t csa_t;
#define TS_USER_PMT_NUMBER (0)
typedef enum arib_modes_e
{
ARIBMODE_AUTO = -1,
ARIBMODE_DISABLED = 0,
ARIBMODE_ENABLED = 1
} arib_modes_e;
typedef struct
{
int i_service;
} vdr_info_t;
struct demux_sys_t
{
stream_t *stream;
bool b_canseek;
bool b_canfastseek;
vlc_mutex_t csa_lock;
/* TS packet size (188, 192, 204) */
unsigned i_packet_size;
/* Additional TS packet header size (BluRay TS packets have 4-byte header before sync byte) */
unsigned i_packet_header_size;
/* how many TS packet we read at once */
unsigned i_ts_read;
bool b_force_seek_per_percent;
bool b_atsc_eas;
struct
{
arib_modes_e e_mode;
#ifdef HAVE_ARIBB24
arib_instance_t *p_instance;
#endif
stream_t *b25stream;
} arib;
/* All pid */
ts_pid_list_t pids;
bool b_user_pmt;
int i_pmt_es;
bool b_es_all; /* If we need to return all es/programs */
enum
{
NO_ES, /* for preparse */
DELAY_ES,
CREATE_ES
} es_creation;
#define PREPARSING p_sys->es_creation == NO_ES
/* */
bool b_es_id_pid;
uint16_t i_next_extraid;
csa_t *csa;
int i_csa_pkt_size;
bool b_split_es;
bool b_trust_pcr;
/* */
bool b_access_control;
bool b_end_preparse;
/* */
bool b_dvb_meta;
int64_t i_tdt_delta;
int64_t i_dvb_start;
int64_t i_dvb_length;
bool b_broken_charset; /* True if broken encoding is used in EPG/SDT */
/* Selected programs */
DECL_ARRAY( int ) programs; /* List of selected/access-filtered programs */
bool b_default_selection; /* True if set by default to first pmt seen (to get data from filtered access) */
struct
{
mtime_t i_first_dts; /* first dts encountered for the stream */
int i_timesourcepid; /* which pid we saved the dts from */
enum { PAT_WAITING = 0, PAT_MISSING, PAT_FIXTRIED } status; /* set if we haven't seen PAT within MIN_PAT_INTERVAL */
} patfix;
vdr_info_t vdr;
/* */
bool b_start_record;
};
bool ProgramIsSelected( demux_sys_t *, uint16_t i_pgrm );
void UpdatePESFilters( demux_t *p_demux, bool b_all );
int ProbeStart( demux_t *p_demux, int i_program );
int ProbeEnd( demux_t *p_demux, int i_program );
void AddAndCreateES( demux_t *p_demux, ts_pid_t *pid, bool b_create_delayed );
int FindPCRCandidate( ts_pmt_t *p_pmt );
#endif
/*****************************************************************************
* ts_hotfixes.c : MPEG PMT/PAT less streams fixups
*****************************************************************************
* Copyright (C) 2014-2016 - VideoLAN Authors
*
* 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_demux.h>
#include <vlc_es.h>
#ifndef _DVBPSI_DVBPSI_H_
#include <dvbpsi/dvbpsi.h>
#endif
#include <dvbpsi/descriptor.h>
#include <dvbpsi/pat.h>
#include <dvbpsi/pmt.h>
#include "../../mux/mpeg/streams.h"
#include "../../mux/mpeg/tsutil.h"
#include "../../mux/mpeg/tables.h"
#include "timestamps.h"
#include "pes.h"
#include "ts_streams.h"
#include "ts_psi.h"
#include "ts_pid.h"
#include "ts_streams_private.h"
#include "ts.h"
#include "ts_hotfixes.h"
#include <assert.h>
void ProbePES( demux_t *p_demux, ts_pid_t *pid, const uint8_t *p_pesstart, size_t i_data, bool b_adaptfield )
{
demux_sys_t *p_sys = p_demux->p_sys;
const uint8_t *p_pes = p_pesstart;
pid->probed.i_type = -1;
if( b_adaptfield )
{
if ( i_data < 2 )
return;
uint8_t len = *p_pes;
p_pes++; i_data--;
if(len == 0)
{
p_pes++; i_data--;/* stuffing */
}
else
{
if( i_data < len )
return;
if( len >= 7 && (p_pes[1] & 0x10) )
pid->probed.i_pcr_count++;
p_pes += len;
i_data -= len;
}
}
if( i_data < 9 )
return;
if( p_pes[0] != 0 || p_pes[1] != 0 || p_pes[2] != 1 )
return;
size_t i_pesextoffset = 8;
mtime_t i_dts = -1;
if( p_pes[7] & 0x80 ) // PTS
{
i_pesextoffset += 5;
if ( i_data < i_pesextoffset )
return;
i_dts = ExtractPESTimestamp( &p_pes[9] );
}
if( p_pes[7] & 0x40 ) // DTS
{
i_pesextoffset += 5;
if ( i_data < i_pesextoffset )
return;
i_dts = ExtractPESTimestamp( &p_pes[14] );
}
if( p_pes[7] & 0x20 ) // ESCR
i_pesextoffset += 6;
if( p_pes[7] & 0x10 ) // ESrate
i_pesextoffset += 3;
if( p_pes[7] & 0x08 ) // DSM
i_pesextoffset += 1;
if( p_pes[7] & 0x04 ) // CopyInfo
i_pesextoffset += 1;
if( p_pes[7] & 0x02 ) // PESCRC
i_pesextoffset += 2;
if ( i_data < i_pesextoffset )
return;
/* HeaderdataLength */
const size_t i_payloadoffset = 8 + 1 + p_pes[8];
i_pesextoffset += 1;
if ( i_data < i_pesextoffset || i_data < i_payloadoffset )
return;
i_data -= 8 + 1 + p_pes[8];
if( p_pes[7] & 0x01 ) // PESExt
{
size_t i_extension2_offset = 1;
if ( p_pes[i_pesextoffset] & 0x80 ) // private data
i_extension2_offset += 16;
if ( p_pes[i_pesextoffset] & 0x40 ) // pack
i_extension2_offset += 1;
if ( p_pes[i_pesextoffset] & 0x20 ) // seq
i_extension2_offset += 2;
if ( p_pes[i_pesextoffset] & 0x10 ) // P-STD
i_extension2_offset += 2;
if ( p_pes[i_pesextoffset] & 0x01 ) // Extension 2
{
uint8_t i_len = p_pes[i_pesextoffset + i_extension2_offset] & 0x7F;
i_extension2_offset += i_len;
}
if( i_data < i_extension2_offset )
return;
i_data -= i_extension2_offset;
}
/* (i_payloadoffset - i_pesextoffset) 0xFF stuffing */
if ( i_data < 4 )
return;
const uint8_t *p_data = &p_pes[i_payloadoffset];
/* NON MPEG audio & subpictures STREAM */
if(p_pes[3] == 0xBD)
{
if( !memcmp( p_data, "\x7F\xFE\x80\x01", 4 ) )
{
pid->probed.i_type = 0x06;
pid->probed.i_fourcc = VLC_CODEC_DTS;
}
else if( !memcmp( p_data, "\x0B\x77", 2 ) )
{
pid->probed.i_type = 0x06;
pid->probed.i_fourcc = VLC_CODEC_EAC3;
}
}
/* MPEG AUDIO STREAM */
else if(p_pes[3] >= 0xC0 && p_pes[3] <= 0xDF)
{
if( p_data[0] == 0xFF && (p_data[1] & 0xE0) == 0xE0 )
{
switch(p_data[1] & 18)
{
/* 10 - MPEG Version 2 (ISO/IEC 13818-3)
11 - MPEG Version 1 (ISO/IEC 11172-3) */
case 0x10:
pid->probed.i_type = 0x04;
break;
case 0x18:
pid->probed.i_type = 0x03;
default:
break;
}
switch(p_data[1] & 6)
{
/* 01 - Layer III
10 - Layer II
11 - Layer I */
case 0x06:
pid->probed.i_type = 0x04;
pid->probed.i_fourcc = VLC_CODEC_MPGA;
break;
case 0x04:
pid->probed.i_type = 0x04;
pid->probed.i_fourcc = VLC_CODEC_MP2;
break;
case 0x02:
pid->probed.i_type = 0x04;
pid->probed.i_fourcc = VLC_CODEC_MP3;
default:
break;
}
}
}
/* VIDEO STREAM */
else if( p_pes[3] >= 0xE0 && p_pes[3] <= 0xEF )
{
if( !memcmp( p_data, "\x00\x00\x00\x01", 4 ) )
{
pid->probed.i_type = 0x1b;
pid->probed.i_fourcc = VLC_CODEC_H264;
}
else if( !memcmp( p_data, "\x00\x00\x01", 4 ) )
{
pid->probed.i_type = 0x02;
pid->probed.i_fourcc = VLC_CODEC_MPGV;
}
}
/* Track timestamps and flag missing PAT */
if( !p_sys->patfix.i_timesourcepid && i_dts > -1 )
{
p_sys->patfix.i_first_dts = i_dts;
p_sys->patfix.i_timesourcepid = pid->i_pid;
}
else if( p_sys->patfix.i_timesourcepid == pid->i_pid && i_dts > -1 &&
p_sys->patfix.status == PAT_WAITING )
{
if( i_dts - p_sys->patfix.i_first_dts > TO_SCALE(MIN_PAT_INTERVAL) )
p_sys->patfix.status = PAT_MISSING;
}
}
static void BuildPATCallback( void *p_opaque, block_t *p_block )
{
ts_pid_t *pat_pid = (ts_pid_t *) p_opaque;
dvbpsi_packet_push( pat_pid->u.p_pat->handle, p_block->p_buffer );
}
static void BuildPMTCallback( void *p_opaque, block_t *p_block )
{
ts_pid_t *program_pid = (ts_pid_t *) p_opaque;
assert(program_pid->type == TYPE_PMT);
while( p_block )
{
dvbpsi_packet_push( program_pid->u.p_pmt->handle,
p_block->p_buffer );
p_block = p_block->p_next;
}
}
void MissingPATPMTFixup( demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
int i_program_number = 1234;
int i_program_pid = 1337;
int i_pcr_pid = 0x1FFF;
int i_num_pes = 0;
ts_pid_t *p_program_pid = GetPID( p_sys, i_program_pid );
if( SEEN(p_program_pid) )
{
/* Find a free one */
for( i_program_pid = MIN_ES_PID;
i_program_pid <= MAX_ES_PID && SEEN(p_program_pid);
i_program_pid++ )
{
p_program_pid = GetPID( p_sys, i_program_pid );
}
}
const ts_pid_t *p_pid = NULL;
ts_pid_next_context_t pidnextctx = ts_pid_NextContextInitValue;
while( (p_pid = ts_pid_Next( &p_sys->pids, &pidnextctx )) )
{
if( !SEEN(p_pid) || p_pid->probed.i_type == -1 )
continue;
if( i_pcr_pid == 0x1FFF && ( p_pid->probed.i_type == 0x03 ||
p_pid->probed.i_pcr_count ) )
i_pcr_pid = p_pid->i_pid;
i_num_pes++;
}
if( i_num_pes == 0 )
return;
ts_stream_t patstream =
{
.i_pid = 0,
.i_continuity_counter = 0x10,
.b_discontinuity = false
};
ts_stream_t pmtprogramstream =
{
.i_pid = i_program_pid,
.i_continuity_counter = 0x0,
.b_discontinuity = false
};
BuildPAT( GetPID(p_sys, 0)->u.p_pat->handle,
&p_sys->pids.pat, BuildPATCallback,
0, 1,
&patstream,
1, &pmtprogramstream, &i_program_number );
/* PAT callback should have been triggered */
if( p_program_pid->type != TYPE_PMT )
{
msg_Err( p_demux, "PAT creation failed" );
return;
}
struct esstreams_t
{
pes_stream_t pes;
ts_stream_t ts;
};
es_format_t esfmt = {0};
struct esstreams_t *esstreams = calloc( i_num_pes, sizeof(struct esstreams_t) );
pes_mapped_stream_t *mapped = calloc( i_num_pes, sizeof(pes_mapped_stream_t) );
if( esstreams && mapped )
{
int j=0;
for( int i=0; i<p_sys->pids.i_all; i++ )
{
const ts_pid_t *p_pid = p_sys->pids.pp_all[i];
if( !SEEN(p_pid) ||
p_pid->probed.i_type == -1 )
continue;
esstreams[j].pes.i_codec = p_pid->probed.i_fourcc;
esstreams[j].pes.i_stream_type = p_pid->probed.i_type;
esstreams[j].ts.i_pid = p_pid->i_pid;
mapped[j].pes = &esstreams[j].pes;
mapped[j].ts = &esstreams[j].ts;
mapped[j].fmt = &esfmt;
j++;
}
BuildPMT( GetPID(p_sys, 0)->u.p_pat->handle, VLC_OBJECT(p_demux),
p_program_pid, BuildPMTCallback,
0, 1,
i_pcr_pid,
NULL,
1, &pmtprogramstream, &i_program_number,
i_num_pes, mapped );
}
free(esstreams);
free(mapped);
}
/*****************************************************************************
* ts_hotfixes.h : MPEG PMT/PAT less streams fixups
*****************************************************************************
* Copyright (C) 2014-2016 - VideoLAN Authors
*
* 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef VLC_TS_HOTFIXES_H
#define VLC_TS_HOTFIXES_H
#define MIN_PAT_INTERVAL CLOCK_FREQ // DVB is 500ms
void ProbePES( demux_t *p_demux, ts_pid_t *pid, const uint8_t *p_pesstart, size_t i_data, bool b_adaptfield );
void MissingPATPMTFixup( demux_t *p_demux );
#endif
/*****************************************************************************
* ts_pid.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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_demux.h>
#include "ts_pid.h"
#include "ts_streams.h"
#include "ts.h"
#include <assert.h>
#define PID_ALLOC_CHUNK 16
void ts_pid_list_Init( ts_pid_list_t *p_list )
{
p_list->dummy.i_pid = 8191;
p_list->dummy.i_flags = FLAG_SEEN;
p_list->pp_all = NULL;
p_list->i_all = 0;
p_list->i_all_alloc = 0;
p_list->i_last_pid = 0;
p_list->p_last = NULL;
}
void ts_pid_list_Release( demux_t *p_demux, ts_pid_list_t *p_list )
{
for( int i = 0; i < p_list->i_all; i++ )
{
ts_pid_t *pid = p_list->pp_all[i];
#ifndef NDEBUG
if( pid->type != TYPE_FREE )
msg_Err( p_demux, "PID %d type %d not freed refcount %d", pid->i_pid, pid->type, pid->i_refcount );
#endif
free( pid );
}
free( p_list->pp_all );
}
ts_pid_t * ts_pid_Get( ts_pid_list_t *p_list, uint16_t i_pid )
{
switch( i_pid )
{
case 0:
return &p_list->pat;
case 0x1FFF:
return &p_list->dummy;
default:
if( p_list->i_last_pid == i_pid )
return p_list->p_last;
break;
}
for( int i=0; i < p_list->i_all; i++ )
{
if( p_list->pp_all[i]->i_pid == i_pid )
{
p_list->p_last = p_list->pp_all[i];
p_list->i_last_pid = i_pid;
return p_list->p_last;
}
}
if( p_list->i_all >= p_list->i_all_alloc )
{
ts_pid_t **p_realloc = realloc( p_list->pp_all,
(p_list->i_all_alloc + PID_ALLOC_CHUNK) * sizeof(ts_pid_t *) );
if( !p_realloc )
{
abort();
//return NULL;
}
p_list->pp_all = p_realloc;
p_list->i_all_alloc += PID_ALLOC_CHUNK;
}
ts_pid_t *p_pid = calloc( 1, sizeof(*p_pid) );
if( !p_pid )
{
abort();
//return NULL;
}
p_pid->i_pid = i_pid;
p_list->pp_all[p_list->i_all++] = p_pid;
p_list->p_last = p_pid;
p_list->i_last_pid = i_pid;
return p_pid;
}
ts_pid_t * ts_pid_Next( ts_pid_list_t *p_list, ts_pid_next_context_t *p_ctx )
{
if( likely(p_list->i_all && p_ctx) )
{
if( p_ctx->i_pos < p_list->i_all )
return p_list->pp_all[p_ctx->i_pos++];
}
return NULL;
}
static void PIDReset( ts_pid_t *pid )
{
assert(pid->i_refcount == 0);
pid->i_cc = 0xff;
pid->i_flags &= ~FLAG_SCRAMBLED;
pid->type = TYPE_FREE;
}
bool PIDSetup( demux_t *p_demux, ts_pid_type_t i_type, ts_pid_t *pid, ts_pid_t *p_parent )
{
if( pid == p_parent || pid->i_pid == 0x1FFF )
return false;
if( pid->i_refcount == 0 )
{
assert( pid->type == TYPE_FREE );
switch( i_type )
{
case TYPE_FREE: /* nonsense ?*/
PIDReset( pid );
return true;
case TYPE_PAT:
PIDReset( pid );
pid->u.p_pat = ts_pat_New( p_demux );
if( !pid->u.p_pat )
return false;
break;
case TYPE_PMT:
PIDReset( pid );
pid->u.p_pmt = ts_pmt_New( p_demux );
if( !pid->u.p_pmt )
return false;
break;
case TYPE_PES:
PIDReset( pid );
pid->u.p_pes = ts_pes_New( p_demux, p_parent->u.p_pmt );
if( !pid->u.p_pes )
return false;
break;
case TYPE_SDT:
case TYPE_TDT:
case TYPE_EIT:
PIDReset( pid );
pid->u.p_psi = ts_psi_New( p_demux );
if( !pid->u.p_psi )
return false;
break;
default:
assert(false);
break;
}
pid->i_refcount++;
pid->type = i_type;
}
else if( pid->type == i_type && pid->i_refcount < UINT16_MAX )
{
pid->i_refcount++;
}
else
{
if( pid->type != TYPE_FREE )
msg_Warn( p_demux, "Tried to redeclare pid %d with another type", pid->i_pid );
return false;
}
return true;
}
void PIDRelease( demux_t *p_demux, ts_pid_t *pid )
{
if( pid->i_refcount == 0 )
{
assert( pid->type == TYPE_FREE );
return;
}
else if( pid->i_refcount == 1 )
{
pid->i_refcount--;
}
else if( pid->i_refcount > 1 )
{
assert( pid->type != TYPE_FREE && pid->type != TYPE_PAT );
pid->i_refcount--;
}
if( pid->i_refcount == 0 )
{
switch( pid->type )
{
default:
case TYPE_FREE: /* nonsense ?*/
assert( pid->type != TYPE_FREE );
break;
case TYPE_PAT:
ts_pat_Del( p_demux, pid->u.p_pat );
pid->u.p_pat = NULL;
break;
case TYPE_PMT:
ts_pmt_Del( p_demux, pid->u.p_pmt );
pid->u.p_pmt = NULL;
break;
case TYPE_PES:
ts_pes_Del( p_demux, pid->u.p_pes );
pid->u.p_pes = NULL;
break;
case TYPE_SDT:
case TYPE_TDT:
case TYPE_EIT:
ts_psi_Del( p_demux, pid->u.p_psi );
pid->u.p_psi = NULL;
break;
}
SetPIDFilter( p_demux->p_sys, pid, false );
PIDReset( pid );
}
}
int UpdateHWFilter( demux_sys_t *p_sys, ts_pid_t *p_pid )
{
if( !p_sys->b_access_control )
return VLC_EGENERIC;
return stream_Control( p_sys->stream, STREAM_SET_PRIVATE_ID_STATE,
p_pid->i_pid, !!(p_pid->i_flags & FLAG_FILTERED) );
}
int SetPIDFilter( demux_sys_t *p_sys, ts_pid_t *p_pid, bool b_selected )
{
if( b_selected )
p_pid->i_flags |= FLAG_FILTERED;
else
p_pid->i_flags &= ~FLAG_FILTERED;
return UpdateHWFilter( p_sys, p_pid );
}
/*****************************************************************************
* ts_pid.h: 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef VLC_TS_PID_H
#define VLC_TS_PID_H
typedef struct ts_pid_t ts_pid_t;
#define MIN_ES_PID 4 /* Should be 32.. broken muxers */
#define MAX_ES_PID 8190
#include "ts_streams.h"
typedef enum
{
TYPE_FREE = 0,
TYPE_PAT,
TYPE_PMT,
TYPE_PES,
TYPE_SDT,
TYPE_TDT,
TYPE_EIT,
} ts_pid_type_t;
enum
{
FLAGS_NONE = 0,
FLAG_SEEN = 1,
FLAG_SCRAMBLED = 2,
FLAG_FILTERED = 4
};
#define SEEN(x) ((x)->i_flags & FLAG_SEEN)
#define SCRAMBLED(x) ((x).i_flags & FLAG_SCRAMBLED)
struct ts_pid_t
{
uint16_t i_pid;
uint8_t i_flags;
uint8_t i_cc; /* countinuity counter */
uint8_t type;
uint16_t i_refcount;
/* */
union
{
ts_pat_t *p_pat;
ts_pmt_t *p_pmt;
ts_pes_t *p_pes;
ts_psi_t *p_psi;
} u;
struct
{
vlc_fourcc_t i_fourcc;
int i_type;
int i_pcr_count;
} probed;
};
typedef struct ts_pid_list_t
{
ts_pid_t pat;
ts_pid_t dummy;
/* all non commons ones, dynamically allocated */
ts_pid_t **pp_all;
int i_all;
int i_all_alloc;
/* last recently used */
uint16_t i_last_pid;
ts_pid_t *p_last;
} ts_pid_list_t;
/* opacified pid list */
void ts_pid_list_Init( ts_pid_list_t * );
void ts_pid_list_Release( demux_t *, ts_pid_list_t * );
/* creates missing pid on the fly */
ts_pid_t * ts_pid_Get( ts_pid_list_t *, uint16_t i_pid );
/* returns NULL on end. requires context */
typedef struct
{
int i_pos;
} ts_pid_next_context_t;
#define ts_pid_NextContextInitValue { 0 }
ts_pid_t * ts_pid_Next( ts_pid_list_t *, ts_pid_next_context_t * );
/* for legacy only: don't use and pass directly list reference */
#define GetPID(p_sys, i_pid) ts_pid_Get((&(p_sys)->pids), i_pid)
int UpdateHWFilter( demux_sys_t *, ts_pid_t * );
int SetPIDFilter( demux_sys_t *, ts_pid_t *, bool b_selected );
bool PIDSetup( demux_t *p_demux, ts_pid_type_t i_type, ts_pid_t *pid, ts_pid_t *p_parent );
void PIDRelease( demux_t *p_demux, ts_pid_t *pid );
#endif
/*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#ifndef _DVBPSI_DVBPSI_H_
# include <dvbpsi/dvbpsi.h>
#endif
#include <dvbpsi/descriptor.h>
#include <dvbpsi/pat.h>
#include <dvbpsi/pmt.h>
#include <dvbpsi/dr.h>
#include <vlc_demux.h>
#include <vlc_bits.h>
#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/opus_header.h"
#include "sections.h"
#include "ts_sl.h"
#include "ts_scte.h"
#include <assert.h>
static void PIDFillFormat( ts_pes_t *p_pes, int i_stream_type, ts_es_data_type_t * );
static void ValidateDVBMeta( demux_t *p_demux, int i_pid )
{
demux_sys_t *p_sys = p_demux->p_sys;
if( !p_sys->b_dvb_meta || ( i_pid != 0x11 && i_pid != 0x12 && i_pid != 0x14 ) )
return;
msg_Warn( p_demux, "Switching to non DVB mode" );
/* This doesn't look like a DVB stream so don't try
* parsing the SDT/EDT/TDT */
PIDRelease( p_demux, GetPID(p_sys, 0x11) );
PIDRelease( p_demux, GetPID(p_sys, 0x12) );
PIDRelease( p_demux, GetPID(p_sys, 0x14) );
p_sys->b_dvb_meta = false;
}
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;
}
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;
}
if( ( p_pat->i_version != -1 &&
( !p_dvbpsipat->b_current_next ||
p_dvbpsipat->i_version == p_pat->i_version ) ) ||
( p_pat->i_ts_id != -1 && p_dvbpsipat->i_ts_id != p_pat->i_ts_id ) ||
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);
/* 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);
ValidateDVBMeta( p_demux, 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 );
}
if( SetPIDFilter( p_sys, pmtpid, true ) )
p_sys->b_access_control = false;
else 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;
for(int i=0; i<old_pmt_rm.i_size; i++)
{
/* decref current or release now unreferenced */
PIDRelease( p_demux, old_pmt_rm.p_elems[i] );
}
ARRAY_RESET(old_pmt_rm);
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; i<ARRAY_SIZE(regs); i++ )
{
if( !memcmp( regs[i].rgs, p_dr->p_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->arib.e_mode == ARIBMODE_AUTO &&
registration_type == TS_PMT_REGISTRATION_NONE &&
i_arib_score_flags == 0x07 ) //0b111
{
registration_type = TS_PMT_REGISTRATION_ARIB;
p_sys->arib.e_mode = ARIBMODE_ENABLED;
}
/* 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(p_dr->i_tag)
{
case 0x88: /* EACEM Simulcast HD Logical channels ordering */
if( registration_type == TS_PMT_REGISTRATION_NONE )
msg_Dbg( p_demux, PMT_DESC_PREFIX "EACEM Simulcast HD" );
/* TODO: apply visibility flags */
break;
case 0xC1:
if( registration_type == TS_PMT_REGISTRATION_ARIB )
msg_Dbg( p_demux, PMT_DESC_PREFIX "Digital copy control (0xC1)" );
break;
case 0xDE:
if( registration_type == TS_PMT_REGISTRATION_ARIB )
msg_Dbg( p_demux, PMT_DESC_PREFIX "Content availability (0xDE)" );
break;
default:
msg_Dbg( p_demux, PMT_DESC_PREFIX "Unknown Private (0x%x)", p_dr->i_tag );
break;
}
}
*p_registration_type = registration_type;
}
/*****************************************************************************
* 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 PMTEsHasComponentTag( const dvbpsi_pmt_es_t *p_es,
int i_component_tag )
{
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_component_tag;
}
static void SetupISO14496Descriptors( demux_t *p_demux, ts_pes_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_pes_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 >= 3 && !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 */
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 );
ts_sections_processor_Add( &p_pes->p_sections_proc, 0x05, 0x13,
false, SLPackets_Section_Handler );
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 SetupAVCDescriptors( demux_t *p_demux, ts_pes_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_pes_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_Init( &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]);
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_pes_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;
/* Gather pages information */
for( unsigned i_tag_idx = 0; i_tag_idx < 2; i_tag_idx++ )
{
dvbpsi_descriptor_t *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 );
}
}
dvbpsi_descriptor_t *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_Init( 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]) );
dvbpsi_descriptor_t *p_dr;
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_pes_es_t *p_page_es;
/* */
if( i == 0 )
{
p_page_es = p_pes->p_es;
}
else
{
p_page_es = ts_pes_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_pes_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_pes_t *p_pes,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
es_format_t *p_fmt = &p_pes->p_es->fmt;
es_format_Init( 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_pes_es_t *p_subs_es;
/* */
if( i == 0 )
{
p_subs_es = p_pes->p_es;
}
else
{
p_subs_es = ts_pes_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_pes_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;
/* 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 map[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], map[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]);
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) {
opus_write_header((uint8_t**)&p_fmt->p_extra, &p_fmt->i_extra, &h, NULL /* FIXME */);
if (p_fmt->p_extra) {
p_fmt->i_cat = AUDIO_ES;
p_fmt->i_codec = VLC_CODEC_OPUS;
p_fmt->audio.i_channels = h.channels;
p_fmt->audio.i_rate = 48000;
}
}
return;
explicit_config_too_short:
msg_Err(demux, "Opus descriptor too short");
}
static void PMTSetupEs0x06( demux_t *p_demux, ts_pes_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) */
p_fmt->i_cat = AUDIO_ES;
p_fmt->i_codec = 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) */
{
p_fmt->i_cat = AUDIO_ES;
p_fmt->i_codec = VLC_CODEC_A52;
}
else if( (desc = PMTEsFindDescriptor( p_dvbpsies, 0x7f ) ) && desc->i_length >= 2 &&
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" ) ||
PMTEsHasRegistration( p_demux, p_dvbpsies, "DTS2" ) ||
PMTEsHasRegistration( p_demux, p_dvbpsies, "DTS3" ) ||
PMTEsFindDescriptor( p_dvbpsies, 0x73 ) )
{
/*registration descriptor(ETSI TS 101 154 Annex F)*/
p_fmt->i_cat = AUDIO_ES;
p_fmt->i_codec = 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*/
p_fmt->i_cat = AUDIO_ES;
p_fmt->b_packetized = true;
p_fmt->i_codec = VLC_CODEC_302M;
}
else if( PMTEsHasRegistration( p_demux, p_dvbpsies, "HEVC" ) )
{
p_fmt->i_cat = VIDEO_ES;
p_fmt->i_codec = VLC_CODEC_HEVC;
}
else if ( p_demux->p_sys->arib.e_mode == ARIBMODE_ENABLED )
{
/* 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 )
{
if( !memcmp( p_dr->p_data, "\x00\x08", 2 ) && (
PMTEsHasComponentTag( p_dvbpsies, 0x30 ) ||
PMTEsHasComponentTag( p_dvbpsies, 0x31 ) ||
PMTEsHasComponentTag( p_dvbpsies, 0x32 ) ||
PMTEsHasComponentTag( p_dvbpsies, 0x33 ) ||
PMTEsHasComponentTag( p_dvbpsies, 0x34 ) ||
PMTEsHasComponentTag( p_dvbpsies, 0x35 ) ||
PMTEsHasComponentTag( p_dvbpsies, 0x36 ) ||
PMTEsHasComponentTag( p_dvbpsies, 0x37 ) ) )
{
es_format_Init( p_fmt, SPU_ES, VLC_CODEC_ARIB_A );
p_fmt->psz_language = strndup ( "jpn", 3 );
p_fmt->psz_description = strdup( _("ARIB subtitles") );
}
else if( !memcmp( p_dr->p_data, "\x00\x12", 2 ) && (
PMTEsHasComponentTag( p_dvbpsies, 0x87 ) ||
PMTEsHasComponentTag( p_dvbpsies, 0x88 ) ) )
{
es_format_Init( 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_pes_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;
}
es_format_t *p_fmt = &p_es->fmt;
/* registration descriptor for VC-1 (SMPTE rp227) */
p_fmt->i_cat = VIDEO_ES;
p_fmt->i_codec = 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_pes_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;
}
es_format_t *p_fmt = &p_es->fmt;
/* registration descriptor for Dirac
* (backwards compatable with VC-2 (SMPTE Sxxxx:2008)) */
p_fmt->i_cat = VIDEO_ES;
p_fmt->i_codec = VLC_CODEC_DIRAC;
}
static void PMTSetupEs0xA0( demux_t *p_demux, ts_pes_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;
p_fmt->i_cat = VIDEO_ES;
p_fmt->i_codec = 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_pes_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_Init( &p_es->fmt, AUDIO_ES, VLC_CODEC_WIDI_LPCM );
}
else
es_format_Init( &p_es->fmt, AUDIO_ES, VLC_CODEC_DVD_LPCM );
}
static bool PMTSetupEsHDMV( demux_t *p_demux, ts_pes_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:
p_fmt->i_cat = AUDIO_ES;
p_fmt->i_codec = VLC_CODEC_BD_LPCM;
break;
case 0x81:
p_fmt->i_cat = AUDIO_ES;
p_fmt->i_codec = VLC_CODEC_A52;
break;
case 0x82:
case 0x85: /* DTS-HD High resolution audio */
case 0x86: /* DTS-HD Master audio */
case 0xA2: /* Secondary DTS audio */
p_fmt->i_cat = AUDIO_ES;
p_fmt->i_codec = VLC_CODEC_DTS;
break;
case 0x83: /* TrueHD AC3 */
p_fmt->i_cat = AUDIO_ES;
p_fmt->i_codec = VLC_CODEC_TRUEHD;
break;
case 0x84: /* E-AC3 */
case 0xA1: /* Secondary E-AC3 */
p_fmt->i_cat = AUDIO_ES;
p_fmt->i_codec = VLC_CODEC_EAC3;
break;
case 0x90: /* Presentation graphics */
p_fmt->i_cat = SPU_ES;
p_fmt->i_codec = VLC_CODEC_BD_PG;
break;
case 0x91: /* Interactive graphics */
case 0x92: /* Subtitle */
return false;
case 0xEA:
p_fmt->i_cat = VIDEO_ES;
p_fmt->i_codec = 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;
break;
}
return true;
}
static bool PMTSetupEsRegistration( demux_t *p_demux, ts_pes_es_t *p_es,
const dvbpsi_pmt_es_t *p_dvbpsies )
{
static const struct
{
char psz_tag[5];
int i_cat;
vlc_fourcc_t i_codec;
} p_regs[] = {
{ "AC-3", AUDIO_ES, VLC_CODEC_A52 },
{ "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 ) )
{
p_fmt->i_cat = p_regs[i].i_cat;
p_fmt->i_codec = p_regs[i].i_codec;
if (p_dvbpsies->i_type == 0x87)
p_fmt->i_codec = VLC_CODEC_EAC3;
return true;
}
}
return false;
}
static char *GetAudioTypeDesc(demux_t *p_demux, int type)
{
static const char *audio_type[] = {
NULL,
N_("clean effects"),
N_("hearing impaired"),
N_("visual impaired commentary"),
};
if (type < 0 || type > 3)
msg_Dbg( p_demux, "unknown audio type: %d", type);
else if (type > 0)
return strdup(audio_type[type]);
return NULL;
}
static void PMTParseEsIso639( demux_t *p_demux, ts_pes_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;
}
#if defined(DR_0A_API_VER) && (DR_0A_API_VER >= 2)
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);
}
int type = p_decoded->code[0].i_audio_type;
p_es->fmt.psz_description = GetAudioTypeDesc(p_demux, type);
if (type == 0)
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++ )
{
p_es->fmt.p_extra_languages[i].psz_language = malloc(4);
if( p_es->fmt.p_extra_languages[i].psz_language )
{
memcpy( p_es->fmt.p_extra_languages[i].psz_language,
p_decoded->code[i+1].iso_639_code, 3 );
p_es->fmt.p_extra_languages[i].psz_language[3] = '\0';
}
int type = p_decoded->code[i].i_audio_type;
p_es->fmt.p_extra_languages[i].psz_description = GetAudioTypeDesc(p_demux, type);
}
}
#else
p_es->fmt.psz_language = malloc( 4 );
if( p_es->fmt.psz_language )
{
memcpy( p_es->fmt.psz_language,
p_decoded->i_iso_639_code, 3 );
p_es->fmt.psz_language[3] = 0;
}
#endif
}
static void PIDFillFormat( ts_pes_t *p_pes, int i_stream_type, ts_es_data_type_t *p_datatype )
{
es_format_t *fmt = &p_pes->p_es->fmt;
switch( i_stream_type )
{
case 0x01: /* MPEG-1 video */
case 0x02: /* MPEG-2 video */
case 0x80: /* MPEG-2 MOTO video */
es_format_Init( fmt, VIDEO_ES, VLC_CODEC_MPGV );
break;
case 0x03: /* MPEG-1 audio */
case 0x04: /* MPEG-2 audio */
es_format_Init( fmt, AUDIO_ES, VLC_CODEC_MPGA );
break;
case 0x11: /* MPEG4 (audio) LATM */
case 0x0f: /* ISO/IEC 13818-7 Audio with ADTS transport syntax */
case 0x1c: /* ISO/IEC 14496-3 Audio, without using any additional
transport syntax, such as DST, ALS and SLS */
es_format_Init( fmt, AUDIO_ES, VLC_CODEC_MP4A );
break;
case 0x10: /* MPEG4 (video) */
es_format_Init( fmt, VIDEO_ES, VLC_CODEC_MP4V );
break;
case 0x1B: /* H264 <- check transport syntax/needed descriptor */
es_format_Init( fmt, VIDEO_ES, VLC_CODEC_H264 );
break;
case 0x24: /* HEVC */
es_format_Init( fmt, VIDEO_ES, VLC_CODEC_HEVC );
break;
case 0x42: /* CAVS (Chinese AVS) */
es_format_Init( fmt, VIDEO_ES, VLC_CODEC_CAVS );
break;
case 0x81: /* A52 (audio) */
es_format_Init( fmt, AUDIO_ES, VLC_CODEC_A52 );
break;
case 0x82: /* SCTE-27 (sub) */
es_format_Init( fmt, SPU_ES, VLC_CODEC_SCTE_27 );
*p_datatype = TS_ES_DATA_TABLE_SECTION;
ts_sections_processor_Add( &p_pes->p_sections_proc, 0xC6, 0x82,
true, SCTE27_Section_Handler );
break;
case 0x84: /* SDDS (audio) */
es_format_Init( fmt, AUDIO_ES, VLC_CODEC_SDDS );
break;
case 0x85: /* DTS (audio) */
es_format_Init( fmt, AUDIO_ES, VLC_CODEC_DTS );
break;
case 0x87: /* E-AC3 */
es_format_Init( fmt, AUDIO_ES, VLC_CODEC_EAC3 );
break;
case 0x91: /* A52 vls (audio) */
es_format_Init( fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', 'b' ) );
break;
case 0x92: /* DVD_SPU vls (sub) */
es_format_Init( fmt, SPU_ES, VLC_FOURCC( 's', 'p', 'u', 'b' ) );
break;
case 0x94: /* SDDS (audio) */
es_format_Init( fmt, AUDIO_ES, VLC_FOURCC( 's', 'd', 'd', 'b' ) );
break;
case 0xa0: /* MSCODEC vlc (video) (fixed later) */
es_format_Init( 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_Init( fmt, UNKNOWN_ES, 0 );
break;
}
/* PES packets usually contain truncated frames */
fmt->b_packetized = false;
}
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_pes_t *p_pes )
{
ts_es_data_type_t type_change = TS_ES_DATA_PES;
PIDFillFormat( 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->data_type = type_change; /* Only change type if registration has not changed meaning */
switch( p_dvbpsies->i_type )
{
case 0x06:
/* Handle PES private data */
PMTSetupEs0x06( p_demux, p_pes, p_dvbpsies );
break;
/* All other private or reserved types */
case 0x13: /* SL in sections */
p_pes->data_type = TS_ES_DATA_TABLE_SECTION;
//ft
case 0x0f:
case 0x10:
case 0x11:
case 0x12:
SetupISO14496Descriptors( p_demux, p_pes, p_pmt, 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 );
}
/* 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;
}
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_mgt )
ARRAY_APPEND( pid_to_decref, p_pmt->p_mgt );
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;
ValidateDVBMeta( p_demux, p_pmt->i_pid_pcr );
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, &registration_type );
dvbpsi_pmt_es_t *p_dvbpsies;
for( p_dvbpsies = p_dvbpsipmt->p_first_es; p_dvbpsies != NULL; p_dvbpsies = p_dvbpsies->p_next )
{
/* Do not mix with arbitrary pid if any */
if( p_sys->b_atsc_eas && p_dvbpsies->i_pid == SCTE18_SI_BASE_PID )
continue;
ts_pid_t *pespid = GetPID(p_sys, p_dvbpsies->i_pid);
if ( pespid->type != TYPE_PES && pespid->type != TYPE_FREE )
{
msg_Warn( p_demux, " * PMT wants to create PES on pid %d used by non PES", pespid->i_pid );
continue;
}
ValidateDVBMeta( p_demux, p_dvbpsies->i_pid );
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 );
for( dvbpsi_descriptor_t *p_dr = p_dvbpsies->p_first_descriptor;
p_dr != NULL; p_dr = p_dr->p_next )
{
msg_Dbg( p_demux, " - ES descriptor tag 0x%x",
p_dr->i_tag );
}
const bool b_pid_inuse = ( pespid->type == TYPE_PES );
ts_pes_t *p_pes;
if ( !PIDSetup( p_demux, TYPE_PES, 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_pes_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_pes;
}
}
/* 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 == VIDEO_ES) ||
(p_pes->p_es->fmt.i_cat == AUDIO_ES));
/* Now check and merge */
if( b_pid_inuse ) /* We need to compare to the existing pes/es */
{
ts_pes_es_t *p_existing_es = ts_pes_Find_es( pespid->u.p_pes, 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_pes_Count_es( p_pes->p_es->p_extraes, false, NULL ) ==
ts_pes_Count_es( p_existing_es->p_extraes, false, NULL )
);
if( b_canreuse )
{
/* Just keep using previous es */
ts_pes_Del( p_demux, p_pes );
}
else
{
ts_pes_es_t *p_new = ts_pes_Extract_es( p_pes, p_pmt );
ts_pes_Add_es( pespid->u.p_pes, p_new, false );
assert(ts_pes_Count_es(p_pes->p_es, false, NULL) == 0);
ts_pes_Del( p_demux, p_pes );
}
}
else /* There was no es for that program on that pid, merge in */
{
assert(ts_pes_Count_es(pespid->u.p_pes->p_es, false, NULL)); /* Used by another program */
ts_pes_es_t *p_new = ts_pes_Extract_es( p_pes, p_pmt );
assert( p_new );
ts_pes_Add_es( pespid->u.p_pes, p_new, false );
ts_pes_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 ) )
{
dvbpsi_pmt_delete( p_dvbpsipmt );
}
else if( stream_Control( p_sys->stream, STREAM_SET_PRIVATE_ID_CA,
p_dvbpsipmt ) != VLC_SUCCESS )
{
if ( p_sys->arib.e_mode == ARIBMODE_ENABLED && !p_sys->arib.b25stream )
{
p_sys->arib.b25stream = stream_FilterNew( p_demux->s, "aribcam" );
p_sys->stream = ( p_sys->arib.b25stream ) ? p_sys->arib.b25stream : p_demux->s;
if (!p_sys->arib.b25stream)
dvbpsi_pmt_delete( p_dvbpsipmt );
} else dvbpsi_pmt_delete( p_dvbpsipmt );
}
/* Add arbitrary PID from here */
if ( p_sys->b_atsc_eas && p_pmt->e_streams.i_size )
{
ts_pid_t *easpid = GetPID(p_sys, SCTE18_SI_BASE_PID);
if ( PIDSetup( p_demux, TYPE_PES, easpid, pmtpid ) )
{
ARRAY_APPEND( p_pmt->e_streams, easpid );
ts_pes_t *p_easpes = easpid->u.p_pes;
p_easpes->data_type = TS_ES_DATA_TABLE_SECTION;
p_easpes->p_es->fmt.i_codec = VLC_CODEC_SCTE_18;
p_easpes->p_es->fmt.i_cat = SPU_ES;
p_easpes->p_es->fmt.i_id = SCTE18_SI_BASE_PID;
p_easpes->p_es->fmt.i_group = p_pmt->i_number;
p_easpes->p_es->fmt.psz_description = strdup(SCTE18_DESCRIPTION);
p_easpes->b_always_receive = true;
ts_sections_processor_Add( &p_easpes->p_sections_proc,
SCTE18_TABLE_ID, 0x00,
false, SCTE18_Section_Handler );
msg_Dbg( p_demux, " * pid=%d listening for EAS events", easpid->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 );
UpdatePESFilters( p_demux, p_sys->b_es_all );
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 );
}
/* 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 );
}
}
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_pid;
int i_number;
if( !psz_dup )
return VLC_ENOMEM;
/* Parse PID */
i_pid = strtol( 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=%d,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 );
psz = strchr( psz, '=' );
if( psz )
psz++;
while( psz && *psz )
{
char *psz_next = strchr( psz, ',' );
int i_pid;
if( psz_next )
*psz_next++ = '\0';
i_pid = strtol( 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_PES, 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_pes->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_Init( fmt, i_cat, i_codec );
fmt->b_packetized = false;
}
else
{
const int i_stream_type = strtol( psz_opt, NULL, 0 );
PIDFillFormat( pid->u.p_pes, i_stream_type, &pid->u.p_pes->data_type );
}
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=%d fcc=%4.4s", i_pid,
(char*)&fmt->i_codec );
pid->u.p_pes->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;
}
/*****************************************************************************
* ts_psi.h: 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef VLC_TS_PSI_H
#define VLC_TS_PSI_H
typedef enum
{
TS_PMT_REGISTRATION_NONE = 0,
TS_PMT_REGISTRATION_BLURAY,
TS_PMT_REGISTRATION_ATSC,
TS_PMT_REGISTRATION_ARIB,
} ts_pmt_registration_type_t;
void PATCallBack( void *, dvbpsi_pat_t * );
void PMTCallBack( void *, dvbpsi_pmt_t * );
int UserPmt( demux_t *p_demux, const char * );
#endif
/*****************************************************************************
* ts_psi_eit.c : TS demuxer EIT handling
*****************************************************************************
* Copyright (C) 2014-2016 - VideoLAN Authors
*
* 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_demux.h>
#include <vlc_meta.h>
#include <vlc_epg.h>
#include <vlc_charset.h> /* FromCharset, for EIT */
#ifndef _DVBPSI_DVBPSI_H_
#include <dvbpsi/dvbpsi.h>
#endif
#include <dvbpsi/demux.h>
#include <dvbpsi/descriptor.h>
#include <dvbpsi/sdt.h>
#include <dvbpsi/eit.h> /* EIT support */
#include <dvbpsi/tot.h> /* TDT support */
#include <dvbpsi/dr.h>
#include "ts_psi_eit.h"
#include "ts_pid.h"
#include "ts_streams_private.h"
#include "ts.h"
#include "../dvb-text.h"
#ifdef HAVE_ARIBB24
#include <aribb24/decoder.h>
#endif
#include <time.h>
#include <assert.h>
static char *EITConvertToUTF8( demux_t *p_demux,
const unsigned char *psz_instring,
size_t i_length,
bool b_broken )
{
demux_sys_t *p_sys = p_demux->p_sys;
#ifdef HAVE_ARIBB24
if( p_sys->arib.e_mode == ARIBMODE_ENABLED )
{
if ( !p_sys->arib.p_instance )
p_sys->arib.p_instance = arib_instance_new( p_demux );
if ( !p_sys->arib.p_instance )
return NULL;
arib_decoder_t *p_decoder = arib_get_decoder( p_sys->arib.p_instance );
if ( !p_decoder )
return NULL;
char *psz_outstring = NULL;
size_t i_out;
i_out = i_length * 4;
psz_outstring = (char*) calloc( i_out + 1, sizeof(char) );
if( !psz_outstring )
return NULL;
arib_initialize_decoder( p_decoder );
i_out = arib_decode_buffer( p_decoder, psz_instring, i_length,
psz_outstring, i_out );
arib_finalize_decoder( p_decoder );
return psz_outstring;
}
#else
VLC_UNUSED(p_sys);
#endif
/* Deal with no longer broken providers (no switch byte
but sending ISO_8859-1 instead of ISO_6937) without
removing them from the broken providers table
(keep the entry for correctly handling recorded TS).
*/
b_broken = b_broken && i_length && *psz_instring > 0x20;
if( b_broken )
return FromCharset( "ISO_8859-1", psz_instring, i_length );
return vlc_from_EIT( psz_instring, i_length );
}
static void SDTCallBack( demux_t *p_demux, dvbpsi_sdt_t *p_sdt )
{
demux_sys_t *p_sys = p_demux->p_sys;
ts_pid_t *sdt = GetPID(p_sys, 0x11);
dvbpsi_sdt_service_t *p_srv;
msg_Dbg( p_demux, "SDTCallBack called" );
if( p_sys->es_creation != CREATE_ES ||
!p_sdt->b_current_next ||
p_sdt->i_version == sdt->u.p_psi->i_version )
{
dvbpsi_sdt_delete( p_sdt );
return;
}
msg_Dbg( p_demux, "new SDT ts_id=%d version=%d current_next=%d "
"network_id=%d",
p_sdt->i_extension,
p_sdt->i_version, p_sdt->b_current_next,
p_sdt->i_network_id );
p_sys->b_broken_charset = false;
for( p_srv = p_sdt->p_first_service; p_srv; p_srv = p_srv->p_next )
{
vlc_meta_t *p_meta;
dvbpsi_descriptor_t *p_dr;
const char *psz_type = NULL;
const char *psz_status = NULL;
msg_Dbg( p_demux, " * service id=%d eit schedule=%d present=%d "
"running=%d free_ca=%d",
p_srv->i_service_id, p_srv->b_eit_schedule,
p_srv->b_eit_present, p_srv->i_running_status,
p_srv->b_free_ca );
if( p_sys->vdr.i_service && p_srv->i_service_id != p_sys->vdr.i_service )
{
msg_Dbg( p_demux, " * service id=%d skipped (not declared in vdr header)",
p_sys->vdr.i_service );
continue;
}
p_meta = vlc_meta_New();
for( p_dr = p_srv->p_first_descriptor; p_dr; p_dr = p_dr->p_next )
{
if( p_dr->i_tag == 0x48 )
{
static const char *ppsz_type[17] = {
"Reserved",
"Digital television service",
"Digital radio sound service",
"Teletext service",
"NVOD reference service",
"NVOD time-shifted service",
"Mosaic service",
"PAL coded signal",
"SECAM coded signal",
"D/D2-MAC",
"FM Radio",
"NTSC coded signal",
"Data broadcast service",
"Reserved for Common Interface Usage",
"RCS Map (see EN 301 790 [35])",
"RCS FLS (see EN 301 790 [35])",
"DVB MHP service"
};
dvbpsi_service_dr_t *pD = dvbpsi_DecodeServiceDr( p_dr );
char *str1 = NULL;
char *str2 = NULL;
/* Workarounds for broadcasters with broken EPG */
if( p_sdt->i_network_id == 133 )
p_sys->b_broken_charset = true; /* SKY DE & BetaDigital use ISO8859-1 */
/* List of providers using ISO8859-1 */
static const char ppsz_broken_providers[][8] = {
"CSAT", /* CanalSat FR */
"GR1", /* France televisions */
"MULTI4", /* NT1 */
"MR5", /* France 2/M6 HD */
""
};
for( int i = 0; *ppsz_broken_providers[i]; i++ )
{
const size_t i_length = strlen(ppsz_broken_providers[i]);
if( pD->i_service_provider_name_length == i_length &&
!strncmp( (char *)pD->i_service_provider_name, ppsz_broken_providers[i], i_length ) )
p_sys->b_broken_charset = true;
}
/* FIXME: Digital+ ES also uses ISO8859-1 */
str1 = EITConvertToUTF8(p_demux,
pD->i_service_provider_name,
pD->i_service_provider_name_length,
p_sys->b_broken_charset );
str2 = EITConvertToUTF8(p_demux,
pD->i_service_name,
pD->i_service_name_length,
p_sys->b_broken_charset );
msg_Dbg( p_demux, " - type=%d provider=%s name=%s",
pD->i_service_type, str1, str2 );
vlc_meta_SetTitle( p_meta, str2 );
vlc_meta_SetPublisher( p_meta, str1 );
if( pD->i_service_type >= 0x01 && pD->i_service_type <= 0x10 )
psz_type = ppsz_type[pD->i_service_type];
free( str1 );
free( str2 );
}
}
if( p_srv->i_running_status >= 0x01 && p_srv->i_running_status <= 0x04 )
{
static const char *ppsz_status[5] = {
"Unknown",
"Not running",
"Starts in a few seconds",
"Pausing",
"Running"
};
psz_status = ppsz_status[p_srv->i_running_status];
}
if( psz_type )
vlc_meta_AddExtra( p_meta, "Type", psz_type );
if( psz_status )
vlc_meta_AddExtra( p_meta, "Status", psz_status );
es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META,
p_srv->i_service_id, p_meta );
vlc_meta_Delete( p_meta );
}
sdt->u.p_psi->i_version = p_sdt->i_version;
dvbpsi_sdt_delete( p_sdt );
}
static void EITDecodeMjd( int i_mjd, int *p_y, int *p_m, int *p_d )
{
const int yp = (int)( ( (double)i_mjd - 15078.2)/365.25 );
const int mp = (int)( ((double)i_mjd - 14956.1 - (int)(yp * 365.25)) / 30.6001 );
const int c = ( mp == 14 || mp == 15 ) ? 1 : 0;
*p_y = 1900 + yp + c*1;
*p_m = mp - 1 - c*12;
*p_d = i_mjd - 14956 - (int)(yp*365.25) - (int)(mp*30.6001);
}
#define CVT_FROM_BCD(v) ((((v) >> 4)&0xf)*10 + ((v)&0xf))
static int64_t EITConvertStartTime( uint64_t i_date )
{
const int i_mjd = i_date >> 24;
struct tm tm;
tm.tm_hour = CVT_FROM_BCD(i_date >> 16);
tm.tm_min = CVT_FROM_BCD(i_date >> 8);
tm.tm_sec = CVT_FROM_BCD(i_date );
/* if all 40 bits are 1, the start is unknown */
if( i_date == UINT64_C(0xffffffffff) )
return -1;
EITDecodeMjd( i_mjd, &tm.tm_year, &tm.tm_mon, &tm.tm_mday );
tm.tm_year -= 1900;
tm.tm_mon--;
tm.tm_isdst = 0;
return timegm( &tm );
}
static int EITConvertDuration( uint32_t i_duration )
{
return CVT_FROM_BCD(i_duration >> 16) * 3600 +
CVT_FROM_BCD(i_duration >> 8 ) * 60 +
CVT_FROM_BCD(i_duration );
}
#undef CVT_FROM_BCD
static void TDTCallBack( demux_t *p_demux, dvbpsi_tot_t *p_tdt )
{
demux_sys_t *p_sys = p_demux->p_sys;
p_sys->i_tdt_delta = CLOCK_FREQ * EITConvertStartTime( p_tdt->i_utc_time )
- mdate();
dvbpsi_tot_delete(p_tdt);
}
static void EITCallBack( demux_t *p_demux,
dvbpsi_eit_t *p_eit, bool b_current_following )
{
demux_sys_t *p_sys = p_demux->p_sys;
dvbpsi_eit_event_t *p_evt;
vlc_epg_t *p_epg;
msg_Dbg( p_demux, "EITCallBack called" );
if( !p_eit->b_current_next )
{
dvbpsi_eit_delete( p_eit );
return;
}
msg_Dbg( p_demux, "new EIT service_id=%d version=%d current_next=%d "
"ts_id=%d network_id=%d segment_last_section_number=%d "
"last_table_id=%d",
p_eit->i_extension,
p_eit->i_version, p_eit->b_current_next,
p_eit->i_ts_id, p_eit->i_network_id,
p_eit->i_segment_last_section_number, p_eit->i_last_table_id );
p_epg = vlc_epg_New( NULL );
for( p_evt = p_eit->p_first_event; p_evt; p_evt = p_evt->p_next )
{
dvbpsi_descriptor_t *p_dr;
char *psz_name = NULL;
char *psz_text = NULL;
char *psz_extra = strdup("");
int64_t i_start;
int i_duration;
int i_min_age = 0;
int64_t i_tot_time = 0;
i_start = EITConvertStartTime( p_evt->i_start_time );
i_duration = EITConvertDuration( p_evt->i_duration );
if( p_sys->arib.e_mode == ARIBMODE_ENABLED )
{
if( p_sys->i_tdt_delta == 0 )
p_sys->i_tdt_delta = CLOCK_FREQ * (i_start + i_duration - 5) - mdate();
i_tot_time = (mdate() + p_sys->i_tdt_delta) / CLOCK_FREQ;
tzset(); // JST -> UTC
i_start += timezone; // FIXME: what about DST?
i_tot_time += timezone;
if( p_evt->i_running_status == 0x00 &&
(i_start - 5 < i_tot_time &&
i_tot_time < i_start + i_duration + 5) )
{
p_evt->i_running_status = 0x04;
msg_Dbg( p_demux, " EIT running status 0x00 -> 0x04" );
}
}
msg_Dbg( p_demux, " * event id=%d start_time:%d duration=%d "
"running=%d free_ca=%d",
p_evt->i_event_id, (int)i_start, (int)i_duration,
p_evt->i_running_status, p_evt->b_free_ca );
for( p_dr = p_evt->p_first_descriptor; p_dr; p_dr = p_dr->p_next )
{
switch(p_dr->i_tag)
{
case 0x4d:
{
dvbpsi_short_event_dr_t *pE = dvbpsi_DecodeShortEventDr( p_dr );
/* Only take first description, as we don't handle language-info
for epg atm*/
if( pE && psz_name == NULL )
{
psz_name = EITConvertToUTF8( p_demux,
pE->i_event_name, pE->i_event_name_length,
p_sys->b_broken_charset );
free( psz_text );
psz_text = EITConvertToUTF8( p_demux,
pE->i_text, pE->i_text_length,
p_sys->b_broken_charset );
msg_Dbg( p_demux, " - short event lang=%3.3s '%s' : '%s'",
pE->i_iso_639_code, psz_name, psz_text );
}
}
break;
case 0x4e:
{
dvbpsi_extended_event_dr_t *pE = dvbpsi_DecodeExtendedEventDr( p_dr );
if( pE )
{
msg_Dbg( p_demux, " - extended event lang=%3.3s [%d/%d]",
pE->i_iso_639_code,
pE->i_descriptor_number, pE->i_last_descriptor_number );
if( pE->i_text_length > 0 )
{
char *psz_text = EITConvertToUTF8( p_demux,
pE->i_text, pE->i_text_length,
p_sys->b_broken_charset );
if( psz_text )
{
msg_Dbg( p_demux, " - text='%s'", psz_text );
psz_extra = xrealloc( psz_extra,
strlen(psz_extra) + strlen(psz_text) + 1 );
strcat( psz_extra, psz_text );
free( psz_text );
}
}
for( int i = 0; i < pE->i_entry_count; i++ )
{
char *psz_dsc = EITConvertToUTF8( p_demux,
pE->i_item_description[i],
pE->i_item_description_length[i],
p_sys->b_broken_charset );
char *psz_itm = EITConvertToUTF8( p_demux,
pE->i_item[i], pE->i_item_length[i],
p_sys->b_broken_charset );
if( psz_dsc && psz_itm )
{
msg_Dbg( p_demux, " - desc='%s' item='%s'", psz_dsc, psz_itm );
#if 0
psz_extra = xrealloc( psz_extra,
strlen(psz_extra) + strlen(psz_dsc) +
strlen(psz_itm) + 3 + 1 );
strcat( psz_extra, "(" );
strcat( psz_extra, psz_dsc );
strcat( psz_extra, " " );
strcat( psz_extra, psz_itm );
strcat( psz_extra, ")" );
#endif
}
free( psz_dsc );
free( psz_itm );
}
}
}
break;
case 0x55:
{
dvbpsi_parental_rating_dr_t *pR = dvbpsi_DecodeParentalRatingDr( p_dr );
if ( pR )
{
for ( int i = 0; i < pR->i_ratings_number; i++ )
{
const dvbpsi_parental_rating_t *p_rating = & pR->p_parental_rating[ i ];
if ( p_rating->i_rating > 0x00 && p_rating->i_rating <= 0x0F )
{
if ( p_rating->i_rating + 3 > i_min_age )
i_min_age = p_rating->i_rating + 3;
msg_Dbg( p_demux, " - parental control set to %d years",
i_min_age );
}
}
}
}
break;
default:
msg_Dbg( p_demux, " - event unknown dr 0x%x(%d)", p_dr->i_tag, p_dr->i_tag );
break;
}
}
/* */
if( i_start > 0 && psz_name && psz_text)
vlc_epg_AddEvent( p_epg, i_start, i_duration, psz_name, psz_text,
*psz_extra ? psz_extra : NULL, i_min_age );
/* Update "now playing" field */
if( p_evt->i_running_status == 0x04 && i_start > 0 && psz_name && psz_text )
vlc_epg_SetCurrent( p_epg, i_start );
free( psz_name );
free( psz_text );
free( psz_extra );
}
if( p_epg->i_event > 0 )
{
if( b_current_following &&
( p_sys->programs.i_size == 0 ||
p_sys->programs.p_elems[0] ==
p_eit->i_extension
) )
{
p_sys->i_dvb_length = 0;
p_sys->i_dvb_start = 0;
if( p_epg->p_current )
{
p_sys->i_dvb_start = CLOCK_FREQ * p_epg->p_current->i_start;
p_sys->i_dvb_length = CLOCK_FREQ * p_epg->p_current->i_duration;
}
}
es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG,
p_eit->i_extension,
p_epg );
}
vlc_epg_Delete( p_epg );
dvbpsi_eit_delete( p_eit );
}
static void EITCallBackCurrentFollowing( demux_t *p_demux, dvbpsi_eit_t *p_eit )
{
EITCallBack( p_demux, p_eit, true );
}
static void EITCallBackSchedule( demux_t *p_demux, dvbpsi_eit_t *p_eit )
{
EITCallBack( p_demux, p_eit, false );
}
static void PSINewTableCallBack( dvbpsi_t *h, uint8_t i_table_id,
uint16_t i_extension, demux_t *p_demux )
{
demux_sys_t *p_sys = p_demux->p_sys;
assert( h );
#if 0
msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
i_table_id, i_table_id, i_extension, i_extension );
#endif
if( GetPID(p_sys, 0)->u.p_pat->i_version != -1 && i_table_id == 0x42 )
{
msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
i_table_id, i_table_id, i_extension, i_extension );
if( !dvbpsi_sdt_attach( h, i_table_id, i_extension, (dvbpsi_sdt_callback)SDTCallBack, p_demux ) )
msg_Err( p_demux, "PSINewTableCallback: failed attaching SDTCallback" );
}
else if( GetPID(p_sys, 0x11)->u.p_psi->i_version != -1 &&
( i_table_id == 0x4e || /* Current/Following */
(i_table_id >= 0x50 && i_table_id <= 0x5f) ) ) /* Schedule */
{
msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
i_table_id, i_table_id, i_extension, i_extension );
dvbpsi_eit_callback cb = i_table_id == 0x4e ?
(dvbpsi_eit_callback)EITCallBackCurrentFollowing :
(dvbpsi_eit_callback)EITCallBackSchedule;
if( !dvbpsi_eit_attach( h, i_table_id, i_extension, cb, p_demux ) )
msg_Err( p_demux, "PSINewTableCallback: failed attaching EITCallback" );
}
else if( GetPID(p_sys, 0x11)->u.p_psi->i_version != -1 &&
(i_table_id == 0x70 /* TDT */ || i_table_id == 0x73 /* TOT */) )
{
msg_Dbg( p_demux, "PSINewTableCallBack: table 0x%x(%d) ext=0x%x(%d)",
i_table_id, i_table_id, i_extension, i_extension );
if( !dvbpsi_tot_attach( h, i_table_id, i_extension, (dvbpsi_tot_callback)TDTCallBack, p_demux ) )
msg_Err( p_demux, "PSINewTableCallback: failed attaching TDTCallback" );
}
}
bool AttachDvbpsiNewEITTableHandler( dvbpsi_t *p_handle, demux_t * p_demux )
{
return dvbpsi_AttachDemux( p_handle, (dvbpsi_demux_new_cb_t)PSINewTableCallBack, p_demux );
}
/*****************************************************************************
* ts_psi_eit.h : TS demuxer EIT handling
*****************************************************************************
* Copyright (C) 2014-2016 - VideoLAN Authors
*
* 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef VLC_TS_PSI_EIT_H
#define VLC_TS_PSI_EIT_H
bool AttachDvbpsiNewEITTableHandler( dvbpsi_t *p_handle, demux_t * p_demux );
#endif
/*****************************************************************************
* ts_scte.c: TS Demux SCTE section decoders/handlers
*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_demux.h>
#include <vlc_es.h>
#include "ts_pid.h"
#include "ts_scte.h"
#include "ts_streams_private.h"
#include "timestamps.h"
#include <assert.h>
void SCTE18_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content )
{
assert( pid->u.p_pes->p_es->fmt.i_codec == VLC_CODEC_SCTE_18 );
ts_pmt_t *p_pmt = pid->u.p_pes->p_es->p_program;
mtime_t i_date = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->pcr.i_current );
int i_priority = scte18_get_EAS_priority( p_content->p_buffer, p_content->i_buffer );
msg_Dbg( p_demux, "Received EAS Alert with priority %d", i_priority );
/* We need to extract the truncated pts stored inside the payload */
ts_pes_es_t *p_es = pid->u.p_pes->p_es;
if( p_es->id )
{
if( i_priority == EAS_PRIORITY_HIGH || i_priority == EAS_PRIORITY_MAX )
es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, p_es->id, true );
p_content->i_dts = p_content->i_pts = FROM_SCALE( i_date );
es_out_Send( p_demux->out, p_es->id, p_content );
}
else
block_Release( p_content );
}
void SCTE27_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content )
{
assert( pid->u.p_pes->p_es->fmt.i_codec == VLC_CODEC_SCTE_27 );
ts_pmt_t *p_pmt = pid->u.p_pes->p_es->p_program;
mtime_t i_date = p_pmt->pcr.i_current;
/* We need to extract the truncated pts stored inside the payload */
int i_index = 0;
size_t i_offset = 4;
if( p_content->p_buffer[3] & 0x40 )
{
i_index = ((p_content->p_buffer[7] & 0x0f) << 8) | /* segment number */
p_content->p_buffer[8];
i_offset += 5;
}
if( i_index == 0 && p_content->i_buffer > i_offset + 8 ) /* message body */
{
bool is_immediate = p_content->p_buffer[i_offset + 3] & 0x40;
if( !is_immediate )
{
mtime_t i_display_in = GetDWBE( &p_content->p_buffer[i_offset + 4] );
if( i_display_in < i_date )
i_date = i_display_in + (1ll << 32);
else
i_date = i_display_in;
}
}
p_content->i_dts = p_content->i_pts = VLC_TS_0 + i_date * 100 / 9;
//PCRFixHandle( p_demux, p_pmt, p_content );
if( pid->u.p_pes->p_es->id )
es_out_Send( p_demux->out, pid->u.p_pes->p_es->id, p_content );
else
block_Release( p_content );
}
/*****************************************************************************
* ts_scte.h: TS Demux SCTE section decoders/handlers
*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef VLC_TS_SCTE_H
#define VLC_TS_SCTE_H
#include "../../codec/scte18.h"
void SCTE18_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content );
void SCTE27_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content );
#endif
/*****************************************************************************
* ts_sl.c : MPEG SL/FMC handling for TS demuxer
*****************************************************************************
* Copyright (C) 2014-2016 - VideoLAN Authors
*
* 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_demux.h>
#include "ts_streams.h"
#include "ts_pid.h"
#include "ts_streams_private.h"
#include "ts.h"
#include "ts_sl.h"
const es_mpeg4_descriptor_t * GetMPEG4DescByEsId( const ts_pmt_t *pmt, uint16_t i_es_id )
{
for( int i = 0; i < ES_DESCRIPTOR_COUNT; i++ )
{
const es_mpeg4_descriptor_t *es_descr = &pmt->iod->es_descr[i];
if( es_descr->i_es_id == i_es_id && es_descr->b_ok )
return es_descr;
}
for( int i=0; i<pmt->od.objects.i_size; i++ )
{
const od_descriptor_t *od = pmt->od.objects.p_elems[i];
for( int j = 0; j < ES_DESCRIPTOR_COUNT; j++ )
{
const es_mpeg4_descriptor_t *es_descr = &od->es_descr[j];
if( es_descr->i_es_id == i_es_id && es_descr->b_ok )
return es_descr;
}
}
return NULL;
}
static ts_pes_es_t * GetPMTESBySLEsId( ts_pmt_t *pmt, uint16_t i_sl_es_id )
{
for( int i=0; i< pmt->e_streams.i_size; i++ )
{
ts_pes_es_t *p_es = pmt->e_streams.p_elems[i]->u.p_pes->p_es;
if( p_es->i_sl_es_id == i_sl_es_id )
return p_es;
}
return NULL;
}
bool SetupISO14496LogicalStream( demux_t *p_demux, const decoder_config_descriptor_t *dcd,
es_format_t *p_fmt )
{
msg_Dbg( p_demux, " - IOD objecttype: %"PRIx8" streamtype:%"PRIx8,
dcd->i_objectTypeIndication, dcd->i_streamType );
if( dcd->i_streamType == 0x04 ) /* VisualStream */
{
p_fmt->i_cat = VIDEO_ES;
switch( dcd->i_objectTypeIndication )
{
case 0x0B: /* mpeg4 sub */
p_fmt->i_cat = SPU_ES;
p_fmt->i_codec = VLC_CODEC_SUBT;
break;
case 0x20: /* mpeg4 */
p_fmt->i_codec = VLC_CODEC_MP4V;
break;
case 0x21: /* h264 */
p_fmt->i_codec = VLC_CODEC_H264;
break;
case 0x60:
case 0x61:
case 0x62:
case 0x63:
case 0x64:
case 0x65: /* mpeg2 */
p_fmt->i_codec = VLC_CODEC_MPGV;
break;
case 0x6a: /* mpeg1 */
p_fmt->i_codec = VLC_CODEC_MPGV;
break;
case 0x6c: /* mpeg1 */
p_fmt->i_codec = VLC_CODEC_JPEG;
break;
default:
p_fmt->i_cat = UNKNOWN_ES;
break;
}
}
else if( dcd->i_streamType == 0x05 ) /* AudioStream */
{
p_fmt->i_cat = AUDIO_ES;
switch( dcd->i_objectTypeIndication )
{
case 0x40: /* mpeg4 */
p_fmt->i_codec = VLC_CODEC_MP4A;
break;
case 0x66:
case 0x67:
case 0x68: /* mpeg2 aac */
p_fmt->i_codec = VLC_CODEC_MP4A;
break;
case 0x69: /* mpeg2 */
p_fmt->i_codec = VLC_CODEC_MPGA;
break;
case 0x6b: /* mpeg1 */
p_fmt->i_codec = VLC_CODEC_MPGA;
break;
default:
p_fmt->i_cat = UNKNOWN_ES;
break;
}
}
else
{
p_fmt->i_cat = UNKNOWN_ES;
}
if( p_fmt->i_cat != UNKNOWN_ES )
{
p_fmt->i_extra = __MIN(dcd->i_extra, INT32_MAX);
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, dcd->p_extra, p_fmt->i_extra );
else
p_fmt->i_extra = 0;
}
}
return true;
}
/* Object stream SL in table sections */
void SLPackets_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content )
{
ts_pmt_t *p_pmt = pid->u.p_pes->p_es->p_program;
const es_mpeg4_descriptor_t *p_mpeg4desc = GetMPEG4DescByEsId( p_pmt, pid->u.p_pes->p_es->i_sl_es_id );
if( p_mpeg4desc && p_mpeg4desc->dec_descr.i_objectTypeIndication == 0x01 &&
p_mpeg4desc->dec_descr.i_streamType == 0x01 /* Object */ )
{
const uint8_t *p_data = p_content->p_buffer;
int i_data = p_content->i_buffer;
od_descriptors_t *p_ods = &p_pmt->od;
sl_header_data header = DecodeSLHeader( i_data, p_data, &p_mpeg4desc->sl_descr );
DecodeODCommand( VLC_OBJECT(p_demux), p_ods, i_data - header.i_size, &p_data[header.i_size] );
bool b_changed = false;
for( int i=0; i<p_ods->objects.i_size; i++ )
{
od_descriptor_t *p_od = p_ods->objects.p_elems[i];
for( int j = 0; j < ES_DESCRIPTOR_COUNT && p_od->es_descr[j].b_ok; j++ )
{
p_mpeg4desc = &p_od->es_descr[j];
ts_pes_es_t *p_es = GetPMTESBySLEsId( p_pmt, p_mpeg4desc->i_es_id );
es_format_t fmt;
es_format_Init( &fmt, UNKNOWN_ES, 0 );
fmt.i_id = p_es->fmt.i_id;
fmt.i_group = p_es->fmt.i_group;
if ( p_mpeg4desc && p_mpeg4desc->b_ok && p_es &&
SetupISO14496LogicalStream( p_demux, &p_mpeg4desc->dec_descr, &fmt ) &&
!es_format_IsSimilar( &fmt, &p_es->fmt ) )
{
es_format_Clean( &p_es->fmt );
p_es->fmt = fmt;
if( p_es->id )
es_out_Del( p_demux->out, p_es->id );
p_es->fmt.b_packetized = true; /* Split by access unit, no sync code */
FREENULL( p_es->fmt.psz_description );
p_es->id = es_out_Add( p_demux->out, &p_es->fmt );
b_changed = true;
}
}
}
if( b_changed )
UpdatePESFilters( p_demux, p_demux->p_sys->b_es_all );
}
block_Release( p_content );
}
/*****************************************************************************
* ts_sl.h : MPEG SL/FMC handling for TS demuxer
*****************************************************************************
* Copyright (C) 2014-2016 - VideoLAN Authors
*
* 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef VLC_TS_SL_H
#define VLC_TS_SL_H
typedef struct es_mpeg4_descriptor_t es_mpeg4_descriptor_t;
typedef struct decoder_config_descriptor_t decoder_config_descriptor_t;
const es_mpeg4_descriptor_t * GetMPEG4DescByEsId( const ts_pmt_t *pmt, uint16_t i_es_id );
void SLPackets_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content );
bool SetupISO14496LogicalStream( demux_t *, const decoder_config_descriptor_t *,
es_format_t * );
#endif
/*****************************************************************************
* ts_streams.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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include "ts_pid.h"
#include "ts_streams.h"
#include "ts_streams_private.h"
#ifndef _DVBPSI_DVBPSI_H_
#include <dvbpsi/dvbpsi.h>
#endif
#include <dvbpsi/demux.h>
#include <dvbpsi/descriptor.h>
#include <dvbpsi/pat.h>
#include <dvbpsi/pmt.h>
#include "../../mux/mpeg/dvbpsi_compat.h" /* dvbpsi_messages */
#include <vlc_demux.h>
#include <vlc_es.h>
#include <vlc_es_out.h>
#include "sections.h"
#include "ts_pid.h"
#include "ts.h"
static inline bool handle_Init( demux_t *p_demux, dvbpsi_t **handle )
{
*handle = dvbpsi_new( &dvbpsi_messages, DVBPSI_MSG_DEBUG );
if( !*handle )
return false;
(*handle)->p_sys = (void *) p_demux;
return true;
}
ts_pat_t *ts_pat_New( demux_t *p_demux )
{
ts_pat_t *pat = malloc( sizeof( ts_pat_t ) );
if( !pat )
return NULL;
if( !handle_Init( p_demux, &pat->handle ) )
{
free( pat );
return NULL;
}
pat->i_version = -1;
pat->i_ts_id = -1;
ARRAY_INIT( pat->programs );
return pat;
}
void ts_pat_Del( demux_t *p_demux, ts_pat_t *pat )
{
if( dvbpsi_decoder_present( pat->handle ) )
dvbpsi_pat_detach( pat->handle );
dvbpsi_delete( pat->handle );
for( int i=0; i<pat->programs.i_size; i++ )
PIDRelease( p_demux, pat->programs.p_elems[i] );
ARRAY_RESET( pat->programs );
free( pat );
}
ts_pmt_t *ts_pmt_New( demux_t *p_demux )
{
ts_pmt_t *pmt = malloc( sizeof( ts_pmt_t ) );
if( !pmt )
return NULL;
if( !handle_Init( p_demux, &pmt->handle ) )
{
free( pmt );
return NULL;
}
ARRAY_INIT( pmt->e_streams );
pmt->i_version = -1;
pmt->i_number = -1;
pmt->i_pid_pcr = 0x1FFF;
pmt->b_selected = false;
pmt->iod = NULL;
pmt->od.i_version = -1;
ARRAY_INIT( pmt->od.objects );
pmt->i_last_dts = -1;
pmt->p_mgt = NULL;
pmt->pcr.i_current = -1;
pmt->pcr.i_first = -1;
pmt->pcr.b_disable = false;
pmt->pcr.i_first_dts = VLC_TS_INVALID;
pmt->pcr.i_pcroffset = -1;
pmt->pcr.b_fix_done = false;
return pmt;
}
void ts_pmt_Del( demux_t *p_demux, ts_pmt_t *pmt )
{
if( dvbpsi_decoder_present( pmt->handle ) )
dvbpsi_pmt_detach( pmt->handle );
dvbpsi_delete( pmt->handle );
for( int i=0; i<pmt->e_streams.i_size; i++ )
PIDRelease( p_demux, pmt->e_streams.p_elems[i] );
ARRAY_RESET( pmt->e_streams );
if( pmt->p_mgt )
PIDRelease( p_demux, pmt->p_mgt );
if( pmt->iod )
ODFree( pmt->iod );
for( int i=0; i<pmt->od.objects.i_size; i++ )
ODFree( pmt->od.objects.p_elems[i] );
ARRAY_RESET( pmt->od.objects );
if( pmt->i_number > -1 )
es_out_Control( p_demux->out, ES_OUT_DEL_GROUP, pmt->i_number );
free( pmt );
}
ts_pes_es_t * ts_pes_es_New( ts_pmt_t *p_program )
{
ts_pes_es_t *p_es = malloc( sizeof(*p_es) );
if( p_es )
{
p_es->p_program = p_program;
p_es->id = NULL;
p_es->i_sl_es_id = 0;
p_es->p_extraes = NULL;
p_es->p_next = NULL;
p_es->b_interlaced = false;
es_format_Init( &p_es->fmt, UNKNOWN_ES, 0 );
p_es->fmt.i_group = p_program->i_number;
}
return p_es;
}
static void ts_pes_es_Clean( demux_t *p_demux, ts_pes_es_t *p_es )
{
if( p_es && p_es->id )
{
/* Ensure we don't wait for overlap hacks #14257 */
es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, p_es->id, false );
es_out_Del( p_demux->out, p_es->id );
p_demux->p_sys->i_pmt_es--;
}
es_format_Clean( &p_es->fmt );
}
void ts_pes_Add_es( ts_pes_t *p_pes, ts_pes_es_t *p_es, bool b_extra )
{
ts_pes_es_t **pp_es = (b_extra && p_pes->p_es) ? /* Ensure extra has main es */
&p_pes->p_es->p_extraes :
&p_pes->p_es;
if( likely(!*pp_es) )
{
*pp_es = p_es;
}
else
{
ts_pes_es_t *p_next = (*pp_es)->p_next;
(*pp_es)->p_next = p_es;
p_es->p_next = p_next;
}
}
ts_pes_es_t * ts_pes_Find_es( ts_pes_t *p_pes, const ts_pmt_t *p_pmt )
{
for( ts_pes_es_t *p_es = p_pes->p_es; p_es; p_es = p_es->p_next )
{
if( p_es->p_program == p_pmt )
return p_es;
}
return NULL;
}
ts_pes_es_t * ts_pes_Extract_es( ts_pes_t *p_pes, const ts_pmt_t *p_pmt )
{
ts_pes_es_t **pp_prev = &p_pes->p_es;
for( ts_pes_es_t *p_es = p_pes->p_es; p_es; p_es = p_es->p_next )
{
if( p_es->p_program == p_pmt )
{
*pp_prev = p_es->p_next;
p_es->p_next = NULL;
return p_es;
}
pp_prev = &p_es->p_next;
}
return NULL;
}
size_t ts_pes_Count_es( const ts_pes_es_t *p_es, bool b_active, const ts_pmt_t *p_pmt )
{
size_t i=0;
for( ; p_es; p_es = p_es->p_next )
{
i += ( b_active ) ? !!p_es->id : ( ( !p_pmt || p_pmt == p_es->p_program ) ? 1 : 0 );
i += ts_pes_Count_es( p_es->p_extraes, b_active, p_pmt );
}
return i;
}
static void ts_pes_ChainDelete_es( demux_t *p_demux, ts_pes_es_t *p_es )
{
while( p_es )
{
ts_pes_es_t *p_next = p_es->p_next;
ts_pes_ChainDelete_es( p_demux, p_es->p_extraes );
ts_pes_es_Clean( p_demux, p_es );
free( p_es );
p_es = p_next;
}
}
ts_pes_t *ts_pes_New( demux_t *p_demux, ts_pmt_t *p_program )
{
VLC_UNUSED(p_demux);
ts_pes_t *pes = malloc( sizeof( ts_pes_t ) );
if( !pes )
return NULL;
pes->p_es = ts_pes_es_New( p_program );
if( !pes->p_es )
{
free( pes );
return NULL;
}
pes->i_stream_type = 0;
pes->data_type = TS_ES_DATA_PES;
pes->i_data_size = 0;
pes->i_data_gathered = 0;
pes->p_data = NULL;
pes->pp_last = &pes->p_data;
pes->b_always_receive = false;
pes->p_sections_proc = NULL;
pes->p_prepcr_outqueue = NULL;
pes->sl.p_data = NULL;
pes->sl.pp_last = &pes->sl.p_data;
return pes;
}
void ts_pes_Del( demux_t *p_demux, ts_pes_t *pes )
{
ts_pes_ChainDelete_es( p_demux, pes->p_es );
if( pes->p_data )
block_ChainRelease( pes->p_data );
if( pes->p_sections_proc )
ts_sections_processor_ChainDelete( pes->p_sections_proc );
if( pes->p_prepcr_outqueue )
block_ChainRelease( pes->p_prepcr_outqueue );
free( pes );
}
ts_psi_t *ts_psi_New( demux_t *p_demux )
{
ts_psi_t *psi = malloc( sizeof( ts_psi_t ) );
if( !psi )
return NULL;
if( !handle_Init( p_demux, &psi->handle ) )
{
free( psi );
return NULL;
}
psi->i_version = -1;
return psi;
}
void ts_psi_Del( demux_t *p_demux, ts_psi_t *psi )
{
VLC_UNUSED(p_demux);
if( dvbpsi_decoder_present( psi->handle ) )
dvbpsi_DetachDemux( psi->handle );
dvbpsi_delete( psi->handle );
free( psi );
}
/*****************************************************************************
* ts_streams.h: 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef VLC_TS_STREAMS_H
#define VLC_TS_STREAMS_H
typedef struct ts_pes_es_t ts_pes_es_t;
typedef struct ts_pat_t ts_pat_t;
typedef struct ts_pmt_t ts_pmt_t;
typedef struct ts_pes_t ts_pes_t;
typedef struct ts_psi_t ts_psi_t;
/* Structs */
ts_pat_t *ts_pat_New( demux_t * );
void ts_pat_Del( demux_t *, ts_pat_t * );
ts_pmt_t *ts_pmt_New( demux_t * );
void ts_pmt_Del( demux_t *, ts_pmt_t * );
ts_pes_es_t * ts_pes_es_New( ts_pmt_t * );
void ts_pes_Add_es( ts_pes_t *, ts_pes_es_t *, bool );
ts_pes_es_t * ts_pes_Extract_es( ts_pes_t *, const ts_pmt_t * );
ts_pes_es_t * ts_pes_Find_es( ts_pes_t *, const ts_pmt_t * );
size_t ts_pes_Count_es( const ts_pes_es_t *, bool, const ts_pmt_t * );
ts_pes_t *ts_pes_New( demux_t *, ts_pmt_t * );
void ts_pes_Del( demux_t *, ts_pes_t * );
ts_psi_t *ts_psi_New( demux_t * );
void ts_psi_Del( demux_t *, ts_psi_t * );
#endif
/*****************************************************************************
* ts_streams_private.h: 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef VLC_TS_STREAMS_PRIVATE_H
#define VLC_TS_STREAMS_PRIVATE_H
typedef struct dvbpsi_s dvbpsi_t;
typedef struct ts_sections_processor_t ts_sections_processor_t;
#include "mpeg4_iod.h"
#include <vlc_common.h>
#include <vlc_es.h>
struct ts_pat_t
{
int i_version;
int i_ts_id;
dvbpsi_t *handle;
DECL_ARRAY(ts_pid_t *) programs;
};
struct ts_pmt_t
{
dvbpsi_t *handle;
int i_version;
int i_number;
int i_pid_pcr;
bool b_selected;
/* IOD stuff (mpeg4) */
od_descriptor_t *iod;
od_descriptors_t od;
DECL_ARRAY(ts_pid_t *) e_streams;
ts_pid_t *p_mgt;
struct
{
mtime_t i_current;
mtime_t i_first; // seen <> != -1
/* broken PCR handling */
mtime_t i_first_dts;
mtime_t i_pcroffset;
bool b_disable; /* ignore PCR field, use dts */
bool b_fix_done;
} pcr;
mtime_t i_last_dts;
};
struct ts_pes_es_t
{
ts_pmt_t *p_program;
es_format_t fmt;
es_out_id_t *id;
uint16_t i_sl_es_id;
ts_pes_es_t *p_extraes; /* Some private streams encapsulate several ES (eg. DVB subtitles) */
ts_pes_es_t *p_next; /* Next es on same pid from different pmt (shared pid) */
/* J2K stuff */
uint8_t b_interlaced;
};
typedef enum
{
TS_ES_DATA_PES,
TS_ES_DATA_TABLE_SECTION
} ts_es_data_type_t;
struct ts_pes_t
{
ts_pes_es_t *p_es;
uint8_t i_stream_type;
ts_es_data_type_t data_type;
int i_data_size;
int i_data_gathered;
block_t *p_data;
block_t **pp_last;
bool b_always_receive;
ts_sections_processor_t *p_sections_proc;
block_t * p_prepcr_outqueue;
/* SL AU */
struct
{
block_t *p_data;
block_t **pp_last;
} sl;
};
struct ts_psi_t
{
/* for special PAT/SDT case */
dvbpsi_t *handle; /* PAT/SDT/EIT */
int i_version;
};
struct ts_psip_t
{
dvbpsi_t *handle;
int i_version;
DECL_ARRAY(ts_pid_t *) eit;
};
#endif
......@@ -494,6 +494,7 @@ modules/demux/mpeg/mpgv.c
modules/demux/mpeg/ps.c
modules/demux/mpeg/ps.h
modules/demux/mpeg/ts.c
modules/demux/mpeg/ts_psi.c
modules/demux/nsc.c
modules/demux/nsv.c
modules/demux/nuv.c
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment