summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HISTORY4
-rw-r--r--Makefile4
-rw-r--r--decoder.h4
-rw-r--r--osd.c195
-rw-r--r--osd.h5
-rw-r--r--osd_info.c160
-rw-r--r--osd_info.h2
-rw-r--r--osdpip.c2
-rw-r--r--patches/osdpip-0.0.10a-fedora.diff49
-rw-r--r--patches/osdpip__decoder_c.diff20
-rw-r--r--po/ca_ES.po7
-rw-r--r--po/cs_CZ.po7
-rw-r--r--po/da_DK.po7
-rw-r--r--po/de_DE.po9
-rw-r--r--po/el_GR.po7
-rw-r--r--po/es_ES.po7
-rw-r--r--po/et_EE.po7
-rw-r--r--po/fi_FI.po7
-rw-r--r--po/fr_FR.po7
-rw-r--r--po/hr_HR.po7
-rw-r--r--po/hu_HU.po7
-rwxr-xr-x[-rw-r--r--]po/it_IT.po87
-rw-r--r--po/nl_NL.po7
-rw-r--r--po/nn_NO.po7
-rw-r--r--po/pl_PL.po7
-rw-r--r--po/pt_PT.po7
-rw-r--r--po/ro_RO.po7
-rw-r--r--po/ru_RU.po7
-rw-r--r--po/sl_SI.po7
-rw-r--r--po/sv_SE.po7
-rw-r--r--po/tr_TR.po7
-rw-r--r--receiver.c9
-rw-r--r--receiver.h8
-rw-r--r--remux.c2228
-rw-r--r--remux.h85
35 files changed, 2856 insertions, 148 deletions
diff --git a/HISTORY b/HISTORY
index ad0efe3..518a5d6 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,6 +1,10 @@
VDR Plugin 'osdpip' Revision History
------------------------------------
+2010-01-01: Version 0.0.10a
+- added support for VDR 1.7.x
+- added PIP while replaying
+
2008-05-03: Version 0.0.10 (written by Andreas Regel)
- support swscale functions of recent FFMPEG versions. Have a look at
README to see how to deactivate it for older FFPMEG versions.
diff --git a/Makefile b/Makefile
index dc5bf38..8521947 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@
#
### uncomment the following line, if you have a recent FFMPEG version that
### has a changed structure of its header files.
-#WITH_NEW_FFMPEG_HEADERS=1
+WITH_NEW_FFMPEG_HEADERS=1
# The official name of this plugin.
# This name will be used in the '-P...' option of VDR to load the plugin.
@@ -60,7 +60,7 @@ DEFINES += -D_GNU_SOURCE
### The object files (add further files here):
-OBJS = $(PLUGIN).o osd_info.o osd.o receiver.o setup.o i18n.o pes.o quantize.o decoder.o
+OBJS = $(PLUGIN).o osd_info.o remux.o osd.o receiver.o setup.o i18n.o pes.o quantize.o decoder.o
ifdef FFMPEG_STATIC
DEFINES += -DHAVE_FFMPEG_STATIC
diff --git a/decoder.h b/decoder.h
index f503402..d139b58 100644
--- a/decoder.h
+++ b/decoder.h
@@ -20,9 +20,9 @@ extern "C"
#include <libswscale/swscale.h>
#endif
#else
- #include <ffmpeg/avcodec.h>
+ #include <libavcodec/avcodec.h>
#ifdef USE_SWSCALE
- #include <ffmpeg/swscale.h>
+ #include <libswscale/swscale.h>
#endif
#endif
}
diff --git a/osd.c b/osd.c
index 00cfde8..4814be6 100644
--- a/osd.c
+++ b/osd.c
@@ -13,11 +13,18 @@
#include "quantize.h"
#include "receiver.h"
#include "setup.h"
+#if VDRVERSNUM > 10703
+#include "remux.h"
+#endif
#include <vdr/ringbuffer.h>
+#if VDRVERSNUM <= 10703
#include <vdr/remux.h>
+#endif
#include <vdr/thread.h>
+#include <vdr/menu.h>
+
cMutex Mutex;
cOsdPipObject::cOsdPipObject(cDevice *Device, const cChannel *Channel)
@@ -31,7 +38,7 @@ cOsdPipObject::cOsdPipObject(cDevice *Device, const cChannel *Channel)
m_Active = false;
m_Ready = false;
m_Reset = true;
- m_PipMode = pipModeNormal;
+ m_PipMode = (cDevice::PrimaryDevice()->Replaying() && cControl::Control()) ? pipModeReplay : pipModeNormal;
m_Width = m_Height = -1;
m_Bitmap = NULL;
m_InfoWindow = NULL;
@@ -109,11 +116,12 @@ void cOsdPipObject::SwitchOsdPipChan(int i)
{
m_Channel = pipChan;
dev->SwitchChannel(m_Channel, false);
+ m_InfoWindow->SetMessage(tr("Zapping mode"));
+ m_InfoWindow->Show(true);
m_Receiver = new cOsdPipReceiver(m_Channel, m_ESBuffer);
dev->AttachReceiver(m_Receiver);
}
Start();
- m_InfoWindow->Hide();
}
}
@@ -210,6 +218,9 @@ void cOsdPipObject::ProcessImage(unsigned char * data, int length)
int infoX = 0;
int infoY = 0;
int infoH = OsdPipSetup.ShowInfo * 30;
+ if (cReplayControl::NowReplaying() && OsdPipSetup.ShowInfo >= 2) {
+ infoH = 60;
+ }
switch (OsdPipSetup.InfoPosition)
{
@@ -469,7 +480,17 @@ void cOsdPipObject::ProcessImage(unsigned char * data, int length)
if (OsdPipSetup.ShowInfo)
{
m_InfoWindow->SetChannel(Channels.GetByNumber(cDevice::ActualDevice()->CurrentChannel()));
- m_InfoWindow->Show();
+ switch (m_PipMode)
+ {
+ case pipModeZapping:
+ m_InfoWindow->SetMessage(tr("Zapping mode"));
+ break;
+ case pipModeMoving:
+ m_InfoWindow->SetMessage(tr("Move mode"));
+ break;
+ default: ;
+ }
+ m_InfoWindow->Show(true);
}
m_Ready = true;
}
@@ -481,6 +502,11 @@ void cOsdPipObject::ProcessImage(unsigned char * data, int length)
void cOsdPipObject::Action(void)
{
+ int pos, end;
+ bool Play, Fwd;
+ int Spd;
+ static int pPos;
+ static time_t tme;
m_Active = true;
isyslog("osdpip: decoder thread started (pid = %d)", getpid());
@@ -543,6 +569,27 @@ void cOsdPipObject::Action(void)
}
}
cCondWait::SleepMs(1);
+ if (cDevice::PrimaryDevice()->Replaying() && cControl::Control()) {
+ cControl::Control()->GetReplayMode(Play, Fwd, Spd);
+ cControl::Control()->GetIndex(pos, end);
+ if ((Play && Fwd && pos == pPos) || pos >= (end - 1))
+ {
+ if (tme == 0)
+ {
+ tme = time(NULL) + 3;
+ }
+ if (tme < time(NULL))
+ {
+ tme = 0;
+ StopReplay();
+ }
+ }
+ if (!Play || !Fwd || pPos != pos)
+ {
+ tme = 0;
+ pPos = pos;
+ }
+ }
}
if (OsdPipSetup.ColorDepth == kDepthColor128var ||
@@ -558,8 +605,21 @@ void cOsdPipObject::Show(void)
Start();
}
+void cOsdPipObject::StopReplay(void)
+{
+ cControl::Control()->ProcessKey(kStop);
+ cDevice::PrimaryDevice()->StopReplay();
+ if (m_InfoWindow->Shown())
+ m_InfoWindow->Hide();
+ Channels.SwitchTo(cDevice::CurrentChannel());
+}
+
eOSState cOsdPipObject::ProcessKey(eKeys Key)
{
+ static cString pPos = "";
+ int pos, end;
+ bool replay = (cReplayControl::NowReplaying() && cControl::Control());
+
eOSState state = cOsdObject::ProcessKey(Key);
if (state == osUnknown)
{
@@ -581,7 +641,7 @@ eOSState cOsdPipObject::ProcessKey(eKeys Key)
if (m_Ready && m_InfoWindow)
{
m_InfoWindow->SetMessage(tr("Move mode"));
- m_InfoWindow->Show();
+ m_InfoWindow->Show(true);
}
break;
case kYellow:
@@ -589,13 +649,26 @@ eOSState cOsdPipObject::ProcessKey(eKeys Key)
if (m_Ready && m_InfoWindow)
{
m_InfoWindow->SetMessage(tr("Normal mode"));
- m_InfoWindow->Show();
+ m_InfoWindow->Show(true);
+ }
+ break;
+ case kBlue:
+ if (replay)
+ {
+ m_PipMode = pipModeReplay;
+ if (m_Ready && m_InfoWindow)
+ {
+ m_InfoWindow->SetMessage(tr("Replay mode"));
+ m_InfoWindow->Show(true);
+ }
}
break;
case kUp:
+ case kChanUp:
SwitchOsdPipChan(1);
break;
case kDown:
+ case kChanDn:
SwitchOsdPipChan(-1);
break;
default:
@@ -616,7 +689,7 @@ eOSState cOsdPipObject::ProcessKey(eKeys Key)
if (m_Ready && m_InfoWindow)
{
m_InfoWindow->SetMessage(tr("Normal mode"));
- m_InfoWindow->Show();
+ m_InfoWindow->Show(true);
}
break;
case kYellow:
@@ -624,7 +697,18 @@ eOSState cOsdPipObject::ProcessKey(eKeys Key)
if (m_Ready && m_InfoWindow)
{
m_InfoWindow->SetMessage(tr("Zapping mode"));
- m_InfoWindow->Show();
+ m_InfoWindow->Show(true);
+ }
+ break;
+ case kBlue:
+ if (replay)
+ {
+ m_PipMode = pipModeReplay;
+ if (m_Ready && m_InfoWindow)
+ {
+ m_InfoWindow->SetMessage(tr("Replay mode"));
+ m_InfoWindow->Show(true);
+ }
}
break;
case kUp:
@@ -650,10 +734,86 @@ eOSState cOsdPipObject::ProcessKey(eKeys Key)
}
state = osContinue;
}
+ else if (m_PipMode == pipModeReplay)
+ {
+ if (replay)
+ {
+ switch (Key & ~k_Repeat)
+ {
+ case kOk:
+ case kBack:
+ state = m_InfoWindow->ProcessKey(Key);
+ break;
+ case k1...k9:
+ case kRed:
+ state = osContinue;
+ break;
+ case kBlue:
+ m_PipMode = pipModeNormal;
+ if (m_Ready && m_InfoWindow)
+ {
+ m_InfoWindow->SetMessage(tr("Normal mode"));
+ m_InfoWindow->Show(replay);
+ }
+ state = osContinue;
+ break;
+ case k0:
+ StopReplay();
+ state = osContinue;
+ break;
+ case kLeft:
+ case kRight:
+ case kYellow:
+ case kGreen:
+ default:
+ cControl::Control()->ProcessKey(Key);
+ state = osContinue;
+ }
+ if (cControl::Control())
+ {
+ cControl::Control()->GetIndex(pos, end);
+ if (pPos == "")
+ {
+ pPos = IndexToHMSF(pos);
+ }
+ if (strcmp(IndexToHMSF(pos), pPos) != 0 && m_InfoWindow)
+ {
+ pPos = IndexToHMSF(pos);
+ if (m_InfoWindow->Shown())
+ {
+ m_InfoWindow->Show(false);
+ }
+ }
+ }
+ } else if (m_Ready && m_InfoWindow)
+ {
+ state = m_InfoWindow->ProcessKey(Key);
+ }
+ }
else
{
- if (m_Ready && m_InfoWindow)
+ if (replay)
+ {
+ switch (Key)
+ {
+ case kBack:
+ case k0:
+ StopReplay();
+ state = osContinue;
+ break;
+ case k1...k9:
+ case kUp:
+ case kChanUp:
+ case kDown:
+ case kChanDn:
+ case kRed:
+ state = osContinue;
+ default: ;
+ }
+ } else if (m_Ready && m_InfoWindow)
+ {
state = m_InfoWindow->ProcessKey(Key);
+ }
}
}
if (state == osUnknown)
@@ -674,7 +834,7 @@ eOSState cOsdPipObject::ProcessKey(eKeys Key)
if (m_Ready && m_InfoWindow)
{
m_InfoWindow->SetMessage(tr("Move mode"));
- m_InfoWindow->Show();
+ m_InfoWindow->Show(true);
}
break;
case kYellow:
@@ -682,7 +842,18 @@ eOSState cOsdPipObject::ProcessKey(eKeys Key)
if (m_Ready && m_InfoWindow)
{
m_InfoWindow->SetMessage(tr("Zapping mode"));
- m_InfoWindow->Show();
+ m_InfoWindow->Show(true);
+ }
+ break;
+ case kBlue:
+ if (replay)
+ {
+ m_PipMode = pipModeReplay;
+ if (m_Ready && m_InfoWindow)
+ {
+ m_InfoWindow->SetMessage(tr("Replay mode"));
+ m_InfoWindow->Show(replay);
+ }
}
break;
case kUp:
@@ -692,7 +863,7 @@ eOSState cOsdPipObject::ProcessKey(eKeys Key)
case kOk:
if (OsdPipSetup.ShowInfo)
{
- m_InfoWindow->Show();
+ m_InfoWindow->Show(true);
}
break;
default:
@@ -716,6 +887,8 @@ void cOsdPipObject::ChannelSwitch(const cDevice * device, int channelNumber)
if (OsdPipSetup.ShowInfo)
{
m_InfoWindow->SetChannel(Channels.GetByNumber(channelNumber));
+ if (m_PipMode == pipModeZapping)
+ m_InfoWindow->SetMessage(tr("Zapping mode"));
m_InfoWindow->Show();
}
}
diff --git a/osd.h b/osd.h
index 2f62d80..d906d71 100644
--- a/osd.h
+++ b/osd.h
@@ -21,7 +21,8 @@ typedef enum _ePipMode
{
pipModeNormal,
pipModeMoving,
- pipModeZapping
+ pipModeZapping,
+ pipModeReplay
} ePipMode;
class cRingBufferFrame;
@@ -56,6 +57,7 @@ private:
void Stop(void);
void SwapChannels(void);
void SwitchOsdPipChan(int i);
+ void StopReplay();
protected:
virtual void Action(void);
virtual void ChannelSwitch(const cDevice * device, int channelNumber);
@@ -64,7 +66,6 @@ protected:
public:
cOsdPipObject(cDevice *Device, const cChannel *Channel);
virtual ~cOsdPipObject(void);
-
virtual void Show(void);
eOSState ProcessKey(eKeys k);
};
diff --git a/osd_info.c b/osd_info.c
index 7c729d8..722d3ab 100644
--- a/osd_info.c
+++ b/osd_info.c
@@ -5,6 +5,8 @@
#include <vdr/device.h>
#include <vdr/thread.h>
+#include <vdr/menu.h>
+
#define DIRECTCHANNELTIMEOUT 1
#define INFOTIMEOUT 5
@@ -44,74 +46,112 @@ void cOsdInfoWindow::SetChannel(const cChannel * channel)
m_Group = -1;
}
-void cOsdInfoWindow::Show()
+void cOsdInfoWindow::Show(bool Refresh)
{
char channel[101] = "";
char presentName[101] = "";
char presentTime[10] = "";
char followingName[101] = "";
char followingTime[10] = "";
-
+ double progress = 0.0;
+ int pos, end;
+ char *tmp = NULL;
+ static bool lastMsg;
+
if (m_Message)
{
snprintf(channel, 100, "%s", m_Message);
- m_Message = NULL;
}
- else if (m_Channel)
+ else if (!(cReplayControl::NowReplaying() && cControl::Control()))
{
- if (m_Channel->GroupSep())
- snprintf(channel, 100, "%s", m_Channel->Name());
- else
- snprintf(channel, 100, "%d%s %s", m_Channel->Number(), m_Number ? "-" : "", m_Channel->Name());
- if (m_WithInfo)
+ if (m_Channel)
{
- const cEvent * present = NULL;
- const cEvent * following = NULL;
- cSchedulesLock schedulesLock;
- const cSchedules * schedules = cSchedules::Schedules(schedulesLock);
- if (schedules)
+ if (m_Channel->GroupSep())
+ snprintf(channel, 100, "%s", m_Channel->Name());
+ else
+ snprintf(channel, 100, "%d%s %s", m_Channel->Number(), m_Number ? "-" : "", m_Channel->Name());
+ if (m_WithInfo)
{
- const cSchedule * schedule = schedules->GetSchedule(m_Channel->GetChannelID());
- if (schedule)
+ const cEvent * present = NULL;
+ const cEvent * following = NULL;
+ cSchedulesLock schedulesLock;
+ const cSchedules * schedules = cSchedules::Schedules(schedulesLock);
+ if (schedules)
{
- if ((present = schedule->GetPresentEvent()) != NULL)
+ const cSchedule * schedule = schedules->GetSchedule(m_Channel->GetChannelID());
+ if (schedule)
{
- const char * presentTitle = present->Title();
- if (!isempty(presentTitle))
+ if ((present = schedule->GetPresentEvent()) != NULL)
{
- sprintf(presentTime, "%s", (const char *) present->GetTimeString());
- sprintf(presentName, "%s", (const char *) presentTitle);
+ const char * presentTitle = present->Title();
+ if (!isempty(presentTitle))
+ {
+ sprintf(presentTime, "%s", (const char *) present->GetTimeString());
+ sprintf(presentName, "%s", (const char *) presentTitle);
+ progress = double(time(NULL) - present->StartTime()) / double(present->EndTime() - present->StartTime());
+ }
}
- }
- if ((following = schedule->GetFollowingEvent()) != NULL)
- {
- const char * followingTitle = following->Title();
- if (!isempty(followingTitle))
+ if ((following = schedule->GetFollowingEvent()) != NULL)
{
- sprintf(followingTime, "%s", (const char *) following->GetTimeString());
- sprintf(followingName, "%s", (const char *) followingTitle);
+ const char * followingTitle = following->Title();
+ if (!isempty(followingTitle))
+ {
+ sprintf(followingTime, "%s", (const char *) following->GetTimeString());
+ sprintf(followingName, "%s", (const char *) followingTitle);
+ }
}
}
}
}
}
+ else if (m_Number)
+ {
+ snprintf(channel, 100, "%d-", m_Number);
+ }
+ else
+ snprintf(channel, 100, "%s", trVDR("*** Invalid Channel ***"));
}
- else if (m_Number)
- snprintf(channel, 100, "%d-", m_Number);
else
- snprintf(channel, 100, "%s", trVDR("*** Invalid Channel ***"));
+ {
+ cControl::Control()->GetIndex(pos, end);
+ cRecording *rec = new cRecording(cReplayControl::NowReplaying());
+ asprintf(&tmp, "%s (%s/%s)", rec->Name(), (const char *)IndexToHMSF(pos), (const char *)IndexToHMSF(end));
+ while (strchr(tmp, '~'))
+ {
+ tmp = strchr(tmp, '~');
+ tmp++;
+ }
+ snprintf(channel, 100, "%s", tmp);
+ delete(rec);
+ rec = NULL;
+ }
Mutex.Lock();
if (OsdPipSetup.ColorDepth == kDepthGrey16)
{
- m_Bitmap->DrawRectangle(0, 0, m_Bitmap->Width() - 1, m_Bitmap->Height() - 1, clrBlack);
+ if (!m_Shown || Refresh || (!m_Message && lastMsg))
+ m_Bitmap->DrawRectangle(0, 0, m_Bitmap->Width() - 1, m_Bitmap->Height() - 1, clrBlack);
const cFont * font = cFont::GetFont(fontOsd);
- m_Bitmap->DrawText(0, 0, channel, clrWhite, clrBlack, font, m_Bitmap->Width(), 29);
+ m_Bitmap->DrawText((cControl::Control() && cReplayControl::NowReplaying()) || m_Message ? 0 : 20, 0, channel, clrWhite, clrBlack, font, m_Bitmap->Width(), 29);
if (m_Bitmap->Height() > 30)
{
- m_Bitmap->DrawText(0, 30, presentTime, clrWhite, clrBlack, font, 80, 29);
- m_Bitmap->DrawText(80, 30, presentName, clrWhite, clrBlack, font, m_Bitmap->Width() - 80, 29);
+ if (cReplayControl::NowReplaying() && cControl::Control() && !m_Message) {
+ m_Bitmap->DrawRectangle(0, 30, m_Bitmap->Width() - 1, m_Bitmap->Height() - 1, clrBlack);
+ cControl::Control()->GetIndex(pos, end);
+ m_Bitmap->DrawRectangle(2, 36, m_Bitmap->Width() - 4, m_Bitmap->Height() - 8, clrWhite);
+ m_Bitmap->DrawRectangle(4, 38, (m_Bitmap->Width() - 6) * double(pos) / double(end), m_Bitmap->Height() - 10, clrGreen);
+ } else
+ {
+ m_Bitmap->DrawText(20, 30, presentTime, clrWhite, clrTransparent, font, 80, 29);
+ m_Bitmap->DrawText(100, 30, presentName, clrWhite, clrTransparent, font, m_Bitmap->Width() - 80, 29);
+ if (progress != 0)
+ {
+ m_Bitmap->DrawRectangle(6, 1, 14, m_Bitmap->Height() - 1, clrWhite);
+ m_Bitmap->DrawRectangle(7, 2, 13, (m_Bitmap->Height() - 1) * progress, clrGreen);
+ m_Bitmap->DrawRectangle(7, (m_Bitmap->Height() - 1) * progress, 13, m_Bitmap->Height() - 2, clrBlack);
+ }
+ }
}
if (m_Bitmap->Height() > 2*30)
{
@@ -122,29 +162,53 @@ void cOsdInfoWindow::Show()
else
{
m_Palette[0] = 0xFF000000;
- m_Palette[255] = 0x00FFFFFF;
- m_Bitmap->DrawRectangle(0, 0, m_Bitmap->Width() - 1, m_Bitmap->Height() - 1, m_Palette[0]);
+ m_Palette[255] = 0xFFFFFFFF;
+ if (!m_Shown || Refresh || (!m_Message && lastMsg))
+ {
+ m_Bitmap->DrawRectangle(0, 0, m_Bitmap->Width() - 1, m_Bitmap->Height() - 1, m_Palette[0]);
+ }
for (int i = 0; i < 256; i++)
m_Bitmap->SetColor(i, m_Palette[i]);
- m_Osd->DrawBitmap(m_InfoX, m_InfoY, *m_Bitmap, 0, 0, true);
- m_Osd->Flush();
- m_Palette[255] = 0xFFFFFFFF;
- m_Bitmap->DrawRectangle(0, 0, m_Bitmap->Width() - 1, m_Bitmap->Height() - 1, m_Palette[0]);
+ if (!m_Shown || Refresh)
+ {
+ m_Osd->DrawBitmap(m_InfoX, m_InfoY, *m_Bitmap, 0, 0, true);
+ m_Osd->Flush();
+ m_Palette[255] = 0xFFFFFFFF;
+ m_Bitmap->DrawRectangle(0, 0, m_Bitmap->Width() - 1, m_Bitmap->Height() - 1, m_Palette[0]);
+ }
for (int i = 0; i < 256; i++)
m_Bitmap->SetColor(i, m_Palette[i]);
const cFont *font = cFont::GetFont(fontOsd);
- m_Bitmap->DrawText(0, 0, channel, m_Palette[255], m_Palette[0], font, m_Bitmap->Width(), 29);
+ m_Bitmap->DrawText((cControl::Control() && cReplayControl::NowReplaying()) || m_Message ? 0 : 20, 0, channel, m_Palette[255], m_Palette[0], font, m_Bitmap->Width(), 29);
if (m_Bitmap->Height() > 30)
{
- m_Bitmap->DrawText(0, 30, presentTime, m_Palette[255], m_Palette[0], font, 80, 29);
- m_Bitmap->DrawText(80, 30, presentName, m_Palette[255], m_Palette[0], font, m_Bitmap->Width() - 80, 29);
+ if (cReplayControl::NowReplaying() && cControl::Control() && !m_Message)
+ {
+ m_Bitmap->DrawRectangle(0, 30, m_Bitmap->Width() - 1, m_Bitmap->Height() - 1, m_Palette[0]);
+ cControl::Control()->GetIndex(pos, end);
+ m_Bitmap->DrawRectangle(2, 36, m_Bitmap->Width() - 4, m_Bitmap->Height() - 8, m_Palette[255]);
+ m_Bitmap->DrawRectangle(4, 38, (m_Bitmap->Width() - 6) * double(pos) / double(end), m_Bitmap->Height() - 10, OsdPipSetup.ColorDepth == kDepthColor128var ? m_Palette[0] : clrGreen);
+ } else
+ {
+ m_Bitmap->DrawText(20, 30, presentTime, m_Palette[255], m_Palette[0], font, 80, 29);
+ m_Bitmap->DrawText(100, 30, presentName, m_Palette[255], m_Palette[0], font, m_Bitmap->Width() - 80, 29);
+ if (progress != 0)
+ {
+ m_Bitmap->DrawRectangle(6, 1, 14, m_Bitmap->Height() - 1, OsdPipSetup.ColorDepth == kDepthColor128var ? m_Palette[255] : m_Palette[0]);
+ m_Bitmap->DrawRectangle(7, 2, 13, (m_Bitmap->Height() - 1) * progress, OsdPipSetup.ColorDepth == kDepthColor128var ? m_Palette[255] : clrGreen);
+ m_Bitmap->DrawRectangle(7, (m_Bitmap->Height() - 1) * progress, 13, m_Bitmap->Height() - 2, OsdPipSetup.ColorDepth == kDepthColor128var ? m_Palette[0] : m_Palette[255]);
+ }
+ }
}
if (m_Bitmap->Height() > 2*30)
{
- m_Bitmap->DrawText(0, 2*30, followingTime, m_Palette[255], m_Palette[0], font, 80, 29);
- m_Bitmap->DrawText(80, 2*30, followingName, m_Palette[255], m_Palette[0], font, m_Bitmap->Width() - 80, 29);
+ m_Bitmap->DrawText(20, 2*30, followingTime, m_Palette[255], m_Palette[0], font, 80, 29);
+ m_Bitmap->DrawText(100, 2*30, followingName, m_Palette[255], m_Palette[0], font, m_Bitmap->Width() - 80, 29);
}
}
+ tmp = NULL;
+ lastMsg = (m_Message != NULL);
+ m_Message = NULL;
m_Osd->DrawBitmap(m_InfoX, m_InfoY, *m_Bitmap, 0, 0, true);
m_Osd->Flush();
m_Shown = true;
@@ -197,7 +261,7 @@ eOSState cOsdInfoWindow::ProcessKey(eKeys key)
cChannel * channel = Channels.GetByNumber(m_Number);
m_Channel = channel;
m_WithInfo = false;
- Show();
+ Show(true);
// Lets see if there can be any useful further input:
int n = channel ? m_Number * 10 : 0;
while (channel && (channel = Channels.Next(channel)) != NULL)
@@ -311,7 +375,7 @@ eOSState cOsdInfoWindow::ProcessKey(eKeys key)
{
m_Group = -1;
m_Number = 0;
- m_Channel = Channels.Get(cDevice::CurrentChannel());
+ m_Channel = Channels.GetByNumber(cDevice::CurrentChannel());
m_WithInfo = true;
Hide();
}
diff --git a/osd_info.h b/osd_info.h
index 309d2d3..2f66ffe 100644
--- a/osd_info.h
+++ b/osd_info.h
@@ -35,7 +35,7 @@ public:
~cOsdInfoWindow();
void SetMessage(const char * message);
void SetChannel(const cChannel * channel);
- void Show();
+ void Show(bool Refresh = false);
void Hide();
eOSState ProcessKey(eKeys key);
diff --git a/osdpip.c b/osdpip.c
index ac5764f..2e6e89f 100644
--- a/osdpip.c
+++ b/osdpip.c
@@ -13,7 +13,7 @@
#include <vdr/plugin.h>
-static const char *VERSION = "0.0.10";
+static const char *VERSION = "0.0.10a";
static const char *DESCRIPTION = trNOOP("OSD Picture-in-Picture");
static const char *MAINMENUENTRY = trNOOP("Picture-in-Picture");
diff --git a/patches/osdpip-0.0.10a-fedora.diff b/patches/osdpip-0.0.10a-fedora.diff
new file mode 100644
index 0000000..08672fb
--- /dev/null
+++ b/patches/osdpip-0.0.10a-fedora.diff
@@ -0,0 +1,49 @@
+diff -Naur osdpip-0.0.10a/decoder.c osdpip-0.0.10a.new/decoder.c
+--- osdpip-0.0.10a/decoder.c 2010-01-01 22:24:06.000000000 +0100
++++ osdpip-0.0.10a.new/decoder.c 2010-01-02 14:11:31.914298872 +0100
+@@ -82,14 +82,14 @@
+ context = sws_getContext(m_Context->width - (OsdPipSetup.CropLeft + OsdPipSetup.CropRight),
+ m_Context->height - (OsdPipSetup.CropTop + OsdPipSetup.CropBottom),
+ PIX_FMT_YUV420P,
+- m_Width, m_Height, ConvertToRGB ? PIX_FMT_RGBA32 : PIX_FMT_YUV420P,
++ m_Width, m_Height, ConvertToRGB ? PIX_FMT_RGB32 : PIX_FMT_YUV420P,
+ SWS_LANCZOS, NULL, NULL, NULL);
+ if (!context) {
+ printf("Error initializing scale context.\n");
+ return -1;
+ }
+ avpicture_fill((AVPicture *) m_PicResample, m_BufferResample,
+- ConvertToRGB ? PIX_FMT_RGBA32 : PIX_FMT_YUV420P,
++ ConvertToRGB ? PIX_FMT_RGB32 : PIX_FMT_YUV420P,
+ m_Width, m_Height);
+ sws_scale(context, pic_crop.data, pic_crop.linesize,
+ 0, m_Context->height - (OsdPipSetup.CropTop + OsdPipSetup.CropBottom),
+diff -Naur osdpip-0.0.10a/Makefile osdpip-0.0.10a.new/Makefile
+--- osdpip-0.0.10a/Makefile 2010-01-01 22:24:06.000000000 +0100
++++ osdpip-0.0.10a.new/Makefile 2010-01-02 14:12:36.561299250 +0100
+@@ -28,12 +28,12 @@
+ ### The C++ compiler and options:
+
+ CXX ?= g++
+-CXXFLAGS ?= -g -O2 -Wall -Woverloaded-virtual
++CXXFLAGS ?= -g -O2 -Wall -Woverloaded-virtual -fPIC
+
+ ### The directory environment:
+
+ DVBDIR = ../../../../DVB
+-FFMDIR = ../../../../ffmpeg-0.4.8
++FFMDIR = /usr/include/ffmpeg
+ VDRDIR = ../../..
+ LIBDIR = ../../lib
+ TMPDIR = /tmp
+@@ -53,8 +53,8 @@
+
+ ### Includes and Defines (add further entries here):
+
+-INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include -I. -I$(FFMDIR)/libavcodec
+-LIBS = -L$(FFMDIR)/libavcodec -lavcodec
++INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include -I. -I$(FFMDIR)
++LIBS = -L$(FFMDIR) -lavcodec
+ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+ DEFINES += -D_GNU_SOURCE
+
diff --git a/patches/osdpip__decoder_c.diff b/patches/osdpip__decoder_c.diff
new file mode 100644
index 0000000..f116978
--- /dev/null
+++ b/patches/osdpip__decoder_c.diff
@@ -0,0 +1,20 @@
+diff -Nur /usr/local/src/vdr-1.7.10/PLUGINS/src/osdpip-0.0.10a/decoder.c.orig /usr/local/src/vdr-1.7.10/PLUGINS/src/osdpip-0.0.10a/decoder.c
+--- /usr/local/src/vdr-1.7.10/PLUGINS/src/osdpip-0.0.10a/decoder.c.orig 2010-01-02 11:36:19.000000000 +0100
++++ /usr/local/src/vdr-1.7.10/PLUGINS/src/osdpip-0.0.10a/decoder.c 2010-01-02 11:37:12.000000000 +0100
+@@ -82,14 +82,14 @@
+ context = sws_getContext(m_Context->width - (OsdPipSetup.CropLeft + OsdPipSetup.CropRight),
+ m_Context->height - (OsdPipSetup.CropTop + OsdPipSetup.CropBottom),
+ PIX_FMT_YUV420P,
+- m_Width, m_Height, ConvertToRGB ? PIX_FMT_RGBA32 : PIX_FMT_YUV420P,
++ m_Width, m_Height, ConvertToRGB ? PIX_FMT_RGB32 : PIX_FMT_YUV420P,
+ SWS_LANCZOS, NULL, NULL, NULL);
+ if (!context) {
+ printf("Error initializing scale context.\n");
+ return -1;
+ }
+ avpicture_fill((AVPicture *) m_PicResample, m_BufferResample,
+- ConvertToRGB ? PIX_FMT_RGBA32 : PIX_FMT_YUV420P,
++ ConvertToRGB ? PIX_FMT_RGB32 : PIX_FMT_YUV420P,
+ m_Width, m_Height);
+ sws_scale(context, pic_crop.data, pic_crop.linesize,
+ 0, m_Context->height - (OsdPipSetup.CropTop + OsdPipSetup.CropBottom),
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 1bdbf46..8316ad2 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Jordi Vilà <jvila@tinet.org>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -17,13 +17,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/cs_CZ.po b/po/cs_CZ.po
index b2c3f40..f7f8525 100644
--- a/po/cs_CZ.po
+++ b/po/cs_CZ.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Vladimír Bárta <vladimir.barta@k2atmitec.cz>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -15,13 +15,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-2\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/da_DK.po b/po/da_DK.po
index 79fb6df..b468406 100644
--- a/po/da_DK.po
+++ b/po/da_DK.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Mogens Elneff <mogens@elneff.dk>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -15,13 +15,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/de_DE.po b/po/de_DE.po
index 1a9e1d3..a030355 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Klaus Schmidinger <kls@cadsoft.de>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -15,14 +15,17 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr "Umschaltmodus"
+
msgid "Move mode"
msgstr "Bewegungsmodus"
msgid "Normal mode"
msgstr "Normaler Modus"
-msgid "Zapping mode"
-msgstr "Umschaltmodus"
+msgid "Replay mode"
+msgstr "Wiedergabemodus"
msgid "OSD Picture-in-Picture"
msgstr "OSD Bild-in-Bild"
diff --git a/po/el_GR.po b/po/el_GR.po
index 5205e12..72ffa64 100644
--- a/po/el_GR.po
+++ b/po/el_GR.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -15,13 +15,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-7\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/es_ES.po b/po/es_ES.po
index 4a13bc0..f797abd 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Ruben Nunez Francisco <ruben.nunez@tang-it.com>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -15,13 +15,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/et_EE.po b/po/et_EE.po
index 96d92ce..ad07902 100644
--- a/po/et_EE.po
+++ b/po/et_EE.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Arthur Konovalov <kasjas@hot.ee>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -15,13 +15,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-13\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/fi_FI.po b/po/fi_FI.po
index 1cce199..45d39c7 100644
--- a/po/fi_FI.po
+++ b/po/fi_FI.po
@@ -10,7 +10,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -18,13 +18,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr "Siirtotoiminto"
msgid "Normal mode"
msgstr "Normaalitoiminto"
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 638825e..0a44c48 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -10,7 +10,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Nicolas Huillard <nhuillard@e-dition.fr>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -18,13 +18,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr "Mode mouvement"
msgid "Normal mode"
msgstr "Mode normale"
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/hr_HR.po b/po/hr_HR.po
index a8de102..6ed0cfc 100644
--- a/po/hr_HR.po
+++ b/po/hr_HR.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Drazen Dupor <drazen.dupor@dupor.com>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -16,13 +16,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-2\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/hu_HU.po b/po/hu_HU.po
index d250094..04fca2a 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Istvan Koenigsberger <istvnko@hotmail.com>, Guido Josten <guido.josten@t-online.de>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -16,13 +16,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-2\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/it_IT.po b/po/it_IT.po
index 9c8182b..d6ed295 100644..100755
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -10,114 +10,121 @@ msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
"POT-Creation-Date: 2008-05-03 21:57+0200\n"
-"PO-Revision-Date: 2008-04-27 18:00+0200\n"
-"Last-Translator: Sean Carlos <seanc@libero.it>\n"
-"Language-Team: <vdr@linuxtv.org>\n"
+"PO-Revision-Date: 2010-01-13 00:15+0100\n"
+"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Italian\n"
+"X-Poedit-Country: ITALY\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Zapping mode"
+msgstr "Modalità zapping"
msgid "Move mode"
-msgstr ""
+msgstr "Modalità spostamento"
msgid "Normal mode"
-msgstr ""
+msgstr "Modalità normale"
-msgid "Zapping mode"
-msgstr ""
+msgid "Replay mode"
+msgstr "Modalità riproduzione"
msgid "OSD Picture-in-Picture"
-msgstr ""
+msgstr "Picture-in-Picture con OSD"
msgid "Picture-in-Picture"
-msgstr ""
+msgstr "Picture-in-Picture"
msgid "Greyscale (16)"
-msgstr ""
+msgstr "Liv. grigio (16)"
msgid "Greyscale (256)"
-msgstr ""
+msgstr "Liv. grigio (256)"
msgid "Color (256, fixed)"
-msgstr ""
+msgstr "Colore (256, fisso)"
msgid "Color (128, variable)"
-msgstr ""
+msgstr "Colore (128, variabile)"
msgid "Color (256, dithered)"
-msgstr ""
+msgstr "Colore (256, distorto)"
msgid "channel only"
-msgstr ""
+msgstr "solo canale"
msgid "simple"
-msgstr ""
+msgstr "semplice"
msgid "complete"
-msgstr ""
+msgstr "completa"
msgid "top left"
-msgstr ""
+msgstr "in alto a sinistra"
msgid "top right"
-msgstr ""
+msgstr "in alto a destra"
msgid "bottom left"
-msgstr ""
+msgstr "in basso a sinistra"
msgid "bottom right"
-msgstr ""
+msgstr "in basso a destra"
msgid "automatic"
-msgstr ""
+msgstr "automatico"
msgid "none"
-msgstr ""
+msgstr "nessuno"
msgid "1 frame"
-msgstr ""
+msgstr "1 frame"
msgid "2 frames"
-msgstr ""
+msgstr "2 frame"
msgid "X Position"
-msgstr ""
+msgstr "Posizione X"
msgid "Y Position"
-msgstr ""
+msgstr "Posizione Y"
msgid "Crop left"
-msgstr ""
+msgstr "Ritaglio sinistro"
msgid "Crop right"
-msgstr ""
+msgstr "Ritaglio destro"
msgid "Crop at top"
-msgstr ""
+msgstr "Ritaglio superiore"
msgid "Crop at bottom"
-msgstr ""
+msgstr "Ritaglio inferiore"
msgid "Color depth"
-msgstr ""
+msgstr "Profondità  colore"
msgid "Size"
-msgstr ""
+msgstr "Dimensione"
msgid "Frames to display"
-msgstr ""
+msgstr "Frame da visualizzare"
msgid "Drop frames"
-msgstr ""
+msgstr "Elimina frame"
msgid "Swap FFMPEG output"
-msgstr ""
+msgstr "Cambia visualizz. FFMPEG"
msgid "Show info window"
-msgstr ""
+msgstr "Mostra info finestre"
msgid "Info window width"
-msgstr ""
+msgstr "Info larghezza finestra"
msgid "Info window position"
-msgstr ""
+msgstr "Info posizione finestra"
+
diff --git a/po/nl_NL.po b/po/nl_NL.po
index 3fef0f8..45b9aaa 100644
--- a/po/nl_NL.po
+++ b/po/nl_NL.po
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Maarten Wisse <Maarten.Wisse@urz.uni-hd.de>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -17,13 +17,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-15\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/nn_NO.po b/po/nn_NO.po
index 60f33d5..3f69191 100644
--- a/po/nn_NO.po
+++ b/po/nn_NO.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Truls Slevigen <truls@slevigen.no>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -16,13 +16,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/pl_PL.po b/po/pl_PL.po
index 61d7d1b..164e98d 100644
--- a/po/pl_PL.po
+++ b/po/pl_PL.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Michael Rakowski <mrak@gmx.de>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -15,13 +15,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-2\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 92522c7..a7e903a 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Paulo Lopes <pmml@netvita.pt>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -15,13 +15,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/ro_RO.po b/po/ro_RO.po
index f74dd22..a9a0fa6 100644
--- a/po/ro_RO.po
+++ b/po/ro_RO.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -16,13 +16,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-2\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/ru_RU.po b/po/ru_RU.po
index 8723fd1..15cc597 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Vyacheslav Dikonov <sdiconov@mail.ru>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -15,13 +15,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-5\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/sl_SI.po b/po/sl_SI.po
index 88d3894..35d6969 100644
--- a/po/sl_SI.po
+++ b/po/sl_SI.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -16,13 +16,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-2\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/sv_SE.po b/po/sv_SE.po
index 39b311f..5a4a5d5 100644
--- a/po/sv_SE.po
+++ b/po/sv_SE.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Tomas Prybil <tomas@prybil.se>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -16,13 +16,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/po/tr_TR.po b/po/tr_TR.po
index 505e145..56f6cc3 100644
--- a/po/tr_TR.po
+++ b/po/tr_TR.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: VDR 1.5.7\n"
"Report-Msgid-Bugs-To: <andreas.regel@powarman.de>\n"
-"POT-Creation-Date: 2008-05-03 21:57+0200\n"
+"POT-Creation-Date: 2009-01-02 19:39+0100\n"
"PO-Revision-Date: 2008-04-27 18:00+0200\n"
"Last-Translator: Oktay Yolgeçen <oktay_73@yahoo.de>\n"
"Language-Team: <vdr@linuxtv.org>\n"
@@ -15,13 +15,16 @@ msgstr ""
"Content-Type: text/plain; charset=ISO-8859-9\n"
"Content-Transfer-Encoding: 8bit\n"
+msgid "Zapping mode"
+msgstr ""
+
msgid "Move mode"
msgstr ""
msgid "Normal mode"
msgstr ""
-msgid "Zapping mode"
+msgid "Replay mode"
msgstr ""
msgid "OSD Picture-in-Picture"
diff --git a/receiver.c b/receiver.c
index 565f13b..a0c49f2 100644
--- a/receiver.c
+++ b/receiver.c
@@ -7,9 +7,14 @@
#include "receiver.h"
#include "pes.h"
#include "setup.h"
+#if VDRVERSNUM > 10703
+#include "remux.h"
+#endif
#include <vdr/channels.h>
+#if VDRVERSNUM <= 10703
#include <vdr/remux.h>
+#endif
#include <vdr/ringbuffer.h>
cOsdPipReceiver::cOsdPipReceiver(const cChannel *Channel,
@@ -24,7 +29,11 @@ cOsdPipReceiver::cOsdPipReceiver(const cChannel *Channel,
m_TSBuffer = new cRingBufferLinear(MEGABYTE(3), TS_SIZE * 2, true);
m_TSBuffer->SetTimeouts(0, 100);
m_ESBuffer = ESBuffer;
+#if VDRVERSNUM > 10703
+ m_Remux = new cRemuxPIP(Channel->Vpid(), NULL, NULL, NULL, true);
+#else
m_Remux = new cRemux(Channel->Vpid(), NULL, NULL, NULL, true);
+#endif
m_Active = false;
}
diff --git a/receiver.h b/receiver.h
index 13c3ddf..a0855aa 100644
--- a/receiver.h
+++ b/receiver.h
@@ -12,13 +12,21 @@
class cRingBufferLinear;
class cRingBufferFrame;
+#if VDRVERSNUM > 10703
+class cRemuxPIP;
+#else
class cRemux;
+#endif
class cOsdPipReceiver: public cReceiver, public cThread {
private:
cRingBufferLinear *m_TSBuffer;
cRingBufferFrame *m_ESBuffer;
+#if VDRVERSNUM > 10703
+ cRemuxPIP *m_Remux;
+#else
cRemux *m_Remux;
+#endif
bool m_Active;
diff --git a/remux.c b/remux.c
new file mode 100644
index 0000000..5375686
--- /dev/null
+++ b/remux.c
@@ -0,0 +1,2228 @@
+/*
+ * 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.
+ *
+ * The cRepacker family's 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.64 2007/11/25 13:56:03 kls Exp $
+ */
+
+#include "remux.h"
+#include <stdlib.h>
+#include <vdr/channels.h>
+#include <vdr/shutdown.h>
+#include <vdr/tools.h>
+
+ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
+{
+ if (Count < 7)
+ return phNeedMoreData; // too short
+
+ if ((Data[6] & 0xC0) == 0x80) { // MPEG 2
+ if (Count < 9)
+ return phNeedMoreData; // too short
+
+ PesPayloadOffset = 6 + 3 + Data[8];
+ if (Count < PesPayloadOffset)
+ return phNeedMoreData; // too short
+
+ if (ContinuationHeader)
+ *ContinuationHeader = ((Data[6] == 0x80) && !Data[7] && !Data[8]);
+
+ return phMPEG2; // MPEG 2
+ }
+
+ // check for MPEG 1 ...
+ PesPayloadOffset = 6;
+
+ // skip up to 16 stuffing bytes
+ for (int i = 0; i < 16; i++) {
+ if (Data[PesPayloadOffset] != 0xFF)
+ break;
+
+ if (Count <= ++PesPayloadOffset)
+ return phNeedMoreData; // too short
+ }
+
+ // skip STD_buffer_scale/size
+ if ((Data[PesPayloadOffset] & 0xC0) == 0x40) {
+ PesPayloadOffset += 2;
+
+ if (Count <= PesPayloadOffset)
+ return phNeedMoreData; // too short
+ }
+
+ if (ContinuationHeader)
+ *ContinuationHeader = false;
+
+ if ((Data[PesPayloadOffset] & 0xF0) == 0x20) {
+ // skip PTS only
+ PesPayloadOffset += 5;
+ }
+ else if ((Data[PesPayloadOffset] & 0xF0) == 0x30) {
+ // skip PTS and DTS
+ PesPayloadOffset += 10;
+ }
+ else if (Data[PesPayloadOffset] == 0x0F) {
+ // continuation header
+ PesPayloadOffset++;
+
+ if (ContinuationHeader)
+ *ContinuationHeader = true;
+ }
+ else
+ return phInvalid; // unknown
+
+ if (Count < PesPayloadOffset)
+ return phNeedMoreData; // too short
+
+ return phMPEG1; // MPEG 1
+}
+
+// --- cRepacker -------------------------------------------------------------
+
+#define MIN_LOG_INTERVAL 10 // min. # of seconds between two consecutive log messages of a cRepacker
+#define LOG(a...) (LogAllowed() && (esyslog(a), true))
+
+class cRepacker {
+protected:
+ bool initiallySyncing;
+ int maxPacketSize;
+ uint8_t subStreamId;
+ time_t lastLog;
+ int suppressedLogMessages;
+ bool LogAllowed(void);
+ void DroppedData(const char *Reason, int Count) { LOG("%s (dropped %d bytes)", Reason, Count); }
+public:
+ static int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
+ cRepacker(void);
+ virtual ~cRepacker() {}
+ virtual void Reset(void) { initiallySyncing = true; }
+ 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; }
+ };
+
+cRepacker::cRepacker(void)
+{
+ initiallySyncing = true;
+ maxPacketSize = 6 + 65535;
+ subStreamId = 0;
+ suppressedLogMessages = 0;;
+ lastLog = 0;
+}
+
+bool cRepacker::LogAllowed(void)
+{
+ bool Allowed = time(NULL) - lastLog >= MIN_LOG_INTERVAL;
+ lastLog = time(NULL);
+ if (Allowed) {
+ if (suppressedLogMessages) {
+ esyslog("%d cRepacker messages suppressed", suppressedLogMessages);
+ suppressedLogMessages = 0;
+ }
+ }
+ else
+ suppressedLogMessages++;
+ return Allowed;
+}
+
+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];
+ int fragmentLen;
+ uchar pesHeader[6 + 3 + 255 + 3];
+ int pesHeaderLen;
+ uchar pesHeaderBackup[6 + 3 + 255];
+ int pesHeaderBackupLen;
+ uint32_t scanner;
+ uint32_t localScanner;
+ int localStart;
+ bool PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int QuerySnoopSize() { return 4; }
+ virtual void Reset(void);
+ };
+
+void cCommonRepacker::Reset(void)
+{
+ cRepacker::Reset();
+ skippedBytes = 0;
+ packetTodo = 0;
+ fragmentLen = 0;
+ pesHeaderLen = 0;
+ pesHeaderBackupLen = 0;
+ localStart = -1;
+}
+
+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
+ // determine PES packet payload
+ 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)
+ LOG("cCommonRepacker: invalid PES packet encountered in fragment buffer!");
+ else if (6 + PacketLen <= PesPayloadOffset) {
+ fragmentLen = 0;
+ 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, 6 + PacketLen);
+ fragmentLen = 0;
+ if (n != Bite)
+ return false;
+ }
+ else if (pesHeaderLen > 0) { // ... which is contained in the PES header buffer
+ 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)
+ LOG("cCommonRepacker: invalid PES packet encountered in header buffer!");
+ else if (6 + PacketLen <= PesPayloadOffset) {
+ pesHeaderLen = 0;
+ 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, 6 + PacketLen);
+ pesHeaderLen = 0;
+ if (n != Bite)
+ return false;
+ }
+ // append further payload
+ if (Count > 0) {
+ // amount of data to put into result buffer
+ int Bite = Count;
+ // put data into result buffer
+ int n = Put(ResultBuffer, Data, Bite, Bite);
+ if (n != Bite)
+ return false;
+ }
+ // we did it ;-)
+ return true;
+}
+
+// --- cVideoRepacker --------------------------------------------------------
+
+class cVideoRepacker : public cCommonRepacker {
+private:
+ enum eState {
+ syncing,
+ findPicture,
+ scanPicture
+ };
+ int state;
+ void HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel);
+ inline bool ScanDataForStartCodeSlow(const uchar *const Data);
+ inline bool ScanDataForStartCodeFast(const uchar *&Data, const uchar *Limit);
+ inline bool ScanDataForStartCode(const uchar *&Data, int &Done, int &Todo);
+ inline void AdjustCounters(const int Delta, int &Done, int &Todo);
+ inline bool ScanForEndOfPictureSlow(const uchar *&Data);
+ inline bool ScanForEndOfPictureFast(const uchar *&Data, const uchar *Limit);
+ inline bool ScanForEndOfPicture(const uchar *&Data, const uchar *Limit);
+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::HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel)
+{
+ // synchronisation is detected some bytes after frame start.
+ const int SkippedBytesLimit = 4;
+
+ // which kind of start code have we got?
+ switch (*Data) {
+ case 0xB9 ... 0xFF: // system start codes
+ LOG("cVideoRepacker: found system start code: stream seems to be scrambled or not demultiplexed");
+ break;
+ case 0xB0 ... 0xB1: // reserved start codes
+ case 0xB6:
+ LOG("cVideoRepacker: found reserved start code: stream seems to be scrambled");
+ break;
+ case 0xB4: // sequence error code
+ LOG("cVideoRepacker: found sequence error code: stream seems to be damaged");
+ case 0xB2: // user data start code
+ case 0xB5: // extension start code
+ break;
+ case 0xB7: // sequence end code
+ case 0xB3: // sequence header code
+ case 0xB8: // group start code
+ case 0x00: // picture start code
+ if (state == scanPicture) {
+ // the above start codes indicate that the current picture is done. So
+ // push out the packet to start a new packet for the next picuture. If
+ // the byte count get's negative then the current buffer ends in a
+ // partitial start code that must be stripped off, as it shall be put
+ // in the next packet.
+ PushOutPacket(ResultBuffer, Payload, Data - 3 - Payload);
+ // go on with syncing to the next picture
+ state = syncing;
+ }
+ if (state == syncing) {
+ if (initiallySyncing) // omit report for the typical initial case
+ initiallySyncing = false;
+ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
+ LOG("cVideoRepacker: skipped %d bytes to sync on next picture", 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 video, if a PTS is present in a PES packet header
+ // it shall refer to the access unit containing the first picture start
+ // code that commences in this PES packet. A picture start code commences
+ // in PES packet if the first byte of the picture start code 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++] = StreamID; // video 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 start code
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x01;
+ // the next packet's payload will begin with the fourth byte of
+ // the start code (= the actual code)
+ Payload = Data;
+ // as there is no length information available, assume the
+ // maximum we can hold in one PES packet
+ packetTodo = maxPacketSize - pesHeaderLen;
+ // go on with finding the picture data
+ state++;
+ }
+ break;
+ case 0x01 ... 0xAF: // slice start codes
+ if (state == findPicture) {
+ // go on with scanning the picture data
+ state++;
+ }
+ break;
+ }
+}
+
+bool cVideoRepacker::ScanDataForStartCodeSlow(const uchar *const Data)
+{
+ scanner <<= 8;
+ bool FoundStartCode = (scanner == 0x00000100);
+ scanner |= *Data;
+ return FoundStartCode;
+}
+
+bool cVideoRepacker::ScanDataForStartCodeFast(const uchar *&Data, const uchar *Limit)
+{
+ Limit--;
+
+ while (Data < Limit && (Data = (const uchar *)memchr(Data, 0x01, Limit - Data))) {
+ if (Data[-2] || Data[-1])
+ Data += 3;
+ else {
+ scanner = 0x00000100 | *++Data;
+ return true;
+ }
+ }
+
+ Data = Limit;
+ uint32_t *Scanner = (uint32_t *)(Data - 3);
+ scanner = ntohl(*Scanner);
+ return false;
+}
+
+bool cVideoRepacker::ScanDataForStartCode(const uchar *&Data, int &Done, int &Todo)
+{
+ const uchar *const DataOrig = Data;
+ const int MinDataSize = 4;
+
+ if (Todo < MinDataSize || (state != syncing && packetTodo < MinDataSize))
+ return ScanDataForStartCodeSlow(Data);
+
+ int Limit = Todo;
+ if (state != syncing && Limit > packetTodo)
+ Limit = packetTodo;
+
+ if (ScanDataForStartCodeSlow(Data))
+ return true;
+
+ if (ScanDataForStartCodeSlow(++Data)) {
+ AdjustCounters(1, Done, Todo);
+ return true;
+ }
+ ++Data;
+
+ bool FoundStartCode = ScanDataForStartCodeFast(Data, DataOrig + Limit);
+ AdjustCounters(Data - DataOrig, Done, Todo);
+ return FoundStartCode;
+}
+
+void cVideoRepacker::AdjustCounters(const int Delta, int &Done, int &Todo)
+{
+ Done += Delta;
+ Todo -= Delta;
+
+ if (state <= syncing)
+ skippedBytes += Delta;
+ else
+ packetTodo -= Delta;
+}
+
+void cVideoRepacker::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("cVideoRepacker: 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++;
+ // did we reach a start code?
+ if (ScanDataForStartCode(data, done, todo))
+ HandleStartCode(data, ResultBuffer, payload, Data[3], mpegLevel);
+ // move on
+ data++;
+ done++;
+ todo--;
+ // do we have to start a new packet as there is no more space left?
+ if (state != syncing && --packetTodo <= 0) {
+ // we connot start a new packet here if the current might end in a start
+ // code and this start code shall possibly be put in the next packet. So
+ // overfill the current packet until we can safely detect that we won't
+ // break a start code into pieces:
+ //
+ // A) the last four bytes were a start code.
+ // B) the current byte introduces a start code.
+ // C) the last three bytes begin a start code.
+ //
+ // Todo : Data : Rule : Result
+ // -----:-------------------------------:------:-------
+ // : XX 00 00 00 01 YY|YY YY YY YY : :
+ // 0 : ^^| : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX 00 00 00 01|YY YY YY YY : :
+ // 0 : ^^| : B : wait
+ // -1 : |^^ : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX 00 00 00|01 YY YY YY : :
+ // 0 : ^^| : C : wait
+ // -1 : |^^ : B : wait
+ // -2 : | ^^ : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX XX 00 00|00 01 YY YY : :
+ // 0 : ^^| : C : wait
+ // -1 : |^^ : C : wait
+ // -2 : | ^^ : B : wait
+ // -3 : | ^^ : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX XX XX 00|00 00 01 YY : :
+ // 0 : ^^| : C : wait
+ // -1 : |^^ : C : wait
+ // -2 : | ^^ : : push
+ // -----:-------------------------------:------:-------
+ bool A = ((scanner & 0xFFFFFF00) == 0x00000100);
+ bool B = ((scanner & 0xFFFFFF) == 0x000001);
+ bool C = ((scanner & 0xFF) == 0x00) && (packetTodo >= -1);
+ if (A || (!B && !C)) {
+ // 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 get's 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.
+ PushOutPacket(ResultBuffer, payload, bite);
+ // create a continuation PES header
+ pesHeaderLen = 0;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x01;
+ pesHeader[pesHeaderLen++] = Data[3]; // video 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 a start code 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) {
+ if (!initiallySyncing) // omit report for the typical initial case
+ LOG("cVideoRepacker: skipped %d bytes while syncing on next picture", skippedBytes - SkippedBytesLimit);
+ skippedBytes = SkippedBytesLimit;
+ }
+}
+
+bool cVideoRepacker::ScanForEndOfPictureSlow(const uchar *&Data)
+{
+ localScanner <<= 8;
+ localScanner |= *Data++;
+ // check start codes which follow picture data
+ switch (localScanner) {
+ case 0x00000100: // picture start code
+ case 0x000001B8: // group start code
+ case 0x000001B3: // sequence header code
+ case 0x000001B7: // sequence end code
+ return true;
+ }
+ return false;
+}
+
+bool cVideoRepacker::ScanForEndOfPictureFast(const uchar *&Data, const uchar *Limit)
+{
+ Limit--;
+
+ while (Data < Limit && (Data = (const uchar *)memchr(Data, 0x01, Limit - Data))) {
+ if (Data[-2] || Data[-1])
+ Data += 3;
+ else {
+ localScanner = 0x00000100 | *++Data;
+ // check start codes which follow picture data
+ switch (localScanner) {
+ case 0x00000100: // picture start code
+ case 0x000001B8: // group start code
+ case 0x000001B3: // sequence header code
+ case 0x000001B7: // sequence end code
+ Data++;
+ return true;
+ default:
+ Data += 3;
+ }
+ }
+ }
+
+ Data = Limit + 1;
+ uint32_t *LocalScanner = (uint32_t *)(Data - 4);
+ localScanner = ntohl(*LocalScanner);
+ return false;
+}
+
+bool cVideoRepacker::ScanForEndOfPicture(const uchar *&Data, const uchar *Limit)
+{
+ const uchar *const DataOrig = Data;
+ const int MinDataSize = 4;
+ bool FoundEndOfPicture;
+
+ if (Limit - Data <= MinDataSize) {
+ FoundEndOfPicture = false;
+ while (Data < Limit) {
+ if (ScanForEndOfPictureSlow(Data)) {
+ FoundEndOfPicture = true;
+ break;
+ }
+ }
+ }
+ else {
+ FoundEndOfPicture = true;
+ if (!ScanForEndOfPictureSlow(Data)) {
+ if (!ScanForEndOfPictureSlow(Data)) {
+ if (!ScanForEndOfPictureFast(Data, Limit))
+ FoundEndOfPicture = false;
+ }
+ }
+ }
+
+ localStart += (Data - DataOrig);
+ return FoundEndOfPicture;
+}
+
+int cVideoRepacker::BreakAt(const uchar *Data, int Count)
+{
+ if (initiallySyncing)
+ return -1; // fill the packet buffer completely until we have synced once
+
+ int PesPayloadOffset = 0;
+
+ if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= phInvalid)
+ return -1; // not enough data for test
+
+ // just detect end of picture
+ if (state == scanPicture) {
+ // 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
+ if (ScanForEndOfPicture(data, limit))
+ return data - Data;
+ }
+ // just fill up packet and append next start code
+ return PesPayloadOffset + packetTodo + 4;
+}
+
+// --- cAudioRepacker --------------------------------------------------------
+
+class cAudioRepacker : public cCommonRepacker {
+private:
+ static int bitRates[2][3][16];
+ enum eState {
+ syncing,
+ scanFrame
+ };
+ int state;
+ int frameTodo;
+ int frameSize;
+ int cid;
+ static bool IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize = 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);
+ };
+
+int cAudioRepacker::bitRates[2][3][16] = { // all values are specified as kbits/s
+ {
+ { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, // MPEG 1, Layer I
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, // MPEG 1, Layer II
+ { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 } // MPEG 1, Layer III
+ },
+ {
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, // MPEG 2, Layer I
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // MPEG 2, Layer II/III
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } // MPEG 2, Layer II/III
+ }
+ };
+
+cAudioRepacker::cAudioRepacker(int Cid)
+{
+ cid = Cid;
+ Reset();
+}
+
+void cAudioRepacker::Reset(void)
+{
+ cCommonRepacker::Reset();
+ scanner = 0;
+ state = syncing;
+ frameTodo = 0;
+ frameSize = 0;
+}
+
+bool cAudioRepacker::IsValidAudioHeader(uint32_t Header, bool Mpeg2, 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 && !Mpeg2) // reserved in MPEG 1
+ 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[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
+
+ *FrameSize = (N + padding_bit) * slotSize; // 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++;
+ // did we reach an audio frame header?
+ scanner <<= 8;
+ scanner |= *data;
+ if ((scanner & 0xFFF00000) == 0xFFF00000) {
+ if (frameTodo <= 0 && (frameSize == 0 || skippedBytes >= 4) && IsValidAudioHeader(scanner, mpegLevel == phMPEG2, &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.
+ PushOutPacket(ResultBuffer, payload, data - 3 - payload);
+ // go on with syncing to the next audio frame
+ state = syncing;
+ }
+ if (state == syncing) {
+ if (initiallySyncing) // omit report for the typical initial case
+ initiallySyncing = false;
+ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
+ LOG("cAudioRepacker(0x%02X): skipped %d bytes to sync on next audio frame", cid, 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;
+ // maximum we can hold in one PES packet
+ packetTodo = maxPacketSize - pesHeaderLen;
+ // expected remainder of audio frame: so far we have read 3 bytes from the frame header
+ frameTodo = frameSize - 3;
+ // go on with collecting the frame's data
+ state++;
+ }
+ }
+ }
+ data++;
+ done++;
+ todo--;
+ // do we have to start a new packet as the current is done?
+ if (frameTodo > 0) {
+ if (--frameTodo == 0) {
+ // the current audio frame is is done now. So push out the packet to
+ // start a new packet for the next audio frame.
+ PushOutPacket(ResultBuffer, payload, data - payload);
+ // go on with syncing to the next audio frame
+ state = syncing;
+ }
+ }
+ // do we have to start a new packet as there is no more space left?
+ if (state != syncing && --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.
+ PushOutPacket(ResultBuffer, payload, bite);
+ // 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) {
+ if (!initiallySyncing) // omit report for the typical initial case
+ LOG("cAudioRepacker(0x%02X): skipped %d bytes while syncing on next audio frame", cid, skippedBytes - SkippedBytesLimit);
+ skippedBytes = SkippedBytesLimit;
+ }
+}
+
+int cAudioRepacker::BreakAt(const uchar *Data, int Count)
+{
+ if (initiallySyncing)
+ return -1; // fill the packet buffer completely until we have synced once
+
+ int PesPayloadOffset = 0;
+
+ ePesHeader MpegLevel = AnalyzePesHeader(Data, Count, PesPayloadOffset);
+ if (MpegLevel <= 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, MpegLevel == phMPEG2))
+ return data - Data;
+ }
+ }
+ // just fill up packet and append next audio frame header
+ return packetRemainder;
+}
+
+// --- cDolbyRepacker --------------------------------------------------------
+
+class cDolbyRepacker : public cRepacker {
+private:
+ static int frameSizes[];
+ uchar fragmentData[6 + 65535];
+ int fragmentLen;
+ int fragmentTodo;
+ uchar pesHeader[6 + 3 + 255 + 4 + 4];
+ int pesHeaderLen;
+ uchar pesHeaderBackup[6 + 3 + 255];
+ int pesHeaderBackupLen;
+ uchar chk1;
+ uchar chk2;
+ int ac3todo;
+ enum eState {
+ find_0b,
+ find_77,
+ store_chk1,
+ store_chk2,
+ get_length,
+ output_packet
+ };
+ int state;
+ int skippedBytes;
+ void ResetPesHeader(bool ContinuationFrame = false);
+ void AppendSubStreamID(bool ContinuationFrame = false);
+ bool FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite);
+ bool StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite);
+public:
+ cDolbyRepacker(void);
+ virtual void Reset(void);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int BreakAt(const uchar *Data, int Count);
+ };
+
+// frameSizes are in words, i. e. multiply them by 2 to get bytes
+int cDolbyRepacker::frameSizes[] = {
+ // fs = 48 kHz
+ 64, 64, 80, 80, 96, 96, 112, 112, 128, 128, 160, 160, 192, 192, 224, 224,
+ 256, 256, 320, 320, 384, 384, 448, 448, 512, 512, 640, 640, 768, 768, 896, 896,
+ 1024, 1024, 1152, 1152, 1280, 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // fs = 44.1 kHz
+ 69, 70, 87, 88, 104, 105, 121, 122, 139, 140, 174, 175, 208, 209, 243, 244,
+ 278, 279, 348, 349, 417, 418, 487, 488, 557, 558, 696, 697, 835, 836, 975, 976,
+ 1114, 1115, 1253, 1254, 1393, 1394, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // fs = 32 kHz
+ 96, 96, 120, 120, 144, 144, 168, 168, 192, 192, 240, 240, 288, 288, 336, 336,
+ 384, 384, 480, 480, 576, 576, 672, 672, 768, 768, 960, 960, 1152, 1152, 1344, 1344,
+ 1536, 1536, 1728, 1728, 1920, 1920, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+
+cDolbyRepacker::cDolbyRepacker(void)
+{
+ pesHeader[0] = 0x00;
+ pesHeader[1] = 0x00;
+ pesHeader[2] = 0x01;
+ pesHeader[3] = 0xBD;
+ pesHeader[4] = 0x00;
+ pesHeader[5] = 0x00;
+ Reset();
+}
+
+void cDolbyRepacker::AppendSubStreamID(bool ContinuationFrame)
+{
+ if (subStreamId) {
+ pesHeader[pesHeaderLen++] = subStreamId;
+ // number of ac3 frames "starting" in this packet (1 by design).
+ pesHeader[pesHeaderLen++] = 0x01;
+ // offset to start of first ac3 frame (0 means "no ac3 frame starting"
+ // so 1 (by design) addresses the first byte after the next two bytes).
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = (ContinuationFrame ? 0x00 : 0x01);
+ }
+}
+
+void cDolbyRepacker::ResetPesHeader(bool ContinuationFrame)
+{
+ pesHeader[6] = 0x80;
+ pesHeader[7] = 0x00;
+ pesHeader[8] = 0x00;
+ pesHeaderLen = 9;
+ AppendSubStreamID(ContinuationFrame);
+}
+
+void cDolbyRepacker::Reset(void)
+{
+ cRepacker::Reset();
+ ResetPesHeader();
+ state = find_0b;
+ ac3todo = 0;
+ chk1 = 0;
+ chk2 = 0;
+ fragmentLen = 0;
+ fragmentTodo = 0;
+ pesHeaderBackupLen = 0;
+ skippedBytes = 0;
+}
+
+bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite)
+{
+ bool success = true;
+ // enough data available to put PES packet into buffer?
+ if (fragmentTodo <= Todo) {
+ // output a previous fragment first
+ if (fragmentLen > 0) {
+ Bite = fragmentLen;
+ int n = Put(ResultBuffer, fragmentData, Bite, fragmentLen + fragmentTodo);
+ if (Bite != n)
+ success = false;
+ fragmentLen = 0;
+ }
+ Bite = fragmentTodo;
+ if (success) {
+ int n = Put(ResultBuffer, Data, Bite, Bite);
+ if (Bite != n)
+ success = false;
+ }
+ fragmentTodo = 0;
+ // ac3 frame completely processed?
+ if (Bite >= ac3todo)
+ state = find_0b; // go on with finding start of next packet
+ }
+ else {
+ // copy the fragment into separate buffer for later processing
+ Bite = Todo;
+ memcpy(fragmentData + fragmentLen, Data, Bite);
+ fragmentLen += Bite;
+ fragmentTodo -= Bite;
+ }
+ return success;
+}
+
+bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite)
+{
+ bool success = true;
+ int packetLen = pesHeaderLen + ac3todo;
+ // limit packet to maximum size
+ if (packetLen > maxPacketSize)
+ packetLen = maxPacketSize;
+ pesHeader[4] = (packetLen - 6) >> 8;
+ pesHeader[5] = (packetLen - 6) & 0xFF;
+ Bite = pesHeaderLen;
+ // enough data available to put PES packet into buffer?
+ if (packetLen - pesHeaderLen <= Todo) {
+ int n = Put(ResultBuffer, pesHeader, Bite, packetLen);
+ if (Bite != n)
+ success = false;
+ Bite = packetLen - pesHeaderLen;
+ if (success) {
+ n = Put(ResultBuffer, Data, Bite, Bite);
+ if (Bite != n)
+ success = false;
+ }
+ // ac3 frame completely processed?
+ if (Bite >= ac3todo)
+ state = find_0b; // go on with finding start of next packet
+ }
+ else {
+ fragmentTodo = packetLen;
+ // copy the pesheader into separate buffer for later processing
+ memcpy(fragmentData + fragmentLen, pesHeader, Bite);
+ fragmentLen += Bite;
+ fragmentTodo -= Bite;
+ // copy the fragment into separate buffer for later processing
+ Bite = Todo;
+ memcpy(fragmentData + fragmentLen, Data, Bite);
+ fragmentLen += Bite;
+ fragmentTodo -= Bite;
+ }
+ return success;
+}
+
+void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
+{
+ // synchronisation is detected some bytes after frame start.
+ const int SkippedBytesLimit = 4;
+
+ // check for MPEG 2
+ if ((Data[6] & 0xC0) != 0x80) {
+ DroppedData("cDolbyRepacker: MPEG 2 PES header expected", Count);
+ return;
+ }
+
+ // backup PES header
+ if (Data[6] != 0x80 || Data[7] != 0x00 || Data[8] != 0x00) {
+ pesHeaderBackupLen = 6 + 3 + Data[8];
+ memcpy(pesHeaderBackup, Data, pesHeaderBackupLen);
+ }
+
+ // skip PES header
+ int done = 6 + 3 + Data[8];
+ int todo = Count - done;
+ const uchar *data = Data + done;
+
+ // look for 0x0B 0x77 <chk1> <chk2> <frameSize>
+ while (todo > 0) {
+ switch (state) {
+ case find_0b:
+ if (*data == 0x0B) {
+ state++;
+ // copy header information once for later use
+ if (pesHeaderBackupLen > 0) {
+ pesHeaderLen = pesHeaderBackupLen;
+ pesHeaderBackupLen = 0;
+ memcpy(pesHeader, pesHeaderBackup, pesHeaderLen);
+ AppendSubStreamID();
+ }
+ }
+ data++;
+ done++;
+ todo--;
+ skippedBytes++; // collect number of skipped bytes while syncing
+ continue;
+ case find_77:
+ if (*data != 0x77) {
+ state = find_0b;
+ continue;
+ }
+ data++;
+ done++;
+ todo--;
+ skippedBytes++; // collect number of skipped bytes while syncing
+ state++;
+ continue;
+ case store_chk1:
+ chk1 = *data++;
+ done++;
+ todo--;
+ skippedBytes++; // collect number of skipped bytes while syncing
+ state++;
+ continue;
+ case store_chk2:
+ chk2 = *data++;
+ done++;
+ todo--;
+ skippedBytes++; // collect number of skipped bytes while syncing
+ state++;
+ continue;
+ case get_length:
+ ac3todo = 2 * frameSizes[*data];
+ // frameSizeCode was invalid => restart searching
+ if (ac3todo <= 0) {
+ // reset PES header instead of using a wrong one
+ ResetPesHeader();
+ if (chk1 == 0x0B) {
+ if (chk2 == 0x77) {
+ state = store_chk1;
+ continue;
+ }
+ if (chk2 == 0x0B) {
+ state = find_77;
+ continue;
+ }
+ state = find_0b;
+ continue;
+ }
+ if (chk2 == 0x0B) {
+ state = find_77;
+ continue;
+ }
+ state = find_0b;
+ continue;
+ }
+ if (initiallySyncing) // omit report for the typical initial case
+ initiallySyncing = false;
+ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
+ LOG("cDolbyRepacker: skipped %d bytes to sync on next AC3 frame", skippedBytes - SkippedBytesLimit);
+ skippedBytes = 0;
+ // append read data to header for common output processing
+ pesHeader[pesHeaderLen++] = 0x0B;
+ pesHeader[pesHeaderLen++] = 0x77;
+ pesHeader[pesHeaderLen++] = chk1;
+ pesHeader[pesHeaderLen++] = chk2;
+ ac3todo -= 4;
+ state++;
+ // fall through to output
+ case output_packet: {
+ int bite = 0;
+ // finish remainder of ac3 frame?
+ if (fragmentTodo > 0)
+ FinishRemainder(ResultBuffer, data, todo, bite);
+ else {
+ // start a new packet
+ StartNewPacket(ResultBuffer, data, todo, bite);
+ // prepare for next (continuation) packet
+ ResetPesHeader(state == output_packet);
+ }
+ data += bite;
+ done += bite;
+ todo -= bite;
+ ac3todo -= bite;
+ }
+ }
+ }
+ // report that syncing dropped some bytes
+ if (skippedBytes > SkippedBytesLimit) {
+ if (!initiallySyncing) // omit report for the typical initial case
+ LOG("cDolbyRepacker: skipped %d bytes while syncing on next AC3 frame", skippedBytes - 4);
+ skippedBytes = SkippedBytesLimit;
+ }
+}
+
+int cDolbyRepacker::BreakAt(const uchar *Data, int Count)
+{
+ if (initiallySyncing)
+ return -1; // fill the packet buffer completely until we have synced once
+ // enough data for test?
+ if (Count < 6 + 3)
+ return -1;
+ // check for MPEG 2
+ if ((Data[6] & 0xC0) != 0x80)
+ return -1;
+ int headerLen = Data[8] + 6 + 3;
+ // break after fragment tail?
+ if (ac3todo > 0)
+ return headerLen + ac3todo;
+ // enough data for test?
+ if (Count < headerLen + 5)
+ return -1;
+ const uchar *data = Data + headerLen;
+ // break after ac3 frame?
+ if (data[0] == 0x0B && data[1] == 0x77 && frameSizes[data[4]] > 0)
+ return headerLen + 2 * frameSizes[data[4]];
+ return -1;
+}
+
+// --- 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_LOAD 0x10
+#define ADAPT_FIELD 0x20
+#define PAY_START 0x40
+#define TS_ERROR 0x80
+
+#define MAX_PLENGTH 0xFFFF // the maximum PES packet length (theoretically)
+#define MMAX_PLENGTH (64*MAX_PLENGTH) // some stations send PES packets that are extremely large, e.g. DVB-T in Finland or HDTV 1920x1080
+
+#define IPACKS 2048
+
+// Start codes:
+#define SC_SEQUENCE 0xB3 // "sequence header code"
+#define SC_GROUP 0xB8 // "group start code"
+#define SC_PICTURE 0x00 // "picture start code"
+
+#define MAXNONUSEFULDATA (10*1024*1024)
+#define MAXNUMUPTERRORS 10
+
+class cTS2PES {
+private:
+ int pid;
+ int size;
+ int found;
+ int count;
+ uint8_t *buf;
+ uint8_t cid;
+ uint8_t rewriteCid;
+ uint8_t subStreamId;
+ int plength;
+ uint8_t plen[2];
+ uint8_t flag1;
+ uint8_t flag2;
+ uint8_t hlength;
+ int mpeg;
+ uint8_t check;
+ int mpeg1_required;
+ int mpeg1_stuffing;
+ bool done;
+ cRingBufferLinear *resultBuffer;
+ int tsErrors;
+ int ccErrors;
+ int ccCounter;
+ cRepacker *repacker;
+ 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(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t RewriteCid = 0x00, uint8_t SubStreamId = 0x00, cRepacker *Repacker = NULL);
+ ~cTS2PES();
+ int Pid(void) { return pid; }
+ 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(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t RewriteCid, uint8_t SubStreamId, cRepacker *Repacker)
+{
+ pid = Pid;
+ resultBuffer = ResultBuffer;
+ size = Size;
+ rewriteCid = RewriteCid;
+ subStreamId = SubStreamId;
+ repacker = Repacker;
+ if (repacker) {
+ repacker->SetMaxPacketSize(size);
+ repacker->SetSubStreamId(subStreamId);
+ size += repacker->QuerySnoopSize();
+ }
+
+ 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);
+ delete repacker;
+}
+
+void cTS2PES::Clear(void)
+{
+ reset_ipack();
+ if (repacker)
+ repacker->Reset();
+}
+
+void cTS2PES::store(uint8_t *Data, int Count)
+{
+ if (repacker)
+ repacker->Repack(resultBuffer, Data, Count);
+ else
+ cRepacker::Put(resultBuffer, Data, Count, Count);
+}
+
+void cTS2PES::reset_ipack(void)
+{
+ found = 0;
+ cid = 0;
+ plength = 0;
+ flag1 = 0;
+ flag2 = 0;
+ hlength = 0;
+ mpeg = 0;
+ check = 0;
+ mpeg1_required = 0;
+ mpeg1_stuffing = 0;
+ done = false;
+ count = 0;
+}
+
+void cTS2PES::send_ipack(void)
+{
+ if (count <= ((mpeg == 2) ? 9 : 7)) // skip empty packets
+ return;
+ buf[3] = rewriteCid ? rewriteCid : 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;
+ if (!repacker && subStreamId) {
+ buf[9] = subStreamId;
+ buf[10] = 1;
+ buf[11] = 0;
+ buf[12] = 1;
+ count = 13;
+ }
+ 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;
+ }
+
+ // determine amount of data to process
+ int bite = Count;
+ if (count + bite > size)
+ bite = size - count;
+ if (repacker) {
+ int breakAt = repacker->BreakAt(buf, count);
+ // avoid memcpy of data after break location
+ if (0 <= breakAt && breakAt < count + bite) {
+ bite = breakAt - count;
+ if (bite < 0) // should never happen
+ bite = 0;
+ }
+ }
+
+ memcpy(buf + count, Data, bite);
+ count += bite;
+
+ if (repacker) {
+ // determine break location
+ int breakAt = repacker->BreakAt(buf, count);
+ if (breakAt > size) // won't fit into packet?
+ breakAt = -1;
+ if (breakAt > count) // not enough data?
+ breakAt = -1;
+ // push out data before break location
+ if (breakAt > 0) {
+ // adjust bite if above memcpy was to large
+ bite -= count - breakAt;
+ count = breakAt;
+ send_ipack();
+ // recurse for data after break location
+ if (Count - bite > 0)
+ write_ipack(Data + bite, Count - bite);
+ }
+ }
+
+ // push out data when buffer is full
+ if (count >= size) {
+ send_ipack();
+ // recurse for remaining data
+ if (Count - bite > 0)
+ write_ipack(Data + bite, Count - bite);
+ }
+}
+
+void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
+{
+ int c = 0;
+
+ while (c < Count && (mpeg == 0 || (mpeg == 1 && found < mpeg1_required) || (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;
+ mpeg1_stuffing = 0;
+ }
+ else {
+ plen[0] = Buf[c];
+ found++;
+ return;
+ }
+ break;
+ case 5: {
+ plen[1] = Buf[c++];
+ 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 {
+ 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 || mpeg1_required > 7)) {
+ flag2 = Buf[c++];
+ found++;
+ }
+ break;
+ case 8:
+ 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:
+ break;
+ }
+ }
+
+ if (!plength)
+ plength = MMAX_PLENGTH - 6;
+
+ 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:
+ case PRIVATE_STREAM1:
+
+ if (mpeg == 2 && found == 9 && count < found) { // make sure to not write the data twice by looking at count
+ write_ipack(&flag1, 1);
+ write_ipack(&flag2, 1);
+ write_ipack(&hlength, 1);
+ }
+
+ if (mpeg == 1 && found == mpeg1_required && count < found) { // make sure to not write the data twice by looking at count
+ 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) {
+ write_ipack(Buf + c, 1);
+ c++;
+ found++;
+ }
+ if (c == Count)
+ return;
+ }
+
+ if (!repacker && subStreamId) {
+ while (c < Count && found < (hlength + 9) && found < plength + 6) {
+ write_ipack(Buf + c, 1);
+ c++;
+ found++;
+ }
+ if (found == (hlength + 9)) {
+ uchar sbuf[] = { 0x01, 0x00, 0x00 };
+ write_ipack(&subStreamId, 1);
+ write_ipack(sbuf, 3);
+ }
+ }
+
+ 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++;
+
+#ifdef USE_DVBSETUP
+ if (!(Buf[3] & (ADAPT_FIELD | PAY_LOAD))) {
+ dsyslog("TS packet discarded due to invalid adaption_field_control");
+ return;
+ }
+#else
+ if (!(Buf[3] & (ADAPT_FIELD | PAY_LOAD)))
+ return; // discard TS packet with adaption_field_control set to '00'.
+#endif /* DVBSETUP */
+
+ if ((Buf[3] & PAY_LOAD) && ((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);
+#ifdef USE_DVBSETUP
+ dsyslog("TS continuity error (%d)", ccCounter);
+#endif /* DVBSETUP */
+ }
+ ccCounter = Buf[3] & CONT_CNT_MASK;
+ }
+
+ if (Buf[1] & PAY_START) {
+ if (found > 6) {
+ if (plength != MMAX_PLENGTH - 6 && plength != found - 6)
+ dsyslog("PES packet shortened to %d bytes (expected: %d bytes)", found, plength + 6);
+ plength = found - 6;
+ send_ipack();
+ reset_ipack();
+ }
+ found = 0;
+ }
+
+ uint8_t off = 0;
+
+ if (Buf[3] & ADAPT_FIELD) { // adaptation field?
+ off = Buf[4] + 1;
+ if (off + 4 > 187)
+ return;
+ }
+
+ if (Buf[3] & PAY_LOAD)
+ instant_repack(Buf + 4 + off, TS_SIZE - 4 - off);
+#ifdef USE_DVBSETUP
+ else if (off + 4 < 188)
+ dsyslog("adaption_field zu short or PAY_LOAD not set");
+#endif /* DVBSETUP */
+}
+
+// --- cRingBufferLinearPes --------------------------------------------------
+
+class cRingBufferLinearPes : public cRingBufferLinear {
+protected:
+ virtual int DataReady(const uchar *Data, int Count);
+public:
+ cRingBufferLinearPes(int Size, int Margin = 0, bool Statistics = false, const char *Description = NULL)
+ :cRingBufferLinear(Size, Margin, Statistics, Description) {}
+ };
+
+int cRingBufferLinearPes::DataReady(const uchar *Data, int Count)
+{
+ int c = cRingBufferLinear::DataReady(Data, Count);
+ if (!c && Count >= 6) {
+ if (!Data[0] && !Data[1] && Data[2] == 0x01) {
+ int Length = 6 + Data[4] * 256 + Data[5];
+ if (Length <= Count)
+ return Length;
+ }
+ }
+ return c;
+}
+
+// --- cRemuxPIP ----------------------------------------------------------------
+
+#define RESULTBUFFERSIZE KILOBYTE(256)
+
+#ifdef USE_SYNCEARLY
+cRemuxPIP::cRemuxPIP(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure, bool SyncEarly)
+#else
+cRemuxPIP::cRemuxPIP(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure)
+#endif /* SYNCEARLY */
+{
+ exitOnFailure = ExitOnFailure;
+ noVideo = VPid == 0 || VPid == 1 || VPid == 0x1FFF;
+ numUPTerrors = 0;
+ synced = false;
+#ifdef USE_SYNCEARLY
+ syncEarly = SyncEarly;
+#endif /* SYNCEARLY */
+ skipped = 0;
+ numTracks = 0;
+ resultSkipped = 0;
+ resultBuffer = new cRingBufferLinearPes(RESULTBUFFERSIZE, IPACKS, false, "Result");
+ resultBuffer->SetTimeouts(0, 100);
+ if (VPid)
+#define TEST_cVideoRepacker
+#ifdef TEST_cVideoRepacker
+ ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0xE0, 0x00, new cVideoRepacker);
+#else
+ ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0xE0);
+#endif
+ 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(0xC0 + n));
+ n++;
+#else
+ ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n++);
+#endif
+ }
+ }
+ if (DPids) {
+ int n = 0;
+ while (*DPids && numTracks < MAXTRACKS && n < MAXDPIDS)
+ ts2pes[numTracks++] = new cTS2PES(*DPids++, resultBuffer, IPACKS, 0x00, 0x80 + n++, new cDolbyRepacker);
+ }
+ if (SPids) {
+ int n = 0;
+ while (*SPids && numTracks < MAXTRACKS && n < MAXSPIDS)
+ ts2pes[numTracks++] = new cTS2PES(*SPids++, resultBuffer, IPACKS, 0x00, 0x20 + n++);
+ }
+}
+
+cRemuxPIP::~cRemuxPIP()
+{
+ for (int t = 0; t < numTracks; t++)
+ delete ts2pes[t];
+ delete resultBuffer;
+}
+
+int cRemuxPIP::GetPid(const uchar *Data)
+{
+ return (((uint16_t)Data[0] & PID_MASK_HI) << 8) | (Data[1] & 0xFF);
+}
+
+int cRemuxPIP::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 cRemuxPIP::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) {
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(Data + Offset, Length, PesPayloadOffset) >= phMPEG1) {
+ const uchar *p = Data + Offset + PesPayloadOffset + 2;
+ const uchar *pLimit = Data + Offset + Length - 3;
+#ifdef TEST_cVideoRepacker
+ // cVideoRepacker ensures that a new PES packet is started for a new sequence,
+ // group or picture which allows us to easily skip scanning through a huge
+ // amount of video data.
+ if (p < pLimit) {
+ if (p[-2] || p[-1] || p[0] != 0x01)
+ pLimit = 0; // skip scanning: packet doesn't start with 0x000001
+ else {
+ switch (p[1]) {
+ case SC_SEQUENCE:
+ case SC_GROUP:
+ case SC_PICTURE:
+ break;
+ default: // skip scanning: packet doesn't start a new sequence, group or picture
+ pLimit = 0;
+ }
+ }
+ }
+#endif
+ while (p < pLimit && (p = (const uchar *)memchr(p, 0x01, pLimit - p))) {
+ if (!p[-2] && !p[-1]) { // found 0x000001
+ switch (p[1]) {
+ case SC_PICTURE: PictureType = (p[3] >> 3) & 0x07;
+ return Length;
+ }
+ p += 4; // continue scanning after 0x01ssxxyy
+ }
+ else
+ p += 3; // continue scanning after 0x01xxyy
+ }
+ }
+ PictureType = NO_PICTURE;
+ return Length;
+ }
+ return -1;
+}
+
+#define TS_SYNC_BYTE 0x47
+
+int cRemuxPIP::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
+ for (int t = 0; t < numTracks; t++) {
+ if (ts2pes[t]->Pid() == pid) {
+ ts2pes[t]->ts_to_pes(Data + i);
+ break;
+ }
+ }
+ }
+ 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)
+ ShutdownHandler.RequestEmergencyExit();
+ }
+ else
+ skipped += used;
+ }
+
+ return used;
+}
+
+uchar *cRemuxPIP::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
+
+ // 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) {
+ ShutdownHandler.RequestEmergencyExit();
+ numUPTerrors = 0;
+ }
+ }
+ else if (!synced) {
+#ifdef USE_SYNCEARLY
+ if (pt == I_FRAME || syncEarly) {
+#else
+ if (pt == I_FRAME) {
+#endif /* SYNCEARLY */
+ if (PictureType)
+ *PictureType = pt;
+ resultSkipped = i; // will drop everything before this position
+#ifdef USE_SYNCEARLY
+ if (!syncEarly)
+#endif /* SYNCEARLY */
+ SetBrokenLink(data + i, l);
+ synced = true;
+#ifdef USE_SYNCEARLY
+ if (syncEarly) {
+ if (pt == I_FRAME) // syncEarly: it's ok but there is no need to call SetBrokenLink()
+ SetBrokenLink(data + i, l);
+ else fprintf(stderr, "video: synced early\n");
+ }
+#endif /* SYNCEARLY */
+ }
+ }
+ 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;
+#ifdef USE_SYNCEARLY
+ if (noVideo || !synced && syncEarly) {
+ if (!synced) {
+ if (PictureType && noVideo)
+#else
+ if (noVideo) {
+ if (!synced) {
+ if (PictureType)
+#endif /* SYNCEARLY */
+ *PictureType = I_FRAME;
+ resultSkipped = i; // will drop everything before this position
+ synced = true;
+#ifdef USE_SYNCEARLY
+ if (!noVideo && syncEarly) fprintf(stderr, "audio: synced early\n");
+#endif /* SYNCEARLY */
+ }
+ else if (Count)
+ return resultData;
+ else if (PictureType)
+ *PictureType = I_FRAME;
+ }
+ }
+ 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 cRemuxPIP::Del(int Count)
+{
+ resultBuffer->Del(Count);
+}
+
+void cRemuxPIP::Clear(void)
+{
+ for (int t = 0; t < numTracks; t++)
+ ts2pes[t]->Clear();
+ resultBuffer->Clear();
+ synced = false;
+ skipped = 0;
+ resultSkipped = 0;
+}
+
+void cRemuxPIP::SetBrokenLink(uchar *Data, int Length)
+{
+ 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;
+ return;
+ }
+ }
+ dsyslog("SetBrokenLink: no GOP header found in video packet");
+ }
+ else
+ dsyslog("SetBrokenLink: no video packet in frame");
+}
diff --git a/remux.h b/remux.h
new file mode 100644
index 0000000..833cb2f
--- /dev/null
+++ b/remux.h
@@ -0,0 +1,85 @@
+/*
+ * remux.h: A streaming MPEG2 remultiplexer
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: remux.h 1.17 2007/09/02 10:19:06 kls Exp $
+ */
+
+#ifndef __REMUX_PIP_H
+#define __REMUX_PIP_H
+
+#include <vdr/ringbuffer.h>
+#include <vdr/tools.h>
+#include <vdr/remux.h>
+
+//ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader = NULL);
+
+// Picture types:
+#define NO_PICTURE 0
+#define I_FRAME 1
+#define P_FRAME 2
+#define B_FRAME 3
+
+#define MAXTRACKS 64
+
+class cTS2PES;
+
+class cRemuxPIP {
+private:
+ bool exitOnFailure;
+ bool noVideo;
+ int numUPTerrors;
+ bool synced;
+#ifdef USE_SYNCEARLY
+ bool syncEarly;
+#endif /* SYNCEARLY */
+ int skipped;
+ cTS2PES *ts2pes[MAXTRACKS];
+ int numTracks;
+ cRingBufferLinear *resultBuffer;
+ int resultSkipped;
+ int GetPid(const uchar *Data);
+public:
+#ifdef USE_SYNCEARLY
+ cRemuxPIP(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure = false, bool SyncEarly = false);
+#else
+ cRemuxPIP(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure = false);
+#endif /* SYNCEARLY */
+ ///< Creates a new remuxer for the given PIDs. VPid is the video PID, while
+ ///< APids, DPids and SPids are pointers to zero terminated lists of audio,
+ ///< dolby and subtitle PIDs (the pointers may be NULL if there is no such
+ ///< PID). If ExitOnFailure is true, the remuxer will initiate an "emergency
+ ///< exit" in case of problems with the data stream.
+ ///< If USE_SYNCEARLY is activated: SyncEarly causes cRemuxPIP to sync as soon
+ ///< as a video or audio frame is seen.
+ ~cRemuxPIP();
+ void SetTimeouts(int PutTimeout, int GetTimeout) { resultBuffer->SetTimeouts(PutTimeout, GetTimeout); }
+ ///< By default cRemuxPIP assumes that Put() and Get() are called from different
+ ///< threads, and uses a timeout in the Get() function in case there is no
+ ///< data available. SetTimeouts() can be used to modify these timeouts.
+ ///< Especially if Put() and Get() are called from the same thread, setting
+ ///< both timeouts to 0 is recommended.
+ int Put(const uchar *Data, int Count);
+ ///< Puts at most Count bytes of Data into the remuxer.
+ ///< \return Returns the number of bytes actually consumed from Data.
+ uchar *Get(int &Count, uchar *PictureType = NULL);
+ ///< Gets all currently available data from the remuxer.
+ ///< \return Count contains the number of bytes the result points to, and
+ ///< PictureType (if not NULL) will contain one of NO_PICTURE, I_FRAME, P_FRAME
+ ///< or B_FRAME.
+ void Del(int Count);
+ ///< Deletes Count bytes from the remuxer. Count must be the number returned
+ ///< from a previous call to Get(). Several calls to Del() with fractions of
+ ///< a previously returned Count may be made, but the total sum of all Count
+ ///< values must be exactly what the previous Get() has returned.
+ void Clear(void);
+ ///< Clears the remuxer of all data it might still contain, keeping the PID
+ ///< settings as they are.
+ 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);
+ };
+
+#endif // __REMUX_PIP_H