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