반응형

출처 : http://bigflake.com/mediacodec/CameraToMpegTest.java.txt


  1. /* 
  2.  * Copyright 2013 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package android.media.cts;  
  18.   
  19. import android.graphics.SurfaceTexture;  
  20. import android.hardware.Camera;  
  21. import android.media.MediaCodec;  
  22. import android.media.MediaCodecInfo;  
  23. import android.media.MediaFormat;  
  24. import android.media.MediaMuxer;  
  25. import android.opengl.EGL14;  
  26. import android.opengl.EGLConfig;  
  27. import android.opengl.EGLContext;  
  28. import android.opengl.EGLDisplay;  
  29. import android.opengl.EGLExt;  
  30. import android.opengl.EGLSurface;  
  31. import android.opengl.GLES11Ext;  
  32. import android.opengl.GLES20;  
  33. import android.opengl.Matrix;  
  34. import android.os.Environment;  
  35. import android.test.AndroidTestCase;  
  36. import android.util.Log;  
  37. import android.view.Surface;  
  38.   
  39. import java.io.File;  
  40. import java.io.IOException;  
  41. import java.nio.ByteBuffer;  
  42. import java.nio.ByteOrder;  
  43. import java.nio.FloatBuffer;  
  44.   
  45. //20131106: removed unnecessary glFinish(), removed hard-coded "/sdcard"  
  46. //20131205: added alpha to EGLConfig  
  47. //20131210: demonstrate un-bind and re-bind of texture, for apps with shared EGL contexts  
  48. //20140123: correct error checks on glGet*Location() and program creation (they don't set error)  
  49.   
  50. /** 
  51.  * Record video from the camera preview and encode it as an MP4 file.  Demonstrates the use 
  52.  * of MediaMuxer and MediaCodec with Camera input.  Does not record audio. 
  53.  * <p> 
  54.  * Generally speaking, it's better to use MediaRecorder for this sort of thing.  This example 
  55.  * demonstrates one possible advantage: editing of video as it's being encoded.  A GLES 2.0 
  56.  * fragment shader is used to perform a silly color tweak every 15 frames. 
  57.  * <p> 
  58.  * This uses various features first available in Android "Jellybean" 4.3 (API 18).  There is 
  59.  * no equivalent functionality in previous releases.  (You can send the Camera preview to a 
  60.  * byte buffer with a fully-specified format, but MediaCodec encoders want different input 
  61.  * formats on different devices, and this use case wasn't well exercised in CTS pre-4.3.) 
  62.  * <p> 
  63.  * The output file will be something like "/sdcard/test.640x480.mp4". 
  64.  * <p> 
  65.  * (This was derived from bits and pieces of CTS tests, and is packaged as such, but is not 
  66.  * currently part of CTS.) 
  67.  */  
  68. public class CameraToMpegTest extends AndroidTestCase {  
  69.     private static final String TAG = "CameraToMpegTest";  
  70.     private static final boolean VERBOSE = false;           // lots of logging  
  71.   
  72.     // where to put the output file (note: /sdcard requires WRITE_EXTERNAL_STORAGE permission)  
  73.     private static final File OUTPUT_DIR = Environment.getExternalStorageDirectory();  
  74.   
  75.     // parameters for the encoder  
  76.     private static final String MIME_TYPE = "video/avc";    // H.264 Advanced Video Coding  
  77.     private static final int FRAME_RATE = 30;               // 30fps  
  78.     private static final int IFRAME_INTERVAL = 5;           // 5 seconds between I-frames  
  79.     private static final long DURATION_SEC = 8;             // 8 seconds of video  
  80.   
  81.     // Fragment shader that swaps color channels around.  
  82.     private static final String SWAPPED_FRAGMENT_SHADER =  
  83.             "#extension GL_OES_EGL_image_external : require\n" +  
  84.             "precision mediump float;\n" +  
  85.             "varying vec2 vTextureCoord;\n" +  
  86.             "uniform samplerExternalOES sTexture;\n" +  
  87.             "void main() {\n" +  
  88.             "  gl_FragColor = texture2D(sTexture, vTextureCoord).gbra;\n" +  
  89.             "}\n";  
  90.   
  91.     // encoder / muxer state  
  92.     private MediaCodec mEncoder;  
  93.     private CodecInputSurface mInputSurface;  
  94.     private MediaMuxer mMuxer;  
  95.     private int mTrackIndex;  
  96.     private boolean mMuxerStarted;  
  97.   
  98.     // camera state  
  99.     private Camera mCamera;  
  100.     private SurfaceTextureManager mStManager;  
  101.   
  102.     // allocate one of these up front so we don't need to do it every time  
  103.     private MediaCodec.BufferInfo mBufferInfo;  
  104.   
  105.     /** test entry point */  
  106.     public void testEncodeCameraToMp4() throws Throwable {  
  107.         CameraToMpegWrapper.runTest(this);  
  108.     }  
  109.   
  110.     /** 
  111.      * Wraps encodeCameraToMpeg().  This is necessary because SurfaceTexture will try to use 
  112.      * the looper in the current thread if one exists, and the CTS tests create one on the 
  113.      * test thread. 
  114.      * 
  115.      * The wrapper propagates exceptions thrown by the worker thread back to the caller. 
  116.      */  
  117.     private static class CameraToMpegWrapper implements Runnable {  
  118.         private Throwable mThrowable;  
  119.         private CameraToMpegTest mTest;  
  120.   
  121.         private CameraToMpegWrapper(CameraToMpegTest test) {  
  122.             mTest = test;  
  123.         }  
  124.   
  125.         @Override  
  126.         public void run() {  
  127.             try {  
  128.                 mTest.encodeCameraToMpeg();  
  129.             } catch (Throwable th) {  
  130.                 mThrowable = th;  
  131.             }  
  132.         }  
  133.   
  134.         /** Entry point. */  
  135.         public static void runTest(CameraToMpegTest obj) throws Throwable {  
  136.             CameraToMpegWrapper wrapper = new CameraToMpegWrapper(obj);  
  137.             Thread th = new Thread(wrapper, "codec test");  
  138.             th.start();  
  139.             th.join();  
  140.             if (wrapper.mThrowable != null) {  
  141.                 throw wrapper.mThrowable;  
  142.             }  
  143.         }  
  144.     }  
  145.   
  146.     /** 
  147.      * Tests encoding of AVC video from Camera input.  The output is saved as an MP4 file. 
  148.      */  
  149.     private void encodeCameraToMpeg() {  
  150.         // arbitrary but popular values  
  151.         int encWidth = 640;  
  152.         int encHeight = 480;  
  153.         int encBitRate = 6000000;      // Mbps  
  154.         Log.d(TAG, MIME_TYPE + " output " + encWidth + "x" + encHeight + " @" + encBitRate);  
  155.   
  156.         try {  
  157.             prepareCamera(encWidth, encHeight);  
  158.             prepareEncoder(encWidth, encHeight, encBitRate);  
  159.             mInputSurface.makeCurrent();  
  160.             prepareSurfaceTexture();  
  161.   
  162.             mCamera.startPreview();  
  163.   
  164.             long startWhen = System.nanoTime();  
  165.             long desiredEnd = startWhen + DURATION_SEC * 1000000000L;  
  166.             SurfaceTexture st = mStManager.getSurfaceTexture();  
  167.             int frameCount = 0;  
  168.   
  169.             while (System.nanoTime() < desiredEnd) {  
  170.                 // Feed any pending encoder output into the muxer.  
  171.                 drainEncoder(false);  
  172.   
  173.                 // Switch up the colors every 15 frames.  Besides demonstrating the use of  
  174.                 // fragment shaders for video editing, this provides a visual indication of  
  175.                 // the frame rate: if the camera is capturing at 15fps, the colors will change  
  176.                 // once per second.  
  177.                 if ((frameCount % 15) == 0) {  
  178.                     String fragmentShader = null;  
  179.                     if ((frameCount & 0x01) != 0) {  
  180.                         fragmentShader = SWAPPED_FRAGMENT_SHADER;  
  181.                     }  
  182.                     mStManager.changeFragmentShader(fragmentShader);  
  183.                 }  
  184.                 frameCount++;  
  185.   
  186.                 // Acquire a new frame of input, and render it to the Surface.  If we had a  
  187.                 // GLSurfaceView we could switch EGL contexts and call drawImage() a second  
  188.                 // time to render it on screen.  The texture can be shared between contexts by  
  189.                 // passing the GLSurfaceView's EGLContext as eglCreateContext()'s share_context  
  190.                 // argument.  
  191.                 mStManager.awaitNewImage();  
  192.                 mStManager.drawImage();  
  193.   
  194.                 // Set the presentation time stamp from the SurfaceTexture's time stamp.  This  
  195.                 // will be used by MediaMuxer to set the PTS in the video.  
  196.                 if (VERBOSE) {  
  197.                     Log.d(TAG, "present: " +  
  198.                             ((st.getTimestamp() - startWhen) / 1000000.0) + "ms");  
  199.                 }  
  200.                 mInputSurface.setPresentationTime(st.getTimestamp());  
  201.   
  202.                 // Submit it to the encoder.  The eglSwapBuffers call will block if the input  
  203.                 // is full, which would be bad if it stayed full until we dequeued an output  
  204.                 // buffer (which we can't do, since we're stuck here).  So long as we fully drain  
  205.                 // the encoder before supplying additional input, the system guarantees that we  
  206.                 // can supply another frame without blocking.  
  207.                 if (VERBOSE) Log.d(TAG, "sending frame to encoder");  
  208.                 mInputSurface.swapBuffers();  
  209.             }  
  210.   
  211.             // send end-of-stream to encoder, and drain remaining output  
  212.             drainEncoder(true);  
  213.         } finally {  
  214.             // release everything we grabbed  
  215.             releaseCamera();  
  216.             releaseEncoder();  
  217.             releaseSurfaceTexture();  
  218.         }  
  219.     }  
  220.   
  221.     /** 
  222.      * Configures Camera for video capture.  Sets mCamera. 
  223.      * <p> 
  224.      * Opens a Camera and sets parameters.  Does not start preview. 
  225.      */  
  226.     private void prepareCamera(int encWidth, int encHeight) {  
  227.         if (mCamera != null) {  
  228.             throw new RuntimeException("camera already initialized");  
  229.         }  
  230.   
  231.         Camera.CameraInfo info = new Camera.CameraInfo();  
  232.   
  233.         // Try to find a front-facing camera (e.g. for videoconferencing).  
  234.         int numCameras = Camera.getNumberOfCameras();  
  235.         for (int i = 0; i < numCameras; i++) {  
  236.             Camera.getCameraInfo(i, info);  
  237.             if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {  
  238.                 mCamera = Camera.open(i);  
  239.                 break;  
  240.             }  
  241.         }  
  242.         if (mCamera == null) {  
  243.             Log.d(TAG, "No front-facing camera found; opening default");  
  244.             mCamera = Camera.open();    // opens first back-facing camera  
  245.         }  
  246.         if (mCamera == null) {  
  247.             throw new RuntimeException("Unable to open camera");  
  248.         }  
  249.   
  250.         Camera.Parameters parms = mCamera.getParameters();  
  251.   
  252.         choosePreviewSize(parms, encWidth, encHeight);  
  253.         // leave the frame rate set to default  
  254.         mCamera.setParameters(parms);  
  255.   
  256.         Camera.Size size = parms.getPreviewSize();  
  257.         Log.d(TAG, "Camera preview size is " + size.width + "x" + size.height);  
  258.     }  
  259.   
  260.     /** 
  261.      * Attempts to find a preview size that matches the provided width and height (which 
  262.      * specify the dimensions of the encoded video).  If it fails to find a match it just 
  263.      * uses the default preview size. 
  264.      * <p> 
  265.      * TODO: should do a best-fit match. 
  266.      */  
  267.     private static void choosePreviewSize(Camera.Parameters parms, int width, int height) {  
  268.         // We should make sure that the requested MPEG size is less than the preferred  
  269.         // size, and has the same aspect ratio.  
  270.         Camera.Size ppsfv = parms.getPreferredPreviewSizeForVideo();  
  271.         if (VERBOSE && ppsfv != null) {  
  272.             Log.d(TAG, "Camera preferred preview size for video is " +  
  273.                     ppsfv.width + "x" + ppsfv.height);  
  274.         }  
  275.   
  276.         for (Camera.Size size : parms.getSupportedPreviewSizes()) {  
  277.             if (size.width == width && size.height == height) {  
  278.                 parms.setPreviewSize(width, height);  
  279.                 return;  
  280.             }  
  281.         }  
  282.   
  283.         Log.w(TAG, "Unable to set preview size to " + width + "x" + height);  
  284.         if (ppsfv != null) {  
  285.             parms.setPreviewSize(ppsfv.width, ppsfv.height);  
  286.         }  
  287.     }  
  288.   
  289.     /** 
  290.      * Stops camera preview, and releases the camera to the system. 
  291.      */  
  292.     private void releaseCamera() {  
  293.         if (VERBOSE) Log.d(TAG, "releasing camera");  
  294.         if (mCamera != null) {  
  295.             mCamera.stopPreview();  
  296.             mCamera.release();  
  297.             mCamera = null;  
  298.         }  
  299.     }  
  300.   
  301.     /** 
  302.      * Configures SurfaceTexture for camera preview.  Initializes mStManager, and sets the 
  303.      * associated SurfaceTexture as the Camera's "preview texture". 
  304.      * <p> 
  305.      * Configure the EGL surface that will be used for output before calling here. 
  306.      */  
  307.     private void prepareSurfaceTexture() {  
  308.         mStManager = new SurfaceTextureManager();  
  309.         SurfaceTexture st = mStManager.getSurfaceTexture();  
  310.         try {  
  311.             mCamera.setPreviewTexture(st);  
  312.         } catch (IOException ioe) {  
  313.             throw new RuntimeException("setPreviewTexture failed", ioe);  
  314.         }  
  315.     }  
  316.   
  317.     /** 
  318.      * Releases the SurfaceTexture. 
  319.      */  
  320.     private void releaseSurfaceTexture() {  
  321.         if (mStManager != null) {  
  322.             mStManager.release();  
  323.             mStManager = null;  
  324.         }  
  325.     }  
  326.   
  327.     /** 
  328.      * Configures encoder and muxer state, and prepares the input Surface.  Initializes 
  329.      * mEncoder, mMuxer, mInputSurface, mBufferInfo, mTrackIndex, and mMuxerStarted. 
  330.      */  
  331.     private void prepareEncoder(int width, int height, int bitRate) {  
  332.         mBufferInfo = new MediaCodec.BufferInfo();  
  333.   
  334.         MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, width, height);  
  335.   
  336.         // Set some properties.  Failing to specify some of these can cause the MediaCodec  
  337.         // configure() call to throw an unhelpful exception.  
  338.         format.setInteger(MediaFormat.KEY_COLOR_FORMAT,  
  339.                 MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);  
  340.         format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);  
  341.         format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);  
  342.         format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);  
  343.         if (VERBOSE) Log.d(TAG, "format: " + format);  
  344.   
  345.         // Create a MediaCodec encoder, and configure it with our format.  Get a Surface  
  346.         // we can use for input and wrap it with a class that handles the EGL work.  
  347.         //  
  348.         // If you want to have two EGL contexts -- one for display, one for recording --  
  349.         // you will likely want to defer instantiation of CodecInputSurface until after the  
  350.         // "display" EGL context is created, then modify the eglCreateContext call to  
  351.         // take eglGetCurrentContext() as the share_context argument.  
  352.         mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);  
  353.         mEncoder.configure(format, nullnull, MediaCodec.CONFIGURE_FLAG_ENCODE);  
  354.         mInputSurface = new CodecInputSurface(mEncoder.createInputSurface());  
  355.         mEncoder.start();  
  356.   
  357.         // Output filename.  Ideally this would use Context.getFilesDir() rather than a  
  358.         // hard-coded output directory.  
  359.         String outputPath = new File(OUTPUT_DIR,  
  360.                 "test." + width + "x" + height + ".mp4").toString();  
  361.         Log.i(TAG, "Output file is " + outputPath);  
  362.   
  363.   
  364.         // Create a MediaMuxer.  We can't add the video track and start() the muxer here,  
  365.         // because our MediaFormat doesn't have the Magic Goodies.  These can only be  
  366.         // obtained from the encoder after it has started processing data.  
  367.         //  
  368.         // We're not actually interested in multiplexing audio.  We just want to convert  
  369.         // the raw H.264 elementary stream we get from MediaCodec into a .mp4 file.  
  370.         try {  
  371.             mMuxer = new MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);  
  372.         } catch (IOException ioe) {  
  373.             throw new RuntimeException("MediaMuxer creation failed", ioe);  
  374.         }  
  375.   
  376.         mTrackIndex = -1;  
  377.         mMuxerStarted = false;  
  378.     }  
  379.   
  380.     /** 
  381.      * Releases encoder resources. 
  382.      */  
  383.     private void releaseEncoder() {  
  384.         if (VERBOSE) Log.d(TAG, "releasing encoder objects");  
  385.         if (mEncoder != null) {  
  386.             mEncoder.stop();  
  387.             mEncoder.release();  
  388.             mEncoder = null;  
  389.         }  
  390.         if (mInputSurface != null) {  
  391.             mInputSurface.release();  
  392.             mInputSurface = null;  
  393.         }  
  394.         if (mMuxer != null) {  
  395.             mMuxer.stop();  
  396.             mMuxer.release();  
  397.             mMuxer = null;  
  398.         }  
  399.     }  
  400.   
  401.     /** 
  402.      * Extracts all pending data from the encoder and forwards it to the muxer. 
  403.      * <p> 
  404.      * If endOfStream is not set, this returns when there is no more data to drain.  If it 
  405.      * is set, we send EOS to the encoder, and then iterate until we see EOS on the output. 
  406.      * Calling this with endOfStream set should be done once, right before stopping the muxer. 
  407.      * <p> 
  408.      * We're just using the muxer to get a .mp4 file (instead of a raw H.264 stream).  We're 
  409.      * not recording audio. 
  410.      */  
  411.     private void drainEncoder(boolean endOfStream) {  
  412.         final int TIMEOUT_USEC = 10000;  
  413.         if (VERBOSE) Log.d(TAG, "drainEncoder(" + endOfStream + ")");  
  414.   
  415.         if (endOfStream) {  
  416.             if (VERBOSE) Log.d(TAG, "sending EOS to encoder");  
  417.             mEncoder.signalEndOfInputStream();  
  418.         }  
  419.   
  420.         ByteBuffer[] encoderOutputBuffers = mEncoder.getOutputBuffers();  
  421.         while (true) {  
  422.             int encoderStatus = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);  
  423.             if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {  
  424.                 // no output available yet  
  425.                 if (!endOfStream) {  
  426.                     break;      // out of while  
  427.                 } else {  
  428.                     if (VERBOSE) Log.d(TAG, "no output available, spinning to await EOS");  
  429.                 }  
  430.             } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {  
  431.                 // not expected for an encoder  
  432.                 encoderOutputBuffers = mEncoder.getOutputBuffers();  
  433.             } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {  
  434.                 // should happen before receiving buffers, and should only happen once  
  435.                 if (mMuxerStarted) {  
  436.                     throw new RuntimeException("format changed twice");  
  437.                 }  
  438.                 MediaFormat newFormat = mEncoder.getOutputFormat();  
  439.                 Log.d(TAG, "encoder output format changed: " + newFormat);  
  440.   
  441.                 // now that we have the Magic Goodies, start the muxer  
  442.                 mTrackIndex = mMuxer.addTrack(newFormat);  
  443.                 mMuxer.start();  
  444.                 mMuxerStarted = true;  
  445.             } else if (encoderStatus < 0) {  
  446.                 Log.w(TAG, "unexpected result from encoder.dequeueOutputBuffer: " +  
  447.                         encoderStatus);  
  448.                 // let's ignore it  
  449.             } else {  
  450.                 ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];  
  451.                 if (encodedData == null) {  
  452.                     throw new RuntimeException("encoderOutputBuffer " + encoderStatus +  
  453.                             " was null");  
  454.                 }  
  455.   
  456.                 if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {  
  457.                     // The codec config data was pulled out and fed to the muxer when we got  
  458.                     // the INFO_OUTPUT_FORMAT_CHANGED status.  Ignore it.  
  459.                     if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");  
  460.                     mBufferInfo.size = 0;  
  461.                 }  
  462.   
  463.                 if (mBufferInfo.size != 0) {  
  464.                     if (!mMuxerStarted) {  
  465.                         throw new RuntimeException("muxer hasn't started");  
  466.                     }  
  467.   
  468.                     // adjust the ByteBuffer values to match BufferInfo (not needed?)  
  469.                     encodedData.position(mBufferInfo.offset);  
  470.                     encodedData.limit(mBufferInfo.offset + mBufferInfo.size);  
  471.   
  472.                     mMuxer.writeSampleData(mTrackIndex, encodedData, mBufferInfo);  
  473.                     if (VERBOSE) Log.d(TAG, "sent " + mBufferInfo.size + " bytes to muxer");  
  474.                 }  
  475.   
  476.                 mEncoder.releaseOutputBuffer(encoderStatus, false);  
  477.   
  478.                 if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {  
  479.                     if (!endOfStream) {  
  480.                         Log.w(TAG, "reached end of stream unexpectedly");  
  481.                     } else {  
  482.                         if (VERBOSE) Log.d(TAG, "end of stream reached");  
  483.                     }  
  484.                     break;      // out of while  
  485.                 }  
  486.             }  
  487.         }  
  488.     }  
  489.   
  490.   
  491.     /** 
  492.      * Holds state associated with a Surface used for MediaCodec encoder input. 
  493.      * <p> 
  494.      * The constructor takes a Surface obtained from MediaCodec.createInputSurface(), and uses 
  495.      * that to create an EGL window surface.  Calls to eglSwapBuffers() cause a frame of data to 
  496.      * be sent to the video encoder. 
  497.      * <p> 
  498.      * This object owns the Surface -- releasing this will release the Surface too. 
  499.      */  
  500.     private static class CodecInputSurface {  
  501.         private static final int EGL_RECORDABLE_ANDROID = 0x3142;  
  502.   
  503.         private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;  
  504.         private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;  
  505.         private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;  
  506.   
  507.         private Surface mSurface;  
  508.   
  509.         /** 
  510.          * Creates a CodecInputSurface from a Surface. 
  511.          */  
  512.         public CodecInputSurface(Surface surface) {  
  513.             if (surface == null) {  
  514.                 throw new NullPointerException();  
  515.             }  
  516.             mSurface = surface;  
  517.   
  518.             eglSetup();  
  519.         }  
  520.   
  521.         /** 
  522.          * Prepares EGL.  We want a GLES 2.0 context and a surface that supports recording. 
  523.          */  
  524.         private void eglSetup() {  
  525.             mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);  
  526.             if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {  
  527.                 throw new RuntimeException("unable to get EGL14 display");  
  528.             }  
  529.             int[] version = new int[2];  
  530.             if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {  
  531.                 throw new RuntimeException("unable to initialize EGL14");  
  532.             }  
  533.   
  534.             // Configure EGL for recording and OpenGL ES 2.0.  
  535.             int[] attribList = {  
  536.                     EGL14.EGL_RED_SIZE, 8,  
  537.                     EGL14.EGL_GREEN_SIZE, 8,  
  538.                     EGL14.EGL_BLUE_SIZE, 8,  
  539.                     EGL14.EGL_ALPHA_SIZE, 8,  
  540.                     EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,  
  541.                     EGL_RECORDABLE_ANDROID, 1,  
  542.                     EGL14.EGL_NONE  
  543.             };  
  544.             EGLConfig[] configs = new EGLConfig[1];  
  545.             int[] numConfigs = new int[1];  
  546.             EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,  
  547.                     numConfigs, 0);  
  548.             checkEglError("eglCreateContext RGB888+recordable ES2");  
  549.   
  550.             // Configure context for OpenGL ES 2.0.  
  551.             int[] attrib_list = {  
  552.                     EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,  
  553.                     EGL14.EGL_NONE  
  554.             };  
  555.             mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,  
  556.                     attrib_list, 0);  
  557.             checkEglError("eglCreateContext");  
  558.   
  559.             // Create a window surface, and attach it to the Surface we received.  
  560.             int[] surfaceAttribs = {  
  561.                     EGL14.EGL_NONE  
  562.             };  
  563.             mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface,  
  564.                     surfaceAttribs, 0);  
  565.             checkEglError("eglCreateWindowSurface");  
  566.         }  
  567.   
  568.         /** 
  569.          * Discards all resources held by this class, notably the EGL context.  Also releases the 
  570.          * Surface that was passed to our constructor. 
  571.          */  
  572.         public void release() {  
  573.             if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {  
  574.                 EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,  
  575.                         EGL14.EGL_NO_CONTEXT);  
  576.                 EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);  
  577.                 EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);  
  578.                 EGL14.eglReleaseThread();  
  579.                 EGL14.eglTerminate(mEGLDisplay);  
  580.             }  
  581.             mSurface.release();  
  582.   
  583.             mEGLDisplay = EGL14.EGL_NO_DISPLAY;  
  584.             mEGLContext = EGL14.EGL_NO_CONTEXT;  
  585.             mEGLSurface = EGL14.EGL_NO_SURFACE;  
  586.   
  587.             mSurface = null;  
  588.         }  
  589.   
  590.         /** 
  591.          * Makes our EGL context and surface current. 
  592.          */  
  593.         public void makeCurrent() {  
  594.             EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);  
  595.             checkEglError("eglMakeCurrent");  
  596.         }  
  597.   
  598.         /** 
  599.          * Calls eglSwapBuffers.  Use this to "publish" the current frame. 
  600.          */  
  601.         public boolean swapBuffers() {  
  602.             boolean result = EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface);  
  603.             checkEglError("eglSwapBuffers");  
  604.             return result;  
  605.         }  
  606.   
  607.         /** 
  608.          * Sends the presentation time stamp to EGL.  Time is expressed in nanoseconds. 
  609.          */  
  610.         public void setPresentationTime(long nsecs) {  
  611.             EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, nsecs);  
  612.             checkEglError("eglPresentationTimeANDROID");  
  613.         }  
  614.   
  615.         /** 
  616.          * Checks for EGL errors.  Throws an exception if one is found. 
  617.          */  
  618.         private void checkEglError(String msg) {  
  619.             int error;  
  620.             if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {  
  621.                 throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error));  
  622.             }  
  623.         }  
  624.     }  
  625.   
  626.   
  627.     /** 
  628.      * Manages a SurfaceTexture.  Creates SurfaceTexture and TextureRender objects, and provides 
  629.      * functions that wait for frames and render them to the current EGL surface. 
  630.      * <p> 
  631.      * The SurfaceTexture can be passed to Camera.setPreviewTexture() to receive camera output. 
  632.      */  
  633.     private static class SurfaceTextureManager  
  634.             implements SurfaceTexture.OnFrameAvailableListener {  
  635.         private SurfaceTexture mSurfaceTexture;  
  636.         private CameraToMpegTest.STextureRender mTextureRender;  
  637.   
  638.         private Object mFrameSyncObject = new Object();     // guards mFrameAvailable  
  639.         private boolean mFrameAvailable;  
  640.   
  641.         /** 
  642.          * Creates instances of TextureRender and SurfaceTexture. 
  643.          */  
  644.         public SurfaceTextureManager() {  
  645.             mTextureRender = new CameraToMpegTest.STextureRender();  
  646.             mTextureRender.surfaceCreated();  
  647.   
  648.             if (VERBOSE) Log.d(TAG, "textureID=" + mTextureRender.getTextureId());  
  649.             mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId());  
  650.   
  651.             // This doesn't work if this object is created on the thread that CTS started for  
  652.             // these test cases.  
  653.             //  
  654.             // The CTS-created thread has a Looper, and the SurfaceTexture constructor will  
  655.             // create a Handler that uses it.  The "frame available" message is delivered  
  656.             // there, but since we're not a Looper-based thread we'll never see it.  For  
  657.             // this to do anything useful, OutputSurface must be created on a thread without  
  658.             // a Looper, so that SurfaceTexture uses the main application Looper instead.  
  659.             //  
  660.             // Java language note: passing "this" out of a constructor is generally unwise,  
  661.             // but we should be able to get away with it here.  
  662.             mSurfaceTexture.setOnFrameAvailableListener(this);  
  663.         }  
  664.   
  665.         public void release() {  
  666.             // this causes a bunch of warnings that appear harmless but might confuse someone:  
  667.             //  W BufferQueue: [unnamed-3997-2] cancelBuffer: BufferQueue has been abandoned!  
  668.             //mSurfaceTexture.release();  
  669.   
  670.             mTextureRender = null;  
  671.             mSurfaceTexture = null;  
  672.         }  
  673.   
  674.         /** 
  675.          * Returns the SurfaceTexture. 
  676.          */  
  677.         public SurfaceTexture getSurfaceTexture() {  
  678.             return mSurfaceTexture;  
  679.         }  
  680.   
  681.         /** 
  682.          * Replaces the fragment shader. 
  683.          */  
  684.         public void changeFragmentShader(String fragmentShader) {  
  685.             mTextureRender.changeFragmentShader(fragmentShader);  
  686.         }  
  687.   
  688.         /** 
  689.          * Latches the next buffer into the texture.  Must be called from the thread that created 
  690.          * the OutputSurface object. 
  691.          */  
  692.         public void awaitNewImage() {  
  693.             final int TIMEOUT_MS = 2500;  
  694.   
  695.             synchronized (mFrameSyncObject) {  
  696.                 while (!mFrameAvailable) {  
  697.                     try {  
  698.                         // Wait for onFrameAvailable() to signal us.  Use a timeout to avoid  
  699.                         // stalling the test if it doesn't arrive.  
  700.                         mFrameSyncObject.wait(TIMEOUT_MS);  
  701.                         if (!mFrameAvailable) {  
  702.                             // TODO: if "spurious wakeup", continue while loop  
  703.                             throw new RuntimeException("Camera frame wait timed out");  
  704.                         }  
  705.                     } catch (InterruptedException ie) {  
  706.                         // shouldn't happen  
  707.                         throw new RuntimeException(ie);  
  708.                     }  
  709.                 }  
  710.                 mFrameAvailable = false;  
  711.             }  
  712.   
  713.             // Latch the data.  
  714.             mTextureRender.checkGlError("before updateTexImage");  
  715.             mSurfaceTexture.updateTexImage();  
  716.         }  
  717.   
  718.         /** 
  719.          * Draws the data from SurfaceTexture onto the current EGL surface. 
  720.          */  
  721.         public void drawImage() {  
  722.             mTextureRender.drawFrame(mSurfaceTexture);  
  723.         }  
  724.   
  725.         @Override  
  726.         public void onFrameAvailable(SurfaceTexture st) {  
  727.             if (VERBOSE) Log.d(TAG, "new frame available");  
  728.             synchronized (mFrameSyncObject) {  
  729.                 if (mFrameAvailable) {  
  730.                     throw new RuntimeException("mFrameAvailable already set, frame could be dropped");  
  731.                 }  
  732.                 mFrameAvailable = true;  
  733.                 mFrameSyncObject.notifyAll();  
  734.             }  
  735.         }  
  736.     }  
  737.   
  738.   
  739.     /** 
  740.      * Code for rendering a texture onto a surface using OpenGL ES 2.0. 
  741.      */  
  742.     private static class STextureRender {  
  743.         private static final int FLOAT_SIZE_BYTES = 4;  
  744.         private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;  
  745.         private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;  
  746.         private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;  
  747.         private final float[] mTriangleVerticesData = {  
  748.                 // X, Y, Z, U, V  
  749.                 -1.0f, -1.0f, 0, 0.f, 0.f,  
  750.                  1.0f, -1.0f, 0, 1.f, 0.f,  
  751.                 -1.0f,  1.0f, 0, 0.f, 1.f,  
  752.                  1.0f,  1.0f, 0, 1.f, 1.f,  
  753.         };  
  754.   
  755.         private FloatBuffer mTriangleVertices;  
  756.   
  757.         private static final String VERTEX_SHADER =  
  758.                 "uniform mat4 uMVPMatrix;\n" +  
  759.                 "uniform mat4 uSTMatrix;\n" +  
  760.                 "attribute vec4 aPosition;\n" +  
  761.                 "attribute vec4 aTextureCoord;\n" +  
  762.                 "varying vec2 vTextureCoord;\n" +  
  763.                 "void main() {\n" +  
  764.                 "    gl_Position = uMVPMatrix * aPosition;\n" +  
  765.                 "    vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +  
  766.                 "}\n";  
  767.   
  768.         private static final String FRAGMENT_SHADER =  
  769.                 "#extension GL_OES_EGL_image_external : require\n" +  
  770.                 "precision mediump float;\n" +      // highp here doesn't seem to matter  
  771.                 "varying vec2 vTextureCoord;\n" +  
  772.                 "uniform samplerExternalOES sTexture;\n" +  
  773.                 "void main() {\n" +  
  774.                 "    gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +  
  775.                 "}\n";  
  776.   
  777.         private float[] mMVPMatrix = new float[16];  
  778.         private float[] mSTMatrix = new float[16];  
  779.   
  780.         private int mProgram;  
  781.         private int mTextureID = -12345;  
  782.         private int muMVPMatrixHandle;  
  783.         private int muSTMatrixHandle;  
  784.         private int maPositionHandle;  
  785.         private int maTextureHandle;  
  786.   
  787.         public STextureRender() {  
  788.             mTriangleVertices = ByteBuffer.allocateDirect(  
  789.                     mTriangleVerticesData.length * FLOAT_SIZE_BYTES)  
  790.                     .order(ByteOrder.nativeOrder()).asFloatBuffer();  
  791.             mTriangleVertices.put(mTriangleVerticesData).position(0);  
  792.   
  793.             Matrix.setIdentityM(mSTMatrix, 0);  
  794.         }  
  795.   
  796.         public int getTextureId() {  
  797.             return mTextureID;  
  798.         }  
  799.   
  800.         public void drawFrame(SurfaceTexture st) {  
  801.             checkGlError("onDrawFrame start");  
  802.             st.getTransformMatrix(mSTMatrix);  
  803.   
  804.             // (optional) clear to green so we can see if we're failing to set pixels  
  805.             GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);  
  806.             GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);  
  807.   
  808.             GLES20.glUseProgram(mProgram);  
  809.             checkGlError("glUseProgram");  
  810.   
  811.             GLES20.glActiveTexture(GLES20.GL_TEXTURE0);  
  812.             GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);  
  813.   
  814.             mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);  
  815.             GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,  
  816.                     TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);  
  817.             checkGlError("glVertexAttribPointer maPosition");  
  818.             GLES20.glEnableVertexAttribArray(maPositionHandle);  
  819.             checkGlError("glEnableVertexAttribArray maPositionHandle");  
  820.   
  821.             mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);  
  822.             GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false,  
  823.                     TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);  
  824.             checkGlError("glVertexAttribPointer maTextureHandle");  
  825.             GLES20.glEnableVertexAttribArray(maTextureHandle);  
  826.             checkGlError("glEnableVertexAttribArray maTextureHandle");  
  827.   
  828.             Matrix.setIdentityM(mMVPMatrix, 0);  
  829.             GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);  
  830.             GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);  
  831.   
  832.             GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);  
  833.             checkGlError("glDrawArrays");  
  834.   
  835.             // IMPORTANT: on some devices, if you are sharing the external texture between two  
  836.             // contexts, one context may not see updates to the texture unless you un-bind and  
  837.             // re-bind it.  If you're not using shared EGL contexts, you don't need to bind  
  838.             // texture 0 here.  
  839.             GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);  
  840.         }  
  841.   
  842.         /** 
  843.          * Initializes GL state.  Call this after the EGL surface has been created and made current. 
  844.          */  
  845.         public void surfaceCreated() {  
  846.             mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);  
  847.             if (mProgram == 0) {  
  848.                 throw new RuntimeException("failed creating program");  
  849.             }  
  850.             maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");  
  851.             checkLocation(maPositionHandle, "aPosition");  
  852.             maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");  
  853.             checkLocation(maTextureHandle, "aTextureCoord");  
  854.   
  855.             muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");  
  856.             checkLocation(muMVPMatrixHandle, "uMVPMatrix");  
  857.             muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");  
  858.             checkLocation(muSTMatrixHandle, "uSTMatrix");  
  859.   
  860.             int[] textures = new int[1];  
  861.             GLES20.glGenTextures(1, textures, 0);  
  862.   
  863.             mTextureID = textures[0];  
  864.             GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);  
  865.             checkGlError("glBindTexture mTextureID");  
  866.   
  867.             GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,  
  868.                     GLES20.GL_NEAREST);  
  869.             GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,  
  870.                     GLES20.GL_LINEAR);  
  871.             GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,  
  872.                     GLES20.GL_CLAMP_TO_EDGE);  
  873.             GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,  
  874.                     GLES20.GL_CLAMP_TO_EDGE);  
  875.             checkGlError("glTexParameter");  
  876.         }  
  877.   
  878.         /** 
  879.          * Replaces the fragment shader.  Pass in null to reset to default. 
  880.          */  
  881.         public void changeFragmentShader(String fragmentShader) {  
  882.             if (fragmentShader == null) {  
  883.                 fragmentShader = FRAGMENT_SHADER;  
  884.             }  
  885.             GLES20.glDeleteProgram(mProgram);  
  886.             mProgram = createProgram(VERTEX_SHADER, fragmentShader);  
  887.             if (mProgram == 0) {  
  888.                 throw new RuntimeException("failed creating program");  
  889.             }  
  890.         }  
  891.   
  892.         private int loadShader(int shaderType, String source) {  
  893.             int shader = GLES20.glCreateShader(shaderType);  
  894.             checkGlError("glCreateShader type=" + shaderType);  
  895.             GLES20.glShaderSource(shader, source);  
  896.             GLES20.glCompileShader(shader);  
  897.             int[] compiled = new int[1];  
  898.             GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);  
  899.             if (compiled[0] == 0) {  
  900.                 Log.e(TAG, "Could not compile shader " + shaderType + ":");  
  901.                 Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader));  
  902.                 GLES20.glDeleteShader(shader);  
  903.                 shader = 0;  
  904.             }  
  905.             return shader;  
  906.         }  
  907.   
  908.         private int createProgram(String vertexSource, String fragmentSource) {  
  909.             int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);  
  910.             if (vertexShader == 0) {  
  911.                 return 0;  
  912.             }  
  913.             int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);  
  914.             if (pixelShader == 0) {  
  915.                 return 0;  
  916.             }  
  917.   
  918.             int program = GLES20.glCreateProgram();  
  919.             if (program == 0) {  
  920.                 Log.e(TAG, "Could not create program");  
  921.             }  
  922.             GLES20.glAttachShader(program, vertexShader);  
  923.             checkGlError("glAttachShader");  
  924.             GLES20.glAttachShader(program, pixelShader);  
  925.             checkGlError("glAttachShader");  
  926.             GLES20.glLinkProgram(program);  
  927.             int[] linkStatus = new int[1];  
  928.             GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);  
  929.             if (linkStatus[0] != GLES20.GL_TRUE) {  
  930.                 Log.e(TAG, "Could not link program: ");  
  931.                 Log.e(TAG, GLES20.glGetProgramInfoLog(program));  
  932.                 GLES20.glDeleteProgram(program);  
  933.                 program = 0;  
  934.             }  
  935.             return program;  
  936.         }  
  937.   
  938.         public void checkGlError(String op) {  
  939.             int error;  
  940.             while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {  
  941.                 Log.e(TAG, op + ": glError " + error);  
  942.                 throw new RuntimeException(op + ": glError " + error);  
  943.             }  
  944.         }  
  945.   
  946.         public static void checkLocation(int location, String label) {  
  947.             if (location < 0) {  
  948.                 throw new RuntimeException("Unable to locate '" + label + "' in program");  
  949.             }  
  950.         }  
  951.     }  
  952. }  
  953.     




반응형
Posted by Real_G