MPlayer is a very good (if not the best) open source multimedia player. But what has got to do with Java? A lot of people will argue with me and they will jump immediately to say JMF. Java Media Framework, in my opinion, is an obsolete, unfriendly, unmaintained library. So let me tell you, although I’m a Java fan, if you want multimedia playing capabilities in your Java apps, until a good revival initiative for JMF is there on the market, MPlayer is the answer.
If anyway, we are not using Java native library, why MPlayer? Here the answer is simpler, even tough still debatable. I’ve chosen MPlayer for several reasons:
- it is open source
- it is well maintained and updated with the latest video formats
- it is easy embeddable in your application using the slave mode and easy to integrate in your installer
- it is ported for almost all the major OSes (Windows, Unix, Mac) so the portability won’t suffer too much
This is gonna be a little bit bigger article as I’ll walk you through all the details, especially the trickier ones). Before reading this you should have basic Java knowledge (especially threads and I/O). You may also want to read before or refer to MPlayer documentation and its slave mode.
First of all, the big picture: we will start, from our Java application, MPlayer in a separate process, in slave mode. MPlayer, in slave mode, accepts commands (terminated by newline character) from standard input, executes them and print the response, if any, to the standard output. So we will inject commands into the MPlayer standard input (that being an output stream for us) and we will parse the MPlayer standard output and standard error (input streams for us) to interpret the answers.
To start MPlayer use the below:
Process mplayerProcess = Runtime.getRuntime().exec("/path/to/mplayer -slave -quiet -idle file/to/play.avi");
This will start MPlayer in a separate process. /path/to/mplayer is the path to the mplayer executable. Then we have a few command line options:
- slave
- mandatory; tells MPlayer to start in slave mode
- quiet
- optional; reduces the amount of messages that MPlayer will output
- idle
- optional; it doesn’t close MPlayer after a file finished playing; this is quite useful as you don’t want to start a new process everytime you want to play a file, but rather loading the file into the existing already started process (for performance reasons)
Now we will redirect the standard output and error of MPlayer into other two (or, my preference, only one) other streams. This should be continuously and we will do it in separate threads. For the sake of simplicity I’ll describe first a helper class:
class LineRedirecter extends Thread { /** The input stream to read from. */ private InputStream in; /** The output stream to write to. */ private OutputStream out; /** * @param in the input stream to read from. * @param out the output stream to write to. * @param prefix the prefix used to prefix the lines when outputting to the logger. */ LineRedirecter(InputStream in, OutputStream out) { this.in = in; this.out = out; } public void run() { try { // creates the decorating reader and writer BufferedReader reader = new BufferedReader(new InputStreamReader(in)); PrintStream printStream = new PrintStream(out); String line; // read line by line while ( (line = reader.readLine()) != null) { printStream.println(line); } } catch (IOException ioe) { ioe.printStackTrace(); } } }
Basically the LineRedirecter class is a thread that will read from one input stream, line by line, and it will write to an output stream. The both streams are specified in the constructor. If an error occurs or if the input stream is closed, the thread ends gracefully. If you have basic knowledge on streams and I/O this is actually very simple so I’ll insist no more here.
And now back to MPlayer
// create the piped streams where to redirect the standard output and error of MPlayer // specify a bigger pipesize than the default of 1024 PipedInputStream readFrom = new PipedInputStream(256*1024); PipedOutputStream writeTo = new PipedOutputStream(readFrom); BufferedReader mplayerOutErr = new BufferedReader(new InputStreamReader(readFrom)); // create the threads to redirect the standard output and error of MPlayer new LineRedirecter(mplayerProcess.getInputStream(), writeTo).start(); new LineRedirecter(mplayerProcess.getErrorStream(), writeTo).start(); // the standard input of MPlayer PrintStream mplayerIn = new PrintStream(mplayerProcess.getOutputStream());
readFrom and writeTo creates a piped input output stream used by the above threads to redirect the standard output and error of MPlayer into a single stream. It is recommended to use a bigger buffer for the piped stream than the default one (in my example I use 256kb).
From now on we will use mplayerIn to send commands to MPlayer and mplayerOutErr to read and parse the answers. Now you’re practically set, but to help you I’ll just give you some sample code to see how everything is working
- Open a new file
-
mplayerIn.print("loadfile \"/path/to/new file.avi\" 0"); mplayerIn.print("\n"); mplayerIn.flush();Notice here two things. First: the path to the new file is enclosed in
"as the file name contains space characters. Second: I haven’t usedprintln, butprintand then printed a newline character.printlnin some OSes (e.g. Windows) also prints the\rcharacter and MPlayer will interpret it as part of the command, leading to an error. - Pause/play the current file
-
mplayerIn.print("pause"); mplayerIn.print("\n"); mplayerIn.flush(); - Get the total playing time of the current file
-
>mplayerIn.print("get_property length"); mplayerIn.print("\n"); mplayerIn.flush(); String answer; int totalTime = -1; try { while ((answer = mplayerOutErr.readLine()) != null) { if (answer.startsWith("ANS_length=")) { totalTime = Integer.parseInt(answer.substring("ANS_length=".length())); break; } } } catch (IOException e) { } System.out.println(totalTime);Of course, this can be refined. We can also look to see if there is no error output, we can try to match against a regular expression, rather than test the starting string.
- Jump to a given time in file
-
mplayerIn.print("set_property time_pos 300"); mplayerIn.print("\n"); mplayerIn.flush();Jumps at the beginning of minute 5 in the movie (300 represents the seconds from the start)
- Close MPlayer
-
mplayerIn.print("pause"); mplayerIn.print("\n"); mplayerIn.flush(); try { mplayerProcess.waitFor(); } catch (InterruptedException e) {}Beside the fact that it sends the
quitcommand to MPlayer also waits until the MPlayer process previously created is finished.
Now that you got the hang of it, you can create pretty complex interfaces or automation tasks with MPlayer.
All the code samples were tested with MPlayer CCCP 1.0rc2-4.2.1.
Later edit: I compiled a small one for you and you can download it here.
the command Runtime.getRuntime().exec()
does not accept regular expressions like file*.avi.
That is a bit disturbing for conversion tasks. Do you know a work around ?
Comment by nico — March 5, 2008 @ 11:00 am
Not sure if I understood exactly what you need.
It supports this only if these are parameters passed to an executable and if that executable supports it.
If you’re referring to opening a file then you should use java.awt.Desktop#open().
Comment by Adrian — March 6, 2008 @ 9:53 am
Do you have a demo application that I can download ? Thanks!!
Comment by Trevor — March 8, 2008 @ 11:38 pm
I compiled a small one for you and you can download it from http://beradrian.users.sourceforge.net/articles/JMPlayer.java.
You can modify it to fit your needs.
Comment by Adrian — March 18, 2008 @ 2:21 pm
Hello!
I tried the solution posted here and it’s working wonderful! It allowed me to embed mplayer in my RCP application. One thing i didn’t manage to do: after the video started playing, how can i change the video size when a certain event occurs, for example when i resize the application window? I’ve seen that mplayer in slave mode has “set_property width new_width” and “set_property height new_height”…but mplayer returns an error when i send it those commands
. Anyone has any ideas regarding this problem?
Thanks in advance!
Comment by Radu — May 24, 2008 @ 4:46 pm
Thanks for the info, finally a kind fellow Java programmer care to share how to embed mplayer. By the way, i have linked to this post from my blog. Do you mind if i archive your code in my blog?
Comment by Vonze — October 1, 2008 @ 1:00 pm
The code is free to share and modify, its solely purpose being to help you in not losing time with this (as I did). So yes, you can put it on your blog.
Comment by Adrian — October 1, 2008 @ 1:59 pm
Hello,
Thanks for the examples but I have a question about the deployment of such application, how can I embed the mplayer in the installer of my application?
(the installer should be platform independent too) or it may be a OS specific installer (i.e. .exe, .msi, .deb, .rpm, .bin or whatever mac os x uses)
Thanks in advance
Comment by dudes — October 12, 2008 @ 11:40 am
Thanks for your code, it work great except the slave mode. e.g. when mplayer window come up, it get focus and still accept input from keyboard. I run it on ubuntu 8.04, Any ideas?
Comment by pbp — October 13, 2008 @ 8:04 am
Have you tried starting MPlayer with the option
−noconsolecontrols?Comment by Adrian — October 13, 2008 @ 8:36 am
Regarding the installer: usually an installer is platform specific. For Windows I personally use NSIS.
To embed MPlayer simply take the MPlayer distribution and pack it into your installer. When you start MPlayer, the path will be relative to your installation directory.
Comment by Adrian — October 13, 2008 @ 8:40 am
Hello again thanks for your replay,
when you say “simply take the MPlayer distribution and pack it into your installer” do you mean a dll file or what??as when you go to the MPlayer website there is just too many builds…
anyway thank you for this great blog
Comment by dudes — October 13, 2008 @ 9:57 am
no luck. mplayer still accept input from keyboard. i can press ‘f’ for toggle ‘fullscreen’ mode.
Comment by pbp — October 13, 2008 @ 10:12 am
I meant the entire MPlayer distribution – executable, libraries, config file; practically everything you need to run MPlayer.
Simply download the binary package for your OS and unpack it in a subfolder mplayer on your installation folder.
Comment by Adrian — October 13, 2008 @ 10:19 am
To inhibit keyboard input, try the option
-input conf=[filename]and specify a blank file. This should practically define no key bind to MPlayer commands.Comment by Adrian — October 13, 2008 @ 10:27 am
Hello,
An not sure that I got what you said cause when I downloaded the mplayer package for windows all I found is a .exe files can you please point me to the package you are talking about.
Thanks again
Comment by dudes — October 13, 2008 @ 3:40 pm
Let’s take for example http://www.mplayerhq.hu/MPlayer/releases/win32/MPlayer-mingw32-1.0rc2.zip. Everything what’s in the zip file under the
MPlayer-1.0rc2folder should go in your installation package.Comment by Adrian — October 13, 2008 @ 9:45 pm
Hi,
could you tell me why the command “get_property length” return error. I’ve tried “get_time_length” too with the same result. I’m testing it on .AVI file or .VBO. Everything else work great. I will be glad for help.
Comment by tian — December 7, 2008 @ 12:13 pm
For me “get_property length” works perfectly on “.avi”. Maybe you can post or send me some code.
Comment by Adrian — December 7, 2008 @ 9:45 pm
jmPlayerVideo
Comment by feras — December 21, 2008 @ 10:23 am
Hello
Is it possible to change the value of loop
during the playback which is running in slave mode.???
I started the process like:
mplayerProcess = Runtime.getRuntime().exec(“mplayer -slave -quiet -idle /home/videos/chacko.wmv -loop 100″);
I want to reduce the loop value to 50. Is it possible??
Thanks.
Comment by quark — January 30, 2009 @ 7:49 am
Yes. Just use
loop [value]orset_property loop [value].And in Java:
Comment by Adrian — January 30, 2009 @ 1:06 pm
Thanks. But that will add value 50 to the existing loop for 100 times. So that the result will be loop 150. I solved this by using a – to subtract a value from the existing loop value. Like,
mplayerIn.print(“loop -50″);
mplayerIn.print(“\n”);
mplayerIn.flush();
Thank you.
Comment by Quark — February 1, 2009 @ 4:22 am
How can i check the video is currently playing or not. Actually i want to execute some code after the playback finished. Like,
mplayerIn.print(”loop -50″);
mplayerIn.print(”\n”);
mplayerIn.flush();
//wait for playback completing. then execute next statements,
…………….
Thanks
Comment by Quark — February 1, 2009 @ 8:27 am
Unfortunately, I don’t think there is a way to do this.
A workaround will be to start mplayer again for every playback. And to detect when it is finished you can see my other article
Comment by Adrian — February 1, 2009 @ 1:50 pm
http://www.geocities.com/beradrian/storage/JMPlayer.java
is really a good piece of code.
Thanks
Comment by Abdullah — March 2, 2009 @ 8:19 am
This is great info. I’m new to mplayer and I’d like to extract the frames of a video and have the frame images piped to stdout so that I can capture them into some kind of Collection from w/in the java program. Any idea how to do this?
Thanks
Comment by Adam — March 17, 2009 @ 4:34 pm
Hello
Thank you for sharing this. I have a small suggestion: When you try to use a command which generates some output you want to read, mplayerOutErr.readLine()blocks for quite some time. You need to flush the printStream in the while loop of the LineRedirecter class after writing to it to speed up things.
Comment by papo — March 20, 2009 @ 5:21 pm
For Adam: I don’t know actually how this can be done, but have you tried the −dumpvideo option in MPlayer?
Comment by Adrian — March 24, 2009 @ 11:03 am
It’s great article, thx a lot.
To @papo: thanks I thought about this problem and I did’n know what’s wrong, you gave me an answer
Comment by Piti — March 29, 2009 @ 11:57 am
Hi!
Really gr8 article, it helped me to know about MPlayer quickly.
When I play the playlist using -playlist option, ther is delay and between the each video play. Can’t we reduce it to play all files sequentially?
And also the player window is getting minimised after every video completion.
And I want to play the video on FullScreen mode.
Can anybody help in this regard.
Any help would be appriciable.
Thanks
KM
Comment by KM — June 28, 2009 @ 9:40 am
You can play entire playlists instead of files.
For fullscreen you can try:
mplayerIn.print(“set_property fullscreen 1″);
mplayerIn.print(“\n”);
mplayerIn.flush();
Comment by Adrian — June 29, 2009 @ 3:52 pm
Thanks Adrian for the replies!
Again, I still see delay between each video present in the playlist. Can we reduce the delay?
When I updated-added new videos to- the playlist in filesystem, MPlayer is not able to reload the lastest chages.
Can not we get the MPlayer loading playlist dynamically with new updates with out stopping the player and reloading it?
Comment by KM — July 3, 2009 @ 3:25 am
I guess that the delay is due to the fact that MPlayer is loading the video. I don’t know if you can do something like preload.
You can load list dinamically. Please see the command “loadlist ” from slave mode documentation.
Comment by Adrian — July 3, 2009 @ 10:21 am
Can I put Mplayer in a new Jframe??
Comment by zzzin — July 9, 2009 @ 1:11 pm
As far as I know, this is not possible. At least in easy way. Maybe using JNI and making your code totally platform dependent.
Comment by Adrian — July 13, 2009 @ 12:28 pm
Thank’s for this tutorial.
My project involves only sound and url instead of files. Your sample code works great to open first url.
But how to (click a button or enter in menu as SMPlayer) and open another url? loadfile doesn’t seem to work, mplayer quit! What may be the code for an empty play function and send url’s as string to open?
Thank’s again.
Comment by Jim — July 29, 2009 @ 8:12 am
loadfile should work. I think you should escape the backslash (simply double it). Try also with append = 1.
Comment by Adrian — July 29, 2009 @ 8:45 am
Adrian, sorry this took a long time.
Appending 1 doesn’t work. Still mplayer quits.
What exactly do you mean by escape backslash?. there is the code:
s.print(“loadfile http://94.232.114.240:6804 0\n”);
Comment by Jim — November 1, 2009 @ 1:41 pm
Have you tried
s.print(“loadfile http://94.232.114.240:6804 1\n”);
s.flush();
?
Opening the same URLs from the GUI does work?
Comment by Adrian — November 2, 2009 @ 6:04 pm
Great article. Thanks for posting this.
One question: Has anyone been successful in embedding the player in the associated java application? Right now, I’ve got everything working, but the MPLayer always opens in its own window. I’d like MPlayer to open up in a Frame within the java app. Is that possible?
Thanks again for the posting.
Comment by Sheldon — October 2, 2009 @ 1:37 pm
Unfortunately I don’t know a (simple) way to do this. For sure you will need to write JNI code for doing it.
Comment by Adrian — October 2, 2009 @ 9:16 pm
While reviewing the protocol documents, I noticed that under the Slave Protocol, there is something called “change_rectangle”. There are also properties for “height” and “width”. But as best I can tell, these don’t seem to do anything. Or I am sending them wrong.
Any ideas on what these commands/properties do and/or how to use them?
There is an option to set the location and size of the video window when the application starts, but I can’t seem to find a way to move or adjust its size once the window is open.
Just curious if anyone has tried these commands and/or if they even work.
Thanks.
Comment by Sheldon — October 8, 2009 @ 5:13 pm