AJA NTV2 SDK  18.0.0.2122
NTV2 SDK 18.0.0.2122
ntv2overlay.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
8 #include "ntv2overlay.h"
9 #include "ntv2devicescanner.h"
10 #include "ajabase/common/types.h"
11 #include "ajabase/system/atomic.h"
12 #include "ajabase/system/process.h"
15 #include <iostream>
16 
17 using namespace std;
18 
19 static const uint32_t kAppSignature (NTV2_FOURCC('O','v','r','l'));
20 
22 
23 
25  : mConfig (inConfig),
26  mPlayThread (AJAThread()),
27  mCaptureThread (AJAThread()),
29  mSavedTaskMode (NTV2_DISABLE_TASKS),
30  mInputPixFormat (inConfig.fPixelFormat),
31  mOutputPixFormat(NTV2_FBF_ARGB), // fixed to 8-bit ARGB
32  mGlobalQuit (false)
33 {
34  mAVCircularBuffer.SetAbortFlag(&mGlobalQuit); // Ring buffer abandons waits when mGlobalQuit goes true
35 } // constructor
36 
37 
39 {
40  Quit(); // Stop my capture and playout threads, then destroy them
45 } // destructor
46 
47 
48 void NTV2Overlay::Quit (void)
49 {
50  // Set the global 'quit' flag, and wait for the threads to go inactive...
51  mGlobalQuit = true;
52 
53  while (mPlayThread.Active())
54  AJATime::Sleep(10);
55 
56  while (mCaptureThread.Active())
57  AJATime::Sleep(10);
58 
59  if (!mConfig.fDoMultiFormat)
60  { // Release the device...
62  if (NTV2_IS_VALID_TASK_MODE(mSavedTaskMode))
63  mDevice.SetTaskMode(mSavedTaskMode); // Restore prior task mode
64  }
65 } // Quit
66 
67 
69 {
71 
72  // Open the device...
74  {cerr << "## ERROR: Device '" << mConfig.fDeviceSpec << "' not found" << endl; return AJA_STATUS_OPEN;}
75  if (!mDevice.IsDeviceReady(false))
76  {cerr << "## ERROR: Device '" << mDevice.GetDescription() << "' not ready" << endl; return AJA_STATUS_INITIALIZE;}
77  if (mDevice.GetDeviceID() != DEVICE_ID_KONAX)
78  cerr << "## WARNING: Device '" << mDevice.GetDescription() << "' is not KONA X" << endl;
79 
80  ULWord appSig(0);
81  int32_t appPID(0);
82  mDevice.GetStreamingApplication (appSig, appPID); // Who currently "owns" the device?
83  mDevice.GetTaskMode(mSavedTaskMode); // Save the current device state
84 
86  {
87  cerr << "## ERROR: Cannot acquire '" << mDevice.GetDescription() << "' because another app (pid " << appPID << ") owns it" << endl;
88  return AJA_STATUS_BUSY; // Some other app is using the device
89  }
90 
91  mDevice.SetTaskMode(NTV2_OEM_TASKS); // Force OEM tasks
92 
95 
96  // Enable/subscribe interrupts...
97  mConfig.fInputChannel = NTV2_CHANNEL1;
98  mConfig.fOutputChannel = NTV2_CHANNEL2;
102 
103  // Set up the overlay image...
104  status = SetupOverlayBug();
105  if (AJA_FAILURE(status))
106  return status;
107 
108  // Set up the video and audio...
109  status = SetupVideo();
110  if (AJA_FAILURE(status))
111  return status;
112  status = SetupAudio();
113  if (AJA_FAILURE(status))
114  return status;
115 
116  // Ready to go...
117  cerr << mConfig;
118  if (mDevice.IsRemote())
119  cerr << "Device Description: " << mDevice.GetDescription() << endl;
120  cerr << endl;
121 
122  CAPINFO("Configuration: " << mConfig); PLINFO("Configuration: " << mConfig);
123  return AJA_STATUS_SUCCESS;
124 } // Init
125 
126 
128 {
129  // Flip the input spigot to "receive" if necessary...
130  mDevice.SetSDITransmitEnable (NTV2_CHANNEL1, false);
131  mDevice.SetSDITransmitEnable (NTV2_CHANNEL2, true);
132 
134 
135  // Since this demo runs in E-to-E mode (thru Mixer/Keyer), reference must be tied to the input...
137 
138  mDevice.SetMultiFormatMode(true);
139 
140  mDevice.ClearRouting();
143 
144  // Set up Mixer...
145  const UWord mixerNum (0);
146  mDevice.SetMixerMode (mixerNum, NTV2MIXERMODE_MIX); // "mix" mode
147  mDevice.SetMixerFGMatteEnabled (mixerNum, false); // no FG matte
148  mDevice.SetMixerBGMatteEnabled (mixerNum, false); // no BG matte
149  mDevice.SetMixerCoefficient (mixerNum, 0x10000); // FG "bug" overlay full strength (initially)
150  mDevice.SetMixerFGInputControl (mixerNum, NTV2MIXERINPUTCONTROL_SHAPED); // respect FG alpha channel
151  mDevice.SetMixerBGInputControl (mixerNum, NTV2MIXERINPUTCONTROL_FULLRASTER); // BG uses full raster
152  mDevice.SetMixerVancOutputFromForeground (mixerNum, false); // false means "use BG VANC, not FG"
153  return AJA_STATUS_SUCCESS;
154 
155 } // SetupVideo
156 
158 {
160  {
162  mDevice.GetHDMIInputColor (cs2, ::NTV2InputSourceToChannel(mConfig.fInputSource));
163  return cs2 == NTV2_LHIHDMIColorSpaceRGB;
164  }
165  return false;
166 }
167 
169 {
170  const bool isCaptureBufferRGB(NTV2_IS_FBF_RGB(mConfig.fPixelFormat)), isSignalRGB(IsInputSignalRGB());
171  NTV2OutputXptID oXptInput (::GetInputSourceOutputXpt (mConfig.fInputSource, /*DS2*/false, isSignalRGB));
172 
173  // Connect input to Mixer1 background...
174  if (isSignalRGB)
175  { // Must convert to YUV...
176  mDevice.Connect(NTV2_XptCSC1VidInput, oXptInput, false); // input => CSC1
177  mDevice.Connect(NTV2_XptMixer1BGVidInput, NTV2_XptCSC1VidYUV, false); // CSC1Vid => Mix1BGVid
178  mDevice.Connect(NTV2_XptMixer1BGKeyInput, NTV2_XptCSC1KeyYUV, false); // CSC1Key => Mix1BGKey
179  }
180  else
181  {
182  mDevice.Connect(NTV2_XptMixer1BGVidInput, oXptInput, false); // input => Mix1BGVid
183  mDevice.Connect(NTV2_XptMixer1BGKeyInput, oXptInput, false); // input => Mix1BGKey
184  }
185 
186  // Connect input to FrameStore1...
187  if (isCaptureBufferRGB && !isSignalRGB)
188  {
189  mDevice.Connect(NTV2_XptCSC1VidInput, oXptInput, false); // input => CSC1
190  mDevice.Connect(NTV2_XptFrameBuffer1Input, NTV2_XptCSC1VidRGB, false); // CSC1RGB => FS1
191  }
192  else if (!isCaptureBufferRGB && isSignalRGB)
193  mDevice.Connect(NTV2_XptFrameBuffer1Input, NTV2_XptCSC1VidYUV, false); // CSC1YUV => FS1
194  else // isCaptureBufferRGB == isSignalRGB
195  mDevice.Connect(NTV2_XptFrameBuffer1Input, oXptInput, false); // input => FS1
196 } // RouteInputSignal
197 
199 {
200  // Connect FrameStore2 RGB output through CSC2 to Mixer1 foreground...
201  mDevice.Connect(NTV2_XptCSC2VidInput, NTV2_XptFrameBuffer2RGB, false); // FS2RGB => CSC2
202  mDevice.Connect(NTV2_XptMixer1FGVidInput, NTV2_XptCSC2VidYUV, false); // CSC2Vid => Mix1FGVid
203  mDevice.Connect(NTV2_XptMixer1FGKeyInput, NTV2_XptCSC2KeyYUV, false); // CSC2Key => Mix1FGKey
204 } // RouteOverlaySignal
205 
206 
208 {
209  // Connect Mixer1 output to SDI & HDMI outputs...
212 } // RouteOutputSignal
213 
214 
216 {
217  // Set up the output audio embedders...
218  NTV2AudioSystem audioSystem = NTV2_AUDIOSYSTEM_1;
219  mDevice.SetSDIOutputAudioSystem (NTV2_CHANNEL2, audioSystem);
221 
222  // Enable loopback for E-E mode (i.e. output whatever audio is in input signal)...
223  mDevice.SetAudioLoopBack (NTV2_AUDIO_LOOPBACK_OFF, audioSystem); // Don't turn loopback on until input signal is stable
224  return AJA_STATUS_SUCCESS;
225 
226 } // SetupAudio
227 
228 
230 {
231  if (!NTV2_IS_VALID_VIDEO_FORMAT(mVideoFormat))
232  return AJA_STATUS_NOINPUT;
233 
235 
236  // Allocate and add each NTV2FrameData to my circular buffer member variable...
237  const ULWord captureBufferSize (::GetVideoWriteSize (mVideoFormat, mInputPixFormat));
238  mBuffers.reserve(CIRCULAR_BUFFER_SIZE);
239  while (mBuffers.size() < CIRCULAR_BUFFER_SIZE)
240  {
241  mBuffers.push_back(NTV2FrameData()); // Make a new NTV2FrameData...
242  NTV2FrameData & frameData(mBuffers.back()); // ...and get a reference to it
243  // Allocate a page-aligned video buffer
244  if (!frameData.fVideoBuffer.Allocate(captureBufferSize, /*pageAlign?*/true))
245  return AJA_STATUS_MEMORY;
246  mAVCircularBuffer.Add(&frameData); // Add to my circular buffer
247  } // for each NTV2FrameData
248  return AJA_STATUS_SUCCESS;
249 } // SetupCaptureBuffers
250 
251 
253 {
254  mBuffers.clear();
255  mAVCircularBuffer.Clear();
256  return;
257 } // ReleaseCaptureBuffers
258 
259 // 4-byte-per-pixel DrawVLine -- draws a vertical line using the given pixel value and line thickness
260 static bool DrawVLine (NTV2Buffer & buf, const NTV2RasterInfo & info, const ULWord argbPixValue, const ULWord pixThickness,
261  const ULWord xPos, const ULWord yTop, const ULWord height)
262 {
263  for (ULWord y(yTop); y < (yTop + height); y++)
264  for (ULWord w(0); w < pixThickness; w++)
265  buf.U32(y * info.linePitch + xPos + w) = argbPixValue;
266  return true;
267 }
268 
269 // 4-byte-per-pixel DrawHLine -- draws a horizontal line using the given pixel value and line thickness
270 static bool DrawHLine (NTV2Buffer & buf, const NTV2RasterInfo & info, const ULWord argbPixValue, const ULWord vThickness,
271  const ULWord yPos, const ULWord xLeft, const ULWord width)
272 {
273  for (ULWord v(0); v < vThickness; v++)
274  for (ULWord x(xLeft); x < (xLeft + width); x++)
275  buf.U32((yPos + v) * info.linePitch + x) = argbPixValue;
276  return true;
277 }
278 
279 // 4-byte-per-pixel DrawBox -- draws a rectangle using the given pixel value and line thickness
280 static bool DrawBox (NTV2Buffer & buf, const NTV2RasterInfo & info, const ULWord argbPixValue, const UWord pixThickness,
281  const UWord topLeftX, const UWord topLeftY, const ULWord width, const ULWord height)
282 {
283  /*T*/DrawVLine (buf, info, argbPixValue, pixThickness, /*xPos*/topLeftX, /*yTop */topLeftY, /*hght*/height);
284  /*B*/DrawVLine (buf, info, argbPixValue, pixThickness, /*xPos*/topLeftX + width - pixThickness, /*yTop */topLeftY, /*hght*/height);
285  /*L*/DrawHLine (buf, info, argbPixValue, pixThickness, /*yPos*/topLeftY, /*xLeft*/topLeftX, /*wdth*/width);
286  /*R*/DrawHLine (buf, info, argbPixValue, pixThickness, /*yPos*/topLeftY + height - pixThickness, /*xLeft*/topLeftX, /*wdth*/width);
287  return true;
288 }
289 
291 {
292  static const ULWord sOpaqueRed(0xFFFF0000), sOpaqueBlue(0xFF0000FF), sOpaqueGreen(0xFF008000),
293  sOpaqueWhite(0xFFFFFFFF), sOpaqueBlack(0xFF000000);
294 
295  // The overlay "bug" is a 256x256 pixel raster image composed of four opaque rectangles of
296  // varying color and diminishing size, each set inside each other, centered in the raster.
297  // All space between them is transparent to reveal the background video. The line thickness
298  // of each rectangle is the same: 1/16th the width or height of the raster. The pixel format
299  // is NTV2_FBF_ARGB, which all devices handle, and easy to traverse at 4-bytes-per-pixel.
300  static const vector<ULWord> sColors = {sOpaqueWhite, sOpaqueRed, sOpaqueBlue, sOpaqueGreen};
301  const ULWord hght(256), wdth(256), thickness(wdth/16);
302 
303  // The overlay bug's NTV2RasterInfo must be hacked together manually:
304  mBugRasterInfo = NTV2RasterInfo (hght, wdth, /*U32s/line*/wdth, /*1stAct*/0, /*lumaBits*/0, /*chromaBits*/8, /*alphaBits*/8);
305 
306  // Allocate the overlay bug's host buffer:
307  if (!mBug.Allocate(mBugRasterInfo.GetTotalBytes()))
308  return AJA_STATUS_MEMORY;
309 
310  // Draw the rectangles into the mBug buffer...
311  for (ULWord n(0); n < ULWord(sColors.size()); n++)
312  DrawBox (mBug, mBugRasterInfo, sColors.at(n), thickness, /*topLeftX*/n*2*thickness, /*topLeftY*/n*2*thickness, wdth-2*n*2*thickness, hght-2*n*2*thickness);
313 
314  // Draw a hatch mark in the middle of it...
315  /*V*/DrawVLine (mBug, mBugRasterInfo, sOpaqueBlack, /*thickness*/1, /*xPos*/wdth/2, /*yTop */hght/2-thickness, /*hght*/2*thickness);
316  /*H*/DrawHLine (mBug, mBugRasterInfo, sOpaqueBlack, /*thickness*/1, /*yPos*/hght/2, /*xLeft*/wdth/2-thickness, /*wdth*/2*thickness);
317  return AJA_STATUS_SUCCESS;
318 
319 } // SetupOverlayBug
320 
322 {
324  ULWord numConsecutiveFrames(0), MIN_NUM_CONSECUTIVE_FRAMES(6);
325 
326  // Detection loop:
327  while (result == NTV2_FORMAT_UNKNOWN)
328  {
329  // Determine the input video signal format...
330  // Warning: if there's no input signal, this loop won't exit until mGlobalQuit goes true!
331  UWord loopCount(0);
332  while (result == NTV2_FORMAT_UNKNOWN)
333  {
334  mDevice.WaitForOutputVerticalInterrupt(NTV2_CHANNEL1); // Just delay til next output VBI
335  if (mGlobalQuit)
336  return NTV2_FORMAT_UNKNOWN; // Terminate if asked to do so
337 
338  const NTV2VideoFormat currVF (mDevice.GetInputVideoFormat(mConfig.fInputSource));
339  if (currVF == NTV2_FORMAT_UNKNOWN)
340  { // Wait for video signal to appear
341  if (++loopCount % 500 == 0) // Log message every minute or so at ~50ms
342  CAPDBG("Waiting for valid video signal to appear at " << ::NTV2InputSourceToString(mConfig.fInputSource,true));
343  }
344  else if (numConsecutiveFrames == 0)
345  {
346  lastVF = currVF; // First valid video format to appear
347  numConsecutiveFrames++; // Start debounce counter
348  }
349  else if (numConsecutiveFrames == MIN_NUM_CONSECUTIVE_FRAMES)
350  {
351  numConsecutiveFrames = 0; // Reset for next signal outage
352  result = currVF; // Set official video format to use
353  }
354  else
355  numConsecutiveFrames = (lastVF == currVF) ? numConsecutiveFrames + 1 : 0;
356  } // loop while input video format is unstable
357 
358  CAPNOTE(::NTV2InputSourceToString(mConfig.fInputSource,true) << " video format: "
359  << ::NTV2VideoFormatToString(result));
360  cerr << endl << "## NOTE: " << ::NTV2InputSourceToString(mConfig.fInputSource,true)
361  << " video format is " << ::NTV2VideoFormatToString(result) << endl;
362  break; // Done!
363  } // loop
364 
365  NTV2_ASSERT(result != NTV2_FORMAT_UNKNOWN);
366  return result;
367 } // WaitForStableInputSignal
368 
369 
371 {
372  // Start the input thread...
374  return AJA_STATUS_SUCCESS;
375 } // Run
376 
377 
379 
380 // This is where the play thread is started
382 {
383  // Create and start the playout thread...
384  mPlayThread.Attach(OutputThreadStatic, this);
386  mPlayThread.Start();
387 } // StartOutputThread
388 
389 // The static output thread function
390 void NTV2Overlay::OutputThreadStatic (AJAThread * pThread, void * pContext) // static
391 { (void) pThread;
392  // Grab the NTV2Overlay instance pointer from the pContext parameter,
393  // then call its OutputThread method...
394  NTV2Overlay * pApp (reinterpret_cast<NTV2Overlay*>(pContext));
395  pApp->OutputThread();
396 } // OutputThreadStatic
397 
399 
400 //
401 // The output thread:
402 // - at startup:
403 // - configures the overlay FrameStore
404 // - initializes (but doesn't start) AutoCirculate (to allocate 2 device buffers)
405 // - while running:
406 // - blits the overlay image into the host overlay raster buffer
407 // - DMAs the overlay raster buffer to the device for mixing
408 // - terminates when AutoCirculate channel is stopped or when mGlobalQuit goes true
409 // - ensures my AutoCirculate channel is stopped
410 //
412 {
414  ULWord thrdNum(gPlayEnterCount), fbNum(10), loops(0);
415  ULWord goodWaits(0), badWaits(0), goodBlits(0), badBlits(0), goodXfers(0), badXfers(0), pingPong(0);
416  AUTOCIRCULATE_STATUS acStatus;
417  NTV2Buffer hostBuffer;
418 
419  // Configure the output FrameStore...
420  mDevice.SetMode (mConfig.fOutputChannel, NTV2_MODE_DISPLAY);
421  mDevice.SetFrameBufferFormat (mConfig.fOutputChannel, mOutputPixFormat);
422  mDevice.SetVideoFormat (mVideoFormat, /*retail*/false, /*keepVanc*/false, mConfig.fOutputChannel);
423  mDevice.SetVANCMode (NTV2_VANCMODE_OFF, mConfig.fOutputChannel);
424  NTV2RasterInfo rasterInfo (mVideoFormat, mOutputPixFormat);
425  if (!hostBuffer.Allocate(rasterInfo.GetTotalRasterBytes(), /*pageAligned*/true))
426  {cerr << "## ERROR: Failed to allocate " << rasterInfo.GetTotalRasterBytes() << "-byte vid buffer" << endl; return;}
427 
428  mDevice.AutoCirculateStop(mConfig.fOutputChannel);
430  && mDevice.AutoCirculateGetStatus(mConfig.fOutputChannel, acStatus)) // Find out which buffers we got
431  fbNum = ULWord(acStatus.acStartFrame); // Use them
432  else
433  {cerr << "## NOTE: Allocate 2-frame AC" << DEC(mConfig.fOutputChannel+1) << " range failed" << endl; return;}
434 
435  PLNOTE(DEC(thrdNum) << " started, " << ::NTV2VideoFormatToString(mVideoFormat)
436  << " raster:" << rasterInfo << ", bug:" << mBugRasterInfo);
437  Bouncer<ULWord> mixPct (/*max*/400, /*min*/100, /*start*/100),
438  xPos (/*max*/rasterInfo.GetRasterWidth() - mBugRasterInfo.GetRasterWidth()),
439  yPos (/*max*/rasterInfo.GetVisibleRasterHeight() - mBugRasterInfo.GetVisibleRasterHeight() - 1);
440 
441  // Loop til Quit or my AC channel stops...
442  while (!mGlobalQuit)
443  {
444  // Terminate this output thread when video format changes...
445  if (mDevice.GetInputVideoFormat(mConfig.fInputSource) != mVideoFormat)
446  break; // Stopped, exit thread
447 
448  NTV2FrameData * pFrameData (mAVCircularBuffer.StartConsumeNextBuffer());
449  if (pFrameData)
450  {
451  // Consume captured frame ... it's simply dropped on the floor.
452  // (The frame can't be used here anyway, since it's NTV2_FBF_10BIT_YCBCR,
453  // and the output pixel format is NTV2_FBF_ARGB.)
454  mAVCircularBuffer.EndConsumeNextBuffer(); // Signal "done with this frame"
455  }
456 
457  // Wait for the next output vertical interrupt event to get signaled...
458  if (mDevice.WaitForOutputVerticalInterrupt(mConfig.fOutputChannel))
459  {
460  ++goodWaits;
461  // Clear host buffer, blit the "bug" into it, then transfer it to device...
462  hostBuffer.Fill(ULWord(0));
463  if (::CopyRaster (mOutputPixFormat, // src & dst pixel format
464  hostBuffer, // dstBuffer
465  rasterInfo.GetBytesPerRow(), // dstBytesPerLine
466  rasterInfo.GetVisibleRasterHeight(), // dstTotalLines
467  yPos.Next(), // dstVertLineOffset
468  xPos.Next(), // dstHorzPixelOffset
469  mBug, // srcBuffer
470  mBugRasterInfo.GetBytesPerRow(), // srcBytesPerLine
471  mBugRasterInfo.GetVisibleRasterHeight(), // srcTotalLines
472  0, // srcVertLineOffset
473  mBugRasterInfo.GetVisibleRasterHeight(), // srcVertLinesToCopy
474  0, // srcHorzPixelOffset
475  mBugRasterInfo.GetRasterWidth())) // srcHorzPixelsToCopy
476  ++goodBlits;
477  else
478  ++badBlits;
479  if (mDevice.DMAWriteFrame (fbNum + pingPong, hostBuffer, hostBuffer.GetByteCount()))
480  ++goodXfers;
481  else
482  ++badXfers;
483  mDevice.SetOutputFrame (mConfig.fOutputChannel, fbNum + pingPong);
484  pingPong = pingPong ? 0 : 1;
485  mDevice.SetMixerCoefficient (MixerNum(), mixPct.Next() * 0x10000 / 400);
486  }
487  else
488  ++badWaits;
489 
490  if (++loops % 500 == 0) // Log message every minute or so
491  PLDBG(DEC(thrdNum) << ": " << DEC(goodXfers) << " xfers (" << DEC(badXfers) << " failed), " << DEC(goodBlits)
492  << " blits (" << DEC(badBlits) << " failed), " << DEC(goodWaits) << " waits ("
493  << DEC(badWaits) << " failed)");
494  } // loop til quit or A/C stopped
495 
496  mDevice.AutoCirculateStop(mConfig.fOutputChannel);
498  PLNOTE(DEC(thrdNum) << " done: " << DEC(goodXfers) << " xfers (" << DEC(badXfers) << " failed), "
499  << DEC(goodBlits) << " blits (" << DEC(badBlits) << " failed), " << DEC(goodWaits) << " waits (" << DEC(badWaits) << " failed)");
501 
502 } // OutputThread
503 
504 
506 
507 
508 // This is where the input thread gets started
510 {
511  // Create and start the capture thread...
512  mCaptureThread.Attach(InputThreadStatic, this);
513  mCaptureThread.SetPriority(AJA_ThreadPriority_High);
514  mCaptureThread.Start();
515 } // StartInputThread
516 
517 // The static input thread function
518 void NTV2Overlay::InputThreadStatic (AJAThread * pThread, void * pContext) // static
519 { (void) pThread;
520  // Grab the NTV2Overlay instance pointer from the pContext parameter,
521  // then call its InputThread method...
522  NTV2Overlay * pApp (reinterpret_cast<NTV2Overlay*>(pContext));
523  pApp->InputThread();
524 } // InputThreadStatic
525 
526 
527 //
528 // The input thread:
529 // - waits indefinitely for a stable input signal, and if one is found:
530 // - configures an input FrameStore (if needed for capturing frames to host)
531 // - initializes AutoCirculate to reserve a couple of device frame buffers
532 // - starts a new output thread
533 // - waits indefinitely to see if the input signal is lost or changes, and if it does:
534 // - stops input AutoCirculate which signals the output thread to exit
535 // - upon termination:
536 // - ensures my monitor/capture AutoCirculate channel is stopped
537 //
539 {
540  ULWord loops(0), bails(0), vfChgTally(0), badWaits(0), waitTally(0), goodXfers(0), badXfers(0);
541  AUTOCIRCULATE_TRANSFER xferInfo;
542  CAPNOTE("Started");
543 
544  // Loop until time to quit...
545  while (!mGlobalQuit)
546  {
547  mDevice.AutoCirculateStop(mConfig.fInputChannel);
548  mVideoFormat = WaitForStableInputSignal();
549  if (mVideoFormat == NTV2_FORMAT_UNKNOWN)
550  break; // Quit
551 
552  // At this point, the input video format is stable.
553  // Configure the input FrameStore...
554  const bool isRGBSignal (IsInputSignalRGB());
555  mDevice.SetMode (mConfig.fInputChannel, NTV2_MODE_CAPTURE);
556  mDevice.SetFrameBufferFormat (mConfig.fInputChannel, mInputPixFormat);
557  mDevice.SetVideoFormat (mVideoFormat, /*retail*/false, /*keepVanc*/false, mConfig.fInputChannel);
558  mDevice.SetVANCMode (NTV2_VANCMODE_OFF, mConfig.fInputChannel);
561  { cerr << "## NOTE: Failed to [re]allocate host buffers" << endl;
562  mGlobalQuit = true;
563  continue;
564  }
565 
566  AUTOCIRCULATE_STATUS acStatus;
567  if (!mDevice.AutoCirculateInitForInput(mConfig.fInputChannel, NTV2ACFrameRange(7)))
568  { cerr << "## NOTE: Input A/C allocate device frame buffer range failed" << endl;
569  mGlobalQuit = true;
570  continue;
571  }
572 
573  mDevice.AutoCirculateStart(mConfig.fInputChannel); // Start capturing
574  StartOutputThread(); // Start a new playout thread
575 
576  // Capture frames until signal format changes...
577  while (!mGlobalQuit)
578  {
579  AUTOCIRCULATE_STATUS acStatus;
580  mDevice.AutoCirculateGetStatus (mConfig.fInputChannel, acStatus);
581  if (acStatus.IsRunning() && acStatus.HasAvailableInputFrame())
582  {
583  // At least one fully-formed frame is available in device frame buffer
584  // memory that can be transferred to the host. Reserve an NTV2FrameData
585  // to "produce", and use it to store the next frame to be transferred...
586  NTV2FrameData * pCaptureData (mAVCircularBuffer.StartProduceNextBuffer());
587  if (pCaptureData)
588  { // Transfer frame from device...
589  xferInfo.SetVideoBuffer (pCaptureData->VideoBuffer(), pCaptureData->VideoBufferSize());
590  if (mDevice.AutoCirculateTransfer (mConfig.fInputChannel, xferInfo)) ++goodXfers;
591  else ++badXfers;
592  mAVCircularBuffer.EndProduceNextBuffer(); // Signal "done with this frame"
593  } // if pCaptureData != NULL
594  else ++bails;
595  } // if A/C running and frame(s) are available for transfer
596  else
597  { // Wait for next input VBI...
598  if (mDevice.WaitForInputVerticalInterrupt(mConfig.fInputChannel)) ++waitTally;
599  else ++badWaits;
600  }
601  if (++loops % 500 == 0) // Log message every minute or so
602  CAPDBG(DEC(vfChgTally) << " sigChgs, " << DEC(bails) << " bails, " << DEC(waitTally)
603  << " waits (" << DEC(badWaits) << " failed), " << DEC(goodXfers) << " xfers ("
604  << DEC(badXfers) << " failed)");
605 
606  // Check input signal format...
607  if (mDevice.GetInputVideoFormat(mConfig.fInputSource) != mVideoFormat
608  || isRGBSignal != IsInputSignalRGB())
609  { // Input signal format changed!
610  ++vfChgTally;
611  mAVCircularBuffer.StartProduceNextBuffer(); // Unblock output thread
612  mAVCircularBuffer.EndProduceNextBuffer();
614  break; // Break to outer loop to wait for stable input signal
615  }
616  } // inner loop -- until signal change
617  } // loop til quit signaled
618 
619  mDevice.AutoCirculateStop(mConfig.fInputChannel); // This will signal OutputThread to exit
620  CAPNOTE("Done: " << DEC(vfChgTally) << " sigChgs, " << DEC(bails) << " bails, " << DEC(waitTally)
621  << " waits (" << DEC(badWaits) << " failed), " << DEC(goodXfers) << " xfers ("
622  << DEC(badXfers) << " failed)");
623 } // InputThread
NTV2Channel NTV2InputSourceToChannel(const NTV2InputSource inInputSource)
Converts a given NTV2InputSource to its equivalent NTV2Channel value.
Definition: ntv2utils.cpp:5047
virtual bool SetMixerBGMatteEnabled(const UWord inWhichMixer, const bool inIsEnabled)
Answers if the given mixer/keyer&#39;s background matte is enabled or not.
virtual bool SetTaskMode(const NTV2TaskMode inMode)
Sets the device&#39;s task mode.
#define NTV2_IS_VALID_TASK_MODE(__m__)
void StartInputThread(void)
Starts input thread.
NTV2AudioSystem
Used to identify an Audio System on an NTV2 device. See Audio System Operation for more information...
Definition: ntv2enums.h:3895
#define CAPDBG(_expr_)
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
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...
NTV2VideoFormat WaitForStableInputSignal(void)
Waits for stable input signal.
void RouteInputSignal(void)
Performs input routing.
Declares common types used in the ajabase library.
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
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...
static bool DrawBox(NTV2Buffer &buf, const NTV2RasterInfo &info, const ULWord argbPixValue, const UWord pixThickness, const UWord topLeftX, const UWord topLeftY, const ULWord width, const ULWord height)
static void InputThreadStatic(AJAThread *pThread, void *pInstance)
Static output/playout thread function.
ULWord GetBytesPerRow(const UWord inPlaneIndex0=0) const
AJAStatus Add(FrameDataPtr pInFrameData)
Appends a new frame buffer to me, increasing my frame storage capacity by one frame.
virtual bool SetMixerMode(const UWord inWhichMixer, const NTV2MixerKeyerMode inMode)
Sets the mode for the given mixer/keyer.
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
void ReleaseCaptureBuffers(void)
Frees capture buffers & ring.
static bool DrawHLine(NTV2Buffer &buf, const NTV2RasterInfo &info, const ULWord argbPixValue, const ULWord vThickness, const ULWord yPos, const ULWord xLeft, const ULWord width)
void Quit(void)
Gracefully stops me.
Definition: ntv2overlay.cpp:48
virtual bool IsDeviceReady(const bool inCheckValid=(0))
UWord MixerNum(void)
Definition: ntv2overlay.h:48
enum _NTV2VideoFormat NTV2VideoFormat
Identifies a particular video format.
static ULWord gPlayEnterCount(0)
static uint64_t GetPid()
Definition: process.cpp:35
AJAStatus SetupCaptureBuffers(void)
Allocates capture buffers & ring.
#define AJA_FAILURE(_status_)
Definition: types.h:373
#define PLINFO(_xpr_)
uint32_t U32(const int inIndex) const
ULWord GetByteCount(void) const
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
Capture (input) mode, which writes into device SDRAM.
Definition: ntv2enums.h:1243
void RouteOutputSignal(void)
Performs output routing.
LWord acStartFrame
First frame to circulate. FIXFIXFIX Why is this signed? CHANGE TO ULWord??
void Clear(void)
Clears my frame collection, their locks, everything.
Declares the AJATime class.
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 NTV2_FOURCC(_a_, _b_, _c_, _d_)
Header file for the NTV2Overlay demonstration class.
ULWord GetTotalBytes(void) const
void OutputThread(void)
The output/playout thread function.
virtual bool SetMixerBGInputControl(const UWord inWhichMixer, const NTV2MixerKeyerInputControl inInputControl)
Sets the background input control value for the given mixer/keyer.
Definition: json.hpp:5362
virtual AJAStatus Start()
Definition: thread.cpp:91
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 bool SetMultiFormatMode(const bool inEnable)
Enables or disables multi-format (per channel) device operation. If enabled, each device channel can ...
virtual bool GetHDMIInputColor(NTV2LHIHDMIColorSpace &outValue, const NTV2Channel inHDMIInput=NTV2_CHANNEL1)
Answers with the current colorspace for the given HDMI input.
Definition: ntv2hdmi.cpp:70
NTV2FormatDesc NTV2RasterInfo
Shorthand for NTV2FormatDescriptor.
NTV2Overlay(const OverlayConfig &inConfig)
Construct from OverlayConfig.
Definition: ntv2overlay.cpp:24
#define NTV2_ASSERT(_expr_)
Definition: ajatypes.h:476
virtual bool ClearRouting(void)
Removes all existing signal path connections between any and all widgets on the AJA device...
int32_t appPID(0)
A handy class that makes it easy to "bounce" an unsigned integer value between a minimum and maximum ...
virtual bool IsRemote(void) const
virtual bool Active()
Definition: thread.cpp:116
bool CopyRaster(const NTV2PixelFormat inPixelFormat, UByte *pDstBuffer, const ULWord inDstBytesPerLine, const UWord inDstTotalLines, const UWord inDstVertLineOffset, const UWord inDstHorzPixelOffset, const UByte *pSrcBuffer, const ULWord inSrcBytesPerLine, const UWord inSrcTotalLines, const UWord inSrcVertLineOffset, const UWord inSrcVertLinesToCopy, const UWord inSrcHorzPixelOffset, const UWord inSrcHorzPixelsToCopy)
Copies all or part of a source raster image into a destination raster at a given position.
Definition: ntv2utils.cpp:1735
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.
AutoCirculate Frame Range.
Definition: ntv2utils.h:971
std::string NTV2InputSourceToString(const NTV2InputSource inValue, const bool inForRetailDisplay=false)
Definition: ntv2utils.cpp:7377
NTV2OutputXptID GetInputSourceOutputXpt(const NTV2InputSource inInputSource, const bool inIsSDI_DS2=false, const bool inIsHDMI_RGB=false, const UWord inHDMI_Quadrant=0)
NTV2PixelFormat fPixelFormat
The pixel format to use.
static bool DrawVLine(NTV2Buffer &buf, const NTV2RasterInfo &info, const ULWord argbPixValue, const ULWord pixThickness, const ULWord xPos, const ULWord yTop, const ULWord height)
NTV2Channel fOutputChannel
The output channel to use.
AJAStatus SetupAudio(void)
Performs all audio setup.
virtual bool SetOutputFrame(const NTV2Channel inChannel, const ULWord inValue)
Sets the output frame index number for the given FrameStore. This identifies which frame in device SD...
unsigned int n
Definition: pstream.cpp:148
0: Disabled (never recommended): device configured exclusively by client application(s).
Playout (output) mode, which reads from device SDRAM.
Definition: ntv2enums.h:1241
virtual std::string GetDescription(void) const
Definition: ntv2card.cpp:139
virtual bool SetMode(const NTV2Channel inChannel, const NTV2Mode inNewValue, const bool inIsRetail=(!(0)))
Determines if a given FrameStore on the AJA device will be used to capture or playout video...
AJAStatus Run(void)
Runs me (only after Init called)
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 SetSDITransmitEnable(const NTV2Channel inChannel, const bool inEnable)
Sets the specified bidirectional SDI connector to act as an input or an output.
#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 ...
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...
#define NTV2_IS_FBF_RGB(__fbf__)
Definition: ntv2enums.h:283
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
This selects audio channels 1 and 2 (Group 1 channels 1 and 2)
Definition: ntv2enums.h:3133
virtual ~NTV2Overlay()
My destructor.
Definition: ntv2overlay.cpp:38
2: OEM (recommended): device configured by client application(s) with some driver involvement...
virtual bool SetSDIOutputAudioSystem(const NTV2Channel inSDIOutputConnector, const NTV2AudioSystem inAudioSystem)
Sets the device&#39;s NTV2AudioSystem that will provide audio for the given SDI output&#39;s audio embedder...
#define PLNOTE(_xpr_)
std::string NTV2VideoFormatToString(const NTV2VideoFormat inValue, const bool inUseFrameRate=false)
Definition: ntv2utils.cpp:6746
See 8-Bit ARGB, RGBA, ABGR Formats.
Definition: ntv2enums.h:224
Embeds silence (zeroes) into the data stream.
Definition: ntv2enums.h:2030
AJAStatus SetupVideo(void)
Performs all video setup.
virtual bool SetMixerCoefficient(const UWord inWhichMixer, const ULWord inMixCoefficient)
Sets the current mix coefficient of the given mixer/keyer.
static int32_t Increment(int32_t volatile *pTarget)
Definition: atomic.cpp:82
Configures an NTV2Burn or NTV2FieldBurn instance.
Specifies channel or FrameStore 2 (or the 2nd item).
Definition: ntv2enums.h:1360
virtual NTV2DeviceID GetDeviceID(void)
static void OutputThreadStatic(AJAThread *pThread, void *pInstance)
ULWord GetVideoWriteSize(const NTV2VideoFormat inVideoFormat, const NTV2FrameBufferFormat inFBFormat, const NTV2VANCMode inVancMode=NTV2_VANCMODE_OFF)
Identical to the GetVideoActiveSize function, except rounds the result up to the nearest 4K page size...
Definition: ntv2utils.cpp:2875
Overlays foreground video on top of background video.
Definition: ntv2enums.h:1794
Declares the CNTV2DeviceScanner class.
AJAStatus Init(void)
Prepares me to Run()
Definition: ntv2overlay.cpp:68
std::string fDeviceSpec
The AJA device to use.
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...
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 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).
virtual bool SetMixerVancOutputFromForeground(const UWord inWhichMixer, const bool inFromForegroundSource=(!(0)))
Sets the VANC source for the given mixer/keyer to the foreground video (or not). See the SDI Ancillar...
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...
Declares the AJAAtomic class.
This object specifies the information that will be transferred to or from the AJA device in the CNTV2...
#define DEC(__x__)
virtual bool SetMixerFGInputControl(const UWord inWhichMixer, const NTV2MixerKeyerInputControl inInputControl)
Sets the foreground input control value for the given mixer/keyer.
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 ...
static const size_t CIRCULAR_BUFFER_SIZE(10)
Number of NTV2FrameData&#39;s in our ring.
virtual AJAStatus Attach(AJAThreadFunction *pThreadFunction, void *pUserContext)
Definition: thread.cpp:169
ULWord GetTotalRasterBytes(const UWord inPlaneIndex0=0) const
FrameDataPtr StartProduceNextBuffer(void)
The thread that&#39;s responsible for providing frames – the producer – calls this function to populate...
bool fDoMultiFormat
If true, enables device-sharing; otherwise takes exclusive control of the device. ...
This is returned from the CNTV2Card::AutoCirculateGetStatus function.
ULWord GetVisibleRasterHeight(void) const
uint16_t UWord
Definition: ajatypes.h:221
ULWord GetRasterWidth(void) const
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...
AJAStatus SetupOverlayBug(void)
Sets up overlay "bug".
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...
I encapsulate the video, audio and anc host buffers used in the AutoCirculate demos. I&#39;m a more modern version of the AVDataBuffer.
bool HasAvailableInputFrame(void) const
#define CAPINFO(_expr_)
NTV2Channel fInputChannel
The input channel to use.
#define NTV2_IS_VALID_VIDEO_FORMAT(__f__)
Definition: ntv2enums.h:725
bool IsRunning(void) const
ULWord linePitch
Number of 32-bit words per line – shadows mLinePitch[0] / sizeof(ULWord)
Outputs live input video overlaid with image having transparency.
Definition: ntv2overlay.h:22
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 ...
See KONA X™.
Definition: ntv2enums.h:80
Declares the AJAAncillaryList class.
#define PLDBG(_xpr_)
void InputThread(void)
The input/capture thread function.
#define CAPNOTE(_expr_)
virtual bool SetVANCMode(const NTV2VANCMode inVancMode, const NTV2Channel inChannel=NTV2_CHANNEL1)
Sets the VANC mode for the given FrameStore.
bool IsInputSignalRGB(void)
static ULWord gPlayExitCount(0)
void RouteOverlaySignal(void)
Performs overlay routing.
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 bool SetMixerFGMatteEnabled(const UWord inWhichMixer, const bool inIsEnabled)
Answers if the given mixer/keyer&#39;s foreground matte is enabled or not.
enum NTV2OutputCrosspointID NTV2OutputXptID
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 ...
This identifies the mode in which there are no VANC lines in the frame buffer.
Definition: ntv2enums.h:3799
bool SetVideoBuffer(ULWord *pInVideoBuffer, const ULWord inVideoByteCount)
Sets my video buffer for use in a subsequent call to CNTV2Card::AutoCirculateTransfer.
NTV2LHIHDMIColorSpace
Definition: ntv2enums.h:3678
NTV2ReferenceSource NTV2InputSourceToReferenceSource(const NTV2InputSource inInputSource)
Converts a given NTV2InputSource to its equivalent NTV2ReferenceSource value.
Definition: ntv2utils.cpp:5023
static const uint32_t kAppSignature(((((uint32_t)( 'O'))<< 24)|(((uint32_t)( 'v'))<< 16)|(((uint32_t)( 'r'))<< 8)|(((uint32_t)( 'l'))<< 0)))
NTV2InputSource fInputSource
The device input connector to use.
void StartOutputThread(void)
Starts output thread.
virtual bool EnableChannel(const NTV2Channel inChannel)
Enables the given FrameStore.