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