1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Implemented cAudioRepacker

This commit is contained in:
Klaus Schmidinger 2005-08-26 13:34:07 +02:00
parent ed807972ac
commit 449ffebcac
4 changed files with 519 additions and 97 deletions

View File

@ -976,6 +976,7 @@ Reinhard Nissl <rnissl@gmx.de>
for making cDvbPlayer::Goto() append a Sequence End Code to get the image shown for making cDvbPlayer::Goto() append a Sequence End Code to get the image shown
immediately with softdevices immediately with softdevices
for fixing cDvbSpuBitmap::putPixel() for fixing cDvbSpuBitmap::putPixel()
for implementing cAudioRepacker in remux.c
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

View File

@ -3722,3 +3722,6 @@ Video Disk Recorder Revision History
- Added missing German OSD texts for 'Audio language'. - Added missing German OSD texts for 'Audio language'.
- The Setup/CICAM menu now only contains the devices that actually have a CI and - The Setup/CICAM menu now only contains the devices that actually have a CI and
dynamically detects the number of slots a CI provides. dynamically detects the number of slots a CI provides.
- Implemented cAudioRepacker for better handling of audio PES packets (thanks to
Reinhard Nissl).

601
remux.c
View File

@ -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.37 2005/08/21 08:58:58 kls Exp $ * $Id: remux.c 1.38 2005/08/26 13:34:07 kls Exp $
*/ */
#include "remux.h" #include "remux.h"
@ -20,49 +20,23 @@
#include "thread.h" #include "thread.h"
#include "tools.h" #include "tools.h"
// --- cRepacker ------------------------------------------------------------- ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
class cRepacker {
protected:
int maxPacketSize;
uint8_t subStreamId;
static void DroppedData(const char *Reason, int Count) { esyslog("%s (dropped %d bytes)", Reason, Count); }
static int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
{
int n = ResultBuffer->Put(Data, Count);
if (n != Count)
esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
return n;
}
static int AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader = 0);
public:
cRepacker(void) { maxPacketSize = 6 + 65535; subStreamId = 0; }
virtual ~cRepacker() {}
virtual void Reset(void) {}
virtual void Repack(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; }
};
int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
{ {
if (Count < 7) if (Count < 7)
return -1; // too short return phNeedMoreData; // too short
if ((Data[6] & 0xC0) == 0x80) { // MPEG 2 if ((Data[6] & 0xC0) == 0x80) { // MPEG 2
if (Count < 9) if (Count < 9)
return -1; // too short return phNeedMoreData; // too short
PesPayloadOffset = 6 + 3 + Data[8]; PesPayloadOffset = 6 + 3 + Data[8];
if (Count < PesPayloadOffset) if (Count < PesPayloadOffset)
return -1; // too short return phNeedMoreData; // too short
if (ContinuationHeader) if (ContinuationHeader)
*ContinuationHeader = ((Data[6] == 0x80) && !Data[7] && !Data[8]); *ContinuationHeader = ((Data[6] == 0x80) && !Data[7] && !Data[8]);
return 2; // MPEG 2 return phMPEG2; // MPEG 2
} }
// check for MPEG 1 ... // check for MPEG 1 ...
@ -74,7 +48,7 @@ int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOff
break; break;
if (Count <= ++PesPayloadOffset) if (Count <= ++PesPayloadOffset)
return -1; // too short return phNeedMoreData; // too short
} }
// skip STD_buffer_scale/size // skip STD_buffer_scale/size
@ -82,7 +56,7 @@ int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOff
PesPayloadOffset += 2; PesPayloadOffset += 2;
if (Count <= PesPayloadOffset) if (Count <= PesPayloadOffset)
return -1; // too short return phNeedMoreData; // too short
} }
if (ContinuationHeader) if (ContinuationHeader)
@ -104,18 +78,49 @@ int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOff
*ContinuationHeader = true; *ContinuationHeader = true;
} }
else else
return 0; // unknown return phInvalid; // unknown
if (Count < PesPayloadOffset) if (Count < PesPayloadOffset)
return -1; // too short return phNeedMoreData; // too short
return 1; // MPEG 1 return phMPEG1; // MPEG 1
} }
// --- cVideoRepacker -------------------------------------------------------- // --- cRepacker -------------------------------------------------------------
class cVideoRepacker : public cRepacker { class cRepacker {
private: protected:
int maxPacketSize;
uint8_t subStreamId;
static void DroppedData(const char *Reason, int Count) { esyslog("%s (dropped %d bytes)", Reason, Count); }
public:
static int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
cRepacker(void) { maxPacketSize = 6 + 65535; subStreamId = 0; }
virtual ~cRepacker() {}
virtual void Reset(void) {}
virtual void Repack(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; }
};
int cRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded)
{
if (CapacityNeeded >= Count && ResultBuffer->Free() < CapacityNeeded) {
esyslog("ERROR: possible result buffer overflow, dropped %d out of %d byte", CapacityNeeded, CapacityNeeded);
return 0;
}
int n = ResultBuffer->Put(Data, Count);
if (n != Count)
esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
return n;
}
// --- cCommonRepacker --------------------------------------------------------
class cCommonRepacker : public cRepacker {
protected:
int skippedBytes; int skippedBytes;
int packetTodo; int packetTodo;
uchar fragmentData[6 + 65535 + 3]; uchar fragmentData[6 + 65535 + 3];
@ -125,40 +130,24 @@ private:
uchar pesHeaderBackup[6 + 3 + 255]; uchar pesHeaderBackup[6 + 3 + 255];
int pesHeaderBackupLen; int pesHeaderBackupLen;
uint32_t scanner; uint32_t scanner;
enum eState {
syncing,
findPicture,
scanPicture
} state;
uint32_t localScanner; uint32_t localScanner;
int localStart; int localStart;
bool PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count); bool PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
public:
cVideoRepacker(void);
virtual void Reset(void);
virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
virtual int BreakAt(const uchar *Data, int Count);
virtual int QuerySnoopSize() { return 4; } virtual int QuerySnoopSize() { return 4; }
virtual void Reset(void);
}; };
cVideoRepacker::cVideoRepacker(void) void cCommonRepacker::Reset(void)
{
Reset();
}
void cVideoRepacker::Reset(void)
{ {
skippedBytes = 0; skippedBytes = 0;
packetTodo = maxPacketSize - 6 - 3; packetTodo = maxPacketSize - 6 - 3;
fragmentLen = 0; fragmentLen = 0;
pesHeaderLen = 0; pesHeaderLen = 0;
pesHeaderBackupLen = 0; pesHeaderBackupLen = 0;
scanner = 0xFFFFFFFF;
state = syncing;
localStart = -1; localStart = -1;
} }
bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) bool cCommonRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
{ {
// enter packet length into PES header ... // enter packet length into PES header ...
if (fragmentLen > 0) { // ... which is contained in the fragment buffer if (fragmentLen > 0) { // ... which is contained in the fragment buffer
@ -166,11 +155,17 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
int PacketLen = fragmentLen + Count - 6; int PacketLen = fragmentLen + Count - 6;
fragmentData[ 4 ] = PacketLen >> 8; fragmentData[ 4 ] = PacketLen >> 8;
fragmentData[ 5 ] = PacketLen & 0xFF; fragmentData[ 5 ] = PacketLen & 0xFF;
// just skip packets with no payload
int PesPayloadOffset = 0;
if (AnalyzePesHeader(fragmentData, fragmentLen, PesPayloadOffset) <= phInvalid)
esyslog("cCommonRepacker: invalid PES packet encountered in fragment buffer!");
else if (6 + PacketLen <= PesPayloadOffset)
return true; // skip empty packet
// amount of data to put into result buffer: a negative Count value means // amount of data to put into result buffer: a negative Count value means
// to strip off any partially contained start code. // to strip off any partially contained start code.
int Bite = fragmentLen + (Count >= 0 ? 0 : Count); int Bite = fragmentLen + (Count >= 0 ? 0 : Count);
// put data into result buffer // put data into result buffer
int n = Put(ResultBuffer, fragmentData, Bite); int n = Put(ResultBuffer, fragmentData, Bite, 6 + PacketLen);
fragmentLen = 0; fragmentLen = 0;
if (n != Bite) if (n != Bite)
return false; return false;
@ -179,11 +174,17 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
int PacketLen = pesHeaderLen + Count - 6; int PacketLen = pesHeaderLen + Count - 6;
pesHeader[ 4 ] = PacketLen >> 8; pesHeader[ 4 ] = PacketLen >> 8;
pesHeader[ 5 ] = PacketLen & 0xFF; pesHeader[ 5 ] = PacketLen & 0xFF;
// just skip packets with no payload
int PesPayloadOffset = 0;
if (AnalyzePesHeader(pesHeader, pesHeaderLen, PesPayloadOffset) <= phInvalid)
esyslog("cCommonRepacker: invalid PES packet encountered in header buffer!");
else if (6 + PacketLen <= PesPayloadOffset)
return true; // skip empty packet
// amount of data to put into result buffer: a negative Count value means // amount of data to put into result buffer: a negative Count value means
// to strip off any partially contained start code. // to strip off any partially contained start code.
int Bite = pesHeaderLen + (Count >= 0 ? 0 : Count); int Bite = pesHeaderLen + (Count >= 0 ? 0 : Count);
// put data into result buffer // put data into result buffer
int n = Put(ResultBuffer, pesHeader, Bite); int n = Put(ResultBuffer, pesHeader, Bite, 6 + PacketLen);
pesHeaderLen = 0; pesHeaderLen = 0;
if (n != Bite) if (n != Bite)
return false; return false;
@ -193,7 +194,7 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
// amount of data to put into result buffer // amount of data to put into result buffer
int Bite = Count; int Bite = Count;
// put data into result buffer // put data into result buffer
int n = Put(ResultBuffer, Data, Bite); int n = Put(ResultBuffer, Data, Bite, Bite);
if (n != Bite) if (n != Bite)
return false; return false;
} }
@ -201,6 +202,34 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
return true; return true;
} }
// --- cVideoRepacker --------------------------------------------------------
class cVideoRepacker : public cCommonRepacker {
private:
enum eState {
syncing,
findPicture,
scanPicture
} state;
public:
cVideoRepacker(void);
virtual void Reset(void);
virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
virtual int BreakAt(const uchar *Data, int Count);
};
cVideoRepacker::cVideoRepacker(void)
{
Reset();
}
void cVideoRepacker::Reset(void)
{
cCommonRepacker::Reset();
scanner = 0xFFFFFFFF;
state = syncing;
}
void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
{ {
// synchronisation is detected some bytes after frame start. // synchronisation is detected some bytes after frame start.
@ -211,8 +240,8 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
int pesPayloadOffset = 0; int pesPayloadOffset = 0;
bool continuationHeader = false; bool continuationHeader = false;
int mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader); ePesHeader mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
if (mpegLevel <= 0) { if (mpegLevel <= phInvalid) {
DroppedData("cVideoRepacker: no valid PES packet header found", Count); DroppedData("cVideoRepacker: no valid PES packet header found", Count);
return; return;
} }
@ -298,7 +327,7 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
pesHeader[pesHeaderLen++] = 0x00; // length still unknown pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x00; // length still unknown pesHeader[pesHeaderLen++] = 0x00; // length still unknown
if (mpegLevel == 2) { if (mpegLevel == phMPEG2) {
pesHeader[pesHeaderLen++] = 0x80; pesHeader[pesHeaderLen++] = 0x80;
pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00;
@ -392,7 +421,7 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
pesHeader[pesHeaderLen++] = 0x00; // length still unknown pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x00; // length still unknown pesHeader[pesHeaderLen++] = 0x00; // length still unknown
if (mpegLevel == 2) { if (mpegLevel == phMPEG2) {
pesHeader[pesHeaderLen++] = 0x80; pesHeader[pesHeaderLen++] = 0x80;
pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00;
@ -443,7 +472,7 @@ int cVideoRepacker::BreakAt(const uchar *Data, int Count)
{ {
int PesPayloadOffset = 0; int PesPayloadOffset = 0;
if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= 0) if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= phInvalid)
return -1; // not enough data for test return -1; // not enough data for test
// just detect end of picture // just detect end of picture
@ -475,6 +504,355 @@ int cVideoRepacker::BreakAt(const uchar *Data, int Count)
return PesPayloadOffset + packetTodo + 4; return PesPayloadOffset + packetTodo + 4;
} }
// --- cAudioRepacker --------------------------------------------------------
class cAudioRepacker : public cCommonRepacker {
private:
static int bitRates[];
enum eState {
syncing,
scanFrame
} state;
int frameTodo;
int frameSize;
bool IsValidAudioHeader(uint32_t Header, int *FrameSize = NULL);
public:
cAudioRepacker(void);
virtual void Reset(void);
virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
virtual int BreakAt(const uchar *Data, int Count);
};
int cAudioRepacker::bitRates[] = { // all values are specified as kbits/s
// Layer I
0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1,
// Layer II
0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1,
// Layer III
0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1
};
cAudioRepacker::cAudioRepacker(void)
{
Reset();
}
void cAudioRepacker::Reset(void)
{
cCommonRepacker::Reset();
scanner = 0;
state = syncing;
frameTodo = 0;
frameSize = 0;
}
bool cAudioRepacker::IsValidAudioHeader(uint32_t Header, int *FrameSize)
{
int syncword = (Header & 0xFFF00000) >> 20;
int id = (Header & 0x00080000) >> 19;
int layer = (Header & 0x00060000) >> 17;
//int protection_bit = (Header & 0x00010000) >> 16;
int bitrate_index = (Header & 0x0000F000) >> 12;
int sampling_frequency = (Header & 0x00000C00) >> 10;
int padding_bit = (Header & 0x00000200) >> 9;
//int private_bit = (Header & 0x00000100) >> 8;
//int mode = (Header & 0x000000C0) >> 6;
//int mode_extension = (Header & 0x00000030) >> 4;
//int copyright = (Header & 0x00000008) >> 3;
//int orignal_copy = (Header & 0x00000004) >> 2;
int emphasis = (Header & 0x00000003);
if (syncword != 0xFFF)
return false;
if (id == 0) // reserved
return false;
if (layer == 0) // reserved
return false;
if (bitrate_index == 0xF) // forbidden
return false;
if (sampling_frequency == 3) // reserved
return false;
if (emphasis == 2) // reserved
return false;
if (FrameSize) {
if (bitrate_index == 0)
*FrameSize = 0;
else {
static int samplingFrequencies[] = { 44100, 48000, 32000 }; // Hz
int br = 1000 * bitRates[ (3 - layer) * 0x10 + bitrate_index ]; // bits/s
int sf = samplingFrequencies[sampling_frequency];
bool layerI = (layer == 3);
int N = (layerI ? 12 : 144) * br / sf; // slots
*FrameSize = (N + padding_bit) * (layerI ? 4 : 1); // bytes
}
}
return true;
}
void cAudioRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
{
// synchronisation is detected some bytes after frame start.
const int SkippedBytesLimit = 4;
// reset local scanner
localStart = -1;
int pesPayloadOffset = 0;
bool continuationHeader = false;
ePesHeader mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
if (mpegLevel <= phInvalid) {
DroppedData("cAudioRepacker: no valid PES packet header found", Count);
return;
}
if (!continuationHeader) {
// backup PES header
pesHeaderBackupLen = pesPayloadOffset;
memcpy(pesHeaderBackup, Data, pesHeaderBackupLen);
}
// skip PES header
int done = pesPayloadOffset;
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++;
else if (frameTodo > 0)
frameTodo--;
// did we reach an audio frame header?
scanner <<= 8;
scanner |= *data;
if ((scanner & 0xFFF00000) == 0xFFF00000) {
if (frameTodo <= 0 && IsValidAudioHeader(scanner, &frameSize)) {
if (state == scanFrame) {
// As a new audio frame starts here, the previous one is done. So push
// out the packet to start a new packet for the next audio frame. If
// the byte count gets negative then the current buffer ends in a
// partitial audio frame header that must be stripped off, as it shall
// be put in the next packet.
if (!PushOutPacket(ResultBuffer, payload, data - 3 - payload)) {
DroppedData("cAudioRepacker: result buffer overflow", Count - (done - 3));
return;
}
// go on with syncing to the next audio frame
state = syncing;
}
if (state == syncing) {
// report that syncing dropped some bytes
if (skippedBytes > SkippedBytesLimit)
esyslog("cAudioRepacker: skipped %d bytes to sync on next audio frame", skippedBytes - SkippedBytesLimit);
skippedBytes = 0;
// if there is a PES header available, then use it ...
if (pesHeaderBackupLen > 0) {
// ISO 13818-1 says:
// In the case of audio, if a PTS is present in a PES packet header
// it shall refer to the access unit commencing in the PES packet. An
// audio access unit commences in a PES packet if the first byte of
// the audio access unit 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]; // audio stream ID
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
if (mpegLevel == phMPEG2) {
pesHeader[pesHeaderLen++] = 0x80;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
}
else
pesHeader[pesHeaderLen++] = 0x0F;
}
// append the first three bytes of the audio frame header
pesHeader[pesHeaderLen++] = 0xFF;
pesHeader[pesHeaderLen++] = (scanner >> 16) & 0xFF;
pesHeader[pesHeaderLen++] = (scanner >> 8) & 0xFF;
// the next packet's payload will begin with the fourth byte of
// the audio frame header (= the actual byte)
payload = data;
// as there is no length information available, assume the
// maximum we can hold in one PES packet
packetTodo = maxPacketSize - pesHeaderLen;
// setup expected audio frame size to omit false audio header detection
frameTodo = frameSize;
// go on with collecting the frame's data
((int &)state)++;
}
}
}
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 an audio
// frame header and this header shall possibly be put in the next packet. So
// overfill the current packet until we can safely detect that we won't
// break an audio frame header into pieces:
//
// A) the last four bytes were an audio frame header.
// B) the last three bytes introduce an audio frame header.
// C) the last two bytes introduce an audio frame header.
// D) the last byte introduces an audio frame header.
//
// Todo : Data : Rule : Result
// -----:-------------------------------:------:-------
// : XX XX FF Fz zz zz|YY YY YY YY : :
// 0 : ^^| : A : push
// -----:-------------------------------:------:-------
// : XX XX XX FF Fz zz|zz YY YY YY : :
// 0 : ^^| : B : wait
// -1 : |^^ : A : push
// -----:-------------------------------:------:-------
// : XX XX XX XX FF Fz|zz zz YY YY : :
// 0 : ^^| : C : wait
// -1 : |^^ : B : wait
// -2 : | ^^ : A : push
// -----:-------------------------------:------:-------
// : XX XX XX XX XX FF|Fz zz zz YY : :
// 0 : ^^| : D : wait
// -1 : |^^ : C : wait
// -2 : | ^^ : B : wait
// -3 : | ^^ : A : push
// -----:-------------------------------:------:-------
bool A = ((scanner & 0xFFF00000) == 0xFFF00000);
bool B = ((scanner & 0xFFF000) == 0xFFF000);
bool C = ((scanner & 0xFFF0) == 0xFFF0);
bool D = ((scanner & 0xFF) == 0xFF);
if (A || (!B && !C && !D)) {
// 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 gets 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)) {
DroppedData("cAudioRepacker: result buffer overflow", Count - done);
return;
}
// create a continuation PES header
pesHeaderLen = 0;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x01;
pesHeader[pesHeaderLen++] = Data[3]; // audio stream ID
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
if (mpegLevel == phMPEG2) {
pesHeader[pesHeaderLen++] = 0x80;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
}
else
pesHeader[pesHeaderLen++] = 0x0F;
// 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 an audio frame header 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;
}
}
// report that syncing dropped some bytes
if (skippedBytes > SkippedBytesLimit) {
esyslog("cAudioRepacker: skipped %d bytes while syncing on next audio frame", skippedBytes - SkippedBytesLimit);
skippedBytes = SkippedBytesLimit;
}
}
int cAudioRepacker::BreakAt(const uchar *Data, int Count)
{
int PesPayloadOffset = 0;
if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= phInvalid)
return -1; // not enough data for test
// determine amount of data to fill up packet and to append next audio frame header
int packetRemainder = PesPayloadOffset + packetTodo + 4;
// just detect end of an audio frame
if (state == scanFrame) {
// when remaining audio frame size is known, then omit scanning
if (frameTodo > 0) {
// determine amount of data to fill up audio frame and to append next audio frame header
int remaining = PesPayloadOffset + frameTodo + 4;
if (remaining < packetRemainder)
return remaining;
return packetRemainder;
}
// setup local scanner
if (localStart < 0) {
localScanner = scanner;
localStart = 0;
}
// start where we've stopped at the last run
const uchar *data = Data + PesPayloadOffset + localStart;
const uchar *limit = Data + Count;
// scan data
while (data < limit) {
localStart++;
localScanner <<= 8;
localScanner |= *data++;
// check whether the next audio frame follows
if ((localScanner & 0xFFF00000) == 0xFFF00000 && IsValidAudioHeader(localScanner))
return data - Data;
}
}
// just fill up packet and append next audio frame header
return packetRemainder;
}
// --- cDolbyRepacker -------------------------------------------------------- // --- cDolbyRepacker --------------------------------------------------------
class cDolbyRepacker : public cRepacker { class cDolbyRepacker : public cRepacker {
@ -587,7 +965,7 @@ bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const ucha
// output a previous fragment first // output a previous fragment first
if (fragmentLen > 0) { if (fragmentLen > 0) {
Bite = fragmentLen; Bite = fragmentLen;
int n = Put(ResultBuffer, fragmentData, Bite); int n = Put(ResultBuffer, fragmentData, Bite, fragmentLen + fragmentTodo);
if (Bite != n) { if (Bite != n) {
Reset(); Reset();
return false; return false;
@ -595,7 +973,7 @@ bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const ucha
fragmentLen = 0; fragmentLen = 0;
} }
Bite = fragmentTodo; Bite = fragmentTodo;
int n = Put(ResultBuffer, Data, Bite); int n = Put(ResultBuffer, Data, Bite, Bite);
if (Bite != n) { if (Bite != n) {
Reset(); Reset();
Done += n; Done += n;
@ -631,13 +1009,13 @@ bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar
Bite = pesHeaderLen; Bite = pesHeaderLen;
// enough data available to put PES packet into buffer? // enough data available to put PES packet into buffer?
if (packetLen - pesHeaderLen <= Todo) { if (packetLen - pesHeaderLen <= Todo) {
int n = Put(ResultBuffer, pesHeader, Bite); int n = Put(ResultBuffer, pesHeader, Bite, packetLen);
if (Bite != n) { if (Bite != n) {
Reset(); Reset();
return false; return false;
} }
Bite = packetLen - pesHeaderLen; Bite = packetLen - pesHeaderLen;
n = Put(ResultBuffer, Data, Bite); n = Put(ResultBuffer, Data, Bite, Bite);
if (Bite != n) { if (Bite != n) {
Reset(); Reset();
Done += n; Done += n;
@ -890,7 +1268,8 @@ private:
uint8_t hlength; uint8_t hlength;
int mpeg; int mpeg;
uint8_t check; uint8_t check;
int which; int mpeg1_required;
int mpeg1_stuffing;
bool done; bool done;
cRingBufferLinear *resultBuffer; cRingBufferLinear *resultBuffer;
int tsErrors; int tsErrors;
@ -956,11 +1335,8 @@ void cTS2PES::store(uint8_t *Data, int Count)
{ {
if (repacker) if (repacker)
repacker->Repack(resultBuffer, Data, Count); repacker->Repack(resultBuffer, Data, Count);
else { else
int n = resultBuffer->Put(Data, Count); cRepacker::Put(resultBuffer, Data, Count, Count);
if (n != Count)
esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
}
} }
void cTS2PES::reset_ipack(void) void cTS2PES::reset_ipack(void)
@ -973,7 +1349,8 @@ void cTS2PES::reset_ipack(void)
hlength = 0; hlength = 0;
mpeg = 0; mpeg = 0;
check = 0; check = 0;
which = 0; mpeg1_required = 0;
mpeg1_stuffing = 0;
done = false; done = false;
count = 0; count = 0;
} }
@ -1057,7 +1434,7 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
{ {
int c = 0; int c = 0;
while (c < Count && (mpeg == 0 || (mpeg == 1 && found < 7) || (mpeg == 2 && found < 9)) && (found < 5 || !done)) { while (c < Count && (mpeg == 0 || (mpeg == 1 && found < mpeg1_required) || (mpeg == 2 && found < 9)) && (found < 5 || !done)) {
switch (found ) { switch (found ) {
case 0: case 0:
case 1: case 1:
@ -1103,6 +1480,7 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
plength = ntohs(*pl); plength = ntohs(*pl);
c += 2; c += 2;
found += 2; found += 2;
mpeg1_stuffing = 0;
} }
else { else {
plen[0] = Buf[c]; plen[0] = Buf[c];
@ -1115,32 +1493,53 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
unsigned short *pl = (unsigned short *)plen; unsigned short *pl = (unsigned short *)plen;
plength = ntohs(*pl); plength = ntohs(*pl);
found++; found++;
mpeg1_stuffing = 0;
} }
break; break;
case 6: case 6:
if (!done) { if (!done) {
flag1 = Buf[c++]; flag1 = Buf[c++];
found++; found++;
if ((flag1 & 0xC0) == 0x80 ) if (mpeg1_stuffing == 0) { // first stuffing iteration: determine MPEG level
mpeg = 2; if ((flag1 & 0xC0) == 0x80)
else { mpeg = 2;
hlength = 0; else
which = 0; mpeg = 1;
mpeg = 1; mpeg1_required = 7;
flag2 = 0; }
if (mpeg == 1) {
if (flag1 == 0xFF) { // MPEG1 stuffing
if (++mpeg1_stuffing > 16)
found = 0; // invalid MPEG1 header
else { // ignore stuffing
found--;
if (plength > 0)
plength--;
}
}
else if ((flag1 & 0xC0) == 0x40) // STD_buffer_scale/size
mpeg1_required += 2;
else if (flag1 != 0x0F && (flag1 & 0xF0) != 0x20 && (flag1 & 0xF0) != 0x30)
found = 0; // invalid MPEG1 header
else {
flag2 = 0;
hlength = 0;
}
} }
} }
break; break;
case 7: case 7:
if (!done && mpeg == 2) { if (!done && (mpeg == 2 || mpeg1_required > 7)) {
flag2 = Buf[c++]; flag2 = Buf[c++];
found++; found++;
} }
break; break;
case 8: case 8:
if (!done && mpeg == 2) { if (!done && (mpeg == 2 || mpeg1_required > 7)) {
hlength = Buf[c++]; hlength = Buf[c++];
found++; found++;
if (mpeg == 1 && hlength != 0x0F && (hlength & 0xF0) != 0x20 && (hlength & 0xF0) != 0x30)
found = 0; // invalid MPEG1 header
} }
break; break;
default: default:
@ -1151,7 +1550,7 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
if (!plength) if (!plength)
plength = MMAX_PLENGTH - 6; plength = MMAX_PLENGTH - 6;
if (done || ((mpeg == 2 && found >= 9) || (mpeg == 1 && found >= 7))) { if (done || ((mpeg == 2 && found >= 9) || (mpeg == 1 && found >= mpeg1_required))) {
switch (cid) { switch (cid) {
case AUDIO_STREAM_S ... AUDIO_STREAM_E: case AUDIO_STREAM_S ... AUDIO_STREAM_E:
case VIDEO_STREAM_S ... VIDEO_STREAM_E: case VIDEO_STREAM_S ... VIDEO_STREAM_E:
@ -1163,8 +1562,13 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
write_ipack(&hlength, 1); write_ipack(&hlength, 1);
} }
if (mpeg == 1 && found == 7) if (mpeg == 1 && found == mpeg1_required) {
write_ipack(&flag1, 1); write_ipack(&flag1, 1);
if (mpeg1_required > 7) {
write_ipack(&flag2, 1);
write_ipack(&hlength, 1);
}
}
if (mpeg == 2 && (flag2 & PTS_ONLY) && found < 14) { if (mpeg == 2 && (flag2 & PTS_ONLY) && found < 14) {
while (c < Count && found < 14) { while (c < Count && found < 14) {
@ -1277,7 +1681,12 @@ cRemux::cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, b
if (APids) { if (APids) {
int n = 0; int n = 0;
while (*APids && numTracks < MAXTRACKS && n < MAXAPIDS) while (*APids && numTracks < MAXTRACKS && n < MAXAPIDS)
#define TEST_cAudioRepacker
#ifdef TEST_cAudioRepacker
ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n++, 0x00, new cAudioRepacker);
#else
ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n++); ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n++);
#endif
} }
if (DPids) { if (DPids) {
int n = 0; int n = 0;
@ -1321,10 +1730,9 @@ int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &Pic
// If the return value is -1 the packet was not completely in the buffer. // If the return value is -1 the packet was not completely in the buffer.
int Length = GetPacketLength(Data, Count, Offset); int Length = GetPacketLength(Data, Count, Offset);
if (Length > 0) { if (Length > 0) {
if (Length >= 8) { int PesPayloadOffset = 0;
int i = Offset + 8; // the minimum length of the video packet header if (AnalyzePesHeader(Data + Offset, Length, PesPayloadOffset) >= phMPEG1) {
i += Data[i] + 1; // possible additional header bytes for (int i = Offset + PesPayloadOffset; i < Offset + Length - 5; i++) {
for (; i < Offset + Length - 5; i++) {
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) { if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
switch (Data[i + 3]) { switch (Data[i + 3]) {
case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07; case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
@ -1499,8 +1907,9 @@ void cRemux::Clear(void)
void cRemux::SetBrokenLink(uchar *Data, int Length) void cRemux::SetBrokenLink(uchar *Data, int Length)
{ {
if (Length > 9 && Data[0] == 0 && Data[1] == 0 && Data[2] == 1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) { int PesPayloadOffset = 0;
for (int i = Data[8] + 9; i < Length - 7; i++) { // +9 to skip video packet header if (AnalyzePesHeader(Data, Length, PesPayloadOffset) >= phMPEG1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) {
for (int i = PesPayloadOffset; i < Length - 7; i++) {
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1 && Data[i + 3] == 0xB8) { if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1 && Data[i + 3] == 0xB8) {
if (!(Data[i + 7] & 0x40)) // set flag only if GOP is not closed if (!(Data[i + 7] & 0x40)) // set flag only if GOP is not closed
Data[i + 7] |= 0x20; Data[i + 7] |= 0x20;

11
remux.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: remux.h 1.14 2005/08/07 10:28:07 kls Exp $ * $Id: remux.h 1.15 2005/08/26 13:22:19 kls Exp $
*/ */
#ifndef __REMUX_H #ifndef __REMUX_H
@ -15,6 +15,15 @@
#include "ringbuffer.h" #include "ringbuffer.h"
#include "tools.h" #include "tools.h"
enum ePesHeader {
phNeedMoreData = -1,
phInvalid = 0,
phMPEG1 = 1,
phMPEG2 = 2
};
ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader = NULL);
// Picture types: // Picture types:
#define NO_PICTURE 0 #define NO_PICTURE 0
#define I_FRAME 1 #define I_FRAME 1