private void FeedClipToEncoder(SamplerClip clip) { mLastSampleTime = 0; MediaCodec decoder = null; MediaExtractor extractor = SetupExtractorForClip(clip); if (extractor == null) { return; } int trackIndex = GetVideoTrackIndex(extractor); extractor.SelectTrack(trackIndex); MediaFormat clipFormat = extractor.GetTrackFormat(trackIndex); if (clip.getStartTime() != -1) { extractor.SeekTo(clip.getStartTime() * 1000, MediaExtractorSeekTo.PreviousSync); clip.setStartTime(extractor.SampleTime / 1000); } try { decoder = MediaCodec.CreateDecoderByType(MediaHelper.MIME_TYPE_AVC); mOutputSurface = new OutputSurface(); decoder.Configure(clipFormat, mOutputSurface.Surface, null, 0); decoder.Start(); ResampleVideo(extractor, decoder, clip); } catch (System.Exception e) { } finally { if (mOutputSurface != null) { mOutputSurface.Release(); } if (decoder != null) { decoder.Stop(); decoder.Release(); } if (extractor != null) { extractor.Release(); extractor = null; } } }
private MediaExtractor SetupExtractorForClip(SamplerClip clip) { MediaExtractor extractor = new MediaExtractor(); try { extractor.SetDataSource(clip.getUri().ToString()); } catch (System.Exception e) { return(null); } return(extractor); }
private void ResampleVideo(MediaExtractor extractor, MediaCodec decoder, SamplerClip clip) { ByteBuffer[] decoderInputBuffers = decoder.GetInputBuffers(); ByteBuffer[] encoderOutputBuffers = mEncoder.GetOutputBuffers(); MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); int inputChunk = 0; int outputCount = 0; long endTime = clip.getEndTime(); if (endTime == -1) { endTime = clip.getVideoDuration(); } bool outputDoneNextTimeWeCheck = false; bool outputDone = false; bool inputDone = false; bool decoderDone = false; while (!outputDone) { // Feed more data to the decoder. if (!inputDone) { int inputBufIndex = decoder.DequeueInputBuffer(TIMEOUT_USEC); if (inputBufIndex >= 0) { if (extractor.SampleTime / 1000 >= endTime) { // End of stream -- send empty frame with EOS flag set. decoder.QueueInputBuffer(inputBufIndex, 0, 0, 0L, MediaCodecBufferFlags.EndOfStream); inputDone = true; } else { // Copy a chunk of input to the decoder. The first chunk should have // the BUFFER_FLAG_CODEC_CONFIG flag set. ByteBuffer inputBuf = decoderInputBuffers[inputBufIndex]; inputBuf.Clear(); int sampleSize = extractor.ReadSampleData(inputBuf, 0); if (sampleSize < 0) { decoder.QueueInputBuffer(inputBufIndex, 0, 0, 0, MediaCodecBufferFlags.EndOfStream); } else { decoder.QueueInputBuffer(inputBufIndex, 0, sampleSize, extractor.SampleTime, 0); extractor.Advance(); } inputChunk++; } } } // Assume output is available. Loop until both assumptions are false. bool decoderOutputAvailable = !decoderDone; bool encoderOutputAvailable = true; while (decoderOutputAvailable || encoderOutputAvailable) { // Start by draining any pending output from the encoder. It's important to // do this before we try to stuff any more data in. int encoderStatus = mEncoder.DequeueOutputBuffer(info, TIMEOUT_USEC); if (encoderStatus == (int)MediaCodecInfoState.TryAgainLater) { encoderOutputAvailable = false; } else if (encoderStatus == (int)MediaCodecInfoState.OutputBuffersChanged) { encoderOutputBuffers = mEncoder.GetOutputBuffers(); } else if (encoderStatus == (int)MediaCodecInfoState.OutputFormatChanged) { MediaFormat newFormat = mEncoder.OutputFormat; mTrackIndex = mMuxer.AddTrack(newFormat); mMuxer.Start(); mMuxerStarted = true; } else if (encoderStatus < 0) { // fail( "unexpected result from encoder.dequeueOutputBuffer: " + encoderStatus ); } else { // encoderStatus >= 0 ByteBuffer encodedData = encoderOutputBuffers[encoderStatus]; if (encodedData == null) { // fail( "encoderOutputBuffer " + encoderStatus + " was null" ); } // Write the data to the output "file". if (info.Size != 0) { encodedData.Position(info.Offset); encodedData.Limit(info.Offset + info.Size); outputCount++; mMuxer.WriteSampleData(mTrackIndex, encodedData, info); } outputDone = (info.Flags & MediaCodecBufferFlags.EndOfStream) != 0; mEncoder.ReleaseOutputBuffer(encoderStatus, false); } if (outputDoneNextTimeWeCheck) { outputDone = true; } if (encoderStatus != (int)MediaCodecInfoState.TryAgainLater) { // Continue attempts to drain output. continue; } // Encoder is drained, check to see if we've got a new frame of output from // the decoder. (The output is going to a Surface, rather than a ByteBuffer, // but we still get information through BufferInfo.) if (!decoderDone) { int decoderStatus = decoder.DequeueOutputBuffer(info, TIMEOUT_USEC); if (decoderStatus == (int)MediaCodecInfoState.TryAgainLater) { decoderOutputAvailable = false; } else if (decoderStatus == (int)MediaCodecInfoState.OutputBuffersChanged) { // decoderOutputBuffers = decoder.GetOutputBuffers(); } else if (decoderStatus == (int)MediaCodecInfoState.OutputFormatChanged) { // expected before first buffer of data MediaFormat newFormat = decoder.OutputFormat; } else if (decoderStatus < 0) { // fail( "unexpected result from decoder.dequeueOutputBuffer: " + decoderStatus ); } else { // decoderStatus >= 0 // The ByteBuffers are null references, but we still get a nonzero // size for the decoded data. bool doRender = (info.Size != 0); // As soon as we call ReleaseOutputBuffer, the buffer will be forwarded // to SurfaceTexture to convert to a texture. The API doesn't // guarantee that the texture will be available before the call // returns, so we need to wait for the onFrameAvailable callback to // fire. If we don't wait, we risk rendering from the previous frame. decoder.ReleaseOutputBuffer(decoderStatus, doRender); if (doRender) { mOutputSurface.AwaitNewImage(true); mOutputSurface.DrawImage(); // Send it to the encoder. long nSecs = info.PresentationTimeUs * 1000; if (clip.getStartTime() != -1) { nSecs = (info.PresentationTimeUs - (clip.getStartTime() * 1000)) * 1000; } nSecs = Java.Lang.Math.Max(0, nSecs); mEncoderPresentationTimeUs += (nSecs - mLastSampleTime); mLastSampleTime = nSecs; mInputSurface.SetPresentationTime(mEncoderPresentationTimeUs); mInputSurface.SwapBuffers(); } if ((info.Flags & MediaCodecBufferFlags.EndOfStream) != 0) { // mEncoder.signalEndOfInputStream(); outputDoneNextTimeWeCheck = true; } } } } } if (inputChunk != outputCount) { // throw new RuntimeException( "frame lost: " + inputChunk + " in, " + outputCount + " out" ); } }
/* * public void setInput( Uri intputUri ) { mInputUri = intputUri; } */ public void AddSamplerClip(SamplerClip clip) { mClips.Add(clip); }