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, |
11 * this list of conditions and the following disclaimer in the documentation | 11 * this list of conditions and the following disclaimer in the documentation |
12 * and/or other materials provided with the distribution. | 12 * and/or other materials provided with the distribution. |
13 * 3. The name of the author may not be used to endorse or promote products | 13 * 3. The name of the author may not be used to endorse or promote products |
14 * derived from this software without specific prior written permission. | 14 * derived from this software without specific prior written permission. |
15 * | 15 * |
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 */ | 26 */ |
27 | 27 |
28 package org.webrtc; | 28 package org.webrtc; |
29 | 29 |
30 import android.graphics.SurfaceTexture; | |
31 import android.media.MediaCodec; | 30 import android.media.MediaCodec; |
32 import android.media.MediaCodecInfo; | 31 import android.media.MediaCodecInfo; |
33 import android.media.MediaCodecInfo.CodecCapabilities; | 32 import android.media.MediaCodecInfo.CodecCapabilities; |
34 import android.media.MediaCodecList; | 33 import android.media.MediaCodecList; |
35 import android.media.MediaFormat; | 34 import android.media.MediaFormat; |
36 import android.opengl.EGLContext; | 35 import android.opengl.EGLContext; |
37 import android.opengl.GLES11Ext; | 36 import android.opengl.GLES11Ext; |
38 import android.opengl.GLES20; | 37 import android.opengl.GLES20; |
39 import android.os.Build; | 38 import android.os.Build; |
40 import android.view.Surface; | 39 import android.view.Surface; |
41 | 40 |
42 import org.webrtc.Logging; | 41 import org.webrtc.Logging; |
43 | 42 |
44 import java.nio.ByteBuffer; | 43 import java.nio.ByteBuffer; |
| 44 import java.util.Arrays; |
45 import java.util.List; | 45 import java.util.List; |
46 import java.util.Arrays; | 46 import java.util.concurrent.TimeUnit; |
47 | 47 |
48 // Java-side of peerconnection_jni.cc:MediaCodecVideoDecoder. | 48 // Java-side of peerconnection_jni.cc:MediaCodecVideoDecoder. |
49 // This class is an implementation detail of the Java PeerConnection API. | 49 // This class is an implementation detail of the Java PeerConnection API. |
50 // MediaCodec is thread-hostile so this class must be operated on a single | 50 // MediaCodec is thread-hostile so this class must be operated on a single |
51 // thread. | 51 // thread. |
52 public class MediaCodecVideoDecoder { | 52 public class MediaCodecVideoDecoder { |
53 // This class is constructed, operated, and destroyed by its C++ incarnation, | 53 // This class is constructed, operated, and destroyed by its C++ incarnation, |
54 // 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 |
55 // 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 |
56 // possibly to minimize the amount of translation work necessary. | 56 // possibly to minimize the amount of translation work necessary. |
(...skipping 29 matching lines...) Expand all Loading... |
86 CodecCapabilities.COLOR_FormatYUV420Planar, | 86 CodecCapabilities.COLOR_FormatYUV420Planar, |
87 CodecCapabilities.COLOR_FormatYUV420SemiPlanar, | 87 CodecCapabilities.COLOR_FormatYUV420SemiPlanar, |
88 CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, | 88 CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, |
89 COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m); | 89 COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m); |
90 private int colorFormat; | 90 private int colorFormat; |
91 private int width; | 91 private int width; |
92 private int height; | 92 private int height; |
93 private int stride; | 93 private int stride; |
94 private int sliceHeight; | 94 private int sliceHeight; |
95 private boolean useSurface; | 95 private boolean useSurface; |
96 private int textureID = 0; | 96 // |isWaitingForTexture| is true when waiting for the transition: |
97 private SurfaceTexture surfaceTexture = null; | 97 // MediaCodec.releaseOutputBuffer() -> onTextureFrameAvailable(). |
| 98 private boolean isWaitingForTexture = false; |
| 99 private TextureListener textureListener; |
98 private Surface surface = null; | 100 private Surface surface = null; |
99 private EglBase eglBase; | |
100 | 101 |
101 private MediaCodecVideoDecoder() { } | 102 private MediaCodecVideoDecoder() { } |
102 | 103 |
103 // Helper struct for findVp8Decoder() below. | 104 // Helper struct for findVp8Decoder() below. |
104 private static class DecoderProperties { | 105 private static class DecoderProperties { |
105 public DecoderProperties(String codecName, int colorFormat) { | 106 public DecoderProperties(String codecName, int colorFormat) { |
106 this.codecName = codecName; | 107 this.codecName = codecName; |
107 this.colorFormat = colorFormat; | 108 this.colorFormat = colorFormat; |
108 } | 109 } |
109 public final String codecName; // OpenMax component name for VP8 codec. | 110 public final String codecName; // OpenMax component name for VP8 codec. |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 } | 174 } |
174 | 175 |
175 private void checkOnMediaCodecThread() throws IllegalStateException { | 176 private void checkOnMediaCodecThread() throws IllegalStateException { |
176 if (mediaCodecThread.getId() != Thread.currentThread().getId()) { | 177 if (mediaCodecThread.getId() != Thread.currentThread().getId()) { |
177 throw new IllegalStateException( | 178 throw new IllegalStateException( |
178 "MediaCodecVideoDecoder previously operated on " + mediaCodecThread + | 179 "MediaCodecVideoDecoder previously operated on " + mediaCodecThread + |
179 " but is now called on " + Thread.currentThread()); | 180 " but is now called on " + Thread.currentThread()); |
180 } | 181 } |
181 } | 182 } |
182 | 183 |
183 // Pass null in |sharedContext| to configure the codec for ByteBuffer output. | 184 // Pass null in |surfaceTextureHelper| to configure the codec for ByteBuffer o
utput. |
184 private boolean initDecode(VideoCodecType type, int width, int height, EGLCont
ext sharedContext) { | 185 private boolean initDecode( |
| 186 VideoCodecType type, int width, int height, SurfaceTextureHelper surfaceTe
xtureHelper) { |
185 if (mediaCodecThread != null) { | 187 if (mediaCodecThread != null) { |
186 throw new RuntimeException("Forgot to release()?"); | 188 throw new RuntimeException("Forgot to release()?"); |
187 } | 189 } |
188 useSurface = (sharedContext != null); | 190 useSurface = (surfaceTextureHelper != null); |
189 String mime = null; | 191 String mime = null; |
190 String[] supportedCodecPrefixes = null; | 192 String[] supportedCodecPrefixes = null; |
191 if (type == VideoCodecType.VIDEO_CODEC_VP8) { | 193 if (type == VideoCodecType.VIDEO_CODEC_VP8) { |
192 mime = VP8_MIME_TYPE; | 194 mime = VP8_MIME_TYPE; |
193 supportedCodecPrefixes = supportedVp8HwCodecPrefixes; | 195 supportedCodecPrefixes = supportedVp8HwCodecPrefixes; |
194 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { | 196 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { |
195 mime = H264_MIME_TYPE; | 197 mime = H264_MIME_TYPE; |
196 supportedCodecPrefixes = supportedH264HwCodecPrefixes; | 198 supportedCodecPrefixes = supportedH264HwCodecPrefixes; |
197 } else { | 199 } else { |
198 throw new RuntimeException("Non supported codec " + type); | 200 throw new RuntimeException("Non supported codec " + type); |
199 } | 201 } |
200 DecoderProperties properties = findDecoder(mime, supportedCodecPrefixes); | 202 DecoderProperties properties = findDecoder(mime, supportedCodecPrefixes); |
201 if (properties == null) { | 203 if (properties == null) { |
202 throw new RuntimeException("Cannot find HW decoder for " + type); | 204 throw new RuntimeException("Cannot find HW decoder for " + type); |
203 } | 205 } |
204 Logging.d(TAG, "Java initDecode: " + type + " : "+ width + " x " + height + | 206 Logging.d(TAG, "Java initDecode: " + type + " : "+ width + " x " + height + |
205 ". Color: 0x" + Integer.toHexString(properties.colorFormat) + | 207 ". Color: 0x" + Integer.toHexString(properties.colorFormat) + |
206 ". Use Surface: " + useSurface); | 208 ". Use Surface: " + useSurface); |
207 if (sharedContext != null) { | |
208 Logging.d(TAG, "Decoder shared EGL Context: " + sharedContext); | |
209 } | |
210 mediaCodecThread = Thread.currentThread(); | 209 mediaCodecThread = Thread.currentThread(); |
211 try { | 210 try { |
212 this.width = width; | 211 this.width = width; |
213 this.height = height; | 212 this.height = height; |
214 stride = width; | 213 stride = width; |
215 sliceHeight = height; | 214 sliceHeight = height; |
216 | 215 |
217 if (useSurface) { | 216 if (useSurface) { |
218 // Create shared EGL context. | 217 textureListener = new TextureListener(surfaceTextureHelper); |
219 eglBase = new EglBase(sharedContext, EglBase.ConfigType.PIXEL_BUFFER); | 218 surface = new Surface(surfaceTextureHelper.getSurfaceTexture()); |
220 eglBase.createDummyPbufferSurface(); | |
221 eglBase.makeCurrent(); | |
222 | |
223 // Create output surface | |
224 textureID = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); | |
225 Logging.d(TAG, "Video decoder TextureID = " + textureID); | |
226 surfaceTexture = new SurfaceTexture(textureID); | |
227 surface = new Surface(surfaceTexture); | |
228 } | 219 } |
229 | 220 |
230 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); | 221 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); |
231 if (!useSurface) { | 222 if (!useSurface) { |
232 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); | 223 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); |
233 } | 224 } |
234 Logging.d(TAG, " Format: " + format); | 225 Logging.d(TAG, " Format: " + format); |
235 mediaCodec = | 226 mediaCodec = |
236 MediaCodecVideoEncoder.createByCodecName(properties.codecName); | 227 MediaCodecVideoEncoder.createByCodecName(properties.codecName); |
237 if (mediaCodec == null) { | 228 if (mediaCodec == null) { |
(...skipping 20 matching lines...) Expand all Loading... |
258 mediaCodec.stop(); | 249 mediaCodec.stop(); |
259 mediaCodec.release(); | 250 mediaCodec.release(); |
260 } catch (IllegalStateException e) { | 251 } catch (IllegalStateException e) { |
261 Logging.e(TAG, "release failed", e); | 252 Logging.e(TAG, "release failed", e); |
262 } | 253 } |
263 mediaCodec = null; | 254 mediaCodec = null; |
264 mediaCodecThread = null; | 255 mediaCodecThread = null; |
265 if (useSurface) { | 256 if (useSurface) { |
266 surface.release(); | 257 surface.release(); |
267 surface = null; | 258 surface = null; |
268 Logging.d(TAG, "Delete video decoder TextureID " + textureID); | 259 textureListener.release(); |
269 GLES20.glDeleteTextures(1, new int[] {textureID}, 0); | |
270 textureID = 0; | |
271 eglBase.release(); | |
272 eglBase = null; | |
273 } | 260 } |
274 } | 261 } |
275 | 262 |
276 // Dequeue an input buffer and return its index, -1 if no input buffer is | 263 // Dequeue an input buffer and return its index, -1 if no input buffer is |
277 // available, or -2 if the codec is no longer operative. | 264 // available, or -2 if the codec is no longer operative. |
278 private int dequeueInputBuffer() { | 265 private int dequeueInputBuffer() { |
279 checkOnMediaCodecThread(); | 266 checkOnMediaCodecThread(); |
280 try { | 267 try { |
281 return mediaCodec.dequeueInputBuffer(DEQUEUE_INPUT_TIMEOUT); | 268 return mediaCodec.dequeueInputBuffer(DEQUEUE_INPUT_TIMEOUT); |
282 } catch (IllegalStateException e) { | 269 } catch (IllegalStateException e) { |
(...skipping 27 matching lines...) Expand all Loading... |
310 } | 297 } |
311 | 298 |
312 private final int index; | 299 private final int index; |
313 private final int offset; | 300 private final int offset; |
314 private final int size; | 301 private final int size; |
315 private final long presentationTimestampUs; | 302 private final long presentationTimestampUs; |
316 } | 303 } |
317 | 304 |
318 private static class DecodedTextureBuffer { | 305 private static class DecodedTextureBuffer { |
319 private final int textureID; | 306 private final int textureID; |
320 private final long presentationTimestampUs; | 307 private final float[] transformMatrix; |
| 308 private final long timestampNs; |
321 | 309 |
322 public DecodedTextureBuffer(int textureID, long presentationTimestampUs) { | 310 public DecodedTextureBuffer(int textureID, float[] transformMatrix, long tim
estampNs) { |
323 this.textureID = textureID; | 311 this.textureID = textureID; |
324 this.presentationTimestampUs = presentationTimestampUs; | 312 this.transformMatrix = transformMatrix; |
| 313 this.timestampNs = timestampNs; |
325 } | 314 } |
326 } | 315 } |
327 | 316 |
| 317 // Poll based texture listener. |
| 318 private static class TextureListener |
| 319 implements SurfaceTextureHelper.OnTextureFrameAvailableListener { |
| 320 private final SurfaceTextureHelper surfaceTextureHelper; |
| 321 private DecodedTextureBuffer textureBuffer; |
| 322 // |newFrameLock| is used to synchronize arrival of new frames with wait()/n
otifyAll(). |
| 323 private final Object newFrameLock = new Object(); |
| 324 |
| 325 public TextureListener(SurfaceTextureHelper surfaceTextureHelper) { |
| 326 this.surfaceTextureHelper = surfaceTextureHelper; |
| 327 surfaceTextureHelper.setListener(this); |
| 328 } |
| 329 |
| 330 // Callback from |surfaceTextureHelper|. May be called on an arbitrary threa
d. |
| 331 @Override |
| 332 public void onTextureFrameAvailable( |
| 333 int oesTextureId, float[] transformMatrix, long timestampNs) { |
| 334 synchronized (newFrameLock) { |
| 335 if (textureBuffer != null) { |
| 336 Logging.e(TAG, |
| 337 "Unexpected onTextureFrameAvailable() called while already holding
a texture."); |
| 338 throw new IllegalStateException("Already holding a texture."); |
| 339 } |
| 340 textureBuffer = new DecodedTextureBuffer(oesTextureId, transformMatrix,
timestampNs); |
| 341 newFrameLock.notifyAll(); |
| 342 } |
| 343 } |
| 344 |
| 345 // Dequeues and returns a texture buffer if available, or null otherwise. |
| 346 public DecodedTextureBuffer dequeueTextureFrame(int timeoutMs) { |
| 347 synchronized (newFrameLock) { |
| 348 if (textureBuffer == null && timeoutMs > 0) { |
| 349 try { |
| 350 newFrameLock.wait(timeoutMs); |
| 351 } catch(InterruptedException e) { |
| 352 // Restore the interrupted status by reinterrupting the thread. |
| 353 Thread.currentThread().interrupt(); |
| 354 } |
| 355 } |
| 356 final DecodedTextureBuffer textureBuffer = this.textureBuffer; |
| 357 this.textureBuffer = null; |
| 358 return textureBuffer; |
| 359 } |
| 360 } |
| 361 |
| 362 public void release() { |
| 363 // SurfaceTextureHelper.disconnect() will block until any onTextureFrameAv
ailable() in |
| 364 // progress is done. Therefore, the call to disconnect() must be outside a
ny synchronized |
| 365 // statement that is also used in the onTextureFrameAvailable() above to a
void deadlocks. |
| 366 surfaceTextureHelper.disconnect(); |
| 367 synchronized (newFrameLock) { |
| 368 if (textureBuffer != null) { |
| 369 surfaceTextureHelper.returnTextureFrame(); |
| 370 textureBuffer = null; |
| 371 } |
| 372 } |
| 373 } |
| 374 } |
| 375 |
328 // Returns null if no decoded buffer is available, and otherwise either a Deco
dedByteBuffer or | 376 // Returns null if no decoded buffer is available, and otherwise either a Deco
dedByteBuffer or |
329 // DecodedTexturebuffer depending on |useSurface| configuration. | 377 // DecodedTexturebuffer depending on |useSurface| configuration. |
330 // Throws IllegalStateException if call is made on the wrong thread, if color
format changes to an | 378 // Throws IllegalStateException if call is made on the wrong thread, if color
format changes to an |
331 // unsupported format, or if |mediaCodec| is not in the Executing state. Throw
s CodecException | 379 // unsupported format, or if |mediaCodec| is not in the Executing state. Throw
s CodecException |
332 // upon codec error. | 380 // upon codec error. |
333 private Object dequeueOutputBuffer(int dequeueTimeoutUs) | 381 private Object dequeueOutputBuffer(int dequeueTimeoutMs) |
334 throws IllegalStateException, MediaCodec.CodecException { | 382 throws IllegalStateException, MediaCodec.CodecException { |
335 checkOnMediaCodecThread(); | 383 checkOnMediaCodecThread(); |
| 384 // Calling multiple MediaCodec.releaseOutputBuffer() with render=true in a r
ow will result in |
| 385 // dropped texture frames. Therefore, wait for any pending onTextureFrameAva
ilable() before |
| 386 // proceeding. |
| 387 if (isWaitingForTexture) { |
| 388 final DecodedTextureBuffer textureBuffer = |
| 389 textureListener.dequeueTextureFrame(dequeueTimeoutMs); |
| 390 isWaitingForTexture = (textureBuffer == null); |
| 391 return textureBuffer; |
| 392 } |
| 393 |
336 // Drain the decoder until receiving a decoded buffer or hitting | 394 // Drain the decoder until receiving a decoded buffer or hitting |
337 // MediaCodec.INFO_TRY_AGAIN_LATER. | 395 // MediaCodec.INFO_TRY_AGAIN_LATER. |
338 final MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); | 396 final MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
339 while (true) { | 397 while (true) { |
340 final int result = mediaCodec.dequeueOutputBuffer(info, dequeueTimeoutUs); | 398 final int result = mediaCodec.dequeueOutputBuffer( |
| 399 info, TimeUnit.MILLISECONDS.toMicros(dequeueTimeoutMs)); |
341 switch (result) { | 400 switch (result) { |
342 case MediaCodec.INFO_TRY_AGAIN_LATER: | 401 case MediaCodec.INFO_TRY_AGAIN_LATER: |
343 return null; | 402 return null; |
344 case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: | 403 case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: |
345 outputBuffers = mediaCodec.getOutputBuffers(); | 404 outputBuffers = mediaCodec.getOutputBuffers(); |
346 Logging.d(TAG, "Decoder output buffers changed: " + outputBuffers.leng
th); | 405 Logging.d(TAG, "Decoder output buffers changed: " + outputBuffers.leng
th); |
347 break; | 406 break; |
348 case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: | 407 case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: |
349 MediaFormat format = mediaCodec.getOutputFormat(); | 408 MediaFormat format = mediaCodec.getOutputFormat(); |
350 Logging.d(TAG, "Decoder format changed: " + format.toString()); | 409 Logging.d(TAG, "Decoder format changed: " + format.toString()); |
(...skipping 13 matching lines...) Expand all Loading... |
364 sliceHeight = format.getInteger("slice-height"); | 423 sliceHeight = format.getInteger("slice-height"); |
365 } | 424 } |
366 Logging.d(TAG, "Frame stride and slice height: " + stride + " x " + sl
iceHeight); | 425 Logging.d(TAG, "Frame stride and slice height: " + stride + " x " + sl
iceHeight); |
367 stride = Math.max(width, stride); | 426 stride = Math.max(width, stride); |
368 sliceHeight = Math.max(height, sliceHeight); | 427 sliceHeight = Math.max(height, sliceHeight); |
369 break; | 428 break; |
370 default: | 429 default: |
371 // Output buffer decoded. | 430 // Output buffer decoded. |
372 if (useSurface) { | 431 if (useSurface) { |
373 mediaCodec.releaseOutputBuffer(result, true /* render */); | 432 mediaCodec.releaseOutputBuffer(result, true /* render */); |
374 // TODO(magjed): Wait for SurfaceTexture.onFrameAvailable() before r
eturning a texture | 433 final DecodedTextureBuffer textureBuffer = |
375 // frame. | 434 textureListener.dequeueTextureFrame(dequeueTimeoutMs); |
376 return new DecodedTextureBuffer(textureID, info.presentationTimeUs); | 435 isWaitingForTexture = (textureBuffer == null); |
| 436 return textureBuffer; |
377 } else { | 437 } else { |
378 return new DecodedByteBuffer(result, info.offset, info.size, info.pr
esentationTimeUs); | 438 return new DecodedByteBuffer(result, info.offset, info.size, info.pr
esentationTimeUs); |
379 } | 439 } |
380 } | 440 } |
381 } | 441 } |
382 } | 442 } |
383 | 443 |
384 // Release a dequeued output byte buffer back to the codec for re-use. Should
only be called for | 444 // Release a dequeued output byte buffer back to the codec for re-use. Should
only be called for |
385 // non-surface decoding. | 445 // non-surface decoding. |
386 // Throws IllegalStateException if the call is made on the wrong thread, if co
dec is configured | 446 // Throws IllegalStateException if the call is made on the wrong thread, if co
dec is configured |
387 // for surface decoding, or if |mediaCodec| is not in the Executing state. Thr
ows | 447 // for surface decoding, or if |mediaCodec| is not in the Executing state. Thr
ows |
388 // MediaCodec.CodecException upon codec error. | 448 // MediaCodec.CodecException upon codec error. |
389 private void returnDecodedByteBuffer(int index) | 449 private void returnDecodedByteBuffer(int index) |
390 throws IllegalStateException, MediaCodec.CodecException { | 450 throws IllegalStateException, MediaCodec.CodecException { |
391 checkOnMediaCodecThread(); | 451 checkOnMediaCodecThread(); |
392 if (useSurface) { | 452 if (useSurface) { |
393 throw new IllegalStateException("returnDecodedByteBuffer() called for surf
ace decoding."); | 453 throw new IllegalStateException("returnDecodedByteBuffer() called for surf
ace decoding."); |
394 } | 454 } |
395 mediaCodec.releaseOutputBuffer(index, false /* render */); | 455 mediaCodec.releaseOutputBuffer(index, false /* render */); |
396 } | 456 } |
397 } | 457 } |
OLD | NEW |