Chromium Code Reviews| 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 */ |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 77 @CalledByNative("DequeueInputResult") | 79 @CalledByNative("DequeueInputResult") |
| 78 private int index() { return mIndex; } | 80 private int index() { return mIndex; } |
| 79 } | 81 } |
| 80 | 82 |
| 81 /** | 83 /** |
| 82 * This class represents supported android codec information. | 84 * This class represents supported android codec information. |
| 83 */ | 85 */ |
| 84 private static class CodecInfo { | 86 private static class CodecInfo { |
| 85 private final String mCodecType; // e.g. "video/x-vnd.on2.vp8". | 87 private final String mCodecType; // e.g. "video/x-vnd.on2.vp8". |
| 86 private final String mCodecName; // e.g. "OMX.google.vp8.decoder". | 88 private final String mCodecName; // e.g. "OMX.google.vp8.decoder". |
| 89 private final boolean mIsEncoder; | |
| 87 | 90 |
| 88 private CodecInfo(String codecType, String codecName) { | 91 private CodecInfo(String codecType, String codecName, |
| 92 boolean isEncoder) { | |
| 89 mCodecType = codecType; | 93 mCodecType = codecType; |
| 90 mCodecName = codecName; | 94 mCodecName = codecName; |
| 95 mIsEncoder = isEncoder; | |
| 91 } | 96 } |
| 92 | 97 |
| 93 @CalledByNative("CodecInfo") | 98 @CalledByNative("CodecInfo") |
| 94 private String codecType() { return mCodecType; } | 99 private String codecType() { return mCodecType; } |
| 95 | 100 |
| 96 @CalledByNative("CodecInfo") | 101 @CalledByNative("CodecInfo") |
| 97 private String codecName() { return mCodecName; } | 102 private String codecName() { return mCodecName; } |
| 103 | |
| 104 @CalledByNative("CodecInfo") | |
| 105 private boolean isEncoder() { return mIsEncoder; } | |
| 98 } | 106 } |
| 99 | 107 |
| 100 private static class DequeueOutputResult { | 108 private static class DequeueOutputResult { |
| 101 private final int mStatus; | 109 private final int mStatus; |
| 102 private final int mIndex; | 110 private final int mIndex; |
| 103 private final int mFlags; | 111 private final int mFlags; |
| 104 private final int mOffset; | 112 private final int mOffset; |
| 105 private final long mPresentationTimeMicroseconds; | 113 private final long mPresentationTimeMicroseconds; |
| 106 private final int mNumBytes; | 114 private final int mNumBytes; |
| 107 | 115 |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 132 | 140 |
| 133 @CalledByNative("DequeueOutputResult") | 141 @CalledByNative("DequeueOutputResult") |
| 134 private int numBytes() { return mNumBytes; } | 142 private int numBytes() { return mNumBytes; } |
| 135 } | 143 } |
| 136 | 144 |
| 137 /** | 145 /** |
| 138 * Get a list of supported android codec mimes. | 146 * Get a list of supported android codec mimes. |
| 139 */ | 147 */ |
| 140 @CalledByNative | 148 @CalledByNative |
| 141 private static CodecInfo[] getCodecsInfo() { | 149 private static CodecInfo[] getCodecsInfo() { |
| 142 Map<String, CodecInfo> CodecInfoMap = new HashMap<String, CodecInfo>(); | 150 Map<String, CodecInfo> encoderInfoMap = new HashMap<String, CodecInfo>() ; |
| 151 Map<String, CodecInfo> decoderInfoMap = new HashMap<String, CodecInfo>() ; | |
|
xhwang
2013/11/19 00:11:25
It's a bit odd to have two maps here then merge th
Ami GONE FROM CHROMIUM
2013/11/21 22:59:07
Agreed.
| |
| 143 int count = MediaCodecList.getCodecCount(); | 152 int count = MediaCodecList.getCodecCount(); |
| 144 for (int i = 0; i < count; ++i) { | 153 for (int i = 0; i < count; ++i) { |
| 145 MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); | 154 MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); |
| 146 if (info.isEncoder()) { | 155 boolean isEncoder = info.isEncoder(); |
| 147 continue; | |
| 148 } | |
| 149 | |
| 150 String codecString = info.getName(); | 156 String codecString = info.getName(); |
| 151 String[] supportedTypes = info.getSupportedTypes(); | 157 String[] supportedTypes = info.getSupportedTypes(); |
| 152 for (int j = 0; j < supportedTypes.length; ++j) { | 158 for (int j = 0; j < supportedTypes.length; ++j) { |
| 153 if (!CodecInfoMap.containsKey(supportedTypes[j])) { | 159 Map<String, CodecInfo> map = isEncoder ? encoderInfoMap : decode rInfoMap; |
| 154 CodecInfoMap.put(supportedTypes[j], new CodecInfo( | 160 if (!map.containsKey(supportedTypes[j])) { |
| 155 supportedTypes[j], codecString)); | 161 map.put(supportedTypes[j], new CodecInfo( |
| 162 supportedTypes[j], codecString, isEncoder)); | |
| 156 } | 163 } |
| 157 } | 164 } |
| 158 } | 165 } |
| 159 return CodecInfoMap.values().toArray( | 166 ArrayList<CodecInfo> codecInfos = new ArrayList<CodecInfo>( |
| 160 new CodecInfo[CodecInfoMap.size()]); | 167 decoderInfoMap.size() + encoderInfoMap.size()); |
| 168 codecInfos.addAll(encoderInfoMap.values()); | |
| 169 codecInfos.addAll(decoderInfoMap.values()); | |
| 170 return codecInfos.toArray(new CodecInfo[codecInfos.size()]); | |
| 161 } | 171 } |
| 162 | 172 |
| 163 private static String getSecureDecoderNameForMime(String mime) { | 173 private static String getSecureDecoderNameForMime(String mime) { |
| 164 int count = MediaCodecList.getCodecCount(); | 174 int count = MediaCodecList.getCodecCount(); |
| 165 for (int i = 0; i < count; ++i) { | 175 for (int i = 0; i < count; ++i) { |
| 166 MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); | 176 MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); |
| 167 if (info.isEncoder()) { | 177 if (info.isEncoder()) { |
| 168 continue; | 178 continue; |
| 169 } | 179 } |
| 170 | 180 |
| 171 String[] supportedTypes = info.getSupportedTypes(); | 181 String[] supportedTypes = info.getSupportedTypes(); |
| 172 for (int j = 0; j < supportedTypes.length; ++j) { | 182 for (int j = 0; j < supportedTypes.length; ++j) { |
| 173 if (supportedTypes[j].equalsIgnoreCase(mime)) { | 183 if (supportedTypes[j].equalsIgnoreCase(mime)) { |
| 174 return info.getName() + ".secure"; | 184 return info.getName() + ".secure"; |
| 175 } | 185 } |
| 176 } | 186 } |
| 177 } | 187 } |
| 178 | 188 |
| 179 return null; | 189 return null; |
| 180 } | 190 } |
| 181 | 191 |
| 182 private MediaCodecBridge(MediaCodec mediaCodec) { | 192 private MediaCodecBridge(MediaCodec mediaCodec) { |
| 183 assert(mediaCodec != null); | 193 assert(mediaCodec != null); |
| 184 mMediaCodec = mediaCodec; | 194 mMediaCodec = mediaCodec; |
| 185 mLastPresentationTimeUs = 0; | 195 mLastPresentationTimeUs = 0; |
| 186 mFlushed = true; | 196 mFlushed = true; |
| 187 } | 197 } |
| 188 | 198 |
| 189 @CalledByNative | 199 @CalledByNative |
| 190 private static MediaCodecBridge create(String mime, boolean isSecure) { | 200 private static MediaCodecBridge create(String mime, boolean isSecure, boolea n isEncoder) { |
| 191 // Creation of ".secure" codecs sometimes crash instead of throwing exce ptions | 201 // Creation of ".secure" codecs sometimes crash instead of throwing exce ptions |
| 192 // on pre-JBMR2 devices. | 202 // on pre-JBMR2 devices. |
| 193 if (isSecure && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_M R2) { | 203 if (isSecure && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_M R2) { |
| 194 return null; | 204 return null; |
| 195 } | 205 } |
| 196 MediaCodec mediaCodec = null; | 206 MediaCodec mediaCodec = null; |
| 197 try { | 207 try { |
| 198 // |isSecure| only applies to video decoders. | 208 // |isSecure| only applies to video decoders. |
| 199 if (mime.startsWith("video") && isSecure) { | 209 if (mime.startsWith("video") && isSecure && !isEncoder) { |
| 200 mediaCodec = MediaCodec.createByCodecName(getSecureDecoderNameFo rMime(mime)); | 210 mediaCodec = MediaCodec.createByCodecName(getSecureDecoderNameFo rMime(mime)); |
| 201 } else { | 211 } else { |
| 202 mediaCodec = MediaCodec.createDecoderByType(mime); | 212 if (isEncoder) { |
| 213 mediaCodec = MediaCodec.createEncoderByType(mime); | |
| 214 } else { | |
| 215 mediaCodec = MediaCodec.createDecoderByType(mime); | |
| 216 } | |
| 203 } | 217 } |
| 204 } catch (Exception e) { | 218 } catch (Exception e) { |
| 205 Log.e(TAG, "Failed to create MediaCodec: " + mime + ", isSecure: " | 219 Log.e(TAG, "Failed to create MediaCodec: " + mime + ", isSecure: " |
| 206 + isSecure + ", " + e.toString()); | 220 + isSecure + ", isEncoder: " + isEncoder, e); |
| 207 } | 221 } |
| 208 | 222 |
| 209 if (mediaCodec == null) { | 223 if (mediaCodec == null) { |
| 210 return null; | 224 return null; |
| 211 } | 225 } |
| 212 | 226 |
| 213 return new MediaCodecBridge(mediaCodec); | 227 return new MediaCodecBridge(mediaCodec); |
| 214 } | 228 } |
| 215 | 229 |
| 216 @CalledByNative | 230 @CalledByNative |
| 217 private void release() { | 231 private void release() { |
| 232 String name = mMediaCodec.getName(); | |
| 233 mMediaCodec.stop(); | |
| 218 mMediaCodec.release(); | 234 mMediaCodec.release(); |
| 235 mMediaCodec = null; | |
| 219 if (mAudioTrack != null) { | 236 if (mAudioTrack != null) { |
| 220 mAudioTrack.release(); | 237 mAudioTrack.release(); |
| 221 } | 238 } |
| 222 } | 239 } |
| 223 | 240 |
| 224 @CalledByNative | 241 @CalledByNative |
| 225 private boolean start() { | 242 private boolean start() { |
| 226 try { | 243 try { |
| 227 mMediaCodec.start(); | 244 mMediaCodec.start(); |
| 228 mInputBuffers = mMediaCodec.getInputBuffers(); | 245 mInputBuffers = mMediaCodec.getInputBuffers(); |
| 229 } catch (IllegalStateException e) { | 246 } catch (IllegalStateException e) { |
| 230 Log.e(TAG, "Cannot start the media codec " + e.toString()); | 247 Log.e(TAG, "Cannot start the media codec", e); |
| 231 return false; | 248 return false; |
| 232 } | 249 } |
| 233 return true; | 250 return true; |
| 234 } | 251 } |
| 235 | 252 |
| 236 @CalledByNative | 253 @CalledByNative |
| 237 private DequeueInputResult dequeueInputBuffer(long timeoutUs) { | 254 private DequeueInputResult dequeueInputBuffer(long timeoutUs) { |
| 238 int status = MEDIA_CODEC_ERROR; | 255 int status = MEDIA_CODEC_ERROR; |
| 239 int index = -1; | 256 int index = -1; |
| 240 try { | 257 try { |
| 241 int index_or_status = mMediaCodec.dequeueInputBuffer(timeoutUs); | 258 int index_or_status = mMediaCodec.dequeueInputBuffer(timeoutUs); |
| 242 if (index_or_status >= 0) { // index! | 259 if (index_or_status >= 0) { // index! |
| 243 status = MEDIA_CODEC_OK; | 260 status = MEDIA_CODEC_OK; |
| 244 index = index_or_status; | 261 index = index_or_status; |
| 245 } else if (index_or_status == MediaCodec.INFO_TRY_AGAIN_LATER) { | 262 } else if (index_or_status == MediaCodec.INFO_TRY_AGAIN_LATER) { |
| 246 Log.e(TAG, "dequeueInputBuffer: MediaCodec.INFO_TRY_AGAIN_LATER" ); | 263 Log.e(TAG, "dequeueInputBuffer: MediaCodec.INFO_TRY_AGAIN_LATER" ); |
| 247 status = MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER; | 264 status = MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER; |
| 248 } else { | 265 } else { |
| 266 Log.e(TAG, "Unexpected index_or_status: " + index_or_status); | |
| 249 assert(false); | 267 assert(false); |
| 250 } | 268 } |
| 251 } catch(Exception e) { | 269 } catch(Exception e) { |
| 252 Log.e(TAG, "Failed to dequeue input buffer: " + e.toString()); | 270 Log.e(TAG, "Failed to dequeue input buffer", e); |
| 253 } | 271 } |
| 254 return new DequeueInputResult(status, index); | 272 return new DequeueInputResult(status, index); |
| 255 } | 273 } |
| 256 | 274 |
| 257 @CalledByNative | 275 @CalledByNative |
| 258 private int flush() { | 276 private int flush() { |
| 259 try { | 277 try { |
| 260 mFlushed = true; | 278 mFlushed = true; |
| 261 if (mAudioTrack != null) { | 279 if (mAudioTrack != null) { |
| 262 mAudioTrack.flush(); | 280 mAudioTrack.flush(); |
| 263 } | 281 } |
| 264 mMediaCodec.flush(); | 282 mMediaCodec.flush(); |
| 265 } catch(IllegalStateException e) { | 283 } catch(IllegalStateException e) { |
| 266 Log.e(TAG, "Failed to flush MediaCodec " + e.toString()); | 284 Log.e(TAG, "Failed to flush MediaCodec", e); |
| 267 return MEDIA_CODEC_ERROR; | 285 return MEDIA_CODEC_ERROR; |
| 268 } | 286 } |
| 269 return MEDIA_CODEC_OK; | 287 return MEDIA_CODEC_OK; |
| 270 } | 288 } |
| 271 | 289 |
| 272 @CalledByNative | 290 @CalledByNative |
| 273 private void stop() { | 291 private void stop() { |
| 274 mMediaCodec.stop(); | 292 mMediaCodec.stop(); |
| 275 if (mAudioTrack != null) { | 293 if (mAudioTrack != null) { |
| 276 mAudioTrack.pause(); | 294 mAudioTrack.pause(); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 297 return mOutputBuffers[index]; | 315 return mOutputBuffers[index]; |
| 298 } | 316 } |
| 299 | 317 |
| 300 @CalledByNative | 318 @CalledByNative |
| 301 private int queueInputBuffer( | 319 private int queueInputBuffer( |
| 302 int index, int offset, int size, long presentationTimeUs, int flags) { | 320 int index, int offset, int size, long presentationTimeUs, int flags) { |
| 303 resetLastPresentationTimeIfNeeded(presentationTimeUs); | 321 resetLastPresentationTimeIfNeeded(presentationTimeUs); |
| 304 try { | 322 try { |
| 305 mMediaCodec.queueInputBuffer(index, offset, size, presentationTimeUs , flags); | 323 mMediaCodec.queueInputBuffer(index, offset, size, presentationTimeUs , flags); |
| 306 } catch(Exception e) { | 324 } catch(Exception e) { |
| 307 Log.e(TAG, "Failed to queue input buffer: " + e.toString()); | 325 Log.e(TAG, "Failed to queue input buffer", e); |
| 308 return MEDIA_CODEC_ERROR; | 326 return MEDIA_CODEC_ERROR; |
| 309 } | 327 } |
| 310 return MEDIA_CODEC_OK; | 328 return MEDIA_CODEC_OK; |
| 311 } | 329 } |
| 312 | 330 |
| 313 @CalledByNative | 331 @CalledByNative |
| 332 private void setVideoBitrate(int bps) { | |
| 333 Bundle b = new Bundle(); | |
| 334 b.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bps); | |
| 335 mMediaCodec.setParameters(b); | |
| 336 } | |
| 337 | |
| 338 @CalledByNative | |
| 339 private void requestKeyFrameSoon() { | |
| 340 Bundle b = new Bundle(); | |
| 341 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); | |
| 342 mMediaCodec.setParameters(b); | |
| 343 } | |
| 344 | |
| 345 @CalledByNative | |
| 314 private int queueSecureInputBuffer( | 346 private int queueSecureInputBuffer( |
| 315 int index, int offset, byte[] iv, byte[] keyId, int[] numBytesOfClea rData, | 347 int index, int offset, byte[] iv, byte[] keyId, int[] numBytesOfClea rData, |
| 316 int[] numBytesOfEncryptedData, int numSubSamples, long presentationT imeUs) { | 348 int[] numBytesOfEncryptedData, int numSubSamples, long presentationT imeUs) { |
| 317 resetLastPresentationTimeIfNeeded(presentationTimeUs); | 349 resetLastPresentationTimeIfNeeded(presentationTimeUs); |
| 318 try { | 350 try { |
| 319 MediaCodec.CryptoInfo cryptoInfo = new MediaCodec.CryptoInfo(); | 351 MediaCodec.CryptoInfo cryptoInfo = new MediaCodec.CryptoInfo(); |
| 320 cryptoInfo.set(numSubSamples, numBytesOfClearData, numBytesOfEncrypt edData, | 352 cryptoInfo.set(numSubSamples, numBytesOfClearData, numBytesOfEncrypt edData, |
| 321 keyId, iv, MediaCodec.CRYPTO_MODE_AES_CTR); | 353 keyId, iv, MediaCodec.CRYPTO_MODE_AES_CTR); |
| 322 mMediaCodec.queueSecureInputBuffer(index, offset, cryptoInfo, presen tationTimeUs, 0); | 354 mMediaCodec.queueSecureInputBuffer(index, offset, cryptoInfo, presen tationTimeUs, 0); |
| 323 } catch (MediaCodec.CryptoException e) { | 355 } catch (MediaCodec.CryptoException e) { |
| 324 Log.e(TAG, "Failed to queue secure input buffer: " + e.toString()); | 356 Log.e(TAG, "Failed to queue secure input buffer", e); |
| 325 // TODO(xhwang): Replace hard coded value with constant/enum. | 357 // TODO(xhwang): Replace hard coded value with constant/enum. |
| 326 if (e.getErrorCode() == 1) { | 358 if (e.getErrorCode() == 1) { |
| 327 Log.e(TAG, "No key available."); | 359 Log.e(TAG, "No key available."); |
| 328 return MEDIA_CODEC_NO_KEY; | 360 return MEDIA_CODEC_NO_KEY; |
| 329 } | 361 } |
| 330 return MEDIA_CODEC_ERROR; | 362 return MEDIA_CODEC_ERROR; |
| 331 } catch(IllegalStateException e) { | 363 } catch(IllegalStateException e) { |
| 332 Log.e(TAG, "Failed to queue secure input buffer: " + e.toString()); | 364 Log.e(TAG, "Failed to queue secure input buffer", e); |
| 333 return MEDIA_CODEC_ERROR; | 365 return MEDIA_CODEC_ERROR; |
| 334 } | 366 } |
| 335 return MEDIA_CODEC_OK; | 367 return MEDIA_CODEC_OK; |
| 336 } | 368 } |
| 337 | 369 |
| 338 @CalledByNative | 370 @CalledByNative |
| 339 private void releaseOutputBuffer(int index, boolean render) { | 371 private void releaseOutputBuffer(int index, boolean render) { |
| 340 mMediaCodec.releaseOutputBuffer(index, render); | 372 mMediaCodec.releaseOutputBuffer(index, render); |
| 341 } | 373 } |
| 342 | 374 |
| 343 @CalledByNative | 375 @CalledByNative |
| 376 private int getInputBuffersCount() { | |
| 377 return mInputBuffers.length; | |
| 378 } | |
| 379 | |
| 380 @CalledByNative | |
| 381 private int getOutputBuffersCount() { | |
| 382 return mOutputBuffers != null ? mOutputBuffers.length : -1; | |
| 383 } | |
| 384 | |
| 385 @CalledByNative | |
| 386 private int getOutputBuffersCapacity() { | |
| 387 return mOutputBuffers != null ? mOutputBuffers[0].capacity() : -1; | |
| 388 } | |
| 389 | |
| 390 @CalledByNative | |
| 344 private boolean getOutputBuffers() { | 391 private boolean getOutputBuffers() { |
| 345 try { | 392 try { |
| 346 mOutputBuffers = mMediaCodec.getOutputBuffers(); | 393 mOutputBuffers = mMediaCodec.getOutputBuffers(); |
| 347 } catch (IllegalStateException e) { | 394 } catch (IllegalStateException e) { |
| 348 Log.e(TAG, "Cannot get output buffers " + e.toString()); | 395 Log.e(TAG, "Cannot get output buffers", e); |
| 349 return false; | 396 return false; |
| 350 } | 397 } |
| 351 return true; | 398 return true; |
| 352 } | 399 } |
|
xhwang
2013/11/19 00:11:25
Move l.375-l.399 to l.317?
Ami GONE FROM CHROMIUM
2013/11/21 22:59:07
Done.
| |
| 353 | 400 |
| 354 @CalledByNative | 401 @CalledByNative |
| 355 private DequeueOutputResult dequeueOutputBuffer(long timeoutUs) { | 402 private DequeueOutputResult dequeueOutputBuffer(long timeoutUs) { |
| 356 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); | 403 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
| 357 int status = MEDIA_CODEC_ERROR; | 404 int status = MEDIA_CODEC_ERROR; |
| 358 int index = -1; | 405 int index = -1; |
| 359 try { | 406 try { |
| 360 int index_or_status = mMediaCodec.dequeueOutputBuffer(info, timeoutU s); | 407 int index_or_status = mMediaCodec.dequeueOutputBuffer(info, timeoutU s); |
| 361 if (info.presentationTimeUs < mLastPresentationTimeUs) { | 408 if (info.presentationTimeUs < mLastPresentationTimeUs) { |
| 362 // TODO(qinmin): return a special code through DequeueOutputResu lt | 409 // TODO(qinmin): return a special code through DequeueOutputResu lt |
| 363 // to notify the native code the the frame has a wrong presentat ion | 410 // to notify the native code the the frame has a wrong presentat ion |
| 364 // timestamp and should be skipped. | 411 // timestamp and should be skipped. |
| 365 info.presentationTimeUs = mLastPresentationTimeUs; | 412 info.presentationTimeUs = mLastPresentationTimeUs; |
| 366 } | 413 } |
| 367 mLastPresentationTimeUs = info.presentationTimeUs; | 414 mLastPresentationTimeUs = info.presentationTimeUs; |
| 368 | 415 |
| 369 if (index_or_status >= 0) { // index! | 416 if (index_or_status >= 0) { // index! |
| 370 status = MEDIA_CODEC_OK; | 417 status = MEDIA_CODEC_OK; |
| 371 index = index_or_status; | 418 index = index_or_status; |
| 372 } else if (index_or_status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED ) { | 419 } else if (index_or_status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED ) { |
| 373 status = MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED; | 420 status = MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED; |
| 374 } else if (index_or_status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { | 421 } else if (index_or_status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { |
| 375 status = MEDIA_CODEC_OUTPUT_FORMAT_CHANGED; | 422 status = MEDIA_CODEC_OUTPUT_FORMAT_CHANGED; |
| 376 } else if (index_or_status == MediaCodec.INFO_TRY_AGAIN_LATER) { | 423 } else if (index_or_status == MediaCodec.INFO_TRY_AGAIN_LATER) { |
| 377 status = MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER; | 424 status = MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER; |
| 378 } else { | 425 } else { |
| 426 Log.e(TAG, "Unexpected index_or_status: " + index_or_status); | |
| 379 assert(false); | 427 assert(false); |
| 380 } | 428 } |
| 381 } catch (IllegalStateException e) { | 429 } catch (IllegalStateException e) { |
| 382 Log.e(TAG, "Failed to dequeue output buffer: " + e.toString()); | 430 Log.e(TAG, "Failed to dequeue output buffer", e); |
| 383 } | 431 } |
| 384 | 432 |
| 385 return new DequeueOutputResult( | 433 return new DequeueOutputResult( |
| 386 status, index, info.flags, info.offset, info.presentationTimeUs, info.size); | 434 status, index, info.flags, info.offset, info.presentationTimeUs, info.size); |
| 387 } | 435 } |
| 388 | 436 |
| 389 @CalledByNative | 437 @CalledByNative |
| 390 private boolean configureVideo(MediaFormat format, Surface surface, MediaCry pto crypto, | 438 private boolean configureVideo(MediaFormat format, Surface surface, MediaCry pto crypto, |
| 391 int flags) { | 439 int flags) { |
| 392 try { | 440 try { |
| 393 mMediaCodec.configure(format, surface, crypto, flags); | 441 mMediaCodec.configure(format, surface, crypto, flags); |
| 394 return true; | 442 return true; |
| 395 } catch (IllegalStateException e) { | 443 } catch (IllegalStateException e) { |
| 396 Log.e(TAG, "Cannot configure the video codec " + e.toString()); | 444 Log.e(TAG, "Cannot configure the video codec", e); |
| 397 } | 445 } |
| 398 return false; | 446 return false; |
| 399 } | 447 } |
| 400 | 448 |
| 401 @CalledByNative | 449 @CalledByNative |
| 402 private static MediaFormat createAudioFormat(String mime, int SampleRate, in t ChannelCount) { | 450 private static MediaFormat createAudioFormat(String mime, int SampleRate, in t ChannelCount) { |
| 403 return MediaFormat.createAudioFormat(mime, SampleRate, ChannelCount); | 451 return MediaFormat.createAudioFormat(mime, SampleRate, ChannelCount); |
| 404 } | 452 } |
| 405 | 453 |
| 406 @CalledByNative | 454 @CalledByNative |
| 407 private static MediaFormat createVideoFormat(String mime, int width, int hei ght) { | 455 private static MediaFormat createVideoDecoderFormat(String mime, int width, int height) { |
| 408 return MediaFormat.createVideoFormat(mime, width, height); | 456 return MediaFormat.createVideoFormat(mime, width, height); |
| 409 } | 457 } |
| 410 | 458 |
| 411 @CalledByNative | 459 @CalledByNative |
| 460 private static MediaFormat createVideoEncoderFormat(String mime, int width, int height, | |
| 461 int bitRate, int frameRate, int iFrameInterval, int colorFormat) { | |
| 462 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); | |
| 463 format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate); | |
| 464 format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate); | |
| 465 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval); | |
| 466 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat); | |
| 467 return format; | |
| 468 } | |
| 469 | |
| 470 @CalledByNative | |
| 412 private static void setCodecSpecificData(MediaFormat format, int index, byte [] bytes) { | 471 private static void setCodecSpecificData(MediaFormat format, int index, byte [] bytes) { |
| 413 String name = null; | 472 String name = null; |
| 414 if (index == 0) { | 473 if (index == 0) { |
| 415 name = "csd-0"; | 474 name = "csd-0"; |
| 416 } else if (index == 1) { | 475 } else if (index == 1) { |
| 417 name = "csd-1"; | 476 name = "csd-1"; |
| 418 } | 477 } |
| 419 if (name != null) { | 478 if (name != null) { |
| 420 format.setByteBuffer(name, ByteBuffer.wrap(bytes)); | 479 format.setByteBuffer(name, ByteBuffer.wrap(bytes)); |
| 421 } | 480 } |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 438 AudioFormat.CHANNEL_OUT_STEREO; | 497 AudioFormat.CHANNEL_OUT_STEREO; |
| 439 // Using 16bit PCM for output. Keep this value in sync with | 498 // Using 16bit PCM for output. Keep this value in sync with |
| 440 // kBytesPerAudioOutputSample in media_codec_bridge.cc. | 499 // kBytesPerAudioOutputSample in media_codec_bridge.cc. |
| 441 int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, chan nelConfig, | 500 int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, chan nelConfig, |
| 442 AudioFormat.ENCODING_PCM_16BIT); | 501 AudioFormat.ENCODING_PCM_16BIT); |
| 443 mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRa te, channelConfig, | 502 mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRa te, channelConfig, |
| 444 AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrac k.MODE_STREAM); | 503 AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrac k.MODE_STREAM); |
| 445 } | 504 } |
| 446 return true; | 505 return true; |
| 447 } catch (IllegalStateException e) { | 506 } catch (IllegalStateException e) { |
| 448 Log.e(TAG, "Cannot configure the audio codec " + e.toString()); | 507 Log.e(TAG, "Cannot configure the audio codec", e); |
| 449 } | 508 } |
| 450 return false; | 509 return false; |
| 451 } | 510 } |
| 452 | 511 |
| 453 @CalledByNative | 512 @CalledByNative |
| 454 private void playOutputBuffer(byte[] buf) { | 513 private void playOutputBuffer(byte[] buf) { |
| 455 if (mAudioTrack != null) { | 514 if (mAudioTrack != null) { |
| 456 if (AudioTrack.PLAYSTATE_PLAYING != mAudioTrack.getPlayState()) { | 515 if (AudioTrack.PLAYSTATE_PLAYING != mAudioTrack.getPlayState()) { |
| 457 mAudioTrack.play(); | 516 mAudioTrack.play(); |
| 458 } | 517 } |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 472 } | 531 } |
| 473 | 532 |
| 474 private void resetLastPresentationTimeIfNeeded(long presentationTimeUs) { | 533 private void resetLastPresentationTimeIfNeeded(long presentationTimeUs) { |
| 475 if (mFlushed) { | 534 if (mFlushed) { |
| 476 mLastPresentationTimeUs = | 535 mLastPresentationTimeUs = |
| 477 Math.max(presentationTimeUs - MAX_PRESENTATION_TIMESTAMP_SHI FT_US, 0); | 536 Math.max(presentationTimeUs - MAX_PRESENTATION_TIMESTAMP_SHI FT_US, 0); |
| 478 mFlushed = false; | 537 mFlushed = false; |
| 479 } | 538 } |
| 480 } | 539 } |
| 481 } | 540 } |
| OLD | NEW |