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 | |
323 public TextureListener(SurfaceTextureHelper surfaceTextureHelper) { | |
324 this.surfaceTextureHelper = surfaceTextureHelper; | |
325 surfaceTextureHelper.setListener(this); | |
326 } | |
327 | |
328 // Callback from |surfaceTextureHelper|. May be called on an arbitrary threa d. | |
329 @Override | |
330 public synchronized void onTextureFrameAvailable( | |
331 int oesTextureId, float[] transformMatrix, long timestampNs) { | |
332 if (textureBuffer != null) { | |
333 throw new IllegalStateException("Already holding a texture."); | |
AlexG
2015/10/06 23:27:35
Print error message in the log
magjed_webrtc
2015/10/07 07:50:57
Done.
| |
334 } | |
335 textureBuffer = new DecodedTextureBuffer(oesTextureId, transformMatrix, ti mestampNs); | |
336 notifyAll(); | |
337 } | |
338 | |
339 // Dequeues and returns a texture buffer if available, or null otherwise. | |
340 public synchronized DecodedTextureBuffer dequeueTextureFrame(int timeoutMs) { | |
341 if (textureBuffer == null && timeoutMs > 0) { | |
342 try { | |
343 wait(timeoutMs); | |
AlexG
2015/10/06 23:27:35
I think it will be cleaner to add separate Object
magjed_webrtc
2015/10/07 07:50:57
Done.
| |
344 } catch(InterruptedException e) { | |
345 Thread.currentThread().interrupt(); | |
AlexG
2015/10/06 23:27:35
This is bad fallback - we need to exit gracefully
magjed_webrtc
2015/10/07 07:50:57
I think reinterrupting the thread to restore the i
| |
346 } | |
347 } | |
348 final DecodedTextureBuffer textureBuffer = this.textureBuffer; | |
349 this.textureBuffer = null; | |
350 return textureBuffer; | |
351 } | |
352 | |
353 public void release() { | |
354 surfaceTextureHelper.disconnect(); | |
355 synchronized (this) { | |
AlexG
2015/10/06 23:27:35
Declare release() as synchronized for parity with
magjed_webrtc
2015/10/07 07:50:57
There is a risk for deadlock if we call disconnect
AlexG
2015/10/07 17:04:30
Add comment on it?
magjed_webrtc
2015/10/07 20:56:56
Done.
| |
356 if (textureBuffer != null) { | |
357 surfaceTextureHelper.returnTextureFrame(); | |
358 textureBuffer = null; | |
359 } | |
360 } | |
361 } | |
362 } | |
363 | |
328 // Returns null if no decoded buffer is available, and otherwise either a Deco dedByteBuffer or | 364 // Returns null if no decoded buffer is available, and otherwise either a Deco dedByteBuffer or |
329 // DecodedTexturebuffer depending on |useSurface| configuration. | 365 // DecodedTexturebuffer depending on |useSurface| configuration. |
330 // Throws IllegalStateException if call is made on the wrong thread, if color format changes to an | 366 // 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 | 367 // unsupported format, or if |mediaCodec| is not in the Executing state. Throw s CodecException |
332 // upon codec error. | 368 // upon codec error. |
333 private Object dequeueOutputBuffer(int dequeueTimeoutUs) | 369 private Object dequeueOutputBuffer(int dequeueTimeoutMs) |
334 throws IllegalStateException, MediaCodec.CodecException { | 370 throws IllegalStateException, MediaCodec.CodecException { |
335 checkOnMediaCodecThread(); | 371 checkOnMediaCodecThread(); |
372 // Calling multiple MediaCodec.releaseOutputBuffer() with render=true in a r ow will result in | |
373 // dropped texture frames. Therefore, wait for any pending onTextureFrameAva ilable() before | |
374 // proceeding. | |
375 if (isWaitingForTexture) { | |
376 final DecodedTextureBuffer textureBuffer = | |
377 textureListener.dequeueTextureFrame(dequeueTimeoutMs); | |
378 isWaitingForTexture = (textureBuffer == null); | |
379 return textureBuffer; | |
380 } | |
381 | |
336 // Drain the decoder until receiving a decoded buffer or hitting | 382 // Drain the decoder until receiving a decoded buffer or hitting |
337 // MediaCodec.INFO_TRY_AGAIN_LATER. | 383 // MediaCodec.INFO_TRY_AGAIN_LATER. |
338 final MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); | 384 final MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
339 while (true) { | 385 while (true) { |
340 final int result = mediaCodec.dequeueOutputBuffer(info, dequeueTimeoutUs); | 386 final int result = mediaCodec.dequeueOutputBuffer( |
387 info, TimeUnit.MILLISECONDS.toMicros(dequeueTimeoutMs)); | |
341 switch (result) { | 388 switch (result) { |
342 case MediaCodec.INFO_TRY_AGAIN_LATER: | 389 case MediaCodec.INFO_TRY_AGAIN_LATER: |
343 return null; | 390 return null; |
344 case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: | 391 case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: |
345 outputBuffers = mediaCodec.getOutputBuffers(); | 392 outputBuffers = mediaCodec.getOutputBuffers(); |
346 Logging.d(TAG, "Decoder output buffers changed: " + outputBuffers.leng th); | 393 Logging.d(TAG, "Decoder output buffers changed: " + outputBuffers.leng th); |
347 break; | 394 break; |
348 case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: | 395 case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: |
349 MediaFormat format = mediaCodec.getOutputFormat(); | 396 MediaFormat format = mediaCodec.getOutputFormat(); |
350 Logging.d(TAG, "Decoder format changed: " + format.toString()); | 397 Logging.d(TAG, "Decoder format changed: " + format.toString()); |
(...skipping 13 matching lines...) Expand all Loading... | |
364 sliceHeight = format.getInteger("slice-height"); | 411 sliceHeight = format.getInteger("slice-height"); |
365 } | 412 } |
366 Logging.d(TAG, "Frame stride and slice height: " + stride + " x " + sl iceHeight); | 413 Logging.d(TAG, "Frame stride and slice height: " + stride + " x " + sl iceHeight); |
367 stride = Math.max(width, stride); | 414 stride = Math.max(width, stride); |
368 sliceHeight = Math.max(height, sliceHeight); | 415 sliceHeight = Math.max(height, sliceHeight); |
369 break; | 416 break; |
370 default: | 417 default: |
371 // Output buffer decoded. | 418 // Output buffer decoded. |
372 if (useSurface) { | 419 if (useSurface) { |
373 mediaCodec.releaseOutputBuffer(result, true /* render */); | 420 mediaCodec.releaseOutputBuffer(result, true /* render */); |
374 // TODO(magjed): Wait for SurfaceTexture.onFrameAvailable() before r eturning a texture | 421 final DecodedTextureBuffer textureBuffer = |
375 // frame. | 422 textureListener.dequeueTextureFrame(dequeueTimeoutMs); |
376 return new DecodedTextureBuffer(textureID, info.presentationTimeUs); | 423 isWaitingForTexture = (textureBuffer == null); |
424 return textureBuffer; | |
377 } else { | 425 } else { |
378 return new DecodedByteBuffer(result, info.offset, info.size, info.pr esentationTimeUs); | 426 return new DecodedByteBuffer(result, info.offset, info.size, info.pr esentationTimeUs); |
379 } | 427 } |
380 } | 428 } |
381 } | 429 } |
382 } | 430 } |
383 | 431 |
384 // Release a dequeued output byte buffer back to the codec for re-use. Should only be called for | 432 // Release a dequeued output byte buffer back to the codec for re-use. Should only be called for |
385 // non-surface decoding. | 433 // non-surface decoding. |
386 // Throws IllegalStateException if the call is made on the wrong thread, if co dec is configured | 434 // 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 | 435 // for surface decoding, or if |mediaCodec| is not in the Executing state. Thr ows |
388 // MediaCodec.CodecException upon codec error. | 436 // MediaCodec.CodecException upon codec error. |
389 private void returnDecodedByteBuffer(int index) | 437 private void returnDecodedByteBuffer(int index) |
390 throws IllegalStateException, MediaCodec.CodecException { | 438 throws IllegalStateException, MediaCodec.CodecException { |
391 checkOnMediaCodecThread(); | 439 checkOnMediaCodecThread(); |
392 if (useSurface) { | 440 if (useSurface) { |
393 throw new IllegalStateException("returnDecodedByteBuffer() called for surf ace decoding."); | 441 throw new IllegalStateException("returnDecodedByteBuffer() called for surf ace decoding."); |
394 } | 442 } |
395 mediaCodec.releaseOutputBuffer(index, false /* render */); | 443 mediaCodec.releaseOutputBuffer(index, false /* render */); |
396 } | 444 } |
397 } | 445 } |
OLD | NEW |