| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 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.media.AudioFormat; | 7 import android.media.AudioFormat; |
| 8 import android.media.AudioManager; | 8 import android.media.AudioManager; |
| 9 import android.media.AudioTrack; | 9 import android.media.AudioTrack; |
| 10 import android.media.MediaCodec; | 10 import android.media.MediaCodec; |
| 11 import android.media.MediaCodecInfo; | 11 import android.media.MediaCodecInfo; |
| 12 import android.media.MediaCodecList; | 12 import android.media.MediaCodecList; |
| 13 import android.media.MediaCrypto; | 13 import android.media.MediaCrypto; |
| 14 import android.media.MediaFormat; | 14 import android.media.MediaFormat; |
| 15 import android.os.Build; | 15 import android.os.Build; |
| 16 import android.os.Bundle; |
| 16 import android.util.Log; | 17 import android.util.Log; |
| 17 import android.view.Surface; | 18 import android.view.Surface; |
| 18 | 19 |
| 19 import java.io.IOException; | 20 import java.io.IOException; |
| 20 import java.nio.ByteBuffer; | 21 import java.nio.ByteBuffer; |
| 21 import java.util.ArrayList; | 22 import java.util.ArrayList; |
| 23 import java.util.Collection; |
| 22 import java.util.HashMap; | 24 import java.util.HashMap; |
| 23 import java.util.Map; | 25 import java.util.Map; |
| 24 | 26 |
| 25 import org.chromium.base.CalledByNative; | 27 import org.chromium.base.CalledByNative; |
| 26 import org.chromium.base.JNINamespace; | 28 import org.chromium.base.JNINamespace; |
| 27 | 29 |
| 28 /** | 30 /** |
| 29 * A wrapper of the MediaCodec class to facilitate exception capturing and | 31 * A wrapper of the MediaCodec class to facilitate exception capturing and |
| 30 * audio rendering. | 32 * audio rendering. |
| 31 */ | 33 */ |
| 32 @JNINamespace("media") | 34 @JNINamespace("media") |
| 33 class MediaCodecBridge { | 35 class MediaCodecBridge { |
| 34 private static final String TAG = "MediaCodecBridge"; | 36 private static final String TAG = "MediaCodecBridge"; |
| 35 | 37 |
| 36 // Error code for MediaCodecBridge. Keep this value in sync with | 38 // Error code for MediaCodecBridge. Keep this value in sync with |
| 37 // MediaCodecStatus in media_codec_bridge.h. | 39 // MediaCodecStatus in media_codec_bridge.h. |
| 38 private static final int MEDIA_CODEC_OK = 0; | 40 private static final int MEDIA_CODEC_OK = 0; |
| 39 private static final int MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER = 1; | 41 private static final int MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER = 1; |
| 40 private static final int MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER = 2; | 42 private static final int MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER = 2; |
| 41 private static final int MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED = 3; | 43 private static final int MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED = 3; |
| 42 private static final int MEDIA_CODEC_OUTPUT_FORMAT_CHANGED = 4; | 44 private static final int MEDIA_CODEC_OUTPUT_FORMAT_CHANGED = 4; |
| 43 private static final int MEDIA_CODEC_INPUT_END_OF_STREAM = 5; | 45 private static final int MEDIA_CODEC_INPUT_END_OF_STREAM = 5; |
| 44 private static final int MEDIA_CODEC_OUTPUT_END_OF_STREAM = 6; | 46 private static final int MEDIA_CODEC_OUTPUT_END_OF_STREAM = 6; |
| 45 private static final int MEDIA_CODEC_NO_KEY = 7; | 47 private static final int MEDIA_CODEC_NO_KEY = 7; |
| 46 private static final int MEDIA_CODEC_STOPPED = 8; | 48 private static final int MEDIA_CODEC_STOPPED = 8; |
| 47 private static final int MEDIA_CODEC_ERROR = 9; | 49 private static final int MEDIA_CODEC_ERROR = 9; |
| 48 | 50 |
| 51 // Codec direction. Keep this in sync with media_codec_bridge.h. |
| 52 private static final int MEDIA_CODEC_DECODER = 0; |
| 53 private static final int MEDIA_CODEC_ENCODER = 1; |
| 54 |
| 49 // After a flush(), dequeueOutputBuffer() can often produce empty presentati
on timestamps | 55 // After a flush(), dequeueOutputBuffer() can often produce empty presentati
on timestamps |
| 50 // for several frames. As a result, the player may find that the time does n
ot increase | 56 // for several frames. As a result, the player may find that the time does n
ot increase |
| 51 // after decoding a frame. To detect this, we check whether the presentation
timestamp from | 57 // after decoding a frame. To detect this, we check whether the presentation
timestamp from |
| 52 // dequeueOutputBuffer() is larger than input_timestamp - MAX_PRESENTATION_T
IMESTAMP_SHIFT_US | 58 // dequeueOutputBuffer() is larger than input_timestamp - MAX_PRESENTATION_T
IMESTAMP_SHIFT_US |
| 53 // after a flush. And we set the presentation timestamp from dequeueOutputBu
ffer() to be | 59 // after a flush. And we set the presentation timestamp from dequeueOutputBu
ffer() to be |
| 54 // non-decreasing for the remaining frames. | 60 // non-decreasing for the remaining frames. |
| 55 private static final long MAX_PRESENTATION_TIMESTAMP_SHIFT_US = 100000; | 61 private static final long MAX_PRESENTATION_TIMESTAMP_SHIFT_US = 100000; |
| 56 | 62 |
| 57 private ByteBuffer[] mInputBuffers; | 63 private ByteBuffer[] mInputBuffers; |
| 58 private ByteBuffer[] mOutputBuffers; | 64 private ByteBuffer[] mOutputBuffers; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 77 @CalledByNative("DequeueInputResult") | 83 @CalledByNative("DequeueInputResult") |
| 78 private int index() { return mIndex; } | 84 private int index() { return mIndex; } |
| 79 } | 85 } |
| 80 | 86 |
| 81 /** | 87 /** |
| 82 * This class represents supported android codec information. | 88 * This class represents supported android codec information. |
| 83 */ | 89 */ |
| 84 private static class CodecInfo { | 90 private static class CodecInfo { |
| 85 private final String mCodecType; // e.g. "video/x-vnd.on2.vp8". | 91 private final String mCodecType; // e.g. "video/x-vnd.on2.vp8". |
| 86 private final String mCodecName; // e.g. "OMX.google.vp8.decoder". | 92 private final String mCodecName; // e.g. "OMX.google.vp8.decoder". |
| 93 private final int mDirection; |
| 87 | 94 |
| 88 private CodecInfo(String codecType, String codecName) { | 95 private CodecInfo(String codecType, String codecName, |
| 96 int direction) { |
| 89 mCodecType = codecType; | 97 mCodecType = codecType; |
| 90 mCodecName = codecName; | 98 mCodecName = codecName; |
| 99 mDirection = direction; |
| 91 } | 100 } |
| 92 | 101 |
| 93 @CalledByNative("CodecInfo") | 102 @CalledByNative("CodecInfo") |
| 94 private String codecType() { return mCodecType; } | 103 private String codecType() { return mCodecType; } |
| 95 | 104 |
| 96 @CalledByNative("CodecInfo") | 105 @CalledByNative("CodecInfo") |
| 97 private String codecName() { return mCodecName; } | 106 private String codecName() { return mCodecName; } |
| 107 |
| 108 @CalledByNative("CodecInfo") |
| 109 private int direction() { return mDirection; } |
| 98 } | 110 } |
| 99 | 111 |
| 100 private static class DequeueOutputResult { | 112 private static class DequeueOutputResult { |
| 101 private final int mStatus; | 113 private final int mStatus; |
| 102 private final int mIndex; | 114 private final int mIndex; |
| 103 private final int mFlags; | 115 private final int mFlags; |
| 104 private final int mOffset; | 116 private final int mOffset; |
| 105 private final long mPresentationTimeMicroseconds; | 117 private final long mPresentationTimeMicroseconds; |
| 106 private final int mNumBytes; | 118 private final int mNumBytes; |
| 107 | 119 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 132 | 144 |
| 133 @CalledByNative("DequeueOutputResult") | 145 @CalledByNative("DequeueOutputResult") |
| 134 private int numBytes() { return mNumBytes; } | 146 private int numBytes() { return mNumBytes; } |
| 135 } | 147 } |
| 136 | 148 |
| 137 /** | 149 /** |
| 138 * Get a list of supported android codec mimes. | 150 * Get a list of supported android codec mimes. |
| 139 */ | 151 */ |
| 140 @CalledByNative | 152 @CalledByNative |
| 141 private static CodecInfo[] getCodecsInfo() { | 153 private static CodecInfo[] getCodecsInfo() { |
| 142 Map<String, CodecInfo> CodecInfoMap = new HashMap<String, CodecInfo>(); | 154 // Return the first (highest-priority) codec for each MIME type. |
| 155 Map<String, CodecInfo> encoderInfoMap = new HashMap<String, CodecInfo>()
; |
| 156 Map<String, CodecInfo> decoderInfoMap = new HashMap<String, CodecInfo>()
; |
| 143 int count = MediaCodecList.getCodecCount(); | 157 int count = MediaCodecList.getCodecCount(); |
| 144 for (int i = 0; i < count; ++i) { | 158 for (int i = 0; i < count; ++i) { |
| 145 MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); | 159 MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); |
| 146 if (info.isEncoder()) { | 160 int direction = |
| 147 continue; | 161 info.isEncoder() ? MEDIA_CODEC_ENCODER : MEDIA_CODEC_DECODER; |
| 148 } | |
| 149 | |
| 150 String codecString = info.getName(); | 162 String codecString = info.getName(); |
| 151 String[] supportedTypes = info.getSupportedTypes(); | 163 String[] supportedTypes = info.getSupportedTypes(); |
| 152 for (int j = 0; j < supportedTypes.length; ++j) { | 164 for (int j = 0; j < supportedTypes.length; ++j) { |
| 153 if (!CodecInfoMap.containsKey(supportedTypes[j])) { | 165 Map<String, CodecInfo> map = info.isEncoder() ? encoderInfoMap :
decoderInfoMap; |
| 154 CodecInfoMap.put(supportedTypes[j], new CodecInfo( | 166 if (!map.containsKey(supportedTypes[j])) { |
| 155 supportedTypes[j], codecString)); | 167 map.put(supportedTypes[j], new CodecInfo( |
| 168 supportedTypes[j], codecString, direction)); |
| 156 } | 169 } |
| 157 } | 170 } |
| 158 } | 171 } |
| 159 return CodecInfoMap.values().toArray( | 172 ArrayList<CodecInfo> codecInfos = new ArrayList<CodecInfo>( |
| 160 new CodecInfo[CodecInfoMap.size()]); | 173 decoderInfoMap.size() + encoderInfoMap.size()); |
| 174 codecInfos.addAll(encoderInfoMap.values()); |
| 175 codecInfos.addAll(decoderInfoMap.values()); |
| 176 return codecInfos.toArray(new CodecInfo[codecInfos.size()]); |
| 161 } | 177 } |
| 162 | 178 |
| 163 private static String getSecureDecoderNameForMime(String mime) { | 179 private static String getSecureDecoderNameForMime(String mime) { |
| 164 int count = MediaCodecList.getCodecCount(); | 180 int count = MediaCodecList.getCodecCount(); |
| 165 for (int i = 0; i < count; ++i) { | 181 for (int i = 0; i < count; ++i) { |
| 166 MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); | 182 MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); |
| 167 if (info.isEncoder()) { | 183 if (info.isEncoder()) { |
| 168 continue; | 184 continue; |
| 169 } | 185 } |
| 170 | 186 |
| 171 String[] supportedTypes = info.getSupportedTypes(); | 187 String[] supportedTypes = info.getSupportedTypes(); |
| 172 for (int j = 0; j < supportedTypes.length; ++j) { | 188 for (int j = 0; j < supportedTypes.length; ++j) { |
| 173 if (supportedTypes[j].equalsIgnoreCase(mime)) { | 189 if (supportedTypes[j].equalsIgnoreCase(mime)) { |
| 174 return info.getName() + ".secure"; | 190 return info.getName() + ".secure"; |
| 175 } | 191 } |
| 176 } | 192 } |
| 177 } | 193 } |
| 178 | 194 |
| 179 return null; | 195 return null; |
| 180 } | 196 } |
| 181 | 197 |
| 182 private MediaCodecBridge(MediaCodec mediaCodec) { | 198 private MediaCodecBridge(MediaCodec mediaCodec) { |
| 183 assert(mediaCodec != null); | 199 assert(mediaCodec != null); |
| 184 mMediaCodec = mediaCodec; | 200 mMediaCodec = mediaCodec; |
| 185 mLastPresentationTimeUs = 0; | 201 mLastPresentationTimeUs = 0; |
| 186 mFlushed = true; | 202 mFlushed = true; |
| 187 } | 203 } |
| 188 | 204 |
| 189 @CalledByNative | 205 @CalledByNative |
| 190 private static MediaCodecBridge create(String mime, boolean isSecure) { | 206 private static MediaCodecBridge create(String mime, boolean isSecure, int di
rection) { |
| 191 // Creation of ".secure" codecs sometimes crash instead of throwing exce
ptions | 207 // Creation of ".secure" codecs sometimes crash instead of throwing exce
ptions |
| 192 // on pre-JBMR2 devices. | 208 // on pre-JBMR2 devices. |
| 193 if (isSecure && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_M
R2) { | 209 if (isSecure && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_M
R2) { |
| 194 return null; | 210 return null; |
| 195 } | 211 } |
| 196 MediaCodec mediaCodec = null; | 212 MediaCodec mediaCodec = null; |
| 197 try { | 213 try { |
| 198 // |isSecure| only applies to video decoders. | 214 // |isSecure| only applies to video decoders. |
| 199 if (mime.startsWith("video") && isSecure) { | 215 if (mime.startsWith("video") && isSecure && direction == MEDIA_CODEC
_DECODER) { |
| 200 mediaCodec = MediaCodec.createByCodecName(getSecureDecoderNameFo
rMime(mime)); | 216 mediaCodec = MediaCodec.createByCodecName(getSecureDecoderNameFo
rMime(mime)); |
| 201 } else { | 217 } else { |
| 202 mediaCodec = MediaCodec.createDecoderByType(mime); | 218 if (direction == MEDIA_CODEC_ENCODER) { |
| 219 mediaCodec = MediaCodec.createEncoderByType(mime); |
| 220 } else { |
| 221 mediaCodec = MediaCodec.createDecoderByType(mime); |
| 222 } |
| 203 } | 223 } |
| 204 } catch (Exception e) { | 224 } catch (Exception e) { |
| 205 Log.e(TAG, "Failed to create MediaCodec: " + mime + ", isSecure: " | 225 Log.e(TAG, "Failed to create MediaCodec: " + mime + ", isSecure: " |
| 206 + isSecure + ", " + e.toString()); | 226 + isSecure + ", direction: " + direction, e); |
| 207 } | 227 } |
| 208 | 228 |
| 209 if (mediaCodec == null) { | 229 if (mediaCodec == null) { |
| 210 return null; | 230 return null; |
| 211 } | 231 } |
| 212 | 232 |
| 213 return new MediaCodecBridge(mediaCodec); | 233 return new MediaCodecBridge(mediaCodec); |
| 214 } | 234 } |
| 215 | 235 |
| 216 @CalledByNative | 236 @CalledByNative |
| 217 private void release() { | 237 private void release() { |
| 238 mMediaCodec.stop(); |
| 218 mMediaCodec.release(); | 239 mMediaCodec.release(); |
| 240 mMediaCodec = null; |
| 219 if (mAudioTrack != null) { | 241 if (mAudioTrack != null) { |
| 220 mAudioTrack.release(); | 242 mAudioTrack.release(); |
| 221 } | 243 } |
| 222 } | 244 } |
| 223 | 245 |
| 224 @CalledByNative | 246 @CalledByNative |
| 225 private boolean start() { | 247 private boolean start() { |
| 226 try { | 248 try { |
| 227 mMediaCodec.start(); | 249 mMediaCodec.start(); |
| 228 mInputBuffers = mMediaCodec.getInputBuffers(); | 250 mInputBuffers = mMediaCodec.getInputBuffers(); |
| 229 } catch (IllegalStateException e) { | 251 } catch (IllegalStateException e) { |
| 230 Log.e(TAG, "Cannot start the media codec " + e.toString()); | 252 Log.e(TAG, "Cannot start the media codec", e); |
| 231 return false; | 253 return false; |
| 232 } | 254 } |
| 233 return true; | 255 return true; |
| 234 } | 256 } |
| 235 | 257 |
| 236 @CalledByNative | 258 @CalledByNative |
| 237 private DequeueInputResult dequeueInputBuffer(long timeoutUs) { | 259 private DequeueInputResult dequeueInputBuffer(long timeoutUs) { |
| 238 int status = MEDIA_CODEC_ERROR; | 260 int status = MEDIA_CODEC_ERROR; |
| 239 int index = -1; | 261 int index = -1; |
| 240 try { | 262 try { |
| 241 int index_or_status = mMediaCodec.dequeueInputBuffer(timeoutUs); | 263 int index_or_status = mMediaCodec.dequeueInputBuffer(timeoutUs); |
| 242 if (index_or_status >= 0) { // index! | 264 if (index_or_status >= 0) { // index! |
| 243 status = MEDIA_CODEC_OK; | 265 status = MEDIA_CODEC_OK; |
| 244 index = index_or_status; | 266 index = index_or_status; |
| 245 } else if (index_or_status == MediaCodec.INFO_TRY_AGAIN_LATER) { | 267 } else if (index_or_status == MediaCodec.INFO_TRY_AGAIN_LATER) { |
| 246 Log.e(TAG, "dequeueInputBuffer: MediaCodec.INFO_TRY_AGAIN_LATER"
); | 268 Log.e(TAG, "dequeueInputBuffer: MediaCodec.INFO_TRY_AGAIN_LATER"
); |
| 247 status = MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER; | 269 status = MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER; |
| 248 } else { | 270 } else { |
| 271 Log.e(TAG, "Unexpected index_or_status: " + index_or_status); |
| 249 assert(false); | 272 assert(false); |
| 250 } | 273 } |
| 251 } catch(Exception e) { | 274 } catch(Exception e) { |
| 252 Log.e(TAG, "Failed to dequeue input buffer: " + e.toString()); | 275 Log.e(TAG, "Failed to dequeue input buffer", e); |
| 253 } | 276 } |
| 254 return new DequeueInputResult(status, index); | 277 return new DequeueInputResult(status, index); |
| 255 } | 278 } |
| 256 | 279 |
| 257 @CalledByNative | 280 @CalledByNative |
| 258 private int flush() { | 281 private int flush() { |
| 259 try { | 282 try { |
| 260 mFlushed = true; | 283 mFlushed = true; |
| 261 if (mAudioTrack != null) { | 284 if (mAudioTrack != null) { |
| 262 mAudioTrack.flush(); | 285 mAudioTrack.flush(); |
| 263 } | 286 } |
| 264 mMediaCodec.flush(); | 287 mMediaCodec.flush(); |
| 265 } catch(IllegalStateException e) { | 288 } catch(IllegalStateException e) { |
| 266 Log.e(TAG, "Failed to flush MediaCodec " + e.toString()); | 289 Log.e(TAG, "Failed to flush MediaCodec", e); |
| 267 return MEDIA_CODEC_ERROR; | 290 return MEDIA_CODEC_ERROR; |
| 268 } | 291 } |
| 269 return MEDIA_CODEC_OK; | 292 return MEDIA_CODEC_OK; |
| 270 } | 293 } |
| 271 | 294 |
| 272 @CalledByNative | 295 @CalledByNative |
| 273 private void stop() { | 296 private void stop() { |
| 274 mMediaCodec.stop(); | 297 mMediaCodec.stop(); |
| 275 if (mAudioTrack != null) { | 298 if (mAudioTrack != null) { |
| 276 mAudioTrack.pause(); | 299 mAudioTrack.pause(); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 291 private ByteBuffer getInputBuffer(int index) { | 314 private ByteBuffer getInputBuffer(int index) { |
| 292 return mInputBuffers[index]; | 315 return mInputBuffers[index]; |
| 293 } | 316 } |
| 294 | 317 |
| 295 @CalledByNative | 318 @CalledByNative |
| 296 private ByteBuffer getOutputBuffer(int index) { | 319 private ByteBuffer getOutputBuffer(int index) { |
| 297 return mOutputBuffers[index]; | 320 return mOutputBuffers[index]; |
| 298 } | 321 } |
| 299 | 322 |
| 300 @CalledByNative | 323 @CalledByNative |
| 324 private int getInputBuffersCount() { |
| 325 return mInputBuffers.length; |
| 326 } |
| 327 |
| 328 @CalledByNative |
| 329 private int getOutputBuffersCount() { |
| 330 return mOutputBuffers != null ? mOutputBuffers.length : -1; |
| 331 } |
| 332 |
| 333 @CalledByNative |
| 334 private int getOutputBuffersCapacity() { |
| 335 return mOutputBuffers != null ? mOutputBuffers[0].capacity() : -1; |
| 336 } |
| 337 |
| 338 @CalledByNative |
| 339 private boolean getOutputBuffers() { |
| 340 try { |
| 341 mOutputBuffers = mMediaCodec.getOutputBuffers(); |
| 342 } catch (IllegalStateException e) { |
| 343 Log.e(TAG, "Cannot get output buffers", e); |
| 344 return false; |
| 345 } |
| 346 return true; |
| 347 } |
| 348 |
| 349 @CalledByNative |
| 301 private int queueInputBuffer( | 350 private int queueInputBuffer( |
| 302 int index, int offset, int size, long presentationTimeUs, int flags)
{ | 351 int index, int offset, int size, long presentationTimeUs, int flags)
{ |
| 303 resetLastPresentationTimeIfNeeded(presentationTimeUs); | 352 resetLastPresentationTimeIfNeeded(presentationTimeUs); |
| 304 try { | 353 try { |
| 305 mMediaCodec.queueInputBuffer(index, offset, size, presentationTimeUs
, flags); | 354 mMediaCodec.queueInputBuffer(index, offset, size, presentationTimeUs
, flags); |
| 306 } catch(Exception e) { | 355 } catch(Exception e) { |
| 307 Log.e(TAG, "Failed to queue input buffer: " + e.toString()); | 356 Log.e(TAG, "Failed to queue input buffer", e); |
| 308 return MEDIA_CODEC_ERROR; | 357 return MEDIA_CODEC_ERROR; |
| 309 } | 358 } |
| 310 return MEDIA_CODEC_OK; | 359 return MEDIA_CODEC_OK; |
| 311 } | 360 } |
| 312 | 361 |
| 313 @CalledByNative | 362 @CalledByNative |
| 363 private void setVideoBitrate(int bps) { |
| 364 Bundle b = new Bundle(); |
| 365 b.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bps); |
| 366 mMediaCodec.setParameters(b); |
| 367 } |
| 368 |
| 369 @CalledByNative |
| 370 private void requestKeyFrameSoon() { |
| 371 Bundle b = new Bundle(); |
| 372 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); |
| 373 mMediaCodec.setParameters(b); |
| 374 } |
| 375 |
| 376 @CalledByNative |
| 314 private int queueSecureInputBuffer( | 377 private int queueSecureInputBuffer( |
| 315 int index, int offset, byte[] iv, byte[] keyId, int[] numBytesOfClea
rData, | 378 int index, int offset, byte[] iv, byte[] keyId, int[] numBytesOfClea
rData, |
| 316 int[] numBytesOfEncryptedData, int numSubSamples, long presentationT
imeUs) { | 379 int[] numBytesOfEncryptedData, int numSubSamples, long presentationT
imeUs) { |
| 317 resetLastPresentationTimeIfNeeded(presentationTimeUs); | 380 resetLastPresentationTimeIfNeeded(presentationTimeUs); |
| 318 try { | 381 try { |
| 319 MediaCodec.CryptoInfo cryptoInfo = new MediaCodec.CryptoInfo(); | 382 MediaCodec.CryptoInfo cryptoInfo = new MediaCodec.CryptoInfo(); |
| 320 cryptoInfo.set(numSubSamples, numBytesOfClearData, numBytesOfEncrypt
edData, | 383 cryptoInfo.set(numSubSamples, numBytesOfClearData, numBytesOfEncrypt
edData, |
| 321 keyId, iv, MediaCodec.CRYPTO_MODE_AES_CTR); | 384 keyId, iv, MediaCodec.CRYPTO_MODE_AES_CTR); |
| 322 mMediaCodec.queueSecureInputBuffer(index, offset, cryptoInfo, presen
tationTimeUs, 0); | 385 mMediaCodec.queueSecureInputBuffer(index, offset, cryptoInfo, presen
tationTimeUs, 0); |
| 323 } catch (MediaCodec.CryptoException e) { | 386 } catch (MediaCodec.CryptoException e) { |
| 324 Log.e(TAG, "Failed to queue secure input buffer: " + e.toString()); | 387 Log.e(TAG, "Failed to queue secure input buffer", e); |
| 325 if (e.getErrorCode() == MediaCodec.CryptoException.ERROR_NO_KEY) { | 388 if (e.getErrorCode() == MediaCodec.CryptoException.ERROR_NO_KEY) { |
| 326 Log.e(TAG, "MediaCodec.CryptoException.ERROR_NO_KEY"); | 389 Log.e(TAG, "MediaCodec.CryptoException.ERROR_NO_KEY"); |
| 327 return MEDIA_CODEC_NO_KEY; | 390 return MEDIA_CODEC_NO_KEY; |
| 328 } | 391 } |
| 329 Log.e(TAG, "MediaCodec.CryptoException with error code " + e.getErro
rCode()); | 392 Log.e(TAG, "MediaCodec.CryptoException with error code " + e.getErro
rCode()); |
| 330 return MEDIA_CODEC_ERROR; | 393 return MEDIA_CODEC_ERROR; |
| 331 } catch(IllegalStateException e) { | 394 } catch(IllegalStateException e) { |
| 332 Log.e(TAG, "Failed to queue secure input buffer: " + e.toString()); | 395 Log.e(TAG, "Failed to queue secure input buffer", e); |
| 333 return MEDIA_CODEC_ERROR; | 396 return MEDIA_CODEC_ERROR; |
| 334 } | 397 } |
| 335 return MEDIA_CODEC_OK; | 398 return MEDIA_CODEC_OK; |
| 336 } | 399 } |
| 337 | 400 |
| 338 @CalledByNative | 401 @CalledByNative |
| 339 private void releaseOutputBuffer(int index, boolean render) { | 402 private void releaseOutputBuffer(int index, boolean render) { |
| 340 mMediaCodec.releaseOutputBuffer(index, render); | 403 mMediaCodec.releaseOutputBuffer(index, render); |
| 341 } | 404 } |
| 342 | 405 |
| 343 @CalledByNative | 406 @CalledByNative |
| 344 private boolean getOutputBuffers() { | |
| 345 try { | |
| 346 mOutputBuffers = mMediaCodec.getOutputBuffers(); | |
| 347 } catch (IllegalStateException e) { | |
| 348 Log.e(TAG, "Cannot get output buffers " + e.toString()); | |
| 349 return false; | |
| 350 } | |
| 351 return true; | |
| 352 } | |
| 353 | |
| 354 @CalledByNative | |
| 355 private DequeueOutputResult dequeueOutputBuffer(long timeoutUs) { | 407 private DequeueOutputResult dequeueOutputBuffer(long timeoutUs) { |
| 356 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); | 408 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
| 357 int status = MEDIA_CODEC_ERROR; | 409 int status = MEDIA_CODEC_ERROR; |
| 358 int index = -1; | 410 int index = -1; |
| 359 try { | 411 try { |
| 360 int index_or_status = mMediaCodec.dequeueOutputBuffer(info, timeoutU
s); | 412 int index_or_status = mMediaCodec.dequeueOutputBuffer(info, timeoutU
s); |
| 361 if (info.presentationTimeUs < mLastPresentationTimeUs) { | 413 if (info.presentationTimeUs < mLastPresentationTimeUs) { |
| 362 // TODO(qinmin): return a special code through DequeueOutputResu
lt | 414 // TODO(qinmin): return a special code through DequeueOutputResu
lt |
| 363 // to notify the native code the the frame has a wrong presentat
ion | 415 // to notify the native code the the frame has a wrong presentat
ion |
| 364 // timestamp and should be skipped. | 416 // timestamp and should be skipped. |
| 365 info.presentationTimeUs = mLastPresentationTimeUs; | 417 info.presentationTimeUs = mLastPresentationTimeUs; |
| 366 } | 418 } |
| 367 mLastPresentationTimeUs = info.presentationTimeUs; | 419 mLastPresentationTimeUs = info.presentationTimeUs; |
| 368 | 420 |
| 369 if (index_or_status >= 0) { // index! | 421 if (index_or_status >= 0) { // index! |
| 370 status = MEDIA_CODEC_OK; | 422 status = MEDIA_CODEC_OK; |
| 371 index = index_or_status; | 423 index = index_or_status; |
| 372 } else if (index_or_status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED
) { | 424 } else if (index_or_status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED
) { |
| 373 status = MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED; | 425 status = MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED; |
| 374 } else if (index_or_status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)
{ | 426 } else if (index_or_status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED)
{ |
| 375 status = MEDIA_CODEC_OUTPUT_FORMAT_CHANGED; | 427 status = MEDIA_CODEC_OUTPUT_FORMAT_CHANGED; |
| 376 } else if (index_or_status == MediaCodec.INFO_TRY_AGAIN_LATER) { | 428 } else if (index_or_status == MediaCodec.INFO_TRY_AGAIN_LATER) { |
| 377 status = MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER; | 429 status = MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER; |
| 378 } else { | 430 } else { |
| 431 Log.e(TAG, "Unexpected index_or_status: " + index_or_status); |
| 379 assert(false); | 432 assert(false); |
| 380 } | 433 } |
| 381 } catch (IllegalStateException e) { | 434 } catch (IllegalStateException e) { |
| 382 Log.e(TAG, "Failed to dequeue output buffer: " + e.toString()); | 435 Log.e(TAG, "Failed to dequeue output buffer", e); |
| 383 } | 436 } |
| 384 | 437 |
| 385 return new DequeueOutputResult( | 438 return new DequeueOutputResult( |
| 386 status, index, info.flags, info.offset, info.presentationTimeUs,
info.size); | 439 status, index, info.flags, info.offset, info.presentationTimeUs,
info.size); |
| 387 } | 440 } |
| 388 | 441 |
| 389 @CalledByNative | 442 @CalledByNative |
| 390 private boolean configureVideo(MediaFormat format, Surface surface, MediaCry
pto crypto, | 443 private boolean configureVideo(MediaFormat format, Surface surface, MediaCry
pto crypto, |
| 391 int flags) { | 444 int flags) { |
| 392 try { | 445 try { |
| 393 mMediaCodec.configure(format, surface, crypto, flags); | 446 mMediaCodec.configure(format, surface, crypto, flags); |
| 394 return true; | 447 return true; |
| 395 } catch (IllegalStateException e) { | 448 } catch (IllegalStateException e) { |
| 396 Log.e(TAG, "Cannot configure the video codec " + e.toString()); | 449 Log.e(TAG, "Cannot configure the video codec", e); |
| 397 } | 450 } |
| 398 return false; | 451 return false; |
| 399 } | 452 } |
| 400 | 453 |
| 401 @CalledByNative | 454 @CalledByNative |
| 402 private static MediaFormat createAudioFormat(String mime, int SampleRate, in
t ChannelCount) { | 455 private static MediaFormat createAudioFormat(String mime, int SampleRate, in
t ChannelCount) { |
| 403 return MediaFormat.createAudioFormat(mime, SampleRate, ChannelCount); | 456 return MediaFormat.createAudioFormat(mime, SampleRate, ChannelCount); |
| 404 } | 457 } |
| 405 | 458 |
| 406 @CalledByNative | 459 @CalledByNative |
| 407 private static MediaFormat createVideoFormat(String mime, int width, int hei
ght) { | 460 private static MediaFormat createVideoDecoderFormat(String mime, int width,
int height) { |
| 408 return MediaFormat.createVideoFormat(mime, width, height); | 461 return MediaFormat.createVideoFormat(mime, width, height); |
| 409 } | 462 } |
| 410 | 463 |
| 411 @CalledByNative | 464 @CalledByNative |
| 465 private static MediaFormat createVideoEncoderFormat(String mime, int width,
int height, |
| 466 int bitRate, int frameRate, int iFrameInterval, int colorFormat) { |
| 467 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); |
| 468 format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate); |
| 469 format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate); |
| 470 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval); |
| 471 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat); |
| 472 return format; |
| 473 } |
| 474 |
| 475 @CalledByNative |
| 412 private static void setCodecSpecificData(MediaFormat format, int index, byte
[] bytes) { | 476 private static void setCodecSpecificData(MediaFormat format, int index, byte
[] bytes) { |
| 413 String name = null; | 477 String name = null; |
| 414 if (index == 0) { | 478 if (index == 0) { |
| 415 name = "csd-0"; | 479 name = "csd-0"; |
| 416 } else if (index == 1) { | 480 } else if (index == 1) { |
| 417 name = "csd-1"; | 481 name = "csd-1"; |
| 418 } | 482 } |
| 419 if (name != null) { | 483 if (name != null) { |
| 420 format.setByteBuffer(name, ByteBuffer.wrap(bytes)); | 484 format.setByteBuffer(name, ByteBuffer.wrap(bytes)); |
| 421 } | 485 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 438 AudioFormat.CHANNEL_OUT_STEREO; | 502 AudioFormat.CHANNEL_OUT_STEREO; |
| 439 // Using 16bit PCM for output. Keep this value in sync with | 503 // Using 16bit PCM for output. Keep this value in sync with |
| 440 // kBytesPerAudioOutputSample in media_codec_bridge.cc. | 504 // kBytesPerAudioOutputSample in media_codec_bridge.cc. |
| 441 int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, chan
nelConfig, | 505 int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, chan
nelConfig, |
| 442 AudioFormat.ENCODING_PCM_16BIT); | 506 AudioFormat.ENCODING_PCM_16BIT); |
| 443 mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRa
te, channelConfig, | 507 mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRa
te, channelConfig, |
| 444 AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrac
k.MODE_STREAM); | 508 AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrac
k.MODE_STREAM); |
| 445 } | 509 } |
| 446 return true; | 510 return true; |
| 447 } catch (IllegalStateException e) { | 511 } catch (IllegalStateException e) { |
| 448 Log.e(TAG, "Cannot configure the audio codec " + e.toString()); | 512 Log.e(TAG, "Cannot configure the audio codec", e); |
| 449 } | 513 } |
| 450 return false; | 514 return false; |
| 451 } | 515 } |
| 452 | 516 |
| 453 @CalledByNative | 517 @CalledByNative |
| 454 private void playOutputBuffer(byte[] buf) { | 518 private void playOutputBuffer(byte[] buf) { |
| 455 if (mAudioTrack != null) { | 519 if (mAudioTrack != null) { |
| 456 if (AudioTrack.PLAYSTATE_PLAYING != mAudioTrack.getPlayState()) { | 520 if (AudioTrack.PLAYSTATE_PLAYING != mAudioTrack.getPlayState()) { |
| 457 mAudioTrack.play(); | 521 mAudioTrack.play(); |
| 458 } | 522 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 472 } | 536 } |
| 473 | 537 |
| 474 private void resetLastPresentationTimeIfNeeded(long presentationTimeUs) { | 538 private void resetLastPresentationTimeIfNeeded(long presentationTimeUs) { |
| 475 if (mFlushed) { | 539 if (mFlushed) { |
| 476 mLastPresentationTimeUs = | 540 mLastPresentationTimeUs = |
| 477 Math.max(presentationTimeUs - MAX_PRESENTATION_TIMESTAMP_SHI
FT_US, 0); | 541 Math.max(presentationTimeUs - MAX_PRESENTATION_TIMESTAMP_SHI
FT_US, 0); |
| 478 mFlushed = false; | 542 mFlushed = false; |
| 479 } | 543 } |
| 480 } | 544 } |
| 481 } | 545 } |
| OLD | NEW |