Gestione dei dati media con GStreamer

Da Gambas-it.org - Wikipedia.

Per gestire i dati, al fine di ottenere il risultato ed il fine desiderato: esecuzione sonora, video o altro, bisognerà utilizzare specifiche funzioni, anche finalizzate alla creazione ed all'uso delle funzionalità degli Elementi di GStreamer.


Impostare un Elemento per la gestione del flusso dei dati

Un Elemento è la Classe di oggetti più importante in GStreamer. Esso svolge una determinata funzione, ed è utilizzato per gestire il flusso di dati, come ad esempio lettura, decodifica ed invio dei medesimi alla scheda audio. Concatenando più Elementi (pipeline) è possibile sfruttare le loro funzionalità ed ottenere gli obiettivi desiderati (come ad esmepio la riproduzione audio) multimediali.

Il modo più semplice per creare un Elemento è utilizzare la funzione:

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

alla quale si passa il nome di un Elemento fra quelli predisposti da GStreamer ed una denominazione per l'Elemento appena creato. È possibile passare NULL come argomento name per ottenere un unico nome predefinito.
Quando un Elemento viene creato, esso assume un riferimento numerico pari ad un'unità pari al numero di elementi in quel momento creati. Così se ad esempio il nuovo Elemento creato è il secondo, esso assumerà un riferimento numerico uguale a 2.
Detta funzione restituisce una variabile di tipo Puntatore al nuovo Elemento creato, che sarà utilizzata per richiamare appunto quell'Elemento.
Il nuovo Elemento creato con un nome previsto da GStreamer è in grado di gestire solitamente file audio e video, nonché, a seconda dell'Elemento richiamato, altre determinate caratteristiche.
In Gambas dichiareremo la funzione nel seguente modo:

Private Extern gst_element_factory_make(factoryname As String, name As String) As Pointer

e la richiameremo in routine ad esempio così:

Private elem As Pointer

Public Sub ......()

 elem = gst_element_factory_make("playbin", "esegui")
......


Eliminare un Elemento

Per eliminare un Elemento, precedentemente creato, si utilizza la funzione:

void gst_object_unref(gpointer object)

alla quale va passata la variabile di tipo Pointer che punta all'Elemento creato.
La distruzione dell'Elemento determina il decremento del riferimento numerico di un'unità.
In Gambas la funzione sarà così dichiarata:

Private Extern gst_object_unref(object As Pointer)

e sarà richiamata in routine ad esempio così:

gst_object_unref(elem)


Impostare lo stato di un Elemento

Per impostare un particolare stato di un Elemento, si utilizzerà la funzione:

GstStateChangeReturn gst_element_set_state(GstElement *element, GstState state)

La funzione può ritornare uno dei seguenti valori:

  • GST_STATE_CHANGE_FAILURE, uguale a 0, se l'ultimo stato di cambiamento è fallito;
  • GST_STATE_CHANGE_SUCCESS, uguale a 1, se l'Elemento non ha stato più attesa e l'ultimo cambiamento di stato è riuscito;
  • GST_STATE_CHANGE_ASYNC, uguale a 2, se l'Elemento sta ancora eseguendo un cambiamento di stato;
  • GST_STATE_CHANGE_NO_PREROLL, uguale a 3, se il cambiamento di stato è riuscito, ma l'Elemento non è in grado di produrre i dati in GST_STATE_PAUSED.

La funzione in Gambas sarà così dichiarata:

Private Extern gst_element_set_state(gstobject As Pointer, state As Integer) As Integer

Variando il parametro state, si varierà lo stato dell'Elemento di GStreamer. I possibili valori di stato di un Elemento sono:

  • GST_STATE_VOID_PENDING = 0 ' nessuno stato sospeso
  • GST_STATE_NULL = 1 ' lo stato con valore Null o lo stato iniziale di un "Elemento"
  • GST_STATE_READY = 2 ' l'Elemento è "pronto" per essere posto in pausa
  • GST_STATE_PAUSED = 3 ' l'Elemento è in "pausa"
  • GST_STATE_PLAYING = 4 ' l'Elemento sta eseguendo i dati che fluiscono


Impostare lo stato per la Riproduzione di un flusso di dati

Per poter ottenere la riproduzione di un flusso di dati, ad esempio caricati da un file audio, si imposterà il parametro state con la costante GST_STATE_PLAYING di GStreamer, che equivale al valore 4. Pertanto la predetta funzione sarà in routine impostata:

Private Const GST_STATE_PLAYING As Integer = 4

Public Sub ......()

gst_element_set_state(elem, GST_STATE_PLAYING)
......

Richiamata detta funzione, si avrà la riproduzione audio dei dati caricati.


Caricare un file da gestire

Per far gestire (ad esempio farlo eseguire) un file audio, è necessario attribuire detto file come proprietà all'Elemento creato. Si utilizzerà la seguente funzione esterna:

void g_object_set(gpointer object, const gchar *first_property_name, const gchar *value, Null)

laddove in particolare la varibile di tipo Puntatore *value conterrà il percorso del file da gestire.
In Gambas detta funzione sarà così dichiarata:

Private Extern g_object_set(object As Pointer, nomeProprietà As String, value As String, nl As String)

Va precisato che il nom del file va passato con la codifica della Universal Resource Identifiers (URIs). Per trasformare un ordinario indirizzo del file nella sua codifica URI, potremo servirci della funzione esterna:

gchar * g_filename_to_uri (const gchar *filename, const gchar *hostname, GError **error)

che dichiareremo come segue:

Private Extern g_filename_to_uri(filename As String, hostname As String, GError As Pointer) As String

Pertanto avremo in routine ad esempio:

Dim percorsoFile As String

 percorsoFile = Dialog.Path

 g_object_set(elem, "nome", g_filename_to_uri(percorsoFile, Null, 0), Null)


Conoscere la durata del flusso di dati da eseguire

Per conoscere la durata del flusso di dati da eseguire, ad esempio la durata di un brano audio, si utilizzerà la funzione esterna:

gboolean gst_element_query_duration(GstElement *element, GstFormat format, gint64 *duration)

laddove il parametro format è un intero che può assumere uno dei seguenti valori da impostare preventivamente a seconda del bisogno:

  • GST_FORMAT_UNDEFINED = 0 ' formato indefinito
  • GST_FORMAT_DEFAULT = 1 ' il formato di default dell'lemento, che può essere in campioni per l'audio, fotogrammi per il video
  • GST_FORMAT_BYTES = 2 ' byte
  • GST_FORMAT_TIME = 3 ' nanosecondi
  • GST_FORMAT_BUFFERS = 4 ' buffer
  • GST_FORMAT_PERCENT = 5 ' percentuale di dati

Il parametro duration è la variabile di tipo Pointer nella quale viene immagazzinata la durata complessiva. Parametro per i ritorni derivanti. Il default è il trasferimento completo. Può assumere il valore 0.
La funzione ritorna un valore booleano True se l'interrogazione, al fine di conoscere la durata dei dati, può essere effettuata. In Gambas questa funzione sarà così dichiarata:

Private Extern gst_element_query_duration(gselement As Pointer, formatI As Integer, duration As Pointer) As Boolean

e sarà richiamata in routine ad esempio così:

Dim Dim GST_FORMAT_TIME As Integer = 3
Dim durata As Long

 gst_element_query_duration(elem, GST_FORMAT_TIME, VarPtr(durata))
 
 Print durata


Conoscere durante l'esecuzione la posizione nel flusso dei dati

Per conoscere la posizione corrente all'interno dei dati da processare, si farà uso della funzione esterna:

gboolean gst_element_query_position(GstElement *element, GstFormat format, gint64 *cur)

laddove il parametro format è un intero che può assumere uno dei seguenti valori da impostare preventivamente a seconda del bisogno:

  • GST_FORMAT_UNDEFINED = 0 ' formato indefinito
  • GST_FORMAT_DEFAULT = 1 ' il formato di default dell'lemento, che può essere in campioni per l'audio, fotogrammi per il video
  • GST_FORMAT_BYTES = 2 ' byte
  • GST_FORMAT_TIME = 3 ' nanosecondi
  • GST_FORMAT_BUFFERS = 4 ' buffer
  • GST_FORMAT_PERCENT = 5 ' percentuale di dati

Il parametro cur è la variabile di tipo Pointer nella quale viene immagazzinata la posizione corrente.
La funzione ritorna un valore booleano True se l'interrogazione, al fine di conoscere la durata dei dati, può essere effettuata. In Gambas questa funzione sarà così dichiarata:

Private Extern gst_element_query_position(gselement As Pointer, formatI As Integer, cur As Pointer) As Boolean

e sarà richiamata in routine ad esempio così:

Dim GST_FORMAT_TIME As Integer = 3
Dim pos As Long

 gst_element_query_position(elem, GST_FORMAT_TIME, VarPtr(pos))

 Print pos


Conocere lo stato dell'Elemento

Per conoscere lo stato corrente di un Elemento, si utilizzerà la funzione esterna:

GstStateChangeReturn gst_element_get_state(GstElement *element, GstState *state, GstState *pending, GstClockTime timeout)

laddove il parametro state è una variabile di tipo Pointer che punta alla zona di memoria, nella quale è contenuto il valore dello stato corrente dell'Elemento. La funzione può ritornare uno dei seguenti valori:

  • GST_STATE_CHANGE_FAILURE, uguale a 0, se l'ultimo stato di cambiamento è fallito;
  • GST_STATE_CHANGE_SUCCESS, uguale a 1, se l'elemento non ha stato più attesa e l'ultimo cambiamento di stato è riuscito;
  • GST_STATE_CHANGE_ASYNC, uguale a 2, se l'elemento sta ancora eseguendo un cambiamento di stato.

Per gli elementi che non ritornano il valore GST_STATE_CHANGE_ASYNC, questa funzione restituisce immediatamente lo stato attuale e in sospeso.
In Gambas questa funzione sarà così dichiarata:

Private Extern gst_element_get_state(gstobject As Pointer, stateP As Pointer, pendingP As Pointer, timeLout As Long) As Integer

e sarà richiamata in routine ad esempio così:

Dim state, pending As Pointer
Dim timeout As Long

 state = Alloc(4)
 pending = Alloc(4)

 gst_element_get_state(player, state, pending, timeout)

 Print Int@(state)


Termine e chiusura di GStreamer

Nel caso in cui si intendano chiudere o comunque interrompere le risorse di GStreamer, ad esempio quando si procede alla chiusura del programma, è buona abitudine effettuare la chiusura mediante la funzione:

void gst_object_unref(gpointer object)

precedentementre vista, che eliminerà l'Elemento; nonché la funzione:

GstStateChangeReturn gst_element_set_state(GstElement *element, GstState state)

anche questa precedentementre vista, alla quale in Gambas attribuiremo il secondo parametro a Null:

gst_element_set_state(elem, GST_STATE_NULL)


Riassunto essenziale per la Riproduzione audio

In conclusione possiamo dire che bastano solo poche funzioni esterne del API di GStreamer per effettuare la riproduzione di un file audio.

Riportiamo alcuni esempi con applicazioni in ambiente grafico ed applicazione a riga di comando.


Esempio con applicazione in ambiente grafico usando la Classe Timer

Mostriamo di seguito un esempio pratico e funzionante di un'applicazione in ambiente grafico che fa uso della la Classe Timer per consentire l'estrazione della durata totale e del tempo trascorso di esecuzione del file mediale:

Private elem As Pointer
Private tempus As Timer


Library "libgstreamer-1.0"

Private Enum GST_STATE_VOID_PENDING = 0, GST_STATE_NULL, GST_STATE_READY, GST_STATE_PAUSED, GST_STATE_PLAYING
Private 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 Form_Open()

  gst_init(0, 0)

  With tempus = New Timer As "Tempus"
    .Delay = 70
  End With
  
End


Public Sub Button1_Click()   ' Avvia la riproduzione del file mediale

 Dim fileaudio as string

  elem = gst_element_factory_make("playbin", "Riproduzione")

  fileaudio = "/percorso/del/file/audio"

  g_object_set(elem, "uri", g_filename_to_uri(fileaudio, Null, 0), Null)
   
' Avviamo la riproduzione audio:
  gst_element_set_state(elem, GST_STATE_PLAYING)

  tempus.Start
 
End


Public Sub Button2_Click()   ' Arresta la riproduzione del file mediale

 Dim rit As Integer

  tempus.Stop
  
  rit = gst_element_set_state(elem, GST_STATE_NULL)
  Print "\nStato di cambiamento = "; rit
   
  gst_object_unref(elem)

End


Public Sub ToggleButton1_Click()   ' Pone in pausa e riprende la riproduzione del file mediale

 Dim rit As Integer

  If ToggleButton1.Value Then
    tempus.Stop
    rit = gst_element_set_state(elem, GST_STATE_PAUSED)
  Else
    rit = gst_element_set_state(elem, GST_STATE_PLAYING)
    tempus.Start
  Endif

  Print "\nStato di cambiamento = "; rit

End


Public Sub Tempus_Timer()
 
 Dim positio, durata As Long
 
  gst_element_query_duration(elem, GST_FORMAT_TIME, VarPtr(durata))
 
  gst_element_query_position(elem, GST_FORMAT_TIME, VarPtr(positio))
  
  Write #File.Out, "\rDurata: " & Date(0, 0, 0, 0, 0, 0, durata / 1000000) & "  -   Posizione: " & Date(0, 0, 0, 0, 0, 0, positio / 1000000)
  
  If (positio > 0) And (durata = positio) Then
    tempus.Stop
    gst_element_set_state(elem, GST_STATE_NULL)
    gst_object_unref(elem)
  Endif

End


Esempio con applicazione in ambiente grafico usando la funzione Wait

Mostriamo di seguito un esempio pratico e funzionante di un'applicazione in ambiente grafico che fa uso della funzione Wait per consentire l'estrazione della durata totale e del tempo trascorso di esecuzione del file mediale:

Private elem As Pointer
Private v As Boolean


Library "libgstreamer-1.0"

Private Enum GST_STATE_VOID_PENDING = 0, GST_STATE_NULL, GST_STATE_READY, GST_STATE_PAUSED, GST_STATE_PLAYING
Private 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 Form_Open()

  gst_init(0, 0)
     
End


Public Sub Button1_Click()   ' Avvia la riproduzione del file mediale

 Dim fileaudio as string
 Dim durata As Long = 1
 Dim positio As Long
  
  elem = gst_element_factory_make("playbin", "Riproduzione")

  fileaudio = "/percorso/del/file/audio"

  g_object_set(elem, "uri", g_filename_to_uri(fileaudio, Null, 0), Null)
   
' Avviamo la riproduzione audio:
  gst_element_set_state(elem, GST_STATE_PLAYING)
  
  v = False
  
  While (positio < durata) And (v = False)
    Wait 0.01
    gst_element_query_duration(elem, GST_FORMAT_TIME, VarPtr(durata))
    gst_element_query_position(elem, GST_FORMAT_TIME, VarPtr(positio))
    Write #File.Out, "\rDurata: " & Date(0, 0, 0, 0, 0, 0, durata / 1000000) & "  -   Posizione: " & Date(0, 0, 0, 0, 0, 0, positio / 1000000)
  Wend
  
  gst_element_set_state(elem, GST_STATE_NULL)
  
  gst_object_unref(elem)
  
End


Public Sub Button2_Click()   ' Arresta la riproduzione del file mediale

  v = True

End


Public Sub ToggleButton1_Click()   ' Pone in pausa e riprende la riproduzione del file mediale
   
  If ToggleButton1.Value Then
    gst_element_set_state(elem, GST_STATE_PAUSED)
  Else
    gst_element_set_state(elem, GST_STATE_PLAYING)
  Endif
  
End


Esempio con applicazione a riga di comando

Mostriamo di seguito un esempio pratico e funzionante di un'applicazione a riga di comando:

Private elem 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
Private stato As Integer = GST_STATE_PLAYING

' 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 fileaudio as string
 Dim posizione, durata As Long

  gst_init(0, 0)

  elem = gst_element_factory_make("playbin", "riproduzione")

  fileaudio = "/percorso/del/file/audio"

  g_object_set(elem, "uri", g_filename_to_uri(fileaudio, Null, 0), Null)

' Avvia la riproduzione audio:
  gst_element_set_state(elem, GST_STATE_PLAYING)
 
  While (posizione = 0) Or (durata > posizione)
    gst_element_query_duration(elem, GST_FORMAT_TIME, VarPtr(durata))
    gst_element_query_position(elem, GST_FORMAT_TIME, VarPtr(posizione))
    Write #File.Out, "\rDurata: " & Date(0, 0, 0, 0, 0, 0, durata / 1000000) & "      Pos. " & Date(0, 0, 0, 0, 0, 0, posizione / 1000000)
    If stato <> 4 Then Break
    Wait 0.01
  Wend
   
  gst_object_unref(elem)
  
  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(elem, GST_STATE_PAUSED)
    Case "r"
      gst_element_set_state(elem, GST_STATE_PLAYING)       ' Riprende la riproduzione del file mediale
    Case "s"
      stato = gst_element_set_state(elem, GST_STATE_NULL)  ' Arresta la riproduzione del file mediale
  End Select
 
End