Riproduzione standard dei file video con le funzioni esterne di GStreamer

Da Gambas-it.org - Wikipedia.

Le funzioni esterne dell'API di GStreamer consentono due modalità per riprodurre un file video:

  • all'interno di una finestra distinta dal Form principale;
  • all'interno di un oggetto capace di supportare il video e in generale le immagini, posto sul Form principale.

Facciamo riferimento, per i casi e gli esempi di codice che seguono, alla pagina della WIKI, nelle quali è stata descritta la gestione del sonoro in GStreamer. Lì si potrà avere contezza anche delle funzioni esterne necessarie di GStreamer dichiarate ed utilizzate.


Riproduzione del video all'interno di una finestra distinta da Form principale

Per ottenere la riproduzione di un file video all'interno di una finestra distinta da Form principale, e generata automaticamente da GStreamer, sarà sufficiente utilizzare la funzione esterna:

GstElement * gst_element_factory_make(const gchar *factoryname, const gchar *name)

alla quale si passerà il nome dell'Elemento, denominato "playbin" ed una denominazione per l'Elemento appena creato. È possibile passare NULL come argomento name per ottenere un unico nome predefinito.


Mostriamo di seguito un semplice esempio per riprodurre un file video all'interno di una finestra generata da GStreamer distinta dal Form di Gambas:

Private video As Pointer


Library "libgstreamer-1.0"

Private Enum GST_STATE_VOID_PENDING = 0, GST_STATE_NULL, GST_STATE_READY, GST_STATE_PAUSED, GST_STATE_PLAYING
Private Const GST_FORMAT_TIME As Integer = 3

' gst_init (int *argc, char **argv[])
' Initializes the GStreamer library, setting up internal path lists, registering built-in elements, and loading standard plugins.
Private Extern gst_init(argc As Pointer, argv As Pointer)

' GstElement * gst_element_factory_make(const gchar *factoryname, Const gchar * name)
' Create a new element of the type defined by the given element factory.
Private Extern gst_element_factory_make(factoryname As String, name As String) As Pointer

' gchar * g_filename_to_uri (const gchar *filename, const gchar *hostname, GError **error)
' Converts an absolute filename to an escaped ASCII-encoded URI.
Private Extern g_filename_to_uri(filename As String, hostname As String, GError As Pointer) As String
 
' void g_object_set(gpointer object, const gchar *first_property_name, ...)
' Sets properties on an object.
Private Extern g_object_set(gobject As Pointer, key As String, value As String, tertium As String)

' GstStateChangeReturn gst_element_set_state(GstElement *element, GstState state)
' Sets the state of the element.
Private Extern gst_element_set_state(gstelement As Pointer, state As Integer) As Integer

' GstStateChangeReturn gst_element_get_state (GstElement *element, GstState *state, GstState *pending, GstClockTime timeout)
' Gets the state of the element.
Private Extern gst_element_get_state(e As Pointer, state As Pointer, pen As Pointer, timeout As Long)

' gboolean gst_element_query_duration (GstElement *element, GstFormat format, gint64 *duration)
' Queries an element for the total stream duration in nanoseconds.
Private Extern gst_element_query_duration(gstelement As Pointer, gstformat As Pointer, duration As Pointer) As Boolean

' gboolean gst_element_query_position (GstElement *element, GstFormat format, gint64 *cur)
' Queries an element for the stream position in nanoseconds.
Private Extern gst_element_query_position(gstelement As Pointer, gstformat As Pointer, cur As Pointer) As Boolean
 

Public Sub ToggleButton1_Click()

 Dim fileVideo As String
 Dim posizione, durata As Long
 Dim status As Integer
 
 If ToggleButton1.Value Then
   gst_element_get_state(video, VarPtr(status), 0, 0)
   If status < 2 Then
     gst_init(0, 0)
     video = gst_element_factory_make("playbin", "video")
     fileVideo = "/percorso/del/file/video"
     g_object_set(video, "uri", g_filename_to_uri(fileVideo, Null, 0), Null)
   Endif  
' Avviamo la riproduzione video:
   gst_element_set_state(video, GST_STATE_PLAYING)
   While (posizione < 1) Or (durata > posizione)
     gst_element_query_duration(video, GST_FORMAT_TIME, VarPtr(durata))
     gst_element_query_position(video, GST_FORMAT_TIME, VarPtr(posizione))
     Write "\rDurata: " & Time(0, 0, 0, durata / 1000000) &
           "      Pos. " & Time(0, 0, 0, posizione / 1000000)
     Wait 0.01
   Wend
 Else
   gst_element_set_state(video, GST_STATE_PAUSED)
 Endif
     
End


Public Sub Button1_Click()
 
 ToggleButton1.Value = False
 
 gst_element_set_state(video, GST_STATE_NULL)
 
End


Riproduzione di un file video all'interno di una finestra generata da GStreamer con applicazione a riga di comando

Di seguito mostriamo un esempio simile al precedente, ma con applicazione a riga di comando:

Private video As Pointer


Library "libgstreamer-1.0"

Private Enum GST_STATE_VOID_PENDING = 0, GST_STATE_NULL, GST_STATE_READY, GST_STATE_PAUSED, GST_STATE_PLAYING
Private Const GST_FORMAT_TIME As Integer = 3

' gst_init (int *argc, char **argv[])
' Initializes the GStreamer library, setting up internal path lists, registering built-in elements, and loading standard plugins.
Private Extern gst_init(argc As Pointer, argv As Pointer)

' GstElement * gst_element_factory_make(const gchar *factoryname, Const gchar * name)
' Create a new element of the type defined by the given element factory.
Private Extern gst_element_factory_make(factoryname As String, name As String) As Pointer

' gchar * g_filename_to_uri (const gchar *filename, const gchar *hostname, GError **error)
' Converts an absolute filename to an escaped ASCII-encoded URI.
Private Extern g_filename_to_uri(filename As String, hostname As String, GError As Pointer) As String

' void g_object_set(gpointer object, const gchar *first_property_name, ...)
' Sets properties on an object.
Private Extern g_object_set(gobject As Pointer, key As String, value As String, tertium As String)

' GstStateChangeReturn gst_element_set_state(GstElement *element, GstState state)
' Sets the state of the element.
Private Extern gst_element_set_state(gstelement As Pointer, state As Integer) As Integer

' gboolean gst_element_query_duration (GstElement *element, GstFormat format, gint64 *duration)
' Queries an element for the total stream duration in nanoseconds.
Private Extern gst_element_query_duration(gstelement As Pointer, gstformat As Pointer, duration As Pointer) As Boolean

' gboolean gst_element_query_position (GstElement *element, GstFormat format, gint64 *cur)
' Queries an element for the stream position in nanoseconds.
Private Extern gst_element_query_position(gstelement As Pointer, gstformat As Pointer, cur As Pointer) As Boolean

' void gst_object_unref(gpointer object)
' Decrements the reference count on object.
Private Extern gst_object_unref(gobject As Pointer)


Public Sub Main()

 Dim fileVideo As String
 Dim posizione, durata As Long

 gst_init(0, 0)
  
 video = gst_element_factory_make("playbin", "video")
 fileVideo = "/percorso/del/file/video"
 g_object_set(video, "uri", g_filename_to_uri(fileVideo, Null, 0), Null)
  
' Avviamo la riproduzione video:
 gst_element_set_state(video, GST_STATE_PLAYING)
  
 While (posizione < 1) Or (durata > posizione)
   gst_element_query_duration(video, GST_FORMAT_TIME, VarPtr(durata))
   gst_element_query_position(video, GST_FORMAT_TIME, VarPtr(posizione))
   Write "\rDurata: " & Time(0, 0, 0, durata / 1000000) &
         "      Pos. " & Time(0, 0, 0, posizione / 1000000)
   Wait 0.01
 Wend
  
 gst_object_unref(video)
  
 Print "\nEsecuzione terminata."
  
 Quit
    
End


Public Sub Application_Read()

 Dim s As String

 Input #File.In, s
  
 Select Case s
   Case "p"
' Pone in pausa la riproduzione del file mediale
     gst_element_set_state(video, GST_STATE_PAUSED)
   Case "r"
' Riprende la riproduzione del file mediale
     gst_element_set_state(video, GST_STATE_PLAYING)
   Case "s"
' Arresta la riproduzione del file mediale
     gst_element_set_state(video, GST_STATE_NULL)
     Quit
 End Select

End


Mostrare il video sul Form

Per mostrare il video sul Form dell'applicazione Gambas principale (e dunque non, come nel caso precedente, in una finestra distinta) si dovrà porre sul Form un oggetto capace di supportare il video (ad esempio in Gambas una DrawingArea) e la seguente funzione esterna:

void gst_video_overlay_set_window_handle(GstVideoOverlay *overlay, guintptr handle)

la quale, però è attualmente contenuta nella libreria libgstvideo-1.0. Il parametro overlay è un pointer che rappresenta l'Elemento sonoro, mentre il parametro handle è in intero che rappresenta il numero identificativo (Id) dell'oggetto, posto sul Form, nel quale si intende mostrare il video. In Gambas, come già accennato, l'oggetto potrà essere una DrawingArea.


Mostriamo un semplice esempio:

Private video As Pointer


Library "libgstreamer-1.0"

Private Enum GST_STATE_VOID_PENDING = 0, GST_STATE_NULL, GST_STATE_READY, GST_STATE_PAUSED, GST_STATE_PLAYING
Private Const GST_FORMAT_TIME As Integer = 3

' gst_init (int *argc, char **argv[])
' Initializes the GStreamer library, setting up internal path lists, registering built-in elements, and loading standard plugins.
Private Extern gst_init(argc As Pointer, argv As Pointer)

' GstElement * gst_element_factory_make(const gchar *factoryname, Const gchar * name)
' Create a new element of the type defined by the given element factory.
Private Extern gst_element_factory_make(factoryname As String, name As String) As Pointer

' gchar * g_filename_to_uri (const gchar *filename, const gchar *hostname, GError **error)
' Converts an absolute filename to an escaped ASCII-encoded URI.
Private Extern g_filename_to_uri(filename As String, hostname As String, GError As Pointer) As String

' void g_object_set(gpointer object, const gchar *first_property_name, ...)
' Sets properties on an object.
Private Extern g_object_set(gobject As Pointer, key As String, value As String, tertium As String)

' GstStateChangeReturn gst_element_set_state(GstElement *element, GstState state)
' Sets the state of the element.
Private Extern gst_element_set_state(gstelement As Pointer, state As Integer) As Integer

' GstStateChangeReturn gst_element_get_state (GstElement *element, GstState *state, GstState *pending, GstClockTime timeout)
' Gets the state of the element.
Private Extern gst_element_get_state(e As Pointer, state As Pointer, pen As Pointer, timeout As Long)

' gboolean gst_element_query_duration (GstElement *element, GstFormat format, gint64 *duration)
' Queries an element for the total stream duration in nanoseconds.
Private Extern gst_element_query_duration(gstelement As Pointer, gstformat As Pointer, duration As Pointer) As Boolean

' gboolean gst_element_query_position (GstElement *element, GstFormat format, gint64 *cur)
' Queries an element for the stream position in nanoseconds.
Private Extern gst_element_query_position(gstelement As Pointer, gstformat As Pointer, cur As Pointer) As Boolean


Library "libgstvideo-1.0"

' void gst_video_overlay_set_window_handle (GstVideoOverlay *overlay, guintptr handle)
' Calls the video overlay's set_window_handle method.
Private Extern gst_video_overlay_set_window_handle(overlay As Pointer, handle As Long)
 

Public Sub ToggleButton1_Click()

 Dim fileVideo As String
 Dim posizione, durata As Long
 Dim status As Integer
 
 If ToggleButton1.Value Then
   gst_element_get_state(video, VarPtr(status), 0, 0)
   If status < 2 Then
     gst_init(0, 0)
     video = gst_element_factory_make("playbin", "video")
     fileVideo = "/percorso/del/file/video"
     g_object_set(video, "uri", g_filename_to_uri(fileVideo, Null, 0), Null)
   Endif
  
' Impostiamo la "DrawingArea" come superficie per la riproduzione video:
   gst_video_overlay_set_window_handle(video, CLong(DrawingArea1.Handle))
    
' Avviamo la riproduzione video:
   gst_element_set_state(video, GST_STATE_PLAYING)
  
   While (posizione < 1) Or (durata > posizione)
     gst_element_query_duration(video, GST_FORMAT_TIME, VarPtr(durata))
     gst_element_query_position(video, GST_FORMAT_TIME, VarPtr(posizione))
     Write "\rDurata: " & Time(0, 0, 0, durata / 1000000) &
           "      Pos. " & Time(0, 0, 0, posizione / 1000000)
     Wait 0.01
   Wend
 Else
   gst_element_set_state(video, GST_STATE_PAUSED)
 Endif
    
End


Public Sub Button1_Click()
 
 ToggleButton1.Value = False
 
 gst_element_set_state(video, GST_STATE_NULL)
 
End


Mostrare il video sulla superficie grafica di un programma esterno

Possiamo mostrare il video anche sulla superficie grafica di un programma esterno, come ad esempio un edito di testo (Gedit, Xed, etc). Il tal caso bisognerà chiudere il foglio bianco dell'editor.

Mostriamo un esempio pratico, nel quale si imposterà il programma "Gedit" come superficie grafica sulla quale mostrare un video. E' necessario attivare i Componenti gb.desktop e gb.dsktop.x11 .

Library "libgstreamer-1.0"

Private Enum GST_STATE_VOID_PENDING = 0, GST_STATE_NULL, GST_STATE_READY, GST_STATE_PAUSED, GST_STATE_PLAYING
Private Const GST_FORMAT_TIME As Integer = 3

' gst_init (int *argc, char **argv[])
' Initializes the GStreamer library, setting up internal path lists, registering built-in elements, and loading standard plugins.
Private Extern gst_init(argc As Pointer, argv As Pointer)

' GstElement * gst_element_factory_make(const gchar *factoryname, Const gchar * name)
' Create a new element of the type defined by the given element factory.
Private Extern gst_element_factory_make(factoryname As String, name As String) As Pointer

' gchar * g_filename_to_uri (const gchar *filename, const gchar *hostname, GError **error)
' Converts an absolute filename to an escaped ASCII-encoded URI.
Private Extern g_filename_to_uri(filename As String, hostname As String, GError As Pointer) As String

' void g_object_set(gpointer object, const gchar *first_property_name, ...)
' Sets properties on an object.
Private Extern g_object_set(gobject As Pointer, key As String, value As String, tertium As String)

' GstStateChangeReturn gst_element_set_state(GstElement *element, GstState state)
' Sets the state of the element.
Private Extern gst_element_set_state(gstelement As Pointer, state As Integer) As Integer

' gboolean gst_element_query_duration (GstElement *element, GstFormat format, gint64 *duration)
' Queries an element for the total stream duration in nanoseconds.
Private Extern gst_element_query_duration(gstelement As Pointer, gstformat As Pointer, duration As Pointer) As Boolean

' gboolean gst_element_query_position (GstElement *element, GstFormat format, gint64 *cur)
' Queries an element for the stream position in nanoseconds.
Private Extern gst_element_query_position(gstelement As Pointer, gstformat As Pointer, cur As Pointer) As Boolean


Library "libgstvideo-1.0"

' void gst_video_overlay_set_window_handle (GstVideoOverlay *overlay, guintptr handle)
' Calls the video overlay's set_window_handle method.
Private Extern gst_video_overlay_set_window_handle(overlay As Pointer, handle As Long)


Public Sub Button1_Click()

 Dim video As Pointer
 Dim fileVideo As String
 Dim posizione, durata As Long
 Dim id As Integer
 Dim dw As DesktopWindow

 fileVideo = "/percorso/del/file/video"
 
 gst_init(0, 0)
 video = gst_element_factory_make("playbin", "video")
 g_object_set(video, "uri", g_filename_to_uri(fileVideo, Null, 0), Null)
 
' Imposta il nome visibile (come è mostrato scritto sul "bordo superiore") della finestra di "gedit":
 For Each dw In Desktop.Windows
   If dw.Name = "gedit" Then id = dw.Id
 Next
 if id == 0 then Error.Raise("Errore !")

 gst_video_overlay_set_window_handle(video, CLong(id))

 gst_element_set_state(video, GST_STATE_PLAYING)
 
 While (posizione < 1) Or (durata > posizione)
   gst_element_query_duration(video, GST_FORMAT_TIME, VarPtr(durata))
   gst_element_query_position(video, GST_FORMAT_TIME, VarPtr(posizione))
   Write "\rDurata: " & Time(0, 0, 0, durata / 1000000) & "      Pos. " & Time(0, 0, 0, posizione / 1000000)
   Flush
   Wait 0.01
 Wend

End


Eseguire un file video usando una linea di pipeline con la funzione "gst_parse_launch()"

La funzione esterna "gst_parse_launch()" della libreria GStreamer consente di costruire e gestire in modo semplice una pipeline GStreamer. La riga della pipeline gestita dalla funzione esterna "gst_parse_launch()" è un insieme di elementi separati da punti esclamativi (!). Le proprietà possono essere aggiunte agli elementi, sotto forma di: proprietà = valore.

Sarà necessario avere installata nel sistema e richiamare nell'applicazione Gambas la libreria condivisa: "libgstreamer-1.0 ".

Mostriamo un esempio pratico:

Library "libgstreamer-1.0"

Private Enum GST_STATE_VOID_PENDING = 0, GST_STATE_NULL, GST_STATE_READY, GST_STATE_PAUSED, GST_STATE_PLAYING
Private Const GST_FORMAT_TIME As Integer = 3

' gst_init (int *argc, char **argv[])
' Initializes the GStreamer library, setting up internal path lists, registering built-in elements, and loading standard plugins.
Private Extern gst_init(argc As Pointer, argv As Pointer)

' GstElement * gst_parse_launch (const gchar *pipeline_description, GError **error)
' Create a new pipeline based on command line syntax.
Private Extern gst_parse_launch(description As String, GError As Pointer) As Pointer

' GstStateChangeReturn gst_element_set_state(GstElement *element, GstState state)
' Sets the state of the element.
Private Extern gst_element_set_state(element As Pointer, state As Integer) As Integer

' gboolean gst_element_query_duration (GstElement *element, GstFormat format, gint64 *cur)
' Queries an element for the total stream position in nanoseconds.
Private Extern gst_element_query_duration(element As Pointer, gstformat As Pointer, cur As Pointer) As Boolean

' void gst_object_unref(gpointer object)
' Decrements the reference count on object.
Private Extern gst_object_unref(gobject As Pointer)


Public Sub Main()
 
 Dim video As Pointer
 Dim tm As Date
 Dim durata, posizione As Long
 Dim df As Integer
     
 gst_init(0, 0)
 
 video = gst_parse_launch("filesrc location=/percorso/del/file/video ! decodebin name=decoder decoder. ! queue ! " &
                          "audioconvert ! audioresample ! autoaudiosink decoder. ! xvimagesink", 0)
          
' Avviamo la riproduzione video:
 gst_element_set_state(video, GST_STATE_PLAYING)
  
 Repeat 
   gst_element_query_duration(video, GST_FORMAT_TIME, VarPtr(durata))
   Wait 0.01
 Until durata > 0
 durata \= 1000000
 Print "Durata: " & Str(Time(0, 0, 0, durata))
 
 tm = Now
 
 Repeat
   df = DateDiff(tm, Now, gb.Millisecond)
   Write "\r\e[0mPosiz.  \e[31m" & Str(Time(0, 0, 0, df))
   Wait 0.01
 Until df >= durata
 
 gst_object_unref(video)
 
End

Nel caso il file video da eseguire sia un MP4 può essere utilizzata nel codice precedente anche la seguente riga della funzione esterna "gst parse launch()":

video = gst_parse_launch("filesrc location=/percorso/del/file/video.mp4 ! qtdemux name=demux demux.audio_0 ! queue ! faad ! audioconvert ! autoaudiosink demux.video_0 ! queue ! avdec_h264 ! videoconvert ! autovideosink", 0)


Inoltre, la predetta riga della funzione esterna "gst parse launch()" può essere impostata più brevemente anche così, usando il plugin playbin:

audio = gst_parse_launch("playbin uri=file:///percorso/del/file/video", 0)

Un esempio pratico caricando un video dal web:

audio = gst_parse_launch("playbin uri=https://files.edge.network/misc/avi/file_example_AVI_640_800kB.avi", 0)


Riferimenti