文章目录
- 解析的两种方式
- MediaCodec异步编码具体过程如下:
- 1.配置Camera回调,获取preview的原始数据
- 2.配置encoder和MediaFormat
- 3.开启encoder
- 4.获取到preview数据,并送如编码器
- 5.mediaCodecCallback解析出编码后的vps sps vps帧,两种方式
- 6.解析每个帧的值
- 7.工具bytesToHex
H264格式编码是没有vps帧,是从sps帧开始,这里从H264和H265两种编码器中分别解析各自编码出的配置。
解析的两种方式
- 直接解析编码器编码的原始码的第一帧数据,第一帧一般为配置帧。H264编码中是按照sps,pps顺序拼接而成一帧;H265是按照vps,sps,pps顺序拼接成一帧。
- 回调函数onOutputFormatChanged中,根据MediaFormat的csd-0,csd-1中分析出配置帧的数据。H264中sps为csd-0,pps为csd-1;H265中vps、sps、pps都在csd-0中按顺序拼接。
MediaCodec异步编码具体过程如下:
1.配置Camera回调,获取preview的原始数据
mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
mCamera.setPreviewCallback(new CameraPreviewCallback());
class CameraPreviewCallback implements Camera.PreviewCallback {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (frameRawDataListener != null)
frameRawDataListener.previewDataCallBack(data);
}
}
public interface FrameRawDataListener{
public void previewDataCallBack(byte[] frameData);
}
2.配置encoder和MediaFormat
public void prepareEncoder(){
Log.d(TAG, "prepareEncoder: ");
MediaFormat videoFormat = MediaFormat.createVideoFormat(VIDEO_MIME_TYPE, mWidth, mHeight);
videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFps);
videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, (int)(mWidth * mHeight));
videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, selectColorFormat(VIDEO_MIME_TYPE));
videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, I_FRAME_INTERVAL);
if(VIDEO_MIME_TYPE.equals(MediaFormat.MIMETYPE_VIDEO_AVC)){
videoFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
videoFormat.setInteger(MediaFormat.KEY_PROFILE,MediaCodecInfo.CodecProfileLevel.AVCProfileHigh);
videoFormat.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel31);
}else{
videoFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
videoFormat.setInteger(MediaFormat.KEY_PROFILE,MediaCodecInfo.CodecProfileLevel.HEVCProfileMain);
videoFormat.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel4);
}
try {
mEncoder = MediaCodec.createEncoderByType(VIDEO_MIME_TYPE);
} catch (IOException e) {
e.printStackTrace();
}
mEncoder.setCallback(mediaCodecCallback);
mEncoder.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
}
private int selectColorFormat(String type) {
return selectColorFormat(selectCodec(type), type);
}
private int selectColorFormat(MediaCodecInfo codecInfo, String mimeType) {
MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
for (int i = 0; i < capabilities.colorFormats.length; i++) {
int colorFormat = capabilities.colorFormats[i];
if (isRecognizedFormat(colorFormat)) {
return colorFormat;
}
}
Log.w(TAG, "Couldn't find color format for " + codecInfo.getName()
+ " / " + mimeType);
return 0;
}
private boolean isRecognizedFormat(int colorFormat) {
switch (colorFormat) {
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
return true;
default:
return false;
}
}
private MediaCodecInfo selectCodec(String mimeType) {
int numCodecs = MediaCodecList.getCodecCount();
for (int i = 0; i < numCodecs; i++) {
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
if (!codecInfo.isEncoder()) {
continue;
}
String[] types = codecInfo.getSupportedTypes();
for (int j = 0; j < types.length; j++) {
if (types[j].equalsIgnoreCase(mimeType)) {
return codecInfo;
}
}
}
return null;
}
3.开启encoder
public void startEncodec(){
mEncoder.start();
mStartTime = System.currentTimeMillis() * 1000;
WorkThread workThread = new WorkThread();
videoEncoderLoop = true;
workThread.start();
}
4.获取到preview数据,并送如编码器
class WorkThread extends Thread{
@Override
public void run() {
while (videoEncoderLoop && !Thread.interrupted()) {
Log.d(TAG, "run: ");
int inputBufferIndex = 0;
try {
inputBufferIndex = mInputIndexQueue.take().arg1;
Log.d(TAG,"inputBufferIndex="+inputBufferIndex);
if (inputBufferIndex >= 0) {
byte[] nv21Data = (byte[]) frameDataLinkedBlockingQueue.take();
ByteBuffer inputBuffer = mEncoder.getInputBuffer(inputBufferIndex);
inputBuffer.put(nv21Data);
long pts = System.currentTimeMillis() * 1000 - mStartTime;
mEncoder.queueInputBuffer(inputBufferIndex, 0, nv21Data.length, pts, 0);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mInputIndexQueue.clear();
frameDataLinkedBlockingQueue.clear();
}
}
@Override
public void previewDataCallBack(byte[] frameData) {
try {
frameDataLinkedBlockingQueue.put(frameData);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
5.mediaCodecCallback解析出编码后的vps sps vps帧,两种方式
private MediaCodec.Callback mediaCodecCallback = new MediaCodec.Callback() {
@Override
public void onInputBufferAvailable(MediaCodec mediaCodec, int i) {
Message msg = new Message();
msg.obj = mediaCodec;
msg.arg1 = i;
mInputIndexQueue.offer(msg);
}
@Override
public void onOutputBufferAvailable(MediaCodec mediaCodec, int i, MediaCodec.BufferInfo bufferInfo) {
if (!videoEncoderLoop)
return;
ByteBuffer buffer = mediaCodec.getOutputBuffer(i);
if (VIDEO_MIME_TYPE.equals(MediaFormat.MIMETYPE_VIDEO_AVC)) {
int typeH264 = buffer.get(4) & 0x1F;
if (typeH264 == 7 || typeH264 == 8) {
byte[] configFps = new byte[bufferInfo.size];
buffer.get(configFps);
Log.d(TAG, "chris buffer.position="+buffer.position()+",H264type= " + typeH264 +",value = "+bytesToHex(configFps));
searchSPSandPPSFromH264(ByteBuffer.wrap(configFps),bufferInfo);
}
} else if (VIDEO_MIME_TYPE.equals(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
int typeH265 = (buffer.get(4) & 0x7e) >> 1;
Log.d(TAG, "chris H265type= " + typeH265);
if (typeH265 == 32 || typeH265 == 33 || typeH265 == 34) {
byte[] configFps = new byte[bufferInfo.size];
buffer.get(configFps);
Log.d(TAG, "chris H265type= " + typeH265 +",value = "+bytesToHex(configFps));
searchVpsSpsPpsFromH265(ByteBuffer.wrap(configFps));
}
}
mediaCodec.releaseOutputBuffer(i, false);
}
@Override
public void onError(MediaCodec mediaCodec, MediaCodec.CodecException e) {
}
@Override
public void onOutputFormatChanged(MediaCodec mediaCodec, MediaFormat mediaFormat) {
if (VIDEO_MIME_TYPE == MediaFormat.MIMETYPE_VIDEO_HEVC) {
searchVpsSpsPpsFromH265(mediaFormat.getByteBuffer("csd-0"));
} else if (VIDEO_MIME_TYPE == MediaFormat.MIMETYPE_VIDEO_AVC) {
ByteBuffer sps = mediaFormat.getByteBuffer("csd-0");
ByteBuffer pps = mediaFormat.getByteBuffer("csd-1");
Log.d(TAG,"H264 onOutputFormatChanged sps="+bytesToHex(sps.array()) + ",pps=" + bytesToHex(pps.array()));
}
}
};
6.解析每个帧的值
public void searchSPSandPPSFromH264(ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo){
byte[] csd = new byte[128];
int len = 0, p = 4, q = 4;
len = bufferInfo.size;
Log.d(TAG,"len="+len);
if (len<128) {
buffer.get(csd,0,len);
if (len>0 && csd[0]==0 && csd[1]==0 && csd[2]==0 && csd[3]==1) {
while (p<len) {
while (!(csd[p+0]==0 && csd[p+1]==0 && csd[p+2]==0 && csd[p+3]==1) && p+3<len) p++;
if (p+3>=len) p=len;
if ((csd[q]&0x1F)==7) {
byte[] sps = new byte[p-q];
System.arraycopy(csd, q, sps, 0, p-q);
Log.d(TAG,"chris, searchSPSandPPSFromH264 SPS="+bytesToHex(sps));
} else {
byte[] pps = new byte[p-q];
System.arraycopy(csd, q, pps, 0, p-q);
Log.d(TAG,"chris, searchSPSandPPSFromH264 PPS="+bytesToHex(pps));
}
p += 4;
q = p;
}
}
}
}
public void searchVpsSpsPpsFromH265(ByteBuffer csd0byteBuffer) {
int vpsPosition = -1;
int spsPosition = -1;
int ppsPosition = -1;
int contBufferInitiation = 0;
byte[] csdArray = csd0byteBuffer.array();
for (int i = 0; i < csdArray.length; i++) {
if (contBufferInitiation == 3 && csdArray[i] == 1) {
if (vpsPosition == -1) {
vpsPosition = i - 3;
} else if (spsPosition == -1) {
spsPosition = i - 3;
} else {
ppsPosition = i - 3;
}
}
if (csdArray[i] == 0) {
contBufferInitiation++;
} else {
contBufferInitiation = 0;
}
}
byte[] vps = new byte[spsPosition];
byte[] sps = new byte[ppsPosition - spsPosition];
byte[] pps = new byte[csdArray.length - ppsPosition];
for (int i = 0; i < csdArray.length; i++) {
if (i < spsPosition) {
vps[i] = csdArray[i];
} else if (i < ppsPosition) {
sps[i - spsPosition] = csdArray[i];
} else {
pps[i - ppsPosition] = csdArray[i];
}
}
Log.d(TAG, "searchVpsSpsPpsFromH265: vps="+ bytesToHex(vps)+",sps="+bytesToHex(sps)+",pps="+bytesToHex(pps));
}
7.工具bytesToHex
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}