VideoCanvas.java
/*
* Copyright © 2012 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();
}
}
}
}
}