Page 1 of 1

Decklink Com Programming

Posted: Sun Dec 17, 2023 12:47 am
by RSrole
I've done a few small projects controlling the BlackMagic Decklink with Purebasic. I've done larger projects with DeckLink and c# and some tests with Powerbasic. Note that in the following code, I've eliminated the validity checks, but the real code has them. And "UpdateActivity" writes to a listviewgadget and to the debug window.

Here's my confusion:

In all three languages I can get an instance of the DeckLinkIterator. In c# and Powerbasic, it's pretty easy. With Purebasic I had to use:

Code: Select all

Protected *deckLinkIterator.IDeckLinkIterator
Protected IID_IDeckLinkIterator.iid
Protected CLSID_DeckLinkIterator.ClSid
IIDFromString_(#IID_DeckLinkAPI_IDeckLinkIterator,@IID_IDeckLinkIterator)
CLSIDFromString_(#CLSID_DeckLinkAPI_CDeckLinkIterator,@CLSID_DeckLinkIterator)
Protected  ok = CoCreateInstance_(@CLSID_DeckLinkIterator,#Null,$1f,@IID_IDeckLinkIterator,@*deckLinkIterator)
The above code returns a proper iterator, from which I can iterate using *deckLinkIterator\Next(@dl(i)) an array iDeckLink instances, which are the internal devices, inputs or outputs. dl(i) is an array of iDeckLink objects.

Let's say we only care about outputs for the moment and I select an iDeckLink object. I then do:

Code: Select all

IIDFromString_(#IID_DeckLinkAPI_IDeckLinkOutput,@IID_IDeckLinkOutput)   
ok = dl(i)\QueryInterface(IID_IDeckLinkOutput, @*deckLinkOutput)
Protected DisplayModeSupport
Protected DisplayMode.IDeckLinkDisplayMode

*deckLinkOutput\DoesSupportVideoMode(#bmdModeHD1080i5994,#bmdFormat8BitYUV,#bmdSupportedVideoModeDefault,@DisplayModeSupport, @DisplayMode)
UpdateActivity("DisplayMode: " + Hex(DisplayMode) 
UpdateActivity(Str(DisplayMode\GetWidth()) + "x" + Str(DisplayMode\GetHeight()))
All of the above works as it should. I get the display mode and the video resolution. However when I try and return a string from DisplayMode\GetName I can't get it to work. I get anywhere from a few bytes of garbage to a string that looks like it's in Chinese or Japanese. Here is the Purebasic interface definition:

Code: Select all

Interface IDeckLinkDisplayMode Extends IUnknown                                         ;#IID_DeckLinkAPI_IDeckLinkDisplayMode 
    GetName.l(*PB_name)
    GetDisplayMode.l()
    GetWidth.l()
    GetHeight.l()
    GetFrameRate.l(*frameDuration.q, *timeScale.q)
    GetFieldDominance.l()
    GetFlags.l()
EndInterface
and how I'm calling the mothod:

Code: Select all

Protected ModeName$ = Space(255)
DisplayMode\GetName(@ModeName$)
UpdateActivity(ModeName$ + "  and just for the heck of it:  " + PeekS(@ModeName$,255,#PB_Unicode))
I've tried AllocateMemory() and SysAllocString_(space(255)) with the appropriate free memory calls and I still don't get a readable string.

This is the interface definition in Powerbasic

Code: Select all

Interface IDeckLinkDisplayMode $IID_DeckLinkAPI_IDeckLinkDisplayMode 
    Inherit IUnknown
    Method GetName(ByRef PB_name As WString) As Long
    Method GetDisplayMode() As Long
    Method GetWidth() As Long
    Method GetHeight() As Long
    Method GetFrameRate(ByRef frameDuration As Quad, ByRef timeScale As Quad) As Long
    Method GetFieldDominance() As Long
    Method GetFlags() As Long
End Interface
And my test code looks like this, which all works as advertised:

Code: Select all

Local displayModeSupport&,displayMode As IDeckLinkDisplayMode  
Local vidFormat&,Result&
vidformat = %bmdModeHD720p5994
deckLinkOutput.DoesSupportVideoMode(VidFormat,%bmdFormat8BitYUV,%bmdVideoOutputFlagDefault,displayModeSupport, displayMode)
UpdateActivity "Output Support" +  Hex$(DisplayModeSupport)
UpdateActivity "resolution: " + Str$(DisplayMode.GetWidth()) + "x" + Str$(DisplayMode.GetHeight())
DisplayMode.getName(mname$$)
UpdateActivity mName$$
I must be doing something wrong, but I am at a loss.

TIA,
Russ

Re: Decklink Com Programming

Posted: Mon Dec 18, 2023 2:36 pm
by spikey
I can't find any publicly available API documentation for this interface. Is there some? If so, post a link, we should be able to give you a more sensible answer.

In the mean time, or if not, try something like this and report back what you see in the memory viewer:

Code: Select all

Define ModeName$ = Space(255)
FillMemory(@ModeName$, 255, 0, #PB_Byte)
DisplayMode\GetName(@ModeName$)
ShowMemoryViewer(@ModeName$, 255)
CallDebugger
This zeros the string buffer before calling - which makes it easier to see what's going on in the Memory viewer.
When it breaks, the memory viewer should be showing the string content as a byte table. Have a look to see how many bytes actually got changed in the output.
1) Are only the first 4 bytes changed (if you're using the 32-bit compiler), or 8 bytes (if 64-bit version)?
If so, the function is (probably) returning a pointer to a string, not a string and you need to "dereference a double pointer".
2) If more than this is changed you're (probably) getting a string back in a different format. Change the combo to the different string views and see if any of them make sense. If so, you need to change your PeekS format to match.
3) If it's still garbage you may need to "double reference" the inbound pointer.

Re: Decklink Com Programming

Posted: Mon Dec 18, 2023 7:06 pm
by RSrole
Spikey,

Thanks for the reply. For testing I'm using the 32 bit compiler. If I ever get past this, I'll test with the 64 bit compiler too.
Without DeckLink hardware, you won't be able to use any of the api. You can download it at ttps://www.blackmagicdesign.com/support/family ... d-playback and download the sdk.

I did a bit more reading on this and com wants to return a bStr which, if I understand things correctly, Purebasic doesn't directly support. I tried using GetModelName.i(*ModelName.p-bstr) in the interface/method definition, but that didn't help either as the method wants a pointer to a bStr and calling it with dl(i)\GetModelName(@ModelName$) would not compile.

I also tried making a structure to fake a bStr
Structure bStrTest
l.l
text.s{255}
EndStructure
Protected t.bStrTest
dl(i)\GetModelName(@t)

Sadly the memory area that should be t\text was still 0's and the t\l was only the first 3 bytes set. The fourth byte is always 0. So I'm still confused, but I appreciate all of your input.

Re: Decklink Com Programming

Posted: Mon Dec 18, 2023 10:40 pm
by spikey
RSrole wrote: Mon Dec 18, 2023 7:06 pmYou can download it at...
Sorry I can't find anything that means anything to me at that link - it just looks like a list of all their downloads!
RSrole wrote: Mon Dec 18, 2023 7:06 pm I did a bit more reading on this and com wants to return a bStr
Ok, based on this information, your original interface definition should be correct. It's just the arguments you are presenting to the function call that need to change.

BSTR's must be created with SysAllocString_() and freed with SysFreeString_(). Try something like this:

Code: Select all

; ...
Define ModeName$
Define *ModeName
ModeName$ = Space(255)
*ModeName = SysAllocString_(@ModeName$)

DisplayMode\GetName(*ModeName)

Debug PeekS(*ModeName)
; ...
SysFreeString_(*ModeName)
RSrole wrote: Mon Dec 18, 2023 7:06 pm GetModelName.i(*ModelName.p-bstr)
Please be consistent though with your postings. In your original post you're asking about the 'GetName' function, in your second message you refer to 'GetModelName'. I don't know if this is significant or not, but if we're not both talking about the same thing, confusion is going to be the only result at both ends!

Re: Decklink Com Programming

Posted: Mon Dec 18, 2023 11:57 pm
by RSrole
Spikey,

You got me on the right track. It took one more step because it was returning a pointer to a pointer, as you suggested above. The following does the trick:

Define ModeName$
Define *ModeName
ModeName$ = Space(255)
*ModeName = SysAllocString_(@ModeName$)
dl(i)\GetModelName(*ModeName) ;actually returns a pointer to a pointer
Protected n = PeekL(*ModeName)
Debug "SysAllocString: " + PeekS(n,-1,#PB_Unicode)
SysFreeString_(*ModeName)
CallDebugger

Sorry about the method call confusion. The problem was with all methods that return a string. So this solution will work with all of them.

Thanks for your help
Russ

Re: Decklink Com Programming

Posted: Tue Dec 19, 2023 9:45 am
by mk-soft
Your definition is wrong and how to work with interfaces.
The return is always whether the method was executed successfully. The result values are always returned as the last parameter as ByRef

Code: Select all

r1 = Object\Method(Arg1, Arg2, ..., Result)
if r1 = #S_OK
  ;
else
  ;
Endif
Since I do not know the interface parameter description, the definition of the parameters may also be wrong.

Code: Select all

; ...

Procedure.s FormatMessage(Errorcode)

  Protected *Buffer, len, result.s

  len = FormatMessage_(#FORMAT_MESSAGE_ALLOCATE_BUFFER|#FORMAT_MESSAGE_FROM_SYSTEM,0,Errorcode,0,@*Buffer,0,0)
  If len
    result = PeekS(*Buffer, len)
    LocalFree_(*Buffer)
    ProcedureReturn result
  Else
    ProcedureReturn "Errorcode: " + Hex(Errorcode)
  EndIf

EndProcedure

Define ModeName$
Define *ModeName

r1 = DisplayMode\GetName(@*ModeName)
If r1 = #S_OK ;Or r1 = #S_FALSE
  If *ModeName
    ModeName$ = PeekS(*ModeName)
    SysFreeString_(*ModeName)
   EndIf
Else
  Error$ = FormatMessage(r1)
  Debug Error$
EndIf

Re: Decklink Com Programming

Posted: Tue Dec 19, 2023 3:32 pm
by spikey
RSrole wrote: Mon Dec 18, 2023 11:57 pm Thanks for your help
You're welcome. I'm glad to have been able to assist.
RSrole wrote: Mon Dec 18, 2023 11:57 pm dl(i)\GetModelName(*ModeName) ;actually returns a pointer to a pointer
Be careful of a memory leak there. If GetModelName also calls SysAllocString on its own behalf and returns a different pointer value, this code may leak your original allocation. (Bear in mind that I still can't see the documentation for all this though!)

Re: Decklink Com Programming

Posted: Tue Dec 19, 2023 4:04 pm
by mk-soft
SysAllocString is here wrong ...

The description should state that the result is a BSTR. This means that the pointer to the BSTR is returned.

For objects, the result of type BSTR the caller must usually release the BSTR itself with SysFreeString.
Just like in my example.

Re: Decklink Com Programming

Posted: Tue Dec 19, 2023 6:09 pm
by RSrole
I made a couple of utility procedures to make this clearer/easier:

Procedure.i CreateBstr()
Protected bStr$ = Space(255)
Protected *bStr = SysAllocString_(@bStr$)
ProcedureReturn *bStr
EndProcedure

and

Procedure.s ConvertBstr(*bStr)
If *bstr = 0:ProcedureReturn:EndIf
Protected n = PeekL(*bstr)
Protected bStr$ = PeekS(n,-1,#PB_Unicode)
SysFreeString_(*bStr)
ProcedureReturn bStr$
EndProcedure

You would use them like this:
Protected *bStr = CreateBstr()
If *bStr
dl(i)\GetModelName(*bStr)
Protected bStr$ = ConvertBstr(*bStr)
Debug ("ModelName: " + bstr$)
EndIf

I don't think there's a problem with memory leaks if you call the ConvertBstr routine. The docs say "This allocated string must be freed by the caller when no longer required." which we are doing. The caller has to create the string.

For any lurkers out there, the BlackMagic Designs Decklink products are capture and playback video cards. Inputs are outputs are usually SDI (Serial Digital Interface) although they have some with HDMI in/out as well. In the professional/broadcast world SDI is the preferred hardware interface.

There are a number of similar cards out there; AJA, Bluefish, DVS, Matrox and others. The Decklink line is the least expensive by far. It's quite powerful although it's missing LTC (timecode input) which most of the higher end cards support. To fix that, I made a LTC reader using an Arduino. Our high end recording products can use most of the cards out there, specified by the customer. Our lower end recording product only works with the Decklink as pricing precludes and of the more expensive cards.

Re: Decklink Com Programming

Posted: Tue Dec 19, 2023 6:31 pm
by mk-soft
Still wrong!

The method creates the BSTR and returns the pointer to it. To do this, you pass the pointer to the variable where the method enters the pointer to the BSTR.
After use, you must release the BSTR SysFreeString.
See my example

Re: Decklink Com Programming

Posted: Tue Dec 19, 2023 6:56 pm
by RSrole
My code is working with the actual api/sdk and hardware

Re: Decklink Com Programming

Posted: Tue Dec 19, 2023 7:09 pm
by mk-soft
RSrole wrote: Tue Dec 19, 2023 6:56 pm My code is working with the actual api/sdk and hardware
Is still completely wrong

You create a BSTR and the method writes the pointer to the BSTR in your string area.
Then you pick the pointer from your string area as LONG (where the pointer may not fit)
Pick the string with PeekS. Release your BSTR again, but not the BSTR that created the method.
Result: Memory leak

Sorry if I say it like that. That is rubbish.

P.S.
define *bstrModeName = 0
You only need to pass the pointer-variable for the BSTR ByRef '@*bstrModeName'

Re: Decklink Com Programming

Posted: Tue Dec 19, 2023 10:55 pm
by Fred
No need to be harsh mk-soft, he's just starting with PB, he will get used to it. Glad to see it's working.

Re: Decklink Com Programming

Posted: Wed Dec 20, 2023 12:27 am
by mk-soft
Fred wrote: Tue Dec 19, 2023 10:55 pm No need to be harsh mk-soft, he's just starting with PB, he will get used to it. Glad to see it's working.
Sorry for sounding a bit harsh. :wink:
I just want to prevent errors from creeping in with the handling of objects, which then eventually stop working or cause memory leaks.
The whole thing with objects is also difficult to understand and implement. I had also worked for a long time on working with DCOM (OPC server and clients) and getting everything to work properly.