先上代碼:
public class ShareREC {
private static final String TAG = ShareREC.class.getSimpleName();
private static final long DEFAULT_TIMEOUT_US = 10000;
private static final int DEFAULT_FRAME_RATE = 20;
private static final int DEFAULT_I_FRAME_INTERVAL = 1;
private static final int DEFAULT_BITRATE = 2000000;
static final int VIDEO_WIDTH = 640;
static final int VIDEO_HEIGHT = 480;
private MediaProjection mMp;
private VirtualDisplay mVd;
private volatile boolean mRunning;
public void start(MediaProjection data) {
if (mRunning) {
release();
return;
}
mMp = data;
Observable.create((Observable.OnSubscribe<Boolean>) subscriber -> {
try {
initEncoder();
initDecode();
mRunning = true;
doRecord();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
if (mOnDecoderCallback != null) {
mOnDecoderCallback.onError();
}
} finally {
release();
}
}).observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe();
}
private MediaCodec mEncoder; //編碼器
private MediaCodec.BufferInfo mEncoderBufferInfo;
private byte[] SPS;
private byte[] PPS;
// 初始化錄屏
private void initEncoder() throws IOException {
MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, VIDEO_WIDTH, VIDEO_HEIGHT);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, DEFAULT_BITRATE);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, DEFAULT_FRAME_RATE);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, DEFAULT_I_FRAME_INTERVAL);
mEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
mEncoder.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
Surface inputSurface = mEncoder.createInputSurface();
mEncoder.start();
mVd = mMp.createVirtualDisplay("ShareREC",
VIDEO_WIDTH, VIDEO_HEIGHT, 1,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
inputSurface, null, null);
mEncoderBufferInfo = new MediaCodec.BufferInfo();
Log.d(TAG, "initEncoder()");
}
private MediaCodec mDeCodec; // 解碼器
private MediaCodec.BufferInfo mDecodeBufferInfo;
private OnDecoderCallback mOnDecoderCallback;
// 初始化解碼器
private void initDecode() throws IOException {
// 獲取硬件編碼器支持的顏色格式牡借,一般是I420或者NV12
mDecodeBufferInfo = new MediaCodec.BufferInfo();
MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, VIDEO_WIDTH, VIDEO_HEIGHT);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
mDeCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
mDeCodec.configure(format, null, null, 0);
mDeCodec.start();
Log.d(TAG, "initDecode()");
}
private void doRecord() {
Log.d(TAG, "doRecord Start");
while (mRunning && mEncoder != null) {
int index = mEncoder.dequeueOutputBuffer(mEncoderBufferInfo, DEFAULT_TIMEOUT_US);
if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat newFormat = mEncoder.getOutputFormat();
// 獲取編碼SPS和PPS信息
SPS = newFormat.getByteBuffer("csd-0").array();
PPS = newFormat.getByteBuffer("csd-1").array();
} else if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
// wait 10ms
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else if (index > 0) {
ByteBuffer buffer = mEncoder.getOutputBuffer(index);
if ((mEncoderBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
mEncoderBufferInfo.size = 0;
}
if (mEncoderBufferInfo.size == 0) {
buffer = null;
}
if (buffer != null) {
buffer.position(mEncoderBufferInfo.offset);
buffer.limit(mEncoderBufferInfo.offset + mEncoderBufferInfo.size);
byte[] h264Bytes;
if (mEncoderBufferInfo.flags == MediaCodec.BUFFER_FLAG_KEY_FRAME) {
// 關鍵幀上添加sps,和pps信息
h264Bytes = new byte[mEncoderBufferInfo.size + SPS.length + PPS.length];
System.arraycopy(SPS, 0, h264Bytes, 0, SPS.length);
System.arraycopy(PPS, 0, h264Bytes, SPS.length, PPS.length);
buffer.get(h264Bytes, SPS.length + PPS.length, mEncoderBufferInfo.size);
} else {
h264Bytes = new byte[mEncoderBufferInfo.size];
buffer.get(h264Bytes, 0, mEncoderBufferInfo.size);
}
decode(h264Bytes);
}
mEncoder.releaseOutputBuffer(index, false);
}
}
Log.d(TAG, "doRecord End");
}
private void decode(byte[] h264Data) {
int inputBufferIndex = mDeCodec.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
if (inputBufferIndex > 0) {
ByteBuffer inputBuffer;
inputBuffer = mDeCodec.getInputBuffer(inputBufferIndex);
if (inputBuffer != null) {
inputBuffer.clear();
inputBuffer.put(h264Data, 0, h264Data.length);
mDeCodec.queueInputBuffer(inputBufferIndex, 0, h264Data.length, 0, 0);
}
}
doDeCode();
}
private void doDeCode() {
int outputBufferIndex = mDeCodec.dequeueOutputBuffer(mDecodeBufferInfo, DEFAULT_TIMEOUT_US);
ByteBuffer outputBuffer;
while (mRunning && outputBufferIndex > 0) {
outputBuffer = mDeCodec.getOutputBuffer(outputBufferIndex);
if (outputBuffer != null) {
outputBuffer.position(0);
outputBuffer.limit(mDecodeBufferInfo.offset + mDecodeBufferInfo.size);
byte[] yuvData = new byte[outputBuffer.remaining()];
outputBuffer.get(yuvData);
MediaFormat mediaFormat = mDeCodec.getOutputFormat();
switch (mediaFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT)) {
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV411Planar:
break;
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV411PackedPlanar:
break;
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
break;
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
yuvData = yuv420spToYuv420P(yuvData);
break;
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
break;
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
default:
break;
}
Log.d(TAG, "decode byte size " + yuvData.length);
if (null != mOnDecoderCallback) {
mOnDecoderCallback.onFrame(yuvData);
}
mDeCodec.releaseOutputBuffer(outputBufferIndex, false);
outputBuffer.clear();
}
outputBufferIndex = mDeCodec.dequeueOutputBuffer(mDecodeBufferInfo, DEFAULT_TIMEOUT_US);
}
}
private static byte[] yuv420spToYuv420P(byte[] yuv420spData) {
byte[] yuv420pData = new byte[ShareREC.VIDEO_WIDTH * ShareREC.VIDEO_HEIGHT * 3 / 2];
int ySize = ShareREC.VIDEO_WIDTH * ShareREC.VIDEO_HEIGHT;
System.arraycopy(yuv420spData, 0, yuv420pData, 0, ySize); //拷貝 Y 分量
for (int j = 0, i = 0; j < ySize / 2; j += 2, i++) {
yuv420pData[ySize + i] = yuv420spData[ySize + j]; //U 分量
yuv420pData[ySize * 5 / 4 + i] = yuv420spData[ySize + j + 1]; //V 分量
}
return yuv420pData;
}
void setDecoderCallback(OnDecoderCallback onDecoderCallback) {
mOnDecoderCallback = onDecoderCallback;
}
public void stop() {
mRunning = false;
Log.d(TAG, "stop");
}
public void release() {
mRunning = false;
if (mEncoder != null) {
mEncoder.stop();
mEncoder.release();
mEncoder = null;
}
if (mVd != null) {
mVd.release();
mVd = null;
}
if (mDeCodec != null) {
mDeCodec.stop();
mDeCodec.release();
mDeCodec = null;
}
if (mMp != null) {
mMp.stop();
}
Log.d(TAG, "release");
}
public interface OnDecoderCallback {
void onFrame(byte[] yuvData);
void onError();
}
}