1b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org/* 2b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * libjingle 3b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * Copyright 2013, Google Inc. 4b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * 5b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * Redistribution and use in source and binary forms, with or without 6b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * modification, are permitted provided that the following conditions are met: 7b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * 8b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * 1. Redistributions of source code must retain the above copyright notice, 9b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * this list of conditions and the following disclaimer. 10b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * 2. Redistributions in binary form must reproduce the above copyright notice, 11b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * this list of conditions and the following disclaimer in the documentation 12b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * and/or other materials provided with the distribution. 13b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * 3. The name of the author may not be used to endorse or promote products 14b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * derived from this software without specific prior written permission. 15b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * 16b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org */ 27b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 28b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 29b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.orgpackage org.webrtc; 30b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 31b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.orgimport android.media.MediaCodec; 32b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.orgimport android.media.MediaCodecInfo; 33b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.orgimport android.media.MediaCodecList; 34b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.orgimport android.media.MediaFormat; 351c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.orgimport android.media.MediaCodecInfo.CodecCapabilities; 36b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.orgimport android.os.Build; 37b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.orgimport android.os.Bundle; 38b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.orgimport android.util.Log; 39b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 40b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.orgimport java.nio.ByteBuffer; 41b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 42b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org// Java-side of peerconnection_jni.cc:MediaCodecVideoEncoder. 43b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org// This class is an implementation detail of the Java PeerConnection API. 44b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org// MediaCodec is thread-hostile so this class must be operated on a single 45b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org// thread. 46b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.orgclass MediaCodecVideoEncoder { 47b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // This class is constructed, operated, and destroyed by its C++ incarnation, 48b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // so the class and its methods have non-public visibility. The API this 49b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // class exposes aims to mimic the webrtc::VideoEncoder API as closely as 50b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // possibly to minimize the amount of translation work necessary. 51b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 52b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private static final String TAG = "MediaCodecVideoEncoder"; 53b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 54b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private static final int DEQUEUE_TIMEOUT = 0; // Non-blocking, no wait. 551c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org private Thread mediaCodecThread; 56b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private MediaCodec mediaCodec; 57b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private ByteBuffer[] outputBuffers; 58b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8"; 591c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org // List of supported HW VP8 codecs. 601c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org private static final String[] supportedHwCodecPrefixes = 611c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org {"OMX.qcom.", "OMX.Nvidia." }; 621c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org // Bitrate mode 631c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org private static final int VIDEO_ControlRateConstant = 2; 641c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org // NV12 color format supported by QCOM codec, but not declared in MediaCodec - 651c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org // see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h 661c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org private static final int 671c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04; 681c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org // Allowable color formats supported by codec - in order of preference. 691c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org private static final int[] supportedColorList = { 701c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org CodecCapabilities.COLOR_FormatYUV420Planar, 711c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org CodecCapabilities.COLOR_FormatYUV420SemiPlanar, 721c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, 731c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m 741c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org }; 751c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org private int colorFormat; 76b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 77b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private MediaCodecVideoEncoder() {} 78b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 791c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org // Helper struct for findVp8HwEncoder() below. 801c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org private static class EncoderProperties { 811c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org EncoderProperties(String codecName, int colorFormat) { 821c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org this.codecName = codecName; 831c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org this.colorFormat = colorFormat; 84b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 851c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org public final String codecName; // OpenMax component name for VP8 codec. 861c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org public final int colorFormat; // Color format supported by codec. 871c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org } 881c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org 891c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org private static EncoderProperties findVp8HwEncoder() { 901c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) 911c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org return null; // MediaCodec.setParameters is missing. 92b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 93b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) { 94b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); 951c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org if (!info.isEncoder()) { 96b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org continue; 971c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org } 98b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org String name = null; 99b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org for (String mimeType : info.getSupportedTypes()) { 100b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org if (mimeType.equals(VP8_MIME_TYPE)) { 101b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org name = info.getName(); 102b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org break; 103b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 104b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 1051c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org if (name == null) { 106b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org continue; // No VP8 support in this codec; try the next one. 107b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 1081c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org Log.d(TAG, "Found candidate encoder " + name); 1091c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org CodecCapabilities capabilities = 1101c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org info.getCapabilitiesForType(VP8_MIME_TYPE); 1111c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org for (int colorFormat : capabilities.colorFormats) { 1121c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org Log.d(TAG, " Color: 0x" + Integer.toHexString(colorFormat)); 1131c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org } 1141c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org 1151c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org // Check if this is supported HW encoder 1161c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org for (String hwCodecPrefix : supportedHwCodecPrefixes) { 1171c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org if (!name.startsWith(hwCodecPrefix)) { 1181c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org continue; 1191c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org } 1201c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org // Check if codec supports either yuv420 or nv12 1211c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org for (int supportedColorFormat : supportedColorList) { 1221c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org for (int codecColorFormat : capabilities.colorFormats) { 1231c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org if (codecColorFormat == supportedColorFormat) { 1241c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org // Found supported HW VP8 encoder 1251c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org Log.d(TAG, "Found target encoder " + name + 1261c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org ". Color: 0x" + Integer.toHexString(codecColorFormat)); 1271c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org return new EncoderProperties(name, codecColorFormat); 1281c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org } 1291c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org } 1301c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org } 1311c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org } 132b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 1331c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org return null; // No HW VP8 encoder. 1341c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org } 1351c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org 1361c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org private static boolean isPlatformSupported() { 1371c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org return findVp8HwEncoder() != null; 138b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 139b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 140b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private static int bitRate(int kbps) { 141b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // webrtc "kilo" means 1000, not 1024. Apparently. 142b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // (and the price for overshooting is frame-dropping as webrtc enforces its 143b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // bandwidth estimation, which is unpleasant). 14419adba6dbc0135742b07b9f138c4a266c8de56effischman@webrtc.org // Since the HW encoder in the N5 overshoots, we clamp to a bit less than 14519adba6dbc0135742b07b9f138c4a266c8de56effischman@webrtc.org // the requested rate. Sad but true. Bug 3194. 14619adba6dbc0135742b07b9f138c4a266c8de56effischman@webrtc.org return kbps * 950; 147b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 148b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 149b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private void checkOnMediaCodecThread() { 150b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org if (mediaCodecThread.getId() != Thread.currentThread().getId()) { 151b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org throw new RuntimeException( 152b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org "MediaCodecVideoEncoder previously operated on " + mediaCodecThread + 153b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org " but is now called on " + Thread.currentThread()); 154b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 155b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 156b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 157b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // Return the array of input buffers, or null on failure. 1585a09f52263e47d9b6f11edf72cb7f19384c9a477glaznev@webrtc.org private ByteBuffer[] initEncode(int width, int height, int kbps, int fps) { 1591c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org Log.d(TAG, "initEncode: " + width + " x " + height + 1605a09f52263e47d9b6f11edf72cb7f19384c9a477glaznev@webrtc.org ". @ " + kbps + " kbps. Fps: " + fps + 1615a09f52263e47d9b6f11edf72cb7f19384c9a477glaznev@webrtc.org ". Color: 0x" + Integer.toHexString(colorFormat)); 162b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org if (mediaCodecThread != null) { 163b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org throw new RuntimeException("Forgot to release()?"); 164b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 1651c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org EncoderProperties properties = findVp8HwEncoder(); 1661c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org if (properties == null) { 1671c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org throw new RuntimeException("Can not find HW VP8 encoder"); 1681c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org } 169b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org mediaCodecThread = Thread.currentThread(); 170b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org try { 171b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org MediaFormat format = 172b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org MediaFormat.createVideoFormat(VP8_MIME_TYPE, width, height); 173b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate(kbps)); 1741c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); 1751c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); 1761c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org // Default WebRTC settings 1775a09f52263e47d9b6f11edf72cb7f19384c9a477glaznev@webrtc.org format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); 1781c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 100); 1791c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org Log.d(TAG, " Format: " + format); 1801c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org mediaCodec = MediaCodec.createByCodecName(properties.codecName); 181b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org if (mediaCodec == null) { 182b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return null; 183b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 184b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org mediaCodec.configure( 185b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 186b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org mediaCodec.start(); 1871c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org colorFormat = properties.colorFormat; 188b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org outputBuffers = mediaCodec.getOutputBuffers(); 1891c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); 1901c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org Log.d(TAG, "Input buffers: " + inputBuffers.length + 1911c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org ". Output buffers: " + outputBuffers.length); 1921c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org return inputBuffers; 193b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } catch (IllegalStateException e) { 194b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org Log.e(TAG, "initEncode failed", e); 195b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return null; 196b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 197b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 198b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 199b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private boolean encode( 200b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org boolean isKeyframe, int inputBuffer, int size, 201b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org long presentationTimestampUs) { 202b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org checkOnMediaCodecThread(); 203b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org try { 204b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org if (isKeyframe) { 205b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could 206b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // indicate this in queueInputBuffer() below and guarantee _this_ frame 207b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // be encoded as a key frame, but sadly that flag is ignored. Instead, 208b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // we request a key frame "soon". 2091c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org Log.d(TAG, "Sync frame request"); 210b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org Bundle b = new Bundle(); 211b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); 212b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org mediaCodec.setParameters(b); 213b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 214b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org mediaCodec.queueInputBuffer( 215b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org inputBuffer, 0, size, presentationTimestampUs, 0); 216b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return true; 217b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 218b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org catch (IllegalStateException e) { 219b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org Log.e(TAG, "encode failed", e); 220b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return false; 221b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 222b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 223b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 224b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private void release() { 2251c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org Log.d(TAG, "release"); 226b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org checkOnMediaCodecThread(); 227b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org try { 228b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org mediaCodec.stop(); 229b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org mediaCodec.release(); 230b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } catch (IllegalStateException e) { 231b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org Log.e(TAG, "release failed", e); 232b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 233b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org mediaCodec = null; 234b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org mediaCodecThread = null; 235b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 236b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 237b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private boolean setRates(int kbps, int frameRateIgnored) { 2381c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org // frameRate argument is ignored - HW encoder is supposed to use 2391c1377b5979f10d87a7b8e1e2c2b929034857f3eglaznev@webrtc.org // video frame timestamps for bit allocation. 240b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org checkOnMediaCodecThread(); 2415a09f52263e47d9b6f11edf72cb7f19384c9a477glaznev@webrtc.org Log.v(TAG, "setRates: " + kbps + " kbps. Fps: " + frameRateIgnored); 242b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org try { 243b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org Bundle params = new Bundle(); 244b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org params.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bitRate(kbps)); 245b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org mediaCodec.setParameters(params); 246b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return true; 247b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } catch (IllegalStateException e) { 248b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org Log.e(TAG, "setRates failed", e); 249b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return false; 250b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 251b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 252b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 253b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // Dequeue an input buffer and return its index, -1 if no input buffer is 254b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // available, or -2 if the codec is no longer operative. 255b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private int dequeueInputBuffer() { 256b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org checkOnMediaCodecThread(); 257b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org try { 258b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return mediaCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT); 259b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } catch (IllegalStateException e) { 260b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org Log.e(TAG, "dequeueIntputBuffer failed", e); 261b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return -2; 262b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 263b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 264b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 265b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // Helper struct for dequeueOutputBuffer() below. 266b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private static class OutputBufferInfo { 267b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org public OutputBufferInfo( 268b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org int index, ByteBuffer buffer, boolean isKeyFrame, 269b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org long presentationTimestampUs) { 270b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org this.index = index; 271b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org this.buffer = buffer; 272b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org this.isKeyFrame = isKeyFrame; 273b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org this.presentationTimestampUs = presentationTimestampUs; 274b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 275b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 276b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private final int index; 277b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private final ByteBuffer buffer; 278b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private final boolean isKeyFrame; 279b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private final long presentationTimestampUs; 280b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 281b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 282b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // Dequeue and return an output buffer, or null if no output is ready. Return 283b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // a fake OutputBufferInfo with index -1 if the codec is no longer operable. 284b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private OutputBufferInfo dequeueOutputBuffer() { 285b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org checkOnMediaCodecThread(); 286b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org try { 287b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 288b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org int result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); 289b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org if (result >= 0) { 290b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // MediaCodec doesn't care about Buffer position/remaining/etc so we can 291b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // mess with them to get a slice and avoid having to pass extra 292b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // (BufferInfo-related) parameters back to C++. 293b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org ByteBuffer outputBuffer = outputBuffers[result].duplicate(); 294b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org outputBuffer.position(info.offset); 295b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org outputBuffer.limit(info.offset + info.size); 296b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org boolean isKeyFrame = 297b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org (info.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0; 2985a09f52263e47d9b6f11edf72cb7f19384c9a477glaznev@webrtc.org if (isKeyFrame) { 2995a09f52263e47d9b6f11edf72cb7f19384c9a477glaznev@webrtc.org Log.d(TAG, "Sync frame generated"); 3005a09f52263e47d9b6f11edf72cb7f19384c9a477glaznev@webrtc.org } 301b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return new OutputBufferInfo( 302b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org result, outputBuffer.slice(), isKeyFrame, info.presentationTimeUs); 303b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 304b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org outputBuffers = mediaCodec.getOutputBuffers(); 305b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return dequeueOutputBuffer(); 306b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } else if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 307b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return dequeueOutputBuffer(); 308b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } else if (result == MediaCodec.INFO_TRY_AGAIN_LATER) { 309b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return null; 310b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 311b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org throw new RuntimeException("dequeueOutputBuffer: " + result); 312b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } catch (IllegalStateException e) { 313b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org Log.e(TAG, "dequeueOutputBuffer failed", e); 314b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return new OutputBufferInfo(-1, null, false, -1); 315b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 316b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 317b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org 318b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // Release a dequeued output buffer back to the codec for re-use. Return 319b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org // false if the codec is no longer operable. 320b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org private boolean releaseOutputBuffer(int index) { 321b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org checkOnMediaCodecThread(); 322b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org try { 323b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org mediaCodec.releaseOutputBuffer(index, false); 324b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return true; 325b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } catch (IllegalStateException e) { 326b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org Log.e(TAG, "releaseOutputBuffer failed", e); 327b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org return false; 328b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 329b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org } 330b0ffe7003e63b7283f3897a919aa8d2905d7a313fischman@webrtc.org} 331