📄 ty.c
字号:
/***************************************************************************** * ty.c - TiVo ty stream video demuxer for VLC ***************************************************************************** * Copyright (C) 2005 the VideoLAN team * Copyright (C) 2005 by Neal Symms (tivo@freakinzoo.com) - February 2005 * based on code by Christopher Wingert for tivo-mplayer * tivo(at)wingert.org, February 2003 * * $Id$ * * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. * * CODE CHANGES: * v1.0.0 - 24-Feb-2005 - Initial release - Series 1 support ONLY! * v1.0.1 - 25-Feb-2005 - Added fix for bad GOP headers - Neal * v1.0.2 - 26-Feb-2005 - No longer require "seekable" input stream - Neal * v2.0.0 - 21-Mar-2005 - Series 2 support! No AC-3 on S2 DTivo yet. * v2.1.0 - 22-Mar-2005 - Support for AC-3 on S2 DTivo (long ac3 packets) * v3.0.0 - 14-Jul-2005 - Support for skipping fwd/back via VLC hotkeys *****************************************************************************//***************************************************************************** * Preamble *****************************************************************************/#ifdef HAVE_CONFIG_H# include "config.h"#endif#include <vlc_common.h>#include <vlc_plugin.h>#include <vlc_demux.h>#include "vlc_codec.h"#include "vlc_meta.h"#include "vlc_input.h"#include "../codec/cc.h"#include <assert.h>/***************************************************************************** * Module descriptor *****************************************************************************/static int Open ( vlc_object_t * );static void Close( vlc_object_t * );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();/***************************************************************************** * Local prototypes *****************************************************************************/static int Demux ( demux_t * );static int Control( demux_t *, int, va_list );#define SERIES1_PES_LENGTH (11) /* length of audio PES hdr on S1 */#define SERIES2_PES_LENGTH (16) /* length of audio PES hdr on S2 */#define AC3_PES_LENGTH (14) /* length of audio PES hdr for AC3 */#define VIDEO_PES_LENGTH (16) /* length of video PES header */#define DTIVO_PTS_OFFSET (6) /* offs into PES for MPEG PTS on DTivo */#define SA_PTS_OFFSET (9) /* offset into PES for MPEG PTS on SA */#define AC3_PTS_OFFSET (9) /* offset into PES for AC3 PTS on DTivo */#define VIDEO_PTS_OFFSET (9) /* offset into PES for video PTS on all */#define AC3_PKT_LENGTH (1536) /* size of TiVo AC3 pkts (w/o PES hdr) */static const uint8_t ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };static const uint8_t ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };#define CHUNK_PEEK_COUNT (3) /* number of chunks to probe *//* packet types for reference: 2/c0: audio data continued 3/c0: audio packet header (PES header) 4/c0: audio data (S/A only?) 9/c0: audio packet header, AC-3 audio 2/e0: video data continued 6/e0: video packet header (PES header) 7/e0: video sequence header start 8/e0: video I-frame header start a/e0: video P-frame header start b/e0: video B-frame header start c/e0: video GOP header start e/01: closed-caption data e/02: Extended data services data e/03: ipreview data ("thumbs up to record" signal) e/05: UK Teletext*/#define TIVO_PES_FILEID ( 0xf5467abd )#define TIVO_PART_LENGTH ( 0x20000000 ) /* 536,870,912 bytes */#define CHUNK_SIZE ( 128 * 1024 )typedef struct{ long l_rec_size; uint8_t ex1, ex2; uint8_t rec_type; uint8_t subrec_type; bool b_ext; uint64_t l_ty_pts; /* TY PTS in the record header */} ty_rec_hdr_t;typedef struct{ uint64_t l_timestamp; uint8_t chunk_bitmask[8];} ty_seq_table_t;typedef enum{ TIVO_TYPE_UNKNOWN, TIVO_TYPE_SA, TIVO_TYPE_DTIVO} tivo_type_t;typedef enum{ TIVO_SERIES_UNKNOWN, TIVO_SERIES1, TIVO_SERIES2} tivo_series_t;typedef enum{ TIVO_AUDIO_UNKNOWN, TIVO_AUDIO_AC3, 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 */ es_out_id_t *p_audio; /* holds either ac3 or mpeg codec ptr */ 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 */ 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 */ int i_Pes_Length; /* Length of Audio PES header */ int i_Pts_Offset; /* offset into audio PES of PTS */ uint8_t pes_buffer[20]; /* holds incomplete pes headers */ int i_pes_buf_cnt; /* how many bytes in our buffer */ size_t l_ac3_pkt_size; /* len of ac3 pkt we've seen so far */ uint64_t l_last_ty_pts; /* last TY timestamp we've seen */ //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 */ mtime_t firstAudioPTS; mtime_t lastAudioPTS; mtime_t lastVideoPTS; ty_rec_hdr_t *rec_hdrs; /* record headers array */ int i_cur_rec; /* current record in this chunk */ 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 */ bool eof; bool b_first_chunk;};static int get_chunk_header(demux_t *);static mtime_t get_pts( const uint8_t *buf );static int find_es_header( const uint8_t *header, const uint8_t *buffer, int i_search_len );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( 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);static void parse_master(demux_t *p_demux);static int DemuxRecVideo( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );static int DemuxRecAudio( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );static int DemuxRecCc( demux_t *p_demux, ty_rec_hdr_t *rec_hdr, block_t *p_block_in );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 * * here's what we do: * 1. peek at the first 12 bytes of the stream for the * magic TiVo PART header & stream type & chunk size * 2. if it's not there, error with VLC_EGENERIC * 3. set up video (mpgv) codec * 4. return VLC_SUCCESS */static int Open(vlc_object_t *p_this){ demux_t *p_demux = (demux_t *)p_this; demux_sys_t *p_sys; es_format_t fmt; const uint8_t *p_peek; int i; /* peek at the first 12 bytes. */ /* for TY streams, they're always the same */ if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 ) return VLC_EGENERIC; if ( U32_AT(p_peek) != TIVO_PES_FILEID || U32_AT(&p_peek[4]) != 0x02 || U32_AT(&p_peek[8]) != CHUNK_SIZE ) { if( !p_demux->b_force && !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..." ); } /* at this point, we assume we have a valid TY stream */ msg_Dbg( p_demux, "valid TY stream detected" ); /* Set exported functions */ p_demux->pf_demux = Demux; p_demux->pf_control = Control; /* create our structure that will hold all data */ p_demux->p_sys = p_sys = malloc(sizeof(demux_sys_t)); 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 = 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); p_sys->tivo_type = TIVO_TYPE_UNKNOWN; p_sys->audio_type = TIVO_AUDIO_UNKNOWN; p_sys->tivo_series = TIVO_SERIES_UNKNOWN; p_sys->i_Pes_Length = 0; p_sys->i_Pts_Offset = 0; p_sys->l_ac3_pkt_size = 0; /* see if this stream is seekable */ stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable ); if (probe_stream(p_demux) != VLC_SUCCESS) { //TyClose(p_demux); return VLC_EGENERIC; } if (!p_sys->b_have_master) msg_Warn(p_demux, "No master chunk found; seeking will be limited."); /* register the proper audio codec */ if (p_sys->audio_type == TIVO_AUDIO_MPEG) { es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'm', 'p', 'g', 'a' ) ); } 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 );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -