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