Android自带的MediaCodec Sample使用MediaCodec+MediaExtrator可以实现视频播放,但不支持ES流的播放。MediaExtractor不能读取ES流, MediaCodec输入为NAL,所以需要自己写个函数来分解出各个NAL。
先说ES文件的提取,我们摄像机默认录像文件为asf文件,从中提取ES流。以下命令的意义不是太清楚,也是网上超来的,实际可用。命令a应该是分解出ES流,命令b应该是重现再打包,还是ES流
命令a:ffmpeg -i hi.asf -vcodec copy -an -f m4vtest.264
命令b:ffmpeg -i test.264 -vcodec h264 -s1920*1080 -an -f m4v t.264
书上说,可以根据文件中的00 00 01序列作为边界来提取每个NAL。
使用命令a提取的 SPS、I帧、P帧为00 00 00 01,SEI、PPS为00 00 01.使用命令b再编码后的SPS、I、P、SEI、PPS都为00 00 00 01。原因不太了解,实测发现:1、无论添加多少个0,都是没问题的。2、将SPS PPS SEI I帧作为一帧一起输入MediaCodec与将三者分别输入效果是一样的。,
分离NAL的函数,需要注意的是,如果每次都去读存储会很慢,需要尽量一次多读点缓冲。
public int nextFrame2(ByteBuffer buffer){ int size = 0; byte[] b_array= new byte[1]; // Log.d("myapp","nextFrame"); int stage = 0; try { if(!has_find_frame_head) { while (true) { getByteFromCache(b_array); // Log.d("myapp", "b=" + b_array[0]); if (stage == 0 ) { if(b_array[0] == 0) { stage = 1; // buffer.put(b_array[0]); // size++; }else{ stage=0; // buffer.clear(); // size = 0; } } else if (stage == 1) { if (b_array[0] == 0) { stage = 2; // buffer.put(b_array[0]); // size++; } else { stage = 0; // buffer.clear(); // size = 0; } } else if (stage == 2) { if (b_array[0] == 1) { // buffer.put(b_array[0]); // size++; break; } else if (b_array[0] == 0) { stage = 2; // buffer.put(b_array[0]); // size++; } else { stage = 0; // buffer.clear(); // size = 0; } } } } size = 3; byte b = 0; buffer.put(b); b=0; buffer.put(b); b=1; buffer.put(b); has_find_frame_head = false; //has find start flag 001 or 0001 or 0000001 while(true){ // inputStream.read(b_array); getByteFromCache(b_array); buffer.put(b_array[0]); if(size >=2 && buffer.get(size)==1 && buffer.get(size - 1)==0 && buffer.get(size-2)==0) { has_find_frame_head = true; size -= 2; break; }else { size++; } } }catch (Exception e){ e.printStackTrace(); return 0; } // Log.d("myapp","size="+size); if(size<=0) EOS=true; return size; }