Home | History | Annotate | Download | only in webrtc
      1 /*
      2  * libjingle
      3  * Copyright 2013 Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 package org.webrtc;
     29 
     30 import java.nio.ByteBuffer;
     31 
     32 /**
     33  * Java version of VideoRendererInterface.  In addition to allowing clients to
     34  * define their own rendering behavior (by passing in a Callbacks object), this
     35  * class also provides a createGui() method for creating a GUI-rendering window
     36  * on various platforms.
     37  */
     38 public class VideoRenderer {
     39   /**
     40    * Java version of cricket::VideoFrame. Frames are only constructed from native code and test
     41    * code.
     42    */
     43   public static class I420Frame {
     44     public final int width;
     45     public final int height;
     46     public final int[] yuvStrides;
     47     public ByteBuffer[] yuvPlanes;
     48     public final boolean yuvFrame;
     49     // Matrix that transforms standard coordinates to their proper sampling locations in
     50     // the texture. This transform compensates for any properties of the video source that
     51     // cause it to appear different from a normalized texture. This matrix does not take
     52     // |rotationDegree| into account.
     53     public final float[] samplingMatrix;
     54     public int textureId;
     55     // Frame pointer in C++.
     56     private long nativeFramePointer;
     57 
     58     // rotationDegree is the degree that the frame must be rotated clockwisely
     59     // to be rendered correctly.
     60     public int rotationDegree;
     61 
     62     /**
     63      * Construct a frame of the given dimensions with the specified planar data.
     64      */
     65     I420Frame(int width, int height, int rotationDegree, int[] yuvStrides, ByteBuffer[] yuvPlanes,
     66         long nativeFramePointer) {
     67       this.width = width;
     68       this.height = height;
     69       this.yuvStrides = yuvStrides;
     70       this.yuvPlanes = yuvPlanes;
     71       this.yuvFrame = true;
     72       this.rotationDegree = rotationDegree;
     73       this.nativeFramePointer = nativeFramePointer;
     74       if (rotationDegree % 90 != 0) {
     75         throw new IllegalArgumentException("Rotation degree not multiple of 90: " + rotationDegree);
     76       }
     77       // The convention in WebRTC is that the first element in a ByteBuffer corresponds to the
     78       // top-left corner of the image, but in glTexImage2D() the first element corresponds to the
     79       // bottom-left corner. This discrepancy is corrected by setting a vertical flip as sampling
     80       // matrix.
     81       samplingMatrix = new float[] {
     82           1,  0, 0, 0,
     83           0, -1, 0, 0,
     84           0,  0, 1, 0,
     85           0,  1, 0, 1};
     86     }
     87 
     88     /**
     89      * Construct a texture frame of the given dimensions with data in SurfaceTexture
     90      */
     91     I420Frame(int width, int height, int rotationDegree, int textureId, float[] samplingMatrix,
     92         long nativeFramePointer) {
     93       this.width = width;
     94       this.height = height;
     95       this.yuvStrides = null;
     96       this.yuvPlanes = null;
     97       this.samplingMatrix = samplingMatrix;
     98       this.textureId = textureId;
     99       this.yuvFrame = false;
    100       this.rotationDegree = rotationDegree;
    101       this.nativeFramePointer = nativeFramePointer;
    102       if (rotationDegree % 90 != 0) {
    103         throw new IllegalArgumentException("Rotation degree not multiple of 90: " + rotationDegree);
    104       }
    105     }
    106 
    107     public int rotatedWidth() {
    108       return (rotationDegree % 180 == 0) ? width : height;
    109     }
    110 
    111     public int rotatedHeight() {
    112       return (rotationDegree % 180 == 0) ? height : width;
    113     }
    114 
    115     @Override
    116     public String toString() {
    117       return width + "x" + height + ":" + yuvStrides[0] + ":" + yuvStrides[1] +
    118           ":" + yuvStrides[2];
    119     }
    120   }
    121 
    122   // Helper native function to do a video frame plane copying.
    123   public static native void nativeCopyPlane(ByteBuffer src, int width,
    124       int height, int srcStride, ByteBuffer dst, int dstStride);
    125 
    126   /** The real meat of VideoRendererInterface. */
    127   public static interface Callbacks {
    128     // |frame| might have pending rotation and implementation of Callbacks
    129     // should handle that by applying rotation during rendering. The callee
    130     // is responsible for signaling when it is done with |frame| by calling
    131     // renderFrameDone(frame).
    132     public void renderFrame(I420Frame frame);
    133   }
    134 
    135    /**
    136     * This must be called after every renderFrame() to release the frame.
    137     */
    138    public static void renderFrameDone(I420Frame frame) {
    139      frame.yuvPlanes = null;
    140      frame.textureId = 0;
    141      if (frame.nativeFramePointer != 0) {
    142        releaseNativeFrame(frame.nativeFramePointer);
    143        frame.nativeFramePointer = 0;
    144      }
    145    }
    146 
    147   // |this| either wraps a native (GUI) renderer or a client-supplied Callbacks
    148   // (Java) implementation; this is indicated by |isWrappedVideoRenderer|.
    149   long nativeVideoRenderer;
    150   private final boolean isWrappedVideoRenderer;
    151 
    152   public static VideoRenderer createGui(int x, int y) {
    153     long nativeVideoRenderer = nativeCreateGuiVideoRenderer(x, y);
    154     if (nativeVideoRenderer == 0) {
    155       return null;
    156     }
    157     return new VideoRenderer(nativeVideoRenderer);
    158   }
    159 
    160   public VideoRenderer(Callbacks callbacks) {
    161     nativeVideoRenderer = nativeWrapVideoRenderer(callbacks);
    162     isWrappedVideoRenderer = true;
    163   }
    164 
    165   private VideoRenderer(long nativeVideoRenderer) {
    166     this.nativeVideoRenderer = nativeVideoRenderer;
    167     isWrappedVideoRenderer = false;
    168   }
    169 
    170   public void dispose() {
    171     if (nativeVideoRenderer == 0) {
    172       // Already disposed.
    173       return;
    174     }
    175     if (!isWrappedVideoRenderer) {
    176       freeGuiVideoRenderer(nativeVideoRenderer);
    177     } else {
    178       freeWrappedVideoRenderer(nativeVideoRenderer);
    179     }
    180     nativeVideoRenderer = 0;
    181   }
    182 
    183   private static native long nativeCreateGuiVideoRenderer(int x, int y);
    184   private static native long nativeWrapVideoRenderer(Callbacks callbacks);
    185 
    186   private static native void freeGuiVideoRenderer(long nativeVideoRenderer);
    187   private static native void freeWrappedVideoRenderer(long nativeVideoRenderer);
    188 
    189   private static native void releaseNativeFrame(long nativeFramePointer);
    190 }
    191