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

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

Issue 1403713002: MediaCodecVideoEncoder add support to encode from textures (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: 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 15 matching lines...) Expand all
26 */ 26 */
27 27
28 28
29 package org.webrtc; 29 package org.webrtc;
30 30
31 import android.media.MediaCodec; 31 import android.media.MediaCodec;
32 import android.media.MediaCodecInfo.CodecCapabilities; 32 import android.media.MediaCodecInfo.CodecCapabilities;
33 import android.media.MediaCodecInfo; 33 import android.media.MediaCodecInfo;
34 import android.media.MediaCodecList; 34 import android.media.MediaCodecList;
35 import android.media.MediaFormat; 35 import android.media.MediaFormat;
36 import android.opengl.EGLContext;
37 import android.opengl.GLES20;
36 import android.os.Build; 38 import android.os.Build;
37 import android.os.Bundle; 39 import android.os.Bundle;
40 import android.view.Surface;
38 41
39 import org.webrtc.Logging; 42 import org.webrtc.Logging;
40 43
41 import java.nio.ByteBuffer; 44 import java.nio.ByteBuffer;
42 import java.util.Arrays; 45 import java.util.Arrays;
43 import java.util.List; 46 import java.util.List;
44 47
45 // Java-side of peerconnection_jni.cc:MediaCodecVideoEncoder. 48 // Java-side of peerconnection_jni.cc:MediaCodecVideoEncoder.
46 // This class is an implementation detail of the Java PeerConnection API. 49 // This class is an implementation detail of the Java PeerConnection API.
47 // MediaCodec is thread-hostile so this class must be operated on a single 50 // MediaCodec is thread-hostile so this class must be operated on a single
(...skipping 10 matching lines...) Expand all
58 public enum VideoCodecType { 61 public enum VideoCodecType {
59 VIDEO_CODEC_VP8, 62 VIDEO_CODEC_VP8,
60 VIDEO_CODEC_VP9, 63 VIDEO_CODEC_VP9,
61 VIDEO_CODEC_H264 64 VIDEO_CODEC_H264
62 } 65 }
63 66
64 private static final int DEQUEUE_TIMEOUT = 0; // Non-blocking, no wait. 67 private static final int DEQUEUE_TIMEOUT = 0; // Non-blocking, no wait.
65 private Thread mediaCodecThread; 68 private Thread mediaCodecThread;
66 private MediaCodec mediaCodec; 69 private MediaCodec mediaCodec;
67 private ByteBuffer[] outputBuffers; 70 private ByteBuffer[] outputBuffers;
71 private EglBase eglBase;
72 private Surface inputSurface;
73 private GlRectDrawer drawer;
68 private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8"; 74 private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8";
69 private static final String H264_MIME_TYPE = "video/avc"; 75 private static final String H264_MIME_TYPE = "video/avc";
70 // List of supported HW VP8 codecs. 76 // List of supported HW VP8 codecs.
71 private static final String[] supportedVp8HwCodecPrefixes = 77 private static final String[] supportedVp8HwCodecPrefixes =
72 {"OMX.qcom.", "OMX.Intel." }; 78 {"OMX.qcom.", "OMX.Intel." };
73 // List of supported HW H.264 codecs. 79 // List of supported HW H.264 codecs.
74 private static final String[] supportedH264HwCodecPrefixes = 80 private static final String[] supportedH264HwCodecPrefixes =
75 {"OMX.qcom." }; 81 {"OMX.qcom." };
76 // List of devices with poor H.264 encoder quality. 82 // List of devices with poor H.264 encoder quality.
77 private static final String[] H264_HW_EXCEPTION_MODELS = new String[] { 83 private static final String[] H264_HW_EXCEPTION_MODELS = new String[] {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
110 private static class EncoderProperties { 116 private static class EncoderProperties {
111 public EncoderProperties(String codecName, int colorFormat) { 117 public EncoderProperties(String codecName, int colorFormat) {
112 this.codecName = codecName; 118 this.codecName = codecName;
113 this.colorFormat = colorFormat; 119 this.colorFormat = colorFormat;
114 } 120 }
115 public final String codecName; // OpenMax component name for HW codec. 121 public final String codecName; // OpenMax component name for HW codec.
116 public final int colorFormat; // Color format supported by codec. 122 public final int colorFormat; // Color format supported by codec.
117 } 123 }
118 124
119 private static EncoderProperties findHwEncoder( 125 private static EncoderProperties findHwEncoder(
120 String mime, String[] supportedHwCodecPrefixes) { 126 String mime, String[] supportedHwCodecPrefixes, boolean useSurface) {
121 // MediaCodec.setParameters is missing for JB and below, so bitrate 127 // MediaCodec.setParameters is missing for JB and below, so bitrate
122 // can not be adjusted dynamically. 128 // can not be adjusted dynamically.
123 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 129 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
124 return null; 130 return null;
125 } 131 }
126 132
127 // Check if device is in H.264 exception list. 133 // Check if device is in H.264 exception list.
128 if (mime.equals(H264_MIME_TYPE)) { 134 if (mime.equals(H264_MIME_TYPE)) {
129 List<String> exceptionModels = Arrays.asList(H264_HW_EXCEPTION_MODELS); 135 List<String> exceptionModels = Arrays.asList(H264_HW_EXCEPTION_MODELS);
130 if (exceptionModels.contains(Build.MODEL)) { 136 if (exceptionModels.contains(Build.MODEL)) {
(...skipping 30 matching lines...) Expand all
161 } 167 }
162 if (!supportedCodec) { 168 if (!supportedCodec) {
163 continue; 169 continue;
164 } 170 }
165 171
166 CodecCapabilities capabilities = info.getCapabilitiesForType(mime); 172 CodecCapabilities capabilities = info.getCapabilitiesForType(mime);
167 for (int colorFormat : capabilities.colorFormats) { 173 for (int colorFormat : capabilities.colorFormats) {
168 Logging.v(TAG, " Color: 0x" + Integer.toHexString(colorFormat)); 174 Logging.v(TAG, " Color: 0x" + Integer.toHexString(colorFormat));
169 } 175 }
170 176
177 if (useSurface) {
178 for (int codecColorFormat : capabilities.colorFormats) {
179 if (codecColorFormat == CodecCapabilities.COLOR_FormatSurface) {
180 // Found supported HW encoder.
181 Logging.d(TAG, "Found target encoder for mime " + mime + " : " + nam e +
182 ". Using Surface as input");
183 return new EncoderProperties(name, codecColorFormat);
184 }
185 }
186 return null;
187 }
188
171 // Check if codec supports either yuv420 or nv12. 189 // Check if codec supports either yuv420 or nv12.
172 for (int supportedColorFormat : supportedColorList) { 190 for (int supportedColorFormat : supportedColorList) {
173 for (int codecColorFormat : capabilities.colorFormats) { 191 for (int codecColorFormat : capabilities.colorFormats) {
174 if (codecColorFormat == supportedColorFormat) { 192 if (codecColorFormat == supportedColorFormat) {
175 // Found supported HW encoder. 193 // Found supported HW encoder.
176 Logging.d(TAG, "Found target encoder for mime " + mime + " : " + nam e + 194 Logging.d(TAG, "Found target encoder for mime " + mime + " : " + nam e +
177 ". Color: 0x" + Integer.toHexString(codecColorFormat)); 195 ". Color: 0x" + Integer.toHexString(codecColorFormat));
178 return new EncoderProperties(name, codecColorFormat); 196 return new EncoderProperties(name, codecColorFormat);
179 } 197 }
180 } 198 }
181 } 199 }
182 } 200 }
183 return null; // No HW VP8 encoder. 201 return null; // No HW VP8 encoder.
184 } 202 }
185 203
186 public static boolean isVp8HwSupported() { 204 public static boolean isVp8HwSupported() {
187 return findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes) != null; 205 return findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes, false) != n ull;
188 } 206 }
189 207
190 public static boolean isH264HwSupported() { 208 public static boolean isH264HwSupported() {
191 return findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes) != null; 209 return findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes, false) != null;
210 }
211
212 public static boolean isVp8HwSupportedUsingTextures() {
AlexG 2015/10/14 23:48:27 Are these 2 functions called from native code or f
perkj_webrtc 2015/11/16 13:08:51 they are here so that an app can ask if the hw cod
213 return findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes, true) != nu ll;
214 }
215
216 public static boolean isH264HwSupportedUsingTextures() {
217 return findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes, true) != null;
192 } 218 }
193 219
194 private void checkOnMediaCodecThread() { 220 private void checkOnMediaCodecThread() {
195 if (mediaCodecThread.getId() != Thread.currentThread().getId()) { 221 if (mediaCodecThread.getId() != Thread.currentThread().getId()) {
196 throw new RuntimeException( 222 throw new RuntimeException(
197 "MediaCodecVideoEncoder previously operated on " + mediaCodecThread + 223 "MediaCodecVideoEncoder previously operated on " + mediaCodecThread +
198 " but is now called on " + Thread.currentThread()); 224 " but is now called on " + Thread.currentThread());
199 } 225 }
200 } 226 }
201 227
202 static MediaCodec createByCodecName(String codecName) { 228 static MediaCodec createByCodecName(String codecName) {
203 try { 229 try {
204 // In the L-SDK this call can throw IOException so in order to work in 230 // In the L-SDK this call can throw IOException so in order to work in
205 // both cases catch an exception. 231 // both cases catch an exception.
206 return MediaCodec.createByCodecName(codecName); 232 return MediaCodec.createByCodecName(codecName);
207 } catch (Exception e) { 233 } catch (Exception e) {
208 return null; 234 return null;
209 } 235 }
210 } 236 }
211 237
212 // Returns false if the hardware encoder currently can't be used. 238 boolean initEncode(VideoCodecType type, int width, int height, int kbps, int f ps,
213 boolean initEncode(VideoCodecType type, int width, int height, int kbps, int f ps) { 239 EGLContext sharedContext) {
240 final boolean useSurface = sharedContext != null;
214 Logging.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height + 241 Logging.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height +
215 ". @ " + kbps + " kbps. Fps: " + fps + "."); 242 ". @ " + kbps + " kbps. Fps: " + fps + ". Encode from texture : " +
243 (useSurface ? "True" : "False"));
216 244
217 if (mediaCodecThread != null) { 245 if (mediaCodecThread != null) {
218 throw new RuntimeException("Forgot to release()?"); 246 throw new RuntimeException("Forgot to release()?");
219 } 247 }
220 EncoderProperties properties = null; 248 EncoderProperties properties = null;
221 String mime = null; 249 String mime = null;
222 int keyFrameIntervalSec = 0; 250 int keyFrameIntervalSec = 0;
223 if (type == VideoCodecType.VIDEO_CODEC_VP8) { 251 if (type == VideoCodecType.VIDEO_CODEC_VP8) {
224 mime = VP8_MIME_TYPE; 252 mime = VP8_MIME_TYPE;
225 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes); 253 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes, use Surface);
226 keyFrameIntervalSec = 100; 254 keyFrameIntervalSec = 100;
227 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { 255 } else if (type == VideoCodecType.VIDEO_CODEC_H264) {
228 mime = H264_MIME_TYPE; 256 mime = H264_MIME_TYPE;
229 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes); 257 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes, u seSurface);
230 keyFrameIntervalSec = 20; 258 keyFrameIntervalSec = 20;
231 } 259 }
232 if (properties == null) { 260 if (properties == null) {
233 throw new RuntimeException("Can not find HW encoder for " + type); 261 throw new RuntimeException("Can not find HW encoder for " + type);
234 } 262 }
235 colorFormat = properties.colorFormat; 263 colorFormat = properties.colorFormat;
236 mediaCodecThread = Thread.currentThread(); 264 mediaCodecThread = Thread.currentThread();
237 try { 265 try {
238 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); 266 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
239 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps); 267 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps);
240 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); 268 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant);
241 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); 269 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat);
242 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); 270 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps);
243 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); 271 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec);
244 Logging.d(TAG, " Format: " + format); 272 Logging.d(TAG, " Format: " + format);
245 mediaCodec = createByCodecName(properties.codecName); 273 mediaCodec = createByCodecName(properties.codecName);
246 this.type = type; 274 this.type = type;
247 if (mediaCodec == null) { 275 if (mediaCodec == null) {
248 return false; 276 return false;
249 } 277 }
250 mediaCodec.configure( 278 mediaCodec.configure(
251 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 279 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
252 280
281 if (useSurface) {
282 eglBase = new EglBase(sharedContext, EglBase.ConfigType.RECORDABLE);
283 // Create an input surface and keep a reference since we must release th e surface when done.
284 inputSurface = mediaCodec.createInputSurface();
285 eglBase.createSurface(inputSurface);
286 drawer = new GlRectDrawer();
287 }
253 mediaCodec.start(); 288 mediaCodec.start();
254 outputBuffers = mediaCodec.getOutputBuffers(); 289 outputBuffers = mediaCodec.getOutputBuffers();
255 290
256 } catch (IllegalStateException e) { 291 } catch (IllegalStateException e) {
257 Logging.e(TAG, "initEncode failed", e); 292 Logging.e(TAG, "initEncode failed", e);
258 return false; 293 return false;
259 } 294 }
260 return true; 295 return true;
261 } 296 }
262 297
(...skipping 21 matching lines...) Expand all
284 mediaCodec.queueInputBuffer( 319 mediaCodec.queueInputBuffer(
285 inputBuffer, 0, size, presentationTimestampUs, 0); 320 inputBuffer, 0, size, presentationTimestampUs, 0);
286 return true; 321 return true;
287 } 322 }
288 catch (IllegalStateException e) { 323 catch (IllegalStateException e) {
289 Logging.e(TAG, "encodeBuffer failed", e); 324 Logging.e(TAG, "encodeBuffer failed", e);
290 return false; 325 return false;
291 } 326 }
292 } 327 }
293 328
329 boolean encodeTexture(boolean isKeyframe, int oesTextureId, float[] transforma tionMatrix,
330 long presentationTimestampUs) {
331 checkOnMediaCodecThread();
332 try {
333 if (isKeyframe) {
334 Logging.d(TAG, "Sync frame request");
335 Bundle b = new Bundle();
336 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
337 mediaCodec.setParameters(b);
338 }
339 eglBase.makeCurrent();
340 drawer.drawOes(oesTextureId, transformationMatrix);
341 eglBase.swapBuffers(presentationTimestampUs * 1000);
342 return true;
343 }
344 catch (RuntimeException e) {
345 Logging.e(TAG, "encodeTexture failed", e);
346 return false;
347 }
348 }
349
294 void release() { 350 void release() {
295 Logging.d(TAG, "Java releaseEncoder"); 351 Logging.d(TAG, "Java releaseEncoder");
296 checkOnMediaCodecThread(); 352 checkOnMediaCodecThread();
297 try { 353 try {
298 mediaCodec.stop(); 354 mediaCodec.stop();
299 mediaCodec.release(); 355 mediaCodec.release();
300 } catch (IllegalStateException e) { 356 } catch (IllegalStateException e) {
301 Logging.e(TAG, "release failed", e); 357 Logging.e(TAG, "release failed", e);
302 } 358 }
303 mediaCodec = null; 359 mediaCodec = null;
304 mediaCodecThread = null; 360 mediaCodecThread = null;
361 if (drawer != null) {
362 drawer.release();
AlexG 2015/10/14 23:48:27 nit: drawer = null and same for 2 checks below?
perkj_webrtc 2015/11/16 13:08:51 Done.
363 }
364 if (eglBase != null) {
365 eglBase.release();
366 }
367 if (inputSurface != null) {
368 inputSurface.release();
369 }
305 } 370 }
306 371
307 private boolean setRates(int kbps, int frameRateIgnored) { 372 private boolean setRates(int kbps, int frameRateIgnored) {
308 // frameRate argument is ignored - HW encoder is supposed to use 373 // frameRate argument is ignored - HW encoder is supposed to use
309 // video frame timestamps for bit allocation. 374 // video frame timestamps for bit allocation.
310 checkOnMediaCodecThread(); 375 checkOnMediaCodecThread();
311 Logging.v(TAG, "setRates: " + kbps + " kbps. Fps: " + frameRateIgnored); 376 Logging.v(TAG, "setRates: " + kbps + " kbps. Fps: " + frameRateIgnored);
312 try { 377 try {
313 Bundle params = new Bundle(); 378 Bundle params = new Bundle();
314 params.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, 1000 * kbps); 379 params.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, 1000 * kbps);
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
424 checkOnMediaCodecThread(); 489 checkOnMediaCodecThread();
425 try { 490 try {
426 mediaCodec.releaseOutputBuffer(index, false); 491 mediaCodec.releaseOutputBuffer(index, false);
427 return true; 492 return true;
428 } catch (IllegalStateException e) { 493 } catch (IllegalStateException e) {
429 Logging.e(TAG, "releaseOutputBuffer failed", e); 494 Logging.e(TAG, "releaseOutputBuffer failed", e);
430 return false; 495 return false;
431 } 496 }
432 } 497 }
433 } 498 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698