Commit 327a8a92 authored by Gildas Bazin's avatar Gildas Bazin

* include/ninput.h: removed left-over from access2/demux2 transition.

* modules/demux/mpeg/*: removed old MPEG TS/PS demuxers.
parent bfa8fe76
/*****************************************************************************
* ninput.h
*****************************************************************************
* Copyright (C) 1999-2004 VideoLAN
* $Id$
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#ifndef _NINPUT_H
#define _NINPUT_H 1
#include "vlc_es.h"
/* Seek point */
struct seekpoint_t
{
int64_t i_byte_offset;
int64_t i_time_offset;
char *psz_name;
};
static inline seekpoint_t *vlc_seekpoint_New( void )
{
seekpoint_t *point = (seekpoint_t*)malloc( sizeof( seekpoint_t ) );
point->i_byte_offset =
point->i_time_offset = 0;
point->psz_name = NULL;
return point;
}
static inline void vlc_seekpoint_Delete( seekpoint_t *point )
{
if( !point ) return;
if( point->psz_name ) free( point->psz_name );
free( point );
}
static inline seekpoint_t *vlc_seekpoint_Duplicate( seekpoint_t *src )
{
seekpoint_t *point = vlc_seekpoint_New();
if( src->psz_name ) point->psz_name = strdup( src->psz_name );
point->i_time_offset = src->i_time_offset;
point->i_byte_offset = src->i_byte_offset;
return point;
}
typedef struct
{
char *psz_name;
vlc_bool_t b_menu; /* Is it a menu or a normal entry */
int64_t i_length; /* Length(microsecond) if known, else 0 */
int64_t i_size; /* Size (bytes) if known, else 0 */
/* Title seekpoint */
int i_seekpoint;
seekpoint_t **seekpoint;
} input_title_t;
static inline input_title_t *vlc_input_title_New( )
{
input_title_t *t = (input_title_t*)malloc( sizeof( input_title_t ) );
t->psz_name = NULL;
t->b_menu = VLC_FALSE;
t->i_length = 0;
t->i_size = 0;
t->i_seekpoint = 0;
t->seekpoint = NULL;
return t;
}
static inline void vlc_input_title_Delete( input_title_t *t )
{
int i;
if( t == NULL )
return;
if( t->psz_name ) free( t->psz_name );
for( i = 0; i < t->i_seekpoint; i++ )
{
if( t->seekpoint[i]->psz_name ) free( t->seekpoint[i]->psz_name );
free( t->seekpoint[i] );
}
if( t->seekpoint ) free( t->seekpoint );
free( t );
}
static inline input_title_t *vlc_input_title_Duplicate( input_title_t *t )
{
input_title_t *dup = vlc_input_title_New( );
int i;
if( t->psz_name ) dup->psz_name = strdup( t->psz_name );
dup->b_menu = t->b_menu;
dup->i_length = t->i_length;
dup->i_size = t->i_size;
dup->i_seekpoint = t->i_seekpoint;
if( t->i_seekpoint > 0 )
{
dup->seekpoint = (seekpoint_t**)calloc( t->i_seekpoint,
sizeof(seekpoint_t*) );
for( i = 0; i < t->i_seekpoint; i++ )
{
dup->seekpoint[i] = vlc_seekpoint_Duplicate( t->seekpoint[i] );
}
}
return dup;
}
/**
* \defgroup es out Es Out
* @{
*/
enum es_out_mode_e
{
ES_OUT_MODE_NONE, /* don't select anything */
ES_OUT_MODE_ALL, /* eg for stream output */
ES_OUT_MODE_AUTO /* best audio/video or for input follow audio-channel, spu-channel */
};
enum es_out_query_e
{
/* activate apply of mode */
ES_OUT_SET_ACTIVE, /* arg1= vlc_bool_t */
/* see if mode is currently aplied or not */
ES_OUT_GET_ACTIVE, /* arg1= vlc_bool_t* */
/* set/get mode */
ES_OUT_SET_MODE, /* arg1= int */
ES_OUT_GET_MODE, /* arg2= int* */
/* set es selected for the es category(audio/video/spu) */
ES_OUT_SET_ES, /* arg1= es_out_id_t* */
/* force selection/unselection of the ES (bypass current mode)*/
ES_OUT_SET_ES_STATE,/* arg1= es_out_id_t* arg2=vlc_bool_t */
ES_OUT_GET_ES_STATE,/* arg1= es_out_id_t* arg2=vlc_bool_t* */
/* PCR handling, by default dts/pts will be automatically computed using thoses PCR */
ES_OUT_SET_PCR, /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
ES_OUT_SET_GROUP_PCR, /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
ES_OUT_RESET_PCR, /* no arg */
};
struct es_out_t
{
es_out_id_t *(*pf_add) ( es_out_t *, es_format_t * );
int (*pf_send) ( es_out_t *, es_out_id_t *, block_t * );
void (*pf_del) ( es_out_t *, es_out_id_t * );
int (*pf_control)( es_out_t *, int i_query, va_list );
es_out_sys_t *p_sys;
};
static inline es_out_id_t * es_out_Add( es_out_t *out, es_format_t *fmt )
{
return out->pf_add( out, fmt );
}
static inline void es_out_Del( es_out_t *out, es_out_id_t *id )
{
out->pf_del( out, id );
}
static inline int es_out_Send( es_out_t *out, es_out_id_t *id,
block_t *p_block )
{
return out->pf_send( out, id, p_block );
}
static inline int es_out_vaControl( es_out_t *out, int i_query, va_list args )
{
return out->pf_control( out, i_query, args );
}
static inline int es_out_Control( es_out_t *out, int i_query, ... )
{
va_list args;
int i_result;
va_start( args, i_query );
i_result = es_out_vaControl( out, i_query, args );
va_end( args );
return i_result;
}
/**
* @}
*/
/* i_update field of access_t/demux_t */
#define INPUT_UPDATE_NONE 0x0000
#define INPUT_UPDATE_SIZE 0x0001
#define INPUT_UPDATE_TITLE 0x0010
#define INPUT_UPDATE_SEEKPOINT 0x0020
/**
* \defgroup access Access
* @{
*/
enum access_query_e
{
/* capabilities */
ACCESS_CAN_SEEK, /* arg1= vlc_bool_t* cannot fail */
ACCESS_CAN_FASTSEEK, /* arg1= vlc_bool_t* cannot fail */
ACCESS_CAN_PAUSE, /* arg1= vlc_bool_t* cannot fail */
ACCESS_CAN_CONTROL_PACE,/* arg1= vlc_bool_t* cannot fail */
/* */
ACCESS_GET_MTU, /* arg1= int* cannot fail(0 if no sense)*/
ACCESS_GET_PTS_DELAY, /* arg1= int64_t* cannot fail */
/* */
ACCESS_GET_TITLE_INFO, /* arg1=input_title_t*** arg2=int* can fail */
/* */
ACCESS_SET_PAUSE_STATE, /* arg1= vlc_bool_t can fail */
/* */
ACCESS_SET_TITLE, /* arg1= int can fail */
ACCESS_SET_SEEKPOINT, /* arg1= int can fail */
/* Special mode for access/demux communication
* XXX: avoid to use it unless you can't */
ACCESS_SET_PRIVATE_ID_STATE, /* arg1= vlc_bool_t b_selected, int i_private_data can fail */
};
struct access_t
{
VLC_COMMON_MEMBERS
/* Module properties */
module_t *p_module;
/* Access name (empty if non forced) */
char *psz_access;
/* Access path/url/filename/.... */
char *psz_path;
/* Access can fill this entry to force a demuxer
* XXX: fill it once you know for sure you will succed
* (if you fail, this value won't be reseted */
char *psz_demux;
/* pf_read/pf_block is used to read data.
* XXX A access should set one and only one of them */
int (*pf_read) ( access_t *, uint8_t *, int ); /* Return -1 if no data yet, 0 if no more data, else real data read */
block_t *(*pf_block)( access_t * ); /* return a block of data in his 'natural' size, NULL if not yet data or eof */
/* Called for each seek.
* XXX can be null */
int (*pf_seek) ( access_t *, int64_t ); /* can be null if can't seek */
/* Used to retreive and configure the access
* XXX mandatory. look at access_query_e to know what query you *have to* support */
int (*pf_control)( access_t *, int i_query, va_list args);
/* Access has to maintain them uptodate */
struct
{
unsigned int i_update; /* Access sets them on change,
Input removes them once take into account*/
int64_t i_size; /* Write only for access, read only for input */
int64_t i_pos; /* idem */
vlc_bool_t b_eof; /* idem */
int i_title; /* idem, start from 0 (could be menu) */
int i_seekpoint;/* idem, start from 0 */
} info;
access_sys_t *p_sys;
};
#define access2_New( a, b, c, d ) __access2_New(VLC_OBJECT(a), b, c, d )
VLC_EXPORT( access_t *, __access2_New, ( vlc_object_t *p_obj, char *psz_access, char *psz_demux, char *psz_path ) );
VLC_EXPORT( void, access2_Delete, ( access_t * ) );
static inline int access2_vaControl( access_t *p_access, int i_query, va_list args )
{
return p_access->pf_control( p_access, i_query, args );
}
static inline int access2_Control( access_t *p_access, int i_query, ... )
{
va_list args;
int i_result;
va_start( args, i_query );
i_result = access2_vaControl( p_access, i_query, args );
va_end( args );
return i_result;
}
/**
* @}
*/
/**
* \defgroup stream Stream
*
* This will allow you to easily handle read/seek in demuxer modules.
* @{
*/
/**
* Possible commands to send to stream_Control() and stream_vaControl()
*/
enum stream_query_e
{
/* capabilities */
STREAM_CAN_SEEK, /**< arg1= vlc_bool_t * res=cannot fail*/
STREAM_CAN_FASTSEEK, /**< arg1= vlc_bool_t * res=cannot fail*/
/* */
STREAM_SET_POSITION, /**< arg1= int64_t res=can fail */
STREAM_GET_POSITION, /**< arg1= int64_t * res=cannot fail*/
STREAM_GET_SIZE, /**< arg1= int64_t * res=cannot fail (0 if no sense)*/
STREAM_GET_MTU, /**< arg1= int * res=cannot fail (0 if no sense)*/
/* Special for direct access control from demuxer.
* XXX: avoid using it by all means */
STREAM_CONTROL_ACCESS, /* arg1= int i_access_query, args res: can fail
if access unreachable or access control answer */
};
/**
* stream_t definition
*/
struct stream_t
{
VLC_COMMON_MEMBERS
block_t *(*pf_block) ( stream_t *, int i_size );
int (*pf_read) ( stream_t *, void *p_read, int i_read );
int (*pf_peek) ( stream_t *, uint8_t **pp_peek, int i_peek );
int (*pf_control)( stream_t *, int i_query, va_list );
stream_sys_t *p_sys;
};
/**
* Try to read "i_read" bytes into a buffer pointed by "p_read". If
* "p_read" is NULL then data are skipped instead of read. The return
* value is the real numbers of bytes read/skip. If this value is less
* than i_read that means that it's the end of the stream.
*/
static inline int stream_Read( stream_t *s, void *p_read, int i_read )
{
return s->pf_read( s, p_read, i_read );
}
/**
* Store in pp_peek a pointer to the next "i_peek" bytes in the stream
* \return The real numbers of valid bytes, if it's less
* or equal to 0, *pp_peek is invalid.
* \note pp_peek is a pointer to internal buffer and it will be invalid as
* soons as other stream_* functions are called.
* \note Due to input limitation, it could be less than i_peek without meaning
* the end of the stream (but only when you have i_peek >=
* p_input->i_bufsize)
*/
static inline int stream_Peek( stream_t *s, uint8_t **pp_peek, int i_peek )
{
return s->pf_peek( s, pp_peek, i_peek );
}
/**
* Use to control the "stream_t *". Look at #stream_query_e for
* possible "i_query" value and format arguments. Return VLC_SUCCESS
* if ... succeed ;) and VLC_EGENERIC if failed or unimplemented
*/
static inline int stream_vaControl( stream_t *s, int i_query, va_list args )
{
return s->pf_control( s, i_query, args );
}
static inline int stream_Control( stream_t *s, int i_query, ... )
{
va_list args;
int i_result;
va_start( args, i_query );
i_result = s->pf_control( s, i_query, args );
va_end( args );
return i_result;
}
static inline int64_t stream_Tell( stream_t *s )
{
int64_t i_pos;
stream_Control( s, STREAM_GET_POSITION, &i_pos );
return i_pos;
}
static inline int64_t stream_Size( stream_t *s )
{
int64_t i_pos;
stream_Control( s, STREAM_GET_SIZE, &i_pos );
return i_pos;
}
static inline int stream_MTU( stream_t *s )
{
int i_mtu;
stream_Control( s, STREAM_GET_MTU, &i_mtu );
return i_mtu;
}
static inline int stream_Seek( stream_t *s, int64_t i_pos )
{
return stream_Control( s, STREAM_SET_POSITION, i_pos );
}
/**
* Read "i_size" bytes and store them in a block_t. If less than "i_size"
* bytes are available then return what is left and if nothing is availble,
* return NULL.
*/
static inline block_t *stream_Block( stream_t *s, int i_size )
{
if( i_size <= 0 ) return NULL;
if( s->pf_block )
{
return s->pf_block( s, i_size );
}
else
{
/* emulate block read */
block_t *p_bk = block_New( s, i_size );
if( p_bk )
{
p_bk->i_buffer = stream_Read( s, p_bk->p_buffer, i_size );
if( p_bk->i_buffer > 0 )
{
return p_bk;
}
}
if( p_bk ) block_Release( p_bk );
return NULL;
}
}
VLC_EXPORT( char *, stream_ReadLine, ( stream_t * ) );
/**
* Create a special stream and a demuxer, this allows chaining demuxers
*/
#define stream_DemuxNew( a, b, c ) __stream_DemuxNew( VLC_OBJECT(a), b, c)
VLC_EXPORT( stream_t *,__stream_DemuxNew, ( vlc_object_t *p_obj, char *psz_demux, es_out_t *out ) );
VLC_EXPORT( void, stream_DemuxSend, ( stream_t *s, block_t *p_block ) );
VLC_EXPORT( void, stream_DemuxDelete,( stream_t *s ) );
/**
* @}
*/
/**
* \defgroup demux Demux
* @{
*/
struct demux_t
{
VLC_COMMON_MEMBERS
/* Module properties */
module_t *p_module;
/* eg informative but needed (we can have access+demux) */
char *psz_access;
char *psz_demux;
char *psz_path;
/* input stream */
stream_t *s; /* NULL in case of a access+demux in one */
/* es output */
es_out_t *out; /* ou p_es_out */
/* set by demuxer */
int (*pf_demux) ( demux_t * ); /* demux one frame only */
int (*pf_control)( demux_t *, int i_query, va_list args);
/* Demux has to maintain them uptodate
* when it is responsible of seekpoint/title
* XXX: don't use them yet */
struct
{
unsigned int i_update; /* Demux sets them on change,
Input removes them once take into account*/
/* Seekpoint/Title at demux level */
int i_title; /* idem, start from 0 (could be menu) */
int i_seekpoint; /* idem, start from 0 */
} info;
demux_sys_t *p_sys;
};
enum demux_query_e
{
DEMUX_GET_POSITION, /* arg1= double * res= */
DEMUX_SET_POSITION, /* arg1= double res=can fail */
DEMUX_GET_LENGTH, /* arg1= int64_t * res= */
DEMUX_GET_TIME, /* arg1= int64_t * res= */
DEMUX_SET_TIME, /* arg1= int64_t res=can fail */
DEMUX_GET_FPS, /* arg1= float * res=can fail */
DEMUX_GET_META, /* arg1= vlc_meta_t ** res=can fail */
DEMUX_GET_TITLE_INFO, /* arg1=input_title_t*** arg2=int* can fail */
DEMUX_SET_TITLE, /* arg1= int can fail */
DEMUX_SET_SEEKPOINT, /* arg1= int can fail */
};
/* Demux */
VLC_EXPORT( int, demux_vaControl, ( input_thread_t *, int i_query, va_list ) );
VLC_EXPORT( int, demux_Control, ( input_thread_t *, int i_query, ... ) );
VLC_EXPORT( int, demux_vaControlDefault, ( input_thread_t *, int i_query, va_list ) );
/* Access */
VLC_EXPORT( int, access_vaControl, ( input_thread_t *, int i_query, va_list ) );
VLC_EXPORT( int, access_Control, ( input_thread_t *, int i_query, ... ) );
VLC_EXPORT( int, access_vaControlDefault,( input_thread_t *, int i_query, va_list ) );
/* stream_t *s could be null and then it mean a access+demux in one */
#define demux2_New( a, b, c, d, e, f ) __demux2_New(VLC_OBJECT(a),b,c,d,e,f)
VLC_EXPORT( demux_t *, __demux2_New, ( vlc_object_t *p_obj, char *psz_access, char *psz_demux, char *psz_path, stream_t *s, es_out_t *out ) );
VLC_EXPORT( void, demux2_Delete, ( demux_t * ) );
VLC_EXPORT( int, demux2_vaControlHelper, ( stream_t *, int64_t i_start, int64_t i_end, int i_bitrate, int i_align, int i_query, va_list args ) );
static inline int demux2_Demux( demux_t *p_demux )
{
return p_demux->pf_demux( p_demux );
}
static inline int demux2_vaControl( demux_t *p_demux, int i_query, va_list args )
{
return p_demux->pf_control( p_demux, i_query, args );
}
static inline int demux2_Control( demux_t *p_demux, int i_query, ... )
{
va_list args;
int i_result;
va_start( args, i_query );
i_result = demux2_vaControl( p_demux, i_query, args );
va_end( args );
return i_result;
}
/* Subtitles */
VLC_EXPORT( char **, subtitles_Detect, ( input_thread_t *, char* path, char *fname ) );
/**
* @}
*/
/**
* \defgroup input Input
* @{
*/
enum input_query_e
{
/* input variable "position" */
INPUT_GET_POSITION, /* arg1= double * res= */
INPUT_SET_POSITION, /* arg1= double res=can fail */
/* input variable "length" */
INPUT_GET_LENGTH, /* arg1= int64_t * res=can fail */
/* input variable "time" */
INPUT_GET_TIME, /* arg1= int64_t * res= */
INPUT_SET_TIME, /* arg1= int64_t res=can fail */
/* input variable "rate" (1 is DEFAULT_RATE) */
INPUT_GET_RATE, /* arg1= int * res= */
INPUT_SET_RATE, /* arg1= int res=can fail */
/* input variable "state" */
INPUT_GET_STATE, /* arg1= int * res= */
INPUT_SET_STATE, /* arg1= int res=can fail */
/* bookmarks */
INPUT_GET_BOOKMARKS, /* arg1= seekpoint_t *** arg2= int * res=can fail */
INPUT_CLEAR_BOOKMARKS, /* res=can fail */
INPUT_ADD_BOOKMARK, /* arg1= seekpoint_t * res=can fail */
INPUT_CHANGE_BOOKMARK, /* arg1= seekpoint_t * arg2= int * res=can fail */
INPUT_DEL_BOOKMARK, /* arg1= seekpoint_t * res=can fail */
INPUT_SET_BOOKMARK, /* arg1= int res=can fail */
INPUT_ADD_OPTION, /* arg1= char * arg2= char * res=can fail */
/* */
INPUT_ADD_INFO, /* arg1= char * arg2= char * arg3=... res=can fail */
INPUT_GET_INFO, /* arg1= char * arg2= char * arg3= char ** res=can fail*/
INPUT_SET_NAME, /* arg1= char * res=can fail */
/* */
INPUT_GET_SUBDELAY, /* arg1 = int* res=can fail */
INPUT_SET_SUBDELAY, /* arg1 = int res=can fail */
};
VLC_EXPORT( int, input_vaControl,( input_thread_t *, int i_query, va_list ) );
VLC_EXPORT( int, input_Control, ( input_thread_t *, int i_query, ... ) );
/**
* @}
*/
#endif
SOURCES_mpeg_system = system.c system.h
SOURCES_m4v = m4v.c
SOURCES_ps_old = ps.c
SOURCES_ts_old = ts.c
SOURCES_ts_old_dvbpsi = ts.c
SOURCES_mpga = mpga.c
SOURCES_mpgv = mpgv.c
SOURCES_h264 = h264.c
/*****************************************************************************
* ps.c : Program Stream input module for vlc
*****************************************************************************
* Copyright (C) 2000-2004, 2004 VideoLAN
* $Id$
*
* Author: Christophe Massiot <massiot@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h> /* malloc(), free() */
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "system.h"
/*****************************************************************************
* Constants
*****************************************************************************/
#define PS_READ_ONCE 50
/*****************************************************************************
* Private structure
*****************************************************************************/
struct demux_sys_t
{
module_t * p_module;
mpeg_demux_t mpeg;
};
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Activate ( vlc_object_t * );
static void Deactivate ( vlc_object_t * );
static int Demux ( input_thread_t * );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("ISO 13818-1 MPEG Program Stream input") );
set_capability( "demux", 1 );
set_callbacks( Activate, Deactivate );
add_shortcut( "ps" );
vlc_module_end();
/*****************************************************************************
* Activate: initialize PS structures
*****************************************************************************/
static int Activate( vlc_object_t * p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
demux_sys_t * p_demux;
byte_t * p_peek;
/* Set the demux function */
p_input->pf_demux = Demux;
p_input->pf_demux_control = demux_vaControlDefault;
/* Initialize access plug-in structures. */
if( p_input->i_mtu == 0 )
{
/* Improve speed. */
p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
}
/* Have a peep at the show. */
if( input_Peek( p_input, &p_peek, 4 ) < 4 )
{
/* Stream shorter than 4 bytes... */
msg_Err( p_input, "cannot peek()" );
return -1;
}
if( *p_peek || *(p_peek + 1) || *(p_peek + 2) != 1 )
{
if( *p_input->psz_demux && !strncmp( p_input->psz_demux, "ps", 3 ) )
{
/* User forced */
msg_Warn( p_input, "this does not look like an MPEG PS stream, continuing" );
}
else
{
msg_Warn( p_input, "this does not look like an MPEG PS stream, "
"but continuing anyway" );
}
}
else if( *(p_peek + 3) <= 0xb9 )
{
if( *p_input->psz_demux && !strncmp( p_input->psz_demux, "ps", 3 ) )
{
/* User forced */
msg_Err( p_input, "this seems to be an elementary stream (ES module?), but continuing" );
}
else
{
msg_Warn( p_input, "this seems to be an elementary stream (ES module?), but continuing" );
}
}
p_demux = p_input->p_demux_data = malloc( sizeof(demux_sys_t ) );
if( p_demux == NULL )
{
return -1;
}
p_input->p_private = (void*)&p_demux->mpeg;
p_demux->p_module = module_Need( p_input, "mpeg-system", NULL, 0 );
if( p_demux->p_module == NULL )
{
free( p_input->p_demux_data );
return -1;
}
if( input_InitStream( p_input, sizeof( stream_ps_data_t ) ) == -1 )
{
module_Unneed( p_input, p_demux->p_module );
free( p_input->p_demux_data );
return -1;
}
input_AddProgram( p_input, 0, sizeof( stream_ps_data_t ) );
p_input->stream.p_selected_program =
p_input->stream.pp_programs[0] ;
if( p_input->stream.b_seekable )
{
stream_ps_data_t * p_demux_data =
(stream_ps_data_t *)p_input->stream.pp_programs[0]->p_demux_data;
off_t i_tell = p_input->stream.p_selected_area->i_tell;
/* Pre-parse the stream to gather stream_descriptor_t. */
p_input->pf_seek( p_input, (off_t) 0 );
p_input->stream.pp_programs[0]->b_is_ok = 0;
p_demux_data->i_PSM_version = EMPTY_PSM_VERSION;
while( !p_input->b_die && !p_input->b_error
&& !p_demux_data->b_has_PSM )
{
ssize_t i_result;
data_packet_t * p_data;
i_result = p_demux->mpeg.pf_read_ps( p_input, &p_data );
if( i_result == 0 )
{
/* EOF */
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.pp_programs[0]->b_is_ok = 1;
vlc_mutex_unlock( &p_input->stream.stream_lock );
break;
}
else if( i_result == -1 )
{
p_input->b_error = 1;
break;
}
p_demux->mpeg.pf_parse_ps( p_input, p_data );
input_DeletePacket( p_input->p_method_data, p_data );
/* File too big. */
if( p_input->stream.p_selected_area->i_tell >
INPUT_PREPARSE_LENGTH )
{
break;
}
}
input_AccessReinit( p_input );
p_input->pf_seek( p_input, i_tell );
vlc_mutex_lock( &p_input->stream.stream_lock );
if( p_demux_data->b_has_PSM )
{
/* (The PSM decoder will care about spawning the decoders) */
p_input->stream.pp_programs[0]->b_is_ok = 1;
}
#ifdef AUTO_SPAWN
else
{
/* (We have to do it ourselves) */
unsigned int i_es;
/* FIXME: we should do multiple passes in case an audio type
* is not present */
for( i_es = 0;
i_es < p_input->stream.pp_programs[0]->i_es_number;
i_es++ )
{
#define p_es p_input->stream.pp_programs[0]->pp_es[i_es]
switch( p_es->i_fourcc )
{
case VLC_FOURCC('m','p','g','v'):
input_SelectES( p_input, p_es );
break;
case VLC_FOURCC('m','p','g','a'):
if( config_GetInt( p_input, "audio-channel" )
== (p_es->i_id & 0x1F) ||
( config_GetInt( p_input, "audio-channel" ) < 0
&& !(p_es->i_id & 0x1F) ) )
switch( config_GetInt( p_input, "audio-type" ) )
{
case -1:
case REQUESTED_MPEG:
input_SelectES( p_input, p_es );
}
break;
case VLC_FOURCC('d','t','s',' '):
case VLC_FOURCC('d','t','s','b'):
if( config_GetInt( p_input, "audio-channel" )
== ((p_es->i_id & 0x700) >> 8) ||
( config_GetInt( p_input, "audio-channel" ) < 0
&& !((p_es->i_id & 0x700) >> 8) ) )
switch( config_GetInt( p_input, "audio-type" ) )
{
case -1:
case REQUESTED_DTS:
input_SelectES( p_input, p_es );
}
break;
case VLC_FOURCC('a','5','2',' '):
case VLC_FOURCC('a','5','2','b'):
if( config_GetInt( p_input, "audio-channel" )
== ((p_es->i_id & 0xF00) >> 8) ||
( config_GetInt( p_input, "audio-channel" ) < 0
&& !((p_es->i_id & 0xF00) >> 8) ) )
switch( config_GetInt( p_input, "audio-type" ) )
{
case -1:
case REQUESTED_A52:
input_SelectES( p_input, p_es );
}
break;
case VLC_FOURCC('s','p','u',' '):
case VLC_FOURCC('s','p','u','b'):
if( config_GetInt( p_input, "spu-channel" )
== ((p_es->i_id & 0x1F00) >> 8) )
{
input_SelectES( p_input, p_es );
}
break;
case VLC_FOURCC('o','g','t',' '):
if( config_GetInt( p_input, "spu-channel" )
== (p_es->i_id & 0x0003) )
{
input_SelectES( p_input, p_es );
}
break;
case VLC_FOURCC('c','v','d',' '):
if( config_GetInt( p_input, "spu-channel" )
== (p_es->i_id & 0x0003) )
{
input_SelectES( p_input, p_es );
}
break;
case VLC_FOURCC('l','p','c','m'):
case VLC_FOURCC('l','p','c','b'):
if( config_GetInt( p_input, "audio-channel" )
== ((p_es->i_id & 0x1F00) >> 8) ||
( config_GetInt( p_input, "audio-channel" ) < 0
&& !((p_es->i_id & 0x1F00) >> 8) ) )
switch( config_GetInt( p_input, "audio-type" ) )
{
case -1:
case REQUESTED_LPCM:
input_SelectES( p_input, p_es );
}
break;
}
#undef p_es
}
}
#endif
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
else
{
/* The programs will be added when we read them. */
vlc_mutex_lock( &p_input->stream.stream_lock );
p_input->stream.pp_programs[0]->b_is_ok = 0;
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
return 0;
}
/*****************************************************************************
* Deactivate: deinitialize PS structures
*****************************************************************************/
static void Deactivate( vlc_object_t * p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
module_Unneed( p_input, p_input->p_demux_data->p_module );
free( p_input->p_demux_data );
}
/*****************************************************************************
* Demux: reads and demuxes data packets
*****************************************************************************
* Returns -1 in case of error, 0 in case of EOF, otherwise the number of
* packets.
*****************************************************************************/
static int Demux( input_thread_t * p_input )
{
int i;
for( i = 0; i < PS_READ_ONCE; i++ )
{
data_packet_t * p_data;
ssize_t i_result;
i_result = p_input->p_demux_data->mpeg.pf_read_ps( p_input, &p_data );
if( i_result <= 0 )
{
return i_result;
}
p_input->p_demux_data->mpeg.pf_demux_ps( p_input, p_data );
}
return i;
}
/*****************************************************************************
* system.c: helper module for TS, PS and PES management
*****************************************************************************
* Copyright (C) 1998-2004 VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Michel Lespinasse <walken@via.ecp.fr>
* Benot Steiner <benny@via.ecp.fr>
* Sam Hocevar <sam@zoy.org>
* Henri Fallon <henri@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "system.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Activate ( vlc_object_t * );
static ssize_t ReadPS ( input_thread_t *, data_packet_t ** );
static es_descriptor_t * ParsePS ( input_thread_t *, data_packet_t * );
static void DemuxPS ( input_thread_t *, data_packet_t * );
static ssize_t ReadTS ( input_thread_t *, data_packet_t ** );
static void DemuxTS ( input_thread_t *, data_packet_t *,
psi_callback_t );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("Generic ISO 13818-1 MPEG demultiplexing") );
set_capability( "mpeg-system", 100 );
set_callbacks( Activate, NULL );
vlc_module_end();
/*****************************************************************************
* Activate: initializes helper functions
*****************************************************************************/
static int Activate ( vlc_object_t *p_this )
{
static mpeg_demux_t mpeg_demux =
{ NULL, ReadPS, ParsePS, DemuxPS, ReadTS, DemuxTS };
mpeg_demux.cur_scr_time = -1;
memcpy( p_this->p_private, &mpeg_demux, sizeof( mpeg_demux ) );
return VLC_SUCCESS;
}
/*
* PES Packet management
*/
/*****************************************************************************
* MoveChunk
*****************************************************************************
* Small utility function used to parse discontinuous headers safely. Copies
* i_buf_len bytes of data to a buffer and returns the size copied.
* It also solves some alignment problems on non-IA-32, non-PPC processors.
* This is a variation on the theme of input_ext-dec.h:GetChunk().
*****************************************************************************/
static inline size_t MoveChunk( byte_t * p_dest, data_packet_t ** pp_data_src,
byte_t ** pp_src, size_t i_buf_len )
{
size_t i_available;
i_available = (ptrdiff_t)((*pp_data_src)->p_payload_end - *pp_src);
if( i_available >= i_buf_len )
{
if( p_dest != NULL )
memcpy( p_dest, *pp_src, i_buf_len );
*pp_src += i_buf_len;
return( i_buf_len );
}
else
{
size_t i_init_len = i_buf_len;
do
{
if( p_dest != NULL )
memcpy( p_dest, *pp_src, i_available );
*pp_data_src = (*pp_data_src)->p_next;
i_buf_len -= i_available;
p_dest += i_available;
if( *pp_data_src == NULL )
{
*pp_src = NULL;
return( i_init_len - i_buf_len );
}
*pp_src = (*pp_data_src)->p_payload_start;
i_available = (ptrdiff_t)((*pp_data_src)->p_payload_end - *pp_src);
}
while( i_available <= i_buf_len );
if( i_buf_len )
{
if( p_dest != NULL )
memcpy( p_dest, *pp_src, i_buf_len );
*pp_src += i_buf_len;
}
return( i_init_len );
}
}
/*****************************************************************************
* ParsePES
*****************************************************************************
* Parse a finished PES packet and analyze its header.
*****************************************************************************/
#define PES_HEADER_SIZE 7
static void ParsePES( input_thread_t * p_input, es_descriptor_t * p_es )
{
data_packet_t * p_data;
byte_t * p_byte;
byte_t p_header[PES_HEADER_SIZE];
int i_done;
#define p_pes (p_es->p_pes)
/* Parse the header. The header has a variable length, but in order
* to improve the algorithm, we will read the 14 bytes we may be
* interested in */
p_data = p_pes->p_first;
p_byte = p_data->p_payload_start;
i_done = 0;
if( MoveChunk( p_header, &p_data, &p_byte, PES_HEADER_SIZE )
!= PES_HEADER_SIZE )
{
msg_Warn( p_input, "PES packet too short to have a header" );
input_DeletePES( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
}
/* Get the PES size if defined */
p_es->i_pes_real_size = U16_AT(p_header + 4);
if( p_es->i_pes_real_size )
{
p_es->i_pes_real_size += 6;
}
/* First read the 6 header bytes common to all PES packets:
* use them to test the PES validity */
if( (p_header[0] || p_header[1] || (p_header[2] != 1)) )
{
/* packet_start_code_prefix != 0x000001 */
msg_Warn( p_input, "data loss, PES packet does not start with 000001" );
input_DeletePES( p_input->p_method_data, p_pes );
p_pes = NULL;
}
else
{
unsigned int i_pes_header_size, i_payload_size;
if ( p_es->i_pes_real_size &&
(p_es->i_pes_real_size != p_pes->i_pes_size) )
{
/* PES_packet_length is set and != total received payload */
/* Warn the decoder that the data may be corrupt. */
msg_Warn( p_input, "packet corrupted, PES sizes do not match" );
}
switch( p_header[3] )
{
case 0xBC: /* Program stream map */
case 0xBE: /* Padding */
case 0xBF: /* Private stream 2 */
case 0xB0: /* ECM */
case 0xB1: /* EMM */
case 0xFF: /* Program stream directory */
case 0xF2: /* DSMCC stream */
case 0xF8: /* ITU-T H.222.1 type E stream */
/* The payload begins immediately after the 6 bytes header, so
* we have finished with the parsing */
i_pes_header_size = 6;
break;
default:
if( (p_header[6] & 0xC0) == 0x80 )
{
/* MPEG-2 : the PES header contains at least 3 more bytes. */
size_t i_max_len;
vlc_bool_t b_has_pts, b_has_dts;
byte_t p_full_header[12];
p_pes->b_data_alignment = p_header[6] & 0x04;
i_max_len = MoveChunk( p_full_header, &p_data, &p_byte, 12 );
if( i_max_len < 2 )
{
msg_Warn( p_input,
"PES packet too short to have a MPEG-2 header" );
input_DeletePES( p_input->p_method_data,
p_pes );
p_pes = NULL;
return;
}
b_has_pts = p_full_header[0] & 0x80;
b_has_dts = p_full_header[0] & 0x40;
i_pes_header_size = p_full_header[1] + 9;
/* Now parse the optional header extensions */
if( b_has_pts )
{
if( i_max_len < 7 )
{
msg_Warn( p_input,
"PES packet too short to have a MPEG-2 header" );
input_DeletePES( p_input->p_method_data,
p_pes );
p_pes = NULL;
return;
}
p_pes->i_pts = input_ClockGetTS( p_input, p_es->p_pgrm,
( ((mtime_t)(p_full_header[2] & 0x0E) << 29) |
((mtime_t)(p_full_header[3]) << 22) |
((mtime_t)(p_full_header[4] & 0xFE) << 14) |
((mtime_t)p_full_header[5] << 7) |
((mtime_t)p_full_header[6] >> 1) ) );
if( b_has_dts )
{
if( i_max_len < 12 )
{
msg_Warn( p_input,
"PES packet too short to have a MPEG-2 header" );
input_DeletePES( p_input->p_method_data,
p_pes );
p_pes = NULL;
return;
}
p_pes->i_dts = input_ClockGetTS( p_input, p_es->p_pgrm,
( ((mtime_t)(p_full_header[7] & 0x0E) << 29) |
(((mtime_t)U16_AT(p_full_header + 8) << 14)
- (1 << 14)) |
((mtime_t)U16_AT(p_full_header + 10) >> 1) ) );
}
}
}
else
{
/* Probably MPEG-1 */
vlc_bool_t b_has_pts, b_has_dts;
i_pes_header_size = 6;
p_data = p_pes->p_first;
p_byte = p_data->p_payload_start;
/* Cannot fail because the previous one succeeded. */
MoveChunk( NULL, &p_data, &p_byte, 6 );
while( *p_byte == 0xFF && i_pes_header_size < 23 )
{
i_pes_header_size++;
if( MoveChunk( NULL, &p_data, &p_byte, 1 ) != 1 )
{
msg_Warn( p_input,
"PES packet too short to have a MPEG-1 header" );
input_DeletePES( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
}
}
if( i_pes_header_size == 23 )
{
msg_Warn( p_input, "too much MPEG-1 stuffing" );
input_DeletePES( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
}
if( (*p_byte & 0xC0) == 0x40 )
{
/* Don't ask why... --Meuuh */
/* Erm... why ? --Sam */
/* Well... According to the recommendation, it is for
* STD_buffer_scale and STD_buffer_size. --Meuuh */
i_pes_header_size += 2;
if( MoveChunk( NULL, &p_data, &p_byte, 2 ) != 2 )
{
msg_Warn( p_input,
"PES packet too short to have a MPEG-1 header" );
input_DeletePES( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
}
}
i_pes_header_size++;
b_has_pts = *p_byte & 0x20;
b_has_dts = *p_byte & 0x10;
if( b_has_pts )
{
byte_t p_ts[5];
i_pes_header_size += 4;
if( MoveChunk( p_ts, &p_data, &p_byte, 5 ) != 5 )
{
msg_Warn( p_input,
"PES packet too short to have a MPEG-1 header" );
input_DeletePES( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
}
p_pes->i_pts = input_ClockGetTS( p_input, p_es->p_pgrm,
( ((mtime_t)(p_ts[0] & 0x0E) << 29) |
(((mtime_t)U32_AT(p_ts) & 0xFFFE00) << 6) |
((mtime_t)p_ts[3] << 7) |
((mtime_t)p_ts[4] >> 1) ) );
if( b_has_dts )
{
i_pes_header_size += 5;
if( MoveChunk( p_ts, &p_data, &p_byte, 5 ) != 5 )
{
msg_Warn( p_input,
"PES packet too short to have a MPEG-1 header" );
input_DeletePES( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
}
p_pes->i_dts = input_ClockGetTS( p_input,
p_es->p_pgrm,
( ((mtime_t)(p_ts[0] & 0x0E) << 29) |
(((mtime_t)U32_AT(p_ts) & 0xFFFE00) << 6) |
((mtime_t)p_ts[3] << 7) |
((mtime_t)p_ts[4] >> 1) ) );
}
}
}
break;
}
/* Welcome to the kludge area ! --Meuuh */
if ( p_es->i_fourcc == VLC_FOURCC('a','5','2','b')
|| p_es->i_fourcc == VLC_FOURCC('d','t','s','b') )
{
/* With A/52 or DTS audio, we need to skip the first 4 bytes */
i_pes_header_size += 4;
}
if ( p_es->i_fourcc == VLC_FOURCC('l','p','c','b')
|| p_es->i_fourcc == VLC_FOURCC('s','p','u','b')
|| p_es->i_fourcc == VLC_FOURCC('s','d','d','b') )
{
/* stream_private_id */
i_pes_header_size += 1;
}
/* Now we've parsed the header, we just have to indicate in some
* specific data packets where the PES payload begins (renumber
* p_payload_start), so that the decoders can find the beginning
* of their data right out of the box. */
p_data = p_pes->p_first;
i_payload_size = p_data->p_payload_end
- p_data->p_payload_start;
while( i_pes_header_size > i_payload_size )
{
/* These packets are entirely filled by the PES header. */
i_pes_header_size -= i_payload_size;
p_data->p_payload_start = p_data->p_payload_end;
/* Go to the next data packet. */
if( (p_data = p_data->p_next) == NULL )
{
msg_Warn( p_input, "PES header bigger than payload" );
input_DeletePES( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
}
i_payload_size = p_data->p_payload_end
- p_data->p_payload_start;
}
/* This last packet is partly header, partly payload. */
if( i_payload_size < i_pes_header_size )
{
msg_Warn( p_input, "PES header bigger than payload" );
input_DeletePES( p_input->p_method_data, p_pes );
p_pes = NULL;
return;
}
p_data->p_payload_start += i_pes_header_size;
/* Now we can eventually put the PES packet in the decoder's
* PES fifo */
if( p_es->p_dec != NULL )
{
input_DecodePES( p_es->p_dec, p_pes );
}
else if ( p_es->p_dec == NULL &&
((es_ts_data_t*)p_es->p_demux_data)->b_dvbsub)
{
es_descriptor_t* p_dvbsub;
uint8_t i_count;
uint8_t i;
i_count = ((es_ts_data_t*)p_es->p_demux_data)->i_dvbsub_es_count;
// If a language is selected, we send the packet to the decoder
for(i = 0; i < i_count; i++)
{
p_dvbsub = ((es_ts_data_t*)p_es->p_demux_data)->p_dvbsub_es[i];
if(p_dvbsub->p_dec !=NULL)
{
input_DecodePES ( p_dvbsub->p_dec, p_pes );
break;
}
}
if(i == i_count)
{
input_DeletePES( p_input->p_method_data, p_pes );
}
}
else
{
msg_Err( p_input, "no fifo to receive PES %p "
"(who wrote this damn code ?)", p_pes );
input_DeletePES( p_input->p_method_data, p_pes );
}
p_pes = NULL;
}
#undef p_pes
}
/*****************************************************************************
* GatherPES:
*****************************************************************************
* Gather a PES packet.
*****************************************************************************/
static void GatherPES( input_thread_t * p_input, data_packet_t * p_data,
es_descriptor_t * p_es,
vlc_bool_t b_unit_start, vlc_bool_t b_packet_lost )
{
#define p_pes (p_es->p_pes)
if( b_packet_lost && p_pes != NULL )
{
p_pes->b_discontinuity = 1;
}
if( b_unit_start && p_pes != NULL )
{
/* If the data packet contains the begining of a new PES packet, and
* if we were reassembling a PES packet, then the PES should be
* complete now, so parse its header and give it to the decoders. */
ParsePES( p_input, p_es );
}
if( !b_unit_start && p_pes == NULL )
{
/* Random access... */
input_DeletePacket( p_input->p_method_data, p_data );
}
else
{
if( b_unit_start )
{
/* If we are at the beginning of a new PES packet, we must fetch
* a new PES buffer to begin with the reassembly of this PES
* packet. This is also here that we can synchronize with the
* stream if we lost packets or if the decoder has just
* started. */
if( (p_pes = input_NewPES( p_input->p_method_data ) ) == NULL )
{
msg_Err( p_input, "out of memory" );
p_input->b_error = 1;
return;
}
p_pes->i_rate = p_input->stream.control.i_rate;
p_pes->p_first = p_data;
/* If the PES header fits in the first data packet, we can
* already set p_gather->i_pes_real_size. */
if( p_data->p_payload_end - p_data->p_payload_start
>= PES_HEADER_SIZE )
{
p_es->i_pes_real_size = ((uint16_t)p_data->p_payload_start[4] << 8)
+ p_data->p_payload_start[5];
if ( p_es->i_pes_real_size )
{
p_es->i_pes_real_size += 6;
}
}
else
{
p_es->i_pes_real_size = 0;
}
}
else
{
/* Update the relations between the data packets */
p_pes->p_last->p_next = p_data;
}
p_pes->p_last = p_data;
p_pes->i_nb_data++;
/* Size of the payload carried in the data packet */
p_pes->i_pes_size += (p_data->p_payload_end
- p_data->p_payload_start);
/* We can check if the packet is finished */
if( p_es->i_pes_real_size && p_pes->i_pes_size >= p_es->i_pes_real_size )
{
if( p_pes->i_pes_size > p_es->i_pes_real_size )
{
msg_Warn( p_input,
"Oversized PES packet for PID %d: expected %d, actual %d",
p_es->i_id, p_es->i_pes_real_size, p_pes->i_pes_size );
}
/* The packet is finished, parse it */
ParsePES( p_input, p_es );
}
}
#undef p_pes
}
/*
* PS Demultiplexing
*/
/*****************************************************************************
* GetID: Get the ID of a stream
*****************************************************************************/
static uint16_t GetID( input_thread_t *p_input, data_packet_t * p_data )
{
uint16_t i_id;
i_id = p_data->p_demux_start[3]; /* stream_id */
if( i_id == 0xBD )
{
mpeg_demux_t *p_mpeg_demux = (mpeg_demux_t *)p_input->p_private;
/* FIXME : this is not valid if the header is split in multiple
* packets */
/* stream_private_id */
if ( p_data->p_demux_start[ 9 + p_data->p_demux_start[8] ] == 0x70 ) {
/* SVCD/OGT ES: 0x70 = private stream. We pick out the subtitle
number this way though. */
i_id = 0x7000 | p_data->p_demux_start[10 + p_data->p_demux_start[8]];
} else {
i_id |= p_data->p_demux_start[ 9 + p_data->p_demux_start[8] ] << 8;
}
/* FIXME: See note about cur_scr_time above. */
p_mpeg_demux->cur_scr_time = -1;
}
return( i_id );
}
/*****************************************************************************
* DecodePSM: Decode the Program Stream Map information
*****************************************************************************
* FIXME : loads are not aligned in this function
*****************************************************************************/
static void DecodePSM( input_thread_t * p_input, data_packet_t * p_data )
{
stream_ps_data_t * p_demux =
(stream_ps_data_t *)p_input->stream.p_demux_data;
byte_t * p_byte;
byte_t * p_end;
unsigned int i;
unsigned int i_new_es_number = 0;
if( p_data->p_demux_start + 10 > p_data->p_payload_end )
{
msg_Err( p_input, "PSM too short, packet corrupt" );
return;
}
if( p_demux->b_has_PSM
&& p_demux->i_PSM_version == (p_data->p_demux_start[6] & 0x1F) )
{
/* Already got that one. */
return;
}
p_demux->b_has_PSM = 1;
p_demux->i_PSM_version = p_data->p_demux_start[6] & 0x1F;
/* Go to elementary_stream_map_length, jumping over
* program_stream_info. */
p_byte = p_data->p_demux_start + 10
+ U16_AT(&p_data->p_demux_start[8]);
if( p_byte > p_data->p_payload_end )
{
msg_Err( p_input, "PSM too short, packet corrupt" );
return;
}
/* This is the full size of the elementary_stream_map.
* 2 == elementary_stream_map_length
* Please note that CRC_32 is not included in the length. */
p_end = p_byte + 2 + U16_AT(p_byte);
p_byte += 2;
if( p_end > p_data->p_payload_end )
{
msg_Err( p_input, "PSM too short, packet corrupt" );
return;
}
vlc_mutex_lock( &p_input->stream.stream_lock );
/* 4 == minimum useful size of a section */
while( p_byte + 4 <= p_end )
{
es_descriptor_t * p_es = NULL;
uint8_t i_stream_id = p_byte[1];
/* FIXME: there will be a problem with private streams... (same
* stream_id) */
/* Look for the ES in the ES table */
for( i = i_new_es_number;
i < p_input->stream.pp_programs[0]->i_es_number;
i++ )
{
if( p_input->stream.pp_programs[0]->pp_es[i]->i_stream_id
== i_stream_id )
{
p_es = p_input->stream.pp_programs[0]->pp_es[i];
/* FIXME: do something below */
#if 0
if( p_es->i_type != p_byte[0] )
{
input_DelES( p_input, p_es );
p_es = NULL;
}
else
#endif
{
/* Move the ES to the beginning. */
p_input->stream.pp_programs[0]->pp_es[i]
= p_input->stream.pp_programs[0]->pp_es[ i_new_es_number ];
p_input->stream.pp_programs[0]->pp_es[ i_new_es_number ]
= p_es;
i_new_es_number++;
}
break;
}
}
/* The goal is to have all the ES we have just read in the
* beginning of the pp_es table, and all the others at the end,
* so that we can close them more easily at the end. */
if( p_es == NULL )
{
int i_fourcc, i_cat;
switch( p_byte[0] )
{
case MPEG1_VIDEO_ES:
case MPEG2_VIDEO_ES:
case MPEG2_MOTO_VIDEO_ES:
i_fourcc = VLC_FOURCC('m','p','g','v');
i_cat = VIDEO_ES;
break;
case DVD_SPU_ES:
i_fourcc = VLC_FOURCC('s','p','u','b');
i_cat = SPU_ES;
break;
case MPEG1_AUDIO_ES:
case MPEG2_AUDIO_ES:
i_fourcc = VLC_FOURCC('m','p','g','a');
i_cat = AUDIO_ES;
break;
case A52_AUDIO_ES:
i_fourcc = VLC_FOURCC('a','5','2','b');
i_cat = AUDIO_ES;
break;
case LPCM_AUDIO_ES:
i_fourcc = VLC_FOURCC('l','p','c','b');
i_cat = AUDIO_ES;
break;
default:
i_cat = UNKNOWN_ES;
i_fourcc = 0;
break;
}
p_es = input_AddES( p_input, p_input->stream.pp_programs[0],
i_stream_id, i_cat, NULL, 0 );
p_es->i_fourcc = i_fourcc;
/* input_AddES has inserted the new element at the end. */
p_input->stream.pp_programs[0]->pp_es[
p_input->stream.pp_programs[0]->i_es_number ]
= p_input->stream.pp_programs[0]->pp_es[ i_new_es_number ];
p_input->stream.pp_programs[0]->pp_es[ i_new_es_number ] = p_es;
i_new_es_number++;
}
p_byte += 4 + U16_AT(&p_byte[2]);
}
/* Un-select the streams that are no longer parts of the program. */
while( i_new_es_number < p_input->stream.pp_programs[0]->i_es_number )
{
/* We remove pp_es[i_new_es_member] and not pp_es[i] because the
* list will be emptied starting from the end */
input_DelES( p_input,
p_input->stream.pp_programs[0]->pp_es[i_new_es_number] );
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
/*****************************************************************************
* ReadPS: store a PS packet into a data_buffer_t
*****************************************************************************/
#define PEEK( SIZE ) \
i_error = input_Peek( p_input, &p_peek, SIZE ); \
if( i_error == -1 ) \
{ \
return( -1 ); \
} \
else if( (size_t)i_error < SIZE ) \
{ \
/* EOF */ \
return( 0 ); \
}
static ssize_t ReadPS( input_thread_t * p_input, data_packet_t ** pp_data )
{
byte_t * p_peek;
size_t i_packet_size;
ssize_t i_error, i_read;
/* Read what we believe to be a packet header. */
PEEK( 4 );
if( p_peek[0] || p_peek[1] || p_peek[2] != 1 || p_peek[3] < 0xB9 )
{
if( p_peek[0] || p_peek[1] || p_peek[2] )
{
/* It is common for MPEG-1 streams to pad with zeros
* (although it is forbidden by the recommendation), so
* don't bother everybody in this case. */
msg_Warn( p_input, "garbage (0x%.2x%.2x%.2x%.2x)",
p_peek[0], p_peek[1], p_peek[2], p_peek[3] );
}
/* This is not the startcode of a packet. Read the stream
* until we find one. */
while( p_peek[0] || p_peek[1] || p_peek[2] != 1 || p_peek[3] < 0xB9 )
{
p_input->p_current_data++;
PEEK( 4 );
if( p_input->b_die ) return( -1 );
}
/* Packet found. */
}
/* 0x1B9 == SYSTEM_END_CODE, it is only 4 bytes long. */
if( p_peek[3] != 0xB9 )
{
/* The packet is at least 6 bytes long. */
PEEK( 6 );
if( p_peek[3] != 0xBA )
{
/* That's the case for all packets, except pack header. */
i_packet_size = (p_peek[4] << 8) | p_peek[5];
}
else
{
/* Pack header. */
if( (p_peek[4] & 0xC0) == 0x40 )
{
/* MPEG-2 */
i_packet_size = 8;
}
else if( (p_peek[4] & 0xF0) == 0x20 )
{
/* MPEG-1 */
i_packet_size = 6;
}
else
{
msg_Err( p_input, "unable to determine stream type" );
p_input->p_current_data++;
return( -1 );
}
}
}
else
{
/* System End Code */
i_packet_size = -2;
}
/* Fetch a packet of the appropriate size. */
i_read = input_SplitBuffer( p_input, pp_data, i_packet_size + 6 );
if( i_read <= 0 )
{
return( i_read );
}
/* In MPEG-2 pack headers we still have to read stuffing bytes. */
if( ((*pp_data)->p_demux_start[3] == 0xBA) && (i_packet_size == 8) )
{
size_t i_stuffing = ((*pp_data)->p_demux_start[13] & 0x7);
/* Force refill of the input buffer - though we don't care
* about p_peek. Please note that this is unoptimized. */
PEEK( i_stuffing );
p_input->p_current_data += i_stuffing;
}
return( 1 );
}
#undef PEEK
/*****************************************************************************
* ParsePS: read the PS header
*****************************************************************************/
static es_descriptor_t * ParsePS( input_thread_t * p_input,
data_packet_t * p_data )
{
uint32_t i_code;
es_descriptor_t * p_es = NULL;
i_code = p_data->p_demux_start[3];
if( i_code > 0xBC ) /* ES start code */
{
uint16_t i_id;
unsigned int i_dummy;
/* This is a PES packet. Find out if we want it or not. */
i_id = GetID( p_input, p_data );
vlc_mutex_lock( &p_input->stream.stream_lock );
if( p_input->stream.pp_programs[0]->b_is_ok )
{
/* Look only at the selected ES. */
for( i_dummy = 0; i_dummy < p_input->stream.i_selected_es_number;
i_dummy++ )
{
if( p_input->stream.pp_selected_es[i_dummy] != NULL
&& p_input->stream.pp_selected_es[i_dummy]->i_id == i_id )
{
p_es = p_input->stream.pp_selected_es[i_dummy];
break;
}
}
}
else
{
vlc_bool_t b_auto_spawn = VLC_FALSE;
stream_ps_data_t * p_demux =
(stream_ps_data_t *)p_input->stream.pp_programs[0]->p_demux_data;
/* Search all ES ; if not found -> AddES */
p_es = input_FindES( p_input, i_id );
if( p_es == NULL && !p_demux->b_has_PSM )
{
int i_fourcc, i_cat;
char *psz_desc = NULL;
/* Set stream type and auto-spawn. */
if( (i_id & 0xF0) == 0xE0 )
{
/* MPEG video */
i_fourcc = VLC_FOURCC('m','p','g','v');
i_cat = VIDEO_ES;
#ifdef AUTO_SPAWN
if( !p_input->stream.b_seekable ) b_auto_spawn = VLC_TRUE;
#endif
}
else if( (i_id & 0xE0) == 0xC0 )
{
/* MPEG audio */
i_fourcc = VLC_FOURCC('m','p','g','a');
i_cat = AUDIO_ES;
#ifdef AUTO_SPAWN
if( !p_input->stream.b_seekable )
if( config_GetInt( p_input, "audio-channel" )
== (i_id & 0x1F) ||
( config_GetInt( p_input, "audio-channel" ) < 0
&& !(i_id & 0x1F) ) )
switch( config_GetInt( p_input, "audio-type" ) )
{
case -1:
case REQUESTED_MPEG:
b_auto_spawn = VLC_TRUE;
}
#endif
}
else if( (i_id & 0xF8FF) == 0x88BD )
{
i_fourcc = VLC_FOURCC('d','t','s','b');
i_cat = AUDIO_ES;
#ifdef AUTO_SPAWN
if( !p_input->stream.b_seekable )
if( config_GetInt( p_input, "audio-channel" )
== ((i_id & 0x700) >> 8) ||
( config_GetInt( p_input, "audio-channel" ) < 0
&& !((i_id & 0x700) >> 8)) )
switch( config_GetInt( p_input, "audio-type" ) )
{
case -1:
case REQUESTED_DTS:
b_auto_spawn = VLC_TRUE;
}
#endif
}
else if( (i_id & 0xF0FF) == 0x80BD )
{
/* A52 audio (0x80->0x8F) */
i_fourcc = VLC_FOURCC('a','5','2','b');
i_cat = AUDIO_ES;
#ifdef AUTO_SPAWN
if( !p_input->stream.b_seekable )
if( config_GetInt( p_input, "audio-channel" )
== ((i_id & 0xF00) >> 8) ||
( config_GetInt( p_input, "audio-channel" ) < 0
&& !((i_id & 0xF00) >> 8)) )
switch( config_GetInt( p_input, "audio-type" ) )
{
case -1:
case REQUESTED_A52:
b_auto_spawn = VLC_TRUE;
}
#endif
}
else if( (i_id & 0xE0FF) == 0x20BD )
{
/* Subtitles video (0x20->0x3F) */
i_fourcc = VLC_FOURCC('s','p','u','b');
i_cat = SPU_ES;
#ifdef AUTO_SPAWN
if( !p_input->stream.b_seekable )
if( config_GetInt( p_input, "spu-channel" )
== ((i_id & 0x1F00) >> 8) )
{
b_auto_spawn = VLC_TRUE;
}
#endif
}
else if( (i_id & 0xF0FF) == 0xA0BD )
{
/* LPCM audio (0xA0->0xAF) */
i_fourcc = VLC_FOURCC('l','p','c','b');
i_cat = AUDIO_ES;
}
else if( (i_id & 0xFF00) == 0x7000 )
{
/* SVCD OGT subtitles in stream 0x070 */
i_fourcc = VLC_FOURCC('o','g','t', ' ');
i_cat = SPU_ES;
b_auto_spawn = VLC_TRUE;
psz_desc = malloc( strlen( _("SVCD Subtitle %i") ) + 2 );
if( psz_desc )
sprintf( psz_desc, _("SVCD Subtitle %i"),
i_id & 0x03 );
}
#define CVD_SUBTITLE_NUM(id) ((i_id >> 8) & 0xFF)
else if( CVD_SUBTITLE_NUM(i_id) <= 0x03 &&
(i_id & 0x00FF) == 0x00BD )
{
/* CVD subtitles (0x00->0x03) */
i_fourcc = VLC_FOURCC('c','v','d', ' ');
i_cat = SPU_ES;
b_auto_spawn = VLC_TRUE;
psz_desc = malloc( strlen( _("CVD Subtitle %i") ) + 2 );
if( psz_desc )
sprintf( psz_desc, _("CVD Subtitle %i"),
CVD_SUBTITLE_NUM(i_id) );
}
else
{
i_cat = UNKNOWN_ES;
i_fourcc = 0;
}
p_es = input_AddES( p_input, p_input->stream.pp_programs[0],
i_id, i_cat, psz_desc, 0 );
p_es->i_stream_id = p_data->p_demux_start[3];
p_es->i_fourcc = i_fourcc;
if( b_auto_spawn ) input_SelectES( p_input, p_es );
/* Tell the interface the stream has changed */
p_input->stream.b_changed = 1;
}
} /* stream.b_is_ok */
vlc_mutex_unlock( &p_input->stream.stream_lock );
} /* i_code > 0xBC */
return( p_es );
}
/*****************************************************************************
* DemuxPS: first step of demultiplexing: the PS header
*****************************************************************************/
static void DemuxPS( input_thread_t * p_input, data_packet_t * p_data )
{
uint32_t i_code;
vlc_bool_t b_trash = 0;
es_descriptor_t * p_es = NULL;
mpeg_demux_t *p_mpeg_demux = (mpeg_demux_t *)p_input->p_private;
i_code = ((uint32_t)p_data->p_demux_start[0] << 24)
| ((uint32_t)p_data->p_demux_start[1] << 16)
| ((uint32_t)p_data->p_demux_start[2] << 8)
| p_data->p_demux_start[3];
if( i_code <= 0x1BC )
{
switch( i_code )
{
case 0x1BA: /* PACK_START_CODE */
{
/* Read the SCR. */
mtime_t scr_time;
uint32_t i_mux_rate;
if( (p_data->p_demux_start[4] & 0xC0) == 0x40 )
{
/* MPEG-2 */
byte_t p_header[14];
byte_t * p_byte;
p_byte = p_data->p_demux_start;
if( MoveChunk( p_header, &p_data, &p_byte, 14 ) != 14 )
{
msg_Warn( p_input,
"packet too short to have a header" );
b_trash = 1;
break;
}
scr_time =
((mtime_t)(p_header[4] & 0x38) << 27) |
((mtime_t)(U32_AT(p_header + 4) & 0x03FFF800)
<< 4) |
((( ((mtime_t)U16_AT(p_header + 6) << 16)
| (mtime_t)U16_AT(p_header + 8) ) & 0x03FFF800)
>> 11);
/* mux_rate */
i_mux_rate = ((uint32_t)U16_AT(p_header + 10) << 6)
| (p_header[12] >> 2);
/* FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
* This is the biggest kludge ever !
* I don't know what's wrong with mux_rate calculation
* but this heuristic works well : */
i_mux_rate <<= 1;
i_mux_rate /= 3;
}
else
{
/* MPEG-1 SCR is like PTS. */
byte_t p_header[12];
byte_t * p_byte;
p_byte = p_data->p_demux_start;
if( MoveChunk( p_header, &p_data, &p_byte, 12 ) != 12 )
{
msg_Warn( p_input,
"packet too short to have a header" );
b_trash = 1;
break;
}
scr_time =
((mtime_t)(p_header[4] & 0x0E) << 29) |
(((mtime_t)U32_AT(p_header + 4) & 0xFFFE00) << 6) |
((mtime_t)p_header[7] << 7) |
((mtime_t)p_header[8] >> 1);
/* mux_rate */
i_mux_rate = (U32_AT(p_header + 8) & 0x7FFFFE) >> 1;
}
p_mpeg_demux->cur_scr_time = scr_time;
p_mpeg_demux->i_cur_mux_rate = i_mux_rate;
b_trash = 1;
}
break;
case 0x1BB: /* SYSTEM_START_CODE */
b_trash = 1; /* Nothing interesting */
break;
case 0x1BC: /* PROGRAM_STREAM_MAP_CODE */
DecodePSM( p_input, p_data );
b_trash = 1;
break;
case 0x1B9: /* PROGRAM_END_CODE */
b_trash = 1;
break;
default:
/* This should not happen */
b_trash = 1;
msg_Warn( p_input, "unwanted packet received "
"with startcode 0x%.8x", i_code );
}
}
else
{
p_es = ParsePS( p_input, p_data );
/* Call the pace control.
* FIXME: see hack note about cur_scr_time in system.h.
*/
if( p_mpeg_demux->cur_scr_time != -1 )
{
input_ClockManageRef( p_input,
p_input->stream.p_selected_program,
p_mpeg_demux->cur_scr_time );
if( p_mpeg_demux->i_cur_mux_rate != p_input->stream.i_mux_rate
&& p_input->stream.i_mux_rate )
{
msg_Warn( p_input,
"mux_rate changed prev: %ud, cur: %ud;"
" expect cosmetic errors" ,
(unsigned int) p_input->stream.i_mux_rate,
(unsigned int) p_mpeg_demux->i_cur_mux_rate );
}
p_input->stream.i_mux_rate = p_mpeg_demux->i_cur_mux_rate;
}
vlc_mutex_lock( &p_input->stream.control.control_lock );
if( p_es != NULL && p_es->p_dec != NULL
&& (p_es->i_cat != AUDIO_ES || !p_input->stream.control.b_mute) )
{
vlc_mutex_unlock( &p_input->stream.control.control_lock );
p_es->c_packets++;
GatherPES( p_input, p_data, p_es, 1, 0 );
}
else
{
vlc_mutex_unlock( &p_input->stream.control.control_lock );
b_trash = 1;
}
}
/* Trash the packet if it has no payload or if it isn't selected */
if( b_trash )
{
input_DeletePacket( p_input->p_method_data, p_data );
p_input->stream.c_packets_trashed++;
}
}
/*
* TS Demultiplexing
*/
/*****************************************************************************
* ReadTS: store a TS packet into a data_buffer_t
*****************************************************************************/
#define PEEK( SIZE ) \
i_error = input_Peek( p_input, &p_peek, SIZE ); \
if( i_error == -1 ) \
{ \
return( -1 ); \
} \
else if( i_error < SIZE ) \
{ \
/* EOF */ \
return( 0 ); \
}
static ssize_t ReadTS( input_thread_t * p_input, data_packet_t ** pp_data )
{
byte_t * p_peek;
ssize_t i_error, i_read;
PEEK( 1 );
if( *p_peek != TS_SYNC_CODE )
{
msg_Warn( p_input, "garbage at input (%x)", *p_peek );
if( p_input->i_mtu )
{
while( *p_peek != TS_SYNC_CODE )
{
/* Try to resync on next packet. */
PEEK( TS_PACKET_SIZE );
p_input->p_current_data += TS_PACKET_SIZE;
PEEK( 1 );
}
}
else
{
/* Move forward until we find 0x47 (and hope it's the good
* one... FIXME) */
while( *p_peek != TS_SYNC_CODE )
{
p_input->p_current_data++;
PEEK( 1 );
}
}
}
i_read = input_SplitBuffer( p_input, pp_data, TS_PACKET_SIZE );
if( i_read <= 0 )
{
return( i_read );
}
return( 1 );
}
/*****************************************************************************
* DemuxTS: first step of demultiplexing: the TS header
*****************************************************************************/
static void DemuxTS( input_thread_t * p_input, data_packet_t * p_data,
psi_callback_t pf_psi_callback )
{
uint16_t i_pid;
unsigned int i_dummy;
vlc_bool_t b_adaptation; /* Adaptation field is present */
vlc_bool_t b_payload; /* Packet carries payload */
vlc_bool_t b_unit_start; /* A PSI or a PES start in the packet */
vlc_bool_t b_trash = 0; /* Is the packet unuseful ? */
vlc_bool_t b_lost = 0; /* Was there a packet loss ? */
vlc_bool_t b_psi = 0; /* Is this a PSI ? */
vlc_bool_t b_dvbsub = 0; /* Is this a dvb subtitle ? */
vlc_bool_t b_pcr = 0; /* Does it have a PCR ? */
vlc_bool_t b_scrambled;
es_descriptor_t * p_es = NULL;
es_ts_data_t * p_es_demux = NULL;
pgrm_ts_data_t * p_pgrm_demux = NULL;
stream_ts_data_t * p_stream_demux =
(stream_ts_data_t *)p_input->stream.p_demux_data;
#define p (p_data->p_demux_start)
/* Extract flags values from TS common header. */
i_pid = ((p[1] & 0x1F) << 8) | p[2];
b_unit_start = (p[1] & 0x40);
b_scrambled = (p[3] & 0xc0);
b_adaptation = (p[3] & 0x20);
b_payload = (p[3] & 0x10);
/* Was there a transport error ? */
if ( p[1] & 0x80 )
{
msg_Warn( p_input, "transport_error_indicator set for PID %d counter %x", i_pid, p[3] & 0x0f );
}
/* Find out the elementary stream. */
vlc_mutex_lock( &p_input->stream.stream_lock );
for( i_dummy = 0; i_dummy < p_input->stream.i_pgrm_number; i_dummy ++ )
{
if( (( pgrm_ts_data_t * ) p_input->stream.pp_programs[i_dummy]->
p_demux_data)->i_pcr_pid == i_pid )
{
b_pcr = 1;
break;
}
}
p_es = input_FindES( p_input, i_pid );
if( (p_es != NULL) && (p_es->p_demux_data != NULL) )
{
p_es_demux = (es_ts_data_t *)p_es->p_demux_data;
if( p_es_demux->b_psi )
{
b_psi = 1;
}
else if ( p_es_demux->b_dvbsub )
{
b_dvbsub = 1;
}
else
{
p_pgrm_demux = (pgrm_ts_data_t *)p_es->p_pgrm->p_demux_data;
}
}
vlc_mutex_lock( &p_input->stream.control.control_lock );
if( ( p_es == NULL ) || (p_es->i_cat == AUDIO_ES
&& p_input->stream.control.b_mute) )
{
/* Not selected. Just read the adaptation field for a PCR. */
b_trash = 1;
}
else if( p_es->p_dec == NULL && !b_psi && !b_dvbsub )
{
b_trash = 1;
}
else if( b_scrambled )
{
msg_Warn( p_input, "scrambled packet for PID %d counter %x", i_pid, p[3] & 0x0f );
b_trash = 1;
}
vlc_mutex_unlock( &p_input->stream.control.control_lock );
vlc_mutex_unlock( &p_input->stream.stream_lock );
/* Don't change the order of the tests : if b_psi then p_pgrm_demux
* may still be null. Who said it was ugly ?
* I have written worse. --Meuuh */
if( ( p_es ) &&
((p_es->p_dec != NULL) || b_psi || b_pcr || b_dvbsub) )
{
p_es->c_packets++;
/* Extract adaptation field information if any */
if( !b_adaptation )
{
/* We don't have any adaptation_field, so payload starts
* immediately after the 4 byte TS header */
p_data->p_payload_start += 4;
}
else
{
/* p[4] is adaptation_field_length minus one */
p_data->p_payload_start += 5 + p[4];
/* The adaptation field can be limited to the
* adaptation_field_length byte, so that there is nothing to do:
* skip this possibility */
if( p[4] )
{
/* If the packet has both adaptation_field and payload,
* adaptation_field cannot be more than 182 bytes long; if
* there is only an adaptation_field, it must fill the next
* 183 bytes. */
if( b_payload ? (p[4] > 182) : (p[4] != 183) )
{
msg_Warn( p_input, "invalid TS adaptation field for PID %d (%2x)",
i_pid, p[4] );
p_data->b_discard_payload = 1;
p_es->c_invalid_packets++;
/* The length was invalid so we shouldn't have added it to
* p_payload_start above. Ensure p_payload_start has a
* valid value by setting it equal to p_payload_end. This
* also stops any data being processed from the packet.
*/
p_data->p_payload_start = p_data->p_payload_end;
}
/* Now we are sure that the byte containing flags is present:
* read it */
else
{
/* discontinuity_indicator */
if( p[5] & 0x80 )
{
msg_Warn( p_input,
"discontinuity_indicator encountered by TS demux "
"(PID %d: current %d, packet %d)",
i_pid,
( p_es_demux->i_continuity_counter ) & 0x0f,
p[3] & 0x0f );
/* If the PID carries the PCR, there will be a system
* time-based discontinuity. We let the PCR decoder
* handle that. */
p_es->p_pgrm->i_synchro_state = SYNCHRO_REINIT;
/* Don't resynchronise the counter here - it will
* be checked later and b_lost will then be set if
* necessary.
*/
}
} /* valid TS adaptation field ? */
} /* length > 0 */
} /* has adaptation field */
/* Check the continuity of the stream. */
i_dummy = ((p[3] & 0x0f) - p_es_demux->i_continuity_counter) & 0x0f;
if( b_payload &&
( i_dummy == 1 || (b_psi && p_stream_demux->b_buggy_psi ) ) )
{
/* Everything is ok, just increase our counter */
(p_es_demux->i_continuity_counter)++;
}
else
{
if( !b_payload && i_dummy == 0 )
{
/* This is a packet without payload, this is allowed by the
* draft. As there is nothing interesting in this packet
* (except PCR that have already been handled), we can trash
* the packet. */
b_trash = 1;
}
else if( !b_payload )
{
/* If there is no payload, the counter should be unchanged */
msg_Warn( p_input, "packet rxd for PID %d with no payload but "
"wrong counter: current %d, packet %d", i_pid,
p_es_demux->i_continuity_counter & 0x0f, p[3] & 0x0f );
}
else if( i_dummy <= 0 )
{
/* Duplicate packet: mark it as being to be trashed. */
msg_Warn( p_input,
"duplicate packet received for PID %d (counter %d)",
p_es->i_id, p[3] & 0x0f );
b_trash = 1;
}
else if( p_es_demux->i_continuity_counter == 0xFF )
{
/* This means that the packet is the first one we receive for
* this ES since the continuity counter ranges between 0 and
* 0x0F excepts when it has been initialized by the input:
* init the counter to the correct value. */
msg_Warn( p_input, "first packet for PID %d received "
"by TS demux", p_es->i_id );
p_es_demux->i_continuity_counter = (p[3] & 0x0f);
}
else
{
/* This can indicate that we missed a packet or that the
* continuity_counter wrapped and we received a dup packet:
* as we don't know, do as if we missed a packet to be sure
* to recover from this situation */
msg_Warn( p_input,
"packet lost by TS demux for PID %d: current %d, packet %d",
i_pid,
p_es_demux->i_continuity_counter & 0x0f,
p[3] & 0x0f );
b_lost = 1;
p_es_demux->i_continuity_counter = p[3] & 0x0f;
} /* not continuous */
} /* continuity */
} /* if selected or PCR */
/* Handle PCR */
if( b_pcr && b_adaptation && (p[5] & 0x10) && p[4]>=7 )
{
/* Read the PCR. */
mtime_t pcr_time;
pcr_time = ( (mtime_t)p[6] << 25 ) |
( (mtime_t)p[7] << 17 ) |
( (mtime_t)p[8] << 9 ) |
( (mtime_t)p[9] << 1 ) |
( (mtime_t)p[10] >> 7 );
/* Call the pace control. */
for( i_dummy = 0; i_dummy < p_input->stream.i_pgrm_number;
i_dummy ++ )
{
if( ( ( pgrm_ts_data_t * ) p_input->stream.pp_programs[i_dummy]->
p_demux_data )->i_pcr_pid == i_pid )
{
input_ClockManageRef( p_input,
p_input->stream.pp_programs[i_dummy], pcr_time );
}
}
}
/* Trash the packet if it has no payload or if it isn't selected */
if( b_trash )
{
input_DeletePacket( p_input->p_method_data, p_data );
p_input->stream.c_packets_trashed++;
}
else
{
if( b_psi )
{
/* The payload contains PSI tables */
(* pf_psi_callback) ( p_input, p_data, p_es, b_unit_start );
}
else
{
/* The payload carries a PES stream */
GatherPES( p_input, p_data, p_es, b_unit_start, b_lost );
}
}
#undef p
}
/*****************************************************************************
* system.h: MPEG demultiplexing.
*****************************************************************************
* Copyright (C) 1999-2002 VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*
* Optional MPEG demultiplexing
*/
/*****************************************************************************
* Constants
*****************************************************************************/
#define TS_PACKET_SIZE 188 /* Size of a TS packet */
#define TS_SYNC_CODE 0x47 /* First byte of a TS packet */
#define TS_SYNC_CODES_MIN 5 /* Min number of sync codes to look for */
#define PSI_SECTION_SIZE 4096 /* Maximum size of a PSI section */
#define PAT_UNINITIALIZED (1 << 6)
#define PMT_UNINITIALIZED (1 << 6)
#define PSI_IS_PAT 0x00
#define PSI_IS_PMT 0x01
#define UNKNOWN_PSI 0xff
/* ES streams types - see ISO/IEC 13818-1 table 2-29 numbers.
* these values are used in mpeg_system.c, and in
* the following plugins: mpeg_ts, mpeg_ts_dvbpsi, satellite. */
#define MPEG1_VIDEO_ES 0x01
#define MPEG2_VIDEO_ES 0x02
#define MPEG1_AUDIO_ES 0x03
#define MPEG2_AUDIO_ES 0x04
/* This one is "private PES". It may carry teletext, DVD subtitles, A52
* We have to check for the presence of a descriptor to have the codec */
#define PES_PRIVATE_ES 0x06
/* ISO/IEC 13818-7 Audio with ADTS transport syntax */
#define AAC_ADTS_AUDIO_ES 0x0F
#define MPEG4_VIDEO_ES 0x10
#define MPEG4_AUDIO_ES 0x11
#define H264_VIDEO_ES 0x1b
#define MPEG2_MOTO_VIDEO_ES 0x80
#define A52_AUDIO_ES 0x81
/* These ones might violate the usage : */
#define DVD_SPU_ES 0x82
#define LPCM_AUDIO_ES 0x83
#define SDDS_AUDIO_ES 0x84
#define DTS_AUDIO_ES 0x85
/* These ones are only here to work around a bug in VLS - VLS doesn't
* skip the first bytes of the PES payload (stream private ID) when
* streaming. This is incompatible with all equipments. 'B' is for
* buggy. Please note that they are associated with FOURCCs '***b'.
* --Meuuh 2002-08-30
*/
#define A52B_AUDIO_ES 0x91
#define DVDB_SPU_ES 0x92
#define LPCMB_AUDIO_ES 0x93
#define MSCODEC_VIDEO_ES 0xa0
/****************************************************************************
* psi_callback_t
****************************************************************************
* Used by TS demux to handle a PSI, either with the builtin decoder, either
* with a library such as libdvbpsi
****************************************************************************/
typedef void( * psi_callback_t )(
input_thread_t * p_input,
data_packet_t * p_data,
es_descriptor_t * p_es,
vlc_bool_t b_unit_start );
/****************************************************************************
* mpeg_demux_t
****************************************************************************
* Demux callbacks exported by the helper plugin
****************************************************************************/
typedef struct mpeg_demux_t
{
module_t * p_module;
ssize_t (*pf_read_ps) ( input_thread_t *, data_packet_t ** );
es_descriptor_t * (*pf_parse_ps) ( input_thread_t *, data_packet_t * );
void (*pf_demux_ps) ( input_thread_t *, data_packet_t * );
ssize_t (*pf_read_ts) ( input_thread_t *, data_packet_t ** );
void (*pf_demux_ts) ( input_thread_t *, data_packet_t *,
psi_callback_t );
/*
rocky:
i_cur_mux_rate and cur_scr_time below are a bit of a hack.
Background: VLC uses the System Clock Reference (SCR) of a PACK
header to read the stream at the right pace (contrary to other
players like xine/mplayer which don't use this info and
synchronise everything on the audio output clock).
The popular SVCD/VCD subtitling WinSubMux does not renumber the
SCRs when merging subtitles into the PES. Perhaps other
multipliexing tools are equally faulty. Until such time as
WinSubMux is fixed or other tools become available and widely
used, we will cater to the WinSubMux kind of buggy stream. The
hack here delays using the PACK SCR until the first PES that
would need it is received. For this we need to temporarily save
this information in the variables below.
*/
uint32_t i_cur_mux_rate;
mtime_t cur_scr_time;
} mpeg_demux_t;
/*****************************************************************************
* psi_section_t
*****************************************************************************
* Describes a PSI section. Beware, it doesn't contain pointers to the TS
* packets that contain it as for a PES, but the data themselves
*****************************************************************************/
typedef struct psi_section_t
{
byte_t buffer[PSI_SECTION_SIZE];
uint8_t i_section_number;
uint8_t i_last_section_number;
uint8_t i_version_number;
uint16_t i_section_length;
uint16_t i_read_in_section;
/* the PSI is complete */
vlc_bool_t b_is_complete;
/* packet missed up ? */
vlc_bool_t b_trash;
/*about sections */
vlc_bool_t b_section_complete;
/* where are we currently ? */
byte_t * p_current;
} psi_section_t;
/*****************************************************************************
* decoder_descriptor_t
*****************************************************************************/
typedef struct decoder_config_descriptor_s
{
uint8_t i_objectTypeIndication;
uint8_t i_streamType;
vlc_bool_t b_upStream;
uint32_t i_bufferSizeDB;
uint32_t i_maxBitrate;
uint32_t i_avgBitrate;
int i_decoder_specific_info_len;
uint8_t *p_decoder_specific_info;
} decoder_config_descriptor_t;
/*****************************************************************************
* sl_descriptor_t:
*****************************************************************************/
typedef struct sl_config_descriptor_s
{
vlc_bool_t b_useAccessUnitStartFlag;
vlc_bool_t b_useAccessUnitEndFlag;
vlc_bool_t b_useRandomAccessPointFlag;
vlc_bool_t b_useRandomAccessUnitsOnlyFlag;
vlc_bool_t b_usePaddingFlag;
vlc_bool_t b_useTimeStampsFlags;
vlc_bool_t b_useIdleFlag;
vlc_bool_t b_durationFlag;
uint32_t i_timeStampResolution;
uint32_t i_OCRResolution;
uint8_t i_timeStampLength;
uint8_t i_OCRLength;
uint8_t i_AU_Length;
uint8_t i_instantBitrateLength;
uint8_t i_degradationPriorityLength;
uint8_t i_AU_seqNumLength;
uint8_t i_packetSeqNumLength;
uint32_t i_timeScale;
uint16_t i_accessUnitDuration;
uint16_t i_compositionUnitDuration;
uint64_t i_startDecodingTimeStamp;
uint64_t i_startCompositionTimeStamp;
} sl_config_descriptor_t;
/*****************************************************************************
* es_mpeg4_descriptor_t: XXX it's not complete but should be enough
*****************************************************************************/
typedef struct es_mpeg4_descriptor_s
{
vlc_bool_t b_ok;
uint16_t i_es_id;
vlc_bool_t b_streamDependenceFlag;
vlc_bool_t b_OCRStreamFlag;
uint8_t i_streamPriority;
char *psz_url;
uint16_t i_dependOn_es_id;
uint16_t i_OCR_es_id;
decoder_config_descriptor_t dec_descr;
sl_config_descriptor_t sl_descr;
} es_mpeg4_descriptor_t;
/*****************************************************************************
* iod_descriptor_t: XXX it's not complete but should be enough
*****************************************************************************/
typedef struct iod_descriptor_s
{
uint8_t i_iod_label;
/* IOD */
uint16_t i_od_id;
char *psz_url;
uint8_t i_ODProfileLevelIndication;
uint8_t i_sceneProfileLevelIndication;
uint8_t i_audioProfileLevelIndication;
uint8_t i_visualProfileLevelIndication;
uint8_t i_graphicsProfileLevelIndication;
es_mpeg4_descriptor_t es_descr[255];
} iod_descriptor_t;
/*****************************************************************************
* es_ts_data_t: extension of es_descriptor_t
*****************************************************************************/
typedef struct es_ts_data_t
{
vlc_bool_t b_psi; /* Does the stream have to be handled by
* the PSI decoder ? */
int i_psi_type; /* There are different types of PSI */
psi_section_t * p_psi_section; /* PSI packets */
/* Markers */
int i_continuity_counter;
/* mpeg4 in TS data specific */
int b_mpeg4;
uint16_t i_es_id;
/* dvb subtitles in TS data specific */
vlc_bool_t b_dvbsub;
uint8_t i_dvbsub_es_count;
es_descriptor_t* p_dvbsub_es[256];
es_mpeg4_descriptor_t *p_es_descr; /* es_descr of IOD */
} es_ts_data_t;
/*****************************************************************************
* pgrm_ts_data_t: extension of pgrm_descriptor_t
*****************************************************************************/
typedef struct pgrm_ts_data_t
{
uint16_t i_pcr_pid; /* PCR ES, for TS streams */
int i_pmt_version;
/* libdvbpsi pmt decoder handle */
void * p_pmt_handle;
/* mpeg4 in TS data specific */
vlc_bool_t b_mpeg4;
iod_descriptor_t iod;
} pgrm_ts_data_t;
/*****************************************************************************
* stream_ts_data_t: extension of stream_descriptor_t
*****************************************************************************/
typedef struct stream_ts_data_t
{
int i_pat_version; /* Current version of the PAT */
vlc_bool_t b_buggy_psi;
/* libdvbpsi PMT decoder handle */
void * p_pat_handle;
} stream_ts_data_t;
/*****************************************************************************
* stream_ps_data_t: extension of stream_descriptor_t
*****************************************************************************/
typedef struct stream_ps_data_t
{
vlc_bool_t b_has_PSM; /* very rare, in fact */
uint8_t i_PSM_version;
} stream_ps_data_t;
/* PSM version is 5 bits, so -1 is not a valid value */
#define EMPTY_PSM_VERSION -1
/*****************************************************************************
* mpeg_ts.c : Transport Stream input module for vlc
*****************************************************************************
* Copyright (C) 2000-2004 VideoLAN
* $Id$
*
* Authors: Henri Fallon <henri@via.ecp.fr>
* Johan Bilien <jobi@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include "iso_lang.h"
#if defined MODULE_NAME_IS_ts_old_dvbpsi
# ifdef HAVE_DVBPSI_DR_H
# include <dvbpsi/dvbpsi.h>
# include <dvbpsi/descriptor.h>
# include <dvbpsi/pat.h>
# include <dvbpsi/pmt.h>
# include <dvbpsi/dr.h>
# else
# include "dvbpsi.h"
# include "descriptor.h"
# include "tables/pat.h"
# include "tables/pmt.h"
# include "descriptors/dr.h"
# endif
#endif
#include "system.h"
#include "codecs.h"
/*****************************************************************************
* Constants
*****************************************************************************/
#define TS_READ_ONCE 200
/*****************************************************************************
* Private structure
*****************************************************************************/
struct demux_sys_t
{
module_t * p_module;
mpeg_demux_t mpeg;
};
#define local_iso639_getlang(p1, p2) \
{ \
const iso639_lang_t * p_iso; \
p_iso = GetLang_2T((char*)(p1)); \
if( p_iso && strcmp(p_iso->psz_native_name,"Unknown")) \
{ \
if( p_iso->psz_native_name[0] ) \
strncpy( (p2), p_iso->psz_native_name, 20 ); \
else \
strncpy( (p2), p_iso->psz_eng_name, 20 ); \
} \
else \
{ \
p_iso = GetLang_2B((char*)(p1)); \
if ( p_iso ) \
{ \
if( p_iso->psz_native_name[0] ) \
strncpy( (p2), p_iso->psz_native_name, 20 ); \
else \
strncpy( (p2), p_iso->psz_eng_name, 20 ); \
} \
else \
{ \
strncpy( (p2), p1, 3 ); \
} \
} \
}
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Activate ( vlc_object_t * );
static void Deactivate ( vlc_object_t * );
static int Demux ( input_thread_t * );
#if defined MODULE_NAME_IS_ts_old
static void TSDemuxPSI ( input_thread_t *, data_packet_t *,
es_descriptor_t *, vlc_bool_t );
static void TSDecodePAT( input_thread_t *, es_descriptor_t *);
static void TSDecodePMT( input_thread_t *, es_descriptor_t *);
#define PSI_CALLBACK TSDemuxPSI
#elif defined MODULE_NAME_IS_ts_old_dvbpsi
static void TS_DVBPSI_DemuxPSI ( input_thread_t *, data_packet_t *,
es_descriptor_t *, vlc_bool_t );
static void TS_DVBPSI_HandlePAT ( input_thread_t *, dvbpsi_pat_t * );
static void TS_DVBPSI_HandlePMT ( input_thread_t *, dvbpsi_pmt_t * );
#define PSI_CALLBACK TS_DVBPSI_DemuxPSI
#endif
/*****************************************************************************
* Module descriptor
*****************************************************************************/
#define VLS_BACKWARDS_COMPAT_TEXT N_("Compatibility with pre-0.4 VLS")
#define VLS_BACKWARDS_COMPAT_LONGTEXT N_( \
"The protocol for transmitting A/52 audio streams changed between VLC " \
"0.3.x and 0.4. By default VLC assumes you have the latest VLS. In case " \
"you're using an old version, select this option.")
#define BUGGY_PSI_TEXT N_("Buggy PSI")
#define BUGGY_PSI_LONGTEXT N_( \
"If you have a stream whose PSI packets do not feature incremented " \
"continuity counters, select this option.")
vlc_module_begin();
#if defined MODULE_NAME_IS_ts_old
set_description( _("ISO 13818-1 MPEG Transport Stream input") );
set_capability( "demux", 6 );
add_shortcut( "ts_old" );
#elif defined MODULE_NAME_IS_ts_old_dvbpsi
set_description( _("ISO 13818-1 MPEG Transport Stream input (libdvbpsi)") );
set_capability( "demux", 5 );
add_shortcut( "ts_old_dvbpsi" );
#endif
add_bool( "vls-backwards-compat", 0, NULL,
VLS_BACKWARDS_COMPAT_TEXT, VLS_BACKWARDS_COMPAT_LONGTEXT, VLC_TRUE );
add_bool( "buggy-psi", 0, NULL, BUGGY_PSI_TEXT, BUGGY_PSI_LONGTEXT, VLC_TRUE );
set_callbacks( Activate, Deactivate );
vlc_module_end();
/*****************************************************************************
* Activate: initialize TS structures
*****************************************************************************/
static int Activate( vlc_object_t * p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
demux_sys_t * p_demux;
es_descriptor_t * p_pat_es;
es_ts_data_t * p_demux_data;
stream_ts_data_t * p_stream_data;
byte_t * p_peek;
vlc_bool_t b_force = VLC_FALSE;
int i_sync_pos;
/* Set the demux function */
p_input->pf_demux = Demux;
p_input->pf_demux_control = demux_vaControlDefault;
/* Have a peep at the show */
if( input_Peek( p_input, &p_peek, TS_PACKET_SIZE ) < TS_PACKET_SIZE )
{
msg_Err( p_input, "cannot peek()" );
return VLC_EGENERIC;
}
if( *p_input->psz_demux && ( !strncmp( p_input->psz_demux, "ts_old", 3 )
|| !strncmp( p_input->psz_demux, "ts_old_dvbpsi", 10 ) ) )
b_force = VLC_TRUE;
/* In a TS_PACKET_SIZE buffer we should find a sync byte */
for( i_sync_pos = 0; i_sync_pos < TS_PACKET_SIZE &&
p_peek[i_sync_pos] != TS_SYNC_CODE; i_sync_pos++ );
if( i_sync_pos >= TS_PACKET_SIZE )
{
if( b_force )
{
/* User forced */
msg_Err( p_input, "this does not look like a TS stream, "
"continuing anyway" );
}
else
{
msg_Warn( p_input, "TS module discarded (no sync)" );
return VLC_EGENERIC;
}
}
/* Now that we have the first TS_SYNC_CODE, check the following
* TS_SYNC_CODEs are where they are supposed to be (one byte sync code
* is not enough to ensure the sync). */
if( !b_force )
{
if( input_Peek( p_input, &p_peek, TS_PACKET_SIZE * TS_SYNC_CODES_MIN )
< TS_PACKET_SIZE * TS_SYNC_CODES_MIN )
{
msg_Err( p_input, "cannot peek()" );
return VLC_EGENERIC;
}
for( ; i_sync_pos < TS_PACKET_SIZE * TS_SYNC_CODES_MIN;
i_sync_pos += TS_PACKET_SIZE )
{
if( p_peek[i_sync_pos] != TS_SYNC_CODE )
{
msg_Warn( p_input, "TS module discarded (lost sync)" );
return VLC_EGENERIC;
}
}
}
/* Adapt the bufsize for our only use. */
if( p_input->i_mtu != 0 )
{
/* Have minimum granularity to avoid bottlenecks at the input level. */
p_input->i_bufsize = (p_input->i_mtu / TS_PACKET_SIZE) * TS_PACKET_SIZE;
}
p_demux = p_input->p_demux_data = malloc( sizeof(demux_sys_t ) );
if( p_demux == NULL )
{
return -1;
}
p_input->p_private = (void*)&p_demux->mpeg;
p_demux->p_module = module_Need( p_input, "mpeg-system", NULL, 0 );
if( p_demux->p_module == NULL )
{
free( p_input->p_demux_data );
return -1;
}
vlc_mutex_lock( &p_input->stream.stream_lock );
if( input_InitStream( p_input, sizeof( stream_ts_data_t ) ) == -1 )
{
module_Unneed( p_input, p_demux->p_module );
free( p_input->p_demux_data );
return -1;
}
p_stream_data = (stream_ts_data_t *)p_input->stream.p_demux_data;
p_stream_data->i_pat_version = PAT_UNINITIALIZED ;
p_stream_data->b_buggy_psi = config_GetInt( p_input, "buggy-psi" );
#ifdef MODULE_NAME_IS_ts_old_dvbpsi
p_stream_data->p_pat_handle = (dvbpsi_handle *)
dvbpsi_AttachPAT( (dvbpsi_pat_callback) &TS_DVBPSI_HandlePAT, p_input );
if( p_stream_data->p_pat_handle == NULL )
{
msg_Err( p_input, "could not create PAT decoder" );
module_Unneed( p_input, p_demux->p_module );
free( p_input->p_demux_data );
return -1;
}
#endif
/* We'll have to catch the PAT in order to continue
* Then the input will catch the PMT and then the others ES
* The PAT es is indepedent of any program. */
p_pat_es = input_AddES( p_input, NULL, 0x00,
UNKNOWN_ES, NULL, sizeof( es_ts_data_t ) );
p_pat_es->i_fourcc = VLC_FOURCC( 'p', 'a', 't', ' ' );
p_demux_data = (es_ts_data_t *)p_pat_es->p_demux_data;
p_demux_data->b_psi = 1;
p_demux_data->i_psi_type = PSI_IS_PAT;
p_demux_data->p_psi_section = malloc(sizeof(psi_section_t));
p_demux_data->p_psi_section->b_is_complete = 1;
p_demux_data->i_continuity_counter = 0xFF;
vlc_mutex_unlock( &p_input->stream.stream_lock );
return 0;
}
/*****************************************************************************
* Deactivate: deinitialize TS structures
*****************************************************************************/
static void Deactivate( vlc_object_t * p_this )
{
input_thread_t * p_input = (input_thread_t *)p_this;
module_Unneed( p_input, p_input->p_demux_data->p_module );
free( p_input->p_demux_data );
}
/*****************************************************************************
* Demux: reads and demuxes data packets
*****************************************************************************
* Returns -1 in case of error, 0 in case of EOF, otherwise the number of
* packets.
*****************************************************************************/
static int Demux( input_thread_t * p_input )
{
demux_sys_t * p_demux = p_input->p_demux_data;
int i_read_once = (p_input->i_mtu ?
p_input->i_bufsize / TS_PACKET_SIZE :
TS_READ_ONCE);
int i;
for( i = 0; i < i_read_once; i++ )
{
data_packet_t * p_data;
ssize_t i_result;
i_result = p_demux->mpeg.pf_read_ts( p_input, &p_data );
if( i_result <= 0 )
{
return i_result;
}
p_demux->mpeg.pf_demux_ts( p_input, p_data,
(psi_callback_t) &PSI_CALLBACK );
}
return i_read_once;
}
#if defined MODULE_NAME_IS_ts_old
/*
* PSI demultiplexing and decoding without libdvbpsi
*/
/*****************************************************************************
* DemuxPSI : makes up complete PSI data
*****************************************************************************/
static void TSDemuxPSI( input_thread_t * p_input, data_packet_t * p_data,
es_descriptor_t * p_es, vlc_bool_t b_unit_start )
{
es_ts_data_t * p_demux_data;
p_demux_data = (es_ts_data_t *)p_es->p_demux_data;
#define p_psi (p_demux_data->p_psi_section)
#define p (p_data->p_payload_start)
if( b_unit_start )
{
/* unit_start set to 1 -> presence of a pointer field
* (see ISO/IEC 13818 (2.4.4.2) which should be set to 0x00 */
if( (uint8_t)p[0] != 0x00 )
{
msg_Warn( p_input,
"non-zero pointer field found, trying to continue" );
p+=(uint8_t)p[0];
}
else
{
p++;
}
/* This is the begining of a new section */
if( ((uint8_t)(p[1]) & 0xc0) != 0x80 )
{
msg_Warn( p_input, "invalid PSI packet" );
p_psi->b_trash = 1;
}
else
{
p_psi->i_section_length = ((p[1] & 0xF) << 8) | p[2];
p_psi->b_section_complete = 0;
p_psi->i_read_in_section = 0;
p_psi->i_section_number = (uint8_t)p[6];
if( p_psi->b_is_complete || p_psi->i_section_number == 0 )
{
/* This is a new PSI packet */
p_psi->b_is_complete = 0;
p_psi->b_trash = 0;
p_psi->i_version_number = ( p[5] >> 1 ) & 0x1f;
p_psi->i_last_section_number = (uint8_t)p[7];
/* We'll write at the begining of the buffer */
p_psi->p_current = p_psi->buffer;
}
else
{
if( p_psi->b_section_complete )
{
/* New Section of an already started PSI */
p_psi->b_section_complete = 0;
if( p_psi->i_version_number != (( p[5] >> 1 ) & 0x1f) )
{
msg_Warn( p_input,
"PSI version differs inside same PAT" );
p_psi->b_trash = 1;
}
if( p_psi->i_section_number + 1 != (uint8_t)p[6] )
{
msg_Warn( p_input,
"PSI Section discontinuity, packet lost?" );
p_psi->b_trash = 1;
}
else
p_psi->i_section_number++;
}
else
{
msg_Warn( p_input, "got unexpected new PSI section" );
p_psi->b_trash = 1;
}
}
}
} /* b_unit_start */
if( !p_psi->b_trash )
{
/* read */
if( (p_data->p_payload_end - p) >=
( p_psi->i_section_length - p_psi->i_read_in_section ) )
{
/* The end of the section is in this TS packet */
memcpy( p_psi->p_current, p,
(p_psi->i_section_length - p_psi->i_read_in_section) );
p_psi->b_section_complete = 1;
p_psi->p_current +=
(p_psi->i_section_length - p_psi->i_read_in_section);
if( p_psi->i_section_number == p_psi->i_last_section_number )
{
/* This was the last section of PSI */
p_psi->b_is_complete = 1;
switch( p_demux_data->i_psi_type)
{
case PSI_IS_PAT:
TSDecodePAT( p_input, p_es );
break;
case PSI_IS_PMT:
TSDecodePMT( p_input, p_es );
break;
default:
msg_Warn( p_input, "received unknown PSI in DemuxPSI" );
}
}
}
else
{
memcpy( p_psi->buffer, p, p_data->p_payload_end - p );
p_psi->i_read_in_section += p_data->p_payload_end - p;
p_psi->p_current += p_data->p_payload_end - p;
}
}
#undef p_psi
#undef p
input_DeletePacket( p_input->p_method_data, p_data );
return ;
}
/*****************************************************************************
* DecodePAT : Decodes Programm association table and deal with it
*****************************************************************************/
static void TSDecodePAT( input_thread_t * p_input, es_descriptor_t * p_es )
{
stream_ts_data_t * p_stream_data;
es_ts_data_t * p_demux_data;
pgrm_descriptor_t * p_pgrm;
es_descriptor_t * p_current_es;
byte_t * p_current_data;
int i_section_length, i_program_id, i_pmt_pid;
int i_loop, i_current_section;
vlc_bool_t b_changed = 0;
p_demux_data = (es_ts_data_t *)p_es->p_demux_data;
p_stream_data = (stream_ts_data_t *)p_input->stream.p_demux_data;
#define p_psi (p_demux_data->p_psi_section)
/* Not so fast, Mike ! If the PAT version has changed, we first check
* that its content has really changed before doing anything */
if( p_stream_data->i_pat_version != p_psi->i_version_number )
{
int i_programs = p_input->stream.i_pgrm_number;
p_current_data = p_psi->buffer;
do
{
i_section_length = ((uint32_t)(p_current_data[1] & 0xF) << 8) |
p_current_data[2];
i_current_section = (uint8_t)p_current_data[6];
for( i_loop = 0;
( i_loop < (i_section_length - 9) / 4 ) && !b_changed;
i_loop++ )
{
i_program_id = ( (uint32_t)*(p_current_data + i_loop * 4 + 8) << 8 )
| *(p_current_data + i_loop * 4 + 9);
i_pmt_pid = ( ((uint32_t)*(p_current_data + i_loop * 4 + 10) & 0x1F)
<< 8 )
| *(p_current_data + i_loop * 4 + 11);
if( i_program_id )
{
if( (p_pgrm = input_FindProgram( p_input, i_program_id ))
&& (p_current_es = input_FindES( p_input, i_pmt_pid ))
&& p_current_es->p_pgrm == p_pgrm
&& p_current_es->i_id == i_pmt_pid
&& ((es_ts_data_t *)p_current_es->p_demux_data)->b_psi
&& ((es_ts_data_t *)p_current_es->p_demux_data)
->i_psi_type == PSI_IS_PMT )
{
i_programs--;
}
else
{
b_changed = 1;
}
}
}
p_current_data += 3 + i_section_length;
} while( ( i_current_section < p_psi->i_last_section_number )
&& !b_changed );
/* If we didn't find the expected amount of programs, the PAT has
* changed. Otherwise, it only changed if b_changed is already != 0 */
b_changed = b_changed || i_programs;
}
if( b_changed )
{
/* PAT has changed. We are going to delete all programs and
* create new ones. We chose not to only change what was needed
* as a PAT change may mean the stream is radically changing and
* this is a secure method to avoid crashes */
es_ts_data_t * p_es_demux;
pgrm_ts_data_t * p_pgrm_demux;
p_current_data = p_psi->buffer;
/* Delete all programs */
while( p_input->stream.i_pgrm_number )
{
input_DelProgram( p_input, p_input->stream.pp_programs[0] );
}
do
{
i_section_length = ((uint32_t)(p_current_data[1] & 0xF) << 8) |
p_current_data[2];
i_current_section = (uint8_t)p_current_data[6];
for( i_loop = 0; i_loop < (i_section_length - 9) / 4 ; i_loop++ )
{
i_program_id = ( (uint32_t)*(p_current_data + i_loop * 4 + 8) << 8 )
| *(p_current_data + i_loop * 4 + 9);
i_pmt_pid = ( ((uint32_t)*(p_current_data + i_loop * 4 + 10) & 0x1F)
<< 8 )
| *(p_current_data + i_loop * 4 + 11);
/* If program = 0, we're having info about NIT not PMT */
if( i_program_id )
{
/* Add this program */
p_pgrm = input_AddProgram( p_input, i_program_id,
sizeof( pgrm_ts_data_t ) );
/* whatis the PID of the PMT of this program */
p_pgrm_demux = (pgrm_ts_data_t *)p_pgrm->p_demux_data;
p_pgrm_demux->i_pmt_version = PMT_UNINITIALIZED;
/* Add the PMT ES to this program */
p_current_es = input_AddES( p_input, p_pgrm,(uint16_t)i_pmt_pid,
UNKNOWN_ES, NULL, sizeof( es_ts_data_t) );
p_current_es->i_fourcc = VLC_FOURCC( 'p', 'm', 't', ' ' );
p_es_demux = (es_ts_data_t *)p_current_es->p_demux_data;
p_es_demux->b_psi = 1;
p_es_demux->i_psi_type = PSI_IS_PMT;
p_es_demux->p_psi_section =
malloc( sizeof( psi_section_t ) );
p_es_demux->p_psi_section->b_is_complete = 0;
p_es_demux->i_continuity_counter = 0xFF;
}
}
p_current_data += 3 + i_section_length;
} while( i_current_section < p_psi->i_last_section_number );
/* Go to the beginning of the next section */
p_stream_data->i_pat_version = p_psi->i_version_number;
}
#undef p_psi
}
/*****************************************************************************
* DecodePMT : decode a given Program Stream Map
* ***************************************************************************
* When the PMT changes, it may mean a deep change in the stream, and it is
* careful to delete the ES and add them again. If the PMT doesn't change,
* there no need to do anything.
*****************************************************************************/
static void TSDecodePMT( input_thread_t * p_input, es_descriptor_t * p_es )
{
pgrm_ts_data_t * p_pgrm_data;
es_ts_data_t * p_demux_data;
vlc_bool_t b_vls_compat = config_GetInt( p_input, "vls-backwards-compat" );
p_demux_data = (es_ts_data_t *)p_es->p_demux_data;
p_pgrm_data = (pgrm_ts_data_t *)p_es->p_pgrm->p_demux_data;
#define p_psi (p_demux_data->p_psi_section)
if( p_psi->i_version_number != p_pgrm_data->i_pmt_version )
{
es_descriptor_t * p_new_es;
es_ts_data_t * p_es_demux;
byte_t * p_current_data, * p_current_section;
int i_section_length,i_current_section;
int i_prog_info_length, i_loop;
int i_es_info_length, i_pid, i_stream_type;
p_current_section = p_psi->buffer;
p_current_data = p_psi->buffer;
p_pgrm_data->i_pcr_pid = ( ((uint32_t)*(p_current_section + 8) & 0x1F) << 8 ) |
*(p_current_section + 9);
/* Lock stream information */
vlc_mutex_lock( &p_input->stream.stream_lock );
/* Delete all ES in this program except the PSI. We start from the
* end because i_es_number gets decremented after each deletion. */
for( i_loop = p_es->p_pgrm->i_es_number ; i_loop ; )
{
i_loop--;
p_es_demux = (es_ts_data_t *)
p_es->p_pgrm->pp_es[i_loop]->p_demux_data;
if ( ! p_es_demux->b_psi )
{
input_DelES( p_input, p_es->p_pgrm->pp_es[i_loop] );
}
}
/* Then add what we received in this PMT */
do
{
i_section_length = ( ((uint32_t)*(p_current_data + 1) & 0xF) << 8 ) |
*(p_current_data + 2);
i_current_section = (uint8_t)p_current_data[6];
i_prog_info_length = ( ((uint32_t)*(p_current_data + 10) & 0xF) << 8 ) |
*(p_current_data + 11);
/* For the moment we ignore program descriptors */
p_current_data += 12 + i_prog_info_length;
/* The end of the section, before the CRC is at
* p_current_section + i_section_length -1 */
while( p_current_data < p_current_section + i_section_length -1 )
{
i_stream_type = (int)p_current_data[0];
i_pid = ( ((uint32_t)*(p_current_data + 1) & 0x1F) << 8 ) |
*(p_current_data + 2);
i_es_info_length = ( ((uint32_t)*(p_current_data + 3) & 0xF) << 8 ) |
*(p_current_data + 4);
/* Tell the interface what kind of stream it is and select
* the required ones */
{
int i_fourcc, i_cat, i_stream_id;
switch( i_stream_type )
{
case MPEG1_VIDEO_ES:
case MPEG2_VIDEO_ES:
case MPEG2_MOTO_VIDEO_ES:
/* This isn't real, but we don't actually use
* it. */
i_stream_id = 0xE0;
i_fourcc = VLC_FOURCC('m','p','g','v');
i_cat = VIDEO_ES;
break;
case MPEG1_AUDIO_ES:
case MPEG2_AUDIO_ES:
/* This isn't real, but we don't actually use
* it. */
i_stream_id = 0xC0;
i_fourcc = VLC_FOURCC('m','p','g','a');
i_cat = AUDIO_ES;
break;
case A52_AUDIO_ES:
if ( !b_vls_compat )
i_fourcc = VLC_FOURCC('a','5','2',' ');
else
i_fourcc = VLC_FOURCC('a','5','2','b');
i_stream_id = 0xBD;
i_cat = AUDIO_ES;
break;
case LPCM_AUDIO_ES:
i_fourcc = VLC_FOURCC('l','p','c','m');
i_stream_id = 0xBD;
i_cat = AUDIO_ES;
break;
case DVD_SPU_ES:
if ( !b_vls_compat )
i_fourcc = VLC_FOURCC('s','p','u',' ');
else
i_fourcc = VLC_FOURCC('s','p','u','b');
i_stream_id = 0xBD;
i_cat = SPU_ES;
break;
case SDDS_AUDIO_ES:
i_fourcc = VLC_FOURCC('s','d','d','s');
i_stream_id = 0xBD;
i_cat = AUDIO_ES;
break;
case DTS_AUDIO_ES:
i_fourcc = VLC_FOURCC('d','t','s',' ');
i_stream_id = 0xBD;
i_cat = AUDIO_ES;
break;
/* 'b' stands for 'buggy' */
case A52B_AUDIO_ES:
i_fourcc = VLC_FOURCC('a','5','2','b');
i_stream_id = 0xBD;
i_cat = AUDIO_ES;
break;
case LPCMB_AUDIO_ES:
i_fourcc = VLC_FOURCC('l','p','c','b');
i_stream_id = 0xBD;
i_cat = AUDIO_ES;
break;
case DVDB_SPU_ES:
i_fourcc = VLC_FOURCC('s','p','u','b');
i_stream_id = 0xBD;
i_cat = SPU_ES;
break;
case AAC_ADTS_AUDIO_ES:
i_fourcc = VLC_FOURCC('m','p','4','a');
i_cat = AUDIO_ES;
i_stream_id = 0xfa;
break;
default :
i_stream_id = 0;
i_fourcc = 0;
i_cat = UNKNOWN_ES;
break;
}
/* Add this ES to the program */
p_new_es = input_AddES( p_input, p_es->p_pgrm, (uint16_t)i_pid,
i_cat, NULL, sizeof( es_ts_data_t ) );
((es_ts_data_t *)p_new_es->p_demux_data)->i_continuity_counter = 0xFF;
p_new_es->i_stream_id = i_stream_id;
p_new_es->i_fourcc = i_fourcc;
}
p_current_data += 5 + i_es_info_length;
}
/* Go to the beginning of the next section*/
p_current_data += 3 + i_section_length;
p_current_section++;
} while( i_current_section < p_psi->i_last_section_number );
p_pgrm_data->i_pmt_version = p_psi->i_version_number;
/* if no program is selected :*/
if( !p_input->stream.p_selected_program )
{
pgrm_descriptor_t * p_pgrm_to_select;
uint16_t i_id = (uint16_t)config_GetInt( p_input, "program" );
if( i_id != 0 ) /* if user specified a program */
{
p_pgrm_to_select = input_FindProgram( p_input, i_id );
if( p_pgrm_to_select && p_pgrm_to_select == p_es->p_pgrm )
p_input->pf_set_program( p_input, p_pgrm_to_select );
}
else
p_input->pf_set_program( p_input, p_es->p_pgrm );
}
/* if the pmt belongs to the currently selected program, we
* reselect it to update its ES */
else if( p_es->p_pgrm == p_input->stream.p_selected_program )
{
p_input->pf_set_program( p_input, p_es->p_pgrm );
}
/* inform interface that stream has changed */
p_input->stream.b_changed = 1;
/* Remove lock */
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
#undef p_psi
}
#elif defined MODULE_NAME_IS_ts_old_dvbpsi
/*
* PSI Decoding using libdvbpsi
*/
/*****************************************************************************
* DemuxPSI : send the PSI to the right libdvbpsi decoder
*****************************************************************************/
static void TS_DVBPSI_DemuxPSI( input_thread_t * p_input,
data_packet_t * p_data,
es_descriptor_t * p_es,
vlc_bool_t b_unit_start )
{
es_ts_data_t * p_es_demux_data;
pgrm_ts_data_t * p_pgrm_demux_data;
stream_ts_data_t * p_stream_demux_data;
p_es_demux_data = (es_ts_data_t *)p_es->p_demux_data;
p_stream_demux_data = (stream_ts_data_t *) p_input->stream.p_demux_data;
switch( p_es_demux_data->i_psi_type)
{
case PSI_IS_PAT:
dvbpsi_PushPacket(
(dvbpsi_handle)p_stream_demux_data->p_pat_handle,
p_data->p_demux_start );
break;
case PSI_IS_PMT:
p_pgrm_demux_data = ( pgrm_ts_data_t * )p_es->p_pgrm->p_demux_data;
dvbpsi_PushPacket(
(dvbpsi_handle)p_pgrm_demux_data->p_pmt_handle,
p_data->p_demux_start );
break;
default:
msg_Warn( p_input, "received unknown PSI in DemuxPSI" );
}
input_DeletePacket( p_input->p_method_data, p_data );
}
/*****************************************************************************
* MP4 specific functions
*****************************************************************************/
static int MP4_DescriptorLength( int *pi_data, uint8_t **pp_data )
{
unsigned int i_b;
unsigned int i_len = 0;
do
{
i_b = **pp_data;
(*pp_data)++;
(*pi_data)--;
i_len = ( i_len << 7 ) + ( i_b&0x7f );
} while( i_b&0x80 );
return( i_len );
}
static int MP4_GetByte( int *pi_data, uint8_t **pp_data )
{
if( *pi_data > 0 )
{
int i_b = **pp_data;
(*pp_data)++;
(*pi_data)--;
return( i_b );
}
else
{
return( 0 );
}
}
static int MP4_GetWord( int *pi_data, uint8_t **pp_data )
{
int i1, i2;
i1 = MP4_GetByte( pi_data, pp_data );
i2 = MP4_GetByte( pi_data, pp_data );
return( ( i1 << 8 ) | i2 );
}
static int MP4_Get3Bytes( int *pi_data, uint8_t **pp_data )
{
int i1, i2, i3;
i1 = MP4_GetByte( pi_data, pp_data );
i2 = MP4_GetByte( pi_data, pp_data );
i3 = MP4_GetByte( pi_data, pp_data );
return( ( i1 << 16 ) | ( i2 << 8) | i3 );
}
static uint32_t MP4_GetDWord( int *pi_data, uint8_t **pp_data )
{
uint32_t i1, i2;
i1 = MP4_GetWord( pi_data, pp_data );
i2 = MP4_GetWord( pi_data, pp_data );
return( ( i1 << 16 ) | i2 );
}
static char* MP4_GetURL( int *pi_data, uint8_t **pp_data )
{
char *url;
int i_url_len, i;
i_url_len = MP4_GetByte( pi_data, pp_data );
url = malloc( i_url_len + 1 );
for( i = 0; i < i_url_len; i++ )
{
url[i] = MP4_GetByte( pi_data, pp_data );
}
url[i_url_len] = '\0';
return( url );
}
static void MP4_IODParse( iod_descriptor_t *p_iod, int i_data, uint8_t *p_data )
{
int i;
int i_es_index;
uint8_t i_flags;
vlc_bool_t b_url;
int i_iod_length;
fprintf( stderr, "\n************ IOD ************" );
for( i = 0; i < 255; i++ )
{
p_iod->es_descr[i].b_ok = 0;
}
i_es_index = 0;
if( i_data < 3 )
{
return;
}
p_iod->i_iod_label = MP4_GetByte( &i_data, &p_data );
fprintf( stderr, "\n* iod_label:%d", p_iod->i_iod_label );
fprintf( stderr, "\n* ===========" );
fprintf( stderr, "\n* tag:0x%x", p_data[0] );
if( MP4_GetByte( &i_data, &p_data ) != 0x02 )
{
fprintf( stderr, "\n ERR: tag != 0x02" );
return;
}
i_iod_length = MP4_DescriptorLength( &i_data, &p_data );
fprintf( stderr, "\n* length:%d", i_iod_length );
if( i_iod_length > i_data )
{
i_iod_length = i_data;
}
p_iod->i_od_id = ( MP4_GetByte( &i_data, &p_data ) << 2 );
i_flags = MP4_GetByte( &i_data, &p_data );
p_iod->i_od_id |= i_flags >> 6;
b_url = ( i_flags >> 5 )&0x01;
fprintf( stderr, "\n* od_id:%d", p_iod->i_od_id );
fprintf( stderr, "\n* url flag:%d", b_url );
fprintf( stderr, "\n* includeInlineProfileLevel flag:%d", ( i_flags >> 4 )&0x01 );
if( b_url )
{
p_iod->psz_url = MP4_GetURL( &i_data, &p_data );
fprintf( stderr, "\n* url string:%s", p_iod->psz_url );
fprintf( stderr, "\n*****************************\n" );
return;
}
else
{
p_iod->psz_url = NULL;
}
p_iod->i_ODProfileLevelIndication = MP4_GetByte( &i_data, &p_data );
p_iod->i_sceneProfileLevelIndication = MP4_GetByte( &i_data, &p_data );
p_iod->i_audioProfileLevelIndication = MP4_GetByte( &i_data, &p_data );
p_iod->i_visualProfileLevelIndication = MP4_GetByte( &i_data, &p_data );
p_iod->i_graphicsProfileLevelIndication = MP4_GetByte( &i_data, &p_data );
fprintf( stderr, "\n* ODProfileLevelIndication:%d", p_iod->i_ODProfileLevelIndication );
fprintf( stderr, "\n* sceneProfileLevelIndication:%d", p_iod->i_sceneProfileLevelIndication );
fprintf( stderr, "\n* audioProfileLevelIndication:%d", p_iod->i_audioProfileLevelIndication );
fprintf( stderr, "\n* visualProfileLevelIndication:%d", p_iod->i_visualProfileLevelIndication );
fprintf( stderr, "\n* graphicsProfileLevelIndication:%d", p_iod->i_graphicsProfileLevelIndication );
while( i_data > 0 && i_es_index < 255)
{
int i_tag, i_length;
int i_data_sav;
uint8_t *p_data_sav;
i_tag = MP4_GetByte( &i_data, &p_data );
i_length = MP4_DescriptorLength( &i_data, &p_data );
i_data_sav = i_data;
p_data_sav = p_data;
i_data = i_length;
switch( i_tag )
{
case 0x03:
{
#define es_descr p_iod->es_descr[i_es_index]
int i_decoderConfigDescr_length;
fprintf( stderr, "\n* - ES_Descriptor length:%d", i_length );
es_descr.b_ok = 1;
es_descr.i_es_id = MP4_GetWord( &i_data, &p_data );
i_flags = MP4_GetByte( &i_data, &p_data );
es_descr.b_streamDependenceFlag = ( i_flags >> 7 )&0x01;
b_url = ( i_flags >> 6 )&0x01;
es_descr.b_OCRStreamFlag = ( i_flags >> 5 )&0x01;
es_descr.i_streamPriority = i_flags & 0x1f;
fprintf( stderr, "\n* * streamDependenceFlag:%d", es_descr.b_streamDependenceFlag );
fprintf( stderr, "\n* * OCRStreamFlag:%d", es_descr.b_OCRStreamFlag );
fprintf( stderr, "\n* * streamPriority:%d", es_descr.i_streamPriority );
if( es_descr.b_streamDependenceFlag )
{
es_descr.i_dependOn_es_id = MP4_GetWord( &i_data, &p_data );
fprintf( stderr, "\n* * dependOn_es_id:%d", es_descr.i_dependOn_es_id );
}
if( b_url )
{
es_descr.psz_url = MP4_GetURL( &i_data, &p_data );
fprintf( stderr, "\n* url string:%s", es_descr.psz_url );
}
else
{
es_descr.psz_url = NULL;
}
if( es_descr.b_OCRStreamFlag )
{
es_descr.i_OCR_es_id = MP4_GetWord( &i_data, &p_data );
fprintf( stderr, "\n* * OCR_es_id:%d", es_descr.i_OCR_es_id );
}
if( MP4_GetByte( &i_data, &p_data ) != 0x04 )
{
fprintf( stderr, "\n* ERR missing DecoderConfigDescr" );
es_descr.b_ok = 0;
break;
}
i_decoderConfigDescr_length = MP4_DescriptorLength( &i_data, &p_data );
fprintf( stderr, "\n* - DecoderConfigDesc length:%d", i_decoderConfigDescr_length );
#define dec_descr es_descr.dec_descr
dec_descr.i_objectTypeIndication = MP4_GetByte( &i_data, &p_data );
i_flags = MP4_GetByte( &i_data, &p_data );
dec_descr.i_streamType = i_flags >> 2;
dec_descr.b_upStream = ( i_flags >> 1 )&0x01;
dec_descr.i_bufferSizeDB = MP4_Get3Bytes( &i_data, &p_data );
dec_descr.i_maxBitrate = MP4_GetDWord( &i_data, &p_data );
dec_descr.i_avgBitrate = MP4_GetDWord( &i_data, &p_data );
fprintf( stderr, "\n* * objectTypeIndication:0x%x", dec_descr.i_objectTypeIndication );
fprintf( stderr, "\n* * streamType:0x%x", dec_descr.i_streamType );
fprintf( stderr, "\n* * upStream:%d", dec_descr.b_upStream );
fprintf( stderr, "\n* * bufferSizeDB:%d", dec_descr.i_bufferSizeDB );
fprintf( stderr, "\n* * maxBitrate:%d", dec_descr.i_maxBitrate );
fprintf( stderr, "\n* * avgBitrate:%d", dec_descr.i_avgBitrate );
if( i_decoderConfigDescr_length > 13 && MP4_GetByte( &i_data, &p_data ) == 0x05 )
{
int i;
dec_descr.i_decoder_specific_info_len =
MP4_DescriptorLength( &i_data, &p_data );
if( dec_descr.i_decoder_specific_info_len > 0 )
{
dec_descr.p_decoder_specific_info =
malloc( dec_descr.i_decoder_specific_info_len );
}
for( i = 0; i < dec_descr.i_decoder_specific_info_len; i++ )
{
dec_descr.p_decoder_specific_info[i] = MP4_GetByte( &i_data, &p_data );
}
}
else
{
dec_descr.i_decoder_specific_info_len = 0;
dec_descr.p_decoder_specific_info = NULL;
}
}
#undef dec_descr
#define sl_descr es_descr.sl_descr
{
int i_SLConfigDescr_length;
int i_predefined;
if( MP4_GetByte( &i_data, &p_data ) != 0x06 )
{
fprintf( stderr, "\n* ERR missing SLConfigDescr" );
es_descr.b_ok = 0;
break;
}
i_SLConfigDescr_length = MP4_DescriptorLength( &i_data, &p_data );
fprintf( stderr, "\n* - SLConfigDescr length:%d", i_SLConfigDescr_length );
i_predefined = MP4_GetByte( &i_data, &p_data );
fprintf( stderr, "\n* * i_predefined:0x%x", i_predefined );
switch( i_predefined )
{
case 0x01:
{
sl_descr.b_useAccessUnitStartFlag = 0;
sl_descr.b_useAccessUnitEndFlag = 0;
sl_descr.b_useRandomAccessPointFlag = 0;
//sl_descr.b_useRandomAccessUnitsOnlyFlag = 0;
sl_descr.b_usePaddingFlag = 0;
sl_descr.b_useTimeStampsFlags = 0;
sl_descr.b_useIdleFlag = 0;
sl_descr.b_durationFlag = 0; // FIXME FIXME
sl_descr.i_timeStampResolution = 1000;
sl_descr.i_OCRResolution = 0; // FIXME FIXME
sl_descr.i_timeStampLength = 32;
sl_descr.i_OCRLength = 0; // FIXME FIXME
sl_descr.i_AU_Length = 0;
sl_descr.i_instantBitrateLength= 0; // FIXME FIXME
sl_descr.i_degradationPriorityLength= 0;
sl_descr.i_AU_seqNumLength = 0;
sl_descr.i_packetSeqNumLength = 0;
if( sl_descr.b_durationFlag )
{
sl_descr.i_timeScale = 0; // FIXME FIXME
sl_descr.i_accessUnitDuration = 0; // FIXME FIXME
sl_descr.i_compositionUnitDuration= 0; // FIXME FIXME
}
if( !sl_descr.b_useTimeStampsFlags )
{
sl_descr.i_startDecodingTimeStamp = 0; // FIXME FIXME
sl_descr.i_startCompositionTimeStamp= 0; // FIXME FIXME
}
}
break;
default:
fprintf( stderr, "\n* ERR unsupported SLConfigDescr predefined" );
es_descr.b_ok = 0;
break;
}
}
break;
#undef sl_descr
#undef es_descr
default:
fprintf( stderr, "\n* - OD tag:0x%x length:%d (Unsupported)", i_tag, i_length );
break;
}
p_data = p_data_sav + i_length;
i_data = i_data_sav - i_length;
i_es_index++;
}
fprintf( stderr, "\n*****************************\n" );
}
static void MP4_IODClean( iod_descriptor_t *p_iod )
{
int i;
if( p_iod->psz_url )
{
free( p_iod->psz_url );
p_iod->psz_url = NULL;
return;
}
for( i = 0; i < 255; i++ )
{
#define es_descr p_iod->es_descr[i]
if( es_descr.b_ok )
{
if( es_descr.psz_url )
{
free( es_descr.psz_url );
es_descr.psz_url = NULL;
}
else
{
if( es_descr.dec_descr.p_decoder_specific_info != NULL )
{
free( es_descr.dec_descr.p_decoder_specific_info );
es_descr.dec_descr.p_decoder_specific_info = NULL;
es_descr.dec_descr.i_decoder_specific_info_len = 0;
}
}
}
es_descr.b_ok = 0;
#undef es_descr
}
}
/*****************************************************************************
* HandlePAT: will treat a PAT returned by dvbpsi
*****************************************************************************/
static void TS_DVBPSI_HandlePAT( input_thread_t * p_input,
dvbpsi_pat_t * p_new_pat )
{
dvbpsi_pat_program_t * p_pgrm;
pgrm_descriptor_t * p_new_pgrm;
pgrm_ts_data_t * p_pgrm_demux;
es_descriptor_t * p_current_es;
es_ts_data_t * p_es_demux;
stream_ts_data_t * p_stream_data;
vlc_mutex_lock( &p_input->stream.stream_lock );
p_stream_data = (stream_ts_data_t *)p_input->stream.p_demux_data;
if( ( p_new_pat->b_current_next &&
( p_new_pat->i_version != p_stream_data->i_pat_version ) ) ||
p_stream_data->i_pat_version == PAT_UNINITIALIZED )
{
msg_Dbg( p_input, "processing PAT version %d", p_new_pat->i_version );
/* Delete all programs */
while( p_input->stream.i_pgrm_number )
{
pgrm_ts_data_t *p_pgrm_demux_old =
(pgrm_ts_data_t *)p_input->stream.pp_programs[0]->p_demux_data;
if( p_pgrm_demux_old->b_mpeg4 )
{
MP4_IODClean( &p_pgrm_demux_old->iod );
}
/* Delete old PMT decoder */
if( p_pgrm_demux_old->p_pmt_handle )
dvbpsi_DetachPMT( p_pgrm_demux_old->p_pmt_handle );
input_DelProgram( p_input, p_input->stream.pp_programs[0] );
}
/* treat the new programs list */
p_pgrm = p_new_pat->p_first_program;
while( p_pgrm )
{
msg_Dbg( p_input, "new program: %d", p_pgrm->i_number );
/* If program = 0, we're having info about NIT not PMT */
if( p_pgrm->i_number )
{
/* Add this program */
p_new_pgrm = input_AddProgram( p_input, p_pgrm->i_number,
sizeof( pgrm_ts_data_t ) );
p_pgrm_demux = (pgrm_ts_data_t *)p_new_pgrm->p_demux_data;
p_pgrm_demux->i_pmt_version = PMT_UNINITIALIZED;
/* Add the PMT ES to this program */
p_current_es = input_AddES( p_input, p_new_pgrm,
(uint16_t)p_pgrm->i_pid, UNKNOWN_ES,
NULL, sizeof(es_ts_data_t) );
p_current_es->i_fourcc = VLC_FOURCC( 'p', 'm', 't', ' ' );
p_es_demux = (es_ts_data_t *)p_current_es->p_demux_data;
p_es_demux->b_psi = 1;
p_es_demux->i_psi_type = PSI_IS_PMT;
p_es_demux->p_psi_section = NULL;
p_es_demux->i_continuity_counter = 0xFF;
/* Create a PMT decoder */
p_pgrm_demux->p_pmt_handle = (dvbpsi_handle *)
dvbpsi_AttachPMT( p_pgrm->i_number,
(dvbpsi_pmt_callback) &TS_DVBPSI_HandlePMT,
p_input );
if( p_pgrm_demux->p_pmt_handle == NULL )
{
msg_Err( p_input, "could not create PMT decoder" );
p_input->b_error = 1;
return;
}
}
p_pgrm = p_pgrm->p_next;
}
p_stream_data->i_pat_version = p_new_pat->i_version;
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
/*****************************************************************************
* HandlePMT: will treat a PMT returned by dvbpsi
*****************************************************************************/
static void TS_DVBPSI_HandlePMT( input_thread_t * p_input,
dvbpsi_pmt_t * p_new_pmt )
{
dvbpsi_pmt_es_t * p_es;
pgrm_descriptor_t * p_pgrm;
es_descriptor_t * p_new_es;
pgrm_ts_data_t * p_pgrm_demux;
vlc_bool_t b_vls_compat = config_GetInt( p_input, "vls-backwards-compat" );
vlc_mutex_lock( &p_input->stream.stream_lock );
p_pgrm = input_FindProgram( p_input, p_new_pmt->i_program_number );
if( p_pgrm == NULL )
{
msg_Warn( p_input, "PMT of unreferenced program found" );
return;
}
p_pgrm_demux = (pgrm_ts_data_t *)p_pgrm->p_demux_data;
p_pgrm_demux->i_pcr_pid = p_new_pmt->i_pcr_pid;
if( ( p_new_pmt->b_current_next &&
( p_new_pmt->i_version != p_pgrm_demux->i_pmt_version ) ) ||
p_pgrm_demux->i_pmt_version == PMT_UNINITIALIZED )
{
dvbpsi_descriptor_t *p_dr = p_new_pmt->p_first_descriptor;
int i_loop;
msg_Dbg( p_input, "processing PMT for program %d version %d",
p_new_pmt->i_program_number, p_new_pmt->i_version );
/* Delete all ES in this program except the PSI. We start from the
* end because i_es_number gets decremented after each deletion. */
for( i_loop = p_pgrm->i_es_number ; i_loop > 0 ; )
{
es_ts_data_t * p_es_demux;
i_loop--;
p_es_demux = (es_ts_data_t *)
p_pgrm->pp_es[i_loop]->p_demux_data;
if ( !p_es_demux->b_psi )
{
input_DelES( p_input, p_pgrm->pp_es[i_loop] );
}
}
/* IOD */
while( p_dr && ( p_dr->i_tag != 0x1d ) )
p_dr = p_dr->p_next;
if( p_dr)
{
msg_Warn( p_input, "found IOD descriptor" );
MP4_IODParse( &p_pgrm_demux->iod, p_dr->i_length, p_dr->p_data );
}
p_es = p_new_pmt->p_first_es;
while( p_es )
{
vlc_fourcc_t i_fourcc;
int i_size, i_cat, i_stream_id = 0;
es_ts_data_t demux_data;
BITMAPINFOHEADER *p_bih = NULL;
WAVEFORMATEX *p_wf = NULL;
char psz_desc[30];
memset( &demux_data, 0, sizeof(es_ts_data_t) );
*psz_desc = 0;
msg_Dbg( p_input, "new PID 0x%x stream type 0x%x",
p_es->i_pid, p_es->i_type );
switch( p_es->i_type )
{
case MPEG1_VIDEO_ES:
case MPEG2_VIDEO_ES:
case MPEG2_MOTO_VIDEO_ES:
i_fourcc = VLC_FOURCC('m','p','g','v');
i_cat = VIDEO_ES;
break;
case MPEG1_AUDIO_ES:
case MPEG2_AUDIO_ES:
i_fourcc = VLC_FOURCC('m','p','g','a');
i_cat = AUDIO_ES;
break;
case A52_AUDIO_ES:
if ( !b_vls_compat )
i_fourcc = VLC_FOURCC('a','5','2',' ');
else
i_fourcc = VLC_FOURCC('a','5','2','b');
i_cat = AUDIO_ES;
i_stream_id = 0xBD;
break;
case DVD_SPU_ES:
if ( !b_vls_compat )
i_fourcc = VLC_FOURCC('s','p','u',' ');
else
i_fourcc = VLC_FOURCC('s','p','u','b');
i_cat = SPU_ES;
i_stream_id = 0xBD;
break;
case LPCM_AUDIO_ES:
i_fourcc = VLC_FOURCC('l','p','c','m');
i_cat = AUDIO_ES;
i_stream_id = 0xBD;
break;
case SDDS_AUDIO_ES:
i_fourcc = VLC_FOURCC('s','d','d','s');
i_stream_id = 0xBD;
i_cat = AUDIO_ES;
break;
case DTS_AUDIO_ES:
i_fourcc = VLC_FOURCC('d','t','s',' ');
i_stream_id = 0xBD;
i_cat = AUDIO_ES;
break;
case A52B_AUDIO_ES:
i_fourcc = VLC_FOURCC('a','5','2','b');
i_cat = AUDIO_ES;
i_stream_id = 0xBD;
break;
case DVDB_SPU_ES:
i_fourcc = VLC_FOURCC('s','p','u','b');
i_cat = SPU_ES;
i_stream_id = 0xBD;
break;
case LPCMB_AUDIO_ES:
i_fourcc = VLC_FOURCC('l','p','c','b');
i_cat = AUDIO_ES;
i_stream_id = 0xBD;
break;
case MPEG4_VIDEO_ES:
i_fourcc = VLC_FOURCC('m','p','4','v');
i_cat = VIDEO_ES;
i_stream_id = 0xfa;
break;
case MPEG4_AUDIO_ES:
case AAC_ADTS_AUDIO_ES:
i_fourcc = VLC_FOURCC('m','p','4','a');
i_cat = AUDIO_ES;
i_stream_id = 0xfa;
break;
case H264_VIDEO_ES:
i_fourcc = VLC_FOURCC('h','2','6','4');
i_cat = VIDEO_ES;
break;
case MSCODEC_VIDEO_ES:
i_fourcc = VLC_FOURCC(0,0,0,0); /* fixed later */
i_cat = VIDEO_ES;
i_stream_id = 0xa0;
break;
case PES_PRIVATE_ES:
/* We need to check a descriptor to find the real codec */
i_fourcc = VLC_FOURCC(0,0,0,0); /* fixed later */
i_cat = UNKNOWN_ES;
i_stream_id = 0xbd;
break;
default:
i_fourcc = 0;
i_cat = UNKNOWN_ES;
i_stream_id = 0;
}
if( p_es->i_type == MPEG4_VIDEO_ES ||
p_es->i_type == MPEG4_AUDIO_ES )
{
/* mpeg4 stream, search sl_descriptor */
dvbpsi_descriptor_t *p_dr = p_es->p_first_descriptor;
while( p_dr && ( p_dr->i_tag != 0x1f ) ) p_dr = p_dr->p_next;
if( p_dr && p_dr->i_length == 2 )
{
int i_es_descr_index;
demux_data.i_es_id =
( p_dr->p_data[0] << 8 ) | p_dr->p_data[1];
demux_data.p_es_descr = NULL;
msg_Warn( p_input, "found SL_descriptor" );
for( i_es_descr_index = 0; i_es_descr_index < 255;
i_es_descr_index++ )
{
if( p_pgrm_demux->iod.es_descr[i_es_descr_index].b_ok &&
p_pgrm_demux->iod.es_descr[i_es_descr_index].i_es_id == demux_data.i_es_id )
{
demux_data.p_es_descr =
&p_pgrm_demux->iod.es_descr[i_es_descr_index];
break;
}
}
}
if( demux_data.p_es_descr != NULL )
{
#define DESCR demux_data.p_es_descr->dec_descr
demux_data.b_mpeg4 = 1;
/* fix fourcc */
switch( DESCR.i_streamType )
{
case 0x04: /* VisualStream */
i_cat = VIDEO_ES;
switch( DESCR.i_objectTypeIndication )
{
case 0x20:
i_fourcc = VLC_FOURCC('m','p','4','v'); // mpeg4
break;
case 0x60:
case 0x61:
case 0x62:
case 0x63:
case 0x64:
case 0x65:
i_fourcc = VLC_FOURCC( 'm','p','g','v' ); // mpeg2
break;
case 0x6a:
i_fourcc = VLC_FOURCC( 'm','p','g','v' ); // mpeg1
break;
case 0x6c:
i_fourcc = VLC_FOURCC( 'j','p','e','g' ); // mpeg1
break;
default:
i_fourcc = 0;
break;
}
break;
case 0x05: /* AudioStream */
i_cat = AUDIO_ES;
switch( DESCR.i_objectTypeIndication )
{
case 0x40:
i_fourcc = VLC_FOURCC('m','p','4','a'); // mpeg4
break;
case 0x66:
case 0x67:
case 0x68:
i_fourcc = VLC_FOURCC('m','p','4','a');// mpeg2 aac
break;
case 0x69:
i_fourcc = VLC_FOURCC('m','p','g','a'); // mpeg2
break;
case 0x6b:
i_fourcc = VLC_FOURCC('m','p','g','a'); // mpeg1
break;
default:
i_fourcc = 0;
break;
}
break;
default:
i_cat = UNKNOWN_ES;
i_fourcc = 0;
break;
}
switch( i_cat )
{
case VIDEO_ES:
i_size = sizeof( BITMAPINFOHEADER ) +
DESCR.i_decoder_specific_info_len;
p_bih = malloc( i_size );
p_bih->biSize = i_size;
p_bih->biWidth = 0;
p_bih->biHeight = 0;
p_bih->biPlanes = 1;
p_bih->biBitCount = 0;
p_bih->biCompression = 0;
p_bih->biSizeImage = 0;
p_bih->biXPelsPerMeter = 0;
p_bih->biYPelsPerMeter = 0;
p_bih->biClrUsed = 0;
p_bih->biClrImportant = 0;
memcpy( &p_bih[1],
DESCR.p_decoder_specific_info,
DESCR.i_decoder_specific_info_len );
break;
case AUDIO_ES:
i_size = sizeof( WAVEFORMATEX ) +
DESCR.i_decoder_specific_info_len;
p_wf = malloc( i_size );
p_wf->wFormatTag = 0xffff;
p_wf->nChannels = 0;
p_wf->nSamplesPerSec = 0;
p_wf->nAvgBytesPerSec = 0;
p_wf->nBlockAlign = 1;
p_wf->wBitsPerSample = 0;
p_wf->cbSize = DESCR.i_decoder_specific_info_len;
memcpy( &p_wf[1],
DESCR.p_decoder_specific_info,
DESCR.i_decoder_specific_info_len );
break;
default:
break;
}
}
else
{
msg_Warn( p_input,
"mpeg4 stream without (valid) sl_descriptor" );
demux_data.b_mpeg4 = 0;
}
}
else if( p_es->i_type == MSCODEC_VIDEO_ES )
{
/* crapy ms codec stream, search private descriptor */
dvbpsi_descriptor_t *p_dr = p_es->p_first_descriptor;
while( p_dr && ( p_dr->i_tag != 0xa0 ) ) p_dr = p_dr->p_next;
if( p_dr && p_dr->i_length >= 8 )
{
int i_bih_size;
i_fourcc = VLC_FOURCC( p_dr->p_data[0], p_dr->p_data[1],
p_dr->p_data[2], p_dr->p_data[3] );
i_bih_size = (p_dr->p_data[8] << 8) | p_dr->p_data[9];
i_size = sizeof( BITMAPINFOHEADER ) + i_bih_size;
p_bih = malloc( i_size );
p_bih->biSize = i_size;
p_bih->biWidth = ( p_dr->p_data[4] << 8 )|p_dr->p_data[5];
p_bih->biHeight = ( p_dr->p_data[6] << 8 )|p_dr->p_data[7];
p_bih->biPlanes = 1;
p_bih->biBitCount = 0;
p_bih->biCompression = 0;
p_bih->biSizeImage = 0;
p_bih->biXPelsPerMeter = 0;
p_bih->biYPelsPerMeter = 0;
p_bih->biClrUsed = 0;
p_bih->biClrImportant = 0;
memcpy( &p_bih[1], &p_dr->p_data[10], i_bih_size );
}
else
{
msg_Warn( p_input, "private ms-codec stream without bih "
"private sl_descriptor" );
i_fourcc = 0;
i_cat = UNKNOWN_ES;
}
}
else if( p_es->i_type == PES_PRIVATE_ES )
{
dvbpsi_descriptor_t *p_dr = p_es->p_first_descriptor;
/* We have to find a descriptor giving the right codec */
for(p_dr = p_es->p_first_descriptor; p_dr; p_dr = p_dr->p_next)
{
if( p_dr->i_tag == 0x6a )
{
/* A52 */
i_fourcc = VLC_FOURCC( 'a', '5', '2', ' ' );
i_cat = AUDIO_ES;
}
#ifdef _DVBPSI_DR_59_H_
else if( p_dr->i_tag == 0x59 )
{
uint16_t n;
es_descriptor_t * p_dvbsub_es;
es_ts_data_t dvbsub_demux_data;
dvb_spuinfo_t* p_info;
dvbpsi_subtitling_dr_t* sub;
demux_data.b_dvbsub = 1;
demux_data.i_dvbsub_es_count = 0;
/* DVB subtitle */
i_fourcc = VLC_FOURCC( 'd', 'v', 'b', 's' );
i_cat = SPU_ES;
sub = dvbpsi_DecodeSubtitlingDr( p_dr );
for( n = 0; n < sub->i_subtitles_number; n++)
{
/* As each subtitle ES contains n languages,
* We are going to create n fake ES for the n
* tracks */
local_iso639_getlang(
sub->p_subtitle[n].i_iso6392_language_code,
psz_desc);
p_dvbsub_es = input_AddES( p_input,
p_pgrm,
0xfe12+n,
SPU_ES,
psz_desc,
sizeof(es_ts_data_t) );
if( p_dvbsub_es == NULL )
{
msg_Err( p_input, "could not add ES %d",
p_es->i_pid );
p_input->b_error = 1;
return;
}
p_dvbsub_es->i_fourcc = i_fourcc;
p_dvbsub_es->i_stream_id = i_stream_id;
p_info = malloc(sizeof(dvb_spuinfo_t));
p_info->i_id =
sub->p_subtitle[n].i_composition_page_id;
p_dvbsub_es->p_spuinfo = (void*) p_info;
memcpy( p_dvbsub_es->p_demux_data,
&dvbsub_demux_data,
sizeof(es_ts_data_t) );
((es_ts_data_t *)p_dvbsub_es->p_demux_data)
->i_continuity_counter = 0xFF;
// Finaly we add this stream to the index
demux_data.p_dvbsub_es[
demux_data.i_dvbsub_es_count++] = p_dvbsub_es;
i_cat = UNKNOWN_ES;
}
}
#endif /* _DVBPSI_DR_59_H_ */
}
if( i_fourcc == VLC_FOURCC(0,0,0,0) )
{
msg_Warn( p_input,
"unknown codec/type for Private PES stream" );
}
}
if( i_cat == AUDIO_ES || i_cat == SPU_ES )
{
dvbpsi_descriptor_t *p_dr = p_es->p_first_descriptor;
while( p_dr && ( p_dr->i_tag != 0x0a ) ) p_dr = p_dr->p_next;
if( p_dr )
{
dvbpsi_iso639_dr_t *p_decoded =
dvbpsi_DecodeISO639Dr( p_dr );
if( p_decoded != NULL )
local_iso639_getlang(p_decoded->i_iso_639_code, psz_desc);
#if 0
if( p_decoded->i_code_count > 0 )
{
const iso639_lang_t * p_iso;
p_iso = GetLang_2T((char*)p_decoded->i_iso_639_code);
if( p_iso && strcmp(p_iso->psz_native_name,"Unknown"))
{
if( p_iso->psz_native_name[0] )
strncpy( psz_desc,
p_iso->psz_native_name, 20 );
else
strncpy( psz_desc,
p_iso->psz_eng_name, 20 );
}
else
{
p_iso = GetLang_2B(
(char*)p_decoded->i_iso_639_code);
if ( p_iso )
{
if( p_iso->psz_native_name[0] )
strncpy( psz_desc,
p_iso->psz_native_name, 20 );
else
strncpy( psz_desc,
p_iso->psz_eng_name, 20 );
}
else
{
strncpy( psz_desc, p_decoded->i_iso_639_code, 3 );
}
}
}
#endif
}
switch( p_es->i_type )
{
case MPEG1_AUDIO_ES:
case MPEG2_AUDIO_ES:
strcat( psz_desc, " (mpeg)" );
break;
case LPCM_AUDIO_ES:
case LPCMB_AUDIO_ES:
strcat( psz_desc, " (lpcm)" );
break;
case A52_AUDIO_ES:
case A52B_AUDIO_ES:
strcat( psz_desc, " (A52)" );
break;
case MPEG4_AUDIO_ES:
case AAC_ADTS_AUDIO_ES:
strcat( psz_desc, " (aac)" );
break;
}
}
/* Add this ES */
p_new_es = input_AddES( p_input, p_pgrm, (uint16_t)p_es->i_pid,
i_cat, psz_desc, sizeof( es_ts_data_t ) );
if( p_new_es == NULL )
{
msg_Err( p_input, "could not add ES %d", p_es->i_pid );
p_input->b_error = 1;
return;
}
p_new_es->i_fourcc = i_fourcc;
p_new_es->i_stream_id = i_stream_id;
p_new_es->p_bitmapinfoheader = (void*)p_bih;
p_new_es->p_waveformatex = (void*)p_wf;
memcpy( p_new_es->p_demux_data, &demux_data,
sizeof(es_ts_data_t) );
((es_ts_data_t *)p_new_es->p_demux_data)->i_continuity_counter =
0xFF;
p_es = p_es->p_next;
}
/* if no program is selected :*/
if( !p_input->stream.p_selected_program )
{
pgrm_descriptor_t * p_pgrm_to_select;
uint16_t i_id = (uint16_t)config_GetInt( p_input, "program" );
if( i_id != 0 ) /* if user specified a program */
{
p_pgrm_to_select = input_FindProgram( p_input, i_id );
if( p_pgrm_to_select && p_pgrm_to_select == p_pgrm )
p_input->pf_set_program( p_input, p_pgrm_to_select );
}
else
p_input->pf_set_program( p_input, p_pgrm );
}
/* if the pmt belongs to the currently selected program, we
* reselect it to update its ES */
else if( p_pgrm == p_input->stream.p_selected_program )
{
p_input->pf_set_program( p_input, p_pgrm );
}
p_pgrm_demux->i_pmt_version = p_new_pmt->i_version;
p_input->stream.b_changed = 1;
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
}
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment