summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS2
-rw-r--r--HISTORY24
-rw-r--r--PLUGINS.html62
-rw-r--r--PLUGINS/src/sky/HISTORY4
-rw-r--r--PLUGINS/src/sky/sky.c6
-rw-r--r--channels.h10
-rw-r--r--device.c266
-rw-r--r--device.h136
-rw-r--r--dvbdevice.c78
-rw-r--r--dvbdevice.h17
-rw-r--r--dvbplayer.c87
-rw-r--r--menu.c15
-rw-r--r--player.c17
-rw-r--r--player.h36
-rw-r--r--skinsttng.c8
-rw-r--r--transfer.c101
-rw-r--r--transfer.h8
-rw-r--r--vdr.c4
18 files changed, 517 insertions, 364 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index f6c27f9b..fb69037f 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -263,6 +263,8 @@ Werner Fink <werner@suse.de>
for suggesting to add more checks and polling when getting frontend events
for setting the VPID before the APID in live mode to avoid unnecessary
overhead in the firmware
+ for a patch that was used as a base for implementing a modified PES packet
+ handling in order to play AC3 audio over full featured DVB cards
Rolf Hakenes <hakenes@hippomi.de>
for providing 'libdtv' and adapting the EIT mechanisms to it
diff --git a/HISTORY b/HISTORY
index dd5a45b6..955bdb5f 100644
--- a/HISTORY
+++ b/HISTORY
@@ -3160,7 +3160,7 @@ Video Disk Recorder Revision History
right day of week for timers in the future.
- Some improvements to cPoller (thanks to Marco Schlüßler).
-2004-11-27: Version 1.3.18
+2004-12-17: Version 1.3.18
- Removed an unused variable from cTimer::GetWDayFromMDay() (thanks to Wayne Keer
for reporting this one).
@@ -3171,3 +3171,25 @@ Video Disk Recorder Revision History
picture mode (thanks to Reinhard Nissl for reporting this one).
- Fixed a possible race condition in generating the DVB device names (thanks to
Rainer Zocholl for reporting this one).
+- Changed the way PES packets are played to allow replay of AC3 sound over the
+ full featured DVB cards (partially based on a patch from Werner Fink).
+ + The new function cDevice::PlayPes() is now called with the complete PES data
+ stream and calls PlayVideo() and PlayAudio() as necessary.
+ + cDevice::PlayVideo() is now only called with actual video PES packets.
+ + cDevice::PlayAudio() is now called with the actual audio PES packets, which
+ can be either "normal" audio or AC3 data. You need at least firmware version
+ 0x261d to replay AC3 sound over a full featured DVB card. This function now
+ has an 'int' return value.
+ + PlayAudio() of derived cDevice classes shall no longer call the base class
+ function. It shall just play the given data as audio.
+ + cPlayer::PlayVideo() and cPlayer::PlayAudio() are now obsolete and have been
+ replaced with cPlayer::PlayPes().
+ + All StripAudioPackets() functions are now obsolete. The functionality has been
+ moved into cDevice::PlayPes(), where only the video and audio packets that are
+ actually required will be processed.
+ + All audio track handling is now done by cDevice; cTransfer and cDvbPlayer no
+ longer care about audio tracks. cPlayer, however, still has the virtual hooks
+ for audio track handling in order to allow plugins to implement players that
+ have their own idea about this.
+ + cChannel::[AD]pid[12]() have been replaced with cChannel::[AD]pid(int i) to
+ allow access to all available PIDs.
diff --git a/PLUGINS.html b/PLUGINS.html
index cfd28d12..878c3670 100644
--- a/PLUGINS.html
+++ b/PLUGINS.html
@@ -14,18 +14,18 @@ Copyright &copy; 2004 Klaus Schmidinger<br>
<a href="http://www.cadsoft.de/vdr">www.cadsoft.de/vdr</a>
</center>
<p>
-<!--X1.2.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
-Important modifications introduced in version 1.2.6 are marked like this.
-<!--X1.2.6--></td></tr></table>
-<!--X1.3.0--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
+<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.0 are marked like this.
<!--X1.3.0--></td></tr></table>
-<!--X1.3.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
+<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.7 are marked like this.
<!--X1.3.7--></td></tr></table>
-<!--X1.3.8--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+<!--X1.3.8--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.8 are marked like this.
<!--X1.3.8--></td></tr></table>
+<!--X1.3.18--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+Important modifications introduced in version 1.3.18 are marked like this.
+<!--X1.3.18--></td></tr></table>
<p>
VDR provides an easy to use plugin interface that allows additional functionality
to be added to the program by implementing a dynamically loadable library file.
@@ -73,11 +73,11 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#Status monitor">Status monitor</a>
<li><a href="#Players">Players</a>
<li><a href="#Receivers">Receivers</a>
-<!--X1.3.0--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
+<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<li><a href="#Filters">Filters</a>
<!--X1.3.0--></td></tr></table>
<li><a href="#The On Screen Display">The On Screen Display</a>
-<!--X1.3.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
+<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<li><a href="#Skins">Skins</a>
<li><a href="#Themes">Themes</a>
<!--X1.3.7--></td></tr></table>
@@ -1023,17 +1023,21 @@ public:
Take a look at the files <tt>player.h</tt> and <tt>dvbplayer.c</tt> to see how VDR implements
its own player for the VDR recordings.
<p>
-To play the video data, the player needs to call its member function
+<!--X1.3.18--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+To play the actual data, the player needs to call its member function
<p><table><tr><td bgcolor=#F0F0F0><pre>
-int PlayVideo(const uchar *Data, int Length);
+int PlayPes(const uchar *Data, int Length, bool VideoOnly);
</pre></td></tr></table><p>
where <tt>Data</tt> points to a block of <tt>Length</tt> bytes of a PES data
-stream. There are no prerequisites regarding the length or alignment of an
+stream containing any combination of video, audio or dolby tracks. Which audio
+or dolby track will actually be played is controlled by the device the player
+is attached to. There are no prerequisites regarding the length or alignment of an
individual block of data. The sum of all blocks must simply result in the
-desired video data stream, and it must be delivered fast enough so that the
+desired data stream, and it must be delivered fast enough so that the
DVB device doesn't run out of data.
+<!--X1.3.18--></td></tr></table>
To avoid busy loops the player should call its member function
<p><table><tr><td bgcolor=#F0F0F0><pre>
@@ -1042,24 +1046,26 @@ bool DevicePoll(cPoller &amp;Poller, int TimeoutMs = 0);
to determine whether the device is ready for further data.
<p>
-If the player can provide more than a single audio track, it can implement the
-following functions to make them available:
+<!--X1.3.18--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+By default all audio track handling is done by the device a player is
+attached to.
+If the player can provide more than a single audio track, and has special
+requirements in order to set a given track, it can implement the
+following function to allow the device to set a specific track:
<p><table><tr><td bgcolor=#F0F0F0><pre>
-virtual int NumAudioTracks(void) const;
-virtual const char **GetAudioTracks(int *CurrentTrack = NULL);
-virtual void SetAudioTrack(int Index);
+virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId)
</pre></td></tr></table><p>
-<p>
-If there is an additional audio track that has to be replayed with external hardware,
-the player shall call its member function
+A player that has special requirements about audio tracks should announce its
+available audio tracks by calling
<p><table><tr><td bgcolor=#F0F0F0><pre>
-void PlayAudio(const uchar *Data, int Length);
+bool DeviceSetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, uint32_t Flags = 0)
</pre></td></tr></table><p>
-where <tt>Data</tt> points to a complete audio PES packet of <tt>Length</tt> bytes.
+See <tt>device.h</tt> for details about the parameters for track handling.
+<!--X1.3.18--></td></tr></table>
<p>
The second part needed here is a control object that receives user input from the main
program loop and reacts on this by telling the player what to do:
@@ -1217,7 +1223,7 @@ Mode</i>).
If the <tt>cReceiver</tt> isn't needed any more, it may simply be <i>deleted</i>
and will automatically detach itself from the <tt>cDevice</tt>.
-<!--X1.3.0--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
+<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<a name="Filters"><hr><h2>Filters</h2>
<center><i><b>A Fistful of Datas</b></i></center><p>
@@ -1263,7 +1269,7 @@ and will automatically detach itself from the <tt>cDevice</tt>.
See VDR/eit.c or VDR/pat.c to learn how to process filter data.
<!--X1.3.0--></td></tr></table>
-<!--X1.3.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
+<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<a name="The On Screen Display"><hr><h2>The On Screen Display</h2>
<center><i><b>Window to the world</b></i></center><p>
@@ -1375,7 +1381,7 @@ new cMySkin;
in the <a href="#Getting started"><tt>Start()</tt></a> function of your plugin.
Do not delete this object, it will be automatically deleted when the program ends.
<p>
-<!--X1.3.8--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+<!--X1.3.8--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
In order to be able to easily identify plugins that implement a skin it is recommended
that the name of such a plugin should be
@@ -1527,9 +1533,7 @@ The functions to implement replaying capabilites are
virtual bool HasDecoder(void) const;
virtual bool CanReplay(void) const;
virtual bool SetPlayMode(ePlayMode PlayMode);
-<!--X1.2.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
virtual int64_t GetSTC(void);
-<!--X1.2.6--></td></tr></table>
virtual void TrickSpeed(int Speed);
virtual void Clear(void);
virtual void Play(void);
@@ -1549,7 +1553,7 @@ virtual void SetVideoFormat(bool VideoFormat16_9);
virtual void SetVolumeDevice(int Volume);
</pre></td></tr></table><p>
-<!--X1.3.0--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
+<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<p>
<b>Section Filtering</b>
<p>
@@ -1579,7 +1583,7 @@ handle section data.
<p>
<b>On Screen Display</b>
<p>
-<!--X1.3.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
+<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
If your device provides On Screen Display (OSD) capabilities (which every device
that is supposed to be used as a primary device should do), it shall implement
an "OSD provider" class, derived from <tt>cOsdProvider</tt>, which, when its <tt>CreateOsd()</tt>
diff --git a/PLUGINS/src/sky/HISTORY b/PLUGINS/src/sky/HISTORY
index 2d15da7e..490ee62b 100644
--- a/PLUGINS/src/sky/HISTORY
+++ b/PLUGINS/src/sky/HISTORY
@@ -28,3 +28,7 @@ VDR Plugin 'sky' Revision History
2004-10-16: Version 0.3.1
- Improved buffer handling.
+
+2004-12-12: Version 0.3.2
+
+- Changed Apid access in cChannel.
diff --git a/PLUGINS/src/sky/sky.c b/PLUGINS/src/sky/sky.c
index eea697ea..8936774e 100644
--- a/PLUGINS/src/sky/sky.c
+++ b/PLUGINS/src/sky/sky.c
@@ -3,7 +3,7 @@
*
* See the README file for copyright information and how to reach the author.
*
- * $Id: sky.c 1.7 2004/10/16 09:10:06 kls Exp $
+ * $Id: sky.c 1.8 2004/12/12 14:27:33 kls Exp $
*/
#include <sys/socket.h>
@@ -14,7 +14,7 @@
#include <vdr/plugin.h>
#include <vdr/sources.h>
-static const char *VERSION = "0.3.1";
+static const char *VERSION = "0.3.2";
static const char *DESCRIPTION = "Sky Digibox interface";
// --- cDigiboxDevice --------------------------------------------------------
@@ -213,7 +213,7 @@ bool cDigiboxDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
cSkyChannel *SkyChannel = SkyChannels.GetSkyChannel(Channel);
if (SkyChannel) {
digiboxChannelNumber = SkyChannel->digiboxChannelNumber;
- apid = Channel->Apid1();
+ apid = Channel->Apid(0);
vpid = Channel->Vpid();
//XXX only when recording??? -> faster channel switching!
LircSend("SKY"); // makes sure the Digibox is "on"
diff --git a/channels.h b/channels.h
index 62b00c3c..a9fd18ec 100644
--- a/channels.h
+++ b/channels.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: channels.h 1.22 2004/10/31 12:54:26 kls Exp $
+ * $Id: channels.h 1.23 2004/12/05 13:49:04 kls Exp $
*/
#ifndef __CHANNELS_H
@@ -145,10 +145,10 @@ public:
int Srate(void) const { return srate; }
int Vpid(void) const { return vpid; }
int Ppid(void) const { return ppid; }
- int Apid1(void) const { return apids[0]; }
- int Apid2(void) const { return apids[1]; }
- int Dpid1(void) const { return dpids[0]; }
- int Dpid2(void) const { return dpids[1]; }
+ int Apid(int i) const { return (0 <= i && i < MAXAPIDS) ? apids[i] : 0; }
+ int Dpid(int i) const { return (0 <= i && i < MAXAPIDS) ? dpids[i] : 0; }
+ const char *Alang(int i) const { return (0 <= i && i < MAXAPIDS) ? alangs[i] : ""; }
+ const char *Dlang(int i) const { return (0 <= i && i < MAXAPIDS) ? dlangs[i] : ""; }
int Tpid(void) const { return tpid; }
int Ca(int Index = 0) const { return Index < MAXCAIDS ? caids[Index] : 0; }
int Nid(void) const { return nid; }
diff --git a/device.c b/device.c
index 5a750ca0..7c8d97cd 100644
--- a/device.c
+++ b/device.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: device.c 1.62 2004/10/30 14:53:38 kls Exp $
+ * $Id: device.c 1.63 2004/12/17 13:51:44 kls Exp $
*/
#include "device.h"
@@ -19,6 +19,87 @@
#include "status.h"
#include "transfer.h"
+// --- cPesAssembler ---------------------------------------------------------
+
+class cPesAssembler {
+private:
+ uchar *data;
+ uint32_t tag;
+ int length;
+ int size;
+ bool Realloc(int Size);
+public:
+ cPesAssembler(void);
+ ~cPesAssembler();
+ int ExpectedLength(void) { return data[4] * 256 + data[5] + 6; }
+ int Length(void) { return length; }
+ const uchar *Data(void) { return data; }
+ void Reset(void);
+ void Put(uchar c);
+ void Put(const uchar *Data, int Length);
+ bool IsPes(void);
+ };
+
+cPesAssembler::cPesAssembler(void)
+{
+ data = NULL;
+ size = 0;
+ Reset();
+}
+
+cPesAssembler::~cPesAssembler()
+{
+ free(data);
+}
+
+void cPesAssembler::Reset(void)
+{
+ tag = 0xFFFFFFFF;
+ length = 0;
+}
+
+bool cPesAssembler::Realloc(int Size)
+{
+ if (Size > size) {
+ size = max(Size, 2048);
+ data = (uchar *)realloc(data, size);
+ if (!data) {
+ esyslog("ERROR: can't allocate memory for PES assembler");
+ length = 0;
+ size = 0;
+ return false;
+ }
+ }
+ return true;
+}
+
+void cPesAssembler::Put(uchar c)
+{
+ if (!length) {
+ tag = (tag << 8) | c;
+ if ((tag & 0xFFFFFF00) == 0x00000100) {
+ if (Realloc(4)) {
+ *(uint32_t *)data = htonl(tag);
+ length = 4;
+ }
+ }
+ }
+ else if (Realloc(length + 1))
+ data[length++] = c;
+}
+
+void cPesAssembler::Put(const uchar *Data, int Length)
+{
+ while (!length && Length > 0) {
+ Put(*Data++);
+ Length--;
+ }
+ if (Length && Realloc(length + Length)) {
+ memcpy(data + length, Data, Length);
+ length += Length;
+ }
+}
+
// --- cDevice ---------------------------------------------------------------
// The default priority for non-primary devices:
@@ -53,6 +134,9 @@ cDevice::cDevice(void)
ciHandler = NULL;
player = NULL;
+ pesAssembler = new cPesAssembler;
+ ClrAvailableTracks();
+ currentAudioTrack = ttAudioFirst;
for (int i = 0; i < MAXRECEIVERS; i++)
receiver[i] = NULL;
@@ -74,6 +158,7 @@ cDevice::~cDevice()
delete patFilter;
delete eitFilter;
delete sectionHandler;
+ delete pesAssembler;
}
void cDevice::SetUseDevice(int n)
@@ -427,7 +512,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
if (CaDevice && CanReplay()) {
cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
if (CaDevice->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
- cControl::Launch(new cTransferControl(CaDevice, Channel->Vpid(), Channel->Apid1(), Channel->Apid2(), Channel->Dpid1(), Channel->Dpid2()));//XXX+
+ cControl::Launch(new cTransferControl(CaDevice, Channel->Vpid(), Channel->Apid(0), Channel->Apid(1), Channel->Dpid(0), Channel->Dpid(1)));
else
Result = scrNoTransfer;
}
@@ -482,17 +567,7 @@ void cDevice::SetVolumeDevice(int Volume)
{
}
-int cDevice::NumAudioTracksDevice(void) const
-{
- return 0;
-}
-
-const char **cDevice::GetAudioTracksDevice(int *CurrentTrack) const
-{
- return NULL;
-}
-
-void cDevice::SetAudioTrackDevice(int Index)
+void cDevice::SetAudioTrackDevice(eTrackType Type)
{
}
@@ -524,22 +599,72 @@ void cDevice::SetVolume(int Volume, bool Absolute)
}
}
+void cDevice::ClrAvailableTracks(void)
+{
+ memset(availableTracks, 0, sizeof(availableTracks));
+}
+
+bool cDevice::SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language, uint32_t Flags)
+{
+ eTrackType t = eTrackType(Type + Index);
+ if ((Type == ttAudio && IS_AUDIO_TRACK(t)) ||
+ (Type == ttDolby && IS_DOLBY_TRACK(t))) {
+ if (Language)
+ strn0cpy(availableTracks[t].language, Language, sizeof(availableTracks[t].language));
+ availableTracks[t].flags = Flags;
+ availableTracks[t].id = Id; // setting 'id' last to avoid the need for extensive locking
+ return true;
+ }
+ else
+ esyslog("ERROR: SetAvailableTrack called with invalid Type/Index (%d/%d)", Type, Index);
+ return false;
+}
+
+const tTrackId *cDevice::GetTrack(eTrackType Type)
+{
+ return (ttNone < Type && Type < ttMaxTrackTypes) ? &availableTracks[Type] : NULL;
+}
+
int cDevice::NumAudioTracks(void) const
{
- return player ? player->NumAudioTracks() : NumAudioTracksDevice();
+ int n = 0;
+ for (int i = ttAudioFirst; i <= ttDolbyLast; i++) {
+ if (availableTracks[i].id)
+ n++;
+ }
+ return n;
}
-const char **cDevice::GetAudioTracks(int *CurrentTrack) const
+bool cDevice::SetCurrentAudioTrack(eTrackType Type)
{
- return player ? player->GetAudioTracks(CurrentTrack) : GetAudioTracksDevice(CurrentTrack);
+ if (ttNone < Type && Type < ttDolbyLast) {
+ if (IS_DOLBY_TRACK(Type))
+ SetDigitalAudioDevice(true);
+ currentAudioTrack = Type;
+ if (player)
+ player->SetAudioTrack(currentAudioTrack, GetTrack(currentAudioTrack));
+ else
+ SetAudioTrackDevice(currentAudioTrack);
+ if (IS_AUDIO_TRACK(Type))
+ SetDigitalAudioDevice(false);
+ return true;
+ }
+ return false;
}
-void cDevice::SetAudioTrack(int Index)
+bool cDevice::IncCurrentAudioTrack(void)
{
- if (player)
- player->SetAudioTrack(Index);
- else
- SetAudioTrackDevice(Index);
+ int i = currentAudioTrack + 1;
+ for (;;) {
+ if (i > ttDolbyLast)
+ i = ttAudioFirst;
+ if (i == currentAudioTrack)
+ break;
+ if (availableTracks[i].id)
+ return SetCurrentAudioTrack(eTrackType(i));
+ i++;
+ }
+ return false;
}
bool cDevice::CanReplay(void) const
@@ -595,6 +720,7 @@ bool cDevice::AttachPlayer(cPlayer *Player)
if (CanReplay()) {
if (player)
Detach(player);
+ ClrAvailableTracks();
player = Player;
SetPlayMode(player->playMode);
player->device = this;
@@ -639,11 +765,105 @@ int cDevice::PlayVideo(const uchar *Data, int Length)
return -1;
}
-void cDevice::PlayAudio(const uchar *Data, int Length)
+int cDevice::PlayAudio(const uchar *Data, int Length)
{
- Audios.PlayAudio(Data, Length);
+ return -1;
+}
+
+int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly)
+{
+ bool FirstLoop = true;
+ uchar c = Data[3];
+ const uchar *Start = Data;
+ const uchar *End = Start + Length;
+ while (Start < End) {
+ int d = End - Start;
+ int w = d;
+ switch (c) {
+ case 0xE0 ... 0xEF: // video
+ w = PlayVideo(Start, d);
+ break;
+ case 0xC0 ... 0xDF: // audio
+ SetAvailableTrack(ttAudio, c - 0xC0, c);
+ if (!VideoOnly && c == availableTracks[currentAudioTrack].id)
+ w = PlayAudio(Start, d);
+ break;
+ case 0xBD: // dolby
+ SetAvailableTrack(ttDolby, 0, c);
+ if (!VideoOnly && c == availableTracks[currentAudioTrack].id) {
+ w = PlayAudio(Start, d);
+ if (FirstLoop)
+ Audios.PlayAudio(Data, Length);
+ }
+ break;
+ default:
+ ;//esyslog("ERROR: unexpected packet id %02X", c);
+ }
+ if (w > 0)
+ Start += w;
+ else if (w <= 0) {
+ if (Start != Data)
+ esyslog("ERROR: incomplete PES packet write!");
+ return Start == Data ? w : Start - Data;
+ }
+ FirstLoop = false;
+ }
+ return Length;
}
+int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
+{
+ if (!Data) {
+ pesAssembler->Reset();
+ return 0;
+ }
+ int Result = 0;
+ if (pesAssembler->Length()) {
+ // Make sure we have a complete PES header:
+ while (pesAssembler->Length() < 6 && Length > 0) {
+ pesAssembler->Put(*Data++);
+ Length--;
+ Result++;
+ }
+ if (pesAssembler->Length() < 6)
+ return Result; // Still no complete PES header - wait for more
+ int l = pesAssembler->ExpectedLength();
+ int Rest = min(l - pesAssembler->Length(), Length);
+ pesAssembler->Put(Data, Rest);
+ Data += Rest;
+ Length -= Rest;
+ Result += Rest;
+ if (pesAssembler->Length() < l)
+ return Result; // Still no complete PES packet - wait for more
+ // Now pesAssembler contains one complete PES packet.
+ int w = PlayPesPacket(pesAssembler->Data(), pesAssembler->Length(), VideoOnly);
+ if (w > 0)
+ pesAssembler->Reset();
+ return Result > 0 ? Result : w < 0 ? w : 0;
+ }
+ int i = 0;
+ while (i <= Length - 6) {
+ if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) {
+ int l = Data[i + 4] * 256 + Data[i + 5] + 6;
+ if (i + l > Length) {
+ // Store incomplete PES packet for later completion:
+ pesAssembler->Put(Data + i, Length - i);
+ return Length;
+ }
+ int w = PlayPesPacket(Data + i, l, VideoOnly);
+ if (w > 0)
+ i += l;
+ else if (w < 0)
+ return i == 0 ? w : i;
+ }
+ else
+ i++;
+ }
+ if (i < Length)
+ pesAssembler->Put(Data + i, Length - i);
+ return Length;
+ }
+
int cDevice::Ca(void) const
{
int ca = 0;
diff --git a/device.h b/device.h
index 16ec2c09..d4bd9f23 100644
--- a/device.h
+++ b/device.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: device.h 1.46 2004/10/30 14:49:56 kls Exp $
+ * $Id: device.h 1.47 2004/12/17 13:44:34 kls Exp $
*/
#ifndef __DEVICE_H
@@ -56,10 +56,36 @@ enum eVideoSystem { vsPAL,
vsNTSC
};
+enum eTrackType { ttNone,
+ ttAudio,
+ ttAudioFirst = ttAudio,
+ ttAudioLast = ttAudioFirst + 31/*XXX MAXAPIDS - 1*/,
+ ttDolby,
+ ttDolbyFirst = ttDolby,
+ ttDolbyLast = ttDolbyFirst + 31/*XXX MAXAPIDS - 1*/,
+ /* future...
+ ttSubtitle,
+ ttSubtitleFirst = ttSubtitle,
+ ttSubtitleLast = ttSubtitleFirst + 31,
+ */
+ ttMaxTrackTypes
+ };
+
+#define IS_AUDIO_TRACK(t) (ttAudioFirst <= (t) && (t) <= ttAudioLast)
+#define IS_DOLBY_TRACK(t) (ttDolbyFirst <= (t) && (t) <= ttDolbyLast)
+
+struct tTrackId {
+ uint16_t id; // The PES packet id or the PID.
+ char language[8]; // something like either "eng" or "deu/eng"
+ // for future use:
+ uint32_t flags; // Used to further identify the actual track.
+ };
+
class cChannel;
class cPlayer;
class cReceiver;
class cSpuDecoder;
+class cPesAssembler;
/// The cDevice class is the base from which actual devices can be derived.
@@ -283,6 +309,37 @@ public:
///< Returns the video system of the currently displayed material
///< (default is PAL).
+// Track facilities
+
+private:
+ tTrackId availableTracks[ttMaxTrackTypes];
+ eTrackType currentAudioTrack;
+protected:
+ virtual void SetAudioTrackDevice(eTrackType Type);
+ ///< Sets the current audio track to the given value.
+public:
+ void ClrAvailableTracks(void);
+ bool SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, uint32_t Flags = 0);
+ ///< Sets the track of the given Type and Index to the given values.
+ ///< Type must be one of the basic eTrackType values, like ttAudio or ttDolby.
+ ///< Index tells which track of the given basic type is meant.
+ ///< \return Returns true if the track was set correctly, false otherwise.
+ const tTrackId *GetTrack(eTrackType Type);
+ ///< Returns a pointer to the given track id, or NULL if Type is not
+ ///< less than ttMaxTrackTypes.
+ int NumAudioTracks(void) const;
+ ///< Returns the number of audio tracks that are currently available.
+ ///< This is just for information, to quickly find out whether there
+ ///< is more than one audio track.
+ eTrackType GetCurrentAudioTrack(void) { return currentAudioTrack; }
+ bool SetCurrentAudioTrack(eTrackType Type);
+ ///< Sets the current audio track to the given Type.
+ ///< \return Returns true if Type is a valid audio track, false otherwise.
+ bool IncCurrentAudioTrack(void);
+ ///< Sets the current audio track to the next available track (wraps to
+ ///< to the first one if necessary).
+ ///< \return Returns true if the audio track has been changed, false otherwise.
+
// Audio facilities
private:
@@ -291,27 +348,9 @@ private:
protected:
virtual void SetVolumeDevice(int Volume);
///< Sets the audio volume on this device (Volume = 0...255).
- virtual int NumAudioTracksDevice(void) const;
- ///< Returns the number of audio tracks that are currently available on this
- ///< device. The default return value is 0, meaning that this device
- ///< doesn't have multiple audio track capabilities. The return value may
- ///< change with every call and need not necessarily be the number of list
- ///< entries returned by GetAudioTracksDevice(). This function is mainly called to
- ///< decide whether there should be an "Audio" button in a menu.
- virtual const char **GetAudioTracksDevice(int *CurrentTrack = NULL) const;
- ///< Returns a list of currently available audio tracks. The last entry in the
- ///< list must be NULL. The number of entries does not necessarily have to be
- ///< the same as returned by a previous call to NumAudioTracksDevice().
- ///< If CurrentTrack is given, it will be set to the index of the current track
- ///< in the returned list. Note that the list must not be changed after it has
- ///< been returned by a call to GetAudioTracksDevice()! The only time the list may
- ///< change is *inside* the GetAudioTracksDevice() function.
- ///< By default the return value is NULL and CurrentTrack, if given, will not
- ///< have any meaning.
- virtual void SetAudioTrackDevice(int Index);
- ///< Sets the current audio track to the given value, which should be within the
- ///< range of the list returned by a previous call to GetAudioTracksDevice()
- ///< (otherwise nothing will happen).
+ virtual void SetDigitalAudioDevice(bool On) {}
+ ///< Tells the actual device that digital audio output shall be switched
+ ///< on or off.
public:
bool IsMute(void) const { return mute; }
bool ToggleMute(void);
@@ -320,32 +359,37 @@ public:
///< Sets the volume to the given value, either absolutely or relative to
///< the current volume.
static int CurrentVolume(void) { return primaryDevice ? primaryDevice->volume : 0; }//XXX???
- int NumAudioTracks(void) const;
- ///< Returns the number of audio tracks that are currently available on this
- ///< device or a player attached to it.
- const char **GetAudioTracks(int *CurrentTrack = NULL) const;
- ///< Returns a list of currently available audio tracks. The last entry in the
- ///< list is NULL. The number of entries does not necessarily have to be
- ///< the same as returned by a previous call to NumAudioTracks().
- ///< If CurrentTrack is given, it will be set to the index of the current track
- ///< in the returned list.
- ///< By default the return value is NULL and CurrentTrack, if given, will not
- ///< have any meaning.
- void SetAudioTrack(int Index);
- ///< Sets the current audio track to the given value, which should be within the
- ///< range of the list returned by a previous call to GetAudioTracks() (otherwise
- ///< nothing will happen).
// Player facilities
private:
cPlayer *player;
+ cPesAssembler *pesAssembler;
protected:
virtual bool CanReplay(void) const;
///< Returns true if this device can currently start a replay session.
virtual bool SetPlayMode(ePlayMode PlayMode);
///< Sets the device into the given play mode.
///< \return true if the operation was successful.
+ virtual int PlayVideo(const uchar *Data, int Length);
+ ///< Plays the given data block as video.
+ ///< Data points to exactly one complete PES packet of the given Length.
+ ///< PlayVideo() shall process the packet either as a whole (returning
+ ///< Length) or not at all (returning 0 or -1 and setting 'errno' to EAGAIN).
+ ///< \return Returns the number of bytes actually taken from Data, or -1
+ ///< in case of an error.
+ virtual int PlayAudio(const uchar *Data, int Length);
+ ///< Plays the given data block as audio.
+ ///< Data points to exactly one complete PES packet of the given Length.
+ ///< PlayAudio() shall process the packet either as a whole (returning
+ ///< Length) or not at all (returning 0 or -1 and setting 'errno' to EAGAIN).
+ ///< \return Returns the number of bytes actually taken from Data, or -1
+ ///< in case of an error.
+ virtual int PlayPesPacket(const uchar *Data, int Length, bool VideoOnly = false);
+ ///< Plays the single PES packet in Data with the given Length.
+ ///< If VideoOnly is true, only the video will be displayed,
+ ///< which is necessary for trick modes like 'fast forward'.
+ ///< Data must point to one single, complete PES packet.
public:
virtual int64_t GetSTC(void);
///< Gets the current System Time Counter, which can be used to
@@ -382,14 +426,16 @@ public:
///< If TimeoutMs is not zero, the device will wait up to the given
///< number of milliseconds before returning in case there is still
///< data in the buffers..
- virtual int PlayVideo(const uchar *Data, int Length);
- ///< Actually plays the given data block as video. The data must be
- ///< part of a PES (Packetized Elementary Stream) which can contain
- ///< one video and one audio stream.
- virtual void PlayAudio(const uchar *Data, int Length);
- ///< Plays additional audio streams, like Dolby Digital.
- ///< A derived class must call the base class function to make sure data
- ///< is distributed to all registered cAudio objects.
+ virtual int PlayPes(const uchar *Data, int Length, bool VideoOnly = false);
+ ///< Plays all valid PES packets in Data with the given Length.
+ ///< If Data is NULL any leftover data from a previous call will be
+ ///< discarded. If VideoOnly is true, only the video will be displayed,
+ ///< which is necessary for trick modes like 'fast forward'.
+ ///< Data should point to a sequence of complete PES packets. If the
+ ///< last packet in Data is not complete, it will be copied and combined
+ ///< to a complete packet with data from the next call to PlayPes().
+ ///< That way any functions called from within PlayPes() will be
+ ///< guaranteed to always receive complete PES packets.
bool Replaying(void) const;
///< Returns true if we are currently replaying.
void StopReplay(void);
diff --git a/dvbdevice.c b/dvbdevice.c
index 0c370c4f..ed3a710e 100644
--- a/dvbdevice.c
+++ b/dvbdevice.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbdevice.c 1.106 2004/11/27 10:24:47 kls Exp $
+ * $Id: dvbdevice.c 1.107 2004/12/17 14:19:48 kls Exp $
*/
#include "dvbdevice.h"
@@ -322,9 +322,9 @@ void cDvbTuner::Action(void)
cCiCaPmt CaPmt(channel.Source(), channel.Transponder(), channel.Sid(), ciHandler->GetCaSystemIds(Slot));
if (CaPmt.Valid()) {
CaPmt.AddPid(channel.Vpid(), 2);
- CaPmt.AddPid(channel.Apid1(), 4);
- CaPmt.AddPid(channel.Apid2(), 4);
- CaPmt.AddPid(channel.Dpid1(), 0);
+ CaPmt.AddPid(channel.Apid(0), 4);
+ CaPmt.AddPid(channel.Apid(1), 4);
+ CaPmt.AddPid(channel.Dpid(0), 0);
if (ciHandler->SetCaPmt(CaPmt, Slot)) {
tunerStatus = tsCam;
startTime = 0;
@@ -352,8 +352,8 @@ cDvbDevice::cDvbDevice(int n)
dvbTuner = NULL;
frontendType = fe_type_t(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
spuDecoder = NULL;
+ digitalAudio = false;
playMode = pmNone;
- aPid1 = aPid2 = 0;
// Devices that are present on all card types:
@@ -728,7 +728,7 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
result = hasPriority;
if (Priority >= 0 && Receiving(true)) {
if (dvbTuner->IsTunedTo(Channel)) {
- if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid1() && !HasPid(Channel->Apid1())) {
+ if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) {
#ifdef DO_MULTIPLE_RECORDINGS
if (Ca() > CACONFBASE || Channel->Ca() > CACONFBASE)
needsDetachReceivers = !ciHandler // only LL-firmware can do non-live CA channels
@@ -801,9 +801,13 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
// PID settings:
if (TurnOnLivePIDs) {
- aPid1 = Channel->Apid1();
- aPid2 = Channel->Apid2();
- if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(Channel->Vpid(), ptVideo) && AddPid(Channel->Apid1(), ptAudio))) {//XXX+ dolby dpid1!!! (if audio plugins are attached)
+ ClrAvailableTracks();
+ for (int i = 0; i < MAXAPIDS; i++) {
+ //XXX do this in cDevice???
+ SetAvailableTrack(ttAudio, i, Channel->Apid(i), Channel->Alang(i));
+ SetAvailableTrack(ttDolby, i, Channel->Dpid(i), Channel->Dlang(i));
+ }
+ if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(Channel->Vpid(), ptVideo) && AddPid(Channel->Apid(0), ptAudio))) {//XXX+ dolby dpid1!!! (if audio plugins are attached)
esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1);
return false;
}
@@ -815,7 +819,7 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
}
else if (StartTransferMode)
- cControl::Launch(new cTransferControl(this, Channel->Vpid(), Channel->Apid1(), Channel->Apid2(), Channel->Dpid1(), Channel->Dpid2()));
+ cControl::Launch(new cTransferControl(this, Channel->Vpid(), Channel->Apid(0), Channel->Apid(1), Channel->Dpid(0), Channel->Dpid(1)));
return true;
}
@@ -835,34 +839,32 @@ void cDvbDevice::SetVolumeDevice(int Volume)
}
}
-int cDvbDevice::NumAudioTracksDevice(void) const
-{
- int n = 0;
- if (aPid1)
- n++;
- if (Ca() <= MAXDEVICES && aPid2 && aPid1 != aPid2) // a CA recording session blocks switching live audio tracks
- n++;
- return n;
-}
-
-const char **cDvbDevice::GetAudioTracksDevice(int *CurrentTrack) const
+void cDvbDevice::SetDigitalAudioDevice(bool On)
{
- if (NumAudioTracksDevice()) {
- if (CurrentTrack)
- *CurrentTrack = (pidHandles[ptAudio].pid == aPid1) ? 0 : 1;
- static const char *audioTracks1[] = { "Audio 1", NULL };
- static const char *audioTracks2[] = { "Audio 1", "Audio 2", NULL };
- return NumAudioTracksDevice() > 1 ? audioTracks2 : audioTracks1;
+ if (digitalAudio != On) {
+ if (digitalAudio)
+ cCondWait::SleepMs(1000); // Wait until any leftover digital data has been flushed
+ SetVolumeDevice(On || IsMute() ? 0 : CurrentVolume());
+ digitalAudio = On;
}
- return NULL;
}
-void cDvbDevice::SetAudioTrackDevice(int Index)
+void cDvbDevice::SetAudioTrackDevice(eTrackType Type)
{
- if (0 <= Index && Index < NumAudioTracksDevice()) {
- int Pid = Index ? aPid2 : aPid1;
- pidHandles[ptAudio].pid = Pid;
- SetPid(&pidHandles[ptAudio], ptAudio, true);
+ const tTrackId *TrackId = GetTrack(Type);
+ if (TrackId && TrackId->id) {
+ if (IS_AUDIO_TRACK(Type)) {
+ pidHandles[ptAudio].pid = TrackId->id;
+ SetPid(&pidHandles[ptAudio], ptAudio, true);
+ }
+ else if (IS_DOLBY_TRACK(Type)) {
+ // Currently this works only in Transfer Mode
+ cChannel *Channel = Channels.GetByNumber(CurrentChannel());
+ if (Channel) {
+ SetChannelDevice(Channel, false);
+ cControl::Launch(new cTransferControl(this, Channel->Vpid(), Channel->Apid(0), Channel->Apid(1), Channel->Dpid(0), Channel->Dpid(1)));
+ }
+ }
}
}
@@ -1120,16 +1122,12 @@ bool cDvbDevice::Flush(int TimeoutMs)
int cDvbDevice::PlayVideo(const uchar *Data, int Length)
{
- int fd = (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video;
- if (fd >= 0)
- return write(fd, Data, Length);
- return -1;
+ return write(fd_video, Data, Length);
}
-void cDvbDevice::PlayAudio(const uchar *Data, int Length)
+int cDvbDevice::PlayAudio(const uchar *Data, int Length)
{
- //XXX actually this function will only be needed to implement replaying AC3 over the DVB card's S/PDIF
- cDevice::PlayAudio(Data, Length);
+ return write(fd_audio, Data, Length);
}
bool cDvbDevice::OpenDvr(void)
diff --git a/dvbdevice.h b/dvbdevice.h
index 1817f371..e0213990 100644
--- a/dvbdevice.h
+++ b/dvbdevice.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbdevice.h 1.30 2004/11/07 10:25:16 kls Exp $
+ * $Id: dvbdevice.h 1.31 2004/12/17 14:01:31 kls Exp $
*/
#ifndef __DVBDEVICE_H
@@ -90,15 +90,18 @@ public:
virtual void SetVideoFormat(bool VideoFormat16_9);
virtual eVideoSystem GetVideoSystem(void);
+// Track facilities
+
+protected:
+ virtual void SetAudioTrackDevice(eTrackType Type);
+
// Audio facilities
private:
- int aPid1, aPid2;
+ bool digitalAudio;
protected:
virtual void SetVolumeDevice(int Volume);
- virtual int NumAudioTracksDevice(void) const;
- virtual const char **GetAudioTracksDevice(int *CurrentTrack = NULL) const;
- virtual void SetAudioTrackDevice(int Index);
+ virtual void SetDigitalAudioDevice(bool On);
// Player facilities
@@ -106,6 +109,8 @@ protected:
ePlayMode playMode;
virtual bool CanReplay(void) const;
virtual bool SetPlayMode(ePlayMode PlayMode);
+ virtual int PlayVideo(const uchar *Data, int Length);
+ virtual int PlayAudio(const uchar *Data, int Length);
public:
virtual int64_t GetSTC(void);
virtual void TrickSpeed(int Speed);
@@ -116,8 +121,6 @@ public:
virtual void StillPicture(const uchar *Data, int Length);
virtual bool Poll(cPoller &Poller, int TimeoutMs = 0);
virtual bool Flush(int TimeoutMs = 0);
- virtual int PlayVideo(const uchar *Data, int Length);
- virtual void PlayAudio(const uchar *Data, int Length);
// Receiver facilities
diff --git a/dvbplayer.c b/dvbplayer.c
index 99a9aad9..aa9c04de 100644
--- a/dvbplayer.c
+++ b/dvbplayer.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbplayer.c 1.27 2004/11/27 10:07:05 kls Exp $
+ * $Id: dvbplayer.c 1.28 2004/12/11 17:02:40 kls Exp $
*/
#include "dvbplayer.h"
@@ -194,13 +194,10 @@ private:
ePlayDirs playDir;
int trickSpeed;
int readIndex, writeIndex;
- bool canToggleAudioTrack;
- uchar audioTrack;
cFrame *readFrame;
cFrame *playFrame;
void TrickSpeed(int Increment);
void Empty(void);
- void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
int Resume(void);
bool Save(void);
@@ -220,9 +217,6 @@ public:
void Goto(int Position, bool Still = false);
virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
- virtual int NumAudioTracks(void) const;
- virtual const char **GetAudioTracks(int *CurrentTrack = NULL) const;
- virtual void SetAudioTrack(int Index);
};
#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
@@ -245,8 +239,6 @@ cDvbPlayer::cDvbPlayer(const char *FileName)
playMode = pmPlay;
playDir = pdForward;
trickSpeed = NORMAL_SPEED;
- canToggleAudioTrack = false;
- audioTrack = 0xC0;
readIndex = writeIndex = -1;
readFrame = NULL;
playFrame = NULL;
@@ -312,41 +304,6 @@ void cDvbPlayer::Empty(void)
firstPacket = true;
}
-void cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except)
-{
- if (index) {
- for (int i = 0; i < Length - 6; i++) {
- if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
- uchar c = b[i + 3];
- int l = b[i + 4] * 256 + b[i + 5] + 6;
- switch (c) {
- case 0xBD: // dolby
- if (Except)
- PlayAudio(&b[i], l);
- // continue with deleting the data - otherwise it disturbs DVB replay
- case 0xC0 ... 0xC1: // audio
- if (c == 0xC1)
- canToggleAudioTrack = true;
- if (!Except || c != Except)
- memset(&b[i], 0x00, min(l, Length-i));
- break;
- case 0xE0 ... 0xEF: // video
- break;
- default:
- //esyslog("ERROR: unexpected packet id %02X", c);
- l = 0;
- }
- if (l)
- i += l - 1; // the loop increments, too!
- }
- /*XXX
- else
- esyslog("ERROR: broken packet header");
- XXX*/
- }
- }
-}
-
bool cDvbPlayer::NextFile(uchar FileNumber, int FileOffset)
{
if (FileNumber > 0)
@@ -413,7 +370,6 @@ void cDvbPlayer::Action(void)
nonBlockingFileReader = new cNonBlockingFileReader;
int Length = 0;
- int AudioTrack = 0; // -1 = any, 0 = none, >0 = audioTrack
running = true;
while (running && (NextFile() || readIndex >= 0 || ringBuffer->Available() || !DeviceFlush(100))) {
@@ -449,9 +405,6 @@ void cDvbPlayer::Action(void)
continue;
}
readIndex = Index;
- AudioTrack = 0;
- // must clear all audio packets because the buffer is not emptied
- // when falling back from "fast forward" to "play" (see above)
}
else if (index) {
uchar FileNumber;
@@ -462,12 +415,9 @@ void cDvbPlayer::Action(void)
eof = true;
continue;
}
- AudioTrack = audioTrack;
}
- else { // allows replay even if the index file is missing
+ else // allows replay even if the index file is missing
Length = MAXFRAMESIZE;
- AudioTrack = -1;
- }
if (Length == -1)
Length = MAXFRAMESIZE; // this means we read up to EOF (see cIndex)
else if (Length > MAXFRAMESIZE) {
@@ -478,8 +428,6 @@ void cDvbPlayer::Action(void)
}
int r = nonBlockingFileReader->Read(replayFile, b, Length);
if (r > 0) {
- if (AudioTrack == 0)
- StripAudioPackets(b, r);
readFrame = new cFrame(b, -r, ftUnknown, readIndex); // hands over b to the ringBuffer
b = NULL;
}
@@ -517,15 +465,14 @@ void cDvbPlayer::Action(void)
pc = playFrame->Count();
if (p) {
if (firstPacket) {
+ PlayPes(NULL, 0);
cRemux::SetBrokenLink(p, pc);
firstPacket = false;
}
- if (AudioTrack > 0)
- StripAudioPackets(p, pc, AudioTrack);
}
}
if (p) {
- int w = PlayVideo(p, pc);
+ int w = PlayPes(p, pc, playMode != pmPlay);
if (w > 0) {
p += w;
pc -= w;
@@ -717,7 +664,6 @@ void cDvbPlayer::Goto(int Index, bool Still)
if (r > 0) {
if (playMode == pmPause)
DevicePlay();
- StripAudioPackets(b, r);
DeviceStillPicture(b, r);
}
playMode = pmStill;
@@ -757,31 +703,6 @@ bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
return true;
}
-int cDvbPlayer::NumAudioTracks(void) const
-{
- return canToggleAudioTrack ? 2 : 1;
-}
-
-const char **cDvbPlayer::GetAudioTracks(int *CurrentTrack) const
-{
- if (NumAudioTracks()) {
- if (CurrentTrack)
- *CurrentTrack = (audioTrack == 0xC0) ? 0 : 1;
- static const char *audioTracks1[] = { "Audio 1", NULL };
- static const char *audioTracks2[] = { "Audio 1", "Audio 2", NULL };
- return NumAudioTracks() > 1 ? audioTracks2 : audioTracks1;
- }
- return NULL;
-}
-
-void cDvbPlayer::SetAudioTrack(int Index)
-{
- if ((audioTrack == 0xC0) != (Index == 0)) {
- audioTrack = (Index == 1) ? 0xC1 : 0xC0;
- Empty();
- }
-}
-
// --- cDvbPlayerControl -----------------------------------------------------
cDvbPlayerControl::cDvbPlayerControl(const char *FileName)
diff --git a/menu.c b/menu.c
index f482ad17..b5d57e5c 100644
--- a/menu.c
+++ b/menu.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.c 1.320 2004/11/20 10:49:17 kls Exp $
+ * $Id: menu.c 1.321 2004/12/12 16:07:05 kls Exp $
*/
#include "menu.h"
@@ -2471,15 +2471,8 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
state = replaying ? osContinue : osRecord;
break;
case kGreen: if (!HadSubMenu) {
- int CurrentAudioTrack = -1;
- const char **AudioTracks = cDevice::PrimaryDevice()->GetAudioTracks(&CurrentAudioTrack);
- if (AudioTracks) {
- const char **at = &AudioTracks[CurrentAudioTrack];
- if (!*++at)
- at = AudioTracks;
- cDevice::PrimaryDevice()->SetAudioTrack(at - AudioTracks);
- state = osEnd;
- }
+ cDevice::PrimaryDevice()->IncCurrentAudioTrack();
+ state = osEnd;
}
break;
case kYellow: if (!HadSubMenu)
@@ -2826,7 +2819,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
isyslog("record %s", fileName);
if (MakeDirs(fileName, true)) {
const cChannel *ch = timer->Channel();
- recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apid1(), ch->Apid2(), ch->Dpid1(), ch->Dpid2());
+ recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apid(0), ch->Apid(1), ch->Dpid(0), ch->Dpid(1));
if (device->AttachReceiver(recorder)) {
Recording.WriteSummary();
cStatus::MsgRecording(device, Recording.Name());
diff --git a/player.c b/player.c
index 2b306c0b..80e384fa 100644
--- a/player.c
+++ b/player.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: player.c 1.8 2004/11/20 11:33:08 kls Exp $
+ * $Id: player.c 1.9 2004/12/12 11:21:07 kls Exp $
*/
#include "player.h"
@@ -23,23 +23,14 @@ cPlayer::~cPlayer()
Detach();
}
-int cPlayer::PlayVideo(const uchar *Data, int Length)
+int cPlayer::PlayPes(const uchar *Data, int Length, bool VideoOnly)
{
if (device)
- return device->PlayVideo(Data, Length);
- esyslog("ERROR: attempt to use cPlayer::PlayVideo() without attaching to a cDevice!");
+ return device->PlayPes(Data, Length, VideoOnly);
+ esyslog("ERROR: attempt to use cPlayer::PlayPes() without attaching to a cDevice!");
return -1;
}
-void cPlayer::PlayAudio(const uchar *Data, int Length)
-{
- if (device) {
- device->PlayAudio(Data, Length);
- return;
- }
- esyslog("ERROR: attempt to use cPlayer::PlayAudio() without attaching to a cDevice!");
-}
-
void cPlayer::Detach(void)
{
if (device)
diff --git a/player.h b/player.h
index 8c5392ae..4ef675b7 100644
--- a/player.h
+++ b/player.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: player.h 1.12 2004/06/19 08:53:07 kls Exp $
+ * $Id: player.h 1.13 2004/12/12 11:20:19 kls Exp $
*/
#ifndef __PLAYER_H
@@ -19,6 +19,7 @@ private:
cDevice *device;
ePlayMode playMode;
protected:
+ bool DeviceSetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, uint32_t Flags = 0) { return device ? device->SetAvailableTrack(Type, Index, Id, Language, Flags) : false; }
bool DevicePoll(cPoller &Poller, int TimeoutMs = 0) { return device ? device->Poll(Poller, TimeoutMs) : false; }
bool DeviceFlush(int TimeoutMs = 0) { return device ? device->Flush(TimeoutMs) : true; }
void DeviceTrickSpeed(int Speed) { if (device) device->TrickSpeed(Speed); }
@@ -32,12 +33,10 @@ protected:
// This function is called right after the cPlayer has been attached to
// (On == true) or before it gets detached from (On == false) a cDevice.
// It can be used to do things like starting/stopping a thread.
- int PlayVideo(const uchar *Data, int Length);
- // Sends the given Data to the video device and returns the number of
- // bytes that have actually been accepted by the video device (or a
+ int PlayPes(const uchar *Data, int Length, bool VideoOnly = false);
+ // Sends the given PES Data to the device and returns the number of
+ // bytes that have actually been accepted by the device (or a
// negative value in case of an error).
- void PlayAudio(const uchar *Data, int Length);
- // Plays additional audio streams, like Dolby Digital.
public:
cPlayer(ePlayMode PlayMode = pmAudioVideo);
virtual ~cPlayer();
@@ -51,27 +50,10 @@ public:
// we are going forward or backward and 'Speed' is -1 if this is normal
// play/pause mode, 0 if it is single speed fast/slow forward/back mode
// and >0 if this is multi speed mode.
- virtual int NumAudioTracks(void) const { return 0; }
- // Returns the number of audio tracks that are currently available on this
- // player. The default return value is 0, meaning that this player
- // doesn't have multiple audio track capabilities. The return value may
- // change with every call and need not necessarily be the number of list
- // entries returned by GetAudioTracks(). This function is mainly called to
- // decide whether there should be an "Audio" button in a menu.
- virtual const char **GetAudioTracks(int *CurrentTrack = NULL) const { return NULL; }
- // Returns a list of currently available audio tracks. The last entry in the
- // list must be NULL. The number of entries does not necessarily have to be
- // the same as returned by a previous call to NumAudioTracks().
- // If CurrentTrack is given, it will be set to the index of the current track
- // in the returned list. Note that the list must not be changed after it has
- // been returned by a call to GetAudioTracks()! The only time the list may
- // change is *inside* the GetAudioTracks() function.
- // By default the return value is NULL and CurrentTrack, if given, will not
- // have any meaning.
- virtual void SetAudioTrack(int Index) {}
- // Sets the current audio track to the given value, which should be within the
- // range of the list returned by a previous call to GetAudioTracks()
- // (otherwise nothing will happen).
+ virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId) {}
+ // Sets the current audio track to the given value.
+ // This is just a virtual hook for players that need to do special things
+ // in order to switch audio tracks.
};
class cControl : public cOsdObject {
diff --git a/skinsttng.c b/skinsttng.c
index f80175f8..f1f79bf5 100644
--- a/skinsttng.c
+++ b/skinsttng.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: skinsttng.c 1.6 2004/07/18 11:32:42 kls Exp $
+ * $Id: skinsttng.c 1.7 2004/12/05 13:19:59 kls Exp $
*/
// Star Trek: The Next Generation® is a registered trademark of Paramount Pictures
@@ -233,14 +233,14 @@ void cSkinSTTNGDisplayChannel::SetChannel(const cChannel *Channel, int Number)
x -= bmEncrypted.Width() + d;
osd->DrawBitmap(x, y0 + (y1 - y0 - bmEncrypted.Height()) / 2, bmEncrypted, Theme.Color(Channel->Ca() ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor);
x -= bmDolbyDigital.Width() + d;
- osd->DrawBitmap(x, y0 + (y1 - y0 - bmDolbyDigital.Height()) / 2, bmDolbyDigital, Theme.Color(Channel->Dpid1() ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor);
+ osd->DrawBitmap(x, y0 + (y1 - y0 - bmDolbyDigital.Height()) / 2, bmDolbyDigital, Theme.Color(Channel->Dpid(0) ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor);
x -= bmAudio.Width() + d;
- osd->DrawBitmap(x, y0 + (y1 - y0 - bmAudio.Height()) / 2, bmAudio, Theme.Color(Channel->Apid2() ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor);
+ osd->DrawBitmap(x, y0 + (y1 - y0 - bmAudio.Height()) / 2, bmAudio, Theme.Color(Channel->Apid(1) ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor);
if (Channel->Vpid()) {
x -= bmTeletext.Width() + d;
osd->DrawBitmap(x, y0 + (y1 - y0 - bmTeletext.Height()) / 2, bmTeletext, Theme.Color(Channel->Tpid() ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor);
}
- else if (Channel->Apid1()) {
+ else if (Channel->Apid(0)) {
x -= bmRadio.Width() + d;
osd->DrawBitmap(x, y0 + (y1 - y0 - bmRadio.Height()) / 2, bmRadio, Theme.Color(clrChannelSymbolOn), frameColor);
}
diff --git a/transfer.c b/transfer.c
index d4ccee20..056299b2 100644
--- a/transfer.c
+++ b/transfer.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: transfer.c 1.18 2004/10/23 13:35:08 kls Exp $
+ * $Id: transfer.c 1.19 2004/11/28 11:51:00 kls Exp $
*/
#include "transfer.h"
@@ -20,8 +20,6 @@ cTransfer::cTransfer(int VPid, int APid1, int APid2, int DPid1, int DPid2)
{
ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer");
remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2);
- canToggleAudioTrack = false;
- audioTrack = 0xC0;
active = false;
}
@@ -60,8 +58,41 @@ void cTransfer::Action(void)
int PollTimeouts = 0;
uchar *p = NULL;
int Result = 0;
+// XXX Apparently this isn't necessary with the new PES data handling that
+// XXX was intorduced in VDR 1.3.18. If you do need this, enable the following
+// XXX line and send an email to kls@cadsoft.de. If nobody requires this, it
+// XXX will be removed later. kls 2004-12-27
+//#define FW_NEEDS_BUFFER_RESERVE_FOR_AC3
+#ifdef FW_NEEDS_BUFFER_RESERVE_FOR_AC3
+ bool Cleared = false;
+ bool GotBufferReserve = false;
+#endif
active = true;
while (active) {
+#ifdef FW_NEEDS_BUFFER_RESERVE_FOR_AC3
+#define HasDolby true
+ if (HasDolby) {
+ if (IsAttached() && !Cleared) {
+ PlayPes(NULL, 0);
+ Cleared = true;
+ }
+ //XXX For dolby we've to fill the buffer because the firmware does
+ //XXX not decode dolby but use a PCM stream for transport, therefore
+ //XXX the firmware has not enough buffer for noiseless skipping early
+ //XXX PCM samples (each dolby frame requires 6144 bytes in PCM and
+ //XXX audio is mostly to early in comparison to video).
+ //XXX To resolve this, the remuxer or PlayPes() should synchronize
+ //XXX audio with the video frames. 2004/09/09 Werner
+ if (!GotBufferReserve) {
+ if (ringBuffer->Available() < 3 * MAXFRAMESIZE / 2) {
+ cCondWait::SleepMs(20); // allow the buffer to collect some reserve
+ continue;
+ }
+ else
+ GotBufferReserve = true;
+ }
+ }
+#endif
int Count;
uchar *b = ringBuffer->Get(Count);
if (b) {
@@ -80,13 +111,13 @@ void cTransfer::Action(void)
if (Count)
ringBuffer->Del(Count);
}
- if (!p && (p = remux->Get(Result)) != NULL)
- StripAudioPackets(p, Result, audioTrack);
+ if (!p)
+ p = remux->Get(Result);
if (p) {
cPoller Poller;
if (DevicePoll(Poller, 100)) {
PollTimeouts = 0;
- int w = PlayVideo(p, Result);
+ int w = PlayPes(p, Result);
if (w > 0) {
p += w;
Result -= w;
@@ -112,64 +143,6 @@ void cTransfer::Action(void)
active = false;
}
-void cTransfer::StripAudioPackets(uchar *b, int Length, uchar Except)
-{
- for (int i = 0; i < Length - 6; i++) {
- if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
- uchar c = b[i + 3];
- int l = b[i + 4] * 256 + b[i + 5] + 6;
- switch (c) {
- case 0xBD: // dolby
- if (Except)
- PlayAudio(&b[i], l);
- // continue with deleting the data - otherwise it disturbs DVB replay
- case 0xC0 ... 0xC1: // audio
- if (c == 0xC1)
- canToggleAudioTrack = true;
- if (!Except || c != Except)
- memset(&b[i], 0x00, min(l, Length-i));
- break;
- case 0xE0 ... 0xEF: // video
- break;
- default:
- //esyslog("ERROR: unexpected packet id %02X", c);
- l = 0;
- }
- if (l)
- i += l - 1; // the loop increments, too!
- }
- /*XXX
- else
- esyslog("ERROR: broken packet header");
- XXX*/
- }
-}
-
-int cTransfer::NumAudioTracks(void) const
-{
- return canToggleAudioTrack ? 2 : 1;
-}
-
-const char **cTransfer::GetAudioTracks(int *CurrentTrack) const
-{
- if (NumAudioTracks()) {
- if (CurrentTrack)
- *CurrentTrack = (audioTrack == 0xC0) ? 0 : 1;
- static const char *audioTracks1[] = { "Audio 1", NULL };
- static const char *audioTracks2[] = { "Audio 1", "Audio 2", NULL };
- return NumAudioTracks() > 1 ? audioTracks2 : audioTracks1;
- }
- return NULL;
-}
-
-void cTransfer::SetAudioTrack(int Index)
-{
- if ((audioTrack == 0xC0) != (Index == 0)) {
- audioTrack = (Index == 1) ? 0xC1 : 0xC0;
- DeviceClear();
- }
-}
-
// --- cTransferControl ------------------------------------------------------
cDevice *cTransferControl::receiverDevice = NULL;
diff --git a/transfer.h b/transfer.h
index f33cb27b..de139705 100644
--- a/transfer.h
+++ b/transfer.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: transfer.h 1.5 2004/10/15 12:39:54 kls Exp $
+ * $Id: transfer.h 1.6 2004/11/28 11:51:37 kls Exp $
*/
#ifndef __TRANSFER_H
@@ -20,10 +20,7 @@ class cTransfer : public cReceiver, public cPlayer, public cThread {
private:
cRingBufferLinear *ringBuffer;
cRemux *remux;
- bool canToggleAudioTrack;
- uchar audioTrack;
bool active;
- void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
protected:
virtual void Activate(bool On);
virtual void Receive(uchar *Data, int Length);
@@ -31,9 +28,6 @@ protected:
public:
cTransfer(int VPid, int APid1, int APid2, int DPid1, int DPid2);
virtual ~cTransfer();
- virtual int NumAudioTracks(void) const;
- virtual const char **GetAudioTracks(int *CurrentTrack = NULL) const;
- virtual void SetAudioTrack(int Index);
};
class cTransferControl : public cControl {
diff --git a/vdr.c b/vdr.c
index c82f2b44..664cc7d3 100644
--- a/vdr.c
+++ b/vdr.c
@@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/vdr
*
- * $Id: vdr.c 1.193 2004/11/06 10:30:30 kls Exp $
+ * $Id: vdr.c 1.194 2004/12/05 13:20:29 kls Exp $
*/
#include <getopt.h>
@@ -532,7 +532,7 @@ int main(int argc, char *argv[])
static time_t lastTime = 0;
if (time(NULL) - lastTime > MINCHANNELWAIT) {
cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel());
- if (Channel && (Channel->Vpid() || Channel->Apid1())) {
+ if (Channel && (Channel->Vpid() || Channel->Apid(0))) {
if (!Channels.SwitchTo(cDevice::CurrentChannel()) // try to switch to the original channel...
&& !(LastTimerChannel > 0 && Channels.SwitchTo(LastTimerChannel)) // ...or the one used by the last timer...
&& !cDevice::SwitchChannel(1) // ...or the next higher available one...