OLD | NEW |
1 /* | 1 /* |
2 * libjingle | 2 * libjingle |
3 * Copyright 2013 Google Inc. | 3 * Copyright 2013 Google Inc. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions are met: | 6 * modification, are permitted provided that the following conditions are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright notice, | 8 * 1. Redistributions of source code must retain the above copyright notice, |
9 * this list of conditions and the following disclaimer. | 9 * this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
(...skipping 17 matching lines...) Expand all Loading... |
28 | 28 |
29 package org.webrtc; | 29 package org.webrtc; |
30 | 30 |
31 import android.media.MediaCodec; | 31 import android.media.MediaCodec; |
32 import android.media.MediaCodecInfo.CodecCapabilities; | 32 import android.media.MediaCodecInfo.CodecCapabilities; |
33 import android.media.MediaCodecInfo; | 33 import android.media.MediaCodecInfo; |
34 import android.media.MediaCodecList; | 34 import android.media.MediaCodecList; |
35 import android.media.MediaFormat; | 35 import android.media.MediaFormat; |
36 import android.os.Build; | 36 import android.os.Build; |
37 import android.os.Bundle; | 37 import android.os.Bundle; |
38 import android.util.Log; | 38 |
| 39 import org.webrtc.Logging; |
39 | 40 |
40 import java.nio.ByteBuffer; | 41 import java.nio.ByteBuffer; |
41 import java.util.Arrays; | 42 import java.util.Arrays; |
42 import java.util.List; | 43 import java.util.List; |
43 | 44 |
44 // Java-side of peerconnection_jni.cc:MediaCodecVideoEncoder. | 45 // Java-side of peerconnection_jni.cc:MediaCodecVideoEncoder. |
45 // This class is an implementation detail of the Java PeerConnection API. | 46 // This class is an implementation detail of the Java PeerConnection API. |
46 // MediaCodec is thread-hostile so this class must be operated on a single | 47 // MediaCodec is thread-hostile so this class must be operated on a single |
47 // thread. | 48 // thread. |
48 public class MediaCodecVideoEncoder { | 49 public class MediaCodecVideoEncoder { |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
118 // MediaCodec.setParameters is missing for JB and below, so bitrate | 119 // MediaCodec.setParameters is missing for JB and below, so bitrate |
119 // can not be adjusted dynamically. | 120 // can not be adjusted dynamically. |
120 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { | 121 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { |
121 return null; | 122 return null; |
122 } | 123 } |
123 | 124 |
124 // Check if device is in H.264 exception list. | 125 // Check if device is in H.264 exception list. |
125 if (mime.equals(H264_MIME_TYPE)) { | 126 if (mime.equals(H264_MIME_TYPE)) { |
126 List<String> exceptionModels = Arrays.asList(H264_HW_EXCEPTION_MODELS); | 127 List<String> exceptionModels = Arrays.asList(H264_HW_EXCEPTION_MODELS); |
127 if (exceptionModels.contains(Build.MODEL)) { | 128 if (exceptionModels.contains(Build.MODEL)) { |
128 Log.w(TAG, "Model: " + Build.MODEL + | 129 Logging.w(TAG, "Model: " + Build.MODEL + |
129 " has black listed H.264 encoder."); | 130 " has black listed H.264 encoder."); |
130 return null; | 131 return null; |
131 } | 132 } |
132 } | 133 } |
133 | 134 |
134 for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) { | 135 for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) { |
135 MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); | 136 MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); |
136 if (!info.isEncoder()) { | 137 if (!info.isEncoder()) { |
137 continue; | 138 continue; |
138 } | 139 } |
139 String name = null; | 140 String name = null; |
140 for (String mimeType : info.getSupportedTypes()) { | 141 for (String mimeType : info.getSupportedTypes()) { |
141 if (mimeType.equals(mime)) { | 142 if (mimeType.equals(mime)) { |
142 name = info.getName(); | 143 name = info.getName(); |
143 break; | 144 break; |
144 } | 145 } |
145 } | 146 } |
146 if (name == null) { | 147 if (name == null) { |
147 continue; // No HW support in this codec; try the next one. | 148 continue; // No HW support in this codec; try the next one. |
148 } | 149 } |
149 Log.v(TAG, "Found candidate encoder " + name); | 150 Logging.v(TAG, "Found candidate encoder " + name); |
150 | 151 |
151 // Check if this is supported HW encoder. | 152 // Check if this is supported HW encoder. |
152 boolean supportedCodec = false; | 153 boolean supportedCodec = false; |
153 for (String hwCodecPrefix : supportedHwCodecPrefixes) { | 154 for (String hwCodecPrefix : supportedHwCodecPrefixes) { |
154 if (name.startsWith(hwCodecPrefix)) { | 155 if (name.startsWith(hwCodecPrefix)) { |
155 supportedCodec = true; | 156 supportedCodec = true; |
156 break; | 157 break; |
157 } | 158 } |
158 } | 159 } |
159 if (!supportedCodec) { | 160 if (!supportedCodec) { |
160 continue; | 161 continue; |
161 } | 162 } |
162 | 163 |
163 CodecCapabilities capabilities = info.getCapabilitiesForType(mime); | 164 CodecCapabilities capabilities = info.getCapabilitiesForType(mime); |
164 for (int colorFormat : capabilities.colorFormats) { | 165 for (int colorFormat : capabilities.colorFormats) { |
165 Log.v(TAG, " Color: 0x" + Integer.toHexString(colorFormat)); | 166 Logging.v(TAG, " Color: 0x" + Integer.toHexString(colorFormat)); |
166 } | 167 } |
167 | 168 |
168 // Check if codec supports either yuv420 or nv12. | 169 // Check if codec supports either yuv420 or nv12. |
169 for (int supportedColorFormat : supportedColorList) { | 170 for (int supportedColorFormat : supportedColorList) { |
170 for (int codecColorFormat : capabilities.colorFormats) { | 171 for (int codecColorFormat : capabilities.colorFormats) { |
171 if (codecColorFormat == supportedColorFormat) { | 172 if (codecColorFormat == supportedColorFormat) { |
172 // Found supported HW encoder. | 173 // Found supported HW encoder. |
173 Log.d(TAG, "Found target encoder for mime " + mime + " : " + name + | 174 Logging.d(TAG, "Found target encoder for mime " + mime + " : " + nam
e + |
174 ". Color: 0x" + Integer.toHexString(codecColorFormat)); | 175 ". Color: 0x" + Integer.toHexString(codecColorFormat)); |
175 return new EncoderProperties(name, codecColorFormat); | 176 return new EncoderProperties(name, codecColorFormat); |
176 } | 177 } |
177 } | 178 } |
178 } | 179 } |
179 } | 180 } |
180 return null; // No HW VP8 encoder. | 181 return null; // No HW VP8 encoder. |
181 } | 182 } |
182 | 183 |
183 public static boolean isVp8HwSupported() { | 184 public static boolean isVp8HwSupported() { |
(...skipping 18 matching lines...) Expand all Loading... |
202 // both cases catch an exception. | 203 // both cases catch an exception. |
203 return MediaCodec.createByCodecName(codecName); | 204 return MediaCodec.createByCodecName(codecName); |
204 } catch (Exception e) { | 205 } catch (Exception e) { |
205 return null; | 206 return null; |
206 } | 207 } |
207 } | 208 } |
208 | 209 |
209 // Return the array of input buffers, or null on failure. | 210 // Return the array of input buffers, or null on failure. |
210 private ByteBuffer[] initEncode( | 211 private ByteBuffer[] initEncode( |
211 VideoCodecType type, int width, int height, int kbps, int fps) { | 212 VideoCodecType type, int width, int height, int kbps, int fps) { |
212 Log.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height + | 213 Logging.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height + |
213 ". @ " + kbps + " kbps. Fps: " + fps + | 214 ". @ " + kbps + " kbps. Fps: " + fps + |
214 ". Color: 0x" + Integer.toHexString(colorFormat)); | 215 ". Color: 0x" + Integer.toHexString(colorFormat)); |
215 if (mediaCodecThread != null) { | 216 if (mediaCodecThread != null) { |
216 throw new RuntimeException("Forgot to release()?"); | 217 throw new RuntimeException("Forgot to release()?"); |
217 } | 218 } |
218 this.type = type; | 219 this.type = type; |
219 EncoderProperties properties = null; | 220 EncoderProperties properties = null; |
220 String mime = null; | 221 String mime = null; |
221 int keyFrameIntervalSec = 0; | 222 int keyFrameIntervalSec = 0; |
222 if (type == VideoCodecType.VIDEO_CODEC_VP8) { | 223 if (type == VideoCodecType.VIDEO_CODEC_VP8) { |
223 mime = VP8_MIME_TYPE; | 224 mime = VP8_MIME_TYPE; |
224 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes); | 225 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes); |
225 keyFrameIntervalSec = 100; | 226 keyFrameIntervalSec = 100; |
226 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { | 227 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { |
227 mime = H264_MIME_TYPE; | 228 mime = H264_MIME_TYPE; |
228 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes); | 229 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes); |
229 keyFrameIntervalSec = 20; | 230 keyFrameIntervalSec = 20; |
230 } | 231 } |
231 if (properties == null) { | 232 if (properties == null) { |
232 throw new RuntimeException("Can not find HW encoder for " + type); | 233 throw new RuntimeException("Can not find HW encoder for " + type); |
233 } | 234 } |
234 mediaCodecThread = Thread.currentThread(); | 235 mediaCodecThread = Thread.currentThread(); |
235 try { | 236 try { |
236 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); | 237 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); |
237 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps); | 238 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps); |
238 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); | 239 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); |
239 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); | 240 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); |
240 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); | 241 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); |
241 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); | 242 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); |
242 Log.d(TAG, " Format: " + format); | 243 Logging.d(TAG, " Format: " + format); |
243 mediaCodec = createByCodecName(properties.codecName); | 244 mediaCodec = createByCodecName(properties.codecName); |
244 if (mediaCodec == null) { | 245 if (mediaCodec == null) { |
245 return null; | 246 return null; |
246 } | 247 } |
247 mediaCodec.configure( | 248 mediaCodec.configure( |
248 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); | 249 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); |
249 mediaCodec.start(); | 250 mediaCodec.start(); |
250 colorFormat = properties.colorFormat; | 251 colorFormat = properties.colorFormat; |
251 outputBuffers = mediaCodec.getOutputBuffers(); | 252 outputBuffers = mediaCodec.getOutputBuffers(); |
252 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); | 253 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); |
253 Log.d(TAG, "Input buffers: " + inputBuffers.length + | 254 Logging.d(TAG, "Input buffers: " + inputBuffers.length + |
254 ". Output buffers: " + outputBuffers.length); | 255 ". Output buffers: " + outputBuffers.length); |
255 return inputBuffers; | 256 return inputBuffers; |
256 } catch (IllegalStateException e) { | 257 } catch (IllegalStateException e) { |
257 Log.e(TAG, "initEncode failed", e); | 258 Logging.e(TAG, "initEncode failed", e); |
258 return null; | 259 return null; |
259 } | 260 } |
260 } | 261 } |
261 | 262 |
262 private boolean encode( | 263 private boolean encode( |
263 boolean isKeyframe, int inputBuffer, int size, | 264 boolean isKeyframe, int inputBuffer, int size, |
264 long presentationTimestampUs) { | 265 long presentationTimestampUs) { |
265 checkOnMediaCodecThread(); | 266 checkOnMediaCodecThread(); |
266 try { | 267 try { |
267 if (isKeyframe) { | 268 if (isKeyframe) { |
268 // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could | 269 // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could |
269 // indicate this in queueInputBuffer() below and guarantee _this_ frame | 270 // indicate this in queueInputBuffer() below and guarantee _this_ frame |
270 // be encoded as a key frame, but sadly that flag is ignored. Instead, | 271 // be encoded as a key frame, but sadly that flag is ignored. Instead, |
271 // we request a key frame "soon". | 272 // we request a key frame "soon". |
272 Log.d(TAG, "Sync frame request"); | 273 Logging.d(TAG, "Sync frame request"); |
273 Bundle b = new Bundle(); | 274 Bundle b = new Bundle(); |
274 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); | 275 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); |
275 mediaCodec.setParameters(b); | 276 mediaCodec.setParameters(b); |
276 } | 277 } |
277 mediaCodec.queueInputBuffer( | 278 mediaCodec.queueInputBuffer( |
278 inputBuffer, 0, size, presentationTimestampUs, 0); | 279 inputBuffer, 0, size, presentationTimestampUs, 0); |
279 return true; | 280 return true; |
280 } | 281 } |
281 catch (IllegalStateException e) { | 282 catch (IllegalStateException e) { |
282 Log.e(TAG, "encode failed", e); | 283 Logging.e(TAG, "encode failed", e); |
283 return false; | 284 return false; |
284 } | 285 } |
285 } | 286 } |
286 | 287 |
287 private void release() { | 288 private void release() { |
288 Log.d(TAG, "Java releaseEncoder"); | 289 Logging.d(TAG, "Java releaseEncoder"); |
289 checkOnMediaCodecThread(); | 290 checkOnMediaCodecThread(); |
290 try { | 291 try { |
291 mediaCodec.stop(); | 292 mediaCodec.stop(); |
292 mediaCodec.release(); | 293 mediaCodec.release(); |
293 } catch (IllegalStateException e) { | 294 } catch (IllegalStateException e) { |
294 Log.e(TAG, "release failed", e); | 295 Logging.e(TAG, "release failed", e); |
295 } | 296 } |
296 mediaCodec = null; | 297 mediaCodec = null; |
297 mediaCodecThread = null; | 298 mediaCodecThread = null; |
298 } | 299 } |
299 | 300 |
300 private boolean setRates(int kbps, int frameRateIgnored) { | 301 private boolean setRates(int kbps, int frameRateIgnored) { |
301 // frameRate argument is ignored - HW encoder is supposed to use | 302 // frameRate argument is ignored - HW encoder is supposed to use |
302 // video frame timestamps for bit allocation. | 303 // video frame timestamps for bit allocation. |
303 checkOnMediaCodecThread(); | 304 checkOnMediaCodecThread(); |
304 Log.v(TAG, "setRates: " + kbps + " kbps. Fps: " + frameRateIgnored); | 305 Logging.v(TAG, "setRates: " + kbps + " kbps. Fps: " + frameRateIgnored); |
305 try { | 306 try { |
306 Bundle params = new Bundle(); | 307 Bundle params = new Bundle(); |
307 params.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, 1000 * kbps); | 308 params.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, 1000 * kbps); |
308 mediaCodec.setParameters(params); | 309 mediaCodec.setParameters(params); |
309 return true; | 310 return true; |
310 } catch (IllegalStateException e) { | 311 } catch (IllegalStateException e) { |
311 Log.e(TAG, "setRates failed", e); | 312 Logging.e(TAG, "setRates failed", e); |
312 return false; | 313 return false; |
313 } | 314 } |
314 } | 315 } |
315 | 316 |
316 // Dequeue an input buffer and return its index, -1 if no input buffer is | 317 // Dequeue an input buffer and return its index, -1 if no input buffer is |
317 // available, or -2 if the codec is no longer operative. | 318 // available, or -2 if the codec is no longer operative. |
318 private int dequeueInputBuffer() { | 319 private int dequeueInputBuffer() { |
319 checkOnMediaCodecThread(); | 320 checkOnMediaCodecThread(); |
320 try { | 321 try { |
321 return mediaCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT); | 322 return mediaCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT); |
322 } catch (IllegalStateException e) { | 323 } catch (IllegalStateException e) { |
323 Log.e(TAG, "dequeueIntputBuffer failed", e); | 324 Logging.e(TAG, "dequeueIntputBuffer failed", e); |
324 return -2; | 325 return -2; |
325 } | 326 } |
326 } | 327 } |
327 | 328 |
328 // Helper struct for dequeueOutputBuffer() below. | 329 // Helper struct for dequeueOutputBuffer() below. |
329 private static class OutputBufferInfo { | 330 private static class OutputBufferInfo { |
330 public OutputBufferInfo( | 331 public OutputBufferInfo( |
331 int index, ByteBuffer buffer, | 332 int index, ByteBuffer buffer, |
332 boolean isKeyFrame, long presentationTimestampUs) { | 333 boolean isKeyFrame, long presentationTimestampUs) { |
333 this.index = index; | 334 this.index = index; |
(...skipping 13 matching lines...) Expand all Loading... |
347 private OutputBufferInfo dequeueOutputBuffer() { | 348 private OutputBufferInfo dequeueOutputBuffer() { |
348 checkOnMediaCodecThread(); | 349 checkOnMediaCodecThread(); |
349 try { | 350 try { |
350 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); | 351 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
351 int result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); | 352 int result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); |
352 // Check if this is config frame and save configuration data. | 353 // Check if this is config frame and save configuration data. |
353 if (result >= 0) { | 354 if (result >= 0) { |
354 boolean isConfigFrame = | 355 boolean isConfigFrame = |
355 (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0; | 356 (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0; |
356 if (isConfigFrame) { | 357 if (isConfigFrame) { |
357 Log.d(TAG, "Config frame generated. Offset: " + info.offset + | 358 Logging.d(TAG, "Config frame generated. Offset: " + info.offset + |
358 ". Size: " + info.size); | 359 ". Size: " + info.size); |
359 configData = ByteBuffer.allocateDirect(info.size); | 360 configData = ByteBuffer.allocateDirect(info.size); |
360 outputBuffers[result].position(info.offset); | 361 outputBuffers[result].position(info.offset); |
361 outputBuffers[result].limit(info.offset + info.size); | 362 outputBuffers[result].limit(info.offset + info.size); |
362 configData.put(outputBuffers[result]); | 363 configData.put(outputBuffers[result]); |
363 // Release buffer back. | 364 // Release buffer back. |
364 mediaCodec.releaseOutputBuffer(result, false); | 365 mediaCodec.releaseOutputBuffer(result, false); |
365 // Query next output. | 366 // Query next output. |
366 result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); | 367 result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); |
367 } | 368 } |
368 } | 369 } |
369 if (result >= 0) { | 370 if (result >= 0) { |
370 // MediaCodec doesn't care about Buffer position/remaining/etc so we can | 371 // MediaCodec doesn't care about Buffer position/remaining/etc so we can |
371 // mess with them to get a slice and avoid having to pass extra | 372 // mess with them to get a slice and avoid having to pass extra |
372 // (BufferInfo-related) parameters back to C++. | 373 // (BufferInfo-related) parameters back to C++. |
373 ByteBuffer outputBuffer = outputBuffers[result].duplicate(); | 374 ByteBuffer outputBuffer = outputBuffers[result].duplicate(); |
374 outputBuffer.position(info.offset); | 375 outputBuffer.position(info.offset); |
375 outputBuffer.limit(info.offset + info.size); | 376 outputBuffer.limit(info.offset + info.size); |
376 // Check key frame flag. | 377 // Check key frame flag. |
377 boolean isKeyFrame = | 378 boolean isKeyFrame = |
378 (info.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0; | 379 (info.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0; |
379 if (isKeyFrame) { | 380 if (isKeyFrame) { |
380 Log.d(TAG, "Sync frame generated"); | 381 Logging.d(TAG, "Sync frame generated"); |
381 } | 382 } |
382 if (isKeyFrame && type == VideoCodecType.VIDEO_CODEC_H264) { | 383 if (isKeyFrame && type == VideoCodecType.VIDEO_CODEC_H264) { |
383 Log.d(TAG, "Appending config frame of size " + configData.capacity() + | 384 Logging.d(TAG, "Appending config frame of size " + configData.capacity
() + |
384 " to output buffer with offset " + info.offset + ", size " + | 385 " to output buffer with offset " + info.offset + ", size " + |
385 info.size); | 386 info.size); |
386 // For H.264 key frame append SPS and PPS NALs at the start | 387 // For H.264 key frame append SPS and PPS NALs at the start |
387 ByteBuffer keyFrameBuffer = ByteBuffer.allocateDirect( | 388 ByteBuffer keyFrameBuffer = ByteBuffer.allocateDirect( |
388 configData.capacity() + info.size); | 389 configData.capacity() + info.size); |
389 configData.rewind(); | 390 configData.rewind(); |
390 keyFrameBuffer.put(configData); | 391 keyFrameBuffer.put(configData); |
391 keyFrameBuffer.put(outputBuffer); | 392 keyFrameBuffer.put(outputBuffer); |
392 keyFrameBuffer.position(0); | 393 keyFrameBuffer.position(0); |
393 return new OutputBufferInfo(result, keyFrameBuffer, | 394 return new OutputBufferInfo(result, keyFrameBuffer, |
394 isKeyFrame, info.presentationTimeUs); | 395 isKeyFrame, info.presentationTimeUs); |
395 } else { | 396 } else { |
396 return new OutputBufferInfo(result, outputBuffer.slice(), | 397 return new OutputBufferInfo(result, outputBuffer.slice(), |
397 isKeyFrame, info.presentationTimeUs); | 398 isKeyFrame, info.presentationTimeUs); |
398 } | 399 } |
399 } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { | 400 } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { |
400 outputBuffers = mediaCodec.getOutputBuffers(); | 401 outputBuffers = mediaCodec.getOutputBuffers(); |
401 return dequeueOutputBuffer(); | 402 return dequeueOutputBuffer(); |
402 } else if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { | 403 } else if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { |
403 return dequeueOutputBuffer(); | 404 return dequeueOutputBuffer(); |
404 } else if (result == MediaCodec.INFO_TRY_AGAIN_LATER) { | 405 } else if (result == MediaCodec.INFO_TRY_AGAIN_LATER) { |
405 return null; | 406 return null; |
406 } | 407 } |
407 throw new RuntimeException("dequeueOutputBuffer: " + result); | 408 throw new RuntimeException("dequeueOutputBuffer: " + result); |
408 } catch (IllegalStateException e) { | 409 } catch (IllegalStateException e) { |
409 Log.e(TAG, "dequeueOutputBuffer failed", e); | 410 Logging.e(TAG, "dequeueOutputBuffer failed", e); |
410 return new OutputBufferInfo(-1, null, false, -1); | 411 return new OutputBufferInfo(-1, null, false, -1); |
411 } | 412 } |
412 } | 413 } |
413 | 414 |
414 // Release a dequeued output buffer back to the codec for re-use. Return | 415 // Release a dequeued output buffer back to the codec for re-use. Return |
415 // false if the codec is no longer operable. | 416 // false if the codec is no longer operable. |
416 private boolean releaseOutputBuffer(int index) { | 417 private boolean releaseOutputBuffer(int index) { |
417 checkOnMediaCodecThread(); | 418 checkOnMediaCodecThread(); |
418 try { | 419 try { |
419 mediaCodec.releaseOutputBuffer(index, false); | 420 mediaCodec.releaseOutputBuffer(index, false); |
420 return true; | 421 return true; |
421 } catch (IllegalStateException e) { | 422 } catch (IllegalStateException e) { |
422 Log.e(TAG, "releaseOutputBuffer failed", e); | 423 Logging.e(TAG, "releaseOutputBuffer failed", e); |
423 return false; | 424 return false; |
424 } | 425 } |
425 } | 426 } |
426 } | 427 } |
OLD | NEW |