/* * Copyright © 2013 Nokia Corporation. All rights reserved. * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. * Oracle and Java are trademarks or registered trademarks of Oracle and/or its * affiliates. Other product and company names mentioned herein may be trademarks * or trade names of their respective owners. * See LICENSE.TXT for license information. */ package com.nokia.example.mmapi.mediasampler.viewer; import java.io.*; import javax.microedition.io.Connector; import javax.microedition.io.HttpConnection; import javax.microedition.lcdui.*; import javax.microedition.media.*; import javax.microedition.media.control.*; import com.nokia.example.mmapi.mediasampler.MediaSamplerMIDlet; import java.util.Timer; import java.util.TimerTask; /** * VideoCanvas renders video on Canvas. */ class VideoCanvas extends Canvas implements CommandListener, PlayerListener { private PlayerController controller; private MediaSamplerMIDlet midlet; private Displayable returnScreen; private String videoFile; private Command stopCommand; private Command replayCommand; private Command backCommand; private Player player; private boolean initDone; private boolean playPending = false; /** * Constructor. * * @param midlet * MediaSamplerMIDlet * @param returnScreen * Displayable to set visible when returned from this Canvas * @param videoFile * String as path of the source viudeo file. */ VideoCanvas(MediaSamplerMIDlet midlet, Displayable returnScreen, String videoFile) { this.midlet = midlet; this.returnScreen = returnScreen; this.videoFile = videoFile; controller = new PlayerController(); replayCommand = new Command("Replay", Command.SCREEN, 1); stopCommand = new Command("Stop", Command.SCREEN, 2); backCommand = new Command("Back", Command.BACK, 1); addCommand(backCommand); setCommandListener(this); } /** * Set play status to "pending". */ void prepareToPlay() { controller.start(); playPending = true; } /** * Play video only when we're displayed. Use playPending flag to avoid * restarting if a system screen is visiable. */ protected void showNotify() { if (playPending) { playPending = false; controller.playVideo(); } final VideoCanvas self = this; Timer timer = new Timer(); timer.schedule(new TimerTask(){ public void run() { self.repaint(); self.serviceRepaints(); } }, 200); } /** * Renders the Canvas. */ public void paint(Graphics g) { g.setColor(0x00FFFF00); // yellow g.fillRect(0, 0, getWidth(), getHeight()); } /** * Implemented CommandListener method. Indicates that a command event has * occurred on Displayable d. */ public void commandAction(Command c, Displayable d) { if (c == stopCommand) { controller.stopVideo(); } else if (c == replayCommand) { controller.playVideo(); } else if (c == backCommand) { discardPlayer(); } } /** * Overriden Canvas method. */ public void keyPressed(int keyCode) { if (getGameAction(keyCode) == FIRE) { int state = player.getState(); if (state == Player.STARTED) { controller.stopVideo(); } else { controller.playVideo(); } } } /** * Reads the content from the specified HTTP URL and returns InputStream * where the contents are read. * * @return InputStream * @throws IOException */ private InputStream urlToStream(String url) throws IOException { // Open connection to the http url... HttpConnection connection = (HttpConnection) Connector.open(url); DataInputStream dataIn = connection.openDataInputStream(); byte[] buffer = new byte[1000]; int read = -1; // Read the content from url. ByteArrayOutputStream byteout = new ByteArrayOutputStream(); while ((read = dataIn.read(buffer)) >= 0) { byteout.write(buffer, 0, read); } dataIn.close(); connection.close(); // Fill InputStream to return with content read from the URL. ByteArrayInputStream byteIn = new ByteArrayInputStream(byteout.toByteArray()); return byteIn; } /** * Stops the Player. */ void doStop() { if (player != null) { try { player.stop(); } catch (MediaException e) { e.printStackTrace(); } } } /** * Initializes and starts the Player. */ void play() { try { if (!initDone || player == null) { initPlayer(); } int state = player.getState(); if (state == Player.CLOSED) { player.prefetch(); } else if (state == Player.UNREALIZED) { player.realize(); } else if (state == Player.REALIZED) { player.prefetch(); } player.start(); } catch (MediaException me) { discardPlayer(); midlet.alertError("MediaException: " + me.getMessage()); } catch (SecurityException se) { discardPlayer(); midlet.alertError("SecurityException: " + se.getMessage()); } catch (Exception e) { discardPlayer(); midlet.alertError("Exception: " + e.getMessage()); } } /** * Initializes the video player. * * Player is initialized only once to save the memory resorces and to * increase performance. */ void initPlayer() { try { initDone = false; if (videoFile == null) { midlet.alertError("No video file specified"); return; } boolean fromHttp = videoFile.startsWith("http://"); InputStream is = fromHttp ? urlToStream(videoFile) : getClass().getResourceAsStream(videoFile); player = Manager.createPlayer(is, "video/3gpp"); player.addPlayerListener(this); player.prefetch(); player.realize(); // get the video control and attach it to our canvas VideoControl videoControl = (VideoControl) (player.getControl("VideoControl")); if (videoControl == null) { midlet.alertError("VideoControl not supported"); } else { videoControl.initDisplayMode(VideoControl.USE_DIRECT_VIDEO, this); videoControl.setVisible(true); } initDone = true; } catch (IOException ioe) { discardPlayer(); midlet.alertError("IOException: " + ioe.getMessage()); } catch (MediaException me) { discardPlayer(); midlet.alertError("MediaException: " + me.getMessage()); } catch (SecurityException se) { discardPlayer(); midlet.alertError("SecurityException: " + se.getMessage()); } catch (Exception ex) { discardPlayer(); midlet.alertError("Exception: " + ex.getMessage()); } } /** * Called in case of exception to make sure invalid players are closed */ void discardPlayer() { if (player != null) { controller.setStopped(); player.close(); player = null; } Display.getDisplay(midlet).setCurrent(returnScreen); } /** * Implemented javax.microedition.media.PlayerListener method. */ public void playerUpdate(final Player p, final String event, final Object eventData) { // queue a call to updateEvent in the user interface event queue Display display = Display.getDisplay(midlet); display.callSerially(new Runnable() { public void run() { VideoCanvas.this.updateEvent(p, event, eventData); } }); } /** * Handles playerUpdate events of the Player. * * @param p * @param event * @param eventData */ void updateEvent(Player p, String event, Object eventData) { if (event.equals(END_OF_MEDIA)) { repaint(); // to ensure smooth operation of the menu button removeCommand(stopCommand); addCommand(replayCommand); } else if (event.equals(CLOSED)) { removeCommand(stopCommand); addCommand(replayCommand); } else if (event.equals(STARTED)) { removeCommand(replayCommand); addCommand(stopCommand); } else if (event.equals(ERROR)) { } } /** * PlayerController calls the play and stop methods of the Player. The * purpose of this class is only to isolate Player method calls from the * event threads (such commandAction(...)) */ public class PlayerController extends Thread { private boolean running; // Lock object of this Thread private Object controlLock = new Object(); public PlayerController() { } // Activates the Player public void playVideo() { synchronized (controlLock) { controlLock.notify(); } } // Deactivates the player public void stopVideo() { synchronized (controlLock) { doStop(); } } // Terminates this thread public void setStopped() { running = false; synchronized (controlLock) { controlLock.notify(); } } public void run() { VideoCanvas.this.play(); running = true; while (running) { try { synchronized (controlLock) { // Set this thread to wait for playVideo() method call. controlLock.wait(); if (!running) { // Leave if controller is stopped. break; } VideoCanvas.this.play(); } } catch (Exception e) { e.printStackTrace(); } } } } }