vdr  2.0.4
dvbhdffdevice.c
Go to the documentation of this file.
1 /*
2  * dvbhdffdevice.c: The DVB HD Full Featured device interface
3  *
4  * See the README file for copyright information and how to reach the author.
5  */
6 
7 #include <stdint.h>
8 
9 #include "dvbhdffdevice.h"
10 #include <errno.h>
11 #include <limits.h>
12 #include <libsi/si.h>
13 #include <linux/videodev2.h>
14 #include <linux/dvb/audio.h>
15 #include <linux/dvb/dmx.h>
16 #include <linux/dvb/video.h>
17 #include <sys/ioctl.h>
18 #include <sys/mman.h>
19 #include <vdr/eitscan.h>
20 #include <vdr/transfer.h>
21 #include "hdffosd.h"
22 #include "setup.h"
23 
24 
25 static uchar *YuvToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality);
26 
27 
28 // --- cDvbHdFfDevice --------------------------------------------------------
29 
31 
32 cDvbHdFfDevice::cDvbHdFfDevice(int Adapter, int Frontend)
33 :cDvbDevice(Adapter, Frontend)
34 {
35  spuDecoder = NULL;
36  audioChannel = 0;
37  playMode = pmNone;
38  mHdffCmdIf = NULL;
39 
40  // Devices that are only present on cards with decoders:
41 
43  fd_video = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDWR | O_NONBLOCK);
44  fd_audio = DvbOpen(DEV_DVB_AUDIO, adapter, frontend, O_RDWR | O_NONBLOCK);
45 
46  //TODO missing /dev/video offset calculation
47 
48  isHdffPrimary = false;
49  if (devHdffOffset < 0) {
51  isHdffPrimary = true;
53 
54  /* reset some stuff in case the VDR was killed before and had no chance
55  to clean up. */
57 
60 
65 
66  ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX);
68  mHdffCmdIf->CmdAvEnableSync(0, true);
69  mHdffCmdIf->CmdAvSetPlayMode(0, true);
70  /* reset done */
71 
77 
78  HdffHdmiConfig_t hdmiConfig;
79  memset(&hdmiConfig, 0, sizeof(hdmiConfig));
80  hdmiConfig.TransmitAudio = true;
81  hdmiConfig.ForceDviMode = false;
82  hdmiConfig.CecEnabled = gHdffSetup.CecEnabled;
83  strcpy(hdmiConfig.CecDeviceName, "VDR");
85  mHdffCmdIf->CmdHdmiConfigure(&hdmiConfig);
86 
89  }
90 }
91 
93 {
94  delete spuDecoder;
95  if (isHdffPrimary)
96  {
97  delete mHdffCmdIf;
98  }
99  // We're not explicitly closing any device files here, since this sometimes
100  // caused segfaults. Besides, the program is about to terminate anyway...
101 }
102 
104 {
105  if (On)
108 }
109 
111 {
112  return isHdffPrimary;
113 }
114 
116 {
117  if (!spuDecoder && IsPrimaryDevice())
118  spuDecoder = new cDvbSpuDecoder();
119  return spuDecoder;
120 }
121 
122 uchar *cDvbHdFfDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY)
123 {
124  #define BUFFER_SIZE (sizeof(struct v4l2_pix_format) + 1920 * 1080 * 2)
125  int fd;
126  uint8_t * buffer;
127  uint8_t * result = NULL;
128 
129  fd = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDONLY);
130  if (fd < 0) {
131  esyslog("GrabImage: failed open DVB video device");
132  return NULL;
133  }
134 
135  buffer = (uint8_t *) malloc(BUFFER_SIZE);
136  if (buffer)
137  {
138  int readBytes;
139 
140  readBytes = read(fd, buffer, BUFFER_SIZE);
141  if (readBytes < (int) sizeof(struct v4l2_pix_format))
142  esyslog("GrabImage: failed reading from DVB video device");
143  else {
144  struct v4l2_pix_format * pixfmt;
145  int dataSize;
146 
147  pixfmt = (struct v4l2_pix_format *) buffer;
148  dsyslog("GrabImage: Read image of size %d x %d",
149  pixfmt->width, pixfmt->height);
150  dataSize = readBytes - sizeof(struct v4l2_pix_format);
151  if (dataSize < (int) pixfmt->sizeimage)
152  esyslog("GrabImage: image is not complete");
153  else {
154  if (Jpeg) {
155  uint8_t * temp;
156  temp = (uint8_t *) malloc(pixfmt->width * 3 * pixfmt->height);
157  if (temp) {
158  int numPixels = pixfmt->width * pixfmt->height;
159  uint8_t * destData = temp;
160  uint8_t * srcData = buffer + sizeof(struct v4l2_pix_format);
161  while (numPixels > 0)
162  {
163  destData[0] = srcData[1];
164  destData[1] = srcData[0];
165  destData[2] = srcData[2];
166  destData[3] = srcData[3];
167  destData[4] = srcData[0];
168  destData[5] = srcData[2];
169  srcData += 4;
170  destData += 6;
171  numPixels -= 2;
172  }
173  if (Quality < 0)
174  Quality = 100;
175  result = YuvToJpeg(temp, pixfmt->width, pixfmt->height, Size, Quality);
176  free(temp);
177  }
178  }
179  else {
180  // convert to PNM:
181  char buf[32];
182  snprintf(buf, sizeof(buf), "P6\n%d\n%d\n255\n",
183  pixfmt->width, pixfmt->height);
184  int l = strlen(buf);
185  Size = l + pixfmt->width * 3 * pixfmt->height;
186  result = (uint8_t *) malloc(Size);
187  if (result)
188  {
189  memcpy(result, buf, l);
190  uint8_t * destData = result + l;
191  uint8_t * srcData = buffer + sizeof(struct v4l2_pix_format);
192  int numPixels = pixfmt->width * pixfmt->height;
193  while (numPixels > 0)
194  {
195  int cb = srcData[0] - 128;
196  int y1 = srcData[1];
197  int cr = srcData[2] - 128;
198  int y2 = srcData[3];
199  int r;
200  int g;
201  int b;
202 
203  r = y1 + (int) (1.402f * cr);
204  g = y1 - (int) (0.344f * cb + 0.714f * cr);
205  b = y1 + (int) (1.772f * cb);
206  destData[0] = r > 255 ? 255 : r < 0 ? 0 : r;
207  destData[1] = g > 255 ? 255 : g < 0 ? 0 : g;
208  destData[2] = b > 255 ? 255 : b < 0 ? 0 : b;
209  r = y2 + (int) (1.402f * cr);
210  g = y2 - (int) (0.344f * cb + 0.714f * cr);
211  b = y2 + (int) (1.772f * cb);
212  destData[3] = r > 255 ? 255 : r < 0 ? 0 : r;
213  destData[4] = g > 255 ? 255 : g < 0 ? 0 : g;
214  destData[5] = b > 255 ? 255 : b < 0 ? 0 : b;
215 
216  srcData += 4;
217  destData += 6;
218  numPixels -= 2;
219  }
220  }
221  }
222  }
223  }
224  free(buffer);
225  }
226 
227  close(fd);
228 
229  return result;
230 }
231 
233 {
234  //TODO???
235  cDevice::SetVideoDisplayFormat(VideoDisplayFormat);
236 }
237 
238 void cDvbHdFfDevice::SetVideoFormat(bool VideoFormat16_9)
239 {
240  HdffVideoFormat_t videoFormat;
241  videoFormat.AutomaticEnabled = true;
242  videoFormat.AfdEnabled = false;
243  videoFormat.TvFormat = (HdffTvFormat_t) gHdffSetup.TvFormat;
245  mHdffCmdIf->CmdAvSetVideoFormat(0, &videoFormat);
246 }
247 
249 {
250  eVideoSystem VideoSystem = vsPAL;
251  if (fd_video >= 0) {
252  video_size_t vs;
253  if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) {
254  if (vs.h == 480 || vs.h == 240)
255  VideoSystem = vsNTSC;
256  }
257  else
258  LOG_ERROR;
259  }
260  return VideoSystem;
261 }
262 
263 void cDvbHdFfDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect)
264 {
265  if (fd_video >= 0) {
266  video_size_t vs;
267  if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) {
268  Width = vs.w;
269  Height = vs.h;
270  switch (vs.aspect_ratio) {
271  default:
272  case VIDEO_FORMAT_4_3: VideoAspect = 4.0 / 3.0; break;
273  case VIDEO_FORMAT_16_9: VideoAspect = 16.0 / 9.0; break;
274  case VIDEO_FORMAT_221_1: VideoAspect = 2.21; break;
275  }
276  return;
277  }
278  else
279  LOG_ERROR;
280  }
281  cDevice::GetVideoSize(Width, Height, VideoAspect);
282 }
283 
284 void cDvbHdFfDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect)
285 {
286  gHdffSetup.GetOsdSize(Width, Height, PixelAspect);
287 }
288 
289 bool cDvbHdFfDevice::SetPid(cPidHandle *Handle, int Type, bool On)
290 {
291  //printf("SetPid Type %d, On %d, PID %5d, streamtype %d, handle %d, used %d\n", Type, On, Handle->pid, Handle->streamType, Handle->handle, Handle->used);
292  if (Handle->pid) {
293  dmx_pes_filter_params pesFilterParams;
294  memset(&pesFilterParams, 0, sizeof(pesFilterParams));
295  if (On) {
296  if (Handle->handle < 0) {
297  Handle->handle = DvbOpen(DEV_DVB_DEMUX, adapter, frontend, O_RDWR | O_NONBLOCK, true);
298  if (Handle->handle < 0) {
299  LOG_ERROR;
300  return false;
301  }
302  }
303  if (Type == ptPcr)
304  mHdffCmdIf->CmdAvSetPcrPid(0, Handle->pid);
305  else if (Type == ptVideo) {
306  if (Handle->streamType == 0x1B)
308  else
310  }
311  else if (Type == ptAudio) {
312  if (Handle->streamType == 0x03)
314  else if (Handle->streamType == 0x04)
316  else if (Handle->streamType == SI::AC3DescriptorTag)
318  else if (Handle->streamType == SI::EnhancedAC3DescriptorTag)
320  else if (Handle->streamType == 0x0F)
322  else if (Handle->streamType == 0x11)
324  else
326  }
327  if (!(Type <= ptDolby && Handle->used <= 1)) {
328  pesFilterParams.pid = Handle->pid;
329  pesFilterParams.input = DMX_IN_FRONTEND;
330  pesFilterParams.output = DMX_OUT_TS_TAP;
331  pesFilterParams.pes_type= DMX_PES_OTHER;
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  }
339  else if (!Handle->used) {
340  CHECK(ioctl(Handle->handle, DMX_STOP));
341  if (Type == ptPcr)
342  mHdffCmdIf->CmdAvSetPcrPid(0, 0);
343  else if (Type == ptVideo)
345  else if (Type == ptAudio)
347  else if (Type == ptDolby)
349  //TODO missing setting to 0x1FFF??? see cDvbDevice::SetPid()
350  close(Handle->handle);
351  Handle->handle = -1;
352  }
353  }
354  return true;
355 }
356 
358 {
359  // Turn off live PIDs:
360 
363  DetachAll(pidHandles[ptPcr].pid);
365  DelPid(pidHandles[ptAudio].pid);
366  DelPid(pidHandles[ptVideo].pid);
367  DelPid(pidHandles[ptPcr].pid, ptPcr);
369  DelPid(pidHandles[ptDolby].pid);
370 }
371 
372 bool cDvbHdFfDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
373 {
374  int apid = Channel->Apid(0);
375  int vpid = Channel->Vpid();
376  int dpid = Channel->Dpid(0);
377 
378  bool DoTune = !IsTunedToTransponder(Channel);
379 
380  bool pidHandlesVideo = pidHandles[ptVideo].pid == vpid;
381  bool pidHandlesAudio = pidHandles[ptAudio].pid == apid;
382 
383  bool TurnOffLivePIDs = DoTune
384  || !IsPrimaryDevice()
385  || LiveView // for a new live view the old PIDs need to be turned off
386  || pidHandlesVideo // for recording the PIDs must be shifted from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
387  ;
388 
389  bool StartTransferMode = IsPrimaryDevice() && !DoTune
390  && (LiveView && HasPid(vpid ? vpid : apid) && (!pidHandlesVideo || (!pidHandlesAudio && (dpid ? pidHandles[ptAudio].pid != dpid : true)))// the PID is already set as DMX_PES_OTHER
391  || !LiveView && (pidHandlesVideo || pidHandlesAudio) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
392  );
394  StartTransferMode |= LiveView && IsPrimaryDevice() && Channel->Ca() >= CA_ENCRYPTED_MIN;
395 
396  //printf("SetChannelDevice Transfer %d, Live %d\n", StartTransferMode, LiveView);
397 
398  bool TurnOnLivePIDs = !StartTransferMode && LiveView;
399 
400  // Turn off live PIDs if necessary:
401 
402  if (TurnOffLivePIDs)
403  TurnOffLiveMode(LiveView);
404 
405  // Set the tuner:
406 
407  if (!cDvbDevice::SetChannelDevice(Channel, LiveView))
408  return false;
409 
410  // PID settings:
411 
412  if (TurnOnLivePIDs) {
413  if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(vpid, ptVideo, Channel->Vtype()) && AddPid(apid ? apid : dpid, ptAudio, apid ? 0 : Channel->Dtype(0)))) {
414  esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1);
415  return false;
416  }
417  }
418  else if (StartTransferMode)
419  cControl::Launch(new cTransferControl(this, Channel));
420 
421  return true;
422 }
423 
425 {
426  return audioChannel;
427 }
428 
430 {
431  mHdffCmdIf->CmdAvSetAudioChannel(AudioChannel);
432  audioChannel = AudioChannel;
433 }
434 
436 {
437  mHdffCmdIf->CmdMuxSetVolume(Volume * 100 / 255);
438 }
439 
441 {
442  // not needed
443 }
444 
446 {
447  //printf("SetAudioTrackDevice %d\n", Type);
448  const tTrackId *TrackId = GetTrack(Type);
449  if (TrackId && TrackId->id) {
450  int streamType = 0;
452  if (channel) {
453  if (IS_AUDIO_TRACK(Type))
454  streamType = channel->Atype(Type - ttAudioFirst);
455  else if (IS_DOLBY_TRACK(Type))
456  streamType = channel->Dtype(Type - ttDolbyFirst);
457  }
458  //printf("SetAudioTrackDevice new %d %d, current %d\n", TrackId->id, streamType, pidHandles[ptAudio].pid);
459  if (pidHandles[ptAudio].pid && pidHandles[ptAudio].pid != TrackId->id) {
461  if (CamSlot())
462  CamSlot()->SetPid(pidHandles[ptAudio].pid, false);
463  pidHandles[ptAudio].pid = TrackId->id;
464  pidHandles[ptAudio].streamType = streamType;
465  SetPid(&pidHandles[ptAudio], ptAudio, true);
466  if (CamSlot()) {
467  CamSlot()->SetPid(pidHandles[ptAudio].pid, true);
469  }
470  }
471  }
472 }
473 
475 {
476  return cDevice::CanReplay();
477 }
478 
480 {
481  if (PlayMode == pmNone) {
482  if (fd_video == -1)
483  fd_video = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDWR | O_NONBLOCK);
484  if (fd_audio == -1)
485  fd_audio = DvbOpen(DEV_DVB_AUDIO, adapter, frontend, O_RDWR | O_NONBLOCK);
486 
489 
491  mHdffCmdIf->CmdAvSetPcrPid(0, 0);
494 
495  ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX);
497  mHdffCmdIf->CmdAvEnableSync(0, true);
498  mHdffCmdIf->CmdAvSetPlayMode(0, true);
499  mHdffCmdIf->CmdAvMuteAudio(0, false);
500  }
501  else {
502  if (playMode == pmNone)
503  TurnOffLiveMode(true);
504 
505  if (PlayMode == pmExtern_THIS_SHOULD_BE_AVOIDED)
506  {
507  close(fd_video);
508  fd_video = -1;
509  close(fd_audio);
510  fd_audio = -1;
511  }
512  else
513  {
515  mHdffCmdIf->CmdAvSetStc(0, 100000);
516  mHdffCmdIf->CmdAvEnableSync(0, false);
518 
519  playVideoPid = -1;
520  playAudioPid = -1;
521  audioCounter = 0;
522  videoCounter = 0;
523  freezed = false;
524  trickMode = false;
525  isPlayingVideo = false;
526 
528  ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY);
529  }
530  }
531  playMode = PlayMode;
532  return true;
533 }
534 
536 {
537  if (isPlayingVideo)
538  {
539  if (fd_video >= 0) {
540  uint64_t pts;
541  if (ioctl(fd_video, VIDEO_GET_PTS, &pts) == -1) {
542  esyslog("ERROR: pts %d: %m", CardIndex() + 1);
543  return -1;
544  }
545  //printf("video PTS %lld\n", pts);
546  return pts;
547  }
548  }
549  else
550  {
551  if (fd_audio >= 0) {
552  uint64_t pts;
553  if (ioctl(fd_audio, AUDIO_GET_PTS, &pts) == -1) {
554  esyslog("ERROR: pts %d: %m", CardIndex() + 1);
555  return -1;
556  }
557  //printf("audio PTS %lld\n", pts);
558  return pts;
559  }
560  }
561  return -1;
562 }
563 
564 cRect cDvbHdFfDevice::CanScaleVideo(const cRect &Rect, int Alignment)
565 {
566  return Rect;
567 }
568 
570 {
571  if (Rect == cRect::Null)
572  {
573  mHdffCmdIf->CmdAvSetVideoWindow(0, false, 0, 0, 0, 0);
574  }
575  else
576  {
577  //printf("ScaleVideo: Rect = %d %d %d %d\n", Rect.X(), Rect.Y(), Rect.Width(), Rect.Height());
578 
579  int osdWidth;
580  int osdHeight;
581  double osdPixelAspect;
582 
583  GetOsdSize(osdWidth, osdHeight, osdPixelAspect);
584  //printf("ScaleVideo: OsdSize = %d %d %g\n", osdWidth, osdHeight, osdPixelAspect);
585 
586  // Convert the video window coordinates in 1/10 percent of the display
587  // resolution.
588  int x = (Rect.X() * 1000 + osdWidth / 2) / osdWidth;
589  int y = (Rect.Y() * 1000 + osdHeight / 2) / osdHeight;
590  int w = (Rect.Width() * 1000 + osdWidth / 2) / osdWidth;
591  int h = (Rect.Height() * 1000 + osdHeight / 2) / osdHeight;
592  //printf("ScaleVideo: Win1 = %d %d %d %d\n", x, y, w, h);
593 
594  // fix aspect ratio, reposition video
595  if (w > h) {
596  x += (w - h) / 2;
597  w = h;
598  }
599  else if (w < h) {
600  y += (h - w) / 2;
601  h = w;
602  }
603 
604  //printf("ScaleVideo: Win2 = %d %d %d %d\n", x, y, w, h);
605  mHdffCmdIf->CmdAvSetVideoWindow(0, true, x, y, w, h);
606  }
607 }
608 
610 {
611  freezed = false;
612  mHdffCmdIf->CmdAvEnableSync(0, false);
614  playAudioPid = -1;
615  if (Speed > 0)
616  mHdffCmdIf->CmdAvSetVideoSpeed(0, 100 / Speed);
617  trickMode = true;
618 }
619 
621 {
622  CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
625  playVideoPid = -1;
626  playAudioPid = -1;
627  cDevice::Clear();
628 }
629 
631 {
632  freezed = false;
633  trickMode = false;
634  if (isPlayingVideo)
635  mHdffCmdIf->CmdAvEnableSync(0, true);
638  mHdffCmdIf->CmdAvMuteAudio(0, false);
639  cDevice::Play();
640 }
641 
643 {
644  freezed = true;
647  cDevice::Freeze();
648 }
649 
651 {
652  mHdffCmdIf->CmdAvMuteAudio(0, true);
653  cDevice::Mute();
654 }
655 
657 {
658  switch (Vtype) {
659  case 0x01: return HDFF_VIDEO_STREAM_MPEG1;
660  case 0x02: return HDFF_VIDEO_STREAM_MPEG2;
661  case 0x1B: return HDFF_VIDEO_STREAM_H264;
662  default: return HDFF_VIDEO_STREAM_MPEG2; // fallback to MPEG2
663  }
664 }
665 
666 void cDvbHdFfDevice::StillPicture(const uchar *Data, int Length)
667 {
668  if (!Data || Length < TS_SIZE)
669  return;
670  if (Data[0] == 0x47) {
671  // TS data
672  cDevice::StillPicture(Data, Length);
673  }
674  else if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01 && (Data[3] & 0xF0) == 0xE0) {
675  // PES data
676  char *buf = MALLOC(char, Length);
677  if (!buf)
678  return;
679  int i = 0;
680  int blen = 0;
681  while (i < Length - 6) {
682  if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) {
683  int len = Data[i + 4] * 256 + Data[i + 5];
684  if ((Data[i + 3] & 0xF0) == 0xE0) { // video packet
685  // skip PES header
686  int offs = i + 6;
687  // skip header extension
688  if ((Data[i + 6] & 0xC0) == 0x80) {
689  // MPEG-2 PES header
690  if (Data[i + 8] >= Length)
691  break;
692  offs += 3;
693  offs += Data[i + 8];
694  len -= 3;
695  len -= Data[i + 8];
696  if (len < 0 || offs + len > Length)
697  break;
698  }
699  else {
700  // MPEG-1 PES header
701  while (offs < Length && len > 0 && Data[offs] == 0xFF) {
702  offs++;
703  len--;
704  }
705  if (offs <= Length - 2 && len >= 2 && (Data[offs] & 0xC0) == 0x40) {
706  offs += 2;
707  len -= 2;
708  }
709  if (offs <= Length - 5 && len >= 5 && (Data[offs] & 0xF0) == 0x20) {
710  offs += 5;
711  len -= 5;
712  }
713  else if (offs <= Length - 10 && len >= 10 && (Data[offs] & 0xF0) == 0x30) {
714  offs += 10;
715  len -= 10;
716  }
717  else if (offs < Length && len > 0) {
718  offs++;
719  len--;
720  }
721  }
722  if (blen + len > Length) // invalid PES length field
723  break;
724  memcpy(&buf[blen], &Data[offs], len);
725  i = offs + len;
726  blen += len;
727  }
728  else if (Data[i + 3] >= 0xBD && Data[i + 3] <= 0xDF) // other PES packets
729  i += len + 6;
730  else
731  i++;
732  }
733  else
734  i++;
735  }
736  mHdffCmdIf->CmdAvShowStillImage(0, (uint8_t *)buf, blen, MapVideoStreamTypes(PatPmtParser()->Vtype()));
737  free(buf);
738  }
739  else {
740  // non-PES data
741  mHdffCmdIf->CmdAvShowStillImage(0, Data, Length, MapVideoStreamTypes(PatPmtParser()->Vtype()));
742  }
743 }
744 
745 bool cDvbHdFfDevice::Poll(cPoller &Poller, int TimeoutMs)
746 {
747  Poller.Add(fd_video, true);
748  return Poller.Poll(TimeoutMs);
749 }
750 
751 bool cDvbHdFfDevice::Flush(int TimeoutMs)
752 {
753  //TODO actually this function should wait until all buffered data has been processed by the card, but how?
754  return true;
755 }
756 
757 void cDvbHdFfDevice::BuildTsPacket(uint8_t * TsBuffer, bool PusiSet, uint16_t Pid, uint8_t Counter, const uint8_t * Data, uint32_t Length)
758 {
759  TsBuffer[0] = 0x47;
760  TsBuffer[1] = PusiSet ? 0x40 : 0x00;
761  TsBuffer[1] |= Pid >> 8;
762  TsBuffer[2] = Pid & 0xFF;
763  if (Length >= 184)
764  {
765  TsBuffer[3] = 0x10 | Counter;
766  memcpy(TsBuffer + 4, Data, 184);
767  }
768  else
769  {
770  uint8_t adaptationLength;
771 
772  TsBuffer[3] = 0x30 | Counter;
773  adaptationLength = 183 - Length;
774  TsBuffer[4] = adaptationLength;
775  if (adaptationLength > 0)
776  {
777  TsBuffer[5] = 0x00;
778  memset(TsBuffer + 6, 0xFF, adaptationLength - 1);
779  }
780  memcpy(TsBuffer + 5 + adaptationLength, Data, Length);
781  }
782 }
783 
784 uint32_t cDvbHdFfDevice::PesToTs(uint8_t * TsBuffer, uint16_t Pid, uint8_t & Counter, const uint8_t * Data, uint32_t Length)
785 {
786  uint32_t tsOffset;
787  uint32_t i;
788 
789  tsOffset = 0;
790  i = 0;
791  while (Length > 0)
792  {
793  BuildTsPacket(TsBuffer + tsOffset, i == 0, Pid, Counter, Data + i * 184, Length);
794  if (Length >= 184)
795  Length -= 184;
796  else
797  Length = 0;
798  Counter = (Counter + 1) & 15;
799  tsOffset += 188;
800  i++;
801  }
802  return tsOffset;
803 }
804 
805 int cDvbHdFfDevice::PlayVideo(const uchar *Data, int Length)
806 {
807  if (freezed)
808  return -1;
809  if (!isPlayingVideo)
810  {
811  mHdffCmdIf->CmdAvEnableSync(0, true);
812  isPlayingVideo = true;
813  }
814  //TODO: support greater Length
815  uint8_t tsBuffer[188 * 16];
816  uint32_t tsLength;
817  int pid = 100;
818 
819  tsLength = PesToTs(tsBuffer, pid, videoCounter, Data, Length);
820 
821  if (pid != playVideoPid) {
822  playVideoPid = pid;
824  }
825  if (WriteAllOrNothing(fd_video, tsBuffer, tsLength, 1000, 10) <= 0)
826  Length = 0;
827  return Length;
828 }
829 
830 int cDvbHdFfDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
831 {
832  if (freezed)
833  return -1;
834  uint8_t streamId;
835  uint8_t tsBuffer[188 * 16];
836  uint32_t tsLength;
839  int pid;
840 
841  streamId = Data[3];
842  if (streamId >= 0xC0 && streamId <= 0xDF)
843  {
844  streamType = HDFF_AUDIO_STREAM_MPEG1;
845  }
846  else if (streamId == 0xBD)
847  {
848  const uint8_t * payload = Data + 9 + Data[8];
849  if ((payload[0] & 0xF8) == 0xA0)
850  {
851  containerType = HDFF_AV_CONTAINER_PES_DVD;
852  streamType = HDFF_AUDIO_STREAM_PCM;
853  }
854  else if ((payload[0] & 0xF8) == 0x88)
855  {
856  containerType = HDFF_AV_CONTAINER_PES_DVD;
857  streamType = HDFF_AUDIO_STREAM_DTS;
858  }
859  else if ((payload[0] & 0xF8) == 0x80)
860  {
861  containerType = HDFF_AV_CONTAINER_PES_DVD;
862  streamType = HDFF_AUDIO_STREAM_AC3;
863  }
864  else
865  {
866  streamType = HDFF_AUDIO_STREAM_AC3;
867  }
868  }
869  pid = 200 + (int) streamType;
870  tsLength = PesToTs(tsBuffer, pid, audioCounter, Data, Length);
871 
872  if (pid != playAudioPid) {
873  playAudioPid = pid;
874  mHdffCmdIf->CmdAvSetAudioPid(0, playAudioPid, streamType, containerType);
875  }
876  if (WriteAllOrNothing(fd_video, tsBuffer, tsLength, 1000, 10) <= 0)
877  Length = 0;
878  return Length;
879 }
880 
881 int cDvbHdFfDevice::PlayTsVideo(const uchar *Data, int Length)
882 {
883  if (freezed)
884  return -1;
885  if (!isPlayingVideo)
886  {
887  mHdffCmdIf->CmdAvEnableSync(0, true);
888  isPlayingVideo = true;
889  }
890 
891  int pid = TsPid(Data);
892  if (pid != playVideoPid) {
893  PatPmtParser();
894  if (pid == PatPmtParser()->Vpid()) {
895  playVideoPid = pid;
897  }
898  }
899  return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
900 }
901 
903 {
904  switch (Atype) {
905  case 0x03: return HDFF_AUDIO_STREAM_MPEG1;
906  case 0x04: return HDFF_AUDIO_STREAM_MPEG2;
909  case 0x0F: return HDFF_AUDIO_STREAM_AAC;
910  case 0x11: return HDFF_AUDIO_STREAM_HE_AAC;
911  default: return HDFF_AUDIO_STREAM_MPEG1;
912  }
913 }
914 
915 int cDvbHdFfDevice::PlayTsAudio(const uchar *Data, int Length)
916 {
917  if (freezed)
918  return -1;
919  int pid = TsPid(Data);
920  if (pid != playAudioPid) {
921  playAudioPid = pid;
922  int AudioStreamType = -1;
923  for (int i = 0; PatPmtParser()->Apid(i); i++) {
924  if (playAudioPid == PatPmtParser()->Apid(i)) {
925  AudioStreamType = PatPmtParser()->Atype(i);
926  break;
927  }
928  }
929  if (AudioStreamType < 0) {
930  for (int i = 0; PatPmtParser()->Dpid(i); i++) {
931  if (playAudioPid == PatPmtParser()->Dpid(i)) {
932  AudioStreamType = PatPmtParser()->Dtype(i);
933  break;
934  }
935  }
936  }
938  }
939  return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
940 }
941 
943 {
944  //TODO why not just keep a pointer?
945  if (devHdffOffset >= 0) {
947  if (device)
948  return device->mHdffCmdIf;
949  }
950  return NULL;
951 }
952 
953 // --- cDvbHdFfDeviceProbe ---------------------------------------------------
954 
955 bool cDvbHdFfDeviceProbe::Probe(int Adapter, int Frontend)
956 {
957  static uint32_t SubsystemIds[] = {
958  0x13C23009, // Technotrend S2-6400 HDFF development samples
959  0x13C2300A, // Technotrend S2-6400 HDFF production version
960  0x00000000
961  };
962  cString FileName;
963  cReadLine ReadLine;
964  FILE *f = NULL;
965  uint32_t SubsystemId = 0;
966  FileName = cString::sprintf("/sys/class/dvb/dvb%d.frontend%d/device/subsystem_vendor", Adapter, Frontend);
967  if ((f = fopen(FileName, "r")) != NULL) {
968  if (char *s = ReadLine.Read(f))
969  SubsystemId = strtoul(s, NULL, 0) << 16;
970  fclose(f);
971  }
972  FileName = cString::sprintf("/sys/class/dvb/dvb%d.frontend%d/device/subsystem_device", Adapter, Frontend);
973  if ((f = fopen(FileName, "r")) != NULL) {
974  if (char *s = ReadLine.Read(f))
975  SubsystemId |= strtoul(s, NULL, 0);
976  fclose(f);
977  }
978  for (uint32_t *sid = SubsystemIds; *sid; sid++) {
979  if (*sid == SubsystemId) {
980  FileName = cString::sprintf("/dev/dvb/adapter%d/osd0", Adapter);
981  int fd = open(FileName, O_RDWR);
982  if (fd != -1) { //TODO treat the second path of the S2-6400 as a budget device
983  close(fd);
984  dsyslog("creating cDvbHdFfDevice");
985  new cDvbHdFfDevice(Adapter, Frontend);
986  return true;
987  }
988  }
989  }
990  return false;
991 }
992 
993 
994 // --- YuvToJpeg -------------------------------------------------------------
995 
996 #include <jpeglib.h>
997 
998 #define JPEGCOMPRESSMEM 4000000
999 
1001  int size;
1003  };
1004 
1005 static void JpegCompressInitDestination(j_compress_ptr cinfo)
1006 {
1007  tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data;
1008  if (jcd) {
1009  cinfo->dest->free_in_buffer = jcd->size = JPEGCOMPRESSMEM;
1010  cinfo->dest->next_output_byte = jcd->mem = MALLOC(uchar, jcd->size);
1011  }
1012 }
1013 
1014 static boolean JpegCompressEmptyOutputBuffer(j_compress_ptr cinfo)
1015 {
1016  tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data;
1017  if (jcd) {
1018  int Used = jcd->size;
1019  int NewSize = jcd->size + JPEGCOMPRESSMEM;
1020  if (uchar *NewBuffer = (uchar *)realloc(jcd->mem, NewSize)) {
1021  jcd->size = NewSize;
1022  jcd->mem = NewBuffer;
1023  }
1024  else {
1025  esyslog("ERROR: out of memory");
1026  return false;
1027  }
1028  if (jcd->mem) {
1029  cinfo->dest->next_output_byte = jcd->mem + Used;
1030  cinfo->dest->free_in_buffer = jcd->size - Used;
1031  return true;
1032  }
1033  }
1034  return false;
1035 }
1036 
1037 static void JpegCompressTermDestination(j_compress_ptr cinfo)
1038 {
1039  tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data;
1040  if (jcd) {
1041  int Used = cinfo->dest->next_output_byte - jcd->mem;
1042  if (Used < jcd->size) {
1043  if (uchar *NewBuffer = (uchar *)realloc(jcd->mem, Used)) {
1044  jcd->size = Used;
1045  jcd->mem = NewBuffer;
1046  }
1047  else
1048  esyslog("ERROR: out of memory");
1049  }
1050  }
1051 }
1052 
1053 static uchar *YuvToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality)
1054 {
1055  if (Quality < 0)
1056  Quality = 0;
1057  else if (Quality > 100)
1058  Quality = 100;
1059 
1060  jpeg_destination_mgr jdm;
1061 
1062  jdm.init_destination = JpegCompressInitDestination;
1063  jdm.empty_output_buffer = JpegCompressEmptyOutputBuffer;
1064  jdm.term_destination = JpegCompressTermDestination;
1065 
1066  struct jpeg_compress_struct cinfo;
1067  struct jpeg_error_mgr jerr;
1068  cinfo.err = jpeg_std_error(&jerr);
1069  jpeg_create_compress(&cinfo);
1070  cinfo.dest = &jdm;
1071  tJpegCompressData jcd;
1072  cinfo.client_data = &jcd;
1073  cinfo.image_width = Width;
1074  cinfo.image_height = Height;
1075  cinfo.input_components = 3;
1076  cinfo.in_color_space = JCS_YCbCr;
1077 
1078  jpeg_set_defaults(&cinfo);
1079  jpeg_set_quality(&cinfo, Quality, true);
1080  jpeg_start_compress(&cinfo, true);
1081 
1082  int rs = Width * 3;
1083  JSAMPROW rp[Height];
1084  for (int k = 0; k < Height; k++)
1085  rp[k] = &Mem[rs * k];
1086  jpeg_write_scanlines(&cinfo, rp, Height);
1087  jpeg_finish_compress(&cinfo);
1088  jpeg_destroy_compress(&cinfo);
1089 
1090  Size = jcd.size;
1091  return jcd.mem;
1092 }
1093