AJA NTV2 SDK  18.1.0.2439
NTV2 SDK 18.1.0.2439
ntv2dolbycapture.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
9 #include "ntv2dolbycapture.h"
10 #include "ntv2devicescanner.h"
11 #include "ajabase/system/process.h"
12 #include <fstream> // for ofstream
13 
14 using namespace std;
15 
16 #define NTV2_BUFFER_LOCK
17 
18 #define NTV2_AUDIOSIZE_MAX (401 * 1024)
19 #define NTV2_ANCILLARYSIZE_MAX (256 * 1024)
20 
21 
23 
25  : mConsumerThread (AJAThread()),
26  mProducerThread (AJAThread()),
27  mConfig (inConfig),
29  mSavedTaskMode (NTV2_DISABLE_TASKS),
30  mAudioSystem (NTV2_AUDIOSYSTEM_1),
31  mHostBuffers (),
32  mAVCircularBuffer (),
33  mGlobalQuit (false)
34 {
35 } // constructor
36 
37 
39 {
40  // Stop my capture and consumer threads, then destroy them...
41  Quit();
42 
43  // Unsubscribe from input vertical event...
45 
46 } // destructor
47 
48 
50 {
51  // Set the global 'quit' flag, and wait for the threads to go inactive...
52  mGlobalQuit = true;
53 
54  while (mConsumerThread.Active())
55  AJATime::Sleep(10);
56 
57  while (mProducerThread.Active())
58  AJATime::Sleep(10);
59 
60  // Restore some of the device's former state...
61  if (!mConfig.fDoMultiFormat)
62  {
64  mDevice.SetTaskMode(mSavedTaskMode); // Restore prior task mode
65  }
66 
67 } // Quit
68 
69 
71 {
73 
74  // Open the device...
76  { if (aja::lower(mConfig.fDeviceSpec) != "list" && mConfig.fDeviceSpec != "?")
77  cerr << "## ERROR: Device '" << mConfig.fDeviceSpec << "' not found" << endl;
78  return AJA_STATUS_OPEN;
79  }
80  if (!mDevice.IsDeviceReady(false))
81  {cerr << "## ERROR: '" << mDevice.GetDescription() << "' not ready" << endl; return AJA_STATUS_INITIALIZE;}
82  if (!mDevice.features().CanDoCapture())
83  {cerr << "## ERROR: '" << mDevice.GetDescription() << "' is playback-only" << endl; return AJA_STATUS_FEATURE;}
84 
85  if (!mDevice.features().CanDoFrameBufferFormat(mConfig.fPixelFormat))
86  { cerr << "## ERROR: '" << mDevice.GetDescription() << "' doesn't support '"
87  << ::NTV2FrameBufferFormatToString(mConfig.fPixelFormat, true) << "' ("
88  << ::NTV2FrameBufferFormatToString(mConfig.fPixelFormat, false) << ", " << DEC(mConfig.fPixelFormat) << ")" << endl;
90  }
91 
92  ULWord appSignature (0);
93  int32_t appPID (0);
94  mDevice.GetStreamingApplication (appSignature, appPID); // Who currently "owns" the device?
95  mDevice.GetTaskMode(mSavedTaskMode); // Save the current device state
96  if (!mConfig.fDoMultiFormat)
97  {
99  {
100  cerr << "## ERROR: Unable to acquire '" << mDevice.GetDescription() << "' because another app (pid " << appPID << ") owns it" << endl;
101  return AJA_STATUS_BUSY; // Another app is using the device
102  }
103  }
104  mDevice.SetTaskMode(NTV2_OEM_TASKS); // Prevent interference from AJA retail services
105 
106  if (mDevice.features().CanDoMultiFormat())
107  mDevice.SetMultiFormatMode(mConfig.fDoMultiFormat);
108 
109  // This demo permits input source and channel to be specified independently.
111  mConfig.fInputChannel = NTV2_CHANNEL1;
116  if (!mDevice.features().CanDoInputSource(mConfig.fInputSource))
117  {
118  cerr << "## ERROR: No such input '" << ::NTV2InputSourceToString(mConfig.fInputSource, /*compact?*/true)
119  << "' on '" << mDevice.GetDescription() << "'" << endl;
120  return AJA_STATUS_UNSUPPORTED;
121  }
122  if (!mConfig.fAncDataFilePath.empty() && !mDevice.features().CanDoHDMIAuxCapture())
123  {cerr << "## ERROR: HDMI aux capture requested, but '" << mDevice.GetDescription() << "' has no HDMI aux extractors" << endl; return AJA_STATUS_UNSUPPORTED;}
124 
125  // Set up the video and audio...
126  status = SetupVideo();
127  if (AJA_FAILURE(status))
128  return status;
129 
130  status = SetupAudio();
131  if (AJA_FAILURE(status))
132  return status;
133 
134  // Set up the circular buffers, the device signal routing, and both playout and capture AutoCirculate...
136  if (!RouteInputSignal())
137  return AJA_STATUS_FAIL;
138 
139  mDolbyState = 0;
140 
141  #if defined(_DEBUG)
142  cerr << mConfig
143  << "Device Description: " << mDevice.GetDescription() << endl
144  << endl;
145  #endif // defined(_DEBUG)
146  return AJA_STATUS_SUCCESS;
147 
148 } // Init
149 
150 
152 {
153  // Sometimes other applications disable some or all of the frame buffers, so turn on ours now...
154  mDevice.EnableChannel(mConfig.fInputChannel);
155 
156  // Enable and subscribe to the interrupts for the channel to be used...
157  mDevice.EnableInputInterrupt(mConfig.fInputChannel);
159 
160  // Determine the input video signal format...
161  mVideoFormat = mDevice.GetInputVideoFormat(mConfig.fInputSource);
162  if (mVideoFormat == NTV2_FORMAT_UNKNOWN)
163  { cerr << "## ERROR: No input signal or unknown format on '" << ::NTV2InputSourceToString(mConfig.fInputSource, true)
164  << "'" << endl;
165  return AJA_STATUS_NOINPUT;
166  }
167  if (!mDevice.features().CanDoVideoFormat(mVideoFormat))
168  { cerr << "## ERROR: '" << mDevice.GetDescription() << "' cannot handle " << ::NTV2VideoFormatToString(mVideoFormat) << endl;
169  return AJA_STATUS_UNSUPPORTED; // Device can't handle this format
170  }
171  CAPNOTE(::NTV2VideoFormatToString(mVideoFormat) << " detected on " << ::NTV2InputSourceToString(mConfig.fInputSource,true) << " on " << mDevice.GetDescription());
172  mFormatDesc = NTV2FormatDescriptor(mVideoFormat, mConfig.fPixelFormat);
173 
174  // Set the device video format to whatever we detected at the input...
175  if (NTV2_IS_4K_VIDEO_FORMAT(mVideoFormat))
176  {
178  mDevice.SetVideoFormat (mVideoFormat, false, false, NTV2_CHANNEL1);
179  mDevice.SetTsiFrameEnable(true, NTV2_CHANNEL1);
181  }
182  else
183  {
185  mDevice.SetVideoFormat (mVideoFormat, false, false, mConfig.fInputChannel);
186  mDevice.SetTsiFrameEnable(false, mConfig.fInputChannel);
187  mDevice.SetFrameBufferFormat (mConfig.fInputChannel, mConfig.fPixelFormat);
188  }
189  return AJA_STATUS_SUCCESS;
190 
191 } // SetupVideo
192 
193 
195 {
196  return AJA_STATUS_SUCCESS;
197 
198 } // SetupAudio
199 
200 
202 {
203  // Let my circular buffer know when it's time to quit...
204  mAVCircularBuffer.SetAbortFlag (&mGlobalQuit);
205 
206  bool isProgressive = NTV2_VIDEO_FORMAT_HAS_PROGRESSIVE_PICTURE(mVideoFormat);
207  if (isProgressive)
209  else
211 
212  ULWord F1AncSize(0), F2AncSize(0);
213  ULWord F1OffsetFromEnd(0), F2OffsetFromEnd(0);
214  mDevice.ReadRegister(kVRegAncField1Offset, F1OffsetFromEnd); // # bytes from end of 8MB/16MB frame
215  mDevice.ReadRegister(kVRegAncField2Offset, F2OffsetFromEnd); // # bytes from end of 8MB/16MB frame
216  // Based on the offsets, calculate the max anc capacity
217  F1AncSize = F2OffsetFromEnd > F1OffsetFromEnd ? 0 : F1OffsetFromEnd - F2OffsetFromEnd;
218  F2AncSize = F2OffsetFromEnd > F1OffsetFromEnd ? F2OffsetFromEnd - F1OffsetFromEnd : F2OffsetFromEnd;
219 
220  // Allocate and add each in-host NTV2FrameData to my circular buffer member variable...
221  const size_t audioBufferSize (NTV2_AUDIOSIZE_MAX);
222  mHostBuffers.reserve(size_t(CIRCULAR_BUFFER_SIZE));
223  while (mHostBuffers.size() < size_t(CIRCULAR_BUFFER_SIZE))
224  {
225  mHostBuffers.push_back(NTV2FrameData());
226  NTV2FrameData & frameData(mHostBuffers.back());
227  frameData.fVideoBuffer.Allocate(mFormatDesc.GetVideoWriteSize());
228  frameData.fAudioBuffer.Allocate(audioBufferSize);
229  frameData.fAncBuffer.Allocate(F1AncSize);
230  frameData.fAncBuffer2.Allocate(F2AncSize);
231  mAVCircularBuffer.Add(&frameData);
232 
233 #ifdef NTV2_BUFFER_LOCK
234  // Page lock the memory
235  if (frameData.fVideoBuffer)
236  mDevice.DMABufferLock(frameData.fVideoBuffer, true);
237  if (frameData.fAncBuffer)
238  mDevice.DMABufferLock(frameData.fAncBuffer, true);
239  if (frameData.fAncBuffer2)
240  mDevice.DMABufferLock(frameData.fAncBuffer2, true);
241 #endif
242  } // for each NTV2FrameData
243 
244 } // SetupHostBuffers
245 
246 
248 {
249 
253 
254  const bool isInputRGB (inputColorSpace == NTV2_LHIHDMIColorSpaceRGB);
256 
257  return CNTV2DemoCommon::GetInputRouting (connections, mConfig, isInputRGB)
258  && mDevice.ApplySignalRoute(connections, !mConfig.fDoMultiFormat);
259 
260 } // RouteInputSignal
261 
262 
264 {
265  // Start the playout and capture threads...
268  return AJA_STATUS_SUCCESS;
269 
270 } // Run
271 
272 
274 
275 // This is where we will start the consumer thread
277 {
278  // Create and start the consumer thread...
279  mConsumerThread.Attach(ConsumerThreadStatic, this);
280  mConsumerThread.SetPriority(AJA_ThreadPriority_High);
281  mConsumerThread.Start();
282 
283 } // StartConsumerThread
284 
285 
286 // The consumer thread function
287 void NTV2DolbyCapture::ConsumerThreadStatic (AJAThread * pThread, void * pContext) // static
288 {
289  (void) pThread;
290 
291  // Grab the NTV2DolbyCapture instance pointer from the pContext parameter,
292  // then call its ConsumeFrames method...
293  NTV2DolbyCapture * pApp (reinterpret_cast <NTV2DolbyCapture *> (pContext));
294  pApp->ConsumeFrames();
295 
296 } // ConsumerThreadStatic
297 
298 
300 {
301  CAPNOTE("Thread started");
302  uint64_t ancTally(0);
303  uint64_t audioTally(0);
304  uint64_t dolbyTally(0);
305  ofstream * pOFS(mConfig.fAncDataFilePath.empty() ? AJA_NULL : new ofstream(mConfig.fAncDataFilePath.c_str(), ios::binary));
306  ofstream * pAFS(mConfig.fAudioDataFilePath.empty() ? AJA_NULL : new ofstream(mConfig.fAudioDataFilePath.c_str(), ios::binary));
307  ofstream * pDFS(mConfig.fDolbyDataFilePath.empty() ? AJA_NULL : new ofstream(mConfig.fDolbyDataFilePath.c_str(), ios::binary));
308  while (!mGlobalQuit)
309  {
310  // Wait for the next frame to become ready to "consume"...
311  NTV2FrameData * pFrameData (mAVCircularBuffer.StartConsumeNextBuffer ());
312  if (pFrameData)
313  {
314  // Do something useful with the frame data...
315  // . . . . . . . . . . . .
316  // . . . . . . . . . . . .
317  // . . . . . . . . . . . .
318  if (mConfig.fDoFrameStats && pFrameData->AncBuffer())
319  {
320  uint8_t* pData = (uint8_t*)pFrameData->AncBuffer().GetHostAddress(0);
321  uint32_t i;
322  for (i = 0; i < pFrameData->AncBufferSize(); i += 32)
323  {
324  if (pData[i] == 0xff)
325  break;
326  }
327  uint32_t audioSize = RecoverAudio(pFrameData->AncBuffer(), pFrameData->AncBufferSize() /*pFrameData->NumCapturedAncBytes()*/, pFrameData->AudioBuffer());
328  cout << "f1 size reg " << DEC(pFrameData->NumCapturedAncBytes()) << " ffs " << DEC(i) << " samples " << DEC(audioSize/4) << endl << flush;
329  }
330 
331  if (mConfig.fDoFrameStats && pFrameData->AncBuffer2())
332  {
333  uint8_t* pData = (uint8_t*)pFrameData->AncBuffer2().GetHostAddress(0);
334  uint32_t i;
335  for (i = 0; i < pFrameData->AncBuffer2Size(); i += 32)
336  {
337  if (pData[i] == 0xff)
338  break;
339  }
340  uint32_t audioSize = RecoverAudio(pFrameData->AncBuffer2(), pFrameData->AncBuffer2Size() /*pFrameData->NumCapturedAnc2Bytes()*/, pFrameData->AudioBuffer());
341  cout << "f2 size reg " << DEC(pFrameData->NumCapturedAnc2Bytes()) << " ffs " << DEC(i) << " samples " << DEC(audioSize/4) << endl << flush;
342  }
343 
344  if (pOFS && pFrameData->AncBuffer())
345  {
346  if (pOFS && !ancTally++)
347  CAPNOTE("Writing raw anc to '" + mConfig.fAncDataFilePath + "'");
348  pOFS->write(pFrameData->AncBuffer(), streamsize(pFrameData->NumCapturedAncBytes()));
349  ancTally++;
350  if ( pFrameData->AncBuffer2())
351  {
352  if (pOFS && pFrameData->AncBuffer2())
353  pOFS->write(pFrameData->AncBuffer2(), streamsize(pFrameData->NumCapturedAnc2Bytes()));
354  }
355  }
356 
357  if (pAFS && pFrameData->AncBuffer() && pFrameData->AudioBuffer())
358  {
359  if (pAFS && !audioTally++)
360  CAPNOTE("Writing raw audio to '" + mConfig.fAudioDataFilePath + "'");
361  uint32_t audioSize = RecoverAudio(pFrameData->AncBuffer(), pFrameData->NumCapturedAncBytes(), pFrameData->AudioBuffer());
362  pAFS->write(pFrameData->AudioBuffer(), streamsize(audioSize));
363  audioTally++;
364  if ( pFrameData->AncBuffer2())
365  {
366  audioSize = RecoverAudio(pFrameData->AncBuffer2(), pFrameData->NumCapturedAnc2Bytes(), pFrameData->AudioBuffer());
367  pAFS->write(pFrameData->AudioBuffer(), streamsize(audioSize));
368  }
369  }
370 
371  if (pDFS && pFrameData->AncBuffer() && pFrameData->AudioBuffer())
372  {
373  if (pDFS && !dolbyTally++)
374  CAPNOTE("Writing dolby file to '" + mConfig.fDolbyDataFilePath + "'");
375  uint32_t audioSize = RecoverAudio(pFrameData->AncBuffer(), pFrameData->NumCapturedAncBytes(), pFrameData->AudioBuffer());
376  uint32_t dolbySize = RecoverDolby(pFrameData->AudioBuffer(), audioSize, pFrameData->AncBuffer());
377  pDFS->write(pFrameData->AncBuffer(), streamsize(dolbySize));
378  dolbyTally++;
379  if ( pFrameData->AncBuffer2())
380  {
381  audioSize = RecoverAudio(pFrameData->AncBuffer2(), pFrameData->NumCapturedAnc2Bytes(), pFrameData->AudioBuffer());
382  dolbySize = RecoverDolby(pFrameData->AudioBuffer(), audioSize, pFrameData->AncBuffer2());
383  pDFS->write(pFrameData->AncBuffer2(), streamsize(dolbySize));
384  }
385  }
386  // Now release and recycle the buffer...
387  mAVCircularBuffer.EndConsumeNextBuffer ();
388  } // if pFrameData
389  } // loop til quit signaled
390  if (pOFS)
391  { delete pOFS; cerr << "Wrote " << DEC(ancTally) << " frames of raw anc data" << endl; }
392  if (pAFS)
393  { delete pAFS; cerr << "Wrote " << DEC(audioTally) << " frames of raw audio data" << endl; }
394  if (pDFS)
395  { delete pDFS; cerr << "Wrote " << DEC(dolbyTally) << " frames of dolby data" << endl; }
396  CAPNOTE("Thread completed, will exit");
397 
398 } // ConsumeFrames
399 
400 
402 
403 // This starts the capture (producer) thread
405 {
406  // Create and start the capture thread...
407  mProducerThread.Attach(ProducerThreadStatic, this);
408  mProducerThread.SetPriority(AJA_ThreadPriority_High);
409  mProducerThread.Start();
410 
411 } // StartProducerThread
412 
413 
414 // The capture thread function
415 void NTV2DolbyCapture::ProducerThreadStatic (AJAThread * pThread, void * pContext) // static
416 {
417  (void) pThread;
418 
419  // Grab the NTV2DolbyCapture instance pointer from the pContext parameter,
420  // then call its CaptureFrames method...
421  NTV2DolbyCapture * pApp (reinterpret_cast <NTV2DolbyCapture*>(pContext));
422  pApp->CaptureFrames ();
423 
424 } // ProducerThreadStatic
425 
426 
428 {
429  AUTOCIRCULATE_TRANSFER inputXfer; // AutoCirculate input transfer info
430  ULWord acOptions (0), overruns(0);
431  UWord hdmiSpigot (UWord(::NTV2InputSourceToChannel(mConfig.fInputSource)));
432  acOptions |= AUTOCIRCULATE_WITH_ANC;
433  acOptions |= AUTOCIRCULATE_WITH_HDMIAUX;
434 
435  CAPNOTE("Thread started");
436  // Initialize and start capture AutoCirculate...
437  mDevice.AutoCirculateStop(mConfig.fInputChannel); // Just in case
438  if (!mDevice.AutoCirculateInitForInput (mConfig.fInputChannel, // primary channel
439  mConfig.fFrames.count(), // numFrames (zero if specifying range)
440  mAudioSystem, // audio system (if any)
441  acOptions, // AutoCirculate options
442  1, // numChannels to gang
443  mConfig.fFrames.firstFrame(), mConfig.fFrames.lastFrame()))
444  mGlobalQuit = true;
445 
446  // The user can opt to INCLUDE only Audio Packets
447  // AuxExtractGetDefaultPacketFilters() retrieves Audio Packet filters DIDs
448  if (mConfig.fDoAudioFilter)
449  {
450  mDevice.AuxExtractSetFilterInclusionMode(hdmiSpigot, /*include?*/true);
451  mDevice.AuxExtractSetPacketFilters(hdmiSpigot, mDevice.AuxExtractGetDefaultPacketFilters());
452  }
453  else
454  { //Otherwise, exclude only 00 Values (excludes nothing / shows all)
455  mDevice.AuxExtractSetFilterInclusionMode(hdmiSpigot,/*include?*/false);
456  NTV2DIDSet zeroSet;
457  zeroSet.insert(0);
458  mDevice.AuxExtractSetPacketFilters(hdmiSpigot, zeroSet);
459  }
460 
461  if (!mGlobalQuit && !mDevice.AutoCirculateStart(mConfig.fInputChannel))
462  mGlobalQuit = true;
463 
464  // Ingest frames til Quit signaled...
465  while (!mGlobalQuit)
466  {
467  AUTOCIRCULATE_STATUS acStatus;
468  mDevice.AutoCirculateGetStatus (mConfig.fInputChannel, acStatus);
469 
470  if (acStatus.IsRunning() && acStatus.HasAvailableInputFrame())
471  {
472  // At this point, there's at least one fully-formed frame available in the device's
473  // frame buffer to transfer to the host. Reserve an NTV2FrameData to "produce", and
474  // use it in the next transfer from the device...
475  NTV2FrameData * pFrameData(mAVCircularBuffer.StartProduceNextBuffer());
476  if (!pFrameData)
477  continue;
478 
479  NTV2FrameData & frameData (*pFrameData);
480  inputXfer.SetVideoBuffer (frameData.VideoBuffer(), frameData.VideoBufferSize());
481  if (acStatus.WithCustomAnc())
482  inputXfer.SetAncBuffers (frameData.AncBuffer(), frameData.AncBufferSize(),
483  frameData.AncBuffer2(), frameData.AncBuffer2Size());
484 
485  // Transfer video/audio/anc from the device into our host buffers...
486  mDevice.AutoCirculateTransfer (mConfig.fInputChannel, inputXfer);
487 
488  // If capturing Anc, clear stale anc data from the anc buffers...
489  if (acStatus.WithCustomAnc() && frameData.AncBuffer())
490  { bool overrun(false);
491  mDevice.AuxExtractGetBufferOverrun (hdmiSpigot, overrun);
492  if (overrun)
493  {overruns++; CAPWARN(overruns << " aux overrun(s)");}
494  frameData.fNumAncBytes = inputXfer.GetCapturedAncByteCount(/*isF2*/false);
495  NTV2Buffer stale (frameData.fAncBuffer.GetHostAddress(frameData.fNumAncBytes),
496  frameData.fAncBuffer.GetByteCount() - frameData.fNumAncBytes);
497  stale.Fill(uint8_t(0));
498  }
499  if (acStatus.WithCustomAnc() && frameData.AncBuffer2())
500  {
501  frameData.fNumAnc2Bytes = inputXfer.GetCapturedAncByteCount(/*isF2*/true);
502  NTV2Buffer stale (frameData.fAncBuffer2.GetHostAddress(frameData.fNumAnc2Bytes),
503  frameData.fAncBuffer2.GetByteCount() - frameData.fNumAnc2Bytes);
504  stale.Fill(uint8_t(0));
505  }
506 
507  // Signal that we're done "producing" the frame, making it available for future "consumption"...
508  mAVCircularBuffer.EndProduceNextBuffer();
509  } // if A/C running and frame(s) are available for transfer
510  else
511  {
512  // Either AutoCirculate is not running, or there were no frames available on the device to transfer.
513  // Rather than waste CPU cycles spinning, waiting until a frame becomes available, it's far more
514  // efficient to wait for the next input vertical interrupt event to get signaled...
516  }
517 
518  } // loop til quit signaled
519 
520  // Stop AutoCirculate...
521  mDevice.AutoCirculateStop(mConfig.fInputChannel);
522  CAPNOTE("Thread completed, will exit");
523 
524 } // CaptureFrames
525 
526 
527 void NTV2DolbyCapture::GetACStatus (ULWord & outGoodFrames, ULWord & outDroppedFrames, ULWord & outBufferLevel)
528 {
529  AUTOCIRCULATE_STATUS status;
530  mDevice.AutoCirculateGetStatus(mConfig.fInputChannel, status);
531  outGoodFrames = status.GetProcessedFrameCount();
532  outDroppedFrames = status.GetDroppedFrameCount();
533  outBufferLevel = status.GetBufferLevel();
534 }
535 
536 uint32_t NTV2DolbyCapture::RecoverAudio (const NTV2Buffer & inAncBuffer, const uint32_t ancSize, NTV2Buffer & outAudioBuffer)
537 {
538  const uint8_t * pInAncData(inAncBuffer);
539  uint8_t * pOutAudioData(outAudioBuffer);
540  uint32_t audioSize(0);
541 
542  // Extract first 16-bit stereo pair from the aux data
543  for (uint32_t num(0); num < ancSize / 32; num++)
544  {
545  // Audio data aux packet?
546  if (pInAncData[0] == 0x02)
547  {
548  // First sample present?
549  if ((pInAncData[1] & 0x01))
550  {
551  // First sample flat?
552  if ((pInAncData[2] & 0x01))
553  {
554  *pOutAudioData++ = 0;
555  *pOutAudioData++ = 0;
556  *pOutAudioData++ = 0;
557  *pOutAudioData++ = 0;
558  }
559  else
560  {
561  *pOutAudioData++ = pInAncData[4];
562  *pOutAudioData++ = pInAncData[5];
563  *pOutAudioData++ = pInAncData[7];
564  *pOutAudioData++ = pInAncData[8];
565  }
566  audioSize += 4;
567  } // if first sample present
568 
569  // Only 2 channels?
570  if ((pInAncData[1] & 0x10) == 0)
571  {
572  // Second sample present?
573  if ((pInAncData[1] & 0x02))
574  {
575  // Second sample flat?
576  if ((pInAncData[2] & 0x02))
577  {
578  *pOutAudioData++ = 0;
579  *pOutAudioData++ = 0;
580  *pOutAudioData++ = 0;
581  *pOutAudioData++ = 0;
582  }
583  else
584  {
585  *pOutAudioData++ = pInAncData[11];
586  *pOutAudioData++ = pInAncData[12];
587  *pOutAudioData++ = pInAncData[14];
588  *pOutAudioData++ = pInAncData[15];
589  }
590  audioSize += 4;
591  }
592 
593  // Third sample present?
594  if ((pInAncData[1] & 0x04))
595  {
596  // Third sample flat?
597  if ((pInAncData[2] & 0x04))
598  {
599  *pOutAudioData++ = 0;
600  *pOutAudioData++ = 0;
601  *pOutAudioData++ = 0;
602  *pOutAudioData++ = 0;
603  }
604  else
605  {
606  *pOutAudioData++ = pInAncData[18];
607  *pOutAudioData++ = pInAncData[19];
608  *pOutAudioData++ = pInAncData[21];
609  *pOutAudioData++ = pInAncData[22];
610  }
611  audioSize += 4;
612  }
613 
614  // Fourth sample present?
615  if ((pInAncData[1] & 0x08))
616  {
617  // Fourth sample flat?
618  if ((pInAncData[2] & 0x08))
619  {
620  *pOutAudioData++ = 0;
621  *pOutAudioData++ = 0;
622  *pOutAudioData++ = 0;
623  *pOutAudioData++ = 0;
624  }
625  else
626  {
627  *pOutAudioData++ = pInAncData[25];
628  *pOutAudioData++ = pInAncData[26];
629  *pOutAudioData++ = pInAncData[28];
630  *pOutAudioData++ = pInAncData[29];
631  }
632  audioSize += 4;
633  } // if 4th sample present
634  } // if only 2 channels
635  } // if audio data aux packet
636  pInAncData += 32;
637  } // for loop
638  return audioSize;
639 } // RecoverAudio
640 
641 uint32_t NTV2DolbyCapture::RecoverDolby (const NTV2Buffer & inAudioBuffer, const uint32_t inAudioSize, NTV2Buffer & outDolbyBuffer)
642 {
643  const uint16_t * pInAudioData(inAudioBuffer);
644  uint16_t * pOutDolbyData(outDolbyBuffer);
645  uint32_t dolbySize(0);
646 
647  // Extract the dolby frames from the IEC61937 bursts...
648  for (uint32_t cnt(0); cnt < inAudioSize / 2; cnt++)
649  {
650  switch (mDolbyState)
651  {
652  // Find IEC61937 burst preamble
653  case 0: mDolbyState = (*pInAudioData == 0xf872) ? 1 : 0;
654  break;
655 
656  case 1: mDolbyState = (*pInAudioData == 0x4e1f) ? 2 : 0;
657  break;
658 
659  // Check dolby code
660  case 2: mDolbyState = ((*pInAudioData & 0xff) == 0x15) ? 3 : 0;
661  break;
662 
663  // Get burst length
664  case 3: mDolbyLength = uint32_t(*pInAudioData / 2);
665  mDolbyState = 4;
666  break;
667 
668  // Copy dolby samples
669  case 4: if (mDolbyLength)
670  {
671  if (dolbySize < outDolbyBuffer.GetByteCount() / 2)
672  {
673  // Endian swap the data...
674  *pOutDolbyData = (*pInAudioData >> 8) & 0xff;
675  *pOutDolbyData |= (*pInAudioData & 0xff) << 8;
676  pOutDolbyData++;
677  dolbySize++;
678  mDolbyLength--;
679  }
680  }
681  else
682  mDolbyState = 0;
683  break;
684 
685  default: mDolbyState = 0;
686  break;
687  } // switch
688  pInAudioData++;
689  } // for loop
690  return dolbySize * 2;
691 }
692 
693 
695 
696 
697 AJALabelValuePairs DolbyCaptureConfig::Get (const bool inCompact) const
698 {
699  AJALabelValuePairs result(CaptureConfig::Get(inCompact));
700  AJASystemInfo::append(result, "Audio Capture File", fAudioDataFilePath.empty() ? "---" : fAudioDataFilePath);
701  AJASystemInfo::append(result, "Dolby Capture File", fDolbyDataFilePath.empty() ? "---" : fDolbyDataFilePath);
702  return result;
703 }
704 
705 
706 std::ostream & operator << (std::ostream & ioStrm, const DolbyCaptureConfig & inObj)
707 {
708  ioStrm << AJASystemInfo::ToString(inObj.Get());
709  return ioStrm;
710 }
NTV2Channel NTV2InputSourceToChannel(const NTV2InputSource inInputSource)
Converts a given NTV2InputSource to its equivalent NTV2Channel value.
Definition: ntv2utils.cpp:5051
Anc Field2 byte offset from end of frame buffer (GUMP on all boards except RTP for SMPTE2022/IP) ...
ULWord GetVideoWriteSize(ULWord inPageSize=4096UL) const
virtual bool SetTaskMode(const NTV2TaskMode inMode)
Sets the device&#39;s task mode.
bool Allocate(const size_t inByteCount, const bool inPageAligned=false)
Allocates (or re-allocates) my user-space storage using the given byte count. I assume full responsib...
virtual bool SetReference(const NTV2ReferenceSource inRefSource, const bool inKeepFramePulseSelect=(0))
Sets the device&#39;s clock reference source. See Video Output Clocking & Synchronization for more inform...
virtual bool ReleaseStreamForApplication(ULWord inApplicationType, int32_t inProcessID)
Releases exclusive use of the AJA device for the given process, permitting other processes to acquire...
bool WithCustomAnc(void) const
AJAStatus Add(FrameDataPtr pInFrameData)
Appends a new frame buffer to me, increasing my frame storage capacity by one frame.
virtual bool AncSetFrameBufferSize(const ULWord inF1Size, const ULWord inF2Size)
Sets the capacity of the SDI ANC or HDMI AUX buffers in device frame memory. (Call NTV2DeviceCanDoCus...
Definition: ntv2anc.cpp:123
ULWord AncBuffer2Size(void) const
virtual bool SetVideoFormat(const NTV2VideoFormat inVideoFormat, const bool inIsAJARetail=(!(0)), const bool inKeepVancSettings=(0), const NTV2Channel inChannel=NTV2_CHANNEL1)
Configures the AJA device to handle a specific video format.
AJAStatus
Definition: types.h:380
virtual void CaptureFrames(void)
Repeatedly captures frames using AutoCirculate (until global quit flag set).
ULWord VideoBufferSize(void) const
bool fDoMultiFormat
If true, use multi-format/multi-channel mode, if device supports it; otherwise normal mode...
virtual bool IsDeviceReady(const bool inCheckValid=(0))
Identifies the 1st HDMI video input.
Definition: ntv2enums.h:1269
static uint64_t GetPid()
Definition: process.cpp:35
ULWord GetBufferLevel(void) const
#define AJA_FAILURE(_status_)
Definition: types.h:373
UWord count(void) const
Definition: ntv2utils.h:997
#define NTV2_VIDEO_FORMAT_HAS_PROGRESSIVE_PICTURE(__f__)
Definition: ntv2enums.h:1053
NTV2InputSource NTV2ChannelToInputSource(const NTV2Channel inChannel, const NTV2IOKinds inKinds=NTV2_IOKINDS_SDI)
Definition: ntv2utils.cpp:5136
ULWord GetByteCount(void) const
std::vector< AJALabelValuePair > AJALabelValuePairs
An ordered sequence of label/value pairs.
Definition: info.h:71
virtual void SetupHostBuffers(void)
Sets up my circular buffers.
#define NTV2_IS_4K_VIDEO_FORMAT(__f__)
Definition: ntv2enums.h:787
virtual AJAStatus SetPriority(AJAThreadPriority priority)
Definition: thread.cpp:133
virtual void StartConsumerThread(void)
Starts my frame consumer thread.
Specifies HDMI input/output kinds.
Definition: ntv2enums.h:1297
NTV2DolbyCapture(const DolbyCaptureConfig &inConfig)
Constructs me using the given settings.
FrameDataPtr StartConsumeNextBuffer(void)
The thread that&#39;s responsible for processing incoming frames – the consumer – calls this function t...
UWord firstFrame(void) const
Definition: ntv2utils.h:998
Definition: json.hpp:5362
virtual AJAStatus Start()
Definition: thread.cpp:91
virtual bool EnableInputInterrupt(const NTV2Channel channel=NTV2_CHANNEL1)
Allows the CNTV2Card instance to wait for and respond to input vertical blanking interrupts originati...
void EndConsumeNextBuffer(void)
The consumer thread calls this function to signal that it has finished processing the frame it obtain...
#define false
uint32_t ULWord
Definition: ajatypes.h:236
virtual bool AutoCirculateGetStatus(const NTV2Channel inChannel, AUTOCIRCULATE_STATUS &outStatus)
Returns the current AutoCirculate status for the given channel.
virtual bool SetFrameBufferFormat(NTV2Channel inChannel, NTV2FrameBufferFormat inNewFormat, bool inIsAJARetail=(!(0)), NTV2HDRXferChars inXferChars=NTV2_VPID_TC_SDR_TV, NTV2HDRColorimetry inColorimetry=NTV2_VPID_Color_Rec709, NTV2HDRLuminance inLuminance=NTV2_VPID_Luminance_YCbCr)
Sets the frame buffer format for the given FrameStore on the AJA device.
mVideoFormat
Definition: ntv2vcam.cpp:801
virtual class DeviceCapabilities & features(void)
Definition: ntv2card.h:148
bool fDoFrameStats
if true, output per frame statistics
bool CanDoInputSource(const NTV2InputSource inSrc)
virtual bool SetMultiFormatMode(const bool inEnable)
Enables or disables multi-format (per channel) device operation. If enabled, each device channel can ...
virtual void StartProducerThread(void)
Starts my capture thread.
virtual bool GetHDMIInputColor(NTV2LHIHDMIColorSpace &outValue, const NTV2Channel inHDMIInput=NTV2_CHANNEL1)
Answers with the current colorspace for the given HDMI input.
Definition: ntv2hdmi.cpp:70
ULWord GetProcessedFrameCount(void) const
int32_t appPID(0)
Anc Field1 byte offset from end of frame buffer (GUMP on all boards except RTP for SMPTE2022/IP) ...
NTV2Channel fInputChannel
The device channel to use.
virtual bool Active()
Definition: thread.cpp:116
bool Fill(const T &inValue)
Fills me with the given scalar value.
virtual bool GetTaskMode(NTV2TaskMode &outMode)
Retrieves the device&#39;s current task mode.
std::string NTV2InputSourceToString(const NTV2InputSource inValue, const bool inForRetailDisplay=false)
Definition: ntv2utils.cpp:7043
0: Disabled (never recommended): device configured exclusively by client application(s).
virtual std::string GetDescription(void) const
Definition: ntv2card.cpp:139
std::string fAncDataFilePath
Optional path to Anc binary data file.
virtual AJAStatus SetupVideo(void)
Sets up everything I need for capturing video.
ULWord AncBufferSize(void) const
static bool GetFirstDeviceFromArgument(const std::string &inArgument, CNTV2Card &outDevice)
Rescans the host, and returns an open CNTV2Card instance for the AJA device that matches a command li...
#define NTV2_INPUT_SOURCE_IS_HDMI(_inpSrc_)
Definition: ntv2enums.h:1285
virtual bool AutoCirculateTransfer(const NTV2Channel inChannel, AUTOCIRCULATE_TRANSFER &transferInfo)
Transfers all or part of a frame as specified in the given AUTOCIRCULATE_TRANSFER object to/from the ...
virtual bool AcquireStreamForApplication(ULWord inApplicationType, int32_t inProcessID)
Reserves exclusive use of the AJA device for a given process, preventing other processes on the host ...
virtual bool DMABufferLock(const NTV2Buffer &inBuffer, bool inMap=(0), bool inRDMA=(0))
Page-locks the given host buffer to reduce transfer time and CPU usage of DMA transfers.
Definition: ntv2dma.cpp:429
std::ostream & operator<<(std::ostream &ioStrm, const DolbyCaptureConfig &inObj)
NTV2Buffer & AncBuffer2(void)
#define AJA_NULL
Definition: ajatypes.h:180
NTV2LHIHDMIColorSpace inputColorSpace(NTV2_LHIHDMIColorSpaceYCbCr)
void SetAbortFlag(const bool *pAbortFlag)
Tells me the boolean variable I should monitor such that when it gets set to "true" will cause any th...
NTV2Buffer & AncBuffer(void)
virtual bool SubscribeInputVerticalEvent(const NTV2Channel inChannel=NTV2_CHANNEL1)
Causes me to be notified when an input vertical blanking interrupt occurs on the given input channel...
Definition: ntv2card.h:2787
virtual NTV2VideoFormat GetInputVideoFormat(const NTV2InputSource inVideoSource=NTV2_INPUTSOURCE_SDI1, const bool inIsProgressive=(0))
Returns the video format of the signal that is present on the given input source. ...
Describes a video frame for a given video standard or format and pixel format, including the total nu...
static void Sleep(const int32_t inMilliseconds)
Suspends execution of the current thread for a given number of milliseconds.
Definition: systemtime.cpp:284
2: OEM (recommended): device configured by client application(s) with some driver involvement...
virtual AJAStatus SetupAudio(void)
Sets up everything I need for capturing audio.
virtual bool ApplySignalRoute(const CNTV2SignalRouter &inRouter, const bool inReplace=(0))
Applies the given routing table to the AJA device.
NTV2XptConnections connections
Definition: ntv2vcam.cpp:1011
std::string NTV2VideoFormatToString(const NTV2VideoFormat inValue, const bool inUseFrameRate=false)
Definition: ntv2utils.cpp:6750
bool fDoAudioFilter
If true, capture only audio anc.
NTV2Buffer & VideoBuffer(void)
ULWord GetDroppedFrameCount(void) const
virtual bool SetTsiFrameEnable(const bool inIsEnabled, const NTV2Channel inChannel)
Enables or disables SMPTE 425 two-sample interleave (Tsi) frame mode on the device.
static void ConsumerThreadStatic(AJAThread *pThread, void *pContext)
This is the consumer thread&#39;s static callback function that gets called when the consumer thread runs...
NTV2Buffer fAncBuffer
Host ancillary data buffer.
#define AUTOCIRCULATE_WITH_ANC
Use this to AutoCirculate with ancillary data.
ULWord fNumAnc2Bytes
Actual number of captured F2 anc bytes.
#define NTV2_ANCILLARYSIZE_MAX
std::string fDolbyDataFilePath
Optional path to Dolby binary data file.
Declares the CNTV2DeviceScanner class.
NTV2Buffer fAncBuffer2
Additional "F2" host anc buffer.
virtual bool ReadRegister(const ULWord inRegNum, ULWord &outValue, const ULWord inMask=0xFFFFFFFF, const ULWord inShift=0)
Reads all or part of the 32-bit contents of a specific register (real or virtual) on the AJA device...
virtual bool AutoCirculateStop(const NTV2Channel inChannel, const bool inAbort=(0))
Stops AutoCirculate for the given channel, and releases the on-device frame buffers that were allocat...
#define NTV2_IS_VALID_CHANNEL(__x__)
Definition: ntv2enums.h:1375
Declares the AJAProcess class.
Describes a user-space buffer on the host computer. I have an address and a length, plus some optional attributes (allocated by SDK?, page-aligned? etc.).
ULWord fNumAncBytes
Actual number of captured F1 anc bytes.
virtual AJAStatus Run(void)
Runs me.
virtual uint32_t RecoverDolby(const NTV2Buffer &inAudioBuffer, const uint32_t inAudioSize, NTV2Buffer &outDolbyBuffer)
Recover Dolby data from the given audio data.
This object specifies the information that will be transferred to or from the AJA device in the CNTV2...
NTV2InputSource fInputSource
The device input connector to use.
virtual bool RouteInputSignal(void)
Sets up device routing for capture.
virtual bool AuxExtractSetPacketFilters(const UWord inHDMIInput, const NTV2DIDSet &inDIDs)
Replaces the HDMI packet types to be excluded (filtered) by the given HDMI input&#39;s Aux extractor...
Definition: ntv2aux.cpp:886
static AJALabelValuePairs & append(AJALabelValuePairs &inOutTable, const std::string &inLabel, const std::string &inValue=std::string())
A convenience function that appends the given label and value strings to the provided AJALabelValuePa...
Definition: info.h:170
#define DEC(__x__)
virtual AJAStatus Init(void)
Initializes me and prepares me to Run.
void EndProduceNextBuffer(void)
The producer thread calls this function to signal that it has finished populating the frame it obtain...
This identifies the first Audio System.
Definition: ntv2enums.h:3903
#define NTV2_IS_VALID_INPUT_SOURCE(_inpSrc_)
Definition: ntv2enums.h:1288
static const size_t CIRCULAR_BUFFER_SIZE(10)
Number of NTV2FrameData&#39;s in our ring.
static const ULWord kDemoAppSignature((((uint32_t)( 'D'))<< 24)|(((uint32_t)( 'E'))<< 16)|(((uint32_t)( 'M'))<< 8)|(((uint32_t)( 'O'))<< 0))
Declares the NTV2DolbyCapture class.
virtual AJAStatus Attach(AJAThreadFunction *pThreadFunction, void *pUserContext)
Definition: thread.cpp:169
FrameDataPtr StartProduceNextBuffer(void)
The thread that&#39;s responsible for providing frames – the producer – calls this function to populate...
virtual bool AuxExtractGetBufferOverrun(const UWord inHDMIInput, bool &outIsOverrun, const UWord inField=0)
Answers whether or not the given HDMI input&#39;s Aux extractor reached its buffer limits.
Definition: ntv2aux.cpp:982
virtual ~NTV2DolbyCapture()
This is returned from the CNTV2Card::AutoCirculateGetStatus function.
uint16_t UWord
Definition: ajatypes.h:234
I capture HDMI Dolby audio from an HDMI input of an AJA device.
#define CAPWARN(_expr_)
Specifies channel or FrameStore 1 (or the first item).
Definition: ntv2enums.h:1363
virtual bool UnsubscribeInputVerticalEvent(const NTV2Channel inChannel=NTV2_CHANNEL1)
Unregisters me so I&#39;m no longer notified when an input VBI is signaled on the given input channel...
Definition: ntv2card.h:2837
virtual bool AutoCirculateStart(const NTV2Channel inChannel, const ULWord64 inStartTime=0)
Starts AutoCirculate streaming for the specified channel that was previously initialized by CNTV2Card...
bool CanDoFrameBufferFormat(const NTV2PixelFormat inPF)
ULWord appSignature(0)
NTV2ACFrameRange fFrames
AutoCirculate frame count or range.
NTV2PixelFormat fPixelFormat
Pixel format to use.
static bool GetInputRouting(NTV2XptConnections &outConnections, const CaptureConfig &inConfig, const bool isInputRGB=(0))
Answers with the crosspoint connections needed to implement the given capture configuration.
bool SetAncBuffers(ULWord *pInANCBuffer, const ULWord inANCByteCount, ULWord *pInANCF2Buffer=NULL, const ULWord inANCF2ByteCount=0)
Sets my ancillary data buffers for use in a subsequent call to CNTV2Card::AutoCirculateTransfer.
std::string NTV2FrameBufferFormatToString(const NTV2FrameBufferFormat inValue, const bool inForRetailDisplay=false)
Definition: ntv2utils.cpp:6940
I encapsulate the video, audio and anc host buffers used in the AutoCirculate demos. I&#39;m a more modern version of the AVDataBuffer.
NTV2Buffer fVideoBuffer
Host video buffer.
bool HasAvailableInputFrame(void) const
bool IsRunning(void) const
virtual void GetACStatus(ULWord &outGoodFrames, ULWord &outDroppedFrames, ULWord &outBufferLevel)
Provides status information about my input (capture) process.
AJALabelValuePairs Get(const bool inCompact=(0)) const
std::string fDeviceSpec
The AJA device to use.
std::map< NTV2InputXptID, NTV2OutputXptID > NTV2XptConnections
virtual void ConsumeFrames(void)
Repeatedly consumes frames from the circular buffer (until global quit flag set). ...
ULWord GetCapturedAncByteCount(const bool inField2=false) const
bool CanDoVideoFormat(const NTV2VideoFormat inVF)
UWord lastFrame(void) const
Definition: ntv2utils.h:999
static NTV2DIDSet AuxExtractGetDefaultPacketFilters(void)
Definition: ntv2aux.cpp:1034
virtual bool AutoCirculateInitForInput(const NTV2Channel inChannel, const UWord inFrameCount=7, const NTV2AudioSystem inAudioSystem=NTV2_AUDIOSYSTEM_INVALID, const ULWord inOptionFlags=0, const UByte inNumChannels=1, const UWord inStartFrameNumber=0, const UWord inEndFrameNumber=0)
Prepares for subsequent AutoCirculate ingest streaming, designating a contiguous block of frame buffe...
std::string & lower(std::string &str)
Definition: common.cpp:436
virtual void Quit(void)
Gracefully stops me from running.
virtual bool AuxExtractSetFilterInclusionMode(const UWord inHDMIInput, const bool inEnable)
Enables or disables HDMI AUX packet filtering for the given HDMI input.
Definition: ntv2aux.cpp:912
void * GetHostAddress(const ULWord inByteOffset, const bool inFromEnd=false) const
This class is used to configure an NTV2Capture instance.
std::string fAudioDataFilePath
Optional path to Audio binary data file.
#define CAPNOTE(_expr_)
#define AUTOCIRCULATE_WITH_HDMIAUX
Use this to AutoCirculate with HDMI auxiliary data.
virtual bool GetStreamingApplication(ULWord &outAppType, int32_t &outProcessID)
Answers with the four-CC type and process ID of the application that currently "owns" the AJA device ...
virtual void ToString(std::string &outAllLabelsAndValues) const
Answers with a multi-line string that contains the complete host system info table.
virtual bool WaitForInputVerticalInterrupt(const NTV2Channel inChannel=NTV2_CHANNEL1, UWord inRepeatCount=1)
Efficiently sleeps the calling thread/process until the next one or more field (interlaced video) or ...
static void ProducerThreadStatic(AJAThread *pThread, void *pContext)
This is the capture thread&#39;s static callback function that gets called when the capture thread runs...
std::set< UByte > NTV2DIDSet
A set of distinct NTV2DID values.
bool SetVideoBuffer(ULWord *pInVideoBuffer, const ULWord inVideoByteCount)
Sets my video buffer for use in a subsequent call to CNTV2Card::AutoCirculateTransfer.
AJALabelValuePairs Get(const bool inCompact=(0)) const
NTV2LHIHDMIColorSpace
Definition: ntv2enums.h:3684
#define NTV2_AUDIOSIZE_MAX
NTV2ReferenceSource NTV2InputSourceToReferenceSource(const NTV2InputSource inInputSource)
Converts a given NTV2InputSource to its equivalent NTV2ReferenceSource value.
Definition: ntv2utils.cpp:5027
virtual uint32_t RecoverAudio(const NTV2Buffer &inAncBuffer, const uint32_t inAncSize, NTV2Buffer &outAudioBuffer)
Recover audio from ancillary data.
virtual bool EnableChannel(const NTV2Channel inChannel)
Enables the given FrameStore.