From 0b3a801ab463186519e9e1c14ea891924c64dff5 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 2 Jan 2005 15:11:44 +0100 Subject: [PATCH] Implemented audio track menu --- HISTORY | 16 ++- MANUAL | 26 +++-- PLUGINS.html | 3 + PLUGINS/src/skincurses/HISTORY | 3 +- PLUGINS/src/skincurses/skincurses.c | 70 +++++++++++- device.c | 27 ++--- device.h | 15 ++- dvbdevice.c | 8 +- eit.c | 32 +++++- epg.c | 165 +++++++++++++++++++++++++++- epg.h | 54 ++++++--- i18n.c | 23 +++- keys.c | 3 +- keys.h | 3 +- menu.c | 126 ++++++++++++++++++++- menu.h | 19 +++- player.h | 4 +- skinclassic.c | 86 ++++++++++++++- skinclassic.h | 3 +- skins.h | 18 ++- skinsttng.c | 141 +++++++++++++++++++++++- skinsttng.h | 3 +- status.c | 8 +- status.h | 6 +- transfer.c | 8 +- vdr.5 | 11 +- vdr.c | 12 +- 27 files changed, 799 insertions(+), 94 deletions(-) diff --git a/HISTORY b/HISTORY index 09f602f3..77ad4447 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-12-26: Version 1.3.18 +2005-01-02: Version 1.3.18 - Removed an unused variable from cTimer::GetWDayFromMDay() (thanks to Wayne Keer for reporting this one). @@ -3251,3 +3251,17 @@ Video Disk Recorder Revision History Zocholl for pointing out this problem). - Added cDevice::mutexReceiver to avoid a race condition when attaching/detaching receivers from different threads. +- The new remote control button "Audio" can be used to switch between different + audio tracks. The "Green" button in the "Main" menu has been changed from "Language" + to "Audio", since it now also controls switching between normal and Dolby Digital + audio tracks. +- The description of the audio tracks is now taken from the "component descriptors" + that are broadcast in the EPG data. However (as no big surprise), not all channels + actually provide useful data here, so there are now some additional EPG bugfixes, + which can be activated by setting the "EPG bugfix level" to 3. +- The format of the 'epg.data' files has been extended by the new tag 'X', which + contains the stream components of an event (see man vdr(5) for details). +- The cStatus class now has the new member function SetAudioTrack(), which can be + used to get notified when the audio track has been switched. +- Skins need to implement the new cSkinDisplayTrack class to display the audio + track menu. diff --git a/MANUAL b/MANUAL index f5a1814c..4125fe70 100644 --- a/MANUAL +++ b/MANUAL @@ -21,7 +21,7 @@ Version 1.2 Menu Menu on Menu off Menu off Menu off Menu off Menu off Menu on Back - Menu off VDR menu VDR menu Discard VDR menu Recordings menu Red - Record Edit Edit ABC/abc Play/Commands(2) Jump - Green - Language New New Ins/Ovr Rewind Skip -60s + Green - Audio New New Ins/Ovr Rewind Skip -60s Yellow - Pause live Delete Delete Delete Delete Skip +60s Blue - Stop/Resume Mark On/Off(1) - Summary Stop 0..9 Ch select - Sort(3) Day(4) Numeric inp. Exec cmd(2) Editing @@ -49,6 +49,8 @@ Version 1.2 Volume- volume down Mute mute + Audio select audio track + Schedule \ Channels | Timers | directly access the VDR @@ -187,15 +189,16 @@ Version 1.2 To bring up the channel display without switching channels you can press the "Ok" button. -* Selecting language specific audio track +* Selecting audio tracks - If the current channel provides different audio tracks (typically for - different languages), the "Green" button in the "VDR" menu can be pressed - to toggle between these. There can be two different audio PIDs per channel, - assuming that typically a channel broadcasts a country specific language - plus the movie's original soundtrack. - Recordings made form such channels will contain both audio tracks, and when - replaying the desired audio track can be selected the same way. + If the current channel or recording provides different audio tracks (for + different languages or Dolby Digital), the "Green" button in the "VDR" menu can + be pressed to bring up the "Audio" menu. Within this menu, the "Up" and "Down" + keys can be used to switch between the audio tracks. If your remote control has + a dedicated "Audio" button, the first press of that button brings up the "Audio" + menu, and every further press switches to the next available audio track. + The "Audio" menu will automatically disappear after 5 seconds of user inactivity, + or if any key other than the ones described above is pressed. * Switching through channel groups @@ -495,14 +498,15 @@ Version 1.2 A value of '0' completely turns off scanning on both single and multiple card systems. - EPG bugfix level = 2 Some tv stations transmit weirdly formatted EPG data. + EPG bugfix level = 3 Some tv stations transmit weirdly formatted EPG data. VDR attempts to fix these bugs up to the given level: 0 = no EPG fixing 1 = basic fixing of text location (Title, Episode and Extended Description) 2 = removal of excess whitespace and hyphens, mapping of wrongly used characters - Default is '2'. + 3 = fix stream component descriptions + Default is '3'. Note that after changing the setting of this parameter any EPG data that has already been received will remain in its existing format - only newly received data will diff --git a/PLUGINS.html b/PLUGINS.html index 878c3670..6897504b 100644 --- a/PLUGINS.html +++ b/PLUGINS.html @@ -1362,6 +1362,9 @@ public: virtual cSkinDisplayMenu *DisplayMenu(void); virtual cSkinDisplayReplay *DisplayReplay(bool ModeOnly); virtual cSkinDisplayVolume *DisplayVolume(void); +
  + virtual cSkinDisplayMessage *DisplayTrack(int NumTracks, const char * const *Tracks); +
virtual cSkinDisplayMessage *DisplayMessage(void); };

diff --git a/PLUGINS/src/skincurses/HISTORY b/PLUGINS/src/skincurses/HISTORY index 057fb6f8..8ba4ae4e 100644 --- a/PLUGINS/src/skincurses/HISTORY +++ b/PLUGINS/src/skincurses/HISTORY @@ -9,6 +9,7 @@ VDR Plugin 'skincurses' Revision History - Fixed some default parameters. -2004-12-26: Version 0.0.3 +2005-01-02: Version 0.0.3 - Made several functions threadsafe. +- New audio track display. diff --git a/PLUGINS/src/skincurses/skincurses.c b/PLUGINS/src/skincurses/skincurses.c index eeae1ef0..ee1df052 100644 --- a/PLUGINS/src/skincurses/skincurses.c +++ b/PLUGINS/src/skincurses/skincurses.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: skincurses.c 1.3 2004/12/26 11:36:35 kls Exp $ + * $Id: skincurses.c 1.4 2005/01/02 15:11:29 kls Exp $ */ #include @@ -558,6 +558,68 @@ void cSkinCursesDisplayVolume::Flush(void) osd->Flush(); } +// --- cSkinCursesDisplayTracks ---------------------------------------------- + +class cSkinCursesDisplayTracks : public cSkinDisplayTracks { +private: + cOsd *osd; + int itemsWidth; + int currentIndex; + void SetItem(const char *Text, int Index, bool Current); +public: + cSkinCursesDisplayTracks(const char *Title, int NumTracks, const char * const *Tracks); + virtual ~cSkinCursesDisplayTracks(); + virtual void SetTrack(int Index, const char * const *Tracks); + virtual void Flush(void); + }; + +cSkinCursesDisplayTracks::cSkinCursesDisplayTracks(const char *Title, int NumTracks, const char * const *Tracks) +{ + currentIndex = -1; + itemsWidth = Font.Width(Title); + for (int i = 0; i < NumTracks; i++) + itemsWidth = max(itemsWidth, Font.Width(Tracks[i])); + itemsWidth = min(itemsWidth, OsdWidth); + osd = new cCursesOsd(0, 0); + osd->DrawRectangle(0, 0, OsdWidth - 1, OsdHeight - 1, clrBackground); + osd->DrawText(0, 0, Title, clrBlack, clrCyan, &Font, itemsWidth); + for (int i = 0; i < NumTracks; i++) + SetItem(Tracks[i], i, false); +} + +cSkinCursesDisplayTracks::~cSkinCursesDisplayTracks() +{ + delete osd; +} + +void cSkinCursesDisplayTracks::SetItem(const char *Text, int Index, bool Current) +{ + int y = 1 + Index; + int ColorFg, ColorBg; + if (Current) { + ColorFg = clrBlack; + ColorBg = clrCyan; + currentIndex = Index; + } + else { + ColorFg = clrWhite; + ColorBg = clrBackground; + } + osd->DrawText(0, y, Text, ColorFg, ColorBg, &Font, itemsWidth); +} + +void cSkinCursesDisplayTracks::SetTrack(int Index, const char * const *Tracks) +{ + if (currentIndex >= 0) + SetItem(Tracks[currentIndex], currentIndex, false); + SetItem(Tracks[Index], Index, true); +} + +void cSkinCursesDisplayTracks::Flush(void) +{ + osd->Flush(); +} + // --- cSkinCursesDisplayMessage --------------------------------------------- class cSkinCursesDisplayMessage : public cSkinDisplayMessage { @@ -600,6 +662,7 @@ public: virtual cSkinDisplayMenu *DisplayMenu(void); virtual cSkinDisplayReplay *DisplayReplay(bool ModeOnly); virtual cSkinDisplayVolume *DisplayVolume(void); + virtual cSkinDisplayTracks *DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks); virtual cSkinDisplayMessage *DisplayMessage(void); }; @@ -633,6 +696,11 @@ cSkinDisplayVolume *cSkinCurses::DisplayVolume(void) return new cSkinCursesDisplayVolume; } +cSkinDisplayTracks *cSkinCurses::DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks) +{ + return new cSkinCursesDisplayTracks(Title, NumTracks, Tracks); +} + cSkinDisplayMessage *cSkinCurses::DisplayMessage(void) { return new cSkinCursesDisplayMessage; diff --git a/device.c b/device.c index 2df50dab..20394a59 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.64 2004/12/24 15:37:11 kls Exp $ + * $Id: device.c 1.65 2005/01/02 14:08:40 kls Exp $ */ #include "device.h" @@ -604,15 +604,19 @@ 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) +bool cDevice::SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language, const char *Description, 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 + if (Description) + strn0cpy(availableTracks[t].description, Description, sizeof(availableTracks[t].description)); + if (Id) { + availableTracks[t].flags = Flags; + availableTracks[t].id = Id; // setting 'id' last to avoid the need for extensive locking + } return true; } else @@ -652,21 +656,6 @@ bool cDevice::SetCurrentAudioTrack(eTrackType Type) return false; } -bool cDevice::IncCurrentAudioTrack(void) -{ - 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 { return HasDecoder(); diff --git a/device.h b/device.h index 2c147986..54e36bd2 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.48 2004/12/24 14:57:24 kls Exp $ + * $Id: device.h 1.49 2005/01/02 14:08:36 kls Exp $ */ #ifndef __DEVICE_H @@ -75,8 +75,9 @@ enum eTrackType { ttNone, #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" + uint16_t id; // The PES packet id or the PID. + char language[8]; // something like either "eng" or "deu/eng" + char description[32]; // something like "Dolby Digital 5.1" // for future use: uint32_t flags; // Used to further identify the actual track. }; @@ -319,10 +320,12 @@ protected: ///< 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); + bool SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, const char *Description = 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. + ///< If Id is 0 any existing id (and flags) will be left untouched and only the + ///< given Language and Description will be set. ///< \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 @@ -335,10 +338,6 @@ public: 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 diff --git a/dvbdevice.c b/dvbdevice.c index ed3a710e..7264d86f 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.107 2004/12/17 14:19:48 kls Exp $ + * $Id: dvbdevice.c 1.108 2005/01/02 11:51:18 kls Exp $ */ #include "dvbdevice.h" @@ -860,10 +860,8 @@ void cDvbDevice::SetAudioTrackDevice(eTrackType Type) 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))); - } + if (Channel) + SetChannelDevice(Channel, false); // this implicitly starts Transfer Mode } } } diff --git a/eit.c b/eit.c index 3d324561..a7d60c49 100644 --- a/eit.c +++ b/eit.c @@ -8,7 +8,7 @@ * Robert Schneider and Rolf Hakenes . * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg . * - * $Id: eit.c 1.101 2004/12/26 10:38:46 kls Exp $ + * $Id: eit.c 1.102 2005/01/02 11:52:12 kls Exp $ */ #include "eit.h" @@ -91,6 +91,8 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL; SI::ShortEventDescriptor *ShortEventDescriptor = NULL; cLinkChannels *LinkChannels = NULL; + int NumComponents = 0; + SI::ComponentDescriptor *ComponentDescriptors[MAXCOMPONENTS]; for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) { switch (d->getDescriptorTag()) { case SI::ExtendedEventDescriptorTag: { @@ -186,6 +188,20 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) } } break; + case SI::ComponentDescriptorTag: { + SI::ComponentDescriptor *cd = (SI::ComponentDescriptor *)d; + uchar Stream = cd->getStreamContent(); + uchar Type = cd->getComponentType(); + if (1 <= Stream && Stream <= 2 && Type != 0) { + if (NumComponents < MAXCOMPONENTS) { + ComponentDescriptors[NumComponents++] = cd; + d = NULL; // so that it is not deleted + } + else + dsyslog("more than %d component descriptors!", MAXCOMPONENTS); + } + } + break; default: ; } delete d; @@ -205,8 +221,20 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) delete ExtendedEventDescriptors; delete ShortEventDescriptor; - pEvent->FixEpgBugs(); + if (NumComponents > 0) { + cComponents *Components = new cComponents(NumComponents); + for (int i = 0; i < NumComponents; i++) { + char buffer[256]; + SI::ComponentDescriptor *cd = ComponentDescriptors[i]; + Components->SetComponent(i, cd->getStreamContent(), cd->getComponentType(), I18nNormalizeLanguageCode(cd->languageCode), cd->description.getText(buffer, sizeof(buffer))); + delete cd; + } + pEvent->SetComponents(Components); + } + else + pEvent->SetComponents(NULL); + pEvent->FixEpgBugs(); if (LinkChannels) channel->SetLinkChannels(LinkChannels); if (Tid == 0x4E) { // we trust only the present/following info on the actual TS diff --git a/epg.c b/epg.c index 2ea199c2..5539ab04 100644 --- a/epg.c +++ b/epg.c @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider and Rolf Hakenes . * - * $Id: epg.c 1.23 2004/12/26 11:32:01 kls Exp $ + * $Id: epg.c 1.24 2005/01/02 11:25:25 kls Exp $ */ #include "epg.h" @@ -16,6 +16,66 @@ #include #include +// --- tComponent ------------------------------------------------------------ + +cString tComponent::ToString(void) +{ + char buffer[256]; + snprintf(buffer, sizeof(buffer), "%X %02X %-3s %s", stream, type, language, description ? description : ""); + return buffer; +} + +bool tComponent::FromString(const char *s) +{ + unsigned int Stream, Type; + int n = sscanf(s, "%X %02X %3c %a[^\n]", &Stream, &Type, language, &description); + if (n != 4) + description = NULL; + else if (isempty(description)) { + free(description); + description = NULL; + } + stream = Stream; + type = Type; + return n >= 3; +} + +// --- cComponents ----------------------------------------------------------- + +cComponents::cComponents(int NumComponents) +{ + numComponents = NumComponents; + components = MALLOC(tComponent, numComponents); + memset(components, 0, sizeof(tComponent) * numComponents); +} + +cComponents::~cComponents(void) +{ + for (int i = 0; i < numComponents; i++) + free(components[i].description); + free(components); +} + +bool cComponents::SetComponent(int Index, const char *s) +{ + if (Index < numComponents) + return components[Index].FromString(s); + return false; +} + +bool cComponents::SetComponent(int Index, uchar Stream, uchar Type, const char *Language, const char *Description) +{ + if (Index < numComponents) { + tComponent *p = &components[Index]; + p->stream = Stream; + p->type = Type; + strn0cpy(p->language, Language, sizeof(p->language)); + p->description = strcpyrealloc(p->description, !isempty(Description) ? Description : NULL); + return true; + } + return false; +} + // --- cEvent ---------------------------------------------------------------- cEvent::cEvent(tChannelID ChannelID, u_int16_t EventID) @@ -28,6 +88,7 @@ cEvent::cEvent(tChannelID ChannelID, u_int16_t EventID) title = NULL; shortText = NULL; description = NULL; + components = NULL; startTime = 0; duration = 0; vps = 0; @@ -39,6 +100,7 @@ cEvent::~cEvent() free(title); free(shortText); free(description); + delete components; } int cEvent::Compare(const cListObject &ListObject) const @@ -85,6 +147,12 @@ void cEvent::SetDescription(const char *Description) description = strcpyrealloc(description, Description); } +void cEvent::SetComponents(cComponents *Components) +{ + delete components; + components = Components; +} + void cEvent::SetStartTime(time_t StartTime) { startTime = StartTime; @@ -168,6 +236,12 @@ void cEvent::Dump(FILE *f, const char *Prefix) const fprintf(f, "%sD %s\n", Prefix, description); strreplace(description, '|', '\n'); } + if (components) { + for (int i = 0; i < components->NumComponents(); i++) { + tComponent *p = components->Component(i); + fprintf(f, "%sX %s\n", Prefix, *p->ToString()); + } + } if (vps) fprintf(f, "%sV %ld\n", Prefix, vps); fprintf(f, "%se\n", Prefix); @@ -178,6 +252,8 @@ bool cEvent::Read(FILE *f, cSchedule *Schedule) { if (Schedule) { cEvent *Event = NULL; + int NumComponents = 0; + char *ComponentStrings[MAXCOMPONENTS]; char *s; cReadLine ReadLine; while ((s = ReadLine.Read(f)) != NULL) { @@ -199,6 +275,7 @@ bool cEvent::Read(FILE *f, cSchedule *Schedule) Event->SetDuration(Duration); } } + NumComponents = 0; } break; case 'T': if (Event) @@ -212,10 +289,26 @@ bool cEvent::Read(FILE *f, cSchedule *Schedule) Event->SetDescription(t); } break; + case 'X': if (Event) { + if (NumComponents < MAXCOMPONENTS) + ComponentStrings[NumComponents++] = strdup(t); + else + dsyslog("more than %d component descriptors!", MAXCOMPONENTS); + } + break; case 'V': if (Event) Event->SetVps(atoi(t)); break; - case 'e': Event = NULL; + case 'e': if (Event && NumComponents > 0) { + cComponents *Components = new cComponents(NumComponents); + for (int i = 0; i < NumComponents; i++) { + if (!Components->SetComponent(i, ComponentStrings[i])) + esyslog("ERROR: faulty component string in EPG data: '%s'", ComponentStrings[i]); + free(ComponentStrings[i]); + } + Event->SetComponents(Components); + } + Event = NULL; break; case 'c': // to keep things simple we react on 'c' here return true; @@ -228,7 +321,7 @@ bool cEvent::Read(FILE *f, cSchedule *Schedule) return false; } -#define MAXEPGBUGFIXSTATS 8 +#define MAXEPGBUGFIXSTATS 12 #define MAXEPGBUGFIXCHANS 100 struct tEpgBugFixStats { int hits; @@ -476,6 +569,72 @@ void cEvent::FixEpgBugs(void) strreplace(title, '`', '\''); strreplace(shortText, '`', '\''); strreplace(description, '`', '\''); + + if (Setup.EPGBugfixLevel <= 2) + return; + + // The stream components have a "description" field which some channels + // apparently have no idea of how to set correctly: + if (components) { + for (int i = 0; i < components->NumComponents(); i++) { + tComponent *p = components->Component(i); + switch (p->stream) { + case 0x01: { // video + if (p->description) { + if (strcasecmp(p->description, "Video") == 0 || + strcasecmp(p->description, "Bildformat") == 0) { + // Yes, we know it's video - that's what the 'stream' code + // is for! But _which_ video is it? + free(p->description); + p->description = NULL; + EpgBugFixStat(8, ChannelID()); + } + } + if (!p->description) { + switch (p->type) { + case 0x01: + case 0x05: p->description = strdup("4:3"); break; + case 0x02: + case 0x03: + case 0x06: + case 0x07: p->description = strdup("16:9"); break; + case 0x04: + case 0x08: p->description = strdup(">16:9"); break; + case 0x09: + case 0x0D: p->description = strdup("HD 4:3"); break; + case 0x0A: + case 0x0B: + case 0x0E: + case 0x0F: p->description = strdup("HD 16:9"); break; + case 0x0C: + case 0x10: p->description = strdup("HD >16:9"); break; + } + EpgBugFixStat(9, ChannelID()); + } + } + break; + case 0x02: { // audio + if (p->description) { + if (strcasecmp(p->description, "Audio") == 0) { + // Yes, we know it's audio - that's what the 'stream' code + // is for! But _which_ audio is it? + free(p->description); + p->description = NULL; + EpgBugFixStat(10, ChannelID()); + } + } + if (!p->description) { + switch (p->type) { + case 0x05: p->description = strdup("Dolby Digital"); break; + // all others will just display the language + } + EpgBugFixStat(11, ChannelID()); + } + } + break; + } + } + } } } diff --git a/epg.h b/epg.h index 0668c49c..88e66029 100644 --- a/epg.h +++ b/epg.h @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider and Rolf Hakenes . * - * $Id: epg.h 1.18 2004/12/26 11:31:27 kls Exp $ + * $Id: epg.h 1.19 2005/01/02 10:44:41 kls Exp $ */ #ifndef __EPG_H @@ -17,26 +17,50 @@ #include "thread.h" #include "tools.h" -#define MAXEPGBUGFIXLEVEL 2 +#define MAXEPGBUGFIXLEVEL 3 +#define MAXCOMPONENTS 32 enum eDumpMode { dmAll, dmPresent, dmFollowing, dmAtTime }; +struct tComponent { + uchar stream; + uchar type; + char language[4]; + char *description; + cString ToString(void); + bool FromString(const char *s); + }; + +class cComponents { +private: + int numComponents; + tComponent *components; +public: + cComponents(int NumComponents); + ~cComponents(void); + int NumComponents(void) const { return numComponents; } + bool SetComponent(int Index, const char *s); + bool SetComponent(int Index, uchar Stream, uchar Type, const char *Language, const char *Description); + tComponent *Component(int Index) const { return (Index < numComponents) ? &components[Index] : NULL; } + }; + class cSchedule; class cEvent : public cListObject { private: - tChannelID channelID; // Channel ID of program for this event - u_int16_t eventID; // Event ID of this event - uchar tableID; // Table ID this event came from - uchar version; // Version number of section this event came from - int runningStatus; // 0=undefined, 1=not running, 2=starts in a few seconds, 3=pausing, 4=running - char *title; // Title of this event - char *shortText; // Short description of this event (typically the episode name in case of a series) - char *description; // Description of this event - time_t startTime; // Start time of this event - int duration; // Duration of this event in seconds - time_t vps; // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL) - time_t seen; // When this event was last seen in the data stream + tChannelID channelID; // Channel ID of program for this event + u_int16_t eventID; // Event ID of this event + uchar tableID; // Table ID this event came from + uchar version; // Version number of section this event came from + int runningStatus; // 0=undefined, 1=not running, 2=starts in a few seconds, 3=pausing, 4=running + char *title; // Title of this event + char *shortText; // Short description of this event (typically the episode name in case of a series) + char *description; // Description of this event + cComponents *components; // The stream components of this event (separated by '\n') + time_t startTime; // Start time of this event + int duration; // Duration of this event in seconds + time_t vps; // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL) + time_t seen; // When this event was last seen in the data stream public: cEvent(tChannelID ChannelID, u_int16_t EventID); ~cEvent(); @@ -49,6 +73,7 @@ public: const char *Title(void) const { return title; } const char *ShortText(void) const { return shortText; } const char *Description(void) const { return description; } + const cComponents *Components(void) const { return components; } time_t StartTime(void) const { return startTime; } time_t EndTime(void) const { return startTime + duration; } int Duration(void) const { return duration; } @@ -67,6 +92,7 @@ public: void SetTitle(const char *Title); void SetShortText(const char *ShortText); void SetDescription(const char *Description); + void SetComponents(cComponents *Components); // Will take ownership of Components! void SetStartTime(time_t StartTime); void SetDuration(int Duration); void SetVps(time_t Vps); diff --git a/i18n.c b/i18n.c index 345ee348..8c5c0cb4 100644 --- a/i18n.c +++ b/i18n.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: i18n.c 1.166 2004/12/18 16:44:24 kls Exp $ + * $Id: i18n.c 1.167 2004/12/27 11:10:42 kls Exp $ * * Translations provided by: * @@ -4610,6 +4610,27 @@ const tI18nPhrase Phrases[] = { "Hääletu", "Sluk lyd", }, + { "Audio", + "Audio", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, // Miscellaneous: { "yes", "ja", diff --git a/keys.c b/keys.c index aaeee97e..8a8a104e 100644 --- a/keys.c +++ b/keys.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: keys.c 1.6 2004/12/19 11:25:47 kls Exp $ + * $Id: keys.c 1.7 2004/12/27 11:08:34 kls Exp $ */ #include "keys.h" @@ -44,6 +44,7 @@ static tKey keyTable[] = { // "Up" and "Down" must be the first two keys! { kVolUp, "Volume+" }, { kVolDn, "Volume-" }, { kMute, "Mute" }, + { kAudio, "Audio" }, { kSchedule, "Schedule" }, { kChannels, "Channels" }, { kTimers, "Timers" }, diff --git a/keys.h b/keys.h index eb4b5b34..09b2c8be 100644 --- a/keys.h +++ b/keys.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: keys.h 1.5 2002/12/14 15:49:42 kls Exp $ + * $Id: keys.h 1.6 2004/12/27 11:10:59 kls Exp $ */ #ifndef __KEYS_H @@ -38,6 +38,7 @@ enum eKeys { // "Up" and "Down" must be the first two keys! kVolUp, kVolDn, kMute, + kAudio, kSchedule, kChannels, kTimers, diff --git a/menu.c b/menu.c index 0c9aaf65..8815b92c 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.323 2004/12/26 12:18:29 kls Exp $ + * $Id: menu.c 1.324 2005/01/02 15:03:53 kls Exp $ */ #include "menu.h" @@ -2410,7 +2410,7 @@ void cMenuMain::Set(const char *Plugin) // Color buttons: - SetHelp(!replaying ? tr("Record") : NULL, cDevice::PrimaryDevice()->NumAudioTracks() > 1 ? tr("Language") : NULL, replaying ? NULL : tr("Pause"), replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Resume") : NULL); + SetHelp(!replaying ? tr("Record") : NULL, cDevice::PrimaryDevice()->NumAudioTracks() > 1 ? tr("Audio") : NULL, replaying ? NULL : tr("Pause"), replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Resume") : NULL); Display(); lastActivity = time(NULL); } @@ -2471,8 +2471,10 @@ eOSState cMenuMain::ProcessKey(eKeys Key) state = replaying ? osContinue : osRecord; break; case kGreen: if (!HadSubMenu) { - cDevice::PrimaryDevice()->IncCurrentAudioTrack(); - state = osEnd; + if (cDevice::PrimaryDevice()->NumAudioTracks() > 1) { + cRemote::Put(kAudio, true); + state = osEnd; + } } break; case kYellow: if (!HadSubMenu) @@ -2764,6 +2766,122 @@ eOSState cDisplayVolume::ProcessKey(eKeys Key) return timeout.TimedOut() ? osEnd : osContinue; } +// --- cDisplayTracks -------------------------------------------------------- + +#define TRACKTIMEOUT 5000 //ms + +cDisplayTracks *cDisplayTracks::currentDisplayTracks = NULL; + +cDisplayTracks::cDisplayTracks(void) +:cOsdObject(true) +{ + // Get the actual audio track descriptions from the EPG if we're not replaying: + if (!cDevice::PrimaryDevice()->Replaying() || cTransferControl::ReceiverDevice()) { + cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel()); + if (Channel) { + cSchedulesLock SchedulesLock; + const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); + if (Schedules) { + const cSchedule *Schedule = Schedules->GetSchedule(Channel->GetChannelID()); + if (Schedule) { + const cEvent *Present = Schedule->GetPresentEvent(true); + if (Present) { + const cComponents *Components = Present->Components(); + if (Components) { + int indexAudio = 0; + int indexDolby = 0; + for (int i = 0; i < Components->NumComponents(); i++) { + const tComponent *p = Components->Component(i); + if (p->stream == 2) { + if (p->type == 0x05) + cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, indexDolby++, 0, NULL, p->description); + else + cDevice::PrimaryDevice()->SetAvailableTrack(ttAudio, indexAudio++, 0, NULL, p->description); + } + } + } + } + } + } + } + } + + currentDisplayTracks = this; + numTracks = track = 0; + eTrackType CurrentAudioTrack = cDevice::PrimaryDevice()->GetCurrentAudioTrack(); + for (int i = ttAudioFirst; i <= ttDolbyLast; i++) { + const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(i)); + if (TrackId && TrackId->id) { + types[numTracks] = eTrackType(i); + descriptions[numTracks] = strdup(*TrackId->description ? TrackId->description : *TrackId->language ? TrackId->language : itoa(i)); + if (i == CurrentAudioTrack) + track = numTracks; + numTracks++; + } + } + timeout.Set(TRACKTIMEOUT); + displayTracks = Skins.Current()->DisplayTracks(tr("Audio"), numTracks, descriptions); + Show(); +} + +cDisplayTracks::~cDisplayTracks() +{ + delete displayTracks; + currentDisplayTracks = NULL; + for (int i = 0; i < numTracks; i++) + free(descriptions[i]); + cStatus::MsgOsdClear(); +} + +void cDisplayTracks::Show(void) +{ + displayTracks->SetTrack(track, descriptions); + displayTracks->Flush(); + cStatus::MsgSetAudioTrack(track, descriptions); +} + +cDisplayTracks *cDisplayTracks::Create(void) +{ + if (!currentDisplayTracks) + new cDisplayTracks; + return currentDisplayTracks; +} + +void cDisplayTracks::Process(eKeys Key) +{ + if (currentDisplayTracks) + currentDisplayTracks->ProcessKey(Key); +} + +eOSState cDisplayTracks::ProcessKey(eKeys Key) +{ + int oldTrack = track; + switch (Key) { + case kUp|k_Repeat: + case kUp: + case kDown|k_Repeat: + case kDown: + if (NORMALKEY(Key) == kUp && track > 0) + track--; + else if (NORMALKEY(Key) == kDown && track < numTracks - 1) + track++; + timeout.Set(TRACKTIMEOUT); + break; + case kAudio: + if (++track >= numTracks) + track = 0; + timeout.Set(TRACKTIMEOUT); + break; + case kNone: break; + default: return osEnd; + } + if (track != oldTrack) { + Show(); + cDevice::PrimaryDevice()->SetCurrentAudioTrack(types[track]); + } + return timeout.TimedOut() ? osEnd : osContinue; +} + // --- cRecordControl -------------------------------------------------------- cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) diff --git a/menu.h b/menu.h index 3b841363..08781800 100644 --- a/menu.h +++ b/menu.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.h 1.65 2004/12/19 17:59:47 kls Exp $ + * $Id: menu.h 1.66 2005/01/02 14:38:00 kls Exp $ */ #ifndef __MENU_H @@ -75,6 +75,23 @@ public: eOSState ProcessKey(eKeys Key); }; +class cDisplayTracks : public cOsdObject { +private: + cSkinDisplayTracks *displayTracks; + cTimeMs timeout; + eTrackType types[ttMaxTrackTypes]; + char *descriptions[ttMaxTrackTypes]; + int numTracks, track; + static cDisplayTracks *currentDisplayTracks; + virtual void Show(void); + cDisplayTracks(void); +public: + virtual ~cDisplayTracks(); + static cDisplayTracks *Create(void); + static void Process(eKeys Key); + eOSState ProcessKey(eKeys Key); + }; + class cMenuCam : public cOsdMenu { private: cCiMenu *ciMenu; diff --git a/player.h b/player.h index 4ef675b7..0f73d9b0 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.13 2004/12/12 11:20:19 kls Exp $ + * $Id: player.h 1.14 2004/12/30 10:44:34 kls Exp $ */ #ifndef __PLAYER_H @@ -19,7 +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 DeviceSetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, const char *Description = NULL, uint32_t Flags = 0) { return device ? device->SetAvailableTrack(Type, Index, Id, Language, Description, 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); } diff --git a/skinclassic.c b/skinclassic.c index 1a14f21a..b184bc5d 100644 --- a/skinclassic.c +++ b/skinclassic.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skinclassic.c 1.9 2004/12/26 11:33:53 kls Exp $ + * $Id: skinclassic.c 1.10 2005/01/02 14:41:08 kls Exp $ */ #include "skinclassic.h" @@ -502,6 +502,84 @@ void cSkinClassicDisplayVolume::Flush(void) osd->Flush(); } +// --- cSkinClassicDisplayTracks --------------------------------------------- + +class cSkinClassicDisplayTracks : public cSkinDisplayTracks { +private: + cOsd *osd; + int x0, x1; + int y0, y1, y2; + int lineHeight; + int currentIndex; + void SetItem(const char *Text, int Index, bool Current); +public: + cSkinClassicDisplayTracks(const char *Title, int NumTracks, const char * const *Tracks); + virtual ~cSkinClassicDisplayTracks(); + virtual void SetTrack(int Index, const char * const *Tracks); + virtual void Flush(void); + }; + +cSkinClassicDisplayTracks::cSkinClassicDisplayTracks(const char *Title, int NumTracks, const char * const *Tracks) +{ + const cFont *font = cFont::GetFont(fontOsd); + lineHeight = font->Height(); + currentIndex = -1; + int ItemsWidth = font->Width(Title); + for (int i = 0; i < NumTracks; i++) + ItemsWidth = max(ItemsWidth, font->Width(Tracks[i])); + ItemsWidth += 10; + x0 = 0; + x1 = Setup.OSDWidth; + int d = x1 - x0; + if (d > ItemsWidth) { + d = (d - ItemsWidth) & ~0x07; // must be multiple of 8 + x1 -= d; + } + y0 = 0; + y1 = lineHeight; + y2 = y1 + NumTracks * lineHeight; + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - y2); + tArea Areas[] = { { x0, y0, x1 - 1, y2 - 1, 4 } }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + osd->DrawText(x0, y0, Title, Theme.Color(clrMenuTitleFg), Theme.Color(clrMenuTitleBg), font, x1 - x0); + for (int i = 0; i < NumTracks; i++) + SetItem(Tracks[i], i, false); +} + +cSkinClassicDisplayTracks::~cSkinClassicDisplayTracks() +{ + delete osd; +} + +void cSkinClassicDisplayTracks::SetItem(const char *Text, int Index, bool Current) +{ + int y = y1 + Index * lineHeight; + tColor ColorFg, ColorBg; + if (Current) { + ColorFg = Theme.Color(clrMenuItemCurrentFg); + ColorBg = Theme.Color(clrMenuItemCurrentBg); + currentIndex = Index; + } + else { + ColorFg = Theme.Color(clrMenuItemSelectable); + ColorBg = Theme.Color(clrBackground); + } + const cFont *font = cFont::GetFont(fontOsd); + osd->DrawText(x0, y, Text, ColorFg, ColorBg, font, x1 - x0); +} + +void cSkinClassicDisplayTracks::SetTrack(int Index, const char * const *Tracks) +{ + if (currentIndex >= 0) + SetItem(Tracks[currentIndex], currentIndex, false); + SetItem(Tracks[Index], Index, true); +} + +void cSkinClassicDisplayTracks::Flush(void) +{ + osd->Flush(); +} + // --- cSkinClassicDisplayMessage -------------------------------------------- class cSkinClassicDisplayMessage : public cSkinDisplayMessage { @@ -571,6 +649,12 @@ cSkinDisplayVolume *cSkinClassic::DisplayVolume(void) return new cSkinClassicDisplayVolume; } + +cSkinDisplayTracks *cSkinClassic::DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks) +{ + return new cSkinClassicDisplayTracks(Title, NumTracks, Tracks); +} + cSkinDisplayMessage *cSkinClassic::DisplayMessage(void) { return new cSkinClassicDisplayMessage; diff --git a/skinclassic.h b/skinclassic.h index e990b588..2ac69b1b 100644 --- a/skinclassic.h +++ b/skinclassic.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skinclassic.h 1.1 2004/04/18 09:38:02 kls Exp $ + * $Id: skinclassic.h 1.2 2005/01/02 14:38:56 kls Exp $ */ #ifndef __SKINCLASSIC_H @@ -20,6 +20,7 @@ public: virtual cSkinDisplayMenu *DisplayMenu(void); virtual cSkinDisplayReplay *DisplayReplay(bool ModeOnly); virtual cSkinDisplayVolume *DisplayVolume(void); + virtual cSkinDisplayTracks *DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks); virtual cSkinDisplayMessage *DisplayMessage(void); }; diff --git a/skins.h b/skins.h index c20dece0..618fd61f 100644 --- a/skins.h +++ b/skins.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skins.h 1.4 2004/12/26 11:57:54 kls Exp $ + * $Id: skins.h 1.5 2005/01/02 14:36:19 kls Exp $ */ #ifndef __SKINS_H @@ -66,8 +66,6 @@ public: Red = Video options Green = Info now Yellow = Info next - Blue = Audio options - AudioOptions VideoOptions */ }; @@ -223,6 +221,14 @@ public: ///< indicator shall be displayed. }; +class cSkinDisplayTracks : public cSkinDisplay { + ///< This class implements the track display. +public: + virtual void SetTrack(int Index, const char * const *Tracks) = 0; + ///< Sets the current track to the one given by Index, which + ///< points into the Tracks array of strings. + }; + class cSkinDisplayMessage : public cSkinDisplay { ///< This class implements a simple message display. public: @@ -275,6 +281,12 @@ public: virtual cSkinDisplayVolume *DisplayVolume(void) = 0; ///< Creates and returns a new object for displaying the current volume. ///< The caller must delete the object after use. + virtual cSkinDisplayTracks *DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks) = 0; + ///< Creates and returns a new object for displaying the available tracks. + ///< NumTracks indicates how many entries in Tracks are available. + ///< Tracks will be valid throughout the entire lifetime of the returned + ///< cSkinDisplayTrack object. + ///< The caller must delete the object after use. virtual cSkinDisplayMessage *DisplayMessage(void) = 0; ///< Creates and returns a new object for displaying a message. ///< The caller must delete the object after use. diff --git a/skinsttng.c b/skinsttng.c index 844cd29f..8ae0180a 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.9 2004/12/26 11:34:29 kls Exp $ + * $Id: skinsttng.c 1.10 2005/01/02 14:41:49 kls Exp $ */ // Star Trek: The Next Generation® is a registered trademark of Paramount Pictures @@ -822,6 +822,140 @@ void cSkinSTTNGDisplayVolume::Flush(void) osd->Flush(); } +// --- cSkinSTTNGDisplayTracks ----------------------------------------------- + +class cSkinSTTNGDisplayTracks : public cSkinDisplayTracks { +private: + cOsd *osd; + int x0, x1, x2, x3, x4, x5, x6, x7; + int y0, y1, y2, y3, y4, y5, y6, y7; + int lineHeight; + tColor frameColor; + int currentIndex; + void SetItem(const char *Text, int Index, bool Current); +public: + cSkinSTTNGDisplayTracks(const char *Title, int NumTracks, const char * const *Tracks); + virtual ~cSkinSTTNGDisplayTracks(); + virtual void SetTrack(int Index, const char * const *Tracks); + virtual void Flush(void); + }; + +cSkinSTTNGDisplayTracks::cSkinSTTNGDisplayTracks(const char *Title, int NumTracks, const char * const *Tracks) +{ + const cFont *font = cFont::GetFont(fontOsd); + lineHeight = font->Height(); + frameColor = Theme.Color(clrMenuFrame); + currentIndex = -1; + int ItemsWidth = font->Width(Title); + for (int i = 0; i < NumTracks; i++) + ItemsWidth = max(ItemsWidth, font->Width(Tracks[i])); + ItemsWidth += 10; + x0 = 0; + x1 = lineHeight / 2; + x3 = (x1 + Roundness + Gap + 7) & ~0x07; // must be multiple of 8 + x2 = x3 - Gap; + x7 = Setup.OSDWidth; + x6 = x7 - lineHeight / 2; + x4 = (x6 - lineHeight / 2 - Gap) & ~0x07; // must be multiple of 8 + x5 = x4 + Gap; + int d = x4 - x3; + if (d > ItemsWidth) { + d = (d - ItemsWidth) & ~0x07; // must be multiple of 8 + x4 -= d; + x5 -= d; + x6 -= d; + x7 -= d; + } + y0 = 0; + y1 = lineHeight; + y2 = y1 + Roundness; + y3 = y2 + Gap; + // limit to Setup.OSDHeight? - what if height is too big??? + y4 = y3 + NumTracks * lineHeight + 2 * Roundness; + y5 = y4 + Gap; + y6 = y5 + Roundness; + y7 = y6 + cFont::GetFont(fontSml)->Height(); + int yt = (y0 + y1) / 2; + int yb = (y6 + y7) / 2; + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - y7); + tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 4 } }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else { + tArea Areas[] = { { x0, y0, x7 - 1, y3 - 1, 2 }, + { x0, y3, x3 - 1, y4 - 1, 1 }, + { x3, y3, x4 - 1, y4 - 1, 2 }, + { x4, y3, x7 - 1, y4 - 1, 2 }, + { x0, y4, x7 - 1, y7 - 1, 4 } + }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } + osd->DrawRectangle(x0, y0, x7 - 1, y7 - 1, Theme.Color(clrBackground)); + osd->DrawRectangle(x0, y0, x1 - 1, y1 - 1, clrTransparent); + osd->DrawRectangle(x0, y6, x1 - 1, y7 - 1, clrTransparent); + osd->DrawRectangle(x6, y0, x7 - 1, yt - 1, clrTransparent); + osd->DrawRectangle(x6, yb, x7 - 1, y7 - 1, clrTransparent); + osd->DrawEllipse (x0, y0, x1 - 1, y1 - 1, frameColor, 2); + osd->DrawRectangle(x1, y0, x2 - 1, y1 - 1, frameColor); + osd->DrawRectangle(x3, y0, x4 - 1, y1 - 1, frameColor); + osd->DrawRectangle(x5, y0, x6 - 1, y1 - 1, frameColor); + osd->DrawEllipse (x6, y0, x7 - 1, y1 - 1, frameColor, 5); + osd->DrawRectangle(x0, y1, x1 - 1, y6 - 1, frameColor); + osd->DrawEllipse (x1, y1, x2 - 1, y2 - 1, frameColor, -2); + osd->DrawEllipse (x1, y5, x2 - 1, y6 - 1, frameColor, -3); + osd->DrawEllipse (x0, y6, x1 - 1, y7 - 1, frameColor, 3); + osd->DrawRectangle(x1, y6, x2 - 1, y7 - 1, frameColor); + osd->DrawRectangle(x3, y6, x4 - 1, y7 - 1, frameColor); + osd->DrawRectangle(x5, y6, x6 - 1, y7 - 1, frameColor); + osd->DrawEllipse (x6, y6, x7 - 1, y7 - 1, frameColor, 5); + osd->DrawText(x3 + 5, y0, Title, Theme.Color(clrMenuTitle), frameColor, font, x4 - x3 - 5); + for (int i = 0; i < NumTracks; i++) + SetItem(Tracks[i], i, false); +} + +cSkinSTTNGDisplayTracks::~cSkinSTTNGDisplayTracks() +{ + delete osd; +} + +void cSkinSTTNGDisplayTracks::SetItem(const char *Text, int Index, bool Current) +{ + int y = y3 + Roundness + Index * lineHeight; + tColor ColorFg, ColorBg; + if (Current) { + ColorFg = Theme.Color(clrMenuItemCurrentFg); + ColorBg = Theme.Color(clrMenuItemCurrentBg); + osd->DrawEllipse (x1, y - Roundness, x2 - 1, y - 1, frameColor, -3); + osd->DrawRectangle(x1, y, x2 - 1, y + lineHeight - 1, frameColor); + osd->DrawEllipse (x1, y + lineHeight, x2 - 1, y + lineHeight + Roundness - 1, frameColor, -2); + osd->DrawRectangle(x3, y, x4 - 1, y + lineHeight - 1, ColorBg); + currentIndex = Index; + } + else { + ColorFg = Theme.Color(clrMenuItemSelectable); + ColorBg = Theme.Color(clrBackground); + if (currentIndex == Index) { + osd->DrawRectangle(x1, y - Roundness, x2 - 1, y + lineHeight + Roundness - 1, Theme.Color(clrBackground)); + osd->DrawRectangle(x3, y, x4 - 1, y + lineHeight - 1, Theme.Color(clrBackground)); + } + } + const cFont *font = cFont::GetFont(fontOsd); + int xt = x3 + 5; + osd->DrawText(xt, y, Text, ColorFg, ColorBg, font, x4 - xt); +} + +void cSkinSTTNGDisplayTracks::SetTrack(int Index, const char * const *Tracks) +{ + if (currentIndex >= 0) + SetItem(Tracks[currentIndex], currentIndex, false); + SetItem(Tracks[Index], Index, true); +} + +void cSkinSTTNGDisplayTracks::Flush(void) +{ + osd->Flush(); +} + // --- cSkinSTTNGDisplayMessage ---------------------------------------------- class cSkinSTTNGDisplayMessage : public cSkinDisplayMessage { @@ -909,6 +1043,11 @@ cSkinDisplayVolume *cSkinSTTNG::DisplayVolume(void) return new cSkinSTTNGDisplayVolume; } +cSkinDisplayTracks *cSkinSTTNG::DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks) +{ + return new cSkinSTTNGDisplayTracks(Title, NumTracks, Tracks); +} + cSkinDisplayMessage *cSkinSTTNG::DisplayMessage(void) { return new cSkinSTTNGDisplayMessage; diff --git a/skinsttng.h b/skinsttng.h index 82b25995..11a96422 100644 --- a/skinsttng.h +++ b/skinsttng.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skinsttng.h 1.1 2004/04/18 09:38:47 kls Exp $ + * $Id: skinsttng.h 1.2 2005/01/02 14:39:29 kls Exp $ */ #ifndef __SKINSTTNG_H @@ -20,6 +20,7 @@ public: virtual cSkinDisplayMenu *DisplayMenu(void); virtual cSkinDisplayReplay *DisplayReplay(bool ModeOnly); virtual cSkinDisplayVolume *DisplayVolume(void); + virtual cSkinDisplayTracks *DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks); virtual cSkinDisplayMessage *DisplayMessage(void); }; diff --git a/status.c b/status.c index 3dbe8543..a39c7acb 100644 --- a/status.c +++ b/status.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: status.c 1.5 2003/05/03 14:47:44 kls Exp $ + * $Id: status.c 1.6 2005/01/02 12:09:12 kls Exp $ */ #include "status.h" @@ -47,6 +47,12 @@ void cStatus::MsgSetVolume(int Volume, bool Absolute) sm->SetVolume(Volume, Absolute); } +void cStatus::MsgSetAudioTrack(int Index, const char * const *Tracks) +{ + for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) + sm->SetAudioTrack(Index, Tracks); +} + void cStatus::MsgOsdClear(void) { for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) diff --git a/status.h b/status.h index de0d9256..1d6494ac 100644 --- a/status.h +++ b/status.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: status.h 1.6 2003/05/03 14:43:18 kls Exp $ + * $Id: status.h 1.7 2005/01/02 12:08:12 kls Exp $ */ #ifndef __STATUS_H @@ -36,6 +36,9 @@ protected: virtual void SetVolume(int Volume, bool Absolute) {} // The volume has been set to the given value, either // absolutely or relative to the current volume. + virtual void SetAudioTrack(int Index, const char * const *Tracks) {} + // The audio track has been set to the one given by Index, which + // points into the Tracks array of strings. virtual void OsdClear(void) {} // The OSD has been cleared. virtual void OsdTitle(const char *Title) {} @@ -67,6 +70,7 @@ public: static void MsgRecording(const cDevice *Device, const char *Name); static void MsgReplaying(const cControl *Control, const char *Name); static void MsgSetVolume(int Volume, bool Absolute); + static void MsgSetAudioTrack(int Index, const char * const *Tracks); static void MsgOsdClear(void); static void MsgOsdTitle(const char *Title); static void MsgOsdStatusMessage(const char *Message); diff --git a/transfer.c b/transfer.c index 056299b2..97585760 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.19 2004/11/28 11:51:00 kls Exp $ + * $Id: transfer.c 1.20 2004/12/27 11:08:34 kls Exp $ */ #include "transfer.h" @@ -58,11 +58,7 @@ 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 +#define FW_NEEDS_BUFFER_RESERVE_FOR_AC3 #ifdef FW_NEEDS_BUFFER_RESERVE_FOR_AC3 bool Cleared = false; bool GotBufferReserve = false; diff --git a/vdr.5 b/vdr.5 index f1ce3c22..c84189bd 100644 --- a/vdr.5 +++ b/vdr.5 @@ -8,7 +8,7 @@ .\" License as specified in the file COPYING that comes with the .\" vdr distribution. .\" -.\" $Id: vdr.5 1.30 2004/12/18 12:40:47 kls Exp $ +.\" $Id: vdr.5 1.31 2005/01/02 13:57:10 kls Exp $ .\" .TH vdr 5 "19 Dec 2004" "1.3.18" "Video Disk Recorder Files" .SH NAME @@ -613,6 +613,7 @@ l l. \fBT\fR@ \fBS\fR@<short text> \fBD\fR@<description> +\fBX\fR@<stream> <type> <language> <descr> \fBV\fR@<vps time> \fBe\fR@ \fBc\fR@ @@ -622,8 +623,10 @@ Lowercase characters mark the end of a sequence that was started by the corresponding uppercase character. The outer frame consists of a sequence of one or more \fBC\fR...\fBc\fR (Channel) entries. Inside these any number of \fBE\fR...\fBe\fR (Event) entries are allowed. -The \fBT\fR, \fBS\fR and \fBD\fR entries are optional (although every event +All other tags are optional (although every event should at least have a \fBT\fR entry). +There may be several \fBX\fR tags, depending on the number of tracks (video, audio etc.) +the event provides. .TS tab (@); @@ -636,6 +639,10 @@ l l. <title> @is the title of the event <short text> @is the short text of the event (typically the name of the episode etc.) <description> @is the description of the event (any '|' characters will be interpreted as newlines) +<stream> @is the stream content (1 = video, 2 = audio) +<type> @is the stream type according to ETSI EN 300 468 +<language> @is the three letter language code +<descr> @is the description of this stream component <vps time> @is the Video Programming Service time of this event .TE diff --git a/vdr.c b/vdr.c index f49a89a1..a424b27c 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.196 2004/12/26 10:30:30 kls Exp $ + * $Id: vdr.c 1.197 2005/01/02 14:39:41 kls Exp $ */ #include <getopt.h> @@ -678,7 +678,7 @@ int main(int argc, char *argv[]) case kChanDn: cDevice::SwitchChannel(NORMALKEY(key) == kChanUp ? 1 : -1); break; - // Volume Control: + // Volume control: case kVolUp|k_Repeat: case kVolUp: case kVolDn|k_Repeat: @@ -697,6 +697,14 @@ int main(int argc, char *argv[]) cDisplayVolume::Process(key); key = kNone; // nobody else needs to see these keys break; + // Audio track control: + case kAudio: + if (!Menu && !cOsd::IsOpen()) + Menu = Temp = cDisplayTracks::Create(); + else + cDisplayTracks::Process(key); + key = kNone; + break; // Pausing live video: case kPause: if (!cControl::Control()) {