diff options
author | Klaus Schmidinger <kls (at) cadsoft (dot) de> | 2011-04-17 16:50:00 +0200 |
---|---|---|
committer | Klaus Schmidinger <Klaus (dot) Schmidinger (at) tvdr (dot) de> | 2011-04-17 17:09:00 +0200 |
commit | 8b9350c091bc5ae8c9bb30b83a1fb567c3a7cb7f (patch) | |
tree | 51880b55c64c719561524fa4721d933b9205656e /PLUGINS/src/dvbhddevice/dvbhdffdevice.c | |
parent | 5619c0602bc623adeddf3385ada8c742aaea9762 (diff) | |
download | vdr-patches-8b9350c091bc5ae8c9bb30b83a1fb567c3a7cb7f.tar.gz vdr-patches-8b9350c091bc5ae8c9bb30b83a1fb567c3a7cb7f.tar.bz2 |
Version 1.7.18
- Changed -O2 to -O3 in Make.config.template (reported by Matti Lehtimäki).
- Added a missing 'default' case in cPixmapMemory::DrawEllipse().
- Fixed some direct comparisons of double values.
- Fixed detecting frames on channels that broadcast with separate "fields" instead
of complete frames.
- Made updating the editing marks during replay react faster in case the marks
file has just been written (with a patch from Udo Richter).
- Fixed horizontal scaling of subtitles (reported by Reinhard Nissl).
- Stripped the note "The data returned by this function is only used for informational
purposes (if any)" from the description of cDevice::GetVideoSize(). The VideoAspect
is now used to properly scale subtitles.
- Fixed cUnbufferedFile::Seek() in case it is compiled without USE_FADVISE (thanks
to Juergen Lock).
- Fixed the Language header of the Serbian translation file (thanks to Ville Skyttä).
- Added anti-aliasing when upscaling bitmaps, which improves the display of SD subtitles
when replayed on an HD OSD (thanks to Reinhard Nissl for his help in debugging).
- Renamed cBitmap::Scale() to Scaled(), because it doesn't modify the bitmap itself,
but rather returns a scaled copy.
- Fixed the description of cReceiver in PLUGINS.html, regarding detaching a receiver
from its device before deleting it (reported by Winfried Köhler). This change in
behavior was introduced in version 1.5.7.
- Fixed scaling subtitles in case the OSD size is exactly the same as the display
size of the subtitles.
- Added a missing initialization to sDvbSpuRect (reported by Sergiu Dotenco).
- Replaced "%lld" and "%llX" print format specifiers with "PRId64" and "PRIX64" to
avoid compiler warnings with gcc 4.5.2 (thanks to Sergiu Dotenco).
On a personal note: I find it a step in the totally wrong direction that there
have been macros introduced to work around this problem in the first place. There
should have been "real" format specifiers defined that address this. These macros
are nothing but an ugly workaround.
- Added Cancel(3) to ~cTrueColorDemo() in the "osddemo" plugin (thanks to Reinhard Nissl).
- Added a missing font deletion in cTrueColorDemo::Action() in the "osddemo" plugin
(thanks to Reinhard Nissl).
- Fixed a buffer overflow in cFont::Bidi() (thanks to Reinhard Nissl).
- Added HD stream content identifiers to vdr.5 (thanks to Christoph Haubrich).
- Made cRecordingInfo::Read(FILE *f) private to avoid calls to it from outside
cRecordingInfo or cRecording (reported by Mika Laitio).
- The dvbhddevice plugin is now part of the VDR distribution archive (thanks to
Andreas Regel).
- Removed an obsolete local variable in dvbsdffosd.c (thanks to Paul Menzel).
- Fixed a possible NULL pointer dereference in osddemo.c (reported by Paul Menzel).
- Now using pkg-config to get fribidi, freetype and fontconfig cflags and libs (thanks
to Ville Skyttä).
- The Makefile now also installs the include files (thanks to Ville Skyttä).
- Added handling of "ANSI/SCTE 57" descriptors (thanks too Rolf Ahrenberg).
- Avoiding an unecessary call to Recordings.ResetResume() (thanks to Reinhard
Nissl).
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; +} |