/*********************************************************************** * mt4j Copyright (c) 2008 - 2010 Christopher Ruff, Fraunhofer-Gesellschaft All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***********************************************************************/ package org.mt4j.components.visibleComponents.widgets.video; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import org.mt4j.MTApplication; import org.mt4j.components.TransformSpace; import org.mt4j.components.bounds.IBoundingShape; import org.mt4j.components.visibleComponents.shapes.MTEllipse; import org.mt4j.components.visibleComponents.shapes.MTPolygon; import org.mt4j.components.visibleComponents.shapes.MTRectangle; import org.mt4j.components.visibleComponents.widgets.MTSlider; import org.mt4j.components.visibleComponents.widgets.buttons.MTImageButton; import org.mt4j.input.inputProcessors.IGestureEventListener; import org.mt4j.input.inputProcessors.MTGestureEvent; import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragEvent; import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragProcessor; import org.mt4j.input.inputProcessors.componentProcessors.lassoProcessor.IdragClusterable; import org.mt4j.input.inputProcessors.componentProcessors.tapProcessor.TapEvent; import org.mt4j.input.inputProcessors.componentProcessors.tapProcessor.TapProcessor; import org.mt4j.util.MT4jSettings; import org.mt4j.util.MTColor; import org.mt4j.util.animation.Animation; import org.mt4j.util.animation.AnimationEvent; import org.mt4j.util.animation.IAnimationListener; import org.mt4j.util.animation.MultiPurposeInterpolator; import org.mt4j.util.math.Vector3D; import org.mt4j.util.math.Vertex; import processing.core.PApplet; import processing.core.PImage; import codeanticode.gsvideo.GSMovie; /** * The Class MTMovieClip. * A widget which can be used as a video player. * <br>NOTE: Needs to have the GStreamer framework to be installed on the system. * * @author Chris */ public class MTMovieClip extends MTRectangle implements IdragClusterable { /** The app. */ private PApplet app; /** The selected. */ private boolean selected; /** The slider. */ private MTSlider slider; /** The dragging. */ private boolean stopSliderAdvance; private MovieClip movieClip; private float topBarHeight = 35; private float sideBarWidth = 15; private float bottomBarHeight = 35; private PlaySymbol playSymbol; private MTImageButton closeButton; private MTSlider volumeSlider; //TODO tap on slider -> amount settable? //TODO delete button svg's from SVN? //TODO (volume control icon) //TODO (title bar) //TODO we actually would need some sort of command queue since this //has to work asynchronously -> read from queue after the next frame //FIXME error at pause sometimes when using gplayer.isPlaying() -> report bug public MTMovieClip(String movieFile, Vertex upperLeft, PApplet pApplet) { this(movieFile, upperLeft, 30, pApplet); } public MTMovieClip(String movieFile, Vertex upperLeft, int ifps, PApplet pApplet) { super(upperLeft, 100, 100, pApplet); this.app = pApplet; this.selected = false; stopSliderAdvance = false; this.setSizeLocal(105 + 2*sideBarWidth, 127 + topBarHeight + bottomBarHeight); this.setStrokeColor(new MTColor(0,0,0)); this.setFillColor(new MTColor(50,50,50,200)); // this.setNoFill(true); //Create movieclip child Vertex movieClipUpperLeft = new Vertex(upperLeft); movieClipUpperLeft.y += topBarHeight; movieClipUpperLeft.x += sideBarWidth; this.movieClip = new MovieClip(movieFile, movieClipUpperLeft, ifps, pApplet); this.movieClip.setStrokeColor(new MTColor(0,0,0)); this.movieClip.setStrokeWeight(0.5f); this.movieClip.setNoFill(true); this.movieClip.setNoStroke(true); this.addChild(movieClip); // MTSvgButton playButton = new MTSvgButton(MT4jSettings.getInstance().getDefaultSVGPath() // + "play.svg" , pApplet); // playButton.addActionListener(new ActionListener() { // public void actionPerformed(ActionEvent arg0) { // switch (arg0.getID()) { // case TapEvent.BUTTON_CLICKED: // if (movieClip != null && movieClip.movie != null){ // movieClip.loop(); // if (slider != null){ // slider.setVisible(true); // } // } // break; // default: // break; // } // } // }); // playButton.scale(0.5f, 0.5f, 1, new Vector3D(0,0,0)); // playButton.translate(upperLeft); // this.addChild(playButton); // MTSvgButton stopButton = new MTSvgButton(MT4jSettings.getInstance().getDefaultSVGPath() // + "stop.svg" , pApplet); // stopButton.addActionListener(new ActionListener() { // public void actionPerformed(ActionEvent arg0) { // switch (arg0.getID()) { // case TapEvent.BUTTON_CLICKED: // if (movieClip != null && movieClip.movie != null){ //// movie.stop(); ////// movie.pause(); //// movie.goToBeginning(); // //// if (getRenderer() instanceof MTApplication) { //// final MTApplication app = (MTApplication) getRenderer() ; //// movie.goToBeginning(); //// app.invokeLater(new Runnable() { //// public void run() { //// app.invokeLater(new Runnable() { //// public void run() { //// movie.pause(); //// } //// }); //// } //// }); //// }else{ // movieClip.goToBeginning(); // movieClip.pause(); //// } //// movie.stop(); // } // if (slider != null){ // slider.setVisible(false); // } // break; // default: // break; // } // } // }); // //m�sste eigentlich gr�sste comp aus svg holen, dann center an die stelle positionieren // this.addChild(stopButton); // stopButton.scale(0.5f, 0.5f, 1, new Vector3D(0,0,0)); // stopButton.translate(new Vector3D(upperLeft.x + 30 , upperLeft.y, upperLeft.z)); float movieClipWidth = movieClip.getWidthXY(TransformSpace.RELATIVE_TO_PARENT); playSymbol = new PlaySymbol(app, this.movieClip.getCenterPointRelativeToParent(), movieClipWidth/2f, movieClipWidth/2f, 35); this.addChild(playSymbol); this.registerInputProcessor(new TapProcessor(app, 30)); this.addGestureListener(TapProcessor.class, new IGestureEventListener() { public boolean processGestureEvent(MTGestureEvent ge) { TapEvent te = (TapEvent)ge; if (te.isTapped()){ if (movieClip != null && movieClip.movie != null){ // if (movieClip.getMovie().isPlaying()){ if (!playSymbol.isVisible()){ // System.out.println("Pause!"); if (playSymbol != null){ playSymbol.setVisible(true); } if (slider != null){ slider.setVisible(false); } movieClip.loop(); //FIXME TEST movieClip.pause(); }else{ // System.out.println("Play!"); if (playSymbol != null){ playSymbol.setVisible(false); } if (slider != null){ slider.setVisible(true); } movieClip.loop(); } } } return false; } }); //Close button PImage closeButtonImage = app.loadImage(MT4jSettings.getInstance().getDefaultImagesPath() + "closeButton64.png"); closeButton = new MTImageButton(closeButtonImage, app); closeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent r) { switch (r.getID()) { case TapEvent.BUTTON_CLICKED: close(); break; default: break; } } }); this.addChild(closeButton); closeButton.setNoStroke(true); closeButton.setSizeXYRelativeToParent(topBarHeight - 0, topBarHeight - 0); this.setAnchor(PositionAnchor.UPPER_LEFT); Vector3D upperRight = new Vector3D(upperLeft.x + this.getWidthXY(TransformSpace.LOCAL), upperLeft.y); Vector3D closeButtonPos = new Vector3D(upperRight.x - closeButton.getWidthXY(TransformSpace.RELATIVE_TO_PARENT), upperRight.y); closeButton.setAnchor(PositionAnchor.UPPER_LEFT); closeButton.setPositionRelativeToParent(closeButtonPos); } private class PlaySymbol extends MTEllipse{ public PlaySymbol(PApplet pApplet, Vector3D centerPoint, float radiusX, float radiusY, int segments) { super(pApplet, centerPoint, radiusX, radiusY, segments); Vertex[] vertices = new Vertex[]{ new Vertex(centerPoint.x - radiusX/3f, centerPoint.y - radiusY/3f, centerPoint.z), new Vertex(centerPoint.x + radiusX/2.5f, centerPoint.y, centerPoint.z), new Vertex(centerPoint.x - radiusX/3f, centerPoint.y + radiusY/3f, centerPoint.z), new Vertex(centerPoint.x - radiusX/3f, centerPoint.y - radiusY/3f, centerPoint.z), }; MTPolygon triangle = new MTPolygon(pApplet, vertices); triangle.setPickable(false); triangle.setNoFill(true); triangle.setStrokeColor(new MTColor(0,0,0,255)); triangle.setStrokeWeight(1.5f); this.addChild(triangle); this.setComposite(true); this.setPickable(false); //We tap on the movie itself instead this.setFillColor(new MTColor(150,150,150,255)); this.setStrokeColor(new MTColor(0,0,0,255)); this.setStrokeWeight(1.5f); } @Override protected void setDefaultGestureActions() { } @Override protected IBoundingShape computeDefaultBounds() { return null; } } private class MovieClip extends MTVideoTexture{ public MovieClip(String movieFile, Vertex upperLeft, int ifps, PApplet pApplet) { super(movieFile, upperLeft, ifps, pApplet); this.setPickable(false); } @Override protected void onFirstFrame() { super.onFirstFrame(); GSMovie m = getMovie(); if (m != null){ this.setNoFill(false); this.setNoStroke(false); //Resize MTMovieClip MTMovieClip.this.setSizeLocal(movieClip.getWidthXY(TransformSpace.RELATIVE_TO_PARENT) + 2*sideBarWidth, movieClip.getHeightXY(TransformSpace.RELATIVE_TO_PARENT) + topBarHeight + bottomBarHeight); //Reposition movie PositionAnchor oldAnchor = MTMovieClip.this.getAnchor(); MTMovieClip.this.setAnchor(PositionAnchor.LOWER_LEFT); Vector3D lowerLeft = MTMovieClip.this.getPosition(TransformSpace.LOCAL); MTMovieClip.this.setAnchor(oldAnchor); //Reposition close button MTMovieClip.this.setAnchor(PositionAnchor.UPPER_LEFT); Vector3D upperLeft = MTMovieClip.this.getPosition(TransformSpace.LOCAL); this.setAnchor(PositionAnchor.UPPER_LEFT); Vector3D upperRight = new Vector3D(upperLeft.x + MTMovieClip.this.getWidthXY(TransformSpace.LOCAL), upperLeft.y); Vector3D closeButtonPos = new Vector3D(upperRight.x - closeButton.getWidthXY(TransformSpace.RELATIVE_TO_PARENT) - sideBarWidth, upperRight.y); closeButton.setPositionRelativeToParent(closeButtonPos); //Reposition play symbol if (playSymbol != null){ // playSymbol.setSizeXYRelativeToParent(this.getHeightXY(TransformSpace.LOCAL), this.getHeightXY(TransformSpace.LOCAL)); playSymbol.setPositionRelativeToParent(this.getCenterPointRelativeToParent()); } //Create movie seek Slider float sliderXPadding = 10; float sliderYPadding = 3; float sliderHeight = bottomBarHeight - 2*sliderYPadding; slider = new MTSlider(lowerLeft.x + sliderXPadding, lowerLeft.y - sliderHeight - sliderYPadding, MTMovieClip.this.getWidthXY(TransformSpace.LOCAL) - sliderXPadding*2, sliderHeight, 0, 10, app); slider.getOuterShape().setFillColor(new MTColor(0, 0, 0, 80)); slider.getOuterShape().setStrokeColor(new MTColor(0, 0, 0, 80)); slider.getKnob().setFillColor(new MTColor(100, 100, 100, 80)); slider.getOuterShape().setStrokeColor(new MTColor(100, 100, 100, 80)); slider.getKnob().addGestureListener(DragProcessor.class, new IGestureEventListener() { public boolean processGestureEvent(MTGestureEvent ge) { DragEvent de = (DragEvent)ge; switch (de.getId()) { case MTGestureEvent.GESTURE_DETECTED: stopSliderAdvance = true; break; case MTGestureEvent.GESTURE_UPDATED: break; case MTGestureEvent.GESTURE_ENDED: if (movieClip != null && movieClip.getMovie() != null /*&& movieClip.getMovie().isPlaying()*/){ float currValue = slider.getValue(); movieClip.jump(currValue); } stopSliderAdvance = false; break; default: break; } return false; } }); //Dont do every frame! Duration is only valid if playing.. slider.setValueRange(0, m.duration()); slider.getOuterShape().addGestureListener(TapProcessor.class, new IGestureEventListener() { public boolean processGestureEvent(MTGestureEvent ge) { TapEvent te = (TapEvent)ge; switch (te.getTapID()) { case TapEvent.BUTTON_DOWN: stopSliderAdvance = true; break; case TapEvent.BUTTON_UP: stopSliderAdvance = false; break; case TapEvent.BUTTON_CLICKED: if (movieClip != null && movieClip.getMovie() != null /*&& movieClip.getMovie().isPlaying()*/){ float currValue = slider.getValue(); movieClip.jump(currValue); } stopSliderAdvance = false; break; default: break; } return false; } }); if (app instanceof MTApplication) { MTApplication mtApp = (MTApplication) app; mtApp.invokeLater(new Runnable() { public void run() { MTMovieClip.this.addChild(slider); } }); }else{ this.addChild(slider); } slider.setVisible(true); //Create volume slider float volSliderWidth = this.getWidthXY(TransformSpace.LOCAL)/7f; float volSliderHeight = topBarHeight - 2*sliderYPadding; this.setAnchor(PositionAnchor.UPPER_LEFT); Vector3D movieUpperLeft = this.getPosition(TransformSpace.RELATIVE_TO_PARENT); volumeSlider = new MTSlider(movieUpperLeft.x + 1.5f, movieUpperLeft.y - volSliderHeight - 1.5f, volSliderWidth, volSliderHeight, 0, 1, app); volumeSlider.getOuterShape().setFillColor(new MTColor(0, 0, 0, 80)); volumeSlider.getOuterShape().setStrokeColor(new MTColor(0, 0, 0, 80)); volumeSlider.getKnob().setFillColor(new MTColor(100, 100, 100, 80)); volumeSlider.getOuterShape().setStrokeColor(new MTColor(100, 100, 100, 80)); if (app instanceof MTApplication) { MTApplication mtApp = (MTApplication) app; mtApp.invokeLater(new Runnable() { public void run() { MTMovieClip.this.addChild(volumeSlider); } }); }else{ this.addChild(volumeSlider); } volumeSlider.setVisible(true); volumeSlider.setValue(1); volumeSlider.addPropertyChangeListener("value", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { if (movie != null){ movie.volume(((Float)e.getNewValue()).doubleValue()); } } }); } } @Override protected void onNewFrame() { super.onNewFrame(); GSMovie m = getMovie(); if (!stopSliderAdvance && m != null && slider != null){ slider.setValue(m.time()); //ONLY UPDATE the slider position WHEN NOT DRAGGING THE SLIDER } } @Override protected void setDefaultGestureActions() { } @Override public void play() { super.play(); if (slider != null){ slider.setVisible(true); } } @Override public void loop() { super.loop(); if (slider != null){ slider.setVisible(true); } } } /** * Stops and Closes the movieclip with an animation. */ public void close(){ float width = this.getWidthXY(TransformSpace.RELATIVE_TO_PARENT); Animation closeAnim = new Animation("Window Fade", new MultiPurposeInterpolator(width, 1, 350, 0.2f, 0.5f, 1), this); closeAnim.addAnimationListener(new IAnimationListener(){ public void processAnimationEvent(AnimationEvent ae) { // float delta = ae.getAnimation().getInterpolator().getCurrentStepDelta(); switch (ae.getId()) { case AnimationEvent.ANIMATION_STARTED: case AnimationEvent.ANIMATION_UPDATED: float currentVal = ae.getAnimation().getInterpolator().getCurrentValue(); setWidthXYRelativeToParent(currentVal); break; case AnimationEvent.ANIMATION_ENDED: setVisible(false); destroy(); break; default: break; }//switch }//processanimation }); closeAnim.start(); } @Override protected void destroyComponent() { super.destroyComponent(); this.movieClip.noLoop(); // if (this.movieClip.getMovie().getGplayer().isPlaying()){ //gplayer.isPlaying still hangs the program sometimes.. // if (this.movieClip.getMovie().isPlaying()){ //not as accurate.. // this.movieClip.getMovie().dispose(); //DONE IN MTVideoTexture child.. // } } public boolean isSelected() { return selected; } public void setSelected(boolean selected) { this.selected = selected; } /** * Gets the video texture. * * @return the video texture */ public MTVideoTexture getVideoTexture(){ return this.movieClip; } }