Code archives/Graphics/Use VLC DLL to play video

This code has been declared by its author to be Public Domain code.

Download source code

Use VLC DLL to play video by BlitzSupport2015
Basic proof-of-concept VLC-based player.

This very simple example is Windows-specific, but the same methods of LibVLC usage should apply on other platforms.

Please note that you need to place this code in a folder alongside copies of libvlc.dll and libvlccore.dll, plus the /plugins folder and contents, from a 32-bit VLC installation (or build yourself from sources if so inclined). This applies even on 64-bit Windows.

The installation should look something like this:



Known problems/limitations:

1) LibVLC writes out any debug/error messages to console; these would not appear in a GUI-built application;

2) Only seems to work with non-Unicode filenames, not sure if this is down to :String or the use of String.ToCString, though String.ToWString didn't work.

3) Licensing: warning -- I am not a lawyer, so this note is only my understanding written out:

LibVLC is LGPL-licensed, and so can be distributed in DLL form alongside your program, without the need to release your program's own source code. However, the codec-specific plugins are a licensing minefield, so you should probably research which ones are also LGPL, encode in one of these formats, and distribute only the specific plugins you need. [EDIT: Note that VLC itself, the player application, is licensed under the GPL.)

Tested with 32-bit LibVLC DLLs, version 2.2.0, copied from VLC Media Player 2.2.1, on Windows 7 64-bit only.
' MaxGUI/Win32-specific example!

' See description -- some setup required!

' Resources:

' https://wiki.videolan.org/LibVLC_Tutorial/
' https://www.videolan.org/developers/vlc/doc/doxygen/html/modules.html
' Search engine + VLC function names!

SuperStrict

Import maxgui.drivers

' LibVLC functions...

Global libvlc_new:Byte Ptr							(argc:Int, argv:Byte Ptr)			"win32"
Global libvlc_release:Byte Ptr						(instance:Byte Ptr)					"win32"	' Void return

Global libvlc_media_new_path:Byte Ptr				(instance:Byte Ptr, path:Byte Ptr)	"win32"
Global libvlc_media_release:Int						(media:Byte Ptr)					"win32"	' Void return

Global libvlc_media_player_new_from_media:Byte Ptr	(media:Byte Ptr)					"win32"
Global libvlc_media_player_set_hwnd:Int				(player:Byte Ptr, hwnd:Int)			"win32"	' Void return
Global libvlc_media_player_play:Int					(player:Byte Ptr)					"win32"
Global libvlc_media_player_stop:Int					(player:Byte Ptr)					"win32"	' Void return
Global libvlc_media_player_release:Int				(player:Byte Ptr)					"win32"	' Void return

Local file:String = RequestFile ("Select a media file...")

If Not file Then End

' Assign functions from DLL...

Global libvlc:Int = LoadLibraryA ("libvlc.dll")

If libvlc

	libvlc_new							= GetProcAddress (libvlc, "libvlc_new")
	libvlc_release						= GetProcAddress (libvlc, "libvlc_release")
	
	libvlc_media_new_path				= GetProcAddress (libvlc, "libvlc_media_new_path")
	libvlc_media_player_new_from_media	= GetProcAddress (libvlc, "libvlc_media_player_new_from_media")
	libvlc_media_release				= GetProcAddress (libvlc, "libvlc_media_release")
	
	libvlc_media_player_set_hwnd		= GetProcAddress (libvlc, "libvlc_media_player_set_hwnd")
	libvlc_media_player_play			= GetProcAddress (libvlc, "libvlc_media_player_play")
	libvlc_media_player_stop			= GetProcAddress (libvlc, "libvlc_media_player_stop")
	libvlc_media_player_release			= GetProcAddress (libvlc, "libvlc_media_player_release")
	
	Local instance:Byte Ptr
	Local media:Byte Ptr
	Local player:Byte Ptr
	
	' Create VLC instance...
	
	instance = libvlc_new (0, Null)
	
	If instance
	
		' Load media from filename...
		
		media = libvlc_media_new_path (instance, file.ToCString ())
		
		If media
		
			' Create VLC player...
			
			player = libvlc_media_player_new_from_media (media)
			
			If player
			
				' Blitz window...
			
				Local window:TGadget = CreateWindow ("Media player", 320, 200, 640, 480)
				
				If window
				
					' Inner client area of window...
					
					Local hwnd:Int = QueryGadget (window, QUERY_HWND_CLIENT)
					
					' Tell VLC to render into client area...
					
					libvlc_media_player_set_hwnd player, hwnd
					
					' Play!
					
					libvlc_media_player_play player
	
					Repeat
						
						Select WaitEvent ()
							
							Case EVENT_WINDOWCLOSE
								Exit

						End Select
						
					Forever
					
					' Stop and release VLC player...
					
					If player
						libvlc_media_player_stop	player
						libvlc_media_player_release	player
					EndIf
					
				EndIf
			
			EndIf
		
			' Release media...
			
			libvlc_media_release media

		EndIf
		
		' Release VLC instance...
		
		libvlc_release instance
		
	EndIf
	
EndIf

Comments

BlitzSupport2015
Minor tweak to play from URL; note that same limitations will apply as per my note above on strings (may need to un-hex URLs encoded with %20 and so on)...




atv2016
James can you send me an email, i'm trying to contact you in regards to this. Thank you very much.


wmaass2016
Hmm, that works nicely for Youtube videos as well. Cool...


Guy Fawkes2016
Can we please get a Blitz3D version of this? I would very much like to use 1080p HD in my demos! :)

Thanks a lot! =)

~GF


grable2016
Here you go Guy :) Been many years since i used Blitz3D, surprisingly i remembered most of it hehe

LIBVLC + video frame capture userlib


Guy Fawkes2016
Thanks alot, grable! :)

~GF


Guy Fawkes2016
How would I apply this to a Texture?


grable2016
How would I apply this to a Texture?
Since VLC draws on top of whatever blitz does, you cant do it directly. But its possible using another window and copying from that window to a bitmap.

I was bored, so i did just that ;)
This requires a custom userlib though, and so as to not clutter up this thread im posting a link: hwndbuffer.7z

EDIT: Fixed crash and rendering bugs.


Guy Fawkes2016
Brother. It's still crashing... :/

With userlib not found for the function, "libvlc_new ( )". Even though I SPECIFICALLY put the DLL in the same folder as the code, and added the up-to-date libvlc.bb to the same folder as the code. and added the DECLs to my DECLs folder :(

~GF


grable2016
Hmm thats weird, it works here..

What version of Blitz3D?
Which function does it fail on?

EDIT: Dont forget to do as the top post says, copy both vlc dlls AND the plugins folder.

Lets continue this in another thread here.


grable2016
For completeness heres how to get the current frame as a TPixmap or TImage in BlitzMax using callbacks.




BlitzSupport2016
Nice one, grable!


Hezkore2016
I'm getting quite a lot of crashes when streaming video.
It keeps throwing me to random parts of the code each time too, so it's real hard to track down where the problem comes from...

Is it possible to make libvlc_video_set_callbacks accept a Method instead of a Function for lock_cb?


Kryzon2016
It's not possible, because a Function in BlitzMax is like a "static" function from C++, they are not associated with an object. Methods operate on an object, so you have direct access to the fields of the object -- you can use the Self keyword, for example, something that you can't do with Functions.

I think you should be able to use Functions contained within types, like this:
Type VLCCallbacks

	Function lock:Byte Ptr( ctx:Byte Ptr, planes:Byte Ptr Ptr )

		'...

	End Function

End Type

libvlc_video_set_callbacks( player, VLCCallbacks.lock, ... )
...if it helps with organisation.

EDIT: If you really need to use a Method, you can send the BlitzMax object as the "ctx" (context) parameter, which is a private data value that you use for whatever you want.

Type VLCCallbacks

	Function lock:Byte Ptr( ctx:Byte Ptr, planes:Byte Ptr Ptr )

		myObject:MyClass = MyClass( ctx[ 0 ] ) 'Cast the pointer to an object.
		Assert( myObject <> Null ) 'Good programming practice. When built on Release, this line does nothing.

		myObject.bla( planes ) 'Call the method of the object.

	End Function

End Type

Type MyClass

	Method bla( planes:Byte Ptr Ptr )
		'...
	End Method

End Type
I don't know if the casting is done like that, I can't test it at the moment.


grable2016
Casting from anything not an Object wont work im afraid, but you can define the argument as an Object and it should work.
And since the context is the first argument you can even use the function pointer of the method directly.

Import BRL.Reflection

Type TTest
	Method Lock:Byte Ptr( planes:Byte Ptr Ptr)
	EndMethod
	
	Method UnLock:Byte Ptr( picture:Byte Ptr, planes:Byte Ptr Ptr)
	EndMethod
EndType

Local o:Test = New TTtest
Local t:TTypeId = TTypeId.ForObject(o)
Local lock:Byte Ptr = bbRefMethodPtr( o, t.FindMethod("Lock")._index)
Local unlock:Byte Ptr = bbRefMethodPtr( o, t.FindMethod("UnLock")._index)

Extern "C"
	' from BRL.Reflection to get method pointer
	Function bbRefMethodPtr:Byte Ptr( obj:Object, index:Int)
EndExtern

' needed to pass the object pointer through unchanged
Global libvlc_video_set_callbacks_object( player:Byte Ptr, lock:Byte Ptr, unlock:Byte Ptr, display:Byte Ptr, ctx:Object) = Byte Ptr libvlc_video_set_callbacks

libvlc_video_set_callbacks_object( player, lock, unlock, Null, o)
libvlc wont know about object ownership though, so you have to make sure the object is kept around in some other place (or bump its refcount)

Note that im using reflection here, but you could just as well use pointers to get at the class and the method yourself if you know its index beforehand.


Hezkore2016
I'm having some trouble understanding all this heh.
Pointers and reflections isn't something I'm very familiar with.

Basically what I'm trying to do is create a Type of this, one that does all the work for you.
So in the end you'd just have to call something like LoadVideo(file)

Here's what I've got so far.
Notice that I'm using a global image atm. since I couldn't set a Method as the callback.

easyvlc.bmx


Here's an example using it.
This crashes all the time for me though, don't know why.
And it keeps saying that the error is something different each time.
Sometimes it just crashes and doesn't even give me an error.
I have gotten "bad refs:obj=$2f403d0 refs=$0" a few times though.
But it seems the problem is with either locking the image, or drawing the image.


I'm guessing the crash is because I'm not actually drawing the image at the callback, so I'm accessing the image while VLC is drawing to it.


grable2016
Hmmm that example of yours has some weird behaviors for sure.

Yeah, some of the crashes are the result of reading and writing to the image at the same time. Doing some locking and/or using a second buffer should get rid of them.

It seems to be the mixing of a Type and libvlc that causes random crashes elsewhere though...
I managed to get rid of some of them by moving things out of the methods (like Draw) and into the main loop,
which is odd to say the least!

And there are differences between debug-mode and release-mode.. In Debug i get "scope stack underflow", but using GCEnter/GCLeave in the callback functions fixes that.

It would appear that the threaded nature of libvlc and blitzmax gc does not like each other very much.
No matter what i try i get crashes somewhere, but my other tests that doesnt use any types works perfectly.
Go figure :/

Heres what i ended up with, they arent pretty and dont work perfectly but maybe youl figure something out...

This one only works in Debug mode and NON-threaded mode and still has issues.

This test works in Release+Threaded mode and requires a module i made a while back grb.libvlc.7z (it has one sample: "libvlc_trackdesc_test.bmx").
It still crashes on exit though...



Guy Fawkes2016
It would be lovely to have a non-crashing video stream from webcam to internet. =)

~GF


LT2016
Have not found a way to get the video size (or aspect ratio). It always returns 0 x 0. :/

EDIT: Getting the video size (but not aspect ratio) works after a few seconds of playing. Looking for a better way...


BlitzSupport2016
Been having a go at getting the width/height data by parsing the media before trying to play, but I'm running into a crash -- see below for *** CRASH HERE ***

It looks to me as if libvlc_media_tracks_get should allocate the necessary data structures and fill in my pointer (tracks:Byte Ptr), but that just crashes. If I allocate a random lump of memory myself, it gets the data... but then crashes with two illegal instruction errors!

Anyone have any idea what's up? (grable, Kryzon?!)

I'm a bit stumped at this point...




Guy Fawkes2016
It would be lovely to have a non-crashing video stream from webcam to internet. =)

Thanks guys! =)

~GF


col2016
Hey guys,

Just found this thread and immediately thought that I had the exact same symtpoms with the FMod encoder using callbacks into Blitzmax code.

The problem I had was that the callback is being called from a different thread that was created inside FMod and the BlitzMax garbage collector doesn't know anything about that thread. It caused havoc and random crashes. The only way to fix my issue was to move the callback code into c++ ( or c ) where there is no garbage collector.

I'm not saying it's the issue - just that I had the same symptoms and it *may* be related.

EDIT: After reading grables last message with the stack mentioned then I'd say that it the same issue that I had.


col2016
BlitzSupport...
I couldn't resist taking a stab with your latest posted example.

I'm testing in NG x32 on Windows10 and it crashes if I have the functions use the "win32" calling convention. I'm using the lastest version of vlc from http://videolan.mirrors.uk2.net/vlc/2.2.3/win32/vlc-2.2.3-win32.zip
If it's not crashing for you on legacy max then oh well :D we'll ignore that bit for the time being.

Also the line trying to get count and tracks needs to pass the address of the tracks variable into it and not its value so use

Local count:Int = libvlc_media_tracks_get (media, Varptr tracks)


Hope it helps!


BlitzSupport2016
Hmm, interesting, thanks Col -- will have another look with all this in mind!


Guy Fawkes2016
@BlitzSupport or somebody here currently, can you build a small demo of a server & client that is able to use this to stream from a webcam, an MP4 real time? I'd like to use that Blitz3D example grable made, but he says it's unstable. :(

Thanks!

~GF


col2016
@Guy Fawkes
I speak for myself and no I won't
build a small demo of a server & client that is able to use this to stream from a webcam
.


Code Archives Forum