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 |