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 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
89 // see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h | 89 // see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h |
90 private static final int | 90 private static final int |
91 COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04; | 91 COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04; |
92 // Allowable color formats supported by codec - in order of preference. | 92 // Allowable color formats supported by codec - in order of preference. |
93 private static final int[] supportedColorList = { | 93 private static final int[] supportedColorList = { |
94 CodecCapabilities.COLOR_FormatYUV420Planar, | 94 CodecCapabilities.COLOR_FormatYUV420Planar, |
95 CodecCapabilities.COLOR_FormatYUV420SemiPlanar, | 95 CodecCapabilities.COLOR_FormatYUV420SemiPlanar, |
96 CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, | 96 CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, |
97 COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m | 97 COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m |
98 }; | 98 }; |
99 private int colorFormat; | |
100 // Video encoder type. | |
101 private VideoCodecType type; | 99 private VideoCodecType type; |
100 private int colorFormat; // Used by native code. | |
101 | |
102 // SPS and PPS NALs (Config frame) for H.264. | 102 // SPS and PPS NALs (Config frame) for H.264. |
103 private ByteBuffer configData = null; | 103 private ByteBuffer configData = null; |
104 | 104 |
105 private MediaCodecVideoEncoder() { | 105 MediaCodecVideoEncoder() { |
106 instance = this; | 106 instance = this; |
107 } | 107 } |
108 | 108 |
109 // Helper struct for findHwEncoder() below. | 109 // Helper struct for findHwEncoder() below. |
110 private static class EncoderProperties { | 110 private static class EncoderProperties { |
111 public EncoderProperties(String codecName, int colorFormat) { | 111 public EncoderProperties(String codecName, int colorFormat) { |
112 this.codecName = codecName; | 112 this.codecName = codecName; |
113 this.colorFormat = colorFormat; | 113 this.colorFormat = colorFormat; |
114 } | 114 } |
115 public final String codecName; // OpenMax component name for HW codec. | 115 public final String codecName; // OpenMax component name for HW codec. |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
214 static MediaCodec createByCodecName(String codecName) { | 214 static MediaCodec createByCodecName(String codecName) { |
215 try { | 215 try { |
216 // In the L-SDK this call can throw IOException so in order to work in | 216 // In the L-SDK this call can throw IOException so in order to work in |
217 // both cases catch an exception. | 217 // both cases catch an exception. |
218 return MediaCodec.createByCodecName(codecName); | 218 return MediaCodec.createByCodecName(codecName); |
219 } catch (Exception e) { | 219 } catch (Exception e) { |
220 return null; | 220 return null; |
221 } | 221 } |
222 } | 222 } |
223 | 223 |
224 // Return the array of input buffers, or null on failure. | 224 // Returns false if the hardware encoder currently can't be used. |
225 private ByteBuffer[] initEncode( | 225 boolean initEncode(VideoCodecType type, int width, int height, int kbps, int f ps) { |
226 VideoCodecType type, int width, int height, int kbps, int fps) { | |
227 Logging.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height + | 226 Logging.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height + |
228 ". @ " + kbps + " kbps. Fps: " + fps + | 227 ". @ " + kbps + " kbps. Fps: " + fps + "."); |
229 ". Color: 0x" + Integer.toHexString(colorFormat)); | 228 |
230 if (mediaCodecThread != null) { | 229 if (mediaCodecThread != null) { |
231 throw new RuntimeException("Forgot to release()?"); | 230 throw new RuntimeException("Forgot to release()?"); |
232 } | 231 } |
233 this.type = type; | |
234 EncoderProperties properties = null; | 232 EncoderProperties properties = null; |
235 String mime = null; | 233 String mime = null; |
236 int keyFrameIntervalSec = 0; | 234 int keyFrameIntervalSec = 0; |
237 if (type == VideoCodecType.VIDEO_CODEC_VP8) { | 235 if (type == VideoCodecType.VIDEO_CODEC_VP8) { |
238 mime = VP8_MIME_TYPE; | 236 mime = VP8_MIME_TYPE; |
239 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes); | 237 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes); |
240 keyFrameIntervalSec = 100; | 238 keyFrameIntervalSec = 100; |
241 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { | 239 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { |
242 mime = H264_MIME_TYPE; | 240 mime = H264_MIME_TYPE; |
243 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes); | 241 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes); |
244 keyFrameIntervalSec = 20; | 242 keyFrameIntervalSec = 20; |
245 } | 243 } |
246 if (properties == null) { | 244 if (properties == null) { |
247 throw new RuntimeException("Can not find HW encoder for " + type); | 245 throw new RuntimeException("Can not find HW encoder for " + type); |
248 } | 246 } |
247 colorFormat = properties.colorFormat; | |
AlexG
2015/10/19 23:38:45
Print color format in the log for debug purposes?
| |
249 mediaCodecThread = Thread.currentThread(); | 248 mediaCodecThread = Thread.currentThread(); |
250 try { | 249 try { |
251 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); | 250 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); |
252 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps); | 251 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps); |
253 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); | 252 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); |
254 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); | 253 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); |
255 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); | 254 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); |
256 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); | 255 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); |
257 Logging.d(TAG, " Format: " + format); | 256 Logging.d(TAG, " Format: " + format); |
258 mediaCodec = createByCodecName(properties.codecName); | 257 mediaCodec = createByCodecName(properties.codecName); |
258 this.type = type; | |
259 if (mediaCodec == null) { | 259 if (mediaCodec == null) { |
260 Logging.e(TAG, "Can not create media encoder"); | 260 Logging.e(TAG, "Can not create media encoder"); |
261 return null; | 261 return false; |
262 } | 262 } |
263 mediaCodec.configure( | 263 mediaCodec.configure( |
264 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); | 264 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); |
265 | |
265 mediaCodec.start(); | 266 mediaCodec.start(); |
266 colorFormat = properties.colorFormat; | |
267 outputBuffers = mediaCodec.getOutputBuffers(); | 267 outputBuffers = mediaCodec.getOutputBuffers(); |
AlexG
2015/10/19 23:38:45
Print outputBuffers.length for debug purposes?
| |
268 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); | 268 |
269 Logging.d(TAG, "Input buffers: " + inputBuffers.length + | |
270 ". Output buffers: " + outputBuffers.length); | |
271 return inputBuffers; | |
272 } catch (IllegalStateException e) { | 269 } catch (IllegalStateException e) { |
273 Logging.e(TAG, "initEncode failed", e); | 270 Logging.e(TAG, "initEncode failed", e); |
274 return null; | 271 return false; |
275 } | 272 } |
273 return true; | |
276 } | 274 } |
277 | 275 |
278 private boolean encode( | 276 ByteBuffer[] getInputBuffers() { |
277 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); | |
278 Logging.d(TAG, "Input buffers: " + inputBuffers.length); | |
279 return inputBuffers; | |
280 } | |
281 | |
282 boolean encodeBuffer( | |
279 boolean isKeyframe, int inputBuffer, int size, | 283 boolean isKeyframe, int inputBuffer, int size, |
280 long presentationTimestampUs) { | 284 long presentationTimestampUs) { |
281 checkOnMediaCodecThread(); | 285 checkOnMediaCodecThread(); |
282 try { | 286 try { |
283 if (isKeyframe) { | 287 if (isKeyframe) { |
284 // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could | 288 // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could |
285 // indicate this in queueInputBuffer() below and guarantee _this_ frame | 289 // indicate this in queueInputBuffer() below and guarantee _this_ frame |
286 // be encoded as a key frame, but sadly that flag is ignored. Instead, | 290 // be encoded as a key frame, but sadly that flag is ignored. Instead, |
287 // we request a key frame "soon". | 291 // we request a key frame "soon". |
288 Logging.d(TAG, "Sync frame request"); | 292 Logging.d(TAG, "Sync frame request"); |
289 Bundle b = new Bundle(); | 293 Bundle b = new Bundle(); |
290 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); | 294 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); |
291 mediaCodec.setParameters(b); | 295 mediaCodec.setParameters(b); |
292 } | 296 } |
293 mediaCodec.queueInputBuffer( | 297 mediaCodec.queueInputBuffer( |
294 inputBuffer, 0, size, presentationTimestampUs, 0); | 298 inputBuffer, 0, size, presentationTimestampUs, 0); |
295 return true; | 299 return true; |
296 } | 300 } |
297 catch (IllegalStateException e) { | 301 catch (IllegalStateException e) { |
298 Logging.e(TAG, "encode failed", e); | 302 Logging.e(TAG, "encodeBuffer failed", e); |
299 return false; | 303 return false; |
300 } | 304 } |
301 } | 305 } |
302 | 306 |
303 private void release() { | 307 void release() { |
304 Logging.d(TAG, "Java releaseEncoder"); | 308 Logging.d(TAG, "Java releaseEncoder"); |
305 checkOnMediaCodecThread(); | 309 checkOnMediaCodecThread(); |
306 try { | 310 try { |
307 mediaCodec.stop(); | 311 mediaCodec.stop(); |
308 mediaCodec.release(); | 312 mediaCodec.release(); |
309 } catch (IllegalStateException e) { | 313 } catch (IllegalStateException e) { |
310 Logging.e(TAG, "release failed", e); | 314 Logging.e(TAG, "release failed", e); |
311 } | 315 } |
312 mediaCodec = null; | 316 mediaCodec = null; |
313 mediaCodecThread = null; | 317 mediaCodecThread = null; |
(...skipping 12 matching lines...) Expand all Loading... | |
326 mediaCodec.setParameters(params); | 330 mediaCodec.setParameters(params); |
327 return true; | 331 return true; |
328 } catch (IllegalStateException e) { | 332 } catch (IllegalStateException e) { |
329 Logging.e(TAG, "setRates failed", e); | 333 Logging.e(TAG, "setRates failed", e); |
330 return false; | 334 return false; |
331 } | 335 } |
332 } | 336 } |
333 | 337 |
334 // Dequeue an input buffer and return its index, -1 if no input buffer is | 338 // Dequeue an input buffer and return its index, -1 if no input buffer is |
335 // available, or -2 if the codec is no longer operative. | 339 // available, or -2 if the codec is no longer operative. |
336 private int dequeueInputBuffer() { | 340 int dequeueInputBuffer() { |
337 checkOnMediaCodecThread(); | 341 checkOnMediaCodecThread(); |
338 try { | 342 try { |
339 return mediaCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT); | 343 return mediaCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT); |
340 } catch (IllegalStateException e) { | 344 } catch (IllegalStateException e) { |
341 Logging.e(TAG, "dequeueIntputBuffer failed", e); | 345 Logging.e(TAG, "dequeueIntputBuffer failed", e); |
342 return -2; | 346 return -2; |
343 } | 347 } |
344 } | 348 } |
345 | 349 |
346 // Helper struct for dequeueOutputBuffer() below. | 350 // Helper struct for dequeueOutputBuffer() below. |
347 private static class OutputBufferInfo { | 351 static class OutputBufferInfo { |
348 public OutputBufferInfo( | 352 public OutputBufferInfo( |
349 int index, ByteBuffer buffer, | 353 int index, ByteBuffer buffer, |
350 boolean isKeyFrame, long presentationTimestampUs) { | 354 boolean isKeyFrame, long presentationTimestampUs) { |
351 this.index = index; | 355 this.index = index; |
352 this.buffer = buffer; | 356 this.buffer = buffer; |
353 this.isKeyFrame = isKeyFrame; | 357 this.isKeyFrame = isKeyFrame; |
354 this.presentationTimestampUs = presentationTimestampUs; | 358 this.presentationTimestampUs = presentationTimestampUs; |
355 } | 359 } |
356 | 360 |
357 private final int index; | 361 public final int index; |
358 private final ByteBuffer buffer; | 362 public final ByteBuffer buffer; |
359 private final boolean isKeyFrame; | 363 public final boolean isKeyFrame; |
360 private final long presentationTimestampUs; | 364 public final long presentationTimestampUs; |
361 } | 365 } |
362 | 366 |
363 // Dequeue and return an output buffer, or null if no output is ready. Return | 367 // Dequeue and return an output buffer, or null if no output is ready. Return |
364 // a fake OutputBufferInfo with index -1 if the codec is no longer operable. | 368 // a fake OutputBufferInfo with index -1 if the codec is no longer operable. |
365 private OutputBufferInfo dequeueOutputBuffer() { | 369 OutputBufferInfo dequeueOutputBuffer() { |
366 checkOnMediaCodecThread(); | 370 checkOnMediaCodecThread(); |
367 try { | 371 try { |
368 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); | 372 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
369 int result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); | 373 int result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); |
370 // Check if this is config frame and save configuration data. | 374 // Check if this is config frame and save configuration data. |
371 if (result >= 0) { | 375 if (result >= 0) { |
372 boolean isConfigFrame = | 376 boolean isConfigFrame = |
373 (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0; | 377 (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0; |
374 if (isConfigFrame) { | 378 if (isConfigFrame) { |
375 Logging.d(TAG, "Config frame generated. Offset: " + info.offset + | 379 Logging.d(TAG, "Config frame generated. Offset: " + info.offset + |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
424 } | 428 } |
425 throw new RuntimeException("dequeueOutputBuffer: " + result); | 429 throw new RuntimeException("dequeueOutputBuffer: " + result); |
426 } catch (IllegalStateException e) { | 430 } catch (IllegalStateException e) { |
427 Logging.e(TAG, "dequeueOutputBuffer failed", e); | 431 Logging.e(TAG, "dequeueOutputBuffer failed", e); |
428 return new OutputBufferInfo(-1, null, false, -1); | 432 return new OutputBufferInfo(-1, null, false, -1); |
429 } | 433 } |
430 } | 434 } |
431 | 435 |
432 // Release a dequeued output buffer back to the codec for re-use. Return | 436 // Release a dequeued output buffer back to the codec for re-use. Return |
433 // false if the codec is no longer operable. | 437 // false if the codec is no longer operable. |
434 private boolean releaseOutputBuffer(int index) { | 438 boolean releaseOutputBuffer(int index) { |
435 checkOnMediaCodecThread(); | 439 checkOnMediaCodecThread(); |
436 try { | 440 try { |
437 mediaCodec.releaseOutputBuffer(index, false); | 441 mediaCodec.releaseOutputBuffer(index, false); |
438 return true; | 442 return true; |
439 } catch (IllegalStateException e) { | 443 } catch (IllegalStateException e) { |
440 Logging.e(TAG, "releaseOutputBuffer failed", e); | 444 Logging.e(TAG, "releaseOutputBuffer failed", e); |
441 return false; | 445 return false; |
442 } | 446 } |
443 } | 447 } |
444 } | 448 } |
OLD | NEW |