diff options
Diffstat (limited to 'PLUGINS/src/dvbhddevice/dvbhdffdevice.c')
-rw-r--r-- | PLUGINS/src/dvbhddevice/dvbhdffdevice.c | 760 |
1 files changed, 760 insertions, 0 deletions
diff --git a/PLUGINS/src/dvbhddevice/dvbhdffdevice.c b/PLUGINS/src/dvbhddevice/dvbhdffdevice.c new file mode 100644 index 0000000..9263009 --- /dev/null +++ b/PLUGINS/src/dvbhddevice/dvbhdffdevice.c @@ -0,0 +1,760 @@ +/* + * dvbhdffdevice.c: The DVB HD Full Featured device interface + * + * See the README file for copyright information and how to reach the author. + * + * $Id: dvbhdffdevice.c 1.29 2011/04/17 11:20:22 kls Exp $ + */ + +#include "dvbhdffdevice.h" +#include <errno.h> +#include <limits.h> +#include <libsi/si.h> +#include <linux/videodev2.h> +#include <linux/dvb/audio.h> +#include <linux/dvb/dmx.h> +#include <linux/dvb/video.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <vdr/eitscan.h> +#include <vdr/transfer.h> +#include "hdffosd.h" +#include "setup.h" + +// --- cDvbHdFfDevice ---------------------------------------------------------- + +int cDvbHdFfDevice::devHdffOffset = -1; + +cDvbHdFfDevice::cDvbHdFfDevice(int Adapter, int Frontend) +:cDvbDevice(Adapter, Frontend) +{ + spuDecoder = NULL; + audioChannel = 0; + playMode = pmNone; + mHdffCmdIf = NULL; + + // Devices that are only present on cards with decoders: + + fd_osd = DvbOpen(DEV_DVB_OSD, adapter, frontend, O_RDWR); + fd_video = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDWR | O_NONBLOCK); + fd_audio = DvbOpen(DEV_DVB_AUDIO, adapter, frontend, O_RDWR | O_NONBLOCK); + + //TODO missing /dev/video offset calculation + + isHdffPrimary = false; + if (devHdffOffset < 0) { + devHdffOffset = adapter; + isHdffPrimary = true; + mHdffCmdIf = new HDFF::cHdffCmdIf(fd_osd); + mHdffCmdIf->CmdAvSetAudioDelay(gHdffSetup.AudioDelay); + mHdffCmdIf->CmdAvSetAudioDownmix((HDFF::eDownmixMode) gHdffSetup.AudioDownmix); + mHdffCmdIf->CmdMuxSetVideoOut((HDFF::eVideoOut) gHdffSetup.AnalogueVideo); + mHdffCmdIf->CmdHdmiSetVideoMode(gHdffSetup.GetVideoMode()); + HDFF::tHdmiConfig hdmiConfig; + hdmiConfig.TransmitAudio = true; + hdmiConfig.ForceDviMode = false; + hdmiConfig.CecEnabled = gHdffSetup.CecEnabled; + mHdffCmdIf->CmdHdmiConfigure(&hdmiConfig); + if (gHdffSetup.CecEnabled) + mHdffCmdIf->CmdHdmiSendCecCommand(HDFF::cecCommandTvOn); + mHdffCmdIf->CmdRemoteSetProtocol((HDFF::eRemoteProtocol) gHdffSetup.RemoteProtocol); + mHdffCmdIf->CmdRemoteSetAddressFilter(gHdffSetup.RemoteAddress >= 0, gHdffSetup.RemoteAddress); + } + + // Video format: + + SetVideoFormat(Setup.VideoFormat); +} + +cDvbHdFfDevice::~cDvbHdFfDevice() +{ + delete spuDecoder; + if (isHdffPrimary) + delete mHdffCmdIf; + // We're not explicitly closing any device files here, since this sometimes + // caused segfaults. Besides, the program is about to terminate anyway... +} + +void cDvbHdFfDevice::MakePrimaryDevice(bool On) +{ + if (On) + new cHdffOsdProvider(mHdffCmdIf); + cDvbDevice::MakePrimaryDevice(On); +} + +bool cDvbHdFfDevice::HasDecoder(void) const +{ + return isHdffPrimary; +} + +cSpuDecoder *cDvbHdFfDevice::GetSpuDecoder(void) +{ + if (!spuDecoder && IsPrimaryDevice()) + spuDecoder = new cDvbSpuDecoder(); + return spuDecoder; +} + +uchar *cDvbHdFfDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) +{ + //TODO + return NULL; +} + +void cDvbHdFfDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat) +{ + //TODO??? + cDevice::SetVideoDisplayFormat(VideoDisplayFormat); +} + +void cDvbHdFfDevice::SetVideoFormat(bool VideoFormat16_9) +{ + HDFF::tVideoFormat videoFormat; + videoFormat.AutomaticEnabled = true; + videoFormat.AfdEnabled = true; + videoFormat.TvFormat = (HDFF::eTvFormat) gHdffSetup.TvFormat; + videoFormat.VideoConversion = (HDFF::eVideoConversion) gHdffSetup.VideoConversion; + mHdffCmdIf->CmdAvSetVideoFormat(0, &videoFormat); +} + +eVideoSystem cDvbHdFfDevice::GetVideoSystem(void) +{ + eVideoSystem VideoSystem = vsPAL; + if (fd_video >= 0) { + video_size_t vs; + if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) { + if (vs.h == 480 || vs.h == 240) + VideoSystem = vsNTSC; + } + else + LOG_ERROR; + } + return VideoSystem; +} + +void cDvbHdFfDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect) +{ + if (fd_video >= 0) { + video_size_t vs; + if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) { + Width = vs.w; + Height = vs.h; + switch (vs.aspect_ratio) { + default: + case VIDEO_FORMAT_4_3: VideoAspect = 4.0 / 3.0; break; + case VIDEO_FORMAT_16_9: VideoAspect = 16.0 / 9.0; break; + case VIDEO_FORMAT_221_1: VideoAspect = 2.21; break; + } + return; + } + else + LOG_ERROR; + } + cDevice::GetVideoSize(Width, Height, VideoAspect); +} + +void cDvbHdFfDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect) +{ + gHdffSetup.GetOsdSize(Width, Height, PixelAspect); +} + +/*TODO obsolete? +bool cDvbHdFfDevice::SetAudioBypass(bool On) +{ + if (setTransferModeForDolbyDigital != 1) + return false; + return ioctl(fd_audio, AUDIO_SET_BYPASS_MODE, On) == 0; +} +TODO*/ + +bool cDvbHdFfDevice::SetPid(cPidHandle *Handle, int Type, bool On) +{ + if (Handle->pid) { + dmx_pes_filter_params pesFilterParams; + memset(&pesFilterParams, 0, sizeof(pesFilterParams)); + if (On) { + if (Handle->handle < 0) { + Handle->handle = DvbOpen(DEV_DVB_DEMUX, adapter, frontend, O_RDWR | O_NONBLOCK, true); + if (Handle->handle < 0) { + LOG_ERROR; + return false; + } + } + if (Type == ptPcr) + mHdffCmdIf->CmdAvSetPcrPid(0, Handle->pid); + else if (Type == ptVideo) { + if (Handle->streamType == 0x1B) + mHdffCmdIf->CmdAvSetVideoPid(0, Handle->pid, HDFF::videoStreamH264); + else + mHdffCmdIf->CmdAvSetVideoPid(0, Handle->pid, HDFF::videoStreamMpeg2); + } + else if (Type == ptAudio) + mHdffCmdIf->CmdAvSetAudioPid(0, Handle->pid, HDFF::audioStreamMpeg1); + else if (Type == ptDolby) + mHdffCmdIf->CmdAvSetAudioPid(0, Handle->pid, HDFF::audioStreamAc3); + if (!(Type <= ptDolby && Handle->used <= 1)) { + pesFilterParams.pid = Handle->pid; + pesFilterParams.input = DMX_IN_FRONTEND; + pesFilterParams.output = DMX_OUT_TS_TAP; + pesFilterParams.pes_type= DMX_PES_OTHER; + pesFilterParams.flags = DMX_IMMEDIATE_START; + if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) { + LOG_ERROR; + return false; + } + } + } + else if (!Handle->used) { + CHECK(ioctl(Handle->handle, DMX_STOP)); + if (Type == ptPcr) + mHdffCmdIf->CmdAvSetPcrPid(0, 0); + else if (Type == ptVideo) + mHdffCmdIf->CmdAvSetVideoPid(0, 0, HDFF::videoStreamMpeg2); + else if (Type == ptAudio) + mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF::audioStreamMpeg1); + else if (Type == ptDolby) + mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF::audioStreamAc3); + //TODO missing setting to 0x1FFF??? see cDvbDevice::SetPid() + close(Handle->handle); + Handle->handle = -1; + } + } + return true; +} + +void cDvbHdFfDevice::TurnOffLiveMode(bool LiveView) +{ + // Turn off live PIDs: + + DetachAll(pidHandles[ptAudio].pid); + DetachAll(pidHandles[ptVideo].pid); + DetachAll(pidHandles[ptPcr].pid); + DetachAll(pidHandles[ptTeletext].pid); + DelPid(pidHandles[ptAudio].pid); + DelPid(pidHandles[ptVideo].pid); + DelPid(pidHandles[ptPcr].pid, ptPcr); + DelPid(pidHandles[ptTeletext].pid); + DelPid(pidHandles[ptDolby].pid); +} + +bool cDvbHdFfDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) +{ + int apid = Channel->Apid(0); + int vpid = Channel->Vpid(); + int dpid = Channel->Dpid(0); + + bool DoTune = !IsTunedToTransponder(Channel); + + bool pidHandlesVideo = pidHandles[ptVideo].pid == vpid; + bool pidHandlesAudio = pidHandles[ptAudio].pid == apid; + + bool TurnOffLivePIDs = DoTune + || !IsPrimaryDevice() + || LiveView // for a new live view the old PIDs need to be turned off + || pidHandlesVideo // for recording the PIDs must be shifted from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER + ; + + bool StartTransferMode = IsPrimaryDevice() && !DoTune + && (LiveView && HasPid(vpid ? vpid : apid) && (!pidHandlesVideo || (!pidHandlesAudio && (dpid ? pidHandles[ptAudio].pid != dpid : true)))// the PID is already set as DMX_PES_OTHER + || !LiveView && (pidHandlesVideo || pidHandlesAudio) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER + ); + if (CamSlot() && !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), CamSlot()->SlotNumber())) + StartTransferMode |= LiveView && IsPrimaryDevice() && Channel->Ca() >= CA_ENCRYPTED_MIN; + + bool TurnOnLivePIDs = !StartTransferMode && LiveView; + + // Turn off live PIDs if necessary: + + if (TurnOffLivePIDs) + TurnOffLiveMode(LiveView); + + // Set the tuner: + + if (!cDvbDevice::SetChannelDevice(Channel, LiveView)) + return false; + + // If this channel switch was requested by the EITScanner we don't wait for + // a lock and don't set any live PIDs (the EITScanner will wait for the lock + // by itself before setting any filters): + + if (EITScanner.UsesDevice(this)) //XXX + return true; + + // PID settings: + + if (TurnOnLivePIDs) { + //SetAudioBypass(false);//TODO obsolete? + if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(vpid, ptVideo, Channel->Vtype()) && AddPid(apid, ptAudio))) { + esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1); + return false; + } + if (IsPrimaryDevice()) + AddPid(Channel->Tpid(), ptTeletext);//TODO obsolete? + } + else if (StartTransferMode) + cControl::Launch(new cTransferControl(this, Channel)); + + return true; +} + +int cDvbHdFfDevice::GetAudioChannelDevice(void) +{ + return audioChannel; +} + +void cDvbHdFfDevice::SetAudioChannelDevice(int AudioChannel) +{ + mHdffCmdIf->CmdAvSetAudioChannel(AudioChannel); + audioChannel = AudioChannel; +} + +void cDvbHdFfDevice::SetVolumeDevice(int Volume) +{ + mHdffCmdIf->CmdMuxSetVolume(Volume * 100 / 255); +} + +void cDvbHdFfDevice::SetDigitalAudioDevice(bool On) +{ + // not needed +} + +void cDvbHdFfDevice::SetAudioTrackDevice(eTrackType Type) +{ + //printf("SetAudioTrackDevice %d\n", Type); + const tTrackId *TrackId = GetTrack(Type); + if (TrackId && TrackId->id) { + if (IS_AUDIO_TRACK(Type)) { + if (pidHandles[ptAudio].pid && pidHandles[ptAudio].pid != TrackId->id) { + DetachAll(pidHandles[ptAudio].pid); + if (CamSlot()) + CamSlot()->SetPid(pidHandles[ptAudio].pid, false); + pidHandles[ptAudio].pid = TrackId->id; + SetPid(&pidHandles[ptAudio], ptAudio, true); + if (CamSlot()) { + CamSlot()->SetPid(pidHandles[ptAudio].pid, true); + CamSlot()->StartDecrypting(); + } + } + } + else if (IS_DOLBY_TRACK(Type)) { + pidHandles[ptDolby].pid = TrackId->id; + SetPid(&pidHandles[ptDolby], ptDolby, true); + } + } +} + +bool cDvbHdFfDevice::CanReplay(void) const +{ + return cDevice::CanReplay(); +} + +bool cDvbHdFfDevice::SetPlayMode(ePlayMode PlayMode) +{ + if (PlayMode == pmNone) { + mHdffCmdIf->CmdAvEnableVideoAfterStop(0, false); + mHdffCmdIf->CmdAvSetPcrPid(0, 0); + mHdffCmdIf->CmdAvSetVideoPid(0, 0, HDFF::videoStreamMpeg2); + mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF::audioStreamMpeg1); + + ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX); + mHdffCmdIf->CmdAvSetDecoderInput(0, 0); + mHdffCmdIf->CmdAvEnableSync(0, true); + mHdffCmdIf->CmdAvSetPlayMode(0, true); + } + else { + if (playMode == pmNone) + TurnOffLiveMode(true); + + mHdffCmdIf->CmdAvSetPlayMode(1, Transferring()); + mHdffCmdIf->CmdAvSetStc(0, 100000); + mHdffCmdIf->CmdAvEnableSync(0, true); + mHdffCmdIf->CmdAvEnableVideoAfterStop(0, true); + + playVideoPid = -1; + playAudioPid = -1; + audioCounter = 0; + videoCounter = 0; + + mHdffCmdIf->CmdAvSetDecoderInput(0, 2); + ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY); + } + playMode = PlayMode; + return true; +} + +int64_t cDvbHdFfDevice::GetSTC(void) +{ + if (fd_video >= 0) { + uint64_t pts; + if (ioctl(fd_video, VIDEO_GET_PTS, &pts) == -1) { + esyslog("ERROR: pts %d: %m", CardIndex() + 1); + return -1; + } + return pts; + } + if (fd_audio >= 0) { + uint64_t pts; + if (ioctl(fd_audio, AUDIO_GET_PTS, &pts) == -1) { + esyslog("ERROR: pts %d: %m", CardIndex() + 1); + return -1; + } + return pts; + } + return -1; +} + +void cDvbHdFfDevice::TrickSpeed(int Speed) +{ + mHdffCmdIf->CmdAvEnableSync(0, false); + mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF::audioStreamMpeg1); + playAudioPid = -1; + if (Speed > 0) + mHdffCmdIf->CmdAvSetVideoSpeed(0, 100 / Speed); +} + +void cDvbHdFfDevice::Clear(void) +{ + CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER)); + mHdffCmdIf->CmdAvSetVideoPid(0, 0, HDFF::videoStreamMpeg1); + mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF::audioStreamMpeg1); + playVideoPid = -1; + playAudioPid = -1; + cDevice::Clear(); +} + +void cDvbHdFfDevice::Play(void) +{ + mHdffCmdIf->CmdAvEnableSync(0, true); + mHdffCmdIf->CmdAvSetVideoSpeed(0, 100); + mHdffCmdIf->CmdAvSetAudioSpeed(0, 100); + cDevice::Play(); +} + +void cDvbHdFfDevice::Freeze(void) +{ + mHdffCmdIf->CmdAvSetVideoSpeed(0, 0); + mHdffCmdIf->CmdAvSetAudioSpeed(0, 0); + cDevice::Freeze(); +} + +void cDvbHdFfDevice::Mute(void) +{ + //TODO??? + cDevice::Mute(); +} + +static HDFF::eVideoStreamType MapVideoStreamTypes(int Vtype) +{ + switch (Vtype) { + case 0x01: return HDFF::videoStreamMpeg1; + case 0x02: return HDFF::videoStreamMpeg2; + case 0x1B: return HDFF::videoStreamH264; + default: return HDFF::videoStreamMpeg2; // fallback to MPEG2 + } +} + +void cDvbHdFfDevice::StillPicture(const uchar *Data, int Length) +{ + if (!Data || Length < TS_SIZE) + return; + if (Data[0] == 0x47) { + // TS data + cDevice::StillPicture(Data, Length); + } + else if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01 && (Data[3] & 0xF0) == 0xE0) { + // PES data + char *buf = MALLOC(char, Length); + if (!buf) + return; + int i = 0; + int blen = 0; + while (i < Length - 6) { + if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) { + int len = Data[i + 4] * 256 + Data[i + 5]; + if ((Data[i + 3] & 0xF0) == 0xE0) { // video packet + // skip PES header + int offs = i + 6; + // skip header extension + if ((Data[i + 6] & 0xC0) == 0x80) { + // MPEG-2 PES header + if (Data[i + 8] >= Length) + break; + offs += 3; + offs += Data[i + 8]; + len -= 3; + len -= Data[i + 8]; + if (len < 0 || offs + len > Length) + break; + } + else { + // MPEG-1 PES header + while (offs < Length && len > 0 && Data[offs] == 0xFF) { + offs++; + len--; + } + if (offs <= Length - 2 && len >= 2 && (Data[offs] & 0xC0) == 0x40) { + offs += 2; + len -= 2; + } + if (offs <= Length - 5 && len >= 5 && (Data[offs] & 0xF0) == 0x20) { + offs += 5; + len -= 5; + } + else if (offs <= Length - 10 && len >= 10 && (Data[offs] & 0xF0) == 0x30) { + offs += 10; + len -= 10; + } + else if (offs < Length && len > 0) { + offs++; + len--; + } + } + if (blen + len > Length) // invalid PES length field + break; + memcpy(&buf[blen], &Data[offs], len); + i = offs + len; + blen += len; + } + else if (Data[i + 3] >= 0xBD && Data[i + 3] <= 0xDF) // other PES packets + i += len + 6; + else + i++; + } + else + i++; + } + mHdffCmdIf->CmdAvShowStillImage(0, (uint8_t *)buf, blen, MapVideoStreamTypes(PatPmtParser()->Vtype())); + free(buf); + } + else { + // non-PES data + mHdffCmdIf->CmdAvShowStillImage(0, Data, Length, MapVideoStreamTypes(PatPmtParser()->Vtype())); + } +} + +bool cDvbHdFfDevice::Poll(cPoller &Poller, int TimeoutMs) +{ + Poller.Add(fd_video, true); + return Poller.Poll(TimeoutMs); +} + +bool cDvbHdFfDevice::Flush(int TimeoutMs) +{ + //TODO actually this function should wait until all buffered data has been processed by the card, but how? + return true; +} + +void cDvbHdFfDevice::BuildTsPacket(uint8_t * TsBuffer, bool PusiSet, uint16_t Pid, uint8_t Counter, const uint8_t * Data, uint32_t Length) +{ + TsBuffer[0] = 0x47; + TsBuffer[1] = PusiSet ? 0x40 : 0x00; + TsBuffer[1] |= Pid >> 8; + TsBuffer[2] = Pid & 0xFF; + if (Length >= 184) + { + TsBuffer[3] = 0x10 | Counter; + memcpy(TsBuffer + 4, Data, 184); + } + else + { + uint8_t adaptationLength; + + TsBuffer[3] = 0x30 | Counter; + adaptationLength = 183 - Length; + TsBuffer[4] = adaptationLength; + if (adaptationLength > 0) + { + TsBuffer[5] = 0x00; + memset(TsBuffer + 6, 0xFF, adaptationLength - 1); + } + memcpy(TsBuffer + 5 + adaptationLength, Data, Length); + } +} + +uint32_t cDvbHdFfDevice::PesToTs(uint8_t * TsBuffer, uint16_t Pid, uint8_t & Counter, const uint8_t * Data, uint32_t Length) +{ + uint32_t tsOffset; + uint32_t i; + + tsOffset = 0; + i = 0; + while (Length > 0) + { + BuildTsPacket(TsBuffer + tsOffset, i == 0, Pid, Counter, Data + i * 184, Length); + if (Length >= 184) + Length -= 184; + else + Length = 0; + Counter = (Counter + 1) & 15; + tsOffset += 188; + i++; + } + return tsOffset; +} + +int cDvbHdFfDevice::PlayVideo(const uchar *Data, int Length) +{ + //TODO: support greater Length + uint8_t tsBuffer[188 * 16]; + uint32_t tsLength; + int pid = 100; + + tsLength = PesToTs(tsBuffer, pid, videoCounter, Data, Length); + + if (pid != playVideoPid) { + playVideoPid = pid; + mHdffCmdIf->CmdAvSetVideoPid(0, playVideoPid, HDFF::videoStreamMpeg2, true); + } + if (WriteAllOrNothing(fd_video, tsBuffer, tsLength, 1000, 10) <= 0) + Length = 0; + return Length; +} + +int cDvbHdFfDevice::PlayAudio(const uchar *Data, int Length, uchar Id) +{ + uint8_t streamId; + uint8_t tsBuffer[188 * 16]; + uint32_t tsLength; + HDFF::eAudioStreamType streamType = HDFF::audioStreamMpeg1; + HDFF::eAVContainerType containerType = HDFF::avContainerPes; + int pid; + + streamId = Data[3]; + if (streamId >= 0xC0 && streamId <= 0xDF) + { + streamType = HDFF::audioStreamMpeg1; + } + else if (streamId == 0xBD) + { + const uint8_t * payload = Data + 9 + Data[8]; + if ((payload[0] & 0xF8) == 0xA0) + { + containerType = HDFF::avContainerPesDvd; + streamType = HDFF::audioStreamPcm; + } + else if ((payload[0] & 0xF8) == 0x88) + { + containerType = HDFF::avContainerPesDvd; + streamType = HDFF::audioStreamDts; + } + else if ((payload[0] & 0xF8) == 0x80) + { + containerType = HDFF::avContainerPesDvd; + streamType = HDFF::audioStreamAc3; + } + else + { + streamType = HDFF::audioStreamAc3; + } + } + pid = 200 + (int) streamType; + tsLength = PesToTs(tsBuffer, pid, audioCounter, Data, Length); + + if (pid != playAudioPid) { + playAudioPid = pid; + mHdffCmdIf->CmdAvSetAudioPid(0, playAudioPid, streamType, containerType); + } + if (WriteAllOrNothing(fd_video, tsBuffer, tsLength, 1000, 10) <= 0) + Length = 0; + return Length; +} + +int cDvbHdFfDevice::PlayTsVideo(const uchar *Data, int Length) +{ + int pid = TsPid(Data); + if (pid != playVideoPid) { + PatPmtParser(); + if (pid == PatPmtParser()->Vpid()) { + playVideoPid = pid; + mHdffCmdIf->CmdAvSetVideoPid(0, playVideoPid, MapVideoStreamTypes(PatPmtParser()->Vtype()), true); + } + } + return WriteAllOrNothing(fd_video, Data, Length, 1000, 10); +} + +static HDFF::eAudioStreamType MapAudioStreamTypes(int Atype) +{ + switch (Atype) { + case 0x03: return HDFF::audioStreamMpeg1; + case 0x04: return HDFF::audioStreamMpeg2; + case SI::AC3DescriptorTag: return HDFF::audioStreamAc3; + case SI::EnhancedAC3DescriptorTag: return HDFF::audioStreamEAc3; + case 0x0F: return HDFF::audioStreamAac; + case 0x11: return HDFF::audioStreamHeAac; + default: return HDFF::audioStreamMaxValue; // there is no HDFF::audioStreamNone + } +} + +int cDvbHdFfDevice::PlayTsAudio(const uchar *Data, int Length) +{ + int pid = TsPid(Data); + if (pid != playAudioPid) { + playAudioPid = pid; + int AudioStreamType = -1; + for (int i = 0; PatPmtParser()->Apid(i); i++) { + if (playAudioPid == PatPmtParser()->Apid(i)) { + AudioStreamType = PatPmtParser()->Atype(i); + break; + } + } + if (AudioStreamType < 0) { + for (int i = 0; PatPmtParser()->Dpid(i); i++) { + if (playAudioPid == PatPmtParser()->Dpid(i)) { + AudioStreamType = PatPmtParser()->Dtype(i); + break; + } + } + } + mHdffCmdIf->CmdAvSetAudioPid(0, playAudioPid, MapAudioStreamTypes(AudioStreamType)); + } + return WriteAllOrNothing(fd_video, Data, Length, 1000, 10); +} + +HDFF::cHdffCmdIf *cDvbHdFfDevice::GetHdffCmdHandler(void) +{ + //TODO why not just keep a pointer? + if (devHdffOffset >= 0) { + cDvbHdFfDevice *device = (cDvbHdFfDevice *)GetDevice(devHdffOffset); + if (device) + return device->mHdffCmdIf; + } + return NULL; +} + +// --- cDvbHdFfDeviceProbe --------------------------------------------------- + +bool cDvbHdFfDeviceProbe::Probe(int Adapter, int Frontend) +{ + static uint32_t SubsystemIds[] = { + 0x13C23009, // Technotrend S2-6400 HDFF + 0x00000000 + }; + cString FileName; + cReadLine ReadLine; + FILE *f = NULL; + uint32_t SubsystemId = 0; + FileName = cString::sprintf("/sys/class/dvb/dvb%d.frontend%d/device/subsystem_vendor", Adapter, Frontend); + if ((f = fopen(FileName, "r")) != NULL) { + if (char *s = ReadLine.Read(f)) + SubsystemId = strtoul(s, NULL, 0) << 16; + fclose(f); + } + FileName = cString::sprintf("/sys/class/dvb/dvb%d.frontend%d/device/subsystem_device", Adapter, Frontend); + if ((f = fopen(FileName, "r")) != NULL) { + if (char *s = ReadLine.Read(f)) + SubsystemId |= strtoul(s, NULL, 0); + fclose(f); + } + for (uint32_t *sid = SubsystemIds; *sid; sid++) { + if (*sid == SubsystemId) { + FileName = cString::sprintf("/dev/dvb/adapter%d/osd0", Adapter); + int fd = open(FileName, O_RDWR); + if (fd != -1) { //TODO treat the second path of the S2-6400 as a budget device + close(fd); + dsyslog("creating cDvbHdFfDevice"); + new cDvbHdFfDevice(Adapter, Frontend); + return true; + } + } + } + return false; +} |