mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Implemented cAudioRepacker
This commit is contained in:
parent
ed807972ac
commit
449ffebcac
@ -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
|
||||||
|
3
HISTORY
3
HISTORY
@ -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
601
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.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
11
remux.h
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user