diff --git a/CONTRIBUTORS b/CONTRIBUTORS index f6c27f9b..fb69037f 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -263,6 +263,8 @@ Werner Fink 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 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 © 2004 Klaus Schmidinger
www.cadsoft.de/vdr

-
  -Important modifications introduced in version 1.2.6 are marked like this. -
-
  +
  Important modifications introduced in version 1.3.0 are marked like this.
-
  +
  Important modifications introduced in version 1.3.7 are marked like this.
-
  +
  Important modifications introduced in version 1.3.8 are marked like this.
+
  +Important modifications introduced in version 1.3.18 are marked like this. +

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

  • Status monitor
  • Players
  • Receivers -
      +
     
  • Filters
  • The On Screen Display -
      +
     
  • Skins
  • Themes
  • @@ -1023,17 +1023,21 @@ public: Take a look at the files player.h and dvbplayer.c to see how VDR implements its own player for the VDR recordings.

    -To play the video data, the player needs to call its member function +
      +To play the actual data, the player needs to call its member function

    -int PlayVideo(const uchar *Data, int Length);
    +int PlayPes(const uchar *Data, int Length, bool VideoOnly);
     

    where Data points to a block of Length 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. +

    To avoid busy loops the player should call its member function

    @@ -1042,24 +1046,26 @@ bool DevicePoll(cPoller &Poller, int TimeoutMs = 0);
     
     to determine whether the device is ready for further data.
     

    -If the player can provide more than a single audio track, it can implement the -following functions to make them available: +
      +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:

    -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)
     

    -

    -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

    -void PlayAudio(const uchar *Data, int Length);
    +bool DeviceSetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, uint32_t Flags = 0)
     

    -where Data points to a complete audio PES packet of Length bytes. +See device.h for details about the parameters for track handling. +

    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). If the cReceiver isn't needed any more, it may simply be deleted and will automatically detach itself from the cDevice. -
      +
     

    Filters

    A Fistful of Datas

    @@ -1263,7 +1269,7 @@ and will automatically detach itself from the cDevice. See VDR/eit.c or VDR/pat.c to learn how to process filter data.

    -
      +
     

    The On Screen Display

    Window to the world

    @@ -1375,7 +1381,7 @@ new cMySkin; in the Start() function of your plugin. Do not delete this object, it will be automatically deleted when the program ends.

    -
      +
      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); -
      virtual int64_t GetSTC(void); -
    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);

    -
      +
     

    Section Filtering

    @@ -1579,7 +1583,7 @@ handle section data.

    On Screen Display

    -
      +
      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 cOsdProvider, which, when its CreateOsd() 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 @@ -14,7 +14,7 @@ #include #include -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 +void cDvbDevice::SetDigitalAudioDevice(bool On) { - 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 -{ - 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 @@ -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...