ATEM SDK Weirdness

Ask software engineering and SDK questions for developers working on Mac OS X, Windows or Linux.
  • Author
  • Message
Offline

AdmiralTriggerHappy

  • Posts: 10
  • Joined: Tue Nov 16, 2021 9:06 pm
  • Real Name: Andrew Wallace

ATEM SDK Weirdness

PostMon Jan 08, 2024 4:36 am

I'm working on an APP for basic ATEM controls
Its written in C++

The code is below (most of it), but the weird thing is the cut I send never occurs even though I get a success code back.
I can however get the ATEMs product name without issue.
So clearly the connection works, and as far as I can tell the code is the same as the switcher panel example which I got working just fine.

Any ideas?

Code: Select all
IBMDSwitcherDiscovery*      mSwitcherDiscovery;
IBMDSwitcher*            mSwitcher;
IBMDSwitcherMixEffectBlock*   mMixEffectBlock;
IBMDSwitcherInputIterator* inputIterator = NULL;
std::list<InputMonitor*>   mInputMonitors;
std::string                                 productName;

BMDSwitcherConnectToFailure connectToFailReason;
HRESULT result;
IBMDSwitcherMixEffectBlockIterator* iterator = NULL;


// Initialize COM and Switcher related members
if (FAILED(CoInitialize(NULL)))
{
   printf("CoInitialize failed.");
   return -1;
}

SwitcherMonitor* mSwitcherMonitor = new SwitcherMonitor();
MixEffectBlockMonitor* mMixEffectBlockMonitor = new MixEffectBlockMonitor();

result = CoCreateInstance(CLSID_CBMDSwitcherDiscovery, NULL, CLSCTX_ALL, IID_IBMDSwitcherDiscovery, (void**)&mSwitcherDiscovery);
    if (result != S_OK)
    {
      fprintf(stderr, "A Switcher Discovery instance could not be created.  The Switcher drivers may not be installed.\n");
      return -1;
    }

    // Connect to switcher with address provided by argv
    const char* ipAddress = "192.168.1.53";
    CComBSTR addressString = _com_util::ConvertStringToBSTR(ipAddress);
    result = mSwitcherDiscovery->ConnectTo(addressString, &mSwitcher, &connectToFailReason);
    if (result != S_OK)
    {
      std::string connectFailReasonStr = LookupString<BMDSwitcherConnectToFailure>(kConnectFailReasonCodes, connectToFailReason);
      fprintf(stderr, "Failed to connect to switcher at address %s, reason = %s\n", ipAddress, connectFailReasonStr.c_str());
      return -1;
    }
    ::SysFreeString(addressString);

    mSwitcher->AddCallback(mSwitcherMonitor);

    // Create an InputMonitor for each input so we can catch any changes to input names
result = mSwitcher->CreateIterator(IID_IBMDSwitcherInputIterator, (void**)&inputIterator);
if (SUCCEEDED(result))
{
   IBMDSwitcherInput* input = NULL;

   // For every input, install a callback to monitor property changes on the input
   while (S_OK == inputIterator->Next(&input))
   {
      InputMonitor* inputMonitor = new InputMonitor(input);
      input->Release();
      mInputMonitors.push_back(inputMonitor);
   }
   inputIterator->Release();
   inputIterator = NULL;
}

    // Print switcher product name
   printf("Switcher found at %s\n", ipAddress);
   productName = get_product_name(mSwitcher);
   printf(" %-40s %s\n", "Product Name:", productName.c_str());
   
    result = mSwitcher->CreateIterator(IID_IBMDSwitcherMixEffectBlockIterator, (void**)&iterator);
    if (FAILED(result))

   {
      printf("Could not create IBMDSwitcherMixEffectBlockIterator iterator");
      return -1;
   }

   // Use the first Mix Effect Block
   if (S_OK != iterator->Next(&mMixEffectBlock))
   {
      printf("Could not get the first IBMDSwitcherMixEffectBlock");
      return -1;
   
   }

    mMixEffectBlock->AddCallback(mMixEffectBlockMonitor);

    if (S_OK != mMixEffectBlock->PerformCut())
    {
        printf("Cut failed");
    }

    std::cout << "Program Completed\n";
    return 0;
Offline

Cameron Nichols

Blackmagic Design

  • Posts: 443
  • Joined: Mon Sep 04, 2017 4:05 am

Re: ATEM SDK Weirdness

PostMon Jan 22, 2024 3:31 am

Hi Andrew,

Thanks for providing the code segment, I was able to replicate locally. The issue is that the program exits before the cut command has been dispatched to the switcher. You can see this by adding a Sleep command prior to the end of main().

The solution is to add a wait until the PGM input has changed, which can be achieved by waiting for Mix Effect Block event bmdSwitcherMixEffectBlockEventTypeProgramInputChanged.

I have modified the original MixEffectBlockMonitor class as follows:
Code: Select all
// Callback class for monitoring property changes on a mix effect block.
class MixEffectBlockMonitor : public IBMDSwitcherMixEffectBlockCallback
{
public:
   MixEffectBlockMonitor() :
      m_refCount(1),
      m_pgmChanged(false)
   {
   }

protected:
   virtual ~MixEffectBlockMonitor() { }

public:
   HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv)
   {
      if (!ppv)
         return E_POINTER;

      if (IsEqualGUID(iid, IID_IBMDSwitcherMixEffectBlockCallback))
      {
         *ppv = static_cast<IBMDSwitcherMixEffectBlockCallback*>(this);
         AddRef();
         return S_OK;
      }

      if (IsEqualGUID(iid, IID_IUnknown))
      {
         *ppv = static_cast<IUnknown*>(this);
         AddRef();
         return S_OK;
      }

      *ppv = NULL;
      return E_NOINTERFACE;
   }

   ULONG STDMETHODCALLTYPE AddRef(void)
   {
      return ++m_refCount;
   }

   ULONG STDMETHODCALLTYPE Release(void)
   {
      int newCount = --m_refCount;
      if (newCount == 0)
         delete this;
      return newCount;
   }

   HRESULT STDMETHODCALLTYPE Notify(BMDSwitcherMixEffectBlockEventType eventType)
   {
      switch (eventType)
      {
         case bmdSwitcherMixEffectBlockEventTypeProgramInputChanged:
            {
               std::lock_guard<std::mutex> lock(m_mutex);
               m_pgmChanged = true;
            }
            m_pgmChangedCondition.notify_one();
            break;

         default:
            break;
      }
      return S_OK;
   }

   void waitForProgramInputChanged(void)
   {
      std::unique_lock<std::mutex> lock(m_mutex);
      m_pgmChangedCondition.wait(lock, [&] { return m_pgmChanged; });
   }

private:
   std::atomic<LONG> m_refCount;
   std::mutex m_mutex;
   bool m_pgmChanged;
   std::condition_variable m_pgmChangedCondition;
};
With this callback class, the program can wait for PGM input change before exiting:
Code: Select all
   mMixEffectBlock->AddCallback(mMixEffectBlockMonitor);

   if (S_OK != mMixEffectBlock->PerformCut())
   {
      printf("Cut failed");
   }

   mMixEffectBlockMonitor->waitForProgramInputChanged();

   std::cout << "Program Completed\n";
Regards
Cameron

Return to Software Developers

Who is online

Users browsing this forum: No registered users and 14 guests