GStreamer java: AppSink not receiving new_sample signal - java

I have the task to stream an IP camera's video stream (RTP/RTSP in h264) via J2EE application server to a browser. For this I am using GStreamer 1.21.3 (latest dev release) with the gstreamer-java library on top. We are aiming towards a Websocket solution as the traditional HLS introduces significant latency.
After having figured out what to do with the gst-launch executable on the commandline, I ended up with this code (for the moment):
/*
* Configuration for RTSP over TCP to WebSocket:
* 1. rtspsrc to ip camera
* 2. rtph264depay ! h246parse to extract the h264 content
* 3. mp4mux to create fragmented MP4
* 4. appsink to grab the frames and use them in Websocket server
*/
final String gstPipeline = String.format("rtspsrc onvif-mode=true protocols=tcp user-id=%s user-pw=%s location=%s latency=200"
+ " ! rtph264depay ! h264parse"
+ " ! mp4mux streamable=true fragment-duration=5000"
+ " ! appsink name=sink", USERNAME, PASSWORD, uri);
final Pipeline pipeline = initGStreamerPipeline(gstPipeline);
// Add listener to consume the incoming data
final AppSink sink = (AppSink) pipeline.getElementByName("sink");
sink.setCaps(Caps.anyCaps());
sink.set("emit-signals", true);
sink.set("max-buffers", 50);
sink.connect((NEW_SAMPLE) appsink -> {
final Sample sample = appsink.pullSample();
if (sample == null)
{
return FlowReturn.OK;
}
final Buffer buffer = sample.getBuffer();
try
{
final ByteBuffer buf = buffer.map(false);
LOGGER.debug("Unicast HTTP/TCP message received: {}", new String(Hex.encodeHex(buf, true)));
if (session != null)
{
try
{
buf.flip();
session.getRemote().sendBytes(buf);
}
catch (final Exception e)
{
LOGGER.error("Failed to send data via WebSocket", e);
}
}
}
finally
{
buffer.unmap();
}
return FlowReturn.OK;
});
sink.connect((AppSink.EOS) s -> LOGGER.info("Appsink is EOS"));
sink.connect((AppSink.NEW_PREROLL) s -> {
LOGGER.info("Appsink NEW_PREROLL");
return FlowReturn.OK;
});
LOGGER.info("Connecting to {}", uri);
/**
* Start the pipeline. Attach a bus listener to call Gst.quit on EOS or error.
*/
pipeline.getBus().connect((Bus.ERROR) ((source, code, message) -> {
LOGGER.info(message);
Gst.quit();
}));
pipeline.getBus().connect((Bus.EOS) (source) -> Gst.quit());
pipeline.play();
/**
* Wait until Gst.quit() called.
*/
LOGGER.info("Starting to consume media stream...");
Gst.main();
pipeline.stop();
server.stop();
Now I seem to be stuck here, because the AppSink at the end of the pipeline never gets its new_sample signal triggered. The complete example works like a charme when I replace the appsink with a filesink. I have noticed that there are some other threads (like this one) with similar problems which normally boil down to "you forgot to set emit-signals=true". Any ideas why my appsink gets no data?
Update:
It appears that the problem is the URL I am passing to the pipeline string. It has two query parameters: http://192.168.xx.xx:544/streaming?video=0&meta=1. If I remove the second parameter (and the ambersand along with it), the pipeline works. Unfortunately I found no docs how to escape URLs in the correct way so GStreamer can read it. Can anyone share such documentation?
Update 2:
It starts getting weired now: It looks like the name of the URL parameter is the problem. I have started to replace it with some dummy argument and it works. So the ambersand is not the problem. Then I used VLC media player to consume the stream with the &meta=1 in place which also worked. Is it possible that the string "meta" is treated special in gstreamer?

Related

Java BufferedInputStream read() does not receive data after not reading from Stream for some time

I wrote an application that listens for status messages from a robot. I have an open Socket connection to the robot. The robots sends constantly status messages which I read ( and parse ) to find out when the robot is done with his current task.
/**
* Listens on the Socket for RobotStateMessage (RSM). A RSM has messagetype 16 at byte[4]. The constructor of the RobotStateMessage class
* throws an exception when the message is found to be corrupt. If that happens the method waits for the next uncorrupted message.
*
* #param command the robot state message is added to the command
* #throws IOException
* #throws GeneralURMessage_Parse_Exception
* #throws TimeExpired_Exception
*/
public void executeRobotStateCommand(RobotStateCommand command) throws IOException, RobotStateMessage.GeneralURMessage_Parse_Exception, TimeExpired_Exception {
byte[] data = new byte[3000];
boolean run = true;
RobotStateMessage message = null;
long momentToQuit = System.currentTimeMillis() + 5000;
while (run) {
if (System.currentTimeMillis() > momentToQuit) {
throw new TimeExpired_Exception(5000);
}
ur_in.read(data);
int type = data[4];
if (type == 16) {
run = false;
try {
// contructor of RobotStateMessage parses the data
message = new RobotStateMessage(data);
} catch (RobotStateMessage.CorruptRobotStateMessage_Exception e) {
// the message received form ur was corrupted , ignore and wait for next message
run = true;
}
}
}
command.robotStateMessage = message;
}
The problem occurs when I stop reading from the BufferedInputStream ur_in for a while. Any pause longer than aproximatly a minute and the ur_in.read(data) blocks after consuming all the buffered old data.
After consuming buffered data there seems to be no new data on the BufferedInputStream. Using a second tool to listen to the data from the robot, I can clearly see that the status messages are still broadcasted.
The Socket is still alive but the Inputstream seems to have "died". I put a timeout on the socket so that the read() does not block forever, but that does not solve my problem, that I receive no more data over the BufferdInputStream.
The only thing that helped was reconnecting the socket, which is not a satisfying solution for my problem.
Any help or suggestions how to solve this are appreciated. If you need more information please ask.
I assume that you are using TCP. If you don't read from the buffer the the robot will be notified by your socket to stop transmission until the buffer has capacity to receive more data. The robot software will evetually have to handle that no data can be written and probably disconnects your client. You should not use the socket buffer to store data and instead as quickly as possible move the data to an other buffer. Setting a larger receive buffer will probably help a little, but is not a good idea.

Too much data on Node.js socket?

I'm currently developing a system that gets data from a battery pack of an electric vehicle, stores it in a database and display it on a screen.
So I have a Java - Application that reads the data from a hardware interface, interprets the values and sends it via Socket to a Node.js-Server. (Java App and Webserver are running on the same computer, so Url = localhost)
JAVA APP:
s = new Socket();
s.connect(new InetSocketAddress(URL, PORT));
out = new PrintWriter( s.getOutputStream(), true);
for (DataEntry e : entries){
out.printf(e.toJson());
}
NODE:
sock.on('data', function(data) {
try{
var data = JSON.parse(data);
db.serialize(function(){
db.run("INSERT INTO DataEntry(value, MessageField, time) values(" + data.value + "," + data.messageFieldID + ", STRFTIME('%Y-%m-%d %H:%M:%f'))");
});
} catch(e){}
});
I get about 20 Messages per second from the hardware interface which are converted into 100 Json - Strings. So the webserver has to process one message in 10 ms, which I thought, is manageable.
But here is the problem: If my entries - List (foreach loop) has more than 2 elements, the webserver gets 2 or more of the Json's in one message.
So the first message was divided into 2 parts (ID 41,42) and was processed correctly. But the second message was divided into 5 parts (ID 43-47), and the first 4 of them weren't sent alone, so only the last one was saved correctly.
How can I ensure, that every Json is sent one another?
Isn't there something like a buffer so that the socket.on method is called correctly for every message I send?
I hope somebody of you can help me
Thank you!
Benedikt :)
TCP sockets are just streams and you shouldn't make any assumptions about how much of a "message" is contained in a single packet.
A simple solution to this is to terminate each message with a newline character since JSON cannot contain such a character. From there it's a simple matter of buffering data until you see a newline character. Then call JSON.parse() on your buffer. For example:
var buf = '';
sock.on('data', function(data) {
buf += data;
var p;
// Use a while loop as it may be possible to have multiple
// messages buffered depending on chunk contents
while (~(p = buf.indexOf('\n'))) {
try {
var msg = JSON.parse(buf.slice(0, p));
} catch (ex) {
console.log('Bad JSON message: ' + ex);
}
buf = buf.slice(p + 1);
}
});
You will also need to change printf() to println() on the Java-side so that a newline character will be appended to each message.

Canceling filewatcher run by TimerTask after receiving websocket message in Java

I've been coding around in circles trying to work this one out, new to java and have been lurking here to find things out for a while but I really can't get passed this one. I adapted some code by Desmond Shaw (http://www.codepool.biz/how-to-implement-a-java-websocket-server-for-image-transmission-with-jetty.html) to create a websocket to tranfer jpg images from a server to remote clients. I want to send files from the server to the browser windows of connected clients when some specific files on the server change (they are pages of music scores that are created using Max/MSP in real-time), but I don't seem to be able to cancel the timers I'm creating to watch these files in my home directory for changes.
More specifically I'm sending messages from the remote browser clients (through javascript buttons operated by the users) over a websocket connection to specify which of the files they wish to see updated on their screen (i.e part one, which refers to a file on the server called "1.png" and is the violin part, or part 2 which is the server file "2.png" and is the cello part etc). This is then used within the websocket handler running on my server to send the right files to that client when a filewatcher detects they have changed on the server. I can get everything going except stopping the timers running the filewatchers, when a different part is requested by the client (say the violin player wants to look at the cello players part). Below is the method I have edited to respond to the messages from the clients:
#OnWebSocketMessage //part request from websocket client (remote browser)
public void onMessage( String message) {
System.out.println("Message: '" + message + "' received");
sFclient = message;
if (sFclient == "1" || sFclient == "2" || sFclient == "3" || sFclient == "4") {
System.out.println("Part " + sFclient + " joined");
}
else {
sFclientOut = 0;
}
}
public void onChange( File file ) {
System.out.println( "File "+ file.getName() +" has changed!" );
TimerTask task = new FileWatcher(new File("/Users/benedict/" + message + ".png")) {
try {
File f = new File("/Users/benedict/" + sFclient + ".png");
BufferedImage bi = ImageIO.read(f);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(bi, "png", out);
ByteBuffer byteBuffer = ByteBuffer.wrap(out.toByteArray());
mSession.getRemote().sendBytes(byteBuffer);
out.close();
byteBuffer.clear();
}
catch (IOException e) {
e.printStackTrace();
}
Timer timer1 = new Timer(); {
timer1.schedule(task , new Date(), 20 );
if (sFclientOut == 0){
task.cancel();
timer1.cancel();
}
}
}
I had it working mostly using an if statement which I've now abandoned but have been editing and now it doesn't make as much sense probably. Any help at all would be appreciated, but my main question is should I be trying to cancel the threads handling the timertasks or use a completely different approach altogether like a switch statement for example. I have tried sending a message before every new message from the browsers ("0") to cancel the old threads but the Timertasks just don't start at all, which I think is because that cancels the timertask and doesn't let it run again?
Thanks,
Benedict
Ok here's the final working solution, I just had to cancel the tasks based on a message from the clients (in this case a 0)
else if (message.equals("0")) {
zerocounter = zerocounter + 1;
if (zerocounter >= 2) {
task.cancel();
}

mediaReader.readPacket() blocks while trying to read rtsp stream in Xuggler

I am trying to download a video (with a Xuggler 5.4 library) from rtsp stream to a file using the code below.
String inputSource = "rtsp://[ip-address]:[port]/user=[username]&[password]=password&channel=1&stream=1.sdp";
String outputFilename = "d:/downloadedrtsp.flv";
try {
IContainerFormat inFormat = IContainerFormat.make();
inFormat.setInputFormat("h246");
IMediaReader mediaReader = ToolFactory.makeReader(inputSource);
mediaReader.setQueryMetaData(false);
IMediaWriter mediaWriter = ToolFactory.makeWriter(outputFilename, mediaReader);
mediaReader.addListener(mediaWriter);
logger.info("before reading");
IError error;
while ((error = mediaReader.readPacket()) == null) {
logger.info("reading packet");
}
logger.info("error: " + error.getDescription());
logger.info(error.getType());
logger.info(error.toString());
} catch (Exception e) {
e.printStackTrace();
}
The problem is that after printing "before reading" the code just stop executing, and after a long time it prints me three lines from logger:
error: Unknown error
ERROR_EOF
Unknown error
Stream works great when i am opening it in the VLC media player. I am shure there is some mistake in my mediaReader configuration, but i don't know where exactly as i have a very little experience working with videos. Here is some information about video, taken from VLC:
It seems like everything works as expected.
The error type ERROR_EOF marks the end of the input stream (see the documentation).
The long time you program "stop executing" is the time it takes for Xuggler to convert the video frames (it actually doesn't "stop", just iterate through the while loop).

WebSocket disconnected when trying to send large size of images

I am testing a WebSocket at localhost using java and javascirpt, running Tomcat 7.0.42 and no proxy in between. It works fine on sending text and small size of image via websocket. However, it will be forced to close the connection on client side(chrome browser) when trying to send a large size of photo (Notice that the tomcat's 'onClose callback in MessageInbound' does not be notified after websocket on browser closed the connection).
how can I solve it? thx.
Here is the capture from chrome development tool
Below is my code on client side:
for (var i = 0, f; f = files[i]; i++) {
// step 1: tell server who the people you want to send
ws.send(JSON.stringify({
action: "binary",
receiver: <%=selectedfriend.getUserId()%>,
timestamp: new Date().getTime()
}));
// step 2: send file
ws.send(f);
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" style="width: 50px;height: 30px;" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'].join('');
appendImage(span.innerHTML, "pullleft");
};
})(f);
reader.readAsDataURL(f);
}
Finally, I found a solution for my question, what i did on Tomcat is to use:
protected StreamInbound createWebSocketInbound(String string, HttpServletRequest hsr) {
MyMessageInbound inbound = new MyMessageInbound();
inbound.setByteBufferMaxSize(9999999);
inbound.setOutboundByteBufferSize(9999999);
return inbound;
}
But there is another problem is:
what is the proper value I should use, here is 9999999, the value should be tested well when I upload under 8-9MB file using WebSocket, but why, how can I measure it?
Please help and discuss here again, thx!
Try to send as ArrayBuffer:
ws.send(f.readAsArrayBuffer());

Categories

Resources