mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Improved frame detection by parsing just far enough into the MPEG-4 NAL units to get the necessary information about frames and slices; the initial syncing of the frame detector is now done immediately after the first complete GOP has been seen
This commit is contained in:
parent
38d48afad9
commit
57a3169013
7
HISTORY
7
HISTORY
@ -7272,7 +7272,7 @@ Video Disk Recorder Revision History
|
|||||||
".keep" to prevent a directory from being deleted when it is empty. Currently the
|
".keep" to prevent a directory from being deleted when it is empty. Currently the
|
||||||
only file name that is ignored is ".sort".
|
only file name that is ignored is ".sort".
|
||||||
|
|
||||||
2012-10-16: Version 1.7.32
|
2012-11-02: Version 1.7.32
|
||||||
|
|
||||||
- Pressing the Play key during normal live viewing mode now opens the Recordings menu
|
- Pressing the Play key during normal live viewing mode now opens the Recordings menu
|
||||||
if there is no "last viewed" recording (thanks to Alexander Wenzel).
|
if there is no "last viewed" recording (thanks to Alexander Wenzel).
|
||||||
@ -7305,3 +7305,8 @@ Video Disk Recorder Revision History
|
|||||||
Sundararaj Reel).
|
Sundararaj Reel).
|
||||||
- Fixed handling timers in case an event is modified and "phased out" while the timer
|
- Fixed handling timers in case an event is modified and "phased out" while the timer
|
||||||
is recording.
|
is recording.
|
||||||
|
- Improved frame detection by parsing just far enough into the MPEG-4 NAL units to get
|
||||||
|
the necessary information about frames and slices.
|
||||||
|
- The initial syncing of the frame detector is now done immediately after the first
|
||||||
|
complete GOP has been seen. This makes recordings and especially pausing live video
|
||||||
|
start up to twice as fast as before.
|
||||||
|
@ -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: recording.c 2.67 2012/10/15 10:23:37 kls Exp $
|
* $Id: recording.c 2.68 2012/11/01 11:51:52 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "recording.h"
|
#include "recording.h"
|
||||||
@ -1550,7 +1550,6 @@ void cIndexFileGenerator::Action(void)
|
|||||||
if (Processed > 0) {
|
if (Processed > 0) {
|
||||||
if (FrameDetector.Synced()) {
|
if (FrameDetector.Synced()) {
|
||||||
// Synced FrameDetector, so rewind for actual processing:
|
// Synced FrameDetector, so rewind for actual processing:
|
||||||
FrameDetector.Reset();
|
|
||||||
Rewind = true;
|
Rewind = true;
|
||||||
}
|
}
|
||||||
Buffer.Del(Processed);
|
Buffer.Del(Processed);
|
||||||
|
694
remux.c
694
remux.c
@ -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.c 2.67 2012/09/19 10:28:42 kls Exp $
|
* $Id: remux.c 2.68 2012/11/02 14:35:57 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "remux.h"
|
#include "remux.h"
|
||||||
@ -23,6 +23,8 @@ static bool DebugFrames = false;
|
|||||||
#define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a)
|
#define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a)
|
||||||
#define dbgframes(a...) if (DebugFrames) fprintf(stderr, a)
|
#define dbgframes(a...) if (DebugFrames) fprintf(stderr, a)
|
||||||
|
|
||||||
|
#define EMPTY_SCANNER (0xFFFFFFFF)
|
||||||
|
|
||||||
ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
|
ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
|
||||||
{
|
{
|
||||||
if (Count < 7)
|
if (Count < 7)
|
||||||
@ -146,6 +148,94 @@ void TsSetTeiOnBrokenPackets(uchar *p, int l)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- cTsPayload ------------------------------------------------------------
|
||||||
|
|
||||||
|
cTsPayload::cTsPayload(void)
|
||||||
|
{
|
||||||
|
data = NULL;
|
||||||
|
length = 0;
|
||||||
|
pid = -1;
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cTsPayload::cTsPayload(uchar *Data, int Length, int Pid)
|
||||||
|
{
|
||||||
|
Setup(Data, Length, Pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cTsPayload::Setup(uchar *Data, int Length, int Pid)
|
||||||
|
{
|
||||||
|
data = Data;
|
||||||
|
length = Length;
|
||||||
|
pid = Pid >= 0 ? Pid : TsPid(Data);
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uchar cTsPayload::GetByte(void)
|
||||||
|
{
|
||||||
|
if (!Eof()) {
|
||||||
|
if (index % TS_SIZE == 0) { // encountered the next TS header
|
||||||
|
for (;; index += TS_SIZE) {
|
||||||
|
if (data[index] == TS_SYNC_BYTE && index + TS_SIZE <= length) { // to make sure we are at a TS header start and drop incomplete TS packets at the end
|
||||||
|
uchar *p = data + index;
|
||||||
|
if (TsPid(p) == pid) { // only handle TS packets for the initial PID
|
||||||
|
if (TsHasPayload(p)) {
|
||||||
|
if (index > 0 && TsPayloadStart(p)) { // checking index to not skip the very first TS packet
|
||||||
|
length = index; // triggers EOF
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
index += TsPayloadOffset(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
length = index; // triggers EOF
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data[index++];
|
||||||
|
}
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cTsPayload::SkipBytes(int Bytes)
|
||||||
|
{
|
||||||
|
while (Bytes-- > 0)
|
||||||
|
GetByte();
|
||||||
|
return !Eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cTsPayload::SkipPesHeader(void)
|
||||||
|
{
|
||||||
|
return SkipBytes(PesPayloadOffset(data + TsPayloadOffset(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int cTsPayload::GetLastIndex(void)
|
||||||
|
{
|
||||||
|
return index - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cTsPayload::SetByte(uchar Byte, int Index)
|
||||||
|
{
|
||||||
|
if (Index >= 0 && Index < length)
|
||||||
|
data[Index] = Byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cTsPayload::Find(uint32_t Code)
|
||||||
|
{
|
||||||
|
int OldIndex = index;
|
||||||
|
uint32_t Scanner = EMPTY_SCANNER;
|
||||||
|
while (!Eof()) {
|
||||||
|
Scanner = (Scanner << 8) | GetByte();
|
||||||
|
if (Scanner == Code)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
index = OldIndex;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// --- cPatPmtGenerator ------------------------------------------------------
|
// --- cPatPmtGenerator ------------------------------------------------------
|
||||||
|
|
||||||
cPatPmtGenerator::cPatPmtGenerator(const cChannel *Channel)
|
cPatPmtGenerator::cPatPmtGenerator(const cChannel *Channel)
|
||||||
@ -665,6 +755,25 @@ void cPatPmtParser::ParsePmt(const uchar *Data, int Length)
|
|||||||
pmtSize = 0;
|
pmtSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cPatPmtParser::ParsePatPmt(const uchar *Data, int Length)
|
||||||
|
{
|
||||||
|
while (Length >= TS_SIZE) {
|
||||||
|
if (*Data != TS_SYNC_BYTE)
|
||||||
|
break; // just for safety
|
||||||
|
int Pid = TsPid(Data);
|
||||||
|
if (Pid == PATPID)
|
||||||
|
ParsePat(Data, TS_SIZE);
|
||||||
|
else if (Pid == PmtPid()) {
|
||||||
|
ParsePmt(Data, TS_SIZE);
|
||||||
|
if (patVersion >= 0 && pmtVersion >= 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Data += TS_SIZE;
|
||||||
|
Length -= TS_SIZE;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool cPatPmtParser::GetVersions(int &PatVersion, int &PmtVersion) const
|
bool cPatPmtParser::GetVersions(int &PatVersion, int &PmtVersion) const
|
||||||
{
|
{
|
||||||
PatVersion = patVersion;
|
PatVersion = patVersion;
|
||||||
@ -809,23 +918,352 @@ void PesDump(const char *Name, const u_char *Data, int Length)
|
|||||||
TsDump(Name, Data, Length);
|
TsDump(Name, Data, Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cFrameDetector --------------------------------------------------------
|
// --- cFrameParser ----------------------------------------------------------
|
||||||
|
|
||||||
#define EMPTY_SCANNER (0xFFFFFFFF)
|
class cFrameParser {
|
||||||
|
protected:
|
||||||
|
bool debug;
|
||||||
|
bool newFrame;
|
||||||
|
bool independentFrame;
|
||||||
|
public:
|
||||||
|
cFrameParser(void);
|
||||||
|
virtual ~cFrameParser() {};
|
||||||
|
virtual int Parse(const uchar *Data, int Length, int Pid) = 0;
|
||||||
|
///< Parses the given Data, which is a sequence of Length bytes of TS packets.
|
||||||
|
///< The payload in the TS packets with the given Pid is searched for just
|
||||||
|
///< enough information to determine the beginning and type of the next video
|
||||||
|
///< frame.
|
||||||
|
///< Returns the number of bytes parsed. Upon return, the functions NewFrame()
|
||||||
|
///< and IndependentFrame() can be called to retrieve the required information.
|
||||||
|
void SetDebug(bool Debug) { debug = Debug; }
|
||||||
|
bool NewFrame(void) { return newFrame; }
|
||||||
|
bool IndependentFrame(void) { return independentFrame; }
|
||||||
|
};
|
||||||
|
|
||||||
|
cFrameParser::cFrameParser(void)
|
||||||
|
{
|
||||||
|
debug = true;
|
||||||
|
newFrame = false;
|
||||||
|
independentFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cAudioParser ----------------------------------------------------------
|
||||||
|
|
||||||
|
class cAudioParser : public cFrameParser {
|
||||||
|
public:
|
||||||
|
cAudioParser(void);
|
||||||
|
virtual int Parse(const uchar *Data, int Length, int Pid);
|
||||||
|
};
|
||||||
|
|
||||||
|
cAudioParser::cAudioParser(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int cAudioParser::Parse(const uchar *Data, int Length, int Pid)
|
||||||
|
{
|
||||||
|
if (TsPayloadStart(Data)) {
|
||||||
|
newFrame = independentFrame = true;
|
||||||
|
if (debug)
|
||||||
|
dbgframes("/");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
newFrame = independentFrame = false;
|
||||||
|
return TS_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cMpeg2Parser ----------------------------------------------------------
|
||||||
|
|
||||||
|
class cMpeg2Parser : public cFrameParser {
|
||||||
|
private:
|
||||||
|
uint32_t scanner;
|
||||||
|
bool seenIndependentFrame;
|
||||||
|
public:
|
||||||
|
cMpeg2Parser(void);
|
||||||
|
virtual int Parse(const uchar *Data, int Length, int Pid);
|
||||||
|
};
|
||||||
|
|
||||||
|
cMpeg2Parser::cMpeg2Parser(void)
|
||||||
|
{
|
||||||
|
scanner = EMPTY_SCANNER;
|
||||||
|
seenIndependentFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid)
|
||||||
|
{
|
||||||
|
newFrame = independentFrame = false;
|
||||||
|
if (Length < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE)
|
||||||
|
return 0; // need more data
|
||||||
|
cTsPayload tsPayload(const_cast<uchar *>(Data), Length, Pid);
|
||||||
|
if (TsPayloadStart(Data)) {
|
||||||
|
tsPayload.SkipPesHeader();
|
||||||
|
scanner = EMPTY_SCANNER;
|
||||||
|
if (debug && seenIndependentFrame)
|
||||||
|
dbgframes("/");
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
scanner = (scanner << 8) | tsPayload.GetByte();
|
||||||
|
if (scanner == 0x00000100) { // Picture Start Code
|
||||||
|
newFrame = true;
|
||||||
|
tsPayload.GetByte();
|
||||||
|
uchar FrameType = (tsPayload.GetByte() >> 3) & 0x07;
|
||||||
|
independentFrame = FrameType == 1; // I-Frame
|
||||||
|
if (debug) {
|
||||||
|
seenIndependentFrame |= independentFrame;
|
||||||
|
if (seenIndependentFrame) {
|
||||||
|
static const char FrameTypes[] = "?IPBD???";
|
||||||
|
dbgframes("%c", FrameTypes[FrameType]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (tsPayload.Available() > (MIN_TS_PACKETS_FOR_FRAME_DETECTOR - 1) * TS_SIZE);
|
||||||
|
return tsPayload.Used();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cMpeg4Parser ----------------------------------------------------------
|
||||||
|
|
||||||
|
class cMpeg4Parser : public cFrameParser {
|
||||||
|
private:
|
||||||
|
enum eNalUnitType {
|
||||||
|
nutCodedSliceNonIdr = 1,
|
||||||
|
nutCodedSliceIdr = 5,
|
||||||
|
nutSequenceParameterSet = 7,
|
||||||
|
nutAccessUnitDelimiter = 9,
|
||||||
|
};
|
||||||
|
cTsPayload tsPayload;
|
||||||
|
uchar byte; // holds the current byte value in case of bitwise access
|
||||||
|
int bit; // the bit index into the current byte (-1 if we're not in bit reading mode)
|
||||||
|
int zeroBytes; // the number of consecutive zero bytes (to detect 0x000003)
|
||||||
|
uint32_t scanner;
|
||||||
|
// Identifiers written in '_' notation as in "ITU-T H.264":
|
||||||
|
bool separate_colour_plane_flag;
|
||||||
|
int log2_max_frame_num;
|
||||||
|
bool frame_mbs_only_flag;
|
||||||
|
//
|
||||||
|
bool gotAccessUnitDelimiter;
|
||||||
|
bool gotSequenceParameterSet;
|
||||||
|
uchar GetByte(bool Raw = false);
|
||||||
|
///< Gets the next data byte. If Raw is true, no filtering will be done.
|
||||||
|
///< With Raw set to false, if the byte sequence 0x000003 is encountered,
|
||||||
|
///< the byte with 0x03 will be skipped.
|
||||||
|
uchar GetBit(void);
|
||||||
|
uint32_t GetBits(int Bits);
|
||||||
|
uint32_t GetGolombUe(void);
|
||||||
|
int32_t GetGolombSe(void);
|
||||||
|
void ParseAccessUnitDelimiter(void);
|
||||||
|
void ParseSequenceParameterSet(void);
|
||||||
|
void ParseSliceHeader(void);
|
||||||
|
public:
|
||||||
|
cMpeg4Parser(void);
|
||||||
|
///< Sets up a new MPEG-4 parser.
|
||||||
|
///< This class parses only the data absolutely necessary to determine the
|
||||||
|
///< frame borders and field count of the given H264 material.
|
||||||
|
virtual int Parse(const uchar *Data, int Length, int Pid);
|
||||||
|
};
|
||||||
|
|
||||||
|
cMpeg4Parser::cMpeg4Parser(void)
|
||||||
|
{
|
||||||
|
byte = 0;
|
||||||
|
bit = -1;
|
||||||
|
zeroBytes = 0;
|
||||||
|
scanner = EMPTY_SCANNER;
|
||||||
|
separate_colour_plane_flag = false;
|
||||||
|
log2_max_frame_num = 0;
|
||||||
|
frame_mbs_only_flag = false;
|
||||||
|
gotAccessUnitDelimiter = false;
|
||||||
|
gotSequenceParameterSet = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uchar cMpeg4Parser::GetByte(bool Raw)
|
||||||
|
{
|
||||||
|
uchar b = tsPayload.GetByte();
|
||||||
|
if (!Raw) {
|
||||||
|
// If we encounter the byte sequence 0x000003, we need to skip the 0x03:
|
||||||
|
if (b == 0x00)
|
||||||
|
zeroBytes++;
|
||||||
|
else {
|
||||||
|
if (b == 0x03 && zeroBytes >= 2)
|
||||||
|
b = tsPayload.GetByte();
|
||||||
|
zeroBytes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
zeroBytes = 0;
|
||||||
|
bit = -1;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
uchar cMpeg4Parser::GetBit(void)
|
||||||
|
{
|
||||||
|
if (bit < 0) {
|
||||||
|
byte = GetByte();
|
||||||
|
bit = 7;
|
||||||
|
}
|
||||||
|
return (byte & (1 << bit--)) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t cMpeg4Parser::GetBits(int Bits)
|
||||||
|
{
|
||||||
|
uint32_t b = 0;
|
||||||
|
while (Bits--)
|
||||||
|
b |= GetBit() << Bits;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t cMpeg4Parser::GetGolombUe(void)
|
||||||
|
{
|
||||||
|
int z = -1;
|
||||||
|
for (int b = 0; !b; z++)
|
||||||
|
b = GetBit();
|
||||||
|
return (1 << z) - 1 + GetBits(z);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t cMpeg4Parser::GetGolombSe(void)
|
||||||
|
{
|
||||||
|
uint32_t v = GetGolombUe();
|
||||||
|
if (v) {
|
||||||
|
if ((v & 0x01) != 0)
|
||||||
|
return (v + 1) / 2; // fails for v == 0xFFFFFFFF, but that will probably never happen
|
||||||
|
else
|
||||||
|
return -int32_t(v / 2);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cMpeg4Parser::Parse(const uchar *Data, int Length, int Pid)
|
||||||
|
{
|
||||||
|
newFrame = independentFrame = false;
|
||||||
|
if (Length < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE)
|
||||||
|
return 0; // need more data
|
||||||
|
tsPayload.Setup(const_cast<uchar *>(Data), Length, Pid);
|
||||||
|
if (TsPayloadStart(Data)) {
|
||||||
|
tsPayload.SkipPesHeader();
|
||||||
|
scanner = EMPTY_SCANNER;
|
||||||
|
if (debug && gotSequenceParameterSet) {
|
||||||
|
dbgframes("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
scanner = (scanner << 8) | GetByte(true);
|
||||||
|
if ((scanner & 0xFFFFFF00) == 0x00000100) { // NAL unit start
|
||||||
|
uchar NalUnitType = scanner & 0x1F;
|
||||||
|
switch (NalUnitType) {
|
||||||
|
case nutAccessUnitDelimiter: ParseAccessUnitDelimiter();
|
||||||
|
gotAccessUnitDelimiter = true;
|
||||||
|
break;
|
||||||
|
case nutSequenceParameterSet: ParseSequenceParameterSet();
|
||||||
|
gotSequenceParameterSet = true;
|
||||||
|
break;
|
||||||
|
case nutCodedSliceNonIdr:
|
||||||
|
case nutCodedSliceIdr: if (gotAccessUnitDelimiter && gotSequenceParameterSet) {
|
||||||
|
ParseSliceHeader();
|
||||||
|
gotAccessUnitDelimiter = false;
|
||||||
|
return tsPayload.Used();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (tsPayload.Available() > (MIN_TS_PACKETS_FOR_FRAME_DETECTOR - 1) * TS_SIZE);
|
||||||
|
return tsPayload.Used();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cMpeg4Parser::ParseAccessUnitDelimiter(void)
|
||||||
|
{
|
||||||
|
if (debug && gotSequenceParameterSet)
|
||||||
|
dbgframes("A");
|
||||||
|
GetByte(); // primary_pic_type
|
||||||
|
}
|
||||||
|
|
||||||
|
void cMpeg4Parser::ParseSequenceParameterSet(void)
|
||||||
|
{
|
||||||
|
uchar profile_idc = GetByte(); // profile_idc
|
||||||
|
GetByte(); // constraint_set[0-5]_flags, reserved_zero_2bits
|
||||||
|
GetByte(); // level_idc
|
||||||
|
GetGolombUe(); // seq_parameter_set_id
|
||||||
|
if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || profile_idc == 86 || profile_idc ==118 || profile_idc == 128) {
|
||||||
|
int chroma_format_idc = GetGolombUe(); // chroma_format_idc
|
||||||
|
if (chroma_format_idc == 3)
|
||||||
|
separate_colour_plane_flag = GetBit();
|
||||||
|
GetGolombUe(); // bit_depth_luma_minus8
|
||||||
|
GetGolombUe(); // bit_depth_chroma_minus8
|
||||||
|
GetBit(); // qpprime_y_zero_transform_bypass_flag
|
||||||
|
if (GetBit()) { // seq_scaling_matrix_present_flag
|
||||||
|
for (int i = 0; i < ((chroma_format_idc != 3) ? 8 : 12); i++) {
|
||||||
|
if (GetBit()) { // seq_scaling_list_present_flag
|
||||||
|
int SizeOfScalingList = (i < 6) ? 16 : 64;
|
||||||
|
int LastScale = 8;
|
||||||
|
int NextScale = 8;
|
||||||
|
for (int j = 0; j < SizeOfScalingList; j++) {
|
||||||
|
if (NextScale)
|
||||||
|
NextScale = (LastScale + GetGolombSe() + 256) % 256; // delta_scale
|
||||||
|
if (NextScale)
|
||||||
|
LastScale = NextScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log2_max_frame_num = GetGolombUe() + 4; // log2_max_frame_num_minus4
|
||||||
|
int pic_order_cnt_type = GetGolombUe(); // pic_order_cnt_type
|
||||||
|
if (pic_order_cnt_type == 0)
|
||||||
|
GetGolombUe(); // log2_max_pic_order_cnt_lsb_minus4
|
||||||
|
else if (pic_order_cnt_type == 1) {
|
||||||
|
GetBit(); // delta_pic_order_always_zero_flag
|
||||||
|
GetGolombSe(); // offset_for_non_ref_pic
|
||||||
|
GetGolombSe(); // offset_for_top_to_bottom_field
|
||||||
|
for (int i = GetGolombUe(); i--; ) // num_ref_frames_in_pic_order_cnt_cycle
|
||||||
|
GetGolombSe(); // offset_for_ref_frame
|
||||||
|
}
|
||||||
|
GetGolombUe(); // max_num_ref_frames
|
||||||
|
GetBit(); // gaps_in_frame_num_value_allowed_flag
|
||||||
|
GetGolombUe(); // pic_width_in_mbs_minus1
|
||||||
|
GetGolombUe(); // pic_height_in_map_units_minus1
|
||||||
|
frame_mbs_only_flag = GetBit(); // frame_mbs_only_flag
|
||||||
|
if (debug) {
|
||||||
|
if (gotAccessUnitDelimiter && !gotSequenceParameterSet)
|
||||||
|
dbgframes("A"); // just for completeness
|
||||||
|
dbgframes(frame_mbs_only_flag ? "S" : "s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cMpeg4Parser::ParseSliceHeader(void)
|
||||||
|
{
|
||||||
|
newFrame = true;
|
||||||
|
GetGolombUe(); // first_mb_in_slice
|
||||||
|
int slice_type = GetGolombUe(); // slice_type, 0 = P, 1 = B, 2 = I, 3 = SP, 4 = SI
|
||||||
|
independentFrame = (slice_type % 5) == 2;
|
||||||
|
if (debug) {
|
||||||
|
static const char SliceTypes[] = "PBIpi";
|
||||||
|
dbgframes("%c", SliceTypes[slice_type % 5]);
|
||||||
|
}
|
||||||
|
if (frame_mbs_only_flag)
|
||||||
|
return; // don't need the rest - a frame is complete
|
||||||
|
GetGolombUe(); // pic_parameter_set_id
|
||||||
|
if (separate_colour_plane_flag)
|
||||||
|
GetBits(2); // colour_plane_id
|
||||||
|
GetBits(log2_max_frame_num); // frame_num
|
||||||
|
if (!frame_mbs_only_flag) {
|
||||||
|
if (GetBit()) // field_pic_flag
|
||||||
|
newFrame = !GetBit(); // bottom_field_flag
|
||||||
|
if (debug)
|
||||||
|
dbgframes(newFrame ? "t" : "b");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cFrameDetector --------------------------------------------------------
|
||||||
|
|
||||||
cFrameDetector::cFrameDetector(int Pid, int Type)
|
cFrameDetector::cFrameDetector(int Pid, int Type)
|
||||||
{
|
{
|
||||||
|
parser = NULL;
|
||||||
SetPid(Pid, Type);
|
SetPid(Pid, Type);
|
||||||
synced = false;
|
synced = false;
|
||||||
newFrame = independentFrame = false;
|
newFrame = independentFrame = false;
|
||||||
numPtsValues = 0;
|
numPtsValues = 0;
|
||||||
numFrames = 0;
|
|
||||||
numIFrames = 0;
|
numIFrames = 0;
|
||||||
framesPerSecond = 0;
|
framesPerSecond = 0;
|
||||||
framesInPayloadUnit = framesPerPayloadUnit = 0;
|
framesInPayloadUnit = framesPerPayloadUnit = 0;
|
||||||
payloadUnitOfFrame = 0;
|
|
||||||
scanning = false;
|
scanning = false;
|
||||||
scanner = EMPTY_SCANNER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int CmpUint32(const void *p1, const void *p2)
|
static int CmpUint32(const void *p1, const void *p2)
|
||||||
@ -840,42 +1278,26 @@ void cFrameDetector::SetPid(int Pid, int Type)
|
|||||||
pid = Pid;
|
pid = Pid;
|
||||||
type = Type;
|
type = Type;
|
||||||
isVideo = type == 0x01 || type == 0x02 || type == 0x1B; // MPEG 1, 2 or 4
|
isVideo = type == 0x01 || type == 0x02 || type == 0x1B; // MPEG 1, 2 or 4
|
||||||
}
|
delete parser;
|
||||||
|
parser = NULL;
|
||||||
void cFrameDetector::Reset(void)
|
if (type == 0x01 || type == 0x02)
|
||||||
{
|
parser = new cMpeg2Parser;
|
||||||
newFrame = independentFrame = false;
|
else if (type == 0x1B)
|
||||||
payloadUnitOfFrame = 0;
|
parser = new cMpeg4Parser;
|
||||||
scanning = false;
|
else if (type == 0x04 || type == 0x06) // MPEG audio or AC3 audio
|
||||||
scanner = EMPTY_SCANNER;
|
parser = new cAudioParser;
|
||||||
}
|
else if (type != 0)
|
||||||
|
esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid);
|
||||||
int cFrameDetector::SkipPackets(const uchar *&Data, int &Length, int &Processed, int &FrameTypeOffset)
|
|
||||||
{
|
|
||||||
if (!synced)
|
|
||||||
dbgframes("%d>", FrameTypeOffset);
|
|
||||||
while (Length >= TS_SIZE) {
|
|
||||||
// switch to the next TS packet, but skip those that have a different PID:
|
|
||||||
Data += TS_SIZE;
|
|
||||||
Length -= TS_SIZE;
|
|
||||||
Processed += TS_SIZE;
|
|
||||||
if (TsPid(Data) == pid)
|
|
||||||
break;
|
|
||||||
else if (Length < TS_SIZE)
|
|
||||||
esyslog("ERROR: out of data while skipping TS packets in cFrameDetector");
|
|
||||||
}
|
|
||||||
FrameTypeOffset -= TS_SIZE;
|
|
||||||
FrameTypeOffset += TsPayloadOffset(Data);
|
|
||||||
return FrameTypeOffset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int cFrameDetector::Analyze(const uchar *Data, int Length)
|
int cFrameDetector::Analyze(const uchar *Data, int Length)
|
||||||
{
|
{
|
||||||
bool SeenPayloadStart = false;
|
if (!parser)
|
||||||
bool SeenAccessUnitDelimiter = false;
|
return 0;
|
||||||
int Processed = 0;
|
int Processed = 0;
|
||||||
newFrame = independentFrame = false;
|
newFrame = independentFrame = false;
|
||||||
while (Length >= TS_SIZE) {
|
while (Length >= MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE) { // makes sure we are looking at enough data, in case the frame type is not stored in the first TS packet
|
||||||
|
// Sync on TS packet borders:
|
||||||
if (Data[0] != TS_SYNC_BYTE) {
|
if (Data[0] != TS_SYNC_BYTE) {
|
||||||
int Skipped = 1;
|
int Skipped = 1;
|
||||||
while (Skipped < Length && (Data[Skipped] != TS_SYNC_BYTE || Length - Skipped > TS_SIZE && Data[Skipped + TS_SIZE] != TS_SYNC_BYTE))
|
while (Skipped < Length && (Data[Skipped] != TS_SYNC_BYTE || Length - Skipped > TS_SIZE && Data[Skipped + TS_SIZE] != TS_SYNC_BYTE))
|
||||||
@ -883,63 +1305,80 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
|||||||
esyslog("ERROR: skipped %d bytes to sync on start of TS packet", Skipped);
|
esyslog("ERROR: skipped %d bytes to sync on start of TS packet", Skipped);
|
||||||
return Processed + Skipped;
|
return Processed + Skipped;
|
||||||
}
|
}
|
||||||
|
// Handle one TS packet:
|
||||||
|
int Handled = TS_SIZE;
|
||||||
if (TsHasPayload(Data) && !TsIsScrambled(Data)) {
|
if (TsHasPayload(Data) && !TsIsScrambled(Data)) {
|
||||||
int Pid = TsPid(Data);
|
int Pid = TsPid(Data);
|
||||||
if (Pid == pid) {
|
if (Pid == pid) {
|
||||||
if (TsPayloadStart(Data)) {
|
if (TsPayloadStart(Data)) {
|
||||||
SeenPayloadStart = true;
|
if (Processed)
|
||||||
if (synced && Processed)
|
|
||||||
return Processed;
|
return Processed;
|
||||||
if (Length < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE)
|
scanning = true;
|
||||||
return Processed; // need more data, in case the frame type is not stored in the first TS packet
|
}
|
||||||
|
if (scanning) {
|
||||||
|
// Detect the beginning of a new frame:
|
||||||
|
if (TsPayloadStart(Data)) {
|
||||||
|
if (!framesPerPayloadUnit)
|
||||||
|
framesPerPayloadUnit = framesInPayloadUnit;
|
||||||
|
}
|
||||||
|
int n = parser->Parse(Data, Length, pid);
|
||||||
|
if (n > 0) {
|
||||||
|
if (parser->NewFrame()) {
|
||||||
|
if (Processed)
|
||||||
|
return Processed; // flush everything before this new frame
|
||||||
|
newFrame = true;
|
||||||
|
independentFrame = parser->IndependentFrame();
|
||||||
|
if (synced) {
|
||||||
|
if (framesPerPayloadUnit <= 1)
|
||||||
|
scanning = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
framesInPayloadUnit++;
|
||||||
|
if (independentFrame)
|
||||||
|
numIFrames++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Handled = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (TsPayloadStart(Data)) {
|
||||||
|
// Determine the frame rate from the PTS values in the PES headers:
|
||||||
if (framesPerSecond <= 0.0) {
|
if (framesPerSecond <= 0.0) {
|
||||||
// frame rate unknown, so collect a sequence of PTS values:
|
// frame rate unknown, so collect a sequence of PTS values:
|
||||||
if (numPtsValues < 2 || numPtsValues < MaxPtsValues && numIFrames < 2) { // collect a sequence containing at least two I-frames
|
if (numPtsValues < 2 || numPtsValues < MaxPtsValues && numIFrames < 2) { // collect a sequence containing at least two I-frames
|
||||||
const uchar *Pes = Data + TsPayloadOffset(Data);
|
if (newFrame) { // only take PTS values at the beginning of a frame (in case if fields!)
|
||||||
if (numIFrames && PesHasPts(Pes)) {
|
const uchar *Pes = Data + TsPayloadOffset(Data);
|
||||||
ptsValues[numPtsValues] = PesGetPts(Pes);
|
if (numIFrames && PesHasPts(Pes)) {
|
||||||
// check for rollover:
|
ptsValues[numPtsValues] = PesGetPts(Pes);
|
||||||
if (numPtsValues && ptsValues[numPtsValues - 1] > 0xF0000000 && ptsValues[numPtsValues] < 0x10000000) {
|
// check for rollover:
|
||||||
dbgframes("#");
|
if (numPtsValues && ptsValues[numPtsValues - 1] > 0xF0000000 && ptsValues[numPtsValues] < 0x10000000) {
|
||||||
numPtsValues = 0;
|
dbgframes("#");
|
||||||
numIFrames = 0;
|
numPtsValues = 0;
|
||||||
numFrames = 0;
|
numIFrames = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
numPtsValues++;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
numPtsValues++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
if (numPtsValues >= 2 && numIFrames >= 2) {
|
||||||
// find the smallest PTS delta:
|
// find the smallest PTS delta:
|
||||||
qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
|
qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
|
||||||
numPtsValues--;
|
numPtsValues--;
|
||||||
for (int i = 0; i < numPtsValues; i++)
|
for (int i = 0; i < numPtsValues; i++)
|
||||||
ptsValues[i] = ptsValues[i + 1] - ptsValues[i];
|
ptsValues[i] = ptsValues[i + 1] - ptsValues[i];
|
||||||
qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
|
qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
|
||||||
uint32_t Delta = ptsValues[0];
|
uint32_t Delta = ptsValues[0] / framesPerPayloadUnit;
|
||||||
// determine frame info:
|
// determine frame info:
|
||||||
if (isVideo) {
|
if (isVideo) {
|
||||||
if (abs(Delta - 3600) <= 1)
|
if (abs(Delta - 3600) <= 1)
|
||||||
framesPerSecond = 25.0;
|
framesPerSecond = 25.0;
|
||||||
else if (Delta % 3003 == 0)
|
else if (Delta % 3003 == 0)
|
||||||
framesPerSecond = 30.0 / 1.001;
|
framesPerSecond = 30.0 / 1.001;
|
||||||
else if (abs(Delta - 1800) <= 1) {
|
else if (abs(Delta - 1800) <= 1)
|
||||||
if (numFrames > 50) {
|
framesPerSecond = 50.0;
|
||||||
// this is a "best guess": if there are more than 50 frames between two I-frames, we assume each "frame" actually contains a "field", so two "fields" make one "frame"
|
|
||||||
framesPerSecond = 25.0;
|
|
||||||
framesPerPayloadUnit = -2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
framesPerSecond = 50.0;
|
|
||||||
}
|
|
||||||
else if (Delta == 1501)
|
else if (Delta == 1501)
|
||||||
if (numFrames > 50) {
|
framesPerSecond = 60.0 / 1.001;
|
||||||
// this is a "best guess": if there are more than 50 frames between two I-frames, we assume each "frame" actually contains a "field", so two "fields" make one "frame"
|
|
||||||
framesPerSecond = 30.0 / 1.001;
|
|
||||||
framesPerPayloadUnit = -2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
framesPerSecond = 60.0 / 1.001;
|
|
||||||
else {
|
else {
|
||||||
framesPerSecond = DEFAULTFRAMESPERSECOND;
|
framesPerSecond = DEFAULTFRAMESPERSECOND;
|
||||||
dsyslog("unknown frame delta (%d), assuming %5.2f fps", Delta, DEFAULTFRAMESPERSECOND);
|
dsyslog("unknown frame delta (%d), assuming %5.2f fps", Delta, DEFAULTFRAMESPERSECOND);
|
||||||
@ -947,122 +1386,21 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
|||||||
}
|
}
|
||||||
else // audio
|
else // audio
|
||||||
framesPerSecond = 90000.0 / Delta; // PTS of audio frames is always increasing
|
framesPerSecond = 90000.0 / Delta; // PTS of audio frames is always increasing
|
||||||
dbgframes("\nDelta = %d FPS = %5.2f FPPU = %d NF = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numFrames);
|
dbgframes("\nDelta = %d FPS = %5.2f FPPU = %d NF = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1);
|
||||||
|
synced = true;
|
||||||
|
parser->SetDebug(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scanner = EMPTY_SCANNER;
|
|
||||||
scanning = true;
|
|
||||||
}
|
|
||||||
if (scanning) {
|
|
||||||
int PayloadOffset = TsPayloadOffset(Data);
|
|
||||||
if (TsPayloadStart(Data)) {
|
|
||||||
PayloadOffset += PesPayloadOffset(Data + PayloadOffset);
|
|
||||||
if (!framesPerPayloadUnit)
|
|
||||||
framesPerPayloadUnit = framesInPayloadUnit;
|
|
||||||
if (DebugFrames && !synced)
|
|
||||||
dbgframes("/");
|
|
||||||
}
|
|
||||||
for (int i = PayloadOffset; scanning && i < TS_SIZE; i++) {
|
|
||||||
scanner <<= 8;
|
|
||||||
scanner |= Data[i];
|
|
||||||
switch (type) {
|
|
||||||
case 0x01: // MPEG 1 video
|
|
||||||
case 0x02: // MPEG 2 video
|
|
||||||
if (scanner == 0x00000100) { // Picture Start Code
|
|
||||||
scanner = EMPTY_SCANNER;
|
|
||||||
if (synced && !SeenPayloadStart && Processed)
|
|
||||||
return Processed; // flush everything before this new frame
|
|
||||||
int FrameTypeOffset = i + 2;
|
|
||||||
if (FrameTypeOffset >= TS_SIZE) // the byte to check is in the next TS packet
|
|
||||||
i = SkipPackets(Data, Length, Processed, FrameTypeOffset);
|
|
||||||
newFrame = true;
|
|
||||||
uchar FrameType = (Data[FrameTypeOffset] >> 3) & 0x07;
|
|
||||||
independentFrame = FrameType == 1; // I-Frame
|
|
||||||
if (synced) {
|
|
||||||
if (framesPerPayloadUnit <= 1)
|
|
||||||
scanning = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
framesInPayloadUnit++;
|
|
||||||
if (independentFrame)
|
|
||||||
numIFrames++;
|
|
||||||
if (numIFrames == 1)
|
|
||||||
numFrames++;
|
|
||||||
dbgframes("%u ", FrameType);
|
|
||||||
}
|
|
||||||
if (synced)
|
|
||||||
return Processed + TS_SIZE; // flag this new frame
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x1B: // MPEG 4 video
|
|
||||||
if (scanner == 0x00000109) { // Access Unit Delimiter
|
|
||||||
scanner = EMPTY_SCANNER;
|
|
||||||
if (synced && !SeenPayloadStart && Processed)
|
|
||||||
return Processed; // flush everything before this new frame
|
|
||||||
SeenAccessUnitDelimiter = true;
|
|
||||||
}
|
|
||||||
else if (SeenAccessUnitDelimiter && scanner == 0x00000001) { // NALU start
|
|
||||||
SeenAccessUnitDelimiter = false;
|
|
||||||
int FrameTypeOffset = i + 1;
|
|
||||||
if (FrameTypeOffset >= TS_SIZE) // the byte to check is in the next TS packet
|
|
||||||
i = SkipPackets(Data, Length, Processed, FrameTypeOffset);
|
|
||||||
newFrame = true;
|
|
||||||
uchar FrameType = Data[FrameTypeOffset] & 0x1F;
|
|
||||||
independentFrame = FrameType == 0x07;
|
|
||||||
if (synced) {
|
|
||||||
if (framesPerPayloadUnit < 0) {
|
|
||||||
payloadUnitOfFrame = (payloadUnitOfFrame + 1) % -framesPerPayloadUnit;
|
|
||||||
if (payloadUnitOfFrame != 0 && independentFrame)
|
|
||||||
payloadUnitOfFrame = 0;
|
|
||||||
if (payloadUnitOfFrame)
|
|
||||||
newFrame = false;
|
|
||||||
}
|
|
||||||
if (framesPerPayloadUnit <= 1)
|
|
||||||
scanning = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
framesInPayloadUnit++;
|
|
||||||
if (independentFrame)
|
|
||||||
numIFrames++;
|
|
||||||
if (numIFrames == 1)
|
|
||||||
numFrames++;
|
|
||||||
dbgframes("%02X ", FrameType);
|
|
||||||
}
|
|
||||||
if (synced)
|
|
||||||
return Processed + TS_SIZE; // flag this new frame
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x04: // MPEG audio
|
|
||||||
case 0x06: // AC3 audio
|
|
||||||
if (synced && Processed)
|
|
||||||
return Processed;
|
|
||||||
newFrame = true;
|
|
||||||
independentFrame = true;
|
|
||||||
if (!synced) {
|
|
||||||
framesInPayloadUnit = 1;
|
|
||||||
if (TsPayloadStart(Data))
|
|
||||||
numIFrames++;
|
|
||||||
}
|
|
||||||
scanning = false;
|
|
||||||
break;
|
|
||||||
default: esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid);
|
|
||||||
pid = 0; // let's just ignore any further data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!synced && framesPerSecond > 0.0 && independentFrame) {
|
|
||||||
synced = true;
|
|
||||||
dbgframes("*\n");
|
|
||||||
Reset();
|
|
||||||
return Processed + TS_SIZE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Pid == PATPID && synced && Processed)
|
else if (Pid == PATPID && synced && Processed)
|
||||||
return Processed; // allow the caller to see any PAT packets
|
return Processed; // allow the caller to see any PAT packets
|
||||||
}
|
}
|
||||||
Data += TS_SIZE;
|
Data += Handled;
|
||||||
Length -= TS_SIZE;
|
Length -= Handled;
|
||||||
Processed += TS_SIZE;
|
Processed += Handled;
|
||||||
|
if (newFrame)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return Processed;
|
return Processed;
|
||||||
}
|
}
|
||||||
|
72
remux.h
72
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 2.32 2011/09/04 12:48:26 kls Exp $
|
* $Id: remux.h 2.33 2012/11/02 14:33:11 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __REMUX_H
|
#ifndef __REMUX_H
|
||||||
@ -151,6 +151,60 @@ inline int64_t PesGetPts(const uchar *p)
|
|||||||
((((int64_t)p[13]) & 0xFE) >> 1);
|
((((int64_t)p[13]) & 0xFE) >> 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A transprent TS payload handler:
|
||||||
|
|
||||||
|
class cTsPayload {
|
||||||
|
private:
|
||||||
|
uchar *data;
|
||||||
|
int length;
|
||||||
|
int pid;
|
||||||
|
int index; // points to the next byte to process
|
||||||
|
public:
|
||||||
|
cTsPayload(void);
|
||||||
|
cTsPayload(uchar *Data, int Length, int Pid = -1);
|
||||||
|
///< Creates a new TS payload handler and calls Setup() with the given Data.
|
||||||
|
void Setup(uchar *Data, int Length, int Pid = -1);
|
||||||
|
///< Sets up this TS payload handler with the given Data, which points to a
|
||||||
|
///< sequence of Length bytes of complete TS packets. Any incomplete TS
|
||||||
|
///< packet at the end will be ignored.
|
||||||
|
///< If Pid is given, only TS packets with data for that PID will be processed.
|
||||||
|
///< Otherwise the PID of the first TS packet defines which payload will be
|
||||||
|
///< delivered.
|
||||||
|
///< Any intermediate TS packets with different PIDs will be skipped.
|
||||||
|
int Available(void) { return length - index; }
|
||||||
|
///< Returns the number of raw bytes (including any TS headers) still available
|
||||||
|
///< in the TS payload handler.
|
||||||
|
int Used(void) { return (index + TS_SIZE - 1) / TS_SIZE * TS_SIZE; }
|
||||||
|
///< Returns the number of raw bytes that have already been used (e.g. by calling
|
||||||
|
///< GetByte()). Any TS packet of which at least a single byte has been delivered
|
||||||
|
///< is counted with its full size.
|
||||||
|
bool Eof(void) const { return index >= length; }
|
||||||
|
///< Returns true if all available bytes of the TS payload have been processed.
|
||||||
|
uchar GetByte(void);
|
||||||
|
///< Gets the next byte of the TS payload, skipping any intermediate TS header data.
|
||||||
|
bool SkipBytes(int Bytes);
|
||||||
|
///< Skips the given number of bytes in the payload and returns true if there
|
||||||
|
///< is still data left to read.
|
||||||
|
bool SkipPesHeader(void);
|
||||||
|
///< Skips all bytes belonging to the PES header of the payload.
|
||||||
|
int GetLastIndex(void);
|
||||||
|
///< Returns the index into the TS data of the payload byte that has most recently
|
||||||
|
///< been read. If no byte has been read yet, -1 will be returned.
|
||||||
|
void SetByte(uchar Byte, int Index);
|
||||||
|
///< Sets the TS data byte at the given Index to the value Byte.
|
||||||
|
///< Index should be one that has been retrieved by a previous call to GetIndex(),
|
||||||
|
///< otherwise the behaviour is undefined. The current read index will not be
|
||||||
|
///< altered by a call to this function.
|
||||||
|
bool Find(uint32_t Code);
|
||||||
|
///< Searches for the four byte sequence given in Code and returns true if it
|
||||||
|
///< was found within the payload data. The next call to GetByte() will return the
|
||||||
|
///< value immediately following the Code. If the code was not found, the read
|
||||||
|
///< index will remain the same as before this call, so that several calls to
|
||||||
|
///< Find() can be performed starting at the same index..
|
||||||
|
///< The special code 0xFFFFFFFF can not be searched, because this value is used
|
||||||
|
///< to initialize the scanner.
|
||||||
|
};
|
||||||
|
|
||||||
// PAT/PMT Generator:
|
// PAT/PMT Generator:
|
||||||
|
|
||||||
#define MAX_SECTION_SIZE 4096 // maximum size of an SI section
|
#define MAX_SECTION_SIZE 4096 // maximum size of an SI section
|
||||||
@ -248,6 +302,10 @@ public:
|
|||||||
///< are delivered to the parser through several subsequent calls to
|
///< are delivered to the parser through several subsequent calls to
|
||||||
///< ParsePmt(). The whole PMT data will be processed once the last packet
|
///< ParsePmt(). The whole PMT data will be processed once the last packet
|
||||||
///< has been received.
|
///< has been received.
|
||||||
|
bool ParsePatPmt(const uchar *Data, int Length);
|
||||||
|
///< Parses the given Data (which may consist of several TS packets, typically
|
||||||
|
///< an entire frame) and extracts the PAT and PMT.
|
||||||
|
///< Returns true if a valid PAT/PMT has been detected.
|
||||||
bool GetVersions(int &PatVersion, int &PmtVersion) const;
|
bool GetVersions(int &PatVersion, int &PmtVersion) const;
|
||||||
///< Returns true if a valid PAT/PMT has been parsed and stores
|
///< Returns true if a valid PAT/PMT has been parsed and stores
|
||||||
///< the current version numbers in the given variables.
|
///< the current version numbers in the given variables.
|
||||||
@ -338,6 +396,8 @@ void PesDump(const char *Name, const u_char *Data, int Length);
|
|||||||
|
|
||||||
#define MIN_TS_PACKETS_FOR_FRAME_DETECTOR 5
|
#define MIN_TS_PACKETS_FOR_FRAME_DETECTOR 5
|
||||||
|
|
||||||
|
class cFrameParser;
|
||||||
|
|
||||||
class cFrameDetector {
|
class cFrameDetector {
|
||||||
private:
|
private:
|
||||||
enum { MaxPtsValues = 150 };
|
enum { MaxPtsValues = 150 };
|
||||||
@ -354,12 +414,9 @@ private:
|
|||||||
double framesPerSecond;
|
double framesPerSecond;
|
||||||
int framesInPayloadUnit;
|
int framesInPayloadUnit;
|
||||||
int framesPerPayloadUnit; // Some broadcasters send one frame per payload unit (== 1),
|
int framesPerPayloadUnit; // Some broadcasters send one frame per payload unit (== 1),
|
||||||
// some put an entire GOP into one payload unit (> 1), and
|
// while others put an entire GOP into one payload unit (> 1).
|
||||||
// some spread a single frame over several payload units (< 0).
|
|
||||||
int payloadUnitOfFrame;
|
|
||||||
bool scanning;
|
bool scanning;
|
||||||
uint32_t scanner;
|
cFrameParser *parser;
|
||||||
int SkipPackets(const uchar *&Data, int &Length, int &Processed, int &FrameTypeOffset);
|
|
||||||
public:
|
public:
|
||||||
cFrameDetector(int Pid = 0, int Type = 0);
|
cFrameDetector(int Pid = 0, int Type = 0);
|
||||||
///< Sets up a frame detector for the given Pid and stream Type.
|
///< Sets up a frame detector for the given Pid and stream Type.
|
||||||
@ -367,9 +424,6 @@ public:
|
|||||||
///< call to SetPid().
|
///< call to SetPid().
|
||||||
void SetPid(int Pid, int Type);
|
void SetPid(int Pid, int Type);
|
||||||
///< Sets the Pid and stream Type to detect frames for.
|
///< Sets the Pid and stream Type to detect frames for.
|
||||||
void Reset(void);
|
|
||||||
///< Resets any counters and flags used while syncing and prepares
|
|
||||||
///< the frame detector for actual work.
|
|
||||||
int Analyze(const uchar *Data, int Length);
|
int Analyze(const uchar *Data, int Length);
|
||||||
///< Analyzes the TS packets pointed to by Data. Length is the number of
|
///< Analyzes the TS packets pointed to by Data. Length is the number of
|
||||||
///< bytes Data points to, and must be a multiple of TS_SIZE.
|
///< bytes Data points to, and must be a multiple of TS_SIZE.
|
||||||
|
Loading…
Reference in New Issue
Block a user