mplayer run from java - playing then hanging

hi

I ran the following method call to start mplayer from java:

Runtime.getRuntime().exec(new String[] { “mplayer -vo gl2 -fs”, “movie.avi” });

After one or two seconds the movie began to play. But only for about half a minute. Than mplayer hung. My program proceeded to run as normal so I could close it on a common way (pressing escape) and mplayer closed with it.

I guess some memory has been filled up. Anyways, does anybody know this problem and a solution?

Qudus

Edit: Ah, before somebody asks, it is not the movie or mplayer itself. They run perfectly from the commandline.

You should empty the output/error streams of the process (in a thread) or this leads to that kind of locking (your player wants to output something, and the buffer is full).

Lilian

thanks. But do I need a thread? Isn’t the process run asynchronously? So it would be enough to handle this buffer emptying in the lines after the Runtime.exec().

Well, you have to poll the output AND error streams, and polls are blocking.

So while you’re waiting for stdout, if something fills up the stderr… the player is blocked.

So you need at least 2 threads.

Lilian

If InputStream.available() from a Process is implemented like those you get from sockets and files, you might get away with using 1 thread.

But in that case, you’ll have to sleep() your thread between available() tests or you’ll endup consuming cpu for nothing when no output comes (“active waiting”)

hmm… good points. I’ll test it this evening.

Thanks

it didn’t help. MPlayer is still hanging.

I’ll give you the code I use to play:


import java.io.IOException;
import java.io.InputStream;

public class ProcessRunner implements Runnable
{
    Process process = null;
    
    private boolean isProcessRunning(Process process)
    {
        try
        {
            process.exitValue();
            return(false);
        }
        catch (IllegalThreadStateException e)
        {
            return(true);
        }
    }
    
    public void run()
    {
        if (process != null)
        {
            InputStream in = process.getInputStream();
            InputStream err = process.getErrorStream();
            
            byte[] bs = new byte[256];
            int n = 0;
            
            while (isProcessRunning(process))
            {
                try
                {
                    if ((n = in.read(bs)) > 0)
                    {
                        //for (int i = 0; i < n; i++)
                          //  System.out.print((char)(bs[i]));
                        n++; // nur damit die Warnung weg ist, solange die zwei Zeilen hier drüber auskommentiert sind
                    }
                
                    if ((n = err.read(bs)) > 0)
                    {
                        //for (int i = 0; i < n; i++)
                          //  System.out.print((char)(bs[i]));
                    }
                    
                    try
                    {
                        Thread.sleep(10);
                    }
                    catch (InterruptedException e)
                    {}
                }
                catch (IOException e)
                {}
            }
        }
    }
    
    private ProcessRunner(Process process)
    {
        this.process = process;
    }
    
    public static void runProcess(String[] cmd) throws IOException
    {
        Process prc = Runtime.getRuntime().exec(cmd);
        
        new Thread(new ProcessRunner(prc)).start();
    }
}

Maybe you have an idea. If this problem cannot besolved, my whole program is led at absurdum.

Qudus

inputstream.read() will block, so it won’t get you far.


InputStream out = ...;
InputStream err = ...;

while(true)
{
    while(out.available() > 0)
        out.read();

    while(err.available() > 0)
        err.read();

   sleep(250);
}

or just do it safer, because the above is just a hack which will run forever.


2 Threads, each:

public void run()
{
   InputStream in = ...; // out OR err

   while(in.read() != -1)
   {
      // do nothing
   }

   in.close();
}

That will run nicely, and having 2 threads that are mainly blocking isn’t going to have any impact on performance anyway. You should go for the best design, as long as it doesn’t get your into trouble.

if it really has something to do with output streams, why not redirect those streams when you start mplayer?
like mplayer movie.mov > null to redirect stdout

Is there a way to do this in windows? I’m not planning to run this program on windows. But the possibility should be there.

I think on windows redirecting to a file works the same as unix/linux.

ah, I thought, you wanted to tell me to redirect it to /dev/null but redirecting it to a file named null (and maybe deleting it afterwards) works well.

I’ll try to implement it in my program and see what it brings.

Edit: but how about redirecting stderr on windows. Is it the same way as on linux, too?

I just tried to run it on windows. But than nothing happens when I call the exec method. But when I close my program, it normally closes and mplayer starts in the same moment. strange! Any Idea for that?

Redirecting to null just redirects to ‘nothing’ in windows, not to a file named null. Maybe on unix this is different, I don’t know.
If I remember correctly, redirecting stderr is done with 2>, (mplayer movie.mov 2> error.log)
I’m not really a unix expert, but I think it’s the same, right?

As for your problem running your app on windows, it sounds really strange. Does it work correctly when you run the command from the command line (no matter in what directory you are)?

[quote=“erikd,post:15,topic:24519”]
On unix you redirect to ‘nothing’ by redirecting to a special file named ‘/dev/null’. But the behaviour is the same then.

[quote=“erikd,post:15,topic:24519”]
yes

[quote=“erikd,post:15,topic:24519”]
yepp

Keep in mind that Runtime.exec isn’t a shell. You should use:

Runtime.getRuntime().exec(new String[] { “mplayer”, “-vo”, “gl2”, “-fs”, “movie.avi” });

see:
http://java.sun.com/developer/JDCTechTips/2003/tt0304.html