001: /*
002: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License version
007: * 2 only, as published by the Free Software Foundation.
008: *
009: * This program is distributed in the hope that it will be useful, but
010: * WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * General Public License version 2 for more details (a copy is
013: * included at /legal/license.txt).
014: *
015: * You should have received a copy of the GNU General Public License
016: * version 2 along with this work; if not, write to the Free Software
017: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
018: * 02110-1301 USA
019: *
020: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
021: * Clara, CA 95054 or visit www.sun.com if you need additional
022: * information or have any questions.
023: */
024: package com.sun.mmedia;
025:
026: import javax.microedition.lcdui.Image;
027: import javax.microedition.lcdui.Canvas;
028: import javax.microedition.lcdui.Graphics;
029:
030: import javax.microedition.media.Control;
031: import javax.microedition.media.Player;
032: import javax.microedition.media.PlayerListener;
033: import javax.microedition.media.MediaException;
034: import javax.microedition.media.control.VideoControl;
035:
036: import com.sun.mmedia.PermissionAccessor;
037:
038: import com.sun.mmedia.protocol.LocatorParser;
039:
040: //import com.sun.mmedia.ImageEncoder;
041:
042: import com.sun.mmedia.MMCustomItem;
043:
044: /**
045: * VideoControl implementation for MIDP
046: */
047: public final class MIDPVideoRenderer extends VideoRenderer implements
048: VideoControl, MIDPVideoPainter {
049:
050: /** If the application requests an Item */
051: private MMItem mmItem;
052: /** If the application requests to draw in a Canvas */
053: private Canvas canvas;
054: /** Full screen mode flag */
055: private boolean fsmode;
056: /** Is the player closed */
057: private boolean closed;
058: /** The display mode */
059: private int mode = -1;
060: /** Container visible flag. True if the Canvas is visible */
061: private boolean cvis;
062: /** Application specified visibility flag. True if setVisible(true) */
063: private boolean pvis;
064: /** Player which is being controlled */
065: private BasicPlayer player;
066:
067: /** Display X */
068: private int dx, tmpdx;
069: /** Display Y */
070: private int dy, tmpdy;
071: /** Display Width */
072: private int dw, tmpdw;
073: /** Display Height */
074: private int dh, tmpdh;
075:
076: /** Source width */
077: private int videoWidth;
078: /** Source height */
079: private int videoHeight;
080:
081: /** Storage for the snapshot */
082: private byte[] tempSnapData;
083:
084: /** To check the frame rate */
085: private static final boolean TRACE_FRAMERATE = false;
086: /** To check the frame rate */
087: private int frameCount;
088: /** To check the frame rate */
089: private long frameStartTime = 0;
090:
091: private static final String UNSUP_PARAMS = "Unsupported parameters";
092:
093: /** used to protect dx, dy, dw, dh set & read */
094: private Object dispBoundsLock = new Object();
095:
096: /****************************************************************
097: * VideoControl implementation
098: ****************************************************************/
099:
100: MIDPVideoRenderer(Player p, int sourceWidth, int sourceHeight) {
101: setSourceSize(sourceWidth, sourceHeight);
102: if (p instanceof BasicPlayer) {
103: this .player = (BasicPlayer) p;
104: } else {
105: System.err
106: .println("video renderer can't work with Players of this class: "
107: + p.toString());
108: }
109: }
110:
111: void setSourceSize(int sourceWidth, int sourceHeight) {
112:
113: videoWidth = sourceWidth;
114: videoHeight = sourceHeight;
115:
116: // Default display width and height
117: synchronized (dispBoundsLock) {
118: dw = videoWidth;
119: dh = videoHeight;
120: }
121: }
122:
123: private void checkState() {
124: if (mode == -1)
125: throw new IllegalStateException(
126: "initDisplayMode not called yet");
127: }
128:
129: public Object initDisplayMode(int mode, Object container) {
130: if (this .mode != -1)
131: throw new IllegalStateException("mode is already set");
132:
133: if (mode == USE_DIRECT_VIDEO) {
134: if (!(container instanceof Canvas))
135: throw new IllegalArgumentException(
136: "container needs to be a Canvas for USE_DIRECT_VIDEO mode");
137:
138: if (mmh == null) {
139: mmh = MMHelper.getMMHelper();
140: if (mmh == null)
141: throw new IllegalArgumentException(
142: "unable to set USE_DIRECT_VIDEO mode");
143: }
144:
145: this .mode = mode;
146: fsmode = false;
147: cvis = true;
148: canvas = (Canvas) container;
149: mmh.registerPlayer(canvas, this );
150: setVisible(false); // By default video is not shown in USE_DIRECT_VIDEO mode
151: return null;
152:
153: } else if (mode == USE_GUI_PRIMITIVE) {
154: if (container != null
155: && (!(container instanceof String) || !(container
156: .equals("javax.microedition.lcdui.Item"))))
157: throw new IllegalArgumentException(
158: "container needs to be a javax.microedition.lcdui.Item for USE_GUI_PRIMITIVE mode");
159:
160: this .mode = mode;
161: fsmode = false;
162: cvis = true;
163: mmItem = new MMItem();
164: setVisible(true);
165: return mmItem;
166:
167: } else {
168: throw new IllegalArgumentException("unsupported mode");
169: }
170: }
171:
172: public void setDisplayLocation(int x, int y) {
173: checkState();
174: // Applicable only in USE_DIRECT_VIDEO mode
175: if (mode == USE_DIRECT_VIDEO) {
176: if (fsmode) { // Just store location in fullscreen mode
177: synchronized (dispBoundsLock) {
178: tmpdx = x;
179: tmpdy = y;
180: }
181: } else {
182: synchronized (dispBoundsLock) {
183: dx = x;
184: dy = y;
185: }
186: if (pvis && cvis)
187: canvas.repaint();
188: }
189: }
190: }
191:
192: public int getDisplayX() {
193: return dx;
194: }
195:
196: public int getDisplayY() {
197: return dy;
198: }
199:
200: /**
201: * Check for the image snapshot permission.
202: *
203: * @exception SecurityException if the permission is not
204: * allowed by this token
205: */
206: private static void checkPermission() throws SecurityException {
207: PermissionAccessor
208: .checkPermissions(PermissionAccessor.PERMISSION_VIDEO_SNAPSHOT);
209: }
210:
211: public void setVisible(boolean visible) {
212: checkState();
213: pvis = visible;
214: if (canvas != null) // USE_DIRECT_VIDEO
215: canvas.repaint();
216: else if (mmItem != null) // USE_GUI_PRIMITIVE
217: mmItem.forcePaint(null);
218: }
219:
220: public void setDisplaySize(int width, int height)
221: throws javax.microedition.media.MediaException {
222: checkState();
223: if (width < 1 || height < 1)
224: throw new IllegalArgumentException("Invalid size");
225:
226: boolean sizeChanged = (dw != width || dh != height);
227:
228: if (fsmode) { // Just store sizes in fullscreen mode
229: synchronized (dispBoundsLock) {
230: tmpdw = width;
231: tmpdh = height;
232: }
233: } else {
234: synchronized (dispBoundsLock) {
235: dw = width;
236: dh = height;
237: }
238: if (pvis)
239: if (mmItem != null)
240: mmItem.forcePaint(null);
241: else if (cvis)
242: canvas.repaint();
243: }
244: // Makes sense only if NOT in Full Screen mode
245: if (sizeChanged && !fsmode)
246: player.sendEvent(PlayerListener.SIZE_CHANGED, this );
247: }
248:
249: public void setDisplayFullScreen(boolean fullScreenMode)
250: throws javax.microedition.media.MediaException {
251: checkState();
252: if (fsmode != fullScreenMode) {
253: fsmode = fullScreenMode;
254: if (fsmode) { //switching from Normal to Full Screen
255: synchronized (dispBoundsLock) {
256: tmpdx = dx;
257: tmpdy = dy;
258: tmpdw = dw;
259: tmpdh = dh;
260: }
261: if (mode == USE_DIRECT_VIDEO) {
262: canvas.setFullScreenMode(true);
263: } else {
264: canvas = mmItem.toFullScreen(this , this );
265: if (canvas == null) {
266: // No owner or no display - thus invisible
267: // Do nothing, but simulate fullscreen (lock sizes - for compliance)
268: return;
269: }
270: }
271: synchronized (dispBoundsLock) {
272: dx = 0;
273: dy = 0;
274:
275: // Keep aspect ratio
276: int scrw = canvas.getWidth();
277: int scrh = canvas.getHeight();
278: dw = scrh * videoWidth / videoHeight;
279: if (dw > scrw) {
280: dw = scrw;
281: dh = scrw * videoHeight / videoWidth;
282: dy = (scrh - dh) / 2;
283: } else {
284: dh = scrh;
285: dx = (scrw - dw) / 2;
286: }
287: }
288: if (cvis)
289: canvas.repaint();
290:
291: } else { //switching from Full to Normal Screen
292: synchronized (dispBoundsLock) {
293: dx = tmpdx;
294: dy = tmpdy;
295: dw = tmpdw;
296: dh = tmpdh;
297: }
298: if (mode == USE_DIRECT_VIDEO) {
299: canvas.setFullScreenMode(false);
300: if (pvis && cvis)
301: canvas.repaint();
302: } else {
303: mmItem.toNormal();
304: canvas = null;
305: if (pvis)
306: mmItem.forcePaint(null);
307: }
308: }
309: player.sendEvent(PlayerListener.SIZE_CHANGED, this );
310: }
311: }
312:
313: public int getDisplayWidth() {
314: checkState();
315: return dw;
316: }
317:
318: public int getDisplayHeight() {
319: checkState();
320: return dh;
321: }
322:
323: public int getSourceWidth() {
324: return videoWidth;
325: }
326:
327: public int getSourceHeight() {
328: return videoHeight;
329: }
330:
331: public byte[] getSnapshot(String imageType) throws MediaException,
332: SecurityException {
333: throw new MediaException("Not supported");
334: }
335:
336: private int tryParam(String tok, String prop, int def) {
337: if (tok.startsWith(prop)) {
338: tok = tok.substring(prop.length(), tok.length());
339: try {
340: return Integer.parseInt(tok);
341: } catch (NumberFormatException nfe) {
342: }
343: }
344: return def;
345: }
346:
347: synchronized public void close() {
348: if (!closed && canvas != null)
349: mmh.unregisterPlayer(canvas, this );
350: rgbData = null;
351: scaledRGB = null;
352: pngData = null;
353: closed = true;
354: }
355:
356: /****************************************************************
357: * Rendering interface
358: ****************************************************************/
359:
360: // Frame types
361: public static final int RGB565 = 1; // short []
362: public static final int RGB888 = 2; // byte []
363: public static final int XRGB888 = 3; // int []
364: public static final int XBGR888 = 4; // int []
365: public static final int RGBX888 = 5; // int []
366: public static final int YUV420_PLANAR = 6; // byte []
367: public static final int YUV422_PLANAR = 7; // byte []
368: public static final int YUYV = 8; // byte []
369: public static final int UYVY = 9; // byte []
370: public static final int YVYU = 10; // byte []
371: public static final int NATIVE_RENDER = 128; // to be ORed with above
372: public static final int USE_ALPHA = 256;
373:
374: int rgbMode;
375: int pWidth;
376: int pHeight;
377: int[] rgbData;
378: int[] scaledRGB;
379: byte[] pngData;
380: int pngDataLength;
381: boolean nativeRender;
382: boolean useAlpha;
383: private Image image;
384: private MMHelper mmh = null;
385:
386: public Control getVideoControl() {
387: return (VideoControl) this ;
388: }
389:
390: public void initRendering(int mode, int width, int height) {
391: rgbMode = mode & 0x7F; // mask out NATIVE_RENDER
392: nativeRender = (mode & NATIVE_RENDER) > 0;
393: useAlpha = (mode & USE_ALPHA) > 0;
394: pWidth = width;
395: pHeight = height;
396: }
397:
398: void setMode(int mode) {
399: rgbMode = mode & 0x7F;
400: nativeRender = (mode >= 128);
401: }
402:
403: int getPreferredRGBMode() {
404: return RGB888;
405: }
406:
407: /**
408: * Public render method
409: */
410: public void render(int[] data) {
411: render((Object) data);
412: }
413:
414: /**
415: * Renders the data to the screen at the component's location
416: * and size, if component is visible.
417: * Returns true if displayed, false if not.
418: */
419: synchronized boolean render(Object data) {
420: if (data == null)
421: return false;
422: if (data instanceof int[])
423: update((int[]) data);
424: else
425: return false;
426:
427: return true;
428: }
429:
430: synchronized boolean renderImage(byte[] imageData, int imageLength) {
431: // Keep these values, in case snapshot is requested
432: pngData = imageData;
433: pngDataLength = imageLength;
434:
435: if (!pvis)
436: return false;
437:
438: if (canvas != null) {
439: if (cvis)
440: canvas.repaint(dx, dy, dw, dh);
441: } else if (mmItem != null) {
442: mmItem.renderImage(imageData, imageLength);
443: }
444: return true;
445: }
446:
447: private void update(int[] frame) {
448: if (rgbMode != XBGR888)
449: return;
450:
451: rgbData = frame;
452:
453: if (!pvis)
454: return;
455:
456: if (canvas != null) {
457: if (cvis) {
458: canvas.repaint(dx, dy, dw, dh);
459: }
460: } else if (mmItem != null) {
461: mmItem.forcePaint(frame);
462: }
463: }
464:
465: /**
466: * Scales an input rgb image to the destination size.
467: */
468: private int[] scaleToDest(int[] source) {
469: int ldw = 0;
470: int ldh = 0;
471: synchronized (dispBoundsLock) {
472: ldw = dw;
473: ldh = dh;
474: }
475: synchronized (this ) { // To avoid interference with close()
476: if (scaledRGB == null || scaledRGB.length < ldw * ldh)
477: scaledRGB = new int[ldw * ldh];
478: // Scale using nearest neighbor
479: int dp = 0;
480: for (int y = 0; y < ldh; y++) {
481: for (int x = 0; x < ldw; x++) {
482: scaledRGB[dp++] = source[((y * videoHeight) / ldh)
483: * videoWidth + ((x * videoWidth) / ldw)];
484: }
485: }
486: return scaledRGB;
487: }
488: }
489:
490: /**
491: * Scale an image to the destination size. This first gets the
492: * pixels from the image and then uses the other scaleToDist()
493: * to do the scaling.
494: */
495: private int[] scaleToDest(Image img) {
496: if (rgbData == null)
497: rgbData = new int[videoWidth * videoHeight];
498: int width = img.getWidth();
499: int height = img.getHeight();
500: img.getRGB(rgbData, 0, videoWidth, 0, 0, width, height);
501: return scaleToDest(rgbData);
502: }
503:
504: /**
505: * Paint video into canvas - in USE_DIRECT_VIDEO mode
506: */
507: public void paintVideo(Graphics g) {
508: // Don't paint if Canvas visible flag is false
509: if (!pvis || !cvis)
510: return;
511:
512: // Save the clip region
513: int cx = g.getClipX();
514: int cy = g.getClipY();
515: int cw = g.getClipWidth();
516: int ch = g.getClipHeight();
517: // Change the clip to clip the video area
518: g.clipRect(dx, dy, dw, dh);
519:
520: // Check if its within our bounds
521: if (g.getClipWidth() > 0 && g.getClipHeight() > 0 && pvis) {
522: int w = dw, h = dh;
523: if (w > videoWidth)
524: w = videoWidth;
525: if (h > videoHeight)
526: h = videoHeight;
527: try {
528: synchronized (this ) {
529: if (pngData != null) {
530: if (image != null) {
531:
532: }
533: image = Image.createImage(pngData, 0,
534: pngDataLength);
535: // We're rendering an image
536: if (dw != videoWidth || dh != videoHeight) {
537: // Scale first and display
538: int[] scaledRGB = scaleToDest(image);
539: g.drawRGB(scaledRGB, 0, dw, dx, dy, dw, dh,
540: useAlpha);
541: } else {
542: // No scaling
543: g.drawImage(image, dx, dy, Graphics.LEFT
544: | Graphics.TOP);
545: }
546: } else if (rgbData != null) {
547: // We're rendering an RGB array
548: if (dw != videoWidth || dh != videoHeight) {
549: // Scale first and display
550: int[] scaledRGB = scaleToDest(rgbData);
551: g.drawRGB(scaledRGB, 0, dw, dx, dy, dw, dh,
552: useAlpha);
553: } else {
554: // No scaling
555: g.drawRGB(rgbData, 0, videoWidth, dx, dy,
556: w, h, useAlpha);
557: }
558: }
559: }
560: } finally {
561: // Revert the clip region
562: g.setClip(cx, cy, cw, ch);
563: }
564: } else {
565: g.setClip(cx, cy, cw, ch);
566: }
567: if (TRACE_FRAMERATE) {
568: if (frameStartTime == 0) {
569: frameStartTime = System.currentTimeMillis();
570: } else {
571: frameCount++;
572: if ((frameCount % 30) == 0) {
573: int frameRate = (int) ((frameCount * 1000) / (System
574: .currentTimeMillis()
575: - frameStartTime + 1));
576: //System.err.println("Frame Rate = " + frameRate);
577: }
578: }
579: }
580: }
581:
582: /**
583: * Enable/disable rendering for canvas (USE_DIRECT_VIDEO mode)
584: */
585: public void showVideo() {
586: if (canvas != null && !cvis) {
587: cvis = true;
588: canvas.repaint();
589: }
590: }
591:
592: public void hideVideo() {
593: if (canvas != null && cvis) {
594: cvis = false;
595: canvas.repaint();
596: }
597: }
598:
599: /****************************************************************
600: * MMItem (CustomItem) - USE_GUI_PRIMITIVE mode
601: ****************************************************************/
602:
603: final class MMItem extends MMCustomItem {
604:
605: int ody, odh, odw;
606: int[] frame;
607: Image image;
608: Object imageLock = new Object();
609:
610: public MMItem() {
611: super ("");
612: }
613:
614: void forcePaint(int[] frame) {
615: if (frame != null)
616: this .frame = frame;
617: else
618: invalidate();
619: repaint();
620: }
621:
622: void renderImage(byte[] imageData, int imageLength) {
623: synchronized (imageLock) {
624: image = Image.createImage(imageData, 0, imageLength);
625: }
626: repaint();
627: }
628:
629: protected void paint(Graphics g, int w, int h) {
630: // Don't paint if VideoControl visible flag is false
631: if (!pvis)
632: return;
633:
634: if (frame != null) {
635: if (dw != videoWidth || dh != videoHeight) {
636: // Scale first
637: int[] scaledRGB = scaleToDest(frame);
638: g.drawRGB(scaledRGB, 0, dw, 0, 0, dw, dh, useAlpha);
639: } else {
640: // No scaling
641: g.drawRGB(frame, 0, videoWidth, 0, 0, videoWidth,
642: videoHeight, useAlpha);
643: }
644: } else {
645: synchronized (imageLock) {
646: if (image != null) {
647: if (dw != videoWidth || dh != videoHeight) {
648: // Scale first
649: int[] scaledRGB = scaleToDest(image);
650: g.drawRGB(scaledRGB, 0, dw, 0, 0, dw, dh,
651: useAlpha);
652: } else {
653: // No scaling
654: g.drawImage(image, 0, 0, Graphics.LEFT
655: | Graphics.TOP);
656: }
657: }
658: }
659: }
660: }
661:
662: protected int getMinContentWidth() {
663: return 1;
664: }
665:
666: protected int getMinContentHeight() {
667: return 1;
668: }
669:
670: protected int getPrefContentWidth(int h) {
671: return dw;
672: }
673:
674: protected int getPrefContentHeight(int w) {
675: return dh;
676: }
677:
678: protected void hideNotify() {
679: super.hideNotify();
680: }
681: }
682: }
|