ConvertStream.c
/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following functions.
- main
- convert_stream
- parse_chunk
- usage
/* Convert a tyStream file into separate mpeg2 audio and video stream.
*
* Gary Steele <gsteele@mit.edu>, 21 June 2001
*
* Adapted from Playstream.c by D18C7DB Jan 2001
*
* released under the Gnu General Public License
*
* Thinking:
*
* ExtractStream could not extract proper video and audio stream from
* my TiVo (Philips HDR112, software version 1.3.0), although it was
* able to extract the raw tyStream with no errors using the -s
* option.
*
* Extracting the tyStream and converting it to mpeg data is really
* two separate steps (read_chunk, parse_chunk).
*
* This simple code is desinged to do only the second operation.
*
* The only significant modification is that some of the "records"/
* subchunks extracted in parrse_chunk were found not to actually be
* part of the video and audio streams.
*
* The resulting m2a file plays with no errors with mpg123, and the
* resulting m2v file with no errors using the libmpeg2 library
* (mplayer, or mpeg2dec, see linuxvideo.org). The m2v file still does
* not play properly using the libsmpeg 0.4.3 library (plaympeg, gtv):
* gets the video size, frame rate, and time right, but displays only
* a black screen.
*
* Multiplex these together into an MPEG2 Program stream using mplex
* with the -f 3 (mpeg2) format option:
*
* mplex -f 3 -o test.mpg test.m2a test.m2v
*
* The resulting mpg file will play properly with mplayer including
* nicely synchronized audio and video streams.
*
* Untested using a tyStream from TiVO software version 2.0
*
* Many thanks to the authors of PlayStream.c and
* ExtractStream.c.
*
* version 0.1: 21 June 2001 initial release
* */
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/fs.h>
#include <asm/unistd.h>
#define VIDEO_ID 0xE0
#define AUDIO_ID 0xC0
#define CHECKSUM 0x3FC
#define MAGIC_SIGNATURE 0x91231ebc
#define CHUNK_SIZE (0x20000)
#define BUFFER_SIZE 0x2000
#define PERMS 0666
// Global variabls
int audio_offset = 0;
int video_offset = 0;
int verbose=0;
// Function declarations
void convert_stream(int stream_in, int video_out, int audio_out);
static void usage(void);
static void parse_chunk(unsigned char buf[CHUNK_SIZE], int video_out, int audio_out);
// Main
int main(int argc, char *argv[]) {
/* [<][>][^][v][top][bottom][index][help] */
int audio_out, video_out, stream_in;
if (argc < 4) usage();
if (argc > 4) {
if (argv[4][0]=='-') {
if (argv[4][1]=='v') verbose=1;
else usage();
}
else usage();
}
if (verbose) printf( "Opening file: %s\n", argv[1]);
stream_in = open(argv[1], O_RDONLY);
if (stream_in == -1) {
printf("Failed to open input file %s\n", argv[1]);
exit(1);
}
if (verbose) printf("Opening file: %s\n", argv[2]);
audio_out = creat(argv[2], PERMS);
if (audio_out == -1) {
printf("Failed to open output file %s\n", argv[2]);
exit(1);
}
if (verbose) printf( "Opening file: %s\n", argv[3]);
video_out = creat(argv[3], PERMS);
if (video_out == -1) {
printf("Failed to open output file %s\n", argv[3]);
exit(1);
}
convert_stream(stream_in, video_out, audio_out);
close(stream_in);
close(video_out);
close(audio_out);
}
void convert_stream(int stream_in, int video_out, int audio_out) {
/* [<][>][^][v][top][bottom][index][help] */
unsigned char buf[CHUNK_SIZE];
unsigned long index, i;
int n;
while ( n = read(stream_in, buf, CHUNK_SIZE) > 0 ) {
// dbug(printf( "%i bytes read from input\n", n));
parse_chunk(buf, video_out, audio_out);
}
}
static void parse_chunk(unsigned char buf[CHUNK_SIZE], int video_out, int audio_out) {
/* [<][>][^][v][top][bottom][index][help] */
int num_recs = buf[0];
int i,j,chksum, ret;
unsigned char *p;
int total_misc=0;
int total_av=0;
long int ofs=0;
if (verbose) {
printf("Records=%x\n", num_recs);
}
if (verbose) {
int j = 0;
int l = 4;
int k = 4 + (num_recs<<4);
printf("Chunk output:\n");
for (i=0; i<l; i++) // the packet header bit, tells us how many records
printf(" %02x", buf[i]);
printf("\n");
for (i=l; i < k; i++) { // the record header, tell us what's in the record
printf(" %02x", buf[i]); // and how big it is, anything else?
if (j == 15) { printf("\n"); j=0; }
else j++;
}
printf("\n");
for (i=k ; i<k+200; i++) {
printf(" %02x", buf[i]);
if (j == 15) {
printf("\n");
j=0;
}
else j++;
}
printf("\n");
}
/* each 128k chunk starts with N 16 byte record headers that tell you
what sorts of things are in the chunk */
p = &buf[4];
ofs = 4 + (num_recs<<4);
for (i=0; i<num_recs; i++) {
unsigned long size = (p[0]<<8 | p[1])<<4 | (p[2]>>4);
unsigned int type = p[3];
int k;
chksum=0;
for (j=0; j<8; j++) chksum += p[j];
if (chksum != CHECKSUM) {
if (verbose) printf("bad checksum, skipping block.\n");
return;
}
if (verbose) printf("\nRecord %x: type %02x size %-x\n", i, type, size);
p += 16;
if (type == VIDEO_ID) {
int j = 0;
if (verbose) {
printf("Writing record to video at offset %x\n", video_offset);
printf("Contents:\n");
for (k=0 ; k<size && k<100 ; k++) {
printf(" %02x", buf[ofs+k]);
if (j == 15) { printf("\n"); j=0; }
else j++;
}
}
// There seems to be some records that have size "10" that are not actually
// part of the stream: these records are causing errors in mpeg2dec of video
// stream and mpg123 of audio stream. Identified it by matching the offset of
// the offending bits found by mpg123 to the record that was put into the
// m2a file at that offset. These are 16 bytes, and contain the video/audio
// id at the fourth byte of the record, and are found at the start of a series
// of audio records, or video records.
if (buf[ofs+3] == VIDEO_ID){
if (verbose) printf("Looks like a bad record, rejecting\n");
}
else write(video_out, &buf[ofs], size);
video_offset += size;
ofs += size;
}
if (type == AUDIO_ID) {
int j = 0;
if (verbose) {
printf("Writing record to audio at offset %x\n", audio_offset);
printf("Contents:\n");
for (k=0 ; k<size && k<100 ; k++) {
printf(" %02x", buf[ofs+k]);
if (j == 15) { printf("\n"); j=0; }
else j++;
}
}
if (buf[ofs+3] == AUDIO_ID) {
if (verbose) printf("Looks like a bad record, rejecting\n");
}
else write(audio_out, &buf[ofs], size);
audio_offset += size;
ofs += size;
}
if ((type==1) || (type==2)) {
total_misc += size;
} else {
total_av+= size;
}
if (verbose) printf("\n");
}
if (verbose) printf("Misc = %x, A/V = %x\n"
"-------------------\n\n", total_misc, total_av);
}
static void usage(void) {
/* [<][>][^][v][top][bottom][index][help] */
printf("
usage: ConvertStream <tyStream> <audio file> <video file> [-v]
ConvertStream takes a TiVo tyStream as an input, outputs the MPEG2
video stream to <video file>, and the MPEG2 audio stream to <audio
file>. Use the -v flag for verboseness.\n\n");
exit(0);
}