Android用MediaCodec硬解碼H264流
import android.graphics.ImageFormat;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.view.Surface;
import android.view.SurfaceHolder;
import com.mirkowu.rishunvideoclient.util.LogUtils;
import com.mirkowu.rishunvideoclient.util.StringUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import static android.media.MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR;
/**
* @author by Mirkowu
* @date on 2020/3/25
* @describe
*/
public class VideoDecoder {
private int width;
private int height;
private int mFrameRate = 15;
private static final String VCODEC_MIME = "video/avc";
private MediaCodec mMediaCodec;
private boolean isCodecStart = false;
private SurfaceHolder mSurfaceHolder;
public VideoDecoder(SurfaceHolder surfaceHolder) {
mSurfaceHolder = surfaceHolder;
}
public VideoDecoder setWidth(int width) {
this.width = width;
return this;
}
public VideoDecoder setHeight(int height) {
this.height = height;
return this;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getFrameRate() {
return mFrameRate;
}
public VideoDecoder setFrameRate(int frameRate) {
this.mFrameRate = frameRate;
return this;
}
/**
* 初始話 最好放在 surfaceCreated 後
* @param surface
*/
public void initMediaCodec(Surface surface) {
LogUtils.d("initMediaCodec");
int bitrate = width * height * mFrameRate * 5;
//另一種設置比特率,N*width*height,N可設置爲1 2 3或者1 3 5等,來區分低/中/高的碼率。
// int bitrate = width * height * 5;
try {
//獲取編碼器
mMediaCodec = MediaCodec.createDecoderByType(VCODEC_MIME);
MediaFormat mediaFormat = MediaFormat.createVideoFormat(VCODEC_MIME, width, height);
//碼率
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
//幀率
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
//關鍵幀間隔時間設置
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
// 如有需要 要設置sps 和pps 不然畫面顯示不出來等問題
// final byte[] header_sps = {0, 0, 0, 1, 103, 66, 0, 42, (byte) 149, (byte) 168, 30, 0, (byte) 137, (byte) 249, 102, (byte) 224, 32, 32, 32, 64};
// final byte[] header_pps = {0, 0, 0, 1, 104, (byte) 206, 60, (byte) 128, 0, 0, 0, 1, 6, (byte) 229, 1, (byte) 151, (byte) 128};
// mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps));
// mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
mMediaCodec.configure(mediaFormat, surface, null, 0);
} catch (IOException e) {
e.printStackTrace();
}
LogUtils.d("Video Config >>> FrameRate:" + mFrameRate + ",width:" + width + ",height:" + height);
}
public void start() {
if (mMediaCodec != null) {
isCodecStart = true;
LogUtils.d("mMediaCodec.start()");
mMediaCodec.start();
}
}
public void stop() {
if (mMediaCodec != null) {
LogUtils.d("mMediaCodec.stop()");
mMediaCodec.stop();
isCodecStart = false;
}
}
public void release() {
if (mMediaCodec != null) {
LogUtils.d("mMediaCodec.release()");
mMediaCodec.release();
mMediaCodec = null;
}
}
/**
* 解碼
* @param buf
* @return
*/
public boolean onFrame(byte[] buf) {
LogUtils.video("onFrame Thread:" + Thread.currentThread().getId() + "mediaCodec =null:" + (mMediaCodec == null));
// Get input buffer index
try {
ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
//ByteBuffer[] outputBuffers = mMediaCodec.getOutputBuffers();
int inputBufferIndex = mMediaCodec.dequeueInputBuffer(-1);
// LogUtils.d("onFrame index:" + inputBufferIndex);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(buf, 0, buf.length);
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, inputBuffers[inputBufferIndex].position(),
System.nanoTime() / 1000, 0);
} else {
return false;
}
// Get output buffer index
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0) {
mMediaCodec.releaseOutputBuffer(outputBufferIndex, true);//解碼 第二個參數一定要爲true 用於顯示在Surface上
outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
LogUtils.video("onFrame end");
return true;
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
return false;
}
}
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import androidx.appcompat.app.AppCompatActivity;
import com.mirkowu.rishunvideoclient.socketUtil.RxSocketManager;
import com.mirkowu.rishunvideoclient.socketUtil.SocketType;
import com.mirkowu.rishunvideoclient.util.LogUtils;
import com.mirkowu.rishunvideoclient.video.VideoDecoder;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
public class PlayerActivity extends AppCompatActivity {
public static final String KEY_IP = "KEY_IP";
public static void start(Context context, String ip) {
Intent starter = new Intent(context, PlayerActivity.class);
starter.putExtra(KEY_IP, ip);
context.startActivity(starter);
}
private int videoWidth = 640;
private int videoHeight = 480;
SurfaceView mSurfaceView;
SurfaceHolder mSurfaceHolder;
VideoDecoder mVideoDecoder;
ExecutorService executor = Executors.newSingleThreadExecutor();
LinkedBlockingQueue<byte[]> mQueue = new LinkedBlockingQueue<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
mSurfaceView = findViewById(R.id.mSurfaceView);
mSurfaceHolder = mSurfaceView.getHolder();
//這裏用UDP作爲數據來源 實際項目看情況獲取
String ip = getIntent().getStringExtra(KEY_IP);
RxSocketManager.getInstance()
.setClient(SocketType.UDP, ip, 8888)
.setResultCallback(resultCallback).build();
initDecoder();
initHolder();
}
private void initDecoder() {
mVideoDecoder = new VideoDecoder(mSurfaceHolder)
.setWidth(videoWidth)
.setHeight(videoHeight);
}
private void initHolder() {
mSurfaceHolder.setKeepScreenOn(true);//設置屏幕常亮
mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
mVideoDecoder.initMediaCodec(holder.getSurface());
mVideoDecoder.start();
if (RxSocketManager.getInstance().getSocketType() == SocketType.UDP) {
try {
RxSocketManager.getInstance().send("第一次".getBytes("utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mVideoDecoder.stop();
}
});
}
RxSocketManager.ResultCallback resultCallback = new RxSocketManager.ResultCallback() {
@Override
public void onSucceed(byte[] data) {
mVideoDecoder.onFrame(data);
}
@Override
public void onFailed(Throwable t) {
}
};
@Override
protected void onDestroy() {
super.onDestroy();
RxSocketManager.getInstance().close();
}
}