在前面,我們已經通過MediaCodec實現了一個AvcDecoder,可以播放H264碼流數據
但是AvcDecoder需要手動和Surface綁定,仍然不太方便
這篇文章我們進一步封裝,將MediaCodec和SurfaceView集合,實現一個帶H264解碼功能的控件
AvcPlayer源碼
//集成了AvcDecoder解碼功能的SurfaceView
//使用AvcDecoder,則需要手動綁定一個SurfaceView
public class AvcPlayer extends SurfaceView {
MediaCodec mediaCodec;
Callback callback;
int width;
int height;
@Getter
boolean playing = false;
public AvcPlayer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void init(int width, int height, Callback callback) {
this.callback = callback;
this.width = width;
this.height = height;
}
@SneakyThrows
private void configureMediaCodec() {
mediaCodec = MediaCodec.createDecoderByType("video/avc");
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);
mediaCodec.configure(mediaFormat, getHolder().getSurface(), null, 0);
mediaCodec.start();
}
public void decodeNalu(byte[] nalu) {
if (!playing) return;
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
if (inputBufferIndex < 0)
return;
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(nalu, 0, nalu.length);
mediaCodec.queueInputBuffer(inputBufferIndex, 0, nalu.length, 0, 0);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 100);
while (outputBufferIndex >= 0) {
mediaCodec.releaseOutputBuffer(outputBufferIndex, true);
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
if (callback != null)
callback.onFrameUpdate(nalu);
}
public void startPreview() {
configureMediaCodec();
playing = true;
}
public void stopPreview() {
playing = false;
mediaCodec.stop();
mediaCodec.release();
mediaCodec = null;
}
public interface Callback {
void onFrameUpdate(byte[] frame);
}
}
AvcPlayer使用方法
AvcPlayer player;
//開始播放,建議在onResume中延時2秒執行,等待SurfaceView初始化完成
player.init(width, height, callback);
player.startPreview();
//解碼並播放H264流數據,視頻源可以來自文件、網絡、設備取流等任何方式,只要字節集是由若干Nalu數據組成即可
player.decodeNalu(byte[] nalu);
//停止播放,在onPause中執行
player.stopPreview();