MythTV  master
NuppelVideoRecorder.cpp
Go to the documentation of this file.
1 #include <cstdio>
2 #include <cstdlib>
3 #include <fcntl.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include "mythconfig.h"
8 #if HAVE_SYS_SOUNDCARD_H
9  #include <sys/soundcard.h>
10 #elif HAVE_SOUNDCARD_H
11  #include <soundcard.h>
12 #endif
13 #include <sys/ioctl.h>
14 #include <sys/mman.h>
15 #include <cerrno>
16 #include <cmath>
17 
18 #include <QStringList>
19 
20 #include <iostream>
21 using namespace std;
22 
23 #include "mythmiscutil.h"
24 #include "mythcontext.h"
25 #include "NuppelVideoRecorder.h"
26 #include "channelbase.h"
27 #include "recordingprofile.h"
28 #include "tv_rec.h"
29 #include "tv_play.h"
30 #include "audioinput.h"
31 #include "mythlogging.h"
32 #include "vbitext/cc.h"
33 #include "vbitext/vbi.h"
34 #include "mythavutil.h"
35 #include "fourcc.h"
36 
37 #if HAVE_BIGENDIAN
38 extern "C" {
39 #include "bswap.h"
40 }
41 #endif
42 
43 extern "C" {
44 #include "libswscale/swscale.h"
45 #include "libavutil/imgutils.h"
46 }
47 
48 #ifdef USING_V4L2
49 #include <linux/videodev2.h>
50 
51 #include "go7007_myth.h"
52 
53 #ifdef USING_V4L1
54 #include <linux/videodev.h>
55 #endif // USING_V4L1
56 
57 #ifndef MJPIOC_S_PARAMS
58 #include "videodev_mjpeg.h"
59 #endif
60 
61 #endif // USING_V4L2
62 
63 #include "ringbuffer.h"
64 #include "RTjpegN.h"
65 
66 #include "programinfo.h"
67 #include "mythsystemevent.h"
68 
69 #define LOC QString("NVR(%1): ").arg(m_videodevice)
70 
72 {
73  RunProlog();
74  m_parent->doWriteThread();
75  RunEpilog();
76 }
77 
79 {
80  RunProlog();
81  m_parent->doAudioThread();
82  RunEpilog();
83 }
84 
86  V4LRecorder(rec)
87 {
88  m_channelObj = channel;
89  m_seektable = new vector<struct seektable_entry>;
90  m_ccd = new CC608Decoder(this);
91 
93 
95 }
96 
98 {
100  {
101  delete m_ringBuffer;
102  m_ringBuffer = nullptr;
103  }
104  delete m_rtjc;
105  delete [] m_mp3buf;
106  if (m_gf)
107  lame_close(m_gf);
108  delete [] m_strm;
109  if (m_audio_device)
110  {
111  delete m_audio_device;
112  m_audio_device = nullptr;
113  }
114  if (m_fd >= 0)
115  close(m_fd);
116  if (m_seektable)
117  {
118  m_seektable->clear();
119  delete m_seektable;
120  }
121 
122  while (!videobuffer.empty())
123  {
124  struct vidbuffertype *vb = videobuffer.back();
125  delete [] vb->buffer;
126  delete vb;
127  videobuffer.pop_back();
128  }
129  while (!audiobuffer.empty())
130  {
131  struct audbuffertype *ab = audiobuffer.back();
132  delete [] ab->buffer;
133  delete ab;
134  audiobuffer.pop_back();
135  }
136  while (!textbuffer.empty())
137  {
138  struct txtbuffertype *tb = textbuffer.back();
139  delete [] tb->buffer;
140  delete tb;
141  textbuffer.pop_back();
142  }
143 
144  if (m_mpa_vidcodec)
145  {
146  QMutexLocker locker(avcodeclock);
147  avcodec_free_context(&m_mpa_vidctx);
148  }
149 
150  delete m_ccd;
151 }
152 
153 void NuppelVideoRecorder::SetOption(const QString &opt, int value)
154 {
155  if (opt == "width")
156  m_w_out = m_width = value;
157  else if (opt == "height")
158  m_h_out = m_height = value;
159  else if (opt == "rtjpegchromafilter")
160  m_M1 = value;
161  else if (opt == "rtjpeglumafilter")
162  m_M2 = value;
163  else if (opt == "rtjpegquality")
164  m_Q = value;
165  else if ((opt == "mpeg4bitrate") || (opt == "mpeg2bitrate"))
166  m_targetbitrate = value;
167  else if (opt == "scalebitrate")
168  m_scalebitrate = value;
169  else if (opt == "mpeg4maxquality")
170  {
171  if (value > 0)
172  m_maxquality = value;
173  else
174  m_maxquality = 1;
175  }
176  else if (opt == "mpeg4minquality")
177  m_minquality = value;
178  else if (opt == "mpeg4qualdiff")
179  m_qualdiff = value;
180  else if (opt == "encodingthreadcount")
181  m_encoding_thread_count = value;
182  else if (opt == "mpeg4optionvhq")
183  {
184  if (value)
185  m_mb_decision = FF_MB_DECISION_RD;
186  else
187  m_mb_decision = FF_MB_DECISION_SIMPLE;
188  }
189  else if (opt == "mpeg4option4mv")
190  {
191  if (value)
192  m_mp4opts |= AV_CODEC_FLAG_4MV;
193  else
194  m_mp4opts &= ~AV_CODEC_FLAG_4MV;
195  }
196  else if (opt == "mpeg4optionidct")
197  {
198  if (value)
199  m_mp4opts |= AV_CODEC_FLAG_INTERLACED_DCT;
200  else
201  m_mp4opts &= ~AV_CODEC_FLAG_INTERLACED_DCT;
202  }
203  else if (opt == "mpeg4optionime")
204  {
205  if (value)
206  m_mp4opts |= AV_CODEC_FLAG_INTERLACED_ME;
207  else
208  m_mp4opts &= ~AV_CODEC_FLAG_INTERLACED_ME;
209  }
210  else if (opt == "hardwaremjpegquality")
211  m_hmjpg_quality = value;
212  else if (opt == "hardwaremjpeghdecimation")
213  m_hmjpg_hdecimation = value;
214  else if (opt == "hardwaremjpegvdecimation")
215  m_hmjpg_vdecimation = value;
216  else if (opt == "audiocompression")
217  m_compressaudio = (value != 0);
218  else if (opt == "mp3quality")
219  m_mp3quality = value;
220  else if (opt == "samplerate")
221  m_audio_samplerate = value;
222  else if (opt == "audioframesize")
223  m_audio_buffer_size = value;
224  else if (opt == "pip_mode")
225  m_pip_mode = value;
226  else if (opt == "inpixfmt")
227  m_inpixfmt = (VideoFrameType)value;
228  else if (opt == "skipbtaudio")
229  m_skip_btaudio = (value != 0);
230  else if (opt == "volume")
231  m_volume = value;
232  else
233  V4LRecorder::SetOption(opt, value);
234 }
235 
236 void NuppelVideoRecorder::SetOption(const QString &name, const QString &value)
237 {
238  V4LRecorder::SetOption(name, value);
239 }
240 
242  const QString &videodev,
243  const QString &audiodev,
244  const QString &vbidev)
245 {
246  SetOption("videodevice", videodev);
247  SetOption("vbidevice", vbidev);
248  SetOption("tvformat", gCoreContext->GetSetting("TVFormat"));
249  SetOption("vbiformat", gCoreContext->GetSetting("VbiFormat"));
250  SetOption("audiodevice", audiodev);
251 
252  QString setting;
253  const StandardSetting *tmp = profile->byName("videocodec");
254  if (tmp)
255  setting = tmp->getValue();
256 
257  if (setting == "MPEG-4")
258  {
259  SetOption("videocodec", "mpeg4");
260 
261  SetIntOption(profile, "mpeg4bitrate");
262  SetIntOption(profile, "scalebitrate");
263  SetIntOption(profile, "mpeg4maxquality");
264  SetIntOption(profile, "mpeg4minquality");
265  SetIntOption(profile, "mpeg4qualdiff");
266 #ifdef USING_FFMPEG_THREADS
267  SetIntOption(profile, "encodingthreadcount");
268 #endif
269  SetIntOption(profile, "mpeg4optionvhq");
270  SetIntOption(profile, "mpeg4option4mv");
271  SetIntOption(profile, "mpeg4optionidct");
272  SetIntOption(profile, "mpeg4optionime");
273  }
274  else if (setting == "MPEG-2")
275  {
276  SetOption("videocodec", "mpeg2video");
277 
278  SetIntOption(profile, "mpeg2bitrate");
279  SetIntOption(profile, "scalebitrate");
280 #ifdef USING_FFMPEG_THREADS
281  SetIntOption(profile, "encodingthreadcount");
282 #endif
283  }
284  else if (setting == "RTjpeg")
285  {
286  SetOption("videocodec", "rtjpeg");
287 
288  SetIntOption(profile, "rtjpegquality");
289  SetIntOption(profile, "rtjpegchromafilter");
290  SetIntOption(profile, "rtjpeglumafilter");
291  }
292  else if (setting == "Hardware MJPEG")
293  {
294  SetOption("videocodec", "hardware-mjpeg");
295 
296  SetIntOption(profile, "hardwaremjpegquality");
297  SetIntOption(profile, "hardwaremjpeghdecimation");
298  SetIntOption(profile, "hardwaremjpegvdecimation");
299  }
300  else
301  {
302  LOG(VB_GENERAL, LOG_ERR, LOC +
303  "Unknown video codec. "
304  "Please go into the TV Settings, Recording Profiles and "
305  "setup the four 'Software Encoders' profiles. "
306  "Assuming RTjpeg for now.");
307 
308  SetOption("videocodec", "rtjpeg");
309 
310  SetIntOption(profile, "rtjpegquality");
311  SetIntOption(profile, "rtjpegchromafilter");
312  SetIntOption(profile, "rtjpeglumafilter");
313  }
314 
315  setting.clear();
316  if ((tmp = profile->byName("audiocodec")))
317  setting = tmp->getValue();
318 
319  if (setting == "MP3")
320  {
321  SetOption("audiocompression", 1);
322  SetIntOption(profile, "mp3quality");
323  SetIntOption(profile, "samplerate");
324  }
325  else if (setting == "Uncompressed")
326  {
327  SetOption("audiocompression", 0);
328  SetIntOption(profile, "samplerate");
329  }
330  else
331  {
332  LOG(VB_GENERAL, LOG_ERR, LOC + "Unknown audio codec");
333  SetOption("audiocompression", 0);
334  }
335 
336  SetIntOption(profile, "volume");
337 
338  SetIntOption(profile, "width");
339  SetIntOption(profile, "height");
340 }
341 
343 {
344  QMutexLocker locker(&m_pauseLock);
347  m_request_pause = true;
348 
349  // The wakeAll is to make sure [write|audio|main]paused are
350  // set immediately, even if we were already paused previously.
351  m_unpauseWait.wakeAll();
352 }
353 
354 bool NuppelVideoRecorder::IsPaused(bool holding_lock) const
355 {
356  if (!holding_lock)
357  m_pauseLock.lock();
358  bool ret = m_audiopaused && m_mainpaused && m_writepaused;
359  if (!holding_lock)
360  m_pauseLock.unlock();
361  return ret;
362 }
363 
364 void NuppelVideoRecorder::SetVideoFilters(QString& /*filters*/)
365 {
366 }
367 
369 {
370  return m_recording;
371 }
372 
374 {
375  return m_framesWritten;
376 }
377 
379 {
380  return m_channelfd;
381 }
382 
384 {
385  if (!m_useavcodec)
386  m_useavcodec = true;
387 
388  if (m_mpa_vidcodec)
389  {
390  QMutexLocker locker(avcodeclock);
391  avcodec_free_context(&m_mpa_vidctx);
392  }
393 
394  QByteArray vcodec = m_videocodec.toLatin1();
395  m_mpa_vidcodec = avcodec_find_encoder_by_name(vcodec.constData());
396 
397  if (!m_mpa_vidcodec)
398  {
399  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Video Codec not found: %1")
400  .arg(vcodec.constData()));
401  return false;
402  }
403 
404  m_mpa_vidctx = avcodec_alloc_context3(nullptr);
405 
406  switch (m_picture_format)
407  {
408  case AV_PIX_FMT_YUV420P:
409  case AV_PIX_FMT_YUV422P:
410  case AV_PIX_FMT_YUVJ420P:
411  m_mpa_vidctx->pix_fmt = m_picture_format;
412  break;
413  default:
414  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unknown picture format: %1")
415  .arg(m_picture_format));
416  }
417 
418  m_mpa_vidctx->width = m_w_out;
419  m_mpa_vidctx->height = (int)(m_height * m_height_multiplier);
420 
421  int usebitrate = m_targetbitrate * 1000;
422  if (m_scalebitrate)
423  {
424  float diff = (m_w_out * m_h_out) / (640.0 * 480.0);
425  usebitrate = (int)(diff * usebitrate);
426  }
427 
428  if (m_targetbitrate == -1)
429  usebitrate = -1;
430 
431  m_mpa_vidctx->time_base.den = (int)ceil(m_video_frame_rate * 100 *
433  m_mpa_vidctx->time_base.num = 100;
434 
435  // avcodec needs specific settings for mpeg2 compression
436  switch (m_mpa_vidctx->time_base.den)
437  {
438  case 2397:
439  case 2398: m_mpa_vidctx->time_base.den = 24000;
440  m_mpa_vidctx->time_base.num = 1001;
441  break;
442  case 2997:
443  case 2998: m_mpa_vidctx->time_base.den = 30000;
444  m_mpa_vidctx->time_base.num = 1001;
445  break;
446  case 5994:
447  case 5995: m_mpa_vidctx->time_base.den = 60000;
448  m_mpa_vidctx->time_base.num = 1001;
449  break;
450  }
451 
452  AVDictionary *opts = nullptr;
453 
454  m_mpa_vidctx->bit_rate = usebitrate;
455  m_mpa_vidctx->bit_rate_tolerance = usebitrate * 100;
456  m_mpa_vidctx->qmin = m_maxquality;
457  m_mpa_vidctx->qmax = m_minquality;
458  m_mpa_vidctx->max_qdiff = m_qualdiff;
459  m_mpa_vidctx->flags = m_mp4opts;
460  m_mpa_vidctx->mb_decision = m_mb_decision;
461 
462  m_mpa_vidctx->qblur = 0.5;
463  m_mpa_vidctx->max_b_frames = 0;
464  m_mpa_vidctx->b_quant_factor = 0;
465  av_dict_set(&opts, "rc_strategy", "2", 0);
466  av_dict_set(&opts, "b_strategy", "0", 0);
467  m_mpa_vidctx->gop_size = 30;
468  m_mpa_vidctx->rc_max_rate = 0;
469  m_mpa_vidctx->rc_min_rate = 0;
470  m_mpa_vidctx->rc_buffer_size = 0;
471  m_mpa_vidctx->rc_override_count = 0;
472  av_dict_set(&opts, "rc_init_cplx", "0", 0);
473  m_mpa_vidctx->dct_algo = FF_DCT_AUTO;
474  m_mpa_vidctx->idct_algo = FF_IDCT_AUTO;
475  av_dict_set_int(&opts, "pred", FF_PRED_LEFT, 0);
476  if (m_videocodec.toLower() == "huffyuv" || m_videocodec.toLower() == "mjpeg")
477  m_mpa_vidctx->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL;
478  m_mpa_vidctx->thread_count = m_encoding_thread_count;
479 
480  QMutexLocker locker(avcodeclock);
481 
482  if (avcodec_open2(m_mpa_vidctx, m_mpa_vidcodec, &opts) < 0)
483  {
484  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unable to open FFMPEG/%1 codec")
485  .arg(m_videocodec));
486  return false;
487  }
488 
489 
490  return true;
491 }
492 
494 {
495  m_picture_format = AV_PIX_FMT_YUV420P;
496 
497  int setval;
498  m_rtjc = new RTjpeg();
499  setval = RTJ_YUV420;
500  m_rtjc->SetFormat(&setval);
501  setval = (int)(m_h_out * m_height_multiplier);
502  m_rtjc->SetSize(&m_w_out, &setval);
503  m_rtjc->SetQuality(&m_Q);
504  setval = 2;
505  m_rtjc->SetIntra(&setval, &m_M1, &m_M2);
506 }
507 
508 
510 {
511  int tot_height = (int)(m_height * m_height_multiplier);
512  double aspectnum = m_w_out / (double)tot_height;
513  uint aspect;
514 
515  if (aspectnum == 0.0)
516  aspect = 0;
517  else if (fabs(aspectnum - 1.3333333333333333) < 0.001)
518  aspect = 2;
519  else if (fabs(aspectnum - 1.7777777777777777) < 0.001)
520  aspect = 3;
521  else if (fabs(aspectnum - 2.21) < 0.001)
522  aspect = 4;
523  else
524  aspect = aspectnum * 1000000;
525 
526  if ((aspect > 0) && (aspect != m_videoAspect))
527  {
528  m_videoAspect = aspect;
529  AspectChange((AspectRatio)aspect, 0);
530  }
531 
532  if (m_w_out && tot_height &&
533  ((uint)tot_height != m_videoHeight ||
535  {
536  m_videoHeight = tot_height;
538  ResolutionChange(m_w_out, tot_height, 0);
539  }
540 
541  int den = (int)ceil(m_video_frame_rate * 100 * m_framerate_multiplier);
542  int num = 100;
543 
544  // avcodec needs specific settings for mpeg2 compression
545  switch (den)
546  {
547  case 2397:
548  case 2398: den = 24000;
549  num = 1001;
550  break;
551  case 2997:
552  case 2998: den = 30000;
553  num = 1001;
554  break;
555  case 5994:
556  case 5995: den = 60000;
557  num = 1001;
558  break;
559  }
560 
561  FrameRate frameRate(den, num);
562  if (frameRate.isNonzero() && frameRate != m_frameRate)
563  {
564  m_frameRate = frameRate;
565  LOG(VB_RECORD, LOG_INFO, LOC + QString("NVR: frame rate = %1")
566  .arg(frameRate.toDouble() * 1000));
567  FrameRateChange(frameRate.toDouble() * 1000, 0);
568  }
569 }
570 
572 {
573  if (AudioInit() != 0)
574  {
575  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to init audio input device");
576  }
577 
578  if (m_videocodec == "hardware-mjpeg")
579  {
580  m_videocodec = "mjpeg";
581  m_hardware_encode = true;
582 
583  MJPEGInit();
584 
586 
587  if (m_ntsc)
588  {
589  switch (m_hmjpg_vdecimation)
590  {
591  case 2: m_height = 240; break;
592  case 4: m_height = 120; break;
593  default: m_height = 480; break;
594  }
595  }
596  else
597  {
598  switch (m_hmjpg_vdecimation)
599  {
600  case 2: m_height = 288; break;
601  case 4: m_height = 144; break;
602  default: m_height = 576; break;
603  }
604  }
605  }
606 
607  if (!m_ringBuffer)
608  {
609  LOG(VB_GENERAL, LOG_WARNING, LOC + "Warning, old RingBuffer creation");
610  m_ringBuffer = RingBuffer::Create("output.nuv", true);
611  m_weMadeBuffer = true;
612  m_livetv = false;
613  if (!m_ringBuffer || !m_ringBuffer->IsOpen())
614  {
615  m_error = "Could not open RingBuffer";
616  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
617  return;
618  }
619  }
620  else
622 
623  m_audiobytes = 0;
624 
625  InitBuffers();
626 }
627 
628 int NuppelVideoRecorder::AudioInit(bool skipdevice)
629 {
630  if (!skipdevice)
631  {
632  int blocksize;
634  if (!m_audio_device)
635  {
636  LOG(VB_GENERAL, LOG_ERR, LOC +
637  QString("Failed to create audio device: %1") .arg(m_audiodevice));
638  return 1;
639  }
640 
642  {
643  LOG(VB_GENERAL, LOG_ERR, LOC +
644  QString("Failed to open audio device %1").arg(m_audiodevice));
645  return 1;
646  }
647 
648  if ((blocksize = m_audio_device->GetBlockSize()) <= 0)
649  {
650  blocksize = 1024;
651  LOG(VB_GENERAL, LOG_ERR, LOC +
652  QString("Failed to determine audio block size on %1,"
653  "using default 1024 bytes").arg(m_audiodevice));
654  }
655 
657  m_audio_buffer_size = blocksize;
658  }
659 
661  LOG(VB_AUDIO, LOG_INFO, LOC +
662  QString("Audio device %1 buffer size: %1 bytes")
663  .arg(m_audio_buffer_size));
664 
665  if (m_compressaudio)
666  {
667  int tmp;
668  m_gf = lame_init();
669  lame_set_bWriteVbrTag(m_gf, 0);
670  lame_set_quality(m_gf, m_mp3quality);
671  lame_set_compression_ratio(m_gf, 11);
672  lame_set_mode(m_gf, m_audio_channels == 2 ? STEREO : MONO);
673  lame_set_num_channels(m_gf, m_audio_channels);
674  lame_set_in_samplerate(m_gf, m_audio_samplerate);
675  if ((tmp = lame_init_params(m_gf)) != 0)
676  {
677  LOG(VB_GENERAL, LOG_ERR, LOC +
678  QString("AudioInit(): lame_init_params error %1").arg(tmp));
679  m_compressaudio = false;
680  }
681 
682  if (m_audio_bits != 16)
683  {
684  LOG(VB_GENERAL, LOG_ERR, LOC +
685  "AudioInit(): lame support requires 16bit audio");
686  m_compressaudio = false;
687  }
688  }
689  m_mp3buf_size = (int)(1.25 * 16384 + 7200);
690  m_mp3buf = new char[m_mp3buf_size];
691 
692  return 0;
693 }
694 
705 {
706 #ifdef USING_V4L1
707  bool we_opened_fd = false;
708  int init_fd = m_fd;
709  if (init_fd < 0)
710  {
711  QByteArray vdevice = m_videodevice.toLatin1();
712  init_fd = open(vdevice.constData(), O_RDWR);
713  we_opened_fd = true;
714 
715  if (init_fd < 0)
716  {
717  LOG(VB_GENERAL, LOG_ERR, LOC + "Can't open video device" + ENO);
718  return false;
719  }
720  }
721 
722  struct video_capability vc;
723  memset(&vc, 0, sizeof(vc));
724  int ret = ioctl(init_fd, VIDIOCGCAP, &vc);
725 
726  if (ret < 0)
727  LOG(VB_GENERAL, LOG_ERR, LOC + "Can't query V4L capabilities" + ENO);
728 
729  if (we_opened_fd)
730  close(init_fd);
731 
732  if (ret < 0)
733  return false;
734 
735  if (vc.maxwidth != 768 && vc.maxwidth != 640)
736  vc.maxwidth = 720;
737 
738  if (vc.type & VID_TYPE_MJPEG_ENCODER)
739  {
740  if (vc.maxwidth >= 768)
741  m_hmjpg_maxw = 768;
742  else if (vc.maxwidth >= 704)
743  m_hmjpg_maxw = 704;
744  else
745  m_hmjpg_maxw = 640;
746  return true;
747  }
748 #endif // USING_V4L1
749 
750  LOG(VB_GENERAL, LOG_ERR, LOC + "MJPEG not supported by device");
751  return false;
752 }
753 
755 {
756  int videomegs;
757  // cppcheck-suppress variableScope
758  int audiomegs = 2;
759 
760  if (!m_video_buffer_size)
761  {
762  m_video_buffer_size = static_cast<long>(
763  GetBufferSize(m_picture_format == AV_PIX_FMT_YUV422P ? FMT_YUV422P : FMT_YV12,
764  m_w_out, m_h_out));
765  }
766 
767  if (m_width >= 480 || m_height > 288)
768  videomegs = 20;
769  else
770  videomegs = 12;
771 
772  m_video_buffer_count = (videomegs * 1000 * 1000) / m_video_buffer_size;
773 
774  if (m_audio_buffer_size != 0)
775  m_audio_buffer_count = (audiomegs * 1000 * 1000) / m_audio_buffer_size;
776  else
778 
779  m_text_buffer_size = 8 * (sizeof(teletextsubtitle) + VT_WIDTH);
781 
782  for (int i = 0; i < m_video_buffer_count; i++)
783  {
784  auto *vidbuf = new vidbuffertype;
785  vidbuf->buffer = new unsigned char[m_video_buffer_size];
786  vidbuf->sample = 0;
787  vidbuf->freeToEncode = 0;
788  vidbuf->freeToBuffer = 1;
789  vidbuf->bufferlen = 0;
790  vidbuf->forcekey = 0;
791 
792  videobuffer.push_back(vidbuf);
793  }
794 
795  for (int i = 0; i < m_audio_buffer_count; i++)
796  {
797  auto *audbuf = new audbuffertype;
798  audbuf->buffer = new unsigned char[m_audio_buffer_size];
799  audbuf->sample = 0;
800  audbuf->freeToEncode = 0;
801  audbuf->freeToBuffer = 1;
802 
803  audiobuffer.push_back(audbuf);
804  }
805 
806  for (int i = 0; i < m_text_buffer_count; i++)
807  {
808  auto *txtbuf = new txtbuffertype;
809  txtbuf->buffer = new unsigned char[m_text_buffer_size];
810  txtbuf->freeToEncode = 0;
811  txtbuf->freeToBuffer = 1;
812 
813  textbuffer.push_back(txtbuf);
814  }
815 }
816 
818 {
819  for (size_t i = 0; i < videobuffer.size(); i++)
820  {
821  delete [] (videobuffer[i]->buffer);
822  videobuffer[i]->buffer = new unsigned char[m_video_buffer_size];
823  }
824 }
825 
827 {
828  delete [] m_strm;
829  m_strm = new signed char[m_width * m_height * 2 + 10];
830 }
831 
833 {
834  if (m_channelfd>0)
835  return true;
836 
837  int retries = 0;
838  QByteArray vdevice = m_videodevice.toLatin1();
839  m_fd = open(vdevice.constData(), O_RDWR);
840  while (m_fd < 0)
841  {
842  usleep(30000);
843  m_fd = open(vdevice.constData(), O_RDWR);
844  if (retries++ > 5)
845  {
846  m_error = QString("Can't open video device: %1").arg(m_videodevice);
847  LOG(VB_GENERAL, LOG_ERR, LOC + m_error + ENO);
848  KillChildren();
849  return false;
850  }
851  }
852 
853  m_channelfd = m_fd;
854  return true;
855 }
856 
858 {
859 #ifdef USING_V4L2
860  m_usingv4l2 = true;
861 
862  struct v4l2_capability vcap {};
863 
864  if (ioctl(m_channelfd, VIDIOC_QUERYCAP, &vcap) < 0)
865  {
866  m_usingv4l2 = false;
867  }
868 
869  if (m_usingv4l2 && !(vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
870  {
871  LOG(VB_GENERAL, LOG_ERR, LOC +
872  "Not a v4l2 capture device, falling back to v4l");
873  m_usingv4l2 = false;
874  }
875 
876  if (m_usingv4l2 && !(vcap.capabilities & V4L2_CAP_STREAMING))
877  {
878  LOG(VB_GENERAL, LOG_ERR, LOC +
879  "Won't work with the streaming interface, falling back");
880  m_usingv4l2 = false;
881  }
882 
883  if (vcap.card[0] == 'B' && vcap.card[1] == 'T' &&
884  vcap.card[2] == '8' && vcap.card[4] == '8')
885  m_correct_bttv = true;
886 
887  QString driver = (char *)vcap.driver;
888  if (driver == "go7007")
889  m_go7007 = true;
890 #endif // USING_V4L2
891 }
892 
894 {
895  if (lzo_init() != LZO_E_OK)
896  {
897  LOG(VB_GENERAL, LOG_ERR, LOC + "lzo_init() failed, exiting");
898  m_error = "lzo_init() failed, exiting";
899  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
900  return;
901  }
902 
903  if (!Open())
904  {
905  m_error = "Failed to open device";
906  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
907  return;
908  }
909 
910  ProbeV4L2();
911 
912  if (m_usingv4l2 && !SetFormatV4L2())
913  {
914  m_error = "Failed to set V4L2 format";
915  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
916  return;
917  }
918 
919  StreamAllocate();
920 
921  m_positionMapLock.lock();
922  m_positionMap.clear();
923  m_positionMapDelta.clear();
924  m_positionMapLock.unlock();
925 
926  m_useavcodec = (m_videocodec.toLower() != "rtjpeg");
927  if (m_useavcodec)
929 
930  if (!m_useavcodec)
931  SetupRTjpeg();
932 
934 
935  if (CreateNuppelFile() != 0)
936  {
937  m_error = QString("Cannot open '%1' for writing")
938  .arg(m_ringBuffer->GetFilename());
939  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
940  return;
941  }
942 
943  if (IsHelperRequested())
944  {
945  LOG(VB_GENERAL, LOG_ERR, LOC + "Children are already alive");
946  m_error = "Children are already alive";
947  return;
948  }
949 
950  {
951  QMutexLocker locker(&m_pauseLock);
952  m_request_recording = true;
953  m_request_helper = true;
954  m_recording = true;
955  m_recordingWait.wakeAll();
956  }
957 
958  m_write_thread = new NVRWriteThread(this);
960 
961  m_audio_thread = new NVRAudioThread(this);
963 
964  if ((m_vbimode != VBIMode::None) && (OpenVBIDevice() >= 0))
965  m_vbi_thread = new VBIThread(this);
966 
967  // save the start time
968  gettimeofday(&m_stm, &m_tzone);
969 
970  // try to get run at higher scheduling priority, ignore failure
971  myth_nice(-10);
972 
973  if (m_usingv4l2)
974  {
975  m_inpixfmt = FMT_NONE;;
976  DoV4L2();
977  }
978  else
979  DoV4L1();
980 
981  {
982  QMutexLocker locker(&m_pauseLock);
983  m_request_recording = false;
984  m_request_helper = false;
985  m_recording = false;
986  m_recordingWait.wakeAll();
987  }
988 }
989 
990 #ifdef USING_V4L1
992 {
993  struct video_capability vc;
994  struct video_mmap mm;
995  struct video_mbuf vm;
996  struct video_channel vchan;
997  struct video_audio va;
998  struct video_tuner vt;
999 
1000  memset(&mm, 0, sizeof(mm));
1001  memset(&vm, 0, sizeof(vm));
1002  memset(&vchan, 0, sizeof(vchan));
1003  memset(&va, 0, sizeof(va));
1004  memset(&vt, 0, sizeof(vt));
1005  memset(&vc, 0, sizeof(vc));
1006 
1007  if (ioctl(m_fd, VIDIOCGCAP, &vc) < 0)
1008  {
1009  QString tmp = "VIDIOCGCAP: " + ENO;
1010  KillChildren();
1011  LOG(VB_GENERAL, LOG_ERR, tmp);
1012  m_error = tmp;
1013  return;
1014  }
1015 
1016  int channelinput = 0;
1017 
1018  if (m_channelObj)
1019  channelinput = m_channelObj->GetCurrentInputNum();
1020 
1021  vchan.channel = channelinput;
1022 
1023  if (ioctl(m_fd, VIDIOCGCHAN, &vchan) < 0)
1024  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCGCHAN: " + ENO);
1025 
1026  // Set volume level for audio recording (unless feature is disabled).
1027  if (!m_skip_btaudio)
1028  {
1029  // v4l1 compat in Linux 2.6.18 does not set VIDEO_VC_AUDIO,
1030  // so we just use VIDIOCGAUDIO unconditionally.. then only
1031  // report a get failure as an error if VIDEO_VC_AUDIO is set.
1032  if (ioctl(m_fd, VIDIOCGAUDIO, &va) < 0)
1033  {
1034  bool reports_audio = vchan.flags & VIDEO_VC_AUDIO;
1035  uint err_level = reports_audio ? VB_GENERAL : VB_AUDIO;
1036  // print at VB_GENERAL if driver reports audio.
1037  LOG(err_level, LOG_ERR, LOC + "Failed to get audio" + ENO);
1038  }
1039  else
1040  {
1041  // if channel has a audio then activate it
1042  va.flags &= ~VIDEO_AUDIO_MUTE; // now this really has to work
1043  va.volume = m_volume * 65535 / 100;
1044  if (ioctl(m_fd, VIDIOCSAUDIO, &va) < 0)
1045  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to set audio" + ENO);
1046  }
1047  }
1048 
1049  if ((vc.type & VID_TYPE_MJPEG_ENCODER) && m_hardware_encode)
1050  {
1051  DoMJPEG();
1052  m_error = "MJPEG requested but not available.";
1053  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1054  return;
1055  }
1056 
1057  m_inpixfmt = FMT_NONE;
1058  InitFilters();
1059 
1060  if (ioctl(m_fd, VIDIOCGMBUF, &vm) < 0)
1061  {
1062  QString tmp = "VIDIOCGMBUF: " + ENO;
1063  KillChildren();
1064  LOG(VB_GENERAL, LOG_ERR, LOC + tmp);
1065  m_error = tmp;
1066  return;
1067  }
1068 
1069  if (vm.frames < 2)
1070  {
1071  QString tmp = "need a minimum of 2 capture buffers";
1072  KillChildren();
1073  LOG(VB_GENERAL, LOG_ERR, LOC + tmp);
1074  m_error = tmp;
1075  return;
1076  }
1077 
1078  int frame;
1079 
1080  unsigned char *buf = (unsigned char *)mmap(0, vm.size,
1081  PROT_READ|PROT_WRITE,
1082  MAP_SHARED,
1083  m_fd, 0);
1084  if (buf == MAP_FAILED)
1085  {
1086  QString tmp = "mmap: " + ENO;
1087  KillChildren();
1088  LOG(VB_GENERAL, LOG_ERR, LOC + tmp);
1089  m_error = tmp;
1090  return;
1091  }
1092 
1093  mm.height = m_height;
1094  mm.width = m_width;
1095  if (m_inpixfmt == FMT_YUV422P)
1096  mm.format = VIDEO_PALETTE_YUV422P;
1097  else
1098  mm.format = VIDEO_PALETTE_YUV420P;
1099 
1100  mm.frame = 0;
1101  if (ioctl(m_fd, VIDIOCMCAPTURE, &mm)<0)
1102  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCMCAPTUREi0: " + ENO);
1103  mm.frame = 1;
1104  if (ioctl(m_fd, VIDIOCMCAPTURE, &mm)<0)
1105  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCMCAPTUREi1: " + ENO);
1106 
1107  int syncerrors = 0;
1108 
1109  while (IsRecordingRequested() && !IsErrored())
1110  {
1111  {
1112  QMutexLocker locker(&m_pauseLock);
1113  if (m_request_pause)
1114  {
1115  if (!m_mainpaused)
1116  {
1117  m_mainpaused = true;
1118  m_pauseWait.wakeAll();
1119  if (IsPaused(true) && m_tvrec)
1121  }
1122  m_unpauseWait.wait(&m_pauseLock, 100);
1123  if (m_cleartimeonpause)
1124  gettimeofday(&m_stm, &m_tzone);
1125  continue;
1126  }
1127 
1128  if (!m_request_pause && m_mainpaused)
1129  {
1130  m_mainpaused = false;
1131  m_unpauseWait.wakeAll();
1132  }
1133  }
1134 
1135  frame = 0;
1136  mm.frame = 0;
1137  if (ioctl(m_fd, VIDIOCSYNC, &frame)<0)
1138  {
1139  syncerrors++;
1140  if (syncerrors == 10)
1141  LOG(VB_GENERAL, LOG_ERR, LOC +
1142  "Multiple bttv errors, further messages supressed");
1143  else if (syncerrors < 10)
1144  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCSYNC: " + ENO);
1145  }
1146  else
1147  {
1148  BufferIt(buf+vm.offsets[0], m_video_buffer_size);
1149  //memset(buf+vm.offsets[0], 0, m_video_buffer_size);
1150  }
1151 
1152  if (ioctl(m_fd, VIDIOCMCAPTURE, &mm)<0)
1153  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCMCAPTURE0: " + ENO);
1154 
1155  frame = 1;
1156  mm.frame = 1;
1157  if (ioctl(m_fd, VIDIOCSYNC, &frame)<0)
1158  {
1159  syncerrors++;
1160  if (syncerrors == 10)
1161  LOG(VB_GENERAL, LOG_ERR, LOC +
1162  "Multiple bttv errors, further messages supressed");
1163  else if (syncerrors < 10)
1164  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCSYNC: " + ENO);
1165  }
1166  else
1167  {
1168  BufferIt(buf+vm.offsets[1], m_video_buffer_size);
1169  //memset(buf+vm.offsets[1], 0, m_video_buffer_size);
1170  }
1171  if (ioctl(m_fd, VIDIOCMCAPTURE, &mm)<0)
1172  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCMCAPTURE1: " + ENO);
1173  }
1174 
1175  munmap(buf, vm.size);
1176 
1177  KillChildren();
1178 
1179  FinishRecording();
1180 
1181  close(m_fd);
1182 }
1183 #else // if !USING_V4L1
1184 void NuppelVideoRecorder::DoV4L1(void) {}
1185 #endif // !USING_V4L1
1186 
1187 #ifdef USING_V4L2
1189 {
1190  struct v4l2_format vfmt {};
1191 
1192  vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1193 
1194  vfmt.fmt.pix.width = m_width;
1195  vfmt.fmt.pix.height = m_height;
1196  vfmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
1197 
1198  if (m_go7007)
1199  vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
1200  else if (m_inpixfmt == FMT_YUV422P)
1201  vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
1202  else
1203  vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
1204 
1205  if (ioctl(m_fd, VIDIOC_S_FMT, &vfmt) < 0)
1206  {
1207  // this is supported by the cx88 and various ati cards.
1208  vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
1209 
1210  if (ioctl(m_fd, VIDIOC_S_FMT, &vfmt) < 0)
1211  {
1212  // this is supported by the HVR-950q
1213  vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
1214  if (ioctl(m_fd, VIDIOC_S_FMT, &vfmt) < 0)
1215  {
1216  LOG(VB_GENERAL, LOG_ERR, LOC +
1217  "v4l2: Unable to set desired format");
1218  return false;
1219  }
1220 
1221  // we need to convert the buffer - we can't deal with uyvy
1222  // directly.
1223  if (m_inpixfmt == FMT_YUV422P)
1224  {
1225  LOG(VB_GENERAL, LOG_ERR, LOC +
1226  "v4l2: uyvy format supported, but yuv422 requested.");
1227  LOG(VB_GENERAL, LOG_ERR, LOC +
1228  "v4l2: unfortunately, this converter hasn't been "
1229  "written yet, exiting");
1230  return false;
1231  }
1232  LOG(VB_RECORD, LOG_INFO, LOC +
1233  "v4l2: format set, getting uyvy from v4l, converting");
1234  }
1235  else
1236  {
1237  // we need to convert the buffer - we can't deal with yuyv directly.
1238  if (m_inpixfmt == FMT_YUV422P)
1239  {
1240  LOG(VB_GENERAL, LOG_ERR, LOC +
1241  "v4l2: yuyv format supported, but yuv422 requested.");
1242  LOG(VB_GENERAL, LOG_ERR, LOC +
1243  "v4l2: unfortunately, this converter hasn't been written "
1244  "yet, exiting");
1245  return false;
1246  }
1247  LOG(VB_RECORD, LOG_INFO, LOC +
1248  "v4l2: format set, getting yuyv from v4l, converting");
1249  }
1250  }
1251  else // cool, we can do our preferred format, most likely running on bttv.
1252  LOG(VB_RECORD, LOG_INFO, LOC +
1253  "v4l2: format set, getting yuv420 from v4l");
1254 
1255  // VIDIOC_S_FMT might change the format, check it
1256  if (m_width != (int)vfmt.fmt.pix.width ||
1257  m_height != (int)vfmt.fmt.pix.height)
1258  {
1259  LOG(VB_RECORD, LOG_INFO, LOC +
1260  QString("v4l2: resolution changed. requested %1x%2, using "
1261  "%3x%4 now")
1262  .arg(m_width).arg(m_height)
1263  .arg(vfmt.fmt.pix.width) .arg(vfmt.fmt.pix.height));
1264  m_w_out = m_width = vfmt.fmt.pix.width;
1265  m_h_out = m_height = vfmt.fmt.pix.height;
1266  }
1267 
1268  m_v4l2_pixelformat = vfmt.fmt.pix.pixelformat;
1269 
1270  return true;
1271 }
1272 #else // if !USING_V4L2
1273 bool NuppelVideoRecorder::SetFormatV4L2(void) { return false; }
1274 #endif // !USING_V4L2
1275 
1276 #ifdef USING_V4L2
1277 #define MAX_VIDEO_BUFFERS 5
1279 {
1280  struct v4l2_buffer vbuf {};
1281  struct v4l2_requestbuffers vrbuf {};
1282  struct v4l2_control vc {};
1283 
1284  vc.id = V4L2_CID_AUDIO_MUTE;
1285  vc.value = 0;
1286 
1287  if (ioctl(m_fd, VIDIOC_S_CTRL, &vc) < 0)
1288  LOG(VB_GENERAL, LOG_ERR, LOC +
1289  "VIDIOC_S_CTRL:V4L2_CID_AUDIO_MUTE: " + ENO);
1290 
1291  if (m_go7007)
1292  {
1293  struct go7007_comp_params comp {};
1294  struct go7007_mpeg_params mpeg {};
1295 
1296  comp.gop_size = m_keyframedist;
1297  comp.max_b_frames = 0;
1298 
1299  if (fabs(m_video_aspect - 1.33333F) < 0.01F)
1300  {
1301  if (m_ntsc)
1303  else
1305  }
1306  else if (fabs(m_video_aspect - 1.77777F) < 0.01F)
1307  {
1308  if (m_ntsc)
1310  else
1312  }
1313  else
1314  {
1316  }
1317 
1318  comp.flags |= GO7007_COMP_CLOSED_GOP;
1319  if (ioctl(m_fd, GO7007IOC_S_COMP_PARAMS, &comp) < 0)
1320  {
1321  m_error = "Unable to set compression params";
1322  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1323  return;
1324  }
1325 
1326  if (m_videocodec == "mpeg2video")
1328  else
1330 
1331  if (ioctl(m_fd, GO7007IOC_S_MPEG_PARAMS, &mpeg) < 0)
1332  {
1333  m_error = "Unable to set MPEG params";
1334  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1335  return;
1336  }
1337 
1338  int usebitrate = m_targetbitrate * 1000;
1339  if (m_scalebitrate)
1340  {
1341  float diff = (m_width * m_height) / (640.0 * 480.0);
1342  usebitrate = (int)(diff * usebitrate);
1343  }
1344 
1345  if (ioctl(m_fd, GO7007IOC_S_BITRATE, &usebitrate) < 0)
1346  {
1347  m_error = "Unable to set bitrate";
1348  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1349  return;
1350  }
1351 
1352  m_hardware_encode = true;
1353  }
1354 
1355  uint numbuffers = MAX_VIDEO_BUFFERS;
1356 
1357  vrbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1358  vrbuf.memory = V4L2_MEMORY_MMAP;
1359  vrbuf.count = numbuffers;
1360 
1361  if (ioctl(m_fd, VIDIOC_REQBUFS, &vrbuf) < 0)
1362  {
1363  m_error = "Not able to get any capture buffers, exiting";
1364  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1365  return;
1366  }
1367 
1368  if (vrbuf.count < numbuffers)
1369  {
1370  LOG(VB_GENERAL, LOG_INFO, LOC +
1371  QString("Requested %1 buffers, but only %2 are available. "
1372  "Proceeding anyway").arg(numbuffers).arg(vrbuf.count));
1373  }
1374 
1375  numbuffers = vrbuf.count;
1376 
1377  unsigned char *buffers[MAX_VIDEO_BUFFERS];
1378  int bufferlen[MAX_VIDEO_BUFFERS];
1379 
1380  for (uint i = 0; i < numbuffers; i++)
1381  {
1382  vbuf.type = vrbuf.type;
1383  vbuf.index = i;
1384 
1385  if (ioctl(m_fd, VIDIOC_QUERYBUF, &vbuf) < 0)
1386  {
1387  LOG(VB_GENERAL, LOG_ERR, LOC +
1388  QString("unable to query capture buffer %1").arg(i));
1389  m_error = "Unable to query capture buffer";
1390  return;
1391  }
1392 
1393  buffers[i] = (unsigned char *)mmap(nullptr, vbuf.length,
1394  PROT_READ|PROT_WRITE, MAP_SHARED,
1395  m_fd, vbuf.m.offset);
1396 
1397  if (buffers[i] == MAP_FAILED)
1398  {
1399  LOG(VB_GENERAL, LOG_ERR, LOC + "mmap: " + ENO);
1400  LOG(VB_GENERAL, LOG_ERR, LOC + "Memory map failed");
1401  m_error = "Memory map failed";
1402  return;
1403  }
1404  bufferlen[i] = vbuf.length;
1405  }
1406 
1407  for (uint i = 0; i < numbuffers; i++)
1408  {
1409  memset(buffers[i], 0, bufferlen[i]);
1410  vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1411  vbuf.index = i;
1412  if (ioctl(m_fd, VIDIOC_QBUF, &vbuf) < 0)
1413  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to enqueue capture buffer (VIDIOC_QBUF failed) " + ENO);
1414  }
1415 
1416  int turnon = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1417  if (ioctl(m_fd, VIDIOC_STREAMON, &turnon) < 0)
1418  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to start capture (VIDIOC_STREAMON failed) " + ENO);
1419 
1420  struct timeval tv;
1421  fd_set rdset {};
1422  int frame = 0;
1423  bool forcekey = false;
1424 
1425  m_resetcapture = false;
1426 
1427  // setup pixel format conversions for YUYV and UYVY
1428  uint8_t *output_buffer = nullptr;
1429  struct SwsContext *convert_ctx = nullptr;
1430  AVFrame img_out;
1431  if (m_v4l2_pixelformat == V4L2_PIX_FMT_YUYV ||
1432  m_v4l2_pixelformat == V4L2_PIX_FMT_UYVY)
1433  {
1434  AVPixelFormat in_pixfmt = m_v4l2_pixelformat == V4L2_PIX_FMT_YUYV ?
1435  AV_PIX_FMT_YUYV422 :
1436  AV_PIX_FMT_UYVY422;
1437 
1438  output_buffer = (uint8_t*)av_malloc(m_height * m_width * 3 / 2);
1439  if (!output_buffer)
1440  {
1441  m_error = "Cannot initialize image conversion buffer";
1442  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1443  return;
1444  }
1445 
1446  convert_ctx = sws_getCachedContext(convert_ctx, m_width, m_height, in_pixfmt,
1447  m_width, m_height, AV_PIX_FMT_YUV420P,
1448  SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
1449  if (!convert_ctx)
1450  {
1451  m_error = "Cannot initialize image conversion context";
1452  av_free(output_buffer);
1453  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1454  return;
1455  }
1456 
1457  av_image_fill_arrays(img_out.data, img_out.linesize,
1458  output_buffer, AV_PIX_FMT_YUV420P, m_width, m_height, IMAGE_ALIGN);
1459  }
1460 
1461  while (IsRecordingRequested() && !IsErrored())
1462  {
1463 again:
1464  {
1465  QMutexLocker locker(&m_pauseLock);
1466  if (m_request_pause)
1467  {
1468  if (!m_mainpaused)
1469  {
1470  m_mainpaused = true;
1471  m_pauseWait.wakeAll();
1472  if (IsPaused(true) && m_tvrec)
1474  }
1475  m_unpauseWait.wait(&m_pauseLock, 100);
1476  if (m_cleartimeonpause)
1477  gettimeofday(&m_stm, &m_tzone);
1478  continue;
1479  }
1480 
1481  if (!m_request_pause && m_mainpaused)
1482  {
1483  m_mainpaused = false;
1484  m_unpauseWait.wakeAll();
1485  }
1486  }
1487 
1488  if (m_resetcapture)
1489  {
1490  LOG(VB_GENERAL, LOG_ERR, LOC + "Resetting and re-queueing");
1491  turnon = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1492  if (ioctl(m_fd, VIDIOC_STREAMOFF, &turnon) < 0)
1493  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to stop capture (VIDIOC_STREAMOFF failed) " + ENO);
1494 
1495  for (uint i = 0; i < numbuffers; i++)
1496  {
1497  memset(buffers[i], 0, bufferlen[i]);
1498  vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1499  vbuf.index = i;
1500  if (ioctl(m_fd, VIDIOC_QBUF, &vbuf) < 0)
1501  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to enqueue capture buffer (VIDIOC_QBUF failed) " + ENO);
1502  }
1503 
1504  if (ioctl(m_fd, VIDIOC_STREAMON, &turnon) < 0)
1505  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to start capture (VIDIOC_STREAMON failed) " + ENO);
1506  m_resetcapture = false;
1507  }
1508 
1509  tv.tv_sec = 5;
1510  tv.tv_usec = 0;
1511  FD_ZERO(&rdset); // NOLINT(readability-isolate-declaration)
1512  FD_SET(m_fd, &rdset);
1513 
1514  switch (select(m_fd+1, &rdset, nullptr, nullptr, &tv))
1515  {
1516  case -1:
1517  if (errno == EINTR)
1518  goto again;
1519  LOG(VB_GENERAL, LOG_ERR, LOC + "select: " + ENO);
1520  continue;
1521  case 0:
1522  LOG(VB_GENERAL, LOG_INFO, LOC + "select timeout");
1523  continue;
1524  default: break;
1525  }
1526 
1527  memset(&vbuf, 0, sizeof(vbuf));
1528  vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1529  vbuf.memory = V4L2_MEMORY_MMAP;
1530  if (ioctl(m_fd, VIDIOC_DQBUF, &vbuf) < 0)
1531  {
1532  LOG(VB_GENERAL, LOG_ERR, LOC + "DQBUF ioctl failed." + ENO);
1533 
1534  // EIO failed DQBUF de-tunes post 2.6.15.3 for cx88
1535  // EIO or EINVAL on bttv means we need to reset the buffers..
1536  if (errno == EIO && m_channelObj)
1537  {
1538  m_channelObj->Retune();
1539  m_resetcapture = true;
1540  continue;
1541  }
1542 
1543  if (errno == EIO || errno == EINVAL)
1544  {
1545  m_resetcapture = true;
1546  continue;
1547  }
1548 
1549  if (errno == EAGAIN)
1550  continue;
1551  }
1552 
1553  frame = vbuf.index;
1554  if (m_go7007)
1555  forcekey = ((vbuf.flags & V4L2_BUF_FLAG_KEYFRAME) != 0U);
1556 
1557  if (!m_request_pause)
1558  {
1559  if ((m_v4l2_pixelformat == V4L2_PIX_FMT_YUYV) &&
1560  (output_buffer != nullptr))
1561  {
1562  AVFrame img_in;
1563  av_image_fill_arrays(img_in.data, img_in.linesize,
1564  buffers[frame], AV_PIX_FMT_YUYV422, m_width, m_height,
1565  IMAGE_ALIGN);
1566  sws_scale(convert_ctx, img_in.data, img_in.linesize,
1567  0, m_height, img_out.data, img_out.linesize);
1568  BufferIt(output_buffer, m_video_buffer_size);
1569  }
1570  else if ((m_v4l2_pixelformat == V4L2_PIX_FMT_UYVY) &&
1571  (output_buffer != nullptr))
1572  {
1573  AVFrame img_in;
1574  av_image_fill_arrays(img_in.data, img_in.linesize,
1575  buffers[frame], AV_PIX_FMT_UYVY422, m_width, m_height,
1576  IMAGE_ALIGN);
1577  sws_scale(convert_ctx, img_in.data, img_in.linesize,
1578  0, m_height, img_out.data, img_out.linesize);
1579  BufferIt(output_buffer, m_video_buffer_size);
1580  }
1581  else
1582  {
1583  // buffer the frame directly
1584  BufferIt(buffers[frame], vbuf.bytesused, forcekey);
1585  }
1586  }
1587 
1588  vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1589  if (ioctl(m_fd, VIDIOC_QBUF, &vbuf) < 0)
1590  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to enqueue capture buffer (VIDIOC_QBUF failed) " + ENO);
1591  }
1592 
1593  KillChildren();
1594 
1595  if (ioctl(m_fd, VIDIOC_STREAMOFF, &turnon) < 0)
1596  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to stop capture (VIDIOC_STREAMOFF failed) " + ENO);
1597 
1598  for (uint i = 0; i < numbuffers; i++)
1599  {
1600  munmap(buffers[i], bufferlen[i]);
1601  }
1602 
1603  FinishRecording();
1604 
1605  av_free(output_buffer);
1606  sws_freeContext(convert_ctx);
1607 
1608  close(m_fd);
1609  close(m_channelfd);
1610 }
1611 #else // if !USING_V4L2
1612 void NuppelVideoRecorder::DoV4L2(void) {}
1613 #endif // !USING_V4L2
1614 
1615 #ifdef USING_V4L1
1617 {
1618  struct mjpeg_params bparm;
1619 
1620  if (ioctl(m_fd, MJPIOC_G_PARAMS, &bparm) < 0)
1621  {
1622  LOG(VB_GENERAL, LOG_ERR, LOC + "MJPIOC_G_PARAMS: " + ENO);
1623  return;
1624  }
1625 
1626  //bparm.input = 2;
1627  //bparm.norm = 1;
1628  bparm.quality = m_hmjpg_quality;
1629 
1631  {
1633  }
1634  else
1635  {
1636  bparm.decimation = 0;
1637  bparm.HorDcm = m_hmjpg_hdecimation;
1638  bparm.VerDcm = (m_hmjpg_vdecimation + 1) / 2;
1639 
1640  if (m_hmjpg_vdecimation == 1)
1641  {
1642  bparm.TmpDcm = 1;
1643  bparm.field_per_buff = 2;
1644  }
1645  else
1646  {
1647  bparm.TmpDcm = 2;
1648  bparm.field_per_buff = 1;
1649  }
1650 
1651  bparm.img_width = m_hmjpg_maxw;
1652 
1653  if (m_ntsc)
1654  bparm.img_height = 240;
1655  else
1656  bparm.img_height = 288;
1657 
1658  bparm.img_x = 0;
1659  bparm.img_y = 0;
1660  }
1661 
1662  bparm.APPn = 0;
1663 
1664  if (m_hmjpg_vdecimation == 1)
1665  bparm.APP_len = 14;
1666  else
1667  bparm.APP_len = 0;
1668 
1669  bparm.odd_even = !(m_hmjpg_vdecimation > 1);
1670 
1671  for (int n = 0; n < bparm.APP_len; n++)
1672  bparm.APP_data[n] = 0;
1673 
1674  if (ioctl(m_fd, MJPIOC_S_PARAMS, &bparm) < 0)
1675  {
1676  LOG(VB_GENERAL, LOG_DEBUG, LOC + "MJPIOC_S_PARAMS: " + ENO);
1677  return;
1678  }
1679 
1680  struct mjpeg_requestbuffers breq;
1681 
1682  breq.count = 64;
1683  breq.size = 256 * 1024;
1684 
1685  if (ioctl(m_fd, MJPIOC_REQBUFS, &breq) < 0)
1686  {
1687  LOG(VB_GENERAL, LOG_DEBUG, LOC + "MJPIOC_REQBUFS: " + ENO);
1688  return;
1689  }
1690 
1691  uint8_t *MJPG_buff = (uint8_t *)mmap(0, breq.count * breq.size,
1692  PROT_READ|PROT_WRITE, MAP_SHARED, m_fd,
1693  0);
1694 
1695  if (MJPG_buff == MAP_FAILED)
1696  {
1697  LOG(VB_GENERAL, LOG_ERR, LOC + "mapping mjpeg buffers");
1698  return;
1699  }
1700 
1701  struct mjpeg_sync bsync;
1702 
1703  for (unsigned int count = 0; count < breq.count; count++)
1704  {
1705  if (ioctl(m_fd, MJPIOC_QBUF_CAPT, &count) < 0)
1706  LOG(VB_GENERAL, LOG_ERR, LOC + "MJPIOC_QBUF_CAPT: " + ENO);
1707  }
1708 
1709  while (IsRecordingRequested() && !IsErrored())
1710  {
1711  {
1712  QMutexLocker locker(&m_pauseLock);
1713  if (m_request_pause)
1714  {
1715  if (!m_mainpaused)
1716  {
1717  m_mainpaused = true;
1718  m_pauseWait.wakeAll();
1719  if (IsPaused(true) && m_tvrec)
1721  }
1722  m_unpauseWait.wait(&m_pauseLock, 100);
1723  if (m_cleartimeonpause)
1724  gettimeofday(&m_stm, &m_tzone);
1725  continue;
1726  }
1727 
1728  if (!m_request_pause && m_mainpaused)
1729  {
1730  m_mainpaused = false;
1731  m_unpauseWait.wakeAll();
1732  }
1733  }
1734 
1735  if (ioctl(m_fd, MJPIOC_SYNC, &bsync) < 0)
1736  {
1737  m_error = "MJPEG sync error";
1738  LOG(VB_GENERAL, LOG_ERR, LOC + m_error + ENO);
1739  break;
1740  }
1741 
1742  BufferIt((unsigned char *)(MJPG_buff + bsync.frame * breq.size),
1743  bsync.length);
1744 
1745  if (ioctl(m_fd, MJPIOC_QBUF_CAPT, &(bsync.frame)) < 0)
1746  {
1747  m_error = "MJPEG Capture error";
1748  LOG(VB_GENERAL, LOG_ERR, LOC + m_error + ENO);
1749  }
1750  }
1751 
1752  munmap(MJPG_buff, breq.count * breq.size);
1753  KillChildren();
1754 
1755  FinishRecording();
1756 
1757  close(m_fd);
1758 }
1759 #else // if !USING_V4L1
1760 void NuppelVideoRecorder::DoMJPEG(void) {}
1761 #endif // !USING_V4L1
1762 
1764 {
1765  {
1766  QMutexLocker locker(&m_pauseLock);
1767  m_request_helper = false;
1768  m_unpauseWait.wakeAll();
1769  }
1770 
1771  if (m_write_thread)
1772  {
1773  m_write_thread->wait();
1774  delete m_write_thread;
1775  m_write_thread = nullptr;
1776  }
1777 
1778  if (m_audio_thread)
1779  {
1780  m_audio_thread->wait();
1781  delete m_audio_thread;
1782  m_audio_thread = nullptr;
1783  }
1784 
1785  if (m_vbi_thread)
1786  {
1787  m_vbi_thread->wait();
1788  delete m_vbi_thread;
1789  m_vbi_thread = nullptr;
1790  CloseVBIDevice();
1791  }
1792 }
1793 
1794 void NuppelVideoRecorder::BufferIt(unsigned char *buf, int len, bool forcekey)
1795 {
1796  int act;
1797  long tcres;
1798  struct timeval now {};
1799 
1800  act = m_act_video_buffer;
1801 
1802  if (!videobuffer[act]->freeToBuffer) {
1803  return;
1804  }
1805 
1806  gettimeofday(&now, &m_tzone);
1807 
1808  tcres = (now.tv_sec-m_stm.tv_sec)*1000 + now.tv_usec/1000 - m_stm.tv_usec/1000;
1809 
1810  m_usebttv = 0;
1811  // here is the non preferable timecode - drop algorithm - fallback
1812  if (!m_usebttv)
1813  {
1814  if (m_tf==0)
1815  m_tf = 2;
1816  else
1817  {
1818  int fn = tcres - m_oldtc;
1819 
1820  // the difference should be less than 1,5*timeperframe or we have
1821  // missed at least one frame, this code might be inaccurate!
1822 
1823  if (m_ntsc_framerate)
1824  fn = (fn+16)/33;
1825  else
1826  fn = (fn+20)/40;
1827  if (fn<1)
1828  fn=1;
1829  m_tf += 2*fn; // two fields
1830  }
1831  }
1832 
1833  m_oldtc = tcres;
1834 
1835  if (!videobuffer[act]->freeToBuffer)
1836  {
1837  LOG(VB_GENERAL, LOG_INFO, LOC +
1838  "DROPPED frame due to full buffer in the recorder.");
1839  return; // we can't buffer the current frame
1840  }
1841 
1842  videobuffer[act]->sample = m_tf;
1843 
1844  // record the time at the start of this frame.
1845  // 'tcres' is at the end of the frame, so subtract the right # of ms
1846  videobuffer[act]->timecode = (m_ntsc_framerate) ? (tcres - 33) : (tcres - 40);
1847 
1848  memcpy(videobuffer[act]->buffer, buf, len);
1849  videobuffer[act]->bufferlen = len;
1850  videobuffer[act]->forcekey = forcekey;
1851 
1852  videobuffer[act]->freeToBuffer = 0;
1855  m_act_video_buffer = 0; // cycle to begin of buffer
1856  videobuffer[act]->freeToEncode = 1; // set last to prevent race
1857 }
1858 
1860 {
1861 #if HAVE_BIGENDIAN
1862  fh->timecode = bswap_32(fh->timecode);
1863  fh->packetlength = bswap_32(fh->packetlength);
1864 #endif
1866 }
1867 
1869 {
1870  if (newaspect == static_cast<double>(m_video_aspect))
1871  return;
1872 
1873  m_video_aspect = newaspect;
1874 
1875  struct rtframeheader frameheader {};
1876 
1877  frameheader.frametype = 'S';
1878  frameheader.comptype = 'M';
1879  frameheader.packetlength = sizeof(struct rtfileheader);
1880 
1881  WriteFrameheader(&frameheader);
1882 
1883  WriteFileHeader();
1884 }
1885 
1887 {
1888  struct rtfileheader fileheader {};
1889  static constexpr char kFinfo[12] = "MythTVVideo";
1890  static constexpr char kVers[5] = "0.07";
1891 
1892  memcpy(fileheader.finfo, kFinfo, sizeof(fileheader.finfo));
1893  memcpy(fileheader.version, kVers, sizeof(fileheader.version));
1894  fileheader.width = m_w_out;
1895  fileheader.height = (int)(m_h_out * m_height_multiplier);
1896  fileheader.desiredwidth = 0;
1897  fileheader.desiredheight = 0;
1898  fileheader.pimode = 'P';
1899  fileheader.aspect = m_video_aspect;
1900  fileheader.fps = m_video_frame_rate;
1901  fileheader.fps *= m_framerate_multiplier;
1902  fileheader.videoblocks = -1;
1903  fileheader.audioblocks = -1;
1904  fileheader.textsblocks = -1; // TODO: make only -1 if VBI support active?
1905  fileheader.keyframedist = KEYFRAMEDIST;
1906 
1907 #if HAVE_BIGENDIAN
1908  fileheader.width = bswap_32(fileheader.width);
1909  fileheader.height = bswap_32(fileheader.height);
1910  fileheader.desiredwidth = bswap_32(fileheader.desiredwidth);
1911  fileheader.desiredheight = bswap_32(fileheader.desiredheight);
1912  fileheader.aspect = bswap_dbl(fileheader.aspect);
1913  fileheader.fps = bswap_dbl(fileheader.fps);
1914  fileheader.videoblocks = bswap_32(fileheader.videoblocks);
1915  fileheader.audioblocks = bswap_32(fileheader.audioblocks);
1916  fileheader.textsblocks = bswap_32(fileheader.textsblocks);
1917  fileheader.keyframedist = bswap_32(fileheader.keyframedist);
1918 #endif
1919  m_ringBuffer->Write(&fileheader, FILEHEADERSIZE);
1920 }
1921 
1923 {
1924  struct rtframeheader frameheader {};
1925 
1926  WriteFileHeader();
1927 
1928  frameheader.frametype = 'D'; // compressor data
1929 
1930  if (m_useavcodec)
1931  {
1932  frameheader.comptype = 'F';
1933  frameheader.packetlength = m_mpa_vidctx->extradata_size;
1934 
1935  WriteFrameheader(&frameheader);
1936  m_ringBuffer->Write(m_mpa_vidctx->extradata, frameheader.packetlength);
1937  }
1938  else
1939  {
1940  static unsigned long int s_tbls[128];
1941 
1942  frameheader.comptype = 'R'; // compressor data for RTjpeg
1943  frameheader.packetlength = sizeof(s_tbls);
1944 
1945  // compression configuration header
1946  WriteFrameheader(&frameheader);
1947 
1948  memset(s_tbls, 0, sizeof(s_tbls));
1949  m_ringBuffer->Write(s_tbls, sizeof(s_tbls));
1950  }
1951 
1952  memset(&frameheader, 0, sizeof(frameheader));
1953  frameheader.frametype = 'X'; // extended data
1954  frameheader.packetlength = sizeof(extendeddata);
1955 
1956  // extended data header
1957  WriteFrameheader(&frameheader);
1958 
1959  struct extendeddata moredata {};
1960 
1961  moredata.version = 1;
1962  if (m_useavcodec)
1963  {
1964  int vidfcc = 0;
1965  switch(m_mpa_vidcodec->id)
1966  {
1967  case AV_CODEC_ID_MPEG4: vidfcc = FOURCC_DIVX; break;
1968  case AV_CODEC_ID_WMV1: vidfcc = FOURCC_WMV1; break;
1969  case AV_CODEC_ID_MSMPEG4V3: vidfcc = FOURCC_DIV3; break;
1970  case AV_CODEC_ID_MSMPEG4V2: vidfcc = FOURCC_MP42; break;
1971  case AV_CODEC_ID_MSMPEG4V1: vidfcc = FOURCC_MPG4; break;
1972  case AV_CODEC_ID_MJPEG: vidfcc = FOURCC_MJPG; break;
1973  case AV_CODEC_ID_H263:
1974  case AV_CODEC_ID_H263P: vidfcc = FOURCC_H263; break;
1975  case AV_CODEC_ID_H263I: vidfcc = FOURCC_I263; break;
1976  case AV_CODEC_ID_MPEG1VIDEO: vidfcc = FOURCC_MPEG; break;
1977  case AV_CODEC_ID_MPEG2VIDEO: vidfcc = FOURCC_MPG2; break;
1978  case AV_CODEC_ID_HUFFYUV: vidfcc = FOURCC_HFYU; break;
1979  default: break;
1980  }
1981  moredata.video_fourcc = vidfcc;
1982  moredata.lavc_bitrate = m_mpa_vidctx->bit_rate;
1983  moredata.lavc_qmin = m_mpa_vidctx->qmin;
1984  moredata.lavc_qmax = m_mpa_vidctx->qmax;
1985  moredata.lavc_maxqdiff = m_mpa_vidctx->max_qdiff;
1986  }
1987  else
1988  {
1989  moredata.video_fourcc = FOURCC_RJPG;
1990  moredata.rtjpeg_quality = m_Q;
1991  moredata.rtjpeg_luma_filter = m_M1;
1992  moredata.rtjpeg_chroma_filter = m_M2;
1993  }
1994 
1995  if (m_compressaudio)
1996  {
1997  moredata.audio_fourcc = FOURCC_LAME;
1998  moredata.audio_compression_ratio = 11;
1999  moredata.audio_quality = m_mp3quality;
2000  }
2001  else
2002  {
2003  moredata.audio_fourcc = FOURCC_RAWA;
2004  }
2005 
2007  moredata.audio_channels = m_audio_channels;
2009 
2011 
2012 #if HAVE_BIGENDIAN
2013  moredata.version = bswap_32(moredata.version);
2014  moredata.video_fourcc = bswap_32(moredata.video_fourcc);
2015  moredata.audio_fourcc = bswap_32(moredata.audio_fourcc);
2016  moredata.audio_sample_rate = bswap_32(moredata.audio_sample_rate);
2017  moredata.audio_bits_per_sample = bswap_32(moredata.audio_bits_per_sample);
2018  moredata.audio_channels = bswap_32(moredata.audio_channels);
2019  moredata.audio_compression_ratio = bswap_32(moredata.audio_compression_ratio);
2020  moredata.audio_quality = bswap_32(moredata.audio_quality);
2021  moredata.rtjpeg_quality = bswap_32(moredata.rtjpeg_quality);
2022  moredata.rtjpeg_luma_filter = bswap_32(moredata.rtjpeg_luma_filter);
2023  moredata.rtjpeg_chroma_filter = bswap_32(moredata.rtjpeg_chroma_filter);
2024  moredata.lavc_bitrate = bswap_32(moredata.lavc_bitrate);
2025  moredata.lavc_qmin = bswap_32(moredata.lavc_qmin);
2026  moredata.lavc_qmax = bswap_32(moredata.lavc_qmax);
2027  moredata.lavc_maxqdiff = bswap_32(moredata.lavc_maxqdiff);
2028  moredata.seektable_offset = bswap_64(moredata.seektable_offset);
2029  moredata.keyframeadjust_offset = bswap_64(moredata.keyframeadjust_offset);
2030 #endif
2031  m_ringBuffer->Write(&moredata, sizeof(moredata));
2032 
2033  m_last_block = 0;
2034  m_lf = 0; // that resets framenumber so that seeking in the
2035  // continues parts works too
2036 }
2037 
2039 {
2040  int numentries = m_seektable->size();
2041 
2042  struct rtframeheader frameheader {};
2043  frameheader.frametype = 'Q'; // SeekTable
2044  frameheader.packetlength = sizeof(struct seektable_entry) * numentries;
2045 
2046  long long currentpos = m_ringBuffer->GetWritePosition();
2047 
2048  m_ringBuffer->Write(&frameheader, sizeof(frameheader));
2049 
2050  char *seekbuf = new char[frameheader.packetlength];
2051  int offset = 0;
2052 
2053  auto it = m_seektable->begin();
2054  for (; it != m_seektable->end(); ++it)
2055  {
2056  memcpy(seekbuf + offset, (const void *)&(*it),
2057  sizeof(struct seektable_entry));
2058  offset += sizeof(struct seektable_entry);
2059  }
2060 
2061  m_ringBuffer->Write(seekbuf, frameheader.packetlength);
2062 
2064  offsetof(struct extendeddata, seektable_offset),
2065  SEEK_SET);
2066 
2067  m_ringBuffer->Write(&currentpos, sizeof(long long));
2068 
2069  m_ringBuffer->WriterSeek(0, SEEK_END);
2070 
2071  delete [] seekbuf;
2072 }
2073 
2075  const vector<struct kfatable_entry> &kfa_table)
2076 {
2077  int numentries = kfa_table.size();
2078 
2079  struct rtframeheader frameheader {};
2080  frameheader.frametype = 'K'; // KFA Table
2081  frameheader.packetlength = sizeof(struct kfatable_entry) * numentries;
2082 
2083  long long currentpos = m_ringBuffer->GetWritePosition();
2084 
2085  m_ringBuffer->Write(&frameheader, sizeof(frameheader));
2086 
2087  char *kfa_buf = new char[frameheader.packetlength];
2088  uint offset = 0;
2089 
2090  auto it = kfa_table.cbegin();
2091  for (; it != kfa_table.cend() ; ++it)
2092  {
2093  memcpy(kfa_buf + offset, &(*it),
2094  sizeof(struct kfatable_entry));
2095  offset += sizeof(struct kfatable_entry);
2096  }
2097 
2098  m_ringBuffer->Write(kfa_buf, frameheader.packetlength);
2099 
2100 
2102  offsetof(struct extendeddata, keyframeadjust_offset),
2103  SEEK_SET);
2104 
2105  m_ringBuffer->Write(&currentpos, sizeof(long long));
2106 
2107  m_ringBuffer->WriterSeek(0, SEEK_END);
2108 
2109  delete [] kfa_buf;
2110 }
2111 
2112 void NuppelVideoRecorder::UpdateSeekTable(int frame_num, long offset)
2113 {
2114  long long position = m_ringBuffer->GetWritePosition() + offset;
2115  struct seektable_entry ste { position, frame_num};
2116  m_seektable->push_back(ste);
2117 
2118  m_positionMapLock.lock();
2119  if (!m_positionMap.contains(ste.keyframe_number))
2120  {
2121  m_positionMapDelta[ste.keyframe_number] = position;
2122  m_positionMap[ste.keyframe_number] = position;
2123  m_lastPositionMapPos = position;
2124  }
2125  m_positionMapLock.unlock();
2126 }
2127 
2129 {
2130  m_framesWritten = 0;
2131 
2132  if (!m_ringBuffer)
2133  {
2134  LOG(VB_GENERAL, LOG_ERR, LOC +
2135  "No ringbuffer, recorder wasn't initialized.");
2136  return -1;
2137  }
2138 
2139  if (!m_ringBuffer->IsOpen())
2140  {
2141  LOG(VB_GENERAL, LOG_ERR, LOC + "Ringbuffer isn't open");
2142  return -1;
2143  }
2144 
2145  WriteHeader();
2146 
2147  return 0;
2148 }
2149 
2151 {
2152  ResetForNewFile();
2153 
2154  for (int i = 0; i < m_video_buffer_count; i++)
2155  {
2156  vidbuffertype *vidbuf = videobuffer[i];
2157  vidbuf->sample = 0;
2158  vidbuf->timecode = 0;
2159  vidbuf->freeToEncode = 0;
2160  vidbuf->freeToBuffer = 1;
2161  vidbuf->forcekey = 0;
2162  }
2163 
2164  for (int i = 0; i < m_audio_buffer_count; i++)
2165  {
2166  audbuffertype *audbuf = audiobuffer[i];
2167  audbuf->sample = 0;
2168  audbuf->timecode = 0;
2169  audbuf->freeToEncode = 0;
2170  audbuf->freeToBuffer = 1;
2171  }
2172 
2173  for (int i = 0; i < m_text_buffer_count; i++)
2174  {
2175  txtbuffertype *txtbuf = textbuffer[i];
2176  txtbuf->freeToEncode = 0;
2177  txtbuf->freeToBuffer = 1;
2178  }
2179 
2180  m_act_video_encode = 0;
2181  m_act_video_buffer = 0;
2182  m_act_audio_encode = 0;
2183  m_act_audio_buffer = 0;
2184  m_act_audio_sample = 0;
2185  m_act_text_encode = 0;
2186  m_act_text_buffer = 0;
2187 
2188  m_audiobytes = 0;
2189  m_effectivedsp = 0;
2190 
2191  if (m_useavcodec)
2193 
2194  if (m_curRecording)
2196 }
2197 
2199 {
2200  if (!m_audio_device)
2201  {
2202  LOG(VB_GENERAL, LOG_ERR, LOC +
2203  QString("Invalid audio device (%1), exiting").arg(m_audiodevice));
2204  return;
2205  }
2206 
2208  {
2209  LOG(VB_GENERAL, LOG_ERR, LOC +
2210  QString("Failed to open audio device %1").arg(m_audiodevice));
2211  return;
2212  }
2213 
2214  if (!m_audio_device->Start())
2215  {
2216  LOG(VB_GENERAL, LOG_ERR, LOC +
2217  QString("Failed to start audio capture on %1").arg(m_audiodevice));
2218  return;
2219  }
2220 
2221  struct timeval anow {};
2222  auto *buffer = new unsigned char[m_audio_buffer_size];
2224 
2225  while (IsHelperRequested() && !IsErrored())
2226  {
2227  {
2228  QMutexLocker locker(&m_pauseLock);
2229  if (m_request_pause)
2230  {
2231  if (!m_audiopaused)
2232  {
2233  m_audiopaused = true;
2234  m_pauseWait.wakeAll();
2235  if (IsPaused(true) && m_tvrec)
2237  }
2238  m_unpauseWait.wait(&m_pauseLock, 100);
2239  continue;
2240  }
2241 
2243  {
2244  m_audiopaused = false;
2245  m_unpauseWait.wakeAll();
2246  }
2247  }
2248 
2249  if (!IsHelperRequested() || IsErrored())
2250  break;
2251 
2252  int lastread = m_audio_device->GetSamples(buffer, m_audio_buffer_size);
2253  if (m_audio_buffer_size != lastread)
2254  {
2255  LOG(VB_GENERAL, LOG_ERR, LOC +
2256  QString("Short read, %1 of %2 bytes from ")
2257  .arg(lastread).arg(m_audio_buffer_size) + m_audiodevice);
2258  }
2259 
2260  /* record the current time */
2261  /* Don't assume that the sound device's record buffer is empty
2262  (like we used to.) Measure to see how much stuff is in there,
2263  and correct for it when calculating the timestamp */
2264  gettimeofday(&anow, &m_tzone);
2265  int bytes_read = max(m_audio_device->GetNumReadyBytes(), 0);
2266 
2267  int act = m_act_audio_buffer;
2268 
2269  if (!audiobuffer[act]->freeToBuffer)
2270  {
2271  LOG(VB_GENERAL, LOG_ERR, LOC + "Ran out of free AUDIO buffers :-(");
2273  continue;
2274  }
2275 
2276  audiobuffer[act]->sample = m_act_audio_sample;
2277 
2278  /* calculate timecode. First compute the difference
2279  between now and stm (start time) */
2280  audiobuffer[act]->timecode = (anow.tv_sec - m_stm.tv_sec) * 1000 +
2281  anow.tv_usec / 1000 - m_stm.tv_usec / 1000;
2282  /* We want the timestamp to point to the start of this
2283  audio chunk. So, subtract off the length of the chunk
2284  and the length of audio still in the capture buffer. */
2285  audiobuffer[act]->timecode -= (int)(
2286  (bytes_read + m_audio_buffer_size)
2288 
2289  memcpy(audiobuffer[act]->buffer, buffer, m_audio_buffer_size);
2290 
2291  audiobuffer[act]->freeToBuffer = 0;
2294  m_act_audio_buffer = 0;
2295  audiobuffer[act]->freeToEncode = 1;
2296 
2298  }
2299 
2300  delete [] buffer;
2301 
2302  if (m_audio_device->IsOpen())
2303  m_audio_device->Close();
2304 }
2305 
2306 #ifdef USING_V4L2
2308 {
2309  struct timeval tnow {};
2310  gettimeofday(&tnow, &m_tzone);
2311 
2312  int act = m_act_text_buffer;
2313  if (!textbuffer[act]->freeToBuffer)
2314  {
2315  LOG(VB_GENERAL, LOG_ERR, LOC +
2316  QString("Teletext #%1: ran out of free TEXT buffers :-(").arg(act));
2317  return;
2318  }
2319 
2320  // calculate timecode:
2321  // compute the difference between now and stm (start time)
2322  textbuffer[act]->timecode = (tnow.tv_sec-m_stm.tv_sec) * 1000 +
2323  tnow.tv_usec/1000 - m_stm.tv_usec/1000;
2324  textbuffer[act]->pagenr = (vbidata->teletextpage.pgno << 16) +
2325  vbidata->teletextpage.subno;
2326 
2327  unsigned char *inpos = vbidata->teletextpage.data[0];
2328  unsigned char *outpos = textbuffer[act]->buffer;
2329  *outpos = 0;
2330  struct teletextsubtitle st {};
2331  unsigned char linebuf[VT_WIDTH + 1];
2332  unsigned char *linebufpos = linebuf;
2333 
2334  for (int y = 0; y < VT_HEIGHT; y++)
2335  {
2336  char c = ' ';
2337  char last_c = ' ';
2338  int hid = 0;
2339  int gfx = 0;
2340  int dbl = 0;
2341  int box = 0;
2342  int sep = 0;
2343  int hold = 0;
2344  int visible = 0;
2345  int fg = 7;
2346  int bg = 0;
2347 
2348  for (int x = 0; x < VT_WIDTH; ++x)
2349  {
2350  c = *inpos++;
2351  switch (c)
2352  {
2353  case 0x00 ... 0x07: /* alpha + fg color */
2354  fg = c & 7;
2355  gfx = 0;
2356  sep = 0;
2357  hid = 0;
2358  goto ctrl;
2359  case 0x08: /* flash */
2360  case 0x09: /* steady */
2361  goto ctrl;
2362  case 0x0a: /* end box */
2363  box = 0;
2364  goto ctrl;
2365  case 0x0b: /* start box */
2366  box = 1;
2367  goto ctrl;
2368  case 0x0c: /* normal height */
2369  dbl = 0;
2370  goto ctrl;
2371  case 0x0d: /* double height */
2372  if (y < VT_HEIGHT-2) /* ignored on last 2 lines */
2373  {
2374  dbl = 1;
2375  }
2376  goto ctrl;
2377  case 0x10 ... 0x17: /* gfx + fg color */
2378  fg = c & 7;
2379  gfx = 1;
2380  hid = 0;
2381  goto ctrl;
2382  case 0x18: /* conceal */
2383  hid = 1;
2384  goto ctrl;
2385  case 0x19: /* contiguous gfx */
2386  hid = 0;
2387  sep = 0;
2388  goto ctrl;
2389  case 0x1a: /* separate gfx */
2390  sep = 1;
2391  goto ctrl;
2392  case 0x1c: /* black bf */
2393  bg = 0;
2394  goto ctrl;
2395  case 0x1d: /* new bg */
2396  bg = fg;
2397  goto ctrl;
2398  case 0x1e: /* hold gfx */
2399  hold = 1;
2400  goto ctrl;
2401  case 0x1f: /* release gfx */
2402  hold = 0;
2403  goto ctrl;
2404  case 0x0e: /* SO */
2405  case 0x0f: /* SI */
2406  case 0x1b: /* ESC */
2407  goto ctrl;
2408 
2409  ctrl:
2410  c = ' ';
2411  if (hold && gfx)
2412  c = last_c;
2413  break;
2414  }
2415  if (gfx)
2416  if ((c & 0xa0) == 0x20)
2417  {
2418  last_c = c;
2419  c += (c & 0x40) ? 32 : -32;
2420  }
2421  if (hid)
2422  c = ' ';
2423 
2424  if (visible || (c != ' '))
2425  {
2426  if (!visible)
2427  {
2428  st.row = y;
2429  st.col = x;
2430  st.dbl = dbl;
2431  st.fg = fg;
2432  st.bg = bg;
2433  linebufpos = linebuf;
2434  *linebufpos = 0;
2435  }
2436  *linebufpos++ = c;
2437  *linebufpos = 0;
2438  visible = 1;
2439  }
2440 
2441  (void) box;
2442  (void) sep;
2443  }
2444  if (visible)
2445  {
2446  st.len = linebufpos - linebuf + 1;;
2447  int max = 200;
2448  int bufsize = ((outpos - textbuffer[act]->buffer + 1) + st.len);
2449  if (bufsize > max)
2450  break;
2451  memcpy(outpos, &st, sizeof(st));
2452  outpos += sizeof(st);
2453  if (st.len < 42)
2454  {
2455  memcpy(outpos, linebuf, st.len);
2456  outpos += st.len;
2457  }
2458  else
2459  {
2460  memcpy(outpos, linebuf, 41);
2461  outpos += 41;
2462  }
2463  *outpos = 0;
2464  }
2465  }
2466 
2467  textbuffer[act]->bufferlen = outpos - textbuffer[act]->buffer + 1;
2468  textbuffer[act]->freeToBuffer = 0;
2471  m_act_text_buffer = 0;
2472  textbuffer[act]->freeToEncode = 1;
2473 }
2474 #else // USING_V4L2
2475 void NuppelVideoRecorder::FormatTT(struct VBIData*) {}
2476 #endif // USING_V4L2
2477 
2479 {
2480  struct timeval tnow {};
2481  gettimeofday (&tnow, &m_tzone);
2482 
2483  // calculate timecode:
2484  // compute the difference between now and stm (start time)
2485  int tc = (tnow.tv_sec - m_stm.tv_sec) * 1000 +
2486  tnow.tv_usec / 1000 - m_stm.tv_usec / 1000;
2487 
2488  m_ccd->FormatCC(tc, code1, code2);
2489 }
2490 
2491 void NuppelVideoRecorder::AddTextData(unsigned char *buf, int len,
2492  int64_t timecode, char /*type*/)
2493 {
2494  int act = m_act_text_buffer;
2495  if (!textbuffer[act]->freeToBuffer)
2496  {
2497  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Teletext#%1").arg(act) +
2498  " ran out of free TEXT buffers :-(");
2499  return;
2500  }
2501 
2502  textbuffer[act]->timecode = timecode;
2503  memcpy(textbuffer[act]->buffer, buf, len);
2504  textbuffer[act]->bufferlen = len + sizeof(ccsubtitle);
2505 
2506  textbuffer[act]->freeToBuffer = 0;
2509  m_act_text_buffer = 0;
2510  textbuffer[act]->freeToEncode = 1;
2511 }
2512 
2514 {
2515  while (IsHelperRequested() && !IsErrored())
2516  {
2517  {
2518  QMutexLocker locker(&m_pauseLock);
2519  if (m_request_pause)
2520  {
2521  if (!m_writepaused)
2522  {
2523  m_writepaused = true;
2524  m_pauseWait.wakeAll();
2525  if (IsPaused(true) && m_tvrec)
2527  }
2528  m_unpauseWait.wait(&m_pauseLock, 100);
2529  continue;
2530  }
2531 
2533  {
2534  m_writepaused = false;
2535  m_unpauseWait.wakeAll();
2536  }
2537  }
2538 
2539  if (!IsHelperRequested() || IsErrored())
2540  break;
2541 
2543 
2544  enum
2545  { ACTION_NONE,
2546  ACTION_VIDEO,
2547  ACTION_AUDIO,
2548  ACTION_TEXT
2549  } action = ACTION_NONE;
2550  int firsttimecode = -1;
2551 
2552  if (videobuffer[m_act_video_encode]->freeToEncode)
2553  {
2554  action = ACTION_VIDEO;
2555  firsttimecode = videobuffer[m_act_video_encode]->timecode;
2556  }
2557 
2558  if (m_audio_buffer_count &&
2559  audiobuffer[m_act_audio_encode]->freeToEncode &&
2560  (action == ACTION_NONE ||
2561  (audiobuffer[m_act_audio_encode]->timecode < firsttimecode)))
2562  {
2563  action = ACTION_AUDIO;
2564  firsttimecode = audiobuffer[m_act_audio_encode]->timecode;
2565  }
2566 
2567  if (m_text_buffer_count &&
2568  textbuffer[m_act_text_encode]->freeToEncode &&
2569  (action == ACTION_NONE ||
2570  (textbuffer[m_act_text_encode]->timecode < firsttimecode)))
2571  {
2572  action = ACTION_TEXT;
2573  }
2574 
2575  switch (action)
2576  {
2577  case ACTION_VIDEO:
2578  {
2579  VideoFrame frame;
2580  init(&frame,
2583 
2584  frame.frameNumber = videobuffer[m_act_video_encode]->sample;
2585  frame.timecode = videobuffer[m_act_video_encode]->timecode;
2586  frame.forcekey = videobuffer[m_act_video_encode]->forcekey;
2587 
2588  WriteVideo(&frame);
2589 
2590  videobuffer[m_act_video_encode]->sample = 0;
2591  videobuffer[m_act_video_encode]->freeToEncode = 0;
2592  videobuffer[m_act_video_encode]->freeToBuffer = 1;
2593  videobuffer[m_act_video_encode]->forcekey = 0;
2596  m_act_video_encode = 0;
2597  break;
2598  }
2599  case ACTION_AUDIO:
2600  {
2603  audiobuffer[m_act_audio_encode]->timecode);
2604  if (IsErrored()) {
2605  LOG(VB_GENERAL, LOG_ERR, LOC +
2606  "ACTION_AUDIO cannot be completed due to error.");
2607  StopRecording();
2608  break;
2609  }
2610  audiobuffer[m_act_audio_encode]->sample = 0;
2611  audiobuffer[m_act_audio_encode]->freeToEncode = 0;
2612  audiobuffer[m_act_audio_encode]->freeToBuffer = 1;
2615  m_act_audio_encode = 0;
2616  break;
2617  }
2618  case ACTION_TEXT:
2619  {
2621  textbuffer[m_act_text_encode]->bufferlen,
2622  textbuffer[m_act_text_encode]->timecode,
2623  textbuffer[m_act_text_encode]->pagenr);
2624  textbuffer[m_act_text_encode]->freeToEncode = 0;
2625  textbuffer[m_act_text_encode]->freeToBuffer = 1;
2628  m_act_text_encode = 0;
2629  break;
2630  }
2631  default:
2632  {
2633  usleep(100);
2634  break;
2635  }
2636  }
2637  }
2638 }
2639 
2641 {
2642  m_framesWritten = 0;
2643  m_lf = 0;
2644  m_last_block = 0;
2645 
2646  m_seektable->clear();
2647 
2648  ClearStatistics();
2649 
2650  m_positionMapLock.lock();
2651  m_positionMap.clear();
2652  m_positionMapDelta.clear();
2653  m_positionMapLock.unlock();
2654 
2655  if (m_go7007)
2656  m_resetcapture = true;
2657 }
2658 
2660 {
2661  CreateNuppelFile();
2662 }
2663 
2665 {
2667 
2668  WriteSeekTable();
2669 
2671 
2672  m_positionMapLock.lock();
2673  m_positionMap.clear();
2674  m_positionMapDelta.clear();
2675  m_positionMapLock.unlock();
2676 }
2677 
2678 void NuppelVideoRecorder::WriteVideo(VideoFrame *frame, bool skipsync,
2679  bool forcekey)
2680 {
2681  int tmp = 0;
2682  lzo_uint out_len = OUT_LEN;
2683  struct rtframeheader frameheader {};
2684  int raw = 0;
2685  int compressthis = m_compression;
2686  // cppcheck-suppress variableScope
2687  uint8_t *planes[3] = {
2688  frame->buf + frame->offsets[0],
2689  frame->buf + frame->offsets[1],
2690  frame->buf + frame->offsets[2] };
2691  int fnum = frame->frameNumber;
2692  long long timecode = frame->timecode;
2693 
2694  if (m_lf == 0)
2695  { // this will be triggered every new file
2696  m_lf = fnum;
2697  m_startnum = fnum;
2698  m_lasttimecode = 0;
2699  m_frameofgop = 0;
2700  forcekey = true;
2701  }
2702 
2703  // see if it's time for a seeker header, sync information and a keyframe
2704  frameheader.keyframe = m_frameofgop; // no keyframe defaulted
2705 
2706  bool wantkeyframe = forcekey;
2707 
2708  bool writesync = false;
2709 
2710  if ((!m_go7007 && (((fnum-m_startnum)>>1) % m_keyframedist == 0 && !skipsync)) ||
2711  (m_go7007 && frame->forcekey))
2712  writesync = true;
2713 
2714  if (writesync)
2715  {
2716  m_ringBuffer->Write("RTjjjjjjjjjjjjjjjjjjjjjjjj", FRAMEHEADERSIZE);
2717 
2718  UpdateSeekTable(((fnum - m_startnum) >> 1) / m_keyframedist);
2719 
2720  frameheader.frametype = 'S'; // sync frame
2721  frameheader.comptype = 'V'; // video sync information
2722  frameheader.filters = 0; // no filters applied
2723  frameheader.packetlength = 0; // no data packet
2724  frameheader.timecode = (fnum-m_startnum)>>1;
2725  // write video sync info
2726  WriteFrameheader(&frameheader);
2727  frameheader.frametype = 'S'; // sync frame
2728  frameheader.comptype = 'A'; // video sync information
2729  frameheader.filters = 0; // no filters applied
2730  frameheader.packetlength = 0; // no data packet
2731  frameheader.timecode = m_effectivedsp; // effective dsp frequency
2732  // write audio sync info
2733  WriteFrameheader(&frameheader);
2734 
2735  wantkeyframe = true;
2736  //m_ringBuffer->Sync();
2737  }
2738 
2739  if (wantkeyframe)
2740  {
2741  frameheader.keyframe=0;
2742  m_frameofgop=0;
2743  }
2744 
2745  if (m_useavcodec)
2746  {
2747  MythAVFrame mpa_picture;
2748  AVPictureFill(mpa_picture, frame);
2749 
2750  if (wantkeyframe)
2751  mpa_picture->pict_type = AV_PICTURE_TYPE_I;
2752  else
2753  mpa_picture->pict_type = AV_PICTURE_TYPE_NONE;
2754 
2755  if (!m_hardware_encode)
2756  {
2757  AVPacket packet;
2758  av_init_packet(&packet);
2759  packet.data = (uint8_t *)m_strm;
2760  packet.size = frame->size;
2761 
2762  int got_packet = 0;
2763 
2764  QMutexLocker locker(avcodeclock);
2765  tmp = avcodec_encode_video2(m_mpa_vidctx, &packet, mpa_picture,
2766  &got_packet);
2767 
2768  if (tmp < 0 || !got_packet)
2769  {
2770  LOG(VB_GENERAL, LOG_ERR, LOC +
2771  "WriteVideo : avcodec_encode_video() failed");
2772  return;
2773  }
2774 
2775  tmp = packet.size;
2776  }
2777  }
2778  else
2779  {
2780  int freecount = 0;
2781  freecount = m_act_video_buffer > m_act_video_encode ?
2784 
2785  if (freecount < (m_video_buffer_count / 3))
2786  compressthis = 0; // speed up the encode process
2787 
2788  if (freecount < 5)
2789  raw = 1; // speed up the encode process
2790 
2791  if (m_transcoding)
2792  {
2793  raw = 0;
2794  compressthis = 1;
2795  }
2796 
2797  if (!raw)
2798  {
2799  if (wantkeyframe)
2800  m_rtjc->SetNextKey();
2802  }
2803  else
2804  tmp = frame->size;
2805 
2806  // here is lzo compression afterwards
2807  if (compressthis)
2808  {
2809  int r = 0;
2810  if (raw)
2811  r = lzo1x_1_compress(frame->buf, frame->size,
2812  m_out, &out_len, wrkmem);
2813  else
2814  r = lzo1x_1_compress((unsigned char *)m_strm, tmp, m_out,
2815  &out_len, wrkmem);
2816  if (r != LZO_E_OK)
2817  {
2818  LOG(VB_GENERAL, LOG_ERR, LOC + "lzo compression failed");
2819  return;
2820  }
2821  }
2822  }
2823 
2824  frameheader.frametype = 'V'; // video frame
2825  frameheader.timecode = timecode;
2826  m_lasttimecode = frameheader.timecode;
2827  frameheader.filters = 0; // no filters applied
2828 
2829  // compr ends here
2830  if (m_useavcodec)
2831  {
2832  if (m_mpa_vidcodec->id == AV_CODEC_ID_RAWVIDEO)
2833  {
2834  frameheader.comptype = '0';
2835  frameheader.packetlength = frame->size;
2836  WriteFrameheader(&frameheader);
2837  m_ringBuffer->Write(frame->buf, frame->size);
2838  }
2839  else if (m_hardware_encode)
2840  {
2841  frameheader.comptype = '4';
2842  frameheader.packetlength = frame->size;
2843  WriteFrameheader(&frameheader);
2844  m_ringBuffer->Write(frame->buf, frame->size);
2845  }
2846  else
2847  {
2848  frameheader.comptype = '4';
2849  frameheader.packetlength = tmp;
2850  WriteFrameheader(&frameheader);
2852  }
2853  }
2854  else if (compressthis == 0 || (tmp < (int)out_len))
2855  {
2856  if (!raw)
2857  {
2858  frameheader.comptype = '1'; // video compression: RTjpeg only
2859  frameheader.packetlength = tmp;
2860  WriteFrameheader(&frameheader);
2862  }
2863  else
2864  {
2865  frameheader.comptype = '0'; // raw YUV420
2866  frameheader.packetlength = frame->size;
2867  WriteFrameheader(&frameheader);
2868  m_ringBuffer->Write(frame->buf, frame->size); // we write buf directly
2869  }
2870  }
2871  else
2872  {
2873  if (!raw)
2874  frameheader.comptype = '2'; // video compression: RTjpeg with lzo
2875  else
2876  frameheader.comptype = '3'; // raw YUV420 with lzo
2877  frameheader.packetlength = out_len;
2878  WriteFrameheader(&frameheader);
2879  m_ringBuffer->Write(m_out, out_len);
2880  }
2881 
2882  if (m_framesWritten == 0)
2883  SendMythSystemRecEvent("REC_STARTED_WRITING", m_curRecording);
2884 
2885  m_frameofgop++;
2886  m_framesWritten++;
2887 
2888  // now we reset the last frame number so that we can find out
2889  // how many frames we didn't get next time
2890  m_lf = fnum;
2891 }
2892 
2893 #if HAVE_BIGENDIAN
2894 static void bswap_16_buf(short int *buf, int buf_cnt, int audio_channels)
2895  __attribute__ ((unused)); /* <- suppress compiler warning */
2896 
2897 static void bswap_16_buf(short int *buf, int buf_cnt, int audio_channels)
2898 {
2899  for (int i = 0; i < audio_channels * buf_cnt; i++)
2900  buf[i] = bswap_16(buf[i]);
2901 }
2902 #endif
2903 
2904 void NuppelVideoRecorder::WriteAudio(unsigned char *buf, int fnum, int timecode)
2905 {
2906  struct rtframeheader frameheader {};
2907 
2908  if (m_last_block == 0)
2909  {
2910  m_firsttc = -1;
2911  }
2912 
2913  if (m_last_block != 0)
2914  {
2915  if (fnum != (m_last_block+1))
2916  {
2917  m_audio_behind = fnum - (m_last_block+1);
2918  LOG(VB_RECORD, LOG_INFO, LOC + QString("audio behind %1 %2").
2919  arg(m_last_block).arg(fnum));
2920  }
2921  }
2922 
2923  frameheader.frametype = 'A'; // audio frame
2924  frameheader.timecode = timecode;
2925 
2926  if (m_firsttc == -1)
2927  {
2928  m_firsttc = timecode;
2929 #if 0
2930  LOG(VB_GENERAL, LOG_DEBUG, LOC +
2931  QString("first timecode=%1").arg(m_firsttc));
2932 #endif
2933  }
2934  else
2935  {
2936  timecode -= m_firsttc; // this is to avoid the lack between the beginning
2937  // of recording and the first timestamp, maybe we
2938  // can calculate the audio-video +-lack at the
2939  // beginning too
2940  auto abytes = (double)m_audiobytes; // - (double)m_audio_buffer_size;
2941  // wrong guess ;-)
2942  // need seconds instead of msec's
2943  auto mt = (double)timecode;
2944  if (mt > 0.0)
2945  {
2946  double eff = (abytes / mt) * (100000.0 / m_audio_bytes_per_sample);
2947  m_effectivedsp = (int)eff;
2948  }
2949  }
2950 
2951  if (m_compressaudio)
2952  {
2953  char mp3gapless[7200];
2954  int compressedsize = 0;
2955  int gaplesssize = 0;
2956  int lameret = 0;
2957 
2958  int sample_cnt = m_audio_buffer_size / m_audio_bytes_per_sample;
2959 
2960 #if HAVE_BIGENDIAN
2961  bswap_16_buf((short int*) buf, sample_cnt, m_audio_channels);
2962 #endif
2963 
2964  if (m_audio_channels == 2)
2965  {
2966  lameret = lame_encode_buffer_interleaved(
2967  m_gf, (short int*) buf, sample_cnt,
2968  (unsigned char*) m_mp3buf, m_mp3buf_size);
2969  }
2970  else
2971  {
2972  lameret = lame_encode_buffer(
2973  m_gf, (short int*) buf, (short int*) buf, sample_cnt,
2974  (unsigned char*) m_mp3buf, m_mp3buf_size);
2975  }
2976 
2977  if (lameret < 0)
2978  {
2979  LOG(VB_GENERAL, LOG_ERR, LOC +
2980  QString("lame error '%1'").arg(lameret));
2981  m_error = QString("Audio Encoding Error '%1'")
2982  .arg(lameret);
2983  return;
2984  }
2985  compressedsize = lameret;
2986 
2987  lameret = lame_encode_flush_nogap(m_gf, (unsigned char *)mp3gapless,
2988  7200);
2989  if (lameret < 0)
2990  {
2991  LOG(VB_GENERAL, LOG_ERR, LOC +
2992  QString("lame error '%1'").arg(lameret));
2993  m_error = QString("Audio Encoding Error '%1'")
2994  .arg(lameret);
2995  return;
2996  }
2997  gaplesssize = lameret;
2998 
2999  frameheader.comptype = '3'; // audio is compressed
3000  frameheader.packetlength = compressedsize + gaplesssize;
3001 
3002  if (frameheader.packetlength > 0)
3003  {
3004  WriteFrameheader(&frameheader);
3005  m_ringBuffer->Write(m_mp3buf, compressedsize);
3006  m_ringBuffer->Write(mp3gapless, gaplesssize);
3007  }
3009  }
3010  else
3011  {
3012  frameheader.comptype = '0'; // uncompressed audio
3013  frameheader.packetlength = m_audio_buffer_size;
3014 
3015  WriteFrameheader(&frameheader);
3017  m_audiobytes += m_audio_buffer_size; // only audio no header!!
3018  }
3019 
3020  // this will probably never happen and if there would be a
3021  // 'uncountable' video frame drop -> material==worthless
3022  if (m_audio_behind > 0)
3023  {
3024  LOG(VB_RECORD, LOG_INFO, LOC + "audio behind");
3025  frameheader.frametype = 'A'; // audio frame
3026  frameheader.comptype = 'N'; // output a nullframe with
3027  frameheader.packetlength = 0;
3028  WriteFrameheader(&frameheader);
3030  m_audio_behind--;
3031  }
3032 
3033  m_last_block = fnum;
3034 }
3035 
3036 void NuppelVideoRecorder::WriteText(unsigned char *buf, int len, int timecode,
3037  int pagenr)
3038 {
3039  struct rtframeheader frameheader {};
3040 
3041  frameheader.frametype = 'T'; // text frame
3042  frameheader.timecode = timecode;
3043 
3044  if (VBIMode::PAL_TT == m_vbimode)
3045  {
3046  frameheader.comptype = 'T'; // european teletext
3047  frameheader.packetlength = len + 4;
3048  WriteFrameheader(&frameheader);
3049  union page_t {
3050  int32_t m_val32;
3051  struct { int8_t m_a,m_b,m_c,m_d; } m_val8;
3052  } v {};
3053  v.m_val32 = pagenr;
3054  m_ringBuffer->Write(&v.m_val8.m_d, sizeof(int8_t));
3055  m_ringBuffer->Write(&v.m_val8.m_c, sizeof(int8_t));
3056  m_ringBuffer->Write(&v.m_val8.m_b, sizeof(int8_t));
3057  m_ringBuffer->Write(&v.m_val8.m_a, sizeof(int8_t));
3058  m_ringBuffer->Write(buf, len);
3059  }
3060  else if (VBIMode::NTSC_CC == m_vbimode)
3061  {
3062  frameheader.comptype = 'C'; // NTSC CC
3063  frameheader.packetlength = len;
3064 
3065  WriteFrameheader(&frameheader);
3066  m_ringBuffer->Write(buf, len);
3067  }
3068 }
3069 
3070 /* vim: set expandtab tabstop=4 shiftwidth=4: */
int timecode
Definition: format.h:153
bool IsRecording(void) override
Tells whether the StartRecorder() loop is running.
int lavc_qmax
Definition: format.h:112
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
#define FOURCC_I263
Definition: fourcc.h:97
int audio_bits_per_sample
Definition: format.h:99
vector< struct audbuffertype * > audiobuffer
unsigned char m_b
Definition: ParseText.cpp:329
vector< struct vidbuffertype * > videobuffer
char version[5]
Definition: format.h:15
QString m_videocodec
Definition: recorderbase.h:310
int textsblocks
Definition: format.h:26
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: v4lrecorder.cpp:59
void WriteFrameheader(rtframeheader *fh)
unsigned long length
void SetNextKey(void)
Definition: RTjpegN.cpp:3281
virtual int GetNumReadyBytes(void)=0
void ResetForNewFile(void) override
int pgno
Definition: vt.h:38
int lavc_qmin
Definition: format.h:111
int sample
Definition: format.h:152
enum go7007_mpeg_video_standard mpeg_video_standard
Definition: go7007_myth.h:58
unsigned char * buf
Definition: mythframe.h:139
frm_pos_map_t m_positionMap
Definition: recorderbase.h:347
unsigned long frame
long long timecode
Definition: mythframe.h:149
friend class VBIThread
Definition: v4lrecorder.h:26
bool LiveMode(void) const
Returns true if this RingBuffer has been assigned a LiveTVChain.
void WriterFlush(void)
Calls ThreadedFileWriter::Flush(void)
unsigned char fg
Definition: format.h:174
void WriteKeyFrameAdjustTable(const vector< struct kfatable_entry > &kfa_table)
#define LOC
int lavc_maxqdiff
Definition: format.h:113
bool m_weMadeBuffer
Definition: recorderbase.h:305
unsigned long long m_audiobytes
QMutex m_pauseLock
Definition: recorderbase.h:326
double m_video_frame_rate
Definition: recorderbase.h:315
#define FOURCC_H263
Definition: fourcc.h:94
#define MJPIOC_SYNC
long long GetFramesWritten(void) override
Returns number of frames written to disk.
int SetIntra(int *key, int *lm, int *cm)
Definition: RTjpegN.cpp:2734
#define VT_WIDTH
Definition: vt.h:4
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
void SetOption(const QString &opt, int value) override
handles the "wait_for_seqstart" option.
virtual void Close(void)=0
void FinishRecording(void) override
Flushes the ringbuffer, and if this is not a live LiveTV recording saves the position map and filesiz...
void SetNewVideoParams(double newaspect)
struct AVFrame AVFrame
QString GetFilename(void) const
Returns name of file used by this RingBuffer.
unsigned char * buffer
Definition: format.h:145
char finfo[12]
Definition: format.h:14
int forcekey
Definition: format.h:147
char frametype
Definition: format.h:32
int lavc_bitrate
Definition: format.h:110
#define FOURCC_HFYU
Definition: fourcc.h:96
#define OUT_LEN
void WriteAudio(unsigned char *buf, int fnum, int timecode)
VideoFrameType
Definition: mythframe.h:23
void CloseVBIDevice(void)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static RingBuffer * Create(const QString &xfilename, bool write, bool usereadahead=true, int timeout_ms=kDefaultOpenTimeout, bool stream_only=false)
Creates a RingBuffer instance.
Definition: ringbuffer.cpp:104
unsigned long count
unsigned char * buffer
Definition: format.h:165
bool IsErrored(void) override
Tells us whether an unrecoverable error has been encountered.
Definition: dtvrecorder.h:48
void AspectChange(uint aspect, long long frame)
Note a change in aspect ratio in the recordedmark table.
static AudioInput * CreateDevice(const QByteArray &device)
Definition: audioinput.cpp:29
void WriteVideo(VideoFrame *frame, bool skipsync=false, bool forcekey=false)
Definition: format.h:123
int SetSize(const int *w, const int *h)
Definition: RTjpegN.cpp:2698
bool MJPEGInit(void)
Determines MJPEG capture resolution.
NuppelVideoRecorder(TVRec *rec, ChannelBase *channel)
int timecode
Definition: format.h:81
static uint planes(VideoFrameType Type)
Definition: mythframe.h:567
void WriteText(unsigned char *buf, int len, int timecode, int pagenr)
int freeToBuffer
Definition: format.h:155
static guint32 * tmp
Definition: goom_core.c:35
volatile bool m_request_helper
Definition: v4lrecorder.h:58
void AddTextData(unsigned char *, int, int64_t, char) override
bool m_request_recording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:332
virtual bool IsHelperRequested(void) const
Definition: v4lrecorder.cpp:53
char pimode
Definition: format.h:20
bool m_ntsc_framerate
Definition: recorderbase.h:314
int timecode
Definition: format.h:142
void StartNewFile(void) override
char comptype
Definition: format.h:39
int freeToEncode
Definition: format.h:143
#define FOURCC_MJPG
Definition: fourcc.h:98
vector< struct seektable_entry > * m_seektable
QWaitCondition m_unpauseWait
Definition: recorderbase.h:330
int audio_quality
Definition: format.h:104
#define GO7007IOC_S_BITRATE
Definition: go7007_myth.h:23
long long seektable_offset
Definition: format.h:115
lzo_byte m_out[OUT_LEN]
double toDouble(void) const
Definition: recorderbase.h:40
RingBuffer * m_ringBuffer
Definition: recorderbase.h:304
#define MJPIOC_REQBUFS
int videoblocks
Definition: format.h:24
vector< struct txtbuffertype * > textbuffer
long long WriterSeek(long long pos, int whence, bool has_lock=false)
Calls ThreadedFileWriter::Seek(long long,int).
#define RTJ_YUV420
Definition: RTjpegN.h:60
int AVPictureFill(AVFrame *pic, const VideoFrame *frame, AVPixelFormat fmt)
AVPictureFill Initialise AVFrame pic with content from VideoFrame frame.
Definition: mythavutil.cpp:196
unsigned char * buffer
Definition: format.h:156
AVContainer m_containerFormat
Definition: recorderbase.h:307
Abstract base class for Video4Linux based recorders.
Definition: v4lrecorder.h:24
void FinishRecording(void) override
Flushes the ringbuffer, and if this is not a live LiveTV recording saves the position map and filesiz...
int OpenVBIDevice(void)
Definition: v4lrecorder.cpp:96
int version
Definition: format.h:94
#define close
Definition: compat.h:16
QWaitCondition m_pauseWait
Definition: recorderbase.h:329
int desiredwidth
Definition: format.h:18
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:141
static void clear(SettingsMap &cache, SettingsMap &overrides, const QString &myKey)
Definition: mythdb.cpp:836
void Initialize(void) override
This is called between SetOptionsFromProfile() and run() to initialize any devices,...
void SetPositionMapType(MarkTypes type)
Set seektable type.
Definition: recorderbase.h:271
int m_encoding_thread_count
Number of threads to use for MPEG-2 and MPEG-4 encoding.
void Reset(void) override
Reset the recorder to the startup state.
char filters
Definition: format.h:71
int Compress(int8_t *sp, uint8_t **planes)
Definition: RTjpegN.cpp:3286
QString GetSetting(const QString &key, const QString &defaultval="")
int freeToBuffer
Definition: format.h:144
NVRAudioThread * m_audio_thread
void SetVideoFilters(QString &filters) override
Tells recorder which filters to use.
lame_global_flags * m_gf
#define GO7007IOC_S_MPEG_PARAMS
Definition: go7007_myth.h:87
QString m_audiodevice
Definition: v4lrecorder.h:46
void RecorderPaused(void)
This is a callback, called by the "recorder" instance when it has actually paused.
Definition: tv_rec.cpp:2936
int SetFormat(const int *fmt)
Definition: RTjpegN.cpp:2692
#define GO7007_COMP_CLOSED_GOP
Definition: go7007_myth.h:45
double aspect
Definition: format.h:22
virtual bool IsOpen(void) const =0
Returns true if open for either reading or writing.
#define FOURCC_RAWA
Definition: fourcc.h:83
void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
unsigned int uint
Definition: compat.h:140
QWaitCondition m_recordingWait
Definition: recorderbase.h:335
int freeToEncode
Definition: format.h:154
unsigned char len
Definition: format.h:176
int rtjpeg_quality
Definition: format.h:106
#define FOURCC_LAME
Definition: fourcc.h:82
#define FOURCC_WMV1
Definition: fourcc.h:104
void FrameRateChange(uint framerate, long long frame)
Note a change in video frame rate in the recordedmark table.
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
void FormatCC(uint code1, uint code2) override
long long keyframeadjust_offset
Definition: format.h:117
unsigned char dbl
Definition: format.h:173
virtual int GetBlockSize(void)=0
int GetVideoFd(void) override
Returns file descriptor of recorder device.
#define FOURCC_RJPG
Definition: fourcc.h:103
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
bool isNonzero(void) const
Definition: recorderbase.h:41
int audio_compression_ratio
Definition: format.h:103
#define FOURCC_DIVX
Definition: fourcc.h:92
unsigned char data[VT_HEIGHT][VT_WIDTH]
Definition: vt.h:43
void * av_malloc(unsigned int size)
int audio_channels
Definition: format.h:100
void UpdateSeekTable(int frame_num, long offset=0)
#define FRAMEHEADERSIZE
Definition: format.h:135
void ClearStatistics(void) override
void StopRecording(void) override
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
Definition: v4lrecorder.cpp:46
static void init(VideoFrame *vf, VideoFrameType _codec, unsigned char *_buf, int _width, int _height, int _size, const int *p=nullptr, const int *o=nullptr, float _aspect=-1.0F, double _rate=-1.0F, int _aligned=MYTH_WIDTH_ALIGNMENT)
Definition: mythframe.h:230
long long frameNumber
Definition: mythframe.h:147
vt_page teletextpage
Definition: v4lrecorder.h:19
NVRWriteThread * m_write_thread
enum go7007_aspect_ratio aspect_ratio
Definition: go7007_myth.h:40
int sample
Definition: format.h:141
#define MJPIOC_QBUF_CAPT
FrameRate m_frameRate
Definition: recorderbase.h:321
bool m_request_pause
Definition: recorderbase.h:327
void SetOptionsFromProfile(RecordingProfile *profile, const QString &videodev, const QString &audiodev, const QString &vbidev) override
Sets basic recorder options.
Definition: RTjpegN.h:64
int rtjpeg_luma_filter
Definition: format.h:107
#define KEYFRAMEDIST
#define GO7007IOC_S_COMP_PARAMS
Definition: go7007_myth.h:91
bool myth_nice(int val)
virtual bool Retune(void)
Definition: channelbase.h:87
#define VT_HEIGHT
Definition: vt.h:5
long long GetWritePosition(void) const
Returns how far into a ThreadedFileWriter file we have written.
int AudioInit(bool skipdevice=false)
int forcekey
hardware encoded .nuv
Definition: mythframe.h:156
#define FOURCC_MPG2
Definition: fourcc.h:101
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
virtual bool Start(void)=0
TVRec * m_tvrec
Definition: recorderbase.h:303
void ResolutionChange(uint width, uint height, long long frame)
Note a change in video size in the recordedmark table.
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:31
QMutex * avcodeclock
This global variable is used to makes certain calls to avlib threadsafe.
int keyframedist
Definition: format.h:27
int offsets[3]
Y, U, & V offsets.
Definition: mythframe.h:160
int video_fourcc
Definition: format.h:95
int SetQuality(int *quality)
Definition: RTjpegN.cpp:2675
uint m_videoAspect
Definition: recorderbase.h:317
void FormatTT(struct VBIData *) override
virtual bool CheckForRingBufferSwitch(void)
If requested, switch to new RingBuffer/ProgramInfo objects.
#define MAX_VIDEO_BUFFERS
char keyframe
Definition: format.h:68
#define FOURCC_MP42
Definition: fourcc.h:99
VBIThread * m_vbi_thread
Definition: v4lrecorder.h:55
frm_pos_map_t m_positionMapDelta
Definition: recorderbase.h:348
void SetIntOption(RecordingProfile *profile, const QString &name)
Convenience function used to set integer options from a profile.
virtual int GetSamples(void *buf, uint nbytes)=0
#define MJPIOC_G_PARAMS
double fps
Definition: format.h:23
int audio_sample_rate
Definition: format.h:98
void run(void) override
run() starts the recording process, and does not exit until the recording is complete.
#define FOURCC_DIV3
Definition: fourcc.h:91
static size_t GetBufferSize(VideoFrameType Type, int Width, int Height, int Aligned=MYTH_WIDTH_ALIGNMENT)
Definition: mythframe.h:655
QString m_videodevice
Definition: recorderbase.h:311
int Write(const void *buf, uint count)
Writes buffer to ThreadedFileWriter::Write(const void*,uint)
RecordingInfo * m_curRecording
Definition: recorderbase.h:323
MythAVFrame little utility class that act as a safe way to allocate an AVFrame which can then be allo...
Definition: mythavutil.h:42
void av_free(void *ptr)
int audioblocks
Definition: format.h:25
int keyframe_number
Definition: format.h:126
#define FOURCC_MPG4
Definition: fourcc.h:102
AVCodecContext * m_mpa_vidctx
int subno
Definition: vt.h:38
void FormatCC(int tc, int code1, int code2)
AVPixelFormat m_picture_format
void BufferIt(unsigned char *buf, int len=-1, bool forcekey=false)
QString m_error
non-empty iff irrecoverable recording error detected
Definition: dtvrecorder.h:162
#define MJPIOC_S_PARAMS
unsigned char bg
Definition: format.h:175
unsigned long size
virtual bool Open(uint sample_bits, uint sample_rate, uint channels)=0
int rtjpeg_chroma_filter
Definition: format.h:108
bool IsPaused(bool holding_lock=false) const override
Returns true iff recorder is paused.
int height
Definition: format.h:17
char APP_data[60]
unsigned char col
Definition: format.h:172
#define FILEHEADERSIZE
Definition: format.h:136
void ClearPositionMap(MarkTypes type) const
Definition: format.h:129
QMutex m_positionMapLock
Definition: recorderbase.h:346
int width
Definition: format.h:16
#define FOURCC_MPEG
Definition: fourcc.h:100
int audio_fourcc
Definition: format.h:96
void Pause(bool clear=true) override
Pause tells recorder to pause, it should not block.
int freeToBuffer
Definition: format.h:164
int freeToEncode
Definition: format.h:163
int desiredheight
Definition: format.h:19
unsigned char row
Definition: format.h:171
int packetlength
Definition: format.h:83
virtual bool IsOpen(void)=0
uint m_videoHeight
Definition: recorderbase.h:319
virtual bool IsRecordingRequested(void)
Tells us if StopRecording() has been called.