To use VLCControl, you construct a new instance and call sendCommand(String s), e.g. control.sendCommand("pause"). The connection is automatically opened on the first call. Calling disconnect() sends the "quit" command to clean up the VLC side. The stuff sent back from VLC is logged and can be captured by registering a PropertyChangeListener for CLIENT_MESSAGE to the VLCControl. VLC must be set up so that it opens a telnet rc control on port 4444.
Here is the code for VLCControl. It is closely based on the TelnetClientExample from Apache commons.
Code: Select all
package vlccontrol;
import java.beans.PropertyChangeSupport;
import java.io.*;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.net.telnet.*;
import org.apache.commons.net.telnet.TelnetClient;
/**
* Exposes control of VLC media player (videolan.org) from java. VLC must be set up to open a telnet control on localhost:4444.
* <p>
* For VLC 1.1.5, use the following setup to expose the remote control (rc) interrface for telnet control:
* <p>
* This setting is in VLC Tools/Preferences/Show settings (All)/Interface/Main interfaces. Select the "Remote Control Interface" and replace "oldrc" with "rc" in the text field.
* In VLC Tools/Preferences/Show settings (All)/Interface/Main interfaces/RC/TCP command input, put the string "localhost:4444" in the text field.
*
* @author Tobi
*/
public class VLCControl extends TelnetClient implements Runnable, TelnetNotificationHandler {
/** VLC should be started with as "vlc --rc-host=localhost:4444" */
public static final int VLC_PORT = 4444;
static final Logger log = Logger.getLogger("VLCControl");
private CharBuffer cbuf = CharBuffer.allocate(1024);
private static VLCControl staticInstance = null; // used to communicate among instances the active client
private PropertyChangeSupport support=new PropertyChangeSupport(this); // listeners get informed by output from VLC strings
public VLCControl() {
}
@Override
public void disconnect() throws IOException {
sendCommand("quit");
super.disconnect();
}
public void connect() throws IOException {
staticInstance = this; // used by reader to get input stream
try {
staticInstance.connect("localhost", VLC_PORT);
Thread thread = new Thread(new VLCControl()); // starts the thread to get the text sent back from VLC
thread.start();
staticInstance.registerNotifHandler(this); // notifications call back to logger
Runtime.getRuntime().addShutdownHook(new Thread() { // shutdown hook here makes sure to disconnect cleanly, as long as we are not terminated
@Override
public void run() {
try {
if (isConnected()) {
disconnect();
}
} catch (IOException ex) {
log.warning(ex.toString());
}
}
});
} catch (IOException e) {
log.warning("couldn't connect to VLC - you may need to start VLC with command line \"vlc --rc-host=localhost:4444\"");
throw new IOException(e);
}
}
/** Sends a string command. Commands do not need to be terminated with a newline.
<p>
<pre>
+----[ Remote control commands ]
| add XYZ . . . . . . . . . . . . . . . . . . . . add XYZ to playlist
| enqueue XYZ . . . . . . . . . . . . . . . . . queue XYZ to playlist
| playlist . . . . . . . . . . . . . .show items currently in playlist
| search [string] . . search for items in playlist (or reset search)
| sort key . . . . . . . . . . . . . . . . . . . . . sort the playlist
| sd [sd] . . . . . . . . . . . . . show services discovery or toggle
| play . . . . . . . . . . . . . . . . . . . . . . . . . . play stream
| stop . . . . . . . . . . . . . . . . . . . . . . . . . . stop stream
| next . . . . . . . . . . . . . . . . . . . . . . next playlist item
| prev . . . . . . . . . . . . . . . . . . . . previous playlist item
| goto . . . . . . . . . . . . . . . . . . . . . . goto item at index
| repeat [on|off] . . . . . . . . . . . . . . toggle playlist repeat
| loop [on|off] . . . . . . . . . . . . . . . . toggle playlist loop
| random [on|off] . . . . . . . . . . . . . . toggle playlist random
| clear . . . . . . . . . . . . . . . . . . . . . .clear the playlist
| status . . . . . . . . . . . . . . . . . . . current playlist status
| title [X] . . . . . . . . . . . . . . set/get title in current item
| title_n . . . . . . . . . . . . . . . . next title in current item
| title_p . . . . . . . . . . . . . . previous title in current item
| chapter [X] . . . . . . . . . . . . set/get chapter in current item
| chapter_n . . . . . . . . . . . . . . next chapter in current item
| chapter_p . . . . . . . . . . . . previous chapter in current item
|
| seek X . . . . . . . . . . . seek in seconds, for instance `seek 12'
| pause . . . . . . . . . . . . . . . . . . . . . . . . toggle pause
| fastforward . . . . . . . . . . . . . . . . . . set to maximum rate
| rewind . . . . . . . . . . . . . . . . . . . . . set to minimum rate
| faster . . . . . . . . . . . . . . . . . . faster playing of stream
| slower . . . . . . . . . . . . . . . . . . slower playing of stream
| normal . . . . . . . . . . . . . . . . . . normal playing of stream
| rate [playback rate] . . . . . . . . . . set playback rate to value
| frame . . . . . . . . . . . . . . . . . . . . . play frame by frame
| fullscreen, f, F [on|off] . . . . . . . . . . . . toggle fullscreen
| info . . . . . . . . . . . . . .information about the current stream
| stats . . . . . . . . . . . . . . . . show statistical information
| get_time . . . . . . . . . .seconds elapsed since stream's beginning
| is_playing . . . . . . . . . . . . 1 if a stream plays, 0 otherwise
| get_title . . . . . . . . . . . . . the title of the current stream
| get_length . . . . . . . . . . . . the length of the current stream
|
| volume [X] . . . . . . . . . . . . . . . . . . set/get audio volume
| volup [X] . . . . . . . . . . . . . . . .raise audio volume X steps
| voldown [X] . . . . . . . . . . . . . . lower audio volume X steps
| adev [X] . . . . . . . . . . . . . . . . . . . .set/get audio device
| achan [X] . . . . . . . . . . . . . . . . . .set/get audio channels
| atrack [X] . . . . . . . . . . . . . . . . . . . set/get audio track
| vtrack [X] . . . . . . . . . . . . . . . . . . . set/get video track
| vratio [X] . . . . . . . . . . . . . . . .set/get video aspect ratio
| vcrop, crop [X] . . . . . . . . . . . . . . . . set/get video crop
| vzoom, zoom [X] . . . . . . . . . . . . . . . . set/get video zoom
| snapshot . . . . . . . . . . . . . . . . . . . . take video snapshot
| strack [X] . . . . . . . . . . . . . . . . . set/get subtitles track
| hotkey, key [hotkey name] . . . . . . . . . . simulate hotkey press
| menu [on|off|up|down|left|right|select] . . . . . . . . . .use menu
|
| set [var [value]] . . . . . . . . . . . . . . . . . set/get env var
| save_env . . . . . . . . . . . . save env vars (for future clients)
| alias [cmd] . . . . . . . . . . . . . . . . set/get command aliases
| description . . . . . . . . . . . . . . . . . .describe this module
| license . . . . . . . . . . . . . . . . print VLC's license message
| help, ? [pattern] . . . . . . . . . . . . . . . . . .a help message
| longhelp [pattern] . . . . . . . . . . . . . . a longer help message
| logout . . . . . . . . . . . . . . exit (if in a socket connection)
| quit . . . . . . . . quit VLC (or logout if in a socket connection)
| shutdown . . . . . . . . . . . . . . . . . . . . . . . .shutdown VLC
+----[ end of help ]
</pre>
*/
public String sendCommand(String s) throws IOException {
if (!isConnected()) {
connect();
}
if (s == null) {
return null;
}
if (!s.endsWith("\n")) {
s = s + "\n";
}
getOutputStream().write(s.getBytes());
getOutputStream().flush();
return s;
}
public static String PAUSE="pause", PLAY="play", STOP="stop", NEXT="next", PREV="prev", VOLUP="volup 1", VOLDOWN = "voldown 1";
public static final String CLIENT_MESSAGE="ClientMessage";
/***
* Reader thread.
* Reads lines from the TelnetClient and echoes them
* on the logger.
* PropertyChangeListeners are called with CLIENT_MESSAGE and String sent from VLC.
***/
@Override
public void run() {
InputStream instr = staticInstance.getInputStream();
byte[] buff = new byte[1024];
int ret_read = 0;
try {
do {
ret_read = instr.read(buff);
if (ret_read > 0) {
String s=new String(buff, 0, ret_read);
log.info(s);
staticInstance.getSupport().firePropertyChange(CLIENT_MESSAGE, null, s); // listener on static instance that actually is connected gets the message
}
} while (ret_read >= 0);
} catch (Exception e) {
log.log(Level.WARNING, "Reader ending - Exception while reading socket:{0}", e.getMessage());
}
}
/***
* Callback method called when TelnetClient receives an option
* negotiation command.
* <p>
* @param negotiation_code - type of negotiation command received
* (RECEIVED_DO, RECEIVED_DONT, RECEIVED_WILL, RECEIVED_WONT)
* <p>
* @param option_code - code of the option negotiated
* <p>
***/
@Override
public void receivedNegotiation(int negotiation_code, int option_code) {
String command = null;
if (negotiation_code == TelnetNotificationHandler.RECEIVED_DO) {
command = "DO";
} else if (negotiation_code == TelnetNotificationHandler.RECEIVED_DONT) {
command = "DONT";
} else if (negotiation_code == TelnetNotificationHandler.RECEIVED_WILL) {
command = "WILL";
} else if (negotiation_code == TelnetNotificationHandler.RECEIVED_WONT) {
command = "WONT";
}
log.log(Level.INFO, "Received {0} for option code {1}", new Object[]{command, option_code});
}
/**
* @return the support. Listeners can get the stuff sent back from VLC with CLIENT_MESSAGE events.
*/
public PropertyChangeSupport getSupport() {
return support;
}
}
Code: Select all
package vlccontrol;
/*
* VLCControlApp.java
*
* Created on Dec 15, 2010, 10:04:22 AM
*/
import java.beans.*;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Demo of controlling VLC from jAER project.
* @author Tobi
*/
public class VLCControlApp extends javax.swing.JFrame implements PropertyChangeListener{
VLCControl vlc=new VLCControl();
public VLCControl getVLCControl(){return vlc;}
static Logger log=Logger.getLogger("VLCControlApp");
/** Creates new form VLCControlApp */
public VLCControlApp() {
initComponents();
vlc.getSupport().addPropertyChangeListener(this);
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
commandTF = new javax.swing.JTextField();
jScrollPane1 = new javax.swing.JScrollPane();
responseTA = new javax.swing.JTextArea();
connectButton = new javax.swing.JButton();
disconnectButton = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("VLCControl");
commandTF.setToolTipText("enter text commands here, e.g. pause, fastforward, rewind");
commandTF.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
commandTFActionPerformed(evt);
}
});
responseTA.setColumns(20);
responseTA.setEditable(false);
responseTA.setRows(5);
responseTA.setToolTipText("shows the output from vlc here");
jScrollPane1.setViewportView(responseTA);
connectButton.setText("Connect");
connectButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
connectButtonActionPerformed(evt);
}
});
disconnectButton.setText("Disconnect");
disconnectButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
disconnectButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(connectButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(disconnectButton))
.addComponent(commandTF, javax.swing.GroupLayout.PREFERRED_SIZE, 202, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(connectButton)
.addComponent(disconnectButton))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(commandTF, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 223, Short.MAX_VALUE)
.addContainerGap())
);
pack();
}// </editor-fold>
private void connectButtonActionPerformed(java.awt.event.ActionEvent evt) {
if(vlc.isConnected()){
return;
}
try {
vlc.connect();
} catch (IOException ex) {
Logger.getLogger(VLCControlApp.class.getName()).log(Level.SEVERE, null, ex);
responseTA.append(ex.toString()+"\n");
}
}
private void disconnectButtonActionPerformed(java.awt.event.ActionEvent evt) {
try {
if(vlc.isConnected()) vlc.disconnect();
} catch (IOException ex) {
log.warning(ex.toString());
responseTA.append(ex.toString()+"\n");
}
}
private void commandTFActionPerformed(java.awt.event.ActionEvent evt) {
try {
vlc.sendCommand(commandTF.getText());
} catch (IOException ex) {
log.warning(ex.toString());
responseTA.append(ex.toString()+"\n");
}
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new VLCControlApp().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JTextField commandTF;
private javax.swing.JButton connectButton;
private javax.swing.JButton disconnectButton;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextArea responseTA;
// End of variables declaration
@Override
public void propertyChange(PropertyChangeEvent evt) {
if(evt.getPropertyName()==VLCControl.CLIENT_MESSAGE){
if(evt.getNewValue()!=null){
String s=(String)evt.getNewValue();
responseTA.append(s);
responseTA.setCaretPosition(responseTA.getText().length());
}
}
}
}