mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Implemented cVideoRepacker in remux.c to make sure every PES packet contains only data from one frame
This commit is contained in:
parent
a8599c451e
commit
60a35366dd
@ -955,6 +955,8 @@ Reinhard Nissl <rnissl@gmx.de>
|
|||||||
for fixing a typo in detecting UTF-8
|
for fixing a typo in detecting UTF-8
|
||||||
for fixing handling fragments of less than 4 byte in cPesAssembler
|
for fixing handling fragments of less than 4 byte in cPesAssembler
|
||||||
for some rearrangements in cDvbPlayer::Action() to avoid lockups on NPTL systems
|
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>
|
Richard Robson <richard_robson@beeb.net>
|
||||||
for reporting freezing replay if a timer starts while in Transfer Mode from the
|
for reporting freezing replay if a timer starts while in Transfer Mode from the
|
||||||
|
2
HISTORY
2
HISTORY
@ -3585,3 +3585,5 @@ Video Disk Recorder Revision History
|
|||||||
- Fixed handling 'summary.vdr' files with more than two empty lines (thanks to
|
- Fixed handling 'summary.vdr' files with more than two empty lines (thanks to
|
||||||
Christian Jacobsen for reporting this one).
|
Christian Jacobsen for reporting this one).
|
||||||
- Improved resetting CAM connections (thanks to Marco Schlüßler).
|
- 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
358
remux.c
@ -11,7 +11,7 @@
|
|||||||
* The cDolbyRepacker code was originally written by Reinhard Nissl <rnissl@gmx.de>,
|
* The cDolbyRepacker code was originally written by Reinhard Nissl <rnissl@gmx.de>,
|
||||||
* and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.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"
|
#include "remux.h"
|
||||||
@ -32,10 +32,363 @@ public:
|
|||||||
virtual void Reset(void) {}
|
virtual void Reset(void) {}
|
||||||
virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) = 0;
|
virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) = 0;
|
||||||
virtual int BreakAt(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 SetMaxPacketSize(int MaxPacketSize) { maxPacketSize = MaxPacketSize; }
|
||||||
void SetSubStreamId(uint8_t SubStreamId) { subStreamId = SubStreamId; }
|
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 --------------------------------------------------------
|
// --- cDolbyRepacker --------------------------------------------------------
|
||||||
|
|
||||||
class cDolbyRepacker : public cRepacker {
|
class cDolbyRepacker : public cRepacker {
|
||||||
@ -462,6 +815,7 @@ cTS2PES::cTS2PES(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t Aud
|
|||||||
if (repacker) {
|
if (repacker) {
|
||||||
repacker->SetMaxPacketSize(size);
|
repacker->SetMaxPacketSize(size);
|
||||||
repacker->SetSubStreamId(subStreamId);
|
repacker->SetSubStreamId(subStreamId);
|
||||||
|
size += repacker->QuerySnoopSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
tsErrors = 0;
|
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 = new cRingBufferLinear(RESULTBUFFERSIZE, IPACKS, false, "Result");
|
||||||
resultBuffer->SetTimeouts(0, 100);
|
resultBuffer->SetTimeouts(0, 100);
|
||||||
if (VPid)
|
if (VPid)
|
||||||
ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS);
|
ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0x00, 0x00, new cVideoRepacker);
|
||||||
if (APids) {
|
if (APids) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
while (*APids && numTracks < MAXTRACKS && n < MAXAPIDS)
|
while (*APids && numTracks < MAXTRACKS && n < MAXAPIDS)
|
||||||
|
Loading…
Reference in New Issue
Block a user