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

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: Rebased Created 5 years, 2 months 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
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 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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698