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 14 matching lines...) Expand all Loading... | |
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 */ | 26 */ |
27 | 27 |
28 package org.webrtc; | 28 package org.webrtc; |
29 | 29 |
30 import android.media.MediaCodec; | 30 import android.media.MediaCodec; |
31 import android.media.MediaCodecInfo.CodecCapabilities; | 31 import android.media.MediaCodecInfo.CodecCapabilities; |
32 import android.media.MediaCodecInfo; | 32 import android.media.MediaCodecInfo; |
33 import android.media.MediaCodecList; | 33 import android.media.MediaCodecList; |
34 import android.media.MediaFormat; | 34 import android.media.MediaFormat; |
35 import android.opengl.GLES20; | |
35 import android.os.Build; | 36 import android.os.Build; |
36 import android.os.Bundle; | 37 import android.os.Bundle; |
38 import android.view.Surface; | |
37 | 39 |
38 import org.webrtc.Logging; | 40 import org.webrtc.Logging; |
39 | 41 |
40 import java.nio.ByteBuffer; | 42 import java.nio.ByteBuffer; |
41 import java.util.Arrays; | 43 import java.util.Arrays; |
42 import java.util.List; | 44 import java.util.List; |
43 import java.util.concurrent.CountDownLatch; | 45 import java.util.concurrent.CountDownLatch; |
44 | 46 |
47 import javax.microedition.khronos.egl.EGLContext; | |
48 | |
45 // Java-side of peerconnection_jni.cc:MediaCodecVideoEncoder. | 49 // Java-side of peerconnection_jni.cc:MediaCodecVideoEncoder. |
46 // This class is an implementation detail of the Java PeerConnection API. | 50 // This class is an implementation detail of the Java PeerConnection API. |
47 public class MediaCodecVideoEncoder { | 51 public class MediaCodecVideoEncoder { |
48 // This class is constructed, operated, and destroyed by its C++ incarnation, | 52 // This class is constructed, operated, and destroyed by its C++ incarnation, |
49 // so the class and its methods have non-public visibility. The API this | 53 // so the class and its methods have non-public visibility. The API this |
50 // class exposes aims to mimic the webrtc::VideoEncoder API as closely as | 54 // class exposes aims to mimic the webrtc::VideoEncoder API as closely as |
51 // possibly to minimize the amount of translation work necessary. | 55 // possibly to minimize the amount of translation work necessary. |
52 | 56 |
53 private static final String TAG = "MediaCodecVideoEncoder"; | 57 private static final String TAG = "MediaCodecVideoEncoder"; |
54 | 58 |
55 // Tracks webrtc::VideoCodecType. | 59 // Tracks webrtc::VideoCodecType. |
56 public enum VideoCodecType { | 60 public enum VideoCodecType { |
57 VIDEO_CODEC_VP8, | 61 VIDEO_CODEC_VP8, |
58 VIDEO_CODEC_VP9, | 62 VIDEO_CODEC_VP9, |
59 VIDEO_CODEC_H264 | 63 VIDEO_CODEC_H264 |
60 } | 64 } |
61 | 65 |
62 private static final int MEDIA_CODEC_RELEASE_TIMEOUT_MS = 5000; // Timeout for codec releasing. | 66 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. | 67 private static final int DEQUEUE_TIMEOUT = 0; // Non-blocking, no wait. |
64 // Active running encoder instance. Set in initEncode() (called from native co de) | 68 // Active running encoder instance. Set in initEncode() (called from native co de) |
65 // and reset to null in release() call. | 69 // and reset to null in release() call. |
66 private static MediaCodecVideoEncoder runningInstance = null; | 70 private static MediaCodecVideoEncoder runningInstance = null; |
67 private static MediaCodecVideoEncoderErrorCallback errorCallback = null; | 71 private static MediaCodecVideoEncoderErrorCallback errorCallback = null; |
68 private static int codecErrors = 0; | 72 private static int codecErrors = 0; |
69 | 73 |
70 private Thread mediaCodecThread; | 74 private Thread mediaCodecThread; |
71 private MediaCodec mediaCodec; | 75 private MediaCodec mediaCodec; |
72 private ByteBuffer[] outputBuffers; | 76 private ByteBuffer[] outputBuffers; |
77 private EglBase eglBase; | |
78 private Surface inputSurface; | |
79 private GlRectDrawer drawer; | |
73 private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8"; | 80 private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8"; |
74 private static final String H264_MIME_TYPE = "video/avc"; | 81 private static final String H264_MIME_TYPE = "video/avc"; |
75 // List of supported HW VP8 codecs. | 82 // List of supported HW VP8 codecs. |
76 private static final String[] supportedVp8HwCodecPrefixes = | 83 private static final String[] supportedVp8HwCodecPrefixes = |
77 {"OMX.qcom.", "OMX.Intel." }; | 84 {"OMX.qcom.", "OMX.Intel." }; |
78 // List of supported HW H.264 codecs. | 85 // List of supported HW H.264 codecs. |
79 private static final String[] supportedH264HwCodecPrefixes = | 86 private static final String[] supportedH264HwCodecPrefixes = |
80 {"OMX.qcom." }; | 87 {"OMX.qcom." }; |
81 // List of devices with poor H.264 encoder quality. | 88 // List of devices with poor H.264 encoder quality. |
82 private static final String[] H264_HW_EXCEPTION_MODELS = new String[] { | 89 private static final String[] H264_HW_EXCEPTION_MODELS = new String[] { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
124 private static class EncoderProperties { | 131 private static class EncoderProperties { |
125 public EncoderProperties(String codecName, int colorFormat) { | 132 public EncoderProperties(String codecName, int colorFormat) { |
126 this.codecName = codecName; | 133 this.codecName = codecName; |
127 this.colorFormat = colorFormat; | 134 this.colorFormat = colorFormat; |
128 } | 135 } |
129 public final String codecName; // OpenMax component name for HW codec. | 136 public final String codecName; // OpenMax component name for HW codec. |
130 public final int colorFormat; // Color format supported by codec. | 137 public final int colorFormat; // Color format supported by codec. |
131 } | 138 } |
132 | 139 |
133 private static EncoderProperties findHwEncoder( | 140 private static EncoderProperties findHwEncoder( |
134 String mime, String[] supportedHwCodecPrefixes) { | 141 String mime, String[] supportedHwCodecPrefixes, boolean useSurface) { |
135 // MediaCodec.setParameters is missing for JB and below, so bitrate | 142 // MediaCodec.setParameters is missing for JB and below, so bitrate |
136 // can not be adjusted dynamically. | 143 // can not be adjusted dynamically. |
137 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { | 144 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { |
138 return null; | 145 return null; |
139 } | 146 } |
140 | 147 |
141 // Check if device is in H.264 exception list. | 148 // Check if device is in H.264 exception list. |
142 if (mime.equals(H264_MIME_TYPE)) { | 149 if (mime.equals(H264_MIME_TYPE)) { |
143 List<String> exceptionModels = Arrays.asList(H264_HW_EXCEPTION_MODELS); | 150 List<String> exceptionModels = Arrays.asList(H264_HW_EXCEPTION_MODELS); |
144 if (exceptionModels.contains(Build.MODEL)) { | 151 if (exceptionModels.contains(Build.MODEL)) { |
(...skipping 29 matching lines...) Expand all Loading... | |
174 } | 181 } |
175 if (!supportedCodec) { | 182 if (!supportedCodec) { |
176 continue; | 183 continue; |
177 } | 184 } |
178 | 185 |
179 CodecCapabilities capabilities = info.getCapabilitiesForType(mime); | 186 CodecCapabilities capabilities = info.getCapabilitiesForType(mime); |
180 for (int colorFormat : capabilities.colorFormats) { | 187 for (int colorFormat : capabilities.colorFormats) { |
181 Logging.v(TAG, " Color: 0x" + Integer.toHexString(colorFormat)); | 188 Logging.v(TAG, " Color: 0x" + Integer.toHexString(colorFormat)); |
182 } | 189 } |
183 | 190 |
191 if (useSurface) { | |
192 for (int codecColorFormat : capabilities.colorFormats) { | |
magjed_webrtc
2015/11/18 13:12:52
This looks like duplicated code. I suggest replaci
perkj_webrtc
2015/11/18 14:51:21
nice. yes
| |
193 if (codecColorFormat == CodecCapabilities.COLOR_FormatSurface) { | |
194 // Found supported HW encoder. | |
195 Logging.d(TAG, "Found target encoder for mime " + mime + " : " + nam e + | |
196 ". Using Surface as input"); | |
197 return new EncoderProperties(name, codecColorFormat); | |
198 } | |
199 } | |
200 return null; | |
201 } | |
202 | |
184 // Check if codec supports either yuv420 or nv12. | 203 // Check if codec supports either yuv420 or nv12. |
185 for (int supportedColorFormat : supportedColorList) { | 204 for (int supportedColorFormat : supportedColorList) { |
186 for (int codecColorFormat : capabilities.colorFormats) { | 205 for (int codecColorFormat : capabilities.colorFormats) { |
187 if (codecColorFormat == supportedColorFormat) { | 206 if (codecColorFormat == supportedColorFormat) { |
188 // Found supported HW encoder. | 207 // Found supported HW encoder. |
189 Logging.d(TAG, "Found target encoder for mime " + mime + " : " + nam e + | 208 Logging.d(TAG, "Found target encoder for mime " + mime + " : " + nam e + |
190 ". Color: 0x" + Integer.toHexString(codecColorFormat)); | 209 ". Color: 0x" + Integer.toHexString(codecColorFormat)); |
191 return new EncoderProperties(name, codecColorFormat); | 210 return new EncoderProperties(name, codecColorFormat); |
192 } | 211 } |
193 } | 212 } |
194 } | 213 } |
195 } | 214 } |
196 return null; // No HW VP8 encoder. | 215 return null; // No HW VP8 encoder. |
197 } | 216 } |
198 | 217 |
199 public static boolean isVp8HwSupported() { | 218 public static boolean isVp8HwSupported() { |
200 return findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes) != null; | 219 return findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes, false) != n ull; |
201 } | 220 } |
202 | 221 |
203 public static boolean isH264HwSupported() { | 222 public static boolean isH264HwSupported() { |
204 return findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes) != null; | 223 return findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes, false) != null; |
224 } | |
225 | |
226 public static boolean isVp8HwSupportedUsingTextures() { | |
227 return findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes, true) != nu ll; | |
228 } | |
229 | |
230 public static boolean isH264HwSupportedUsingTextures() { | |
231 return findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes, true) != null; | |
205 } | 232 } |
206 | 233 |
207 private void checkOnMediaCodecThread() { | 234 private void checkOnMediaCodecThread() { |
208 if (mediaCodecThread.getId() != Thread.currentThread().getId()) { | 235 if (mediaCodecThread.getId() != Thread.currentThread().getId()) { |
209 throw new RuntimeException( | 236 throw new RuntimeException( |
210 "MediaCodecVideoEncoder previously operated on " + mediaCodecThread + | 237 "MediaCodecVideoEncoder previously operated on " + mediaCodecThread + |
211 " but is now called on " + Thread.currentThread()); | 238 " but is now called on " + Thread.currentThread()); |
212 } | 239 } |
213 } | 240 } |
214 | 241 |
(...skipping 12 matching lines...) Expand all Loading... | |
227 static MediaCodec createByCodecName(String codecName) { | 254 static MediaCodec createByCodecName(String codecName) { |
228 try { | 255 try { |
229 // In the L-SDK this call can throw IOException so in order to work in | 256 // In the L-SDK this call can throw IOException so in order to work in |
230 // both cases catch an exception. | 257 // both cases catch an exception. |
231 return MediaCodec.createByCodecName(codecName); | 258 return MediaCodec.createByCodecName(codecName); |
232 } catch (Exception e) { | 259 } catch (Exception e) { |
233 return null; | 260 return null; |
234 } | 261 } |
235 } | 262 } |
236 | 263 |
237 // Returns false if the hardware encoder currently can't be used. | 264 boolean initEncode(VideoCodecType type, int width, int height, int kbps, int f ps, |
238 boolean initEncode(VideoCodecType type, int width, int height, int kbps, int f ps) { | 265 EGLContext sharedContext) { |
266 final boolean useSurface = sharedContext != null; | |
239 Logging.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height + | 267 Logging.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height + |
240 ". @ " + kbps + " kbps. Fps: " + fps + "."); | 268 ". @ " + kbps + " kbps. Fps: " + fps + ". Encode from texture : " + |
269 (useSurface ? "True" : "False")); | |
magjed_webrtc
2015/11/18 13:12:52
Just print useSurface, you don't need to convert i
perkj_webrtc
2015/11/18 14:51:21
Done.
| |
241 | 270 |
242 if (mediaCodecThread != null) { | 271 if (mediaCodecThread != null) { |
243 throw new RuntimeException("Forgot to release()?"); | 272 throw new RuntimeException("Forgot to release()?"); |
244 } | 273 } |
245 EncoderProperties properties = null; | 274 EncoderProperties properties = null; |
246 String mime = null; | 275 String mime = null; |
247 int keyFrameIntervalSec = 0; | 276 int keyFrameIntervalSec = 0; |
248 if (type == VideoCodecType.VIDEO_CODEC_VP8) { | 277 if (type == VideoCodecType.VIDEO_CODEC_VP8) { |
249 mime = VP8_MIME_TYPE; | 278 mime = VP8_MIME_TYPE; |
250 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes); | 279 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes, use Surface); |
251 keyFrameIntervalSec = 100; | 280 keyFrameIntervalSec = 100; |
252 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { | 281 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { |
253 mime = H264_MIME_TYPE; | 282 mime = H264_MIME_TYPE; |
254 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes); | 283 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes, u seSurface); |
255 keyFrameIntervalSec = 20; | 284 keyFrameIntervalSec = 20; |
256 } | 285 } |
257 if (properties == null) { | 286 if (properties == null) { |
258 throw new RuntimeException("Can not find HW encoder for " + type); | 287 throw new RuntimeException("Can not find HW encoder for " + type); |
259 } | 288 } |
260 runningInstance = this; // Encoder is now running and can be queried for sta ck traces. | 289 runningInstance = this; // Encoder is now running and can be queried for sta ck traces. |
261 colorFormat = properties.colorFormat; | 290 colorFormat = properties.colorFormat; |
262 Logging.d(TAG, "Color format: " + colorFormat); | 291 Logging.d(TAG, "Color format: " + colorFormat); |
263 | 292 |
264 mediaCodecThread = Thread.currentThread(); | 293 mediaCodecThread = Thread.currentThread(); |
265 try { | 294 try { |
266 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); | 295 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); |
267 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps); | 296 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps); |
268 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); | 297 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); |
269 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); | 298 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); |
270 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); | 299 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); |
271 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); | 300 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); |
272 Logging.d(TAG, " Format: " + format); | 301 Logging.d(TAG, " Format: " + format); |
273 mediaCodec = createByCodecName(properties.codecName); | 302 mediaCodec = createByCodecName(properties.codecName); |
274 this.type = type; | 303 this.type = type; |
275 if (mediaCodec == null) { | 304 if (mediaCodec == null) { |
276 Logging.e(TAG, "Can not create media encoder"); | 305 Logging.e(TAG, "Can not create media encoder"); |
277 return false; | 306 return false; |
278 } | 307 } |
279 mediaCodec.configure( | 308 mediaCodec.configure( |
280 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); | 309 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); |
281 | 310 |
311 if (useSurface) { | |
312 eglBase = new EglBase(sharedContext, EglBase.ConfigType.RECORDABLE); | |
313 // Create an input surface and keep a reference since we must release th e surface when done. | |
314 inputSurface = mediaCodec.createInputSurface(); | |
315 eglBase.createSurface(inputSurface); | |
316 drawer = new GlRectDrawer(); | |
317 } | |
282 mediaCodec.start(); | 318 mediaCodec.start(); |
283 outputBuffers = mediaCodec.getOutputBuffers(); | 319 outputBuffers = mediaCodec.getOutputBuffers(); |
284 Logging.d(TAG, "Output buffers: " + outputBuffers.length); | 320 Logging.d(TAG, "Output buffers: " + outputBuffers.length); |
285 | 321 |
286 } catch (IllegalStateException e) { | 322 } catch (IllegalStateException e) { |
287 Logging.e(TAG, "initEncode failed", e); | 323 Logging.e(TAG, "initEncode failed", e); |
288 return false; | 324 return false; |
289 } | 325 } |
290 return true; | 326 return true; |
291 } | 327 } |
(...skipping 22 matching lines...) Expand all Loading... | |
314 mediaCodec.queueInputBuffer( | 350 mediaCodec.queueInputBuffer( |
315 inputBuffer, 0, size, presentationTimestampUs, 0); | 351 inputBuffer, 0, size, presentationTimestampUs, 0); |
316 return true; | 352 return true; |
317 } | 353 } |
318 catch (IllegalStateException e) { | 354 catch (IllegalStateException e) { |
319 Logging.e(TAG, "encodeBuffer failed", e); | 355 Logging.e(TAG, "encodeBuffer failed", e); |
320 return false; | 356 return false; |
321 } | 357 } |
322 } | 358 } |
323 | 359 |
360 boolean encodeTexture(boolean isKeyframe, int oesTextureId, float[] transforma tionMatrix, | |
361 long presentationTimestampUs) { | |
362 checkOnMediaCodecThread(); | |
363 try { | |
364 if (isKeyframe) { | |
365 Logging.d(TAG, "Sync frame request"); | |
366 Bundle b = new Bundle(); | |
367 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); | |
368 mediaCodec.setParameters(b); | |
369 } | |
370 eglBase.makeCurrent(); | |
371 drawer.drawOes(oesTextureId, transformationMatrix); | |
372 // TODO(perkj): Do we have to call EGLExt.eglPresentationTimeANDROID ? | |
373 // If not, remove |presentationTimestampUs|. | |
374 eglBase.swapBuffers(); | |
375 return true; | |
376 } | |
377 catch (RuntimeException e) { | |
378 Logging.e(TAG, "encodeTexture failed", e); | |
379 return false; | |
380 } | |
381 } | |
382 | |
324 void release() { | 383 void release() { |
325 Logging.d(TAG, "Java releaseEncoder"); | 384 Logging.d(TAG, "Java releaseEncoder"); |
326 checkOnMediaCodecThread(); | 385 checkOnMediaCodecThread(); |
327 | 386 |
328 // Run Mediacodec stop() and release() on separate thread since sometime | 387 // Run Mediacodec stop() and release() on separate thread since sometime |
329 // Mediacodec.stop() may hang. | 388 // Mediacodec.stop() may hang. |
330 final CountDownLatch releaseDone = new CountDownLatch(1); | 389 final CountDownLatch releaseDone = new CountDownLatch(1); |
331 | 390 |
332 Runnable runMediaCodecRelease = new Runnable() { | 391 Runnable runMediaCodecRelease = new Runnable() { |
333 @Override | 392 @Override |
(...skipping 15 matching lines...) Expand all Loading... | |
349 Logging.e(TAG, "Media encoder release timeout"); | 408 Logging.e(TAG, "Media encoder release timeout"); |
350 codecErrors++; | 409 codecErrors++; |
351 if (errorCallback != null) { | 410 if (errorCallback != null) { |
352 Logging.e(TAG, "Invoke codec error callback. Errors: " + codecErrors); | 411 Logging.e(TAG, "Invoke codec error callback. Errors: " + codecErrors); |
353 errorCallback.onMediaCodecVideoEncoderCriticalError(codecErrors); | 412 errorCallback.onMediaCodecVideoEncoderCriticalError(codecErrors); |
354 } | 413 } |
355 } | 414 } |
356 | 415 |
357 mediaCodec = null; | 416 mediaCodec = null; |
358 mediaCodecThread = null; | 417 mediaCodecThread = null; |
418 if (drawer != null) { | |
419 drawer.release(); | |
420 drawer = null; | |
421 } | |
422 if (eglBase != null) { | |
423 eglBase.release(); | |
424 eglBase = null; | |
425 } | |
426 if (inputSurface != null) { | |
427 inputSurface.release(); | |
428 inputSurface = null; | |
429 } | |
359 runningInstance = null; | 430 runningInstance = null; |
360 Logging.d(TAG, "Java releaseEncoder done"); | 431 Logging.d(TAG, "Java releaseEncoder done"); |
361 } | 432 } |
362 | 433 |
363 private boolean setRates(int kbps, int frameRateIgnored) { | 434 private boolean setRates(int kbps, int frameRateIgnored) { |
364 // frameRate argument is ignored - HW encoder is supposed to use | 435 // frameRate argument is ignored - HW encoder is supposed to use |
365 // video frame timestamps for bit allocation. | 436 // video frame timestamps for bit allocation. |
366 checkOnMediaCodecThread(); | 437 checkOnMediaCodecThread(); |
367 Logging.v(TAG, "setRates: " + kbps + " kbps. Fps: " + frameRateIgnored); | 438 Logging.v(TAG, "setRates: " + kbps + " kbps. Fps: " + frameRateIgnored); |
368 try { | 439 try { |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
480 checkOnMediaCodecThread(); | 551 checkOnMediaCodecThread(); |
481 try { | 552 try { |
482 mediaCodec.releaseOutputBuffer(index, false); | 553 mediaCodec.releaseOutputBuffer(index, false); |
483 return true; | 554 return true; |
484 } catch (IllegalStateException e) { | 555 } catch (IllegalStateException e) { |
485 Logging.e(TAG, "releaseOutputBuffer failed", e); | 556 Logging.e(TAG, "releaseOutputBuffer failed", e); |
486 return false; | 557 return false; |
487 } | 558 } |
488 } | 559 } |
489 } | 560 } |
OLD | NEW |