Java tutorial
/* * Copyright 2013 ENTERTAILION LLC * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * <http://www.apache.org/licenses/LICENSE-2.0> * * Unless required by applicable law or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ package com.entertailion.java.fling; import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridLayout; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.Inet4Address; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.ServerSocket; import java.net.URL; import java.net.UnknownHostException; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.ResourceBundle; import java.util.TimeZone; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.basic.BasicSliderUI; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.http.Header; import org.apache.http.HttpResponse; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import uk.co.caprica.vlcj.player.MediaPlayer; import uk.co.caprica.vlcj.player.MediaPlayerEventAdapter; import uk.co.caprica.vlcj.player.MediaPlayerFactory; /** * Main window frame for the Fling application * * @author leon_nicholls * */ public class FlingFrame extends JFrame implements ActionListener, BroadcastDiscoveryHandler, ChangeListener, WindowListener { private static final String LOG_TAG = "FlingFrame"; // https://www.gstatic.com/cv/receiver.html?${POST_DATA} public static final String CHROMECAST = "ChromeCast"; // TODO Add your own app id here private static final String APP_ID = "YOUR_APP_ID_HERE"; private static final String HEADER_APPLICATION_URL = "Application-URL"; private static final String CHROME_CAST_MODEL_NAME = "Eureka Dongle"; private static final String TRANSCODING_EXTENSIONS = "wmv,avi,mkv,mpg,mpeg,flv,3gp,ogm"; private static final String TRANSCODING_PARAMETERS = "vcodec=VP80,vb=1000,vfilter=canvas{width=640,height=360},acodec=vorb,ab=128,channels=2,samplerate=44100,threads=2"; private static final String PROPERTY_TRANSCODING_EXTENSIONS = "transcoding.extensions"; private static final String PROPERTY_TRANSCODING_PARAMETERS = "transcoding.parameters"; private static final String PROPERTY_MANUAL_SERVERS = "manual.servers"; private int port = EmbeddedServer.HTTP_PORT; private List<DialServer> servers = new ArrayList<DialServer>(); private List<DialServer> manualServers = new ArrayList<DialServer>(); private JComboBox deviceList; private JDialog progressDialog; private JButton refreshButton, playButton, pauseButton, stopButton, settingsButton; private JLabel label; private JSlider scrubber; private JSlider volume; private ResourceBundle resourceBundle; private EmbeddedServer embeddedServer; private BroadcastDiscoveryClient broadcastClient; private Thread broadcastClientThread; private TrackedDialServers trackedServers = new TrackedDialServers(); private RampClient rampClient; private MediaPlayerFactory mediaPlayerFactory; private MediaPlayer mediaPlayer; private String transcodingParameterValues = TRANSCODING_PARAMETERS; private String transcodingExtensionValues = TRANSCODING_EXTENSIONS; private DialServer selectedDialServer; private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss"); private int duration; private boolean playbackValueIsAdjusting; private boolean isTranscoding; private String appId; public FlingFrame(String appId) { super(); this.appId = appId; rampClient = new RampClient(this); addWindowListener(this); simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); Locale locale = Locale.getDefault(); resourceBundle = ResourceBundle.getBundle("com/entertailion/java/fling/resources/resources", locale); setTitle(MessageFormat.format(resourceBundle.getString("fling.title"), Fling.VERSION)); JPanel listPane = new JPanel(); // show list of ChromeCast devices detected on the local network listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS)); JPanel devicePane = new JPanel(); devicePane.setLayout(new BoxLayout(devicePane, BoxLayout.LINE_AXIS)); deviceList = new JComboBox(); deviceList.addActionListener(this); devicePane.add(deviceList); URL url = ClassLoader.getSystemResource("com/entertailion/java/fling/resources/refresh.png"); ImageIcon icon = new ImageIcon(url, resourceBundle.getString("button.refresh")); refreshButton = new JButton(icon); refreshButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // refresh the list of devices if (deviceList.getItemCount() > 0) { deviceList.setSelectedIndex(0); } discoverDevices(); } }); refreshButton.setToolTipText(resourceBundle.getString("button.refresh")); devicePane.add(refreshButton); url = ClassLoader.getSystemResource("com/entertailion/java/fling/resources/settings.png"); icon = new ImageIcon(url, resourceBundle.getString("settings.title")); settingsButton = new JButton(icon); settingsButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JTextField transcodingExtensions = new JTextField(50); transcodingExtensions.setText(transcodingExtensionValues); JTextField transcodingParameters = new JTextField(50); transcodingParameters.setText(transcodingParameterValues); JPanel myPanel = new JPanel(new BorderLayout()); JPanel labelPanel = new JPanel(new GridLayout(3, 1)); JPanel fieldPanel = new JPanel(new GridLayout(3, 1)); myPanel.add(labelPanel, BorderLayout.WEST); myPanel.add(fieldPanel, BorderLayout.CENTER); labelPanel.add(new JLabel(resourceBundle.getString("transcoding.extensions"), JLabel.RIGHT)); fieldPanel.add(transcodingExtensions); labelPanel.add(new JLabel(resourceBundle.getString("transcoding.parameters"), JLabel.RIGHT)); fieldPanel.add(transcodingParameters); labelPanel.add(new JLabel(resourceBundle.getString("device.manual"), JLabel.RIGHT)); JPanel devicePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); final JComboBox manualDeviceList = new JComboBox(); if (manualServers.size() == 0) { manualDeviceList.setVisible(false); } else { for (DialServer dialServer : manualServers) { manualDeviceList.addItem(dialServer); } } devicePanel.add(manualDeviceList); JButton addButton = new JButton(resourceBundle.getString("device.manual.add")); addButton.setToolTipText(resourceBundle.getString("device.manual.add.tooltip")); addButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JTextField name = new JTextField(); JTextField ipAddress = new JTextField(); Object[] message = { resourceBundle.getString("device.manual.name") + ":", name, resourceBundle.getString("device.manual.ipaddress") + ":", ipAddress }; int option = JOptionPane.showConfirmDialog(null, message, resourceBundle.getString("device.manual"), JOptionPane.OK_CANCEL_OPTION); if (option == JOptionPane.OK_OPTION) { try { manualServers.add( new DialServer(name.getText(), InetAddress.getByName(ipAddress.getText()))); Object selected = deviceList.getSelectedItem(); int selectedIndex = deviceList.getSelectedIndex(); deviceList.removeAllItems(); deviceList.addItem(resourceBundle.getString("devices.select")); for (DialServer dialServer : servers) { deviceList.addItem(dialServer); } for (DialServer dialServer : manualServers) { deviceList.addItem(dialServer); } deviceList.invalidate(); if (selectedIndex > 0) { deviceList.setSelectedItem(selected); } else { if (deviceList.getItemCount() == 2) { // Automatically select single device deviceList.setSelectedIndex(1); } } manualDeviceList.removeAllItems(); for (DialServer dialServer : manualServers) { manualDeviceList.addItem(dialServer); } manualDeviceList.setVisible(true); storeProperties(); } catch (UnknownHostException e1) { Log.e(LOG_TAG, "manual IP address", e1); JOptionPane.showMessageDialog(FlingFrame.this, resourceBundle.getString("device.manual.invalidip")); } } } }); devicePanel.add(addButton); JButton removeButton = new JButton(resourceBundle.getString("device.manual.remove")); removeButton.setToolTipText(resourceBundle.getString("device.manual.remove.tooltip")); removeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Object selected = manualDeviceList.getSelectedItem(); manualDeviceList.removeItem(selected); if (manualDeviceList.getItemCount() == 0) { manualDeviceList.setVisible(false); } deviceList.removeItem(selected); deviceList.invalidate(); manualServers.remove(selected); storeProperties(); } }); devicePanel.add(removeButton); fieldPanel.add(devicePanel); int result = JOptionPane.showConfirmDialog(FlingFrame.this, myPanel, resourceBundle.getString("settings.title"), JOptionPane.OK_CANCEL_OPTION); if (result == JOptionPane.OK_OPTION) { transcodingParameterValues = transcodingParameters.getText(); transcodingExtensionValues = transcodingExtensions.getText(); storeProperties(); } } }); settingsButton.setToolTipText(resourceBundle.getString("settings.title")); devicePane.add(settingsButton); listPane.add(devicePane); // TODO volume = new JSlider(JSlider.VERTICAL, 0, 100, 0); volume.setUI(new MySliderUI(volume)); volume.setMajorTickSpacing(25); // volume.setMinorTickSpacing(5); volume.setPaintTicks(true); volume.setEnabled(true); volume.setValue(100); volume.setToolTipText(resourceBundle.getString("volume.title")); volume.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { JSlider source = (JSlider) e.getSource(); if (!source.getValueIsAdjusting()) { int position = (int) source.getValue(); rampClient.volume(position / 100.0f); } } }); JPanel centerPanel = new JPanel(new BorderLayout()); // centerPanel.add(volume, BorderLayout.WEST); centerPanel.add(DragHereIcon.makeUI(this), BorderLayout.CENTER); listPane.add(centerPanel); scrubber = new JSlider(JSlider.HORIZONTAL, 0, 100, 0); scrubber.addChangeListener(this); scrubber.setMajorTickSpacing(25); scrubber.setMinorTickSpacing(5); scrubber.setPaintTicks(true); scrubber.setEnabled(false); listPane.add(scrubber); // panel of playback buttons JPanel buttonPane = new JPanel(); buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS)); label = new JLabel("00:00:00"); buttonPane.add(label); url = ClassLoader.getSystemResource("com/entertailion/java/fling/resources/play.png"); icon = new ImageIcon(url, resourceBundle.getString("button.play")); playButton = new JButton(icon); playButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { rampClient.play(); } }); buttonPane.add(playButton); url = ClassLoader.getSystemResource("com/entertailion/java/fling/resources/pause.png"); icon = new ImageIcon(url, resourceBundle.getString("button.pause")); pauseButton = new JButton(icon); pauseButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { rampClient.pause(); } }); buttonPane.add(pauseButton); url = ClassLoader.getSystemResource("com/entertailion/java/fling/resources/stop.png"); icon = new ImageIcon(url, resourceBundle.getString("button.stop")); stopButton = new JButton(icon); stopButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { rampClient.stop(); setDuration(0); scrubber.setValue(0); scrubber.setEnabled(false); } }); buttonPane.add(stopButton); listPane.add(buttonPane); getContentPane().add(listPane); createProgressDialog(); startWebserver(); discoverDevices(); } // http://stackoverflow.com/questions/6992633/painting-the-slider-icon-of-jslider private static class MySliderUI extends BasicSliderUI { private Font font = new Font(Font.SERIF, Font.PLAIN, 12); public MySliderUI(JSlider slider) { super(slider); } @Override public void paintTrack(Graphics g) { Graphics2D g2d = (Graphics2D) g; Rectangle t = trackRect; g.setColor(Color.darkGray); g.drawRect(t.x, t.y, t.width, t.height); if (g instanceof Graphics2D) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setFont(font); int w2 = slider.getFontMetrics(font).stringWidth("Volume") / 2; g2.drawString("Volume", t.width / 2 - w2, t.height); } } @Override public void paintThumb(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Rectangle t = thumbRect; g2d.setColor(Color.black); int tw2 = t.width / 2; g2d.drawLine(t.x, t.y, t.x + t.width - 1, t.y); g2d.drawLine(t.x, t.y, t.x + tw2, t.y + t.height); g2d.drawLine(t.x + t.width - 1, t.y, t.x + tw2, t.y + t.height); } } public void stateChanged(ChangeEvent e) { JSlider source = (JSlider) e.getSource(); if (!source.getValueIsAdjusting() && !playbackValueIsAdjusting) { int position = (int) source.getValue(); if (position == 0) { rampClient.play(0); } else { rampClient.play((int) (position / 100.0f * duration)); } } } /** * Start a web server to serve the videos to the media player on the * ChromeCast device */ private void startWebserver() { new Thread(new Runnable() { @Override public void run() { boolean started = false; while (!started) { try { embeddedServer = new EmbeddedServer(port); Log.d(LOG_TAG, "Started web server on port " + port); started = true; } catch (IOException ioe) { ioe.printStackTrace(); port++; } catch (Exception ex) { break; } } } }).start(); } /** * Discover ChromeCast devices on the local network * */ private void discoverDevices() { Properties properties = loadProperties(); broadcastClient = new BroadcastDiscoveryClient(this); broadcastClientThread = new Thread(broadcastClient); deviceList.removeAllItems(); // Prompt selection option deviceList.addItem(resourceBundle.getString("devices.select")); // discovering devices can take time, so do it in a thread new Thread(new Runnable() { public void run() { try { showProgressDialog(resourceBundle.getString("progress.discoveringDevices")); broadcastClientThread.start(); // wait a while... // TODO do this better Thread.sleep(BroadcastDiscoveryClient.PROBE_INTERVAL_MS - 1); broadcastClient.stop(); hideProgressDialog(); Log.d(LOG_TAG, "size=" + trackedServers.size()); for (DialServer dialServer : trackedServers) { deviceList.addItem(dialServer); servers.add(dialServer); } // Now add user's manual servers for (DialServer dialServer : manualServers) { deviceList.addItem(dialServer); } deviceList.invalidate(); if (deviceList.getItemCount() == 1) { JOptionPane.showMessageDialog(FlingFrame.this, resourceBundle.getString("device.notfound")); } else if (deviceList.getItemCount() == 2) { // Automatically select single device deviceList.setSelectedIndex(1); } } catch (InterruptedException e) { Log.e(LOG_TAG, "discoverDevices", e); } } }).start(); } public void onBroadcastFound(final BroadcastAdvertisement advert) { if (advert.getLocation() != null) { new Thread(new Runnable() { public void run() { Log.d(LOG_TAG, "location=" + advert.getLocation()); HttpResponse response = new HttpRequestHelper().sendHttpGet(advert.getLocation()); if (response != null) { String appsUrl = null; Header header = response.getLastHeader(HEADER_APPLICATION_URL); if (header != null) { appsUrl = header.getValue(); if (!appsUrl.endsWith("/")) { appsUrl = appsUrl + "/"; } Log.d(LOG_TAG, "appsUrl=" + appsUrl); } try { InputStream inputStream = response.getEntity().getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); InputSource inStream = new org.xml.sax.InputSource(); inStream.setCharacterStream(reader); SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); XMLReader xr = sp.getXMLReader(); BroadcastHandler broadcastHandler = new BroadcastHandler(); xr.setContentHandler(broadcastHandler); xr.parse(inStream); Log.d(LOG_TAG, "modelName=" + broadcastHandler.getDialServer().getModelName()); // Only handle ChromeCast devices; not other DIAL // devices like ChromeCast devices if (broadcastHandler.getDialServer().getModelName().equals(CHROME_CAST_MODEL_NAME)) { Log.d(LOG_TAG, "ChromeCast device found: " + advert.getIpAddress().getHostAddress()); DialServer dialServer = new DialServer(advert.getLocation(), advert.getIpAddress(), advert.getPort(), appsUrl, broadcastHandler.getDialServer().getFriendlyName(), broadcastHandler.getDialServer().getUuid(), broadcastHandler.getDialServer().getManufacturer(), broadcastHandler.getDialServer().getModelName()); trackedServers.add(dialServer); } } catch (Exception e) { Log.e(LOG_TAG, "parse device description", e); } } } }).start(); } } private InterfaceAddress getPreferredInetAddress(String prefix) { InterfaceAddress selectedInterfaceAddress = null; try { Enumeration<NetworkInterface> list = NetworkInterface.getNetworkInterfaces(); while (list.hasMoreElements()) { NetworkInterface iface = list.nextElement(); if (iface == null) continue; Log.d(LOG_TAG, "interface=" + iface.getName()); Iterator<InterfaceAddress> it = iface.getInterfaceAddresses().iterator(); while (it.hasNext()) { InterfaceAddress interfaceAddress = it.next(); if (interfaceAddress == null) continue; InetAddress address = interfaceAddress.getAddress(); Log.d(LOG_TAG, "address=" + address); if (address instanceof Inet4Address) { // Only pick an interface that is likely to be on the // same subnet as the selected ChromeCast device if (address.getHostAddress().toString().startsWith(prefix)) { return interfaceAddress; } } } } } catch (Exception ex) { } return selectedInterfaceAddress; } /** * Get the network address. * * @return */ public Inet4Address getNetworAddress() { Inet4Address selectedInetAddress = null; try { InterfaceAddress interfaceAddress = null; if (selectedDialServer != null) { String address = selectedDialServer.getIpAddress().getHostAddress(); String prefix = address.substring(0, address.indexOf('.') + 1); Log.d(LOG_TAG, "prefix=" + prefix); interfaceAddress = getPreferredInetAddress(prefix); } else { InterfaceAddress oneNineTwoInetAddress = getPreferredInetAddress("192."); if (oneNineTwoInetAddress != null) { interfaceAddress = oneNineTwoInetAddress; } else { InterfaceAddress oneSevenTwoInetAddress = getPreferredInetAddress("172."); if (oneSevenTwoInetAddress != null) { interfaceAddress = oneSevenTwoInetAddress; } else { interfaceAddress = getPreferredInetAddress("10."); } } } if (interfaceAddress != null) { InetAddress networkAddress = interfaceAddress.getAddress(); Log.d(LOG_TAG, "networkAddress=" + networkAddress); if (networkAddress != null) { return (Inet4Address) networkAddress; } } } catch (Exception ex) { } return selectedInetAddress; } /** * Create a progress indicator */ private void createProgressDialog() { progressDialog = new JDialog(this, resourceBundle.getString("progress.title"), true); JProgressBar progressBar = new JProgressBar(0, 100); progressBar.setStringPainted(true); progressBar.setIndeterminate(true); progressDialog.add(BorderLayout.CENTER, progressBar); progressDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); progressDialog.setSize(300, 75); progressDialog.setLocationRelativeTo(this); } /** * Show the progress indicator with a title message * * @param message */ private void showProgressDialog(final String message) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { progressDialog.setLocationRelativeTo(FlingFrame.this); progressDialog.setTitle(message); progressDialog.setVisible(true); } }); } /** * Hide the progress indicator */ private void hideProgressDialog() { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { progressDialog.setVisible(false); } }); } /** * Event handler for device dropdown list selection * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent e) { JComboBox cb = (JComboBox) e.getSource(); int pos = cb.getSelectedIndex(); // when device is selected, attempt to connect if (servers != null && pos > 0) { selectedDialServer = (DialServer) cb.getSelectedItem(); } } /** * Send a uri to the ChromeCast device * * @param keycode */ protected void sendMediaUrl(String file) { if (selectedDialServer == null) { JOptionPane.showMessageDialog(this, resourceBundle.getString("device.select")); return; } isTranscoding = false; Log.d(LOG_TAG, "sendMediaUrl=" + file); if (file != null) { duration = 0; boolean found = false; String[] extensions = transcodingExtensionValues.split(","); for (String extension : extensions) { if (file.endsWith(extension.trim())) { found = true; break; } } if (!found) { try { int pos = file.lastIndexOf('.'); String extension = ""; if (pos > -1) { extension = file.substring(pos); } Inet4Address address = getNetworAddress(); if (address != null) { final String url = "http://" + address.getHostAddress() + ":" + port + "/video" + extension; if (!rampClient.isClosed()) { rampClient.stop(); } rampClient.launchApp(appId == null ? APP_ID : appId, selectedDialServer); // wait for socket to be ready... new Thread(new Runnable() { public void run() { while (!rampClient.isStarted() && !rampClient.isClosed()) { try { // make less than 3 second ping time Thread.sleep(500); } catch (InterruptedException e) { } } if (!rampClient.isClosed()) { try { Thread.sleep(500); } catch (InterruptedException e) { } rampClient.load(url); } } }).start(); } else { Log.d(LOG_TAG, "could not find a network interface"); } } catch (Exception e) { e.printStackTrace(); } } else { vlcTranscode(file); } // TODO if (!volume.getValueIsAdjusting()) { int position = (int) volume.getValue(); // rampClient.volume(position / 100.0f); } } } protected void vlcTranscode(final String file) { // Transcoding does not support jumps isTranscoding = true; // http://caprica.github.io/vlcj/javadoc/2.1.0/index.html try { // clean up previous session if (mediaPlayer != null) { mediaPlayer.release(); } if (mediaPlayerFactory != null) { mediaPlayerFactory.release(); } mediaPlayerFactory = new MediaPlayerFactory(); mediaPlayer = mediaPlayerFactory.newHeadlessMediaPlayer(); // Add a component to be notified of player events mediaPlayer.addMediaPlayerEventListener(new MediaPlayerEventAdapter() { public void opening(MediaPlayer mediaPlayer) { Log.d(LOG_TAG, "VLC Transcoding: Opening"); } public void buffering(MediaPlayer mediaPlayer, float newCache) { Log.d(LOG_TAG, "VLC Transcoding: Buffering"); } public void playing(MediaPlayer mediaPlayer) { Log.d(LOG_TAG, "VLC Transcoding: Playing"); setDuration((int) (mediaPlayer.getLength() / 1000.0f)); Log.d(LOG_TAG, "duration=" + duration); } public void paused(MediaPlayer mediaPlayer) { Log.d(LOG_TAG, "VLC Transcoding: Paused"); } public void stopped(MediaPlayer mediaPlayer) { Log.d(LOG_TAG, "VLC Transcoding: Stopped"); } public void finished(MediaPlayer mediaPlayer) { Log.d(LOG_TAG, "VLC Transcoding: Finished"); } public void error(MediaPlayer mediaPlayer) { Log.d(LOG_TAG, "VLC Transcoding: Error"); } public void videoOutput(MediaPlayer mediaPlayer, int newCount) { Log.d(LOG_TAG, "VLC Transcoding: VideoOutput"); } }); // Find a port for VLC HTTP server boolean started = false; int vlcPort = port + 1; while (!started) { try { ServerSocket serverSocket = new ServerSocket(vlcPort); Log.d(LOG_TAG, "Available port for VLC: " + vlcPort); started = true; serverSocket.close(); } catch (IOException ioe) { ioe.printStackTrace(); vlcPort++; } catch (Exception ex) { break; } } if (!rampClient.isClosed()) { rampClient.stop(); } Inet4Address address = getNetworAddress(); if (address != null) { // Play a particular item, with options if necessary final String options[] = { ":sout=#transcode{" + transcodingParameterValues + "}:http{mux=webm,dst=:" + vlcPort + "/cast.webm}", ":sout-keep" }; // http://192.168.0.8:8087/cast.webm final String url = "http://" + address.getHostAddress() + ":" + vlcPort + "/cast.webm"; Log.d(LOG_TAG, "url=" + url); if (true || isChromeCast()) { rampClient.launchApp(appId == null ? APP_ID : appId, selectedDialServer); // wait for socket to be ready... new Thread(new Runnable() { public void run() { while (!rampClient.isStarted() && !rampClient.isClosed()) { try { Thread.sleep(500); } catch (InterruptedException e) { } } if (!rampClient.isClosed()) { mediaPlayer.playMedia(file, options); rampClient.load(url); } } }).start(); } else { rampClient.load(url); } } else { Log.d(LOG_TAG, "could not find a network interface"); } } catch (Throwable e) { Log.e(LOG_TAG, "vlcTranscode: " + file, e); } } // Store user settings private Properties loadProperties() { Properties prop = new Properties(); try { prop.load(new FileInputStream("config.properties")); transcodingParameterValues = prop.getProperty(PROPERTY_TRANSCODING_PARAMETERS); transcodingExtensionValues = prop.getProperty(PROPERTY_TRANSCODING_EXTENSIONS); String manual = prop.getProperty(PROPERTY_MANUAL_SERVERS); if (manual != null) { String[] parts = manual.split(":"); for (int i = 0; i < parts.length / 2; i++) { manualServers.add(new DialServer(parts[i * 2], InetAddress.getByName(parts[i * 2 + 1]))); } } } catch (Exception ex) { } if (transcodingParameterValues == null) { transcodingParameterValues = TRANSCODING_PARAMETERS; } if (transcodingExtensionValues == null) { transcodingExtensionValues = TRANSCODING_EXTENSIONS; } return prop; } // Restore user settings private void storeProperties() { Properties prop = new Properties(); try { prop.setProperty(PROPERTY_TRANSCODING_PARAMETERS, transcodingParameterValues); prop.setProperty(PROPERTY_TRANSCODING_EXTENSIONS, transcodingExtensionValues); String manual = ""; for (DialServer dialServer : manualServers) { if (manual.length() > 0) { manual = manual + ":"; } manual = manual + dialServer.getFriendlyName() + ":" + dialServer.getIpAddress().getHostAddress(); } prop.setProperty(PROPERTY_MANUAL_SERVERS, manual); prop.store(new FileOutputStream("config.properties"), null); } catch (Exception ex) { } } public void updateTime(int time) { label.setText(simpleDateFormat.format(new Date(time * 1000))); if (duration > 0 && !scrubber.getValueIsAdjusting()) { playbackValueIsAdjusting = true; scrubber.setValue((int) ((time * 1.0f) / duration * 100)); playbackValueIsAdjusting = false; } } // Current video duration in seconds public int getDuration() { return duration; } // Display the current duration in the slider public void setDuration(int duration) { this.duration = duration; if (duration >= 0) { Hashtable labelTable = new Hashtable(); labelTable.put(new Integer(0), new JLabel("0")); labelTable.put(new Integer(100), new JLabel(simpleDateFormat.format(new Date(duration * 1000)))); scrubber.setLabelTable(labelTable); scrubber.setPaintLabels(true); if (!isTranscoding) { scrubber.setEnabled(true); } else { scrubber.setEnabled(false); } } } @Override public void windowActivated(WindowEvent arg0) { // TODO Auto-generated method stub } @Override public void windowClosed(WindowEvent arg0) { // TODO Auto-generated method stub } @Override public void windowClosing(WindowEvent arg0) { // TODO Auto-generated method stub if (rampClient != null) { rampClient.stop(); } } @Override public void windowDeactivated(WindowEvent arg0) { // TODO Auto-generated method stub } @Override public void windowDeiconified(WindowEvent arg0) { // TODO Auto-generated method stub } @Override public void windowIconified(WindowEvent arg0) { // TODO Auto-generated method stub } @Override public void windowOpened(WindowEvent arg0) { // TODO Auto-generated method stub } public boolean isChromeCast() { String id = appId == null ? APP_ID : appId; return id.equals(FlingFrame.CHROMECAST); } }