mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
- Removed the '#define FE_CAN_2ND_GEN_MODULATION', since it was wrong and the flag is now in the driver, anyway. - The full-featured DVB cards are now given the TS data directly for replay (thanks to Oliver Endriss for enhancing the av7110 driver to make it replay TS data). The patch from ftp://ftp.cadsoft.de/vdr/Developer/av7110_ts_replay__1.diff implements this change in the driver. The patch av7110_v4ldvb_api5_audiobuf_test_1.diff mentioned in version 1.7.2 is still necessary to avoid audio and video glitches on some channels. - Added a typecast in cUnbufferedFile::Write() to avoid an error message when compiling on 64 bit systems. - Added some missing 'const' statements to cBitmap (thanks to Andreas Regel). - Fixed returning complete PES packets in cTsToPes::GetPes() (thanks to Reinhard Nissl). - Added a missing Detach() in cTransfer::Activate() (thanks to Marco Schlüßler). - Added clearing the TS buffers in cDevice::Detach() (thanks to Marco Schlüßler). - Fixed incrementing the continuity counter in cPatPmtGenerator::GetPmt() (thanks to Johann Friedrichs). - Fixed removing deleted recordings in case there is a problem. Once a recording caused a problem with removing, no others were removed any more and an ongoing recording could fill up the disk and cause other recordings to be deleted automatically (reported by Reinhard Nissl). - Added "DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE" to Make.config.template (thanks to Johann Friedrichs for pointing this out). Plugin authors should add this line to their Makefile or Make.config if they use file access functions that need special versions for 64 bit offsets. - The new command line option -i can be used to set an "instance id", which will be used to distinguish recordings of the same broadcast made by different instances of VDR (suggested by Frank Schmirler). This replaces the use of the "resume id" that was introduced in version 1.7.3. - Added checking mutexCurrentAudioTrack to cDevice::PlayTs() (thanks to Reinhard Nissl for pointing this out). - Fixed handling the pointer field in cPatPmtParser::ParsePmt() (thanks to Frank Schmirler - sorry I swapped two lines when adopting the original patch). - Checking the remaining packet length after processing the pointer field in cPatPmtParser::ParsePat() and cPatPmtParser::ParsePmt() (suggested by Frank Schmirler). - Checking the pointer field in cPatPmtParser::ParsePmt() only in 'payload start' packets (suggested by Frank Schmirler). - Changed cPatPmtGenerator to make sure the PMT pid doesn't collide with any of the actual pids of the channel. - Fixed cDevice::PlayTsAudio() and made cDevice::PlayTsVideo() return 0 if PlayVideo() didn't play anything. - Added an 'int' typecast to calculations involving FramesPerSecond() to avoid compiler warnings (reported by Winfried Koehler). - Fixed detecting frames for pure audio recordings. - Fixed editing PES recordings. The frame type in the index.vdr file generated for the edited PES recording is set to 1 for I-frames and 2 for all others (P- and B-frames). The exact frame type doesn't matter for VDR, it only needs to know if it's an I-frame or not. - The PAT/PMT is now only processed if its version changes (reported by Reinhard Nissl). - Fixed handling the maximum video file size (reported by Udo Richter). - Improved fast-forward/-rewind for audio recordings. The actual data is now sent to the output device, so that it can be replayed and thus cause the proper delay. For pure audio recordings the audio is no longer muted in fast-forward/-rewind mode, so that some orientation regarding the position within the recording is possible. There may still be some offset in the replay position displayed by the progress indicator when switching from fast-forward/-rewind to play mode, as well as in the current position during normal play mode. This is due to the various buffers between the player and the output device and will be addressed later. Note the new function cDevice::IsPlayingVideo(), which is used to inform the player whether there is video data in the currently replayed stream. If a derived cDevice class reimplements PlayTs() or PlayPes(), it also needs to make sure this new function works as expected.
298 lines
8.8 KiB
C++
298 lines
8.8 KiB
C++
/*
|
|
* remux.h: Tools for detecting frames and handling PAT/PMT
|
|
*
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
* how to reach the author.
|
|
*
|
|
* $Id: remux.h 2.7 2009/01/24 13:38:10 kls Exp $
|
|
*/
|
|
|
|
#ifndef __REMUX_H
|
|
#define __REMUX_H
|
|
|
|
#include "channels.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);
|
|
|
|
class cRemux {
|
|
public:
|
|
static void SetBrokenLink(uchar *Data, int Length);
|
|
};
|
|
|
|
// Some TS handling tools.
|
|
// The following functions all take a pointer to one complete TS packet.
|
|
|
|
#define TS_SYNC_BYTE 0x47
|
|
#define TS_SIZE 188
|
|
#define TS_ERROR 0x80
|
|
#define TS_PAYLOAD_START 0x40
|
|
#define TS_TRANSPORT_PRIORITY 0x20
|
|
#define TS_PID_MASK_HI 0x1F
|
|
#define TS_SCRAMBLING_CONTROL 0xC0
|
|
#define TS_ADAPT_FIELD_EXISTS 0x20
|
|
#define TS_PAYLOAD_EXISTS 0x10
|
|
#define TS_CONT_CNT_MASK 0x0F
|
|
#define TS_ADAPT_DISCONT 0x80
|
|
#define TS_ADAPT_RANDOM_ACC 0x40 // would be perfect for detecting independent frames, but unfortunately not used by all broadcasters
|
|
#define TS_ADAPT_ELEM_PRIO 0x20
|
|
#define TS_ADAPT_PCR 0x10
|
|
#define TS_ADAPT_OPCR 0x08
|
|
#define TS_ADAPT_SPLICING 0x04
|
|
#define TS_ADAPT_TP_PRIVATE 0x02
|
|
#define TS_ADAPT_EXTENSION 0x01
|
|
|
|
inline bool TsHasPayload(const uchar *p)
|
|
{
|
|
return p[3] & TS_PAYLOAD_EXISTS;
|
|
}
|
|
|
|
inline bool TsHasAdaptationField(const uchar *p)
|
|
{
|
|
return p[3] & TS_ADAPT_FIELD_EXISTS;
|
|
}
|
|
|
|
inline bool TsPayloadStart(const uchar *p)
|
|
{
|
|
return p[1] & TS_PAYLOAD_START;
|
|
}
|
|
|
|
inline bool TsError(const uchar *p)
|
|
{
|
|
return p[1] & TS_ERROR;
|
|
}
|
|
|
|
inline int TsPid(const uchar *p)
|
|
{
|
|
return (p[1] & TS_PID_MASK_HI) * 256 + p[2];
|
|
}
|
|
|
|
inline bool TsIsScrambled(const uchar *p)
|
|
{
|
|
return p[3] & TS_SCRAMBLING_CONTROL;
|
|
}
|
|
|
|
inline int TsPayloadOffset(const uchar *p)
|
|
{
|
|
return (p[3] & TS_ADAPT_FIELD_EXISTS) ? p[4] + 5 : 4;
|
|
}
|
|
|
|
inline int TsGetPayload(const uchar **p)
|
|
{
|
|
int o = TsPayloadOffset(*p);
|
|
*p += o;
|
|
return TS_SIZE - o;
|
|
}
|
|
|
|
inline int TsContinuityCounter(const uchar *p)
|
|
{
|
|
return p[3] & TS_CONT_CNT_MASK;
|
|
}
|
|
|
|
inline int TsGetAdaptationField(const uchar *p)
|
|
{
|
|
return TsHasAdaptationField(p) ? p[5] : 0x00;
|
|
}
|
|
|
|
// Some PES handling tools:
|
|
// The following functions that take a pointer to PES data all assume that
|
|
// there is enough data so that PesLongEnough() returns true.
|
|
|
|
inline bool PesLongEnough(int Length)
|
|
{
|
|
return Length >= 6;
|
|
}
|
|
|
|
inline bool PesHasLength(const uchar *p)
|
|
{
|
|
return p[4] | p[5];
|
|
}
|
|
|
|
inline int PesLength(const uchar *p)
|
|
{
|
|
return 6 + p[4] * 256 + p[5];
|
|
}
|
|
|
|
inline int PesPayloadOffset(const uchar *p)
|
|
{
|
|
return 9 + p[8];
|
|
}
|
|
|
|
inline bool PesHasPts(const uchar *p)
|
|
{
|
|
return (p[7] & 0x80) && p[8] >= 5;
|
|
}
|
|
|
|
inline int64_t PesGetPts(const uchar *p)
|
|
{
|
|
return ((((int64_t)p[ 9]) & 0x0E) << 29) |
|
|
(( (int64_t)p[10]) << 22) |
|
|
((((int64_t)p[11]) & 0xFE) << 14) |
|
|
(( (int64_t)p[12]) << 7) |
|
|
((((int64_t)p[13]) & 0xFE) >> 1);
|
|
}
|
|
|
|
// PAT/PMT Generator:
|
|
|
|
#define MAX_SECTION_SIZE 4096 // maximum size of an SI section
|
|
#define MAX_PMT_TS (MAX_SECTION_SIZE / TS_SIZE + 1)
|
|
|
|
class cPatPmtGenerator {
|
|
private:
|
|
uchar pat[TS_SIZE]; // the PAT always fits into a single TS packet
|
|
uchar pmt[MAX_PMT_TS][TS_SIZE]; // the PMT may well extend over several TS packets
|
|
int numPmtPackets;
|
|
int patCounter;
|
|
int pmtCounter;
|
|
int patVersion;
|
|
int pmtVersion;
|
|
int pmtPid;
|
|
uchar *esInfoLength;
|
|
void IncCounter(int &Counter, uchar *TsPacket);
|
|
void IncVersion(int &Version);
|
|
void IncEsInfoLength(int Length);
|
|
protected:
|
|
int MakeStream(uchar *Target, uchar Type, int Pid);
|
|
int MakeAC3Descriptor(uchar *Target);
|
|
int MakeSubtitlingDescriptor(uchar *Target, const char *Language);
|
|
int MakeLanguageDescriptor(uchar *Target, const char *Language);
|
|
int MakeCRC(uchar *Target, const uchar *Data, int Length);
|
|
void GeneratePmtPid(cChannel *Channel);
|
|
///< Generates a PMT pid that doesn't collide with any of the actual
|
|
///< pids of the Channel.
|
|
void GeneratePat(void);
|
|
///< Generates a PAT section for later use with GetPat().
|
|
void GeneratePmt(cChannel *Channel);
|
|
///< Generates a PMT section for the given Channel, for later use
|
|
///< with GetPmt().
|
|
public:
|
|
cPatPmtGenerator(cChannel *Channel = NULL);
|
|
void SetChannel(cChannel *Channel);
|
|
///< Sets the Channel for which the PAT/PMT shall be generated.
|
|
uchar *GetPat(void);
|
|
///< Returns a pointer to the PAT section, which consists of exactly
|
|
///< one TS packet.
|
|
uchar *GetPmt(int &Index);
|
|
///< Returns a pointer to the Index'th TS packet of the PMT section.
|
|
///< Index must be initialized to 0 and will be incremented by each
|
|
///< call to GetPmt(). Returns NULL is all packets of the PMT section
|
|
///< have been fetched..
|
|
};
|
|
|
|
// PAT/PMT Parser:
|
|
|
|
class cPatPmtParser {
|
|
private:
|
|
uchar pmt[MAX_SECTION_SIZE];
|
|
int pmtSize;
|
|
int patVersion;
|
|
int pmtVersion;
|
|
int pmtPid;
|
|
int vpid;
|
|
int vtype;
|
|
protected:
|
|
int SectionLength(const uchar *Data, int Length) { return (Length >= 3) ? ((int(Data[1]) & 0x0F) << 8)| Data[2] : 0; }
|
|
public:
|
|
cPatPmtParser(void);
|
|
void Reset(void);
|
|
///< Resets the parser. This function must be called whenever a new
|
|
///< stream is parsed.
|
|
void ParsePat(const uchar *Data, int Length);
|
|
///< Parses the PAT data from the single TS packet in Data.
|
|
///< Length is always TS_SIZE.
|
|
void ParsePmt(const uchar *Data, int Length);
|
|
///< Parses the PMT data from the single TS packet in Data.
|
|
///< Length is always TS_SIZE.
|
|
///< The PMT may consist of several TS packets, which
|
|
///< are delivered to the parser through several subsequent calls to
|
|
///< ParsePmt(). The whole PMT data will be processed once the last packet
|
|
///< has been received.
|
|
int PmtPid(void) { return pmtPid; }
|
|
///< Returns the PMT pid as defined by the current PAT.
|
|
///< If no PAT has been received yet, -1 will be returned.
|
|
int Vpid(void) { return vpid; }
|
|
///< Returns the video pid as defined by the current PMT.
|
|
int Vtype(void) { return vtype; }
|
|
};
|
|
|
|
// TS to PES converter:
|
|
// Puts together the payload of several TS packets that form one PES
|
|
// packet.
|
|
|
|
class cTsToPes {
|
|
private:
|
|
uchar *data;
|
|
int size;
|
|
int length;
|
|
int offset;
|
|
bool synced;
|
|
public:
|
|
cTsToPes(void);
|
|
~cTsToPes();
|
|
void PutTs(const uchar *Data, int Length);
|
|
///< Puts the payload data of the single TS packet at Data into the converter.
|
|
///< Length is always 188.
|
|
///< If the given TS packet starts a new PES payload packet, the converter
|
|
///< will be automatically reset. Any packets before the first one that starts
|
|
///< a new PES payload packet will be ignored.
|
|
const uchar *GetPes(int &Length);
|
|
///< Gets a pointer to the complete PES packet, or NULL if the packet
|
|
///< is not complete yet. If the packet is complete, Length will contain
|
|
///< the total packet length. The returned pointer is only valid until
|
|
///< the next call to PutTs() or Reset(), or until this object is destroyed.
|
|
void Reset(void);
|
|
///< Resets the converter. This needs to be called after a PES packet has
|
|
///< been fetched by a call to GetPes(), and before the next call to
|
|
///< PutTs().
|
|
};
|
|
|
|
// Some helper functions for debugging:
|
|
|
|
void BlockDump(const char *Name, const u_char *Data, int Length);
|
|
void TsDump(const char *Name, const u_char *Data, int Length);
|
|
void PesDump(const char *Name, const u_char *Data, int Length);
|
|
|
|
// Frame detector:
|
|
|
|
class cFrameDetector {
|
|
private:
|
|
int pid;
|
|
int type;
|
|
bool newFrame;
|
|
bool independentFrame;
|
|
int64_t lastPts;
|
|
bool isVideo;
|
|
int frameDuration;
|
|
int framesPerPayloadUnit;
|
|
bool scanning;
|
|
uint32_t scanner;
|
|
public:
|
|
cFrameDetector(int Pid, int Type);
|
|
int Analyze(const uchar *Data, int Length);
|
|
///< Analyzes the TS packets pointed to by Data. Length is the number of
|
|
///< bytes Data points to, and must be a multiple of 188.
|
|
///< Returns the number of bytes that have been analyzed and may be written
|
|
///< to the recording file. If the return value is 0, the data was not
|
|
///< sufficient for analyzing and Analyze() needs to be called again with
|
|
///< more actual data.
|
|
bool NewFrame(void) { return newFrame; }
|
|
///< Returns true if the data given to the last call to Analyze() started a
|
|
///< new frame.
|
|
bool IndependentFrame(void) { return independentFrame; }
|
|
///< Returns true if a new frame was detected and this is an independent frame
|
|
///< (i.e. one that can be displayed by itself, without using data from any
|
|
///< other frames).
|
|
double FramesPerSecond(void) { return frameDuration ? 90000.0 / frameDuration : 0; }
|
|
///< Returns the number of frames per second, or 0 if this information is not
|
|
///< available.
|
|
};
|
|
|
|
#endif // __REMUX_H
|