GStreamer open plain m3u from http - java

I'm trying to open plain m3u file without any meta data from http server:
1.ts
2.ts
3.ts
Simple way with playbin fails, telling it's a text file (yes, it is! tried both m3u and m3u8 extensions)
gst-launch-1.0.exe playbin uri=http://10.42.0.3:8765/list.m3u8
ERROR: from element /GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0/GstDecodeBin:decodebin0: This appears to be a text file
decodebin cannot decode plain text files
decodebin in solo refuses to get file from http:
gst-launch-1.0 filesrc location=http://10.42.0.3:8765/list.m3u8 ! decodebin
ERROR: from element /GstPipeline:pipeline0/GstFileSrc:filesrc0: Could not open file "http://10.42.0.3:8765/list.m3u8" for reading.
and forced uridecodebin (that is supposed to accept playlists according to GStreamer documentation) fails like playbin:
gst-launch-1.0.exe uridecodebin uri=http://10.42.0.3:8765/list.m3u
ERROR: from element /GstPipeline:pipeline0/GstURIDecodeBin:uridecodebin0/GstDecodeBin:decodebin0: This appears to be a text file
decodebin cannot decode plain text files
If I add HLS-style meta data to same file:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:2.662589,
1.ts
#EXTINF:2.829011,
2.ts
#EXTINF:2.995411,
3.ts
it is correctly displayed with:
gst-launch-1.0.exe playbin uri=http://10.42.0.3:8765/list.m3u8
without any problems.
Is there any way to show video from m3u (or any other simle-list fileformat) without meta data?
And is there any way to jump to specified track during playback (I use Java bindings but they seem to have same naming as original lib)?
p.s. Same pure m3u is correctly played with vlc, ffplay and mpv
p.p.s. I understand that I can parse m3u and send links one-by-one on finished event, but I hope to gain some gapless playback without extra pain :-)
ADDED:
I wrote quick-and-durty parser with switcher:
playbin.getBus().connect(new Bus.EOS() {
#Override
public void endOfStream(GstObject source) {
EventQueue.invokeLater(() -> {
if (filesIndex < files.length) {
try {
playbin.stop();
playbin.setURI(new URI(request + files[filesIndex]));
playbin.play();
System.out.println("SWITCHED TO: " + request + files[filesIndex]);
} catch (Exception e){
e.printStackTrace();
}
}
filesIndex++;
});
}
});
playbin.getBus().connect(new Bus.ERROR() {
#Override
public void errorMessage(GstObject gstObject, int i, String s) {
EventQueue.invokeLater(() -> {
if (filesIndex < files.length) {
try {
playbin.stop();
// filesIndex++; Retry?
playbin.setURI(new URI(request + files[filesIndex]));
playbin.play();
System.out.println("SWITCHED ON ERROR TO: " + request + files[filesIndex]); // Error counter?
} catch (Exception e){
e.printStackTrace();
}
}
filesIndex++;
});
}
});
where files[] is m3u splited by lines, and this works very well (including jumping), but I still wonder what is a "built-in" way of playing lists and switching indexes

Related

GStreamer java: AppSink not receiving new_sample signal

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?

Resume last downloaded file upon Exception

I'm using Java SDK 1.11.534
In my tool I declared a download named 'down' using TransferManager,
since the call:
down.waitForCompletion();
is a blocking call and stops the ProgressBar acknowledgement by ProgressListener I had to introduce a SwingWorker as follows:
SwingWorker worker = new SwingWorker<Void,Integer>(){
#Override
protected void process(List<Integer> chunks) {
int j = chunks.get(chunks.size()-1);
if (i<=fileNum) jLabel4.setText("Scaricamento file " + i+ " di " + fileNum + " del DCP "+ DCPname+" in corso, attendere....");
else jLabel4.setText("Scaricamento DCP "+ DCPname+" completato con successo.");
}
#Override
protected Void doInBackground(){
for (S3ObjectSummary file: fileList){
if((!isPresent(destination,file.getKey().substring(file.getKey().lastIndexOf("/") + 1),file.getSize())) && (!(file.getKey().substring(0, file.getKey().length()-1).equals(DCPname)))){
publish(i);
GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, file.getKey());
down = tx.download(getObjectRequest,new File(percorso+File.separator + file.getKey().substring(file.getKey().lastIndexOf("/") + 1)));
down.addProgressListener(progressListener);
try {
down.waitForCompletion();
} catch (AmazonClientException ex) {
ex.printStackTrace();
tx.shutdownNow(true);
//jButton4.setEnabled(true);
jButton4.doClick();
} catch (InterruptedException ex) {
ex.printStackTrace();
tx.shutdownNow(true);
//jButton4.setEnabled(true);
jButton4.doClick();
}
i++;
}
This is a portion of the code where doInBackground() shows the operations to do.
It happens sometimes to have an AmazonClientException reporting:
Not all bytes from S3inputstream were read
And this leads to have a corrupted file and a stop of the program itself upon exception.
At the beginning of my code (not reported here) before reaching the SwingWorker declaration, I stated that when the jButton4 is clicked the action starts checking if there's a size mismatch between files in the download folder and the ones on Amazon s3 and if there's a truncated file it gets deleted and the name is added to the download list again.
So the only solution I've found so far is to add the following line code:
jButton4.doClick();
In the exception code, meaning when an exception is hit the progress restarts and checks for truncated files and restarts downloads adding such a file too.
My question is:
Is there any way in the SDK to resume or better cancel and then download file again upon exception without restarting the program? I find the usage of:
jButton4.doClick();
is not a professional way of coding.
You could extract the content of the click action method into a new method and call that method instead.

How to write file on OpenStack container using JOSS

I need help because i need to integrate JOSS in a existing code. My code uses the Consumer feature of Java 8.
Consumer<? super GHRepository> action = repo -> {
try {
if(github.getRateLimit().remaining > 0) {
Files.write(this.path, (repo.toString() + "\n").getBytes(), StandardOpenOption.APPEND);
totalIteration++;
} else {
logger.info("Time to pause for " + (github.getRateLimit().reset.getTime() - new Date().getTime()));
//wait until rate limit is ok.
do {
Thread.sleep(60000);
} while(github.getRateLimit().reset.after(new Date()));
}
} catch (Exception e) {
logger.error("Erreur d'écriture dans le fichier : " + e.getMessage());
}
};
This code works fine but disk space available on the machine is not enough. So i need to write the file directly on an OpenStack container.
I've read in the doc that JOSS uses this function to upload a file.
StoredObject object = container.getObject("dog.png");
object.uploadObject(new File("/dog.png"));
This is the method to upload a file already written. But I need to write the file directly on the container. The uploadObject function can receive a InputStream in parameter. So i want to use it. But i don't know how to integrate it with my existing code. Can you help me?
Ok, i find the way .
object.uploadObject(Files.newInputStream(Files.write(this.path, (repo.toString() + "\n").getBytes(), StandardOpenOption.APPEND)));

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).

How to pass URL parameters from Java to local HTML file in Windows 7?

I desperately need your expertise in resolving a Windows-7 issue.
Scenario: I have a frame-based Help package that is set up for context-sensitive help calls. A Java application is able to control what page the Help packages opens to by passing a tag representing the desired HTML named anchor to an HTML file called pophelp. This file has javascripting that reads the passed tag from the end of the URL and maps it to the appropriate HTML file in the help package and opens it.
Issue: The above scenario works in Windows XP, but no longer in Windows 7.
Calling mechanism from Java application: rundll32 url.dll,FileProtocolHandler file://filepath/pophelp.html?tag
Summary of findings so far: It appears that url.dll no longer allows parameters to be passed with URLs in Windows 7. Parameters are being stripped. I also tried the same type of call using Desktop.getDesktop().browse() from Java, but it too seems to strip off all parameters after .html.
Sample code:
Original call that works in Windows XP --
Running command: rundll32 url.dll,FileProtocolHandler file://C:\Program Files\App System\App-Editor-8.0.1\help\pophelp.html?TAG=callsubflow
Result: ?TAG=callsubflow is not passed.
New code using Desktop.getDesktop().browse() --
public static void main(String[] args) {
String url = "file:///C:/Program Files/App System/App-Editor-8.0.1/help/pophelp.html?TAG=callsubflow";
try {
if (Desktop.isDesktopSupported()) {
Desktop desktop = Desktop.getDesktop();
if (desktop.isSupported(Desktop.Action.BROWSE)) {
desktop.browse(new URI(url.replace(" ", "%20")));
}
}
} catch (IOException e) {
System.out.println("Unable to open "+url+": "+e.getMessage());
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
Result: ?TAG=callsubflow is not passed.
Any assistance would be appreciated!
I really can't tell why Windows removes parameters on local files. As mentioned in the comments this seams to be some kind of weird restrictions for security. But I once had a similar problem and I found a workaround that fits in this situation as well.
Simply create a local temporary HTML file (without parameters) that redirects you to the desired one (with parameters).Have a look at these two methods:
// creates w3c conform redirect HTML page
public String createRedirectPage(String url){
return "<!DOCTYPE HTML>" +
"<meta charset=\"UTF-8\">" +
"<meta http-equiv=\"refresh\" content=\"1; url=" + url + "\">" +
"<script>" +
"window.location.href = \"" + url + "\"" +
"</script>" +
"<title>Page Redirection</title>" +
"<!-- Note: don't tell people to `click` the link, just tell them that it is a link. -->" +
"If you are not redirected automatically, follow the <a href='" + url + "'>link</a>";
}
// creates temporary file with content of redirect HTML page
public URI createRedirectTempFile(String url) {
BufferedWriter writer = null;
File tmpFile = null;
try {
// creates temporary file
tmpFile = File.createTempFile("pophelp", ".html", null);
// deletes file when the virtual machine terminate
tmpFile.deleteOnExit();
// writes redirect page content to file
writer = new BufferedWriter(new FileWriter(tmpFile));
writer.write(createRedirectPage(url));
writer.close();
}
catch (IOException e) {
return null;
}
return tmpFile.toURI();
}
Now you can use these like this:
String url = "file:///C:/Program Files/App System/App-Editor-8.0.1/help/pophelp.html?TAG=callsubflow";
if (Desktop.isDesktopSupported()) {
Desktop desktop = Desktop.getDesktop();
if (desktop.isSupported(Desktop.Action.BROWSE)) {
desktop.browse(createRedirectTempFile(url.replace(" ", "%20")));
}
}
I have a solution, not a quick (or pretty) solution, but a solution nonetheless :)
rundll32 url.dll,FileProtocolHandler does pass params when using URLs with http/s protocol (try rundll32 url.dll,FileProtocolHandler http://www.google.com?q=google), so you can setup small http server (like Jetty i guess) to serve your help files and show them using
rundll32 url.dll,FileProtocolHandler http://localhost:[helpServerIp]/help/pophelp.html?TAG=callsubflow

Categories

Resources