// started from https://github.com/google/grafika/blob/f3c8c3dee60153f471312e21acac8b3a3cddd7dc/src/com/android/grafika/VideoEncoderCore.java /* * Copyright 2014 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.cine.android.streaming; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaFormat; import android.util.Log; import android.view.Surface; import java.io.IOException; /** * This class wraps up the core components used for surface-input video encoding. * <p/> * Once created, frames are fed to the input surface. Remember to provide the presentation * time stamp, and always call drainEncoder() before swapBuffers() to ensure that the * producer side doesn't get backed up. * <p/> * This class is not thread-safe, with one exception: it is valid to use the input surface * on one thread, and drain the output on a different thread. */ public class VideoEncoderCore extends AndroidEncoder { private static final String TAG = "VideoEncoderCore"; private static final boolean VERBOSE = false; // TODO: these ought to be configurable as well private static final String MIME_TYPE = "video/avc"; // H.264 Advanced Video Coding private static final int FRAME_RATE = 15; // 15fps private static final int IFRAME_INTERVAL = 3; // 3 seconds between I-frames private Surface mInputSurface; /** * Configures encoder and muxer state, and prepares the input Surface. */ public VideoEncoderCore(Muxer muxer) throws IOException { super(muxer); EncodingConfig config = muxer.getConfig(); mBufferInfo = new MediaCodec.BufferInfo(); MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, config.getWidth(), config.getHeight()); // Set some properties. Failing to specify some of these can cause the MediaCodec // configure() call to throw an unhelpful exception. format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); format.setInteger(MediaFormat.KEY_BIT_RATE, config.getBitrate()); format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL); if (VERBOSE) Log.d(TAG, "format: " + format); // Create a MediaCodec encoder, and configure it with our format. Get a Surface // we can use for input and wrap it with a class that handles the EGL work. mEncoder = MediaCodec.createEncoderByType(MIME_TYPE); mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); mInputSurface = mEncoder.createInputSurface(); mEncoder.start(); // Create a MediaMuxer. We can't add the video track and start() the muxer here, // because our MediaFormat doesn't have the Magic Goodies. These can only be // obtained from the encoder after it has started processing data. // // We're not actually interested in multiplexing audio. We just want to convert // the raw H.264 elementary stream we get from MediaCodec into a .mp4 file. mTrackIndex = -1; } /** * Returns the encoder's input surface. */ public Surface getInputSurface() { return mInputSurface; } @Override protected boolean isSurfaceInputEncoder() { return true; } }