public class RtspDecoder {
private static final String TAG = RtspDecoder.class.getSimpleName();
//处理音视频的编解码的类MediaCodec
private MediaCodec video_decoder;
//显示画面的Surface
private Surface surface;
// 0: live, 1: playback, 2: local file
private int state = 0;
//视频数据
private BlockingQueue<byte[]> video_data_Queue = new ArrayBlockingQueue<byte[]>(10000);
//音频数据
private BlockingQueue<byte[]> audio_data_Queue = new ArrayBlockingQueue<byte[]>(10000);
private boolean isReady = false;
private int fps = 0;
private ByteBuffer[] inputBuffers;
private ByteBuffer[] outputBuffers;
private MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
private int frameCount = 0;
private long deltaTime = 0;
private long counterTime = System.currentTimeMillis();
private boolean isRuning = false;
public RtspDecoder(Surface surface, int playerState) {
this.surface = surface;
this.state = playerState;
}
public void stopRunning() {
video_data_Queue.clear();
audio_data_Queue.clear();
}
//添加视频数据
public void setVideoData(byte[] data) {
try {
video_data_Queue.put(data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//添加音频数据
public void setAudioData(byte[] data) {
try {
audio_data_Queue.put(data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public int getFPS() {
return fps;
}
public void initial(byte[] sps) throws IOException {
MediaFormat format = null;
boolean isVGA = true;
// use sps to check isVGAorHD?
byte[] header_sps = {0, 0, 0, 1, 103, 100, 64, 41, -84, 44, -88, 10, 2, -1, -107};
for (int i = 0; i < sps.length; i++) {
if (header_sps[i] != sps[i]) {
isVGA = false;
break;
}
}
if (isVGA) {
format = MediaFormat.createVideoFormat("video/avc", 1280, 640);
byte[] header_pps = {0, 0, 0, 1, 104, -18, 56, -128};
format.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps));
format.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 640 * 360);
} else {
format = MediaFormat.createVideoFormat("video/avc", 1280, 640);
byte[] header_HD_sps = {0, 0, 0, 1, 103, 100, 64, 41, -84, 44, -88, 5, 0, 91, -112};
byte[] header_pps = {0, 0, 0, 1, 104, -18, 56, -128};
format.setByteBuffer("csd-0", ByteBuffer.wrap(header_HD_sps));
format.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
// format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1280 * 720);
}
if (video_decoder != null) {
video_decoder.stop();
video_decoder.release();
video_decoder = null;
}
video_decoder = MediaCodec.createDecoderByType("video/avc");
if (video_decoder == null) {
return;
}
video_decoder.configure(format, surface, null, 0);
video_decoder.start();
inputBuffers = video_decoder.getInputBuffers();
outputBuffers = video_decoder.getOutputBuffers();
frameCount = 0;
deltaTime = 0;
isRuning = true;
runDecodeVideoThread();
}
boolean flag = true;
private void runDecodeVideoThread() {
Thread t = new Thread() {
@SuppressLint("NewApi")
public void run() {
while (isRuning) {
if(flag){
Log.i(TAG,"tag 1");
}
int inIndex = -1;
try {
inIndex = video_decoder.dequeueInputBuffer(-1);
} catch (Exception e) {
return;
}
try {
if (inIndex >= 0) {
ByteBuffer buffer = inputBuffers[inIndex];
buffer.clear();
if (!video_data_Queue.isEmpty()) {
byte[] data;
data = video_data_Queue.take();
buffer.put(data);
if (state == 0) {
video_decoder.queueInputBuffer(inIndex, 0, data.length, 66, 0);
} else {
video_decoder.queueInputBuffer(inIndex, 0, data.length, 33, 0);
}
} else {
if (state == 0) {
video_decoder.queueInputBuffer(inIndex, 0, 0, 66, 0);
} else {
video_decoder.queueInputBuffer(inIndex, 0, 0, 33, 0);
}
}
} else {
video_decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}
int outIndex = video_decoder.dequeueOutputBuffer(info, 0);
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
outputBuffers = video_decoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
isReady = true;
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
break;
default:
video_decoder.releaseOutputBuffer(outIndex, true);
frameCount++;
deltaTime = System.currentTimeMillis() - counterTime;
if (deltaTime > 1000) {
fps = (int) (((float) frameCount / (float) deltaTime) * 1000);
counterTime = System.currentTimeMillis();
frameCount = 0;
}
break;
}
//所有流数据解码完成,可以进行关闭等操作
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
// Log.e(Constant.LOG_TAG, "BUFFER_FLAG_END_OF_STREAM");
}
} catch (Exception e) {
e.printStackTrace();
}
if(flag){
Log.i(TAG,"tag 2");
flag = false;
}
}
}
};
t.start();
}
}
public class H264Stream {
private final static String TAG = "H24Stream";
private MediaCodec mMeidaCodec;
private SurfaceView mSurfaceView;
private ByteBuffer[] inputBuffers;
private Handler mHandler;
private final static int TIME_INTERNAL = 30;
private LinkedBlockingDeque<bufferUnit> bufferQueue = new LinkedBlockingDeque<bufferUnit>();
private int picWidth = 768;
private int picHeight = 432;
private HandlerThread thread;
private boolean flag = true;
private int totalcount = 0;
private boolean initial = true;
public long lastTimeStamp = 0;
private long startMill = Calendar.getInstance().getTimeInMillis();
private final static String MIME_TYPE = "video/avc"; // H.264 Advanced Video 视频格式, video/avc 指的是 H264格式
private IVideoFrameListener videoFrameListener;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public void setVideoFrameListener(IVideoFrameListener videoFrameListener){
this.videoFrameListener = videoFrameListener;
}
public void clearVideoFrameListener(){
this.videoFrameListener = null;
}
public H264Stream(RtspClient.SDPInfo sp) {
// mSDPinfo = sp;
thread = new HandlerThread("H264StreamThread");
thread.start();
mHandler = new Handler(thread.getLooper());
flag = true;
}
private void configMediaDecoder() {
if (Build.VERSION.SDK_INT > 15) {
//创建解码器
getBestMediaCodeC();
MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, picWidth, picHeight);//指定解码类型,数据尺寸
// mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
// MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar);
mMeidaCodec.configure(mediaFormat, mSurfaceView.getHolder().getSurface(), null, 0);//
// mMeidaCodec.configure(mediaFormat, null, null, 0);//
mMeidaCodec.start();
}
}
private void getBestMediaCodeC() {
int numberCodec = MediaCodecList.getCodecCount();
List<String> codeList = new ArrayList<>();
for (int i =0;i<numberCodec;i++){
codeList.add(MediaCodecList.getCodecInfoAt(i).getName());
}
try {
if (codeList.size() > 0){
if (codeList.contains("OMX.qcom.video.decoder.avc")){
mMeidaCodec = MediaCodec.createByCodecName("OMX.qcom.video.decoder.avc");
return;
}else if (codeList.contains("OMX.google.h264.decoder")){
mMeidaCodec = MediaCodec.createByCodecName("OMX.google.h264.decoder");
return;
}
}
mMeidaCodec = MediaCodec.createDecoderByType(MIME_TYPE);
} catch (IOException e) {
try {
mMeidaCodec = MediaCodec.createDecoderByType(MIME_TYPE);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
public void startMediaHardwareDecode() {
mHandler.post(hardwareDecodeThread);
}
private Runnable hardwareDecodeThread = new Runnable() {
@Override
public void run() {
int mCount = 0;
byte[] tmpByte;
int framType;
boolean startKeyFrame = false;
configMediaDecoder();
while (flag) {
try {
if (initial){
int validFrame = 0;
bufferUnit[] objects = new bufferUnit[bufferQueue.size()];
objects = bufferQueue.toArray(objects);
for(int i=0;i<objects.length;i++){
int tempFrameType = (objects[i]).data[4] & 0x1F;
if (tempFrameType == 5 || tempFrameType == 1){
validFrame++;
}
}
if (validFrame < 4){
// initial = false;
Thread.sleep(30);
continue;
}
}
initial = false;
tmpByte = bufferQueue.take().data;
framType = tmpByte[4] & 0x1F;
// if (framType == 5) startKeyFrame = true;
if (framType == 5||framType == 1 ||framType == 6 || framType == 7 || framType == 8) {
if(framType == 1||framType == 5 ){
if (videoFrameListener != null){
videoFrameListener.onFrameChanged(1);
}
long endMill = Calendar.getInstance().getTimeInMillis();
int totalTime = 33;
if (totalTime +startMill- endMill > 0){
Thread.sleep(totalTime +startMill- endMill);
startMill = totalTime +startMill;
}else{
startMill = endMill;
}
}
ByteBuffer[] inputBuffers = mMeidaCodec.getInputBuffers();//获取输入缓存队列
int inputBufferIndex = mMeidaCodec.dequeueInputBuffer(-1);//请求一个输入缓存,-1表示一直等,如果是具体的时间,如1000,表示等1秒钟;返回整形变量表示获取到的输入缓存的index;
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];//输入缓存对象
inputBuffer.clear();//使用之前clear一下,防止之前缓存数据影响当前的数据
inputBuffer.put(tmpByte, 0, tmpByte.length);//将数据添加进缓存中
mMeidaCodec.queueInputBuffer(inputBufferIndex, 0, tmpByte.length, 0, 0);//将缓存数据入队,接下来就是解码
mCount++;
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mMeidaCodec.dequeueOutputBuffer(bufferInfo, 0);//获取一个输出缓存,传入bufferInfo存放ByteBuffer的信息。
while (outputBufferIndex >= 0) {
mMeidaCodec.releaseOutputBuffer(outputBufferIndex, true);//输出缓存使用完了释放,true是是否渲染到surfaceView
outputBufferIndex = mMeidaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
}
} catch (InterruptedException e) {
Log.e(TAG, "Wait the buffer come..");
e.printStackTrace();
} catch (Exception e) {
Log.e(TAG, "Wait the buffer come..");
e.printStackTrace();
}
}
bufferQueue.clear();
if (mMeidaCodec != null) {
try {
mMeidaCodec.stop();
mMeidaCodec.release();
mMeidaCodec = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
public void stop() {
//暂停解码
flag = false;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//释放资源
try {
bufferQueue.clear();
mHandler.removeCallbacks(hardwareDecodeThread);
if (mMeidaCodec != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
try {
mMeidaCodec.stop();
} catch (Exception e) {
e.printStackTrace();
}
try {
mMeidaCodec.release();
} catch (Exception e) {
e.printStackTrace();
}
}
mMeidaCodec = null;
}
thread.quit();
} catch (Exception e) {
e.printStackTrace();
}
}
public void setSurfaceView(SurfaceView s) {
this.mSurfaceView = s;
if (Build.VERSION.SDK_INT > 15) {
startMediaHardwareDecode();
} else {
Log.e(TAG, "The Platform not support the hardware decode H264!");
}
}
public void decodeH264Stream(byte[] data) {
try {
lastTimeStamp = new Date().getTime();
bufferUnit tmpBuffer = new bufferUnit();
tmpBuffer.data = data;
bufferQueue.put(tmpBuffer);
} catch (InterruptedException e) {
Log.e(TAG, "The buffer queue is full , wait for the place..");
}
}
//传过来的数据
private class bufferUnit {
public byte[] data;
}
}
注意:
1、如果送来的流的第一帧Frame有pps和sps,可以不需要配置format.setByteBuffer的”csd-0” (sps) 和”csd-1”(pps); 否则必须配置相应的pps和sps,通常情况下sps和pps如下:
byte[] sps = { 0, 0, 0, 1, 103, 100, 0, 40, -84, 52, -59, 1, -32, 17, 31, 120, 11, 80, 16, 16, 31, 0, 0, 3, 3, -23, 0, 0, -22, 96, -108 };
byte[] pps = { 0, 0, 0, 1, 104, -18, 60, -128 };。
2、需要使用生产者-消费者模式,否则有可能存在:上一帧还没有解码完成,下一帧就传入进来了,解码发生错误。其实也就是MediaCodec状态发生错误的异常。