Implementing VideoCanvas

The VideoCanvas class:

  • Draws the video or camera viewfinder on a Canvas

  • Draws the ControlBar on top of the Canvas

  • Controls video playback with the overlay buttons

The following code initializes the video player:

    public void initPlayer() {

        initializingVideo = true;

        // Always start player in normal mode
        fsMode = false;
        this.setFullScreenMode(fsMode);
        try {
            if (this.type == VideoOverlayMIDlet.CAMERA) {
                player = Manager.createPlayer("capture://video");
            } else {
                InputStream is = this.getClass().getResourceAsStream(
                        this.VIDEO_LOCATOR);
                player = Manager.createPlayer(is,
                        this.VIDEO_LOCATOR.substring(
                        this.VIDEO_LOCATOR.lastIndexOf('.') + 1));
            }
            player.realize();
            player.prefetch();

            videoControl = (VideoControl) player.getControl("VideoControl");

            if (videoControl != null) {
                videoControl.initDisplayMode(
                        VideoControl.USE_DIRECT_VIDEO, this);
                videoControl.setDisplayFullScreen(true);

                if (this.type == VideoOverlayMIDlet.CAMERA) {
                    addCommand(captureCommand);
                } else {
                    player.setLoopCount(-1);
                }
                this.controlBar.setX(videoControl.getDisplayX());
                this.controlBar.setY(videoControl.getDisplayY());

                videoControl.setVisible(true);

                player.start();

                // Empty paint is called to ensure
                // that videoControl area is clear
                emptyPaint = true;
                repaint();
                serviceRepaints();
                emptyPaint = false;
            }

The code above can raise a number of exceptions:

        } catch (IOException ioe) {
            discardPlayer();
            System.out.println(ioe.getMessage());
            Display.getDisplay(this.midlet).setCurrent(
                    new Alert("IOException:", ioe.getMessage(),
                    null, AlertType.ERROR));

        } catch (MediaException me) {
            discardPlayer();
            System.out.println(me.getMessage());
            Display.getDisplay(this.midlet).setCurrent(
                    new Alert("MediaException:", me.getMessage(),
                    null, AlertType.ERROR));

        } catch (SecurityException se) {
            discardPlayer();
            System.out.println(se.getMessage());
            Display.getDisplay(this.midlet).setCurrent(
                    new Alert("SecurityException", se.getMessage(),
                    null, AlertType.ERROR));

        } catch (Exception e) {
            discardPlayer();
            System.out.println(e.getMessage());
            Display.getDisplay(this.midlet).setCurrent(
                    new Alert("Exception", e.getMessage(),
                    null, AlertType.ERROR));

        } finally {
            initializingVideo = false;
        }
    }

The paint method is called when the video player needs to be drawn on the screen.

The following code first checks if it should draw "nothing" in order to clear the screen. Then, if the player is being initialized, it draws a string on the screen. If the player is done, it calls the paintOverlay method.

    public void paint(Graphics g) {
        if (emptyPaint == true) {
            // Do not draw anything
            // Ensure that videoControl area is clear
            return;

        }

        int state = Player.UNREALIZED;
        if (player != null) {
            state = player.getState();
        }

        if (initializingVideo) {

            paintInitString(g);

        } else {

            switch (state) {
                case Player.PREFETCHED:
                    paintOverlay(g);
                    break;
                case Player.STARTED:
                    paintOverlay(g);
                    break;
                case Player.CLOSED:
                    paintString("Closing...", g);
                    break;
                case Player.UNREALIZED:
                    break;
                case Player.REALIZED:
                    break;
                default:
                    g.setColor(BACKGROUND_COLOR);
                    g.fillRect(0, 0, getWidth(), getHeight());
                    paintString("Closing...", g);
                    break;
            }
        }
        prevRepaint = System.currentTimeMillis();

    }

The paintOverlay method paints the controls on top of the player canvas:

    private void paintOverlay(Graphics g) {

        // Check if canvas changes (e.g., in screen rotation)
        if (canvasW != getWidth()
                || canvasH != getHeight()) {
            canvasW = getWidth();
            canvasH = getHeight();
            // Setting new location
            this.controlBar.setX(videoControl.getDisplayX());
            this.controlBar.setY(videoControl.getDisplayY());
        }
        video_x = videoControl.getDisplayX();
        video_y = videoControl.getDisplayY();
        videoW = videoControl.getDisplayWidth();
        videoH = videoControl.getDisplayHeight();

        // Clear areas outside the videoControl
        g.setColor(BACKGROUND_COLOR);
        g.fillRect(0, 0, canvasW, video_y);
        g.fillRect(0, video_y + videoH, canvasW, canvasH);
        g.fillRect(0, 0, video_x, videoH);
        g.fillRect(video_x + videoW, 0, canvasW, canvasH);

        // Stop painting if clearing flag is up
        if (hideControlBar) {
            return;
        }

        // Setting ControlBar boundaries
        // Height is fixed size
        controlBar.setWidth(videoW / 2);

        // Control Bar
        this.drawButtons(g);

        // In fullscreen mode, drawing exit button
        if (fsMode) {
            // Portrait
            if (canvasH > canvasW) {
                exitX = videoW - (videoW / 3);
                exitY = canvasH - (back_icon.getHeight() + 20);
            } // Landscape
            else {
                exitX = videoW;
                exitY = videoH - (back_icon.getHeight() + 20);
            }

            g.drawImage(back_icon,
                    (exitX + canvasW) / 2, exitY,
                    Graphics.TOP | Graphics.HCENTER);
        }
    }

The MIDlet takes pictures using the takeSnapshot method:

    private void takeSnapshot() {
        if (player != null) {
            try {
                byte pngImage[] =
                        videoControl.getSnapshot(
                        "encoding=png&width=" + videoControl.getDisplayWidth()
                        + "&height=" + videoControl.getDisplayHeight());
                player.stop();
                midlet.cameraCanvasCaptured(pngImage);
            } catch (MediaException me) {
                System.out.println(me.getMessage());
                Display.getDisplay(this.midlet).setCurrent(
                        new Alert("MediaException:", me.getMessage(),
                        null, AlertType.ERROR));
            } catch (Exception e) {
                System.out.println(e.getMessage());
                Display.getDisplay(this.midlet).setCurrent(
                        new Alert("Exception:", e.getMessage(),
                        null, AlertType.ERROR));
            }
        }
    }

The above code calls the getSnapshot method of the VideoControl class and saves the result in a byte array. It then calls the main MIDlet's cameraCanvasCaptured method, which changes the view to the image view.

Since it is possible to move the video controls, the class listens for the pointerPressed and pointerDragged events. When these events occur, the class changes the position of the video controls.