Why calling methods on gstreamer sometimes is blocking forever ?

Nicolas Castillejos djos06 at gmail.com
Mon Dec 12 09:47:07 UTC 2016


Hello,

Thank you for your answers, I put my java code in attachment .
I don't really think it comes from my application.
As you can see, my code is really simple. I fill the appsrc with a byte 
array. There is no possible deadlock I can see.
I understand the multi-process solution, but it is a pain to do this as 
I have to display video pictures in my java application.
Do you think it can be a problem with the java mapping ? it is not the 
first time I met problems with it so....

Nicolas Castillejos


-------------- next part --------------
package com.swingws.shaft.mosaic.gstreamer.players.hls.mosaic;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.freedesktop.gstreamer.Bin;
import org.freedesktop.gstreamer.Buffer;
import org.freedesktop.gstreamer.Caps;
import org.freedesktop.gstreamer.Element;
import org.freedesktop.gstreamer.ElementFactory;
import org.freedesktop.gstreamer.FlowReturn;
import org.freedesktop.gstreamer.GhostPad;
import org.freedesktop.gstreamer.Pad;
import org.freedesktop.gstreamer.Pipeline;
import org.freedesktop.gstreamer.State;
import org.freedesktop.gstreamer.Structure;
import org.freedesktop.gstreamer.elements.AppSrc;
import org.freedesktop.gstreamer.elements.AppSrc.Type;

import com.swingws.shaft.mosaic.gstreamer.hls.ChunkListProvider;
import com.swingws.shaft.mosaic.gstreamer.hls.optimize.HlsInputStreamByteArrayFixed;
import com.swingws.shaft.mosaic.gstreamer.players.listeners.IHLSPlayer;
import com.swingws.shaft.mosaic.gstreamer.players.listeners.IPlayerStateListener;
import com.swingws.shaft.mosaic.gstreamer.players.listeners.IPositionListener;
import com.swingws.shaft.mosaic.ui.beans.PlayerSource;

/**
 * A Gstreamer Player that takes a Java InputStream as input.
 * @author Nicolas Castillejos
 *
 */
public class HLSMosaicPlayer implements IHLSPlayer{
	
	
	private long last_time_read = 0;
	private long TEMPO_DEBIT = 40;

	private PlayerSource player_source;
	
	private HlsInputStreamByteArrayFixed input_stream;
	
	private Pipeline pipe;

	private AppSrc src;

	private Element tsDemux;

	private static Log logger = LogFactory.getLog(HLSMosaicPlayer.class);
	
	private Element sink_element;
	
	/** 
	 * The Entire DVR into an array.
	 */
	private byte[] video_data;
	
	/**
	 * The current position into the file.
	 */
	private int position = 0;
	
	// VIDEO BIN ELEMENTS
	private Element h264parse;
	private Element avdec_h264;
	private Element videoconvert;
	private Element capsfilter;
	private Bin videoBin;
	
	// VIDEO PADS
	private Pad pad;
	private Pad videoPad;
	private Pad ghost_pad;
	private Caps caps;
	
	// HANDLERS
	private NEED_DATA_HANDLER need_data_h;
	private PAD_ADDED_HANDLER pad_added_h;

	private enum ETAT{
		UNLOADED,LOADED,CREATING_PIPELINE,PIPELINE_CREATED,PLAYING,STOPPING_FEEDING,STOPPING,STOPPED;
	}
	
	private ETAT state = ETAT.UNLOADED;
	
	
	public HLSMosaicPlayer(Element sink_element){
		this.sink_element = sink_element;
		
	}

	private void setEtat(ETAT new_etat){
		state = new_etat;
		info_log(" state is now " + state);
	}

	private void create_pipeline(){
		pad = null;
		videoPad= null;
		
		create_app_src();
		create_video_bin(sink_element);
		create_ts_demux();
		
		pipe = new Pipeline("main pipeline");
		pipe.add(src);
		pipe.add(tsDemux);
		pipe.add(videoBin);

		src.link(tsDemux);
		info_log("pipeline created.");
	}
	
	private void destroy_pipeline(){
		
		if (pipe == null /*|| pipe.isDisposed()*/) return ;
		
		src.disconnect(need_data_h);
		tsDemux.disconnect(pad_added_h);
		
		if (pad != null && videoPad != null && !pad.isDisposed() && !videoPad.isDisposed())
			pad.unlink(videoPad);
		
		caps.dispose();;
		
		h264parse.unlink(avdec_h264);
		avdec_h264.unlink(videoconvert);
		videoconvert.unlink(capsfilter);
		capsfilter.unlink(sink_element);
		
		videoBin.removePad(ghost_pad);
		ghost_pad.dispose();
		
		videoBin.removeMany(h264parse,avdec_h264,videoconvert,capsfilter,sink_element);
		
		h264parse.dispose();
		avdec_h264.dispose();
		videoconvert.dispose();
		capsfilter.dispose();

		src.unlink(tsDemux);
		
		pipe.remove(videoBin);
		pipe.remove(tsDemux);
		pipe.remove(src);
		
		videoBin.dispose();
		tsDemux.dispose();
		src.dispose();
		
		pipe.dispose();
		
		src=null;
		pipe = null;
		tsDemux=null;
		video_data=null;
		input_stream=null;
		player_source=null;
		sink_element=null;
		
		// VIDEO BIN ELEMENTS
		h264parse=null;
		avdec_h264=null;
		videoconvert=null;
		capsfilter=null;
		videoBin=null;
		
		// VIDEO PADS
		pad=null;
		videoPad=null;
		ghost_pad=null;
		caps=null;
		
		// HANDLERS
		need_data_h=null;
		pad_added_h=null;
		
		
	}
	
	private void create_ts_demux(){
		tsDemux = ElementFactory.make("tsdemux", "tsdemux");
		
		pad_added_h = new PAD_ADDED_HANDLER();
		
		tsDemux.connect(pad_added_h);
	}
	
	private void create_app_src(){
		src = (AppSrc) ElementFactory.make("appsrc", "source");
		src.set("block", true);
		src.setMaxBytes(16000);
		
		debug_log("block property = " + src.get("block") + " max-bytes = " + src.get("max-bytes"));
		
		
		src.setLive(true);
		src.setStreamType(Type.STREAM);
		
		src.setLive(false);
		//src.setStreamType(Type.RANDOM_ACCESS);
		
		src.set("emit-signals", true);
		
		need_data_h = new NEED_DATA_HANDLER();
		
		src.connect(need_data_h);
		
		
	}
	
	private void create_video_bin(Element sink_element){
		videoBin = new Bin("Video Bin");
		
		h264parse = ElementFactory.make("h264parse", "h264parse");
		avdec_h264 = ElementFactory.make("avdec_h264", "H264 Decoder");
		videoconvert = ElementFactory.make("videoconvert", "videoconvert");
		capsfilter = ElementFactory.make("capsfilter", "capsfilter");
		
		StringBuilder capsString = new StringBuilder("video/x-raw, ");
		if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
			capsString.append("format=BGRx");
        } else {
        	capsString.append("format=xRGB");
        }
		
		caps = Caps.fromString(capsString.toString());
		capsfilter.set("caps", caps);

		videoBin.addMany(h264parse,avdec_h264,videoconvert,capsfilter,sink_element);
		
		h264parse.link(avdec_h264);
		avdec_h264.link(videoconvert);
		videoconvert.link(capsfilter);
		
		capsfilter.link(sink_element);
		
		ghost_pad = new GhostPad("sink", h264parse.getStaticPad("sink"));
		videoBin.addPad(ghost_pad);

	}
	
	private void pipe_play(){
		info_log("pipe.play()");
		pipe.play();
	}
	private void pipe_pause(){
		info_log("pipe.pause()");
		pipe.pause();
	}
	
	

	
	/**
	 * Play the Input Stream as source into a GStreamer Player
	 */
	//	@Override
	public boolean play() {
		info_log("play is called , etat = " + state);

		if (state == ETAT.LOADED){
			setEtat(ETAT.CREATING_PIPELINE);
			create_pipeline();
			setEtat(ETAT.PIPELINE_CREATED);
			pipe_play();
		} else if (state == ETAT.UNLOADED){
			throw new IllegalStateException("player should be loaded before calling play() method");
		}

		return true;
	}

	//	@Override
	public void seek(int percent){
		// pas de deplacement dans la video
	}

	
	/**
	 * Stop the player.
	 */
	public void stop() {
		info_log("HLSFileInputStreamJavaPlayer is stopping...");
		setEtat(ETAT.STOPPING);
		info_log("src is stopped");
		src.endOfStream();
		
		position = 0;
		
		if (pipe!=null){
			info_log("stop() is called");
			pipe.stop();
			info_log("destroying pipeline ... ");/*(current state = " + pipe.getState() + ")");*/
			
			destroy_pipeline();
			info_log("pipeline destroyed. ");/*(current state = " + pipe.getState() + ")");*/
		}
		setEtat(ETAT.STOPPED);
		info_log("HLSFileInputStreamJavaPlayer is stopped !");

	}

	

	

	public void setPause(boolean pause) {
		if (pause){
			if (pipe.isPlaying())
				pipe_pause();
		} else {
			if (!pipe.isPlaying())
				pipe_play();
		}
		
	}

	
	//	    __                       _     
	//	   / _\ ___  _   _ _ __   __| |    
	//	   \ \ / _ \| | | | '_ \ / _` |    
	//	   _\ \ (_) | |_| | | | | (_| |    
	//	   \__/\___/ \__,_|_| |_|\__,_|    

	//	@Override
	public void unmute() {
		// pas de son
	}

	//	@Override
	public void mute() {
		// pas de son
	}

	//	@Override
	public void setVolumePercent(double percent) {
		// pas de son
	}
	
	/**
	 * REMPLISSEUR DU BUFFER DE GSTREAMER. (à sa demande)
	 * @author Nicolas
	 *
	 */
	private class NEED_DATA_HANDLER implements AppSrc.NEED_DATA{

		@Override
		public void needData(AppSrc appsrc, int buffer_size) {
		
			
			// FIN VIDEO
			if (position >=video_data.length){
				return;
			}
			
			temporisation();
			// PUSHING BUFFER
			FlowReturn r = pushBuffer(appsrc,buffer_size);
			if (r == FlowReturn.OK){
				position = (position + buffer_size)%video_data.length;
			} else {
				warn_log("ATTENTION retour de pushBuffer == "+ r.name() + " appsrc state = " + appsrc.getState());
			}
			
			
			
		}
	}
	
	private FlowReturn pushBuffer(AppSrc appsrc, int buffer_size){
		Buffer buffer = new Buffer(buffer_size);
		ByteBuffer bb = buffer.map(true);
		bb.put(video_data,position,Math.min(video_data.length-position,buffer_size));
		buffer.unmap(); // essentiel pour libérer la mémoire c++ du buffer.
		
		FlowReturn retour = appsrc.pushBuffer(buffer);
		return retour;
	}
	
	
	/**
	 * Temporise le remplissage du buffer de gstreamer.
	 * Pour ne pas que la vidéo défile trop vite et que le cpu ne soit pas utilisé à 100%.
	 */
	private void temporisation(){
		while (( (System.currentTimeMillis()- last_time_read) < TEMPO_DEBIT )  && state != ETAT.STOPPING_FEEDING){
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		last_time_read = System.currentTimeMillis();
	}
	
	/**
	 * Connecteur des pads vidéos.
	 * @author Nicolas
	 *
	 */
	private class PAD_ADDED_HANDLER implements Element.PAD_ADDED{

		
		@Override
		public void padAdded(Element element, Pad pad) {
			HLSMosaicPlayer.this.pad = pad;
			debug_log("padAdded");

				// check media type
				Caps caps = HLSMosaicPlayer.this.pad .getCaps();
				Structure struct = caps.getStructure(0);
				debug_log("padAdded : " + struct.getName());
				if (struct.getName().startsWith("video/")) {
					// only link once
					videoPad = videoBin.getStaticPad("sink");
					if (HLSMosaicPlayer.this.pad .isLinked()) {
						return;
					}
					

					//logger.info("Got video pad : " + caps);
					HLSMosaicPlayer.this.pad.link(videoPad);
			
					state = ETAT.PLAYING;

				}
	
		}
	}


	@Override
	public void started(boolean started) {
		// not used
	}
	
	@Override
	public ChunkListProvider getChunkListProvider() {
		return this.input_stream.getChunkListProvider();
	}

	@Override
	public void load(PlayerSource source, IPositionListener position_listener, String camera, String idPLayer,IPlayerStateListener state_listener) {
		this.player_source = source;
		this.input_stream = new HlsInputStreamByteArrayFixed(source.getUrl(),camera,idPLayer);
		this.input_stream.start(source.getStart(),source.getEnd());
		player_source.setVideo_bytes(input_stream.getVideo_bytes());
		video_data = ((HlsInputStreamByteArrayFixed)input_stream).getVideo_bytes();
		setEtat(ETAT.LOADED);
		info_log("loaded " + video_data.length + " bytes");

	}
	
	
	private void debug_log(String message){
		logger.debug("[" + this.hashCode() + "] " + message);
	}
	
	private void warn_log(String message){
		logger.warn("[" + this.hashCode() + "] " + message);
	}
	
	private void error_log(String message){
		logger.error("[" + this.hashCode() + "] " + message);
	}
	private void info_log(String message){
		logger.info("[" + this.hashCode() + "] " + message);
	}
	


	
	public float getTotalDuration(){
		return input_stream.getTotalDuration();
	}

	@Override
	public PlayerSource getSource() {
		return player_source;
	}

	@Override
	public void destroy() {
		
	}

}


More information about the gstreamer-devel mailing list