package com.laifeng.sopcastsdk.video;
import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import com.laifeng.sopcastsdk.configuration.VideoConfiguration;
import com.laifeng.sopcastsdk.constant.SopCastConstant;
import com.laifeng.sopcastsdk.mediacodec.VideoMediaCodec;
import com.laifeng.sopcastsdk.utils.SopCastLog;
import java.nio.ByteBuffer;
import java.util.concurrent.locks.ReentrantLock;
@TargetApi(18)
public class MyRecorder {
private MediaCodec mMediaCodec;
private InputSurface mInputSurface;
private OnVideoEncodeListener mListener;
private boolean mPause;
private MediaCodec.BufferInfo mBufferInfo;
private VideoConfiguration mConfiguration;
private HandlerThread mHandlerThread;
private Handler mEncoderHandler;
private ReentrantLock encodeLock = new ReentrantLock();
private volatile boolean isStarted;
public MyRecorder(VideoConfiguration configuration) {
mConfiguration = configuration;
}
public void setVideoEncodeListener(OnVideoEncodeListener listener) {
mListener = listener;
}
public void setPause(boolean pause) {
mPause = pause;
}
public void prepareEncoder() {
if (mMediaCodec != null || mInputSurface != null) {
throw new RuntimeException("prepareEncoder called twice?");
}
mMediaCodec = VideoMediaCodec.getVideoMediaCodec(mConfiguration);
mHandlerThread = new HandlerThread("SopCastEncode");
mHandlerThread.start();
mEncoderHandler = new Handler(mHandlerThread.getLooper());
mBufferInfo = new MediaCodec.BufferInfo();
isStarted = true;
}
public boolean firstTimeSetup() {
if (mMediaCodec == null || mInputSurface != null) {
return false;
}
try {
mInputSurface = new InputSurface(mMediaCodec.createInputSurface());
mMediaCodec.start();
} catch (Exception e) {
releaseEncoder();
throw (RuntimeException)e;
}
return true;
}
public void startSwapData() {
mEncoderHandler.post(swapDataRunnable);
}
public void makeCurrent() {
mInputSurface.makeCurrent();
}
public void swapBuffers() {
if (mMediaCodec == null || mPause) {
return;
}
mInputSurface.swapBuffers();
mInputSurface.setPresentationTime(System.nanoTime());
}
private Runnable swapDataRunnable = new Runnable() {
@Override
public void run() {
drainEncoder();
}
};
public void stop() {
if (!isStarted) {
return;
}
isStarted = false;
mEncoderHandler.removeCallbacks(null);
mHandlerThread.quit();
encodeLock.lock();
releaseEncoder();
encodeLock.unlock();
}
private void releaseEncoder() {
if (mMediaCodec != null) {
mMediaCodec.signalEndOfInputStream();
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
}
if (mInputSurface != null) {
mInputSurface.release();
mInputSurface = null;
}
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public boolean setRecorderBps(int bps) {
if (mMediaCodec == null || mInputSurface == null) {
return false;
}
SopCastLog.d(SopCastConstant.TAG, "bps :" + bps * 1024);
Bundle bitrate = new Bundle();
bitrate.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bps * 1024);
mMediaCodec.setParameters(bitrate);
return true;
}
private void drainEncoder() {
ByteBuffer[] outBuffers = mMediaCodec.getOutputBuffers();
while (isStarted) {
encodeLock.lock();
if(mMediaCodec != null) {
int outBufferIndex = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 12000);
if (outBufferIndex >= 0) {
ByteBuffer bb = outBuffers[outBufferIndex];
if (mListener != null) {
mListener.onVideoEncode(bb, mBufferInfo);
}
mMediaCodec.releaseOutputBuffer(outBufferIndex, false);
} else {
try {
// wait 10ms
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
encodeLock.unlock();
} else {
encodeLock.unlock();
break;
}
}
}
}