| 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; |
| 30 import android.media.MediaCodec; | 31 import android.media.MediaCodec; |
| 31 import android.media.MediaCodecInfo; | 32 import android.media.MediaCodecInfo; |
| 32 import android.media.MediaCodecInfo.CodecCapabilities; | 33 import android.media.MediaCodecInfo.CodecCapabilities; |
| 33 import android.media.MediaCodecList; | 34 import android.media.MediaCodecList; |
| 34 import android.media.MediaFormat; | 35 import android.media.MediaFormat; |
| 36 import android.opengl.EGLContext; |
| 37 import android.opengl.GLES11Ext; |
| 38 import android.opengl.GLES20; |
| 35 import android.os.Build; | 39 import android.os.Build; |
| 36 import android.view.Surface; | 40 import android.view.Surface; |
| 37 | 41 |
| 38 import org.webrtc.Logging; | 42 import org.webrtc.Logging; |
| 39 | 43 |
| 40 import java.nio.ByteBuffer; | 44 import java.nio.ByteBuffer; |
| 41 import java.util.Arrays; | 45 import java.util.Arrays; |
| 42 import java.util.List; | 46 import java.util.List; |
| 43 import java.util.concurrent.TimeUnit; | |
| 44 | 47 |
| 45 // Java-side of peerconnection_jni.cc:MediaCodecVideoDecoder. | 48 // Java-side of peerconnection_jni.cc:MediaCodecVideoDecoder. |
| 46 // This class is an implementation detail of the Java PeerConnection API. | 49 // 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 | 50 // MediaCodec is thread-hostile so this class must be operated on a single |
| 48 // thread. | 51 // thread. |
| 49 public class MediaCodecVideoDecoder { | 52 public class MediaCodecVideoDecoder { |
| 50 // This class is constructed, operated, and destroyed by its C++ incarnation, | 53 // 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 | 54 // 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 | 55 // class exposes aims to mimic the webrtc::VideoDecoder API as closely as |
| 53 // possibly to minimize the amount of translation work necessary. | 56 // possibly to minimize the amount of translation work necessary. |
| (...skipping 30 matching lines...) Expand all Loading... |
| 84 CodecCapabilities.COLOR_FormatYUV420Planar, | 87 CodecCapabilities.COLOR_FormatYUV420Planar, |
| 85 CodecCapabilities.COLOR_FormatYUV420SemiPlanar, | 88 CodecCapabilities.COLOR_FormatYUV420SemiPlanar, |
| 86 CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, | 89 CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, |
| 87 COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m); | 90 COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m); |
| 88 private int colorFormat; | 91 private int colorFormat; |
| 89 private int width; | 92 private int width; |
| 90 private int height; | 93 private int height; |
| 91 private int stride; | 94 private int stride; |
| 92 private int sliceHeight; | 95 private int sliceHeight; |
| 93 private boolean useSurface; | 96 private boolean useSurface; |
| 94 // |isWaitingForTexture| is true when waiting for the transition: | 97 private int textureID = 0; |
| 95 // MediaCodec.releaseOutputBuffer() -> onTextureFrameAvailable(). | 98 private SurfaceTexture surfaceTexture = null; |
| 96 private boolean isWaitingForTexture = false; | |
| 97 private TextureListener textureListener; | |
| 98 private Surface surface = null; | 99 private Surface surface = null; |
| 100 private EglBase eglBase; |
| 99 | 101 |
| 100 private MediaCodecVideoDecoder() { | 102 private MediaCodecVideoDecoder() { |
| 101 instance = this; | 103 instance = this; |
| 102 } | 104 } |
| 103 | 105 |
| 104 // Helper struct for findVp8Decoder() below. | 106 // Helper struct for findVp8Decoder() below. |
| 105 private static class DecoderProperties { | 107 private static class DecoderProperties { |
| 106 public DecoderProperties(String codecName, int colorFormat) { | 108 public DecoderProperties(String codecName, int colorFormat) { |
| 107 this.codecName = codecName; | 109 this.codecName = codecName; |
| 108 this.colorFormat = colorFormat; | 110 this.colorFormat = colorFormat; |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 186 } | 188 } |
| 187 | 189 |
| 188 private void checkOnMediaCodecThread() throws IllegalStateException { | 190 private void checkOnMediaCodecThread() throws IllegalStateException { |
| 189 if (mediaCodecThread.getId() != Thread.currentThread().getId()) { | 191 if (mediaCodecThread.getId() != Thread.currentThread().getId()) { |
| 190 throw new IllegalStateException( | 192 throw new IllegalStateException( |
| 191 "MediaCodecVideoDecoder previously operated on " + mediaCodecThread + | 193 "MediaCodecVideoDecoder previously operated on " + mediaCodecThread + |
| 192 " but is now called on " + Thread.currentThread()); | 194 " but is now called on " + Thread.currentThread()); |
| 193 } | 195 } |
| 194 } | 196 } |
| 195 | 197 |
| 196 // Pass null in |surfaceTextureHelper| to configure the codec for ByteBuffer o
utput. | 198 // Pass null in |sharedContext| to configure the codec for ByteBuffer output. |
| 197 private boolean initDecode( | 199 private boolean initDecode(VideoCodecType type, int width, int height, EGLCont
ext sharedContext) { |
| 198 VideoCodecType type, int width, int height, SurfaceTextureHelper surfaceTe
xtureHelper) { | |
| 199 if (mediaCodecThread != null) { | 200 if (mediaCodecThread != null) { |
| 200 throw new RuntimeException("Forgot to release()?"); | 201 throw new RuntimeException("Forgot to release()?"); |
| 201 } | 202 } |
| 202 useSurface = (surfaceTextureHelper != null); | 203 useSurface = (sharedContext != null); |
| 203 String mime = null; | 204 String mime = null; |
| 204 String[] supportedCodecPrefixes = null; | 205 String[] supportedCodecPrefixes = null; |
| 205 if (type == VideoCodecType.VIDEO_CODEC_VP8) { | 206 if (type == VideoCodecType.VIDEO_CODEC_VP8) { |
| 206 mime = VP8_MIME_TYPE; | 207 mime = VP8_MIME_TYPE; |
| 207 supportedCodecPrefixes = supportedVp8HwCodecPrefixes; | 208 supportedCodecPrefixes = supportedVp8HwCodecPrefixes; |
| 208 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { | 209 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { |
| 209 mime = H264_MIME_TYPE; | 210 mime = H264_MIME_TYPE; |
| 210 supportedCodecPrefixes = supportedH264HwCodecPrefixes; | 211 supportedCodecPrefixes = supportedH264HwCodecPrefixes; |
| 211 } else { | 212 } else { |
| 212 throw new RuntimeException("Non supported codec " + type); | 213 throw new RuntimeException("Non supported codec " + type); |
| 213 } | 214 } |
| 214 DecoderProperties properties = findDecoder(mime, supportedCodecPrefixes); | 215 DecoderProperties properties = findDecoder(mime, supportedCodecPrefixes); |
| 215 if (properties == null) { | 216 if (properties == null) { |
| 216 throw new RuntimeException("Cannot find HW decoder for " + type); | 217 throw new RuntimeException("Cannot find HW decoder for " + type); |
| 217 } | 218 } |
| 218 Logging.d(TAG, "Java initDecode: " + type + " : "+ width + " x " + height + | 219 Logging.d(TAG, "Java initDecode: " + type + " : "+ width + " x " + height + |
| 219 ". Color: 0x" + Integer.toHexString(properties.colorFormat) + | 220 ". Color: 0x" + Integer.toHexString(properties.colorFormat) + |
| 220 ". Use Surface: " + useSurface); | 221 ". Use Surface: " + useSurface); |
| 222 if (sharedContext != null) { |
| 223 Logging.d(TAG, "Decoder shared EGL Context: " + sharedContext); |
| 224 } |
| 221 mediaCodecThread = Thread.currentThread(); | 225 mediaCodecThread = Thread.currentThread(); |
| 222 try { | 226 try { |
| 223 this.width = width; | 227 this.width = width; |
| 224 this.height = height; | 228 this.height = height; |
| 225 stride = width; | 229 stride = width; |
| 226 sliceHeight = height; | 230 sliceHeight = height; |
| 227 | 231 |
| 228 if (useSurface) { | 232 if (useSurface) { |
| 229 textureListener = new TextureListener(surfaceTextureHelper); | 233 // Create shared EGL context. |
| 230 surface = new Surface(surfaceTextureHelper.getSurfaceTexture()); | 234 eglBase = new EglBase(sharedContext, EglBase.ConfigType.PIXEL_BUFFER); |
| 235 eglBase.createDummyPbufferSurface(); |
| 236 eglBase.makeCurrent(); |
| 237 |
| 238 // Create output surface |
| 239 textureID = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); |
| 240 Logging.d(TAG, "Video decoder TextureID = " + textureID); |
| 241 surfaceTexture = new SurfaceTexture(textureID); |
| 242 surface = new Surface(surfaceTexture); |
| 231 } | 243 } |
| 232 | 244 |
| 233 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); | 245 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); |
| 234 if (!useSurface) { | 246 if (!useSurface) { |
| 235 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); | 247 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); |
| 236 } | 248 } |
| 237 Logging.d(TAG, " Format: " + format); | 249 Logging.d(TAG, " Format: " + format); |
| 238 mediaCodec = | 250 mediaCodec = |
| 239 MediaCodecVideoEncoder.createByCodecName(properties.codecName); | 251 MediaCodecVideoEncoder.createByCodecName(properties.codecName); |
| 240 if (mediaCodec == null) { | 252 if (mediaCodec == null) { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 263 mediaCodec.release(); | 275 mediaCodec.release(); |
| 264 } catch (IllegalStateException e) { | 276 } catch (IllegalStateException e) { |
| 265 Logging.e(TAG, "release failed", e); | 277 Logging.e(TAG, "release failed", e); |
| 266 } | 278 } |
| 267 mediaCodec = null; | 279 mediaCodec = null; |
| 268 mediaCodecThread = null; | 280 mediaCodecThread = null; |
| 269 instance = null; | 281 instance = null; |
| 270 if (useSurface) { | 282 if (useSurface) { |
| 271 surface.release(); | 283 surface.release(); |
| 272 surface = null; | 284 surface = null; |
| 273 textureListener.release(); | 285 Logging.d(TAG, "Delete video decoder TextureID " + textureID); |
| 286 GLES20.glDeleteTextures(1, new int[] {textureID}, 0); |
| 287 textureID = 0; |
| 288 eglBase.release(); |
| 289 eglBase = null; |
| 274 } | 290 } |
| 275 Logging.d(TAG, "Java releaseDecoder done"); | 291 Logging.d(TAG, "Java releaseDecoder done"); |
| 276 } | 292 } |
| 277 | 293 |
| 278 // Dequeue an input buffer and return its index, -1 if no input buffer is | 294 // Dequeue an input buffer and return its index, -1 if no input buffer is |
| 279 // available, or -2 if the codec is no longer operative. | 295 // available, or -2 if the codec is no longer operative. |
| 280 private int dequeueInputBuffer() { | 296 private int dequeueInputBuffer() { |
| 281 checkOnMediaCodecThread(); | 297 checkOnMediaCodecThread(); |
| 282 try { | 298 try { |
| 283 return mediaCodec.dequeueInputBuffer(DEQUEUE_INPUT_TIMEOUT); | 299 return mediaCodec.dequeueInputBuffer(DEQUEUE_INPUT_TIMEOUT); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 312 } | 328 } |
| 313 | 329 |
| 314 private final int index; | 330 private final int index; |
| 315 private final int offset; | 331 private final int offset; |
| 316 private final int size; | 332 private final int size; |
| 317 private final long presentationTimestampUs; | 333 private final long presentationTimestampUs; |
| 318 } | 334 } |
| 319 | 335 |
| 320 private static class DecodedTextureBuffer { | 336 private static class DecodedTextureBuffer { |
| 321 private final int textureID; | 337 private final int textureID; |
| 322 private final float[] transformMatrix; | 338 private final long presentationTimestampUs; |
| 323 private final long timestampNs; | |
| 324 | 339 |
| 325 public DecodedTextureBuffer(int textureID, float[] transformMatrix, long tim
estampNs) { | 340 public DecodedTextureBuffer(int textureID, long presentationTimestampUs) { |
| 326 this.textureID = textureID; | 341 this.textureID = textureID; |
| 327 this.transformMatrix = transformMatrix; | 342 this.presentationTimestampUs = presentationTimestampUs; |
| 328 this.timestampNs = timestampNs; | |
| 329 } | 343 } |
| 330 } | 344 } |
| 331 | 345 |
| 332 // Poll based texture listener. | |
| 333 private static class TextureListener | |
| 334 implements SurfaceTextureHelper.OnTextureFrameAvailableListener { | |
| 335 private final SurfaceTextureHelper surfaceTextureHelper; | |
| 336 private DecodedTextureBuffer textureBuffer; | |
| 337 // |newFrameLock| is used to synchronize arrival of new frames with wait()/n
otifyAll(). | |
| 338 private final Object newFrameLock = new Object(); | |
| 339 | |
| 340 public TextureListener(SurfaceTextureHelper surfaceTextureHelper) { | |
| 341 this.surfaceTextureHelper = surfaceTextureHelper; | |
| 342 surfaceTextureHelper.setListener(this); | |
| 343 } | |
| 344 | |
| 345 // Callback from |surfaceTextureHelper|. May be called on an arbitrary threa
d. | |
| 346 @Override | |
| 347 public void onTextureFrameAvailable( | |
| 348 int oesTextureId, float[] transformMatrix, long timestampNs) { | |
| 349 synchronized (newFrameLock) { | |
| 350 if (textureBuffer != null) { | |
| 351 Logging.e(TAG, | |
| 352 "Unexpected onTextureFrameAvailable() called while already holding
a texture."); | |
| 353 throw new IllegalStateException("Already holding a texture."); | |
| 354 } | |
| 355 textureBuffer = new DecodedTextureBuffer(oesTextureId, transformMatrix,
timestampNs); | |
| 356 newFrameLock.notifyAll(); | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 // Dequeues and returns a texture buffer if available, or null otherwise. | |
| 361 public DecodedTextureBuffer dequeueTextureFrame(int timeoutMs) { | |
| 362 synchronized (newFrameLock) { | |
| 363 if (textureBuffer == null && timeoutMs > 0) { | |
| 364 try { | |
| 365 newFrameLock.wait(timeoutMs); | |
| 366 } catch(InterruptedException e) { | |
| 367 // Restore the interrupted status by reinterrupting the thread. | |
| 368 Thread.currentThread().interrupt(); | |
| 369 } | |
| 370 } | |
| 371 final DecodedTextureBuffer textureBuffer = this.textureBuffer; | |
| 372 this.textureBuffer = null; | |
| 373 return textureBuffer; | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 public void release() { | |
| 378 // SurfaceTextureHelper.disconnect() will block until any onTextureFrameAv
ailable() in | |
| 379 // progress is done. Therefore, the call to disconnect() must be outside a
ny synchronized | |
| 380 // statement that is also used in the onTextureFrameAvailable() above to a
void deadlocks. | |
| 381 surfaceTextureHelper.disconnect(); | |
| 382 synchronized (newFrameLock) { | |
| 383 if (textureBuffer != null) { | |
| 384 surfaceTextureHelper.returnTextureFrame(); | |
| 385 textureBuffer = null; | |
| 386 } | |
| 387 } | |
| 388 } | |
| 389 } | |
| 390 | |
| 391 // Returns null if no decoded buffer is available, and otherwise either a Deco
dedByteBuffer or | 346 // Returns null if no decoded buffer is available, and otherwise either a Deco
dedByteBuffer or |
| 392 // DecodedTexturebuffer depending on |useSurface| configuration. | 347 // DecodedTexturebuffer depending on |useSurface| configuration. |
| 393 // Throws IllegalStateException if call is made on the wrong thread, if color
format changes to an | 348 // Throws IllegalStateException if call is made on the wrong thread, if color
format changes to an |
| 394 // unsupported format, or if |mediaCodec| is not in the Executing state. Throw
s CodecException | 349 // unsupported format, or if |mediaCodec| is not in the Executing state. Throw
s CodecException |
| 395 // upon codec error. | 350 // upon codec error. |
| 396 private Object dequeueOutputBuffer(int dequeueTimeoutMs) | 351 private Object dequeueOutputBuffer(int dequeueTimeoutUs) |
| 397 throws IllegalStateException, MediaCodec.CodecException { | 352 throws IllegalStateException, MediaCodec.CodecException { |
| 398 checkOnMediaCodecThread(); | 353 checkOnMediaCodecThread(); |
| 399 // Calling multiple MediaCodec.releaseOutputBuffer() with render=true in a r
ow will result in | |
| 400 // dropped texture frames. Therefore, wait for any pending onTextureFrameAva
ilable() before | |
| 401 // proceeding. | |
| 402 if (isWaitingForTexture) { | |
| 403 final DecodedTextureBuffer textureBuffer = | |
| 404 textureListener.dequeueTextureFrame(dequeueTimeoutMs); | |
| 405 isWaitingForTexture = (textureBuffer == null); | |
| 406 return textureBuffer; | |
| 407 } | |
| 408 | |
| 409 // Drain the decoder until receiving a decoded buffer or hitting | 354 // Drain the decoder until receiving a decoded buffer or hitting |
| 410 // MediaCodec.INFO_TRY_AGAIN_LATER. | 355 // MediaCodec.INFO_TRY_AGAIN_LATER. |
| 411 final MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); | 356 final MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
| 412 while (true) { | 357 while (true) { |
| 413 final int result = mediaCodec.dequeueOutputBuffer( | 358 final int result = mediaCodec.dequeueOutputBuffer(info, dequeueTimeoutUs); |
| 414 info, TimeUnit.MILLISECONDS.toMicros(dequeueTimeoutMs)); | |
| 415 switch (result) { | 359 switch (result) { |
| 416 case MediaCodec.INFO_TRY_AGAIN_LATER: | 360 case MediaCodec.INFO_TRY_AGAIN_LATER: |
| 417 return null; | 361 return null; |
| 418 case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: | 362 case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: |
| 419 outputBuffers = mediaCodec.getOutputBuffers(); | 363 outputBuffers = mediaCodec.getOutputBuffers(); |
| 420 Logging.d(TAG, "Decoder output buffers changed: " + outputBuffers.leng
th); | 364 Logging.d(TAG, "Decoder output buffers changed: " + outputBuffers.leng
th); |
| 421 break; | 365 break; |
| 422 case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: | 366 case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: |
| 423 MediaFormat format = mediaCodec.getOutputFormat(); | 367 MediaFormat format = mediaCodec.getOutputFormat(); |
| 424 Logging.d(TAG, "Decoder format changed: " + format.toString()); | 368 Logging.d(TAG, "Decoder format changed: " + format.toString()); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 438 sliceHeight = format.getInteger("slice-height"); | 382 sliceHeight = format.getInteger("slice-height"); |
| 439 } | 383 } |
| 440 Logging.d(TAG, "Frame stride and slice height: " + stride + " x " + sl
iceHeight); | 384 Logging.d(TAG, "Frame stride and slice height: " + stride + " x " + sl
iceHeight); |
| 441 stride = Math.max(width, stride); | 385 stride = Math.max(width, stride); |
| 442 sliceHeight = Math.max(height, sliceHeight); | 386 sliceHeight = Math.max(height, sliceHeight); |
| 443 break; | 387 break; |
| 444 default: | 388 default: |
| 445 // Output buffer decoded. | 389 // Output buffer decoded. |
| 446 if (useSurface) { | 390 if (useSurface) { |
| 447 mediaCodec.releaseOutputBuffer(result, true /* render */); | 391 mediaCodec.releaseOutputBuffer(result, true /* render */); |
| 448 final DecodedTextureBuffer textureBuffer = | 392 // TODO(magjed): Wait for SurfaceTexture.onFrameAvailable() before r
eturning a texture |
| 449 textureListener.dequeueTextureFrame(dequeueTimeoutMs); | 393 // frame. |
| 450 isWaitingForTexture = (textureBuffer == null); | 394 return new DecodedTextureBuffer(textureID, info.presentationTimeUs); |
| 451 return textureBuffer; | |
| 452 } else { | 395 } else { |
| 453 return new DecodedByteBuffer(result, info.offset, info.size, info.pr
esentationTimeUs); | 396 return new DecodedByteBuffer(result, info.offset, info.size, info.pr
esentationTimeUs); |
| 454 } | 397 } |
| 455 } | 398 } |
| 456 } | 399 } |
| 457 } | 400 } |
| 458 | 401 |
| 459 // Release a dequeued output byte buffer back to the codec for re-use. Should
only be called for | 402 // Release a dequeued output byte buffer back to the codec for re-use. Should
only be called for |
| 460 // non-surface decoding. | 403 // non-surface decoding. |
| 461 // Throws IllegalStateException if the call is made on the wrong thread, if co
dec is configured | 404 // Throws IllegalStateException if the call is made on the wrong thread, if co
dec is configured |
| 462 // for surface decoding, or if |mediaCodec| is not in the Executing state. Thr
ows | 405 // for surface decoding, or if |mediaCodec| is not in the Executing state. Thr
ows |
| 463 // MediaCodec.CodecException upon codec error. | 406 // MediaCodec.CodecException upon codec error. |
| 464 private void returnDecodedByteBuffer(int index) | 407 private void returnDecodedByteBuffer(int index) |
| 465 throws IllegalStateException, MediaCodec.CodecException { | 408 throws IllegalStateException, MediaCodec.CodecException { |
| 466 checkOnMediaCodecThread(); | 409 checkOnMediaCodecThread(); |
| 467 if (useSurface) { | 410 if (useSurface) { |
| 468 throw new IllegalStateException("returnDecodedByteBuffer() called for surf
ace decoding."); | 411 throw new IllegalStateException("returnDecodedByteBuffer() called for surf
ace decoding."); |
| 469 } | 412 } |
| 470 mediaCodec.releaseOutputBuffer(index, false /* render */); | 413 mediaCodec.releaseOutputBuffer(index, false /* render */); |
| 471 } | 414 } |
| 472 } | 415 } |
| OLD | NEW |