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_audioDevice)
110  {
111  delete m_audioDevice;
112  m_audioDevice = 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 (!m_videoBuffer.empty())
123  {
124  struct vidbuffertype *vb = m_videoBuffer.back();
125  delete [] vb->buffer;
126  delete vb;
127  m_videoBuffer.pop_back();
128  }
129  while (!m_audioBuffer.empty())
130  {
131  struct audbuffertype *ab = m_audioBuffer.back();
132  delete [] ab->buffer;
133  delete ab;
134  m_audioBuffer.pop_back();
135  }
136  while (!m_textBuffer.empty())
137  {
138  struct txtbuffertype *tb = m_textBuffer.back();
139  delete [] tb->buffer;
140  delete tb;
141  m_textBuffer.pop_back();
142  }
143 
144  if (m_mpaVidCodec)
145  {
146  QMutexLocker locker(avcodeclock);
147  avcodec_free_context(&m_mpaVidCtx);
148  }
149 
150  delete m_ccd;
151 }
152 
153 void NuppelVideoRecorder::SetOption(const QString &opt, int value)
154 {
155  if (opt == "width")
156  m_wOut = m_width = value;
157  else if (opt == "height")
158  m_hOut = 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_encodingThreadCount = value;
182  else if (opt == "mpeg4optionvhq")
183  {
184  if (value)
185  m_mbDecision = FF_MB_DECISION_RD;
186  else
187  m_mbDecision = 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_hmjpgQuality = value;
212  else if (opt == "hardwaremjpeghdecimation")
213  m_hmjpgHDecimation = value;
214  else if (opt == "hardwaremjpegvdecimation")
215  m_hmjpgVDecimation = 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_audioSampleRate = value;
222  else if (opt == "audioframesize")
223  m_audioBufferSize = value;
224  else if (opt == "pip_mode")
225  m_pipMode = value;
226  else if (opt == "inpixfmt")
227  m_inPixFmt = (VideoFrameType)value;
228  else if (opt == "skipbtaudio")
229  m_skipBtAudio = (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_requestPause = 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_mpaVidCodec)
389  {
390  QMutexLocker locker(avcodeclock);
391  avcodec_free_context(&m_mpaVidCtx);
392  }
393 
394  QByteArray vcodec = m_videocodec.toLatin1();
395  m_mpaVidCodec = avcodec_find_encoder_by_name(vcodec.constData());
396 
397  if (!m_mpaVidCodec)
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_mpaVidCtx = avcodec_alloc_context3(nullptr);
405 
406  switch (m_pictureFormat)
407  {
408  case AV_PIX_FMT_YUV420P:
409  case AV_PIX_FMT_YUV422P:
410  case AV_PIX_FMT_YUVJ420P:
411  m_mpaVidCtx->pix_fmt = m_pictureFormat;
412  break;
413  default:
414  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unknown picture format: %1")
415  .arg(m_pictureFormat));
416  }
417 
418  m_mpaVidCtx->width = m_wOut;
419  m_mpaVidCtx->height = (int)(m_height * m_heightMultiplier);
420 
421  int usebitrate = m_targetBitRate * 1000;
422  if (m_scaleBitRate)
423  {
424  float diff = (m_wOut * m_hOut) / (640.0 * 480.0);
425  usebitrate = (int)(diff * usebitrate);
426  }
427 
428  if (m_targetBitRate == -1)
429  usebitrate = -1;
430 
431  m_mpaVidCtx->time_base.den = (int)ceil(m_videoFrameRate * 100 *
433  m_mpaVidCtx->time_base.num = 100;
434 
435  // avcodec needs specific settings for mpeg2 compression
436  switch (m_mpaVidCtx->time_base.den)
437  {
438  case 2397:
439  case 2398: m_mpaVidCtx->time_base.den = 24000;
440  m_mpaVidCtx->time_base.num = 1001;
441  break;
442  case 2997:
443  case 2998: m_mpaVidCtx->time_base.den = 30000;
444  m_mpaVidCtx->time_base.num = 1001;
445  break;
446  case 5994:
447  case 5995: m_mpaVidCtx->time_base.den = 60000;
448  m_mpaVidCtx->time_base.num = 1001;
449  break;
450  }
451 
452  AVDictionary *opts = nullptr;
453 
454  m_mpaVidCtx->bit_rate = usebitrate;
455  m_mpaVidCtx->bit_rate_tolerance = usebitrate * 100;
456  m_mpaVidCtx->qmin = m_maxQuality;
457  m_mpaVidCtx->qmax = m_minQuality;
458  m_mpaVidCtx->max_qdiff = m_qualDiff;
459  m_mpaVidCtx->flags = m_mp4Opts;
460  m_mpaVidCtx->mb_decision = m_mbDecision;
461 
462  m_mpaVidCtx->qblur = 0.5;
463  m_mpaVidCtx->max_b_frames = 0;
464  m_mpaVidCtx->b_quant_factor = 0;
465  av_dict_set(&opts, "rc_strategy", "2", 0);
466  av_dict_set(&opts, "b_strategy", "0", 0);
467  m_mpaVidCtx->gop_size = 30;
468  m_mpaVidCtx->rc_max_rate = 0;
469  m_mpaVidCtx->rc_min_rate = 0;
470  m_mpaVidCtx->rc_buffer_size = 0;
471  m_mpaVidCtx->rc_override_count = 0;
472  av_dict_set(&opts, "rc_init_cplx", "0", 0);
473  m_mpaVidCtx->dct_algo = FF_DCT_AUTO;
474  m_mpaVidCtx->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_mpaVidCtx->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL;
478  m_mpaVidCtx->thread_count = m_encodingThreadCount;
479 
480  QMutexLocker locker(avcodeclock);
481 
482  if (avcodec_open2(m_mpaVidCtx, m_mpaVidCodec, &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_pictureFormat = AV_PIX_FMT_YUV420P;
496 
497  m_rtjc = new RTjpeg();
498  int setval = RTJ_YUV420;
499  m_rtjc->SetFormat(&setval);
500  setval = (int)(m_hOut * m_heightMultiplier);
501  m_rtjc->SetSize(&m_wOut, &setval);
502  m_rtjc->SetQuality(&m_q);
503  setval = 2;
504  m_rtjc->SetIntra(&setval, &m_m1, &m_m2);
505 }
506 
507 
509 {
510  int tot_height = (int)(m_height * m_heightMultiplier);
511  double aspectnum = m_wOut / (double)tot_height;
512  uint aspect = 0;
513 
514  if (aspectnum == 0.0)
515  aspect = 0;
516  else if (fabs(aspectnum - 1.3333333333333333) < 0.001)
517  aspect = 2;
518  else if (fabs(aspectnum - 1.7777777777777777) < 0.001)
519  aspect = 3;
520  else if (fabs(aspectnum - 2.21) < 0.001)
521  aspect = 4;
522  else
523  aspect = aspectnum * 1000000;
524 
525  if ((aspect > 0) && (aspect != m_videoAspect))
526  {
527  m_videoAspect = aspect;
528  AspectChange((AspectRatio)aspect, 0);
529  }
530 
531  if (m_wOut && tot_height &&
532  ((uint)tot_height != m_videoHeight ||
533  (uint)m_wOut != m_videoWidth))
534  {
535  m_videoHeight = tot_height;
537  ResolutionChange(m_wOut, tot_height, 0);
538  }
539 
540  int den = (int)ceil(m_videoFrameRate * 100 * m_frameRateMultiplier);
541  int num = 100;
542 
543  // avcodec needs specific settings for mpeg2 compression
544  switch (den)
545  {
546  case 2397:
547  case 2398: den = 24000;
548  num = 1001;
549  break;
550  case 2997:
551  case 2998: den = 30000;
552  num = 1001;
553  break;
554  case 5994:
555  case 5995: den = 60000;
556  num = 1001;
557  break;
558  }
559 
560  FrameRate frameRate(den, num);
561  if (frameRate.isNonzero() && frameRate != m_frameRate)
562  {
563  m_frameRate = frameRate;
564  LOG(VB_RECORD, LOG_INFO, LOC + QString("NVR: frame rate = %1")
565  .arg(frameRate.toDouble() * 1000));
566  FrameRateChange(frameRate.toDouble() * 1000, 0);
567  }
568 }
569 
571 {
572  if (AudioInit() != 0)
573  {
574  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to init audio input device");
575  }
576 
577  if (m_videocodec == "hardware-mjpeg")
578  {
579  m_videocodec = "mjpeg";
580  m_hardwareEncode = true;
581 
582  MJPEGInit();
583 
585 
586  if (m_ntsc)
587  {
588  switch (m_hmjpgVDecimation)
589  {
590  case 2: m_height = 240; break;
591  case 4: m_height = 120; break;
592  default: m_height = 480; break;
593  }
594  }
595  else
596  {
597  switch (m_hmjpgVDecimation)
598  {
599  case 2: m_height = 288; break;
600  case 4: m_height = 144; break;
601  default: m_height = 576; break;
602  }
603  }
604  }
605 
606  if (!m_ringBuffer)
607  {
608  LOG(VB_GENERAL, LOG_WARNING, LOC + "Warning, old RingBuffer creation");
609  m_ringBuffer = RingBuffer::Create("output.nuv", true);
610  m_weMadeBuffer = true;
611  m_livetv = false;
612  if (!m_ringBuffer || !m_ringBuffer->IsOpen())
613  {
614  m_error = "Could not open RingBuffer";
615  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
616  return;
617  }
618  }
619  else
621 
622  m_audioBytes = 0;
623 
624  InitBuffers();
625 }
626 
627 int NuppelVideoRecorder::AudioInit(bool skipdevice)
628 {
629  if (!skipdevice)
630  {
631  int blocksize = 0;
633  if (!m_audioDevice)
634  {
635  LOG(VB_GENERAL, LOG_ERR, LOC +
636  QString("Failed to create audio device: %1") .arg(m_audioDeviceName));
637  return 1;
638  }
639 
641  {
642  LOG(VB_GENERAL, LOG_ERR, LOC +
643  QString("Failed to open audio device %1").arg(m_audioDeviceName));
644  return 1;
645  }
646 
647  if ((blocksize = m_audioDevice->GetBlockSize()) <= 0)
648  {
649  blocksize = 1024;
650  LOG(VB_GENERAL, LOG_ERR, LOC +
651  QString("Failed to determine audio block size on %1,"
652  "using default 1024 bytes").arg(m_audioDeviceName));
653  }
654 
655  m_audioDevice->Close();
656  m_audioBufferSize = blocksize;
657  }
658 
660  LOG(VB_AUDIO, LOG_INFO, LOC +
661  QString("Audio device %1 buffer size: %1 bytes")
662  .arg(m_audioBufferSize));
663 
664  if (m_compressAudio)
665  {
666  int tmp = 0;
667  m_gf = lame_init();
668  lame_set_bWriteVbrTag(m_gf, 0);
669  lame_set_quality(m_gf, m_mp3Quality);
670  lame_set_compression_ratio(m_gf, 11);
671  lame_set_mode(m_gf, m_audioChannels == 2 ? STEREO : MONO);
672  lame_set_num_channels(m_gf, m_audioChannels);
673  lame_set_in_samplerate(m_gf, m_audioSampleRate);
674  if ((tmp = lame_init_params(m_gf)) != 0)
675  {
676  LOG(VB_GENERAL, LOG_ERR, LOC +
677  QString("AudioInit(): lame_init_params error %1").arg(tmp));
678  m_compressAudio = false;
679  }
680 
681  if (m_audioBits != 16)
682  {
683  LOG(VB_GENERAL, LOG_ERR, LOC +
684  "AudioInit(): lame support requires 16bit audio");
685  m_compressAudio = false;
686  }
687  }
688  m_mp3BufSize = (int)(1.25 * 16384 + 7200);
689  m_mp3Buf = new char[m_mp3BufSize];
690 
691  return 0;
692 }
693 
704 {
705 #ifdef USING_V4L1
706  bool we_opened_fd = false;
707  int init_fd = m_fd;
708  if (init_fd < 0)
709  {
710  QByteArray vdevice = m_videodevice.toLatin1();
711  init_fd = open(vdevice.constData(), O_RDWR);
712  we_opened_fd = true;
713 
714  if (init_fd < 0)
715  {
716  LOG(VB_GENERAL, LOG_ERR, LOC + "Can't open video device" + ENO);
717  return false;
718  }
719  }
720 
721  struct video_capability vc;
722  memset(&vc, 0, sizeof(vc));
723  int ret = ioctl(init_fd, VIDIOCGCAP, &vc);
724 
725  if (ret < 0)
726  LOG(VB_GENERAL, LOG_ERR, LOC + "Can't query V4L capabilities" + ENO);
727 
728  if (we_opened_fd)
729  close(init_fd);
730 
731  if (ret < 0)
732  return false;
733 
734  if (vc.maxwidth != 768 && vc.maxwidth != 640)
735  vc.maxwidth = 720;
736 
737  if (vc.type & VID_TYPE_MJPEG_ENCODER)
738  {
739  if (vc.maxwidth >= 768)
740  m_hmjpgMaxW = 768;
741  else if (vc.maxwidth >= 704)
742  m_hmjpgMaxW = 704;
743  else
744  m_hmjpgMaxW = 640;
745  return true;
746  }
747 #endif // USING_V4L1
748 
749  LOG(VB_GENERAL, LOG_ERR, LOC + "MJPEG not supported by device");
750  return false;
751 }
752 
754 {
755  int videomegs = 0;
756  // cppcheck-suppress variableScope
757  int audiomegs = 2;
758 
759  if (!m_videoBufferSize)
760  {
761  m_videoBufferSize = static_cast<long>(
762  GetBufferSize(m_pictureFormat == AV_PIX_FMT_YUV422P ? FMT_YUV422P : FMT_YV12,
763  m_wOut, m_hOut));
764  }
765 
766  if (m_width >= 480 || m_height > 288)
767  videomegs = 20;
768  else
769  videomegs = 12;
770 
771  m_videoBufferCount = (videomegs * 1000 * 1000) / m_videoBufferSize;
772 
773  if (m_audioBufferSize != 0)
774  m_audioBufferCount = (audiomegs * 1000 * 1000) / m_audioBufferSize;
775  else
776  m_audioBufferCount = 0;
777 
778  m_textBufferSize = 8 * (sizeof(teletextsubtitle) + VT_WIDTH);
780 
781  for (int i = 0; i < m_videoBufferCount; i++)
782  {
783  auto *vidbuf = new vidbuffertype;
784  vidbuf->buffer = new unsigned char[m_videoBufferSize];
785  vidbuf->sample = 0;
786  vidbuf->freeToEncode = 0;
787  vidbuf->freeToBuffer = 1;
788  vidbuf->bufferlen = 0;
789  vidbuf->forcekey = 0;
790 
791  m_videoBuffer.push_back(vidbuf);
792  }
793 
794  for (int i = 0; i < m_audioBufferCount; i++)
795  {
796  auto *audbuf = new audbuffertype;
797  audbuf->buffer = new unsigned char[m_audioBufferSize];
798  audbuf->sample = 0;
799  audbuf->freeToEncode = 0;
800  audbuf->freeToBuffer = 1;
801 
802  m_audioBuffer.push_back(audbuf);
803  }
804 
805  for (int i = 0; i < m_textBufferCount; i++)
806  {
807  auto *txtbuf = new txtbuffertype;
808  txtbuf->buffer = new unsigned char[m_textBufferSize];
809  txtbuf->freeToEncode = 0;
810  txtbuf->freeToBuffer = 1;
811 
812  m_textBuffer.push_back(txtbuf);
813  }
814 }
815 
817 {
818  for (auto & vidbuf : m_videoBuffer)
819  {
820  delete [] (vidbuf->buffer);
821  vidbuf->buffer = new unsigned char[m_videoBufferSize];
822  }
823 }
824 
826 {
827  delete [] m_strm;
828  m_strm = new signed char[m_width * m_height * 2 + 10];
829 }
830 
832 {
833  if (m_channelFd>0)
834  return true;
835 
836  int retries = 0;
837  QByteArray vdevice = m_videodevice.toLatin1();
838  m_fd = open(vdevice.constData(), O_RDWR);
839  while (m_fd < 0)
840  {
841  usleep(30000);
842  m_fd = open(vdevice.constData(), O_RDWR);
843  if (retries++ > 5)
844  {
845  m_error = QString("Can't open video device: %1").arg(m_videodevice);
846  LOG(VB_GENERAL, LOG_ERR, LOC + m_error + ENO);
847  KillChildren();
848  return false;
849  }
850  }
851 
852  m_channelFd = m_fd;
853  return true;
854 }
855 
857 {
858 #ifdef USING_V4L2
859  m_usingV4l2 = true;
860 
861  struct v4l2_capability vcap {};
862 
863  if (ioctl(m_channelFd, VIDIOC_QUERYCAP, &vcap) < 0)
864  {
865  m_usingV4l2 = false;
866  }
867 
868  if (m_usingV4l2 && !(vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
869  {
870  LOG(VB_GENERAL, LOG_ERR, LOC +
871  "Not a v4l2 capture device, falling back to v4l");
872  m_usingV4l2 = false;
873  }
874 
875  if (m_usingV4l2 && !(vcap.capabilities & V4L2_CAP_STREAMING))
876  {
877  LOG(VB_GENERAL, LOG_ERR, LOC +
878  "Won't work with the streaming interface, falling back");
879  m_usingV4l2 = false;
880  }
881 
882  if (vcap.card[0] == 'B' && vcap.card[1] == 'T' &&
883  vcap.card[2] == '8' && vcap.card[4] == '8')
884  m_correctBttv = true;
885 
886  QString driver = (char *)vcap.driver;
887  if (driver == "go7007")
888  m_go7007 = true;
889 #endif // USING_V4L2
890 }
891 
893 {
894  if (lzo_init() != LZO_E_OK)
895  {
896  LOG(VB_GENERAL, LOG_ERR, LOC + "lzo_init() failed, exiting");
897  m_error = "lzo_init() failed, exiting";
898  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
899  return;
900  }
901 
902  if (!Open())
903  {
904  m_error = "Failed to open device";
905  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
906  return;
907  }
908 
909  ProbeV4L2();
910 
911  if (m_usingV4l2 && !SetFormatV4L2())
912  {
913  m_error = "Failed to set V4L2 format";
914  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
915  return;
916  }
917 
918  StreamAllocate();
919 
920  m_positionMapLock.lock();
921  m_positionMap.clear();
922  m_positionMapDelta.clear();
923  m_positionMapLock.unlock();
924 
925  m_useAvCodec = (m_videocodec.toLower() != "rtjpeg");
926  if (m_useAvCodec)
928 
929  if (!m_useAvCodec)
930  SetupRTjpeg();
931 
933 
934  if (CreateNuppelFile() != 0)
935  {
936  m_error = QString("Cannot open '%1' for writing")
937  .arg(m_ringBuffer->GetFilename());
938  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
939  return;
940  }
941 
942  if (IsHelperRequested())
943  {
944  LOG(VB_GENERAL, LOG_ERR, LOC + "Children are already alive");
945  m_error = "Children are already alive";
946  return;
947  }
948 
949  {
950  QMutexLocker locker(&m_pauseLock);
951  m_requestRecording = true;
952  m_requestHelper = true;
953  m_recording = true;
954  m_recordingWait.wakeAll();
955  }
956 
957  m_writeThread = new NVRWriteThread(this);
958  m_writeThread->start();
959 
960  m_audioThread = new NVRAudioThread(this);
961  m_audioThread->start();
962 
963  if ((m_vbiMode != VBIMode::None) && (OpenVBIDevice() >= 0))
964  m_vbiThread = new VBIThread(this);
965 
966  // save the start time
967  gettimeofday(&m_stm, &m_tzone);
968 
969  // try to get run at higher scheduling priority, ignore failure
970  myth_nice(-10);
971 
972  if (m_usingV4l2)
973  {
974  m_inPixFmt = FMT_NONE;;
975  DoV4L2();
976  }
977  else
978  DoV4L1();
979 
980  {
981  QMutexLocker locker(&m_pauseLock);
982  m_requestRecording = false;
983  m_requestHelper = false;
984  m_recording = false;
985  m_recordingWait.wakeAll();
986  }
987 }
988 
989 #ifdef USING_V4L1
991 {
992  struct video_capability vc;
993  struct video_mmap mm;
994  struct video_mbuf vm;
995  struct video_channel vchan;
996  struct video_audio va;
997  struct video_tuner vt;
998 
999  memset(&mm, 0, sizeof(mm));
1000  memset(&vm, 0, sizeof(vm));
1001  memset(&vchan, 0, sizeof(vchan));
1002  memset(&va, 0, sizeof(va));
1003  memset(&vt, 0, sizeof(vt));
1004  memset(&vc, 0, sizeof(vc));
1005 
1006  if (ioctl(m_fd, VIDIOCGCAP, &vc) < 0)
1007  {
1008  QString tmp = "VIDIOCGCAP: " + ENO;
1009  KillChildren();
1010  LOG(VB_GENERAL, LOG_ERR, tmp);
1011  m_error = tmp;
1012  return;
1013  }
1014 
1015  int channelinput = 0;
1016 
1017  if (m_channelObj)
1018  channelinput = m_channelObj->GetCurrentInputNum();
1019 
1020  vchan.channel = channelinput;
1021 
1022  if (ioctl(m_fd, VIDIOCGCHAN, &vchan) < 0)
1023  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCGCHAN: " + ENO);
1024 
1025  // Set volume level for audio recording (unless feature is disabled).
1026  if (!m_skipBtAudio)
1027  {
1028  // v4l1 compat in Linux 2.6.18 does not set VIDEO_VC_AUDIO,
1029  // so we just use VIDIOCGAUDIO unconditionally.. then only
1030  // report a get failure as an error if VIDEO_VC_AUDIO is set.
1031  if (ioctl(m_fd, VIDIOCGAUDIO, &va) < 0)
1032  {
1033  bool reports_audio = vchan.flags & VIDEO_VC_AUDIO;
1034  uint err_level = reports_audio ? VB_GENERAL : VB_AUDIO;
1035  // print at VB_GENERAL if driver reports audio.
1036  LOG(err_level, LOG_ERR, LOC + "Failed to get audio" + ENO);
1037  }
1038  else
1039  {
1040  // if channel has a audio then activate it
1041  va.flags &= ~VIDEO_AUDIO_MUTE; // now this really has to work
1042  va.volume = m_volume * 65535 / 100;
1043  if (ioctl(m_fd, VIDIOCSAUDIO, &va) < 0)
1044  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to set audio" + ENO);
1045  }
1046  }
1047 
1048  if ((vc.type & VID_TYPE_MJPEG_ENCODER) && m_hardwareEncode)
1049  {
1050  DoMJPEG();
1051  m_error = "MJPEG requested but not available.";
1052  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1053  return;
1054  }
1055 
1056  m_inPixFmt = FMT_NONE;
1057  InitFilters();
1058 
1059  if (ioctl(m_fd, VIDIOCGMBUF, &vm) < 0)
1060  {
1061  QString tmp = "VIDIOCGMBUF: " + ENO;
1062  KillChildren();
1063  LOG(VB_GENERAL, LOG_ERR, LOC + tmp);
1064  m_error = tmp;
1065  return;
1066  }
1067 
1068  if (vm.frames < 2)
1069  {
1070  QString tmp = "need a minimum of 2 capture buffers";
1071  KillChildren();
1072  LOG(VB_GENERAL, LOG_ERR, LOC + tmp);
1073  m_error = tmp;
1074  return;
1075  }
1076 
1077  int frame;
1078 
1079  unsigned char *buf = (unsigned char *)mmap(0, vm.size,
1080  PROT_READ|PROT_WRITE,
1081  MAP_SHARED,
1082  m_fd, 0);
1083  if (buf == MAP_FAILED)
1084  {
1085  QString tmp = "mmap: " + ENO;
1086  KillChildren();
1087  LOG(VB_GENERAL, LOG_ERR, LOC + tmp);
1088  m_error = tmp;
1089  return;
1090  }
1091 
1092  mm.height = m_height;
1093  mm.width = m_width;
1094  if (m_inPixFmt == FMT_YUV422P)
1095  mm.format = VIDEO_PALETTE_YUV422P;
1096  else
1097  mm.format = VIDEO_PALETTE_YUV420P;
1098 
1099  mm.frame = 0;
1100  if (ioctl(m_fd, VIDIOCMCAPTURE, &mm)<0)
1101  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCMCAPTUREi0: " + ENO);
1102  mm.frame = 1;
1103  if (ioctl(m_fd, VIDIOCMCAPTURE, &mm)<0)
1104  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCMCAPTUREi1: " + ENO);
1105 
1106  int syncerrors = 0;
1107 
1108  while (IsRecordingRequested() && !IsErrored())
1109  {
1110  {
1111  QMutexLocker locker(&m_pauseLock);
1112  if (m_request_pause)
1113  {
1114  if (!m_mainPaused)
1115  {
1116  m_mainPaused = true;
1117  m_pauseWait.wakeAll();
1118  if (IsPaused(true) && m_tvrec)
1120  }
1121  m_unpauseWait.wait(&m_pauseLock, 100);
1122  if (m_clearTimeOnPause)
1123  gettimeofday(&m_stm, &m_tzone);
1124  continue;
1125  }
1126 
1127  if (!m_request_pause && m_mainPaused)
1128  {
1129  m_mainPaused = false;
1130  m_unpauseWait.wakeAll();
1131  }
1132  }
1133 
1134  frame = 0;
1135  mm.frame = 0;
1136  if (ioctl(m_fd, VIDIOCSYNC, &frame)<0)
1137  {
1138  syncerrors++;
1139  if (syncerrors == 10)
1140  LOG(VB_GENERAL, LOG_ERR, LOC +
1141  "Multiple bttv errors, further messages supressed");
1142  else if (syncerrors < 10)
1143  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCSYNC: " + ENO);
1144  }
1145  else
1146  {
1147  BufferIt(buf+vm.offsets[0], m_videoBufferSize);
1148  //memset(buf+vm.offsets[0], 0, m_videoBufferSize);
1149  }
1150 
1151  if (ioctl(m_fd, VIDIOCMCAPTURE, &mm)<0)
1152  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCMCAPTURE0: " + ENO);
1153 
1154  frame = 1;
1155  mm.frame = 1;
1156  if (ioctl(m_fd, VIDIOCSYNC, &frame)<0)
1157  {
1158  syncerrors++;
1159  if (syncerrors == 10)
1160  LOG(VB_GENERAL, LOG_ERR, LOC +
1161  "Multiple bttv errors, further messages supressed");
1162  else if (syncerrors < 10)
1163  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCSYNC: " + ENO);
1164  }
1165  else
1166  {
1167  BufferIt(buf+vm.offsets[1], m_videoBufferSize);
1168  //memset(buf+vm.offsets[1], 0, m_videoBufferSize);
1169  }
1170  if (ioctl(m_fd, VIDIOCMCAPTURE, &mm)<0)
1171  LOG(VB_GENERAL, LOG_ERR, LOC + "VIDIOCMCAPTURE1: " + ENO);
1172  }
1173 
1174  munmap(buf, vm.size);
1175 
1176  KillChildren();
1177 
1178  FinishRecording();
1179 
1180  close(m_fd);
1181 }
1182 #else // if !USING_V4L1
1183 void NuppelVideoRecorder::DoV4L1(void) {}
1184 #endif // !USING_V4L1
1185 
1186 #ifdef USING_V4L2
1188 {
1189  struct v4l2_format vfmt {};
1190 
1191  vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1192 
1193  vfmt.fmt.pix.width = m_width;
1194  vfmt.fmt.pix.height = m_height;
1195  vfmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
1196 
1197  if (m_go7007)
1198  vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
1199  else if (m_inPixFmt == FMT_YUV422P)
1200  vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
1201  else
1202  vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
1203 
1204  if (ioctl(m_fd, VIDIOC_S_FMT, &vfmt) < 0)
1205  {
1206  // this is supported by the cx88 and various ati cards.
1207  vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
1208 
1209  if (ioctl(m_fd, VIDIOC_S_FMT, &vfmt) < 0)
1210  {
1211  // this is supported by the HVR-950q
1212  vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
1213  if (ioctl(m_fd, VIDIOC_S_FMT, &vfmt) < 0)
1214  {
1215  LOG(VB_GENERAL, LOG_ERR, LOC +
1216  "v4l2: Unable to set desired format");
1217  return false;
1218  }
1219 
1220  // we need to convert the buffer - we can't deal with uyvy
1221  // directly.
1222  if (m_inPixFmt == FMT_YUV422P)
1223  {
1224  LOG(VB_GENERAL, LOG_ERR, LOC +
1225  "v4l2: uyvy format supported, but yuv422 requested.");
1226  LOG(VB_GENERAL, LOG_ERR, LOC +
1227  "v4l2: unfortunately, this converter hasn't been "
1228  "written yet, exiting");
1229  return false;
1230  }
1231  LOG(VB_RECORD, LOG_INFO, LOC +
1232  "v4l2: format set, getting uyvy from v4l, converting");
1233  }
1234  else
1235  {
1236  // we need to convert the buffer - we can't deal with yuyv directly.
1237  if (m_inPixFmt == FMT_YUV422P)
1238  {
1239  LOG(VB_GENERAL, LOG_ERR, LOC +
1240  "v4l2: yuyv format supported, but yuv422 requested.");
1241  LOG(VB_GENERAL, LOG_ERR, LOC +
1242  "v4l2: unfortunately, this converter hasn't been written "
1243  "yet, exiting");
1244  return false;
1245  }
1246  LOG(VB_RECORD, LOG_INFO, LOC +
1247  "v4l2: format set, getting yuyv from v4l, converting");
1248  }
1249  }
1250  else // cool, we can do our preferred format, most likely running on bttv.
1251  LOG(VB_RECORD, LOG_INFO, LOC +
1252  "v4l2: format set, getting yuv420 from v4l");
1253 
1254  // VIDIOC_S_FMT might change the format, check it
1255  if (m_width != (int)vfmt.fmt.pix.width ||
1256  m_height != (int)vfmt.fmt.pix.height)
1257  {
1258  LOG(VB_RECORD, LOG_INFO, LOC +
1259  QString("v4l2: resolution changed. requested %1x%2, using "
1260  "%3x%4 now")
1261  .arg(m_width).arg(m_height)
1262  .arg(vfmt.fmt.pix.width) .arg(vfmt.fmt.pix.height));
1263  m_wOut = m_width = vfmt.fmt.pix.width;
1264  m_hOut = m_height = vfmt.fmt.pix.height;
1265  }
1266 
1267  m_v4l2PixelFormat = vfmt.fmt.pix.pixelformat;
1268 
1269  return true;
1270 }
1271 #else // if !USING_V4L2
1272 bool NuppelVideoRecorder::SetFormatV4L2(void) { return false; }
1273 #endif // !USING_V4L2
1274 
1275 #ifdef USING_V4L2
1276 #define MAX_VIDEO_BUFFERS 5
1278 {
1279  struct v4l2_buffer vbuf {};
1280  struct v4l2_requestbuffers vrbuf {};
1281  struct v4l2_control vc {};
1282 
1283  vc.id = V4L2_CID_AUDIO_MUTE;
1284  vc.value = 0;
1285 
1286  if (ioctl(m_fd, VIDIOC_S_CTRL, &vc) < 0)
1287  LOG(VB_GENERAL, LOG_ERR, LOC +
1288  "VIDIOC_S_CTRL:V4L2_CID_AUDIO_MUTE: " + ENO);
1289 
1290  if (m_go7007)
1291  {
1292  struct go7007_comp_params comp {};
1293  struct go7007_mpeg_params mpeg {};
1294 
1295  comp.gop_size = m_keyframeDist;
1296  comp.max_b_frames = 0;
1297 
1298  if (fabs(m_videoAspect - 1.33333F) < 0.01F)
1299  {
1300  if (m_ntsc)
1302  else
1304  }
1305  else if (fabs(m_videoAspect - 1.77777F) < 0.01F)
1306  {
1307  if (m_ntsc)
1309  else
1311  }
1312  else
1313  {
1315  }
1316 
1317  comp.flags |= GO7007_COMP_CLOSED_GOP;
1318  if (ioctl(m_fd, GO7007IOC_S_COMP_PARAMS, &comp) < 0)
1319  {
1320  m_error = "Unable to set compression params";
1321  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1322  return;
1323  }
1324 
1325  if (m_videocodec == "mpeg2video")
1327  else
1329 
1330  if (ioctl(m_fd, GO7007IOC_S_MPEG_PARAMS, &mpeg) < 0)
1331  {
1332  m_error = "Unable to set MPEG params";
1333  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1334  return;
1335  }
1336 
1337  int usebitrate = m_targetBitRate * 1000;
1338  if (m_scaleBitRate)
1339  {
1340  float diff = (m_width * m_height) / (640.0 * 480.0);
1341  usebitrate = (int)(diff * usebitrate);
1342  }
1343 
1344  if (ioctl(m_fd, GO7007IOC_S_BITRATE, &usebitrate) < 0)
1345  {
1346  m_error = "Unable to set bitrate";
1347  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1348  return;
1349  }
1350 
1351  m_hardwareEncode = true;
1352  }
1353 
1354  uint numbuffers = MAX_VIDEO_BUFFERS;
1355 
1356  vrbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1357  vrbuf.memory = V4L2_MEMORY_MMAP;
1358  vrbuf.count = numbuffers;
1359 
1360  if (ioctl(m_fd, VIDIOC_REQBUFS, &vrbuf) < 0)
1361  {
1362  m_error = "Not able to get any capture buffers, exiting";
1363  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1364  return;
1365  }
1366 
1367  if (vrbuf.count < numbuffers)
1368  {
1369  LOG(VB_GENERAL, LOG_INFO, LOC +
1370  QString("Requested %1 buffers, but only %2 are available. "
1371  "Proceeding anyway").arg(numbuffers).arg(vrbuf.count));
1372  }
1373 
1374  numbuffers = vrbuf.count;
1375 
1376  unsigned char *buffers[MAX_VIDEO_BUFFERS];
1377  int bufferlen[MAX_VIDEO_BUFFERS];
1378 
1379  for (uint i = 0; i < numbuffers; i++)
1380  {
1381  vbuf.type = vrbuf.type;
1382  vbuf.index = i;
1383 
1384  if (ioctl(m_fd, VIDIOC_QUERYBUF, &vbuf) < 0)
1385  {
1386  LOG(VB_GENERAL, LOG_ERR, LOC +
1387  QString("unable to query capture buffer %1").arg(i));
1388  m_error = "Unable to query capture buffer";
1389  return;
1390  }
1391 
1392  buffers[i] = (unsigned char *)mmap(nullptr, vbuf.length,
1393  PROT_READ|PROT_WRITE, MAP_SHARED,
1394  m_fd, vbuf.m.offset);
1395 
1396  if (buffers[i] == MAP_FAILED)
1397  {
1398  LOG(VB_GENERAL, LOG_ERR, LOC + "mmap: " + ENO);
1399  LOG(VB_GENERAL, LOG_ERR, LOC + "Memory map failed");
1400  m_error = "Memory map failed";
1401  return;
1402  }
1403  bufferlen[i] = vbuf.length;
1404  }
1405 
1406  for (uint i = 0; i < numbuffers; i++)
1407  {
1408  memset(buffers[i], 0, bufferlen[i]);
1409  vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1410  vbuf.index = i;
1411  if (ioctl(m_fd, VIDIOC_QBUF, &vbuf) < 0)
1412  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to enqueue capture buffer (VIDIOC_QBUF failed) " + ENO);
1413  }
1414 
1415  int turnon = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1416  if (ioctl(m_fd, VIDIOC_STREAMON, &turnon) < 0)
1417  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to start capture (VIDIOC_STREAMON failed) " + ENO);
1418 
1419  struct timeval tv {};
1420  fd_set rdset {};
1421  int frame = 0;
1422  bool forcekey = false;
1423 
1424  m_resetCapture = false;
1425 
1426  // setup pixel format conversions for YUYV and UYVY
1427  uint8_t *output_buffer = nullptr;
1428  struct SwsContext *convert_ctx = nullptr;
1429  AVFrame img_out;
1430  if (m_v4l2PixelFormat == V4L2_PIX_FMT_YUYV ||
1431  m_v4l2PixelFormat == V4L2_PIX_FMT_UYVY)
1432  {
1433  AVPixelFormat in_pixfmt = m_v4l2PixelFormat == V4L2_PIX_FMT_YUYV ?
1434  AV_PIX_FMT_YUYV422 :
1435  AV_PIX_FMT_UYVY422;
1436 
1437  output_buffer = (uint8_t*)av_malloc(m_height * m_width * 3 / 2);
1438  if (!output_buffer)
1439  {
1440  m_error = "Cannot initialize image conversion buffer";
1441  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1442  return;
1443  }
1444 
1445  convert_ctx = sws_getCachedContext(convert_ctx, m_width, m_height, in_pixfmt,
1446  m_width, m_height, AV_PIX_FMT_YUV420P,
1447  SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
1448  if (!convert_ctx)
1449  {
1450  m_error = "Cannot initialize image conversion context";
1451  av_free(output_buffer);
1452  LOG(VB_GENERAL, LOG_ERR, LOC + m_error);
1453  return;
1454  }
1455 
1456  av_image_fill_arrays(img_out.data, img_out.linesize,
1457  output_buffer, AV_PIX_FMT_YUV420P, m_width, m_height, IMAGE_ALIGN);
1458  }
1459 
1460  while (IsRecordingRequested() && !IsErrored())
1461  {
1462 again:
1463  {
1464  QMutexLocker locker(&m_pauseLock);
1465  if (m_requestPause)
1466  {
1467  if (!m_mainPaused)
1468  {
1469  m_mainPaused = true;
1470  m_pauseWait.wakeAll();
1471  if (IsPaused(true) && m_tvrec)
1473  }
1474  m_unpauseWait.wait(&m_pauseLock, 100);
1475  if (m_clearTimeOnPause)
1476  gettimeofday(&m_stm, &m_tzone);
1477  continue;
1478  }
1479 
1480  if (!m_requestPause && m_mainPaused)
1481  {
1482  m_mainPaused = false;
1483  m_unpauseWait.wakeAll();
1484  }
1485  }
1486 
1487  if (m_resetCapture)
1488  {
1489  LOG(VB_GENERAL, LOG_ERR, LOC + "Resetting and re-queueing");
1490  turnon = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1491  if (ioctl(m_fd, VIDIOC_STREAMOFF, &turnon) < 0)
1492  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to stop capture (VIDIOC_STREAMOFF failed) " + ENO);
1493 
1494  for (uint i = 0; i < numbuffers; i++)
1495  {
1496  memset(buffers[i], 0, bufferlen[i]);
1497  vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1498  vbuf.index = i;
1499  if (ioctl(m_fd, VIDIOC_QBUF, &vbuf) < 0)
1500  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to enqueue capture buffer (VIDIOC_QBUF failed) " + ENO);
1501  }
1502 
1503  if (ioctl(m_fd, VIDIOC_STREAMON, &turnon) < 0)
1504  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to start capture (VIDIOC_STREAMON failed) " + ENO);
1505  m_resetCapture = false;
1506  }
1507 
1508  tv.tv_sec = 5;
1509  tv.tv_usec = 0;
1510  FD_ZERO(&rdset); // NOLINT(readability-isolate-declaration)
1511  FD_SET(m_fd, &rdset);
1512 
1513  switch (select(m_fd+1, &rdset, nullptr, nullptr, &tv))
1514  {
1515  case -1:
1516  if (errno == EINTR)
1517  goto again;
1518  LOG(VB_GENERAL, LOG_ERR, LOC + "select: " + ENO);
1519  continue;
1520  case 0:
1521  LOG(VB_GENERAL, LOG_INFO, LOC + "select timeout");
1522  continue;
1523  default: break;
1524  }
1525 
1526  memset(&vbuf, 0, sizeof(vbuf));
1527  vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1528  vbuf.memory = V4L2_MEMORY_MMAP;
1529  if (ioctl(m_fd, VIDIOC_DQBUF, &vbuf) < 0)
1530  {
1531  LOG(VB_GENERAL, LOG_ERR, LOC + "DQBUF ioctl failed." + ENO);
1532 
1533  // EIO failed DQBUF de-tunes post 2.6.15.3 for cx88
1534  // EIO or EINVAL on bttv means we need to reset the buffers..
1535  if (errno == EIO && m_channelObj)
1536  {
1537  m_channelObj->Retune();
1538  m_resetCapture = true;
1539  continue;
1540  }
1541 
1542  if (errno == EIO || errno == EINVAL)
1543  {
1544  m_resetCapture = true;
1545  continue;
1546  }
1547 
1548  if (errno == EAGAIN)
1549  continue;
1550  }
1551 
1552  frame = vbuf.index;
1553  if (m_go7007)
1554  forcekey = ((vbuf.flags & V4L2_BUF_FLAG_KEYFRAME) != 0U);
1555 
1556  if (!m_requestPause)
1557  {
1558  if ((m_v4l2PixelFormat == V4L2_PIX_FMT_YUYV) &&
1559  (output_buffer != nullptr))
1560  {
1561  AVFrame img_in;
1562  av_image_fill_arrays(img_in.data, img_in.linesize,
1563  buffers[frame], AV_PIX_FMT_YUYV422, m_width, m_height,
1564  IMAGE_ALIGN);
1565  sws_scale(convert_ctx, img_in.data, img_in.linesize,
1566  0, m_height, img_out.data, img_out.linesize);
1567  BufferIt(output_buffer, m_videoBufferSize);
1568  }
1569  else if ((m_v4l2PixelFormat == V4L2_PIX_FMT_UYVY) &&
1570  (output_buffer != nullptr))
1571  {
1572  AVFrame img_in;
1573  av_image_fill_arrays(img_in.data, img_in.linesize,
1574  buffers[frame], AV_PIX_FMT_UYVY422, m_width, m_height,
1575  IMAGE_ALIGN);
1576  sws_scale(convert_ctx, img_in.data, img_in.linesize,
1577  0, m_height, img_out.data, img_out.linesize);
1578  BufferIt(output_buffer, m_videoBufferSize);
1579  }
1580  else
1581  {
1582  // buffer the frame directly
1583  BufferIt(buffers[frame], vbuf.bytesused, forcekey);
1584  }
1585  }
1586 
1587  vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1588  if (ioctl(m_fd, VIDIOC_QBUF, &vbuf) < 0)
1589  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to enqueue capture buffer (VIDIOC_QBUF failed) " + ENO);
1590  }
1591 
1592  KillChildren();
1593 
1594  if (ioctl(m_fd, VIDIOC_STREAMOFF, &turnon) < 0)
1595  LOG(VB_GENERAL, LOG_ERR, LOC + "unable to stop capture (VIDIOC_STREAMOFF failed) " + ENO);
1596 
1597  for (uint i = 0; i < numbuffers; i++)
1598  {
1599  munmap(buffers[i], bufferlen[i]);
1600  }
1601 
1602  FinishRecording();
1603 
1604  av_free(output_buffer);
1605  sws_freeContext(convert_ctx);
1606 
1607  close(m_fd);
1608  close(m_channelFd);
1609 }
1610 #else // if !USING_V4L2
1611 void NuppelVideoRecorder::DoV4L2(void) {}
1612 #endif // !USING_V4L2
1613 
1614 #ifdef USING_V4L1
1616 {
1617  struct mjpeg_params bparm;
1618 
1619  if (ioctl(m_fd, MJPIOC_G_PARAMS, &bparm) < 0)
1620  {
1621  LOG(VB_GENERAL, LOG_ERR, LOC + "MJPIOC_G_PARAMS: " + ENO);
1622  return;
1623  }
1624 
1625  //bparm.input = 2;
1626  //bparm.norm = 1;
1627  bparm.quality = m_hmjpgQuality;
1628 
1630  {
1632  }
1633  else
1634  {
1635  bparm.decimation = 0;
1636  bparm.HorDcm = m_hmjpgHDecimation;
1637  bparm.VerDcm = (m_hmjpgVDecimation + 1) / 2;
1638 
1639  if (m_hmjpgVDecimation == 1)
1640  {
1641  bparm.TmpDcm = 1;
1642  bparm.field_per_buff = 2;
1643  }
1644  else
1645  {
1646  bparm.TmpDcm = 2;
1647  bparm.field_per_buff = 1;
1648  }
1649 
1650  bparm.img_width = m_hmjpgMaxW;
1651 
1652  if (m_ntsc)
1653  bparm.img_height = 240;
1654  else
1655  bparm.img_height = 288;
1656 
1657  bparm.img_x = 0;
1658  bparm.img_y = 0;
1659  }
1660 
1661  bparm.APPn = 0;
1662 
1663  if (m_hmjpgVDecimation == 1)
1664  bparm.APP_len = 14;
1665  else
1666  bparm.APP_len = 0;
1667 
1668  bparm.odd_even = !(m_hmjpgVDecimation > 1);
1669 
1670  for (int n = 0; n < bparm.APP_len; n++)
1671  bparm.APP_data[n] = 0;
1672 
1673  if (ioctl(m_fd, MJPIOC_S_PARAMS, &bparm) < 0)
1674  {
1675  LOG(VB_GENERAL, LOG_DEBUG, LOC + "MJPIOC_S_PARAMS: " + ENO);
1676  return;
1677  }
1678 
1679  struct mjpeg_requestbuffers breq;
1680 
1681  breq.count = 64;
1682  breq.size = 256 * 1024;
1683 
1684  if (ioctl(m_fd, MJPIOC_REQBUFS, &breq) < 0)
1685  {
1686  LOG(VB_GENERAL, LOG_DEBUG, LOC + "MJPIOC_REQBUFS: " + ENO);
1687  return;
1688  }
1689 
1690  uint8_t *MJPG_buff = (uint8_t *)mmap(0, breq.count * breq.size,
1691  PROT_READ|PROT_WRITE, MAP_SHARED, m_fd,
1692  0);
1693 
1694  if (MJPG_buff == MAP_FAILED)
1695  {
1696  LOG(VB_GENERAL, LOG_ERR, LOC + "mapping mjpeg buffers");
1697  return;
1698  }
1699 
1700  struct mjpeg_sync bsync;
1701 
1702  for (unsigned int count = 0; count < breq.count; count++)
1703  {
1704  if (ioctl(m_fd, MJPIOC_QBUF_CAPT, &count) < 0)
1705  LOG(VB_GENERAL, LOG_ERR, LOC + "MJPIOC_QBUF_CAPT: " + ENO);
1706  }
1707 
1708  while (IsRecordingRequested() && !IsErrored())
1709  {
1710  {
1711  QMutexLocker locker(&m_pauseLock);
1712  if (m_request_pause)
1713  {
1714  if (!m_mainPaused)
1715  {
1716  m_mainPaused = true;
1717  m_pauseWait.wakeAll();
1718  if (IsPaused(true) && m_tvrec)
1720  }
1721  m_unpauseWait.wait(&m_pauseLock, 100);
1722  if (m_clearTimeOnPause)
1723  gettimeofday(&m_stm, &m_tzone);
1724  continue;
1725  }
1726 
1727  if (!m_request_pause && m_mainPaused)
1728  {
1729  m_mainPaused = false;
1730  m_unpauseWait.wakeAll();
1731  }
1732  }
1733 
1734  if (ioctl(m_fd, MJPIOC_SYNC, &bsync) < 0)
1735  {
1736  m_error = "MJPEG sync error";
1737  LOG(VB_GENERAL, LOG_ERR, LOC + m_error + ENO);
1738  break;
1739  }
1740 
1741  BufferIt((unsigned char *)(MJPG_buff + bsync.frame * breq.size),
1742  bsync.length);
1743 
1744  if (ioctl(m_fd, MJPIOC_QBUF_CAPT, &(bsync.frame)) < 0)
1745  {
1746  m_error = "MJPEG Capture error";
1747  LOG(VB_GENERAL, LOG_ERR, LOC + m_error + ENO);
1748  }
1749  }
1750 
1751  munmap(MJPG_buff, breq.count * breq.size);
1752  KillChildren();
1753 
1754  FinishRecording();
1755 
1756  close(m_fd);
1757 }
1758 #else // if !USING_V4L1
1759 void NuppelVideoRecorder::DoMJPEG(void) {}
1760 #endif // !USING_V4L1
1761 
1763 {
1764  {
1765  QMutexLocker locker(&m_pauseLock);
1766  m_requestHelper = false;
1767  m_unpauseWait.wakeAll();
1768  }
1769 
1770  if (m_writeThread)
1771  {
1772  m_writeThread->wait();
1773  delete m_writeThread;
1774  m_writeThread = nullptr;
1775  }
1776 
1777  if (m_audioThread)
1778  {
1779  m_audioThread->wait();
1780  delete m_audioThread;
1781  m_audioThread = nullptr;
1782  }
1783 
1784  if (m_vbiThread)
1785  {
1786  m_vbiThread->wait();
1787  delete m_vbiThread;
1788  m_vbiThread = nullptr;
1789  CloseVBIDevice();
1790  }
1791 }
1792 
1793 void NuppelVideoRecorder::BufferIt(unsigned char *buf, int len, bool forcekey)
1794 {
1795  struct timeval now {};
1796 
1797  int act = m_actVideoBuffer;
1798 
1799  if (!m_videoBuffer[act]->freeToBuffer) {
1800  return;
1801  }
1802 
1803  gettimeofday(&now, &m_tzone);
1804 
1805  long tcres = (now.tv_sec-m_stm.tv_sec)*1000 + now.tv_usec/1000 - m_stm.tv_usec/1000;
1806 
1807  m_useBttv = 0;
1808  // here is the non preferable timecode - drop algorithm - fallback
1809  if (!m_useBttv)
1810  {
1811  if (m_tf==0)
1812  m_tf = 2;
1813  else
1814  {
1815  int fn = tcres - m_oldTc;
1816 
1817  // the difference should be less than 1,5*timeperframe or we have
1818  // missed at least one frame, this code might be inaccurate!
1819 
1820  if (m_ntscFrameRate)
1821  fn = (fn+16)/33;
1822  else
1823  fn = (fn+20)/40;
1824  if (fn<1)
1825  fn=1;
1826  m_tf += 2*fn; // two fields
1827  }
1828  }
1829 
1830  m_oldTc = tcres;
1831 
1832  if (!m_videoBuffer[act]->freeToBuffer)
1833  {
1834  LOG(VB_GENERAL, LOG_INFO, LOC +
1835  "DROPPED frame due to full buffer in the recorder.");
1836  return; // we can't buffer the current frame
1837  }
1838 
1839  m_videoBuffer[act]->sample = m_tf;
1840 
1841  // record the time at the start of this frame.
1842  // 'tcres' is at the end of the frame, so subtract the right # of ms
1843  m_videoBuffer[act]->timecode = (m_ntscFrameRate) ? (tcres - 33) : (tcres - 40);
1844 
1845  memcpy(m_videoBuffer[act]->buffer, buf, len);
1846  m_videoBuffer[act]->bufferlen = len;
1847  m_videoBuffer[act]->forcekey = forcekey;
1848 
1849  m_videoBuffer[act]->freeToBuffer = 0;
1850  m_actVideoBuffer++;
1852  m_actVideoBuffer = 0; // cycle to begin of buffer
1853  m_videoBuffer[act]->freeToEncode = 1; // set last to prevent race
1854 }
1855 
1857 {
1858 #if HAVE_BIGENDIAN
1859  fh->timecode = bswap_32(fh->timecode);
1860  fh->packetlength = bswap_32(fh->packetlength);
1861 #endif
1863 }
1864 
1866 {
1867  if (newaspect == static_cast<double>(m_videoAspect))
1868  return;
1869 
1870  m_videoAspect = newaspect;
1871 
1872  struct rtframeheader frameheader {};
1873 
1874  frameheader.frametype = 'S';
1875  frameheader.comptype = 'M';
1876  frameheader.packetlength = sizeof(struct rtfileheader);
1877 
1878  WriteFrameheader(&frameheader);
1879 
1880  WriteFileHeader();
1881 }
1882 
1884 {
1885  struct rtfileheader fileheader {};
1886  static constexpr char kFinfo[12] = "MythTVVideo";
1887  static constexpr char kVers[5] = "0.07";
1888 
1889  memcpy(fileheader.finfo, kFinfo, sizeof(fileheader.finfo));
1890  memcpy(fileheader.version, kVers, sizeof(fileheader.version));
1891  fileheader.width = m_wOut;
1892  fileheader.height = (int)(m_hOut * m_heightMultiplier);
1893  fileheader.desiredwidth = 0;
1894  fileheader.desiredheight = 0;
1895  fileheader.pimode = 'P';
1896  fileheader.aspect = m_videoAspect;
1897  fileheader.fps = m_videoFrameRate;
1898  fileheader.fps *= m_frameRateMultiplier;
1899  fileheader.videoblocks = -1;
1900  fileheader.audioblocks = -1;
1901  fileheader.textsblocks = -1; // TODO: make only -1 if VBI support active?
1902  fileheader.keyframedist = KEYFRAMEDIST;
1903 
1904 #if HAVE_BIGENDIAN
1905  fileheader.width = bswap_32(fileheader.width);
1906  fileheader.height = bswap_32(fileheader.height);
1907  fileheader.desiredwidth = bswap_32(fileheader.desiredwidth);
1908  fileheader.desiredheight = bswap_32(fileheader.desiredheight);
1909  fileheader.aspect = bswap_dbl(fileheader.aspect);
1910  fileheader.fps = bswap_dbl(fileheader.fps);
1911  fileheader.videoblocks = bswap_32(fileheader.videoblocks);
1912  fileheader.audioblocks = bswap_32(fileheader.audioblocks);
1913  fileheader.textsblocks = bswap_32(fileheader.textsblocks);
1914  fileheader.keyframedist = bswap_32(fileheader.keyframedist);
1915 #endif
1916  m_ringBuffer->Write(&fileheader, FILEHEADERSIZE);
1917 }
1918 
1920 {
1921  struct rtframeheader frameheader {};
1922 
1923  WriteFileHeader();
1924 
1925  frameheader.frametype = 'D'; // compressor data
1926 
1927  if (m_useAvCodec)
1928  {
1929  frameheader.comptype = 'F';
1930  frameheader.packetlength = m_mpaVidCtx->extradata_size;
1931 
1932  WriteFrameheader(&frameheader);
1933  m_ringBuffer->Write(m_mpaVidCtx->extradata, frameheader.packetlength);
1934  }
1935  else
1936  {
1937  static unsigned long int s_tbls[128];
1938 
1939  frameheader.comptype = 'R'; // compressor data for RTjpeg
1940  frameheader.packetlength = sizeof(s_tbls);
1941 
1942  // compression configuration header
1943  WriteFrameheader(&frameheader);
1944 
1945  memset(s_tbls, 0, sizeof(s_tbls));
1946  m_ringBuffer->Write(s_tbls, sizeof(s_tbls));
1947  }
1948 
1949  memset(&frameheader, 0, sizeof(frameheader));
1950  frameheader.frametype = 'X'; // extended data
1951  frameheader.packetlength = sizeof(extendeddata);
1952 
1953  // extended data header
1954  WriteFrameheader(&frameheader);
1955 
1956  struct extendeddata moredata {};
1957 
1958  moredata.version = 1;
1959  if (m_useAvCodec)
1960  {
1961  int vidfcc = 0;
1962  switch(m_mpaVidCodec->id)
1963  {
1964  case AV_CODEC_ID_MPEG4: vidfcc = FOURCC_DIVX; break;
1965  case AV_CODEC_ID_WMV1: vidfcc = FOURCC_WMV1; break;
1966  case AV_CODEC_ID_MSMPEG4V3: vidfcc = FOURCC_DIV3; break;
1967  case AV_CODEC_ID_MSMPEG4V2: vidfcc = FOURCC_MP42; break;
1968  case AV_CODEC_ID_MSMPEG4V1: vidfcc = FOURCC_MPG4; break;
1969  case AV_CODEC_ID_MJPEG: vidfcc = FOURCC_MJPG; break;
1970  case AV_CODEC_ID_H263:
1971  case AV_CODEC_ID_H263P: vidfcc = FOURCC_H263; break;
1972  case AV_CODEC_ID_H263I: vidfcc = FOURCC_I263; break;
1973  case AV_CODEC_ID_MPEG1VIDEO: vidfcc = FOURCC_MPEG; break;
1974  case AV_CODEC_ID_MPEG2VIDEO: vidfcc = FOURCC_MPG2; break;
1975  case AV_CODEC_ID_HUFFYUV: vidfcc = FOURCC_HFYU; break;
1976  default: break;
1977  }
1978  moredata.video_fourcc = vidfcc;
1979  moredata.lavc_bitrate = m_mpaVidCtx->bit_rate;
1980  moredata.lavc_qmin = m_mpaVidCtx->qmin;
1981  moredata.lavc_qmax = m_mpaVidCtx->qmax;
1982  moredata.lavc_maxqdiff = m_mpaVidCtx->max_qdiff;
1983  }
1984  else
1985  {
1986  moredata.video_fourcc = FOURCC_RJPG;
1987  moredata.rtjpeg_quality = m_q;
1988  moredata.rtjpeg_luma_filter = m_m1;
1989  moredata.rtjpeg_chroma_filter = m_m2;
1990  }
1991 
1992  if (m_compressAudio)
1993  {
1994  moredata.audio_fourcc = FOURCC_LAME;
1995  moredata.audio_compression_ratio = 11;
1996  moredata.audio_quality = m_mp3Quality;
1997  }
1998  else
1999  {
2000  moredata.audio_fourcc = FOURCC_RAWA;
2001  }
2002 
2004  moredata.audio_channels = m_audioChannels;
2006 
2008 
2009 #if HAVE_BIGENDIAN
2010  moredata.version = bswap_32(moredata.version);
2011  moredata.video_fourcc = bswap_32(moredata.video_fourcc);
2012  moredata.audio_fourcc = bswap_32(moredata.audio_fourcc);
2013  moredata.audio_sample_rate = bswap_32(moredata.audio_sample_rate);
2014  moredata.audio_bits_per_sample = bswap_32(moredata.audio_bits_per_sample);
2015  moredata.audio_channels = bswap_32(moredata.audio_channels);
2016  moredata.audio_compression_ratio = bswap_32(moredata.audio_compression_ratio);
2017  moredata.audio_quality = bswap_32(moredata.audio_quality);
2018  moredata.rtjpeg_quality = bswap_32(moredata.rtjpeg_quality);
2019  moredata.rtjpeg_luma_filter = bswap_32(moredata.rtjpeg_luma_filter);
2020  moredata.rtjpeg_chroma_filter = bswap_32(moredata.rtjpeg_chroma_filter);
2021  moredata.lavc_bitrate = bswap_32(moredata.lavc_bitrate);
2022  moredata.lavc_qmin = bswap_32(moredata.lavc_qmin);
2023  moredata.lavc_qmax = bswap_32(moredata.lavc_qmax);
2024  moredata.lavc_maxqdiff = bswap_32(moredata.lavc_maxqdiff);
2025  moredata.seektable_offset = bswap_64(moredata.seektable_offset);
2026  moredata.keyframeadjust_offset = bswap_64(moredata.keyframeadjust_offset);
2027 #endif
2028  m_ringBuffer->Write(&moredata, sizeof(moredata));
2029 
2030  m_lastBlock = 0;
2031  m_lf = 0; // that resets framenumber so that seeking in the
2032  // continues parts works too
2033 }
2034 
2036 {
2037  int numentries = m_seekTable->size();
2038 
2039  struct rtframeheader frameheader {};
2040  frameheader.frametype = 'Q'; // SeekTable
2041  frameheader.packetlength = sizeof(struct seektable_entry) * numentries;
2042 
2043  long long currentpos = m_ringBuffer->GetWritePosition();
2044 
2045  m_ringBuffer->Write(&frameheader, sizeof(frameheader));
2046 
2047  char *seekbuf = new char[frameheader.packetlength];
2048  int offset = 0;
2049 
2050  for (auto & entry : *m_seekTable)
2051  {
2052  memcpy(seekbuf + offset, (const void *)&entry,
2053  sizeof(struct seektable_entry));
2054  offset += sizeof(struct seektable_entry);
2055  }
2056 
2057  m_ringBuffer->Write(seekbuf, frameheader.packetlength);
2058 
2060  offsetof(struct extendeddata, seektable_offset),
2061  SEEK_SET);
2062 
2063  m_ringBuffer->Write(&currentpos, sizeof(long long));
2064 
2065  m_ringBuffer->WriterSeek(0, SEEK_END);
2066 
2067  delete [] seekbuf;
2068 }
2069 
2071  const vector<struct kfatable_entry> &kfa_table)
2072 {
2073  int numentries = kfa_table.size();
2074 
2075  struct rtframeheader frameheader {};
2076  frameheader.frametype = 'K'; // KFA Table
2077  frameheader.packetlength = sizeof(struct kfatable_entry) * numentries;
2078 
2079  long long currentpos = m_ringBuffer->GetWritePosition();
2080 
2081  m_ringBuffer->Write(&frameheader, sizeof(frameheader));
2082 
2083  char *kfa_buf = new char[frameheader.packetlength];
2084  uint offset = 0;
2085 
2086  for (auto kfa : kfa_table)
2087  {
2088  memcpy(kfa_buf + offset, &kfa,
2089  sizeof(struct kfatable_entry));
2090  offset += sizeof(struct kfatable_entry);
2091  }
2092 
2093  m_ringBuffer->Write(kfa_buf, frameheader.packetlength);
2094 
2095 
2097  offsetof(struct extendeddata, keyframeadjust_offset),
2098  SEEK_SET);
2099 
2100  m_ringBuffer->Write(&currentpos, sizeof(long long));
2101 
2102  m_ringBuffer->WriterSeek(0, SEEK_END);
2103 
2104  delete [] kfa_buf;
2105 }
2106 
2107 void NuppelVideoRecorder::UpdateSeekTable(int frame_num, long offset)
2108 {
2109  long long position = m_ringBuffer->GetWritePosition() + offset;
2110  struct seektable_entry ste { position, frame_num};
2111  m_seekTable->push_back(ste);
2112 
2113  m_positionMapLock.lock();
2114  if (!m_positionMap.contains(ste.keyframe_number))
2115  {
2116  m_positionMapDelta[ste.keyframe_number] = position;
2117  m_positionMap[ste.keyframe_number] = position;
2118  m_lastPositionMapPos = position;
2119  }
2120  m_positionMapLock.unlock();
2121 }
2122 
2124 {
2125  m_framesWritten = 0;
2126 
2127  if (!m_ringBuffer)
2128  {
2129  LOG(VB_GENERAL, LOG_ERR, LOC +
2130  "No ringbuffer, recorder wasn't initialized.");
2131  return -1;
2132  }
2133 
2134  if (!m_ringBuffer->IsOpen())
2135  {
2136  LOG(VB_GENERAL, LOG_ERR, LOC + "Ringbuffer isn't open");
2137  return -1;
2138  }
2139 
2140  WriteHeader();
2141 
2142  return 0;
2143 }
2144 
2146 {
2147  ResetForNewFile();
2148 
2149  for (int i = 0; i < m_videoBufferCount; i++)
2150  {
2151  vidbuffertype *vidbuf = m_videoBuffer[i];
2152  vidbuf->sample = 0;
2153  vidbuf->timecode = 0;
2154  vidbuf->freeToEncode = 0;
2155  vidbuf->freeToBuffer = 1;
2156  vidbuf->forcekey = 0;
2157  }
2158 
2159  for (int i = 0; i < m_audioBufferCount; i++)
2160  {
2161  audbuffertype *audbuf = m_audioBuffer[i];
2162  audbuf->sample = 0;
2163  audbuf->timecode = 0;
2164  audbuf->freeToEncode = 0;
2165  audbuf->freeToBuffer = 1;
2166  }
2167 
2168  for (int i = 0; i < m_textBufferCount; i++)
2169  {
2170  txtbuffertype *txtbuf = m_textBuffer[i];
2171  txtbuf->freeToEncode = 0;
2172  txtbuf->freeToBuffer = 1;
2173  }
2174 
2175  m_actVideoEncode = 0;
2176  m_actVideoBuffer = 0;
2177  m_actAudioEncode = 0;
2178  m_actAudioBuffer = 0;
2179  m_actAudioSample = 0;
2180  m_actTextEncode = 0;
2181  m_actTextBuffer = 0;
2182 
2183  m_audioBytes = 0;
2184  m_effectiveDsp = 0;
2185 
2186  if (m_useAvCodec)
2188 
2189  if (m_curRecording)
2191 }
2192 
2194 {
2195  if (!m_audioDevice)
2196  {
2197  LOG(VB_GENERAL, LOG_ERR, LOC +
2198  QString("Invalid audio device (%1), exiting").arg(m_audioDeviceName));
2199  return;
2200  }
2201 
2203  {
2204  LOG(VB_GENERAL, LOG_ERR, LOC +
2205  QString("Failed to open audio device %1").arg(m_audioDeviceName));
2206  return;
2207  }
2208 
2209  if (!m_audioDevice->Start())
2210  {
2211  LOG(VB_GENERAL, LOG_ERR, LOC +
2212  QString("Failed to start audio capture on %1").arg(m_audioDeviceName));
2213  return;
2214  }
2215 
2216  struct timeval anow {};
2217  auto *buffer = new unsigned char[m_audioBufferSize];
2219 
2220  while (IsHelperRequested() && !IsErrored())
2221  {
2222  {
2223  QMutexLocker locker(&m_pauseLock);
2224  if (m_requestPause)
2225  {
2226  if (!m_audioPaused)
2227  {
2228  m_audioPaused = true;
2229  m_pauseWait.wakeAll();
2230  if (IsPaused(true) && m_tvrec)
2232  }
2233  m_unpauseWait.wait(&m_pauseLock, 100);
2234  continue;
2235  }
2236 
2237  if (!m_requestPause && m_audioPaused)
2238  {
2239  m_audioPaused = false;
2240  m_unpauseWait.wakeAll();
2241  }
2242  }
2243 
2244  if (!IsHelperRequested() || IsErrored())
2245  break;
2246 
2247  int lastread = m_audioDevice->GetSamples(buffer, m_audioBufferSize);
2248  if (m_audioBufferSize != lastread)
2249  {
2250  LOG(VB_GENERAL, LOG_ERR, LOC +
2251  QString("Short read, %1 of %2 bytes from ")
2252  .arg(lastread).arg(m_audioBufferSize) + m_audioDeviceName);
2253  }
2254 
2255  /* record the current time */
2256  /* Don't assume that the sound device's record buffer is empty
2257  (like we used to.) Measure to see how much stuff is in there,
2258  and correct for it when calculating the timestamp */
2259  gettimeofday(&anow, &m_tzone);
2260  int bytes_read = max(m_audioDevice->GetNumReadyBytes(), 0);
2261 
2262  int act = m_actAudioBuffer;
2263 
2264  if (!m_audioBuffer[act]->freeToBuffer)
2265  {
2266  LOG(VB_GENERAL, LOG_ERR, LOC + "Ran out of free AUDIO buffers :-(");
2267  m_actAudioSample++;
2268  continue;
2269  }
2270 
2271  m_audioBuffer[act]->sample = m_actAudioSample;
2272 
2273  /* calculate timecode. First compute the difference
2274  between now and stm (start time) */
2275  m_audioBuffer[act]->timecode = (anow.tv_sec - m_stm.tv_sec) * 1000 +
2276  anow.tv_usec / 1000 - m_stm.tv_usec / 1000;
2277  /* We want the timestamp to point to the start of this
2278  audio chunk. So, subtract off the length of the chunk
2279  and the length of audio still in the capture buffer. */
2280  m_audioBuffer[act]->timecode -= (int)(
2281  (bytes_read + m_audioBufferSize)
2282  * 1000.0 / (m_audioSampleRate * m_audioBytesPerSample));
2283 
2284  memcpy(m_audioBuffer[act]->buffer, buffer, m_audioBufferSize);
2285 
2286  m_audioBuffer[act]->freeToBuffer = 0;
2287  m_actAudioBuffer++;
2289  m_actAudioBuffer = 0;
2290  m_audioBuffer[act]->freeToEncode = 1;
2291 
2292  m_actAudioSample++;
2293  }
2294 
2295  delete [] buffer;
2296 
2297  if (m_audioDevice->IsOpen())
2298  m_audioDevice->Close();
2299 }
2300 
2301 #ifdef USING_V4L2
2303 {
2304  struct timeval tnow {};
2305  gettimeofday(&tnow, &m_tzone);
2306 
2307  int act = m_actTextBuffer;
2308  if (!m_textBuffer[act]->freeToBuffer)
2309  {
2310  LOG(VB_GENERAL, LOG_ERR, LOC +
2311  QString("Teletext #%1: ran out of free TEXT buffers :-(").arg(act));
2312  return;
2313  }
2314 
2315  // calculate timecode:
2316  // compute the difference between now and stm (start time)
2317  m_textBuffer[act]->timecode = (tnow.tv_sec-m_stm.tv_sec) * 1000 +
2318  tnow.tv_usec/1000 - m_stm.tv_usec/1000;
2319  m_textBuffer[act]->pagenr = (vbidata->teletextpage.pgno << 16) +
2320  vbidata->teletextpage.subno;
2321 
2322  unsigned char *inpos = vbidata->teletextpage.data[0];
2323  unsigned char *outpos = m_textBuffer[act]->buffer;
2324  *outpos = 0;
2325  struct teletextsubtitle st {};
2326  unsigned char linebuf[VT_WIDTH + 1];
2327  unsigned char *linebufpos = linebuf;
2328 
2329  for (int y = 0; y < VT_HEIGHT; y++)
2330  {
2331  char c = ' ';
2332  char last_c = ' ';
2333  int hid = 0;
2334  int gfx = 0;
2335  int dbl = 0;
2336  int box = 0;
2337  int sep = 0;
2338  int hold = 0;
2339  int visible = 0;
2340  int fg = 7;
2341  int bg = 0;
2342 
2343  for (int x = 0; x < VT_WIDTH; ++x)
2344  {
2345  c = *inpos++;
2346  switch (c)
2347  {
2348  case 0x00 ... 0x07: /* alpha + fg color */
2349  fg = c & 7;
2350  gfx = 0;
2351  sep = 0;
2352  hid = 0;
2353  goto ctrl;
2354  case 0x08: /* flash */
2355  case 0x09: /* steady */
2356  goto ctrl;
2357  case 0x0a: /* end box */
2358  box = 0;
2359  goto ctrl;
2360  case 0x0b: /* start box */
2361  box = 1;
2362  goto ctrl;
2363  case 0x0c: /* normal height */
2364  dbl = 0;
2365  goto ctrl;
2366  case 0x0d: /* double height */
2367  if (y < VT_HEIGHT-2) /* ignored on last 2 lines */
2368  {
2369  dbl = 1;
2370  }
2371  goto ctrl;
2372  case 0x10 ... 0x17: /* gfx + fg color */
2373  fg = c & 7;
2374  gfx = 1;
2375  hid = 0;
2376  goto ctrl;
2377  case 0x18: /* conceal */
2378  hid = 1;
2379  goto ctrl;
2380  case 0x19: /* contiguous gfx */
2381  hid = 0;
2382  sep = 0;
2383  goto ctrl;
2384  case 0x1a: /* separate gfx */
2385  sep = 1;
2386  goto ctrl;
2387  case 0x1c: /* black bf */
2388  bg = 0;
2389  goto ctrl;
2390  case 0x1d: /* new bg */
2391  bg = fg;
2392  goto ctrl;
2393  case 0x1e: /* hold gfx */
2394  hold = 1;
2395  goto ctrl;
2396  case 0x1f: /* release gfx */
2397  hold = 0;
2398  goto ctrl;
2399  case 0x0e: /* SO */
2400  case 0x0f: /* SI */
2401  case 0x1b: /* ESC */
2402  goto ctrl;
2403 
2404  ctrl:
2405  c = ' ';
2406  if (hold && gfx)
2407  c = last_c;
2408  break;
2409  }
2410  if (gfx)
2411  {
2412  if ((c & 0xa0) == 0x20)
2413  {
2414  last_c = c;
2415  c += (c & 0x40) ? 32 : -32;
2416  }
2417  }
2418  if (hid)
2419  c = ' ';
2420 
2421  if (visible || (c != ' '))
2422  {
2423  if (!visible)
2424  {
2425  st.row = y;
2426  st.col = x;
2427  st.dbl = dbl;
2428  st.fg = fg;
2429  st.bg = bg;
2430  linebufpos = linebuf;
2431  *linebufpos = 0;
2432  }
2433  *linebufpos++ = c;
2434  *linebufpos = 0;
2435  visible = 1;
2436  }
2437 
2438  (void) box;
2439  (void) sep;
2440  }
2441  if (visible)
2442  {
2443  st.len = linebufpos - linebuf + 1;;
2444  int max = 200;
2445  int bufsize = ((outpos - m_textBuffer[act]->buffer + 1) + st.len);
2446  if (bufsize > max)
2447  break;
2448  memcpy(outpos, &st, sizeof(st));
2449  outpos += sizeof(st);
2450  if (st.len < 42)
2451  {
2452  memcpy(outpos, linebuf, st.len);
2453  outpos += st.len;
2454  }
2455  else
2456  {
2457  memcpy(outpos, linebuf, 41);
2458  outpos += 41;
2459  }
2460  *outpos = 0;
2461  }
2462  }
2463 
2464  m_textBuffer[act]->bufferlen = outpos - m_textBuffer[act]->buffer + 1;
2465  m_textBuffer[act]->freeToBuffer = 0;
2466  m_actTextBuffer++;
2468  m_actTextBuffer = 0;
2469  m_textBuffer[act]->freeToEncode = 1;
2470 }
2471 #else // USING_V4L2
2472 void NuppelVideoRecorder::FormatTT(struct VBIData*) {}
2473 #endif // USING_V4L2
2474 
2476 {
2477  struct timeval tnow {};
2478  gettimeofday (&tnow, &m_tzone);
2479 
2480  // calculate timecode:
2481  // compute the difference between now and stm (start time)
2482  int tc = (tnow.tv_sec - m_stm.tv_sec) * 1000 +
2483  tnow.tv_usec / 1000 - m_stm.tv_usec / 1000;
2484 
2485  m_ccd->FormatCC(tc, code1, code2);
2486 }
2487 
2488 void NuppelVideoRecorder::AddTextData(unsigned char *buf, int len,
2489  int64_t timecode, char /*type*/)
2490 {
2491  int act = m_actTextBuffer;
2492  if (!m_textBuffer[act]->freeToBuffer)
2493  {
2494  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Teletext#%1").arg(act) +
2495  " ran out of free TEXT buffers :-(");
2496  return;
2497  }
2498 
2499  m_textBuffer[act]->timecode = timecode;
2500  memcpy(m_textBuffer[act]->buffer, buf, len);
2501  m_textBuffer[act]->bufferlen = len + sizeof(ccsubtitle);
2502 
2503  m_textBuffer[act]->freeToBuffer = 0;
2504  m_actTextBuffer++;
2506  m_actTextBuffer = 0;
2507  m_textBuffer[act]->freeToEncode = 1;
2508 }
2509 
2511 {
2512  while (IsHelperRequested() && !IsErrored())
2513  {
2514  {
2515  QMutexLocker locker(&m_pauseLock);
2516  if (m_requestPause)
2517  {
2518  if (!m_writePaused)
2519  {
2520  m_writePaused = true;
2521  m_pauseWait.wakeAll();
2522  if (IsPaused(true) && m_tvrec)
2524  }
2525  m_unpauseWait.wait(&m_pauseLock, 100);
2526  continue;
2527  }
2528 
2529  if (!m_requestPause && m_writePaused)
2530  {
2531  m_writePaused = false;
2532  m_unpauseWait.wakeAll();
2533  }
2534  }
2535 
2536  if (!IsHelperRequested() || IsErrored())
2537  break;
2538 
2540 
2541  enum
2542  { ACTION_NONE,
2543  ACTION_VIDEO,
2544  ACTION_AUDIO,
2545  ACTION_TEXT
2546  } action = ACTION_NONE;
2547  int firsttimecode = -1;
2548 
2549  if (m_videoBuffer[m_actVideoEncode]->freeToEncode)
2550  {
2551  action = ACTION_VIDEO;
2552  firsttimecode = m_videoBuffer[m_actVideoEncode]->timecode;
2553  }
2554 
2555  if (m_audioBufferCount &&
2556  m_audioBuffer[m_actAudioEncode]->freeToEncode &&
2557  (action == ACTION_NONE ||
2558  (m_audioBuffer[m_actAudioEncode]->timecode < firsttimecode)))
2559  {
2560  action = ACTION_AUDIO;
2561  firsttimecode = m_audioBuffer[m_actAudioEncode]->timecode;
2562  }
2563 
2564  if (m_textBufferCount &&
2565  m_textBuffer[m_actTextEncode]->freeToEncode &&
2566  (action == ACTION_NONE ||
2567  (m_textBuffer[m_actTextEncode]->timecode < firsttimecode)))
2568  {
2569  action = ACTION_TEXT;
2570  }
2571 
2572  switch (action)
2573  {
2574  case ACTION_VIDEO:
2575  {
2576  VideoFrame frame {};
2577  init(&frame,
2580 
2581  frame.frameNumber = m_videoBuffer[m_actVideoEncode]->sample;
2582  frame.timecode = m_videoBuffer[m_actVideoEncode]->timecode;
2583  frame.forcekey = m_videoBuffer[m_actVideoEncode]->forcekey;
2584 
2585  WriteVideo(&frame);
2586 
2587  m_videoBuffer[m_actVideoEncode]->sample = 0;
2588  m_videoBuffer[m_actVideoEncode]->freeToEncode = 0;
2589  m_videoBuffer[m_actVideoEncode]->freeToBuffer = 1;
2590  m_videoBuffer[m_actVideoEncode]->forcekey = 0;
2591  m_actVideoEncode++;
2593  m_actVideoEncode = 0;
2594  break;
2595  }
2596  case ACTION_AUDIO:
2597  {
2600  m_audioBuffer[m_actAudioEncode]->timecode);
2601  if (IsErrored()) {
2602  LOG(VB_GENERAL, LOG_ERR, LOC +
2603  "ACTION_AUDIO cannot be completed due to error.");
2604  StopRecording();
2605  break;
2606  }
2607  m_audioBuffer[m_actAudioEncode]->sample = 0;
2608  m_audioBuffer[m_actAudioEncode]->freeToEncode = 0;
2609  m_audioBuffer[m_actAudioEncode]->freeToBuffer = 1;
2610  m_actAudioEncode++;
2612  m_actAudioEncode = 0;
2613  break;
2614  }
2615  case ACTION_TEXT:
2616  {
2618  m_textBuffer[m_actTextEncode]->bufferlen,
2619  m_textBuffer[m_actTextEncode]->timecode,
2620  m_textBuffer[m_actTextEncode]->pagenr);
2621  m_textBuffer[m_actTextEncode]->freeToEncode = 0;
2622  m_textBuffer[m_actTextEncode]->freeToBuffer = 1;
2623  m_actTextEncode++;
2625  m_actTextEncode = 0;
2626  break;
2627  }
2628  default:
2629  {
2630  usleep(100);
2631  break;
2632  }
2633  }
2634  }
2635 }
2636 
2638 {
2639  m_framesWritten = 0;
2640  m_lf = 0;
2641  m_lastBlock = 0;
2642 
2643  m_seekTable->clear();
2644 
2645  ClearStatistics();
2646 
2647  m_positionMapLock.lock();
2648  m_positionMap.clear();
2649  m_positionMapDelta.clear();
2650  m_positionMapLock.unlock();
2651 
2652  if (m_go7007)
2653  m_resetCapture = true;
2654 }
2655 
2657 {
2658  CreateNuppelFile();
2659 }
2660 
2662 {
2664 
2665  WriteSeekTable();
2666 
2668 
2669  m_positionMapLock.lock();
2670  m_positionMap.clear();
2671  m_positionMapDelta.clear();
2672  m_positionMapLock.unlock();
2673 }
2674 
2675 void NuppelVideoRecorder::WriteVideo(VideoFrame *frame, bool skipsync,
2676  bool forcekey)
2677 {
2678  int tmp = 0;
2679  lzo_uint out_len = OUT_LEN;
2680  struct rtframeheader frameheader {};
2681  int raw = 0;
2682  int compressthis = m_compression;
2683  // cppcheck-suppress variableScope
2684  uint8_t *planes[3] = {
2685  frame->buf + frame->offsets[0],
2686  frame->buf + frame->offsets[1],
2687  frame->buf + frame->offsets[2] };
2688  int fnum = frame->frameNumber;
2689  long long timecode = frame->timecode;
2690 
2691  if (m_lf == 0)
2692  { // this will be triggered every new file
2693  m_lf = fnum;
2694  m_startNum = fnum;
2695  m_lastTimecode = 0;
2696  m_frameOfGop = 0;
2697  forcekey = true;
2698  }
2699 
2700  // see if it's time for a seeker header, sync information and a keyframe
2701  frameheader.keyframe = m_frameOfGop; // no keyframe defaulted
2702 
2703  bool wantkeyframe = forcekey;
2704 
2705  bool writesync = false;
2706 
2707  if ((!m_go7007 && (((fnum-m_startNum)>>1) % m_keyframeDist == 0 && !skipsync)) ||
2708  (m_go7007 && frame->forcekey))
2709  writesync = true;
2710 
2711  if (writesync)
2712  {
2713  m_ringBuffer->Write("RTjjjjjjjjjjjjjjjjjjjjjjjj", FRAMEHEADERSIZE);
2714 
2715  UpdateSeekTable(((fnum - m_startNum) >> 1) / m_keyframeDist);
2716 
2717  frameheader.frametype = 'S'; // sync frame
2718  frameheader.comptype = 'V'; // video sync information
2719  frameheader.filters = 0; // no filters applied
2720  frameheader.packetlength = 0; // no data packet
2721  frameheader.timecode = (fnum-m_startNum)>>1;
2722  // write video sync info
2723  WriteFrameheader(&frameheader);
2724  frameheader.frametype = 'S'; // sync frame
2725  frameheader.comptype = 'A'; // video sync information
2726  frameheader.filters = 0; // no filters applied
2727  frameheader.packetlength = 0; // no data packet
2728  frameheader.timecode = m_effectiveDsp; // effective dsp frequency
2729  // write audio sync info
2730  WriteFrameheader(&frameheader);
2731 
2732  wantkeyframe = true;
2733  //m_ringBuffer->Sync();
2734  }
2735 
2736  if (wantkeyframe)
2737  {
2738  frameheader.keyframe=0;
2739  m_frameOfGop=0;
2740  }
2741 
2742  if (m_useAvCodec)
2743  {
2744  MythAVFrame mpa_picture;
2745  AVPictureFill(mpa_picture, frame);
2746 
2747  if (wantkeyframe)
2748  mpa_picture->pict_type = AV_PICTURE_TYPE_I;
2749  else
2750  mpa_picture->pict_type = AV_PICTURE_TYPE_NONE;
2751 
2752  if (!m_hardwareEncode)
2753  {
2754  AVPacket packet;
2755  av_init_packet(&packet);
2756  packet.data = (uint8_t *)m_strm;
2757  packet.size = frame->size;
2758 
2759  int got_packet = 0;
2760 
2761  QMutexLocker locker(avcodeclock);
2762  tmp = avcodec_encode_video2(m_mpaVidCtx, &packet, mpa_picture,
2763  &got_packet);
2764 
2765  if (tmp < 0 || !got_packet)
2766  {
2767  LOG(VB_GENERAL, LOG_ERR, LOC +
2768  "WriteVideo : avcodec_encode_video() failed");
2769  return;
2770  }
2771 
2772  tmp = packet.size;
2773  }
2774  }
2775  else
2776  {
2777  int freecount = 0;
2778  freecount = m_actVideoBuffer > m_actVideoEncode ?
2781 
2782  if (freecount < (m_videoBufferCount / 3))
2783  compressthis = 0; // speed up the encode process
2784 
2785  if (freecount < 5)
2786  raw = 1; // speed up the encode process
2787 
2788  if (m_transcoding)
2789  {
2790  raw = 0;
2791  compressthis = 1;
2792  }
2793 
2794  if (!raw)
2795  {
2796  if (wantkeyframe)
2797  m_rtjc->SetNextKey();
2799  }
2800  else
2801  tmp = frame->size;
2802 
2803  // here is lzo compression afterwards
2804  if (compressthis)
2805  {
2806  int r = 0;
2807  if (raw)
2808  {
2809  r = lzo1x_1_compress(frame->buf, frame->size,
2810  m_out, &out_len, wrkmem);
2811  }
2812  else
2813  {
2814  r = lzo1x_1_compress((unsigned char *)m_strm, tmp, m_out,
2815  &out_len, wrkmem);
2816  }
2817  if (r != LZO_E_OK)
2818  {
2819  LOG(VB_GENERAL, LOG_ERR, LOC + "lzo compression failed");
2820  return;
2821  }
2822  }
2823  }
2824 
2825  frameheader.frametype = 'V'; // video frame
2826  frameheader.timecode = timecode;
2827  m_lastTimecode = frameheader.timecode;
2828  frameheader.filters = 0; // no filters applied
2829 
2830  // compr ends here
2831  if (m_useAvCodec)
2832  {
2833  if (m_mpaVidCodec->id == AV_CODEC_ID_RAWVIDEO)
2834  {
2835  frameheader.comptype = '0';
2836  frameheader.packetlength = frame->size;
2837  WriteFrameheader(&frameheader);
2838  m_ringBuffer->Write(frame->buf, frame->size);
2839  }
2840  else if (m_hardwareEncode)
2841  {
2842  frameheader.comptype = '4';
2843  frameheader.packetlength = frame->size;
2844  WriteFrameheader(&frameheader);
2845  m_ringBuffer->Write(frame->buf, frame->size);
2846  }
2847  else
2848  {
2849  frameheader.comptype = '4';
2850  frameheader.packetlength = tmp;
2851  WriteFrameheader(&frameheader);
2853  }
2854  }
2855  else if (compressthis == 0 || (tmp < (int)out_len))
2856  {
2857  if (!raw)
2858  {
2859  frameheader.comptype = '1'; // video compression: RTjpeg only
2860  frameheader.packetlength = tmp;
2861  WriteFrameheader(&frameheader);
2863  }
2864  else
2865  {
2866  frameheader.comptype = '0'; // raw YUV420
2867  frameheader.packetlength = frame->size;
2868  WriteFrameheader(&frameheader);
2869  m_ringBuffer->Write(frame->buf, frame->size); // we write buf directly
2870  }
2871  }
2872  else
2873  {
2874  if (!raw)
2875  frameheader.comptype = '2'; // video compression: RTjpeg with lzo
2876  else
2877  frameheader.comptype = '3'; // raw YUV420 with lzo
2878  frameheader.packetlength = out_len;
2879  WriteFrameheader(&frameheader);
2880  m_ringBuffer->Write(m_out, out_len);
2881  }
2882 
2883  if (m_framesWritten == 0)
2884  SendMythSystemRecEvent("REC_STARTED_WRITING", m_curRecording);
2885 
2886  m_frameOfGop++;
2887  m_framesWritten++;
2888 
2889  // now we reset the last frame number so that we can find out
2890  // how many frames we didn't get next time
2891  m_lf = fnum;
2892 }
2893 
2894 #if HAVE_BIGENDIAN
2895 static void bswap_16_buf(short int *buf, int buf_cnt, int audio_channels)
2896  __attribute__ ((unused)); /* <- suppress compiler warning */
2897 
2898 static void bswap_16_buf(short int *buf, int buf_cnt, int audio_channels)
2899 {
2900  for (int i = 0; i < audio_channels * buf_cnt; i++)
2901  buf[i] = bswap_16(buf[i]);
2902 }
2903 #endif
2904 
2905 void NuppelVideoRecorder::WriteAudio(unsigned char *buf, int fnum, int timecode)
2906 {
2907  struct rtframeheader frameheader {};
2908 
2909  if (m_lastBlock == 0)
2910  {
2911  m_firstTc = -1;
2912  }
2913 
2914  if (m_lastBlock != 0)
2915  {
2916  if (fnum != (m_lastBlock+1))
2917  {
2918  m_audioBehind = fnum - (m_lastBlock+1);
2919  LOG(VB_RECORD, LOG_INFO, LOC + QString("audio behind %1 %2").
2920  arg(m_lastBlock).arg(fnum));
2921  }
2922  }
2923 
2924  frameheader.frametype = 'A'; // audio frame
2925  frameheader.timecode = timecode;
2926 
2927  if (m_firstTc == -1)
2928  {
2929  m_firstTc = timecode;
2930 #if 0
2931  LOG(VB_GENERAL, LOG_DEBUG, LOC +
2932  QString("first timecode=%1").arg(m_firstTc));
2933 #endif
2934  }
2935  else
2936  {
2937  timecode -= m_firstTc; // this is to avoid the lack between the beginning
2938  // of recording and the first timestamp, maybe we
2939  // can calculate the audio-video +-lack at the
2940  // beginning too
2941  auto abytes = (double)m_audioBytes; // - (double)m_audioBufferSize;
2942  // wrong guess ;-)
2943  // need seconds instead of msec's
2944  auto mt = (double)timecode;
2945  if (mt > 0.0)
2946  {
2947  double eff = (abytes / mt) * (100000.0 / m_audioBytesPerSample);
2948  m_effectiveDsp = (int)eff;
2949  }
2950  }
2951 
2952  if (m_compressAudio)
2953  {
2954  char mp3gapless[7200];
2955  int compressedsize = 0;
2956  int gaplesssize = 0;
2957  int lameret = 0;
2958 
2959  int sample_cnt = m_audioBufferSize / m_audioBytesPerSample;
2960 
2961 #if HAVE_BIGENDIAN
2962  bswap_16_buf((short int*) buf, sample_cnt, m_audioChannels);
2963 #endif
2964 
2965  if (m_audioChannels == 2)
2966  {
2967  lameret = lame_encode_buffer_interleaved(
2968  m_gf, (short int*) buf, sample_cnt,
2969  (unsigned char*) m_mp3Buf, m_mp3BufSize);
2970  }
2971  else
2972  {
2973  lameret = lame_encode_buffer(
2974  m_gf, (short int*) buf, (short int*) buf, sample_cnt,
2975  (unsigned char*) m_mp3Buf, m_mp3BufSize);
2976  }
2977 
2978  if (lameret < 0)
2979  {
2980  LOG(VB_GENERAL, LOG_ERR, LOC +
2981  QString("lame error '%1'").arg(lameret));
2982  m_error = QString("Audio Encoding Error '%1'")
2983  .arg(lameret);
2984  return;
2985  }
2986  compressedsize = lameret;
2987 
2988  lameret = lame_encode_flush_nogap(m_gf, (unsigned char *)mp3gapless,
2989  7200);
2990  if (lameret < 0)
2991  {
2992  LOG(VB_GENERAL, LOG_ERR, LOC +
2993  QString("lame error '%1'").arg(lameret));
2994  m_error = QString("Audio Encoding Error '%1'")
2995  .arg(lameret);
2996  return;
2997  }
2998  gaplesssize = lameret;
2999 
3000  frameheader.comptype = '3'; // audio is compressed
3001  frameheader.packetlength = compressedsize + gaplesssize;
3002 
3003  if (frameheader.packetlength > 0)
3004  {
3005  WriteFrameheader(&frameheader);
3006  m_ringBuffer->Write(m_mp3Buf, compressedsize);
3007  m_ringBuffer->Write(mp3gapless, gaplesssize);
3008  }
3010  }
3011  else
3012  {
3013  frameheader.comptype = '0'; // uncompressed audio
3014  frameheader.packetlength = m_audioBufferSize;
3015 
3016  WriteFrameheader(&frameheader);
3018  m_audioBytes += m_audioBufferSize; // only audio no header!!
3019  }
3020 
3021  // this will probably never happen and if there would be a
3022  // 'uncountable' video frame drop -> material==worthless
3023  if (m_audioBehind > 0)
3024  {
3025  LOG(VB_RECORD, LOG_INFO, LOC + "audio behind");
3026  frameheader.frametype = 'A'; // audio frame
3027  frameheader.comptype = 'N'; // output a nullframe with
3028  frameheader.packetlength = 0;
3029  WriteFrameheader(&frameheader);
3031  m_audioBehind--;
3032  }
3033 
3034  m_lastBlock = fnum;
3035 }
3036 
3037 void NuppelVideoRecorder::WriteText(unsigned char *buf, int len, int timecode,
3038  int pagenr)
3039 {
3040  struct rtframeheader frameheader {};
3041 
3042  frameheader.frametype = 'T'; // text frame
3043  frameheader.timecode = timecode;
3044 
3045  if (VBIMode::PAL_TT == m_vbiMode)
3046  {
3047  frameheader.comptype = 'T'; // european teletext
3048  frameheader.packetlength = len + 4;
3049  WriteFrameheader(&frameheader);
3050  union page_t {
3051  int32_t m_val32;
3052  struct { int8_t m_a,m_b,m_c,m_d; } m_val8;
3053  } v {};
3054  v.m_val32 = pagenr;
3055  m_ringBuffer->Write(&v.m_val8.m_d, sizeof(int8_t));
3056  m_ringBuffer->Write(&v.m_val8.m_c, sizeof(int8_t));
3057  m_ringBuffer->Write(&v.m_val8.m_b, sizeof(int8_t));
3058  m_ringBuffer->Write(&v.m_val8.m_a, sizeof(int8_t));
3059  m_ringBuffer->Write(buf, len);
3060  }
3061  else if (VBIMode::NTSC_CC == m_vbiMode)
3062  {
3063  frameheader.comptype = 'C'; // NTSC CC
3064  frameheader.packetlength = len;
3065 
3066  WriteFrameheader(&frameheader);
3067  m_ringBuffer->Write(buf, len);
3068  }
3069 }
3070 
3071 /* vim: set expandtab tabstop=4 shiftwidth=4: */
int timecode
Definition: format.h:153
bool IsRecording(void) override
Tells whether the StartRecorder() loop is running.
double m_videoFrameRate
Definition: recorderbase.h:315
int lavc_qmax
Definition: format.h:112
#define FOURCC_I263
Definition: fourcc.h:97
int audio_bits_per_sample
Definition: format.h:99
QString m_audioDeviceName
Definition: v4lrecorder.h:46
unsigned char m_b
Definition: ParseText.cpp:329
void AddTextData(unsigned char *buf, int len, int64_t timecode, char type) override
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:3264
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
NVRAudioThread * m_audioThread
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
QMutex m_pauseLock
Definition: recorderbase.h:326
#define FOURCC_H263
Definition: fourcc.h:94
#define MJPIOC_SYNC
void FormatTT(struct VBIData *vbidata) override
long long GetFramesWritten(void) override
Returns number of frames written to disk.
int SetIntra(int *key, int *lm, int *cm)
Definition: RTjpegN.cpp:2724
#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:47
void AspectChange(uint aspect, long long frame)
Note a change in aspect ratio in the recordedmark table.
NVRWriteThread * m_writeThread
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:2689
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
AVPixelFormat m_pictureFormat
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
vector< struct seektable_entry > * m_seekTable
virtual bool IsHelperRequested(void) const
Definition: v4lrecorder.cpp:53
VBIThread * m_vbiThread
Definition: v4lrecorder.h:55
char pimode
Definition: format.h:20
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
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
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
unsigned long long m_audioBytes
#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:142
static void clear(SettingsMap &cache, SettingsMap &overrides, const QString &myKey)
Definition: mythdb.cpp:846
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
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:3269
QString GetSetting(const QString &key, const QString &defaultval="")
int freeToBuffer
Definition: format.h:144
void SetVideoFilters(QString &filters) override
Tells recorder which filters to use.
bool m_requestRecording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:332
lame_global_flags * m_gf
#define GO7007IOC_S_MPEG_PARAMS
Definition: go7007_myth.h:87
void RecorderPaused(void)
This is a callback, called by the "recorder" instance when it has actually paused.
Definition: tv_rec.cpp:2942
int SetFormat(const int *fmt)
Definition: RTjpegN.cpp:2683
#define GO7007_COMP_CLOSED_GOP
Definition: go7007_myth.h:45
double aspect
Definition: format.h:22
bool m_requestPause
Definition: recorderbase.h:327
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
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
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:2666
int m_encodingThreadCount
Number of threads to use for MPEG-2 and MPEG-4 encoding.
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
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
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
int subno
Definition: vt.h:38
vector< struct vidbuffertype * > m_videoBuffer
void FormatCC(int tc, int code1, int code2)
void BufferIt(unsigned char *buf, int len=-1, bool forcekey=false)
QString m_error
non-empty iff irrecoverable recording error detected
Definition: dtvrecorder.h:161
#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.
AVCodecContext * m_mpaVidCtx
vector< struct audbuffertype * > m_audioBuffer
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
bool m_ntscFrameRate
Definition: recorderbase.h:314
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
vector< struct txtbuffertype * > m_textBuffer
uint m_videoHeight
Definition: recorderbase.h:319
virtual bool IsRecordingRequested(void)
Tells us if StopRecording() has been called.
volatile bool m_requestHelper
Definition: v4lrecorder.h:58