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 25 matching lines...) Expand all Loading... |
36 import android.opengl.GLES11Ext; | 36 import android.opengl.GLES11Ext; |
37 import android.opengl.GLES20; | 37 import android.opengl.GLES20; |
38 import android.os.Build; | 38 import android.os.Build; |
39 import android.view.Surface; | 39 import android.view.Surface; |
40 | 40 |
41 import org.webrtc.Logging; | 41 import org.webrtc.Logging; |
42 | 42 |
43 import java.nio.ByteBuffer; | 43 import java.nio.ByteBuffer; |
44 import java.util.Arrays; | 44 import java.util.Arrays; |
45 import java.util.List; | 45 import java.util.List; |
| 46 import java.util.concurrent.CountDownLatch; |
46 | 47 |
47 import javax.microedition.khronos.egl.EGLContext; | 48 import javax.microedition.khronos.egl.EGLContext; |
48 | 49 |
49 // Java-side of peerconnection_jni.cc:MediaCodecVideoDecoder. | 50 // Java-side of peerconnection_jni.cc:MediaCodecVideoDecoder. |
50 // This class is an implementation detail of the Java PeerConnection API. | 51 // This class is an implementation detail of the Java PeerConnection API. |
51 // MediaCodec is thread-hostile so this class must be operated on a single | |
52 // thread. | |
53 public class MediaCodecVideoDecoder { | 52 public class MediaCodecVideoDecoder { |
54 // This class is constructed, operated, and destroyed by its C++ incarnation, | 53 // This class is constructed, operated, and destroyed by its C++ incarnation, |
55 // so the class and its methods have non-public visibility. The API this | 54 // so the class and its methods have non-public visibility. The API this |
56 // class exposes aims to mimic the webrtc::VideoDecoder API as closely as | 55 // class exposes aims to mimic the webrtc::VideoDecoder API as closely as |
57 // possibly to minimize the amount of translation work necessary. | 56 // possibly to minimize the amount of translation work necessary. |
58 | 57 |
59 private static final String TAG = "MediaCodecVideoDecoder"; | 58 private static final String TAG = "MediaCodecVideoDecoder"; |
60 | 59 |
61 // Tracks webrtc::VideoCodecType. | 60 // Tracks webrtc::VideoCodecType. |
62 public enum VideoCodecType { | 61 public enum VideoCodecType { |
63 VIDEO_CODEC_VP8, | 62 VIDEO_CODEC_VP8, |
64 VIDEO_CODEC_VP9, | 63 VIDEO_CODEC_VP9, |
65 VIDEO_CODEC_H264 | 64 VIDEO_CODEC_H264 |
66 } | 65 } |
67 | 66 |
68 private static final int DEQUEUE_INPUT_TIMEOUT = 500000; // 500 ms timeout. | 67 private static final int DEQUEUE_INPUT_TIMEOUT = 500000; // 500 ms timeout. |
| 68 private static final int MEDIA_CODEC_RELEASE_TIMEOUT_MS = 5000; // Timeout for
codec releasing. |
69 // Active running decoder instance. Set in initDecode() (called from native co
de) | 69 // Active running decoder instance. Set in initDecode() (called from native co
de) |
70 // and reset to null in release() call. | 70 // and reset to null in release() call. |
71 private static MediaCodecVideoDecoder runningInstance = null; | 71 private static MediaCodecVideoDecoder runningInstance = null; |
| 72 private static MediaCodecVideoDecoderErrorCallback errorCallback = null; |
| 73 private static int codecErrors = 0; |
| 74 |
72 private Thread mediaCodecThread; | 75 private Thread mediaCodecThread; |
73 private MediaCodec mediaCodec; | 76 private MediaCodec mediaCodec; |
74 private ByteBuffer[] inputBuffers; | 77 private ByteBuffer[] inputBuffers; |
75 private ByteBuffer[] outputBuffers; | 78 private ByteBuffer[] outputBuffers; |
76 private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8"; | 79 private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8"; |
77 private static final String H264_MIME_TYPE = "video/avc"; | 80 private static final String H264_MIME_TYPE = "video/avc"; |
78 // List of supported HW VP8 decoders. | 81 // List of supported HW VP8 decoders. |
79 private static final String[] supportedVp8HwCodecPrefixes = | 82 private static final String[] supportedVp8HwCodecPrefixes = |
80 {"OMX.qcom.", "OMX.Nvidia.", "OMX.Exynos.", "OMX.Intel." }; | 83 {"OMX.qcom.", "OMX.Nvidia.", "OMX.Exynos.", "OMX.Intel." }; |
81 // List of supported HW H.264 decoders. | 84 // List of supported HW H.264 decoders. |
(...skipping 16 matching lines...) Expand all Loading... |
98 private int sliceHeight; | 101 private int sliceHeight; |
99 private boolean useSurface; | 102 private boolean useSurface; |
100 private int textureID = 0; | 103 private int textureID = 0; |
101 private SurfaceTexture surfaceTexture = null; | 104 private SurfaceTexture surfaceTexture = null; |
102 private Surface surface = null; | 105 private Surface surface = null; |
103 private EglBase eglBase; | 106 private EglBase eglBase; |
104 | 107 |
105 private MediaCodecVideoDecoder() { | 108 private MediaCodecVideoDecoder() { |
106 } | 109 } |
107 | 110 |
| 111 // MediaCodec error handler - invoked when critical error happens which may pr
event |
| 112 // further use of media codec API. Now it means that one of media codec instan
ces |
| 113 // is hanging and can no longer be used in the next call. |
| 114 public static interface MediaCodecVideoDecoderErrorCallback { |
| 115 void onMediaCodecVideoDecoderCriticalError(int codecErrors); |
| 116 } |
| 117 |
| 118 public static void setErrorCallback(MediaCodecVideoDecoderErrorCallback errorC
allback) { |
| 119 Logging.d(TAG, "Set error callback"); |
| 120 MediaCodecVideoDecoder.errorCallback = errorCallback; |
| 121 } |
| 122 |
108 // Helper struct for findVp8Decoder() below. | 123 // Helper struct for findVp8Decoder() below. |
109 private static class DecoderProperties { | 124 private static class DecoderProperties { |
110 public DecoderProperties(String codecName, int colorFormat) { | 125 public DecoderProperties(String codecName, int colorFormat) { |
111 this.codecName = codecName; | 126 this.codecName = codecName; |
112 this.colorFormat = colorFormat; | 127 this.colorFormat = colorFormat; |
113 } | 128 } |
114 public final String codecName; // OpenMax component name for VP8 codec. | 129 public final String codecName; // OpenMax component name for VP8 codec. |
115 public final int colorFormat; // Color format supported by codec. | 130 public final int colorFormat; // Color format supported by codec. |
116 } | 131 } |
117 | 132 |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
266 return true; | 281 return true; |
267 } catch (IllegalStateException e) { | 282 } catch (IllegalStateException e) { |
268 Logging.e(TAG, "initDecode failed", e); | 283 Logging.e(TAG, "initDecode failed", e); |
269 return false; | 284 return false; |
270 } | 285 } |
271 } | 286 } |
272 | 287 |
273 private void release() { | 288 private void release() { |
274 Logging.d(TAG, "Java releaseDecoder"); | 289 Logging.d(TAG, "Java releaseDecoder"); |
275 checkOnMediaCodecThread(); | 290 checkOnMediaCodecThread(); |
276 try { | 291 |
277 mediaCodec.stop(); | 292 // Run Mediacodec stop() and release() on separate thread since sometime |
278 mediaCodec.release(); | 293 // Mediacodec.stop() may hang. |
279 } catch (IllegalStateException e) { | 294 final CountDownLatch releaseDone = new CountDownLatch(1); |
280 Logging.e(TAG, "release failed", e); | 295 |
| 296 Runnable runMediaCodecRelease = new Runnable() { |
| 297 @Override |
| 298 public void run() { |
| 299 try { |
| 300 Logging.d(TAG, "Java releaseDecoder on release thread"); |
| 301 mediaCodec.stop(); |
| 302 mediaCodec.release(); |
| 303 Logging.d(TAG, "Java releaseDecoder on release thread done"); |
| 304 } catch (Exception e) { |
| 305 Logging.e(TAG, "Media decoder release failed", e); |
| 306 } |
| 307 releaseDone.countDown(); |
| 308 } |
| 309 }; |
| 310 new Thread(runMediaCodecRelease).start(); |
| 311 |
| 312 if (!ThreadUtils.awaitUninterruptibly(releaseDone, MEDIA_CODEC_RELEASE_TIMEO
UT_MS)) { |
| 313 Logging.e(TAG, "Media decoder release timeout"); |
| 314 codecErrors++; |
| 315 if (errorCallback != null) { |
| 316 Logging.e(TAG, "Invoke codec error callback. Errors: " + codecErrors); |
| 317 errorCallback.onMediaCodecVideoDecoderCriticalError(codecErrors); |
| 318 } |
281 } | 319 } |
| 320 |
282 mediaCodec = null; | 321 mediaCodec = null; |
283 mediaCodecThread = null; | 322 mediaCodecThread = null; |
284 runningInstance = null; | 323 runningInstance = null; |
285 if (useSurface) { | 324 if (useSurface) { |
286 surface.release(); | 325 surface.release(); |
287 surface = null; | 326 surface = null; |
288 Logging.d(TAG, "Delete video decoder TextureID " + textureID); | 327 Logging.d(TAG, "Delete video decoder TextureID " + textureID); |
289 GLES20.glDeleteTextures(1, new int[] {textureID}, 0); | 328 GLES20.glDeleteTextures(1, new int[] {textureID}, 0); |
290 textureID = 0; | 329 textureID = 0; |
291 eglBase.release(); | 330 eglBase.release(); |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
347 } | 386 } |
348 | 387 |
349 // Returns null if no decoded buffer is available, and otherwise either a Deco
dedByteBuffer or | 388 // Returns null if no decoded buffer is available, and otherwise either a Deco
dedByteBuffer or |
350 // DecodedTexturebuffer depending on |useSurface| configuration. | 389 // DecodedTexturebuffer depending on |useSurface| configuration. |
351 // Throws IllegalStateException if call is made on the wrong thread, if color
format changes to an | 390 // Throws IllegalStateException if call is made on the wrong thread, if color
format changes to an |
352 // unsupported format, or if |mediaCodec| is not in the Executing state. Throw
s CodecException | 391 // unsupported format, or if |mediaCodec| is not in the Executing state. Throw
s CodecException |
353 // upon codec error. | 392 // upon codec error. |
354 private Object dequeueOutputBuffer(int dequeueTimeoutUs) | 393 private Object dequeueOutputBuffer(int dequeueTimeoutUs) |
355 throws IllegalStateException, MediaCodec.CodecException { | 394 throws IllegalStateException, MediaCodec.CodecException { |
356 checkOnMediaCodecThread(); | 395 checkOnMediaCodecThread(); |
| 396 |
357 // Drain the decoder until receiving a decoded buffer or hitting | 397 // Drain the decoder until receiving a decoded buffer or hitting |
358 // MediaCodec.INFO_TRY_AGAIN_LATER. | 398 // MediaCodec.INFO_TRY_AGAIN_LATER. |
359 final MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); | 399 final MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
360 while (true) { | 400 while (true) { |
361 final int result = mediaCodec.dequeueOutputBuffer(info, dequeueTimeoutUs); | 401 final int result = mediaCodec.dequeueOutputBuffer(info, dequeueTimeoutUs); |
362 switch (result) { | 402 switch (result) { |
363 case MediaCodec.INFO_TRY_AGAIN_LATER: | 403 case MediaCodec.INFO_TRY_AGAIN_LATER: |
364 return null; | 404 return null; |
365 case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: | 405 case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: |
366 outputBuffers = mediaCodec.getOutputBuffers(); | 406 outputBuffers = mediaCodec.getOutputBuffers(); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
409 // MediaCodec.CodecException upon codec error. | 449 // MediaCodec.CodecException upon codec error. |
410 private void returnDecodedByteBuffer(int index) | 450 private void returnDecodedByteBuffer(int index) |
411 throws IllegalStateException, MediaCodec.CodecException { | 451 throws IllegalStateException, MediaCodec.CodecException { |
412 checkOnMediaCodecThread(); | 452 checkOnMediaCodecThread(); |
413 if (useSurface) { | 453 if (useSurface) { |
414 throw new IllegalStateException("returnDecodedByteBuffer() called for surf
ace decoding."); | 454 throw new IllegalStateException("returnDecodedByteBuffer() called for surf
ace decoding."); |
415 } | 455 } |
416 mediaCodec.releaseOutputBuffer(index, false /* render */); | 456 mediaCodec.releaseOutputBuffer(index, false /* render */); |
417 } | 457 } |
418 } | 458 } |
OLD | NEW |