记录一下编码SurfaceView画板内容的骚操作
一般来说,视频编码是直接获取视频流进行编码,摄像头和屏幕共享画面属于比较常规的视频流。除此之外,MediaCodec提供了setInputSurface()
方法,可以直接对Surface进行编码,但这个Surface需要是一个persistent对象,由MediaCodec.createPersistentInputSurface()
创建。
虽然不能将surfaceView的surface直接进行编码,但是可以让persistent surface获取到画板的画面,进行同步绘制。
实现画板:(代码省略,参照文章:用SurfaceView实现的画板,书写和擦除)
private float mX;
private float mY;
private Paint mPaint = null;
private Path mPath = null;
// 线程结束标志位
boolean mLoop = true;
SurfaceHolder mSurfaceHolder = null;
Canvas mCanvas, canvas;
private Surface surface;
......
//传入持久化Surface,在Draw时进行同步绘制
public void setSurface(Surface surface) {
this.surface = surface;
}
......
// 绘图
private void Draw() {
mCanvas = mSurfaceHolder.lockCanvas();
canvas = surface.lockCanvas(null);
try {
canvas.drawPath(mPath, mPaint); //同步绘制
mCanvas.drawPath(mPath, mPaint);
} catch (Exception e) {
} finally {
if (canvas != null) {
surface.unlockCanvasAndPost(canvas);
}
if (mCanvas != null) {
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
......
编解码使用
private SurfaceView svDecode;
private PaintSurfaceView svEncode;
private MediaCodec mediaCodec, mediaDecode;
private Surface inputSurface;
private Button stop;
private String TAG = "yforyoung";
private final static ArrayBlockingQueue<byte[]> mOutputDataQueue = new ArrayBlockingQueue<>(8);
private MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 640, 480);
@TargetApi(Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
@SuppressLint("ClickableViewAccessibility")
private void init() {
svDecode = findViewById(R.id.sv_decode);
svEncode = findViewById(R.id.sv_encode);
stop = findViewById(R.id.stop);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 1024);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25);
//设置surface
inputSurface = MediaCodec.createPersistentInputSurface();
svEncode.setSurface(inputSurface);
//surface创建
svEncode.setVisibility(View.VISIBLE);
svDecode.setVisibility(View.VISIBLE);
stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mediaCodec.stop();
mediaDecode.stop();
mediaDecode.release();
mediaCodec.release();
}
});
svEncode.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Log.i(TAG, "surfaceCreated: ");
encode();//开始编码
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
});
svDecode.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Log.i(TAG, "surfaceCreated: ");
decode(surfaceHolder.getSurface());
//解码,传入用于显示画面的surface
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
});
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void encode() {
try {
mediaCodec = MediaCodec.createEncoderByType("video/avc");
} catch (IOException e) {
e.printStackTrace();
}
mediaCodec.setCallback(new MediaCodec.Callback() {
@Override
public void onInputBufferAvailable(MediaCodec codec, int index) {
}
@Override
public void onOutputBufferAvailable(MediaCodec codec, int index, MediaCodec.BufferInfo info) {
ByteBuffer outputBuffer = codec.getOutputBuffer(index);
//这里将编码后的流存入byte[]队列,也可以在这里将画面输出到文件或者发送到远端
if (outputBuffer != null && info.size > 0) {
byte[] buffer = new byte[outputBuffer.remaining()];
outputBuffer.get(buffer);
boolean result = mOutputDataQueue.offer(buffer);
Log.i(TAG, "onOutputBufferAvailable: offer");
if (!result) {
Log.e(TAG, "onOutputBufferAvailable: offer to queue failed");
}
}
codec.releaseOutputBuffer(index, false);
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
mediaCodec.release();
}
}
@Override
public void onError(MediaCodec codec, MediaCodec.CodecException e) {
}
@Override
public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
}
});
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mediaCodec.setInputSurface(inputSurface);//设置输入surface必须在configure之后,start之前
mediaCodec.start();
}
private void decode(Surface surface) {
try {
mediaDecode = MediaCodec.createDecoderByType("video/avc");
mediaDecode.setCallback(new MediaCodec.Callback() {
@Override
public void onInputBufferAvailable(MediaCodec mediaCodec, int i) {
//从队列中获取视频流的byte[] 解码成画面显示到surface
ByteBuffer byteBuffer = mediaCodec.getInputBuffer(i);
byteBuffer.clear();
byte[] dataSource = mOutputDataQueue.poll();
int length = 0;
if (dataSource != null) {
byteBuffer.put(dataSource);
length = dataSource.length;
}
mediaDecode.queueInputBuffer(i, 0, length, 0, 0);
}
@Override
public void onOutputBufferAvailable(MediaCodec mediaCodec, int i, MediaCodec.BufferInfo bufferInfo) {
ByteBuffer outputBuffer = mediaDecode.getOutputBuffer(i);
if (outputBuffer != null && bufferInfo.size > 0) {
byte[] buffer = new byte[outputBuffer.remaining()];
outputBuffer.get(buffer);
}
mediaDecode.releaseOutputBuffer(i, true);
}
@Override
public void onError(MediaCodec mediaCodec, MediaCodec.CodecException e) {
}
@Override
public void onOutputFormatChanged(MediaCodec mediaCodec, MediaFormat mediaFormat) {
}
});
mediaDecode.configure(mediaFormat, surface, null, 0);
mediaDecode.start();
} catch (IOException e) {
e.printStackTrace();
}
}