mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
- Removed an unused variable from cTimer::GetWDayFromMDay() (thanks to Wayne Keer for reporting this one). - Some more changes to the 'childTid' handling in cThread (based on suggestions by Stefan Huelswitt). - Fixed the spelling of 'canceling' (thanks to Wayne Keer for reporting this one). - Re-introduced a sleep to cDvbPlayer::Action() to avoid high CPU load in still picture mode (thanks to Reinhard Nissl for reporting this one). - Fixed a possible race condition in generating the DVB device names (thanks to Rainer Zocholl for reporting this one). - Changed the way PES packets are played to allow replay of AC3 sound over the full featured DVB cards (partially based on a patch from Werner Fink). + The new function cDevice::PlayPes() is now called with the complete PES data stream and calls PlayVideo() and PlayAudio() as necessary. + cDevice::PlayVideo() is now only called with actual video PES packets. + cDevice::PlayAudio() is now called with the actual audio PES packets, which can be either "normal" audio or AC3 data. You need at least firmware version 0x261d to replay AC3 sound over a full featured DVB card. This function now has an 'int' return value. + PlayAudio() of derived cDevice classes shall no longer call the base class function. It shall just play the given data as audio. + cPlayer::PlayVideo() and cPlayer::PlayAudio() are now obsolete and have been replaced with cPlayer::PlayPes(). + All StripAudioPackets() functions are now obsolete. The functionality has been moved into cDevice::PlayPes(), where only the video and audio packets that are actually required will be processed. + All audio track handling is now done by cDevice; cTransfer and cDvbPlayer no longer care about audio tracks. cPlayer, however, still has the virtual hooks for audio track handling in order to allow plugins to implement players that have their own idea about this. + cChannel::[AD]pid[12]() have been replaced with cChannel::[AD]pid(int i) to allow access to all available PIDs. - Escaped the '-' and 'ö' characters in the man pages (thanks to Darren Salt for pointing this out). - Completed the Italian OSD texts (thanks to Sean Carlos). - Fixed setting 'synced' in cRemux when recording radio channels (thanks to Laurence Abbott). - Removed the LOCK_THREAD from the LIRC thread (thanks to Ludwig Nussel). - Fixed genfontfile.c (sometimes the character width was wrong, and the codes were shifted one too far to the left). - Fixed the character width and shifted the codes one to the right in all font files. - Renamed font???.c to font???-iso8859-1.c for symmetry. - Switched the character set to iso8859-15 for English, German and Finnish (thanks to Andreas Brugger for reporting the missing Euro sign in iso8859-1). - Added 'channels.conf.terr' entries for Lübeck (thanks to Stefan Hußfeldt). - Fixed a race condition in starting a thread (thanks to Reinhard Nissl for reporting this one). - Replaced non-threadsafe library functions with their threadsafe versions (thanks to Rainer Zocholl for pointing this out). - Other non-threadsafe functions have been replaced by threadsafe classes that hide the actual buffering. In particular these are: readdir() -> cReadDir readline() -> cReadLine - Several formerly non-threadsafe functions now have a return type of cString: cChannel::ToText() tChannelID::ToString() cEvent::GetDateString() cEvent::GetTimeString() cEvent::GetEndTimeString() cEvent::GetVpsString() cMark::ToText() cTimer::ToText() cSource::ToString() cTimer::PrintDay() cTimer::PrintFirstDay() PrefixVideoFileName() IndexToHMSF() ChannelString() strescape() AddDirectory() itoa() WeekDayName() DayDateTime() When using these functions in a 'const char *' context there is nothing special to consider, except that you can no longer have a pointer to the return value, as in const char *date = DayDateTime(); Although this will compile without error message, the resulting 'date' will not be valid after this line. Use this instead: cString date = DayDateTime(); In a 'const void *' context (as in printf() etc.) the result needs to be dereferenced with a '*', as in printf("%s", *DayDateTime()); to make it a 'const char *'. - Removed delay_ms(), using cCondWait::SleepMs() instead. - Replaced time_ms() with a threadsafe and non-overflowing cTimeMs (thanks to Rainer Zocholl for pointing out this problem). - Added cDevice::mutexReceiver to avoid a race condition when attaching/detaching receivers from different threads. - The new remote control button "Audio" can be used to switch between different audio tracks. The "Green" button in the "Main" menu has been changed from "Language" to "Audio", since it now also controls switching between normal and Dolby Digital audio tracks (see MANUAL for details). - The description of the audio tracks is now taken from the "component descriptors" that are broadcast in the EPG data. However (as no big surprise), not all channels actually provide useful data here, so there are now some additional EPG bugfixes, which can be activated by setting the "EPG bugfix level" to 3. - The format of the 'epg.data' files has been extended by the new tag 'X', which contains the stream components of an event (see man vdr(5) for details). - The cStatus class now has the new member function SetAudioTrack(), which can be used to get notified when the audio track has been switched, and the new member function SetAudioChannel() which is called when the audio channel is changed. - Skins need to implement the new cSkinDisplayTrack class to display the audio track menu. - The ST:TNG skin now displays the current audio track description (if any) at the botton left side. - The new setup option "DVB/Audio languages" can be used to control which audio language shall be selected in case a channel broadcasts in different languages (see MANUAL for details). - The "Left" and "Right" keys in the "Audio" menu can be used to switch between the left and right stereo channels in case there are different audio tracks in these channels (see MANUAL for details). - Fixed a possible race condition in cDevice::Action() (thanks to Mattias Grönlund). - Fixed the default quality value when grabbing a JPEG image (thanks to Patrick Gleichmann). - Fixed deleting a menu item in case the next item is not selectable (thanks to Dino Ravnic). - Implemented displaying mandatory subtitles in the SPU decoder (thanks to Marco Schlüßler). - The setup option "Recording/Record Dolby Digital" has been renamed and moved to "DVB/Use Dolby Digital". It now controls whether Dolby Digital is recorded and whether an available DD audio track will appear in the "Audio" menu. - Added support for circular polarization (thanks to Jonan Santiago). - Thanks to Werner Fink, Reinhard Nissl, Sascha Volkenandt and Bjørnar Nilsen for their support in testing and fine tuning this version.
641 lines
18 KiB
C
641 lines
18 KiB
C
/*
|
|
* remux.c: A streaming MPEG2 remultiplexer
|
|
*
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
* how to reach the author.
|
|
*
|
|
* The parts of this code that implement cTS2PES have been taken from
|
|
* the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
|
|
* VDR's needs.
|
|
*
|
|
* $Id: remux.c 1.23 2004/12/18 13:15:02 kls Exp $
|
|
*/
|
|
|
|
#include "remux.h"
|
|
#include <stdlib.h>
|
|
#include "thread.h"
|
|
#include "tools.h"
|
|
|
|
// --- cTS2PES ---------------------------------------------------------------
|
|
|
|
#include <netinet/in.h>
|
|
|
|
//XXX TODO: these should really be available in some driver header file!
|
|
#define PROG_STREAM_MAP 0xBC
|
|
#ifndef PRIVATE_STREAM1
|
|
#define PRIVATE_STREAM1 0xBD
|
|
#endif
|
|
#define PADDING_STREAM 0xBE
|
|
#ifndef PRIVATE_STREAM2
|
|
#define PRIVATE_STREAM2 0xBF
|
|
#endif
|
|
#define AUDIO_STREAM_S 0xC0
|
|
#define AUDIO_STREAM_E 0xDF
|
|
#define VIDEO_STREAM_S 0xE0
|
|
#define VIDEO_STREAM_E 0xEF
|
|
#define ECM_STREAM 0xF0
|
|
#define EMM_STREAM 0xF1
|
|
#define DSM_CC_STREAM 0xF2
|
|
#define ISO13522_STREAM 0xF3
|
|
#define PROG_STREAM_DIR 0xFF
|
|
|
|
//pts_dts flags
|
|
#define PTS_ONLY 0x80
|
|
|
|
#define TS_SIZE 188
|
|
#define PID_MASK_HI 0x1F
|
|
#define CONT_CNT_MASK 0x0F
|
|
|
|
// Flags:
|
|
#define PAY_START 0x40
|
|
#define TS_ERROR 0x80
|
|
#define ADAPT_FIELD 0x20
|
|
|
|
#define MAX_PLENGTH 0xFFFF // the maximum PES packet length (theoretically)
|
|
#define MMAX_PLENGTH (8*MAX_PLENGTH) // some stations send PES packets that are extremely large, e.g. DVB-T in Finland
|
|
|
|
#define IPACKS 2048
|
|
|
|
// Start codes:
|
|
#define SC_PICTURE 0x00 // "picture header"
|
|
|
|
#define MAXNONUSEFULDATA (10*1024*1024)
|
|
#define MAXNUMUPTERRORS 10
|
|
|
|
class cTS2PES {
|
|
private:
|
|
int size;
|
|
int found;
|
|
int count;
|
|
uint8_t *buf;
|
|
uint8_t cid;
|
|
uint8_t audioCid;
|
|
int plength;
|
|
uint8_t plen[2];
|
|
uint8_t flag1;
|
|
uint8_t flag2;
|
|
uint8_t hlength;
|
|
int mpeg;
|
|
uint8_t check;
|
|
int which;
|
|
bool done;
|
|
cRingBufferLinear *resultBuffer;
|
|
int tsErrors;
|
|
int ccErrors;
|
|
int ccCounter;
|
|
static uint8_t headr[];
|
|
void store(uint8_t *Data, int Count);
|
|
void reset_ipack(void);
|
|
void send_ipack(void);
|
|
void write_ipack(const uint8_t *Data, int Count);
|
|
void instant_repack(const uint8_t *Buf, int Count);
|
|
public:
|
|
cTS2PES(cRingBufferLinear *ResultBuffer, int Size, uint8_t AudioCid = 0x00);
|
|
~cTS2PES();
|
|
void ts_to_pes(const uint8_t *Buf); // don't need count (=188)
|
|
void Clear(void);
|
|
};
|
|
|
|
uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 };
|
|
|
|
cTS2PES::cTS2PES(cRingBufferLinear *ResultBuffer, int Size, uint8_t AudioCid)
|
|
{
|
|
resultBuffer = ResultBuffer;
|
|
size = Size;
|
|
audioCid = AudioCid;
|
|
|
|
tsErrors = 0;
|
|
ccErrors = 0;
|
|
ccCounter = -1;
|
|
|
|
if (!(buf = MALLOC(uint8_t, size)))
|
|
esyslog("Not enough memory for ts_transform");
|
|
|
|
reset_ipack();
|
|
}
|
|
|
|
cTS2PES::~cTS2PES()
|
|
{
|
|
if (tsErrors || ccErrors)
|
|
dsyslog("cTS2PES got %d TS errors, %d TS continuity errors", tsErrors, ccErrors);
|
|
free(buf);
|
|
}
|
|
|
|
void cTS2PES::Clear(void)
|
|
{
|
|
reset_ipack();
|
|
}
|
|
|
|
void cTS2PES::store(uint8_t *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);
|
|
}
|
|
|
|
void cTS2PES::reset_ipack(void)
|
|
{
|
|
found = 0;
|
|
cid = 0;
|
|
plength = 0;
|
|
flag1 = 0;
|
|
flag2 = 0;
|
|
hlength = 0;
|
|
mpeg = 0;
|
|
check = 0;
|
|
which = 0;
|
|
done = false;
|
|
count = 0;
|
|
}
|
|
|
|
void cTS2PES::send_ipack(void)
|
|
{
|
|
if (count < 10)
|
|
return;
|
|
buf[3] = (AUDIO_STREAM_S <= cid && cid <= AUDIO_STREAM_E && audioCid) ? audioCid : cid;
|
|
buf[4] = (uint8_t)(((count - 6) & 0xFF00) >> 8);
|
|
buf[5] = (uint8_t)((count - 6) & 0x00FF);
|
|
store(buf, count);
|
|
|
|
switch (mpeg) {
|
|
case 2:
|
|
buf[6] = 0x80;
|
|
buf[7] = 0x00;
|
|
buf[8] = 0x00;
|
|
count = 9;
|
|
break;
|
|
case 1:
|
|
buf[6] = 0x0F;
|
|
count = 7;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void cTS2PES::write_ipack(const uint8_t *Data, int Count)
|
|
{
|
|
if (count < 6) {
|
|
memcpy(buf, headr, 3);
|
|
count = 6;
|
|
}
|
|
|
|
if (count + Count < size) {
|
|
memcpy(buf + count, Data, Count);
|
|
count += Count;
|
|
}
|
|
else {
|
|
int rest = size - count;
|
|
memcpy(buf + count, Data, rest);
|
|
count += rest;
|
|
send_ipack();
|
|
if (Count - rest > 0)
|
|
write_ipack(Data + rest, Count - rest);
|
|
}
|
|
}
|
|
|
|
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)) {
|
|
switch (found ) {
|
|
case 0:
|
|
case 1:
|
|
if (Buf[c] == 0x00)
|
|
found++;
|
|
else
|
|
found = 0;
|
|
c++;
|
|
break;
|
|
case 2:
|
|
if (Buf[c] == 0x01)
|
|
found++;
|
|
else if (Buf[c] != 0)
|
|
found = 0;
|
|
c++;
|
|
break;
|
|
case 3:
|
|
cid = 0;
|
|
switch (Buf[c]) {
|
|
case PROG_STREAM_MAP:
|
|
case PRIVATE_STREAM2:
|
|
case PROG_STREAM_DIR:
|
|
case ECM_STREAM :
|
|
case EMM_STREAM :
|
|
case PADDING_STREAM :
|
|
case DSM_CC_STREAM :
|
|
case ISO13522_STREAM:
|
|
done = true;
|
|
case PRIVATE_STREAM1:
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
found++;
|
|
cid = Buf[c++];
|
|
break;
|
|
default:
|
|
found = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (Count - c > 1) {
|
|
unsigned short *pl = (unsigned short *)(Buf + c);
|
|
plength = ntohs(*pl);
|
|
c += 2;
|
|
found += 2;
|
|
}
|
|
else {
|
|
plen[0] = Buf[c];
|
|
found++;
|
|
return;
|
|
}
|
|
break;
|
|
case 5: {
|
|
plen[1] = Buf[c++];
|
|
unsigned short *pl = (unsigned short *)plen;
|
|
plength = ntohs(*pl);
|
|
found++;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (!done) {
|
|
flag1 = Buf[c++];
|
|
found++;
|
|
if ((flag1 & 0xC0) == 0x80 )
|
|
mpeg = 2;
|
|
else {
|
|
hlength = 0;
|
|
which = 0;
|
|
mpeg = 1;
|
|
flag2 = 0;
|
|
}
|
|
}
|
|
break;
|
|
case 7:
|
|
if (!done && mpeg == 2) {
|
|
flag2 = Buf[c++];
|
|
found++;
|
|
}
|
|
break;
|
|
case 8:
|
|
if (!done && mpeg == 2) {
|
|
hlength = Buf[c++];
|
|
found++;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!plength)
|
|
plength = MMAX_PLENGTH - 6;
|
|
|
|
if (done || ((mpeg == 2 && found >= 9) || (mpeg == 1 && found >= 7))) {
|
|
switch (cid) {
|
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
|
case PRIVATE_STREAM1:
|
|
|
|
if (mpeg == 2 && found == 9) {
|
|
write_ipack(&flag1, 1);
|
|
write_ipack(&flag2, 1);
|
|
write_ipack(&hlength, 1);
|
|
}
|
|
|
|
if (mpeg == 1 && found == 7)
|
|
write_ipack(&flag1, 1);
|
|
|
|
if (mpeg == 2 && (flag2 & PTS_ONLY) && found < 14) {
|
|
while (c < Count && found < 14) {
|
|
write_ipack(Buf + c, 1);
|
|
c++;
|
|
found++;
|
|
}
|
|
if (c == Count)
|
|
return;
|
|
}
|
|
|
|
while (c < Count && found < plength + 6) {
|
|
int l = Count - c;
|
|
if (l + found > plength + 6)
|
|
l = plength + 6 - found;
|
|
write_ipack(Buf + c, l);
|
|
found += l;
|
|
c += l;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (done) {
|
|
if (found + Count - c < plength + 6) {
|
|
found += Count - c;
|
|
c = Count;
|
|
}
|
|
else {
|
|
c += plength + 6 - found;
|
|
found = plength + 6;
|
|
}
|
|
}
|
|
|
|
if (plength && found == plength + 6) {
|
|
if (plength == MMAX_PLENGTH - 6)
|
|
esyslog("ERROR: PES packet length overflow in remuxer (stream corruption)");
|
|
send_ipack();
|
|
reset_ipack();
|
|
if (c < Count)
|
|
instant_repack(Buf + c, Count - c);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188)
|
|
{
|
|
if (!Buf)
|
|
return;
|
|
|
|
if (Buf[1] & TS_ERROR)
|
|
tsErrors++;
|
|
if ((Buf[3] ^ ccCounter) & CONT_CNT_MASK) {
|
|
// This should check duplicates and packets which do not increase the counter.
|
|
// But as the errors usually come in bursts this should be enough to
|
|
// show you there is something wrong with signal quality.
|
|
if (ccCounter != -1 && ((Buf[3] ^ (ccCounter + 1)) & CONT_CNT_MASK)) {
|
|
ccErrors++;
|
|
// Enable this if you are having problems with signal quality.
|
|
// These are the errors I used to get with Nova-T when antenna
|
|
// was not positioned correcly (not transport errors). //tvr
|
|
//dsyslog("TS continuity error (%d)", ccCounter);
|
|
}
|
|
ccCounter = Buf[3] & CONT_CNT_MASK;
|
|
}
|
|
|
|
if (Buf[1] & PAY_START) {
|
|
if (plength == MMAX_PLENGTH - 6 && found > 6) {
|
|
plength = found - 6;
|
|
found = 0;
|
|
send_ipack();
|
|
reset_ipack();
|
|
}
|
|
}
|
|
|
|
uint8_t off = 0;
|
|
|
|
if (Buf[3] & ADAPT_FIELD) { // adaptation field?
|
|
off = Buf[4] + 1;
|
|
if (off + 4 > 187)
|
|
return;
|
|
}
|
|
|
|
instant_repack(Buf + 4 + off, TS_SIZE - 4 - off);
|
|
}
|
|
|
|
// --- cRemux ----------------------------------------------------------------
|
|
|
|
#define RESULTBUFFERSIZE KILOBYTE(256)
|
|
|
|
cRemux::cRemux(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOnFailure)
|
|
{
|
|
vPid = VPid;
|
|
aPid1 = APid1;
|
|
aPid2 = APid2;
|
|
dPid1 = DPid1;
|
|
dPid2 = DPid2;
|
|
exitOnFailure = ExitOnFailure;
|
|
numUPTerrors = 0;
|
|
synced = false;
|
|
skipped = 0;
|
|
resultSkipped = 0;
|
|
resultBuffer = new cRingBufferLinear(RESULTBUFFERSIZE, IPACKS, false, "Result");
|
|
resultBuffer->SetTimeouts(0, 100);
|
|
vTS2PES = new cTS2PES(resultBuffer, IPACKS);
|
|
aTS2PES1 = new cTS2PES(resultBuffer, IPACKS, 0xC0);
|
|
aTS2PES2 = aPid2 ? new cTS2PES(resultBuffer, IPACKS, 0xC1) : NULL;
|
|
dTS2PES1 = dPid1 ? new cTS2PES(resultBuffer, IPACKS) : NULL;
|
|
//XXX don't yet know how to tell apart primary and secondary DD data...
|
|
dTS2PES2 = /*XXX dPid2 ? new cTS2PES(resultBuffer, IPACKS) : XXX*/ NULL;
|
|
}
|
|
|
|
cRemux::~cRemux()
|
|
{
|
|
delete vTS2PES;
|
|
delete aTS2PES1;
|
|
delete aTS2PES2;
|
|
delete dTS2PES1;
|
|
delete dTS2PES2;
|
|
delete resultBuffer;
|
|
}
|
|
|
|
int cRemux::GetPid(const uchar *Data)
|
|
{
|
|
return (((uint16_t)Data[0] & PID_MASK_HI) << 8) | (Data[1] & 0xFF);
|
|
}
|
|
|
|
int cRemux::GetPacketLength(const uchar *Data, int Count, int Offset)
|
|
{
|
|
// Returns the length of the packet starting at Offset, or -1 if Count is
|
|
// too small to contain the entire packet.
|
|
int Length = (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1;
|
|
if (Length > 0 && Offset + Length <= Count)
|
|
return Length;
|
|
return -1;
|
|
}
|
|
|
|
int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType)
|
|
{
|
|
// Scans the video packet starting at Offset and returns its length.
|
|
// 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++) {
|
|
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;
|
|
return Length;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PictureType = NO_PICTURE;
|
|
return Length;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#define TS_SYNC_BYTE 0x47
|
|
|
|
int cRemux::Put(const uchar *Data, int Count)
|
|
{
|
|
int used = 0;
|
|
|
|
// Make sure we are looking at a TS packet:
|
|
|
|
while (Count > TS_SIZE) {
|
|
if (Data[0] == TS_SYNC_BYTE && Data[TS_SIZE] == TS_SYNC_BYTE)
|
|
break;
|
|
Data++;
|
|
Count--;
|
|
used++;
|
|
}
|
|
if (used)
|
|
esyslog("ERROR: skipped %d byte to sync on TS packet", used);
|
|
|
|
// Convert incoming TS data into multiplexed PES:
|
|
|
|
for (int i = 0; i < Count; i += TS_SIZE) {
|
|
if (Count - i < TS_SIZE)
|
|
break;
|
|
if (Data[i] != TS_SYNC_BYTE)
|
|
break;
|
|
if (resultBuffer->Free() < 2 * IPACKS)
|
|
break; // A cTS2PES might write one full packet and also a small rest
|
|
int pid = GetPid(Data + i + 1);
|
|
if (Data[i + 3] & 0x10) { // got payload
|
|
if (pid == vPid) vTS2PES->ts_to_pes(Data + i);
|
|
else if (pid == aPid1) aTS2PES1->ts_to_pes(Data + i);
|
|
else if (pid == aPid2 && aTS2PES2) aTS2PES2->ts_to_pes(Data + i);
|
|
else if (pid == dPid1 && dTS2PES1) dTS2PES1->ts_to_pes(Data + i);
|
|
else if (pid == dPid2 && dTS2PES2) dTS2PES2->ts_to_pes(Data + i);
|
|
}
|
|
used += TS_SIZE;
|
|
}
|
|
|
|
// Check if we're getting anywhere here:
|
|
if (!synced && skipped >= 0) {
|
|
if (skipped > MAXNONUSEFULDATA) {
|
|
esyslog("ERROR: no useful data seen within %d byte of video stream", skipped);
|
|
skipped = -1;
|
|
if (exitOnFailure)
|
|
cThread::EmergencyExit(true);
|
|
}
|
|
else
|
|
skipped += used;
|
|
}
|
|
|
|
return used;
|
|
}
|
|
|
|
uchar *cRemux::Get(int &Count, uchar *PictureType)
|
|
{
|
|
// Remove any previously skipped data from the result buffer:
|
|
|
|
if (resultSkipped > 0) {
|
|
resultBuffer->Del(resultSkipped);
|
|
resultSkipped = 0;
|
|
}
|
|
|
|
#if 0
|
|
// Test recording without determining the real frame borders:
|
|
if (PictureType)
|
|
*PictureType = I_FRAME;
|
|
return resultBuffer->Get(Count);
|
|
#endif
|
|
|
|
// Special VPID case to enable recording radio channels:
|
|
|
|
if (vPid == 0 || vPid == 1 || vPid == 0x1FFF) {
|
|
// XXX actually '0' should be enough, but '1' must be used with encrypted channels (driver bug?)
|
|
// XXX also allowing 0x1FFF to not break Michael Paar's original patch,
|
|
// XXX but it would probably be best to only use '0'
|
|
// Force syncing of radio channels to avoid "no useful data" error
|
|
synced = true;
|
|
if (PictureType)
|
|
*PictureType = I_FRAME;
|
|
return resultBuffer->Get(Count);
|
|
}
|
|
|
|
// Check for frame borders:
|
|
|
|
if (PictureType)
|
|
*PictureType = NO_PICTURE;
|
|
|
|
Count = 0;
|
|
uchar *resultData = NULL;
|
|
int resultCount = 0;
|
|
uchar *data = resultBuffer->Get(resultCount);
|
|
if (data) {
|
|
for (int i = 0; i < resultCount - 3; i++) {
|
|
if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
|
|
int l = 0;
|
|
uchar StreamType = data[i + 3];
|
|
if (VIDEO_STREAM_S <= StreamType && StreamType <= VIDEO_STREAM_E) {
|
|
uchar pt = NO_PICTURE;
|
|
l = ScanVideoPacket(data, resultCount, i, pt);
|
|
if (l < 0)
|
|
return resultData;
|
|
if (pt != NO_PICTURE) {
|
|
if (pt < I_FRAME || B_FRAME < pt) {
|
|
esyslog("ERROR: unknown picture type '%d'", pt);
|
|
if (++numUPTerrors > MAXNUMUPTERRORS && exitOnFailure)
|
|
cThread::EmergencyExit(true);
|
|
}
|
|
else if (!synced) {
|
|
if (pt == I_FRAME) {
|
|
if (PictureType)
|
|
*PictureType = pt;
|
|
resultSkipped = i; // will drop everything before this position
|
|
SetBrokenLink(data + i, l);
|
|
synced = true;
|
|
}
|
|
}
|
|
else if (Count)
|
|
return resultData;
|
|
else if (PictureType)
|
|
*PictureType = pt;
|
|
}
|
|
}
|
|
else { //if (AUDIO_STREAM_S <= StreamType && StreamType <= AUDIO_STREAM_E || StreamType == PRIVATE_STREAM1) {
|
|
l = GetPacketLength(data, resultCount, i);
|
|
if (l < 0)
|
|
return resultData;
|
|
}
|
|
if (synced) {
|
|
if (!Count)
|
|
resultData = data + i;
|
|
Count += l;
|
|
}
|
|
else
|
|
resultSkipped = i + l;
|
|
if (l > 0)
|
|
i += l - 1; // the loop increments, too
|
|
}
|
|
}
|
|
}
|
|
return resultData;
|
|
}
|
|
|
|
void cRemux::Del(int Count)
|
|
{
|
|
resultBuffer->Del(Count);
|
|
}
|
|
|
|
void cRemux::Clear(void)
|
|
{
|
|
if (vTS2PES) vTS2PES->Clear();
|
|
if (aTS2PES1) aTS2PES1->Clear();
|
|
if (aTS2PES2) aTS2PES2->Clear();
|
|
if (dTS2PES1) dTS2PES1->Clear();
|
|
if (dTS2PES2) dTS2PES2->Clear();
|
|
resultBuffer->Clear();
|
|
}
|
|
|
|
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
|
|
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;
|
|
return;
|
|
}
|
|
}
|
|
dsyslog("SetBrokenLink: no GOP header found in video packet");
|
|
}
|
|
else
|
|
dsyslog("SetBrokenLink: no video packet in frame");
|
|
}
|