javauis/mmapi_qt/baseline/javasrc/com/nokia/microedition/media/animation/AnimationPlayer.java
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 11 May 2010 16:07:20 +0300
branchRCL_3
changeset 24 0fd27995241b
permissions -rw-r--r--
Revision: v2.1.24 Kit: 201019

/**
 * 
 */
package com.nokia.microedition.media.animation;

import java.io.InputStream;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.microedition.media.Control;
import javax.microedition.media.MediaException;
import javax.microedition.media.PlayerListener;
import javax.microedition.media.protocol.DataSource;

import org.eclipse.ercp.swt.mobile.MobileShell;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import com.nokia.microedition.media.BufferDataSource;
import com.nokia.microedition.media.InputStreamDataSource;
import com.nokia.microedition.media.InputStreamSourceStream;
import com.nokia.microedition.media.Locator;
import com.nokia.microedition.media.PlayerBase;
import com.nokia.microedition.media.PlayerListenerImpl;

/**
 * @author d35kumar
 *
 */
public class AnimationPlayer extends PlayerBase {
	// GIF image information, array length will be equal to the number of frames in image 
	protected ImageData [] iImageData;
	// number of times we need to repeat the animation
	// by default it's value is one 
	private int iTotalLoopCount=1;
	// this holds all control related to this player 
	Hashtable iControls= new Hashtable();
	// Current frame index of the Animation file
	private int iFrameindex;
	// Current Loop Count 
	private int iCurrentLoopCount;
	// Display object of the eSWT API, creating this object will integrate the current java code with eSWT API. 
	Display iDisplay; 
	// Control object of ESWT API, return from LCDUIInvoker. 
	org.eclipse.swt.widgets.Control iControl;
	// Constants used everywhere to compare the string 
	private static final String ANIMATION_CONTENT_TYPE = "image/gif";
	
	private final String fVideoControl=VideoControl.class.getName();
	private final String fFramePositioningControl=FramePositioningControl.class.getName();
	private final String fStopTimeControl=StopTimeControl.class.getName();
	private final String fRateControl=RateControl.class.getName();
	 /**
     * Default control package. Used when getting control with
     * getControl method which appends default control package if package is
     * not specified.
     */
    static final String CONTROL_DEFAULT_PACKAGE =
        "javax.microedition.media.control.";
    
	//For Player listener 
	protected PlayerListenerImpl iPlayerListenerImpl;
	//Image to be displayed, again object of eSWT API. 
	protected Image iImage;
	// Actual dimension of the image, this should be initialized while creating the player
	// as user can change the size of the image later, in that case too, getSourceheight and getSourceWidth 
	// of VideoControl should return the actual width and height of the image 
	Point iSourceDimension;
	long iMediaTime;
	
	// Display Location, of the image   
	private Point iDisplayLocation= new Point(0,0);
	

	/**
	 * 
	 * @param ds DataSource which contains the data to be displayed 
	 */
	public AnimationPlayer(DataSource ds){
		final String DEBUG_STR="AnimationPlayer::AnimationPlayer(DataSource)";
		System.out.println(DEBUG_STR+"+");
		iPlayerListenerImpl= new PlayerListenerImpl(this);
		//TODO check if we can do it in better way 
		// this is temporary solution
		// for this I have written two functions getDataSource and getInputStream function 
		BufferDataSource bds =(BufferDataSource )ds;
		InputStreamDataSource isds=(InputStreamDataSource)bds.getDataSource();
		InputStreamSourceStream isss=(InputStreamSourceStream )isds.getStreams()[0];
		InputStream is = isss.getInputStream();
		
		if(is!=null){
			ImageLoader imageLoader= new ImageLoader();
			iImageData=imageLoader.load(is);
			//iRepeatCount=imageLoader.repeatCount;
			iSourceDimension= new Point(imageLoader.logicalScreenWidth, imageLoader.logicalScreenHeight);
		}
		pupulateControl();
		System.out.println(DEBUG_STR+"-");
	}
	
	/**
	 * 
	 * @param locator
	 * @throws SWTException
	 */
	public AnimationPlayer(String locator) throws SWTException{
		final String DEBUG_STR="AnimationPlayer::AnimationPlayer(Locator )";
		System.out.println(DEBUG_STR+"+");
		ImageLoader imageLoader= new ImageLoader();
		System.out.println(DEBUG_STR+"Locator string is "+locator);
		// Following line may throw SWTException 
		iImageData=imageLoader.load(locator);
		//iRepeatCount=imageLoader.repeatCount;
		iSourceDimension= new Point(imageLoader.logicalScreenWidth, imageLoader.logicalScreenHeight);
		pupulateControl();
		System.out.println(DEBUG_STR+"-");
	}
	
	/**
	 * Moved the player to close state and releases all resources, called from PlayerBase class  
	 */
	protected void doClose() {
		final String DEBUG_STR="AnimationPlayer::doClose()";
		System.out.println(DEBUG_STR+"+");
		iState=CLOSED;
		iPlayerListenerImpl.postEvent(PlayerListener.CLOSED, null);
		System.out.println(DEBUG_STR+"-");
	}

	protected void doDeallocate() {
		// what to do here, nullify image data and display etc???
	}

	protected void doPrefetch() throws MediaException {
		final String DEBUG_STR="AnimationPlayer::doPrefetch()";
		System.out.println(DEBUG_STR+"+");
		iState=PREFETCHED;
		System.out.println(DEBUG_STR+"-");
	}

	protected void doRealize() throws MediaException {
		final String DEBUG_STR="AnimationPlayer::doRealize()";
		System.out.println(DEBUG_STR+"+");
		iState=REALIZED;
		// this is temporary solution implement it in proper way 
		iImage=new Image(iDisplay, iImageData[0]);
		System.out.println(DEBUG_STR+"-");
	}

	protected void doStop() throws MediaException {
		final String DEBUG_STR="AnimationPlayer::doStop()";
		System.out.println(DEBUG_STR+"+");
		// since after stopping the player the player state will move to pre-fetched state 
		iState=PREFETCHED;
		iPlayerListenerImpl.postEvent(PlayerListener.STOPPED, new Long(iMediaTime * 10000));
		System.out.println(DEBUG_STR+"-");
	}

	public void addControl(Control aControl, String aControlType)
			throws MediaException {
		iControls.put(aControlType, aControl);
	}

	public void addPlayerListener(PlayerListener aPlayerListener) {
		closeCheck();
        iPlayerListenerImpl.addPlayerListener(aPlayerListener);
	}
	
	/**
	 * Returns the Content type("image/GIF") supported for by this player, 
	 */
	public String getContentType() {
		closeCheck();
        unrealizedCheck();
		return ANIMATION_CONTENT_TYPE;
	}
	
	/**
	 * This function will return, total time in microseconds this player can be played 
	 */
	public long getDuration() {
		closeCheck();
		long time = TIME_UNKNOWN;
		int totalNoOfFrames = iImageData.length;
		for (int i = 0; i < totalNoOfFrames; i++) {
			time += iImageData[i].delayTime;
		}
		// Since we have to return it in microsecond multiply it with 1000; 
		return time * 10000;
	}
	
	/**
	 * This returned the total time taken, till now, to play the video.  
	 */
	public long getMediaTime() {
		return iMediaTime*10000;
	}

	public int getState() {
		return iState;
	}
	
	/**
	 * Removes the specified playerListner from this player  
	 */
	public void removePlayerListener(PlayerListener aPlayerListener) {
		closeCheck();
        iPlayerListenerImpl.removePlayerListener(aPlayerListener);
	}
	public void start() throws MediaException {
		final String DEBUG_STR = "AnimationPlayer::start()";
//		prefetch();
		initialize();
		addPaintListener(iControl);
		iState = STARTED;
		Thread thread = new Thread("Animation") {
			int frameIndex = iFrameindex;
			int loopCount = iCurrentLoopCount;
			GC gc=null;
			public void run() {
				final int noOfFrames = iImageData.length;
				while (frameIndex < noOfFrames && (loopCount < iTotalLoopCount)
						&& (iState == STARTED)) {
					final int delayTimeForNextFrame = iImageData[frameIndex].delayTime;
					System.out.println("\n\n\n************" + DEBUG_STR
							+ "Inside the while loop " + frameIndex + "/"
							+ noOfFrames);
					// Since we are going to display first frame, notify all
					// PlayerListener that Player has started
					if (frameIndex == 0) {
						iPlayerListenerImpl.postEvent(PlayerListener.STARTED,
								new Long(iMediaTime * 10000));
					}
					iDisplay.asyncExec(new Runnable() {
						public void run() {
							System.out.println(DEBUG_STR+"Inside run method");
							try {
								if (gc == null)gc = new GC(iImage);
							} catch (Exception e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
							System.out.println(DEBUG_STR+"Inside run method246");
							Image tempImage = new Image(iDisplay,
									iImageData[frameIndex]);
							System.out.println(DEBUG_STR+"Inside run method246");
							gc.drawImage(tempImage, 0, 0);
							tempImage.dispose();
							frameIndex = (frameIndex + 1) % noOfFrames;
							System.out.println(DEBUG_STR+"Inside run method253>>>>>>>>>"+frameIndex);
							iControl.redraw();
							// update the mediaTime, as Animation progress
							iMediaTime += delayTimeForNextFrame;
							// If imageIndex becomes zero it means, all frames
							// has been displayed
							// So increase the loopCount
							if (frameIndex == 0) {
								// send the END_OF_MEDIA event to all listener
								iPlayerListenerImpl.postEvent(
										PlayerListener.END_OF_MEDIA, new Long(
												iMediaTime * 10000));
								loopCount++;
								// since player is again going to start from the
								// first frame
								// so media time should be set to zero
								iMediaTime = 0;
							}
							System.out
									.println(DEBUG_STR
											+ "end of asynchronous block imageIndex is "
											+ frameIndex + " and loop count is"
											+ loopCount );
						}
					});
					try {
						Thread.sleep(delayTimeForNextFrame * 10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out
							.println("**********************End of while loop\n\n\n");
				}
				iCurrentLoopCount=loopCount;
				System.out.println(DEBUG_STR + "Came out side the while loop "
						+ iState);
			}
		};
		thread.start();
		System.out.println(DEBUG_STR + "-");
		// }catch(Exception e){
		// System.out.println(DEBUG_STR+"Exception caught"+e);
		// e.printStackTrace();
		// }
	}
	
	/**
	 * This function is also being called from VideoControl class, 
	 * Since on each event 
	 * @param aControl
	 * @throws MediaException 
	 */
	 void addPaintListener(org.eclipse.swt.widgets.Control aControl) throws MediaException{
		 final String DEBUG_STR="AnimationPlayer::addPaintListener()";
		 iControl=aControl;
		// iDisplay and IControl shouldn't be null here 
		System.out.println(DEBUG_STR+" Control is "+iControl+" Display "+iDisplay);
		// Following line should never execute
		// TODO check what message to provide in case of Exception 
		if(iControl==null)
			throw new MediaException("Update this message");
		iDisplay.syncExec(new Runnable(){
			public void run() {
				// if user is writing his MIDlet using eSWT API, then the Control object will be MobileShell
				// and in that case it is mandatory to open the shell
//				if(iControl instanceof MobileShell){
				if(iControl instanceof Shell){
					((Shell)iControl).open();
				}
				iControl.addPaintListener(new PaintListener(){
					public void paintControl(PaintEvent pe) {
//						System.out.println(DEBUG_STR+"paintControl() paintEvent is "+pe);
//						System.out.println(DEBUG_STR+"paintControl() Display location is "+iDisplayLocation.x+", "+iDisplayLocation.y);
						if(iImage!=null){
							pe.gc.drawImage(iImage, iDisplayLocation.x, iDisplayLocation.y);
						}
					}
				});
			}
		});
	}
	/**
	 * 
	 */
	public Control getControl(String aControlType) {
		if (aControlType == null) {
			throw new IllegalArgumentException("argument is null");
		}
		
		String controlType = aControlType;

        // check if package name exists
        if (controlType.indexOf(".") == -1)
        {
            // add package name
            controlType = CONTROL_DEFAULT_PACKAGE + aControlType;
        }
        Control control = (Control)iControls.get(controlType);
        // If control does not exists with default name, check if there is
        // is a control with same type ( extends the given class name ).
		if (control == null) {
			try {
				// try to create class for control
				Class controlClass = Class.forName(controlType);
				Enumeration elements = iControls.elements();
				// search if any control is same type that requested control
				while (elements.hasMoreElements()) {
					Control tmpControl = (Control) elements.nextElement();
					if (controlClass.isInstance(tmpControl)) {
						// control is found
						control = tmpControl;
					}
				}
			} catch (ClassNotFoundException cnfe) // the class could not be
													// found
			{
				// Exception is ignored and null is returned from this method
			} catch (Error e) // the function failed for any other reason.
			{
				// Error is ignored and null is returned from this method
			}
		}
        return control;
	}
	/**
	 * Returns all the control associated with this player 
	 */
	public Control[] getControls() {
		Control control[]= new Control[iControls.size()];
		Enumeration enumeration= iControls.elements();
		byte index=0;
		while(enumeration.hasMoreElements()){
			control[index++]=(Control)enumeration.nextElement();
		}
		return control;
	}
	/**
	 * 
	 */
	public void setLoopCount(int aCount){
		super.setLoopCount(aCount);
		iTotalLoopCount=aCount;
	}
	/**
     * From PlayerBase
     * @see PlayerBase
     */
	public long setMediaTime(long aNow) throws MediaException {
		long now = super.setMediaTime(aNow);
		int totalFrames = iImageData.length;
		int totalTime = 0;
		for (int i = 0; i < totalFrames; i++) {
			if (now < totalTime) {
				iFrameindex=i;
				break;
			}
			totalTime += iImageData[i].delayTime * 10000;
		}
		return totalTime;
	}
	//////////////////////////////////////////////////////////////////////////////////////
	// Following functions are for internal use, and not exposed to MIDlet developer//////
	/////////////////////////////////////////////////////////////////////////////////////
	/**
	 * 
	 */
	private void doStart(){
		
	}
	
	
    /**
	 * This function is responsible for creating all controls and adding it into Controls hashtable.
	 */
	private void pupulateControl(){
		final String DEBUG_STR="AnimationPlayer::pupulateControl()";
		System.out.println(DEBUG_STR+"+");
		VideoControl videoControl= new VideoControl(this);
		FramePositioningControl fpc= new FramePositioningControl(this);
		StopTimeControl stc= new StopTimeControl(this);
		RateControl rc= new RateControl(this);
		try {
			addControl(videoControl, fVideoControl);
			addControl(fpc, fFramePositioningControl);
			addControl(stc, fStopTimeControl);
			addControl(rc,fRateControl);
		} catch (MediaException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(DEBUG_STR+"-");
	}
	/**
	 * This function initialize iControl and iDisplay object if it is null,
	 * otherwise return immediately.
	 * 
	 * In case of Canvas, eSWT control will be returned immediately from VideoControl(vc.getControl()), 
	 * but in case of CustomItem we need to keep polling, eSWT doesn't return the control for CustomItem 
	 * until CustomItem is appended to Form.      
	 */
	
	private void initialize() {
		final String DEBUG_STR = "AnimationPlayer::initialize()";
		System.out.println(DEBUG_STR + "+");
		System.out.println(DEBUG_STR + " Control is " + iControl
				+ " display is " + iDisplay);
		if (iControl == null || iDisplay == null) {
			VideoControl vc = (VideoControl) getControl(fVideoControl);
			iDisplay = vc.getiDisplay();
			// in case of CustomItem,
			while ((iControl = vc.getControl()) == null) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		System.out.println(DEBUG_STR + "-");
	}
	
	/**
	 * This function will be called from setDisplaySize(int width, int height) of animation/VideoControl class
	 * When MIDlet developer will try to alter the size of the image 
	 * @param width : to be set of the video(Animated GIF) 
	 * @param height : height to be set of video(Animated GIF) 
	 */
	void updateImageData(int width, int height){
		final String DEBUG_STR= "AnimationPlayer::updateImageData(int width, int height)";
		System.out.println(DEBUG_STR+"+");
		int noOfFrames= iImageData.length;
		for(int i=0;i<noOfFrames;i++){
			iImageData[i]=iImageData[i].scaledTo(width, height);
		}
		iImage=new Image(iDisplay, iImageData[0]);
		System.out.println(DEBUG_STR+"-");
	}
	
	/**
	 * This function will be called from getSnapshot() function of VideoControl class 
	 * to get the snap shot of the video 
	 * 
	 * @param format
	 */
	// This function is not implemented fully  
	int[] getCurrentFrame(String format){
		final String DEBUG_STR="AnimationPlayer::getCurrentFrame()";
		System.out.println(DEBUG_STR+"+");
		ImageData currentFrameImageData=iImage.getImageData();
		int data[][]= new int[currentFrameImageData.height][currentFrameImageData.width];
		System.out.println("Width of the current Image is "+data[0].length+" height "+data.length);
		
		int imageWidth=data[0].length;
		int imageHeight=data.length;
		System.out.println("Image Width "+imageWidth+", "+currentFrameImageData.width+" height "+imageHeight+", "+currentFrameImageData.height);
		
		for(int i=0;i<imageHeight;i++){
			currentFrameImageData.getPixels(0, i, imageWidth, data[i], 0);
			System.out.println(DEBUG_STR+"=========>"+data[i]);
		}
		System.out.println(DEBUG_STR+"After reading all pixel value");
		int[] byteArray=new int[imageWidth*imageHeight];
		//int dataLength=data.length;
		
		for(int i=0;i<imageHeight;i++){
			System.arraycopy(data[i], 0, byteArray, i*imageWidth, imageWidth);
		}
		System.out.println(DEBUG_STR+"After merging all array into single array ");
		System.out.println(DEBUG_STR+"-"+byteArray+">>>>>"+byteArray.length);
		return byteArray;
	}
	
	/**
	 * Returns Point object, which contains the width and height of the image
	 * Called from VideoControl to get the image width and height, 
	 * so that Item will be created exactly of same dimension 
	 */
	org.eclipse.swt.graphics.Point getImageDimension(){
		return new org.eclipse.swt.graphics.Point(iImageData[0].width, iImageData[0].height);  
	}
	
	/**
	 * This returns the imageData array, 
	 * called from FramePositioningControl class to calculate the frame time 
	 */
	ImageData[] getImageData(){
		return iImageData;
	}
	
	/**
	 * 
	 * @param aDisplayLocation x,y coordinate where image is to be displayed 
	 */
	void setDisplayLocation(int aX, int aY){
		iDisplayLocation=new Point(aX,aY);
	}
	
	/**
	 * @return the position of the image to be displayed 
	 */
	Point getiDisplayLocation() {
		return iDisplayLocation;
	}
	/**
	 * Called from VideoControl to get the dimension of original image size 
	 * @return
	 */
	Point getSourceDimension() {
		return iSourceDimension;
	}

	/**
	 * @return the iPlayerListenerImpl
	 */
	PlayerListenerImpl getiPlayerListenerImpl() {
		return iPlayerListenerImpl;
	}
}