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.atomic.AtomicBoolean; | |
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 // MediaCodec is thread-hostile so this class must be operated on a single |
52 // thread. | 53 // thread. |
53 public class MediaCodecVideoDecoder { | 54 public class MediaCodecVideoDecoder { |
54 // This class is constructed, operated, and destroyed by its C++ incarnation, | 55 // 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 | 56 // 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 | 57 // class exposes aims to mimic the webrtc::VideoDecoder API as closely as |
57 // possibly to minimize the amount of translation work necessary. | 58 // possibly to minimize the amount of translation work necessary. |
58 | 59 |
59 private static final String TAG = "MediaCodecVideoDecoder"; | 60 private static final String TAG = "MediaCodecVideoDecoder"; |
60 | 61 |
61 // Tracks webrtc::VideoCodecType. | 62 // Tracks webrtc::VideoCodecType. |
62 public enum VideoCodecType { | 63 public enum VideoCodecType { |
63 VIDEO_CODEC_VP8, | 64 VIDEO_CODEC_VP8, |
64 VIDEO_CODEC_VP9, | 65 VIDEO_CODEC_VP9, |
65 VIDEO_CODEC_H264 | 66 VIDEO_CODEC_H264 |
66 } | 67 } |
67 | 68 |
68 private static final int DEQUEUE_INPUT_TIMEOUT = 500000; // 500 ms timeout. | 69 private static final int DEQUEUE_INPUT_TIMEOUT = 500000; // 500 ms timeout. |
70 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) | 71 // Active running decoder instance. Set in initDecode() (called from native co de) |
70 // and reset to null in release() call. | 72 // and reset to null in release() call. |
71 private static MediaCodecVideoDecoder runningInstance = null; | 73 private static MediaCodecVideoDecoder runningInstance = null; |
74 private static MediaCodecVideoDecoderErrorCallback errorCallback = null; | |
magjed_webrtc
2015/10/30 14:01:57
We signal other MediaCodec errors with exceptions
AlexG
2015/10/30 20:07:59
These are different errors. Exceptions and other m
| |
75 private static int codecErrors = 0; | |
magjed_webrtc
2015/10/30 14:01:57
Why do we need to report the number of codec error
AlexG
2015/10/30 20:07:58
See comment above
| |
76 | |
72 private Thread mediaCodecThread; | 77 private Thread mediaCodecThread; |
73 private MediaCodec mediaCodec; | 78 private MediaCodec mediaCodec; |
74 private ByteBuffer[] inputBuffers; | 79 private ByteBuffer[] inputBuffers; |
75 private ByteBuffer[] outputBuffers; | 80 private ByteBuffer[] outputBuffers; |
76 private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8"; | 81 private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8"; |
77 private static final String H264_MIME_TYPE = "video/avc"; | 82 private static final String H264_MIME_TYPE = "video/avc"; |
78 // List of supported HW VP8 decoders. | 83 // List of supported HW VP8 decoders. |
79 private static final String[] supportedVp8HwCodecPrefixes = | 84 private static final String[] supportedVp8HwCodecPrefixes = |
80 {"OMX.qcom.", "OMX.Nvidia.", "OMX.Exynos.", "OMX.Intel." }; | 85 {"OMX.qcom.", "OMX.Nvidia.", "OMX.Exynos.", "OMX.Intel." }; |
81 // List of supported HW H.264 decoders. | 86 // List of supported HW H.264 decoders. |
(...skipping 16 matching lines...) Expand all Loading... | |
98 private int sliceHeight; | 103 private int sliceHeight; |
99 private boolean useSurface; | 104 private boolean useSurface; |
100 private int textureID = 0; | 105 private int textureID = 0; |
101 private SurfaceTexture surfaceTexture = null; | 106 private SurfaceTexture surfaceTexture = null; |
102 private Surface surface = null; | 107 private Surface surface = null; |
103 private EglBase eglBase; | 108 private EglBase eglBase; |
104 | 109 |
105 private MediaCodecVideoDecoder() { | 110 private MediaCodecVideoDecoder() { |
106 } | 111 } |
107 | 112 |
113 // MediaCodec error handler - invoked when critical error happens | |
114 // which prevents further use of media codec API. | |
115 public static interface MediaCodecVideoDecoderErrorCallback { | |
116 void onMediaCodecVideoDecoderError(int codecErrors); | |
117 } | |
118 | |
119 public static void setErrorCallback(MediaCodecVideoDecoderErrorCallback errorC allback) { | |
magjed_webrtc
2015/10/30 14:01:57
I really don't like hooking stuff up with static f
AlexG
2015/10/30 20:07:58
There is similar to VideoCapturerAndroid.create()
| |
120 Logging.d(TAG, "Set error callback"); | |
121 MediaCodecVideoDecoder.errorCallback = errorCallback; | |
122 } | |
123 | |
108 // Helper struct for findVp8Decoder() below. | 124 // Helper struct for findVp8Decoder() below. |
109 private static class DecoderProperties { | 125 private static class DecoderProperties { |
110 public DecoderProperties(String codecName, int colorFormat) { | 126 public DecoderProperties(String codecName, int colorFormat) { |
111 this.codecName = codecName; | 127 this.codecName = codecName; |
112 this.colorFormat = colorFormat; | 128 this.colorFormat = colorFormat; |
113 } | 129 } |
114 public final String codecName; // OpenMax component name for VP8 codec. | 130 public final String codecName; // OpenMax component name for VP8 codec. |
115 public final int colorFormat; // Color format supported by codec. | 131 public final int colorFormat; // Color format supported by codec. |
116 } | 132 } |
117 | 133 |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
266 return true; | 282 return true; |
267 } catch (IllegalStateException e) { | 283 } catch (IllegalStateException e) { |
268 Logging.e(TAG, "initDecode failed", e); | 284 Logging.e(TAG, "initDecode failed", e); |
269 return false; | 285 return false; |
270 } | 286 } |
271 } | 287 } |
272 | 288 |
273 private void release() { | 289 private void release() { |
274 Logging.d(TAG, "Java releaseDecoder"); | 290 Logging.d(TAG, "Java releaseDecoder"); |
275 checkOnMediaCodecThread(); | 291 checkOnMediaCodecThread(); |
276 try { | 292 |
277 mediaCodec.stop(); | 293 // Run Mediacodec stop() and release() on separate thread since sometime |
278 mediaCodec.release(); | 294 // Mediacodec.stop() may hang. |
279 } catch (IllegalStateException e) { | 295 final AtomicBoolean releaseDone = new AtomicBoolean(false); |
magjed_webrtc
2015/10/30 14:01:57
I think it would be cleaner to write this with a C
AlexG
2015/10/30 20:07:59
Done.
| |
280 Logging.e(TAG, "release failed", e); | 296 |
297 Runnable runMediaCodecRelease = new Runnable() { | |
298 @Override | |
299 public void run() { | |
300 try { | |
301 Logging.d(TAG, "Java releaseDecoder on release thread"); | |
302 mediaCodec.stop(); | |
303 mediaCodec.release(); | |
304 Logging.d(TAG, "Java releaseDecoder on release thread done"); | |
305 } catch (Exception e) { | |
306 Logging.e(TAG, "Media decoder release failed", e); | |
307 } | |
308 synchronized (releaseDone) { | |
309 releaseDone.set(true); | |
310 releaseDone.notifyAll(); | |
311 } | |
312 } | |
313 }; | |
314 new Thread(runMediaCodecRelease).start(); | |
magjed_webrtc
2015/10/30 14:01:57
We have this comment at the top of this file: "Med
AlexG
2015/10/30 20:07:58
I didn't see this requirement in Android docs - I
| |
315 | |
316 if (!releaseDone.get()) { | |
317 synchronized(releaseDone) { | |
318 try { | |
319 releaseDone.wait(MEDIA_CODEC_RELEASE_TIMEOUT_MS); | |
magjed_webrtc
2015/10/30 14:01:57
You should not swallow InterruptedException, i.e.
AlexG
2015/10/30 20:07:58
Done. Thanks! I moved your ThreadUtils changes to
magjed_webrtc
2015/10/30 21:50:37
If you want, just submit the ThreadUtils change in
| |
320 } catch (InterruptedException e) { | |
321 Logging.e(TAG, "Wait exception.", e); | |
322 } | |
323 } | |
281 } | 324 } |
325 if (!releaseDone.get()) { | |
326 Logging.e(TAG, "Media decoder release timeout"); | |
327 codecErrors++; | |
328 if (errorCallback != null) { | |
329 Logging.e(TAG, "Invoke codec error callback. Errors: " + codecErrors); | |
330 errorCallback.onMediaCodecVideoDecoderError(codecErrors); | |
331 } | |
332 } | |
333 | |
282 mediaCodec = null; | 334 mediaCodec = null; |
283 mediaCodecThread = null; | 335 mediaCodecThread = null; |
284 runningInstance = null; | 336 runningInstance = null; |
285 if (useSurface) { | 337 if (useSurface) { |
286 surface.release(); | 338 surface.release(); |
287 surface = null; | 339 surface = null; |
288 Logging.d(TAG, "Delete video decoder TextureID " + textureID); | 340 Logging.d(TAG, "Delete video decoder TextureID " + textureID); |
289 GLES20.glDeleteTextures(1, new int[] {textureID}, 0); | 341 GLES20.glDeleteTextures(1, new int[] {textureID}, 0); |
290 textureID = 0; | 342 textureID = 0; |
291 eglBase.release(); | 343 eglBase.release(); |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
409 // MediaCodec.CodecException upon codec error. | 461 // MediaCodec.CodecException upon codec error. |
410 private void returnDecodedByteBuffer(int index) | 462 private void returnDecodedByteBuffer(int index) |
411 throws IllegalStateException, MediaCodec.CodecException { | 463 throws IllegalStateException, MediaCodec.CodecException { |
412 checkOnMediaCodecThread(); | 464 checkOnMediaCodecThread(); |
413 if (useSurface) { | 465 if (useSurface) { |
414 throw new IllegalStateException("returnDecodedByteBuffer() called for surf ace decoding."); | 466 throw new IllegalStateException("returnDecodedByteBuffer() called for surf ace decoding."); |
415 } | 467 } |
416 mediaCodec.releaseOutputBuffer(index, false /* render */); | 468 mediaCodec.releaseOutputBuffer(index, false /* render */); |
417 } | 469 } |
418 } | 470 } |
OLD | NEW |