diff options
-rw-r--r-- | CONTRIBUTORS | 2 | ||||
-rw-r--r-- | HISTORY | 24 | ||||
-rw-r--r-- | PLUGINS.html | 62 | ||||
-rw-r--r-- | PLUGINS/src/sky/HISTORY | 4 | ||||
-rw-r--r-- | PLUGINS/src/sky/sky.c | 6 | ||||
-rw-r--r-- | channels.h | 10 | ||||
-rw-r--r-- | device.c | 266 | ||||
-rw-r--r-- | device.h | 136 | ||||
-rw-r--r-- | dvbdevice.c | 78 | ||||
-rw-r--r-- | dvbdevice.h | 17 | ||||
-rw-r--r-- | dvbplayer.c | 87 | ||||
-rw-r--r-- | menu.c | 15 | ||||
-rw-r--r-- | player.c | 17 | ||||
-rw-r--r-- | player.h | 36 | ||||
-rw-r--r-- | skinsttng.c | 8 | ||||
-rw-r--r-- | transfer.c | 101 | ||||
-rw-r--r-- | transfer.h | 8 | ||||
-rw-r--r-- | vdr.c | 4 |
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 @@ -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 © 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> </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> </td><td width=100%> +<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA> </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> </td><td width=100%> +<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00> </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> </td><td width=100%> +<!--X1.3.8--><table width=100%><tr><td bgcolor=#AA0000> </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> </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> </td><td width=100%> +<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA> </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> </td><td width=100%> +<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00> </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> </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 &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> </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> </td><td width=100%> +<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA> </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> </td><td width=100%> +<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00> </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> </td><td width=100%> +<!--X1.3.8--><table width=100%><tr><td bgcolor=#AA0000> </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> </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> </td><td width=100%> +<!--X1.3.0--><table width=100%><tr><td bgcolor=#0000AA> </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> </td><td width=100%> +<!--X1.3.7--><table width=100%><tr><td bgcolor=#00AA00> </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" @@ -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; } @@ -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; @@ -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) @@ -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()); @@ -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) @@ -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); } @@ -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; @@ -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 { @@ -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... |