You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
378 lines
8.6 KiB
378 lines
8.6 KiB
15 years ago
|
/*
|
||
|
demux transport stream
|
||
|
Copyright (C) 2001 Martin Vogt
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU Library General Public License as published by
|
||
|
the Free Software Foundation.
|
||
|
|
||
|
For more information look at the file COPYRIGHT in this package
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
#include "tsSystemStream.h"
|
||
|
|
||
|
|
||
|
#define PKT_SIZE 188
|
||
|
|
||
|
|
||
|
TSSystemStream::TSSystemStream(InputStream* input) {
|
||
|
this->input=input;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
TSSystemStream::~TSSystemStream() {
|
||
|
}
|
||
|
|
||
|
int TSSystemStream::read(char* ptr,int bytes) {
|
||
|
if (input->read(ptr,bytes) != bytes) {
|
||
|
return false;
|
||
|
}
|
||
|
paket_read+=bytes;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
int TSSystemStream::getByteDirect() {
|
||
|
unsigned char byte;
|
||
|
if (input->read((char*)&byte,1) != 1) {
|
||
|
return -1;
|
||
|
}
|
||
|
paket_read++;
|
||
|
return (int)byte;
|
||
|
}
|
||
|
|
||
|
|
||
|
// nuke bytes modulo 10
|
||
|
int TSSystemStream::nukeBytes(int bytes) {
|
||
|
// nukebuffer
|
||
|
char nuke[10];
|
||
|
|
||
|
while(bytes > 0) {
|
||
|
int doNuke=10;
|
||
|
if (bytes < 10) doNuke=bytes;
|
||
|
if (input->read((char*)&nuke,doNuke) != doNuke) {
|
||
|
return false;
|
||
|
}
|
||
|
bytes-=doNuke;
|
||
|
paket_read+=doNuke;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
int TSSystemStream::skipNextByteInLength() {
|
||
|
int length=getByteDirect();
|
||
|
if (length < 0) return false;
|
||
|
|
||
|
/*
|
||
|
* Skip read byte in length, but check paket_size
|
||
|
*/
|
||
|
if (paket_read+length > PKT_SIZE) {
|
||
|
printf ("demux error! invalid payload size %d\n",length);
|
||
|
return false;
|
||
|
}
|
||
|
if (nukeBytes(length) == false) return false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
int TSSystemStream::processStartCode(MpegSystemHeader* mpegHeader) {
|
||
|
paket_len=PKT_SIZE;
|
||
|
paket_read=4; // startcode=4 bytes
|
||
|
|
||
|
mpegHeader->setTSPacketLen(0);
|
||
|
mpegHeader->setPacketID(_PAKET_ID_NUKE);
|
||
|
|
||
|
unsigned int pid=mpegHeader->getPid();
|
||
|
unsigned int pmtPID=mpegHeader->getPMTPID();
|
||
|
if ( (pmtPID == INVALID_PID) && (pid != 0)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ((mpegHeader->getAdaption_field_control() & 0x1)==0) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Has a payload! Calculate & check payload length.
|
||
|
*/
|
||
|
if (mpegHeader->getAdaption_field_control() & 0x2) {
|
||
|
if (skipNextByteInLength() == false) return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Do the demuxing in based on the pids
|
||
|
*/
|
||
|
|
||
|
if (pid == mpegHeader->getPMTPID()) {
|
||
|
return demux_ts_pmt_parse(mpegHeader);
|
||
|
}
|
||
|
|
||
|
if (pid == 0) {
|
||
|
return demux_ts_pat_parse(mpegHeader);
|
||
|
}
|
||
|
//
|
||
|
// ok, no the only things left to do is the
|
||
|
// decision what to do with the packet
|
||
|
//
|
||
|
|
||
|
mpegHeader->setTSPacketLen(paket_len-paket_read);
|
||
|
|
||
|
if (pid == 0x1fff) {
|
||
|
printf("Nuke Packet\n");
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
MapPidStream* mapPidStream=mpegHeader->lookup(pid);
|
||
|
|
||
|
if (mapPidStream->isValid == true) {
|
||
|
// set to something different from "NUKE_PAKET"
|
||
|
mpegHeader->setPacketID(_PAKET_ID_AUDIO_1);
|
||
|
return true;
|
||
|
}
|
||
|
// well the raw stream has a TS header and a PID, but we have not a valid
|
||
|
// mapping pid->tsType.
|
||
|
// we return false here to have a recovery if our
|
||
|
// previous decision that we actually have a TS stream was wrong.
|
||
|
|
||
|
// force resync
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* NAME demux_ts_pmt_parse
|
||
|
*
|
||
|
* Parse a PMT. The PMT is expected to be exactly one section long,
|
||
|
* and that section is expected to be contained in a single TS packet.
|
||
|
*
|
||
|
* In other words, the PMT is assumed to describe a reasonable number of
|
||
|
* video, audio and other streams (with descriptors).
|
||
|
*/
|
||
|
|
||
|
int TSSystemStream::demux_ts_pmt_parse(MpegSystemHeader* mpegHeader) {
|
||
|
int sectionLength=processSection(mpegHeader);
|
||
|
if (sectionLength == 0) return false;
|
||
|
|
||
|
//?
|
||
|
if (nukeBytes(2) == false) return false;
|
||
|
sectionLength-=2;
|
||
|
|
||
|
/*
|
||
|
* ES definitions start here...we are going to learn upto one video
|
||
|
* PID and one audio PID.
|
||
|
*/
|
||
|
|
||
|
|
||
|
unsigned char pkt[2];
|
||
|
if (read((char*)pkt,2) == false) return false;
|
||
|
sectionLength-=2;
|
||
|
|
||
|
unsigned int programInfoLength;
|
||
|
|
||
|
programInfoLength=(((unsigned int)pkt[0] & 0x0f) << 8) | pkt[1];
|
||
|
if (paket_read+programInfoLength > paket_len) {
|
||
|
printf ("demux error! PMT with inconsistent progInfo length\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (nukeBytes(programInfoLength) == false) return false;
|
||
|
sectionLength-=programInfoLength;
|
||
|
|
||
|
return processElementary(sectionLength,mpegHeader);
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
return false on error or section length info on success
|
||
|
*/
|
||
|
int TSSystemStream::processSection(MpegSystemHeader* mpegHeader) {
|
||
|
unsigned int pus=mpegHeader->getPayload_unit_start_indicator();
|
||
|
|
||
|
/*
|
||
|
* A PAT in a single section should start with a payload unit start
|
||
|
* indicator set.
|
||
|
*/
|
||
|
if (pus==0) {
|
||
|
printf ("demux error! PAT without payload unit start\n");
|
||
|
return false;
|
||
|
}
|
||
|
/*
|
||
|
* PAT packets with a pus start with a pointer. Skip it!
|
||
|
*/
|
||
|
if (skipNextByteInLength() == false) return false;
|
||
|
|
||
|
// ??
|
||
|
if (nukeBytes(1) == false) return false;
|
||
|
|
||
|
// read sectionLength
|
||
|
unsigned char pkt[2];
|
||
|
if (read((char*)pkt,2) ==false) return false;
|
||
|
|
||
|
|
||
|
int sectionLength=(((unsigned int)pkt[0] & 0x3) << 8) | pkt[1];
|
||
|
if (paket_read+sectionLength > PKT_SIZE) {
|
||
|
printf ("demux error! invalid section size %d\n",sectionLength);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// ??
|
||
|
if (nukeBytes(2) == false) return false;
|
||
|
|
||
|
int byte=getByteDirect();
|
||
|
if (byte < 0) return false;
|
||
|
|
||
|
if ((byte & 0x01) == false) {
|
||
|
/*
|
||
|
* Not current!
|
||
|
*/
|
||
|
return false;
|
||
|
}
|
||
|
if (read((char*)pkt,2) == false) return false;
|
||
|
|
||
|
if ((pkt[0]) || (pkt[1])) {
|
||
|
printf ("demux error! PAT with invalid section %02x of %02x\n",
|
||
|
pkt[0], pkt[1]);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* TBD: at this point, we should check the CRC. Its not that expensive, and
|
||
|
* the consequences of getting it wrong are dire!
|
||
|
*/
|
||
|
return sectionLength-5;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* NAME demux_ts_pat_parse
|
||
|
*
|
||
|
* Parse a PAT. The PAT is expected to be exactly one section long,
|
||
|
* and that section is expected to be contained in a single TS packet.
|
||
|
*
|
||
|
* The PAT is assumed to contain a single program definition, though
|
||
|
* we can cope with the stupidity of SPTSs which contain NITs.
|
||
|
*/
|
||
|
|
||
|
int TSSystemStream::demux_ts_pat_parse(MpegSystemHeader* mpegHeader) {
|
||
|
int sectionLength=processSection(mpegHeader);
|
||
|
if (sectionLength == 0) return false;
|
||
|
|
||
|
return processPrograms(sectionLength,mpegHeader);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Process all programs in the program loop.
|
||
|
*/
|
||
|
int TSSystemStream::processPrograms(int sectionLength,
|
||
|
MpegSystemHeader* mpegHeader) {
|
||
|
int programs=sectionLength / 4;
|
||
|
int i;
|
||
|
// what happens with the last 4 byte?
|
||
|
// seems they have no meaning?
|
||
|
programs--;
|
||
|
|
||
|
for(i=0;i<programs;i++) {
|
||
|
unsigned char program[4];
|
||
|
if (read((char*)program,4) == false) return false;
|
||
|
|
||
|
|
||
|
unsigned int programNumber;
|
||
|
unsigned int pmtPid;
|
||
|
unsigned int programCount;
|
||
|
programNumber = ((unsigned int)program[0] << 8) | program[1];
|
||
|
|
||
|
/*
|
||
|
* Skip NITs completely.
|
||
|
*/
|
||
|
if (!programNumber)
|
||
|
continue;
|
||
|
|
||
|
pmtPid = (((unsigned int)program[2] & 0x1f) << 8) | program[3];
|
||
|
|
||
|
|
||
|
/*
|
||
|
* If we have yet to learn our program number, then learn it.
|
||
|
*/
|
||
|
|
||
|
if (mpegHeader->getProgramNumber() == INVALID_PROGRAM) {
|
||
|
mpegHeader->setProgramNumber(programNumber);
|
||
|
mpegHeader->setPMTPID(pmtPid);
|
||
|
}
|
||
|
|
||
|
if (mpegHeader->getProgramNumber() != programNumber) {
|
||
|
printf("demux error! MPTS: programNumber=%u pmtPid=%04x\n",
|
||
|
programNumber, pmtPid);
|
||
|
}
|
||
|
|
||
|
if (mpegHeader->getPMTPID() != pmtPid) {
|
||
|
printf("pmtPid changed %04x\n", pmtPid);
|
||
|
mpegHeader->setPMTPID(pmtPid);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
// nuke last four bytes
|
||
|
if (nukeBytes(4) == false) return false;
|
||
|
|
||
|
//
|
||
|
// now we can nuke the rest of the PAKET_SIZE (188 byte)
|
||
|
//
|
||
|
mpegHeader->setTSPacketLen(paket_len-paket_read);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
int TSSystemStream::processElementary(int sectionLength,
|
||
|
MpegSystemHeader* mpegHeader) {
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Extract the elementary streams.
|
||
|
*/
|
||
|
int mediaIndex=0;
|
||
|
// what happens with the last 4 byte?
|
||
|
// seems they have no meaning?
|
||
|
while (sectionLength > 4) {
|
||
|
unsigned int streamInfoLength;
|
||
|
unsigned char stream[5];
|
||
|
if (read((char*)stream,5) == false) return false;
|
||
|
sectionLength-=5;
|
||
|
|
||
|
unsigned int pid;
|
||
|
pid = (((unsigned int)stream[1] & 0x1f) << 8) | stream[2];
|
||
|
|
||
|
streamInfoLength = (((unsigned int)stream[3] & 0xf) << 8) | stream[4];
|
||
|
if(paket_read+streamInfoLength > paket_len) {
|
||
|
printf ("demux error! PMT with inconsistent streamInfo length\n");
|
||
|
return false;
|
||
|
}
|
||
|
mpegHeader->insert(pid,stream[0],mpegHeader);
|
||
|
|
||
|
}
|
||
|
|
||
|
// nuke last four bytes
|
||
|
if (nukeBytes(4) == false) return false;
|
||
|
//
|
||
|
// now we can nuke the rest of the PAKET_SIZE (188 byte)
|
||
|
//
|
||
|
mpegHeader->setTSPacketLen(paket_len-paket_read);
|
||
|
|
||
|
//
|
||
|
// now we can be sure that we have in fact an TS stream
|
||
|
// so, switch to MPEG2 PES now
|
||
|
mpegHeader->setMPEG2(true);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|