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 | 106 |
107 // Helper struct for findHwEncoder() below. | 107 // Helper struct for findHwEncoder() below. |
108 private static class EncoderProperties { | 108 private static class EncoderProperties { |
109 public EncoderProperties(String codecName, int colorFormat) { | 109 public EncoderProperties(String codecName, int colorFormat) { |
110 this.codecName = codecName; | 110 this.codecName = codecName; |
111 this.colorFormat = colorFormat; | 111 this.colorFormat = colorFormat; |
112 } | 112 } |
113 public final String codecName; // OpenMax component name for HW codec. | 113 public final String codecName; // OpenMax component name for HW codec. |
114 public final int colorFormat; // Color format supported by codec. | 114 public final int colorFormat; // Color format supported by codec. |
115 } | 115 } |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
200 static MediaCodec createByCodecName(String codecName) { | 200 static MediaCodec createByCodecName(String codecName) { |
201 try { | 201 try { |
202 // In the L-SDK this call can throw IOException so in order to work in | 202 // In the L-SDK this call can throw IOException so in order to work in |
203 // both cases catch an exception. | 203 // both cases catch an exception. |
204 return MediaCodec.createByCodecName(codecName); | 204 return MediaCodec.createByCodecName(codecName); |
205 } catch (Exception e) { | 205 } catch (Exception e) { |
206 return null; | 206 return null; |
207 } | 207 } |
208 } | 208 } |
209 | 209 |
210 // Return the array of input buffers, or null on failure. | 210 boolean initEncode(VideoCodecType type, int width, int height, int kbps, int f ps) { |
magjed_webrtc
2015/10/12 08:22:53
Add short comment about return value, e.g. "Return
perkj_webrtc
2015/10/12 09:16:06
Done.
| |
211 private ByteBuffer[] initEncode( | |
212 VideoCodecType type, int width, int height, int kbps, int fps) { | |
213 Logging.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height + | 211 Logging.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height + |
214 ". @ " + kbps + " kbps. Fps: " + fps + | 212 ". @ " + kbps + " kbps. Fps: " + fps + "."); |
215 ". Color: 0x" + Integer.toHexString(colorFormat)); | 213 |
216 if (mediaCodecThread != null) { | 214 if (mediaCodecThread != null) { |
217 throw new RuntimeException("Forgot to release()?"); | 215 throw new RuntimeException("Forgot to release()?"); |
218 } | 216 } |
219 this.type = type; | |
220 EncoderProperties properties = null; | 217 EncoderProperties properties = null; |
221 String mime = null; | 218 String mime = null; |
222 int keyFrameIntervalSec = 0; | 219 int keyFrameIntervalSec = 0; |
223 if (type == VideoCodecType.VIDEO_CODEC_VP8) { | 220 if (type == VideoCodecType.VIDEO_CODEC_VP8) { |
224 mime = VP8_MIME_TYPE; | 221 mime = VP8_MIME_TYPE; |
225 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes); | 222 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes); |
226 keyFrameIntervalSec = 100; | 223 keyFrameIntervalSec = 100; |
227 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { | 224 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { |
228 mime = H264_MIME_TYPE; | 225 mime = H264_MIME_TYPE; |
229 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes); | 226 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes); |
230 keyFrameIntervalSec = 20; | 227 keyFrameIntervalSec = 20; |
231 } | 228 } |
232 if (properties == null) { | 229 if (properties == null) { |
233 throw new RuntimeException("Can not find HW encoder for " + type); | 230 throw new RuntimeException("Can not find HW encoder for " + type); |
234 } | 231 } |
232 colorFormat = properties.colorFormat; | |
235 mediaCodecThread = Thread.currentThread(); | 233 mediaCodecThread = Thread.currentThread(); |
236 try { | 234 try { |
237 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); | 235 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); |
238 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps); | 236 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps); |
239 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); | 237 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); |
240 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); | 238 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); |
241 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); | 239 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); |
242 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); | 240 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); |
243 Logging.d(TAG, " Format: " + format); | 241 Logging.d(TAG, " Format: " + format); |
244 mediaCodec = createByCodecName(properties.codecName); | 242 mediaCodec = createByCodecName(properties.codecName); |
243 this.type = type; | |
245 if (mediaCodec == null) { | 244 if (mediaCodec == null) { |
246 return null; | 245 return false; |
247 } | 246 } |
248 mediaCodec.configure( | 247 mediaCodec.configure( |
249 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); | 248 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); |
249 | |
250 mediaCodec.start(); | 250 mediaCodec.start(); |
251 colorFormat = properties.colorFormat; | |
252 outputBuffers = mediaCodec.getOutputBuffers(); | 251 outputBuffers = mediaCodec.getOutputBuffers(); |
253 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); | 252 |
254 Logging.d(TAG, "Input buffers: " + inputBuffers.length + | |
255 ". Output buffers: " + outputBuffers.length); | |
256 return inputBuffers; | |
257 } catch (IllegalStateException e) { | 253 } catch (IllegalStateException e) { |
258 Logging.e(TAG, "initEncode failed", e); | 254 Logging.e(TAG, "initEncode failed", e); |
259 return null; | 255 return false; |
260 } | 256 } |
257 return true; | |
261 } | 258 } |
262 | 259 |
263 private boolean encode( | 260 ByteBuffer[] getInputBuffers() { |
261 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); | |
262 Logging.d(TAG, "Input buffers: " + inputBuffers.length); | |
263 return inputBuffers; | |
264 } | |
265 | |
266 boolean encodeBuffer( | |
264 boolean isKeyframe, int inputBuffer, int size, | 267 boolean isKeyframe, int inputBuffer, int size, |
265 long presentationTimestampUs) { | 268 long presentationTimestampUs) { |
266 checkOnMediaCodecThread(); | 269 checkOnMediaCodecThread(); |
267 try { | 270 try { |
268 if (isKeyframe) { | 271 if (isKeyframe) { |
269 // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could | 272 // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could |
270 // indicate this in queueInputBuffer() below and guarantee _this_ frame | 273 // indicate this in queueInputBuffer() below and guarantee _this_ frame |
271 // be encoded as a key frame, but sadly that flag is ignored. Instead, | 274 // be encoded as a key frame, but sadly that flag is ignored. Instead, |
272 // we request a key frame "soon". | 275 // we request a key frame "soon". |
273 Logging.d(TAG, "Sync frame request"); | 276 Logging.d(TAG, "Sync frame request"); |
274 Bundle b = new Bundle(); | 277 Bundle b = new Bundle(); |
275 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); | 278 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); |
276 mediaCodec.setParameters(b); | 279 mediaCodec.setParameters(b); |
277 } | 280 } |
278 mediaCodec.queueInputBuffer( | 281 mediaCodec.queueInputBuffer( |
279 inputBuffer, 0, size, presentationTimestampUs, 0); | 282 inputBuffer, 0, size, presentationTimestampUs, 0); |
280 return true; | 283 return true; |
281 } | 284 } |
282 catch (IllegalStateException e) { | 285 catch (IllegalStateException e) { |
283 Logging.e(TAG, "encode failed", e); | 286 Logging.e(TAG, "encodeBuffer failed", e); |
284 return false; | 287 return false; |
285 } | 288 } |
286 } | 289 } |
287 | 290 |
288 private void release() { | 291 void release() { |
289 Logging.d(TAG, "Java releaseEncoder"); | 292 Logging.d(TAG, "Java releaseEncoder"); |
290 checkOnMediaCodecThread(); | 293 checkOnMediaCodecThread(); |
291 try { | 294 try { |
292 mediaCodec.stop(); | 295 mediaCodec.stop(); |
293 mediaCodec.release(); | 296 mediaCodec.release(); |
294 } catch (IllegalStateException e) { | 297 } catch (IllegalStateException e) { |
295 Logging.e(TAG, "release failed", e); | 298 Logging.e(TAG, "release failed", e); |
296 } | 299 } |
297 mediaCodec = null; | 300 mediaCodec = null; |
298 mediaCodecThread = null; | 301 mediaCodecThread = null; |
(...skipping 10 matching lines...) Expand all Loading... | |
309 mediaCodec.setParameters(params); | 312 mediaCodec.setParameters(params); |
310 return true; | 313 return true; |
311 } catch (IllegalStateException e) { | 314 } catch (IllegalStateException e) { |
312 Logging.e(TAG, "setRates failed", e); | 315 Logging.e(TAG, "setRates failed", e); |
313 return false; | 316 return false; |
314 } | 317 } |
315 } | 318 } |
316 | 319 |
317 // Dequeue an input buffer and return its index, -1 if no input buffer is | 320 // 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. | 321 // available, or -2 if the codec is no longer operative. |
319 private int dequeueInputBuffer() { | 322 int dequeueInputBuffer() { |
320 checkOnMediaCodecThread(); | 323 checkOnMediaCodecThread(); |
321 try { | 324 try { |
322 return mediaCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT); | 325 return mediaCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT); |
323 } catch (IllegalStateException e) { | 326 } catch (IllegalStateException e) { |
324 Logging.e(TAG, "dequeueIntputBuffer failed", e); | 327 Logging.e(TAG, "dequeueIntputBuffer failed", e); |
325 return -2; | 328 return -2; |
326 } | 329 } |
327 } | 330 } |
328 | 331 |
329 // Helper struct for dequeueOutputBuffer() below. | 332 // Helper struct for dequeueOutputBuffer() below. |
330 private static class OutputBufferInfo { | 333 static class OutputBufferInfo { |
331 public OutputBufferInfo( | 334 public OutputBufferInfo( |
332 int index, ByteBuffer buffer, | 335 int index, ByteBuffer buffer, |
333 boolean isKeyFrame, long presentationTimestampUs) { | 336 boolean isKeyFrame, long presentationTimestampUs) { |
334 this.index = index; | 337 this.index = index; |
335 this.buffer = buffer; | 338 this.buffer = buffer; |
336 this.isKeyFrame = isKeyFrame; | 339 this.isKeyFrame = isKeyFrame; |
337 this.presentationTimestampUs = presentationTimestampUs; | 340 this.presentationTimestampUs = presentationTimestampUs; |
338 } | 341 } |
339 | 342 |
340 private final int index; | 343 public final int index; |
341 private final ByteBuffer buffer; | 344 public final ByteBuffer buffer; |
342 private final boolean isKeyFrame; | 345 public final boolean isKeyFrame; |
343 private final long presentationTimestampUs; | 346 public final long presentationTimestampUs; |
344 } | 347 } |
345 | 348 |
346 // Dequeue and return an output buffer, or null if no output is ready. Return | 349 // Dequeue and return an output buffer, or null if no output is ready. Return |
347 // a fake OutputBufferInfo with index -1 if the codec is no longer operable. | 350 // a fake OutputBufferInfo with index -1 if the codec is no longer operable. |
348 private OutputBufferInfo dequeueOutputBuffer() { | 351 OutputBufferInfo dequeueOutputBuffer() { |
349 checkOnMediaCodecThread(); | 352 checkOnMediaCodecThread(); |
350 try { | 353 try { |
351 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); | 354 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
352 int result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); | 355 int result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); |
353 // Check if this is config frame and save configuration data. | 356 // Check if this is config frame and save configuration data. |
354 if (result >= 0) { | 357 if (result >= 0) { |
355 boolean isConfigFrame = | 358 boolean isConfigFrame = |
356 (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0; | 359 (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0; |
357 if (isConfigFrame) { | 360 if (isConfigFrame) { |
358 Logging.d(TAG, "Config frame generated. Offset: " + info.offset + | 361 Logging.d(TAG, "Config frame generated. Offset: " + info.offset + |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
407 } | 410 } |
408 throw new RuntimeException("dequeueOutputBuffer: " + result); | 411 throw new RuntimeException("dequeueOutputBuffer: " + result); |
409 } catch (IllegalStateException e) { | 412 } catch (IllegalStateException e) { |
410 Logging.e(TAG, "dequeueOutputBuffer failed", e); | 413 Logging.e(TAG, "dequeueOutputBuffer failed", e); |
411 return new OutputBufferInfo(-1, null, false, -1); | 414 return new OutputBufferInfo(-1, null, false, -1); |
412 } | 415 } |
413 } | 416 } |
414 | 417 |
415 // Release a dequeued output buffer back to the codec for re-use. Return | 418 // Release a dequeued output buffer back to the codec for re-use. Return |
416 // false if the codec is no longer operable. | 419 // false if the codec is no longer operable. |
417 private boolean releaseOutputBuffer(int index) { | 420 boolean releaseOutputBuffer(int index) { |
418 checkOnMediaCodecThread(); | 421 checkOnMediaCodecThread(); |
419 try { | 422 try { |
420 mediaCodec.releaseOutputBuffer(index, false); | 423 mediaCodec.releaseOutputBuffer(index, false); |
421 return true; | 424 return true; |
422 } catch (IllegalStateException e) { | 425 } catch (IllegalStateException e) { |
423 Logging.e(TAG, "releaseOutputBuffer failed", e); | 426 Logging.e(TAG, "releaseOutputBuffer failed", e); |
424 return false; | 427 return false; |
425 } | 428 } |
426 } | 429 } |
427 } | 430 } |
OLD | NEW |