Estrarre un'immagine da un file video con le funzioni esterne del API di GStreamer

Da Gambas-it.org - Wikipedia.

La risorsa GStreamer consente anche di estrarre fotogrammi da un file video e salvarli come file immagine.

Sarà necessario avere installata nel sistema e richiamare nell'applicazione Gambas la librerie condivise:
- libgstreamer-1.0.so.0.2003.0
- libgdk_pixbuf-2.0.so.0.4200.8


Mostriamo un esempio, nel quale sarà creato un file immagine di tipo .png da un fotogramma di un file video:

Library "libgstreamer-1.0"

Public Struct GstMapInfo
  GstMemory As Pointer
  flags As Integer
  data As Pointer
  size As Long
  maxsize As Long
End Struct
 
Private Enum GST_STATE_VOID_PENDING = 0, GST_STATE_NULL, GST_STATE_READY, GST_STATE_PAUSED, GST_STATE_PLAYING
Private Enum GST_STATE_CHANGE_FAILURE = 0, GST_STATE_CHANGE_SUCCESS, GST_STATE_CHANGE_ASYNC, GST_STATE_CHANGE_NO_PREROLL
Private Const GST_SECOND As Long = 1000000000
Private Const GST_FORMAT_TIME As Integer = 3
Private Const GST_SEEK_FLAG_FLUSH As Integer = 1
Private Const GST_SEEK_FLAG_KEY_UNIT As Integer = 4
Private Const GST_MAP_READ As Integer = 1
Private Const GDK_COLORSPACE_RGB As Integer = 0

' 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

' GstElement * gst_bin_get_by_name (GstBin *bin, const gchar *name)
' Gets the element with the given name from a bin.
Private Extern gst_bin_get_by_name(bin As Pointer, name As String) 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

' 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(element As Pointer, state As Pointer, pending As Pointer, timeout As Long) As Integer

' gboolean gst_element_query_duration (GstElement *element, GstFormat format, gint64 *duration)
' Queries an element (usually top-level pipeline or playbin element) for the total stream duration in nanoseconds.
Private Extern gst_element_query_duration(element As Pointer, GstFormat As Integer, duration As Pointer) As Boolean

' gboolean gst_element_seek_simple (GstElement *element, GstFormat format, GstSeekFlags seek_flags, gint64 seek_pos)
' Simple API to perform a seek on the given element.
Private Extern gst_element_seek_simple(element As Pointer, GstFormat As Integer, seek_flags As Integer, seek_pos As Long) As Boolean

' void g_signal_emit_by_name (gpointer instance, const gchar *detailed_signal, ...)
' Emits a signal.
Private Extern g_signal_emit_by_name(instance As Pointer, detailed_signal As String, GstSample As Pointer)

' GstCaps * gst_sample_get_caps (GstSample *sample)
' Get the caps associated with sample.
Private Extern gst_sample_get_caps(GstSample As Pointer) As Pointer

' GstStructure * gst_caps_get_structure (const GstCaps *caps, guint index)
' Finds the structure in caps that has the index index , and returns it.
Private Extern gst_caps_get_structure(GstCaps As Pointer, index As Integer) As Pointer

' gboolean gst_structure_get_int (const GstStructure *structure, const gchar *fieldname, gint *value)
' Sets the int pointed to by value corresponding to the value of the given field.
Private Extern gst_structure_get_int(structure As Pointer, fieldname As String, value As Pointer) As Boolean

' GstBuffer * gst_sample_get_buffer (GstSample *sample)
' Get the buffer associated with sample.
Private Extern gst_sample_get_buffer(GstSample As Pointer) As Pointer

' gboolean gst_buffer_map (GstBuffer *buffer, GstMapInfo *info, GstMapFlags flags)
' Fills info with the GstMapInfo of all merged memory blocks in buffer.
Private Extern gst_buffer_map(GstBuffer As Pointer, info As GstMapInfo, flags As Integer) As Boolean

' void gst_buffer_unmap (GstBuffer *buffer, GstMapInfo *info)
' Release the memory previously mapped with gst_buffer_map().
Private Extern gst_buffer_unmap(GstBuffer As Pointer, info As GstMapInfo)

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


Library "libgdk_pixbuf-2.0:0.4000.0"

' GdkPixbuf * gdk_pixbuf_new_from_data (const guchar *data, GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height, int rowstride, GdkPixbufDestroyNotify destroy_fn, gpointer destroy_fn_data)
' Creates a new GdkPixbuf out of in-memory image data.
Private Extern gdk_pixbuf_new_from_data(data As GstMapInfo, colorspace As Integer, has_alpha As Boolean, bits_per_sample As Integer, width As Integer, height As Integer, rowstride As Integer, destroy_fn As Pointer, destroy_fn_data As Pointer) As Pointer

' gboolean gdk_pixbuf_save (GdkPixbuf *pixbuf, const char *filename, const char *type, GError **error, ...)
' Saves pixbuf to a file in format type. By default, "jpeg", "png", "ico" and "bmp" are possible file formats to save in.
Private Extern gdk_pixbuf_save(GdkPixbuf As Pointer, filename As String, type As String, GError As Pointer, altro As String) As Boolean


Public Sub Main()

  Dim uri, caps, linea, imagen As String
  Dim pipeline, sink, sample As Pointer
  Dim capsP, s, buffer, pixbuf As Pointer
  Dim rit, width, height As Integer
  Dim dur, pos As Long
  Dim tempus As Float
  Dim bo As Boolean
  Dim map As New GstMapInfo
  
  uri = "file://" & "/percorso/del/file/fideo"
   
  caps = "\x22video/x-raw,format=RGB,width=640,height=480,pixel-aspect-ratio=1/1\x22"
   
  imagen = "/percorso/del/file.png"
   
' Estrae il fotogramma alla posizione pari al 44,33% dell'intera durata del video:
  tempus = 44.33

' Inizializza la libreria Gstreamer:
  gst_init(0, 0)
 
' Crea una nuova pipeline:
  linea = "uridecodebin uri=" & uri & " ! videoconvert ! videoscale ! appsink name=sink caps=" & caps
  pipeline = gst_parse_launch(linea, s)
  If pipeline == 0 Then Error.Raise("Impossibile creare una pipeline !")
   
  sink = gst_bin_get_by_name(pipeline, "sink")
   
  rit = gst_element_set_state(pipeline, GST_STATE_PAUSED)
  If rit = GST_STATE_CHANGE_FAILURE Then Error.Raise("Impossibile eseguire il file video !")
   
  rit = gst_element_get_state(pipeline, 0, 0, 5 * GST_SECOND)
  If rit = GST_STATE_CHANGE_FAILURE Then Error.Raise("Impossibile eseguire il file video !")
   
' Ottiene la durata complessiva del video:
  gst_element_query_duration(pipeline, GST_FORMAT_TIME, VarPtr(dur))
  If dur <> -1 Then
    pos = dur * tempus / 100
  Else
    pos = 1 * GST_SECOND
  Endif
 
  gst_element_seek_simple(pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_KEY_UNIT Or GST_SEEK_FLAG_FLUSH, pos)
   
  g_signal_emit_by_name(sink, "pull-preroll", VarPtr(sample))
  gst_object_unref(sink)
 
 
  If sample > 0 Then
    capsP = gst_sample_get_caps(sample)
    If capsP == 0 Then Error.Raise("Impossibile ottenere il formato del frame !")
    s = gst_caps_get_structure(capsP, 0)
   
    bo = gst_structure_get_int(s, "width", VarPtr(width))
    bo = bo Or gst_structure_get_int(s, "height", VarPtr(height))
    If bo = False Then Error.Raise("Impossibile ottenere la dimensione del frame !")
   
' Crea un oggetto "pixmap" dal buffer e lo salva:
    buffer = gst_sample_get_buffer(sample)
    gst_buffer_map(buffer, map, GST_MAP_READ)
    pixbuf = gdk_pixbuf_new_from_data(map.data, GDK_COLORSPACE_RGB, False, 8, width, height, ((width * 3) + 3) Xor 3, Null, Null)
    If pixbuf == 0 Then Error.Raise("Impossibile creare un nuovo pixbuf dai dati !")
    gdk_pixbuf_save(pixbuf, imagen, "png", 0, Null)
  
    gst_buffer_unmap(buffer, map)
  
  Else
    Error.Raise("Impossibile salvare il file immagine !")
  Endif
   
  
' Va in chiusura:
  gst_element_set_state(pipeline, GST_STATE_NULL)
  gst_object_unref(pipeline)
  
End


Riferimenti