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
|
||||
immediately with softdevices
|
||||
for fixing cDvbSpuBitmap::putPixel()
|
||||
for implementing cAudioRepacker in remux.c
|
||||
|
||||
Richard Robson <richard_robson@beeb.net>
|
||||
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'.
|
||||
- The Setup/CICAM menu now only contains the devices that actually have a CI and
|
||||
dynamically detects the number of slots a CI provides.
|
||||
- Implemented cAudioRepacker for better handling of audio PES packets (thanks to
|
||||
Reinhard Nissl).
|
||||
|
||||
|
593
remux.c
593
remux.c
@ -11,7 +11,7 @@
|
||||
* The cDolbyRepacker code was originally written by Reinhard Nissl <rnissl@gmx.de>,
|
||||
* and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de.
|
||||
*
|
||||
* $Id: remux.c 1.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"
|
||||
@ -20,49 +20,23 @@
|
||||
#include "thread.h"
|
||||
#include "tools.h"
|
||||
|
||||
// --- cRepacker -------------------------------------------------------------
|
||||
|
||||
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)
|
||||
ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
|
||||
{
|
||||
if (Count < 7)
|
||||
return -1; // too short
|
||||
return phNeedMoreData; // too short
|
||||
|
||||
if ((Data[6] & 0xC0) == 0x80) { // MPEG 2
|
||||
if (Count < 9)
|
||||
return -1; // too short
|
||||
return phNeedMoreData; // too short
|
||||
|
||||
PesPayloadOffset = 6 + 3 + Data[8];
|
||||
if (Count < PesPayloadOffset)
|
||||
return -1; // too short
|
||||
return phNeedMoreData; // too short
|
||||
|
||||
if (ContinuationHeader)
|
||||
*ContinuationHeader = ((Data[6] == 0x80) && !Data[7] && !Data[8]);
|
||||
|
||||
return 2; // MPEG 2
|
||||
return phMPEG2; // MPEG 2
|
||||
}
|
||||
|
||||
// check for MPEG 1 ...
|
||||
@ -74,7 +48,7 @@ int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOff
|
||||
break;
|
||||
|
||||
if (Count <= ++PesPayloadOffset)
|
||||
return -1; // too short
|
||||
return phNeedMoreData; // too short
|
||||
}
|
||||
|
||||
// skip STD_buffer_scale/size
|
||||
@ -82,7 +56,7 @@ int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOff
|
||||
PesPayloadOffset += 2;
|
||||
|
||||
if (Count <= PesPayloadOffset)
|
||||
return -1; // too short
|
||||
return phNeedMoreData; // too short
|
||||
}
|
||||
|
||||
if (ContinuationHeader)
|
||||
@ -104,18 +78,49 @@ int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOff
|
||||
*ContinuationHeader = true;
|
||||
}
|
||||
else
|
||||
return 0; // unknown
|
||||
return phInvalid; // unknown
|
||||
|
||||
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 {
|
||||
private:
|
||||
class cRepacker {
|
||||
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 packetTodo;
|
||||
uchar fragmentData[6 + 65535 + 3];
|
||||
@ -125,40 +130,24 @@ private:
|
||||
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 void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
|
||||
virtual int BreakAt(const uchar *Data, int Count);
|
||||
virtual int QuerySnoopSize() { return 4; }
|
||||
virtual void Reset(void);
|
||||
};
|
||||
|
||||
cVideoRepacker::cVideoRepacker(void)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void cVideoRepacker::Reset(void)
|
||||
void cCommonRepacker::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)
|
||||
bool cCommonRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
|
||||
{
|
||||
// enter packet length into PES header ...
|
||||
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;
|
||||
fragmentData[ 4 ] = PacketLen >> 8;
|
||||
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
|
||||
// to strip off any partially contained start code.
|
||||
int Bite = fragmentLen + (Count >= 0 ? 0 : Count);
|
||||
// put data into result buffer
|
||||
int n = Put(ResultBuffer, fragmentData, Bite);
|
||||
int n = Put(ResultBuffer, fragmentData, Bite, 6 + PacketLen);
|
||||
fragmentLen = 0;
|
||||
if (n != Bite)
|
||||
return false;
|
||||
@ -179,11 +174,17 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
|
||||
int PacketLen = pesHeaderLen + Count - 6;
|
||||
pesHeader[ 4 ] = PacketLen >> 8;
|
||||
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
|
||||
// to strip off any partially contained start code.
|
||||
int Bite = pesHeaderLen + (Count >= 0 ? 0 : Count);
|
||||
// put data into result buffer
|
||||
int n = Put(ResultBuffer, pesHeader, Bite);
|
||||
int n = Put(ResultBuffer, pesHeader, Bite, 6 + PacketLen);
|
||||
pesHeaderLen = 0;
|
||||
if (n != Bite)
|
||||
return false;
|
||||
@ -193,7 +194,7 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
|
||||
// amount of data to put into result buffer
|
||||
int Bite = Count;
|
||||
// put data into result buffer
|
||||
int n = Put(ResultBuffer, Data, Bite);
|
||||
int n = Put(ResultBuffer, Data, Bite, Bite);
|
||||
if (n != Bite)
|
||||
return false;
|
||||
}
|
||||
@ -201,6 +202,34 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
|
||||
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)
|
||||
{
|
||||
// synchronisation is detected some bytes after frame start.
|
||||
@ -211,8 +240,8 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
|
||||
|
||||
int pesPayloadOffset = 0;
|
||||
bool continuationHeader = false;
|
||||
int mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
|
||||
if (mpegLevel <= 0) {
|
||||
ePesHeader mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
|
||||
if (mpegLevel <= phInvalid) {
|
||||
DroppedData("cVideoRepacker: no valid PES packet header found", Count);
|
||||
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
|
||||
|
||||
if (mpegLevel == 2) {
|
||||
if (mpegLevel == phMPEG2) {
|
||||
pesHeader[pesHeaderLen++] = 0x80;
|
||||
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
|
||||
|
||||
if (mpegLevel == 2) {
|
||||
if (mpegLevel == phMPEG2) {
|
||||
pesHeader[pesHeaderLen++] = 0x80;
|
||||
pesHeader[pesHeaderLen++] = 0x00;
|
||||
pesHeader[pesHeaderLen++] = 0x00;
|
||||
@ -443,7 +472,7 @@ int cVideoRepacker::BreakAt(const uchar *Data, int Count)
|
||||
{
|
||||
int PesPayloadOffset = 0;
|
||||
|
||||
if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= 0)
|
||||
if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= phInvalid)
|
||||
return -1; // not enough data for test
|
||||
|
||||
// just detect end of picture
|
||||
@ -475,6 +504,355 @@ int cVideoRepacker::BreakAt(const uchar *Data, int Count)
|
||||
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 --------------------------------------------------------
|
||||
|
||||
class cDolbyRepacker : public cRepacker {
|
||||
@ -587,7 +965,7 @@ bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const ucha
|
||||
// output a previous fragment first
|
||||
if (fragmentLen > 0) {
|
||||
Bite = fragmentLen;
|
||||
int n = Put(ResultBuffer, fragmentData, Bite);
|
||||
int n = Put(ResultBuffer, fragmentData, Bite, fragmentLen + fragmentTodo);
|
||||
if (Bite != n) {
|
||||
Reset();
|
||||
return false;
|
||||
@ -595,7 +973,7 @@ bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const ucha
|
||||
fragmentLen = 0;
|
||||
}
|
||||
Bite = fragmentTodo;
|
||||
int n = Put(ResultBuffer, Data, Bite);
|
||||
int n = Put(ResultBuffer, Data, Bite, Bite);
|
||||
if (Bite != n) {
|
||||
Reset();
|
||||
Done += n;
|
||||
@ -631,13 +1009,13 @@ bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar
|
||||
Bite = pesHeaderLen;
|
||||
// enough data available to put PES packet into buffer?
|
||||
if (packetLen - pesHeaderLen <= Todo) {
|
||||
int n = Put(ResultBuffer, pesHeader, Bite);
|
||||
int n = Put(ResultBuffer, pesHeader, Bite, packetLen);
|
||||
if (Bite != n) {
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
Bite = packetLen - pesHeaderLen;
|
||||
n = Put(ResultBuffer, Data, Bite);
|
||||
n = Put(ResultBuffer, Data, Bite, Bite);
|
||||
if (Bite != n) {
|
||||
Reset();
|
||||
Done += n;
|
||||
@ -890,7 +1268,8 @@ private:
|
||||
uint8_t hlength;
|
||||
int mpeg;
|
||||
uint8_t check;
|
||||
int which;
|
||||
int mpeg1_required;
|
||||
int mpeg1_stuffing;
|
||||
bool done;
|
||||
cRingBufferLinear *resultBuffer;
|
||||
int tsErrors;
|
||||
@ -956,11 +1335,8 @@ void cTS2PES::store(uint8_t *Data, int Count)
|
||||
{
|
||||
if (repacker)
|
||||
repacker->Repack(resultBuffer, Data, Count);
|
||||
else {
|
||||
int n = resultBuffer->Put(Data, Count);
|
||||
if (n != Count)
|
||||
esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
|
||||
}
|
||||
else
|
||||
cRepacker::Put(resultBuffer, Data, Count, Count);
|
||||
}
|
||||
|
||||
void cTS2PES::reset_ipack(void)
|
||||
@ -973,7 +1349,8 @@ void cTS2PES::reset_ipack(void)
|
||||
hlength = 0;
|
||||
mpeg = 0;
|
||||
check = 0;
|
||||
which = 0;
|
||||
mpeg1_required = 0;
|
||||
mpeg1_stuffing = 0;
|
||||
done = false;
|
||||
count = 0;
|
||||
}
|
||||
@ -1057,7 +1434,7 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
|
||||
{
|
||||
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 ) {
|
||||
case 0:
|
||||
case 1:
|
||||
@ -1103,6 +1480,7 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
|
||||
plength = ntohs(*pl);
|
||||
c += 2;
|
||||
found += 2;
|
||||
mpeg1_stuffing = 0;
|
||||
}
|
||||
else {
|
||||
plen[0] = Buf[c];
|
||||
@ -1115,32 +1493,53 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
|
||||
unsigned short *pl = (unsigned short *)plen;
|
||||
plength = ntohs(*pl);
|
||||
found++;
|
||||
mpeg1_stuffing = 0;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if (!done) {
|
||||
flag1 = Buf[c++];
|
||||
found++;
|
||||
if (mpeg1_stuffing == 0) { // first stuffing iteration: determine MPEG level
|
||||
if ((flag1 & 0xC0) == 0x80)
|
||||
mpeg = 2;
|
||||
else {
|
||||
hlength = 0;
|
||||
which = 0;
|
||||
else
|
||||
mpeg = 1;
|
||||
mpeg1_required = 7;
|
||||
}
|
||||
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;
|
||||
case 7:
|
||||
if (!done && mpeg == 2) {
|
||||
if (!done && (mpeg == 2 || mpeg1_required > 7)) {
|
||||
flag2 = Buf[c++];
|
||||
found++;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
if (!done && mpeg == 2) {
|
||||
if (!done && (mpeg == 2 || mpeg1_required > 7)) {
|
||||
hlength = Buf[c++];
|
||||
found++;
|
||||
if (mpeg == 1 && hlength != 0x0F && (hlength & 0xF0) != 0x20 && (hlength & 0xF0) != 0x30)
|
||||
found = 0; // invalid MPEG1 header
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -1151,7 +1550,7 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
|
||||
if (!plength)
|
||||
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) {
|
||||
case AUDIO_STREAM_S ... AUDIO_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);
|
||||
}
|
||||
|
||||
if (mpeg == 1 && found == 7)
|
||||
if (mpeg == 1 && found == mpeg1_required) {
|
||||
write_ipack(&flag1, 1);
|
||||
if (mpeg1_required > 7) {
|
||||
write_ipack(&flag2, 1);
|
||||
write_ipack(&hlength, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (mpeg == 2 && (flag2 & PTS_ONLY) && 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) {
|
||||
int n = 0;
|
||||
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++);
|
||||
#endif
|
||||
}
|
||||
if (DPids) {
|
||||
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.
|
||||
int Length = GetPacketLength(Data, Count, Offset);
|
||||
if (Length > 0) {
|
||||
if (Length >= 8) {
|
||||
int i = Offset + 8; // the minimum length of the video packet header
|
||||
i += Data[i] + 1; // possible additional header bytes
|
||||
for (; i < Offset + Length - 5; i++) {
|
||||
int PesPayloadOffset = 0;
|
||||
if (AnalyzePesHeader(Data + Offset, Length, PesPayloadOffset) >= phMPEG1) {
|
||||
for (int i = Offset + PesPayloadOffset; i < Offset + Length - 5; i++) {
|
||||
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
|
||||
switch (Data[i + 3]) {
|
||||
case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
|
||||
@ -1499,8 +1907,9 @@ void cRemux::Clear(void)
|
||||
|
||||
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) {
|
||||
for (int i = Data[8] + 9; i < Length - 7; i++) { // +9 to skip video packet header
|
||||
int PesPayloadOffset = 0;
|
||||
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 + 7] & 0x40)) // set flag only if GOP is not closed
|
||||
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
|
||||
* 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
|
||||
@ -15,6 +15,15 @@
|
||||
#include "ringbuffer.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:
|
||||
#define NO_PICTURE 0
|
||||
#define I_FRAME 1
|
||||
|
Loading…
Reference in New Issue
Block a user