X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Fty.c;h=63d8402dc994992a7ef839c9410f08091d3ea494;hb=b36b614a1469679d21ec24e2f89b95bb89358251;hp=f9e0514b6de647c78e3fcfd8ef868dea670a0171;hpb=caed6fe745fbbcee7846ca440b74d0e5bda8d83e;p=vlc diff --git a/modules/demux/ty.c b/modules/demux/ty.c index f9e0514b6d..63d8402dc9 100644 --- a/modules/demux/ty.c +++ b/modules/demux/ty.c @@ -35,31 +35,40 @@ * Preamble *****************************************************************************/ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include #include #include "vlc_codec.h" +#include "vlc_meta.h" +#include "vlc_input.h" #include "../codec/cc.h" +#include + /***************************************************************************** * Module descriptor *****************************************************************************/ static int Open ( vlc_object_t * ); static void Close( vlc_object_t * ); -vlc_module_begin(); - set_shortname( _("TY") ); - set_description(_("TY Stream audio/video demux")); - set_category( CAT_INPUT ); - set_subcategory( SUBCAT_INPUT_DEMUX ); - set_capability("demux2", 6); +vlc_module_begin () + set_shortname( N_("TY") ) + set_description(N_("TY Stream audio/video demux")) + set_category( CAT_INPUT ) + set_subcategory( SUBCAT_INPUT_DEMUX ) + set_capability("demux", 6) /* FIXME: there seems to be a segfault when using PVR access * and TY demux has a bigger priority than PS * Something must be wrong. */ - set_callbacks( Open, Close ); - add_shortcut("ty"); - add_shortcut("tivo"); -vlc_module_end(); + set_callbacks( Open, Close ) + add_shortcut("ty") + add_shortcut("tivo") +vlc_module_end () /***************************************************************************** * Local prototypes @@ -107,10 +116,10 @@ static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd }; typedef struct { long l_rec_size; - uint8_t ex1, ex2; + uint8_t ex[2]; uint8_t rec_type; uint8_t subrec_type; - vlc_bool_t b_ext; + bool b_ext; uint64_t l_ty_pts; /* TY PTS in the record header */ } ty_rec_hdr_t; @@ -141,6 +150,69 @@ typedef enum TIVO_AUDIO_MPEG } tivo_audio_t; +#define XDS_MAX_DATA_SIZE (32) +typedef enum +{ + XDS_CLASS_CURRENT = 0, + XDS_CLASS_FUTURE = 1, + XDS_CLASS_CHANNEL = 2, + XDS_CLASS_MISCELLANEOUS = 3, + XDS_CLASS_PUBLIC_SERVICE = 4, + XDS_CLASS_RESERVED = 5, + XDS_CLASS_UNDEFINED = 6, + XDS_CLASS_OTHER = 7, + + XDS_MAX_CLASS_COUNT +} xds_class_t; +typedef struct +{ + bool b_started; + int i_data; + uint8_t p_data[XDS_MAX_DATA_SIZE]; + int i_sum; +} xds_packet_t; +typedef enum +{ + XDS_META_PROGRAM_RATING_NONE, + XDS_META_PROGRAM_RATING_MPAA, + XDS_META_PROGRAM_RATING_TPG, + /* TODO add CA/CE rating */ +} xds_meta_program_rating_t; +typedef struct +{ + char *psz_name; + xds_meta_program_rating_t rating; + char *psz_rating; + /* Add the other fields once I have the samples */ +} xds_meta_program_t; +typedef struct +{ + char *psz_channel_name; + char *psz_channel_call_letter; + char *psz_channel_number; + + xds_meta_program_t current; + xds_meta_program_t future; +} xds_meta_t; +typedef struct +{ + /* Are we in XDS mode */ + bool b_xds; + + /* Current class type */ + xds_class_t i_class; + int i_type; + bool b_future; + + /* */ + xds_packet_t pkt[XDS_MAX_CLASS_COUNT][128]; /* XXX it is way too much, but simpler */ + + /* */ + bool b_meta_changed; + xds_meta_t meta; + +} xds_t; + struct demux_sys_t { es_out_id_t *p_video; /* ptr to video codec */ @@ -149,12 +221,14 @@ struct demux_sys_t cc_data_t cc; es_out_id_t *p_cc[4]; + xds_t xds; + int i_cur_chunk; int i_stuff_cnt; size_t i_stream_size; /* size of input stream (if known) */ //uint64_t l_program_len; /* length of this stream in msec */ - vlc_bool_t b_seekable; /* is this stream seekable? */ - vlc_bool_t b_have_master; /* are master chunks present? */ + bool b_seekable; /* is this stream seekable? */ + bool b_have_master; /* are master chunks present? */ tivo_type_t tivo_type; /* tivo type (SA / DTiVo) */ tivo_series_t tivo_series; /* Series1 or Series2 */ tivo_audio_t audio_type; /* AC3 or MPEG */ @@ -167,8 +241,8 @@ struct demux_sys_t //mtime_t l_last_ty_pts_sync; /* audio PTS at time of last TY PTS */ uint64_t l_first_ty_pts; /* first TY PTS in this master chunk */ uint64_t l_final_ty_pts; /* final TY PTS in this master chunk */ - int i_seq_table_size; /* number of entries in SEQ table */ - int i_bits_per_seq_entry; /* # of bits in SEQ table bitmask */ + unsigned i_seq_table_size; /* number of entries in SEQ table */ + unsigned i_bits_per_seq_entry; /* # of bits in SEQ table bitmask */ mtime_t firstAudioPTS; mtime_t lastAudioPTS; @@ -179,8 +253,8 @@ struct demux_sys_t int i_num_recs; /* number of recs in this chunk */ int i_seq_rec; /* record number where seq start is */ ty_seq_table_t *seq_table; /* table of SEQ entries from mstr chk */ - vlc_bool_t eof; - vlc_bool_t b_first_chunk; + bool eof; + bool b_first_chunk; }; static int get_chunk_header(demux_t *); @@ -190,7 +264,7 @@ static int find_es_header( const uint8_t *header, static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct); static int ty_stream_seek_time(demux_t *, uint64_t); -static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf, +static ty_rec_hdr_t *parse_chunk_headers( const uint8_t *p_buf, int i_num_recs, int *pi_payload_size); static int probe_stream(demux_t *p_demux); static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk); @@ -202,6 +276,11 @@ static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 ); +static void XdsInit( xds_t * ); +static void XdsExit( xds_t * ); + +#define TY_ES_GROUP (1) + /* * Open: check file and initialize demux structures * @@ -230,8 +309,8 @@ static int Open(vlc_object_t *p_this) U32_AT(&p_peek[8]) != CHUNK_SIZE ) { if( !p_demux->b_force && - !demux2_IsPathExtension( p_demux, ".ty" ) && - !demux2_IsPathExtension( p_demux, ".ty+" ) ) + !demux_IsPathExtension( p_demux, ".ty" ) && + !demux_IsPathExtension( p_demux, ".ty+" ) ) return VLC_EGENERIC; msg_Warn( p_demux, "this does not look like a TY file, " "continuing anyway..." ); @@ -249,7 +328,7 @@ static int Open(vlc_object_t *p_this) memset(p_sys, 0, sizeof(demux_sys_t)); /* set up our struct (most were zero'd out with the memset above) */ - p_sys->b_first_chunk = VLC_TRUE; + p_sys->b_first_chunk = true; p_sys->b_have_master = (U32_AT(p_peek) == TIVO_PES_FILEID); p_sys->firstAudioPTS = -1; p_sys->i_stream_size = stream_Size(p_demux->s); @@ -277,10 +356,12 @@ static int Open(vlc_object_t *p_this) } else { es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', ' ' ) ); } + fmt.i_group = TY_ES_GROUP; p_sys->p_audio = es_out_Add( p_demux->out, &fmt ); /* register the video stream */ es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( 'm', 'p', 'g', 'v' ) ); + fmt.i_group = TY_ES_GROUP; p_sys->p_video = es_out_Add( p_demux->out, &fmt ); /* */ @@ -288,6 +369,8 @@ static int Open(vlc_object_t *p_this) p_sys->p_cc[i] = NULL; cc_Init( &p_sys->cc ); + XdsInit( &p_sys->xds ); + return VLC_SUCCESS; } @@ -422,7 +505,7 @@ static int Control(demux_t *p_demux, int i_query, va_list args) if( ( i64 = p_sys->i_stream_size ) > 0 ) { pf = (double*) va_arg( args, double* ); - *pf = (double)stream_Tell( p_demux->s ) / (double) i64; + *pf = ((double)1.0) * stream_Tell( p_demux->s ) / (double) i64; return VLC_SUCCESS; } return VLC_EGENERIC; @@ -462,10 +545,10 @@ static void Close( vlc_object_t *p_this ) demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys = p_demux->p_sys; + XdsExit( &p_sys->xds ); cc_Exit( &p_sys->cc ); free( p_sys->rec_hdrs ); - if( p_sys->seq_table ) - free( p_sys->seq_table ); + free( p_sys->seq_table ); free(p_sys); } @@ -610,7 +693,7 @@ static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl //subrec_type, esOffset1); p_sys->lastVideoPTS = get_pts( &p_block_in->p_buffer[ esOffset1 + VIDEO_PTS_OFFSET ] ); - /*msg_Dbg(p_demux, "Video rec %d PTS "I64Fd, p_sys->i_cur_rec, + /*msg_Dbg(p_demux, "Video rec %d PTS %"PRId64, p_sys->i_cur_rec, p_sys->lastVideoPTS );*/ if (subrec_type != 0x06) { /* if we found a PES, and it's not type 6, then we're S2 */ @@ -697,7 +780,8 @@ static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl continue; es_format_Init( &fmt, SPU_ES, fcc[i] ); - fmt.psz_description = strdup( _(ppsz_description[i]) ); + fmt.psz_description = strdup( vlc_gettext(ppsz_description[i]) ); + fmt.i_group = TY_ES_GROUP; p_sys->p_cc[i] = es_out_Add( p_demux->out, &fmt ); es_format_Clean( &fmt ); @@ -705,24 +789,17 @@ static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl /* Send the CC data */ if( p_block_in->i_pts > 0 && p_sys->cc.i_data > 0 ) { - int i_cc_count; - - block_t *p_cc = block_New( p_demux, p_sys->cc.i_data ); - p_cc->i_flags |= BLOCK_FLAG_TYPE_I; - p_cc->i_pts = p_block_in->i_pts; - memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data ); - - for( i = 0, i_cc_count = 0; i < 4; i++ ) - i_cc_count += p_sys->p_cc[i] ? 1 : 0; - for( i = 0; i < 4; i++ ) { - if( !p_sys->p_cc[i] ) - continue; - if( i_cc_count > 1 ) - es_out_Send( p_demux->out, p_sys->p_cc[i], block_Duplicate( p_cc ) ); - else + if( p_sys->p_cc[i] ) + { + block_t *p_cc = block_New( p_demux, p_sys->cc.i_data ); + p_cc->i_flags |= BLOCK_FLAG_TYPE_I; + p_cc->i_pts = p_block_in->i_pts; + memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data ); + es_out_Send( p_demux->out, p_sys->p_cc[i], p_cc ); + } } cc_Flush( &p_sys->cc ); } @@ -744,10 +821,10 @@ static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_bl return -1; #if 0 int i; - printf( "Audio Packet Header " ); + fprintf( stderr, "Audio Packet Header " ); for( i = 0 ; i < 24 ; i++ ) - printf( "%2.2x ", p_block_in->p_buffer[i] ); - printf( "\n" ); + fprintf( stderr, "%2.2x ", p_block_in->p_buffer[i] ); + fprintf( stderr, "\n" ); #endif if( subrec_type == 2 ) @@ -939,7 +1016,6 @@ static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block { demux_sys_t *p_sys = p_demux->p_sys; int i_field; - int i_channel; if( p_block_in ) block_Release(p_block_in); @@ -953,19 +1029,12 @@ static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block /* XDS data (extract programs infos) transmitted on field 2 only */ if( i_field == 1 ) - DemuxDecodeXds( p_demux, rec_hdr->ex1, rec_hdr->ex2 ); + DemuxDecodeXds( p_demux, rec_hdr->ex[0], rec_hdr->ex[1] ); if( p_sys->cc.i_data + 3 > CC_MAX_DATA_SIZE ) return 0; - p_sys->cc.p_data[p_sys->cc.i_data+0] = i_field; - p_sys->cc.p_data[p_sys->cc.i_data+1] = rec_hdr->ex1; - p_sys->cc.p_data[p_sys->cc.i_data+2] = rec_hdr->ex2; - p_sys->cc.i_data += 3; - - i_channel = cc_Channel( i_field, &p_sys->cc.p_data[p_sys->cc.i_data+1] ); - if( i_channel >= 0 && i_channel < 4 ) - p_sys->cc.pb_present[i_channel] = VLC_TRUE; + cc_AppendData( &p_sys->cc, i_field, rec_hdr->ex ); return 0; } @@ -974,8 +1043,9 @@ static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct) { demux_sys_t *p_sys = p_demux->p_sys; int64_t seek_pos = p_sys->i_stream_size * seek_pct; - int i, i_cur_part; long l_skip_amt; + int i; + unsigned i_cur_part; /* if we're not seekable, there's nothing to do */ if (!p_sys->b_seekable) @@ -1006,7 +1076,7 @@ static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct) /* seek within the chunk to get roughly to where we want */ p_sys->i_cur_rec = (int) ((double) ((seek_pos % CHUNK_SIZE) / (double) (CHUNK_SIZE)) * p_sys->i_num_recs); - msg_Dbg(p_demux, "Seeked to file pos " I64Fd, seek_pos); + msg_Dbg(p_demux, "Seeked to file pos %"PRId64, seek_pos); msg_Dbg(p_demux, " (chunk %d, record %d)", p_sys->i_cur_chunk - 1, p_sys->i_cur_rec); @@ -1020,13 +1090,373 @@ static int ty_stream_seek_pct(demux_t *p_demux, double seek_pct) /* to hell with syncing any audio or video, just start reading records... :) */ /*p_sys->lastAudioPTS = p_sys->lastVideoPTS = 0;*/ - es_out_Control( p_demux->out, ES_OUT_RESET_PCR ); return VLC_SUCCESS; } +/* XDS decoder */ +//#define TY_XDS_DEBUG +static void XdsInit( xds_t *h ) +{ + int i, j; + + h->b_xds = false; + h->i_class = XDS_MAX_CLASS_COUNT; + h->i_type = 0; + h->b_future = false; + for( i = 0; i < XDS_MAX_CLASS_COUNT; i++ ) + { + for( j = 0; j < 128; j++ ) + h->pkt[i][j].b_started = false; + } + h->b_meta_changed = false; + memset( &h->meta, 0, sizeof(h->meta) ); +} +static void XdsExit( xds_t *h ) +{ + /* */ + free( h->meta.psz_channel_name ); + free( h->meta.psz_channel_call_letter ); + free( h->meta.psz_channel_number ); + + /* */ + free( h->meta.current.psz_name ); + free( h->meta.current.psz_rating ); + /* */ + free( h->meta.future.psz_name ); + free( h->meta.future.psz_rating ); +} +static void XdsStringUtf8( char dst[2*32+1], const uint8_t *p_src, int i_src ) +{ + int i; + int i_dst; + + for( i = 0, i_dst = 0; i < i_src; i++ ) + { + switch( p_src[i] ) + { +#define E2( c, u1, u2 ) case c: dst[i_dst++] = u1; dst[i_dst++] = u2; break + E2( 0x2a, 0xc3,0xa1); // lowercase a, acute accent + E2( 0x5c, 0xc3,0xa9); // lowercase e, acute accent + E2( 0x5e, 0xc3,0xad); // lowercase i, acute accent + E2( 0x5f, 0xc3,0xb3); // lowercase o, acute accent + E2( 0x60, 0xc3,0xba); // lowercase u, acute accent + E2( 0x7b, 0xc3,0xa7); // lowercase c with cedilla + E2( 0x7c, 0xc3,0xb7); // division symbol + E2( 0x7d, 0xc3,0x91); // uppercase N tilde + E2( 0x7e, 0xc3,0xb1); // lowercase n tilde +#undef E2 + default: + dst[i_dst++] = p_src[i]; + break; + } + } + dst[i_dst++] = '\0'; +} +static bool XdsChangeString( xds_t *h, char **ppsz_dst, const char *psz_new ) +{ + if( *ppsz_dst && psz_new && !strcmp( *ppsz_dst, psz_new ) ) + return false; + if( *ppsz_dst == NULL && psz_new == NULL ) + return false; + + free( *ppsz_dst ); + if( psz_new ) + *ppsz_dst = strdup( psz_new ); + else + *ppsz_dst = NULL; + + h->b_meta_changed = true; + return true; +} + +static void XdsDecodeCurrentFuture( xds_t *h, xds_packet_t *pk ) +{ + xds_meta_program_t *p_prg = h->b_future ? &h->meta.future : &h->meta.current; + char name[2*32+1]; + int i_rating; + + switch( h->i_type ) + { + case 0x03: + XdsStringUtf8( name, pk->p_data, pk->i_data ); + if( XdsChangeString( h, &p_prg->psz_name, name ) ) + { + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Program Name) %d'\n", pk->i_data ); + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> program name %s\n", name ); + } + break; + case 0x05: + i_rating = (pk->p_data[0] & 0x18); + if( i_rating == 0x08 ) + { + /* TPG */ + static const char *pppsz_ratings[8][2] = { + { "None", "No rating (no content advisory)" }, + { "TV-Y", "All Children (no content advisory)" }, + { "TV-Y7", "Directed to Older Children (V = Fantasy Violence)" }, + { "TV-G", "General Audience (no content advisory)" }, + { "TV-PG", "Parental Guidance Suggested" }, + { "TV-14", "Parents Strongly Cautioned" }, + { "TV-MA", "Mature Audience Only" }, + { "None", "No rating (no content advisory)" } + }; + p_prg->rating = XDS_META_PROGRAM_RATING_TPG; + if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[1]&0x07][0] ) ) + { + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data ); + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n", + // pppsz_ratings[pk->p_data[1]&0x07][0], pppsz_ratings[pk->p_data[1]&0x07][1] ); + } + } + else if( i_rating == 0x00 || i_rating == 0x10 ) + { + /* MPAA */ + static const char *pppsz_ratings[8][2] = { + { "N/A", "N/A" }, + { "G", "General Audiences" }, + { "PG", "Parental Guidance Suggested" }, + { "PG-13", "Parents Strongly Cautioned" }, + { "R", "Restricted" }, + { "NC-17", "No one 17 and under admitted" }, + { "X", "No one under 17 admitted" }, + { "NR", "Not Rated" }, + }; + p_prg->rating = XDS_META_PROGRAM_RATING_MPAA; + if( XdsChangeString( h, &p_prg->psz_rating, pppsz_ratings[pk->p_data[0]&0x07][0] ) ) + { + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data ); + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> TPG Rating %s (%s)\n", + // pppsz_ratings[pk->p_data[0]&0x07][0], pppsz_ratings[pk->p_data[0]&0x07][1] ); + } + } + else + { + /* Non US Rating TODO */ + assert( i_rating == 0x18 ); // only left value possible */ + p_prg->rating = XDS_META_PROGRAM_RATING_NONE; + if( XdsChangeString( h, &p_prg->psz_rating, NULL ) ) + { + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Rating) %d'\n", pk->i_data ); + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> 0x%2.2x 0x%2.2x\n", pk->p_data[0], pk->p_data[1] ); + } + } + break; + + default: +#ifdef TY_XDS_DEBUG + fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Current/Future (Unknown 0x%x)'\n", h->i_type ); +#endif + break; + } +} + +static void XdsDecodeChannel( xds_t *h, xds_packet_t *pk ) +{ + char name[2*32+1]; + char chan[2*32+1]; + + switch( h->i_type ) + { + case 0x01: + if( pk->i_data < 2 ) + return; + XdsStringUtf8( name, pk->p_data, pk->i_data ); + if( XdsChangeString( h, &h->meta.psz_channel_name, name ) ) + { + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Name) %d'\n", pk->i_data ); + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> %s\n", name ); + } + break; + + case 0x02: + if( pk->i_data < 4 ) + return; + + XdsStringUtf8( name, pk->p_data, 4 ); + if( XdsChangeString( h, &h->meta.psz_channel_call_letter, name ) ) + { + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data ); + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> call letter %s\n", name ); + } + if( pk->i_data >= 6 ) + { + XdsStringUtf8( chan, &pk->p_data[4], 2 ); + if( XdsChangeString( h, &h->meta.psz_channel_number, chan ) ) + { + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data ); + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> channel number %s\n", chan ); + } + } + else + { + if( XdsChangeString( h, &h->meta.psz_channel_number, NULL ) ) + { + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Network Call Letter)' %d\n", pk->i_data ); + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: ====> no channel number letter anymore\n" ); + } + } + break; + case 0x03: + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Channel Tape Delay)'\n" ); + break; + case 0x04: + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Transmission Signal Identifier)'\n" ); + break; + default: +#ifdef TY_XDS_DEBUG + fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Channel (Unknown 0x%x)'\n", h->i_type ); +#endif + break; + } +} + +static void XdsDecode( xds_t *h, xds_packet_t *pk ) +{ + switch( h->i_class ) + { + case XDS_CLASS_CURRENT: + case XDS_CLASS_FUTURE: + XdsDecodeCurrentFuture( h, pk ); + break; + case XDS_CLASS_CHANNEL: + XdsDecodeChannel( h, pk ); + break; + case XDS_CLASS_MISCELLANEOUS: +#ifdef TY_XDS_DEBUG + fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Miscellaneous'\n" ); +#endif + break; + case XDS_CLASS_PUBLIC_SERVICE: +#ifdef TY_XDS_DEBUG + fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: class 'Public Service'\n" ); +#endif + break; + default: + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS XdsDecode: unknown class\n" ); + break; + } +} + +static void XdsParse( xds_t *h, uint8_t d1, uint8_t d2 ) +{ + /* TODO check parity */ + d1 &= 0x7f; + d2 &= 0x7f; + + /* */ + if( d1 >= 0x01 && d1 <= 0x0e ) + { + const xds_class_t i_class = ( d1 - 1 ) >> 1; + const int i_type = d2; + const bool b_start = d1 & 0x01; + xds_packet_t *pk = &h->pkt[i_class][i_type]; + + if( !b_start && !pk->b_started ) + { + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS Continuying a non started packet, ignoring\n" ); + h->b_xds = false; + return; + } + + h->b_xds = true; + h->i_class = i_class; + h->i_type = i_type; + h->b_future = !b_start; + pk->b_started = true; + if( b_start ) + { + pk->i_data = 0; + pk->i_sum = d1 + d2; + } + } + else if( d1 == 0x0f && h->b_xds ) + { + xds_packet_t *pk = &h->pkt[h->i_class][h->i_type]; + + /* TODO checksum and decode */ + pk->i_sum += d1 + d2; + if( pk->i_sum & 0x7f ) + { + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS invalid checksum, ignoring ---------------------------------\n" ); + pk->b_started = false; + return; + } + if( pk->i_data <= 0 ) + { + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS empty packet, ignoring ---------------------------------\n" ); + pk->b_started = false; + return; + } + + //if( pk->p_data[pk->i_data-1] == 0x40 ) /* Padding byte */ + // pk->i_data--; + XdsDecode( h, pk ); + + /* Reset it */ + pk->b_started = false; + } + else if( d1 >= 0x20 && h->b_xds ) + { + xds_packet_t *pk = &h->pkt[h->i_class][h->i_type]; + + if( pk->i_data+2 > XDS_MAX_DATA_SIZE ) + { + /* Broken -> reinit */ + //fprintf( stderr, "xxxxxxxxxxxxxxxXDS broken, reset\n" ); + h->b_xds = false; + pk->b_started = false; + return; + } + /* TODO check parity bit */ + pk->p_data[pk->i_data++] = d1 & 0x7f; + pk->p_data[pk->i_data++] = d2 & 0x7f; + pk->i_sum += d1+d2; + } + else + { + h->b_xds = false; + } +} + static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 ) { - /* TODO */ + demux_sys_t *p_sys = p_demux->p_sys; + + XdsParse( &p_demux->p_sys->xds, d1, d2 ); + if( p_demux->p_sys->xds.b_meta_changed ) + { + xds_meta_t *m = &p_sys->xds.meta; + vlc_meta_t *p_meta; + vlc_epg_t *p_epg; + + /* Channel meta data */ + p_meta = vlc_meta_New(); + if( m->psz_channel_name ) + vlc_meta_SetPublisher( p_meta, m->psz_channel_name ); + if( m->psz_channel_call_letter ) + vlc_meta_SetTitle( p_meta, m->psz_channel_call_letter ); + if( m->psz_channel_number ) + vlc_meta_AddExtra( p_meta, "Channel number", m->psz_channel_number ); + es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, TY_ES_GROUP, p_meta ); + vlc_meta_Delete( p_meta ); + + /* Event meta data (current/future) */ + p_epg = vlc_epg_New( NULL ); + if( m->current.psz_name ) + { + vlc_epg_AddEvent( p_epg, 0, 0, m->current.psz_name, NULL, NULL ); + //if( m->current.psz_rating ) + // TODO but VLC cannot yet handle rating per epg event + vlc_epg_SetCurrent( p_epg, 0 ); + } + if( m->future.psz_name ) + { + } + if( p_epg->i_event > 0 ) + es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG, TY_ES_GROUP, p_epg ); + vlc_epg_Delete( p_epg ); + } + p_demux->p_sys->xds.b_meta_changed = false; } /* seek to an exact time position within the stream, if possible. @@ -1035,10 +1465,11 @@ static void DemuxDecodeXds( demux_t *p_demux, uint8_t d1, uint8_t d2 ) static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time) { demux_sys_t *p_sys = p_demux->p_sys; - int i, i_seq_entry = 0; + int i_seq_entry = 0; int i_skip_cnt; + unsigned i; long l_cur_pos = stream_Tell(p_demux->s); - int i_cur_part = l_cur_pos / TIVO_PART_LENGTH; + unsigned i_cur_part = l_cur_pos / TIVO_PART_LENGTH; long l_seek_secs = l_seek_time / 1000000000; uint64_t l_fwd_stamp = 1; @@ -1161,8 +1592,8 @@ static int ty_stream_seek_time(demux_t *p_demux, uint64_t l_seek_time) so we need to skip past any stream data prior to the seq_rec in this chunk */ i_skip_cnt = 0; - for (i=0; ii_seq_rec; i++) - i_skip_cnt += p_sys->rec_hdrs[i].l_rec_size; + for (int j=0; ji_seq_rec; j++) + i_skip_cnt += p_sys->rec_hdrs[j].l_rec_size; stream_Read(p_demux->s, NULL, i_skip_cnt); p_sys->i_cur_rec = p_sys->i_seq_rec; //p_sys->l_last_ty_pts = p_sys->rec_hdrs[p_sys->i_seq_rec].l_ty_pts; @@ -1179,7 +1610,7 @@ static void parse_master(demux_t *p_demux) { demux_sys_t *p_sys = p_demux->p_sys; uint8_t mst_buf[32]; - int i, i_map_size; + uint32_t i, i_map_size; int64_t i_save_pos = stream_Tell(p_demux->s); int64_t i_pts_secs; @@ -1190,8 +1621,7 @@ static void parse_master(demux_t *p_demux) entire table directly from the stream into memory in place. */ /* clear the SEQ table */ - if (p_sys->seq_table != NULL) - free(p_sys->seq_table); + free(p_sys->seq_table); /* parse header info */ stream_Read(p_demux->s, mst_buf, 32); @@ -1201,14 +1631,20 @@ static void parse_master(demux_t *p_demux) p_sys->i_seq_table_size = i / (8 + i_map_size); /* parse all the entries */ - p_sys->seq_table = malloc(p_sys->i_seq_table_size * sizeof(ty_seq_table_t)); - for (i=0; ii_seq_table_size; i++) { - stream_Read(p_demux->s, mst_buf, 8 + i_map_size); + p_sys->seq_table = calloc(p_sys->i_seq_table_size, sizeof(ty_seq_table_t)); + if (p_sys->seq_table == NULL) + { + p_sys->i_seq_table_size = 0; + return; + } + for (unsigned i=0; ii_seq_table_size; i++) { + stream_Read(p_demux->s, mst_buf, 8); p_sys->seq_table[i].l_timestamp = U64_AT(&mst_buf[0]); if (i_map_size > 8) { msg_Err(p_demux, "Unsupported SEQ bitmap size in master chunk"); - memset(p_sys->seq_table[i].chunk_bitmask, i_map_size, 0); + stream_Read(p_demux->s, NULL, i_map_size); } else { + stream_Read(p_demux->s, mst_buf + 8, i_map_size); memcpy(p_sys->seq_table[i].chunk_bitmask, &mst_buf[8], i_map_size); } } @@ -1217,14 +1653,16 @@ static void parse_master(demux_t *p_demux) p_sys->l_first_ty_pts = p_sys->seq_table[0].l_timestamp; p_sys->l_final_ty_pts = p_sys->seq_table[p_sys->i_seq_table_size - 1].l_timestamp; - p_sys->b_have_master = VLC_TRUE; + p_sys->b_have_master = true; i_pts_secs = p_sys->l_first_ty_pts / 1000000000; - msg_Dbg( p_demux, "first TY pts in master is %02d:%02d:%02d", - (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) ); + msg_Dbg( p_demux, + "first TY pts in master is %02"PRId64":%02"PRId64":%02"PRId64, + i_pts_secs / 3600, (i_pts_secs / 60) % 60, i_pts_secs % 60 ); i_pts_secs = p_sys->l_final_ty_pts / 1000000000; - msg_Dbg( p_demux, "final TY pts in master is %02d:%02d:%02d", - (int)(i_pts_secs / 3600), (int)((i_pts_secs / 60) % 60), (int)(i_pts_secs % 60) ); + msg_Dbg( p_demux, + "final TY pts in master is %02"PRId64":%02"PRId64":%02"PRId64, + i_pts_secs / 3600, (i_pts_secs / 60) % 60, i_pts_secs % 60 ); /* seek past this chunk */ stream_Seek(p_demux->s, i_save_pos + CHUNK_SIZE); @@ -1242,7 +1680,7 @@ static int probe_stream(demux_t *p_demux) demux_sys_t *p_sys = p_demux->p_sys; const uint8_t *p_buf; int i; - vlc_bool_t b_probe_error = VLC_FALSE; + bool b_probe_error = false; /* we need CHUNK_PEEK_COUNT chunks of data, first one might be a Part header, so ... */ if (stream_Peek( p_demux->s, &p_buf, CHUNK_PEEK_COUNT * CHUNK_SIZE ) < @@ -1265,15 +1703,15 @@ static int probe_stream(demux_t *p_demux) /* the final tally */ if (p_sys->tivo_series == TIVO_SERIES_UNKNOWN) { msg_Err(p_demux, "Can't determine Tivo Series."); - b_probe_error = VLC_TRUE; + b_probe_error = true; } if (p_sys->audio_type == TIVO_AUDIO_UNKNOWN) { msg_Err(p_demux, "Can't determine Tivo Audio Type."); - b_probe_error = VLC_TRUE; + b_probe_error = true; } if (p_sys->tivo_type == TIVO_TYPE_UNKNOWN) { msg_Err(p_demux, "Can't determine Tivo Type (SA/DTivo)."); - b_probe_error = VLC_TRUE; + b_probe_error = true; } return b_probe_error?VLC_EGENERIC:VLC_SUCCESS; } @@ -1287,7 +1725,7 @@ static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk) int i_num_recs, i; ty_rec_hdr_t *p_hdrs; int i_num_6e0, i_num_be0, i_num_9c0, i_num_3c0; - uint32_t i_payload_size; + int i_payload_size; /* skip if it's a Part header */ if( U32_AT( &p_chunk[ 0 ] ) == TIVO_PES_FILEID ) @@ -1303,7 +1741,7 @@ static void analyze_chunk(demux_t *p_demux, const uint8_t *p_chunk) p_chunk += 4; /* skip past rec count & SEQ bytes */ //msg_Dbg(p_demux, "probe: chunk has %d recs", i_num_recs); - p_hdrs = parse_chunk_headers(p_demux, p_chunk, i_num_recs, &i_payload_size); + p_hdrs = parse_chunk_headers(p_chunk, i_num_recs, &i_payload_size); /* scan headers. * 1. check video packets. Presence of 0x6e0 means S1. * No 6e0 but have be0 means S2. @@ -1445,12 +1883,11 @@ static int get_chunk_header(demux_t *p_demux) p_sys->i_seq_rec = p_peek[1]; } p_sys->i_cur_rec = 0; - p_sys->b_first_chunk = VLC_FALSE; + p_sys->b_first_chunk = false; /*msg_Dbg( p_demux, "chunk has %d records", i_num_recs );*/ - if (p_sys->rec_hdrs) - free(p_sys->rec_hdrs); + free(p_sys->rec_hdrs); /* skip past the 4 bytes we "peeked" earlier */ stream_Read( p_demux->s, NULL, 4 ); @@ -1459,11 +1896,11 @@ static int get_chunk_header(demux_t *p_demux) p_hdr_buf = malloc(i_num_recs * 16); if (stream_Read(p_demux->s, p_hdr_buf, i_num_recs * 16) < i_num_recs * 16) { free( p_hdr_buf ); - p_sys->eof = VLC_TRUE; + p_sys->eof = true; return 0; } /* parse them */ - p_sys->rec_hdrs = parse_chunk_headers(p_demux, p_hdr_buf, i_num_recs, + p_sys->rec_hdrs = parse_chunk_headers(p_hdr_buf, i_num_recs, &i_payload_size); free(p_hdr_buf); @@ -1476,7 +1913,7 @@ static int get_chunk_header(demux_t *p_demux) } -static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf, +static ty_rec_hdr_t *parse_chunk_headers( const uint8_t *p_buf, int i_num_recs, int *pi_payload_size) { int i; @@ -1497,23 +1934,21 @@ static ty_rec_hdr_t *parse_chunk_headers( demux_t *p_demux, const uint8_t *p_buf /* marker bit 2 set, so read extended data */ b1 = ( ( ( record_header[ 0 ] & 0x0f ) << 4 ) | ( ( record_header[ 1 ] & 0xf0 ) >> 4 ) ); - b1 &= 0x7f; b2 = ( ( ( record_header[ 1 ] & 0x0f ) << 4 ) | ( ( record_header[ 2 ] & 0xf0 ) >> 4 ) ); - b2 &= 0x7f; - p_rec_hdr->ex1 = b1; - p_rec_hdr->ex2 = b2; + p_rec_hdr->ex[0] = b1; + p_rec_hdr->ex[1] = b2; p_rec_hdr->l_rec_size = 0; p_rec_hdr->l_ty_pts = 0; - p_rec_hdr->b_ext = VLC_TRUE; + p_rec_hdr->b_ext = true; } else { p_rec_hdr->l_rec_size = ( record_header[ 0 ] << 8 | record_header[ 1 ] ) << 4 | ( record_header[ 2 ] >> 4 ); *pi_payload_size += p_rec_hdr->l_rec_size; - p_rec_hdr->b_ext = VLC_FALSE; + p_rec_hdr->b_ext = false; p_rec_hdr->l_ty_pts = U64_AT( &record_header[ 8 ] ); } //fprintf( stderr, "parse_chunk_headers[%d] t=0x%x s=%d\n", i, p_rec_hdr->rec_type, p_rec_hdr->subrec_type );