| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.media; | 5 package org.chromium.media; |
| 6 | 6 |
| 7 import android.annotation.TargetApi; | 7 import android.annotation.TargetApi; |
| 8 import android.media.AudioFormat; | 8 import android.media.AudioFormat; |
| 9 import android.media.AudioManager; | 9 import android.media.AudioManager; |
| 10 import android.media.AudioTrack; | 10 import android.media.AudioTrack; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 67 private static final String KEY_CROP_LEFT = "crop-left"; | 67 private static final String KEY_CROP_LEFT = "crop-left"; |
| 68 private static final String KEY_CROP_RIGHT = "crop-right"; | 68 private static final String KEY_CROP_RIGHT = "crop-right"; |
| 69 private static final String KEY_CROP_BOTTOM = "crop-bottom"; | 69 private static final String KEY_CROP_BOTTOM = "crop-bottom"; |
| 70 private static final String KEY_CROP_TOP = "crop-top"; | 70 private static final String KEY_CROP_TOP = "crop-top"; |
| 71 | 71 |
| 72 private ByteBuffer[] mInputBuffers; | 72 private ByteBuffer[] mInputBuffers; |
| 73 private ByteBuffer[] mOutputBuffers; | 73 private ByteBuffer[] mOutputBuffers; |
| 74 | 74 |
| 75 private MediaCodec mMediaCodec; | 75 private MediaCodec mMediaCodec; |
| 76 private AudioTrack mAudioTrack; | 76 private AudioTrack mAudioTrack; |
| 77 private ArrayList<byte[]> mPendingAudioBuffers; |
| 77 private boolean mFlushed; | 78 private boolean mFlushed; |
| 78 private long mLastPresentationTimeUs; | 79 private long mLastPresentationTimeUs; |
| 79 private String mMime; | 80 private String mMime; |
| 80 private boolean mAdaptivePlaybackSupported; | 81 private boolean mAdaptivePlaybackSupported; |
| 81 | 82 |
| 82 private static class DequeueInputResult { | 83 private static class DequeueInputResult { |
| 83 private final int mStatus; | 84 private final int mStatus; |
| 84 private final int mIndex; | 85 private final int mIndex; |
| 85 | 86 |
| 86 private DequeueInputResult(int status, int index) { | 87 private DequeueInputResult(int status, int index) { |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 295 } | 296 } |
| 296 } | 297 } |
| 297 | 298 |
| 298 return null; | 299 return null; |
| 299 } | 300 } |
| 300 | 301 |
| 301 private MediaCodecBridge( | 302 private MediaCodecBridge( |
| 302 MediaCodec mediaCodec, String mime, boolean adaptivePlaybackSupporte
d) { | 303 MediaCodec mediaCodec, String mime, boolean adaptivePlaybackSupporte
d) { |
| 303 assert mediaCodec != null; | 304 assert mediaCodec != null; |
| 304 mMediaCodec = mediaCodec; | 305 mMediaCodec = mediaCodec; |
| 306 mPendingAudioBuffers = new ArrayList<byte[]>(); |
| 305 mMime = mime; | 307 mMime = mime; |
| 306 mLastPresentationTimeUs = 0; | 308 mLastPresentationTimeUs = 0; |
| 307 mFlushed = true; | 309 mFlushed = true; |
| 308 mAdaptivePlaybackSupported = adaptivePlaybackSupported; | 310 mAdaptivePlaybackSupported = adaptivePlaybackSupported; |
| 309 } | 311 } |
| 310 | 312 |
| 311 @CalledByNative | 313 @CalledByNative |
| 312 private static MediaCodecBridge create(String mime, boolean isSecure, int di
rection) { | 314 private static MediaCodecBridge create(String mime, boolean isSecure, int di
rection) { |
| 313 // Creation of ".secure" codecs sometimes crash instead of throwing exce
ptions | 315 // Creation of ".secure" codecs sometimes crash instead of throwing exce
ptions |
| 314 // on pre-JBMR2 devices. | 316 // on pre-JBMR2 devices. |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 358 mMediaCodec.release(); | 360 mMediaCodec.release(); |
| 359 } catch (IllegalStateException e) { | 361 } catch (IllegalStateException e) { |
| 360 // The MediaCodec is stuck in a wrong state, possibly due to losing | 362 // The MediaCodec is stuck in a wrong state, possibly due to losing |
| 361 // the surface. | 363 // the surface. |
| 362 Log.e(TAG, "Cannot release media codec", e); | 364 Log.e(TAG, "Cannot release media codec", e); |
| 363 } | 365 } |
| 364 mMediaCodec = null; | 366 mMediaCodec = null; |
| 365 if (mAudioTrack != null) { | 367 if (mAudioTrack != null) { |
| 366 mAudioTrack.release(); | 368 mAudioTrack.release(); |
| 367 } | 369 } |
| 370 mPendingAudioBuffers.clear(); |
| 368 } | 371 } |
| 369 | 372 |
| 370 @SuppressWarnings("deprecation") | 373 @SuppressWarnings("deprecation") |
| 371 @CalledByNative | 374 @CalledByNative |
| 372 private boolean start() { | 375 private boolean start() { |
| 373 try { | 376 try { |
| 374 mMediaCodec.start(); | 377 mMediaCodec.start(); |
| 375 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { | 378 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { |
| 376 mInputBuffers = mMediaCodec.getInputBuffers(); | 379 mInputBuffers = mMediaCodec.getInputBuffers(); |
| 377 mOutputBuffers = mMediaCodec.getOutputBuffers(); | 380 mOutputBuffers = mMediaCodec.getOutputBuffers(); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 406 } | 409 } |
| 407 | 410 |
| 408 @CalledByNative | 411 @CalledByNative |
| 409 private int flush() { | 412 private int flush() { |
| 410 try { | 413 try { |
| 411 mFlushed = true; | 414 mFlushed = true; |
| 412 if (mAudioTrack != null) { | 415 if (mAudioTrack != null) { |
| 413 // Need to call pause() here, or otherwise flush() is a no-op. | 416 // Need to call pause() here, or otherwise flush() is a no-op. |
| 414 mAudioTrack.pause(); | 417 mAudioTrack.pause(); |
| 415 mAudioTrack.flush(); | 418 mAudioTrack.flush(); |
| 419 mPendingAudioBuffers.clear(); |
| 416 } | 420 } |
| 417 mMediaCodec.flush(); | 421 mMediaCodec.flush(); |
| 418 } catch (IllegalStateException e) { | 422 } catch (IllegalStateException e) { |
| 419 Log.e(TAG, "Failed to flush MediaCodec", e); | 423 Log.e(TAG, "Failed to flush MediaCodec", e); |
| 420 return MEDIA_CODEC_ERROR; | 424 return MEDIA_CODEC_ERROR; |
| 421 } | 425 } |
| 422 return MEDIA_CODEC_OK; | 426 return MEDIA_CODEC_OK; |
| 423 } | 427 } |
| 424 | 428 |
| 425 @CalledByNative | 429 @CalledByNative |
| (...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 718 } | 722 } |
| 719 | 723 |
| 720 /** | 724 /** |
| 721 * Play the audio buffer that is passed in. | 725 * Play the audio buffer that is passed in. |
| 722 * | 726 * |
| 723 * @param buf Audio buffer to be rendered. | 727 * @param buf Audio buffer to be rendered. |
| 724 * @return The number of frames that have already been consumed by the | 728 * @return The number of frames that have already been consumed by the |
| 725 * hardware. This number resets to 0 after each flush call. | 729 * hardware. This number resets to 0 after each flush call. |
| 726 */ | 730 */ |
| 727 @CalledByNative | 731 @CalledByNative |
| 728 private long playOutputBuffer(byte[] buf) { | 732 private long playOutputBuffer(byte[] buf, boolean postpone) { |
| 733 if (postpone) { |
| 734 Log.v(TAG, "Saving pending buffer"); |
| 735 mPendingAudioBuffers.add(buf); |
| 736 return 0; |
| 737 } |
| 738 |
| 729 if (mAudioTrack == null) { | 739 if (mAudioTrack == null) { |
| 730 return 0; | 740 return 0; |
| 731 } | 741 } |
| 732 | 742 |
| 733 if (AudioTrack.PLAYSTATE_PLAYING != mAudioTrack.getPlayState()) { | 743 if (AudioTrack.PLAYSTATE_PLAYING != mAudioTrack.getPlayState()) { |
| 734 mAudioTrack.play(); | 744 mAudioTrack.play(); |
| 735 } | 745 } |
| 736 int size = mAudioTrack.write(buf, 0, buf.length); | 746 |
| 747 int size = 0; |
| 748 for (byte[] pbuf : mPendingAudioBuffers) { |
| 749 Log.v(TAG, "Writing pending buffer"); |
| 750 size = mAudioTrack.write(pbuf, 0, pbuf.length); |
| 751 if (pbuf.length != size) { |
| 752 Log.i(TAG, "Failed to send all data to audio output, expected si
ze: " + pbuf.length |
| 753 + ", actual size: " + size); |
| 754 } |
| 755 } |
| 756 |
| 757 mPendingAudioBuffers.clear(); |
| 758 |
| 759 size = mAudioTrack.write(buf, 0, buf.length); |
| 737 if (buf.length != size) { | 760 if (buf.length != size) { |
| 738 Log.i(TAG, "Failed to send all data to audio output, expected size:
" | 761 Log.i(TAG, "Failed to send all data to audio output, expected size:
" |
| 739 + buf.length + ", actual size: " + size); | 762 + buf.length + ", actual size: " + size); |
| 740 } | 763 } |
| 741 // TODO(qinmin): Returning the head position allows us to estimate | 764 // TODO(qinmin): Returning the head position allows us to estimate |
| 742 // the current presentation time in native code. However, it is | 765 // the current presentation time in native code. However, it is |
| 743 // better to use AudioTrack.getCurrentTimestamp() to get the last | 766 // better to use AudioTrack.getCurrentTimestamp() to get the last |
| 744 // known time when a frame is played. However, we will need to | 767 // known time when a frame is played. However, we will need to |
| 745 // convert the java nano time to C++ timestamp. | 768 // convert the java nano time to C++ timestamp. |
| 746 // If the stream runs too long, getPlaybackHeadPosition() could | 769 // If the stream runs too long, getPlaybackHeadPosition() could |
| (...skipping 28 matching lines...) Expand all Loading... |
| 775 return AudioFormat.CHANNEL_OUT_QUAD; | 798 return AudioFormat.CHANNEL_OUT_QUAD; |
| 776 case 6: | 799 case 6: |
| 777 return AudioFormat.CHANNEL_OUT_5POINT1; | 800 return AudioFormat.CHANNEL_OUT_5POINT1; |
| 778 case 8: | 801 case 8: |
| 779 return AudioFormat.CHANNEL_OUT_7POINT1; | 802 return AudioFormat.CHANNEL_OUT_7POINT1; |
| 780 default: | 803 default: |
| 781 return AudioFormat.CHANNEL_OUT_DEFAULT; | 804 return AudioFormat.CHANNEL_OUT_DEFAULT; |
| 782 } | 805 } |
| 783 } | 806 } |
| 784 } | 807 } |
| OLD | NEW |