Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(467)

Side by Side Diff: talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoEncoder.java

Issue 1406203002: Reland "Prepare MediaCodecVideoEncoder for surface textures."" (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Addressed magjeds comments. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « talk/app/webrtc/java/jni/androidmediaencoder_jni.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
54 54
55 // Tracks webrtc::VideoCodecType. 55 // Tracks webrtc::VideoCodecType.
56 public enum VideoCodecType { 56 public enum VideoCodecType {
57 VIDEO_CODEC_VP8, 57 VIDEO_CODEC_VP8,
58 VIDEO_CODEC_VP9, 58 VIDEO_CODEC_VP9,
59 VIDEO_CODEC_H264 59 VIDEO_CODEC_H264
60 } 60 }
61 61
62 private static final int MEDIA_CODEC_RELEASE_TIMEOUT_MS = 5000; // Timeout for codec releasing. 62 private static final int MEDIA_CODEC_RELEASE_TIMEOUT_MS = 5000; // Timeout for codec releasing.
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 static MediaCodecVideoEncoderErrorCallback errorCallback = null; 67 private static MediaCodecVideoEncoderErrorCallback errorCallback = null;
68 private static int codecErrors = 0; 68 private static int codecErrors = 0;
69 69
70 private Thread mediaCodecThread; 70 private Thread mediaCodecThread;
71 private MediaCodec mediaCodec; 71 private MediaCodec mediaCodec;
72 private ByteBuffer[] outputBuffers; 72 private ByteBuffer[] outputBuffers;
73 private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8"; 73 private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8";
74 private static final String H264_MIME_TYPE = "video/avc"; 74 private static final String H264_MIME_TYPE = "video/avc";
(...skipping 20 matching lines...) Expand all
95 // see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h 95 // see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h
96 private static final int 96 private static final int
97 COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04; 97 COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04;
98 // Allowable color formats supported by codec - in order of preference. 98 // Allowable color formats supported by codec - in order of preference.
99 private static final int[] supportedColorList = { 99 private static final int[] supportedColorList = {
100 CodecCapabilities.COLOR_FormatYUV420Planar, 100 CodecCapabilities.COLOR_FormatYUV420Planar,
101 CodecCapabilities.COLOR_FormatYUV420SemiPlanar, 101 CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
102 CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, 102 CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
103 COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m 103 COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m
104 }; 104 };
105 private int colorFormat;
106 // Video encoder type.
107 private VideoCodecType type; 105 private VideoCodecType type;
106 private int colorFormat; // Used by native code.
107
108 // SPS and PPS NALs (Config frame) for H.264. 108 // SPS and PPS NALs (Config frame) for H.264.
109 private ByteBuffer configData = null; 109 private ByteBuffer configData = null;
110 110
111 private MediaCodecVideoEncoder() {
112 }
113
114 // MediaCodec error handler - invoked when critical error happens which may pr event 111 // MediaCodec error handler - invoked when critical error happens which may pr event
115 // further use of media codec API. Now it means that one of media codec instan ces 112 // further use of media codec API. Now it means that one of media codec instan ces
116 // is hanging and can no longer be used in the next call. 113 // is hanging and can no longer be used in the next call.
117 public static interface MediaCodecVideoEncoderErrorCallback { 114 public static interface MediaCodecVideoEncoderErrorCallback {
118 void onMediaCodecVideoEncoderCriticalError(int codecErrors); 115 void onMediaCodecVideoEncoderCriticalError(int codecErrors);
119 } 116 }
120 117
121 public static void setErrorCallback(MediaCodecVideoEncoderErrorCallback errorC allback) { 118 public static void setErrorCallback(MediaCodecVideoEncoderErrorCallback errorC allback) {
122 Logging.d(TAG, "Set error callback"); 119 Logging.d(TAG, "Set error callback");
123 MediaCodecVideoEncoder.errorCallback = errorCallback; 120 MediaCodecVideoEncoder.errorCallback = errorCallback;
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
230 static MediaCodec createByCodecName(String codecName) { 227 static MediaCodec createByCodecName(String codecName) {
231 try { 228 try {
232 // In the L-SDK this call can throw IOException so in order to work in 229 // In the L-SDK this call can throw IOException so in order to work in
233 // both cases catch an exception. 230 // both cases catch an exception.
234 return MediaCodec.createByCodecName(codecName); 231 return MediaCodec.createByCodecName(codecName);
235 } catch (Exception e) { 232 } catch (Exception e) {
236 return null; 233 return null;
237 } 234 }
238 } 235 }
239 236
240 // Return the array of input buffers, or null on failure. 237 // Returns false if the hardware encoder currently can't be used.
241 private ByteBuffer[] initEncode( 238 boolean initEncode(VideoCodecType type, int width, int height, int kbps, int f ps) {
242 VideoCodecType type, int width, int height, int kbps, int fps) {
243 Logging.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height + 239 Logging.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height +
244 ". @ " + kbps + " kbps. Fps: " + fps + 240 ". @ " + kbps + " kbps. Fps: " + fps + ".");
245 ". Color: 0x" + Integer.toHexString(colorFormat)); 241
246 if (mediaCodecThread != null) { 242 if (mediaCodecThread != null) {
247 throw new RuntimeException("Forgot to release()?"); 243 throw new RuntimeException("Forgot to release()?");
248 } 244 }
249 this.type = type;
250 EncoderProperties properties = null; 245 EncoderProperties properties = null;
251 String mime = null; 246 String mime = null;
252 int keyFrameIntervalSec = 0; 247 int keyFrameIntervalSec = 0;
253 if (type == VideoCodecType.VIDEO_CODEC_VP8) { 248 if (type == VideoCodecType.VIDEO_CODEC_VP8) {
254 mime = VP8_MIME_TYPE; 249 mime = VP8_MIME_TYPE;
255 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes); 250 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes);
256 keyFrameIntervalSec = 100; 251 keyFrameIntervalSec = 100;
257 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { 252 } else if (type == VideoCodecType.VIDEO_CODEC_H264) {
258 mime = H264_MIME_TYPE; 253 mime = H264_MIME_TYPE;
259 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes); 254 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes);
260 keyFrameIntervalSec = 20; 255 keyFrameIntervalSec = 20;
261 } 256 }
262 if (properties == null) { 257 if (properties == null) {
263 throw new RuntimeException("Can not find HW encoder for " + type); 258 throw new RuntimeException("Can not find HW encoder for " + type);
264 } 259 }
265 runningInstance = this; // Encoder is now running and can be queried for sta ck traces. 260 runningInstance = this; // Encoder is now running and can be queried for sta ck traces.
261 colorFormat = properties.colorFormat;
262 Logging.d(TAG, "Color format: " + colorFormat);
263
266 mediaCodecThread = Thread.currentThread(); 264 mediaCodecThread = Thread.currentThread();
267 try { 265 try {
268 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); 266 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
269 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps); 267 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps);
270 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); 268 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant);
271 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); 269 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat);
272 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); 270 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps);
273 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); 271 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec);
274 Logging.d(TAG, " Format: " + format); 272 Logging.d(TAG, " Format: " + format);
275 mediaCodec = createByCodecName(properties.codecName); 273 mediaCodec = createByCodecName(properties.codecName);
274 this.type = type;
276 if (mediaCodec == null) { 275 if (mediaCodec == null) {
277 Logging.e(TAG, "Can not create media encoder"); 276 Logging.e(TAG, "Can not create media encoder");
278 return null; 277 return false;
279 } 278 }
280 mediaCodec.configure( 279 mediaCodec.configure(
281 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 280 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
281
282 mediaCodec.start(); 282 mediaCodec.start();
283 colorFormat = properties.colorFormat;
284 outputBuffers = mediaCodec.getOutputBuffers(); 283 outputBuffers = mediaCodec.getOutputBuffers();
285 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); 284 Logging.d(TAG, "Output buffers: " + outputBuffers.length);
286 Logging.d(TAG, "Input buffers: " + inputBuffers.length + 285
287 ". Output buffers: " + outputBuffers.length);
288 return inputBuffers;
289 } catch (IllegalStateException e) { 286 } catch (IllegalStateException e) {
290 Logging.e(TAG, "initEncode failed", e); 287 Logging.e(TAG, "initEncode failed", e);
291 return null; 288 return false;
292 } 289 }
290 return true;
293 } 291 }
294 292
295 private boolean encode( 293 ByteBuffer[] getInputBuffers() {
294 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
295 Logging.d(TAG, "Input buffers: " + inputBuffers.length);
296 return inputBuffers;
297 }
298
299 boolean encodeBuffer(
296 boolean isKeyframe, int inputBuffer, int size, 300 boolean isKeyframe, int inputBuffer, int size,
297 long presentationTimestampUs) { 301 long presentationTimestampUs) {
298 checkOnMediaCodecThread(); 302 checkOnMediaCodecThread();
299 try { 303 try {
300 if (isKeyframe) { 304 if (isKeyframe) {
301 // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could 305 // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could
302 // indicate this in queueInputBuffer() below and guarantee _this_ frame 306 // indicate this in queueInputBuffer() below and guarantee _this_ frame
303 // be encoded as a key frame, but sadly that flag is ignored. Instead, 307 // be encoded as a key frame, but sadly that flag is ignored. Instead,
304 // we request a key frame "soon". 308 // we request a key frame "soon".
305 Logging.d(TAG, "Sync frame request"); 309 Logging.d(TAG, "Sync frame request");
306 Bundle b = new Bundle(); 310 Bundle b = new Bundle();
307 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); 311 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
308 mediaCodec.setParameters(b); 312 mediaCodec.setParameters(b);
309 } 313 }
310 mediaCodec.queueInputBuffer( 314 mediaCodec.queueInputBuffer(
311 inputBuffer, 0, size, presentationTimestampUs, 0); 315 inputBuffer, 0, size, presentationTimestampUs, 0);
312 return true; 316 return true;
313 } 317 }
314 catch (IllegalStateException e) { 318 catch (IllegalStateException e) {
315 Logging.e(TAG, "encode failed", e); 319 Logging.e(TAG, "encodeBuffer failed", e);
316 return false; 320 return false;
317 } 321 }
318 } 322 }
319 323
320 private void release() { 324 void release() {
321 Logging.d(TAG, "Java releaseEncoder"); 325 Logging.d(TAG, "Java releaseEncoder");
322 checkOnMediaCodecThread(); 326 checkOnMediaCodecThread();
323 327
324 // Run Mediacodec stop() and release() on separate thread since sometime 328 // Run Mediacodec stop() and release() on separate thread since sometime
325 // Mediacodec.stop() may hang. 329 // Mediacodec.stop() may hang.
326 final CountDownLatch releaseDone = new CountDownLatch(1); 330 final CountDownLatch releaseDone = new CountDownLatch(1);
327 331
328 Runnable runMediaCodecRelease = new Runnable() { 332 Runnable runMediaCodecRelease = new Runnable() {
329 @Override 333 @Override
330 public void run() { 334 public void run() {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
367 mediaCodec.setParameters(params); 371 mediaCodec.setParameters(params);
368 return true; 372 return true;
369 } catch (IllegalStateException e) { 373 } catch (IllegalStateException e) {
370 Logging.e(TAG, "setRates failed", e); 374 Logging.e(TAG, "setRates failed", e);
371 return false; 375 return false;
372 } 376 }
373 } 377 }
374 378
375 // Dequeue an input buffer and return its index, -1 if no input buffer is 379 // Dequeue an input buffer and return its index, -1 if no input buffer is
376 // available, or -2 if the codec is no longer operative. 380 // available, or -2 if the codec is no longer operative.
377 private int dequeueInputBuffer() { 381 int dequeueInputBuffer() {
378 checkOnMediaCodecThread(); 382 checkOnMediaCodecThread();
379 try { 383 try {
380 return mediaCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT); 384 return mediaCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT);
381 } catch (IllegalStateException e) { 385 } catch (IllegalStateException e) {
382 Logging.e(TAG, "dequeueIntputBuffer failed", e); 386 Logging.e(TAG, "dequeueIntputBuffer failed", e);
383 return -2; 387 return -2;
384 } 388 }
385 } 389 }
386 390
387 // Helper struct for dequeueOutputBuffer() below. 391 // Helper struct for dequeueOutputBuffer() below.
388 private static class OutputBufferInfo { 392 static class OutputBufferInfo {
389 public OutputBufferInfo( 393 public OutputBufferInfo(
390 int index, ByteBuffer buffer, 394 int index, ByteBuffer buffer,
391 boolean isKeyFrame, long presentationTimestampUs) { 395 boolean isKeyFrame, long presentationTimestampUs) {
392 this.index = index; 396 this.index = index;
393 this.buffer = buffer; 397 this.buffer = buffer;
394 this.isKeyFrame = isKeyFrame; 398 this.isKeyFrame = isKeyFrame;
395 this.presentationTimestampUs = presentationTimestampUs; 399 this.presentationTimestampUs = presentationTimestampUs;
396 } 400 }
397 401
398 private final int index; 402 public final int index;
399 private final ByteBuffer buffer; 403 public final ByteBuffer buffer;
400 private final boolean isKeyFrame; 404 public final boolean isKeyFrame;
401 private final long presentationTimestampUs; 405 public final long presentationTimestampUs;
402 } 406 }
403 407
404 // Dequeue and return an output buffer, or null if no output is ready. Return 408 // Dequeue and return an output buffer, or null if no output is ready. Return
405 // a fake OutputBufferInfo with index -1 if the codec is no longer operable. 409 // a fake OutputBufferInfo with index -1 if the codec is no longer operable.
406 private OutputBufferInfo dequeueOutputBuffer() { 410 OutputBufferInfo dequeueOutputBuffer() {
407 checkOnMediaCodecThread(); 411 checkOnMediaCodecThread();
408 try { 412 try {
409 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 413 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
410 int result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); 414 int result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT);
411 // Check if this is config frame and save configuration data. 415 // Check if this is config frame and save configuration data.
412 if (result >= 0) { 416 if (result >= 0) {
413 boolean isConfigFrame = 417 boolean isConfigFrame =
414 (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0; 418 (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0;
415 if (isConfigFrame) { 419 if (isConfigFrame) {
416 Logging.d(TAG, "Config frame generated. Offset: " + info.offset + 420 Logging.d(TAG, "Config frame generated. Offset: " + info.offset +
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
465 } 469 }
466 throw new RuntimeException("dequeueOutputBuffer: " + result); 470 throw new RuntimeException("dequeueOutputBuffer: " + result);
467 } catch (IllegalStateException e) { 471 } catch (IllegalStateException e) {
468 Logging.e(TAG, "dequeueOutputBuffer failed", e); 472 Logging.e(TAG, "dequeueOutputBuffer failed", e);
469 return new OutputBufferInfo(-1, null, false, -1); 473 return new OutputBufferInfo(-1, null, false, -1);
470 } 474 }
471 } 475 }
472 476
473 // Release a dequeued output buffer back to the codec for re-use. Return 477 // Release a dequeued output buffer back to the codec for re-use. Return
474 // false if the codec is no longer operable. 478 // false if the codec is no longer operable.
475 private boolean releaseOutputBuffer(int index) { 479 boolean releaseOutputBuffer(int index) {
476 checkOnMediaCodecThread(); 480 checkOnMediaCodecThread();
477 try { 481 try {
478 mediaCodec.releaseOutputBuffer(index, false); 482 mediaCodec.releaseOutputBuffer(index, false);
479 return true; 483 return true;
480 } catch (IllegalStateException e) { 484 } catch (IllegalStateException e) {
481 Logging.e(TAG, "releaseOutputBuffer failed", e); 485 Logging.e(TAG, "releaseOutputBuffer failed", e);
482 return false; 486 return false;
483 } 487 }
484 } 488 }
485 } 489 }
OLDNEW
« no previous file with comments | « talk/app/webrtc/java/jni/androidmediaencoder_jni.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698