001: /*
002: * Copyright (c) 2007, Sun Microsystems, Inc.
003: *
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * * Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: * * Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: * * Neither the name of Sun Microsystems, Inc. nor the names of its
017: * contributors may be used to endorse or promote products derived
018: * from this software without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
023: * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
024: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031: */
032: package example.mmademo;
033:
034: import javax.microedition.midlet.*;
035: import javax.microedition.lcdui.*;
036: import javax.microedition.media.*;
037: import javax.microedition.media.control.*;
038: import java.io.*;
039:
040: /**
041: * Play/Capture Video in a Canvas
042: *
043: */
044: public class VideoCanvas extends Canvas implements Runnable,
045: CommandListener, PlayerListener {
046:
047: private static Player player = null;
048: private static boolean isCapturePlayer;
049: private int idx = 0;
050: private boolean fsmode = false;
051: private Display parentDisplay;
052: private long duration;
053: private long lastTime = -1;
054: private Thread sliderThread;
055: private Command backCommand = new Command("Back", Command.BACK, 1);
056: private Command playCommand = new Command("Play", Command.ITEM, 1);
057: private Command snapCommand = new Command("Snapshot", Command.ITEM,
058: 1);
059: private Command pauseCommand = new Command("Pause", Command.ITEM,
060: 10);
061: private VolumeControl vc;
062: private RateControl rc;
063: private VideoControl vidc;
064: private FramePositioningControl fpc;
065: private int CB_Y = 0; // Control Bar Y location
066: private static final int CB_H = 8; // Control Bar Height
067: private static final int TH_W = 6; // Slider Thumb Width
068: private static final int VB_W = 10; // Video Border Width
069: private static final int VB_H = 8; // Video Border Height
070:
071: private boolean vis = true;
072:
073: private int canvasW; // Canvas Width
074: private int canvasH; // Canvas Height
075:
076: private int videoW; // Video Display Area Width
077: private int videoH; // Video Display Area Height
078:
079: // pause/resume support
080: private boolean suspended = false;
081: private boolean restartOnResume = false;
082: private long restartMediaTime;
083:
084: public VideoCanvas(Display parentDisplay) {
085: this .idx = 0;
086: this .parentDisplay = parentDisplay;
087: canvasW = getWidth();
088: canvasH = getHeight();
089: CB_Y = canvasH - CB_H - VB_H - 20;
090: videoW = canvasW - VB_W * 2;
091: videoH = CB_Y - VB_H * 2;
092: initialize();
093: }
094:
095: public void paint(Graphics g) {
096: // Only video player uses the screen in fullscreen mode
097: if (fsmode)
098: return;
099:
100: // Draw the GUI and media time slider
101: g.setColor(0x9090E0);
102: g.fillRect(0, 0, canvasW, canvasH);
103: g.setColor(0x202050);
104: //Video Border
105: g.drawLine(VB_W - 1, VB_H - 1, videoW + VB_W - 1, VB_H - 1);
106: g.drawLine(VB_W - 1, VB_H - 1, VB_W - 1, videoH + VB_H - 1);
107: //Control Bar
108: if (sliderThread != null) {
109: g.drawLine(VB_W - 1, CB_Y, videoW + VB_W - 1, CB_Y);
110: g.drawLine(VB_W - 1, CB_Y, VB_W - 1, CB_Y + CB_H);
111: }
112: g.setColor(0xD0D0FF);
113: //Video Border
114: g.drawLine(videoW + VB_W, VB_H, videoW + VB_W, videoH + VB_H);
115: g.drawLine(videoW + VB_W, videoH + VB_H, VB_W, videoH + VB_H);
116: //Control Bar
117: if (sliderThread != null) {
118: g.drawLine(videoW + VB_W, CB_Y + 1, videoW + VB_W, CB_Y
119: + CB_H + 1);
120: g.drawLine(videoW + VB_W, CB_Y + CB_H + 1, VB_H, CB_Y
121: + CB_H + 1);
122: }
123: if (sliderThread != null) {
124: int p = time2pix(lastTime);
125: g.drawLine(VB_W + p, CB_Y + 1, VB_W + p, CB_Y + CB_H);
126: g.drawLine(VB_W + p, CB_Y + 1, VB_W + p + TH_W - 1,
127: CB_Y + 1);
128: g.setColor(0x202050);
129: g.drawLine(VB_W + p + 1, CB_Y + CB_H, VB_W + p + TH_W, CB_Y
130: + CB_H);
131: g.drawLine(VB_W + p + TH_W, CB_Y + CB_H, VB_W + p + TH_W,
132: CB_Y + 1);
133: g.setColor(0xA0A0FF);
134: g.fillRect(VB_W + p + 1, CB_Y + 2, TH_W - 2, CB_H - 2);
135: g.setColor(0);
136: g.drawString("00:00", VB_W, CB_Y + CB_H + 4, Graphics.TOP
137: | Graphics.LEFT);
138: g.drawString(time2String(lastTime) + "/"
139: + time2String(duration), VB_W + videoW, CB_Y + CB_H
140: + 4, Graphics.TOP | Graphics.RIGHT);
141: }
142: }
143:
144: private void initialize() {
145: addCommand(backCommand);
146: addCommand(snapCommand);
147: setCommandListener(this );
148: }
149:
150: private int time2pix(long time) {
151: int t2p = (int) ((time * (videoW - 1 - TH_W)) / duration);
152: return t2p;
153: }
154:
155: private String time2String(long time) {
156: time = time / 1000000;
157: String strTime = "" + (time % 10);
158: time = time / 10;
159: strTime = (time % 6) + strTime;
160: time = time / 6;
161: strTime = (time % 10) + ":" + strTime;
162: time = time / 10;
163: strTime = (time % 6) + strTime;
164: time = time / 6;
165: return strTime;
166: }
167:
168: public void run() {
169: while (player != null && player.getState() != Player.CLOSED) {
170: try {
171: long time = player.getMediaTime();
172: if (time2pix(time) != time2pix(lastTime)) {
173: lastTime = time;
174: repaint(VB_W - 2, CB_Y, videoW + 2, CB_H + 2 + 20);
175: }
176: // sleep for 250 millis
177: // if suspended, sleep until the MIDlet is restarted
178: do {
179: Thread.sleep(250);
180: } while (player != null && suspended);
181: } catch (Exception e) {
182: System.err.println("In run(): " + e);
183: //System.out.println("DEBUG: GOT EXCEPTION in VideoCanvas.run()!");
184: //e.printStackTrace();
185: break;
186: }
187: }
188: }
189:
190: /*
191: * Respond to commands, including back
192: */
193: public void commandAction(Command c, Displayable s) {
194: //try {
195: if (s == this ) {
196: if (c == backCommand) {
197: close();
198: parentDisplay.setCurrent(VideoTest.getList());
199: } else if (vidc != null && c == snapCommand) {
200: doSnapshot();
201: } else if (vidc == null && c == pauseCommand) {
202: removeCommand(pauseCommand);
203: addCommand(playCommand);
204: pause();
205: } else if (vidc == null && c == playCommand) {
206: start();
207: removeCommand(playCommand);
208: addCommand(pauseCommand);
209: }
210: }
211: //} catch (Exception e) {
212: // System.out.println("DEBUG: GOT EXCEPTION in VideoCanvas.commandAction("+c.toString()+","+s.toString()+")!");
213: // e.printStackTrace();
214: //}
215: }
216:
217: /*
218: * Process clicking in slider region
219: */
220: protected void pointerPressed(int x, int y) {
221: if (duration == Player.TIME_UNKNOWN || isCapturePlayer)
222: return;
223: if (y >= CB_Y && y < CB_Y + CB_H) {
224: if (x >= VB_W - 2 && x <= VB_W - 2 + videoW) {
225: x = x - (VB_W + 1);
226: if (x < 0)
227: x = 0;
228: if (x > videoW - TH_W)
229: x = videoW - TH_W;
230: long time = (duration * x) / (videoW - TH_W);
231: try {
232: player.setMediaTime(time);
233: } catch (MediaException me) {
234: System.out.println(me);
235: }
236: }
237: }
238: }
239:
240: protected void pointerDragged(int x, int y) {
241: pointerPressed(x, y);
242: }
243:
244: public void open(String url) {
245: try {
246: synchronized (this ) {
247: if (player == null) {
248: if (url.startsWith("resource:")) {
249: InputStream ins = getClass()
250: .getResourceAsStream(url.substring(9));
251: String ct = Utils.guessContentType(url);
252: player = Manager.createPlayer(ins, ct);
253: } else {
254: player = Manager.createPlayer(url);
255: }
256: player.addPlayerListener(this );
257: isCapturePlayer = url.startsWith("capture:");
258: }
259: }
260: player.realize();
261: if ((vidc = (VideoControl) player
262: .getControl("VideoControl")) != null) {
263: vidc.initDisplayMode(VideoControl.USE_DIRECT_VIDEO,
264: this );
265: int frameW = vidc.getSourceWidth();
266: int frameH = vidc.getSourceHeight();
267: // Clip the video to available area
268: if (frameW > videoW)
269: frameW = videoW;
270: if (frameH > videoH)
271: frameH = videoH;
272: int frameX = (videoW - frameW) / 2 + VB_W;
273: int frameY = (videoH - frameH) / 2 + VB_H;
274: vidc.setDisplayLocation(frameX, frameY);
275: vidc.setDisplaySize(frameW, frameH);
276: vidc.setVisible(true);
277: }
278: Control[] controls = player.getControls();
279:
280: for (int i = 0; i < controls.length; i++) {
281: if (controls[i] instanceof VolumeControl)
282: vc = (VolumeControl) controls[i];
283: if (controls[i] instanceof RateControl)
284: rc = (RateControl) controls[i];
285: if (controls[i] instanceof FramePositioningControl)
286: fpc = (FramePositioningControl) controls[i];
287: }
288: player.prefetch();
289: if (vidc == null)
290: addCommand(pauseCommand);
291: } catch (Exception e) {
292: System.err.println(e);
293: close();
294: }
295: }
296:
297: public void start() {
298: if (player == null)
299: return;
300: try {
301: duration = player.getDuration();
302: player.start();
303: if (duration != Player.TIME_UNKNOWN) {
304: sliderThread = new Thread(this );
305: sliderThread.start();
306: }
307: } catch (Exception e) {
308: System.err.println(e);
309: close();
310: }
311: }
312:
313: public void close() {
314: synchronized (this ) {
315: pause();
316: if (player != null) {
317: player.close();
318: player = null;
319: }
320: }
321: VideoTest.getInstance().nullPlayer();
322: }
323:
324: public void pause() {
325: if (player != null) {
326: try {
327: player.stop();
328: } catch (MediaException me) {
329: System.err.println(me);
330: }
331: }
332: }
333:
334: public void playerUpdate(Player plyr, String evt, Object evtData) {
335: //try {
336: if (evt == END_OF_MEDIA) {
337: try {
338: player.setMediaTime(0);
339: player.start();
340: } catch (MediaException me) {
341: System.err.println(me);
342: }
343: }
344: //} catch (Exception e) {
345: // System.out.println("DEBUG: GOT EXCEPTION in VideoCanvas.playerUpdate("+evt.toString()+")!");
346: // e.printStackTrace();
347: //}
348: }
349:
350: private void doSnapshot() {
351: new SnapshotThread().start();
352: }
353:
354: class SnapshotThread extends Thread {
355: final Canvas tThis = (Canvas) VideoCanvas.this ;
356:
357: public void run() {
358: try {
359: byte[] snap = vidc.getSnapshot("encoding=jpeg");
360: if (snap != null) {
361: Image im = Image.createImage(snap, 0, snap.length);
362: Alert al = new Alert("Snapshot", "Here's the snap",
363: im, AlertType.INFO);
364: al.setTimeout(2000);
365: parentDisplay.setCurrent(al, tThis);
366: }
367: } catch (MediaException me) {
368: System.err.println(me);
369: //} catch (Exception e) {
370: // System.out.println("DEBUG: GOT EXCEPTION in VideoCanvas.SnapshotThread.run()!");
371: // e.printStackTrace();
372: }
373: }
374: }
375:
376: public synchronized void stopVideoCanvas() {
377: // stop & deallocate
378: player.deallocate();
379: }
380:
381: /* Handle the different keys pressed on the phone GUI emulator */
382: public void keyPressed(int keyCode) {
383: int cr, cv;
384: switch (keyCode) {
385: case Canvas.KEY_NUM4:
386: cr = rc.getRate();
387: cr -= 10000;
388: cr = rc.setRate(cr);
389: break;
390: case Canvas.KEY_NUM6:
391: cr = rc.getRate();
392: cr += 10000;
393: cr = rc.setRate(cr);
394: break;
395: case Canvas.KEY_STAR:
396: if (vc != null) {
397: cv = vc.getLevel();
398: cv -= 10;
399: cv = vc.setLevel(cv);
400: }
401: break;
402: case Canvas.KEY_NUM0:
403: if (vc != null) {
404: vc.setMute(!vc.isMuted());
405: }
406: break;
407: case Canvas.KEY_POUND:
408: if (vc != null) {
409: cv = vc.getLevel();
410: cv += 10;
411: cv = vc.setLevel(cv);
412: }
413: break;
414: case Canvas.KEY_NUM7:
415: if (fpc != null) {
416: fpc.skip(-1);
417: }
418: break;
419: case Canvas.KEY_NUM5:
420: try {
421: player.stop();
422: if (!isCapturePlayer) {
423: player.setMediaTime(0);
424: }
425: player.deallocate();
426: } catch (MediaException me) {
427: System.err.println(me);
428: }
429: break;
430: case Canvas.KEY_NUM9:
431: if (fpc != null) {
432: fpc.skip(1);
433: }
434: break;
435: case Canvas.KEY_NUM2:
436: try {
437: if (player.getState() == Player.STARTED)
438: player.stop();
439: else
440: player.start();
441: } catch (Exception e) {
442: System.err.println(e);
443: }
444: break;
445: case Canvas.KEY_NUM8:
446: try {
447: // Full screen
448: if (vidc != null)
449: vidc.setDisplayFullScreen(fsmode = !fsmode);
450:
451: repaint();
452: } catch (MediaException me) {
453: System.err.println(me);
454: }
455: break;
456: case Canvas.KEY_NUM1:
457: case Canvas.KEY_NUM3:
458: if (!isCapturePlayer) {
459: long mTime = player.getMediaTime();
460: long duration = player.getDuration();
461: if (duration == Player.TIME_UNKNOWN
462: || mTime == Player.TIME_UNKNOWN)
463: return;
464: try {
465: if (keyCode == Canvas.KEY_NUM3) {
466: // Jump forward 10%
467: mTime += duration / 10;
468: if (mTime > duration)
469: mTime = duration;
470: player.setMediaTime(mTime);
471: } else if (keyCode == Canvas.KEY_NUM1) {
472: mTime -= duration / 10;
473: if (mTime < 0)
474: mTime = 0;
475: player.setMediaTime(mTime);
476: } else
477: return;
478: } catch (MediaException me) {
479: System.err.println(me);
480: }
481: }
482: break;
483: /* Code to move the video around using cursor keys */
484: default:
485: int game = getGameAction(keyCode);
486: int x = vidc.getDisplayX();
487: int y = vidc.getDisplayY();
488: if (game == UP)
489: vidc.setDisplayLocation(x, y - 10);
490: else if (game == DOWN)
491: vidc.setDisplayLocation(x, y + 10);
492: else if (game == LEFT)
493: vidc.setDisplayLocation(x - 10, y);
494: else if (game == RIGHT)
495: vidc.setDisplayLocation(x + 10, y);
496: else if (game == FIRE)
497: vidc.setVisible(vis = !vis);
498: repaint();
499: break;
500: }
501: }
502:
503: /**
504: * Deallocate the player and the display thread.
505: * Some VM's may stop players and threads
506: * on their own, but for consistent user
507: * experience, it's a good idea to explicitely
508: * stop and start resources such as player
509: * and threads.
510: */
511: public synchronized void pauseApp() {
512: suspended = true;
513: if (player != null && player.getState() >= Player.STARTED) {
514: // player was playing, so stop it and release resources.
515: if (!isCapturePlayer) {
516: restartMediaTime = player.getMediaTime();
517: }
518: player.deallocate();
519: // make sure to restart upon resume
520: restartOnResume = true;
521: } else {
522: restartOnResume = false;
523: }
524: }
525:
526: /**
527: * If the player was playing when the MIDlet was paused,
528: * then the player will be restarted here.
529: */
530: public synchronized void startApp() {
531: suspended = false;
532: if (player != null && restartOnResume) {
533: try {
534: player.prefetch();
535: if (!isCapturePlayer) {
536: try {
537: player.setMediaTime(restartMediaTime);
538: } catch (MediaException me) {
539: System.err.println(me);
540: }
541: }
542: player.start();
543: } catch (MediaException me) {
544: System.err.println(me);
545: }
546: }
547: restartOnResume = false;
548: }
549: }
|