| OLD | NEW |
| 1 /* | 1 /* |
| 2 * libjingle | 2 * libjingle |
| 3 * Copyright 2014 Google Inc. | 3 * Copyright 2014 Google Inc. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are met: | 6 * modification, are permitted provided that the following conditions are met: |
| 7 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright notice, | 8 * 1. Redistributions of source code must retain the above copyright notice, |
| 9 * this list of conditions and the following disclaimer. | 9 * this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, | 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 import android.graphics.SurfaceTexture; | 30 import android.graphics.SurfaceTexture; |
| 31 import android.media.MediaCodec; | 31 import android.media.MediaCodec; |
| 32 import android.media.MediaCodecInfo; | 32 import android.media.MediaCodecInfo; |
| 33 import android.media.MediaCodecInfo.CodecCapabilities; | 33 import android.media.MediaCodecInfo.CodecCapabilities; |
| 34 import android.media.MediaCodecList; | 34 import android.media.MediaCodecList; |
| 35 import android.media.MediaFormat; | 35 import android.media.MediaFormat; |
| 36 import android.opengl.EGLContext; | 36 import android.opengl.EGLContext; |
| 37 import android.opengl.GLES11Ext; | 37 import android.opengl.GLES11Ext; |
| 38 import android.opengl.GLES20; | 38 import android.opengl.GLES20; |
| 39 import android.os.Build; | 39 import android.os.Build; |
| 40 import android.util.Log; | |
| 41 import android.view.Surface; | 40 import android.view.Surface; |
| 42 | 41 |
| 42 import org.webrtc.Logging; |
| 43 |
| 43 import java.nio.ByteBuffer; | 44 import java.nio.ByteBuffer; |
| 44 | 45 |
| 45 // Java-side of peerconnection_jni.cc:MediaCodecVideoDecoder. | 46 // Java-side of peerconnection_jni.cc:MediaCodecVideoDecoder. |
| 46 // This class is an implementation detail of the Java PeerConnection API. | 47 // This class is an implementation detail of the Java PeerConnection API. |
| 47 // MediaCodec is thread-hostile so this class must be operated on a single | 48 // MediaCodec is thread-hostile so this class must be operated on a single |
| 48 // thread. | 49 // thread. |
| 49 public class MediaCodecVideoDecoder { | 50 public class MediaCodecVideoDecoder { |
| 50 // This class is constructed, operated, and destroyed by its C++ incarnation, | 51 // This class is constructed, operated, and destroyed by its C++ incarnation, |
| 51 // so the class and its methods have non-public visibility. The API this | 52 // so the class and its methods have non-public visibility. The API this |
| 52 // class exposes aims to mimic the webrtc::VideoDecoder API as closely as | 53 // class exposes aims to mimic the webrtc::VideoDecoder API as closely as |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 121 String name = null; | 122 String name = null; |
| 122 for (String mimeType : info.getSupportedTypes()) { | 123 for (String mimeType : info.getSupportedTypes()) { |
| 123 if (mimeType.equals(mime)) { | 124 if (mimeType.equals(mime)) { |
| 124 name = info.getName(); | 125 name = info.getName(); |
| 125 break; | 126 break; |
| 126 } | 127 } |
| 127 } | 128 } |
| 128 if (name == null) { | 129 if (name == null) { |
| 129 continue; // No HW support in this codec; try the next one. | 130 continue; // No HW support in this codec; try the next one. |
| 130 } | 131 } |
| 131 Log.v(TAG, "Found candidate decoder " + name); | 132 Logging.v(TAG, "Found candidate decoder " + name); |
| 132 | 133 |
| 133 // Check if this is supported decoder. | 134 // Check if this is supported decoder. |
| 134 boolean supportedCodec = false; | 135 boolean supportedCodec = false; |
| 135 for (String codecPrefix : supportedCodecPrefixes) { | 136 for (String codecPrefix : supportedCodecPrefixes) { |
| 136 if (name.startsWith(codecPrefix)) { | 137 if (name.startsWith(codecPrefix)) { |
| 137 supportedCodec = true; | 138 supportedCodec = true; |
| 138 break; | 139 break; |
| 139 } | 140 } |
| 140 } | 141 } |
| 141 if (!supportedCodec) { | 142 if (!supportedCodec) { |
| 142 continue; | 143 continue; |
| 143 } | 144 } |
| 144 | 145 |
| 145 // Check if codec supports either yuv420 or nv12. | 146 // Check if codec supports either yuv420 or nv12. |
| 146 CodecCapabilities capabilities = | 147 CodecCapabilities capabilities = |
| 147 info.getCapabilitiesForType(mime); | 148 info.getCapabilitiesForType(mime); |
| 148 for (int colorFormat : capabilities.colorFormats) { | 149 for (int colorFormat : capabilities.colorFormats) { |
| 149 Log.v(TAG, " Color: 0x" + Integer.toHexString(colorFormat)); | 150 Logging.v(TAG, " Color: 0x" + Integer.toHexString(colorFormat)); |
| 150 } | 151 } |
| 151 for (int supportedColorFormat : supportedColorList) { | 152 for (int supportedColorFormat : supportedColorList) { |
| 152 for (int codecColorFormat : capabilities.colorFormats) { | 153 for (int codecColorFormat : capabilities.colorFormats) { |
| 153 if (codecColorFormat == supportedColorFormat) { | 154 if (codecColorFormat == supportedColorFormat) { |
| 154 // Found supported HW decoder. | 155 // Found supported HW decoder. |
| 155 Log.d(TAG, "Found target decoder " + name + | 156 Logging.d(TAG, "Found target decoder " + name + |
| 156 ". Color: 0x" + Integer.toHexString(codecColorFormat)); | 157 ". Color: 0x" + Integer.toHexString(codecColorFormat)); |
| 157 return new DecoderProperties(name, codecColorFormat); | 158 return new DecoderProperties(name, codecColorFormat); |
| 158 } | 159 } |
| 159 } | 160 } |
| 160 } | 161 } |
| 161 } | 162 } |
| 162 return null; // No HW decoder. | 163 return null; // No HW decoder. |
| 163 } | 164 } |
| 164 | 165 |
| 165 public static boolean isVp8HwSupported() { | 166 public static boolean isVp8HwSupported() { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 192 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { | 193 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { |
| 193 mime = H264_MIME_TYPE; | 194 mime = H264_MIME_TYPE; |
| 194 supportedCodecPrefixes = supportedH264HwCodecPrefixes; | 195 supportedCodecPrefixes = supportedH264HwCodecPrefixes; |
| 195 } else { | 196 } else { |
| 196 throw new RuntimeException("Non supported codec " + type); | 197 throw new RuntimeException("Non supported codec " + type); |
| 197 } | 198 } |
| 198 DecoderProperties properties = findDecoder(mime, supportedCodecPrefixes); | 199 DecoderProperties properties = findDecoder(mime, supportedCodecPrefixes); |
| 199 if (properties == null) { | 200 if (properties == null) { |
| 200 throw new RuntimeException("Cannot find HW decoder for " + type); | 201 throw new RuntimeException("Cannot find HW decoder for " + type); |
| 201 } | 202 } |
| 202 Log.d(TAG, "Java initDecode: " + type + " : "+ width + " x " + height + | 203 Logging.d(TAG, "Java initDecode: " + type + " : "+ width + " x " + height + |
| 203 ". Color: 0x" + Integer.toHexString(properties.colorFormat) + | 204 ". Color: 0x" + Integer.toHexString(properties.colorFormat) + |
| 204 ". Use Surface: " + useSurface); | 205 ". Use Surface: " + useSurface); |
| 205 if (sharedContext != null) { | 206 if (sharedContext != null) { |
| 206 Log.d(TAG, "Decoder shared EGL Context: " + sharedContext); | 207 Logging.d(TAG, "Decoder shared EGL Context: " + sharedContext); |
| 207 } | 208 } |
| 208 mediaCodecThread = Thread.currentThread(); | 209 mediaCodecThread = Thread.currentThread(); |
| 209 try { | 210 try { |
| 210 Surface decodeSurface = null; | 211 Surface decodeSurface = null; |
| 211 this.width = width; | 212 this.width = width; |
| 212 this.height = height; | 213 this.height = height; |
| 213 stride = width; | 214 stride = width; |
| 214 sliceHeight = height; | 215 sliceHeight = height; |
| 215 | 216 |
| 216 if (useSurface) { | 217 if (useSurface) { |
| 217 // Create shared EGL context. | 218 // Create shared EGL context. |
| 218 eglBase = new EglBase(sharedContext, EglBase.ConfigType.PIXEL_BUFFER); | 219 eglBase = new EglBase(sharedContext, EglBase.ConfigType.PIXEL_BUFFER); |
| 219 eglBase.createDummyPbufferSurface(); | 220 eglBase.createDummyPbufferSurface(); |
| 220 eglBase.makeCurrent(); | 221 eglBase.makeCurrent(); |
| 221 | 222 |
| 222 // Create output surface | 223 // Create output surface |
| 223 textureID = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); | 224 textureID = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); |
| 224 Log.d(TAG, "Video decoder TextureID = " + textureID); | 225 Logging.d(TAG, "Video decoder TextureID = " + textureID); |
| 225 surfaceTexture = new SurfaceTexture(textureID); | 226 surfaceTexture = new SurfaceTexture(textureID); |
| 226 surface = new Surface(surfaceTexture); | 227 surface = new Surface(surfaceTexture); |
| 227 decodeSurface = surface; | 228 decodeSurface = surface; |
| 228 } | 229 } |
| 229 | 230 |
| 230 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); | 231 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); |
| 231 if (!useSurface) { | 232 if (!useSurface) { |
| 232 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); | 233 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); |
| 233 } | 234 } |
| 234 Log.d(TAG, " Format: " + format); | 235 Logging.d(TAG, " Format: " + format); |
| 235 mediaCodec = | 236 mediaCodec = |
| 236 MediaCodecVideoEncoder.createByCodecName(properties.codecName); | 237 MediaCodecVideoEncoder.createByCodecName(properties.codecName); |
| 237 if (mediaCodec == null) { | 238 if (mediaCodec == null) { |
| 238 return false; | 239 return false; |
| 239 } | 240 } |
| 240 mediaCodec.configure(format, decodeSurface, null, 0); | 241 mediaCodec.configure(format, decodeSurface, null, 0); |
| 241 mediaCodec.start(); | 242 mediaCodec.start(); |
| 242 colorFormat = properties.colorFormat; | 243 colorFormat = properties.colorFormat; |
| 243 outputBuffers = mediaCodec.getOutputBuffers(); | 244 outputBuffers = mediaCodec.getOutputBuffers(); |
| 244 inputBuffers = mediaCodec.getInputBuffers(); | 245 inputBuffers = mediaCodec.getInputBuffers(); |
| 245 Log.d(TAG, "Input buffers: " + inputBuffers.length + | 246 Logging.d(TAG, "Input buffers: " + inputBuffers.length + |
| 246 ". Output buffers: " + outputBuffers.length); | 247 ". Output buffers: " + outputBuffers.length); |
| 247 return true; | 248 return true; |
| 248 } catch (IllegalStateException e) { | 249 } catch (IllegalStateException e) { |
| 249 Log.e(TAG, "initDecode failed", e); | 250 Logging.e(TAG, "initDecode failed", e); |
| 250 return false; | 251 return false; |
| 251 } | 252 } |
| 252 } | 253 } |
| 253 | 254 |
| 254 private void release() { | 255 private void release() { |
| 255 Log.d(TAG, "Java releaseDecoder"); | 256 Logging.d(TAG, "Java releaseDecoder"); |
| 256 checkOnMediaCodecThread(); | 257 checkOnMediaCodecThread(); |
| 257 try { | 258 try { |
| 258 mediaCodec.stop(); | 259 mediaCodec.stop(); |
| 259 mediaCodec.release(); | 260 mediaCodec.release(); |
| 260 } catch (IllegalStateException e) { | 261 } catch (IllegalStateException e) { |
| 261 Log.e(TAG, "release failed", e); | 262 Logging.e(TAG, "release failed", e); |
| 262 } | 263 } |
| 263 mediaCodec = null; | 264 mediaCodec = null; |
| 264 mediaCodecThread = null; | 265 mediaCodecThread = null; |
| 265 if (useSurface) { | 266 if (useSurface) { |
| 266 surface.release(); | 267 surface.release(); |
| 267 if (textureID != 0) { | 268 if (textureID != 0) { |
| 268 Log.d(TAG, "Delete video decoder TextureID " + textureID); | 269 Logging.d(TAG, "Delete video decoder TextureID " + textureID); |
| 269 GLES20.glDeleteTextures(1, new int[] {textureID}, 0); | 270 GLES20.glDeleteTextures(1, new int[] {textureID}, 0); |
| 270 textureID = 0; | 271 textureID = 0; |
| 271 } | 272 } |
| 272 eglBase.release(); | 273 eglBase.release(); |
| 273 eglBase = null; | 274 eglBase = null; |
| 274 } | 275 } |
| 275 } | 276 } |
| 276 | 277 |
| 277 // Dequeue an input buffer and return its index, -1 if no input buffer is | 278 // Dequeue an input buffer and return its index, -1 if no input buffer is |
| 278 // available, or -2 if the codec is no longer operative. | 279 // available, or -2 if the codec is no longer operative. |
| 279 private int dequeueInputBuffer() { | 280 private int dequeueInputBuffer() { |
| 280 checkOnMediaCodecThread(); | 281 checkOnMediaCodecThread(); |
| 281 try { | 282 try { |
| 282 return mediaCodec.dequeueInputBuffer(DEQUEUE_INPUT_TIMEOUT); | 283 return mediaCodec.dequeueInputBuffer(DEQUEUE_INPUT_TIMEOUT); |
| 283 } catch (IllegalStateException e) { | 284 } catch (IllegalStateException e) { |
| 284 Log.e(TAG, "dequeueIntputBuffer failed", e); | 285 Logging.e(TAG, "dequeueIntputBuffer failed", e); |
| 285 return -2; | 286 return -2; |
| 286 } | 287 } |
| 287 } | 288 } |
| 288 | 289 |
| 289 private boolean queueInputBuffer( | 290 private boolean queueInputBuffer( |
| 290 int inputBufferIndex, int size, long timestampUs) { | 291 int inputBufferIndex, int size, long timestampUs) { |
| 291 checkOnMediaCodecThread(); | 292 checkOnMediaCodecThread(); |
| 292 try { | 293 try { |
| 293 inputBuffers[inputBufferIndex].position(0); | 294 inputBuffers[inputBufferIndex].position(0); |
| 294 inputBuffers[inputBufferIndex].limit(size); | 295 inputBuffers[inputBufferIndex].limit(size); |
| 295 mediaCodec.queueInputBuffer(inputBufferIndex, 0, size, timestampUs, 0); | 296 mediaCodec.queueInputBuffer(inputBufferIndex, 0, size, timestampUs, 0); |
| 296 return true; | 297 return true; |
| 297 } | 298 } |
| 298 catch (IllegalStateException e) { | 299 catch (IllegalStateException e) { |
| 299 Log.e(TAG, "decode failed", e); | 300 Logging.e(TAG, "decode failed", e); |
| 300 return false; | 301 return false; |
| 301 } | 302 } |
| 302 } | 303 } |
| 303 | 304 |
| 304 // Helper struct for dequeueOutputBuffer() below. | 305 // Helper struct for dequeueOutputBuffer() below. |
| 305 private static class DecoderOutputBufferInfo { | 306 private static class DecoderOutputBufferInfo { |
| 306 public DecoderOutputBufferInfo( | 307 public DecoderOutputBufferInfo( |
| 307 int index, int offset, int size, long presentationTimestampUs) { | 308 int index, int offset, int size, long presentationTimestampUs) { |
| 308 this.index = index; | 309 this.index = index; |
| 309 this.offset = offset; | 310 this.offset = offset; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 321 // buffer available or -2 if error happened. | 322 // buffer available or -2 if error happened. |
| 322 private DecoderOutputBufferInfo dequeueOutputBuffer(int dequeueTimeoutUs) { | 323 private DecoderOutputBufferInfo dequeueOutputBuffer(int dequeueTimeoutUs) { |
| 323 checkOnMediaCodecThread(); | 324 checkOnMediaCodecThread(); |
| 324 try { | 325 try { |
| 325 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); | 326 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
| 326 int result = mediaCodec.dequeueOutputBuffer(info, dequeueTimeoutUs); | 327 int result = mediaCodec.dequeueOutputBuffer(info, dequeueTimeoutUs); |
| 327 while (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED || | 328 while (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED || |
| 328 result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { | 329 result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { |
| 329 if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { | 330 if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { |
| 330 outputBuffers = mediaCodec.getOutputBuffers(); | 331 outputBuffers = mediaCodec.getOutputBuffers(); |
| 331 Log.d(TAG, "Decoder output buffers changed: " + outputBuffers.length); | 332 Logging.d(TAG, "Decoder output buffers changed: " + outputBuffers.leng
th); |
| 332 } else if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { | 333 } else if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { |
| 333 MediaFormat format = mediaCodec.getOutputFormat(); | 334 MediaFormat format = mediaCodec.getOutputFormat(); |
| 334 Log.d(TAG, "Decoder format changed: " + format.toString()); | 335 Logging.d(TAG, "Decoder format changed: " + format.toString()); |
| 335 width = format.getInteger(MediaFormat.KEY_WIDTH); | 336 width = format.getInteger(MediaFormat.KEY_WIDTH); |
| 336 height = format.getInteger(MediaFormat.KEY_HEIGHT); | 337 height = format.getInteger(MediaFormat.KEY_HEIGHT); |
| 337 if (!useSurface && format.containsKey(MediaFormat.KEY_COLOR_FORMAT)) { | 338 if (!useSurface && format.containsKey(MediaFormat.KEY_COLOR_FORMAT)) { |
| 338 colorFormat = format.getInteger(MediaFormat.KEY_COLOR_FORMAT); | 339 colorFormat = format.getInteger(MediaFormat.KEY_COLOR_FORMAT); |
| 339 Log.d(TAG, "Color: 0x" + Integer.toHexString(colorFormat)); | 340 Logging.d(TAG, "Color: 0x" + Integer.toHexString(colorFormat)); |
| 340 // Check if new color space is supported. | 341 // Check if new color space is supported. |
| 341 boolean validColorFormat = false; | 342 boolean validColorFormat = false; |
| 342 for (int supportedColorFormat : supportedColorList) { | 343 for (int supportedColorFormat : supportedColorList) { |
| 343 if (colorFormat == supportedColorFormat) { | 344 if (colorFormat == supportedColorFormat) { |
| 344 validColorFormat = true; | 345 validColorFormat = true; |
| 345 break; | 346 break; |
| 346 } | 347 } |
| 347 } | 348 } |
| 348 if (!validColorFormat) { | 349 if (!validColorFormat) { |
| 349 Log.e(TAG, "Non supported color format"); | 350 Logging.e(TAG, "Non supported color format"); |
| 350 return new DecoderOutputBufferInfo(-1, 0, 0, -1); | 351 return new DecoderOutputBufferInfo(-1, 0, 0, -1); |
| 351 } | 352 } |
| 352 } | 353 } |
| 353 if (format.containsKey("stride")) { | 354 if (format.containsKey("stride")) { |
| 354 stride = format.getInteger("stride"); | 355 stride = format.getInteger("stride"); |
| 355 } | 356 } |
| 356 if (format.containsKey("slice-height")) { | 357 if (format.containsKey("slice-height")) { |
| 357 sliceHeight = format.getInteger("slice-height"); | 358 sliceHeight = format.getInteger("slice-height"); |
| 358 } | 359 } |
| 359 Log.d(TAG, "Frame stride and slice height: " | 360 Logging.d(TAG, "Frame stride and slice height: " |
| 360 + stride + " x " + sliceHeight); | 361 + stride + " x " + sliceHeight); |
| 361 stride = Math.max(width, stride); | 362 stride = Math.max(width, stride); |
| 362 sliceHeight = Math.max(height, sliceHeight); | 363 sliceHeight = Math.max(height, sliceHeight); |
| 363 } | 364 } |
| 364 result = mediaCodec.dequeueOutputBuffer(info, dequeueTimeoutUs); | 365 result = mediaCodec.dequeueOutputBuffer(info, dequeueTimeoutUs); |
| 365 } | 366 } |
| 366 if (result >= 0) { | 367 if (result >= 0) { |
| 367 return new DecoderOutputBufferInfo(result, info.offset, info.size, | 368 return new DecoderOutputBufferInfo(result, info.offset, info.size, |
| 368 info.presentationTimeUs); | 369 info.presentationTimeUs); |
| 369 } | 370 } |
| 370 return null; | 371 return null; |
| 371 } catch (IllegalStateException e) { | 372 } catch (IllegalStateException e) { |
| 372 Log.e(TAG, "dequeueOutputBuffer failed", e); | 373 Logging.e(TAG, "dequeueOutputBuffer failed", e); |
| 373 return new DecoderOutputBufferInfo(-1, 0, 0, -1); | 374 return new DecoderOutputBufferInfo(-1, 0, 0, -1); |
| 374 } | 375 } |
| 375 } | 376 } |
| 376 | 377 |
| 377 // Release a dequeued output buffer back to the codec for re-use. Return | 378 // Release a dequeued output buffer back to the codec for re-use. Return |
| 378 // false if the codec is no longer operable. | 379 // false if the codec is no longer operable. |
| 379 private boolean releaseOutputBuffer(int index) { | 380 private boolean releaseOutputBuffer(int index) { |
| 380 checkOnMediaCodecThread(); | 381 checkOnMediaCodecThread(); |
| 381 try { | 382 try { |
| 382 mediaCodec.releaseOutputBuffer(index, useSurface); | 383 mediaCodec.releaseOutputBuffer(index, useSurface); |
| 383 return true; | 384 return true; |
| 384 } catch (IllegalStateException e) { | 385 } catch (IllegalStateException e) { |
| 385 Log.e(TAG, "releaseOutputBuffer failed", e); | 386 Logging.e(TAG, "releaseOutputBuffer failed", e); |
| 386 return false; | 387 return false; |
| 387 } | 388 } |
| 388 } | 389 } |
| 389 } | 390 } |
| OLD | NEW |