From efc1cee95dc48555898191fb07e0194098402f93 Mon Sep 17 00:00:00 2001 From: Francois Cartegnie <fcvlcdev@free.fr> Date: Wed, 3 Feb 2016 13:49:58 +0100 Subject: [PATCH] demux: ts: split the beast --- modules/demux/Makefile.am | 21 +- modules/demux/mpeg/mpeg4_iod.h | 14 +- modules/demux/mpeg/pes.h | 11 +- modules/demux/mpeg/ps.h | 1 + modules/demux/mpeg/sections.c | 8 +- modules/demux/mpeg/sections.h | 7 - modules/demux/mpeg/timestamps.h | 37 + modules/demux/mpeg/ts.c | 3777 +---------------------- modules/demux/mpeg/ts.h | 110 +- modules/demux/mpeg/ts_hotfixes.c | 354 +++ modules/demux/mpeg/ts_hotfixes.h | 27 + modules/demux/mpeg/ts_pid.c | 268 ++ modules/demux/mpeg/ts_pid.h | 118 + modules/demux/mpeg/ts_psi.c | 1784 +++++++++++ modules/demux/mpeg/ts_psi.h | 35 + modules/demux/mpeg/ts_psi_eit.c | 554 ++++ modules/demux/mpeg/ts_psi_eit.h | 24 + modules/demux/mpeg/ts_scte.c | 91 + modules/demux/mpeg/ts_scte.h | 27 + modules/demux/mpeg/ts_sl.c | 204 ++ modules/demux/mpeg/ts_sl.h | 29 + modules/demux/mpeg/ts_streams.c | 305 ++ modules/demux/mpeg/ts_streams.h | 47 + modules/demux/mpeg/ts_streams_private.h | 128 + po/POTFILES.in | 1 + 25 files changed, 4292 insertions(+), 3690 deletions(-) create mode 100644 modules/demux/mpeg/timestamps.h create mode 100644 modules/demux/mpeg/ts_hotfixes.c create mode 100644 modules/demux/mpeg/ts_hotfixes.h create mode 100644 modules/demux/mpeg/ts_pid.c create mode 100644 modules/demux/mpeg/ts_pid.h create mode 100644 modules/demux/mpeg/ts_psi.c create mode 100644 modules/demux/mpeg/ts_psi.h create mode 100644 modules/demux/mpeg/ts_psi_eit.c create mode 100644 modules/demux/mpeg/ts_psi_eit.h create mode 100644 modules/demux/mpeg/ts_scte.c create mode 100644 modules/demux/mpeg/ts_scte.h create mode 100644 modules/demux/mpeg/ts_sl.c create mode 100644 modules/demux/mpeg/ts_sl.h create mode 100644 modules/demux/mpeg/ts_streams.c create mode 100644 modules/demux/mpeg/ts_streams.h create mode 100644 modules/demux/mpeg/ts_streams_private.h diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am index cf0fd8c8bcbb..8518ef774868 100644 --- a/modules/demux/Makefile.am +++ b/modules/demux/Makefile.am @@ -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 diff --git a/modules/demux/mpeg/mpeg4_iod.h b/modules/demux/mpeg/mpeg4_iod.h index 4fca1306fb07..2ae91fd14ed6 100644 --- a/modules/demux/mpeg/mpeg4_iod.h +++ b/modules/demux/mpeg/mpeg4_iod.h @@ -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 diff --git a/modules/demux/mpeg/pes.h b/modules/demux/mpeg/pes.h index d76ba93f97ec..1decfc05a827 100644 --- a/modules/demux/mpeg/pes.h +++ b/modules/demux/mpeg/pes.h @@ -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 diff --git a/modules/demux/mpeg/ps.h b/modules/demux/mpeg/ps.h index 07e58dce654e..ceb1244ecb68 100644 --- a/modules/demux/mpeg/ps.h +++ b/modules/demux/mpeg/ps.h @@ -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) diff --git a/modules/demux/mpeg/sections.c b/modules/demux/mpeg/sections.c index 3a5573c9648b..45aad3cb491c 100644 --- a/modules/demux/mpeg/sections.c +++ b/modules/demux/mpeg/sections.c @@ -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; diff --git a/modules/demux/mpeg/sections.h b/modules/demux/mpeg/sections.h index caa0f1faae5c..eb6d90a0c9dc 100644 --- a/modules/demux/mpeg/sections.h +++ b/modules/demux/mpeg/sections.h @@ -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; diff --git a/modules/demux/mpeg/timestamps.h b/modules/demux/mpeg/timestamps.h new file mode 100644 index 000000000000..57f4fb08025a --- /dev/null +++ b/modules/demux/mpeg/timestamps.h @@ -0,0 +1,37 @@ +/***************************************************************************** + * 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 diff --git a/modules/demux/mpeg/ts.c b/modules/demux/mpeg/ts.c index fdd43ad55971..d33cf2a531b6 100644 --- a/modules/demux/mpeg/ts.c +++ b/modules/demux/mpeg/ts.c @@ -1,7 +1,7 @@ /***************************************************************************** * ts.c: Transport Stream input module for VLC. ***************************************************************************** - * Copyright (C) 2004-2005 VLC authors and VideoLAN + * Copyright (C) 2004-2016 VLC authors and VideoLAN * $Id$ * * Authors: Laurent Aimar <fenrir@via.ecp.fr> @@ -32,63 +32,40 @@ #include <vlc_common.h> #include <vlc_plugin.h> - -#include <assert.h> -#include <time.h> - #include <vlc_access.h> /* DVB-specific things */ #include <vlc_demux.h> -#include <vlc_meta.h> -#include <vlc_epg.h> -#include <vlc_charset.h> /* FromCharset, for EIT */ -#include <vlc_bits.h> - -#include "../../mux/mpeg/csa.h" - -#include "ts.h" -#include "ts_strings.h" /* Include dvbpsi headers */ -# include <dvbpsi/dvbpsi.h> -# include <dvbpsi/demux.h> +#ifndef _DVBPSI_DVBPSI_H_ + # include <dvbpsi/dvbpsi.h> +#endif # include <dvbpsi/descriptor.h> # include <dvbpsi/pat.h> # include <dvbpsi/pmt.h> -# include <dvbpsi/sdt.h> -# include <dvbpsi/dr.h> -# include <dvbpsi/psi.h> -/* EIT support */ -# include <dvbpsi/eit.h> +#include "ts_pid.h" +#include "ts_streams.h" +#include "ts_streams_private.h" +#include "ts_psi.h" +#include "ts_psi_eit.h" -/* TDT support */ -# include <dvbpsi/tot.h> +#include "ts_hotfixes.h" +#include "ts_sl.h" +#include "sections.h" +#include "pes.h" +#include "timestamps.h" -#include "../../mux/mpeg/dvbpsi_compat.h" -#include "../../mux/mpeg/streams.h" -#include "../../mux/mpeg/tsutil.h" -#include "../../mux/mpeg/tables.h" +#include "ts.h" -#include "../../codec/opus_header.h" #include "../../codec/scte18.h" - #include "../opus.h" - -#include "pes.h" -#include "mpeg4_iod.h" -#include "sections.h" +#include "../../mux/mpeg/csa.h" #ifdef HAVE_ARIBB24 #include <aribb24/aribb24.h> - #include <aribb24/decoder.h> #endif -typedef enum arib_modes_e -{ - ARIBMODE_AUTO = -1, - ARIBMODE_DISABLED = 0, - ARIBMODE_ENABLED = 1 -} arib_modes_e; +#include <assert.h> /***************************************************************************** * Module descriptor @@ -190,301 +167,14 @@ vlc_module_end () /***************************************************************************** * Local prototypes *****************************************************************************/ -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") -}; - -typedef struct -{ - int i_version; - int i_ts_id; - dvbpsi_t *handle; - DECL_ARRAY(ts_pid_t *) programs; - -} ts_pat_t; - -typedef struct -{ - 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; - - 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; - -} ts_pmt_t; - -typedef struct ts_pes_es_t ts_pes_es_t; - -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; - -typedef struct -{ - 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; -} ts_pes_t; - - -typedef struct -{ - /* for special PAT/SDT case */ - dvbpsi_t *handle; /* PAT/SDT/EIT */ - int i_version; - -} ts_psi_t; - -typedef enum -{ - TS_PMT_REGISTRATION_NONE = 0, - TS_PMT_REGISTRATION_BLURAY, - TS_PMT_REGISTRATION_ATSC, - TS_PMT_REGISTRATION_ARIB, -} ts_pmt_registration_type_t; - -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 -{ - int i_service; -} vdr_info_t; - -#define MIN_ES_PID 4 /* Should be 32.. broken muxers */ -#define MAX_ES_PID 8190 -#define MIN_PAT_INTERVAL CLOCK_FREQ // DVB is 500ms - -#define PID_ALLOC_CHUNK 16 - -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 */ - struct - { - 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; - } 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; -}; - static int Demux ( demux_t *p_demux ); static int Control( demux_t *p_demux, int i_query, va_list args ); -static void PIDFillFormat( ts_pes_t *p_pes, int i_stream_type, ts_es_data_type_t * ); - -static bool PIDSetup( demux_t *p_demux, ts_pid_type_t i_type, ts_pid_t *pid, ts_pid_t *p_parent ); -static void PIDRelease( demux_t *p_demux, ts_pid_t *pid ); - -static void PATCallBack( void*, dvbpsi_pat_t * ); -static void PMTCallBack( void *data, dvbpsi_pmt_t *p_pmt ); -static void PSINewTableCallBack( dvbpsi_t *handle, uint8_t i_table_id, - uint16_t i_extension, demux_t * ); - static int ChangeKeyCallback( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * ); -/* Structs */ -static ts_pat_t *ts_pat_New( demux_t * ); -static void ts_pat_Del( demux_t *, ts_pat_t * ); -static ts_pmt_t *ts_pmt_New( demux_t * ); -static void ts_pmt_Del( demux_t *, ts_pmt_t * ); -static ts_pes_es_t * ts_pes_es_New( ts_pmt_t * ); -static void ts_pes_Add_es( ts_pes_t *, ts_pes_es_t *, bool ); -static ts_pes_es_t * ts_pes_Extract_es( ts_pes_t *, const ts_pmt_t * ); -static ts_pes_es_t * ts_pes_Find_es( ts_pes_t *, const ts_pmt_t * ); -static size_t ts_pes_Count_es( const ts_pes_es_t *, bool, const ts_pmt_t * ); -static ts_pes_t *ts_pes_New( demux_t *, ts_pmt_t * ); -static void ts_pes_Del( demux_t *, ts_pes_t * ); -static ts_psi_t *ts_psi_New( demux_t * ); -static void ts_psi_Del( demux_t *, ts_psi_t * ); - /* Helpers */ -static ts_pid_t *GetPID( demux_sys_t *, uint16_t i_pid ); static bool PIDReferencedByProgram( const ts_pmt_t *, uint16_t ); -static bool ProgramIsSelected( demux_sys_t *, uint16_t i_pgrm ); -static void UpdatePESFilters( demux_t *p_demux, bool b_all ); +void UpdatePESFilters( demux_t *p_demux, bool b_all ); static inline void FlushESBuffer( ts_pes_t *p_pes ); static void UpdatePIDScrambledState( demux_t *p_demux, ts_pid_t *p_pid, bool ); static inline int PIDGet( block_t *p ) @@ -493,29 +183,13 @@ static inline int PIDGet( block_t *p ) } static bool GatherData( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk ); -static void AddAndCreateES( demux_t *p_demux, ts_pid_t *pid, bool ); static void ProgramSetPCR( demux_t *p_demux, ts_pmt_t *p_prg, mtime_t i_pcr ); static block_t* ReadTSPacket( demux_t *p_demux ); -static int ProbeStart( demux_t *p_demux, int i_program ); -static int ProbeEnd( demux_t *p_demux, int i_program ); static int SeekToTime( demux_t *p_demux, const ts_pmt_t *, int64_t time ); static void ReadyQueuesPostSeek( demux_t *p_demux ); static void PCRHandle( demux_t *p_demux, ts_pid_t *, block_t * ); static void PCRFixHandle( demux_t *, ts_pmt_t *, block_t * ); -static int64_t TimeStampWrapAround( const ts_pmt_t *, int64_t ); - -/* MPEG4 related */ -static const es_mpeg4_descriptor_t * GetMPEG4DescByEsId( const ts_pmt_t *, uint16_t ); -static ts_pes_es_t * GetPMTESBySLEsId( ts_pmt_t *, uint16_t ); -static bool SetupISO14496LogicalStream( demux_t *, const decoder_config_descriptor_t *, - es_format_t * ); - -#define TS_USER_PMT_NUMBER (0) -static int UserPmt( demux_t *p_demux, const char * ); - -static int UpdateHWFilter( demux_sys_t *, ts_pid_t * ); -static int SetPIDFilter( demux_sys_t *, ts_pid_t *, bool b_selected ); #define TS_PACKET_SIZE_188 188 #define TS_PACKET_SIZE_192 192 @@ -670,315 +344,15 @@ static int DetectPVRHeadersAndHeaderSize( demux_t *p_demux, unsigned *pi_header_ return DetectPacketSize( p_demux, pi_header_size, 0 ); } -static 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; - } -} - -static 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 ); - } - } - - 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; - - 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); -} - /***************************************************************************** * Open *****************************************************************************/ +# define VLC_DVBPSI_DEMUX_TABLE_INIT(table,obj) \ + do { \ + if( !AttachDvbpsiNewEITTableHandler( (table)->u.p_psi->handle, obj ) ) \ + msg_Warn( obj, "Can't dvbpsi_AttachDemux on pid %d", (table)->i_pid );\ + } while (0) + static int Open( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t*)p_this; @@ -1021,8 +395,7 @@ static int Open( vlc_object_t *p_this ) p_sys->b_broken_charset = false; - p_sys->pids.dummy.i_pid = 8191; - p_sys->pids.dummy.i_flags = FLAG_SEEN; + ts_pid_list_Init( &p_sys->pids ); p_sys->i_packet_size = i_packet_size; p_sys->i_packet_header_size = i_packet_header_size; @@ -1034,14 +407,6 @@ static int Open( vlc_object_t *p_this ) p_sys->patfix.i_timesourcepid = 0; p_sys->patfix.status = PAT_WAITING; -# define VLC_DVBPSI_DEMUX_TABLE_INIT(table,obj) \ - do { \ - if( !dvbpsi_AttachDemux( (table)->u.p_psi->handle, (dvbpsi_demux_new_cb_t)PSINewTableCallBack, (obj) ) ) \ - { \ - msg_Warn( obj, "Can't dvbpsi_AttachDemux on pid %d", (table)->i_pid );\ - } \ - } while (0) - /* Init PAT handler */ patpid = GetPID(p_sys, 0); if ( !PIDSetup( p_demux, TYPE_PAT, patpid, NULL ) ) @@ -1217,16 +582,7 @@ static void Close( vlc_object_t *p_this ) vlc_mutex_destroy( &p_sys->csa_lock ); /* Release all non default pids */ - for( int i = 0; i < p_sys->pids.i_all; i++ ) - { - ts_pid_t *pid = p_sys->pids.pp_all[i]; -#ifndef NDEBUG - if( pid->type != TYPE_FREE ) - msg_Err( p_demux, "PID %d type %d not freed", pid->i_pid, pid->type ); -#endif - free( pid ); - } - free( p_sys->pids.pp_all ); + ts_pid_list_Release( p_demux, &p_sys->pids ); free( p_sys ); } @@ -1412,7 +768,7 @@ static inline void HasSelectedES( es_out_t *out, const ts_pes_es_t *p_es, } } -static void UpdatePESFilters( demux_t *p_demux, bool b_all ) +void UpdatePESFilters( demux_t *p_demux, bool b_all ) { demux_sys_t *p_sys = p_demux->p_sys; ts_pat_t *p_pat = GetPID(p_sys, 0)->u.p_pat; @@ -1534,10 +890,10 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) p_pmt->pcr.i_first > -1 && p_pmt->i_last_dts > VLC_TS_INVALID && p_pmt->pcr.i_current > -1 ) { - double i_length = TimeStampWrapAround( p_pmt, + double i_length = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->i_last_dts ) - p_pmt->pcr.i_first; i_length += p_pmt->pcr.i_pcroffset; - double i_pos = TimeStampWrapAround( p_pmt, + double i_pos = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->pcr.i_current ) - p_pmt->pcr.i_first; if( i_length > 0 ) { @@ -1578,7 +934,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) p_pmt->pcr.i_first > -1 && p_pmt->i_last_dts > VLC_TS_INVALID && p_pmt->pcr.i_current > -1 ) { - int64_t i_length = TimeStampWrapAround( p_pmt, + int64_t i_length = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->i_last_dts ) - p_pmt->pcr.i_first; i64 = p_pmt->pcr.i_first + (int64_t)(i_length * f); if( i64 <= p_pmt->i_last_dts ) @@ -1625,7 +981,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) if( p_pmt && p_pmt->pcr.i_current > -1 && p_pmt->pcr.i_first > -1 ) { - int64_t i_pcr = TimeStampWrapAround( p_pmt, p_pmt->pcr.i_current ); + int64_t i_pcr = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->pcr.i_current ); *pi64 = FROM_SCALE(i_pcr - p_pmt->pcr.i_first); return VLC_SUCCESS; } @@ -1646,7 +1002,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) { int64_t i_start = (p_pmt->pcr.i_first > -1) ? p_pmt->pcr.i_first : TO_SCALE(p_pmt->pcr.i_first_dts); - int64_t i_last = TimeStampWrapAround( p_pmt, p_pmt->i_last_dts ); + int64_t i_last = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->i_last_dts ); i_last += p_pmt->pcr.i_pcroffset; *pi64 = FROM_SCALE(i_last - i_start); return VLC_SUCCESS; @@ -1750,326 +1106,51 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) /***************************************************************************** * *****************************************************************************/ -static int UserPmt( demux_t *p_demux, const char *psz_fmt ) +static int16_t read_opus_flag(uint8_t **buf, size_t *len) { - 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 ); + if (*len < 2) + return -1; - psz = strchr( psz, '=' ); - if( psz ) - psz++; - while( psz && *psz ) - { - char *psz_next = strchr( psz, ',' ); - int i_pid; + int16_t ret = ((*buf)[0] << 8) | (*buf)[1]; - if( psz_next ) - *psz_next++ = '\0'; + *len -= 2; + *buf += 2; - i_pid = strtol( psz, &psz, 0 ); - if( *psz != ':' || i_pid < 2 || i_pid >= 8192 ) - goto next; + if (ret & (3<<13)) + ret = -1; - 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); + return ret; +} - char *psz_arg = strchr( psz_opt, '=' ); - if( psz_arg ) - *psz_arg++ = '\0'; +static block_t *Opus_Parse(demux_t *demux, block_t *block) +{ + block_t *out = NULL; + block_t **last = NULL; - if ( !PIDSetup( p_demux, TYPE_PES, pid, pmtpid ) ) - continue; + uint8_t *buf = block->p_buffer; + size_t len = block->i_buffer; - ARRAY_APPEND( p_pmt->e_streams, pid ); + while (len > 3 && ((buf[0] << 3) | (buf[1] >> 5)) == 0x3ff) { + int16_t start_trim = 0, end_trim = 0; + int start_trim_flag = (buf[1] >> 4) & 1; + int end_trim_flag = (buf[1] >> 3) & 1; + int control_extension_flag = (buf[1] >> 2) & 1; - if( p_pmt->i_pid_pcr <= 0 ) - p_pmt->i_pid_pcr = i_pid; + len -= 2; + buf += 2; - es_format_t *fmt = &pid->u.p_pes->p_es->fmt; + unsigned au_size = 0; + while (len--) { + int c = *buf++; + au_size += c; + if (c != 0xff) + break; + } - 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; -} - -static 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) ); -} - -static 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 ); -} - -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; -} - -static 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; -} - -static 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 ); - } -} - -static int16_t read_opus_flag(uint8_t **buf, size_t *len) -{ - if (*len < 2) - return -1; - - int16_t ret = ((*buf)[0] << 8) | (*buf)[1]; - - *len -= 2; - *buf += 2; - - if (ret & (3<<13)) - ret = -1; - - return ret; -} - -static block_t *Opus_Parse(demux_t *demux, block_t *block) -{ - block_t *out = NULL; - block_t **last = NULL; - - uint8_t *buf = block->p_buffer; - size_t len = block->i_buffer; - - while (len > 3 && ((buf[0] << 3) | (buf[1] >> 5)) == 0x3ff) { - int16_t start_trim = 0, end_trim = 0; - int start_trim_flag = (buf[1] >> 4) & 1; - int end_trim_flag = (buf[1] >> 3) & 1; - int control_extension_flag = (buf[1] >> 2) & 1; - - len -= 2; - buf += 2; - - unsigned au_size = 0; - while (len--) { - int c = *buf++; - au_size += c; - if (c != 0xff) - break; - } - - if (start_trim_flag) { - start_trim = read_opus_flag(&buf, &len); - if (start_trim < 0) { - msg_Err(demux, "Invalid start trimming flag"); + if (start_trim_flag) { + start_trim = read_opus_flag(&buf, &len); + if (start_trim < 0) { + msg_Err(demux, "Invalid start trimming flag"); } } if (end_trim_flag) { @@ -2195,10 +1276,10 @@ static void ParsePES( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes ) } else { - if( i_pts != -1 ) - i_pts = TimeStampWrapAround( p_es->p_program, i_pts ); - if( i_dts != -1 ) - i_dts = TimeStampWrapAround( p_es->p_program, i_dts ); + if( i_pts != -1 && p_es->p_program ) + i_pts = TimeStampWrapAround( p_es->p_program->pcr.i_first, i_pts ); + if( i_dts != -1 && p_es->p_program ) + i_dts = TimeStampWrapAround( p_es->p_program->pcr.i_first, i_dts ); } if( p_es->i_sl_es_id ) @@ -2360,7 +1441,7 @@ static void ParsePES( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes ) p_pmt->pcr.i_current > VLC_TS_INVALID ) { int64_t i_dts27 = (p_block->i_dts - VLC_TS_0) * 9 / 100; - int64_t i_pcr = TimeStampWrapAround( p_pmt, p_pmt->pcr.i_current ); + int64_t i_pcr = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->pcr.i_current ); if( i_dts27 < i_pcr ) { p_pmt->pcr.i_pcroffset = i_pcr - i_dts27 + 80000; @@ -2478,119 +1559,6 @@ static void ParsePES( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes ) } } -static 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, 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 ); -} - -static 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 ); -} - -/* Object stream SL in table sections */ -static 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 ); -} - static void ParseData( demux_t *p_demux, ts_pid_t *pid ) { block_t *p_datachain = pid->u.p_pes->p_data; @@ -2700,15 +1668,6 @@ static block_t* ReadTSPacket( demux_t *p_demux ) return p_pkt; } -static int64_t TimeStampWrapAround( const ts_pmt_t *p_pmt, int64_t i_time ) -{ - int64_t i_adjust = 0; - if( p_pmt && p_pmt->pcr.i_first > 0x0FFFFFFFF && i_time < 0x0FFFFFFFF ) - i_adjust = 0x1FFFFFFFF; - - return i_time + i_adjust; -} - static mtime_t GetPCR( block_t *p_pkt ) { const uint8_t *p = p_pkt->p_buffer; @@ -2898,7 +1857,7 @@ static int SeekToTime( demux_t *p_demux, const ts_pmt_t *p_pmt, int64_t i_scaled if( i_pcr != -1 ) { - int64_t i_diff = i_scaledtime - TimeStampWrapAround( p_pmt, i_pcr ); + int64_t i_diff = i_scaledtime - TimeStampWrapAround( p_pmt->pcr.i_first, i_pcr ); if ( i_diff < 0 ) i_tail_pos = i_splitpos - p_sys->i_packet_size; else if( i_diff < TO_SCALE(VLC_TS_0 + CLOCK_FREQ / 2) ) // 500ms @@ -2922,56 +1881,6 @@ static int SeekToTime( demux_t *p_demux, const ts_pmt_t *p_pmt, int64_t i_scaled return VLC_SUCCESS; } -static ts_pid_t *GetPID( demux_sys_t *p_sys, uint16_t i_pid ) -{ - switch( i_pid ) - { - case 0: - return &p_sys->pids.pat; - case 0x1FFF: - return &p_sys->pids.dummy; - default: - if( p_sys->pids.i_last_pid == i_pid ) - return p_sys->pids.p_last; - break; - } - - for( int i=0; i < p_sys->pids.i_all; i++ ) - { - if( p_sys->pids.pp_all[i]->i_pid == i_pid ) - { - p_sys->pids.p_last = p_sys->pids.pp_all[i]; - p_sys->pids.i_last_pid = i_pid; - return p_sys->pids.p_last; - } - } - - if( p_sys->pids.i_all >= p_sys->pids.i_all_alloc ) - { - ts_pid_t **p_realloc = realloc( p_sys->pids.pp_all, - (p_sys->pids.i_all_alloc + PID_ALLOC_CHUNK) * sizeof(ts_pid_t *) ); - if( !p_realloc ) - return NULL; - p_sys->pids.pp_all = p_realloc; - p_sys->pids.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_sys->pids.pp_all[p_sys->pids.i_all++] = p_pid; - - p_sys->pids.p_last = p_pid; - p_sys->pids.i_last_pid = i_pid; - - return p_pid; -} - #define PROBE_CHUNK_COUNT 250 static int ProbeChunk( demux_t *p_demux, int i_program, bool b_end, int64_t *pi_pcr, bool *pb_found ) @@ -3066,7 +1975,7 @@ static int ProbeChunk( demux_t *p_demux, int i_program, bool b_end, int64_t *pi_ return i_count; } -static int ProbeStart( demux_t *p_demux, int i_program ) +int ProbeStart( demux_t *p_demux, int i_program ) { demux_sys_t *p_sys = p_demux->p_sys; const int64_t i_initial_pos = stream_Tell( p_sys->stream ); @@ -3097,7 +2006,7 @@ static int ProbeStart( demux_t *p_demux, int i_program ) return (b_found) ? VLC_SUCCESS : VLC_EGENERIC; } -static int ProbeEnd( demux_t *p_demux, int i_program ) +int ProbeEnd( demux_t *p_demux, int i_program ) { demux_sys_t *p_sys = p_demux->p_sys; const int64_t i_initial_pos = stream_Tell( p_sys->stream ); @@ -3236,7 +2145,7 @@ static void PCRHandle( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk ) for( int i = 0; i < p_pat->programs.i_size; i++ ) { ts_pmt_t *p_pmt = p_pat->programs.p_elems[i]->u.p_pmt; - mtime_t i_program_pcr = TimeStampWrapAround( p_pmt, i_pcr ); + mtime_t i_program_pcr = TimeStampWrapAround( p_pmt->pcr.i_first, i_pcr ); if( p_pmt->i_pid_pcr == 0x1FFF ) /* That program has no dedicated PCR pid ISO/IEC 13818-1 2.4.4.9 */ { @@ -3260,7 +2169,7 @@ static void PCRHandle( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk ) } } -static int FindPCRCandidate( ts_pmt_t *p_pmt ) +int FindPCRCandidate( ts_pmt_t *p_pmt ) { ts_pid_t *p_cand = NULL; int i_previous = p_pmt->i_pid_pcr; @@ -3359,7 +2268,7 @@ static bool GatherData( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk ) pid->u.p_pes->p_data->i_flags |= BLOCK_FLAG_CORRUPTED; } - if( p_demux->p_sys->csa ) + if( p_demux->p_sys->csa && SCRAMBLED(*pid) ) { vlc_mutex_lock( &p_demux->p_sys->csa_lock ); csa_Decrypt( p_demux->p_sys->csa, p_bk->p_buffer, p_demux->p_sys->i_csa_pkt_size ); @@ -3513,91 +2422,12 @@ static bool GatherData( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk ) return i_ret; } -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; -} - /**************************************************************************** **************************************************************************** ** libdvbpsi callbacks **************************************************************************** ****************************************************************************/ -static bool ProgramIsSelected( demux_sys_t *p_sys, uint16_t i_pgrm ) +bool ProgramIsSelected( demux_sys_t *p_sys, uint16_t i_pgrm ) { for(int i=0; i<p_sys->programs.i_size; i++) if( p_sys->programs.p_elems[i] == i_pgrm ) @@ -3615,2447 +2445,60 @@ static bool PIDReferencedByProgram( const ts_pmt_t *p_pmt, uint16_t i_pid ) return false; } -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; -} - -#include "../dvb-text.h" - -static char *EITConvertToUTF8( demux_t *p_demux, - const unsigned char *psz_instring, - size_t i_length, - bool b_broken ) +static void DoCreateES( demux_t *p_demux, ts_pes_es_t *p_es, const ts_pes_es_t *p_parent_es ) { demux_sys_t *p_sys = p_demux->p_sys; -#ifdef HAVE_ARIBB24 - if( p_sys->arib.e_mode == ARIBMODE_ENABLED ) + for( ; p_es ; p_es = p_es->p_next ) { - 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" ); - } -} - -/***************************************************************************** - * 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 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; -} - -static 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; -} - -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 ); - } -} - -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; - } - } -} - -typedef struct -{ - int i_type; - int i_magazine; - int i_page; - char p_iso639[3]; -} ts_teletext_page_t; - -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 DoCreateES( demux_t *p_demux, ts_pes_es_t *p_es, const ts_pes_es_t *p_parent_es ) -{ - demux_sys_t *p_sys = p_demux->p_sys; - for( ; p_es ; p_es = p_es->p_next ) - { - if( !p_es->id ) - { - if( !p_es->fmt.i_group ) - p_es->fmt.i_group = p_es->p_program->i_number; - p_es->id = es_out_Add( p_demux->out, &p_es->fmt ); - if( p_parent_es ) /* Set Extra ES group and original ID */ - { - if ( p_sys->b_es_id_pid ) /* pid is 13 bits */ - p_es->fmt.i_id = (p_sys->i_next_extraid++ << 13) | p_parent_es->fmt.i_id; - p_es->fmt.i_group = p_parent_es->fmt.i_group; - } - p_sys->i_pmt_es++; - } - DoCreateES( p_demux, p_es->p_extraes, p_es ); - } -} - -static void AddAndCreateES( demux_t *p_demux, ts_pid_t *pid, bool b_create_delayed ) -{ - demux_sys_t *p_sys = p_demux->p_sys; - - if( b_create_delayed ) - p_sys->es_creation = CREATE_ES; - - if( pid && p_sys->es_creation == CREATE_ES ) - { - DoCreateES( p_demux, pid->u.p_pes->p_es, NULL ); - - /* Update the default program == first created ES group */ - if( p_sys->b_default_selection && p_sys->programs.i_size > 0) - { - p_sys->b_default_selection = false; - const int i_first_program = pid->u.p_pes->p_es->p_program->i_number; - if( p_sys->programs.p_elems[0] != i_first_program ) - p_sys->programs.p_elems[0] = i_first_program; - msg_Dbg( p_demux, "Default program is %d", i_first_program ); - } - } - - if( b_create_delayed ) - { - ts_pat_t *p_pat = GetPID(p_sys, 0)->u.p_pat; - for( int i=0; i< p_pat->programs.i_size; i++ ) - { - ts_pmt_t *p_pmt = p_pat->programs.p_elems[i]->u.p_pmt; - for( int j=0; j<p_pmt->e_streams.i_size; j++ ) - DoCreateES( p_demux, p_pmt->e_streams.p_elems[j]->u.p_pes->p_es, NULL ); - } - } - - UpdatePESFilters( p_demux, p_sys->b_es_all ); -} - -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; -} - -#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; -} - -static void PMTCallBack( void *data, dvbpsi_pmt_t *p_dvbpsipmt ) -{ - demux_t *p_demux = data; - demux_sys_t *p_sys = p_demux->p_sys; - - ts_pid_t *pmtpid = NULL; - ts_pmt_t *p_pmt = NULL; - - msg_Dbg( p_demux, "PMTCallBack called for program %d", p_dvbpsipmt->i_program_number ); - - if (unlikely(GetPID(p_sys, 0)->type != TYPE_PAT)) - { - assert(GetPID(p_sys, 0)->type == TYPE_PAT); - dvbpsi_pmt_delete(p_dvbpsipmt); - } - - const ts_pat_t *p_pat = GetPID(p_sys, 0)->u.p_pat; - - /* First find this PMT declared in PAT */ - for( int i = 0; !pmtpid && i < p_pat->programs.i_size; i++ ) - { - const int i_pmt_prgnumber = p_pat->programs.p_elems[i]->u.p_pmt->i_number; - if( i_pmt_prgnumber != TS_USER_PMT_NUMBER && - i_pmt_prgnumber == p_dvbpsipmt->i_program_number ) - { - pmtpid = p_pat->programs.p_elems[i]; - assert(pmtpid->type == TYPE_PMT); - p_pmt = pmtpid->u.p_pmt; - } - } - - if( pmtpid == NULL ) - { - msg_Warn( p_demux, "unreferenced program (broken stream)" ); - dvbpsi_pmt_delete(p_dvbpsipmt); - return; - } - - pmtpid->i_flags |= FLAG_SEEN; - - if( p_pmt->i_version != -1 && - ( !p_dvbpsipmt->b_current_next || p_pmt->i_version == p_dvbpsipmt->i_version ) ) - { - dvbpsi_pmt_delete( p_dvbpsipmt ); - return; - } - - /* Save old es array */ - DECL_ARRAY(ts_pid_t *) old_es_rm; - old_es_rm.i_alloc = p_pmt->e_streams.i_alloc; - old_es_rm.i_size = p_pmt->e_streams.i_size; - old_es_rm.p_elems = p_pmt->e_streams.p_elems; - 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, ®istration_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 */ + if( !p_es->id ) { - 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 */ + if( !p_es->fmt.i_group ) + p_es->fmt.i_group = p_es->p_program->i_number; + p_es->id = es_out_Add( p_demux->out, &p_es->fmt ); + if( p_parent_es ) /* Set Extra ES group and original ID */ { - 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 ); + if ( p_sys->b_es_id_pid ) /* pid is 13 bits */ + p_es->fmt.i_id = (p_sys->i_next_extraid++ << 13) | p_parent_es->fmt.i_id; + p_es->fmt.i_group = p_parent_es->fmt.i_group; } + p_sys->i_pmt_es++; } - /* 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 < old_es_rm.i_size; i++ ) - PIDRelease( p_demux, old_es_rm.p_elems[i] ); - ARRAY_RESET( old_es_rm ); - - 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 ); - } -} - -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; + DoCreateES( p_demux, p_es->p_extraes, p_es ); } - return VLC_SUCCESS; } -static void PATCallBack( void *data, dvbpsi_pat_t *p_dvbpsipat ) +void AddAndCreateES( demux_t *p_demux, ts_pid_t *pid, bool b_create_delayed ) { - 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 ); + demux_sys_t *p_sys = p_demux->p_sys; - /* 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); + if( b_create_delayed ) + p_sys->es_creation = CREATE_ES; - /* now create programs */ - for( p_program = p_dvbpsipat->p_first_program; p_program != NULL; - p_program = p_program->p_next ) + if( pid && p_sys->es_creation == CREATE_ES ) { - 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 ); + DoCreateES( p_demux, pid->u.p_pes->p_es, NULL ); - /* Now select PID at access level */ - if( p_sys->programs.i_size == 0 || - ProgramIsSelected( p_sys, p_program->i_number ) ) + /* Update the default program == first created ES group */ + if( p_sys->b_default_selection && p_sys->programs.i_size > 0) { - 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_sys->b_default_selection = false; + const int i_first_program = pid->u.p_pes->p_es->p_program->i_number; + if( p_sys->programs.p_elems[0] != i_first_program ) + p_sys->programs.p_elems[0] = i_first_program; + msg_Dbg( p_demux, "Default program is %d", i_first_program ); } } - 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 ); -} - -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; -} - -static 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; -} - -static 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 ); -} - -static 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->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; -} - -static 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->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 ); -} - -static 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 ); -} - -static 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; - } -} - -static 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; -} -static 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( b_create_delayed ) { - if( p_es->p_program == p_pmt ) + ts_pat_t *p_pat = GetPID(p_sys, 0)->u.p_pat; + for( int i=0; i< p_pat->programs.i_size; i++ ) { - *pp_prev = p_es->p_next; - p_es->p_next = NULL; - return p_es; + ts_pmt_t *p_pmt = p_pat->programs.p_elems[i]->u.p_pmt; + for( int j=0; j<p_pmt->e_streams.i_size; j++ ) + DoCreateES( p_demux, p_pmt->e_streams.p_elems[j]->u.p_pes->p_es, NULL ); } - pp_prev = &p_es->p_next; - } - return NULL; -} - -static 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; - } -} - -static 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; -} - -static 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 ); -} - -static 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; -} - -static 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 ); + UpdatePESFilters( p_demux, p_sys->b_es_all ); } diff --git a/modules/demux/mpeg/ts.h b/modules/demux/mpeg/ts.h index 79427530986f..e55a02c126f7 100644 --- a/modules/demux/mpeg/ts.h +++ b/modules/demux/mpeg/ts.h @@ -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 diff --git a/modules/demux/mpeg/ts_hotfixes.c b/modules/demux/mpeg/ts_hotfixes.c new file mode 100644 index 000000000000..c485b1ba37c7 --- /dev/null +++ b/modules/demux/mpeg/ts_hotfixes.c @@ -0,0 +1,354 @@ +/***************************************************************************** + * 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); +} diff --git a/modules/demux/mpeg/ts_hotfixes.h b/modules/demux/mpeg/ts_hotfixes.h new file mode 100644 index 000000000000..25faf05b1fa4 --- /dev/null +++ b/modules/demux/mpeg/ts_hotfixes.h @@ -0,0 +1,27 @@ +/***************************************************************************** + * 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 diff --git a/modules/demux/mpeg/ts_pid.c b/modules/demux/mpeg/ts_pid.c new file mode 100644 index 000000000000..41a7b6f1b059 --- /dev/null +++ b/modules/demux/mpeg/ts_pid.c @@ -0,0 +1,268 @@ +/***************************************************************************** + * 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 ); +} diff --git a/modules/demux/mpeg/ts_pid.h b/modules/demux/mpeg/ts_pid.h new file mode 100644 index 000000000000..3a94a0a6d81d --- /dev/null +++ b/modules/demux/mpeg/ts_pid.h @@ -0,0 +1,118 @@ +/***************************************************************************** + * 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 diff --git a/modules/demux/mpeg/ts_psi.c b/modules/demux/mpeg/ts_psi.c new file mode 100644 index 000000000000..a31715c74374 --- /dev/null +++ b/modules/demux/mpeg/ts_psi.c @@ -0,0 +1,1784 @@ +/***************************************************************************** + * 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, ®istration_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; +} diff --git a/modules/demux/mpeg/ts_psi.h b/modules/demux/mpeg/ts_psi.h new file mode 100644 index 000000000000..9926ae444d77 --- /dev/null +++ b/modules/demux/mpeg/ts_psi.h @@ -0,0 +1,35 @@ +/***************************************************************************** + * 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 diff --git a/modules/demux/mpeg/ts_psi_eit.c b/modules/demux/mpeg/ts_psi_eit.c new file mode 100644 index 000000000000..91a795bfcee0 --- /dev/null +++ b/modules/demux/mpeg/ts_psi_eit.c @@ -0,0 +1,554 @@ +/***************************************************************************** + * 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 ); +} diff --git a/modules/demux/mpeg/ts_psi_eit.h b/modules/demux/mpeg/ts_psi_eit.h new file mode 100644 index 000000000000..726eb886aae0 --- /dev/null +++ b/modules/demux/mpeg/ts_psi_eit.h @@ -0,0 +1,24 @@ +/***************************************************************************** + * 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 diff --git a/modules/demux/mpeg/ts_scte.c b/modules/demux/mpeg/ts_scte.c new file mode 100644 index 000000000000..ba8765841e7b --- /dev/null +++ b/modules/demux/mpeg/ts_scte.c @@ -0,0 +1,91 @@ +/***************************************************************************** + * 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 ); +} diff --git a/modules/demux/mpeg/ts_scte.h b/modules/demux/mpeg/ts_scte.h new file mode 100644 index 000000000000..c60d644f8034 --- /dev/null +++ b/modules/demux/mpeg/ts_scte.h @@ -0,0 +1,27 @@ +/***************************************************************************** + * 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 diff --git a/modules/demux/mpeg/ts_sl.c b/modules/demux/mpeg/ts_sl.c new file mode 100644 index 000000000000..b55b927fa38a --- /dev/null +++ b/modules/demux/mpeg/ts_sl.c @@ -0,0 +1,204 @@ +/***************************************************************************** + * 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 ); +} + diff --git a/modules/demux/mpeg/ts_sl.h b/modules/demux/mpeg/ts_sl.h new file mode 100644 index 000000000000..414bcda0cfae --- /dev/null +++ b/modules/demux/mpeg/ts_sl.h @@ -0,0 +1,29 @@ +/***************************************************************************** + * 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 diff --git a/modules/demux/mpeg/ts_streams.c b/modules/demux/mpeg/ts_streams.c new file mode 100644 index 000000000000..f6b7e438c011 --- /dev/null +++ b/modules/demux/mpeg/ts_streams.c @@ -0,0 +1,305 @@ +/***************************************************************************** + * 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 ); +} diff --git a/modules/demux/mpeg/ts_streams.h b/modules/demux/mpeg/ts_streams.h new file mode 100644 index 000000000000..5a1986c910a4 --- /dev/null +++ b/modules/demux/mpeg/ts_streams.h @@ -0,0 +1,47 @@ +/***************************************************************************** + * 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 diff --git a/modules/demux/mpeg/ts_streams_private.h b/modules/demux/mpeg/ts_streams_private.h new file mode 100644 index 000000000000..b61b01250378 --- /dev/null +++ b/modules/demux/mpeg/ts_streams_private.h @@ -0,0 +1,128 @@ +/***************************************************************************** + * 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 diff --git a/po/POTFILES.in b/po/POTFILES.in index a85ce89c1aee..394b72a8584a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -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 -- GitLab