解码流程
- 1.获取从surfaceView初始化后得到的surface
- 2.配置解码器mediaCodec,并在configure中设置surface
- 3.获取解码的ByteBuffer,从h264数据中通过分隔符000001/00000001查找帧信息,将数据拷贝到ByteBuffer中,告知mediaCodec数据准备好了
- 4.获取输出的BufferInfo和OutIndex
- 5.releaseOutputBuffer并渲染
完整例子
H264DecodeActivity
public class H264DecodeActivity extends AppCompatActivity {
public boolean checkPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
}, 1);
}
return false;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_h264_decode);
checkPermission();
initSurface();
}
private H264Player h264Player;
private void initSurface() {
SurfaceView surface = (SurfaceView) findViewById(R.id.preview);
surface.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
Surface surface1= surfaceHolder.getSurface();
h264Player = new H264Player(
new File("/data/data/com.example.testh264/cache/out.h264")
.getAbsolutePath()
,surface1);
h264Player.play();
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
}
});
}
}
H264Player
public class H264Player implements Runnable{
private MediaCodec mediaCodec
private String path
public H264Player(String absolutePath, Surface surface) {
Log.d("hucaihua" , "exsit :" + new File(absolutePath).exists())
try {
this.path = absolutePath
//设置解码器使用的数据类型 h264
mediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC)
MediaFormat mediaformat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC,64, 368)
//设置关键帧间隔
mediaformat.setInteger(MediaFormat.KEY_FRAME_RATE, 15)
//加入的surface表示后续的内容输出到surface中,flag=0,如果是编码则需要设置flag为编码
mediaCodec.configure(mediaformat, surface, null, 0)
}catch (Exception e){
e.printStackTrace()
}
Log.i("hucaihua", "配置成功")
}
public void play() {
Log.d("hucaihua" , "start play")
new Thread(this).start()
}
@Override
public void run() {
mediaCodec.start()
try {
decodeH264()
} catch (Exception e) {
Log.i("hucaihua", "run: "+e.toString())
}
}
private void decodeH264() {
try {
byte[] bytes = getBytes(path)
int startIndex = 0
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo()
while (true){
int nextFrameStart = findByFrame(bytes , startIndex + 2, bytes.length)
int size = nextFrameStart -startIndex
if (size > 0 ){
//从mediaCodec中找到一块空闲的buffer,mediaCodec是为所有的app服务的,因此不能通过回调的形式传回处理好的数据
int bufferIndex = mediaCodec.dequeueInputBuffer(10000)
if (bufferIndex >= 0){
//获取这块空闲的buffer
ByteBuffer byteBuffer = mediaCodec.getInputBuffer(bufferIndex)
Log.i("hucaihua", "decodeH264: 输入 "+size)
//往buffer中放入要解码的数据
byteBuffer.put(bytes , startIndex , size)
//解码不需要设置pts,因为文件中的顺序,刚好就是解码顺序。
//告知mediaCodec已经放置好数据,可以开始解码了
mediaCodec.queueInputBuffer(bufferIndex ,0 , size , 0 , 0)
startIndex = nextFrameStart
}else {
Log.e("hucaihua" , "没有合适的Buffer")
}
}
// info用来装decode出来的数据的信息,它是远远大于我们输入的信息的。
// 从mediaCodec中获取输出数据,输出数据的信息与输入数据信息不一样,因此mediaCodec通过BufferInfo来告知我们
int outIndex = mediaCodec.dequeueOutputBuffer(info , 1100)
if (outIndex >= 0){
//控制播放速度
Thread.sleep(33)
//将解码好的数据渲染到surface中
mediaCodec.releaseOutputBuffer(outIndex , true)
}else{
break
}
}
mediaCodec.stop()
} catch (Exception e) {
e.printStackTrace()
}
}
// 0x 00 00 00 01 或者 0x 00 00 01
private int findByFrame( byte[] bytes, int start, int totalSize) {
for (int i = start
if (((bytes[i] == 0x00) && (bytes[i + 1] == 0x00) && (bytes[i + 2] == 0x00) && (bytes[i + 3] == 0x01))
||((bytes[i] == 0x00) && (bytes[i + 1] == 0x00) && (bytes[i + 2] == 0x01))) {
return i
}
}
return -1
}
public byte[] getBytes(String path) throws IOException {
InputStream is = new DataInputStream(new FileInputStream(new File(path)))
int len
int size = 1024
byte[] buf
ByteArrayOutputStream bos = new ByteArrayOutputStream()
buf = new byte[size]
while ((len = is.read(buf, 0, size)) != -1)
bos.write(buf, 0, len)
buf = bos.toByteArray()
return buf
}
}