diff options
Diffstat (limited to 'device.c')
-rw-r--r-- | device.c | 312 |
1 files changed, 291 insertions, 21 deletions
@@ -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.73 2005/01/09 12:36:48 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; } @@ -455,8 +540,35 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) } if (Result == scrOk) { - if (LiveView && IsPrimaryDevice()) + if (LiveView && IsPrimaryDevice()) { currentChannel = Channel->Number(); + // Set the available audio tracks: + ClrAvailableTracks(); + currentAudioTrack = ttAudioFirst; + for (int i = 0; i < MAXAPIDS; i++) { + SetAvailableTrack(ttAudio, i, Channel->Apid(i), Channel->Alang(i)); + if (Setup.UseDolbyDigital) + SetAvailableTrack(ttDolby, i, Channel->Dpid(i), Channel->Dlang(i)); + } + // Select the preferred audio track: + eTrackType PreferredTrack = ttAudioFirst; + int LanguagePreference = -1; + int StartCheck = Setup.CurrentDolby ? ttDolbyFirst : ttAudioFirst; + int EndCheck = ttDolbyLast; + for (int i = StartCheck; i <= EndCheck; i++) { + const tTrackId *TrackId = GetTrack(eTrackType(i)); + if (TrackId && TrackId->id && I18nIsPreferredLanguage(Setup.AudioLanguages, I18nLanguageIndex(TrackId->language), LanguagePreference)) + PreferredTrack = eTrackType(i); + if (Setup.CurrentDolby && i == ttDolbyLast) { + i = ttAudioFirst - 1; + EndCheck = ttAudioLast; + } + } + // Make sure we're set to an available audio track: + const tTrackId *Track = GetTrack(GetCurrentAudioTrack()); + if (!Track || !Track->id || PreferredTrack != GetCurrentAudioTrack()) + SetCurrentAudioTrack(PreferredTrack); + } cStatus::MsgChannelSwitch(this, Channel->Number()); // only report status if channel switch successfull } @@ -478,21 +590,24 @@ bool cDevice::HasProgramme(void) return Replaying() || pidHandles[ptAudio].pid || pidHandles[ptVideo].pid; } -void cDevice::SetVolumeDevice(int Volume) +int cDevice::GetAudioChannelDevice(void) { + return 0; } -int cDevice::NumAudioTracksDevice(void) const +void cDevice::SetAudioChannelDevice(int AudioChannel) { - return 0; } -const char **cDevice::GetAudioTracksDevice(int *CurrentTrack) const +void cDevice::SetVolumeDevice(int Volume) { - return NULL; } -void cDevice::SetAudioTrackDevice(int Index) +void cDevice::SetDigitalAudioDevice(bool On) +{ +} + +void cDevice::SetAudioTrackDevice(eTrackType Type) { } @@ -513,6 +628,18 @@ bool cDevice::ToggleMute(void) return mute; } +int cDevice::GetAudioChannel(void) +{ + int c = GetAudioChannelDevice(); + return (0 <= c && c <= 2) ? c : 0; +} + +void cDevice::SetAudioChannel(int AudioChannel) +{ + if (0 <= AudioChannel && AudioChannel <= 2) + SetAudioChannelDevice(AudioChannel); +} + void cDevice::SetVolume(int Volume, bool Absolute) { volume = min(max(Absolute ? Volume : volume + Volume, 0), MAXVOLUME); @@ -524,22 +651,66 @@ void cDevice::SetVolume(int Volume, bool Absolute) } } -int cDevice::NumAudioTracks(void) const +void cDevice::ClrAvailableTracks(bool DescriptionsOnly) { - return player ? player->NumAudioTracks() : NumAudioTracksDevice(); + if (DescriptionsOnly) { + for (int i = ttNone; i < ttMaxTrackTypes; i++) + *availableTracks[i].description = 0; + } + else + memset(availableTracks, 0, sizeof(availableTracks)); } -const char **cDevice::GetAudioTracks(int *CurrentTrack) const +bool cDevice::SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language, const char *Description, uint32_t Flags) { - return player ? player->GetAudioTracks(CurrentTrack) : GetAudioTracksDevice(CurrentTrack); + 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)); + 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 + esyslog("ERROR: SetAvailableTrack called with invalid Type/Index (%d/%d)", Type, Index); + return false; } -void cDevice::SetAudioTrack(int Index) +const tTrackId *cDevice::GetTrack(eTrackType Type) { - if (player) - player->SetAudioTrack(Index); - else - SetAudioTrackDevice(Index); + return (ttNone < Type && Type < ttMaxTrackTypes) ? &availableTracks[Type] : NULL; +} + +int cDevice::NumAudioTracks(void) const +{ + int n = 0; + for (int i = ttAudioFirst; i <= ttDolbyLast; i++) { + if (availableTracks[i].id) + n++; + } + return n; +} + +bool cDevice::SetCurrentAudioTrack(eTrackType Type) +{ + 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; } bool cDevice::CanReplay(void) const @@ -595,6 +766,7 @@ bool cDevice::AttachPlayer(cPlayer *Player) if (CanReplay()) { if (player) Detach(player); + ClrAvailableTracks(); player = Player; SetPlayMode(player->playMode); player->device = this; @@ -639,11 +811,107 @@ 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) +{ + return -1; +} + +int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly) { - Audios.PlayAudio(Data, Length); + 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 + if (Setup.UseDolbyDigital) { + 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; @@ -722,8 +990,8 @@ bool cDevice::Receiving(bool CheckAny) const void cDevice::Action(void) { + active = true; if (OpenDvr()) { - active = true; for (; active;) { // Read data from the DVR device: uchar *b = NULL; @@ -770,6 +1038,7 @@ bool cDevice::AttachReceiver(cReceiver *Receiver) esyslog("ERROR: device %d has no lock, can't attach receiver!", CardIndex() + 1); return false; } + cMutexLock MutexLock(&mutexReceiver); for (int i = 0; i < MAXRECEIVERS; i++) { if (!receiver[i]) { for (int n = 0; n < MAXRECEIVEPIDS; n++) { @@ -797,6 +1066,7 @@ void cDevice::Detach(cReceiver *Receiver) if (!Receiver || Receiver->device != this) return; bool receiversLeft = false; + cMutexLock MutexLock(&mutexReceiver); for (int i = 0; i < MAXRECEIVERS; i++) { if (receiver[i] == Receiver) { Receiver->Activate(false); |