📄 ty.c
字号:
/***************************************************************************** * ty.c - TiVo ty stream video demuxer for VLC ***************************************************************************** * Copyright (C) 2005 VideoLAN * 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: ty.c 11257 2005-06-02 17:06:00Z fkuehne $ * * 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. * * 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 *****************************************************************************//***************************************************************************** * Preamble *****************************************************************************/#include <stdlib.h>#include <vlc/vlc.h>#include <vlc/input.h>#include "vlc_codec.h"#define SERIES1_PES_LENGTH (11)#define SERIES2_PES_LENGTH (16)#define AC3_PES_LENGTH (14)#define DTIVO_PTS_OFFSET (6)#define SA_PTS_OFFSET (9)#define AC3_PTS_OFFSET (9)static const unsigned char ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };static const unsigned char ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };static const unsigned char ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };/***************************************************************************** * Local prototypes *****************************************************************************/static int get_chunk_header(demux_t *);static void setup_audio_streams(char, demux_t *);static mtime_t get_pts( unsigned char *buf );static int find_es_header( unsigned const char *header, unsigned char *buffer, int bufferSize, int *esOffset1 );static int ty_stream_seek(demux_t *p_demux, double seek_pct);static int TyOpen (vlc_object_t *);static void TyClose(vlc_object_t *);static int TyDemux(demux_t *);static int Control(demux_t *, int, va_list);/***************************************************************************** * Module descriptor *****************************************************************************/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", 8); /* 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(TyOpen, TyClose); add_shortcut("ty"); add_shortcut("tivo");vlc_module_end();/* 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)*/#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; unsigned char ex1, ex2; unsigned char rec_type; unsigned char subrec_type; char b_ext;} ty_rec_hdr_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 */ int i_chunk_count; int i_stuff_cnt; size_t i_stream_size; /* size of input stream (if known) */ vlc_bool_t b_seekable; /* is this stream seekable? */ int tivoType; /* 1 = SA, 2 = DTiVo */ vlc_bool_t b_mpeg_audio; /* true if we're using MPEG audio */ uint8_t pes_buffer[20]; /* holds incomplete pes headers */ int i_pes_buf_cnt; /* how many bytes in our buffer */ 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 */ vlc_bool_t eof; vlc_bool_t b_first_chunk;};/* * TyOpen: 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 TyOpen(vlc_object_t *p_this){ demux_t *p_demux = (demux_t *)p_this; demux_sys_t *p_sys; es_format_t fmt; uint8_t *p_peek; /* 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 ) { /* doesn't look like a TY file... */ char *psz_ext = strrchr(p_demux->psz_path, '.'); if( !p_demux->b_force && (!psz_ext || strcasecmp(psz_ext, ".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 = TyDemux; 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 = VLC_TRUE; p_sys->firstAudioPTS = -1; p_sys->i_stream_size = stream_Size(p_demux->s); p_sys->b_mpeg_audio = VLC_FALSE; /* see if this stream is seekable */ stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable ); /* TODO: read first chunk & parse first audio PTS, then (if seekable) * seek to last chunk & last record; read its PTS and compute * overall program time. Also determine Tivo type. */ /* NOTE: we wait to create the audio ES until we know what * audio type we have. */ p_sys->p_audio = NULL; /* register the video stream */ es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( 'm', 'p', 'g', 'v' ) ); p_sys->p_video = es_out_Add( p_demux->out, &fmt );#if 0 /* register the CC decoder */ es_format_Init( &fmt, SPU_ES, VLC_FOURCC('s', 'u', 'b', 't')); p_sys->p_subt_es = es_out_Add(p_demux->out, &fmt);#endif return VLC_SUCCESS;}/* set up audio codec. * this will be called once we determine audio type */static void setup_audio_streams(char stream_type, demux_t *p_demux){ demux_sys_t *p_sys = p_demux->p_sys; es_format_t fmt; if (stream_type == 'A') { /* AC3 audio detected */ es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', '5', '2', ' ' ) ); p_sys->tivoType = 2; /* AC3 is only on dtivo */ } else { /* assume MPEG */ es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'm', 'p', 'g', 'a' ) ); p_sys->b_mpeg_audio = VLC_TRUE; } /* register the chosen audio output codec */ p_sys->p_audio = es_out_Add( p_demux->out, &fmt );}/* =========================================================================== *//* Compute Presentation Time Stamp (PTS) * Assume buf points to beginning of PTS */static mtime_t get_pts( unsigned char *buf ){ mtime_t i_pts; i_pts = ((mtime_t)(buf[0]&0x0e ) << 29)| (mtime_t)(buf[1] << 22)| ((mtime_t)(buf[2]&0xfe) << 14)| (mtime_t)(buf[3] << 7)| (mtime_t)(buf[4] >> 1); i_pts *= 100 / 9; /* convert PTS (90Khz clock) to microseconds */ return i_pts;}/* =========================================================================== */static int find_es_header( unsigned const char *header, unsigned char *buffer, int bufferSize, int *esOffset1 ){ int count; *esOffset1 = -1; for( count = 0 ; count < bufferSize ; count++ ) { if ( ( buffer[ count + 0 ] == header[ 0 ] ) && ( buffer[ count + 1 ] == header[ 1 ] ) && ( buffer[ count + 2 ] == header[ 2 ] ) && ( buffer[ count + 3 ] == header[ 3 ] ) ) { *esOffset1 = count; return 1; } } return( -1 );}/* =========================================================================== *//* check if we have a full PES header, if not, then save what we have. * this is called when audio-start packets are encountered. * Returns: * 1 partial PES hdr found, some audio data found (buffer adjusted), * -1 partial PES hdr found, no audio data found * 0 otherwise (complete PES found, pts extracted, pts set, buffer adjusted) *//* TODO: fix it so it works with S2 / SA / DTivo / HD etc... */static int check_sync_pes( demux_t *p_demux, block_t *p_block, int32_t offset, int32_t rec_len ){ demux_sys_t *p_sys = p_demux->p_sys; int pts_offset; int pes_length = p_sys->b_mpeg_audio?SERIES1_PES_LENGTH:AC3_PES_LENGTH; if( p_sys->tivoType == 1 ) { /* SA tivo */ pts_offset = SA_PTS_OFFSET; } else { /* DTivo */ pts_offset = p_sys->b_mpeg_audio?DTIVO_PTS_OFFSET:AC3_PTS_OFFSET; } if ( offset < 0 || offset + pes_length > rec_len ) { /* entire PES header not present */ msg_Dbg( p_demux, "PES header at %d not complete in record. storing.", offset ); /* save the partial pes header */ if( offset < 0 ) { /* no header found, fake some 00's (this works, believe me) */ memset( p_sys->pes_buffer, 4, 0 ); p_sys->i_pes_buf_cnt = 4; if( rec_len > 4 ) msg_Err( p_demux, "PES header not found in record of %d bytes!", rec_len ); return -1; } /* copy the partial pes header we found */ memcpy( p_sys->pes_buffer, p_block->p_buffer + offset, rec_len - offset ); p_sys->i_pes_buf_cnt = rec_len - offset;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -