AJA NTV2 SDK  18.0.0.2122
NTV2 SDK 18.0.0.2122
ntv2dolbyplayer.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
8 #include "ntv2dolbyplayer.h"
9 #include "ntv2debug.h"
10 #include "ntv2devicescanner.h"
11 #include "ntv2testpatterngen.h"
12 #include "ntv2audiodefines.h"
14 #include "ajabase/system/process.h"
15 
16 //#include "ajabase/system/debug.h"
17 
18 
19 using namespace std;
20 
21 // Convenience macros for EZ logging:
22 #define TCFAIL(_expr_) AJA_sERROR (AJA_DebugUnit_TimecodeGeneric, AJAFUNC << ": " << _expr_)
23 #define TCWARN(_expr_) AJA_sWARNING(AJA_DebugUnit_TimecodeGeneric, AJAFUNC << ": " << _expr_)
24 #define TCNOTE(_expr_) AJA_sNOTICE (AJA_DebugUnit_TimecodeGeneric, AJAFUNC << ": " << _expr_)
25 #define TCINFO(_expr_) AJA_sINFO (AJA_DebugUnit_TimecodeGeneric, AJAFUNC << ": " << _expr_)
26 #define TCDBG(_expr_) AJA_sDEBUG (AJA_DebugUnit_TimecodeGeneric, AJAFUNC << ": " << _expr_)
27 
35 static const uint32_t AUDIOBYTES_MAX_48K (201 * 1024);
36 
44 static const uint32_t AUDIOBYTES_MAX_192K (824 * 1024);
45 
46 static const bool BUFFER_PAGE_ALIGNED (true);
47 
48 // Audio tone generator data
49 static const double gFrequencies [] = {250.0, 500.0, 1000.0, 2000.0};
50 static const ULWord gNumFrequencies (sizeof (gFrequencies) / sizeof (double));
51 static const double gAmplitudes [] = { 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85,
52  0.85, 0.80, 0.75, 0.70, 0.65, 0.60, 0.55, 0.50, 0.45, 0.40, 0.35, 0.30, 0.25, 0.20, 0.15, 0.10};
53 
55  : mConfig (inConfig),
56  mConsumerThread (),
57  mProducerThread (),
58  mDevice (),
59  mSavedTaskMode (NTV2_TASK_MODE_INVALID),
60  mCurrentFrame (0),
61  mCurrentSample (0),
62  mToneFrequency (440.0),
63  mAudioSystem (NTV2_AUDIOSYSTEM_INVALID),
64  mFormatDesc (),
65  mTCIndexes (),
66  mGlobalQuit (false),
67  mTCBurner (),
68  mHostBuffers (),
69  mFrameDataRing (),
70  mTestPatRasters (),
71  mAudioRate (NTV2_AUDIO_192K),
72  mRampSample (0),
73  mBurstIndex (0),
74  mBurstSamples (0),
75  mBurstBuffer (NULL),
76  mBurstSize (0),
77  mBurstOffset (0),
78  mBurstMax (0),
79  mDolbyBuffer (NULL),
80  mDolbySize (0),
81  mDolbyBlocks (0)
82 { }
83 
84 
86 {
87  // Stop my playout and producer threads, then destroy them...
88  Quit ();
89 
91 
92  if (mDolbyBuffer)
93  {
94  delete [] mDolbyBuffer;
95  mDolbyBuffer = NULL;
96  }
97 
98  if (mBurstBuffer)
99  {
100  delete [] mBurstBuffer;
101  mBurstBuffer = NULL;
102  }
103 
104 } // destructor
105 
106 
108 {
109  // Set the global 'quit' flag, and wait for the threads to go inactive...
110  mGlobalQuit = true;
111 
112  while (mProducerThread.Active())
113  AJATime::Sleep(10);
114 
115  while (mConsumerThread.Active())
116  AJATime::Sleep(10);
117 
118  mDevice.SetAudioRate (NTV2_AUDIO_48K, mAudioSystem);
121 
122 #if defined(NTV2_BUFFER_LOCKING)
123  mDevice.DMABufferUnlockAll();
124 #endif // NTV2_BUFFER_LOCKING
125  if (!mConfig.fDoMultiFormat && mDevice.IsOpen())
126  {
128  if (NTV2_IS_VALID_TASK_MODE(mSavedTaskMode))
129  mDevice.SetTaskMode(mSavedTaskMode); // Restore prior task mode
130  }
131 } // Quit
132 
133 
135 {
136  AJAStatus status (AJA_STATUS_SUCCESS);
137 
138  // Open the device...
140  {cerr << "## ERROR: Device '" << mConfig.fDeviceSpec << "' not found" << endl; return AJA_STATUS_OPEN;}
141 
142  if (!mDevice.IsDeviceReady(false))
143  {cerr << "## ERROR: Device '" << mDevice.GetDisplayName() << "' not ready" << endl; return AJA_STATUS_INITIALIZE;}
144  if (!mDevice.features().CanDoPlayback())
145  {cerr << "## ERROR: '" << mDevice.GetDisplayName() << "' is capture-only" << endl; return AJA_STATUS_FEATURE;}
146  if (!mDevice.features().GetNumHDMIVideoOutputs())
147  {cerr << "## ERROR: '" << mDevice.GetDisplayName() << "' has no HDMI outputs" << endl; return AJA_STATUS_FEATURE;}
148 
149  const UWord maxNumChannels (mDevice.features().GetNumFrameStores());
150 
151  // Beware -- some older devices (e.g. Corvid1) can only output from FrameStore 2...
152  if ((mConfig.fOutputChannel == NTV2_CHANNEL1) && (!mDevice.features().CanDoFrameStore1Display()))
153  mConfig.fOutputChannel = NTV2_CHANNEL2;
154  if (UWord(mConfig.fOutputChannel) >= maxNumChannels)
155  {
156  cerr << "## ERROR: '" << mDevice.GetDisplayName() << "' can't use Ch" << DEC(mConfig.fOutputChannel+1)
157  << " -- only supports Ch1" << (maxNumChannels > 1 ? string("-Ch") + string(1, char(maxNumChannels+'0')) : "") << endl;
158  return AJA_STATUS_UNSUPPORTED;
159  }
160 
161  if (!mConfig.fDoMultiFormat)
162  {
163  mDevice.GetTaskMode(mSavedTaskMode); // Save the current task mode
165  return AJA_STATUS_BUSY; // Device is in use by another app -- fail
166  }
167  mDevice.SetTaskMode(NTV2_OEM_TASKS); // Set OEM service level
168 
169  if (mDevice.features().CanDoMultiFormat())
170  mDevice.SetMultiFormatMode(mConfig.fDoMultiFormat);
171  else
172  mConfig.fDoMultiFormat = false;
173 
174  if (!mConfig.fDolbyFilePath.empty())
175  {
176  status = mDolbyFileIO.Open(mConfig.fDolbyFilePath.c_str(), eAJAReadOnly, 0);
177  if (status != AJA_STATUS_SUCCESS)
178  {cerr << "## ERROR: Could not open file: " << mConfig.fDolbyFilePath << endl; return status;}
179  }
180 
181  // Set up the video and audio...
182  status = SetUpVideo();
183  if (AJA_FAILURE(status))
184  return status;
185  status = mConfig.WithAudio() ? SetUpAudio() : AJA_STATUS_SUCCESS;
186  if (AJA_FAILURE(status))
187  return status;
188 
189  // Set up the circular buffers, and the test pattern buffers...
190  status = SetUpHostBuffers();
191  if (AJA_FAILURE(status))
192  return status;
193  status = SetUpTestPatternBuffers();
194  if (AJA_FAILURE(status))
195  return status;
196 
197  // Set up the device signal routing...
198  if (!RouteOutputSignal())
199  return AJA_STATUS_FAIL;
200 
201  // Lastly, prepare my AJATimeCodeBurn instance...
202  if (!mTCBurner.RenderTimeCodeFont (CNTV2DemoCommon::GetAJAPixelFormat(mConfig.fPixelFormat), mFormatDesc.numPixels, mFormatDesc.numLines))
203  {cerr << "## ERROR: RenderTimeCodeFont failed for: " << mFormatDesc << endl; return AJA_STATUS_UNSUPPORTED;}
204 
205  // Ready to go...
206  #if defined(_DEBUG)
207  cerr << mConfig;
208  if (mDevice.IsRemote())
209  cerr << "Device Description: " << mDevice.GetDescription() << endl;
210  cerr << endl;
211  #endif // defined(_DEBUG)
212  return AJA_STATUS_SUCCESS;
213 
214 } // Init
215 
216 
218 {
219  // Configure the device to output the requested video format...
220  if (mConfig.fVideoFormat == NTV2_FORMAT_UNKNOWN)
221  return AJA_STATUS_BAD_PARAM;
222 
223  if (!mDevice.features().CanDoVideoFormat(mConfig.fVideoFormat))
224  { cerr << "## ERROR: '" << mDevice.GetDisplayName() << "' doesn't support "
225  << ::NTV2VideoFormatToString(mConfig.fVideoFormat) << endl;
226  return AJA_STATUS_UNSUPPORTED;
227  }
228  if (!mDevice.features().CanDoFrameBufferFormat(mConfig.fPixelFormat))
229  { cerr << "## ERROR: '" << mDevice.GetDisplayName() << "' doesn't support "
230  << ::NTV2FrameBufferFormatString(mConfig.fPixelFormat) << endl;
231  return AJA_STATUS_UNSUPPORTED;
232  }
233 
234  // Keep the raster description handy...
235  mFormatDesc = NTV2FormatDescriptor(mConfig.fVideoFormat, mConfig.fPixelFormat);
236  if (!mFormatDesc.IsValid())
237  return AJA_STATUS_FAIL;
238 
239  // Turn on the FrameStore (to read frame buffer memory and transmit video)...
240  mDevice.EnableChannel(mConfig.fOutputChannel);
241 
242  // This demo assumes VANC is disabled...
243  mDevice.SetVANCMode(NTV2_VANCMODE_OFF, mConfig.fOutputChannel);
245 
246  // Set the FrameStore video format...
247  mDevice.SetVideoFormat (mConfig.fVideoFormat, false, false, mConfig.fOutputChannel);
248 
249  // Set the frame buffer pixel format for the device FrameStore...
250  mDevice.SetFrameBufferFormat (mConfig.fOutputChannel, mConfig.fPixelFormat);
251 
252  // The output interrupt is Enabled by default, but on some platforms, you must subscribe to it
253  // in order to be able to wait on its event/semaphore...
255 
256  // Set output clock reference...
258 
259  return AJA_STATUS_SUCCESS;
260 
261 } // SetUpVideo
262 
263 
265 {
266  mAudioSystem = NTV2_AUDIOSYSTEM_1; // Use NTV2_AUDIOSYSTEM_1...
267  if (mDevice.features().GetNumAudioSystems() > 1) // ...but if the device has more than one audio system...
268  mAudioSystem = ::NTV2ChannelToAudioSystem(mConfig.fOutputChannel); // ...base it on the channel
269  // However, there are a few older devices that have only 1 audio system,
270  // yet 2 frame stores (or must use channel 2 for playout)...
271  if (!mDevice.features().CanDoFrameStore1Display())
272  mAudioSystem = NTV2_AUDIOSYSTEM_1;
273 
274  mDevice.SetNumberAudioChannels (8, mAudioSystem); // This demo uses 8 channels -- no more, no less
275  mDevice.SetAudioRate (mAudioRate, mAudioSystem);
276 
277  // How big should the on-device audio buffer be? 1MB? 2MB? 4MB? 8MB?
278  // For this demo, 4MB will work best across all platforms (Windows, Mac & Linux)...
279  mDevice.SetAudioBufferSize (NTV2_AUDIO_BUFFER_BIG, mAudioSystem);
280 
281  // Set the SDI output audio embedders to embed audio samples from the output of mAudioSystem...
282  mDevice.SetHDMIOutAudioSource2Channel (NTV2_AudioChannel1_2, mAudioSystem);
283 
284  mDevice.SetHDMIOutAudioRate(mAudioRate);
285 
287 
288  // If the last app using the device left it in end-to-end mode (input passthru),
289  // then loopback must be disabled, or else the output will contain whatever audio
290  // is present in whatever signal is feeding the device's SDI input...
291  mDevice.SetAudioLoopBack (NTV2_AUDIO_LOOPBACK_OFF, mAudioSystem);
292 
293  return AJA_STATUS_SUCCESS;
294 
295 } // SetUpAudio
296 
297 
299 {
300  CNTV2DemoCommon::SetDefaultPageSize(); // Set host-specific page size
301 
302  // Let my circular buffer know when it's time to quit...
303  mFrameDataRing.SetAbortFlag (&mGlobalQuit);
304 
305  // Calculate the size of the audio buffer, which mostly depends on the sample rate...
306  const uint32_t AUDIOBYTES_MAX = (mAudioRate == NTV2_AUDIO_192K) ? AUDIOBYTES_MAX_192K : AUDIOBYTES_MAX_48K;
307 
308  // Allocate and add each in-host NTV2FrameData to my circular buffer member variable...
309  mHostBuffers.reserve(CIRCULAR_BUFFER_SIZE);
310  while (mHostBuffers.size() < CIRCULAR_BUFFER_SIZE)
311  {
312  mHostBuffers.push_back(NTV2FrameData()); // Make a new NTV2FrameData...
313  NTV2FrameData & frameData(mHostBuffers.back()); // ...and get a reference to it
314 
315  // Allocate a page-aligned video buffer
316  if (mConfig.WithVideo())
317  if (!frameData.fVideoBuffer.Allocate (mFormatDesc.GetTotalBytes(), BUFFER_PAGE_ALIGNED))
318  {
319  PLFAIL("Failed to allocate " << xHEX0N(mFormatDesc.GetTotalBytes(),8) << "-byte video buffer");
320  return AJA_STATUS_MEMORY;
321  }
322  #ifdef NTV2_BUFFER_LOCKING
323  if (frameData.fVideoBuffer)
324  mDevice.DMABufferLock(frameData.fVideoBuffer, true);
325  #endif
326 
327  // Allocate a page-aligned audio buffer (if transmitting audio)
328  if (mConfig.WithAudio())
329  if (!frameData.fAudioBuffer.Allocate (AUDIOBYTES_MAX, BUFFER_PAGE_ALIGNED))
330  {
331  PLFAIL("Failed to allocate " << xHEX0N(AUDIOBYTES_MAX,8) << "-byte audio buffer");
332  return AJA_STATUS_MEMORY;
333  }
334  #ifdef NTV2_BUFFER_LOCKING
335  if (frameData.fAudioBuffer)
336  mDevice.DMABufferLock(frameData.fAudioBuffer, /*alsoPreLockSGL*/true);
337  #endif
338  mFrameDataRing.Add (&frameData);
339  } // for each NTV2FrameData
340 
341  if (mDolbyFileIO.IsOpen())
342  {
343  // Initialize IEC61937 burst size (32 milliseconds) for HDMI 192 kHz sample rate
344  mBurstSamples = 6144; // 192000 * 0.032 samples
345  mBurstMax = mBurstSamples * 2;
346  mBurstBuffer = new uint16_t [mBurstMax];
347  mDolbyBuffer = new uint16_t [mBurstMax];
348  }
349 
350  return AJA_STATUS_SUCCESS;
351 
352 } // SetUpHostBuffers
353 
355 {
356  vector<NTV2TestPatternSelect> testPatIDs;
357  testPatIDs.push_back(NTV2_TestPatt_ColorBars100);
358  testPatIDs.push_back(NTV2_TestPatt_ColorBars75);
359  testPatIDs.push_back(NTV2_TestPatt_Ramp);
360  testPatIDs.push_back(NTV2_TestPatt_MultiBurst);
361  testPatIDs.push_back(NTV2_TestPatt_LineSweep);
362  testPatIDs.push_back(NTV2_TestPatt_CheckField);
363  testPatIDs.push_back(NTV2_TestPatt_FlatField);
364  testPatIDs.push_back(NTV2_TestPatt_MultiPattern);
365 
366  mTestPatRasters.clear();
367  for (size_t tpNdx(0); tpNdx < testPatIDs.size(); tpNdx++)
368  mTestPatRasters.push_back(NTV2Buffer());
369 
370  if (!mFormatDesc.IsValid())
371  {PLFAIL("Bad format descriptor"); return AJA_STATUS_FAIL;}
372  if (mFormatDesc.IsVANC())
373  {PLFAIL("VANC should have been disabled: " << mFormatDesc); return AJA_STATUS_FAIL;}
374 
375  // Set up one video buffer for each test pattern...
376  for (size_t tpNdx(0); tpNdx < testPatIDs.size(); tpNdx++)
377  {
378  // Allocate the buffer memory...
379  if (!mTestPatRasters.at(tpNdx).Allocate (mFormatDesc.GetTotalBytes(), BUFFER_PAGE_ALIGNED))
380  { PLFAIL("Test pattern buffer " << DEC(tpNdx+1) << " of " << DEC(testPatIDs.size()) << ": "
381  << xHEX0N(mFormatDesc.GetTotalBytes(),8) << "-byte page-aligned alloc failed");
382  return AJA_STATUS_MEMORY;
383  }
384 
385  // Fill the buffer with test pattern...
386  NTV2TestPatternGen testPatternGen;
387  if (!testPatternGen.DrawTestPattern (testPatIDs.at(tpNdx), mFormatDesc, mTestPatRasters.at(tpNdx)))
388  {
389  cerr << "## ERROR: DrawTestPattern " << DEC(tpNdx) << " failed: " << mFormatDesc << endl;
390  return AJA_STATUS_FAIL;
391  }
392 
393  #ifdef NTV2_BUFFER_LOCKING
394  // Try to prelock the memory, including its scatter-gather list...
395  if (!mDevice.DMABufferLock(mTestPatRasters.at(tpNdx), /*alsoLockSegmentMap=*/true))
396  PLWARN("Test pattern buffer " << DEC(tpNdx+1) << " of " << DEC(testPatIDs.size()) << ": failed to pre-lock");
397  #endif
398  } // loop for each predefined pattern
399 
400  return AJA_STATUS_SUCCESS;
401 
402 } // SetUpTestPatternBuffers
403 
404 
406 {
407  const bool isRGB (::IsRGBFormat(mConfig.fPixelFormat));
408  const NTV2OutputXptID fsVidOutXpt (::GetFrameStoreOutputXptFromChannel (mConfig.fOutputChannel, isRGB, false/*is425*/));
409 
410  mDevice.ClearRouting(); // Start with clean slate
411 
412  // Connect HDMI video output...
413  return mDevice.Connect (::GetOutputDestInputXpt(NTV2_OUTPUTDESTINATION_HDMI1), fsVidOutXpt);
414 
415 } // RouteOutputSignal
416 
417 
419 {
420  // Start my consumer and producer threads...
423  return AJA_STATUS_SUCCESS;
424 
425 } // Run
426 
427 
428 
430 // This is where the play thread starts
431 
433 {
434  // Create and start the playout thread...
435  mConsumerThread.Attach (ConsumerThreadStatic, this);
436  mConsumerThread.SetPriority (AJA_ThreadPriority_High);
437  mConsumerThread.Start ();
438 
439 } // StartConsumerThread
440 
441 
442 // The playout thread function
443 void NTV2DolbyPlayer::ConsumerThreadStatic (AJAThread * pThread, void * pContext) // static
444 { (void) pThread;
445  // Grab the NTV2DolbyPlayer instance pointer from the pContext parameter,
446  // then call its ConsumeFrames method...
447  NTV2DolbyPlayer * pApp (reinterpret_cast<NTV2DolbyPlayer*>(pContext));
448  if (pApp)
449  pApp->ConsumeFrames();
450 
451 } // ConsumerThreadStatic
452 
453 
455 {
456  AUTOCIRCULATE_TRANSFER outputXfer;
457  AUTOCIRCULATE_STATUS outputStatus;
458  ULWord goodXfers(0), badXfers(0), starves(0), noRoomWaits(0);
459 
460  // Stop AutoCirculate, just in case someone else left it running...
461  mDevice.AutoCirculateStop(mConfig.fOutputChannel);
462  mDevice.WaitForOutputVerticalInterrupt(mConfig.fOutputChannel, 4); // Let it stop
463  PLNOTE("Thread started");
464 
465  // Initialize & start AutoCirculate...
466  bool initOK = mDevice.AutoCirculateInitForOutput (mConfig.fOutputChannel, mConfig.fFrames.count(),
467  mAudioSystem, AUTOCIRCULATE_WITH_RP188, /*numChannels*/ 1,
468  mConfig.fFrames.firstFrame(), mConfig.fFrames.lastFrame());
469  if (!initOK)
470  {PLFAIL("AutoCirculateInitForOutput failed"); mGlobalQuit = true;}
471  else if (!mConfig.WithVideo())
472  { // Video suppressed --
473  // Clear device frame buffers being AutoCirculated (prevent garbage output frames)
474  NTV2Buffer tmpFrame (mFormatDesc.GetVideoWriteSize());
475  NTV2TestPatternGen blackPatternGen;
476  blackPatternGen.DrawTestPattern (NTV2_TestPatt_Black, mFormatDesc, tmpFrame);
477  mDevice.AutoCirculateGetStatus (mConfig.fOutputChannel, outputStatus);
478  for (uint16_t frmNum(outputStatus.GetStartFrame()); frmNum <= outputStatus.GetEndFrame(); frmNum++)
479  mDevice.DMAWriteFrame(ULWord(frmNum), tmpFrame, mFormatDesc.GetTotalBytes());
480  } // else if --novideo
481 
482 
483  while (!mGlobalQuit)
484  {
485  mDevice.AutoCirculateGetStatus (mConfig.fOutputChannel, outputStatus);
486 
487  // Check if there's room for another frame on the card...
488  if (outputStatus.CanAcceptMoreOutputFrames())
489  {
490  // Device has at least one free frame buffer that can be filled.
491  // Wait for the next frame in our ring to become ready to "consume"...
492  NTV2FrameData * pFrameData (mFrameDataRing.StartConsumeNextBuffer());
493  if (!pFrameData)
494  {starves++; continue;}
495 
496  outputXfer.SetOutputTimeCodes(pFrameData->fTimecodes);
497 
498  if (pFrameData->VideoBuffer()) // Transfer the timecode-burned frame to the device for playout...
499  outputXfer.SetVideoBuffer (pFrameData->VideoBuffer(), pFrameData->VideoBufferSize());
500  if (pFrameData->AudioBuffer()) // If also playing audio...
501  outputXfer.SetAudioBuffer (pFrameData->AudioBuffer(), pFrameData->fNumAudioBytes);
502 
503 
504  // Perform the DMA transfer to the device...
505  if (mDevice.AutoCirculateTransfer (mConfig.fOutputChannel, outputXfer))
506  goodXfers++;
507  else
508  badXfers++;
509 
510  if (goodXfers == 3)
511  mDevice.AutoCirculateStart(mConfig.fOutputChannel);
512 
513  // Signal that the frame has been "consumed"...
514  mFrameDataRing.EndConsumeNextBuffer();
515  continue; // Back to top of while loop
516 
517  }
518 
519  // Wait for one or more buffers to become available on the device, which should occur at next VBI...
520  noRoomWaits++;
522  } // loop til quit signaled
523 
524  // Stop AutoCirculate...
525  mDevice.AutoCirculateStop(mConfig.fOutputChannel);
526  PLNOTE("Thread completed: " << DEC(goodXfers) << " xfers, " << DEC(badXfers) << " failed, "
527  << DEC(starves) << " starves, " << DEC(noRoomWaits) << " VBI waits");
528 
529 } // ConsumeFrames
530 
531 
532 
534 // This is where the producer thread starts
535 
537 {
538  // Create and start the producer thread...
539  mProducerThread.Attach(ProducerThreadStatic, this);
540  mProducerThread.SetPriority(AJA_ThreadPriority_High);
541  mProducerThread.Start();
542 
543 } // StartProducerThread
544 
545 
546 void NTV2DolbyPlayer::ProducerThreadStatic (AJAThread * pThread, void * pContext) // static
547 {
548  (void) pThread;
549  NTV2DolbyPlayer * pApp (reinterpret_cast <NTV2DolbyPlayer *> (pContext));
550  if (pApp)
551  pApp->ProduceFrames ();
552 
553 } // ProducerThreadStatic
554 
555 
557 {
558  ULWord freqNdx(0), testPatNdx(0), badTally(0);
559  double timeOfLastSwitch (0.0);
560 
563  //const bool isInterlace (!NTV2_VIDEO_FORMAT_HAS_PROGRESSIVE_PICTURE(mConfig.fVideoFormat));
564  //const bool isPAL (NTV2_IS_PAL_VIDEO_FORMAT(mConfig.fVideoFormat));
565  const NTV2FrameRate ntv2FrameRate (::GetNTV2FrameRateFromVideoFormat(mConfig.fVideoFormat));
566  const TimecodeFormat tcFormat (CNTV2DemoCommon::NTV2FrameRate2TimecodeFormat(ntv2FrameRate));
567 
568  PLNOTE("Thread started");
569  while (!mGlobalQuit)
570  {
571  NTV2FrameData * pFrameData (mFrameDataRing.StartProduceNextBuffer());
572  // If no frame is available, wait and try again
573  if (!pFrameData)
574  { badTally++; // No frame available!
575  AJATime::Sleep(10); // Wait a bit for the consumer thread to free one up for me...
576  continue; // ...then try again
577  }
578 
579  // Copy my pre-made test pattern into my video buffer...
580  if (pFrameData->VideoBuffer()) // Copy fresh, unmodified, pre-rendered test pattern into this frame's video buffer...
581  pFrameData->fVideoBuffer.CopyFrom (mTestPatRasters.at(testPatNdx),
582  /*srcOffset*/ 0,
583  /*dstOffset*/ 0,
584  /*byteCount*/ pFrameData->fVideoBuffer.GetByteCount());
585 
586  const CRP188 rp188Info (mCurrentFrame++, 0, 0, 10, tcFormat);
587  NTV2_RP188 tcF1;
588  string tcString;
589 
590  rp188Info.GetRP188Reg (tcF1);
591  rp188Info.GetRP188Str (tcString);
592 
593  if (pFrameData->VideoBuffer()) // Burn current timecode into the video buffer...
594  mTCBurner.BurnTimeCode (pFrameData->VideoBuffer(), tcString.c_str(), 80);
595  TCDBG("F" << DEC0N(mCurrentFrame-1,6) << ": " << tcF1 << ": " << tcString);
596 
597  // If also playing audio...
598  if (pFrameData->AudioBuffer()) // ...then generate audio tone data for this frame...
599  {
600  if (mDolbyFileIO.IsOpen())
601  pFrameData->fNumAudioBytes = AddDolby(*pFrameData);
602  else
603  {
604  if (mConfig.fDoRamp)
605  pFrameData->fNumAudioBytes = AddRamp(*pFrameData);
606  else
607  pFrameData->fNumAudioBytes = AddTone(*pFrameData);
608  }
609  }
610 
611  // Every few seconds, change the test pattern and tone frequency...
612  const double currentTime (timeBase.FramesToSeconds(mCurrentFrame));
613  if (currentTime > timeOfLastSwitch + 4.0)
614  {
615  freqNdx = (freqNdx + 1) % gNumFrequencies;
616  testPatNdx = (testPatNdx + 1) % ULWord(mTestPatRasters.size());
617  mToneFrequency = gFrequencies[freqNdx];
618  timeOfLastSwitch = currentTime;
619  PLINFO("F" << DEC0N(mCurrentFrame,6) << ": " << tcString << ": tone=" << mToneFrequency << "Hz, pattern='" << tpNames.at(testPatNdx) << "'");
620  } // if time to switch test pattern & tone frequency
621 
622  // Signal that I'm done producing this FrameData, making it immediately available for transfer/playout...
623  mFrameDataRing.EndProduceNextBuffer();
624 
625  } // loop til mGlobalQuit goes true
626  PLNOTE("Thread completed: " << DEC(mCurrentFrame) << " frame(s) produced, " << DEC(badTally) << " failed");
627 
628 } // ProduceFrames
629 
631 {
632  mDevice.AutoCirculateGetStatus (mConfig.fOutputChannel, outStatus);
633 }
634 
635 
636 uint32_t NTV2DolbyPlayer::AddTone (NTV2FrameData & inFrameData)
637 {
640  ULWord numChannels (0);
641 
642  mDevice.GetFrameRate (frameRate, mConfig.fOutputChannel);
643  mDevice.GetAudioRate (audioRate, mAudioSystem);
644  mDevice.GetNumberAudioChannels (numChannels, mAudioSystem);
645 
646  // Set per-channel tone frequencies...
647  double pFrequencies [kNumAudioChannelsMax];
648  pFrequencies [0] = (mToneFrequency / 2.0);
649  for (ULWord chan (1); chan < numChannels; chan++)
650  // The 1.154782 value is the 16th root of 10, to ensure that if mToneFrequency is 2000,
651  // that the calculated frequency of audio channel 16 will be 20kHz...
652  pFrequencies [chan] = pFrequencies [chan - 1] * 1.154782;
653 
654  // Because audio on AJA devices use fixed sample rates (typically 48KHz), certain video frame rates will necessarily
655  // result in some frames having more audio samples than others. The GetAudioSamplesPerFrame function
656  // is used to calculate the correct sample count...
657  const ULWord numSamples (::GetAudioSamplesPerFrame (frameRate, audioRate, mCurrentFrame));
658  const double sampleRateHertz (::GetAudioSamplesPerSecond(audioRate));
659 
660  return ::AddAudioTone ( inFrameData.AudioBuffer(), // audio buffer to fill
661  mCurrentSample, // which sample for continuing the waveform
662  numSamples, // number of samples to generate
663  sampleRateHertz, // sample rate [Hz]
664  gAmplitudes, // per-channel amplitudes
665  pFrequencies, // per-channel tone frequencies [Hz]
666  31, // bits per sample
667  false, // don't byte swap
668  numChannels); // number of audio channels to generate
669 } // AddTone
670 
671 uint32_t NTV2DolbyPlayer::AddRamp (NTV2FrameData & inFrameData)
672 {
673  ULWord * audioBuffer (inFrameData.AudioBuffer());
676  ULWord numChannels (0);
677 
678  mDevice.GetFrameRate (frameRate, mConfig.fOutputChannel);
679  mDevice.GetAudioRate (audioRate, mAudioSystem);
680  mDevice.GetNumberAudioChannels (numChannels, mAudioSystem);
681 
682  // Because audio on AJA devices use fixed sample rates (typically 48KHz), certain video frame rates will necessarily
683  // result in some frames having more audio samples than others. The GetAudioSamplesPerFrame function
684  // is used to calculate the correct sample count...
685  const ULWord numSamples (::GetAudioSamplesPerFrame (frameRate, audioRate, mCurrentFrame));
686 
687  for (uint32_t samp(0); samp < numSamples; samp++)
688  {
689  for (uint32_t ch(0); ch < numChannels; ch++)
690  {
691  *audioBuffer = uint32_t(mRampSample) << 16;
692  audioBuffer++;
693  mRampSample++;
694  } // for each channel
695  } // for each sample
696 
697  return numSamples * numChannels * 4;
698 } // AddRamp
699 
700 #if defined(DOLBY_FULL_PARSER)
701 
702 uint32_t NTV2DolbyPlayer::AddDolby (NTV2FrameData & inFrameData)
703 {
704  ULWord* audioBuffer (inFrameData.AudioBuffer());
707  ULWord numChannels (0);
708  ULWord sampleOffset(0);
709  ULWord sampleCount (0);
710  NTV2DolbyBSI bsi;
711 
712  mDevice.GetFrameRate (frameRate, mConfig.fOutputChannel);
713  mDevice.GetAudioRate (audioRate, mAudioSystem);
714  mDevice.GetNumberAudioChannels (numChannels, mAudioSystem);
715  const ULWord numSamples (::GetAudioSamplesPerFrame (frameRate, audioRate, mCurrentFrame));
716 
717  if (!mDolbyFileIO.IsOpen() || !mDolbyBuffer)
718  goto silence;
719 
720  // Generate the samples for this frame
721  while (sampleOffset < numSamples)
722  {
723  if ((mBurstSize != 0) && (mBurstIndex < mBurstSamples))
724  {
725  if(mBurstOffset < mBurstSize)
726  {
727  uint32_t data0 = 0;
728  uint32_t data1 = 0;
729  if (mBurstIndex == 0)
730  {
731  // Add IEC61937 burst preamble
732  data0 = 0xf872; // Sync stuff
733  data1 = 0x4e1f;
734  }
735  else if (mBurstIndex == 1)
736  {
737  // Add more IEC61937 burst preamble
738  data0 = ((bsi.bsmod & 0x7) << 8) | 0x0015; // This is Dolby
739  data1 = mBurstSize * 2; // Data size in bytes
740  }
741  else
742  {
743  // Add sync frame data
744  data0 = (uint32_t)(mBurstBuffer[mBurstOffset]);
745  data0 = ((data0 & 0x00ff) << 8) | ((data0 & 0xff00) >> 8);
746  mBurstOffset++;
747  data1 = (uint32_t)(mBurstBuffer[mBurstOffset]);
748  data1 = ((data1 & 0x00ff) << 8) | ((data1 & 0xff00) >> 8);
749  mBurstOffset++;
750  }
751 
752  // Write data into 16 msbs of all audio channel pairs
753  data0 <<= 16;
754  data1 <<= 16;
755  for (ULWord i = 0; i < numChannels; i += 2)
756  {
757  audioBuffer[sampleOffset * numChannels + i] = data0;
758  audioBuffer[sampleOffset * numChannels + i + 1] = data1;
759  }
760  }
761  else
762  {
763  // Pad samples out to burst size
764  for (ULWord i = 0; i < numChannels; i++)
765  {
766  audioBuffer[sampleOffset * numChannels + i] = 0;
767  }
768  }
769  sampleOffset++;
770  mBurstIndex++;
771  }
772  else
773  {
774  ULWord dolbyOffset = 0;
775  ULWord burstOffset = 0;
776  ULWord numBlocks = 0;
777 
778  mBurstIndex = 0;
779  mBurstOffset = 0;
780 
781  if (mDolbySize == 0)
782  {
783  // Find first Dolby Digital Plus burst frame
784  while (true)
785  {
786  if (!GetDolbyFrame(&mDolbyBuffer[0], sampleCount))
787  {
788  cerr << "## ERROR: Dolby frame not found" << endl;
789  mDolbyFileIO.Close();
790  goto silence;
791  }
792 
793  if (!ParseBSI(&mDolbyBuffer[1], sampleCount - 1, &bsi))
794  continue;
795 
796  if ((bsi.strmtyp == 0) &&
797  (bsi.substreamid == 0) &&
798  (bsi.bsid == 16) &&
799  ((bsi.numblkscod == 3) || (bsi.convsync == 1)))
800  break;
801  }
802 
803  mDolbySize = sampleCount;
804  switch (bsi.numblkscod)
805  {
806  case 0: mDolbyBlocks = 1; break;
807  case 1: mDolbyBlocks = 2; break;
808  case 2: mDolbyBlocks = 3; break;
809  case 3: mDolbyBlocks = 6; break;
810  default: goto silence;
811  }
812  }
813 
814  while (numBlocks <= 6)
815  {
816  // Copy the Dolby frame into the burst buffer
817  while (dolbyOffset < mDolbySize)
818  {
819  // Check for burst size overrun
820  if (burstOffset >= mBurstMax)
821  {
822  cerr << "## ERROR: Dolby burst too large" << endl;
823  mDolbyFileIO.Close();
824  goto silence;
825  }
826 
827  // Copy sample
828  mBurstBuffer[burstOffset] = mDolbyBuffer[dolbyOffset];
829  burstOffset++;
830  dolbyOffset++;
831  }
832 
833  // Get the next Dolby frame
834  if (!GetDolbyFrame(&mDolbyBuffer[0], sampleCount))
835  {
836  // try to loop
837  if (!GetDolbyFrame(&mDolbyBuffer[0], sampleCount))
838  {
839  cerr << "## ERROR: Dolby frame not found" << endl;
840  mDolbyFileIO.Close();
841  goto silence;
842  }
843  }
844 
845  // Parse the Dolby bitstream header
846  if (!ParseBSI(&mDolbyBuffer[1], sampleCount - 1, &bsi))
847  continue;
848 
849  // Only Dolby Digital Plus
850  if (bsi.bsid != 16)
851  {
852  cerr << "## ERROR: Dolby frame bad bsid = " << bsi.bsid << endl;
853  continue;
854  }
855 
856  mDolbySize = sampleCount;
857  dolbyOffset = 0;
858 
859  // Increment block count on first substream
860  if ((bsi.strmtyp == 0) && (bsi.substreamid == 0))
861  {
862  // increment block count
863  numBlocks += mDolbyBlocks;
864 
865  switch (bsi.numblkscod)
866  {
867  case 0: mDolbyBlocks = 1; break;
868  case 1: mDolbyBlocks = 2; break;
869  case 2: mDolbyBlocks = 3; break;
870  case 3: mDolbyBlocks = 6; break;
871  default:
872  cerr << "## ERROR: Dolby frame bad numblkscod = " << bsi.numblkscod << endl;
873  goto silence;
874  }
875  }
876 
877  // Are we done?
878  if (numBlocks >= 6)
879  {
880  // First frame of new burst must have convsync == 1
881  if ((bsi.numblkscod != 3) &&
882  (bsi.convsync != 1))
883  {
884  cerr << "## ERROR: Dolby frame unexpected convsync = " << bsi.convsync << endl;
885  mDolbySize = 0;
886  mDolbyBlocks = 0;
887  }
888 
889  // Keep the burst size
890  mBurstSize = burstOffset;
891  break;
892  }
893  }
894  }
895  }
896 
897  return numSamples * numChannels * 4;
898 
899 silence:
900  // Output silence when done with file
901  memset(&audioBuffer[sampleOffset * numChannels], 0, (numSamples - sampleOffset) * numChannels * 4);
902  return numSamples * numChannels * 4;
903 }
904 
905 
906 bool NTV2DolbyPlayer::GetDolbyFrame (uint16_t * pInDolbyBuffer, uint32_t & numSamples)
907 {
908  uint32_t bytes;
909  bool done = false;
910 
911  while (!done)
912  {
913  bytes = mDolbyFileIO.Read((uint8_t*)(&pInDolbyBuffer[0]), 2);
914  if (bytes != 2)
915  {
916  // Reset file
917  mDolbyFileIO.Seek(0, eAJASeekSet);
918  return false;
919  }
920 
921  // Check sync word
922  if ((mDolbyBuffer[0] == 0x7705) ||
923  (mDolbyBuffer[0] == 0x770b))
924  done = true;
925  }
926 
927  // Read more of the sync frame header
928  bytes = mDolbyFileIO.Read((uint8_t*)(&pInDolbyBuffer[1]), 4);
929  if (bytes != 4)
930  return false;
931 
932  // Get frame size - 16 bit words plus sync word
933  uint32_t size = (uint32_t)mDolbyBuffer[1];
934  size = (((size & 0x00ff) << 8) | ((size & 0xff00) >> 8));
935  size = (size & 0x7ff) + 1;
936 
937  // Read the rest of the sync frame
938  uint32_t len = (size - 3) * 2;
939  bytes = mDolbyFileIO.Read((uint8_t*)(&pInDolbyBuffer[3]), len);
940  if (bytes != len)
941  return false;
942 
943  numSamples = size;
944 
945  return true;
946 }
947 
948 
949 bool NTV2DolbyPlayer::ParseBSI(uint16_t * pInDolbyBuffer, uint32_t numSamples, NTV2DolbyBSI * pBsi)
950 {
951  if ((pInDolbyBuffer == NULL) || (pBsi == NULL))
952  return false;
953 
954  memset(pBsi, 0, sizeof(NTV2DolbyBSI));
955 
956  SetBitBuffer((uint8_t*)pInDolbyBuffer, numSamples * 2);
957 
958  if (!GetBits(pBsi->strmtyp, 2)) return false;
959  if (!GetBits(pBsi->substreamid, 3)) return false;
960  if (!GetBits(pBsi->frmsiz, 11)) return false;
961  if (!GetBits(pBsi->fscod, 2)) return false;
962  if (!GetBits(pBsi->numblkscod, 2)) return false;
963  if (!GetBits(pBsi->acmod, 3)) return false;
964  if (!GetBits(pBsi->lfeon, 1)) return false;
965  if (!GetBits(pBsi->bsid, 5)) return false;
966  if (!GetBits(pBsi->dialnorm, 5)) return false;
967  if (!GetBits(pBsi->compre, 1)) return false;
968  if (pBsi->compre)
969  {
970  if (!GetBits(pBsi->compr, 8)) return false;
971  }
972  if (pBsi->acmod == 0x0) /* if 1+1 mode (dual mono, so some items need a second value) */
973  {
974  if (!GetBits(pBsi->dialnorm2, 5)) return false;
975  if (!GetBits(pBsi->compr2e, 1)) return false;
976  if (pBsi->compr2e)
977  {
978  if (!GetBits(pBsi->compr2, 8)) return false;
979  }
980  }
981  if (pBsi->strmtyp == 0x1) /* if dependent stream */
982  {
983  if (!GetBits(pBsi->chanmape, 1)) return false;
984  if (pBsi->chanmape)
985  {
986  if (!GetBits(pBsi->chanmap, 16)) return false;
987  }
988  }
989  if (!GetBits(pBsi->mixmdate, 1)) return false;
990  if (pBsi->mixmdate) /* mixing metadata */
991  {
992  if (pBsi->acmod > 0x2) /* if more than 2 channels */
993  {
994  if (!GetBits(pBsi->dmixmod, 2)) return false;
995  }
996  if ((pBsi->acmod & 0x1) && (pBsi->acmod > 0x2)) /* if three front channels exist */
997  {
998  if (!GetBits(pBsi->ltrtcmixlev, 3)) return false;
999  if (!GetBits(pBsi->lorocmixlev, 3)) return false;
1000  }
1001  if (pBsi->acmod & 0x4) /* if a surround channel exists */
1002  {
1003  if (!GetBits(pBsi->ltrtsurmixlev, 3)) return false;
1004  if (!GetBits(pBsi->lorosurmixlev, 3)) return false;
1005  }
1006  if (pBsi->lfeon) /* if the LFE channel exists */
1007  {
1008  if (!GetBits(pBsi->lfemixlevcode, 1)) return false;
1009  if (pBsi->lfemixlevcode)
1010  {
1011  if (!GetBits(pBsi->lfemixlevcod, 5)) return false;
1012  }
1013  }
1014  if (pBsi->strmtyp == 0x0) /* if independent stream */
1015  {
1016  if (!GetBits(pBsi->pgmscle, 1)) return false;
1017  if (pBsi->pgmscle)
1018  {
1019  if (!GetBits(pBsi->pgmscl, 6)) return false;
1020  }
1021  if (pBsi->acmod == 0x0) /* if 1+1 mode (dual mono, so some items need a second value) */
1022  {
1023  if (!GetBits(pBsi->pgmscl2e, 1)) return false;
1024  if (pBsi->pgmscl2e)
1025  {
1026  if (!GetBits(pBsi->pgmscl2, 6)) return false;
1027  }
1028  }
1029  if (!GetBits(pBsi->extpgmscle, 1)) return false;
1030  if (pBsi->extpgmscle)
1031  {
1032  if (!GetBits(pBsi->extpgmscl, 6)) return false;
1033  }
1034  if (!GetBits(pBsi->mixdef, 2)) return false;
1035  if (pBsi->mixdef == 0x1) /* mixing option 2 */
1036  {
1037  if (!GetBits(pBsi->premixcmpsel, 1)) return false;
1038  if (!GetBits(pBsi->drcsrc, 1)) return false;
1039  if (!GetBits(pBsi->premixcmpscl, 3)) return false;
1040  }
1041  else if (pBsi->mixdef == 0x2) /* mixing option 3 */
1042  {
1043  if (!GetBits(pBsi->mixdata, 12)) return false;
1044  }
1045  else if (pBsi->mixdef == 0x3) /* mixing option 4 */
1046  {
1047  if (!GetBits(pBsi->mixdeflen, 5)) return false;
1048  if (!GetBits(pBsi->mixdata2e, 1)) return false;
1049  if (pBsi->mixdata2e)
1050  {
1051  if (!GetBits(pBsi->premixcmpsel, 1)) return false;
1052  if (!GetBits(pBsi->drcsrc, 1)) return false;
1053  if (!GetBits(pBsi->premixcmpscl, 3)) return false;
1054  if (!GetBits(pBsi->extpgmlscle, 1)) return false;
1055  if (pBsi->extpgmlscle)
1056  {
1057  if (!GetBits(pBsi->extpgmlscl, 4)) return false;
1058  }
1059  if (!GetBits(pBsi->extpgmcscle, 1)) return false;
1060  if (pBsi->extpgmcscle)
1061  {
1062  if (!GetBits(pBsi->extpgmcscl, 4)) return false;
1063  }
1064  if (!GetBits(pBsi->extpgmrscle, 1)) return false;
1065  if (pBsi->extpgmrscle)
1066  {
1067  if (!GetBits(pBsi->extpgmrscl, 4)) return false;
1068  }
1069  if (!GetBits(pBsi->extpgmlsscle, 1)) return false;
1070  if (pBsi->extpgmlsscle)
1071  {
1072  if (!GetBits(pBsi->extpgmlsscl, 4)) return false;
1073  }
1074  if (!GetBits(pBsi->extpgmrsscle, 1)) return false;
1075  if (pBsi->extpgmrsscle)
1076  {
1077  if (!GetBits(pBsi->extpgmrsscl, 4)) return false;
1078  }
1079  if (!GetBits(pBsi->extpgmlfescle, 1)) return false;
1080  if (pBsi->extpgmlfescle)
1081  {
1082  if (!GetBits(pBsi->extpgmlfescl, 4)) return false;
1083  }
1084  if (!GetBits(pBsi->dmixscle, 1)) return false;
1085  if (pBsi->dmixscle)
1086  {
1087  if (!GetBits(pBsi->dmixscl, 4)) return false;
1088  }
1089  if (!GetBits(pBsi->addche, 1)) return false;
1090  if (pBsi->addche)
1091  {
1092  if (!GetBits(pBsi->extpgmaux1scle, 1)) return false;
1093  if (pBsi->extpgmaux1scle)
1094  {
1095  if (!GetBits(pBsi->extpgmaux1scl, 4)) return false;
1096  }
1097  if (!GetBits(pBsi->extpgmaux2scle, 1)) return false;
1098  if (pBsi->extpgmaux2scle)
1099  {
1100  if (!GetBits(pBsi->extpgmaux2scl, 4)) return false;
1101  }
1102  }
1103  }
1104  if (!GetBits(pBsi->mixdata3e, 1)) return false;
1105  if (pBsi->mixdata3e)
1106  {
1107  if (!GetBits(pBsi->spchdat, 5)) return false;
1108  if (!GetBits(pBsi->addspchdate, 1)) return false;
1109  if (pBsi->addspchdate)
1110  {
1111  if (!GetBits(pBsi->spchdat1, 5)) return false;
1112  if (!GetBits(pBsi->spchan1att, 2)) return false;
1113  if (!GetBits(pBsi->addspchdat1e, 1)) return false;
1114  if (pBsi->addspdat1e)
1115  {
1116  if (!GetBits(pBsi->spchdat2, 5)) return false;
1117  if (!GetBits(pBsi->spchan2att, 3)) return false;
1118  }
1119  }
1120  }
1121  {
1122  uint32_t data;
1123  uint32_t size = 8 * (pBsi->mixdeflen + 2);
1124  size = (size + 7) / 8 * 8;
1125  uint32_t index = 0;
1126  while (size > 0)
1127  {
1128  if (!GetBits(data, (size > 8)? 8 : size)) return false;
1129  pBsi->mixdatabuffer[index++] = (uint8_t)data;
1130  size = size - 8;
1131  }
1132  }
1133  }
1134  if (pBsi->acmod < 0x2) /* if mono or dual mono source */
1135  {
1136  if (!GetBits(pBsi->paninfoe, 1)) return false;
1137  if (pBsi->paninfoe)
1138  {
1139  if (!GetBits(pBsi->panmean, 8)) return false;
1140  if (!GetBits(pBsi->paninfo, 6)) return false;
1141  }
1142  if (pBsi->acmod == 0x0) /* if 1+1 mode (dual mono - some items need a second value) */
1143  {
1144  if (!GetBits(pBsi->paninfo2e, 1)) return false;
1145  if (pBsi->paninfo2e)
1146  {
1147  if (!GetBits(pBsi->panmean2, 8)) return false;
1148  if (!GetBits(pBsi->paninfo2, 6)) return false;
1149  }
1150  }
1151  }
1152  if (!GetBits(pBsi->frmmixcfginfoe, 1)) return false;
1153  if (pBsi->frmmixcfginfoe) /* mixing configuration information */
1154  {
1155  if (pBsi->numblkscod == 0x0)
1156  {
1157  if (!GetBits(pBsi->blkmixcfginfo[0], 5)) return false;
1158  }
1159  else
1160  {
1161  uint32_t blk;
1162  uint32_t numblk;
1163  if (pBsi->numblkscod == 0x1)
1164  numblk = 2;
1165  else if (pBsi->numblkscod == 0x2)
1166  numblk = 3;
1167  else if (pBsi->numblkscod == 0x3)
1168  numblk = 6;
1169  else
1170  return false;
1171  for(blk = 0; blk < numblk; blk++)
1172  {
1173  if (!GetBits(pBsi->blkmixcfginfoe, 1)) return false;
1174  if (pBsi->blkmixcfginfoe)
1175  {
1176  if (!GetBits(pBsi->blkmixcfginfo[blk], 5)) return false;
1177  }
1178  }
1179  }
1180  }
1181  }
1182  }
1183  if (!GetBits(pBsi->infomdate, 1)) return false;
1184  if (pBsi->infomdate) /* informational metadata */
1185  {
1186  if (!GetBits(pBsi->bsmod, 3)) return false;
1187  if (!GetBits(pBsi->copyrightb, 1)) return false;
1188  if (!GetBits(pBsi->origbs, 1)) return false;
1189  if (pBsi->acmod == 0x2) /* if in 2/0 mode */
1190  {
1191  if (!GetBits(pBsi->dsurmod, 2)) return false;
1192  if (!GetBits(pBsi->dheadphonmod, 2)) return false;
1193  }
1194  if (pBsi->acmod >= 0x6) /* if both surround channels exist */
1195  {
1196  if (!GetBits(pBsi->dsurexmod, 2)) return false;
1197  }
1198  if (!GetBits(pBsi->audprodie, 1)) return false;
1199  if (pBsi->audprodie)
1200  {
1201  if (!GetBits(pBsi->mixlevel, 5)) return false;
1202  if (!GetBits(pBsi->roomtyp, 2)) return false;
1203  if (!GetBits(pBsi->adconvtyp, 1)) return false;
1204  }
1205  if (pBsi->acmod == 0x0) /* if 1+1 mode (dual mono, so some items need a second value) */
1206  {
1207  if (!GetBits(pBsi->audprodi2e, 1)) return false;
1208  if (pBsi->audprodi2e)
1209  {
1210  if (!GetBits(pBsi->mixlevel2, 5)) return false;
1211  if (!GetBits(pBsi->roomtyp2, 2)) return false;
1212  if (!GetBits(pBsi->adconvtyp2, 1)) return false;
1213  }
1214  }
1215  if (pBsi->fscod < 0x3) /* if not half sample rate */
1216  {
1217  if (!GetBits(pBsi->sourcefscod, 1)) return false;
1218  }
1219  }
1220  if ((pBsi->strmtyp == 0x0) && (pBsi->numblkscod != 0x3))
1221  {
1222  if (!GetBits(pBsi->convsync, 1)) return false;
1223  }
1224  if (pBsi->strmtyp == 0x2) /* if bit stream converted from AC-3 */
1225  {
1226  if (pBsi->numblkscod == 0x3) /* 6 blocks per syncframe */
1227  {
1228  pBsi->blkid = 1;
1229  }
1230  else
1231  {
1232  if (!GetBits(pBsi->blkid, 1)) return false;
1233  }
1234  if (pBsi->blkid)
1235  {
1236  if (!GetBits(pBsi->frmsizecod, 6)) return false;
1237  }
1238  }
1239  if (!GetBits(pBsi->addbsie, 1)) return false;
1240  if (pBsi->addbsie)
1241  {
1242  if (!GetBits(pBsi->addbsil, 6)) return false;
1243  {
1244  uint32_t data;
1245  uint32_t size = 8 * (pBsi->addbsil + 1);
1246  uint32_t index = 0;
1247  while (size > 0)
1248  {
1249  if (!GetBits(data, 8)) return false;
1250  pBsi->addbsibuffer[index++] = (uint8_t)data;
1251  size = size - 8;
1252  }
1253  }
1254  }
1255 
1256  return true;
1257 }
1258 
1259 void NTV2DolbyPlayer::SetBitBuffer(uint8_t * pBuffer, uint32_t size)
1260 {
1261  mBitBuffer = pBuffer;
1262  mBitSize = size;
1263  mBitIndex = 8;
1264 }
1265 
1266 bool NTV2DolbyPlayer::GetBits(uint32_t & data, uint32_t bits)
1267 {
1268  uint32_t cb;
1269  static uint8_t bitMask[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
1270 
1271  data = 0;
1272  while (bits > 0)
1273  {
1274  if (mBitSize == 0)
1275  return false;
1276 
1277  if (mBitIndex == 0)
1278  {
1279  mBitBuffer++;
1280  mBitSize--;
1281  mBitIndex = 8;
1282  }
1283 
1284  cb = bits;
1285  if (cb > mBitIndex)
1286  cb = mBitIndex;
1287  bits -= cb;
1288  mBitIndex -= cb;
1289  data |= ((*mBitBuffer >> mBitIndex) & bitMask[cb]) << bits;
1290  }
1291 
1292  return true;
1293 }
1294 
1295 #else // DOLBY_FULL_PARSER
1296 // NOTE: The code under this #else is out of date and not maintaiend
1297 uint32_t NTV2DolbyPlayer::AddDolby (ULWord * pInAudioBuffer)
1298 {
1301  ULWord numChannels (0);
1302  ULWord sampleOffset(0);
1303 
1304  mDevice.GetFrameRate (frameRate, mOutputChannel);
1305  mDevice.GetAudioRate (audioRate, mAudioSystem);
1306  mDevice.GetNumberAudioChannels (numChannels, mAudioSystem);
1307  const ULWord numSamples (::GetAudioSamplesPerFrame (frameRate, audioRate, mCurrentFrame));
1308 
1309  if ((mConfig.fDolbyFile == NULL) || (mDolbyBuffer == NULL))
1310  goto silence;
1311 
1312  // Generate the samples for this frame
1313  while (sampleOffset < numSamples)
1314  {
1315  // Time for a new IEC61937 burst
1316  if (mBurstIndex >= mBurstSamples)
1317  mBurstIndex = 0;
1318 
1319  // Get a new Dolby Digital Plus sync frame
1320  if (mBurstIndex == 0)
1321  {
1322  // Read the sync word (all big endian)
1323  uint32_t bytes = mConfig.fDolbyFile->Read((uint8_t*)(&mDolbyBuffer[0]), 2);
1324  if (bytes != 2)
1325  {
1326  // Try to loop
1327  mConfig.fDolbyFile->Seek(0, eAJASeekSet);
1328  bytes = mConfig.fDolbyFile->Read((uint8_t*)(&mDolbyBuffer[0]), 2);
1329  if (bytes != 2)
1330  goto silence;
1331  }
1332 
1333  // Check sync word
1334  if ((mDolbyBuffer[0] != 0x7705) &&
1335  (mDolbyBuffer[0] != 0x770b))
1336  goto silence;
1337 
1338  // Read more of the sync frame header
1339  bytes = mConfig.fDolbyFile->Read((uint8_t*)(&mDolbyBuffer[1]), 4);
1340  if (bytes != 4)
1341  goto silence;
1342 
1343  // Get frame size - 16 bit words plus sync word
1344  uint32_t size = (uint32_t)mDolbyBuffer[1];
1345  size = (((size & 0x00ff) << 8) | ((size & 0xff00) >> 8)) + 1;
1346 
1347  // Read the rest of the sync frame
1348  uint32_t len = (size - 3) * 2;
1349  bytes = mConfig.fDolbyFile->Read((uint8_t*)(&mDolbyBuffer[3]), len);
1350  if (bytes != len)
1351  goto silence;
1352 
1353  // Good frame
1354  mBurstOffset = 0;
1355  mBurstSize = size;
1356  }
1357 
1358  // Add the Dolby data to the audio stream
1359  if (mBurstOffset < mBurstSize)
1360  {
1361  uint32_t data0 = 0;
1362  uint32_t data1 = 0;
1363  if (mBurstIndex == 0)
1364  {
1365  // Add IEC61937 burst preamble
1366  data0 = 0xf872; // Sync stuff
1367  data1 = 0x4e1f;
1368  }
1369  else if (mBurstIndex == 1)
1370  {
1371  // Add more IEC61937 burst preamble
1372  data0 = 0x0015; // This is Dolby
1373  data1 = mBurstSize * 2; // Data size in bytes
1374  }
1375  else
1376  {
1377  // Add sync frame data
1378  data0 = (uint32_t)(mDolbyBuffer[mBurstOffset]);
1379  data0 = ((data0 & 0x00ff) << 8) | ((data0 & 0xff00) >> 8);
1380  mBurstOffset++;
1381  data1 = (uint32_t)(mDolbyBuffer[mBurstOffset]);
1382  data1 = ((data1 & 0x00ff) << 8) | ((data1 & 0xff00) >> 8);
1383  mBurstOffset++;
1384  }
1385 
1386  // Write data into 16 msbs of all audio channel pairs
1387  data0 <<= 16;
1388  data1 <<= 16;
1389  for (ULWord i = 0; i < numChannels; i += 2)
1390  {
1391  pInAudioBuffer[sampleOffset * numChannels + i] = data0;
1392  pInAudioBuffer[sampleOffset * numChannels + i + 1] = data1;
1393  }
1394  }
1395  else
1396  {
1397  // Pad samples out to burst size
1398  for (ULWord i = 0; i < numChannels; i++)
1399  {
1400  pInAudioBuffer[sampleOffset * numChannels + i] = 0;
1401  }
1402  }
1403 
1404  sampleOffset++;
1405  mBurstIndex++;
1406  }
1407 
1408  return numSamples * numChannels * 4;
1409 
1410 silence:
1411  // Output silence when done with file
1412  memset(&pInAudioBuffer[sampleOffset * numChannels], 0, (numSamples - sampleOffset) * numChannels * 4);
1413  return numSamples * numChannels * 4;
1414 }
1415 #endif // else DOLBY_FULL_PARSER
1416 
1417 AJALabelValuePairs DolbyPlayerConfig::Get (const bool inCompact) const
1418 {
1419  AJALabelValuePairs result (PlayerConfig::Get(inCompact));
1420  AJASystemInfo::append (result, "Dolby Playback File", fDolbyFilePath.empty() ? "---" : fDolbyFilePath);
1421  AJASystemInfo::append (result, "Audio Data Ramp", fDoRamp ? "Yes" : "No");
1422  return result;
1423 }
1424 
1425 std::ostream & operator << (std::ostream & ioStrm, const DolbyPlayerConfig & inObj)
1426 {
1427  ioStrm << AJASystemInfo::ToString(inObj.Get());
1428  return ioStrm;
1429 }
ULWord GetVideoWriteSize(ULWord inPageSize=4096UL) const
bool SetOutputTimeCodes(const NTV2TimeCodes &inValues)
Intended for playout, replaces the contents of my acOutputTimeCodes member.
AJALabelValuePairs Get(const bool inCompact=(0)) const
Renders a human-readable representation of me.
NTV2FrameRate GetNTV2FrameRateFromVideoFormat(const NTV2VideoFormat inVideoFormat)
Definition: ntv2utils.cpp:3630
double FramesToSeconds(int64_t frames) const
Definition: timebase.cpp:197
virtual bool SetTaskMode(const NTV2TaskMode inMode)
Sets the device&#39;s task mode.
virtual bool GetDolbyFrame(uint16_t *pInDolbyBuffer, uint32_t &numSamples)
Get a dolby audio frame from the input file.
static const bool BUFFER_PAGE_ALIGNED((!(0)))
static void ProducerThreadStatic(AJAThread *pThread, void *pContext)
This is the producer thread&#39;s static callback function that gets called when the producer thread star...
#define NTV2_IS_VALID_TASK_MODE(__m__)
virtual bool SetHDMIOutAudioSource2Channel(const NTV2AudioChannelPair inNewValue, const NTV2AudioSystem inAudioSystem=NTV2_AUDIOSYSTEM_1, const NTV2Channel inWhichHDMIOut=NTV2_CHANNEL1)
Sets the HDMI output&#39;s 2-channel audio source.
Definition: ntv2audio.cpp:868
virtual bool SetAudioLoopBack(const NTV2AudioLoopBack inMode, const NTV2AudioSystem inAudioSystem=NTV2_AUDIOSYSTEM_1)
Enables or disables NTV2AudioLoopBack mode for the given NTV2AudioSystem.
Definition: ntv2audio.cpp:300
Specifies the device&#39;s internal clock.
Definition: ntv2enums.h:1459
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...
#define NULL
virtual AJAStatus Run(void)
Runs me.
AJAStatus Add(FrameDataPtr pInFrameData)
Appends a new frame buffer to me, increasing my frame storage capacity by one frame.
The NTV2 test pattern generator.
#define DEC0N(__x__, __n__)
static const double gAmplitudes[]
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 bool IsDeviceReady(const bool inCheckValid=(0))
static uint64_t GetPid()
Definition: process.cpp:35
#define AJA_FAILURE(_status_)
Definition: types.h:373
#define PLINFO(_xpr_)
UWord count(void) const
Definition: ntv2utils.h:984
Declares the NTV2TestPatternGen class.
AJALabelValuePairs Get(const bool inCompact=(0)) const
virtual bool GetAudioRate(NTV2AudioRate &outRate, const NTV2AudioSystem inAudioSystem=NTV2_AUDIOSYSTEM_1)
Returns the current NTV2AudioRate for the given Audio System.
Definition: ntv2audio.cpp:226
virtual AJAStatus SetUpHostBuffers(void)
Sets up my host video & audio buffers.
virtual bool DMAWriteFrame(const ULWord inFrameNumber, const ULWord *pInFrameBuffer, const ULWord inByteCount)
Transfers a single frame from the host to the AJA device.
Definition: ntv2dma.cpp:65
virtual AJAStatus SetUpAudio(void)
Performs all audio setup.
std::vector< AJALabelValuePair > AJALabelValuePairs
An ordered sequence of label/value pairs.
Definition: info.h:71
virtual AJAStatus SetPriority(AJAThreadPriority priority)
Definition: thread.cpp:133
FrameDataPtr StartConsumeNextBuffer(void)
The thread that&#39;s responsible for processing incoming frames – the consumer – calls this function t...
#define TCDBG(_expr_)
ULWord GetTotalBytes(void) const
virtual AJAStatus SetUpVideo(void)
Performs all video setup.
I am similar to NTV2Player, but I demonstrate how to play/output 8 channels of audio tone (or ramp da...
bool CanAcceptMoreOutputFrames(void) const
UWord firstFrame(void) const
Definition: ntv2utils.h:985
Definition: json.hpp:5362
virtual bool SetAudioRate(const NTV2AudioRate inRate, const NTV2AudioSystem inAudioSystem=NTV2_AUDIOSYSTEM_1)
Sets the NTV2AudioRate for the given Audio System.
Definition: ntv2audio.cpp:205
virtual AJAStatus Start()
Definition: thread.cpp:91
void EndConsumeNextBuffer(void)
The consumer thread calls this function to signal that it has finished processing the frame it obtain...
Declares common audio macros and structs used in the SDK.
std::string fDeviceSpec
The AJA device to use.
#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.
virtual class DeviceCapabilities & features(void)
Definition: ntv2card.h:148
virtual bool SetMultiFormatMode(const bool inEnable)
Enables or disables multi-format (per channel) device operation. If enabled, each device channel can ...
virtual bool ClearRouting(void)
Removes all existing signal path connections between any and all widgets on the AJA device...
Declares the AJATimeBase class.
This struct replaces the old RP188_STRUCT.
virtual bool IsRemote(void) const
ULWord numPixels
Width – total number of pixels per line.
virtual bool Active()
Definition: thread.cpp:116
virtual bool GetTaskMode(NTV2TaskMode &outMode)
Retrieves the device&#39;s current task mode.
bool WithAudio(void) const
virtual bool SetHDMIOutAudioRate(const NTV2AudioRate inNewValue, const NTV2Channel inWhichHDMIOut=NTV2_CHANNEL1)
Sets the HDMI output&#39;s audio rate.
Definition: ntv2audio.cpp:1009
static AJA_PixelFormat GetAJAPixelFormat(const NTV2PixelFormat inFormat)
NTV2FrameRate
Identifies a particular video frame rate.
Definition: ntv2enums.h:412
virtual bool DrawTestPattern(const std::string &inTPName, const NTV2FormatDescriptor &inFormatDesc, NTV2Buffer &inBuffer)
Renders the given test pattern or color into a host raster buffer.
static const ULWord gNumFrequencies(sizeof(gFrequencies)/sizeof(double))
virtual std::string GetDescription(void) const
Definition: ntv2card.cpp:139
static TimecodeFormat NTV2FrameRate2TimecodeFormat(const NTV2FrameRate inFrameRate)
NTV2InputXptID GetOutputDestInputXpt(const NTV2OutputDestination inOutputDest, const bool inIsSDI_DS2=false, const UWord inHDMI_Quadrant=99)
virtual bool IsOpen(void) const
virtual void ConsumeFrames(void)
My consumer thread that repeatedly plays frames using AutoCirculate (until quit). ...
virtual bool UnsubscribeOutputVerticalEvent(const NTV2Channel inChannel)
Unregisters me so I&#39;m no longer notified when an output VBI is signaled on the given output channel...
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...
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
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
virtual void ProduceFrames(void)
My producer thread that repeatedly produces video frames.
virtual uint32_t AddDolby(NTV2FrameData &inFrameData)
Inserts dolby audio into the given NTV2FrameData&#39;s audio buffer.
Describes a video frame for a given video standard or format and pixel format, including the total nu...
AJAStatus Open(const std::string &fileName, const int flags, const int properties)
Definition: file_io.cpp:201
NTV2DolbyPlayer(const DolbyPlayerConfig &inConfigData)
Constructs me using the given configuration settings.
static void Sleep(const int32_t inMilliseconds)
Suspends execution of the current thread for a given number of milliseconds.
Definition: systemtime.cpp:284
This selects audio channels 1 and 2 (Group 1 channels 1 and 2)
Definition: ntv2enums.h:3133
NTV2PixelFormat fPixelFormat
The pixel format to use.
2: OEM (recommended): device configured by client application(s) with some driver involvement...
Configures an NTV2DolbyPlayer instance.
std::vector< std::string > NTV2StringList
Definition: ntv2utils.h:1155
static const uint32_t AUDIOBYTES_MAX_48K(201 *1024)
The maximum number of bytes of 48KHz audio that can be transferred for a single frame. Worst case, assuming 16 channels of audio (max), 4 bytes per sample, and 67 msec per frame (assuming the lowest possible frame rate of 14.98 fps)... 48,000 samples per second requires 3,204 samples x 4 bytes/sample x 16 = 205,056 bytes 201K will suffice, with 768 bytes to spare.
#define PLNOTE(_xpr_)
NTV2VideoFormat fVideoFormat
The video format to use.
std::string NTV2VideoFormatToString(const NTV2VideoFormat inValue, const bool inUseFrameRate=false)
Definition: ntv2utils.cpp:6746
Embeds silence (zeroes) into the data stream.
Definition: ntv2enums.h:2030
virtual bool SetVANCShiftMode(NTV2Channel inChannel, NTV2VANCDataShiftMode inMode)
Enables or disables the "VANC Shift Mode" feature for the given channel.
bool IsOpen()
Definition: file_io.cpp:317
virtual void Quit(void)
Gracefully stops me from running.
virtual uint32_t AddTone(NTV2FrameData &inFrameData)
Inserts audio tone (based on my current tone frequency) into the given NTV2FrameData&#39;s audio buffer...
AJA_EXPORT bool RenderTimeCodeFont(AJA_PixelFormat pixelFormat, uint32_t numPixels, uint32_t numLines)
virtual bool DMABufferUnlockAll()
Unlocks all previously-locked buffers used for DMA transfers.
Definition: ntv2dma.cpp:457
Specifies the PTP source on SFP 1.
Definition: ntv2enums.h:1468
Specifies channel or FrameStore 2 (or the 2nd item).
Definition: ntv2enums.h:1360
virtual void StartProducerThread(void)
Starts my producer thread.
AJAStatus Seek(const int64_t distance, const AJAFileSetFlag flag) const
Definition: file_io.cpp:545
virtual AJAStatus SetUpTestPatternBuffers(void)
Creates my test pattern buffers.
Declares the CNTV2DeviceScanner class.
std::ostream & operator<<(std::ostream &ioStrm, const DolbyPlayerConfig &inObj)
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...
bool AddAudioTone(ULWord &outNumBytesWritten, NTV2Buffer &inAudioBuffer, ULWord &inOutCurrentSample, const ULWord inNumSamples, const double inSampleRate, const double inAmplitude, const double inFrequency, const ULWord inNumBits, const bool inByteSwap, const ULWord inNumChannels)
Fills the given buffer with 32-bit (ULWord) audio tone samples.
Definition: ntv2utils.cpp:4413
Declares the AJAProcess class.
std::string fDolbyFilePath
Optional path to Dolby audio source file.
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.).
#define PLFAIL(_xpr_)
virtual bool Connect(const NTV2InputCrosspointID inInputXpt, const NTV2OutputCrosspointID inOutputXpt, const bool inValidate=(0))
Connects the given widget signal input (sink) to the given widget signal output (source).
bool fDoMultiFormat
If true, enable device-sharing; otherwise take exclusive control of device.
static NTV2TestPatternNames getTestPatternNames(void)
uint16_t GetEndFrame(void) const
NTV2Buffer & AudioBuffer(void)
bool IsRGBFormat(const NTV2FrameBufferFormat format)
Definition: ntv2utils.cpp:5410
This object specifies the information that will be transferred to or from the AJA device in the CNTV2...
virtual bool SetAudioBufferSize(const NTV2AudioBufferSize inValue, const NTV2AudioSystem inAudioSystem=NTV2_AUDIOSYSTEM_1)
Changes the size of the audio buffer that is used for a given Audio System in the AJA device...
Definition: ntv2audio.cpp:249
virtual bool GetNumberAudioChannels(ULWord &outNumChannels, const NTV2AudioSystem inAudioSystem=NTV2_AUDIOSYSTEM_1)
Returns the current number of audio channels being captured or played by a given Audio System on the ...
Definition: ntv2audio.cpp:180
bool fDoRamp
If true, use audio ramp pattern instead of tone.
Header file for NTV2DolbyPlayer demonstration class.
AJAStatus Close()
Definition: file_io.cpp:281
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__)
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
virtual bool WaitForOutputVerticalInterrupt(const NTV2Channel inChannel=NTV2_CHANNEL1, UWord inRepeatCount=1)
Efficiently sleeps the calling thread/process until the next one or more field (interlaced video) or ...
NTV2Channel fOutputChannel
The device channel to use.
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))
ULWord numLines
Height – total number of lines.
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...
double GetAudioSamplesPerSecond(const NTV2AudioRate inAudioRate)
Returns the audio sample rate as a number of audio samples per second.
Definition: ntv2utils.cpp:3303
virtual bool ParseBSI(uint16_t *pInDolbyBuffer, uint32_t numSamples, NTV2DolbyBSI *pBsi)
Parse the dolby audio bit stream information block.
virtual void StartConsumerThread(void)
Starts my consumer thread.
mFormatDesc
Definition: ntv2vcam.cpp:942
This is returned from the CNTV2Card::AutoCirculateGetStatus function.
NTV2OutputXptID GetFrameStoreOutputXptFromChannel(const NTV2Channel inFrameStore, const bool inIsRGB=false, const bool inIs425=false)
virtual bool SetHDMIOutAudioFormat(const NTV2AudioFormat inNewValue, const NTV2Channel inWhichHDMIOut=NTV2_CHANNEL1)
Sets the HDMI output&#39;s audio format.
Definition: ntv2audio.cpp:1031
uint16_t UWord
Definition: ajatypes.h:221
ULWord GetAudioSamplesPerFrame(const NTV2FrameRate inFrameRate, const NTV2AudioRate inAudioRate, ULWord inCadenceFrame=0, bool inIsSMPTE372Enabled=false)
Returns the number of audio samples for a given video frame rate, audio sample rate, and frame number. This is useful since AJA devices use fixed audio sample rates (typically 48KHz), and some video frame rates will necessarily result in some frames having more audio samples than others.
Definition: ntv2utils.cpp:2889
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...
virtual uint32_t AddRamp(NTV2FrameData &inFrameData)
Inserts audio test ramp into the given NTV2FrameData&#39;s audio buffer.
virtual bool RouteOutputSignal(void)
Performs all widget/signal routing for playout.
bool CanDoFrameBufferFormat(const NTV2PixelFormat inPF)
virtual bool SubscribeOutputVerticalEvent(const NTV2Channel inChannel)
Causes me to be notified when an output vertical blanking interrupt is generated for the given output...
virtual bool AutoCirculateInitForOutput(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 playout, designating a contiguous block of frame buffers on the...
#define xHEX0N(__x__, __n__)
TimecodeFormat
Definition: ntv2rp188.h:22
static void ConsumerThreadStatic(AJAThread *pThread, void *pContext)
This is the consumer thread&#39;s static callback function that gets called when the consumer thread star...
virtual AJAStatus Init(void)
Initializes me and prepares me to Run.
virtual bool GetFrameRate(NTV2FrameRate &outValue, NTV2Channel inChannel=NTV2_CHANNEL1)
Returns the AJA device&#39;s currently configured frame rate via its "value" parameter.
static AJA_FrameRate GetAJAFrameRate(const NTV2FrameRate inFrameRate)
AJA_EXPORT bool BurnTimeCode(void *pBaseVideoAddress, const std::string &inTimeCodeStr, const uint32_t inYPercent)
I encapsulate the video, audio and anc host buffers used in the AutoCirculate demos. I&#39;m a more modern version of the AVDataBuffer.
NTV2AudioSystem NTV2ChannelToAudioSystem(const NTV2Channel inChannel)
Converts the given NTV2Channel value into its equivalent NTV2AudioSystem.
Definition: ntv2utils.cpp:4869
virtual void GetACStatus(AUTOCIRCULATE_STATUS &outStatus)
Provides status information about my output (playout) process.
NTV2ACFrameRange fFrames
AutoCirculate frame count or range.
uint32_t Read(uint8_t *pBuffer, const uint32_t length)
Definition: file_io.cpp:328
bool GetRP188Reg(RP188_STRUCT &outRP188) const
Definition: ntv2rp188.cpp:1241
NTV2AudioRate
Definition: ntv2enums.h:1928
virtual bool GetBits(uint32_t &data, uint32_t inBitCount)
Retreive the specified number of bits from the bitstream buffer.
static size_t SetDefaultPageSize(void)
#define PLWARN(_xpr_)
bool CanDoVideoFormat(const NTV2VideoFormat inVF)
UWord lastFrame(void) const
Definition: ntv2utils.h:986
#define AUTOCIRCULATE_WITH_RP188
Use this to AutoCirculate with RP188.
const char * NTV2FrameBufferFormatString(NTV2FrameBufferFormat fmt)
Definition: ntv2debug.cpp:214
bool GetRP188Str(std::string &sRP188) const
Definition: ntv2rp188.cpp:918
static const uint32_t AUDIOBYTES_MAX_192K(824 *1024)
The maximum number of bytes of 192KHz audio that can be transferred for a single frame. Worst case, assuming 8 channels of audio (max), 4 bytes per sample, and 67 msec per frame (assuming the lowest possible frame rate of 14.98 fps)... 96,000 samples per second requires 12,864 samples x 4 bytes/sample x 16 = 823,296 bytes 824K will suffice.
bool WithVideo(void) const
bool SetAudioBuffer(ULWord *pInAudioBuffer, const ULWord inAudioByteCount)
Sets my audio buffer for use in a subsequent call to CNTV2Card::AutoCirculateTransfer.
static const double gFrequencies[]
virtual bool SetVANCMode(const NTV2VANCMode inVancMode, const NTV2Channel inChannel=NTV2_CHANNEL1)
Sets the VANC mode for the given FrameStore.
virtual bool SetNumberAudioChannels(const ULWord inNumChannels, const NTV2AudioSystem inAudioSystem=NTV2_AUDIOSYSTEM_1)
Sets the number of audio channels to be concurrently captured or played for a given Audio System on t...
Definition: ntv2audio.cpp:146
virtual void ToString(std::string &outAllLabelsAndValues) const
Answers with a multi-line string that contains the complete host system info table.
enum NTV2OutputCrosspointID NTV2OutputXptID
uint16_t GetStartFrame(void) const
This identifies the mode in which there are no VANC lines in the frame buffer.
Definition: ntv2enums.h:3799
virtual ~NTV2DolbyPlayer(void)
bool SetVideoBuffer(ULWord *pInVideoBuffer, const ULWord inVideoByteCount)
Sets my video buffer for use in a subsequent call to CNTV2Card::AutoCirculateTransfer.
virtual void SetBitBuffer(uint8_t *pBuffer, uint32_t size)
Set the bitstream buffer for bit retrieval.
virtual bool EnableChannel(const NTV2Channel inChannel)
Enables the given FrameStore.