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