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 |