GStreamer 0.10

Installation

Avec l'installateur GStreamer

<WRAP center round important 60%> Nécéssite les droits administrateurs </WRAP> Téléchargez l'installateur Page de téléchargement <WRAP center round info 60%> Si vous constatez des bugs ou des problèmes avec la dernière version proposé n'hésitez pas à télécharger d'anciennes version GStreamer 2012.11 </WRAP> l'installateur va se charger de créer vos variable d'environnement et de configurer GStreamer pour qu'il soit utilisable clé en main. <WRAP center round important 60%> J'ai rencontré des difficulté avec la variable de GStreamer dans le PATH Windows, il faut parfois la réécrire ou supprimer des entrée pour que ça puisse fonctionner </WRAP>

Sans installateur GStreamer

Si vous ne disposez pas des droit d'administration ce n'est pas grave j'ai préparé une version de GStreamer portable. Il s'agit d'une installation 2013.6 LGPL ( je n'ai pas intégré les plugins GPL dont le H264).

Téléchargez : GStreamer Portable 0.10.

décompresser le à l'endroit où vous le souhaitez.

Pour utiliser GStreamer en ligne de commande il va falloir configurer votre variable PATH. par exemple dans un terminal :

set GStreamerPATH=C:\Chemin\Vers\GStreamer
set PATH=%PATH%;%GStreamerPATH%\bin

Afin de gagner mon temps et ne pas avoir à configurer ma variable path dans windows je me suis créé un script “gstreamer.bat” me permettant de me trouver avec un environnement GStreamer bien configurer quand j'en ai besoin

set GStreamerPATH=C:\Chemin\Vers\GStreamer
set PATH=%PATH%;%GStreamerPATH%\bin
cls
cmd /k 

Test de votre environnement

lancer un terminal si GStreamer est configuré dans le PATH windows ou “gstreamer.bat” puis executez :

gst-launch-0.10 videotestsrc ! autovideosink

Une mire doit s'afficher :

Principe de GStreamer

GStreamer fonctionne sur le principe de Pipeline :

Le Pipeline est composé d'éléments qui peuvent être de plusieurs types :

  • Sources
  • Filtres
  • Sorties

un pipeline doit être composé d'au moins d'une sources et d'une sorties pour fonctionner. Les sources, les filtres et les sorties doivent être composés dans un ordre particulier comme dans l'exemple suivant :

Exemples de Pipeline

Lire une vidéo

  • Version facile :
gst-launch-0.10 playbin2 uri=file:///C:/video/trailer.mp4

Ici tous le Pipeline est déjà créé dans playbin2 pour permettre de fournir un lecteur vidéo facilement

  • Version complexe :
gst-launch-0.10 filesrc location=C:/video/trailer.mp4 ! decodebin2 name=demux \
demux. ! autovideosink demux. ! autoaudiosink

Ici on a l'équivalent de Playbin2 décomposé en pipeline

  1. filesrc est un élement sources
  2. decodebin2 permet de décoder tous type de fichier vidéo et audio sans avoir à se préoccuper du format
  3. lien entre decodebin2 “demux” et l'affichage vidéo
  4. lien entre decodebin2 “demux” et l'affichage audio

Lire le flux d'une Webcam

gst-launch-0.10 ksvideosrc ! autovideosink

ksvideosrc : permet de récupérer le flux d'une webcam installé sur votre ordinateur <WRAP center round info 60%> si vous avez deux ou plus de webcam de brancher sur votre ordinateur vous pouvez sélectionner la webcam que vous souhaitez ouvrir grâce au paramètre “device-index” </WRAP>

gst-launch-0.10 ksvideosrc device-index=1 ! autovideosink

cette commande sélectionne la caméra correspondant à l'index 1

Lire le flux d'une webcam et l'enregistrer en même temps

gst-launch-0.10 ksvideosrc ! tee name=videoOutput videoOutput. ! queue \
! autovideosink videoOutput. ! queue ! ffmpegcolorspace ! ffenc_mpeg4 ! avimux \
! filesink location=destination.avi
  1. ksvideosrc : On récupère le flux d'une webcam
  2. tee : On divise le flux en deux sous flux nommé videoOutput
  3. Première partie :
    1. Queue : On met le flux dans une queue pour éviter tous problème d'interbloquage
    2. autovideosink : on affiche la vidéo
  4. Deuxième partie :
    1. Queue : On met le flux dans une queue pour éviter tous problème d'interbloquage
    2. ffmpegcolorspace : On converti le flux brute dans l'espace de couleur ffmpeg
    3. ffenc_mpeg : on encode en mp4 à l'aide du plugin ffmpeg
    4. avimux : on encapsule le flux dans le format avi
    5. filesink : on enregistre la vidéo dans un fichier

<WRAP center round info 60%> Ici on voit que l'élément tee permet de décomposer un flux en deux flux que l'on va traiter de manière différentes. </WRAP>

Création d'un serveur RTP

gst-launch-0.10 ksvideosrc device-index=0 ! videoscale ! video/x-raw-yuv, \
width=640,height=480 ! videorate ! video/x-raw-yuv,framerate=25/1 \
! ffmpegcolorspace ! ffenc_mpeg4 bitrate=1000000 ! rtpmp4vpay send-config=true \
! udpsink host=127.0.0.1 port=50000
  1. ksvideosrc : on récupère le flux de la webcam
  2. videoscale : on la redimensionne au format 640×480
  3. videorate : on défini le nombre d'image par seconde à 25
  4. ffmpegcolorspace : on transforme le flux dans l'espace de couleur ffmpeg
  5. ffenc_mpeg4 : on encode le flux en mpeg4 avec un débit de 100ko/s.
  6. rtpmp4vpay : on encapsule le flux dans un flux rtp
  7. udpsink : on envoi le flux vers 127.0.0.1 sur le port 50000 en UDP

<WRAP center round info 60%> Ici on voit que l'on peut retravailler le format de la vidéo et l'envoyer à travers le réseau. </WRAP>

Création d'un client RTP

gst-launch-0.10 udpsrc caps="application/x-rtp, media=(string)video,\
 clock-rate=(int)90000, encoding-name=(string)MP4V-ES" port=50001 \
! .recv_rtp_sink_0 gstrtpbin ! rtpmp4vdepay ! ffdec_mpeg4 ! ffmpegcolorspace \
! autovideosink
  • udpsrc : récupère le flux udp sur le port 50001
  • gstrtpbin : decode le flux udp
  • rtpmp4vdepay : decapsule le flux rtp
  • ffdec_mpeg4 : décode le flux mpeg4
  • ffmpegcolorspace : converti l'espace de couleur ffmpeg
  • autovideosink : affiche le flux distant

Portage GStreamer dans Java

GStreamer a été porté sur java grâce à la bibliothèque jna qui permet d'appeler du code C directement depuis Java. GStreamer Java

Un Pipeline simple

le pipeline correspondant à la commande suivante :

gst-launch-0.10 videotestsrc ! autovideosink

peut être codé de la manière suivante :

package gstreamer;
 
import java.io.File;
import java.util.ArrayList;
import java.util.List;
 
import org.gstreamer.Gst;
import org.gstreamer.Pipeline;
import org.gstreamer.State;
 
public class VideoTest {
 
	private static void loadGStreamerLibrary(String libraryPath) {
		File f = new File(libraryPath);
		String[] names = f.list();
		List<String> dllLoaded = new ArrayList<String>();
		boolean allLibraryLoaded = false;
		int count = 0;
		while (!allLibraryLoaded && count < 10) {
			count++;
			System.out.println("Try: " + count);
			allLibraryLoaded = true;
			for (String name : names) {
				if (name.contains("dll")) {
					if (!dllLoaded.contains(name)) {
						try {
							System.load(libraryPath + name);
							System.out.println("succeed to load: " + name);
							dllLoaded.add(name);
						} catch (UnsatisfiedLinkError e) {
							System.out.println("failed to load: " + name);
							allLibraryLoaded = false;
						}
					}
				}
			}
		}
	}
 
	public static void main(String[] args) {
                //charge la libraire gstreamer se trouvant dans bin et les plugin se trouvant dans ../lib 
                //à partir de bin
		loadGStreamerLibrary("C:/gstreamer-sdk/vers/repertoire/bin/");
		Gst.init("VideoTest", args);
		Pipeline pipe = Pipeline.launch("videotestsrc ! autovideosink");
		pipe.setState(State.PLAYING);
		while (true) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

Cependant ce code n'affiche la vidéo que dans une fenêtre externe à Java. pour lancer il faut faire

java gstreamer.VideoTest -Djna.library.path=C:/gstreamer-sdk/vers/repertoire/bin/

Charger les librairies GStreamer dans OSGI

Pour mes besoins liés à la compatibilité OSGI j'ai créé une classe utilitaire permettant de charger chacune des librairie dynamique nécéssaire au fonctionnement de GStreamer sans avoir à spécifier le jna.library.path il suffit donc d'appeler la méthode initGStreamer et celle-ci se chargera pour vous d'ajouter toutes les librairies nécéssaires si ce n'est pas déjà fait de plus si comme moi vous avez besoin de créer plusieurs lecteur vidéo en parallèle j'ai implémenté la méthode initGStreamer de manière bloquante pour qu'elle ne charge qu'une seule fois les classes dynamique et que si d'autre appel à initGStreamer sont effectué pendant le chargement qu'il attendent le chargement du premier appel.

<WRAP center round info 60%> Si vous n'utilisez pas OSGI cette classe n'est pas nécéssaire mais permet de simplifier le lancement de votre programme (Le paramètre -Djna.library.path=C:/gstreamer-sdk/vers/repertoire/bin/ n'étant plus nécéssaire). </WRAP>

package com.thales.tms.video.utils;
 
import java.awt.Toolkit;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
 
import org.gstreamer.Gst;
 
/**
 * The Class GStreamerUtils provide utils method to load GStreamer native
 * library.
 */
public class GStreamerUtils {
 
	/** The Constant GSTREAMER_CONFIGURATION_ENVIRONMENT_PROPERTY. */
	public static final String GSTREAMER_CONFIGURATION_ENVIRONMENT_PROPERTY = "gstreamer.lib";
 
	/** The Constant DEFAULT_GSTREAMER_FOLDER_RELATIVE_PATH. */
	private static final String DEFAULT_GSTREAMER_FOLDER_RELATIVE_PATH = "..\\lib\\gstreamer\\bin\\";
 
	/** The Constant DLL_EXTENSIONS. */
	private static final String DLL_EXTENSIONS = "dll";
 
	/** The Constant LOAD_RETRY. */
	private static final int LOAD_RETRY = 10;
 
	/** The is GStreamer library loaded. */
	private static Boolean isGStreamerLibraryLoadedStart = false;
 
	/** The is GStreamer library loaded success. */
	private static Boolean isGStreamerLibraryLoadedSuccess = false;
 
	/** The mutex. */
	private static ReentrantLock mutex = new ReentrantLock();
 
	/** The gstreamer loaded. */
	private static Condition gstreamerLoaded = mutex.newCondition();
 
	/** The Constant SWING_START_WAIT. */
	private static final int SWING_START_WAIT = 100;
 
	/**
	 * Instantiates a new GStreamerUtils.
	 */
	private GStreamerUtils() {
	}
 
	/**
	 * Patch to wait until Swing is running This method monitor the EventQueue
	 * of The default toolkit and wait until it has been instantiated.
	 */
	public static void waitForSwingLaunch() {
		while (Toolkit.getDefaultToolkit().getSystemEventQueue() == null) {
			try {
				Thread.sleep(SWING_START_WAIT);
			} catch (final InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
 
	/**
	 * Inits the GStreamer instances and load native dynamic library associated.
	 * 
	 * @param instanceName
	 *            the instance name of GStreamer
	 */
	public static void initGStreamer(final String instanceName) {
		final StringBuilder libraryPath = new StringBuilder();
		libraryPath.append(System.getProperty("user.dir"));
		String gstreamerFolderRelativePath = System.getProperty(GSTREAMER_CONFIGURATION_ENVIRONMENT_PROPERTY);
		if (gstreamerFolderRelativePath == null) {
			gstreamerFolderRelativePath = DEFAULT_GSTREAMER_FOLDER_RELATIVE_PATH;
		}
		libraryPath.append("\\").append(gstreamerFolderRelativePath);
		mutex.lock();
		try {
			if (!isGStreamerLibraryLoadedStart) {
				// if the GStreamer dynamic Libraries have never been loaded
				isGStreamerLibraryLoadedStart = true;
				loadGStreamerLibrary(libraryPath.toString());
				isGStreamerLibraryLoadedSuccess = true;
				// Signal other GStreamer init that the GStreamer library have
				// been loaded
				gstreamerLoaded.signalAll();
			} else {
				if (!isGStreamerLibraryLoadedSuccess) {
					// if GStreamer dynamic libraries have not finished to be
					// loaded wait for GStreamer library loading to end
					try {
						gstreamerLoaded.await();
					} catch (final InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		} finally {
			mutex.unlock();
		}
		// Use the default glib main context to prevent launch problem.
		Gst.setUseDefaultContext(true);
		Gst.init(instanceName, new String[] {});
	}
 
	/**
	 * Load GStreamer library.
	 * 
	 * @param libraryPath
	 *            the library path .to load
	 */
	private static void loadGStreamerLibrary(final String libraryPath) {
		final File f = new File(libraryPath);
		final String[] names = f.list();
		final List<String> dllLoaded = new ArrayList<String>();
		boolean allLibraryLoaded = false;
		int count = 0;
		while (!allLibraryLoaded && count < LOAD_RETRY) {
			count++;
			allLibraryLoaded = true;
			for (final String name : names) {
				if (name.contains(DLL_EXTENSIONS)) {
					if (!dllLoaded.contains(name)) {
						try {
							System.load(libraryPath + name);
							dllLoaded.add(name);
						} catch (final UnsatisfiedLinkError e) {
							allLibraryLoaded = false;
						}
					}
				}
			}
		}
	}
}

Un Serveur RTP

Voici le code pour créer un simple Server RTP en java

public static void startRTPServer(final String ip, final String port) {
		System.setProperty(GStreamerUtils.GSTREAMER_CONFIGURATION_ENVIRONMENT_PROPERTY,
			"..\\..\\executable\\com.thales.tms.felix\\lib\\gstreamer\\bin\\");
		GStreamerUtils.initGStreamer("RTPMP4Server");
		final Pipeline pipe = Pipeline
			.launch("ksvideosrc device-index=0 ! videoscale ! video/x-raw-yuv,width=640,height=480 
                                ! videorate ! video/x-raw-yuv,framerate=25/1 ! ffmpegcolorspace ! ffenc_mpeg4
                                 bitrate=1000000 ! rtpmp4vpay send-config=true ! udpsink host="
                                 + ip + " port=" + port);
		pipe.setState(State.PLAYING);
		while (true) {
			try {
				Thread.sleep(100);
			} catch (final InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

Sources