GStreamer il Framework multimediale Audio/Video

Pubblicato: ottobre 29, 2013 in Programmazione & Software
Tag:, , , ,

gstremareIco

GStreamer è un insieme di di librerie, tools e plug-ins grazie ai quali è possibile manipolare e trattare una moltitudine di formati audio e video con una discreta semplicità.
Molti dei player multimediali o editor video inclusi nelle principali distribuzioni linux ( Totem Player  o  PiTiVi ad esempio) utilizzano questo framework per la riproduzione e la manipolazione dei  filmati e dei file audio.
In questo post propongo una serie di esperienze e di test per i più curiosi e gli smanettoni del codice, e cercherò di dare una spiegazione di massima della filosofia e dei componenti principali di questo framework.

Ma quali sono i suoi vantaggi  e perchè lo si dovrebbe studiare e/o utilizzare?
Il grande vantaggio di GStreamer è che consente la manipolazione di diversi formati ( audio e video ) in modo uniforme; questo permette allo sviluppatori di concentrarsi sullo sviluppo dell’applicazione generale e non su come trattare e\o leggere i singoli formati.
In poche parole invece di scrivere un player mp3 o un riproduttore DivX, lo sviluppatore può scrivere un player musical o un player video e Gstremaer penserà a come trattare e leggere i singoli file mp3 o wave wave o i file Avi o i file DivX. Gstreamer è multi piattaforma, ovvero è possibile installarlo ed utilizzarlo sia su windows , che su Linux che su Mac; ultimamente sono state rilasciate anche le versioni per Android e IOS.

In generale GStreamer include:

  • una serie di API per applicazioni Multimediali
  • un’architettura a plugin
  • un’architettura a pipeline
  • più di 150 plug-in
  • un set di tools specifici.

La licenza d’uso è GNU Lesser GPL

gstreamer-overview

Come o dove installarlo.
A questo link:
http://docs.gstreamer.com/display/GstSDK/Installing+the+SDK
potete trovare tutto il necessario per installare il framework sulla vostra piattaforma di lavoro.

Solitamente l’installazione ( precompilata )  prevede una serie di moduli:

gst-plugins-base an essential exemplary set of elements
gst-plugins-good a set of good-quality plug-ins under our preferred license, LGPL
gst-plugins-ugly a set of good-quality plug-ins that might pose distribution problems
gst-plugins-bad a set of plug-ins that need more quality, testing or documentation
gst-libav Libav-based plug-in containing many decoders and encoders
gnonlin Non-linear editing elements
gst-python python bindings
gst-editing-services Editing Services
gst-rtsp-server RTSP server
gst-streaming-server Streaming server
gst-plugins-gl plug-in and helper libraries for OpenGL integration
qt-gstreamer QtGStreamer
gstreamer-sharp C#/.NET bindings
gst-android GStreamer for Android

Come vedete ce né per tutti i gusti

Vorrei porre l’attenzione sulla classificazione dei plugins che viene fatta in base alle licenze e alla qualità della documentazione o dei test da fare.

Gstreamer i concetti fondamentali.
Senza aver la pretesa di spiegare il funzionamento di Gstreamer diamo un’occhiata veloce ai concetti fondamentali.

In GStreamer un player (Audio o Video) può essere visto come una “catena” ( Multimedia Pipeline ) di “blocchi” ( Elements o Bin ) ognuno dei quali specializzato in un particolare compito.
Ad esempio, in una catena che fa il player di un file Mp3, ci sarà almeno un elemento che legge i dati da un file, un altro che si occuperà della decodifica, un altro ancora che si occuperà di inviare i dati ala scheda audio.

simple-player

Vediamo nel dettaglio il significato di alcuni termini in cui spesso ci si imbatte quando si lavora con GStreamer:

Elements
Gli Elements sono i componenti di un pipeline.
Ogni elemento ha un proprio compito ( leggerei i dati da un file, effettuare la decodifica, inviare i dati alla scheda audio etc..) .
Combinando insieme vari elementi si può generare una pipeline per la riproduzione o la cattura di un flusso audio/video.
Ovviamente è possibile oltre ad utilizzare gli elementi già forniti, scriverne di nuovi.

bin-element

Pads.
Sono i “punti” di ingresso e di uscita di un Elements dove è possibile connettere altri elementi.
I pad permettono il passaggio dei dati un uno specifico formato.
E’ possibile effettuare dei link solo tra pad che hanno un formato dei dati compatibile.
I dati fluiscono fuori da un elemento attraverso uno o più source pads mentre i dati entrano in un elemento attraverso uno o più sink pads.

filter-element

Bins e Pipeline
Un bin è un “contenitore” per una collezione di elementi.
Una pipeline è un sottotipo di bin che permette l’esecuzione di tutti i suoi elementi contenuti.
In poche parole una pipeline è un “top level bin“.

Comunicazione
Lo scambio dei dati e delle informazioni sullo stato dei vari elementi viene effettuata tramite:

  • Buffer usati per passare dati tra elementi. I buffer viaggiano dalla sorgente (souce) alla destinazione ( sinks)
  • Eventi sono oggetti inviati tra gli elementi o tra l’applicazione e gli elementi.
  • Messaggi oggetti inviati dagli elementi attraverso il bus all’applicazione.
    Possono essere ricevuti in modo sincrono o asincrono.
  • Queries grazie alle quali è possibile richiedere informazioni alla pipeline.

Uno sguardo più attento ai GstElement
Abbiamo detto che i GstElement sono i blocchi basi che compongono la catena di riproduzione e cattura multimediale. Gli elementi sono ovviamente gli oggetti più importanti per i programmatori.
Un programmatore deve saper creare un elemento ( che svolge il compito desiderato ) collegarlo ad altri elementi e controllarne lo stato ed il funzionamento.
Gli elementi possono essere pensati a come delle “scatole nere” che svolgono dei compiti, in particolare esistono tre diverse classi di elementi:

Source Elements
Generano i dati che possono essere utilizzati dalla pipeline.
Un elemento che legge un file mp3 è un source elements ad esempio ma lo è anche un elemento che effettua il capture dei dati audio/video dal vostro hardware.

Filtri, convertiorie etc..
sono elementi che accettano sia dati in ingresso che in uscita.
Essi ricevono dati dal Pad sink e forniscono dati dal Pad source.
Possono avere anche un sink pad e più source pad come ad esempio un demuxer.

Sink elemets.
Sono i punti finali di una pipeline multimediale.
Essi accettano dati ma non ne producono.
Disk writing, soundcard playback e video output ne sono degli esempi.

Come creare un elemento GstElement
Grazie alla funzione gst_element_factory_make() è possibile creare un nuovo elemento.
Per rimuovere un elemento creato precedentemente possiamo utilizzare la funzione gst_object_unref().
Ad ogni elemento in una pipeline può essere associato un nome univoco che può risultare utile per il debug o per indirizzare un particolare elemento.
Il nome può essere fornito in fase di creazione, se non specificato, GStreamer assegna un nome univoco all’oggetto creato in modo automatico.

Esempio di creazione di un elemento, controllo dell’avvenuta creazione e poi lo liberiamo.

#include <gst/gst.h>
GstElement *element;
st_init(NULL,NULL);
element = gst_element_factory_make(“fakesrc”,”source”);
if(!element){
g_print (“NO\n”);
return;
}
gst_object_unref(GST_OBJECT(element));

In queste semplici righe abbiamo creato un elemento Sorgente e poi lo abbiamo distrutto.

Usare un elemento come Gobject
GstElemet sono delle classi che derivano dalla classe base Gobject ed pertanto da essa ereditano una serie di metodi e funzioni utili alla loro gestione e manipolazione.
Ad esempio i Gobject hanno una serie di metodi per il Get ed il Set dei valori e dei parametri che ovviamente ritroviamo anche nei GstElemet.

GstObject
|
|——> GstElemet

GstElement eredita almeno la proprietà name.
Quindi è possibile impostare o prender il nome di un elemento utilizzando i metodi dell’oggetto GObject → gst_object_set_name() o gst_object_get_name()

Es:
//creo elemento
GstElemts * element:
element = gst_element_factory_make(“fakesrc”,”source”);

//Prendo il nome
gchar* name;
g_object_get(G_OBJECT(element),”name”,&name,NULL);
gprint(“%s\n”,name);
g_free(name);
gst_object_unref(GST_OBJECT (element));

Manipolazione dei dati in una pipeline.
Vediamo come manipolare dati in una pipeline da un’applicazione e come intercettare i dati provenienti da una pipeline.
Per tale scopo possiamo usare i Probes, in poche parole in questo modo aggiungiamo una callback ad un pad che ci notifica tutto quello che accade sul pad compresa anche la presenza di nuovi dati.
In particolare i Data probes, (probes per i quali al momento della creazione si è specificato la caratteristica GST_PAD_PROBE_TYPE_BUFFER o GST_PAD_PROBE_TYPE_BUFFER_LIST) permettono di notificare quando i dati passano nel pad e a tal punto è possibile chiamare la relativa callback nella quali i dati possono essere modificati e manipolati.

Una cosa più interessante è come manualmente iniettare o estrarre dati dalla pipeline.
A tale scopo ci sono due elementi dedicati: appsrc (una sorgente immaginaria) e appsink (un’uscita immaginaria).
Per inserire dati si usa appsrc, per estrarre i dati si usa appsink.

Come installare e fare i primi test su Ubuntu.
Tutte le prove seguenti sono state effettuate su Ubuntu 12.04 LTS.

In ogni caso,  tutte le istruzioni su come scaricare il framework per la propria piattaforma le trovate qui.

Il pacchetto dal quale scaricare GStremer non è nel repository ufficiale di Ubuntu per cui è necessario aggiungerlo prima di poterlo scaricare.

  • Scarica il file relativo alla versione di Ubuntu posseduta:

GStreamer8

E comunque segui quanto riportato nella pagina di installazione.

A questo punto l’ SDK è stato scaricato e lo potete trovare nella directory: /opt/gstreamer-sdk

Vediamo di farci qualcosa.

Apriamo il Qt Creator e creiamo un nuovo progetto Widget.

Qt_Gst_Imm00

So che a questo punto i puristi del codice iniziano a storcere il naso ma, io sono IDE per natura, la linea di comando mi fa tristezza e preferisco usare il Creator solo per fatto per per compilare o avviare il programma c’è un’icona da cliccare piuttosto che una riga da scrivere.
Inoltre, cosa che non è proprio da trascurare, il Creator lo trovate praticamente identico su Linux, Windows e Mac, scusate se è poco e poi, il tutorial lo faccio io e faccio come voglio!

Per prima cosa apriamo il file .pro ed aggiungiamo le magiche righe che permettono al compilatore di sapere dov’è il framework e dove sono i file h da includere durante la compilazione.

Qt_Gst_Imm0

CONFIG += link_pkgconfig
PKGCONFIG += gstreamer-0.10 gstreamer-interfaces-0.10

Apriamo ora il file mainwindow.cpp ed inseriamo l’include e le inizializzazioni di GStreamer.

#include <gst/gst.h>

const gchar *nano_str;
guint major, minor, micro, nano;
gst_init (NULL, NULL);
gst_version (&major, &minor, &micro, &nano);
if (nano == 1)
nano_str = "(CVS)";
else if (nano == 2)
nano_str = "(Prerelease)";
else
nano_str = "";
qDebug() << QString("This program is linked against GStreamer %1.%2.%3 %4").arg(major).arg(minor).arg(micro).arg(nano_str);

Qt_Gst_Imm1

A questo punto compilate ed avviate il programma.

Se tutto è andato a buon fine e nella parte bassa dello schermo compare la scritta:

"This program is linked against GStreamer 0.10.36 "

Il tutto ha funzionato e siamo già a buon punto.
In poche parole abbiamo inizializzato il framework;

gst_init(NULL,NULL);

ed ottenuto la versione  GStreamer installata chiamando la funzione:

gst_version(&magior,&minor,&micro,&nano);

magior,minor,micro,nano rapperenstano i numeri di versione della librearia GStreamer “linkata”.

Una pipeline di test per il  Video.

Nel file mainwindow.h aggiungiamo il seguente codice:

// Structure to contain all our information, so we can pass it around
typedef struct _CustomData {
GstElement *mypipeline;
GstElement *videotestsrc;
GstElement *videsink;
MainWindow* pMyApp;
} CustomData;

Si tratta di un struttura che nella quale andremo a “puntare ” i vari elementi della nostra prima pipeline.


private:
Ui::MainWindow *ui;
CustomData data;

definiamo la relativa variabile nella sezione private, mentre nel file Cpp. andiamo a scrivere il seguente codice:

data.mypipeline = gst_pipeline_new("testVideo");
data.videotestsrc = gst_element_factory_make ("videotestsrc", "videoTestSource");
data.videsink = gst_element_factory_make ("xvimagesink", "videoTestOut");
gst_bin_add_many (GST_BIN (data.mypipeline), data.videotestsrc, data.videsink, NULL );
gst_element_link(data.videotestsrc, data.videsink);
bool ret = gst_element_set_state (data.mypipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
qDebug() << "Unable to set the pipeline to the playing state.\n";
}

In pratica abbiamo:

  1. Creato una pipeline dal nome “testVideo”
  2. Abbiamo creato una sorgente (videotestsrc) di prova
  3. Abbiamo creato “un’uscita” video per il nostro flusso ( xvimagesink).
  4. Abbiamo aggiunto i di Elementi nella nostra pipeline (gst_bin_add_many)
  5. Abbiamo collegato l’uscita della sorgente all’ingresso video di del sink (gst_element_link)
  6. Visto che titti gli elementi sons stati inglobati nella mypipeline, mettendo in PLAY la mypipeline di avvia la riproduzione del flosso video; questo è stato fatto con: gst_element_set_state (data.mypipeline, GST_STATE_PLAYING);

Il risultato una bella finestra “live” con barre di colore che ricorda i vecchi monoscopi della TV.

Qt_Gst_Imm2

Vi sembra troppo poco?

Certo voi mi direte: “io non sono qui a leggere i tuoi post per fare un finestra a barre verticali colorate e con un po di neve che si muove in basso a sinistra” ma, per arrivare alla vetta si parte sempre dal basso inoltre, se siete qui a leggere questo post vuol dire che  di GStreamer ne sapere meno di me quindi “zitti” e continuate a leggere!

Se proprio siete impazienti, qui potete scaricare il progetto per Creator di un programma che fa un test video più evoluto.

In questo programma,  utilizzando   g_object_set e grazie al fatto  che ogni elemento di GStreamer è una classe che deriva dai GObject e quindi è possibile accedere ai sui metodi con i classici set e get,
potete modificare il patter standard del videosource cliccando sui vari pulsante di controllo.

Qt_Gst_Imm3

Maggiori informazioni sulle proprietà  del videotestsrc le trovate qui.

Non sento l’audio?

In effetti sino ad ora abbiamo solo visto barre colorate o pattern video ma non si sente nulla uscire dalle nostre casse, ci sarà forse un testo anche per vedere se la nostra scheda audio funziona? Certo che si!

Modifichiamo il nostro CustomData aggiungendo due nuovi puntatori agli elementi che andranno a gestire l’audio, sorgente e uscita; audiotestsrc e audiosink;

typedef struct _CustomData {
GstElement *mypipeline;
GstElement *videotestsrc;
GstElement *videsink;
GstElement *audiotestsrc;
GstElement *audiosink;
MainWindow* pMyApp;
} CustomData;

Sul file Cpp aggiungiamo le seguenti righe:

data.mypipeline = gst_pipeline_new("testVideo");
data.videotestsrc = gst_element_factory_make ("videotestsrc", "videoTestSource");
data.videsink = gst_element_factory_make ("xvimagesink", "videoTestOut");
data.audiotestsrc = gst_element_factory_make ("audiotestsrc", "audioTestSource");
data.audiosink = gst_element_factory_make ("alsasink", "audioOut");
gst_bin_add_many (GST_BIN (data.mypipeline), data.videotestsrc, data.videsink, data.audiotestsrc, data.audiosink, NULL );
gst_element_link(data.videotestsrc, data.videsink);
gst_element_link(data.audiotestsrc, data.audiosink);
bool ret = gst_element_set_state (data.mypipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
qDebug() << "Unable to set the pipeline to the playing state.";
}

In prativa abbiamo creato due nuovi oggetti, li abbiamo messi nelle pipeline e li abbiano collegati tra loro.

L’audio è servito!
All’avvio del programma sentierete il classico 440 mentre sulla finestra video avrete il testo con le barre.

Ma io voglio di più!
Con questo programma hai a disposizione la selezione di una serie di forma d’onda per l’audio test e la possibilità di regolare il volume.

Qt_Gst_Imm6

Ma io voglio di più!
Be allora senti:
riproduciamo un video letto dalla rete con audio, e sottotitoli!
Per fare tutto questo e farlo da principiante, utilizza un elemento “magico” di GStremare che si chiama playbin2.

Modifichiamo il CustomData in questo modo:

typedef struct _CustomData {
GstElement *playbin2;
} CustomData;

Un solo elemento!

sul file cpp scriviamo:

data.playbin2 = gst_element_factory_make ("playbin2", "playbin2");
g_object_set (data.playbin2, "uri", "http://docs.gstreamer.com/media/sintel_cropped_multilingual.webm", NULL);
g_object_set (data.playbin2, "connection-speed", 56, NULL);
bool ret = gst_element_set_state (data.playbin2, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
qDebug() << "Unable to set the pipeline to the playing state.\n";
gst_object_unref (data.playbin2);
}

Qt_Gst_Imm7

Magia delle magie ecco il video ( letto da http://docs.gstreamer.com/media/sintel_cropped_multilingual.webm) con audio e tutto il resto che compare sul nostro schermo.

Qt_Gst_Imm8

Mai io voglio di più!
Voglio il play, lo stop, la posizione, il volume, la possibilità di scegliere un altro filmato…..

Be magari allo facciamo che ci diamo appuntamento alla prossima puntata nella quale magari parleremo di CallBack, di controllo del flusso, di QtGStreamer etc..etc..

commenti
  1. […] GStreamer il Framework multimediale Audio/Video […]

Lascia un commento

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...