001: /*
002: *
003: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025:
026: package com.sun.mmedia;
027:
028: import java.util.*;
029: import java.lang.IllegalArgumentException;
030: import java.lang.IllegalStateException;
031: import javax.microedition.lcdui.Image;
032: import javax.microedition.lcdui.Graphics;
033: import javax.microedition.lcdui.Item;
034: import javax.microedition.lcdui.Canvas;
035: import javax.microedition.lcdui.CustomItem;
036: import javax.microedition.media.Control;
037: import javax.microedition.media.MediaException;
038: import javax.microedition.media.control.VideoControl;
039: import javax.microedition.media.PlayerListener;
040:
041: import com.sun.midp.midlet.Scheduler;
042: import com.sun.midp.midlet.MIDletSuite;
043: import com.sun.midp.configurator.Constants;
044: import com.sun.midp.security.*;
045: import com.sun.midp.log.Logging;
046: import com.sun.midp.log.LogChannels;
047:
048: /**
049: * Video direct player
050: * it implements VideoControl
051: */
052: public class DirectVideo extends DirectPlayer implements VideoControl,
053: MIDPVideoPainter, ImplicitlyTrustedClass {
054:
055: private final int SCREEN_WIDTH = nGetScreenWidth();
056: private final int SCREEN_HEIGHT = nGetScreenHeight();
057: private final int DEFAULT_WIDTH = 80;
058: private final int DEFAULT_HEIGHT = 80;
059: private final int ALPHA_COLOR = 1;
060:
061: // NOTE: You have to calibrate this value carefully
062: // If you increase this value, fake preview quality goes down but, system overhead decrease
063: // If you decrease this value, fake preview quality goes up but, system overhead increase
064: // If you set this value 0 or negative value, fake preview support still image only
065: private final static int FAKE_PREVIEW_INTERVAL = 250;
066:
067: // Canvas and item reference
068: private Canvas canvas;
069: private DVItem item;
070:
071: // original video size
072: private int sw;
073: private int sh;
074:
075: // Display position and size
076: private int dx;
077: private int dy;
078: private int dw;
079: private int dh;
080:
081: // visible?
082: private boolean visible = false;
083: private boolean hidden = false;
084: private boolean started = false;
085: private boolean locationInited = false; // Is location initialized?
086:
087: // current display mode
088: private int displayMode = -1;
089: // MMHelper to communicate with Canvas
090: private MMHelper mmh = null;
091: // Lock
092: private Object boundLock = new Object();
093:
094: /** This class has a different security domain than the MIDlet suite */
095: private static SecurityToken classSecurityToken;
096:
097: // native functions /////////////////////////////////////////////
098:
099: // Get video width
100: protected native int nGetWidth(int handle);
101:
102: // Get video height
103: protected native int nGetHeight(int handle);
104:
105: // Set display location of video
106: protected native boolean nSetLocation(int handle, int x, int y,
107: int w, int h);
108:
109: // Get snapshot
110: protected native byte[] nSnapShot(int handle, String imageType);
111:
112: // Set visible
113: protected native boolean nSetVisible(int handle, boolean visible);
114:
115: // Get screen full width
116: private native int nGetScreenWidth();
117:
118: // Get screen full height
119: private native int nGetScreenHeight();
120:
121: // Turn on or off alpha channel
122: private native int nSetAlpha(boolean on, int color);
123:
124: // member functions /////////////////////////////////////////////
125:
126: public DirectVideo() {
127: }
128:
129: /**
130: * Initializes the security token for this class, so it can
131: * perform actions that a normal MIDlet Suite cannot.
132: *
133: * @param token security token for this class.
134: */
135: public final void initSecurityToken(SecurityToken token) {
136: if (classSecurityToken != null) {
137: return;
138: }
139: classSecurityToken = token;
140: }
141:
142: /**
143: * Check snapshot permission
144: */
145: protected void checkPermission() {
146: try {
147: Scheduler scheduler = Scheduler.getScheduler();
148: MIDletSuite midletSuite = scheduler.getMIDletSuite();
149: midletSuite.checkForPermission(
150: Permissions.MM_IMAGE_CAPTURING, null);
151: } catch (InterruptedException e) {
152: throw new SecurityException(
153: "Interrupted while trying to ask the user permission");
154: }
155: }
156:
157: /**
158: * Is in clipping area?
159: */
160: private boolean isInClippingArea(Graphics g, int x, int y, int w,
161: int h) {
162: int diffx = g.getTranslateX();
163: int diffy = g.getTranslateY();
164: int clipx = g.getClipX();
165: int clipy = g.getClipY();
166: int clipw = g.getClipWidth();
167: int cliph = g.getClipHeight();
168:
169: x += diffx;
170: y += diffy;
171: clipx += diffx;
172: clipy += diffy;
173:
174: if (x < clipx)
175: return false;
176: if (y < clipy)
177: return false;
178: if (x + w > clipx + clipw)
179: return false;
180: if (y + h > clipy + cliph)
181: return false;
182:
183: if (debug) {
184: Logging
185: .report(Logging.INFORMATION, LogChannels.LC_MMAPI,
186: "isInClippingArea return true - No graphic outside of clipping area");
187: }
188:
189: return true;
190: }
191:
192: /**
193: *
194: */
195: private void setTranslatedVideoLocation(Graphics g, int x, int y,
196: int w, int h) {
197: int diffx = g.getTranslateX();
198: int diffy = g.getTranslateY();
199: int px, py, pw, ph;
200:
201: // Calcurate positions
202: // And, do a physical clipping
203: // Currently, Zoran chipset does not support negative position and exceed position
204: px = x + diffx;
205: py = y + diffy;
206: pw = w;
207: ph = h;
208:
209: if (px + pw <= 0) {
210: return;
211: }
212: if (py + ph <= 0) {
213: return;
214: }
215: if (px >= SCREEN_WIDTH) {
216: return;
217: }
218: if (py >= SCREEN_HEIGHT) {
219: return;
220: }
221: if (px < 0) {
222: pw += px;
223: px = 0;
224: }
225: if (py < 0) {
226: ph += py;
227: py = 0;
228: }
229: if (px + pw > SCREEN_WIDTH) {
230: pw = SCREEN_WIDTH - px;
231: }
232: if (py + ph > SCREEN_HEIGHT) {
233: ph = SCREEN_HEIGHT - py;
234: }
235:
236: if (hNative != 0) {
237: nSetLocation(hNative, px, py, pw, ph);
238: }
239: }
240:
241: /**
242: * Prepare direct video rendering surface
243: */
244: private void prepareVideoSurface(Graphics g, int x, int y, int w,
245: int h) {
246: if (debug) {
247: Logging.report(Logging.INFORMATION, LogChannels.LC_MMAPI,
248: "prepareVideoSurface " + x + "," + y + "," + w
249: + "," + h);
250: }
251:
252: // Turn off alpha channel
253: nSetAlpha(false, ALPHA_COLOR);
254: setTranslatedVideoLocation(g, x, y, w, h);
255:
256: // set location and size of display region
257: if (hNative != 0) {
258: nSetVisible(hNative, true);
259: }
260: }
261:
262: /**
263: * Prepare clipped preview region by using alpha channel masking
264: */
265: private void prepareClippedPreview(Graphics g, int x, int y, int w,
266: int h) {
267: if (1 == nSetAlpha(true, ALPHA_COLOR)) {
268: g.setColor(0, 0, 8); // IMPL NOTE - Consider RGB565 conversion
269: g.fillRect(x, y, w, h);
270: setTranslatedVideoLocation(g, x, y, w, h);
271: if (hNative != 0) {
272: nSetVisible(hNative, true);
273: }
274: } else {
275: if (hNative != 0) {
276: nSetVisible(hNative, false);
277: }
278: }
279: }
280:
281: /**
282: * request to repaint
283: */
284: private void repaint() {
285: if (canvas != null) {
286: canvas.repaint();
287: } else if (item != null) {
288: item.forcePaint();
289: }
290: }
291:
292: /**
293: * request to repaint canvas and wait until that processed
294: */
295: private void repaintAndWait() {
296: if (canvas != null) {
297: canvas.repaint();
298: canvas.serviceRepaints();
299: } else if (item != null) {
300: item.forcePaint();
301: }
302: }
303:
304: /**
305: * Check mode value
306: */
307: protected void checkState() {
308: if (displayMode == -1) {
309: throw new IllegalStateException(
310: "initDisplayMode not called yet");
311: }
312: }
313:
314: /**
315: * Override doGetControl
316: * return VideoControl and GUIControl
317: */
318: protected Control doGetControl(String type) {
319: Control c = super .doGetControl(type);
320:
321: if (c == null) {
322: String prefix = "javax.microedition.media.control.";
323: if (type.equals(prefix + vicName)) { // VideoControl
324: return this ;
325: } else if (type.equals(prefix + guiName)) { // GUIControl
326: return this ;
327: }
328: }
329: return c;
330: }
331:
332: /**
333: * Override doRealize
334: * Prepare soure video informations
335: */
336: protected void doRealize() throws MediaException {
337: super .doRealize();
338: sw = nGetWidth(hNative);
339: sh = nGetHeight(hNative);
340: // initialize default rendering width and height
341: if (sw <= 0)
342: dw = DEFAULT_WIDTH;
343: else
344: dw = sw;
345: if (sh <= 0)
346: dh = DEFAULT_HEIGHT;
347: else
348: dh = sh;
349: }
350:
351: protected boolean doStart() {
352: started = true;
353: repaintAndWait();
354: return super .doStart();
355: }
356:
357: protected void doStop() throws MediaException {
358: started = false;
359: super .doStop();
360: }
361:
362: /**
363: * Init display mode
364: */
365: public Object initDisplayMode(int mode, Object container) {
366: if (debug) {
367: Logging.report(Logging.INFORMATION, LogChannels.LC_MMAPI,
368: "initDisplayMode mode=" + mode + ", container="
369: + container);
370: }
371:
372: Object ret = null;
373:
374: if (displayMode != -1) {
375: throw new IllegalStateException("mode already set");
376: }
377: if (mode != USE_DIRECT_VIDEO && mode != USE_GUI_PRIMITIVE) {
378: throw new IllegalArgumentException("unsupported mode");
379: }
380: if (mode == USE_DIRECT_VIDEO && !(container instanceof Canvas)) {
381: throw new IllegalArgumentException(
382: "container needs to be a Canvas");
383: }
384: if (mode == USE_GUI_PRIMITIVE && container != null) {
385: if (!(container instanceof String)) {
386: throw new IllegalArgumentException(
387: "container not valid");
388: }
389: if (!(container.equals("javax.microedition.lcdui.Item"))) {
390: throw new IllegalArgumentException(
391: "container not valid");
392: }
393: }
394:
395: if (mode == USE_DIRECT_VIDEO) {
396: canvas = (Canvas) container;
397: if (mmh == null) {
398: mmh = MMHelper.getMMHelper();
399: if (mmh == null) {
400: throw new RuntimeException(
401: "initDisplayMode: unable to set the display mode");
402: }
403: }
404: displayMode = mode;
405: // register this direct video handler to MMH
406: // MMH used to communicate with Canvas
407: mmh.registerPlayer(canvas, this );
408: setDisplayLocation(dx, dy);
409: } else {
410: displayMode = mode;
411: item = new DVItem(null);
412: ret = (Object) item;
413: visible = true;
414: }
415:
416: return ret;
417: }
418:
419: /**
420: * Set display location
421: */
422: public void setDisplayLocation(int x, int y) {
423: if (debug) {
424: Logging.report(Logging.INFORMATION, LogChannels.LC_MMAPI,
425: "setDisplayLocation x=" + x + ",y=" + y);
426: }
427: checkState();
428: if (displayMode == USE_DIRECT_VIDEO) {
429: synchronized (boundLock) {
430: dx = x;
431: dy = y;
432: }
433: if (dw != 0 && dh != 0) {
434: repaintAndWait();
435: }
436: }
437: }
438:
439: /**
440: * Set display size
441: */
442: public void setDisplaySize(int width, int height)
443: throws MediaException {
444: if (debug) {
445: Logging.report(Logging.INFORMATION, LogChannels.LC_MMAPI,
446: "setDisplaySize w=" + width + ",h=" + height);
447: }
448:
449: boolean sizeChanged = false;
450:
451: checkState();
452: if (width < 1 || height < 1) {
453: throw new IllegalArgumentException("invalid size");
454: }
455:
456: synchronized (boundLock) {
457: if (dw != width && dh != height)
458: sizeChanged = true;
459: dw = width;
460: dh = height;
461: }
462: if (item != null) {
463: // this will raise sizeChanged event
464: // and sizeChanged shall raise paint event also
465: item.setPreferredSize(width, height);
466: }
467: repaintAndWait();
468:
469: if (sizeChanged) {
470: sendEvent(PlayerListener.SIZE_CHANGED, this );
471: }
472: }
473:
474: public int getDisplayX() {
475: return dx;
476: }
477:
478: public int getDisplayY() {
479: return dy;
480: }
481:
482: /**
483: * Get actual width of rendering
484: */
485: public int getDisplayWidth() {
486: checkState();
487: return dw;
488: }
489:
490: /**
491: * Get actual height of rendering
492: */
493: public int getDisplayHeight() {
494: checkState();
495: return dh;
496: }
497:
498: /**
499: * return source video width
500: */
501: public int getSourceWidth() {
502: return sw;
503: }
504:
505: /**
506: * return source video height
507: */
508: public int getSourceHeight() {
509: return sh;
510: }
511:
512: /**
513: * set visible or unvisible
514: */
515: public void setVisible(boolean visible) {
516: boolean old = this .visible;
517: checkState();
518: this .visible = visible;
519:
520: if (old != visible) {
521: repaintAndWait();
522: }
523:
524: if (visible == false && hNative != 0) {
525: nSetVisible(hNative, false);
526: }
527: }
528:
529: /**
530: * There is no full screen mode now
531: */
532: public void setDisplayFullScreen(boolean fullScreenMode)
533: throws MediaException {
534: checkState();
535: if (fullScreenMode) {
536: throw new MediaException("No Fullscreen mode");
537: }
538: }
539:
540: /**
541: * There is no snap shot support now
542: */
543: public byte[] getSnapshot(String imageType) throws MediaException {
544: checkState();
545: throw new MediaException("No snapshot support");
546: }
547:
548: /**
549: * called from Canvas.paint routine
550: * We have to paint direct video region on Canvas
551: * Notice: This have to be done before device painting action
552: * Zoran ESDK use mask color to draw direct video
553: */
554: public void paintVideo(Graphics g) {
555: int x, y, w, h;
556:
557: synchronized (boundLock) {
558: x = dx;
559: y = dy;
560: w = dw;
561: h = dh;
562: }
563:
564: if (debug) {
565: Logging.report(Logging.INFORMATION, LogChannels.LC_MMAPI,
566: "paintVideo x=" + x + ",y=" + y + ",w=" + w + ",h="
567: + h);
568: }
569:
570: if (hidden) {
571: prepareClippedPreview(g, x, y, w, h);
572: } else if (visible && started) {
573: if (true == isInClippingArea(g, x, y, w, h)) {
574: prepareVideoSurface(g, x, y, w, h);
575: } else {
576: int cx = g.getClipX();
577: int cy = g.getClipY();
578: int cw = g.getClipWidth();
579: int ch = g.getClipHeight();
580: g.setClip(x, y, w, h);
581: prepareClippedPreview(g, x, y, w, h);
582: g.setClip(cx, cy, cw, ch);
583: }
584: }
585: }
586:
587: /**
588: * Hide video preview (called from CanvasLFImpl)
589: */
590: public void hideVideo() {
591: if (debug) {
592: Logging.report(Logging.INFORMATION, LogChannels.LC_MMAPI,
593: "hideVideoPreview");
594: }
595: hidden = true;
596: nSetAlpha(true, ALPHA_COLOR);
597: repaint();
598: }
599:
600: /**
601: * Show video preview (called from CanvasLFImpl)
602: */
603: public void showVideo() {
604: if (debug) {
605: Logging.report(Logging.INFORMATION, LogChannels.LC_MMAPI,
606: "showVideoPreview");
607: }
608: hidden = false;
609: nSetAlpha(false, ALPHA_COLOR);
610: repaint();
611: }
612:
613: // Inner class ///////////////////////////////////////////////////////////
614:
615: /**
616: * Support USE_GUI_PRIMITIVE mode
617: */
618: class DVItem extends CustomItem {
619:
620: DVItem(String label) {
621: super (label);
622: }
623:
624: void forcePaint() {
625: repaint();
626: }
627:
628: protected void paint(Graphics g, int w, int h) {
629: if (debug) {
630: Logging.report(Logging.INFORMATION,
631: LogChannels.LC_MMAPI, "DVItem.paint visible="
632: + visible);
633: }
634:
635: // Is in hidden state, then just draw fake preview
636: if (hidden) {
637: prepareClippedPreview(g, 0, 0, w, h);
638: // Is out of hidden state, then check about clipping regions and
639: // determind what to show
640: } else if (visible) {
641: if (true == isInClippingArea(g, 0, 0, w, h)) {
642: // Prepare video preview
643: prepareVideoSurface(g, 0, 0, w, h);
644: } else {
645: prepareClippedPreview(g, 0, 0, w, h);
646: }
647: }
648: }
649:
650: protected int getMinContentWidth() {
651: return 1;
652: }
653:
654: protected int getMinContentHeight() {
655: return 1;
656: }
657:
658: protected int getPrefContentWidth(int height) {
659: return dw;
660: }
661:
662: protected int getPrefContentHeight(int width) {
663: return dh;
664: }
665:
666: protected void sizeChanged(int w, int h) {
667: synchronized (boundLock) {
668: dw = w;
669: dh = h;
670: }
671: repaint();
672: }
673:
674: // Now this function used to control visible state of direct video preview
675: // Called from MIDPWindow class
676: protected void showNotify() {
677: if (debug) {
678: Logging.report(Logging.INFORMATION,
679: LogChannels.LC_MMAPI, "showNotify");
680: }
681: hidden = false;
682: repaint();
683: }
684:
685: // Now this function used to control visible state of direct video preview
686: // Called from MIDPWindow class
687: protected void hideNotify() {
688: if (debug) {
689: Logging.report(Logging.INFORMATION,
690: LogChannels.LC_MMAPI, "hideNotify");
691: }
692: hidden = true;
693: repaint();
694: }
695: }
696:
697: }
|