AJA NTV2 SDK  18.0.0.2122
NTV2 SDK 18.0.0.2122
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  mDeviceID (DEVICE_ID_NOTFOUND),
28  mConfig (inConfig),
30  mSavedTaskMode (NTV2_DISABLE_TASKS),
31  mAudioSystem (NTV2_AUDIOSYSTEM_1),
32  mHostBuffers (),
33  mAVCircularBuffer (),
34  mGlobalQuit (false)
35 {
36 } // constructor
37 
38 
40 {
41  // Stop my capture and consumer threads, then destroy them...
42  Quit();
43 
44  // Unsubscribe from input vertical event...
46 
47 } // destructor
48 
49 
51 {
52  // Set the global 'quit' flag, and wait for the threads to go inactive...
53  mGlobalQuit = true;
54 
55  while (mConsumerThread.Active())
56  AJATime::Sleep(10);
57 
58  while (mProducerThread.Active())
59  AJATime::Sleep(10);
60 
61  // Restore some of the device's former state...
62  if (!mConfig.fDoMultiFormat)
63  {
65  mDevice.SetTaskMode(mSavedTaskMode); // Restore prior task mode
66  }
67 
68 } // Quit
69 
70 
72 {
74 
75  // Open the device...
77  {cerr << "## ERROR: Device '" << mConfig.fDeviceSpec << "' not found" << endl; return AJA_STATUS_OPEN;}
78 
79  if (!mDevice.IsDeviceReady(false))
80  {cerr << "## ERROR: '" << mDevice.GetDisplayName() << "' not ready" << endl; return AJA_STATUS_INITIALIZE;}
81 
82  mDeviceID = mDevice.GetDeviceID(); // Keep the device ID handy, as it's used frequently
83  if (!mDevice.features().CanDoCapture())
84  {cerr << "## ERROR: '" << mDevice.GetDisplayName() << "' is playback-only" << endl; return AJA_STATUS_FEATURE;}
85 
86  if (!mDevice.features().CanDoFrameBufferFormat(mConfig.fPixelFormat))
87  { cerr << "## ERROR: '" << mDevice.GetDisplayName() << "' doesn't support '"
88  << ::NTV2FrameBufferFormatToString(mConfig.fPixelFormat, true) << "' ("
89  << ::NTV2FrameBufferFormatToString(mConfig.fPixelFormat, false) << ", " << DEC(mConfig.fPixelFormat) << ")" << endl;
91  }
92 
93  ULWord appSignature (0);
94  int32_t appPID (0);
95  mDevice.GetStreamingApplication (appSignature, appPID); // Who currently "owns" the device?
96  mDevice.GetTaskMode(mSavedTaskMode); // Save the current device state
97  if (!mConfig.fDoMultiFormat)
98  {
100  {
101  cerr << "## ERROR: Unable to acquire '" << mDevice.GetDisplayName() << "' because another app (pid " << appPID << ") owns it" << endl;
102  return AJA_STATUS_BUSY; // Another app is using the device
103  }
104  }
105  mDevice.SetTaskMode(NTV2_OEM_TASKS); // Prevent interference from AJA retail services
106 
107  if (mDevice.features().CanDoMultiFormat())
108  mDevice.SetMultiFormatMode(mConfig.fDoMultiFormat);
109 
110  // This demo permits input source and channel to be specified independently.
112  mConfig.fInputChannel = NTV2_CHANNEL1;
117  if (!mDevice.features().CanDoInputSource(mConfig.fInputSource))
118  {
119  cerr << "## ERROR: No such input '" << ::NTV2InputSourceToString(mConfig.fInputSource, /*compact?*/true)
120  << "' on '" << mDevice.GetDisplayName() << "'" << endl;
121  return AJA_STATUS_UNSUPPORTED;
122  }
123  if (!mConfig.fAncDataFilePath.empty() && !mDevice.features().CanDoHDMIAuxCapture())
124  {cerr << "## ERROR: HDMI aux capture requested, but '" << mDevice.GetDisplayName() << "' has no HDMI aux extractors" << endl; return AJA_STATUS_UNSUPPORTED;}
125 
126  // Set up the video and audio...
127  status = SetupVideo();
128  if (AJA_FAILURE(status))
129  return status;
130 
131  status = SetupAudio();
132  if (AJA_FAILURE(status))
133  return status;
134 
135  // Set up the circular buffers, the device signal routing, and both playout and capture AutoCirculate...
137  if (!RouteInputSignal())
138  return AJA_STATUS_FAIL;
139 
140  mDolbyState = 0;
141 
142  #if defined(_DEBUG)
143  cerr << mConfig;
144  if (mDevice.IsRemote())
145  cerr << "Device Description: " << mDevice.GetDescription() << endl;
146  cerr << endl;
147  #endif // defined(_DEBUG)
148  return AJA_STATUS_SUCCESS;
149 
150 } // Init
151 
152 
154 {
155  // Sometimes other applications disable some or all of the frame buffers, so turn on ours now...
156  mDevice.EnableChannel(mConfig.fInputChannel);
157 
158  // Enable and subscribe to the interrupts for the channel to be used...
159  mDevice.EnableInputInterrupt(mConfig.fInputChannel);
161 
162  // Determine the input video signal format...
163  mVideoFormat = mDevice.GetInputVideoFormat(mConfig.fInputSource);
164  if (mVideoFormat == NTV2_FORMAT_UNKNOWN)
165  { cerr << "## ERROR: No input signal or unknown format on '" << ::NTV2InputSourceToString(mConfig.fInputSource, true)
166  << "'" << endl;
167  return AJA_STATUS_NOINPUT;
168  }
169  if (!mDevice.features().CanDoVideoFormat(mVideoFormat))
170  { cerr << "## ERROR: '" << mDevice.GetDisplayName() << "' cannot handle " << ::NTV2VideoFormatToString(mVideoFormat) << endl;
171  return AJA_STATUS_UNSUPPORTED; // Device can't handle this format
172  }
173  CAPNOTE(::NTV2VideoFormatToString(mVideoFormat) << " detected on " << ::NTV2InputSourceToString(mConfig.fInputSource,true) << " on " << mDevice.GetDisplayName());
174  mFormatDesc = NTV2FormatDescriptor(mVideoFormat, mConfig.fPixelFormat);
175 
176  // Set the device video format to whatever we detected at the input...
177  if (NTV2_IS_4K_VIDEO_FORMAT(mVideoFormat))
178  {
180  mDevice.SetVideoFormat (mVideoFormat, false, false, NTV2_CHANNEL1);
181  mDevice.SetTsiFrameEnable(true, NTV2_CHANNEL1);
183  }
184  else
185  {
187  mDevice.SetVideoFormat (mVideoFormat, false, false, mConfig.fInputChannel);
188  mDevice.SetTsiFrameEnable(false, mConfig.fInputChannel);
189  mDevice.SetFrameBufferFormat (mConfig.fInputChannel, mConfig.fPixelFormat);
190  }
191  return AJA_STATUS_SUCCESS;
192 
193 } // SetupVideo
194 
195 
197 {
198  return AJA_STATUS_SUCCESS;
199 
200 } // SetupAudio
201 
202 
204 {
205  // Let my circular buffer know when it's time to quit...
206  mAVCircularBuffer.SetAbortFlag (&mGlobalQuit);
207 
208  bool isProgressive = NTV2_VIDEO_FORMAT_HAS_PROGRESSIVE_PICTURE(mVideoFormat);
209  if (isProgressive)
211  else
213 
214  ULWord F1AncSize(0), F2AncSize(0);
215  ULWord F1OffsetFromEnd(0), F2OffsetFromEnd(0);
216  mDevice.ReadRegister(kVRegAncField1Offset, F1OffsetFromEnd); // # bytes from end of 8MB/16MB frame
217  mDevice.ReadRegister(kVRegAncField2Offset, F2OffsetFromEnd); // # bytes from end of 8MB/16MB frame
218  // Based on the offsets, calculate the max anc capacity
219  F1AncSize = F2OffsetFromEnd > F1OffsetFromEnd ? 0 : F1OffsetFromEnd - F2OffsetFromEnd;
220  F2AncSize = F2OffsetFromEnd > F1OffsetFromEnd ? F2OffsetFromEnd - F1OffsetFromEnd : F2OffsetFromEnd;
221 
222  // Allocate and add each in-host NTV2FrameData to my circular buffer member variable...
223  const size_t audioBufferSize (NTV2_AUDIOSIZE_MAX);
224  mHostBuffers.reserve(size_t(CIRCULAR_BUFFER_SIZE));
225  while (mHostBuffers.size() < size_t(CIRCULAR_BUFFER_SIZE))
226  {
227  mHostBuffers.push_back(NTV2FrameData());
228  NTV2FrameData & frameData(mHostBuffers.back());
229  frameData.fVideoBuffer.Allocate(mFormatDesc.GetVideoWriteSize());
230  frameData.fAudioBuffer.Allocate(audioBufferSize);
231  frameData.fAncBuffer.Allocate(F1AncSize);
232  frameData.fAncBuffer2.Allocate(F2AncSize);
233  mAVCircularBuffer.Add(&frameData);
234 
235 #ifdef NTV2_BUFFER_LOCK
236  // Page lock the memory
237  if (frameData.fVideoBuffer)
238  mDevice.DMABufferLock(frameData.fVideoBuffer, true);
239  if (frameData.fAncBuffer)
240  mDevice.DMABufferLock(frameData.fAncBuffer, true);
241  if (frameData.fAncBuffer2)
242  mDevice.DMABufferLock(frameData.fAncBuffer2, true);
243 #endif
244  } // for each NTV2FrameData
245 
246 } // SetupHostBuffers
247 
248 
250 {
251 
255 
256  const bool isInputRGB (inputColorSpace == NTV2_LHIHDMIColorSpaceRGB);
258 
259  return CNTV2DemoCommon::GetInputRouting (connections, mConfig, isInputRGB)
260  && mDevice.ApplySignalRoute(connections, !mConfig.fDoMultiFormat);
261 
262 } // RouteInputSignal
263 
264 
266 {
267  // Start the playout and capture threads...
270  return AJA_STATUS_SUCCESS;
271 
272 } // Run
273 
274 
276 
277 // This is where we will start the consumer thread
279 {
280  // Create and start the consumer thread...
281  mConsumerThread.Attach(ConsumerThreadStatic, this);
282  mConsumerThread.SetPriority(AJA_ThreadPriority_High);
283  mConsumerThread.Start();
284 
285 } // StartConsumerThread
286 
287 
288 // The consumer thread function
289 void NTV2DolbyCapture::ConsumerThreadStatic (AJAThread * pThread, void * pContext) // static
290 {
291  (void) pThread;
292 
293  // Grab the NTV2DolbyCapture instance pointer from the pContext parameter,
294  // then call its ConsumeFrames method...
295  NTV2DolbyCapture * pApp (reinterpret_cast <NTV2DolbyCapture *> (pContext));
296  pApp->ConsumeFrames();
297 
298 } // ConsumerThreadStatic
299 
300 
302 {
303  CAPNOTE("Thread started");
304  uint64_t ancTally(0);
305  uint64_t audioTally(0);
306  uint64_t dolbyTally(0);
307  ofstream * pOFS(mConfig.fAncDataFilePath.empty() ? AJA_NULL : new ofstream(mConfig.fAncDataFilePath.c_str(), ios::binary));
308  ofstream * pAFS(mConfig.fAudioDataFilePath.empty() ? AJA_NULL : new ofstream(mConfig.fAudioDataFilePath.c_str(), ios::binary));
309  ofstream * pDFS(mConfig.fDolbyDataFilePath.empty() ? AJA_NULL : new ofstream(mConfig.fDolbyDataFilePath.c_str(), ios::binary));
310  while (!mGlobalQuit)
311  {
312  // Wait for the next frame to become ready to "consume"...
313  NTV2FrameData * pFrameData (mAVCircularBuffer.StartConsumeNextBuffer ());
314  if (pFrameData)
315  {
316  // Do something useful with the frame data...
317  // . . . . . . . . . . . .
318  // . . . . . . . . . . . .
319  // . . . . . . . . . . . .
320  if (mConfig.fDoFrameStats && pFrameData->AncBuffer())
321  {
322  uint8_t* pData = (uint8_t*)pFrameData->AncBuffer().GetHostAddress(0);
323  uint32_t i;
324  for (i = 0; i < pFrameData->AncBufferSize(); i += 32)
325  {
326  if (pData[i] == 0xff)
327  break;
328  }
329  uint32_t audioSize = RecoverAudio(pFrameData->AncBuffer(), pFrameData->AncBufferSize() /*pFrameData->NumCapturedAncBytes()*/, pFrameData->AudioBuffer());
330  cout << "f1 size reg " << DEC(pFrameData->NumCapturedAncBytes()) << " ffs " << DEC(i) << " samples " << DEC(audioSize/4) << endl << flush;
331  }
332 
333  if (mConfig.fDoFrameStats && pFrameData->AncBuffer2())
334  {
335  uint8_t* pData = (uint8_t*)pFrameData->AncBuffer2().GetHostAddress(0);
336  uint32_t i;
337  for (i = 0; i < pFrameData->AncBuffer2Size(); i += 32)
338  {
339  if (pData[i] == 0xff)
340  break;
341  }
342  uint32_t audioSize = RecoverAudio(pFrameData->AncBuffer2(), pFrameData->AncBuffer2Size() /*pFrameData->NumCapturedAnc2Bytes()*/, pFrameData->AudioBuffer());
343  cout << "f2 size reg " << DEC(pFrameData->NumCapturedAnc2Bytes()) << " ffs " << DEC(i) << " samples " << DEC(audioSize/4) << endl << flush;
344  }
345 
346  if (pOFS && pFrameData->AncBuffer())
347  {
348  if (pOFS && !ancTally++)
349  CAPNOTE("Writing raw anc to '" + mConfig.fAncDataFilePath + "'");
350  pOFS->write(pFrameData->AncBuffer(), streamsize(pFrameData->NumCapturedAncBytes()));
351  ancTally++;
352  if ( pFrameData->AncBuffer2())
353  {
354  if (pOFS && pFrameData->AncBuffer2())
355  pOFS->write(pFrameData->AncBuffer2(), streamsize(pFrameData->NumCapturedAnc2Bytes()));
356  }
357  }
358 
359  if (pAFS && pFrameData->AncBuffer() && pFrameData->AudioBuffer())
360  {
361  if (pAFS && !audioTally++)
362  CAPNOTE("Writing raw audio to '" + mConfig.fAudioDataFilePath + "'");
363  uint32_t audioSize = RecoverAudio(pFrameData->AncBuffer(), pFrameData->NumCapturedAncBytes(), pFrameData->AudioBuffer());
364  pAFS->write(pFrameData->AudioBuffer(), streamsize(audioSize));
365  audioTally++;
366  if ( pFrameData->AncBuffer2())
367  {
368  audioSize = RecoverAudio(pFrameData->AncBuffer2(), pFrameData->NumCapturedAnc2Bytes(), pFrameData->AudioBuffer());
369  pAFS->write(pFrameData->AudioBuffer(), streamsize(audioSize));
370  }
371  }
372 
373  if (pDFS && pFrameData->AncBuffer() && pFrameData->AudioBuffer())
374  {
375  if (pDFS && !dolbyTally++)
376  CAPNOTE("Writing dolby file to '" + mConfig.fDolbyDataFilePath + "'");
377  uint32_t audioSize = RecoverAudio(pFrameData->AncBuffer(), pFrameData->NumCapturedAncBytes(), pFrameData->AudioBuffer());
378  uint32_t dolbySize = RecoverDolby(pFrameData->AudioBuffer(), audioSize, pFrameData->AncBuffer());
379  pDFS->write(pFrameData->AncBuffer(), streamsize(dolbySize));
380  dolbyTally++;
381  if ( pFrameData->AncBuffer2())
382  {
383  audioSize = RecoverAudio(pFrameData->AncBuffer2(), pFrameData->NumCapturedAnc2Bytes(), pFrameData->AudioBuffer());
384  dolbySize = RecoverDolby(pFrameData->AudioBuffer(), audioSize, pFrameData->AncBuffer2());
385  pDFS->write(pFrameData->AncBuffer2(), streamsize(dolbySize));
386  }
387  }
388  // Now release and recycle the buffer...
389  mAVCircularBuffer.EndConsumeNextBuffer ();
390  } // if pFrameData
391  } // loop til quit signaled
392  if (pOFS)
393  { delete pOFS; cerr << "Wrote " << DEC(ancTally) << " frames of raw anc data" << endl; }
394  if (pAFS)
395  { delete pAFS; cerr << "Wrote " << DEC(audioTally) << " frames of raw audio data" << endl; }
396  if (pDFS)
397  { delete pDFS; cerr << "Wrote " << DEC(dolbyTally) << " frames of dolby data" << endl; }
398  CAPNOTE("Thread completed, will exit");
399 
400 } // ConsumeFrames
401 
402 
404 
405 // This starts the capture (producer) thread
407 {
408  // Create and start the capture thread...
409  mProducerThread.Attach(ProducerThreadStatic, this);
410  mProducerThread.SetPriority(AJA_ThreadPriority_High);
411  mProducerThread.Start();
412 
413 } // StartProducerThread
414 
415 
416 // The capture thread function
417 void NTV2DolbyCapture::ProducerThreadStatic (AJAThread * pThread, void * pContext) // static
418 {
419  (void) pThread;
420 
421  // Grab the NTV2DolbyCapture instance pointer from the pContext parameter,
422  // then call its CaptureFrames method...
423  NTV2DolbyCapture * pApp (reinterpret_cast <NTV2DolbyCapture*>(pContext));
424  pApp->CaptureFrames ();
425 
426 } // ProducerThreadStatic
427 
428 
430 {
431  AUTOCIRCULATE_TRANSFER inputXfer; // AutoCirculate input transfer info
432  ULWord acOptions (0), overruns(0);
433  UWord hdmiSpigot (UWord(::NTV2InputSourceToChannel(mConfig.fInputSource)));
434  acOptions |= AUTOCIRCULATE_WITH_ANC;
435  acOptions |= AUTOCIRCULATE_WITH_HDMIAUX;
436 
437  CAPNOTE("Thread started");
438  // Initialize and start capture AutoCirculate...
439  mDevice.AutoCirculateStop(mConfig.fInputChannel); // Just in case
440  if (!mDevice.AutoCirculateInitForInput (mConfig.fInputChannel, // primary channel
441  mConfig.fFrames.count(), // numFrames (zero if specifying range)
442  mAudioSystem, // audio system (if any)
443  acOptions, // AutoCirculate options
444  1, // numChannels to gang
445  mConfig.fFrames.firstFrame(), mConfig.fFrames.lastFrame()))
446  mGlobalQuit = true;
447 
448  // The user can opt to INCLUDE only Audio Packets
449  // AuxExtractGetDefaultPacketFilters() retrieves Audio Packet filters DIDs
450  if (mConfig.fDoAudioFilter)
451  {
452  mDevice.AuxExtractSetFilterInclusionMode(hdmiSpigot, /*include?*/true);
453  mDevice.AuxExtractSetPacketFilters(hdmiSpigot, mDevice.AuxExtractGetDefaultPacketFilters());
454  }
455  else
456  { //Otherwise, exclude only 00 Values (excludes nothing / shows all)
457  mDevice.AuxExtractSetFilterInclusionMode(hdmiSpigot,/*include?*/false);
458  NTV2DIDSet zeroSet;
459  zeroSet.insert(0);
460  mDevice.AuxExtractSetPacketFilters(hdmiSpigot, zeroSet);
461  }
462 
463  if (!mGlobalQuit && !mDevice.AutoCirculateStart(mConfig.fInputChannel))
464  mGlobalQuit = true;
465 
466  // Ingest frames til Quit signaled...
467  while (!mGlobalQuit)
468  {
469  AUTOCIRCULATE_STATUS acStatus;
470  mDevice.AutoCirculateGetStatus (mConfig.fInputChannel, acStatus);
471 
472  if (acStatus.IsRunning() && acStatus.HasAvailableInputFrame())
473  {
474  // At this point, there's at least one fully-formed frame available in the device's
475  // frame buffer to transfer to the host. Reserve an NTV2FrameData to "produce", and
476  // use it in the next transfer from the device...
477  NTV2FrameData * pFrameData(mAVCircularBuffer.StartProduceNextBuffer());
478  if (!pFrameData)
479  continue;
480 
481  NTV2FrameData & frameData (*pFrameData);
482  inputXfer.SetVideoBuffer (frameData.VideoBuffer(), frameData.VideoBufferSize());
483  if (acStatus.WithCustomAnc())
484  inputXfer.SetAncBuffers (frameData.AncBuffer(), frameData.AncBufferSize(),
485  frameData.AncBuffer2(), frameData.AncBuffer2Size());
486 
487  // Transfer video/audio/anc from the device into our host buffers...
488  mDevice.AutoCirculateTransfer (mConfig.fInputChannel, inputXfer);
489 
490  // If capturing Anc, clear stale anc data from the anc buffers...
491  if (acStatus.WithCustomAnc() && frameData.AncBuffer())
492  { bool overrun(false);
493  mDevice.AuxExtractGetBufferOverrun (hdmiSpigot, overrun);
494  if (overrun)
495  {overruns++; CAPWARN(overruns << " aux overrun(s)");}
496  frameData.fNumAncBytes = inputXfer.GetCapturedAncByteCount(/*isF2*/false);
497  NTV2Buffer stale (frameData.fAncBuffer.GetHostAddress(frameData.fNumAncBytes),
498  frameData.fAncBuffer.GetByteCount() - frameData.fNumAncBytes);
499  stale.Fill(uint8_t(0));
500  }
501  if (acStatus.WithCustomAnc() && frameData.AncBuffer2())
502  {
503  frameData.fNumAnc2Bytes = inputXfer.GetCapturedAncByteCount(/*isF2*/true);
504  NTV2Buffer stale (frameData.fAncBuffer2.GetHostAddress(frameData.fNumAnc2Bytes),
505  frameData.fAncBuffer2.GetByteCount() - frameData.fNumAnc2Bytes);
506  stale.Fill(uint8_t(0));
507  }
508 
509  // Signal that we're done "producing" the frame, making it available for future "consumption"...
510  mAVCircularBuffer.EndProduceNextBuffer();
511  } // if A/C running and frame(s) are available for transfer
512  else
513  {
514  // Either AutoCirculate is not running, or there were no frames available on the device to transfer.
515  // Rather than waste CPU cycles spinning, waiting until a frame becomes available, it's far more
516  // efficient to wait for the next input vertical interrupt event to get signaled...
518  }
519 
520  } // loop til quit signaled
521 
522  // Stop AutoCirculate...
523  mDevice.AutoCirculateStop(mConfig.fInputChannel);
524  CAPNOTE("Thread completed, will exit");
525 
526 } // CaptureFrames
527 
528 
529 void NTV2DolbyCapture::GetACStatus (ULWord & outGoodFrames, ULWord & outDroppedFrames, ULWord & outBufferLevel)
530 {
531  AUTOCIRCULATE_STATUS status;
532  mDevice.AutoCirculateGetStatus(mConfig.fInputChannel, status);
533  outGoodFrames = status.GetProcessedFrameCount();
534  outDroppedFrames = status.GetDroppedFrameCount();
535  outBufferLevel = status.GetBufferLevel();
536 }
537 
538 uint32_t NTV2DolbyCapture::RecoverAudio (const NTV2Buffer & inAncBuffer, const uint32_t ancSize, NTV2Buffer & outAudioBuffer)
539 {
540  const uint8_t * pInAncData(inAncBuffer);
541  uint8_t * pOutAudioData(outAudioBuffer);
542  uint32_t audioSize(0);
543 
544  // Extract first 16-bit stereo pair from the aux data
545  for (uint32_t num(0); num < ancSize / 32; num++)
546  {
547  // Audio data aux packet?
548  if (pInAncData[0] == 0x02)
549  {
550  // First sample present?
551  if ((pInAncData[1] & 0x01))
552  {
553  // First sample flat?
554  if ((pInAncData[2] & 0x01))
555  {
556  *pOutAudioData++ = 0;
557  *pOutAudioData++ = 0;
558  *pOutAudioData++ = 0;
559  *pOutAudioData++ = 0;
560  }
561  else
562  {
563  *pOutAudioData++ = pInAncData[4];
564  *pOutAudioData++ = pInAncData[5];
565  *pOutAudioData++ = pInAncData[7];
566  *pOutAudioData++ = pInAncData[8];
567  }
568  audioSize += 4;
569  } // if first sample present
570 
571  // Only 2 channels?
572  if ((pInAncData[1] & 0x10) == 0)
573  {
574  // Second sample present?
575  if ((pInAncData[1] & 0x02))
576  {
577  // Second sample flat?
578  if ((pInAncData[2] & 0x02))
579  {
580  *pOutAudioData++ = 0;
581  *pOutAudioData++ = 0;
582  *pOutAudioData++ = 0;
583  *pOutAudioData++ = 0;
584  }
585  else
586  {
587  *pOutAudioData++ = pInAncData[11];
588  *pOutAudioData++ = pInAncData[12];
589  *pOutAudioData++ = pInAncData[14];
590  *pOutAudioData++ = pInAncData[15];
591  }
592  audioSize += 4;
593  }
594 
595  // Third sample present?
596  if ((pInAncData[1] & 0x04))
597  {
598  // Third sample flat?
599  if ((pInAncData[2] & 0x04))
600  {
601  *pOutAudioData++ = 0;
602  *pOutAudioData++ = 0;
603  *pOutAudioData++ = 0;
604  *pOutAudioData++ = 0;
605  }
606  else
607  {
608  *pOutAudioData++ = pInAncData[18];
609  *pOutAudioData++ = pInAncData[19];
610  *pOutAudioData++ = pInAncData[21];
611  *pOutAudioData++ = pInAncData[22];
612  }
613  audioSize += 4;
614  }
615 
616  // Fourth sample present?
617  if ((pInAncData[1] & 0x08))
618  {
619  // Fourth sample flat?
620  if ((pInAncData[2] & 0x08))
621  {
622  *pOutAudioData++ = 0;
623  *pOutAudioData++ = 0;
624  *pOutAudioData++ = 0;
625  *pOutAudioData++ = 0;
626  }
627  else
628  {
629  *pOutAudioData++ = pInAncData[25];
630  *pOutAudioData++ = pInAncData[26];
631  *pOutAudioData++ = pInAncData[28];
632  *pOutAudioData++ = pInAncData[29];
633  }
634  audioSize += 4;
635  } // if 4th sample present
636  } // if only 2 channels
637  } // if audio data aux packet
638  pInAncData += 32;
639  } // for loop
640  return audioSize;
641 } // RecoverAudio
642 
643 uint32_t NTV2DolbyCapture::RecoverDolby (const NTV2Buffer & inAudioBuffer, const uint32_t inAudioSize, NTV2Buffer & outDolbyBuffer)
644 {
645  const uint16_t * pInAudioData(inAudioBuffer);
646  uint16_t * pOutDolbyData(outDolbyBuffer);
647  uint32_t dolbySize(0);
648 
649  // Extract the dolby frames from the IEC61937 bursts...
650  for (uint32_t cnt(0); cnt < inAudioSize / 2; cnt++)
651  {
652  switch (mDolbyState)
653  {
654  // Find IEC61937 burst preamble
655  case 0: mDolbyState = (*pInAudioData == 0xf872) ? 1 : 0;
656  break;
657 
658  case 1: mDolbyState = (*pInAudioData == 0x4e1f) ? 2 : 0;
659  break;
660 
661  // Check dolby code
662  case 2: mDolbyState = ((*pInAudioData & 0xff) == 0x15) ? 3 : 0;
663  break;
664 
665  // Get burst length
666  case 3: mDolbyLength = uint32_t(*pInAudioData / 2);
667  mDolbyState = 4;
668  break;
669 
670  // Copy dolby samples
671  case 4: if (mDolbyLength)
672  {
673  if (dolbySize < outDolbyBuffer.GetByteCount() / 2)
674  {
675  // Endian swap the data...
676  *pOutDolbyData = (*pInAudioData >> 8) & 0xff;
677  *pOutDolbyData |= (*pInAudioData & 0xff) << 8;
678  pOutDolbyData++;
679  dolbySize++;
680  mDolbyLength--;
681  }
682  }
683  else
684  mDolbyState = 0;
685  break;
686 
687  default: mDolbyState = 0;
688  break;
689  } // switch
690  pInAudioData++;
691  } // for loop
692  return dolbySize * 2;
693 }
694 
695 
697 
698 
699 AJALabelValuePairs DolbyCaptureConfig::Get (const bool inCompact) const
700 {
701  AJALabelValuePairs result(CaptureConfig::Get(inCompact));
702  AJASystemInfo::append(result, "Audio Capture File", fAudioDataFilePath.empty() ? "---" : fAudioDataFilePath);
703  AJASystemInfo::append(result, "Dolby Capture File", fDolbyDataFilePath.empty() ? "---" : fDolbyDataFilePath);
704  return result;
705 }
706 
707 
708 std::ostream & operator << (std::ostream & ioStrm, const DolbyCaptureConfig & inObj)
709 {
710  ioStrm << AJASystemInfo::ToString(inObj.Get());
711  return ioStrm;
712 }
NTV2Channel NTV2InputSourceToChannel(const NTV2InputSource inInputSource)
Converts a given NTV2InputSource to its equivalent NTV2Channel value.
Definition: ntv2utils.cpp:5047
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:1265
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:984
#define NTV2_VIDEO_FORMAT_HAS_PROGRESSIVE_PICTURE(__f__)
Definition: ntv2enums.h:1049
NTV2InputSource NTV2ChannelToInputSource(const NTV2Channel inChannel, const NTV2IOKinds inKinds=NTV2_IOKINDS_SDI)
Definition: ntv2utils.cpp:5132
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:783
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:1293
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:985
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...
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...
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:223
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 IsRemote(void) const
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:7377
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
Invalid or "not found".
Definition: ntv2enums.h:98
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:1281
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:167
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...
virtual std::string GetDisplayName(void)
Answers with this device&#39;s display name.
Definition: ntv2card.cpp:88
NTV2Buffer & AncBuffer(void)
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:6746
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.
virtual NTV2DeviceID GetDeviceID(void)
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:1371
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.).
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...
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:3897
#define NTV2_IS_VALID_INPUT_SOURCE(_inpSrc_)
Definition: ntv2enums.h:1284
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:221
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:1359
virtual bool AutoCirculateStart(const NTV2Channel inChannel, const ULWord64 inStartTime=0)
Starts AutoCirculating the specified channel that was previously initialized by CNTV2Card::AutoCircul...
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:6936
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:986
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, designating a contiguous block of frame buffers on the ...
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:3678
#define NTV2_AUDIOSIZE_MAX
NTV2ReferenceSource NTV2InputSourceToReferenceSource(const NTV2InputSource inInputSource)
Converts a given NTV2InputSource to its equivalent NTV2ReferenceSource value.
Definition: ntv2utils.cpp:5023
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.