Skip to content

Instantly share code, notes, and snippets.

@adinbied
Created July 4, 2022 00:26
Show Gist options
  • Save adinbied/eca8cfd233f14056002a4f139a38f855 to your computer and use it in GitHub Desktop.
Save adinbied/eca8cfd233f14056002a4f139a38f855 to your computer and use it in GitHub Desktop.
Blackmagic SDK Extract Metadata Sample
/* -LICENSE-START-
** Copyright (c) 2018 Blackmagic Design
**
** Permission is hereby granted, free of charge, to any person or organization
** obtaining a copy of the software and accompanying documentation covered by
** this license (the "Software") to use, reproduce, display, distribute,
** execute, and transmit the Software, and to prepare derivative works of the
** Software, and to permit third-parties to whom the Software is furnished to
** do so, all subject to the following:
**
** The copyright notices in the Software and this entire statement, including
** the above license grant, this restriction and the following disclaimer,
** must be included in all copies of the Software, in whole or in part, and
** all derivative works of the Software, unless such copies or derivative
** works are solely in the form of machine-executable object code generated by
** a source language processor.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
** DEALINGS IN THE SOFTWARE.
** -LICENSE-END-
*/
#include "BlackmagicRawAPIDispatch.h"
#include <cassert>
#include <atomic>
#include <iostream>
class CameraCodecCallback : public IBlackmagicRawCallback
{
public:
explicit CameraCodecCallback() = default;
virtual ~CameraCodecCallback()
{
assert(m_refCount == 0);
SetFrame(nullptr);
}
IBlackmagicRawFrame* GetFrame() { return m_frame; }
virtual void ReadComplete(IBlackmagicRawJob* readJob, HRESULT result, IBlackmagicRawFrame* frame)
{
if (result == S_OK)
{
SetFrame(frame);
}
}
virtual void ProcessComplete(IBlackmagicRawJob* job, HRESULT result, IBlackmagicRawProcessedImage* processedImage) {}
virtual void DecodeComplete(IBlackmagicRawJob*, HRESULT) {}
virtual void TrimProgress(IBlackmagicRawJob*, float) {}
virtual void TrimComplete(IBlackmagicRawJob*, HRESULT) {}
virtual void SidecarMetadataParseWarning(IBlackmagicRawClip*, BSTR, uint32_t, BSTR) {}
virtual void SidecarMetadataParseError(IBlackmagicRawClip*, BSTR, uint32_t, BSTR) {}
virtual void PreparePipelineComplete(void*, HRESULT) {}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)
{
return E_NOTIMPL;
}
virtual ULONG STDMETHODCALLTYPE AddRef(void)
{
return ++m_refCount;
}
virtual ULONG STDMETHODCALLTYPE Release(void)
{
const int32_t newRefValue = --m_refCount;
if (newRefValue == 0)
{
delete this;
}
assert(newRefValue >= 0);
return newRefValue;
}
private:
void SetFrame(IBlackmagicRawFrame* frame)
{
if (m_frame != nullptr)
m_frame->Release();
m_frame = frame;
if (m_frame != nullptr)
m_frame->AddRef();
}
IBlackmagicRawFrame* m_frame = nullptr;
std::atomic<int32_t> m_refCount = {0};
};
static void DumpMetadata(const char *header, IBlackmagicRawMetadataIterator* metadataIterator)
{
BSTR key;
VARIANT value;
HRESULT result;
std::cout << std::endl << header << std::endl;
while (SUCCEEDED(metadataIterator->GetKey(&key)))
{
std::wcout << key << ": ";
VariantInit(&value);
result = metadataIterator->GetData(&value);
if (result != S_OK)
{
std::cerr << "Failed to get data from IBlackmagicRawMetadataIterator!" << std::endl;
break;
}
VARTYPE variantType = value.vt;
switch (variantType)
{
case blackmagicRawVariantTypeS16:
{
short s16 = value.iVal;
std::cout << s16;
}
break;
case blackmagicRawVariantTypeU16:
{
unsigned short u16 = value.uiVal;
std::cout << u16;
}
break;
case blackmagicRawVariantTypeS32:
{
int i32 = value.intVal;
std::cout << i32;
}
break;
case blackmagicRawVariantTypeU32:
{
unsigned int u32 = value.uintVal;
std::cout << u32;
}
break;
case blackmagicRawVariantTypeFloat32:
{
float f32 = value.fltVal;
std::cout << f32;
}
break;
case blackmagicRawVariantTypeString:
{
BSTR bstr = value.bstrVal;
std::wcout << bstr;
}
break;
case blackmagicRawVariantTypeSafeArray:
{
SafeArray* safeArray = value.parray;
void* safeArrayData = nullptr;
result = SafeArrayAccessData(safeArray, &safeArrayData);
if (result != S_OK)
{
std::cerr << "Failed to access safeArray data!" << std::endl;
break;
}
VARTYPE arrayVarType;
result = SafeArrayGetVartype(safeArray, &arrayVarType);
if (result != S_OK)
{
std::cerr << "Failed to get BlackmagicRawVariantType from safeArray!" << std::endl;
break;
}
long lBound;
result = SafeArrayGetLBound(safeArray, 1, &lBound);
if (result != S_OK)
{
std::cerr << "Failed to get LBound from safeArray!" << std::endl;
break;
}
long uBound;
result = SafeArrayGetUBound(safeArray, 1, &uBound);
if (result != S_OK)
{
std::cerr << "Failed to get UBound from safeArray!" << std::endl;
break;
}
long safeArrayLength = (uBound - lBound) + 1;
long arrayLength = safeArrayLength > 32 ? 32 : safeArrayLength;
for (int i = 0; i < arrayLength; ++i)
{
switch (arrayVarType)
{
case blackmagicRawVariantTypeU8:
{
int u8 = static_cast<int>(static_cast<unsigned char*>(safeArrayData)[i]);
if (i > 0)
std::cout << ",";
std::cout << u8;
}
break;
case blackmagicRawVariantTypeS16:
{
short s16 = static_cast<short*>(safeArrayData)[i];
std::cout << s16 << ",";
}
break;
case blackmagicRawVariantTypeU16:
{
unsigned short u16 = static_cast<unsigned short*>(safeArrayData)[i];
std::cout << u16 << ",";
}
break;
case blackmagicRawVariantTypeS32:
{
int i32 = static_cast<int*>(safeArrayData)[i];
std::cout << i32 << ",";
}
break;
case blackmagicRawVariantTypeU32:
{
unsigned int u32 = static_cast<unsigned int*>(safeArrayData)[i];
std::cout << u32 << ",";
}
break;
case blackmagicRawVariantTypeFloat32:
{
float f32 = static_cast<float*>(safeArrayData)[i];
std::cout << f32 << ",";
}
break;
default:
break;
}
}
}
default:
break;
}
VariantClear(&value);
std::cout << std::endl;
metadataIterator->Next();
}
}
int main(int argc, const char* argv[])
{
if (argc > 2)
{
std::cerr << "Usage: " << argv[0] << " clipName.braw" << std::endl;
return 1;
}
BSTR clipName;
bool clipNameProvided = argc == 2;
if (clipNameProvided)
{
const char* str = argv[1];
int strLength = (int)strlen(str);
int wslen = MultiByteToWideChar(CP_ACP, 0, str, strLength, 0, 0);
clipName = SysAllocStringLen(0, wslen);
MultiByteToWideChar(CP_ACP, 0, argv[1], strLength, clipName, wslen);
}
else
{
clipName = SysAllocString(L"C:\Program Files (x86)\Blackmagic Design\Blackmagic RAW\Blackmagic RAW SDK\Media\sample.braw");
}
HRESULT result = S_OK;
IBlackmagicRawFactory* factory = nullptr;
IBlackmagicRaw* codec = nullptr;
IBlackmagicRawClip* clip = nullptr;
IBlackmagicRawMetadataIterator* clipMetadataIterator = nullptr;
IBlackmagicRawMetadataIterator* frameMetadataIterator = nullptr;
CameraCodecCallback* callback = nullptr;
do
{
result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (result != S_OK)
{
std::cerr << "Initialization of COM failed!" << std::endl;
break;
}
BSTR libraryPath = SysAllocString(L"../../Libraries");
factory = CreateBlackmagicRawFactoryInstanceFromPath(libraryPath);
SysFreeString(libraryPath);
if (factory == nullptr)
{
std::cerr << "Failed to create IBlackmagicRawFactory!" << std::endl;
break;
}
result = factory->CreateCodec(&codec);
if (result != S_OK)
{
std::cerr << "Failed to create IBlackmagicRaw!" << std::endl;
break;
}
result = codec->OpenClip(clipName, &clip);
if (result != S_OK)
{
std::cerr << "Failed to open IBlackmagicRawClip!" << std::endl;
break;
}
callback = new CameraCodecCallback();
callback->AddRef();
result = codec->SetCallback(callback);
if (result != S_OK)
{
std::cerr << "Failed to set IBlackmagicRawCallback!" << std::endl;
break;
}
result = clip->GetMetadataIterator(&clipMetadataIterator);
if (result != S_OK)
{
std::cerr << "Failed to get clip IBlackmagicRawMetadataIterator!" << std::endl;
break;
}
IBlackmagicRawJob* readJob = nullptr;
result = clip->CreateJobReadFrame(0, &readJob);
if (result != S_OK)
{
std::cerr << "Failed to get IBlackmagicRawJob!" << std::endl;
break;
}
result = readJob->Submit();
readJob->Release();
if (result != S_OK)
{
std::cerr << "Failed to submit IBlackmagicRawJob!" << std::endl;
break;
}
codec->FlushJobs();
IBlackmagicRawFrame* frame = callback->GetFrame();
if (frame == nullptr)
{
std::cerr << "Failed to get IBlackmagicRawFrame!" << std::endl;
break;
}
result = frame->GetMetadataIterator(&frameMetadataIterator);
if (result != S_OK)
{
std::cerr << "Failed to get frame IBlackmagicRawMetadataIterator!" << std::endl;
break;
}
DumpMetadata("Clip Metadata", clipMetadataIterator);
DumpMetadata("Frame 0 Metadata", frameMetadataIterator);
} while (0);
if (callback != nullptr)
callback->Release();
if (clipMetadataIterator != nullptr)
clipMetadataIterator->Release();
if (frameMetadataIterator != nullptr)
frameMetadataIterator->Release();
if (clip != nullptr)
clip->Release();
if (codec != nullptr)
codec->Release();
if (factory != nullptr)
factory->Release();
CoUninitialize();
SysFreeString(clipName);
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment