Implemented cVideoRepacker in remux.c to make sure every PES packet contains only data from one frame

This commit is contained in:
Klaus Schmidinger 2005-06-04 14:49:25 +02:00
parent a8599c451e
commit 60a35366dd
3 changed files with 360 additions and 2 deletions

View File

@ -955,6 +955,8 @@ Reinhard Nissl <rnissl@gmx.de>
for fixing a typo in detecting UTF-8
for fixing handling fragments of less than 4 byte in cPesAssembler
for some rearrangements in cDvbPlayer::Action() to avoid lockups on NPTL systems
for implementing cVideoRepacker in remux.c to make sure every PES packet contains
only data from one frame
Richard Robson <richard_robson@beeb.net>
for reporting freezing replay if a timer starts while in Transfer Mode from the

View File

@ -3585,3 +3585,5 @@ Video Disk Recorder Revision History
- Fixed handling 'summary.vdr' files with more than two empty lines (thanks to
Christian Jacobsen for reporting this one).
- Improved resetting CAM connections (thanks to Marco Schlüßler).
- Implemented cVideoRepacker in remux.c to make sure every PES packet contains
only data from one frame (thanks to Reinhard Nissl).

358
remux.c
View File

@ -11,7 +11,7 @@
* The cDolbyRepacker code was originally written by Reinhard Nissl <rnissl@gmx.de>,
* and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de.
*
* $Id: remux.c 1.33 2005/03/20 13:18:15 kls Exp $
* $Id: remux.c 1.34 2005/06/04 14:49:25 kls Exp $
*/
#include "remux.h"
@ -32,10 +32,363 @@ public:
virtual void Reset(void) {}
virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) = 0;
virtual int BreakAt(const uchar *Data, int Count) = 0;
virtual int QuerySnoopSize(void) { return 0; }
void SetMaxPacketSize(int MaxPacketSize) { maxPacketSize = MaxPacketSize; }
void SetSubStreamId(uint8_t SubStreamId) { subStreamId = SubStreamId; }
};
// --- cVideoRepacker --------------------------------------------------------
class cVideoRepacker : public cRepacker {
private:
int skippedBytes;
int packetTodo;
uchar fragmentData[6 + 65535 + 3];
int fragmentLen;
uchar pesHeader[6 + 3 + 255 + 3];
int pesHeaderLen;
uchar pesHeaderBackup[6 + 3 + 255];
int pesHeaderBackupLen;
uint32_t scanner;
enum eState {
syncing,
findPicture,
scanPicture
} state;
uint32_t localScanner;
int localStart;
bool PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
public:
cVideoRepacker(void);
virtual void Reset(void);
virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
virtual int BreakAt(const uchar *Data, int Count);
virtual int QuerySnoopSize() { return 4; }
};
cVideoRepacker::cVideoRepacker(void)
{
Reset();
}
void cVideoRepacker::Reset(void)
{
skippedBytes = 0;
packetTodo = maxPacketSize - 6 - 3;
fragmentLen = 0;
pesHeaderLen = 0;
pesHeaderBackupLen = 0;
scanner = 0xFFFFFFFF;
state = syncing;
localStart = -1;
}
bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
{
// enter packet length into PES header ...
if (fragmentLen > 0) { // ... which is contained in the fragment buffer
// determine PES packet payload
int PacketLen = fragmentLen + Count - 6;
fragmentData[ 4 ] = PacketLen >> 8;
fragmentData[ 5 ] = PacketLen & 0xFF;
// amount of data to put into result buffer: a negative Count value means
// to strip off any partially contained start code.
int Bite = fragmentLen + (Count >= 0 ? 0 : Count);
// put data into result buffer
int n = ResultBuffer->Put(fragmentData, Bite);
if (n != Bite) {
Reset();
return false;
}
fragmentLen = 0;
}
else if (pesHeaderLen > 0) { // ... which is contained in the PES header buffer
int PacketLen = pesHeaderLen + Count - 6;
pesHeader[ 4 ] = PacketLen >> 8;
pesHeader[ 5 ] = PacketLen & 0xFF;
// amount of data to put into result buffer: a negative Count value means
// to strip off any partially contained start code.
int Bite = pesHeaderLen + (Count >= 0 ? 0 : Count);
// put data into result buffer
int n = ResultBuffer->Put(pesHeader, Bite);
if (n != Bite) {
Reset();
return false;
}
pesHeaderLen = 0;
}
// append further payload
if (Count > 0) {
// amount of data to put into result buffer
int Bite = Count;
// put data into result buffer
int n = ResultBuffer->Put(Data, Bite);
if (n != Bite) {
Reset();
return false;
}
}
// we did it ;-)
return true;
}
int cVideoRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
{
// reset local scanner
localStart = -1;
// check for MPEG 2
if ((Data[6] & 0xC0) != 0x80)
return 0;
// backup PES header
if (Data[6] != 0x80 || Data[7] != 0x00 || Data[8] != 0x00) {
pesHeaderBackupLen = 6 + 3 + Data[8];
memcpy(pesHeaderBackup, Data, pesHeaderBackupLen);
}
// skip PES header
int done = 6 + 3 + Data[8];
int todo = Count - done;
const uchar *data = Data + done;
// remember start of the data
const uchar *payload = data;
while (todo > 0) {
// collect number of skipped bytes while syncing
if (state <= syncing)
skippedBytes++;
// did we reach a start code?
scanner <<= 8;
if (scanner != 0x00000100)
scanner |= *data;
else {
scanner |= *data;
// which kind of start code have we got?
switch (*data) {
case 0xB9 ... 0xFF: // system start codes
esyslog("cVideoRepacker: found system start code: stream seems to be scrambled or not demultiplexed");
Reset();
break;
case 0xB0 ... 0xB1: // reserved start codes
case 0xB6:
esyslog("cVideoRepacker: found reserved start code: stream seems to be scrambled");
Reset();
break;
case 0xB4: // sequence error code
isyslog("cVideoRepacker: found sequence error code: stream seems to be damaged");
case 0xB2: // user data start code
case 0xB5: // extension start code
break;
case 0xB7: // sequence end code
case 0xB3: // sequence header code
case 0xB8: // group start code
case 0x00: // picture start code
if (state == scanPicture) {
// the above start codes indicate that the current picture is done. So
// push out the packet to start a new packet for the next picuture. If
// the byte count get's negative then the current buffer ends in a
// partitial start code that must be stripped off, as it shall be put
// in the next packet.
if (!PushOutPacket(ResultBuffer, payload, data - 3 - payload))
return done - 3;
// go on with syncing to the next picture
state = syncing;
}
if (state == syncing) {
// report that syncing dropped some bytes
if (skippedBytes > 4)
esyslog("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - 4);
skippedBytes = 0;
// if there is a PES header available, then use it ...
if (pesHeaderBackupLen > 0) {
// ISO 13818-1 says:
// In the case of video, if a PTS is present in a PES packet header
// it shall refer to the access unit containing the first picture start
// code that commences in this PES packet. A picture start code commences
// in PES packet if the first byte of the picture start code is present
// in the PES packet.
memcpy(pesHeader, pesHeaderBackup, pesHeaderBackupLen);
pesHeaderLen = pesHeaderBackupLen;
pesHeaderBackupLen = 0;
}
else {
// ... otherwise create a continuation PES header
pesHeaderLen = 0;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x01;
pesHeader[pesHeaderLen++] = Data[3]; // video stream ID
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x80;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
}
// append the first three bytes of the start code
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x01;
// the next packet's payload will begin with the fourth byte of
// the start code (= the actual code)
payload = data;
// as there is no length information available, assume the
// maximum we can hold in one PES packet
packetTodo = maxPacketSize - pesHeaderLen;
// go on with finding the picture data
((int &)state)++;
}
break;
case 0x01 ... 0xAF: // slice start codes
if (state == findPicture) {
// go on with scanning the picture data
((int &)state)++;
}
break;
}
}
data++;
done++;
todo--;
// do we have to start a new packet as there is no more space left?
if (--packetTodo <= 0) {
// we connot start a new packet here if the current might end in a start
// code and this start code shall possibly be put in the next packet. So
// overfill the current packet until we can safely detect that we won't
// break a start code into pieces:
//
// A) the last four bytes were a start code.
// B) the current byte introduces a start code.
// C) the last three bytes begin a start code.
//
// Todo : Data : Rule : Result
// -----:-------------------------------:------:-------
// : XX 00 00 00 01 YY|YY YY YY YY : :
// 0 : ^^| : A : push
// -----:-------------------------------:------:-------
// : XX XX 00 00 00 01|YY YY YY YY : :
// 0 : ^^| : B : wait
// -1 : |^^ : A : push
// -----:-------------------------------:------:-------
// : XX XX XX 00 00 00|01 YY YY YY : :
// 0 : ^^| : C : wait
// -1 : |^^ : B : wait
// -2 : | ^^ : A : push
// -----:-------------------------------:------:-------
// : XX XX XX XX 00 00|00 01 YY YY : :
// 0 : ^^| : C : wait
// -1 : |^^ : C : wait
// -2 : | ^^ : B : wait
// -3 : | ^^ : A : push
// -----:-------------------------------:------:-------
// : XX XX XX XX XX 00|00 00 01 YY : :
// 0 : ^^| : C : wait
// -1 : |^^ : C : wait
// -2 : | ^^ : : push
// -----:-------------------------------:------:-------
bool A = ((scanner & 0xFFFFFF00) == 0x00000100);
bool B = ((scanner & 0xFFFFFF) == 0x000001);
bool C = ((scanner & 0xFF) == 0x00) && (packetTodo >= -1);
if (A || (!B && !C)) {
// actually we cannot push out an overfull packet. So we'll have to
// adjust the byte count and payload start as necessary. If the byte
// count get's negative we'll have to append the excess from fragment's
// tail to the next PES header.
int bite = data + packetTodo - payload;
const uchar *excessData = fragmentData + fragmentLen + bite;
// a negative byte count means to drop some bytes from the current
// fragment's tail, to not exceed the maximum packet size.
if (!PushOutPacket(ResultBuffer, payload, bite))
return done;
// create a continuation PES header
pesHeaderLen = 0;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x01;
pesHeader[pesHeaderLen++] = Data[3]; // video stream ID
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x80;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
// copy any excess data
while (bite++ < 0) {
// append the excess data here
pesHeader[pesHeaderLen++] = *excessData++;
packetTodo++;
}
// the next packet's payload will begin here
payload = data + packetTodo;
// as there is no length information available, assume the
// maximum we can hold in one PES packet
packetTodo += maxPacketSize - pesHeaderLen;
}
}
}
// the packet is done. Now store any remaining data into fragment buffer
// if we are no longer syncing.
if (state != syncing) {
// append the PES header ...
int bite = pesHeaderLen;
pesHeaderLen = 0;
if (bite > 0) {
memcpy(fragmentData + fragmentLen, pesHeader, bite);
fragmentLen += bite;
}
// append payload. It may contain part of a start code at it's end,
// which will be removed when the next packet gets processed.
bite = data - payload;
if (bite > 0) {
memcpy(fragmentData + fragmentLen, payload, bite);
fragmentLen += bite;
}
}
// we've eaten the whole packet ;-)
return Count;
}
int cVideoRepacker::BreakAt(const uchar *Data, int Count)
{
// enough data for test?
if (Count < 6 + 3)
return -1;
// check for MPEG 2
if ((Data[6] & 0xC0) != 0x80)
return -1;
int headerLen = Data[8] + 6 + 3;
// enough data for test?
if (Count < headerLen)
return -1;
// just detect end of picture
if (state == scanPicture) {
// setup local scanner
if (localStart < 0) {
localScanner = scanner;
localStart = 0;
}
// start where we've stopped at the last run
const uchar *data = Data + headerLen + localStart;
const uchar *limit = Data + Count;
// scan data
while (data < limit) {
localStart++;
localScanner <<= 8;
localScanner |= *data++;
// check start codes which follow picture data
switch (localScanner) {
case 0x00000100: // picture start code
case 0x000001B8: // group start code
case 0x000001B3: // sequence header code
case 0x000001B7: // sequence end code
return data - Data;
}
}
}
// just fill up packet and append next start code
return headerLen + packetTodo + 4;
}
// --- cDolbyRepacker --------------------------------------------------------
class cDolbyRepacker : public cRepacker {
@ -462,6 +815,7 @@ cTS2PES::cTS2PES(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t Aud
if (repacker) {
repacker->SetMaxPacketSize(size);
repacker->SetSubStreamId(subStreamId);
size += repacker->QuerySnoopSize();
}
tsErrors = 0;
@ -801,7 +1155,7 @@ cRemux::cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, b
resultBuffer = new cRingBufferLinear(RESULTBUFFERSIZE, IPACKS, false, "Result");
resultBuffer->SetTimeouts(0, 100);
if (VPid)
ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS);
ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0x00, 0x00, new cVideoRepacker);
if (APids) {
int n = 0;
while (*APids && numTracks < MAXTRACKS && n < MAXAPIDS)