| 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 17 matching lines...) Expand all Loading... |
| 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.os.Build; | 36 import android.os.Build; |
| 37 import android.os.Bundle; | 37 import android.os.Bundle; |
| 38 import android.util.Log; | 38 |
| 39 import org.webrtc.Logging; |
| 39 | 40 |
| 40 import java.nio.ByteBuffer; | 41 import java.nio.ByteBuffer; |
| 41 import java.util.Arrays; | 42 import java.util.Arrays; |
| 42 import java.util.List; | 43 import java.util.List; |
| 43 | 44 |
| 44 // Java-side of peerconnection_jni.cc:MediaCodecVideoEncoder. | 45 // Java-side of peerconnection_jni.cc:MediaCodecVideoEncoder. |
| 45 // This class is an implementation detail of the Java PeerConnection API. | 46 // This class is an implementation detail of the Java PeerConnection API. |
| 46 // MediaCodec is thread-hostile so this class must be operated on a single | 47 // MediaCodec is thread-hostile so this class must be operated on a single |
| 47 // thread. | 48 // thread. |
| 48 public class MediaCodecVideoEncoder { | 49 public class MediaCodecVideoEncoder { |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 // MediaCodec.setParameters is missing for JB and below, so bitrate | 119 // MediaCodec.setParameters is missing for JB and below, so bitrate |
| 119 // can not be adjusted dynamically. | 120 // can not be adjusted dynamically. |
| 120 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { | 121 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { |
| 121 return null; | 122 return null; |
| 122 } | 123 } |
| 123 | 124 |
| 124 // Check if device is in H.264 exception list. | 125 // Check if device is in H.264 exception list. |
| 125 if (mime.equals(H264_MIME_TYPE)) { | 126 if (mime.equals(H264_MIME_TYPE)) { |
| 126 List<String> exceptionModels = Arrays.asList(H264_HW_EXCEPTION_MODELS); | 127 List<String> exceptionModels = Arrays.asList(H264_HW_EXCEPTION_MODELS); |
| 127 if (exceptionModels.contains(Build.MODEL)) { | 128 if (exceptionModels.contains(Build.MODEL)) { |
| 128 Log.w(TAG, "Model: " + Build.MODEL + | 129 Logging.w(TAG, "Model: " + Build.MODEL + |
| 129 " has black listed H.264 encoder."); | 130 " has black listed H.264 encoder."); |
| 130 return null; | 131 return null; |
| 131 } | 132 } |
| 132 } | 133 } |
| 133 | 134 |
| 134 for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) { | 135 for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) { |
| 135 MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); | 136 MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); |
| 136 if (!info.isEncoder()) { | 137 if (!info.isEncoder()) { |
| 137 continue; | 138 continue; |
| 138 } | 139 } |
| 139 String name = null; | 140 String name = null; |
| 140 for (String mimeType : info.getSupportedTypes()) { | 141 for (String mimeType : info.getSupportedTypes()) { |
| 141 if (mimeType.equals(mime)) { | 142 if (mimeType.equals(mime)) { |
| 142 name = info.getName(); | 143 name = info.getName(); |
| 143 break; | 144 break; |
| 144 } | 145 } |
| 145 } | 146 } |
| 146 if (name == null) { | 147 if (name == null) { |
| 147 continue; // No HW support in this codec; try the next one. | 148 continue; // No HW support in this codec; try the next one. |
| 148 } | 149 } |
| 149 Log.v(TAG, "Found candidate encoder " + name); | 150 Logging.v(TAG, "Found candidate encoder " + name); |
| 150 | 151 |
| 151 // Check if this is supported HW encoder. | 152 // Check if this is supported HW encoder. |
| 152 boolean supportedCodec = false; | 153 boolean supportedCodec = false; |
| 153 for (String hwCodecPrefix : supportedHwCodecPrefixes) { | 154 for (String hwCodecPrefix : supportedHwCodecPrefixes) { |
| 154 if (name.startsWith(hwCodecPrefix)) { | 155 if (name.startsWith(hwCodecPrefix)) { |
| 155 supportedCodec = true; | 156 supportedCodec = true; |
| 156 break; | 157 break; |
| 157 } | 158 } |
| 158 } | 159 } |
| 159 if (!supportedCodec) { | 160 if (!supportedCodec) { |
| 160 continue; | 161 continue; |
| 161 } | 162 } |
| 162 | 163 |
| 163 CodecCapabilities capabilities = info.getCapabilitiesForType(mime); | 164 CodecCapabilities capabilities = info.getCapabilitiesForType(mime); |
| 164 for (int colorFormat : capabilities.colorFormats) { | 165 for (int colorFormat : capabilities.colorFormats) { |
| 165 Log.v(TAG, " Color: 0x" + Integer.toHexString(colorFormat)); | 166 Logging.v(TAG, " Color: 0x" + Integer.toHexString(colorFormat)); |
| 166 } | 167 } |
| 167 | 168 |
| 168 // Check if codec supports either yuv420 or nv12. | 169 // Check if codec supports either yuv420 or nv12. |
| 169 for (int supportedColorFormat : supportedColorList) { | 170 for (int supportedColorFormat : supportedColorList) { |
| 170 for (int codecColorFormat : capabilities.colorFormats) { | 171 for (int codecColorFormat : capabilities.colorFormats) { |
| 171 if (codecColorFormat == supportedColorFormat) { | 172 if (codecColorFormat == supportedColorFormat) { |
| 172 // Found supported HW encoder. | 173 // Found supported HW encoder. |
| 173 Log.d(TAG, "Found target encoder for mime " + mime + " : " + name + | 174 Logging.d(TAG, "Found target encoder for mime " + mime + " : " + nam
e + |
| 174 ". Color: 0x" + Integer.toHexString(codecColorFormat)); | 175 ". Color: 0x" + Integer.toHexString(codecColorFormat)); |
| 175 return new EncoderProperties(name, codecColorFormat); | 176 return new EncoderProperties(name, codecColorFormat); |
| 176 } | 177 } |
| 177 } | 178 } |
| 178 } | 179 } |
| 179 } | 180 } |
| 180 return null; // No HW VP8 encoder. | 181 return null; // No HW VP8 encoder. |
| 181 } | 182 } |
| 182 | 183 |
| 183 public static boolean isVp8HwSupported() { | 184 public static boolean isVp8HwSupported() { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 202 // both cases catch an exception. | 203 // both cases catch an exception. |
| 203 return MediaCodec.createByCodecName(codecName); | 204 return MediaCodec.createByCodecName(codecName); |
| 204 } catch (Exception e) { | 205 } catch (Exception e) { |
| 205 return null; | 206 return null; |
| 206 } | 207 } |
| 207 } | 208 } |
| 208 | 209 |
| 209 // Return the array of input buffers, or null on failure. | 210 // Return the array of input buffers, or null on failure. |
| 210 private ByteBuffer[] initEncode( | 211 private ByteBuffer[] initEncode( |
| 211 VideoCodecType type, int width, int height, int kbps, int fps) { | 212 VideoCodecType type, int width, int height, int kbps, int fps) { |
| 212 Log.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height + | 213 Logging.d(TAG, "Java initEncode: " + type + " : " + width + " x " + height + |
| 213 ". @ " + kbps + " kbps. Fps: " + fps + | 214 ". @ " + kbps + " kbps. Fps: " + fps + |
| 214 ". Color: 0x" + Integer.toHexString(colorFormat)); | 215 ". Color: 0x" + Integer.toHexString(colorFormat)); |
| 215 if (mediaCodecThread != null) { | 216 if (mediaCodecThread != null) { |
| 216 throw new RuntimeException("Forgot to release()?"); | 217 throw new RuntimeException("Forgot to release()?"); |
| 217 } | 218 } |
| 218 this.type = type; | 219 this.type = type; |
| 219 EncoderProperties properties = null; | 220 EncoderProperties properties = null; |
| 220 String mime = null; | 221 String mime = null; |
| 221 int keyFrameIntervalSec = 0; | 222 int keyFrameIntervalSec = 0; |
| 222 if (type == VideoCodecType.VIDEO_CODEC_VP8) { | 223 if (type == VideoCodecType.VIDEO_CODEC_VP8) { |
| 223 mime = VP8_MIME_TYPE; | 224 mime = VP8_MIME_TYPE; |
| 224 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes); | 225 properties = findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes); |
| 225 keyFrameIntervalSec = 100; | 226 keyFrameIntervalSec = 100; |
| 226 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { | 227 } else if (type == VideoCodecType.VIDEO_CODEC_H264) { |
| 227 mime = H264_MIME_TYPE; | 228 mime = H264_MIME_TYPE; |
| 228 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes); | 229 properties = findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes); |
| 229 keyFrameIntervalSec = 20; | 230 keyFrameIntervalSec = 20; |
| 230 } | 231 } |
| 231 if (properties == null) { | 232 if (properties == null) { |
| 232 throw new RuntimeException("Can not find HW encoder for " + type); | 233 throw new RuntimeException("Can not find HW encoder for " + type); |
| 233 } | 234 } |
| 234 mediaCodecThread = Thread.currentThread(); | 235 mediaCodecThread = Thread.currentThread(); |
| 235 try { | 236 try { |
| 236 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); | 237 MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); |
| 237 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps); | 238 format.setInteger(MediaFormat.KEY_BIT_RATE, 1000 * kbps); |
| 238 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); | 239 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); |
| 239 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); | 240 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); |
| 240 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); | 241 format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); |
| 241 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); | 242 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); |
| 242 Log.d(TAG, " Format: " + format); | 243 Logging.d(TAG, " Format: " + format); |
| 243 mediaCodec = createByCodecName(properties.codecName); | 244 mediaCodec = createByCodecName(properties.codecName); |
| 244 if (mediaCodec == null) { | 245 if (mediaCodec == null) { |
| 245 return null; | 246 return null; |
| 246 } | 247 } |
| 247 mediaCodec.configure( | 248 mediaCodec.configure( |
| 248 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); | 249 format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); |
| 249 mediaCodec.start(); | 250 mediaCodec.start(); |
| 250 colorFormat = properties.colorFormat; | 251 colorFormat = properties.colorFormat; |
| 251 outputBuffers = mediaCodec.getOutputBuffers(); | 252 outputBuffers = mediaCodec.getOutputBuffers(); |
| 252 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); | 253 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); |
| 253 Log.d(TAG, "Input buffers: " + inputBuffers.length + | 254 Logging.d(TAG, "Input buffers: " + inputBuffers.length + |
| 254 ". Output buffers: " + outputBuffers.length); | 255 ". Output buffers: " + outputBuffers.length); |
| 255 return inputBuffers; | 256 return inputBuffers; |
| 256 } catch (IllegalStateException e) { | 257 } catch (IllegalStateException e) { |
| 257 Log.e(TAG, "initEncode failed", e); | 258 Logging.e(TAG, "initEncode failed", e); |
| 258 return null; | 259 return null; |
| 259 } | 260 } |
| 260 } | 261 } |
| 261 | 262 |
| 262 private boolean encode( | 263 private boolean encode( |
| 263 boolean isKeyframe, int inputBuffer, int size, | 264 boolean isKeyframe, int inputBuffer, int size, |
| 264 long presentationTimestampUs) { | 265 long presentationTimestampUs) { |
| 265 checkOnMediaCodecThread(); | 266 checkOnMediaCodecThread(); |
| 266 try { | 267 try { |
| 267 if (isKeyframe) { | 268 if (isKeyframe) { |
| 268 // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could | 269 // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could |
| 269 // indicate this in queueInputBuffer() below and guarantee _this_ frame | 270 // indicate this in queueInputBuffer() below and guarantee _this_ frame |
| 270 // be encoded as a key frame, but sadly that flag is ignored. Instead, | 271 // be encoded as a key frame, but sadly that flag is ignored. Instead, |
| 271 // we request a key frame "soon". | 272 // we request a key frame "soon". |
| 272 Log.d(TAG, "Sync frame request"); | 273 Logging.d(TAG, "Sync frame request"); |
| 273 Bundle b = new Bundle(); | 274 Bundle b = new Bundle(); |
| 274 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); | 275 b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); |
| 275 mediaCodec.setParameters(b); | 276 mediaCodec.setParameters(b); |
| 276 } | 277 } |
| 277 mediaCodec.queueInputBuffer( | 278 mediaCodec.queueInputBuffer( |
| 278 inputBuffer, 0, size, presentationTimestampUs, 0); | 279 inputBuffer, 0, size, presentationTimestampUs, 0); |
| 279 return true; | 280 return true; |
| 280 } | 281 } |
| 281 catch (IllegalStateException e) { | 282 catch (IllegalStateException e) { |
| 282 Log.e(TAG, "encode failed", e); | 283 Logging.e(TAG, "encode failed", e); |
| 283 return false; | 284 return false; |
| 284 } | 285 } |
| 285 } | 286 } |
| 286 | 287 |
| 287 private void release() { | 288 private void release() { |
| 288 Log.d(TAG, "Java releaseEncoder"); | 289 Logging.d(TAG, "Java releaseEncoder"); |
| 289 checkOnMediaCodecThread(); | 290 checkOnMediaCodecThread(); |
| 290 try { | 291 try { |
| 291 mediaCodec.stop(); | 292 mediaCodec.stop(); |
| 292 mediaCodec.release(); | 293 mediaCodec.release(); |
| 293 } catch (IllegalStateException e) { | 294 } catch (IllegalStateException e) { |
| 294 Log.e(TAG, "release failed", e); | 295 Logging.e(TAG, "release failed", e); |
| 295 } | 296 } |
| 296 mediaCodec = null; | 297 mediaCodec = null; |
| 297 mediaCodecThread = null; | 298 mediaCodecThread = null; |
| 298 } | 299 } |
| 299 | 300 |
| 300 private boolean setRates(int kbps, int frameRateIgnored) { | 301 private boolean setRates(int kbps, int frameRateIgnored) { |
| 301 // frameRate argument is ignored - HW encoder is supposed to use | 302 // frameRate argument is ignored - HW encoder is supposed to use |
| 302 // video frame timestamps for bit allocation. | 303 // video frame timestamps for bit allocation. |
| 303 checkOnMediaCodecThread(); | 304 checkOnMediaCodecThread(); |
| 304 Log.v(TAG, "setRates: " + kbps + " kbps. Fps: " + frameRateIgnored); | 305 Logging.v(TAG, "setRates: " + kbps + " kbps. Fps: " + frameRateIgnored); |
| 305 try { | 306 try { |
| 306 Bundle params = new Bundle(); | 307 Bundle params = new Bundle(); |
| 307 params.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, 1000 * kbps); | 308 params.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, 1000 * kbps); |
| 308 mediaCodec.setParameters(params); | 309 mediaCodec.setParameters(params); |
| 309 return true; | 310 return true; |
| 310 } catch (IllegalStateException e) { | 311 } catch (IllegalStateException e) { |
| 311 Log.e(TAG, "setRates failed", e); | 312 Logging.e(TAG, "setRates failed", e); |
| 312 return false; | 313 return false; |
| 313 } | 314 } |
| 314 } | 315 } |
| 315 | 316 |
| 316 // Dequeue an input buffer and return its index, -1 if no input buffer is | 317 // Dequeue an input buffer and return its index, -1 if no input buffer is |
| 317 // available, or -2 if the codec is no longer operative. | 318 // available, or -2 if the codec is no longer operative. |
| 318 private int dequeueInputBuffer() { | 319 private int dequeueInputBuffer() { |
| 319 checkOnMediaCodecThread(); | 320 checkOnMediaCodecThread(); |
| 320 try { | 321 try { |
| 321 return mediaCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT); | 322 return mediaCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT); |
| 322 } catch (IllegalStateException e) { | 323 } catch (IllegalStateException e) { |
| 323 Log.e(TAG, "dequeueIntputBuffer failed", e); | 324 Logging.e(TAG, "dequeueIntputBuffer failed", e); |
| 324 return -2; | 325 return -2; |
| 325 } | 326 } |
| 326 } | 327 } |
| 327 | 328 |
| 328 // Helper struct for dequeueOutputBuffer() below. | 329 // Helper struct for dequeueOutputBuffer() below. |
| 329 private static class OutputBufferInfo { | 330 private static class OutputBufferInfo { |
| 330 public OutputBufferInfo( | 331 public OutputBufferInfo( |
| 331 int index, ByteBuffer buffer, | 332 int index, ByteBuffer buffer, |
| 332 boolean isKeyFrame, long presentationTimestampUs) { | 333 boolean isKeyFrame, long presentationTimestampUs) { |
| 333 this.index = index; | 334 this.index = index; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 347 private OutputBufferInfo dequeueOutputBuffer() { | 348 private OutputBufferInfo dequeueOutputBuffer() { |
| 348 checkOnMediaCodecThread(); | 349 checkOnMediaCodecThread(); |
| 349 try { | 350 try { |
| 350 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); | 351 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
| 351 int result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); | 352 int result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); |
| 352 // Check if this is config frame and save configuration data. | 353 // Check if this is config frame and save configuration data. |
| 353 if (result >= 0) { | 354 if (result >= 0) { |
| 354 boolean isConfigFrame = | 355 boolean isConfigFrame = |
| 355 (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0; | 356 (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0; |
| 356 if (isConfigFrame) { | 357 if (isConfigFrame) { |
| 357 Log.d(TAG, "Config frame generated. Offset: " + info.offset + | 358 Logging.d(TAG, "Config frame generated. Offset: " + info.offset + |
| 358 ". Size: " + info.size); | 359 ". Size: " + info.size); |
| 359 configData = ByteBuffer.allocateDirect(info.size); | 360 configData = ByteBuffer.allocateDirect(info.size); |
| 360 outputBuffers[result].position(info.offset); | 361 outputBuffers[result].position(info.offset); |
| 361 outputBuffers[result].limit(info.offset + info.size); | 362 outputBuffers[result].limit(info.offset + info.size); |
| 362 configData.put(outputBuffers[result]); | 363 configData.put(outputBuffers[result]); |
| 363 // Release buffer back. | 364 // Release buffer back. |
| 364 mediaCodec.releaseOutputBuffer(result, false); | 365 mediaCodec.releaseOutputBuffer(result, false); |
| 365 // Query next output. | 366 // Query next output. |
| 366 result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); | 367 result = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT); |
| 367 } | 368 } |
| 368 } | 369 } |
| 369 if (result >= 0) { | 370 if (result >= 0) { |
| 370 // MediaCodec doesn't care about Buffer position/remaining/etc so we can | 371 // MediaCodec doesn't care about Buffer position/remaining/etc so we can |
| 371 // mess with them to get a slice and avoid having to pass extra | 372 // mess with them to get a slice and avoid having to pass extra |
| 372 // (BufferInfo-related) parameters back to C++. | 373 // (BufferInfo-related) parameters back to C++. |
| 373 ByteBuffer outputBuffer = outputBuffers[result].duplicate(); | 374 ByteBuffer outputBuffer = outputBuffers[result].duplicate(); |
| 374 outputBuffer.position(info.offset); | 375 outputBuffer.position(info.offset); |
| 375 outputBuffer.limit(info.offset + info.size); | 376 outputBuffer.limit(info.offset + info.size); |
| 376 // Check key frame flag. | 377 // Check key frame flag. |
| 377 boolean isKeyFrame = | 378 boolean isKeyFrame = |
| 378 (info.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0; | 379 (info.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0; |
| 379 if (isKeyFrame) { | 380 if (isKeyFrame) { |
| 380 Log.d(TAG, "Sync frame generated"); | 381 Logging.d(TAG, "Sync frame generated"); |
| 381 } | 382 } |
| 382 if (isKeyFrame && type == VideoCodecType.VIDEO_CODEC_H264) { | 383 if (isKeyFrame && type == VideoCodecType.VIDEO_CODEC_H264) { |
| 383 Log.d(TAG, "Appending config frame of size " + configData.capacity() + | 384 Logging.d(TAG, "Appending config frame of size " + configData.capacity
() + |
| 384 " to output buffer with offset " + info.offset + ", size " + | 385 " to output buffer with offset " + info.offset + ", size " + |
| 385 info.size); | 386 info.size); |
| 386 // For H.264 key frame append SPS and PPS NALs at the start | 387 // For H.264 key frame append SPS and PPS NALs at the start |
| 387 ByteBuffer keyFrameBuffer = ByteBuffer.allocateDirect( | 388 ByteBuffer keyFrameBuffer = ByteBuffer.allocateDirect( |
| 388 configData.capacity() + info.size); | 389 configData.capacity() + info.size); |
| 389 configData.rewind(); | 390 configData.rewind(); |
| 390 keyFrameBuffer.put(configData); | 391 keyFrameBuffer.put(configData); |
| 391 keyFrameBuffer.put(outputBuffer); | 392 keyFrameBuffer.put(outputBuffer); |
| 392 keyFrameBuffer.position(0); | 393 keyFrameBuffer.position(0); |
| 393 return new OutputBufferInfo(result, keyFrameBuffer, | 394 return new OutputBufferInfo(result, keyFrameBuffer, |
| 394 isKeyFrame, info.presentationTimeUs); | 395 isKeyFrame, info.presentationTimeUs); |
| 395 } else { | 396 } else { |
| 396 return new OutputBufferInfo(result, outputBuffer.slice(), | 397 return new OutputBufferInfo(result, outputBuffer.slice(), |
| 397 isKeyFrame, info.presentationTimeUs); | 398 isKeyFrame, info.presentationTimeUs); |
| 398 } | 399 } |
| 399 } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { | 400 } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { |
| 400 outputBuffers = mediaCodec.getOutputBuffers(); | 401 outputBuffers = mediaCodec.getOutputBuffers(); |
| 401 return dequeueOutputBuffer(); | 402 return dequeueOutputBuffer(); |
| 402 } else if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { | 403 } else if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { |
| 403 return dequeueOutputBuffer(); | 404 return dequeueOutputBuffer(); |
| 404 } else if (result == MediaCodec.INFO_TRY_AGAIN_LATER) { | 405 } else if (result == MediaCodec.INFO_TRY_AGAIN_LATER) { |
| 405 return null; | 406 return null; |
| 406 } | 407 } |
| 407 throw new RuntimeException("dequeueOutputBuffer: " + result); | 408 throw new RuntimeException("dequeueOutputBuffer: " + result); |
| 408 } catch (IllegalStateException e) { | 409 } catch (IllegalStateException e) { |
| 409 Log.e(TAG, "dequeueOutputBuffer failed", e); | 410 Logging.e(TAG, "dequeueOutputBuffer failed", e); |
| 410 return new OutputBufferInfo(-1, null, false, -1); | 411 return new OutputBufferInfo(-1, null, false, -1); |
| 411 } | 412 } |
| 412 } | 413 } |
| 413 | 414 |
| 414 // Release a dequeued output buffer back to the codec for re-use. Return | 415 // Release a dequeued output buffer back to the codec for re-use. Return |
| 415 // false if the codec is no longer operable. | 416 // false if the codec is no longer operable. |
| 416 private boolean releaseOutputBuffer(int index) { | 417 private boolean releaseOutputBuffer(int index) { |
| 417 checkOnMediaCodecThread(); | 418 checkOnMediaCodecThread(); |
| 418 try { | 419 try { |
| 419 mediaCodec.releaseOutputBuffer(index, false); | 420 mediaCodec.releaseOutputBuffer(index, false); |
| 420 return true; | 421 return true; |
| 421 } catch (IllegalStateException e) { | 422 } catch (IllegalStateException e) { |
| 422 Log.e(TAG, "releaseOutputBuffer failed", e); | 423 Logging.e(TAG, "releaseOutputBuffer failed", e); |
| 423 return false; | 424 return false; |
| 424 } | 425 } |
| 425 } | 426 } |
| 426 } | 427 } |
| OLD | NEW |