vdr  1.7.27
dvbsdffdevice.c
Go to the documentation of this file.
00001 /*
00002  * dvbsdffdevice.h: The DVB SD Full Featured device interface
00003  *
00004  * See the README file for copyright information and how to reach the author.
00005  *
00006  * $Id: dvbsdffdevice.c 2.33 2012/03/11 13:32:42 kls Exp $
00007  */
00008 
00009 #include "dvbsdffdevice.h"
00010 #include <errno.h>
00011 #include <limits.h>
00012 #include <linux/videodev2.h>
00013 #include <linux/dvb/audio.h>
00014 #include <linux/dvb/dmx.h>
00015 #include <linux/dvb/video.h>
00016 #include <sys/ioctl.h>
00017 #include <sys/mman.h>
00018 #include <vdr/eitscan.h>
00019 #include <vdr/transfer.h>
00020 #include "dvbsdffosd.h"
00021 
00022 // --- cDvbSdFfDevice --------------------------------------------------------
00023 
00024 int cDvbSdFfDevice::devVideoOffset = -1;
00025 
00026 cDvbSdFfDevice::cDvbSdFfDevice(int Adapter, int Frontend, bool OutputOnly)
00027 :cDvbDevice(Adapter, Frontend)
00028 {
00029   spuDecoder = NULL;
00030   digitalAudio = false;
00031   playMode = pmNone;
00032   outputOnly = OutputOnly;
00033 
00034   // Devices that are only present on cards with decoders:
00035 
00036   fd_osd      = DvbOpen(DEV_DVB_OSD,    adapter, frontend, O_RDWR);
00037   fd_video    = DvbOpen(DEV_DVB_VIDEO,  adapter, frontend, O_RDWR | O_NONBLOCK);
00038   fd_audio    = DvbOpen(DEV_DVB_AUDIO,  adapter, frontend, O_RDWR | O_NONBLOCK);
00039   fd_stc      = DvbOpen(DEV_DVB_DEMUX,  adapter, frontend, O_RDWR);
00040 
00041   // The offset of the /dev/video devices:
00042 
00043   if (devVideoOffset < 0) { // the first one checks this
00044      FILE *f = NULL;
00045      char buffer[PATH_MAX];
00046      for (int ofs = 0; ofs < 100; ofs++) {
00047          snprintf(buffer, sizeof(buffer), "/proc/video/dev/video%d", ofs);
00048          if ((f = fopen(buffer, "r")) != NULL) {
00049             if (fgets(buffer, sizeof(buffer), f)) {
00050                if (strstr(buffer, "DVB Board")) { // found the _first_ DVB card
00051                   devVideoOffset = ofs;
00052                   dsyslog("video device offset is %d", devVideoOffset);
00053                   break;
00054                   }
00055                }
00056             else
00057                break;
00058             fclose(f);
00059             }
00060          else
00061             break;
00062          }
00063      if (devVideoOffset < 0)
00064         devVideoOffset = 0;
00065      if (f)
00066         fclose(f);
00067      }
00068   devVideoIndex = devVideoOffset >= 0 ? devVideoOffset++ : -1;
00069 }
00070 
00071 cDvbSdFfDevice::~cDvbSdFfDevice()
00072 {
00073   delete spuDecoder;
00074   // We're not explicitly closing any device files here, since this sometimes
00075   // caused segfaults. Besides, the program is about to terminate anyway...
00076 }
00077 
00078 void cDvbSdFfDevice::MakePrimaryDevice(bool On)
00079 {
00080   if (On)
00081      new cDvbOsdProvider(fd_osd);
00082   cDvbDevice::MakePrimaryDevice(On);
00083 }
00084 
00085 bool cDvbSdFfDevice::HasDecoder(void) const
00086 {
00087   return true;
00088 }
00089 
00090 bool cDvbSdFfDevice::AvoidRecording(void) const
00091 {
00092   return true;
00093 }
00094 
00095 cSpuDecoder *cDvbSdFfDevice::GetSpuDecoder(void)
00096 {
00097   if (!spuDecoder && IsPrimaryDevice())
00098      spuDecoder = new cDvbSpuDecoder();
00099   return spuDecoder;
00100 }
00101 
00102 uchar *cDvbSdFfDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY)
00103 {
00104   if (devVideoIndex < 0)
00105      return NULL;
00106   char buffer[PATH_MAX];
00107   snprintf(buffer, sizeof(buffer), "%s%d", DEV_VIDEO, devVideoIndex);
00108   int videoDev = open(buffer, O_RDWR);
00109   if (videoDev >= 0) {
00110      uchar *result = NULL;
00111      // set up the size and RGB
00112      v4l2_format fmt;
00113      memset(&fmt, 0, sizeof(fmt));
00114      fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00115      fmt.fmt.pix.width = SizeX;
00116      fmt.fmt.pix.height = SizeY;
00117      fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24;
00118      fmt.fmt.pix.field = V4L2_FIELD_ANY;
00119      if (ioctl(videoDev, VIDIOC_S_FMT, &fmt) == 0) {
00120         v4l2_requestbuffers reqBuf;
00121         memset(&reqBuf, 0, sizeof(reqBuf));
00122         reqBuf.count = 2;
00123         reqBuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00124         reqBuf.memory = V4L2_MEMORY_MMAP;
00125         if (ioctl(videoDev, VIDIOC_REQBUFS, &reqBuf) >= 0) {
00126            v4l2_buffer mbuf;
00127            memset(&mbuf, 0, sizeof(mbuf));
00128            mbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00129            mbuf.memory = V4L2_MEMORY_MMAP;
00130            if (ioctl(videoDev, VIDIOC_QUERYBUF, &mbuf) == 0) {
00131               int msize = mbuf.length;
00132               unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0);
00133               if (mem && mem != (unsigned char *)-1) {
00134                  v4l2_buffer buf;
00135                  memset(&buf, 0, sizeof(buf));
00136                  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00137                  buf.memory = V4L2_MEMORY_MMAP;
00138                  buf.index = 0;
00139                  if (ioctl(videoDev, VIDIOC_QBUF, &buf) == 0) {
00140                     v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00141                     if (ioctl (videoDev, VIDIOC_STREAMON, &type) == 0) {
00142                        memset(&buf, 0, sizeof(buf));
00143                        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00144                        buf.memory = V4L2_MEMORY_MMAP;
00145                        buf.index = 0;
00146                        if (ioctl(videoDev, VIDIOC_DQBUF, &buf) == 0) {
00147                           if (ioctl(videoDev, VIDIOC_STREAMOFF, &type) == 0) {
00148                              // make RGB out of BGR:
00149                              int memsize = fmt.fmt.pix.width * fmt.fmt.pix.height;
00150                              unsigned char *mem1 = mem;
00151                              for (int i = 0; i < memsize; i++) {
00152                                  unsigned char tmp = mem1[2];
00153                                  mem1[2] = mem1[0];
00154                                  mem1[0] = tmp;
00155                                  mem1 += 3;
00156                                  }
00157 
00158                              if (Quality < 0)
00159                                 Quality = 100;
00160 
00161                              dsyslog("grabbing to %s %d %d %d", Jpeg ? "JPEG" : "PNM", Quality, fmt.fmt.pix.width, fmt.fmt.pix.height);
00162                              if (Jpeg) {
00163                                 // convert to JPEG:
00164                                 result = RgbToJpeg(mem, fmt.fmt.pix.width, fmt.fmt.pix.height, Size, Quality);
00165                                 if (!result)
00166                                    esyslog("ERROR: failed to convert image to JPEG");
00167                                 }
00168                              else {
00169                                 // convert to PNM:
00170                                 char buf[32];
00171                                 snprintf(buf, sizeof(buf), "P6\n%d\n%d\n255\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
00172                                 int l = strlen(buf);
00173                                 int bytes = memsize * 3;
00174                                 Size = l + bytes;
00175                                 result = MALLOC(uchar, Size);
00176                                 if (result) {
00177                                    memcpy(result, buf, l);
00178                                    memcpy(result + l, mem, bytes);
00179                                    }
00180                                 else
00181                                    esyslog("ERROR: failed to convert image to PNM");
00182                                 }
00183                              }
00184                           else
00185                              esyslog("ERROR: video device VIDIOC_STREAMOFF failed");
00186                           }
00187                        else
00188                           esyslog("ERROR: video device VIDIOC_DQBUF failed");
00189                        }
00190                     else
00191                        esyslog("ERROR: video device VIDIOC_STREAMON failed");
00192                     }
00193                  else
00194                     esyslog("ERROR: video device VIDIOC_QBUF failed");
00195                  munmap(mem, msize);
00196                  }
00197               else
00198                  esyslog("ERROR: failed to memmap video device");
00199               }
00200            else
00201               esyslog("ERROR: video device VIDIOC_QUERYBUF failed");
00202            }
00203         else
00204            esyslog("ERROR: video device VIDIOC_REQBUFS failed");
00205         }
00206      else
00207         esyslog("ERROR: video device VIDIOC_S_FMT failed");
00208      close(videoDev);
00209      return result;
00210      }
00211   else
00212      LOG_ERROR_STR(buffer);
00213   return NULL;
00214 }
00215 
00216 void cDvbSdFfDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat)
00217 {
00218   cDevice::SetVideoDisplayFormat(VideoDisplayFormat);
00219   if (Setup.VideoFormat) {
00220      CHECK(ioctl(fd_video, VIDEO_SET_DISPLAY_FORMAT, VIDEO_LETTER_BOX));
00221      }
00222   else {
00223      switch (VideoDisplayFormat) {
00224        case vdfPanAndScan:
00225             CHECK(ioctl(fd_video, VIDEO_SET_DISPLAY_FORMAT, VIDEO_PAN_SCAN));
00226             break;
00227        case vdfLetterBox:
00228             CHECK(ioctl(fd_video, VIDEO_SET_DISPLAY_FORMAT, VIDEO_LETTER_BOX));
00229             break;
00230        case vdfCenterCutOut:
00231             CHECK(ioctl(fd_video, VIDEO_SET_DISPLAY_FORMAT, VIDEO_CENTER_CUT_OUT));
00232             break;
00233        default: esyslog("ERROR: unknown video display format %d", VideoDisplayFormat);
00234        }
00235      }
00236 }
00237 
00238 void cDvbSdFfDevice::SetVideoFormat(bool VideoFormat16_9)
00239 {
00240   CHECK(ioctl(fd_video, VIDEO_SET_FORMAT, VideoFormat16_9 ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3));
00241   SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
00242 }
00243 
00244 eVideoSystem cDvbSdFfDevice::GetVideoSystem(void)
00245 {
00246   eVideoSystem VideoSystem = vsPAL;
00247   if (fd_video >= 0) {
00248      video_size_t vs;
00249      if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) {
00250         if (vs.h == 480 || vs.h == 240)
00251            VideoSystem = vsNTSC;
00252         }
00253      else
00254         LOG_ERROR;
00255      }
00256   return VideoSystem;
00257 }
00258 
00259 void cDvbSdFfDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect)
00260 {
00261   if (fd_video >= 0) {
00262      video_size_t vs;
00263      if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) {
00264         Width = vs.w;
00265         Height = vs.h;
00266         switch (vs.aspect_ratio) {
00267           default:
00268           case VIDEO_FORMAT_4_3:   VideoAspect =  4.0 / 3.0; break;
00269           case VIDEO_FORMAT_16_9:  VideoAspect = 16.0 / 9.0; break;
00270           case VIDEO_FORMAT_221_1: VideoAspect =       2.21; break;
00271           }
00272         return;
00273         }
00274      else
00275         LOG_ERROR;
00276      }
00277   cDevice::GetVideoSize(Width, Height, VideoAspect);
00278 }
00279 
00280 void cDvbSdFfDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect)
00281 {
00282   if (fd_video >= 0) {
00283      video_size_t vs;
00284      if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) {
00285         Width = 720;
00286         if (vs.h != 480 && vs.h != 240)
00287            Height = 576; // PAL
00288         else
00289            Height = 480; // NTSC
00290         switch (Setup.VideoFormat ? vs.aspect_ratio : VIDEO_FORMAT_4_3) {
00291           default:
00292           case VIDEO_FORMAT_4_3:   PixelAspect =  4.0 / 3.0; break;
00293           case VIDEO_FORMAT_221_1: // FF DVB cards only distinguish between 4:3 and 16:9
00294           case VIDEO_FORMAT_16_9:  PixelAspect = 16.0 / 9.0; break;
00295           }
00296         PixelAspect /= double(Width) / Height;
00297         return;
00298         }
00299      else
00300         LOG_ERROR;
00301      }
00302   cDevice::GetOsdSize(Width, Height, PixelAspect);
00303 }
00304 
00305 bool cDvbSdFfDevice::SetAudioBypass(bool On)
00306 {
00307   if (setTransferModeForDolbyDigital != 1)
00308      return false;
00309   return ioctl(fd_audio, AUDIO_SET_BYPASS_MODE, On) == 0;
00310 }
00311 
00312 //                                   ptAudio        ptVideo        ptPcr        ptTeletext        ptDolby        ptOther
00313 static dmx_pes_type_t PesTypes[] = { DMX_PES_AUDIO, DMX_PES_VIDEO, DMX_PES_PCR, DMX_PES_TELETEXT, DMX_PES_OTHER, DMX_PES_OTHER };
00314 
00315 bool cDvbSdFfDevice::SetPid(cPidHandle *Handle, int Type, bool On)
00316 {
00317   if (Handle->pid) {
00318      dmx_pes_filter_params pesFilterParams;
00319      memset(&pesFilterParams, 0, sizeof(pesFilterParams));
00320      if (On) {
00321         if (Handle->handle < 0) {
00322            Handle->handle = DvbOpen(DEV_DVB_DEMUX, adapter, frontend, O_RDWR | O_NONBLOCK, true);
00323            if (Handle->handle < 0) {
00324               LOG_ERROR;
00325               return false;
00326               }
00327            }
00328         pesFilterParams.pid     = Handle->pid;
00329         pesFilterParams.input   = DMX_IN_FRONTEND;
00330         pesFilterParams.output  = (Type <= ptTeletext && Handle->used <= 1) ? DMX_OUT_DECODER : DMX_OUT_TS_TAP;
00331         pesFilterParams.pes_type= PesTypes[Type < ptOther ? Type : ptOther];
00332         pesFilterParams.flags   = DMX_IMMEDIATE_START;
00333         if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
00334            LOG_ERROR;
00335            return false;
00336            }
00337         }
00338      else if (!Handle->used) {
00339         CHECK(ioctl(Handle->handle, DMX_STOP));
00340         if (Type <= ptTeletext) {
00341            pesFilterParams.pid     = 0x1FFF;
00342            pesFilterParams.input   = DMX_IN_FRONTEND;
00343            pesFilterParams.output  = DMX_OUT_DECODER;
00344            pesFilterParams.pes_type= PesTypes[Type];
00345            pesFilterParams.flags   = DMX_IMMEDIATE_START;
00346            CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams));
00347            if (PesTypes[Type] == DMX_PES_VIDEO) // let's only do this once
00348               SetPlayMode(pmNone); // necessary to switch a PID from DMX_PES_VIDEO/AUDIO to DMX_PES_OTHER
00349            }
00350         close(Handle->handle);
00351         Handle->handle = -1;
00352         }
00353      }
00354   return true;
00355 }
00356 
00357 bool cDvbSdFfDevice::ProvidesSource(int Source) const
00358 {
00359   if (outputOnly)
00360      return false;
00361   else
00362      return cDvbDevice::ProvidesSource(Source);
00363 }
00364 
00365 void cDvbSdFfDevice::TurnOffLiveMode(bool LiveView)
00366 {
00367   if (LiveView) {
00368      // Avoid noise while switching:
00369      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
00370      CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
00371      CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
00372      CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
00373      }
00374 
00375   // Turn off live PIDs:
00376 
00377   DetachAll(pidHandles[ptAudio].pid);
00378   DetachAll(pidHandles[ptVideo].pid);
00379   DetachAll(pidHandles[ptPcr].pid);
00380   DetachAll(pidHandles[ptTeletext].pid);
00381   DelPid(pidHandles[ptAudio].pid);
00382   DelPid(pidHandles[ptVideo].pid);
00383   DelPid(pidHandles[ptPcr].pid, ptPcr);
00384   DelPid(pidHandles[ptTeletext].pid);
00385   DelPid(pidHandles[ptDolby].pid);
00386 }
00387 
00388 bool cDvbSdFfDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
00389 {
00390   int apid = Channel->Apid(0);
00391   int vpid = Channel->Vpid();
00392   int dpid = Channel->Dpid(0);
00393 
00394   bool DoTune = !IsTunedToTransponder(Channel);
00395 
00396   bool pidHandlesVideo = pidHandles[ptVideo].pid == vpid;
00397   bool pidHandlesAudio = pidHandles[ptAudio].pid == apid;
00398 
00399   bool TurnOffLivePIDs = DoTune
00400                          || !IsPrimaryDevice()
00401                          || LiveView // for a new live view the old PIDs need to be turned off
00402                          || pidHandlesVideo // for recording the PIDs must be shifted from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
00403                          ;
00404 
00405   bool StartTransferMode = IsPrimaryDevice() && !DoTune
00406                            && (LiveView && HasPid(vpid ? vpid : apid) && (!pidHandlesVideo || (!pidHandlesAudio && (dpid ? pidHandles[ptAudio].pid != dpid : true)))// the PID is already set as DMX_PES_OTHER
00407                               || !LiveView && (pidHandlesVideo || pidHandlesAudio) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
00408                               );
00409   if (CamSlot() && !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), CamSlot()->SlotNumber()))
00410      StartTransferMode |= LiveView && IsPrimaryDevice() && Channel->Ca() >= CA_ENCRYPTED_MIN;
00411 
00412   bool TurnOnLivePIDs = !StartTransferMode && LiveView;
00413 
00414   // Turn off live PIDs if necessary:
00415 
00416   if (TurnOffLivePIDs)
00417      TurnOffLiveMode(LiveView);
00418 
00419   // Set the tuner:
00420 
00421   if (!cDvbDevice::SetChannelDevice(Channel, LiveView))
00422      return false;
00423 
00424   // PID settings:
00425 
00426   if (TurnOnLivePIDs) {
00427      SetAudioBypass(false);
00428      if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(vpid, ptVideo) && AddPid(apid, ptAudio))) {
00429         esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1);
00430         return false;
00431         }
00432      if (IsPrimaryDevice())
00433         AddPid(Channel->Tpid(), ptTeletext);
00434      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true)); // actually one would expect 'false' here, but according to Marco Schluessler <marco@lordzodiac.de> this works
00435                                                    // to avoid missing audio after replaying a DVD; with 'false' there is an audio disturbance when switching
00436                                                    // between two channels on the same transponder on DVB-S
00437      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
00438      }
00439   else if (StartTransferMode)
00440      cControl::Launch(new cTransferControl(this, Channel));
00441 
00442   return true;
00443 }
00444 
00445 int cDvbSdFfDevice::GetAudioChannelDevice(void)
00446 {
00447   audio_status_t as;
00448   CHECK(ioctl(fd_audio, AUDIO_GET_STATUS, &as));
00449   return as.channel_select;
00450 }
00451 
00452 void cDvbSdFfDevice::SetAudioChannelDevice(int AudioChannel)
00453 {
00454   CHECK(ioctl(fd_audio, AUDIO_CHANNEL_SELECT, AudioChannel));
00455 }
00456 
00457 void cDvbSdFfDevice::SetVolumeDevice(int Volume)
00458 {
00459   if (digitalAudio)
00460      Volume = 0;
00461   audio_mixer_t am;
00462   // conversion for linear volume response:
00463   am.volume_left = am.volume_right = 2 * Volume - Volume * Volume / 255;
00464   CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am));
00465 }
00466 
00467 void cDvbSdFfDevice::SetDigitalAudioDevice(bool On)
00468 {
00469   if (digitalAudio != On) {
00470      if (digitalAudio)
00471         cCondWait::SleepMs(1000); // Wait until any leftover digital data has been flushed
00472      digitalAudio = On;
00473      SetVolumeDevice(On || IsMute() ? 0 : CurrentVolume());
00474      }
00475 }
00476 
00477 void cDvbSdFfDevice::SetAudioTrackDevice(eTrackType Type)
00478 {
00479   const tTrackId *TrackId = GetTrack(Type);
00480   if (TrackId && TrackId->id) {
00481      SetAudioBypass(false);
00482      if (IS_AUDIO_TRACK(Type) || (IS_DOLBY_TRACK(Type) && SetAudioBypass(true))) {
00483         if (pidHandles[ptAudio].pid && pidHandles[ptAudio].pid != TrackId->id) {
00484            DetachAll(pidHandles[ptAudio].pid);
00485            if (CamSlot())
00486               CamSlot()->SetPid(pidHandles[ptAudio].pid, false);
00487            pidHandles[ptAudio].pid = TrackId->id;
00488            SetPid(&pidHandles[ptAudio], ptAudio, true);
00489            if (CamSlot()) {
00490               CamSlot()->SetPid(pidHandles[ptAudio].pid, true);
00491               CamSlot()->StartDecrypting();
00492               }
00493            }
00494         }
00495      else if (IS_DOLBY_TRACK(Type)) {
00496         if (setTransferModeForDolbyDigital == 0)
00497            return;
00498         // Currently this works only in Transfer Mode
00499         ForceTransferMode();
00500         }
00501      }
00502 }
00503 
00504 bool cDvbSdFfDevice::CanReplay(void) const
00505 {
00506   return cDevice::CanReplay();
00507 }
00508 
00509 bool cDvbSdFfDevice::SetPlayMode(ePlayMode PlayMode)
00510 {
00511   if (PlayMode != pmExtern_THIS_SHOULD_BE_AVOIDED && fd_video < 0 && fd_audio < 0) {
00512      // reopen the devices
00513      fd_video = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDWR | O_NONBLOCK);
00514      fd_audio = DvbOpen(DEV_DVB_AUDIO, adapter, frontend, O_RDWR | O_NONBLOCK);
00515      SetVideoFormat(Setup.VideoFormat);
00516      }
00517 
00518   switch (PlayMode) {
00519     case pmNone:
00520          // special handling to return from PCM replay:
00521          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
00522          CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
00523          CHECK(ioctl(fd_video, VIDEO_PLAY));
00524 
00525          CHECK(ioctl(fd_video, VIDEO_STOP, true));
00526          CHECK(ioctl(fd_audio, AUDIO_STOP, true));
00527          CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
00528          CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
00529          CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX));
00530          CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX));
00531          CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
00532          CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
00533          break;
00534     case pmAudioVideo:
00535     case pmAudioOnlyBlack:
00536          if (playMode == pmNone)
00537             TurnOffLiveMode(true);
00538          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
00539          CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
00540          CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, PlayMode == pmAudioVideo));
00541          CHECK(ioctl(fd_audio, AUDIO_PLAY));
00542          CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
00543          CHECK(ioctl(fd_video, VIDEO_PLAY));
00544          break;
00545     case pmAudioOnly:
00546          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
00547          CHECK(ioctl(fd_audio, AUDIO_STOP, true));
00548          CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
00549          CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
00550          CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
00551          CHECK(ioctl(fd_audio, AUDIO_PLAY));
00552          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
00553          break;
00554     case pmVideoOnly:
00555          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
00556          CHECK(ioctl(fd_video, VIDEO_STOP, true));
00557          CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX));
00558          CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
00559          CHECK(ioctl(fd_audio, AUDIO_PLAY));
00560          CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
00561          CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
00562          CHECK(ioctl(fd_video, VIDEO_PLAY));
00563          break;
00564     case pmExtern_THIS_SHOULD_BE_AVOIDED:
00565          close(fd_video);
00566          close(fd_audio);
00567          fd_video = fd_audio = -1;
00568          break;
00569     default: esyslog("ERROR: unknown playmode %d", PlayMode);
00570     }
00571   playMode = PlayMode;
00572   return true;
00573 }
00574 
00575 int64_t cDvbSdFfDevice::GetSTC(void)
00576 {
00577   if (fd_stc >= 0) {
00578      struct dmx_stc stc;
00579      stc.num = 0;
00580      if (ioctl(fd_stc, DMX_GET_STC, &stc) == -1) {
00581         esyslog("ERROR: stc %d: %m", CardIndex() + 1);
00582         return -1;
00583         }
00584      return stc.stc / stc.base;
00585      }
00586   return -1;
00587 }
00588 
00589 void cDvbSdFfDevice::TrickSpeed(int Speed)
00590 {
00591   if (fd_video >= 0)
00592      CHECK(ioctl(fd_video, VIDEO_SLOWMOTION, Speed));
00593 }
00594 
00595 void cDvbSdFfDevice::Clear(void)
00596 {
00597   if (fd_video >= 0)
00598      CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
00599   if (fd_audio >= 0)
00600      CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
00601   cDevice::Clear();
00602 }
00603 
00604 void cDvbSdFfDevice::Play(void)
00605 {
00606   if (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) {
00607      if (fd_audio >= 0)
00608         CHECK(ioctl(fd_audio, AUDIO_CONTINUE));
00609      }
00610   else {
00611      if (fd_audio >= 0) {
00612         CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
00613         CHECK(ioctl(fd_audio, AUDIO_CONTINUE));
00614         }
00615      if (fd_video >= 0)
00616         CHECK(ioctl(fd_video, VIDEO_CONTINUE));
00617      }
00618   cDevice::Play();
00619 }
00620 
00621 void cDvbSdFfDevice::Freeze(void)
00622 {
00623   if (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) {
00624      if (fd_audio >= 0)
00625         CHECK(ioctl(fd_audio, AUDIO_PAUSE));
00626      }
00627   else {
00628      if (fd_audio >= 0) {
00629         CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
00630         CHECK(ioctl(fd_audio, AUDIO_PAUSE));
00631         }
00632      if (fd_video >= 0)
00633         CHECK(ioctl(fd_video, VIDEO_FREEZE));
00634      }
00635   cDevice::Freeze();
00636 }
00637 
00638 void cDvbSdFfDevice::Mute(void)
00639 {
00640   if (fd_audio >= 0) {
00641      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
00642      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
00643      }
00644   cDevice::Mute();
00645 }
00646 
00647 void cDvbSdFfDevice::StillPicture(const uchar *Data, int Length)
00648 {
00649   if (!Data || Length < TS_SIZE)
00650      return;
00651   if (Data[0] == 0x47) {
00652      // TS data
00653      cDevice::StillPicture(Data, Length);
00654      }
00655   else if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01 && (Data[3] & 0xF0) == 0xE0) {
00656      // PES data
00657      char *buf = MALLOC(char, Length);
00658      if (!buf)
00659         return;
00660      int i = 0;
00661      int blen = 0;
00662      while (i < Length - 6) {
00663            if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) {
00664               int len = Data[i + 4] * 256 + Data[i + 5];
00665               if ((Data[i + 3] & 0xF0) == 0xE0) { // video packet
00666                  // skip PES header
00667                  int offs = i + 6;
00668                  // skip header extension
00669                  if ((Data[i + 6] & 0xC0) == 0x80) {
00670                     // MPEG-2 PES header
00671                     if (Data[i + 8] >= Length)
00672                        break;
00673                     offs += 3;
00674                     offs += Data[i + 8];
00675                     len -= 3;
00676                     len -= Data[i + 8];
00677                     if (len < 0 || offs + len > Length)
00678                        break;
00679                     }
00680                  else {
00681                     // MPEG-1 PES header
00682                     while (offs < Length && len > 0 && Data[offs] == 0xFF) {
00683                           offs++;
00684                           len--;
00685                           }
00686                     if (offs <= Length - 2 && len >= 2 && (Data[offs] & 0xC0) == 0x40) {
00687                        offs += 2;
00688                        len -= 2;
00689                        }
00690                     if (offs <= Length - 5 && len >= 5 && (Data[offs] & 0xF0) == 0x20) {
00691                        offs += 5;
00692                        len -= 5;
00693                        }
00694                     else if (offs <= Length - 10 && len >= 10 && (Data[offs] & 0xF0) == 0x30) {
00695                        offs += 10;
00696                        len -= 10;
00697                        }
00698                     else if (offs < Length && len > 0) {
00699                        offs++;
00700                        len--;
00701                        }
00702                     }
00703                  if (blen + len > Length) // invalid PES length field
00704                     break;
00705                  memcpy(&buf[blen], &Data[offs], len);
00706                  i = offs + len;
00707                  blen += len;
00708                  }
00709               else if (Data[i + 3] >= 0xBD && Data[i + 3] <= 0xDF) // other PES packets
00710                  i += len + 6;
00711               else
00712                  i++;
00713               }
00714            else
00715               i++;
00716            }
00717      video_still_picture sp = { buf, blen };
00718      CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp));
00719      free(buf);
00720      }
00721   else {
00722      // non-PES data
00723      video_still_picture sp = { (char *)Data, Length };
00724      CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp));
00725      }
00726 }
00727 
00728 bool cDvbSdFfDevice::Poll(cPoller &Poller, int TimeoutMs)
00729 {
00730   Poller.Add((playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video, true);
00731   return Poller.Poll(TimeoutMs);
00732 }
00733 
00734 bool cDvbSdFfDevice::Flush(int TimeoutMs)
00735 {
00736   //TODO actually this function should wait until all buffered data has been processed by the card, but how?
00737   return true;
00738 }
00739 
00740 int cDvbSdFfDevice::PlayVideo(const uchar *Data, int Length)
00741 {
00742   return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
00743 }
00744 
00745 int cDvbSdFfDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
00746 {
00747   return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10);
00748 }
00749 
00750 int cDvbSdFfDevice::PlayTsVideo(const uchar *Data, int Length)
00751 {
00752   return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
00753 }
00754 
00755 int cDvbSdFfDevice::PlayTsAudio(const uchar *Data, int Length)
00756 {
00757   return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10);
00758 }
00759 
00760 // --- cDvbSdFfDeviceProbe ---------------------------------------------------
00761 
00762 cDvbSdFfDeviceProbe::cDvbSdFfDeviceProbe(void)
00763 {
00764   outputOnly = false;
00765 }
00766 
00767 bool cDvbSdFfDeviceProbe::Probe(int Adapter, int Frontend)
00768 {
00769   static uint32_t SubsystemIds[] = {
00770     0x110A0000, // Fujitsu Siemens DVB-C
00771     0x13C20000, // Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C
00772     0x13C20001, // Technotrend/Hauppauge WinTV DVB-T rev1.X
00773     0x13C20002, // Technotrend/Hauppauge WinTV DVB-C rev2.X
00774     0x13C20003, // Technotrend/Hauppauge WinTV Nexus-S rev2.X
00775     0x13C20004, // Galaxis DVB-S rev1.3
00776     0x13C20006, // Fujitsu Siemens DVB-S rev1.6
00777     0x13C20008, // Technotrend/Hauppauge DVB-T
00778     0x13C2000A, // Technotrend/Hauppauge WinTV Nexus-CA rev1.X
00779     0x13C2000E, // Technotrend/Hauppauge WinTV Nexus-S rev2.3
00780     0x13C21002, // Technotrend/Hauppauge WinTV DVB-S rev1.3 SE
00781     0x00000000
00782     };
00783   uint32_t SubsystemId = GetSubsystemId(Adapter, Frontend);
00784   for (uint32_t *sid = SubsystemIds; *sid; sid++) {
00785       if (*sid == SubsystemId) {
00786          dsyslog("creating cDvbSdFfDevice");
00787          new cDvbSdFfDevice(Adapter, Frontend, outputOnly);
00788          return true;
00789          }
00790       }
00791   return false; 
00792 }