fbvlc - FreeBASIC video player using libvlc

User projects written in or related to FreeBASIC.
Landeel
Posts: 749
Joined: Jan 25, 2007 10:32
Location: Brazil
Contact:

fbvlc - FreeBASIC video player using libvlc

Postby Landeel » Jun 21, 2020 15:10

I needed video playback in my WIP game, so this is how I did it.

Special thanks to the VLC authors and D.J Peters who helped figure out the callbacks.

Download source code with libraries, plugins and sample video files here: https://sourceforge.net/projects/freebasic-vlc/

Code: Select all

' ***********************************************
' * fbvlc - FreeBASIC video player using libvlc *
' ***********************************************
' By Cleber M. Casali

' Special thanks to the VLC autors, and D.J. Peters who helped me understand the callback functions.

' This code is free to use for any free or commercial project. Credit is appreciated.

' I know there are libvlc headers for FreeBASIC, but in my own project, I didn't want libvlc to be a hard dependency.

' I have only included the necessary plugins to play theora videos, which is all I need.

' If you need to play other formats, just drop the plugins in the platform's corresponding 'plugin' directory.

' ------------------------------------------------------------------

' VIDEO MODE STARTUP

'comment the next line to use fbgfx only
#define use_opengl

#ifdef use_opengl
   
   'USING OPENGL

   'OpenGL headers
   #include once "GL/gl.bi"
   #include once "GL/glext.bi"

   'vsync procedures
   dim shared glSwapInterval as function (byval interval as gluint) as gluint  'glXSwapIntervalSGI  glXSwapIntervalMESA  wglSwapIntervalEXT
   dim shared glXSwapIntervalEXT as sub (display_ptr as any ptr, byval drawable as gluint, byval interval as gluint)  'PFNGLXSWAPINTERVALEXTPROC  'DRI2SwapInterval 'glXSwapIntervalEXT

   sub searchSwapIntervalMethod()
      'find a suitable method for vsync
      if glXSwapIntervalEXT=0 then glXSwapIntervalEXT = ScreenGLProc("glXSwapIntervalEXT")
      if glXSwapIntervalEXT=0 then glXSwapIntervalEXT = ScreenGLProc("DRI2SwapInterval")
      if glSwapInterval=0 then glSwapInterval = ScreenGLProc("wglSwapIntervalEXT")
      if glSwapInterval=0 then glSwapInterval = ScreenGLProc("glXSwapIntervalSGI")
      if glSwapInterval=0 then glSwapInterval = ScreenGLProc("glXSwapIntervalMESA")
   end sub

   #ifdef __FB_UNIX__
      'declare some GLX stuff
      extern "C" lib "GL"
         declare function glXGetCurrentDisplay() as any ptr
         declare function glXGetCurrentDrawable() as gluint
      end extern
   #endif

   sub gl_setvsync(byval interval as integer=1) 'enable or disable vsync
      if glSwapInterval then glSwapInterval(interval) : exit sub
      #ifdef __FB_UNIX__
         if glXSwapIntervalEXT then glXSwapIntervalEXT(glXGetCurrentDisplay(), glXGetCurrentDrawable(), interval) : exit sub
      #endif
   end sub


   sub gl_init(byval x as integer, byval y as integer) 'init opengl stuff

      glMatrixMode(GL_PROJECTION)
   
      glLoadIdentity

      glPushMatrix 'store matrix

      glMatrixMode(GL_MODELVIEW)

      glLoadIdentity()

      glOrtho -x\2, x\2, y\2, -y\2, 0.0 , 10.0 ' ortho with (0,0) at center

      glPushMatrix() 'store matrix

      glClearColor 0.0, 0.0, 0.0, 1.0
   
      glClear GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT 'clear gl buffer

      glDisable (GL_DEPTH_TEST)
   
      glDisable (GL_TEXTURE_2D)
      
      gl_setVsync(1)
   
      flip()

   end sub


   sub gl_Put(byval x as glfloat, byval y as glfloat, byval buffer as any ptr)
      if buffer=0 then exit sub
      dim as integer xx,yy,pitch, bypp
      dim as ubyte ptr pixels
      if imageinfo(buffer,xx,yy,bypp,pitch,pixels) then exit sub
      dim as integer pitch4=pitch/bypp
      glRasterPos2d(0, 0)
      glPixelZoom( 1, -1) 'otherwise it will render upside-down
      glBitmap 0, 0, 0, 0, (x - xx/2), -(y - yy/2), 0 'fix raster and move bitmap
      glDrawPixels(pitch4, yy,  GL_BGRA,  GL_UNSIGNED_BYTE, pixels) 'deprecated - you should use textures instead
   end sub
   
   sub initScreen(byval x as integer=1280, byval y as integer=720, byval fullscreen as integer=0, byval syncrate as integer=60)
      screenres x, y, 32, 1, iif(fullscreen,&h01,0) or &h02, syncrate 'set video mode
      WindowTitle "fbvlc - OpenGL" 'set window title
      gl_init(x,y)
   end sub
   
#else

   'NOT USING OPENGL

   sub initScreen(byval x as integer=1280, byval y as integer=720, byval fullscreen as integer=0, byval syncrate as integer=60)
      screenres x, y, 32, 2, iif(fullscreen,&h01,0) or 0, syncrate 'set video mode
      WindowTitle "fbvlc - fbgfx" 'set window title
   end sub

#endif


' ------------------------------------------------------------------

' FBVLC


'libvlc types
type libvlc_media_player_t as any
type libvlc_instance_t as any
type libvlc_media_t as any
type libvlc_state_t as long
type libvlc_video_lock_cb as function cdecl(byval opaque as any ptr, byval planes as any ptr ptr) as any ptr
type libvlc_video_unlock_cb as sub cdecl(byval opaque as any ptr, byval picture as any ptr, byval planes as any const ptr ptr)
type libvlc_video_display_cb as sub cdecl(byval opaque as any ptr, byval picture as any ptr)

'yeah, some enumerations
enum
   libvlc_NothingSpecial = 0
   libvlc_Opening
   libvlc_Buffering
   libvlc_Playing
   libvlc_Paused
   libvlc_Stopped
   libvlc_Ended
   libvlc_Error
end enum

'our libvlc class
type fbvlctype
   started as boolean=false
   libavutil_ptr as any ptr
   libswscale_ptr as any ptr
   libvlccore_ptr as any ptr
   libvlc_ptr as any ptr
   instance as libvlc_instance_t Ptr
   player as libvlc_media_player_t Ptr
   playerState as libvlc_state_t
   videoEnded as boolean=true
   media as libvlc_media_t Ptr
   vlcmutex as any ptr
   internal_image as any ptr
   image as any ptr
   xsize as integer
   ysize as integer
   pitch as integer
   declare sub init()
   declare sub load(byval filename as string="")
   declare sub setVolume(byval volume as integer=100)
   declare sub playVideoFile(byval filename as string="", byval volume as integer=100)
   declare sub sync()
   declare sub drawToScreen()
   declare sub unload()
   declare sub finish()
end type

static shared fbvlc as fbvlctype 'libvlc object

'declare libvlc procedures
dim shared libvlc_get_version as function cdecl() As ZString Ptr
dim shared libvlc_new as function cdecl (ByVal argc As Integer, ByVal argv As any Ptr) As libvlc_instance_t Ptr
dim shared libvlc_release as sub cdecl (ByVal p_instance As libvlc_instance_t Ptr)
dim shared libvlc_media_new_location as function cdecl (ByVal p_mi As libvlc_media_t Ptr,  pt As ZString Ptr) As libvlc_media_t Ptr
dim shared libvlc_media_new_path as function cdecl (ByVal p_mi As libvlc_instance_t Ptr, pt As ZString Ptr) As libvlc_media_t Ptr
dim shared libvlc_media_player_new_from_media as function cdecl (ByVal p_mi As libvlc_media_t Ptr) As libvlc_media_player_t Ptr
dim shared libvlc_media_release as sub cdecl (ByVal p_md As libvlc_media_t Ptr)
dim shared libvlc_media_player_set_hwnd as sub cdecl (ByVal p_mi As libvlc_media_player_t Ptr, ByVal drawable As Any Ptr)
dim shared libvlc_media_player_set_xwindow as sub cdecl (ByVal p_mi As libvlc_media_player_t Ptr, ByVal xid As ulong)
dim shared libvlc_media_player_play as function cdecl (ByVal p_mi As libvlc_media_player_t Ptr) As Integer
dim shared libvlc_media_player_release as sub cdecl (ByVal p_mi As libvlc_media_player_t Ptr)
dim shared libvlc_media_player_stop as sub cdecl (ByVal p_mi As libvlc_media_player_t Ptr)
dim shared libvlc_audio_set_volume as function cdecl (ByVal p_mi As libvlc_media_player_t Ptr, ByVal i_volume As Integer) As Integer
dim shared libvlc_media_player_get_length as function cdecl (ByVal p_mi As libvlc_media_player_t Ptr) As Integer
dim shared libvlc_media_player_get_time as function cdecl (ByVal p_mi As libvlc_media_player_t Ptr) As Integer
dim shared libvlc_media_player_get_position as function cdecl (ByVal p_mi As libvlc_media_player_t Ptr) As Integer
dim shared libvlc_video_set_marquee_string as sub cdecl (ByVal p_mi As libvlc_media_player_t Ptr, ByVal option As UInteger, ByVal psz_text As ZString Ptr)
dim shared libvlc_video_set_marquee_int as sub cdecl (ByVal p_mi As libvlc_media_player_t Ptr, ByVal option As UInteger, ByVal i_val As Integer)
dim shared libvlc_media_player_is_playing as function cdecl (ByVal p_mi As libvlc_media_player_t Ptr) As boolean
dim shared libvlc_set_fullscreen as sub cdecl (ByVal p_mi As libvlc_media_player_t Ptr, byval b_fullscreen As boolean)
dim shared libvlc_video_set_key_input as sub cdecl (ByVal p_mi As libvlc_media_player_t Ptr, byval tovlc As boolean)
dim shared libvlc_video_set_format as sub cdecl (byval mp as libvlc_media_player_t ptr, byval chroma as const zstring ptr, byval width as ulong, byval height as ulong, byval pitch as ulong)
dim shared libvlc_media_player_get_state as function cdecl (byval p_mi as libvlc_media_player_t ptr) as libvlc_state_t
dim shared libvlc_video_set_callbacks as sub cdecl (byval mp as libvlc_media_player_t ptr, byval lock_ as libvlc_video_lock_cb, byval unlock_ as libvlc_video_unlock_cb, byval display as libvlc_video_display_cb, byval opaque as any ptr)


sub fbvlctype.init()  'initialize libvlc
   
   if this.started then exit sub 'leave if already started
   
   'create a scren sized image
   screeninfo this.xsize, this.ysize
   this.internal_image=imagecreate( this.xsize, this.ysize, rgba(0,0,0,255), 32)
   this.image=imagecreate( this.xsize, this.ysize, rgba(0,0,0,255), 32)

   'create a mutex
   this.vlcmutex=mutexcreate()

   'load libraries
   #Ifdef __FB_WIN32__
      if this.libvlc_ptr=0 then
         #ifdef __FB_64BIT__
            'load from dll64\libvlc-win64\
            SetEnviron ("VLC_PLUGIN_PATH="+exepath()+"\dll64\libvlc-win64\plugins")
            this.libvlccore_ptr = DyLibLoad(exepath()+"\dll64\libvlc-win64\libvlccore.dll")
            this.libvlc_ptr = DyLibLoad(exepath()+"\dll64\libvlc-win64\libvlc.dll")
         #else
            'load from dll\libvlc-win64\
            SetEnviron ("VLC_PLUGIN_PATH="+exepath()+"\dll\libvlc-win32\plugins")
            this.libvlccore_ptr = DyLibLoad(exepath()+"\dll\libvlc-win32\libvlccore.dll")
            this.libvlc_ptr = DyLibLoad(exepath()+"\dll\libvlc-win32\libvlc.dll")
         #endif
      end if
      if this.libvlc_ptr=0 then this.libvlc_ptr = DyLibLoad("libvlc.dll") 'try to load libvlc from system
   #Else
      if this.libvlc_ptr=0 then this.libvlc_ptr = DyLibLoad("libvlc.so") 'try to load libvlc from system
      if this.libvlc_ptr=0 then
         #ifdef __FB_64BIT__
            'load from lib64\libvlc\
            SetEnviron ("VLC_PLUGIN_PATH="+exepath()+"/lib64/libvlc/vlc/plugins")
            this.libavutil_ptr = DyLibLoad(exepath()+"/lib64/libavutil.so.55")
            this.libswscale_ptr = DyLibLoad(exepath()+"/lib64/libswscale.so.4")
            this.libvlccore_ptr = DyLibLoad(exepath()+"/lib64/libvlc/libvlccore.so.9")
            this.libvlc_ptr = DyLibLoad(exepath()+"/lib64/libvlc/libvlc.so")
         #else
            'load from lib32\libvlc\
            'the 32-bit version needs libidn11:i386 to run on 64-bit systems
            SetEnviron ("VLC_PLUGIN_PATH="+exepath()+"/lib32/libvlc/vlc/plugins")
            this.libavutil_ptr = DyLibLoad(exepath()+"/lib32/libavutil.so.55")
            this.libswscale_ptr = DyLibLoad(exepath()+"/lib32/libswscale.so.4")
            this.libvlccore_ptr = DyLibLoad(exepath()+"/lib32/libvlc/libvlccore.so.9")
            this.libvlc_ptr = DyLibLoad(exepath()+"/lib32/libvlc/libvlc.so")
         #endif
      end if
   #endIf
   
   ' ERROR loading libvlc
   if this.libvlc_ptr=0 then
      print "Could not load libvlc. Video playback will not work."
      end 1
   end if

   'get procedure pointers
   libvlc_get_version=DyLibSymbol(this.libvlc_ptr,"libvlc_get_version")
   libvlc_new =DyLibSymbol(this.libvlc_ptr,"libvlc_new")
   libvlc_release=DyLibSymbol(this.libvlc_ptr,"libvlc_release")
   libvlc_media_new_location=DyLibSymbol(this.libvlc_ptr,"libvlc_media_new_location")
   libvlc_media_new_path=DyLibSymbol(this.libvlc_ptr,"libvlc_media_new_path")
   libvlc_media_player_new_from_media=DyLibSymbol(this.libvlc_ptr,"libvlc_media_player_new_from_media")
   libvlc_media_release=DyLibSymbol(this.libvlc_ptr,"libvlc_media_release")
   libvlc_media_player_set_hwnd=DyLibSymbol(this.libvlc_ptr,"libvlc_media_player_set_hwnd")
   libvlc_media_player_set_xwindow=DyLibSymbol(this.libvlc_ptr,"libvlc_media_player_set_xwindow")
   libvlc_media_player_play=DyLibSymbol(this.libvlc_ptr,"libvlc_media_player_play")
   libvlc_media_player_release=DyLibSymbol(this.libvlc_ptr,"libvlc_media_player_release")
   libvlc_media_player_stop=DyLibSymbol(this.libvlc_ptr,"libvlc_media_player_stop")
   libvlc_audio_set_volume=DyLibSymbol(this.libvlc_ptr,"libvlc_audio_set_volume")
   libvlc_media_player_get_length=DyLibSymbol(this.libvlc_ptr,"libvlc_media_player_get_length")
   libvlc_media_player_get_time=DyLibSymbol(this.libvlc_ptr,"libvlc_media_player_get_time")
   libvlc_media_player_get_position=DyLibSymbol(this.libvlc_ptr,"libvlc_media_player_get_position")
   libvlc_video_set_marquee_string=DyLibSymbol(this.libvlc_ptr,"libvlc_video_set_marquee_string")
   libvlc_video_set_marquee_int=DyLibSymbol(this.libvlc_ptr,"libvlc_video_set_marquee_string")
   libvlc_media_player_is_playing=DyLibSymbol(this.libvlc_ptr,"libvlc_media_player_is_playing")
   libvlc_set_fullscreen=DyLibSymbol(this.libvlc_ptr,"libvlc_set_fullscreen")
   libvlc_video_set_key_input=DyLibSymbol(this.libvlc_ptr,"libvlc_video_set_key_input")
   libvlc_media_player_get_state=DyLibSymbol(this.libvlc_ptr,"libvlc_media_player_get_state")
   libvlc_video_set_format=DyLibSymbol(this.libvlc_ptr,"libvlc_video_set_format")
   libvlc_video_set_callbacks=DyLibSymbol(this.libvlc_ptr,"libvlc_video_set_callbacks")


   'vlc command-line parameters
   Dim As Any Ptr vlc_args(0 To ...) = { @"--no-xlib", _
                                @"--quiet" }

'   Dim As Any Ptr vlc_args(0 To ...) = { @"--no-xlib", _
'                                @"-vvv", _
'                                @"--ignore-config", _
'                                @"--quiet", _

   'create vlc instance
   this.instance = libvlc_new (UBound(vlc_args) + 1, @vlc_args(0))       

   'get screen/image info
   if this.internal_image=0 then ' we use the screen
      screeninfo this.xsize, this.ysize, , , this.pitch
   else ' we use an image
      'ImageInfo( image [, [width] [, [height] [, [bypp] [, [pitch] [, [pixdata] [, size]]]]]] )
      imageinfo this.internal_image, this.xsize, this.ysize, , this.pitch
   end if

   this.started=true 'it's started!
   
end sub


function libvlc_lockCB cdecl(byval image as any ptr, byval pPlanes as any ptr ptr) as any ptr 'lock callback
  if image=0 then
    ' no image : lock and get the address of the screen pixel buffer
    screenlock
    pPlanes[0]=screenptr()
  else
   ' lock and use image get the address of the pixel buffer
   mutexlock(fbvlc.vlcmutex)
   Imageinfo(image,,,,,pPlanes[0])
  end if
  return 0
end function

sub libvlc_unlockCB cdecl(byval image as any ptr, byval pPicture as any ptr, byval pPlanes as any const ptr ptr) 'unlock callback
   if image=0 then
      'no image : unlock screen
      screenunlock
   else
      'unlock image
      mutexunlock(fbvlc.vlcmutex)
   end if
end sub

sub libvlc_displayCB cdecl(byval image as any ptr, byval pPicture as any ptr) 'display callback - unused
   'UNUSED
   'if image=0 then return
end sub

sub fbvlctype.sync() 'update fbvlc.image
   mutexlock(this.vlcmutex)
      put this.image, (0,0), this.internal_image, pset 'update image buffer
   mutexunlock(this.vlcmutex)
   this.playerState = libvlc_media_player_get_state(this.player) 'update libvlc state
   if this.playerState=libvlc_Ended then this.videoEnded=true 'check if video ended
end sub


sub fbvlctype.drawToScreen()

   #ifdef use_opengl
      'render using OpenGL
      gl_Put(0,0,this.image)
      glFinish()
   #else
      'render using fbgfx
      put (0,0),this.image,PSET
   #endif

end sub


sub fbvlctype.load(byval filename as string) 'load video file

   if this.started=false then this.init() 'init if not already started

   if filename="" then filename=dir("*.ogv") 'search .ogv files
   if filename="" then exit sub 'none found - exit
   
   this.unload()

   this.videoEnded=false
      
   this.media = libvlc_media_new_path(this.instance, strptr(filename)) 'load media
   
   this.player = libvlc_media_player_new_from_media(this.media) 'create player
   
   if this.media then
      libvlc_media_release(this.media) 'release media as it's no longer needed
      this.media=0
   end if
   
   libvlc_video_set_format(this.player, @"BGRA", this.xsize, this.ysize, this.pitch) 'set image parameters
   
   'libvlc_video_set_callbacks(fbvlc.player, @libvlc_lockCB, @libvlc_unlockCB, @libvlc_displayCB, fbvlc.internal_image)
   libvlc_video_set_callbacks(this.player, @libvlc_lockCB, @libvlc_unlockCB, 0, this.internal_image) 'configure callbacks

   ' start the video/audio decoder stream
   var result = libvlc_media_player_play(this.player) 'play!
   if (result<>0) then
      'ERROR!
      print "libvlc_media_player_play returned an error!"
      libvlc_media_player_release(this.player) 'release media
      libvlc_release(this.instance) 'release instance
      exit sub 'leave
   end if

end sub


sub fbvlctype.setVolume(byval volume as integer) 'set audio volume
   libvlc_audio_set_volume(this.player, volume)
end sub


sub fbvlctype.unload() 'stop and clear

   if this.player then
      
      libvlc_media_player_stop(this.player) 'stop player
      
      libvlc_media_player_release(this.player) 'close player
      
      this.player=0
      
   end if

   this.videoEnded=true

end sub


sub fbvlctype.playVideoFile(byval filename as string, byval volume as integer) 'simple procedure to play videos

   if this.started=false then this.init() 'init if not already started

   this.load(filename) 'load video

   this.setVolume(volume) 'set volume
   
   do

      screensync() 'wait sync rate interval
      
      flip() 'swap buffers

      this.sync() 'update image buffer
      
      this.drawToScreen() 'render to screen
      
   loop until multikey(1)=true or this.videoEnded=true 'wait for ESC or video ended

   this.unload()
   
end sub


sub fbvlctype.finish() 'finish libvlc
   libvlc_release(this.instance) 'close vlc instance
   if fbvlc.libavutil_ptr then dylibfree(fbvlc.libavutil_ptr)
   if fbvlc.libswscale_ptr then dylibfree(fbvlc.libswscale_ptr)
   if fbvlc.libvlccore_ptr then dylibfree(fbvlc.libvlccore_ptr)
   if fbvlc.libvlc_ptr then dylibfree(fbvlc.libvlc_ptr)
end sub


' ------------------------------------------------------------------

' simple example:

chdir exepath() 'set the working dir to the exe dir

initScreen(1280,800,0,60) 'set video mode

fbvlc.init() 'init fbvlc

fbvlc.playVideoFile("./small.ogv",100) 'play a video

sleep 200,1

fbvlc.playVideoFile("./sample.ogv",100)  'play a video

sleep 200,1

fbvlc.finish()  'finish

Return to “Projects”

Who is online

Users browsing this forum: No registered users and 5 guests