libvlcrealbasicxojo

Proper use of libvlc_media_open_cb


I'm trying to use the libvlc_media_new_callbacks function to play media from memory using libvlc. This function expects pointers to four callback functions: Open, Read, Seek, and Close.

Declare Function libvlc_media_new_callbacks Lib "libvlc.dll" (Instance As Ptr, OpenCB As Ptr, ReadCB As Ptr, SeekCB As Ptr, CloseCB As Ptr, Opaque As Ptr) As Ptr

Dim opaque As Ptr = SomeOpaqueValue()
Dim handle As Ptr = libvlc_media_new_callbacks(instance, AddressOf MediaOpen, AddressOf MediaRead, AddressOf MediaSeek, AddressOf MediaClose, opaque)

The Read, Seek, and Close callbacks all work fine if I don't specify an Open callback but that means libvlc won't know how long the media is.

When I do specify an Open callback it gets invoked with the correct opaque parameter but the other callbacks get a null pointer instead. This makes it impossible to know which stream the callback should be working on.

Have I misunderstood the purpose of the open callback?

Here are my callback functions:

Shared Function MediaOpen(Opaque As Ptr, Buffer As Ptr, ByRef BufferSize As UInt64) As UInt32
  Dim r As BinaryStream = Streams.Lookup(Opaque, Nil)
  If r = Nil Then Return 1 ' invalid Opaque

  Buffer = Nil  ' Not sure what to do with this parameter
  BufferSize = r.Length
  Return 0
End Function
Shared Sub MediaClose(Opaque As Ptr)
  If Streams.HasKey(Opaque) Then Streams.Remove(Opaque)
  If Streams.Count = 0 Then Streams = Nil
End Sub
Shared Function MediaRead(Opaque As Ptr, Buffer As Ptr, BufferSize As Integer) As UInt32
  Dim r As BinaryStream = Streams.Lookup(Opaque, Nil)
  If r = Nil Then Return 0 ' invalid Opaque

  Dim mb As MemoryBlock = Buffer
  Dim data As MemoryBlock = r.Read(BufferSize)
  mb.StringValue(0, data.Size) = data
  Return data.Size
End Function
Shared Function MediaSeek(Opaque As Ptr, Offset As UInt64) As Int32
  Dim r As BinaryStream = Streams.Lookup(Opaque, Nil)
  If r = Nil Then Return 1 ' invalid Opaque
  If Offset > r.Length Then Return 1 ' invalid offset

  r.Position = Offset
  Return 0
End Function

Solution

  • According to the docs, you have to allocate a buffer and return its address in the open's Buffer parameter (you need to change it into a ByRef parm for that!). Then you'll get that same buffer ptr passed in the other functions.

    Ideally, you'd set

    Buffer = r
    

    in MediaOpen. But Xojo won't let you. Instead, you need to maintain a Dictionary in which you store r and then associate that with a value you can assign to the Buffer parm.

    Here's an example (not tested):

    Static Property OpenedBuffers as Dictionary
    Static Property OpenedBufferID as Integer
    
    Shared Function MediaOpen(Opaque As Ptr, ByRef HandleOut As Integer, ByRef BufferSize As UInt64) As UInt32
      Dim r As BinaryStream = Streams.Lookup(Opaque, Nil)
      If r = Nil Then Return 1 ' invalid Opaque
      if OpenedBuffers = nil then OpenedBuffers = new Dictionary
      OpenedBufferID = OpenedBufferID + 1
      OpenedBuffers.Value(OpenedBufferID) = r
      HandleOut = OpenedBufferID
      BufferSize = r.Length
      Return 0
    End Function
    
    Shared Function MediaRead(Handle As Integer, Buffer As Ptr, BufferSize As Integer) As UInt32
      Dim r As BinaryStream = OpenedBuffers.Value(Handle)
      ...
    
    Shared Sub MediaClose(Handle As Integer)
      Dim r As BinaryStream = OpenedBuffers.Value(Handle)
      OpenedBuffers.Remove (Handle)
      If Streams.HasKey(r) Then Streams.Remove(r)
      ...
    

    Hope that helps