@@ -19,6 +19,7 @@
#include "channels.h"
#include "thread.h"
#include "tools.h"
+#include "recording.h"
ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
{
@@ -690,12 +691,13 @@ private:
int frameTodo;
int frameSize;
int cid;
- static bool IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize = NULL);
+ static bool IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize = NULL, int *FrameDuration = NULL);
public:
cAudioRepacker(int Cid);
virtual void Reset(void);
virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
virtual int BreakAt(const uchar *Data, int Count);
+ static int GetFrameDuration(const uchar *Data, int Count, int *TrackIndex = NULL);
};
int cAudioRepacker::bitRates[2][3][16] = { // all values are specified as kbits/s
@@ -711,6 +713,25 @@ int cAudioRepacker::bitRates[2][3][16] =
}
};
+int cAudioRepacker::GetFrameDuration(const uchar *Data, int Count, int *TrackIndex)
+{
+ int PesPayloadOffset = 0;
+ ePesHeader PH = AnalyzePesHeader(Data, Count, PesPayloadOffset);
+ if (PH < phMPEG1)
+ return -1;
+
+ const uchar *Payload = Data + PesPayloadOffset;
+ const int PayloadCount = Count - PesPayloadOffset;
+
+ int FrameDuration = -1;
+ if ((Data[3] & 0xE0) == 0xC0 && PayloadCount >= 4) {
+ if (IsValidAudioHeader(((Payload[0] << 8 | Payload[1]) << 8 | Payload[2]) << 8 | Payload[3], PH == phMPEG2, NULL, &FrameDuration) && TrackIndex)
+ *TrackIndex = Data[3] - 0xC0;
+ }
+
+ return FrameDuration;
+}
+
cAudioRepacker::cAudioRepacker(int Cid)
{
cid = Cid;
@@ -726,7 +747,7 @@ void cAudioRepacker::Reset(void)
frameSize = 0;
}
-bool cAudioRepacker::IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize)
+bool cAudioRepacker::IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize, int *FrameDuration)
{
int syncword = (Header & 0xFFF00000) >> 20;
int id = (Header & 0x00080000) >> 19;
@@ -760,32 +781,36 @@ bool cAudioRepacker::IsValidAudioHeader(
if (emphasis == 2) // reserved
return false;
- if (FrameSize) {
- if (bitrate_index == 0)
- *FrameSize = 0;
- else {
- static int samplingFrequencies[2][4] = { // all values are specified in Hz
- { 44100, 48000, 32000, -1 }, // MPEG 1
- { 22050, 24000, 16000, -1 } // MPEG 2
- };
-
- static int slots_per_frame[2][3] = {
- { 12, 144, 144 }, // MPEG 1, Layer I, II, III
- { 12, 144, 72 } // MPEG 2, Layer I, II, III
- };
-
- int mpegIndex = 1 - id;
- int layerIndex = 3 - layer;
-
- // Layer I (i. e., layerIndex == 0) has a larger slot size
- int slotSize = (layerIndex == 0) ? 4 : 1; // bytes
-
- int br = 1000 * bitRates[mpegIndex][layerIndex][bitrate_index]; // bits/s
- int sf = samplingFrequencies[mpegIndex][sampling_frequency];
-
- int N = slots_per_frame[mpegIndex][layerIndex] * br / sf; // slots
+ if (FrameSize || FrameDuration) {
+ static int samplingFrequencies[2][4] = { // all values are specified in Hz
+ { 44100, 48000, 32000, -1 }, // MPEG 1
+ { 22050, 24000, 16000, -1 } // MPEG 2
+ };
+
+ static int slots_per_frame[2][3] = {
+ { 12, 144, 144 }, // MPEG 1, Layer I, II, III
+ { 12, 144, 72 } // MPEG 2, Layer I, II, III
+ };
+
+ int mpegIndex = 1 - id;
+ int layerIndex = 3 - layer;
+
+ // Layer I (i. e., layerIndex == 0) has a larger slot size
+ int slotSize = (layerIndex == 0) ? 4 : 1; // bytes
+ int sf = samplingFrequencies[mpegIndex][sampling_frequency];
+
+ if (FrameDuration)
+ *FrameDuration = 90000 * 8 * slotSize * slots_per_frame[mpegIndex][layerIndex] / sf;
+
+ if (FrameSize) {
+ if (bitrate_index == 0)
+ *FrameSize = 0;
+ else {
+ int br = 1000 * bitRates[mpegIndex][layerIndex][bitrate_index]; // bits/s
+ int N = slots_per_frame[mpegIndex][layerIndex] * br / sf; // slots
- *FrameSize = (N + padding_bit) * slotSize; // bytes
+ *FrameSize = (N + padding_bit) * slotSize; // bytes
+ }
}
}
@@ -1086,6 +1111,7 @@ public:
virtual void Reset(void);
virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
virtual int BreakAt(const uchar *Data, int Count);
+ static int GetFrameDuration(const uchar *Data, int Count, int *TrackIndex = NULL);
};
// frameSizes are in words, i. e. multiply them by 2 to get bytes
@@ -1112,6 +1138,30 @@ int cDolbyRepacker::frameSizes[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
+int cDolbyRepacker::GetFrameDuration(const uchar *Data, int Count, int *TrackIndex)
+{
+ int PesPayloadOffset = 0;
+ ePesHeader PH = AnalyzePesHeader(Data, Count, PesPayloadOffset);
+ if (PH < phMPEG1)
+ return -1;
+
+ const uchar *Payload = Data + PesPayloadOffset;
+ const int PayloadCount = Count - PesPayloadOffset;
+
+ if (Data[3] == 0xBD && PayloadCount >= 9 && ((Payload[0] & 0xF0) == 0x80) && Payload[4] == 0x0B && Payload[5] == 0x77 && frameSizes[Payload[8]] > 0) {
+ if (TrackIndex)
+ *TrackIndex = Payload[0] - 0x80;
+
+ static int samplingFrequencies[4] = { // all values are specified in Hz
+ 48000, 44100, 32000, -1
+ };
+
+ return 90000 * 1536 / samplingFrequencies[Payload[8] >> 6];
+ }
+
+ return -1;
+}
+
cDolbyRepacker::cDolbyRepacker(void)
{
pesHeader[0] = 0x00;
@@ -1849,6 +1899,58 @@ void cTS2PES::ts_to_pes(const uint8_t *B
instant_repack(Buf + 4 + off, TS_SIZE - 4 - off);
}
+// --- cAudioIndexer ---------------------------------------------------------
+
+class cAudioIndexer {
+private:
+ int frameTrack;
+ int frameDuration;
+ int64_t trackTime[MAXAPIDS + MAXDPIDS];
+ int64_t nextIndexTime;
+
+public:
+ cAudioIndexer(void);
+ void Clear(void);
+ void PrepareFrame(const uchar *Data, int Count, int Offset, uchar &PictureType);
+ void ProcessFrame(void);
+ };
+
+cAudioIndexer::cAudioIndexer(void)
+{
+ Clear();
+}
+
+void cAudioIndexer::Clear(void)
+{
+ memset(trackTime, 0, sizeof (trackTime));
+ nextIndexTime = 0;
+ frameTrack = -1;
+}
+
+void cAudioIndexer::PrepareFrame(const uchar *Data, int Count, int Offset, uchar &PictureType)
+{
+ frameDuration = cRemux::GetAudioFrameDuration(Data + Offset, Count - Offset, &frameTrack);
+ if (frameDuration <= 0)
+ return;
+
+ if (Data[Offset + 3] == 0xBD)
+ frameTrack += MAXAPIDS;
+
+ PictureType = (trackTime[frameTrack] >= nextIndexTime) ? I_FRAME : NO_PICTURE;
+}
+
+void cAudioIndexer::ProcessFrame(void)
+{
+ if (frameTrack < 0)
+ return;
+
+ if (trackTime[frameTrack] >= nextIndexTime)
+ nextIndexTime += 90000 / FRAMESPERSEC;
+
+ trackTime[frameTrack] += frameDuration;
+ frameTrack = -1;
+}
+
// --- cRemux ----------------------------------------------------------------
#define RESULTBUFFERSIZE KILOBYTE(256)
@@ -1895,6 +1997,8 @@ cRemux::cRemux(int VPid, const int *APid
ts2pes[numTracks++] = new cTS2PES(*SPids++, resultBuffer, IPACKS, 0x00, 0x28 + n++);
}
*/
+ if (isRadio)
+ audioIndexer = new cAudioIndexer;
}
cRemux::~cRemux()
@@ -1902,6 +2006,18 @@ cRemux::~cRemux()
for (int t = 0; t < numTracks; t++)
delete ts2pes[t];
delete resultBuffer;
+ delete audioIndexer;
+}
+
+int cRemux::GetAudioFrameDuration(const uchar *Data, int Count, int *TrackIndex)
+{
+ if (Count <= 4)
+ return -1;
+
+ if (Data[3] == 0xBD)
+ return cDolbyRepacker::GetFrameDuration(Data, Count, TrackIndex);
+
+ return cAudioRepacker::GetFrameDuration(Data, Count, TrackIndex);
}
int cRemux::GetPid(const uchar *Data)
@@ -2081,16 +2197,19 @@ uchar *cRemux::Get(int &Count, uchar *Pi
if (l < 0)
return resultData;
if (isRadio) {
+ uchar pt = NO_PICTURE;
+ if (audioIndexer && !Count)
+ audioIndexer->PrepareFrame(data, resultCount, i, pt);
if (!synced) {
if (PictureType)
- *PictureType = I_FRAME;
+ *PictureType = pt;
resultSkipped = i; // will drop everything before this position
synced = true;
}
else if (Count)
return resultData;
else if (PictureType)
- *PictureType = I_FRAME;
+ *PictureType = pt;
}
}
if (synced) {
@@ -2111,6 +2230,8 @@ uchar *cRemux::Get(int &Count, uchar *Pi
void cRemux::Del(int Count)
{
resultBuffer->Del(Count);
+ if (audioIndexer && Count > 0)
+ audioIndexer->ProcessFrame();
}
void cRemux::Clear(void)
@@ -2118,6 +2239,7 @@ void cRemux::Clear(void)
for (int t = 0; t < numTracks; t++)
ts2pes[t]->Clear();
resultBuffer->Clear();
+ audioIndexer->Clear();
synced = false;
skipped = 0;
resultSkipped = 0;
@@ -33,6 +33,7 @@ ePesHeader AnalyzePesHeader(const uchar
#define MAXTRACKS 64
class cTS2PES;
+class cAudioIndexer;
class cRemux {
private:
@@ -45,6 +46,7 @@ private:
int numTracks;
cRingBufferLinear *resultBuffer;
int resultSkipped;
+ cAudioIndexer *audioIndexer;
int GetPid(const uchar *Data);
public:
cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure = false);
@@ -79,6 +81,7 @@ public:
static void SetBrokenLink(uchar *Data, int Length);
static int GetPacketLength(const uchar *Data, int Count, int Offset);
static int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
+ static int GetAudioFrameDuration(const uchar *Data, int Count, int *TrackIndex = NULL);
};
#endif // __REMUX_H