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