vdr  1.7.31
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.33 2012/03/11 13:32:42 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 (LiveView) {
368  // Avoid noise while switching:
369  CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
370  CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
371  CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
372  CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
373  }
374 
375  // Turn off live PIDs:
376 
379  DetachAll(pidHandles[ptPcr].pid);
381  DelPid(pidHandles[ptAudio].pid);
382  DelPid(pidHandles[ptVideo].pid);
383  DelPid(pidHandles[ptPcr].pid, ptPcr);
385  DelPid(pidHandles[ptDolby].pid);
386 }
387 
388 bool cDvbSdFfDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
389 {
390  int apid = Channel->Apid(0);
391  int vpid = Channel->Vpid();
392  int dpid = Channel->Dpid(0);
393 
394  bool DoTune = !IsTunedToTransponder(Channel);
395 
396  bool pidHandlesVideo = pidHandles[ptVideo].pid == vpid;
397  bool pidHandlesAudio = pidHandles[ptAudio].pid == apid;
398 
399  bool TurnOffLivePIDs = DoTune
400  || !IsPrimaryDevice()
401  || LiveView // for a new live view the old PIDs need to be turned off
402  || pidHandlesVideo // for recording the PIDs must be shifted from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
403  ;
404 
405  bool StartTransferMode = IsPrimaryDevice() && !DoTune
406  && (LiveView && HasPid(vpid ? vpid : apid) && (!pidHandlesVideo || (!pidHandlesAudio && (dpid ? pidHandles[ptAudio].pid != dpid : true)))// the PID is already set as DMX_PES_OTHER
407  || !LiveView && (pidHandlesVideo || pidHandlesAudio) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
408  );
410  StartTransferMode |= LiveView && IsPrimaryDevice() && Channel->Ca() >= CA_ENCRYPTED_MIN;
411 
412  bool TurnOnLivePIDs = !StartTransferMode && LiveView;
413 
414  // Turn off live PIDs if necessary:
415 
416  if (TurnOffLivePIDs)
417  TurnOffLiveMode(LiveView);
418 
419  // Set the tuner:
420 
421  if (!cDvbDevice::SetChannelDevice(Channel, LiveView))
422  return false;
423 
424  // PID settings:
425 
426  if (TurnOnLivePIDs) {
427  SetAudioBypass(false);
428  if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(vpid, ptVideo) && AddPid(apid, ptAudio))) {
429  esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1);
430  return false;
431  }
432  if (IsPrimaryDevice())
433  AddPid(Channel->Tpid(), ptTeletext);
434  CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true)); // actually one would expect 'false' here, but according to Marco Schluessler <marco@lordzodiac.de> this works
435  // to avoid missing audio after replaying a DVD; with 'false' there is an audio disturbance when switching
436  // between two channels on the same transponder on DVB-S
437  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
438  }
439  else if (StartTransferMode)
440  cControl::Launch(new cTransferControl(this, Channel));
441 
442  return true;
443 }
444 
446 {
447  audio_status_t as;
448  CHECK(ioctl(fd_audio, AUDIO_GET_STATUS, &as));
449  return as.channel_select;
450 }
451 
453 {
454  CHECK(ioctl(fd_audio, AUDIO_CHANNEL_SELECT, AudioChannel));
455 }
456 
458 {
459  if (digitalAudio)
460  Volume = 0;
461  audio_mixer_t am;
462  // conversion for linear volume response:
463  am.volume_left = am.volume_right = 2 * Volume - Volume * Volume / 255;
464  CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am));
465 }
466 
468 {
469  if (digitalAudio != On) {
470  if (digitalAudio)
471  cCondWait::SleepMs(1000); // Wait until any leftover digital data has been flushed
472  digitalAudio = On;
473  SetVolumeDevice(On || IsMute() ? 0 : CurrentVolume());
474  }
475 }
476 
478 {
479  const tTrackId *TrackId = GetTrack(Type);
480  if (TrackId && TrackId->id) {
481  SetAudioBypass(false);
482  if (IS_AUDIO_TRACK(Type) || (IS_DOLBY_TRACK(Type) && SetAudioBypass(true))) {
483  if (pidHandles[ptAudio].pid && pidHandles[ptAudio].pid != TrackId->id) {
485  if (CamSlot())
486  CamSlot()->SetPid(pidHandles[ptAudio].pid, false);
487  pidHandles[ptAudio].pid = TrackId->id;
488  SetPid(&pidHandles[ptAudio], ptAudio, true);
489  if (CamSlot()) {
490  CamSlot()->SetPid(pidHandles[ptAudio].pid, true);
492  }
493  }
494  }
495  else if (IS_DOLBY_TRACK(Type)) {
497  return;
498  // Currently this works only in Transfer Mode
500  }
501  }
502 }
503 
505 {
506  return cDevice::CanReplay();
507 }
508 
510 {
511  if (PlayMode != pmExtern_THIS_SHOULD_BE_AVOIDED && fd_video < 0 && fd_audio < 0) {
512  // reopen the devices
513  fd_video = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDWR | O_NONBLOCK);
514  fd_audio = DvbOpen(DEV_DVB_AUDIO, adapter, frontend, O_RDWR | O_NONBLOCK);
516  }
517 
518  switch (PlayMode) {
519  case pmNone:
520  // special handling to return from PCM replay:
521  CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
522  CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
523  CHECK(ioctl(fd_video, VIDEO_PLAY));
524 
525  CHECK(ioctl(fd_video, VIDEO_STOP, true));
526  CHECK(ioctl(fd_audio, AUDIO_STOP, true));
527  CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
528  CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
529  CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX));
530  CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX));
531  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
532  CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
533  break;
534  case pmAudioVideo:
535  case pmAudioOnlyBlack:
536  if (playMode == pmNone)
537  TurnOffLiveMode(true);
538  CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
539  CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
540  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, PlayMode == pmAudioVideo));
541  CHECK(ioctl(fd_audio, AUDIO_PLAY));
542  CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
543  CHECK(ioctl(fd_video, VIDEO_PLAY));
544  break;
545  case pmAudioOnly:
546  CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
547  CHECK(ioctl(fd_audio, AUDIO_STOP, true));
548  CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
549  CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
550  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
551  CHECK(ioctl(fd_audio, AUDIO_PLAY));
552  CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
553  break;
554  case pmVideoOnly:
555  CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
556  CHECK(ioctl(fd_video, VIDEO_STOP, true));
557  CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX));
558  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
559  CHECK(ioctl(fd_audio, AUDIO_PLAY));
560  CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
561  CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
562  CHECK(ioctl(fd_video, VIDEO_PLAY));
563  break;
565  close(fd_video);
566  close(fd_audio);
567  fd_video = fd_audio = -1;
568  break;
569  default: esyslog("ERROR: unknown playmode %d", PlayMode);
570  }
571  playMode = PlayMode;
572  return true;
573 }
574 
576 {
577  if (fd_stc >= 0) {
578  struct dmx_stc stc;
579  stc.num = 0;
580  if (ioctl(fd_stc, DMX_GET_STC, &stc) == -1) {
581  esyslog("ERROR: stc %d: %m", CardIndex() + 1);
582  return -1;
583  }
584  return stc.stc / stc.base;
585  }
586  return -1;
587 }
588 
590 {
591  if (fd_video >= 0)
592  CHECK(ioctl(fd_video, VIDEO_SLOWMOTION, Speed));
593 }
594 
596 {
597  if (fd_video >= 0)
598  CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
599  if (fd_audio >= 0)
600  CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
601  cDevice::Clear();
602 }
603 
605 {
607  if (fd_audio >= 0)
608  CHECK(ioctl(fd_audio, AUDIO_CONTINUE));
609  }
610  else {
611  if (fd_audio >= 0) {
612  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
613  CHECK(ioctl(fd_audio, AUDIO_CONTINUE));
614  }
615  if (fd_video >= 0)
616  CHECK(ioctl(fd_video, VIDEO_CONTINUE));
617  }
618  cDevice::Play();
619 }
620 
622 {
624  if (fd_audio >= 0)
625  CHECK(ioctl(fd_audio, AUDIO_PAUSE));
626  }
627  else {
628  if (fd_audio >= 0) {
629  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
630  CHECK(ioctl(fd_audio, AUDIO_PAUSE));
631  }
632  if (fd_video >= 0)
633  CHECK(ioctl(fd_video, VIDEO_FREEZE));
634  }
635  cDevice::Freeze();
636 }
637 
639 {
640  if (fd_audio >= 0) {
641  CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
642  CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
643  }
644  cDevice::Mute();
645 }
646 
647 void cDvbSdFfDevice::StillPicture(const uchar *Data, int Length)
648 {
649  if (!Data || Length < TS_SIZE)
650  return;
651  if (Data[0] == 0x47) {
652  // TS data
653  cDevice::StillPicture(Data, Length);
654  }
655  else if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01 && (Data[3] & 0xF0) == 0xE0) {
656  // PES data
657  char *buf = MALLOC(char, Length);
658  if (!buf)
659  return;
660  int i = 0;
661  int blen = 0;
662  while (i < Length - 6) {
663  if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) {
664  int len = Data[i + 4] * 256 + Data[i + 5];
665  if ((Data[i + 3] & 0xF0) == 0xE0) { // video packet
666  // skip PES header
667  int offs = i + 6;
668  // skip header extension
669  if ((Data[i + 6] & 0xC0) == 0x80) {
670  // MPEG-2 PES header
671  if (Data[i + 8] >= Length)
672  break;
673  offs += 3;
674  offs += Data[i + 8];
675  len -= 3;
676  len -= Data[i + 8];
677  if (len < 0 || offs + len > Length)
678  break;
679  }
680  else {
681  // MPEG-1 PES header
682  while (offs < Length && len > 0 && Data[offs] == 0xFF) {
683  offs++;
684  len--;
685  }
686  if (offs <= Length - 2 && len >= 2 && (Data[offs] & 0xC0) == 0x40) {
687  offs += 2;
688  len -= 2;
689  }
690  if (offs <= Length - 5 && len >= 5 && (Data[offs] & 0xF0) == 0x20) {
691  offs += 5;
692  len -= 5;
693  }
694  else if (offs <= Length - 10 && len >= 10 && (Data[offs] & 0xF0) == 0x30) {
695  offs += 10;
696  len -= 10;
697  }
698  else if (offs < Length && len > 0) {
699  offs++;
700  len--;
701  }
702  }
703  if (blen + len > Length) // invalid PES length field
704  break;
705  memcpy(&buf[blen], &Data[offs], len);
706  i = offs + len;
707  blen += len;
708  }
709  else if (Data[i + 3] >= 0xBD && Data[i + 3] <= 0xDF) // other PES packets
710  i += len + 6;
711  else
712  i++;
713  }
714  else
715  i++;
716  }
717  video_still_picture sp = { buf, blen };
718  CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp));
719  free(buf);
720  }
721  else {
722  // non-PES data
723  video_still_picture sp = { (char *)Data, Length };
724  CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp));
725  }
726 }
727 
728 bool cDvbSdFfDevice::Poll(cPoller &Poller, int TimeoutMs)
729 {
730  Poller.Add((playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video, true);
731  return Poller.Poll(TimeoutMs);
732 }
733 
734 bool cDvbSdFfDevice::Flush(int TimeoutMs)
735 {
736  //TODO actually this function should wait until all buffered data has been processed by the card, but how?
737  return true;
738 }
739 
740 int cDvbSdFfDevice::PlayVideo(const uchar *Data, int Length)
741 {
742  return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
743 }
744 
745 int cDvbSdFfDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
746 {
747  return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10);
748 }
749 
750 int cDvbSdFfDevice::PlayTsVideo(const uchar *Data, int Length)
751 {
752  return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
753 }
754 
755 int cDvbSdFfDevice::PlayTsAudio(const uchar *Data, int Length)
756 {
757  return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10);
758 }
759 
760 // --- cDvbSdFfDeviceProbe ---------------------------------------------------
761 
763 {
764  outputOnly = false;
765 }
766 
768 {
769  static uint32_t SubsystemIds[] = {
770  0x110A0000, // Fujitsu Siemens DVB-C
771  0x13C20000, // Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C
772  0x13C20001, // Technotrend/Hauppauge WinTV DVB-T rev1.X
773  0x13C20002, // Technotrend/Hauppauge WinTV DVB-C rev2.X
774  0x13C20003, // Technotrend/Hauppauge WinTV Nexus-S rev2.X
775  0x13C20004, // Galaxis DVB-S rev1.3
776  0x13C20006, // Fujitsu Siemens DVB-S rev1.6
777  0x13C20008, // Technotrend/Hauppauge DVB-T
778  0x13C2000A, // Technotrend/Hauppauge WinTV Nexus-CA rev1.X
779  0x13C2000E, // Technotrend/Hauppauge WinTV Nexus-S rev2.3
780  0x13C21002, // Technotrend/Hauppauge WinTV DVB-S rev1.3 SE
781  0x00000000
782  };
783  uint32_t SubsystemId = GetSubsystemId(Adapter, Frontend);
784  for (uint32_t *sid = SubsystemIds; *sid; sid++) {
785  if (*sid == SubsystemId) {
786  dsyslog("creating cDvbSdFfDevice");
787  new cDvbSdFfDevice(Adapter, Frontend, outputOnly);
788  return true;
789  }
790  }
791  return false;
792 }