/** * Method to set byte array to the MediaCodec encoder * * @param buffer * @param length length of byte array, zero means EOS. * @param presentationTimeUs */ protected void encode(byte[] buffer, int length, long presentationTimeUs) { if (!mIsCapturing) return; int ix = 0, sz; final ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers(); while (mIsCapturing && ix < length) { final int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_USEC); if (inputBufferIndex >= 0) { final ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; inputBuffer.clear(); sz = inputBuffer.remaining(); sz = (ix + sz < length) ? sz : length - ix; if (sz > 0 && (buffer != null)) { inputBuffer.put(buffer, ix, sz); } ix += sz; // if (DEBUG) Log.v(TAG, "encode:queueInputBuffer"); if (length <= 0) { // send EOS mIsEOS = true; if (DEBUG) Log.i(TAG, "send BUFFER_FLAG_END_OF_STREAM"); mMediaCodec.queueInputBuffer( inputBufferIndex, 0, 0, presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM); break; } else { mMediaCodec.queueInputBuffer(inputBufferIndex, 0, sz, presentationTimeUs, 0); } } else if (inputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { // wait for MediaCodec encoder is ready to encode // nothing to do here because MediaCodec#dequeueInputBuffer(TIMEOUT_USEC) // will wait for maximum TIMEOUT_USEC(10msec) on each call } } }
private final void handleStart() { if (DEBUG) Log.v(TAG, "handleStart:"); synchronized (mSync) { if (mState != STATE_PREPARED) throw new RuntimeException("invalid state:" + mState); mState = STATE_PLAYING; } if (mRequestTime > 0) { handleSeek(mRequestTime); } previousVideoPresentationTimeUs = -1; mVideoInputDone = mVideoOutputDone = true; Thread videoThread = null; if (mVideoTrackIndex >= 0) { final MediaCodec codec = internal_start_video(mVideoMediaExtractor, mVideoTrackIndex); if (codec != null) { mVideoMediaCodec = codec; mVideoBufferInfo = new MediaCodec.BufferInfo(); mVideoInputBuffers = codec.getInputBuffers(); mVideoOutputBuffers = codec.getOutputBuffers(); } mVideoInputDone = mVideoOutputDone = false; videoThread = new Thread(mVideoTask, "VideoTask"); } if (videoThread != null) videoThread.start(); }
private final void handleStop() { if (DEBUG) Log.v(TAG, "handleStop:"); synchronized (mVideoTask) { internal_stop_video(); mVideoTrackIndex = -1; } if (mVideoMediaCodec != null) { mVideoMediaCodec.stop(); mVideoMediaCodec.release(); mVideoMediaCodec = null; } if (mVideoMediaExtractor != null) { mVideoMediaExtractor.release(); mVideoMediaExtractor = null; } mVideoBufferInfo = null; mVideoInputBuffers = mVideoOutputBuffers = null; if (mMetadata != null) { mMetadata.release(); mMetadata = null; } synchronized (mSync) { mVideoOutputDone = mVideoInputDone = true; mState = STATE_STOP; } mCallback.onFinished(); }
/** * Write a media sample to the decoder. * * <p>A "sample" here refers to a single atomic access unit in the media stream. The definition of * "access unit" is dependent on the type of encoding used, but it typically refers to a single * frame of video or a few seconds of audio. {@link android.media.MediaExtractor} extracts data * from a stream one sample at a time. * * @param extractor Instance of {@link android.media.MediaExtractor} wrapping the media. * @param presentationTimeUs The time, relative to the beginning of the media stream, at which * this buffer should be rendered. * @param flags Flags to pass to the decoder. See {@link MediaCodec#queueInputBuffer(int, int, * int, long, int)} * @throws MediaCodec.CryptoException */ public boolean writeSample( final MediaExtractor extractor, final boolean isSecure, final long presentationTimeUs, int flags) { boolean result = false; boolean isEos = false; if (!mAvailableInputBuffers.isEmpty()) { int index = mAvailableInputBuffers.remove(); ByteBuffer buffer = mInputBuffers[index]; // reads the sample from the file using extractor into the buffer int size = extractor.readSampleData(buffer, 0); if (size <= 0) { flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM; } // Submit the buffer to the codec for decoding. The presentationTimeUs // indicates the position (play time) for the current sample. if (!isSecure) { mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags); } else { extractor.getSampleCryptoInfo(cryptoInfo); mDecoder.queueSecureInputBuffer(index, 0, cryptoInfo, presentationTimeUs, flags); } result = true; } return result; }
/** * Constructs the {@link MediaCodecWrapper} wrapper object around the video codec. The codec is * created using the encapsulated information in the {@link MediaFormat} object. * * @param trackFormat The format of the media object to be decoded. * @param surface Surface to render the decoded frames. * @return */ public static MediaCodecWrapper fromVideoFormat(final MediaFormat trackFormat, Surface surface) { MediaCodecWrapper result = null; MediaCodec videoCodec = null; // BEGIN_INCLUDE(create_codec) final String mimeType = trackFormat.getString(MediaFormat.KEY_MIME); // Check to see if this is actually a video mime type. If it is, then create // a codec that can decode this mime type. if (mimeType.contains("video/")) { try { videoCodec = MediaCodec.createDecoderByType(mimeType); videoCodec.configure(trackFormat, surface, null, 0); } catch (Exception e) { } } // If codec creation was successful, then create a wrapper object around the // newly created codec. if (videoCodec != null) { result = new MediaCodecWrapper(videoCodec); } // END_INCLUDE(create_codec) return result; }
private final void handleInputVideo() { long presentationTimeUs = mVideoMediaExtractor.getSampleTime(); if (presentationTimeUs < previousVideoPresentationTimeUs) { presentationTimeUs += previousVideoPresentationTimeUs - presentationTimeUs; // + EPS; } previousVideoPresentationTimeUs = presentationTimeUs; final boolean b = internal_process_input( mVideoMediaCodec, mVideoMediaExtractor, mVideoInputBuffers, presentationTimeUs, false); if (!b) { if (DEBUG) Log.i(TAG, "video track input reached EOS"); while (mIsRunning) { final int inputBufIndex = mVideoMediaCodec.dequeueInputBuffer(TIMEOUT_USEC); if (inputBufIndex >= 0) { mVideoMediaCodec.queueInputBuffer( inputBufIndex, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM); if (DEBUG) Log.v(TAG, "sent input EOS:" + mVideoMediaCodec); break; } } synchronized (mSync) { mVideoInputDone = true; mSync.notifyAll(); } } }
/** Release all releated objects */ protected void release() { if (DEBUG) Log.d(TAG, "release:"); try { mListener.onStopped(this); } catch (Exception e) { Log.e(TAG, "failed onStopped", e); } mIsCapturing = false; if (mMediaCodec != null) { try { mMediaCodec.stop(); mMediaCodec.release(); mMediaCodec = null; } catch (Exception e) { Log.e(TAG, "failed releasing MediaCodec", e); } } if (mMuxerStarted) { final MediaMuxerWrapper muxer = mWeakMuxer.get(); if (muxer != null) { try { muxer.stop(); } catch (Exception e) { Log.e(TAG, "failed stopping muxer", e); } } } mBufferInfo = null; }
private MediaCodecWrapper(MediaCodec codec) { mDecoder = codec; codec.start(); mInputBuffers = codec.getInputBuffers(); mOutputBuffers = codec.getOutputBuffers(); mOutputBufferInfo = new MediaCodec.BufferInfo[mOutputBuffers.length]; mAvailableInputBuffers = new ArrayDeque<Integer>(mOutputBuffers.length); mAvailableOutputBuffers = new ArrayDeque<Integer>(mInputBuffers.length); }
@Override public void onDestroy() { super.onDestroy(); if (encoder != null) { encoder.stop(); encoder.release(); encoder = null; } }
// Dequeue and return an output buffer index, -1 if no output // buffer available or -2 if error happened. private DecoderOutputBufferInfo dequeueOutputBuffer(int dequeueTimeoutUs) { checkOnMediaCodecThread(); try { MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); int result = mediaCodec.dequeueOutputBuffer(info, dequeueTimeoutUs); while (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED || result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { outputBuffers = mediaCodec.getOutputBuffers(); Logging.d(TAG, "Decoder output buffers changed: " + outputBuffers.length); } else if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { MediaFormat format = mediaCodec.getOutputFormat(); Logging.d(TAG, "Decoder format changed: " + format.toString()); width = format.getInteger(MediaFormat.KEY_WIDTH); height = format.getInteger(MediaFormat.KEY_HEIGHT); if (!useSurface && format.containsKey(MediaFormat.KEY_COLOR_FORMAT)) { colorFormat = format.getInteger(MediaFormat.KEY_COLOR_FORMAT); Logging.d(TAG, "Color: 0x" + Integer.toHexString(colorFormat)); // Check if new color space is supported. boolean validColorFormat = false; for (int supportedColorFormat : supportedColorList) { if (colorFormat == supportedColorFormat) { validColorFormat = true; break; } } if (!validColorFormat) { Logging.e(TAG, "Non supported color format"); return new DecoderOutputBufferInfo(-1, 0, 0, -1); } } if (format.containsKey("stride")) { stride = format.getInteger("stride"); } if (format.containsKey("slice-height")) { sliceHeight = format.getInteger("slice-height"); } Logging.d(TAG, "Frame stride and slice height: " + stride + " x " + sliceHeight); stride = Math.max(width, stride); sliceHeight = Math.max(height, sliceHeight); } result = mediaCodec.dequeueOutputBuffer(info, dequeueTimeoutUs); } if (result >= 0) { return new DecoderOutputBufferInfo(result, info.offset, info.size, info.presentationTimeUs); } return null; } catch (IllegalStateException e) { Logging.e(TAG, "dequeueOutputBuffer failed", e); return new DecoderOutputBufferInfo(-1, 0, 0, -1); } }
/* */ private void g() { /* 434 */ a.a("formatNotSupportMsg start"); /* 435 */ if (a.a != null) { /* 436 */ Message localMessage = new Message(); /* 437 */ localMessage.what = 1; /* 438 */ localMessage.arg1 = HKErrorCode.ERROR_MEDIA_UNSUPPORTED.ordinal(); /* 439 */ Bundle localBundle = new Bundle(); /* 440 */ localBundle.putString("errorMesg", "media format is not supported !"); /* 441 */ localMessage.setData(localBundle); /* 442 */ a.a.sendMessage(localMessage); /* */ } /* 444 */ a.a("formatNotSupportMsg end"); /* */ }
private void releaseEncoder() { // Release encoder if (VERBOSE) { Log.v(TAG, "releasing encoder"); } if (mEncoder != null) { mEncoder.stop(); mEncoder.release(); if (mRecordingSurface != null) { mRecordingSurface.release(); } mEncoder = null; } }
/** * @param media_extractor * @param trackIndex * @return */ protected MediaCodec internal_start_video( final MediaExtractor media_extractor, final int trackIndex) { if (DEBUG) Log.v(TAG, "internal_start_video:"); MediaCodec codec = null; if (trackIndex >= 0) { final MediaFormat format = media_extractor.getTrackFormat(trackIndex); final String mime = format.getString(MediaFormat.KEY_MIME); codec = MediaCodec.createDecoderByType(mime); codec.configure(format, mOutputSurface, null, 0); codec.start(); if (DEBUG) Log.v(TAG, "internal_start_video:codec started"); } return codec; }
/** Synchronize this object's state with the internal state of the wrapped MediaCodec. */ private void update() { // BEGIN_INCLUDE(update_codec_state) int index; // Get valid input buffers from the codec to fill later in the same order they were // made available by the codec. while ((index = mDecoder.dequeueInputBuffer(0)) != MediaCodec.INFO_TRY_AGAIN_LATER) { mAvailableInputBuffers.add(index); } // Likewise with output buffers. If the output buffers have changed, start using the // new set of output buffers. If the output format has changed, notify listeners. MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); while ((index = mDecoder.dequeueOutputBuffer(info, 0)) != MediaCodec.INFO_TRY_AGAIN_LATER) { switch (index) { case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: mOutputBuffers = mDecoder.getOutputBuffers(); mOutputBufferInfo = new MediaCodec.BufferInfo[mOutputBuffers.length]; mAvailableOutputBuffers.clear(); break; case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: if (mOutputFormatChangedListener != null) { mHandler.post( new Runnable() { @Override public void run() { mOutputFormatChangedListener.outputFormatChanged( MediaCodecWrapper.this, mDecoder.getOutputFormat()); } }); } break; default: // Making sure the index is valid before adding to output buffers. We've already // handled INFO_TRY_AGAIN_LATER, INFO_OUTPUT_FORMAT_CHANGED & // INFO_OUTPUT_BUFFERS_CHANGED i.e all the other possible return codes but // asserting index value anyways for future-proofing the code. if (index >= 0) { mOutputBufferInfo[index] = info; mAvailableOutputBuffers.add(index); } else { throw new IllegalStateException("Unknown status from dequeueOutputBuffer"); } break; } } // END_INCLUDE(update_codec_state) }
private void initStream() throws IOException, IllegalArgumentException { L.v(TAG, "initStream called in state=" + state); lock.lock(); try { extractor = new MediaExtractor(); if (path != null) { extractor.setDataSource(path); } else { error("initStream"); throw new IOException(); } int trackNum = 0; final MediaFormat oFormat = extractor.getTrackFormat(trackNum); if (!oFormat.containsKey(MediaFormat.KEY_SAMPLE_RATE)) { error("initStream"); throw new IOException("No KEY_SAMPLE_RATE"); } int sampleRate = oFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE); if (!oFormat.containsKey(MediaFormat.KEY_CHANNEL_COUNT)) { error("initStream"); throw new IOException("No KEY_CHANNEL_COUNT"); } int channelCount = oFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT); if (!oFormat.containsKey(MediaFormat.KEY_MIME)) { error("initStream"); throw new IOException("No KEY_MIME"); } final String mime = oFormat.getString(MediaFormat.KEY_MIME); if (!oFormat.containsKey(MediaFormat.KEY_DURATION)) { error("initStream"); throw new IOException("No KEY_DURATION"); } duration = oFormat.getLong(MediaFormat.KEY_DURATION); L.v(TAG, "Sample rate: " + sampleRate); L.v(TAG, "Mime type: " + mime); initDevice(sampleRate, channelCount); extractor.selectTrack(trackNum); codec = MediaCodec.createDecoderByType(mime); codec.configure(oFormat, null, null, 0); } finally { lock.unlock(); } }
@Override protected void prepare() throws IOException { if (DEBUG) Log.v(TAG, "prepare:"); mTrackIndex = -1; mMuxerStarted = mIsEOS = false; // prepare MediaCodec for AAC encoding of audio data from inernal mic. final MediaCodecInfo audioCodecInfo = selectAudioCodec(MIME_TYPE); if (audioCodecInfo == null) { Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE); return; } if (DEBUG) Log.i(TAG, "selected codec: " + audioCodecInfo.getName()); final MediaFormat audioFormat = MediaFormat.createAudioFormat(MIME_TYPE, SAMPLE_RATE, 1); audioFormat.setInteger( MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); audioFormat.setInteger(MediaFormat.KEY_CHANNEL_MASK, AudioFormat.CHANNEL_IN_MONO); audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE); audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); // audioFormat.setLong(MediaFormat.KEY_MAX_INPUT_SIZE, inputFile.length()); // audioFormat.setLong(MediaFormat.KEY_DURATION, (long)durationInMs ); if (DEBUG) Log.i(TAG, "format: " + audioFormat); mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE); mMediaCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); mMediaCodec.start(); if (DEBUG) Log.i(TAG, "prepare finishing"); if (mListener != null) { try { mListener.onPrepared(this); } catch (final Exception e) { Log.e(TAG, "prepare:", e); } } }
/** Video encoding is done by a MediaCodec. But here we will use the buffer-to-surface method */ @SuppressLint({"InlinedApi", "NewApi"}) protected void encodeWithMediaCodecMethod2() throws RuntimeException, IOException { Log.d(TAG, "Video encoded using the MediaCodec API with a surface"); // Updates the parameters of the camera if needed createCamera(); updateCamera(); // Estimates the framerate of the camera measureFramerate(); EncoderDebugger debugger = EncoderDebugger.debug(mSettings, mQuality.resX, mQuality.resY); mMediaCodec = MediaCodec.createByCodecName(debugger.getEncoderName()); MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", mQuality.resX, mQuality.resY); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, mQuality.bitrate); mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mQuality.framerate); mediaFormat.setInteger( MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); Surface surface = mMediaCodec.createInputSurface(); ((SurfaceView) mSurfaceView).addMediaCodecSurface(surface); mMediaCodec.start(); mStreaming = true; }
@Override protected void onOutputFormatChanged(MediaCodec codec, android.media.MediaFormat outputFormat) { boolean hasCrop = outputFormat.containsKey(KEY_CROP_RIGHT) && outputFormat.containsKey(KEY_CROP_LEFT) && outputFormat.containsKey(KEY_CROP_BOTTOM) && outputFormat.containsKey(KEY_CROP_TOP); currentWidth = hasCrop ? outputFormat.getInteger(KEY_CROP_RIGHT) - outputFormat.getInteger(KEY_CROP_LEFT) + 1 : outputFormat.getInteger(android.media.MediaFormat.KEY_WIDTH); currentHeight = hasCrop ? outputFormat.getInteger(KEY_CROP_BOTTOM) - outputFormat.getInteger(KEY_CROP_TOP) + 1 : outputFormat.getInteger(android.media.MediaFormat.KEY_HEIGHT); currentPixelWidthHeightRatio = pendingPixelWidthHeightRatio; if (Util.SDK_INT >= 21) { // On API level 21 and above the decoder applies the rotation when rendering to the surface. // Hence currentUnappliedRotation should always be 0. For 90 and 270 degree rotations, we need // to flip the width, height and pixel aspect ratio to reflect the rotation that was applied. if (pendingRotationDegrees == 90 || pendingRotationDegrees == 270) { int rotatedHeight = currentWidth; currentWidth = currentHeight; currentHeight = rotatedHeight; currentPixelWidthHeightRatio = 1 / currentPixelWidthHeightRatio; } } else { // On API level 20 and below the decoder does not apply the rotation. currentUnappliedRotationDegrees = pendingRotationDegrees; } // Must be applied each time the output format changes. codec.setVideoScalingMode(videoScalingMode); }
/** * Create the display surface out of the encoder. The data to encoder will be fed from this * Surface itself. * * @return * @throws IOException */ @TargetApi(19) private Surface createDisplaySurface() throws IOException { MediaFormat mMediaFormat = MediaFormat.createVideoFormat(CodecUtils.MIME_TYPE, CodecUtils.WIDTH, CodecUtils.HEIGHT); mMediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, (int) (1024 * 1024 * 0.5)); mMediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30); mMediaFormat.setInteger( MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); mMediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); Log.i(TAG, "Starting encoder"); encoder = MediaCodec.createEncoderByType(CodecUtils.MIME_TYPE); encoder.configure(mMediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); Surface surface = encoder.createInputSurface(); return surface; }
// Dequeue an input buffer and return its index, -1 if no input buffer is // available, or -2 if the codec is no longer operative. private int dequeueInputBuffer() { checkOnMediaCodecThread(); try { return mediaCodec.dequeueInputBuffer(DEQUEUE_INPUT_TIMEOUT); } catch (IllegalStateException e) { Logging.e(TAG, "dequeueIntputBuffer failed", e); return -2; } }
public void prepareEncode() { MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight); // 描述视频格式的信息 mediaFormat.setInteger( MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BITRATE); // 比特率 mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE); // 帧数 mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL); // 关键帧间隔时间 try { mEncoder = MediaCodec.createEncoderByType(MIME_TYPE); mEncoder.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); mSurface = mEncoder.createInputSurface(); mEncoder.start(); mediaMuxer = new MediaMuxer(filePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); } catch (IOException e) { e.printStackTrace(); } }
// Override configureCodec to provide the surface. @Override protected void configureCodec( MediaCodec codec, boolean codecIsAdaptive, android.media.MediaFormat format, MediaCrypto crypto) { maybeSetMaxInputSize(format, codecIsAdaptive); codec.configure(format, surface, crypto, 0); }
protected void renderOutputBuffer(MediaCodec codec, int bufferIndex) { maybeNotifyVideoSizeChanged(); TraceUtil.beginSection("releaseOutputBuffer"); codec.releaseOutputBuffer(bufferIndex, true); TraceUtil.endSection(); codecCounters.renderedOutputBufferCount++; renderedFirstFrame = true; maybeNotifyDrawnToSurface(); }
/** @param frameCallback */ private final void handleOutputVideo(final IFrameCallback frameCallback) { // if (DEBUG) Log.v(TAG, "handleDrainVideo:"); while (mIsRunning && !mVideoOutputDone) { final int decoderStatus = mVideoMediaCodec.dequeueOutputBuffer(mVideoBufferInfo, TIMEOUT_USEC); if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { return; } else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { mVideoOutputBuffers = mVideoMediaCodec.getOutputBuffers(); if (DEBUG) Log.d(TAG, "INFO_OUTPUT_BUFFERS_CHANGED:"); } else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { final MediaFormat newFormat = mVideoMediaCodec.getOutputFormat(); if (DEBUG) Log.d(TAG, "video decoder output format changed: " + newFormat); } else if (decoderStatus < 0) { throw new RuntimeException( "unexpected result from video decoder.dequeueOutputBuffer: " + decoderStatus); } else { // decoderStatus >= 0 boolean doRender = false; if (mVideoBufferInfo.size > 0) { doRender = (mVideoBufferInfo.size != 0) && !internal_write_video( mVideoOutputBuffers[decoderStatus], 0, mVideoBufferInfo.size, mVideoBufferInfo.presentationTimeUs); if (doRender) { if (!frameCallback.onFrameAvailable(mVideoBufferInfo.presentationTimeUs)) mVideoStartTime = adjustPresentationTime( mVideoSync, mVideoStartTime, mVideoBufferInfo.presentationTimeUs); } } mVideoMediaCodec.releaseOutputBuffer(decoderStatus, doRender); if ((mVideoBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { if (DEBUG) Log.d(TAG, "video:output EOS"); synchronized (mSync) { mVideoOutputDone = true; mSync.notifyAll(); } } } } }
// Release a dequeued output buffer back to the codec for re-use. Return // false if the codec is no longer operable. private boolean releaseOutputBuffer(int index) { checkOnMediaCodecThread(); try { mediaCodec.releaseOutputBuffer(index, useSurface); return true; } catch (IllegalStateException e) { Logging.e(TAG, "releaseOutputBuffer failed", e); return false; } }
void setup() { int width = 0, height = 0; extractor = new MediaExtractor(); try { extractor.setDataSource(mVideoFile.getPath()); } catch (IOException e) { return; } for (int i = 0; i < extractor.getTrackCount(); i++) { MediaFormat format = extractor.getTrackFormat(i); String mime = format.getString(MediaFormat.KEY_MIME); width = format.getInteger(MediaFormat.KEY_WIDTH); height = format.getInteger(MediaFormat.KEY_HEIGHT); if (mime.startsWith("video/")) { extractor.selectTrack(i); try { decoder = MediaCodec.createDecoderByType(mime); } catch (IOException e) { continue; } // Decode to surface // decoder.configure(format, surface, null, 0); // Decode to offscreen surface surface = new CtsMediaOutputSurface(width, height); mMatBuffer = new MatBuffer(width, height); decoder.configure(format, surface.getSurface(), null, 0); break; } } if (decoder == null) { Log.e("VideoDecoderForOpenCV", "Can't find video info!"); return; } valid = true; }
private void release() { if (mEncoder != null) { mEncoder.stop(); mEncoder.release(); mEncoder = null; } if (mVirtualDisplay != null) { mVirtualDisplay.release(); } if (displayManager != null) { displayManager = null; } if (mediaMuxer != null) { mediaMuxer.stop(); mediaMuxer.release(); mediaMuxer = null; } }
private boolean queueInputBuffer(int inputBufferIndex, int size, long timestampUs) { checkOnMediaCodecThread(); try { inputBuffers[inputBufferIndex].position(0); inputBuffers[inputBufferIndex].limit(size); mediaCodec.queueInputBuffer(inputBufferIndex, 0, size, timestampUs, 0); return true; } catch (IllegalStateException e) { Logging.e(TAG, "decode failed", e); return false; } }
/** * Checks the video file to see if the contents match our expectations. We decode the video to a * Surface and check the pixels with GL. */ private void checkVideoFile(VideoChunks inputData) { OutputSurface surface = null; MediaCodec decoder = null; mLargestColorDelta = -1; if (VERBOSE) Log.d(TAG, "checkVideoFile"); try { surface = new OutputSurface(mWidth, mHeight); MediaFormat format = inputData.getMediaFormat(); decoder = MediaCodec.createDecoderByType(MIME_TYPE); decoder.configure(format, surface.getSurface(), null, 0); decoder.start(); int badFrames = checkVideoData(inputData, decoder, surface); if (badFrames != 0) { fail("Found " + badFrames + " bad frames"); } } finally { if (surface != null) { surface.release(); } if (decoder != null) { decoder.stop(); decoder.release(); } Log.i(TAG, "Largest color delta: " + mLargestColorDelta); } }
/** * @param codec * @param extractor * @param inputBuffers * @param presentationTimeUs * @param isAudio */ protected boolean internal_process_input( final MediaCodec codec, final MediaExtractor extractor, final ByteBuffer[] inputBuffers, final long presentationTimeUs, final boolean isAudio) { // if (DEBUG) Log.v(TAG, "internal_process_input:presentationTimeUs=" + presentationTimeUs); boolean result = true; while (mIsRunning) { final int inputBufIndex = codec.dequeueInputBuffer(TIMEOUT_USEC); if (inputBufIndex == MediaCodec.INFO_TRY_AGAIN_LATER) break; if (inputBufIndex >= 0) { final int size = extractor.readSampleData(inputBuffers[inputBufIndex], 0); if (size > 0) { codec.queueInputBuffer(inputBufIndex, 0, size, presentationTimeUs, 0); } result = extractor.advance(); // return false if no data is available break; } } return result; }