vdr  2.0.2
dvbsdffdevice.c
Go to the documentation of this file.
1 /*
2  * dvbsdffdevice.h: The DVB SD Full Featured device interface
3  *
4  * See the README file for copyright information and how to reach the author.
5  *
6  * $Id: dvbsdffdevice.c 2.35 2013/02/17 13:16:18 kls Exp $
7  */
8 
9 #include "dvbsdffdevice.h"
10 #include <errno.h>
11 #include <limits.h>
12 #include <linux/videodev2.h>
13 #include <linux/dvb/audio.h>
14 #include <linux/dvb/dmx.h>
15 #include <linux/dvb/video.h>
16 #include <sys/ioctl.h>
17 #include <sys/mman.h>
18 #include <vdr/eitscan.h>
19 #include <vdr/transfer.h>
20 #include "dvbsdffosd.h"
21 
22 // --- cDvbSdFfDevice --------------------------------------------------------
23 
25 
26 cDvbSdFfDevice::cDvbSdFfDevice(int Adapter, int Frontend, bool OutputOnly)
27 :cDvbDevice(Adapter, Frontend)
28 {
29  spuDecoder = NULL;
30  digitalAudio = false;
31  playMode = pmNone;
32  outputOnly = OutputOnly;
33 
34  // Devices that are only present on cards with decoders:
35 
37  fd_video = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDWR | O_NONBLOCK);
38  fd_audio = DvbOpen(DEV_DVB_AUDIO, adapter, frontend, O_RDWR | O_NONBLOCK);
40 
41  // The offset of the /dev/video devices:
42 
43  if (devVideoOffset < 0) { // the first one checks this
44  FILE *f = NULL;
45  char buffer[PATH_MAX];
46  for (int ofs = 0; ofs < 100; ofs++) {
47  snprintf(buffer, sizeof(buffer), "/proc/video/dev/video%d", ofs);
48  if ((f = fopen(buffer, "r")) != NULL) {
49  if (fgets(buffer, sizeof(buffer), f)) {
50  if (strstr(buffer, "DVB Board")) { // found the _first_ DVB card
51  devVideoOffset = ofs;
52  dsyslog("video device offset is %d", devVideoOffset);
53  break;
54  }
55  }
56  else
57  break;
58  fclose(f);
59  }
60  else
61  break;
62  }
63  if (devVideoOffset < 0)
64  devVideoOffset = 0;
65  if (f)
66  fclose(f);
67  }
69 }
70 
72 {
73  delete spuDecoder;
74  // We're not explicitly closing any device files here, since this sometimes
75  // caused segfaults. Besides, the program is about to terminate anyway...
76 }
77 
79 {
80  if (On)
83 }
84 
86 {
87  return true;
88 }
89 
91 {
92  return true;
93 }
94 
96 {
97  if (!spuDecoder && IsPrimaryDevice())
98  spuDecoder = new cDvbSpuDecoder();
99  return spuDecoder;
100 }
101 
102 uchar *cDvbSdFfDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY)
103 {
104  if (devVideoIndex < 0)
105  return NULL;
106  char buffer[PATH_MAX];
107  snprintf(buffer, sizeof(buffer), "%s%d", DEV_VIDEO, devVideoIndex);
108  int videoDev = open(buffer, O_RDWR);
109  if (videoDev >= 0) {
110  uchar *result = NULL;
111  // set up the size and RGB
112  v4l2_format fmt;
113  memset(&fmt, 0, sizeof(fmt));
114  fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
115  fmt.fmt.pix.width = SizeX;
116  fmt.fmt.pix.height = SizeY;
117  fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24;
118  fmt.fmt.pix.field = V4L2_FIELD_ANY;
119  if (ioctl(videoDev, VIDIOC_S_FMT, &fmt) == 0) {
120  v4l2_requestbuffers reqBuf;
121  memset(&reqBuf, 0, sizeof(reqBuf));
122  reqBuf.count = 2;
123  reqBuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
124  reqBuf.memory = V4L2_MEMORY_MMAP;
125  if (ioctl(videoDev, VIDIOC_REQBUFS, &reqBuf) >= 0) {
126  v4l2_buffer mbuf;
127  memset(&mbuf, 0, sizeof(mbuf));
128  mbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
129  mbuf.memory = V4L2_MEMORY_MMAP;
130  if (ioctl(videoDev, VIDIOC_QUERYBUF, &mbuf) == 0) {
131  int msize = mbuf.length;
132  unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0);
133  if (mem && mem != (unsigned char *)-1) {
134  v4l2_buffer buf;
135  memset(&buf, 0, sizeof(buf));
136  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
137  buf.memory = V4L2_MEMORY_MMAP;
138  buf.index = 0;
139  if (ioctl(videoDev, VIDIOC_QBUF, &buf) == 0) {
140  v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
141  if (ioctl (videoDev, VIDIOC_STREAMON, &type) == 0) {
142  memset(&buf, 0, sizeof(buf));
143  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
144  buf.memory = V4L2_MEMORY_MMAP;
145  buf.index = 0;
146  if (ioctl(videoDev, VIDIOC_DQBUF, &buf) == 0) {
147  if (ioctl(videoDev, VIDIOC_STREAMOFF, &type) == 0) {
148  // make RGB out of BGR:
149  int memsize = fmt.fmt.pix.width * fmt.fmt.pix.height;
150  unsigned char *mem1 = mem;
151  for (int i = 0; i < memsize; i++) {
152  unsigned char tmp = mem1[2];
153  mem1[2] = mem1[0];
154  mem1[0] = tmp;
155  mem1 += 3;
156  }
157 
158  if (Quality < 0)
159  Quality = 100;
160 
161  dsyslog("grabbing to %s %d %d %d", Jpeg ? "JPEG" : "PNM", Quality, fmt.fmt.pix.width, fmt.fmt.pix.height);
162  if (Jpeg) {
163  // convert to JPEG:
164  result = RgbToJpeg(mem, fmt.fmt.pix.width, fmt.fmt.pix.height, Size, Quality);
165  if (!result)
166  esyslog("ERROR: failed to convert image to JPEG");
167  }
168  else {
169  // convert to PNM:
170  char buf[32];
171  snprintf(buf, sizeof(buf), "P6\n%d\n%d\n255\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
172  int l = strlen(buf);
173  int bytes = memsize * 3;
174  Size = l + bytes;
175  result = MALLOC(uchar, Size);
176  if (result) {
177  memcpy(result, buf, l);
178  memcpy(result + l, mem, bytes);
179  }
180  else
181  esyslog("ERROR: failed to convert image to PNM");
182  }
183  }
184  else
185  esyslog("ERROR: video device VIDIOC_STREAMOFF failed");
186  }
187  else
188  esyslog("ERROR: video device VIDIOC_DQBUF failed");
189  }
190  else
191  esyslog("ERROR: video device VIDIOC_STREAMON failed");
192  }
193  else
194  esyslog("ERROR: video device VIDIOC_QBUF failed");
195  munmap(mem, msize);
196  }
197  else
198  esyslog("ERROR: failed to memmap video device");
199  }
200  else
201  esyslog("ERROR: video device VIDIOC_QUERYBUF failed");
202  }
203  else
204  esyslog("ERROR: video device VIDIOC_REQBUFS failed");
205  }
206  else
207  esyslog("ERROR: video device VIDIOC_S_FMT failed");
208  close(videoDev);
209  return result;
210  }
211  else
212  LOG_ERROR_STR(buffer);
213  return NULL;
214 }
215 
217 {
218  cDevice::SetVideoDisplayFormat(VideoDisplayFormat);
219  if (Setup.VideoFormat) {
220  CHECK(ioctl(fd_video, VIDEO_SET_DISPLAY_FORMAT, VIDEO_LETTER_BOX));
221  }
222  else {
223  switch (VideoDisplayFormat) {
224  case vdfPanAndScan:
225  CHECK(ioctl(fd_video, VIDEO_SET_DISPLAY_FORMAT, VIDEO_PAN_SCAN));
226  break;
227  case vdfLetterBox:
228  CHECK(ioctl(fd_video, VIDEO_SET_DISPLAY_FORMAT, VIDEO_LETTER_BOX));
229  break;
230  case vdfCenterCutOut:
231  CHECK(ioctl(fd_video, VIDEO_SET_DISPLAY_FORMAT, VIDEO_CENTER_CUT_OUT));
232  break;
233  default: esyslog("ERROR: unknown video display format %d", VideoDisplayFormat);
234  }
235  }
236 }
237 
238 void cDvbSdFfDevice::SetVideoFormat(bool VideoFormat16_9)
239 {
240  CHECK(ioctl(fd_video, VIDEO_SET_FORMAT, VideoFormat16_9 ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3));
242 }
243 
245 {
246  eVideoSystem VideoSystem = vsPAL;
247  if (fd_video >= 0) {
248  video_size_t vs;
249  if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) {
250  if (vs.h == 480 || vs.h == 240)
251  VideoSystem = vsNTSC;
252  }
253  else
254  LOG_ERROR;
255  }
256  return VideoSystem;
257 }
258 
259 void cDvbSdFfDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect)
260 {
261  if (fd_video >= 0) {
262  video_size_t vs;
263  if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) {
264  Width = vs.w;
265  Height = vs.h;
266  switch (vs.aspect_ratio) {
267  default:
268  case VIDEO_FORMAT_4_3: VideoAspect = 4.0 / 3.0; break;
269  case VIDEO_FORMAT_16_9: VideoAspect = 16.0 / 9.0; break;
270  case VIDEO_FORMAT_221_1: VideoAspect = 2.21; break;
271  }
272  return;
273  }
274  else
275  LOG_ERROR;
276  }
277  cDevice::GetVideoSize(Width, Height, VideoAspect);
278 }
279 
280 void cDvbSdFfDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect)
281 {
282  if (fd_video >= 0) {
283  video_size_t vs;
284  if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) {
285  Width = 720;
286  if (vs.h != 480 && vs.h != 240)
287  Height = 576; // PAL
288  else
289  Height = 480; // NTSC
290  switch (Setup.VideoFormat ? vs.aspect_ratio : VIDEO_FORMAT_4_3) {
291  default:
292  case VIDEO_FORMAT_4_3: PixelAspect = 4.0 / 3.0; break;
293  case VIDEO_FORMAT_221_1: // FF DVB cards only distinguish between 4:3 and 16:9
294  case VIDEO_FORMAT_16_9: PixelAspect = 16.0 / 9.0; break;
295  }
296  PixelAspect /= double(Width) / Height;
297  return;
298  }
299  else
300  LOG_ERROR;
301  }
302  cDevice::GetOsdSize(Width, Height, PixelAspect);
303 }
304 
306 {
308  return false;
309  return ioctl(fd_audio, AUDIO_SET_BYPASS_MODE, On) == 0;
310 }
311 
312 // ptAudio ptVideo ptPcr ptTeletext ptDolby ptOther
313 static dmx_pes_type_t PesTypes[] = { DMX_PES_AUDIO, DMX_PES_VIDEO, DMX_PES_PCR, DMX_PES_TELETEXT, DMX_PES_OTHER, DMX_PES_OTHER };
314 
315 bool cDvbSdFfDevice::SetPid(cPidHandle *Handle, int Type, bool On)
316 {
317  if (Handle->pid) {
318  dmx_pes_filter_params pesFilterParams;
319  memset(&pesFilterParams, 0, sizeof(pesFilterParams));
320  if (On) {
321  if (Handle->handle < 0) {
322  Handle->handle = DvbOpen(DEV_DVB_DEMUX, adapter, frontend, O_RDWR | O_NONBLOCK, true);
323  if (Handle->handle < 0) {
324  LOG_ERROR;
325  return false;
326  }
327  }
328  pesFilterParams.pid = Handle->pid;
329  pesFilterParams.input = DMX_IN_FRONTEND;
330  pesFilterParams.output = (Type <= ptTeletext && Handle->used <= 1) ? DMX_OUT_DECODER : DMX_OUT_TS_TAP;
331  pesFilterParams.pes_type= PesTypes[Type < ptOther ? Type : ptOther];
332  pesFilterParams.flags = DMX_IMMEDIATE_START;
333  if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
334  LOG_ERROR;
335  return false;
336  }
337  }
338  else if (!Handle->used) {
339  CHECK(ioctl(Handle->handle, DMX_STOP));
340  if (Type <= ptTeletext) {
341  pesFilterParams.pid = 0x1FFF;
342  pesFilterParams.input = DMX_IN_FRONTEND;
343  pesFilterParams.output = DMX_OUT_DECODER;
344  pesFilterParams.pes_type= PesTypes[Type];
345  pesFilterParams.flags = DMX_IMMEDIATE_START;
346  CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams));
347  if (PesTypes[Type] == DMX_PES_VIDEO) // let's only do this once
348  SetPlayMode(pmNone); // necessary to switch a PID from DMX_PES_VIDEO/AUDIO to DMX_PES_OTHER
349  }
350  close(Handle->handle);
351  Handle->handle = -1;
352  }
353  }
354  return true;
355 }
356 
357 bool cDvbSdFfDevice::ProvidesSource(int Source) const
358 {
359  if (outputOnly)
360  return false;
361  else
362  return cDvbDevice::ProvidesSource(Source);
363 }
364 
366 {
367  if (outputOnly)
368  return 0;
370 }
371 
373 {
374  if (LiveView) {
375  // Avoid noise while switching:
376  CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
377  CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
378  CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
379  CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
380  }
381 
382  // Turn off live PIDs:
383 
386  DetachAll(pidHandles[ptPcr].pid);
388  DelPid(pidHandles[ptAudio].pid);
389  DelPid(pidHandles[ptVideo].pid);
390  DelPid(pidHandles[ptPcr].pid, ptPcr);
392  DelPid(pidHandles[ptDolby].pid);
393 }
394 
395 bool cDvbSdFfDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
396 {
397  int apid = Channel->Apid(0);
398  int vpid = Channel->Vpid();
399  int dpid = Channel->Dpid(0);
400 
401  bool DoTune = !IsTunedToTransponder(Channel);
402 
403  bool pidHandlesVideo = pidHandles[ptVideo].pid == vpid;
404  bool pidHandlesAudio = pidHandles[ptAudio].pid == apid;
405 
406  bool TurnOffLivePIDs = DoTune
407  || !IsPrimaryDevice()
408  || LiveView // for a new live view the old PIDs need to be turned off
409  || pidHandlesVideo // for recording the PIDs must be shifted from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
410  ;
411 
412  bool StartTransferMode = IsPrimaryDevice() && !DoTune
413  && (LiveView && HasPid(vpid ? vpid : apid) && (!pidHandlesVideo || (!pidHandlesAudio && (dpid ? pidHandles[ptAudio].pid != dpid : true)))// the PID is already set as DMX_PES_OTHER
414  || !LiveView && (pidHandlesVideo || pidHandlesAudio) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
415  );
417  StartTransferMode |= LiveView && IsPrimaryDevice() && Channel->Ca() >= CA_ENCRYPTED_MIN;
418 
419  bool TurnOnLivePIDs = !StartTransferMode && LiveView;
420 
421  // Turn off live PIDs if necessary:
422 
423  if (TurnOffLivePIDs)
424  TurnOffLiveMode(LiveView);
425 
426  // Set the tuner:
427 
428  if (!cDvbDevice::SetChannelDevice(Channel, LiveView))
429  return false;
430 
431  // PID settings:
432 
433  if (TurnOnLivePIDs) {
434  SetAudioBypass(false);
435  if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(vpid, ptVideo) && AddPid(apid, ptAudio))) {
436  esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1);
437  return false;
438  }
439  if (IsPrimaryDevice())
440  AddPid(Channel->Tpid(), ptTeletext);
441  CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true)); // actually one would expect 'false' here, but according to Marco Schluessler <marco@lordzodiac.de> this works
442  // to avoid missing audio after replaying a DVD; with 'false' there is an audio disturbance when switching
443  // between two channels on the same transponder on DVB-S
444  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
445  }
446  else if (StartTransferMode)
447  cControl::Launch(new cTransferControl(this, Channel));
448 
449  return true;
450 }
451 
453 {
454  audio_status_t as;
455  CHECK(ioctl(fd_audio, AUDIO_GET_STATUS, &as));
456  return as.channel_select;
457 }
458 
460 {
461  CHECK(ioctl(fd_audio, AUDIO_CHANNEL_SELECT, AudioChannel));
462 }
463 
465 {
466  if (digitalAudio)
467  Volume = 0;
468  audio_mixer_t am;
469  // conversion for linear volume response:
470  am.volume_left = am.volume_right = 2 * Volume - Volume * Volume / 255;
471  CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am));
472 }
473 
475 {
476  if (digitalAudio != On) {
477  if (digitalAudio)
478  cCondWait::SleepMs(1000); // Wait until any leftover digital data has been flushed
479  digitalAudio = On;
480  SetVolumeDevice(On || IsMute() ? 0 : CurrentVolume());
481  }
482 }
483 
485 {
486  const tTrackId *TrackId = GetTrack(Type);
487  if (TrackId && TrackId->id) {
488  SetAudioBypass(false);
489  if (IS_AUDIO_TRACK(Type) || (IS_DOLBY_TRACK(Type) && SetAudioBypass(true))) {
490  if (pidHandles[ptAudio].pid && pidHandles[ptAudio].pid != TrackId->id) {
492  if (CamSlot())
493  CamSlot()->SetPid(pidHandles[ptAudio].pid, false);
494  pidHandles[ptAudio].pid = TrackId->id;
495  SetPid(&pidHandles[ptAudio], ptAudio, true);
496  if (CamSlot()) {
497  CamSlot()->SetPid(pidHandles[ptAudio].pid, true);
499  }
500  }
501  }
502  else if (IS_DOLBY_TRACK(Type)) {
504  return;
505  // Currently this works only in Transfer Mode
507  }
508  }
509 }
510 
512 {
513  return cDevice::CanReplay();
514 }
515 
517 {
518  if (PlayMode != pmExtern_THIS_SHOULD_BE_AVOIDED && fd_video < 0 && fd_audio < 0) {
519  // reopen the devices
520  fd_video = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDWR | O_NONBLOCK);
521  fd_audio = DvbOpen(DEV_DVB_AUDIO, adapter, frontend, O_RDWR | O_NONBLOCK);
523  }
524 
525  switch (PlayMode) {
526  case pmNone:
527  // special handling to return from PCM replay:
528  CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
529  CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
530  CHECK(ioctl(fd_video, VIDEO_PLAY));
531 
532  CHECK(ioctl(fd_video, VIDEO_STOP, true));
533  CHECK(ioctl(fd_audio, AUDIO_STOP, true));
534  CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
535  CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
536  CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX));
537  CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX));
538  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
539  CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
540  break;
541  case pmAudioVideo:
542  case pmAudioOnlyBlack:
543  if (playMode == pmNone)
544  TurnOffLiveMode(true);
545  CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
546  CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
547  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, PlayMode == pmAudioVideo));
548  CHECK(ioctl(fd_audio, AUDIO_PLAY));
549  CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
550  CHECK(ioctl(fd_video, VIDEO_PLAY));
551  break;
552  case pmAudioOnly:
553  CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
554  CHECK(ioctl(fd_audio, AUDIO_STOP, true));
555  CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
556  CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
557  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
558  CHECK(ioctl(fd_audio, AUDIO_PLAY));
559  CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
560  break;
561  case pmVideoOnly:
562  CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
563  CHECK(ioctl(fd_video, VIDEO_STOP, true));
564  CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX));
565  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
566  CHECK(ioctl(fd_audio, AUDIO_PLAY));
567  CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
568  CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
569  CHECK(ioctl(fd_video, VIDEO_PLAY));
570  break;
572  close(fd_video);
573  close(fd_audio);
574  fd_video = fd_audio = -1;
575  break;
576  default: esyslog("ERROR: unknown playmode %d", PlayMode);
577  }
578  playMode = PlayMode;
579  return true;
580 }
581 
583 {
584  if (fd_stc >= 0) {
585  struct dmx_stc stc;
586  stc.num = 0;
587  if (ioctl(fd_stc, DMX_GET_STC, &stc) == -1) {
588  esyslog("ERROR: stc %d: %m", CardIndex() + 1);
589  return -1;
590  }
591  return stc.stc / stc.base;
592  }
593  return -1;
594 }
595 
597 {
598  if (fd_video >= 0)
599  CHECK(ioctl(fd_video, VIDEO_SLOWMOTION, Speed));
600 }
601 
603 {
604  if (fd_video >= 0)
605  CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
606  if (fd_audio >= 0)
607  CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
608  cDevice::Clear();
609 }
610 
612 {
614  if (fd_audio >= 0)
615  CHECK(ioctl(fd_audio, AUDIO_CONTINUE));
616  }
617  else {
618  if (fd_audio >= 0) {
619  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
620  CHECK(ioctl(fd_audio, AUDIO_CONTINUE));
621  }
622  if (fd_video >= 0)
623  CHECK(ioctl(fd_video, VIDEO_CONTINUE));
624  }
625  cDevice::Play();
626 }
627 
629 {
631  if (fd_audio >= 0)
632  CHECK(ioctl(fd_audio, AUDIO_PAUSE));
633  }
634  else {
635  if (fd_audio >= 0) {
636  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
637  CHECK(ioctl(fd_audio, AUDIO_PAUSE));
638  }
639  if (fd_video >= 0)
640  CHECK(ioctl(fd_video, VIDEO_FREEZE));
641  }
642  cDevice::Freeze();
643 }
644 
646 {
647  if (fd_audio >= 0) {
648  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
649  CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
650  }
651  cDevice::Mute();
652 }
653 
654 void cDvbSdFfDevice::StillPicture(const uchar *Data, int Length)
655 {
656  if (!Data || Length < TS_SIZE)
657  return;
658  if (Data[0] == 0x47) {
659  // TS data
660  cDevice::StillPicture(Data, Length);
661  }
662  else if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01 && (Data[3] & 0xF0) == 0xE0) {
663  // PES data
664  char *buf = MALLOC(char, Length);
665  if (!buf)
666  return;
667  int i = 0;
668  int blen = 0;
669  while (i < Length - 6) {
670  if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) {
671  int len = Data[i + 4] * 256 + Data[i + 5];
672  if ((Data[i + 3] & 0xF0) == 0xE0) { // video packet
673  // skip PES header
674  int offs = i + 6;
675  // skip header extension
676  if ((Data[i + 6] & 0xC0) == 0x80) {
677  // MPEG-2 PES header
678  if (Data[i + 8] >= Length)
679  break;
680  offs += 3;
681  offs += Data[i + 8];
682  len -= 3;
683  len -= Data[i + 8];
684  if (len < 0 || offs + len > Length)
685  break;
686  }
687  else {
688  // MPEG-1 PES header
689  while (offs < Length && len > 0 && Data[offs] == 0xFF) {
690  offs++;
691  len--;
692  }
693  if (offs <= Length - 2 && len >= 2 && (Data[offs] & 0xC0) == 0x40) {
694  offs += 2;
695  len -= 2;
696  }
697  if (offs <= Length - 5 && len >= 5 && (Data[offs] & 0xF0) == 0x20) {
698  offs += 5;
699  len -= 5;
700  }
701  else if (offs <= Length - 10 && len >= 10 && (Data[offs] & 0xF0) == 0x30) {
702  offs += 10;
703  len -= 10;
704  }
705  else if (offs < Length && len > 0) {
706  offs++;
707  len--;
708  }
709  }
710  if (blen + len > Length) // invalid PES length field
711  break;
712  memcpy(&buf[blen], &Data[offs], len);
713  i = offs + len;
714  blen += len;
715  }
716  else if (Data[i + 3] >= 0xBD && Data[i + 3] <= 0xDF) // other PES packets
717  i += len + 6;
718  else
719  i++;
720  }
721  else
722  i++;
723  }
724  video_still_picture sp = { buf, blen };
725  CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp));
726  free(buf);
727  }
728  else {
729  // non-PES data
730  video_still_picture sp = { (char *)Data, Length };
731  CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp));
732  }
733 }
734 
735 bool cDvbSdFfDevice::Poll(cPoller &Poller, int TimeoutMs)
736 {
737  Poller.Add((playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video, true);
738  return Poller.Poll(TimeoutMs);
739 }
740 
741 bool cDvbSdFfDevice::Flush(int TimeoutMs)
742 {
743  //TODO actually this function should wait until all buffered data has been processed by the card, but how?
744  return true;
745 }
746 
747 int cDvbSdFfDevice::PlayVideo(const uchar *Data, int Length)
748 {
749  return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
750 }
751 
752 int cDvbSdFfDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
753 {
754  return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10);
755 }
756 
757 int cDvbSdFfDevice::PlayTsVideo(const uchar *Data, int Length)
758 {
759  return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
760 }
761 
762 int cDvbSdFfDevice::PlayTsAudio(const uchar *Data, int Length)
763 {
764  return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10);
765 }
766 
767 // --- cDvbSdFfDeviceProbe ---------------------------------------------------
768 
770 {
771  outputOnly = false;
772 }
773 
775 {
776  static uint32_t SubsystemIds[] = {
777  0x110A0000, // Fujitsu Siemens DVB-C
778  0x13C20000, // Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C
779  0x13C20001, // Technotrend/Hauppauge WinTV DVB-T rev1.X
780  0x13C20002, // Technotrend/Hauppauge WinTV DVB-C rev2.X
781  0x13C20003, // Technotrend/Hauppauge WinTV Nexus-S rev2.X
782  0x13C20004, // Galaxis DVB-S rev1.3
783  0x13C20006, // Fujitsu Siemens DVB-S rev1.6
784  0x13C20008, // Technotrend/Hauppauge DVB-T
785  0x13C2000A, // Technotrend/Hauppauge WinTV Nexus-CA rev1.X
786  0x13C2000E, // Technotrend/Hauppauge WinTV Nexus-S rev2.3
787  0x13C21002, // Technotrend/Hauppauge WinTV DVB-S rev1.3 SE
788  0x00000000
789  };
790  uint32_t SubsystemId = GetSubsystemId(Adapter, Frontend);
791  for (uint32_t *sid = SubsystemIds; *sid; sid++) {
792  if (*sid == SubsystemId) {
793  dsyslog("creating cDvbSdFfDevice");
794  new cDvbSdFfDevice(Adapter, Frontend, outputOnly);
795  return true;
796  }
797  }
798  return false;
799 }
800