Interrupting Java DataInputStream readFully()
Asked Answered
S

2

9

I have a Java applet that streams video (MJPEG) from a server. I wrote a proxy server in C# (Windows service) to put between the applet and multiple video servers. A HTML/CSS/Js frontend is used along with the Java applet. All functionality works fine (finally!!!), except one thing.

The video server allows you to play back recorded video through a REST interface. When the clip is done, the server leaves the connection open in case you want to send it commands like rewind or seek. The clip is being played fine in the applet until the end. If you try to start a new clip (which entails sending a command from Javscript to the applet), the browser freezes up. However, subsequent commands that would use the same connection work, such as play, pause, and seek. If I stop the windows service, the browser becomes responsive again.

This is what I'm assuming is happening: The clip ends (or is paused); no more data is sent but the connection is still active. The applet is waiting on the proxy for the next frame, but the proxy is waiting on the video server for the next frame, which is not going to send any more data.

This is the code in a while loop that reads each frame

byte[] img = new byte[mContentLength];
inputStream.skipBytes(headerLen);
inputStream.readFully(img);

I need to interrupt this code somehow.

When a new video clip is selected in the HTML frontend, we notify the applet, which calls disconnect() on the CameraStream class. This is that function:

// DataInputStream inputStream
// HttpURLConnection conn
public void disconnect() {
    System.out.println("disconnect called.");
    if(running) {
        running = false;
        try {
            // close the socket
            if(inputStream != null) {
                inputStream.close();
            }
            if(conn != null) {
                conn.disconnect();
            }
            inputStream = null;
            System.out.println("closed.");
        } catch(Exception ignored) {
            System.out.println("exc:" + ignored.getMessage());
            main.reportErrorFromThrowable(ignored);
        }
    }
}

To test this, I let a quick clip play and run to the end. I then select a new clip. In my Java console, I get the output disconnect called. but I don't get the subsequent closed. message, nor does that generic Exception get caught. When I stop the Windows service, I finally get the closed. message, so it seems like inputStream.close(); is blocking.

So I guess my question is how can I stop the blocking? Is the readFully(img) call blocking? Or is it the disconnect function (as suggested by the console output I get)?

edit: just to clarify, I wrote the Java applet, HTML, CSS, Javascript, and C# proxy server, so I have access to all of that code. The only code I can't modify is that of the REST interface on the video server.

edit2: i meant to make bounty for this post https://stackoverflow.com/questions/12219758/proxy-design-pattern

Sandra answered 29/8, 2012 at 21:15 Comment(0)
S
0

I finally figured out the answer:

public void disconnect() {
    if(running) {
        running = false;
        try {
            try{
                // had to add this
                conn.getOutputStream().close();
            }
            catch(Exception exc){
            }
            // close the socket
            if(inputStream != null) {
                inputStream.close();
            }
            if(conn != null) {
                conn.disconnect();
            }
            inputStream = null;


        } catch(Exception ignored) {
            main.reportErrorFromThrowable(ignored);
        }
    }
}

Even though I'm using an HttpUrlConnection, which is one way and doesn't have an output stream, trying to close the output stream raised an exception and for some reason made it all work.

Sandra answered 6/9, 2012 at 22:1 Comment(1)
HttpURLConnection is not one way and does have an output stream. Consider POST or PUT.Amputate
A
0

In general, Java I/O methods block. The best solution appears to be to create another thread for reading the data and using NIO buffers. Example of NIO-based read (warning: untested!):

// get the InputStream from somewhere (a queue possibly)
ReadableByteChannel inChannel = Channels.newChannel(inputStream);
ByteBuffer buf = ByteBuffer.allocate(mContentLength + headerLen);
inChannel.read(buf);
byte[] img = new byte[mContentLength];
inChannel.get(img, headerLen, mContentLength);

This code creates a Channel from the InputStream and uses the Channel to read data. The JavaDoc for the ReadableByteChannel.read(ByteBuffer) function says that interrupting the thread that contains the call to inChannel.read(buf) will stop the read.

You will have to adapt this code, I just pulled it out of my head. Good luck!

Attalie answered 3/9, 2012 at 21:26 Comment(1)
I'm sorry, I meant to offer the bounty for my other question: stackoverflow.com/questions/12219758/proxy-design-pattern . do you have any experience in C#? I think I've identified the real problem here.Sandra
S
0

I finally figured out the answer:

public void disconnect() {
    if(running) {
        running = false;
        try {
            try{
                // had to add this
                conn.getOutputStream().close();
            }
            catch(Exception exc){
            }
            // close the socket
            if(inputStream != null) {
                inputStream.close();
            }
            if(conn != null) {
                conn.disconnect();
            }
            inputStream = null;


        } catch(Exception ignored) {
            main.reportErrorFromThrowable(ignored);
        }
    }
}

Even though I'm using an HttpUrlConnection, which is one way and doesn't have an output stream, trying to close the output stream raised an exception and for some reason made it all work.

Sandra answered 6/9, 2012 at 22:1 Comment(1)
HttpURLConnection is not one way and does have an output stream. Consider POST or PUT.Amputate

© 2022 - 2024 — McMap. All rights reserved.