最近在高通8974A android5.0 平台调试mediacodec硬件编解码,发现之前在android 4.1上可以用的代码报错不能用了。经过分析和网上搜索解决了,现贴出来。
demo的功能就是显示camera 的预览图像,从camera回调里拿到yuv数据,然后送给mediacodec编码,输出的编码数据给到mediacodec解码,解码后的图像
显示出来。
package com.example.mediacodectest;
import android.os.Bundle;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.WindowManager;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.media.MediaCodec;
import android.media.MediaCodecList;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity implements OnClickListener {
/** Called when the activity is first created. */
private SurfaceView CamSurfaceView = null;
private SurfaceHolder CamSurfaceHolder = null;
private SurfaceView DecSurfaceView = null;
private SurfaceHolder DecSurfaceHolder = null;
private String TAG = "MediaCodecTestActivity";
private Camera camera ;
private int cameraId = 1;
private int width = 640;
private int height = 480;
private int fps = 25;
private int bitrate = 1024000;
private int m_nPacksPutIn = 0;
private int runstate=0;
private Queue<byte[]> encqueue=null;
private Queue<byte[]> decqueue=null;
private String videoformat263= "video/3gpp";
private String videoformatvp8= "video/x-vnd.on2.vp8";
private String videoformat264= "video/avc";
private final long kTimeOutUs = -1;//5000;
//private MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
//true:use queue ,two thread encode and decode
//false:one thread encode and decode
private boolean rmode = true;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
Button btnStartDec = (Button)findViewById(R.id.start);
Button btnStopDec = (Button)findViewById(R.id.stop);
CamSurfaceView =(SurfaceView) this.findViewById(R.id.cameraview);
DecSurfaceView =(SurfaceView) this.findViewById(R.id.decodeview);
Log.d(TAG,"init surfaceview ,cam:"+CamSurfaceView+" dec:"+DecSurfaceView);
CamSurfaceHolder = CamSurfaceView.getHolder();
CamSurfaceHolder.addCallback(new CamSufaceListener());
CamSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
CamSurfaceView.setVisibility(View.VISIBLE);
DecSurfaceHolder = DecSurfaceView.getHolder();
DecSurfaceHolder.addCallback(new DecSufaceListener());
DecSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
DecSurfaceView.setVisibility(View.VISIBLE);
btnStartDec.setOnClickListener(this);
btnStopDec.setOnClickListener(this);
encqueue = new LinkedList<byte[]>();
decqueue = new LinkedList<byte[]>();
}
/*
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
int key = event.getKeyCode();
int keyaction = event.getAction();
//Log.i(TAG, "[test] dispatchKeyEvent event = " + event);
if (key == KeyEvent.KEYCODE_BACK ) {
Log.i(TAG, " catch BACK event!! keyaction: "+keyaction);
return true;
}
if (key == KeyEvent.KEYCODE_MENU) {
Log.i(TAG, " catch MENU event!! return true! ");
return true;
}
return super.dispatchKeyEvent(event);
}
@Override
public void onAttachedToWindow()
{
// TODO Auto-generated method stub
this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);
super.onAttachedToWindow();
}
public boolean onKeyUp(int keyCode, KeyEvent event)
{
switch(keyCode)
{
case KeyEvent.KEYCODE_MENU:
Log.d(TAG,"MENU key up test!");
break;
case KeyEvent.KEYCODE_BACK:
Log.d(TAG,"BACK key up test!");
break;
case KeyEvent.KEYCODE_HOME:
Log.d(TAG,"HOME key up test!");
break;
}
return super.onKeyUp(keyCode, event);
} */
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.start:
start();
break;
case R.id.stop:
stop();
break;
default:
break;
}
}
private int init()
{
Log.d(TAG,"dxp--init");
cameraInit();
mediaEncInit();
mediaDecInit();
return 0;
}
private int uninit()
{
Log.d(TAG,"dxp--uninit");
mediaEncUninit();
mediaDecUninit();
cameraUninit();
return 0;
}
private int start()
{
if(runstate ==1)return -1;
init();
Log.d(TAG,"dxp--start");
runstate = 1;
cameraStart();
mediaEncStart();
mediaDecStart();
return 0;
}
private int stop()
{
if(runstate ==0)return -1;
Log.d(TAG,"dxp--stop");
runstate = 0;
mediaEncStop();
mediaDecStop();
cameraStop();
uninit();
return 0;
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
stop();
uninit();
}
public byte[] copy(byte[] rSource) {
byte[] aResult = new byte[rSource.length];
System.arraycopy(rSource, 0, aResult, 0, aResult.length);
return aResult;
}
///==============================================
///for camera start
public int cameraInit()
{
camera = Camera.open(cameraId);
if(camera==null){
Log.d(TAG, "open camera object fail " );
return -1;
}
Camera.Parameters parameters = camera.getParameters();
List<Size> sizes;
sizes = parameters.getSupportedPreviewSizes();
Log.i(TAG, "==Cam supported SizeList of camera ==");
for(Size temp:sizes)
Log.i(TAG, temp.width+", "+temp.height);
//parameters.setRotation(90);
// 横竖屏镜头自动调整
if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE)
{
Log.d(TAG, "set camera direction when portrait");
parameters.set("orientation", "portrait");
parameters.set("rotation", 90); // 镜头角度转90度(默认摄像头是横拍)
camera.setDisplayOrientation(90);
} else// 如果是横屏
{
Log.d(TAG, "set camera direction when landscape");
parameters.set("orientation", "landscape");
camera.setDisplayOrientation(0);
}
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
Size PreviewSizes = parameters.getPreviewSize();
//width =PreviewSizes.width;
//height = PreviewSizes.height;
Log.d(TAG,"dxp-- camera get size:"+PreviewSizes.width+" ,"+PreviewSizes.height);
Log.d(TAG,"dxp-- camera previw size:"+width+" ,"+height);
parameters.setPreviewSize(width, height);
parameters.setPreviewFormat(ImageFormat.NV21);
parameters.setPreviewFrameRate(fps);
camera.setParameters(parameters);
camera.autoFocus(null);
//camera.setDisplayOrientation(90);
camera.setPreviewCallback(new Camera.PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
Log.d(TAG, "dxp-- onPreviewFrame length:"+data.length );
if(rmode){
synchronized(encqueue){
encqueue.offer(data);
}
}else{
EncoderFrame(data);
}
}
});
return 0;
}
public void cameraStart()
{
if(camera==null)return;
try {
camera.setPreviewDisplay(CamSurfaceHolder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
camera.startPreview();
}
public void cameraStop()
{
if(camera==null)return;
camera.stopPreview();
}
public void cameraUninit()
{
if(camera == null)return;
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
}
private final class CamSufaceListener implements SurfaceHolder.Callback{
public void surfaceCreated(SurfaceHolder holder) {//Surface锟斤拷锟斤拷录锟斤拷拇锟斤拷锟?
CamSurfaceHolder = holder;
//cameraInit();
Log.d(TAG,"test-- camera surfaceCreated!");
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//Surface锟侥憋拷锟铰硷拷锟侥达拷锟斤拷
CamSurfaceHolder = holder;
//cameraStart();
start();
Log.d(TAG,"dxp-- surfaceChanged:"+width+" ,"+height);
}
public void surfaceDestroyed(SurfaceHolder holder) {//Surface锟斤拷锟绞憋拷拇锟斤拷锟?
//cameraStop();
stop();
CamSurfaceHolder = null;
Log.d(TAG,"test-- camera surfaceDestroyed!");
}
}
///for camera end
///==============================================
///for mediacodec encoder start
private MediaCodec enccodec;
private EncThread et;
private MediaCodecInfo selectCodec(){
boolean found = false;
int numCodecs = MediaCodecList.getCodecCount();
Log.d(TAG, "--dxp-- numCodecs:" + numCodecs);
MediaCodecInfo codecInfo = null;
for (int i = 0; i < numCodecs ; i++) {
MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
if (!info.isEncoder()) {
continue;
}
String[] types = info.getSupportedTypes();
for (int j = 0; j < types.length && !found; j++) {
if (types[j].equals(videoformat264)){
Log.d(TAG, "--dxp-- codec:" + info.getName());
Log.d(TAG, "--dxp-- can be used!" );
found = true;
codecInfo = info;
break;
}
//Log.d(TAG, "--dxp-- support:" + types[j]);
}
}
return codecInfo;
}
public int mediaEncInit()
{
Log.d(TAG,"test-- mediaEncInit 1");
MediaCodecInfo info = selectCodec();
Log.d(TAG, "--dxp--we use codec:" + info.getName());
//enccodec = MediaCodec.createEncoderByType(videoformat264);
enccodec = MediaCodec.createByCodecName(info.getName());
MediaFormat mediaFormat = MediaFormat.createVideoFormat(videoformat264, width, height);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, fps);
/*
* public static final int COLOR_FormatYUV420Planar = 19;
* public static final int COLOR_FormatYUV420PackedPlanar = 20;
* public static final int COLOR_FormatYUV420SemiPlanar = 21;
* public static final int COLOR_FormatYUV420PackedSemiPlanar = 39;
* public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100;
* public static final int COLOR_FormatYUV420Flexible = 0x7F420888;
* public static final int COLOR_QCOM_FormatYUV420SemiPlanar = 0x7fa30c00;
* */
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
enccodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
MediaFormat outputFormat = enccodec.getOutputFormat();
return 0;
}
public void mediaEncStart()
{
Log.d(TAG,"test-- mediaEncStart ");
if(enccodec == null)return;
enccodec.start();
if(rmode){
et= new EncThread();
et.start();
}
}
public void mediaEncStop()
{
Log.d(TAG,"test-- mediaEncStop ");
if(enccodec == null)return;
if(rmode){
et.interrupt();
}
enccodec.stop();
}
public void mediaEncUninit()
{
Log.d(TAG,"test-- mediaEncUninit ");
if(enccodec==null)return;
enccodec.release();
enccodec=null;
}
private class EncThread extends Thread {
public void run() {
byte[] yuvdata = null;
while(runstate==1){
synchronized(encqueue){
yuvdata = encqueue.poll();
}
if(yuvdata!=null){
//Log.d(TAG, "dxp-- enc thread");
EncoderFrame(yuvdata);
yuvdata = null;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.d(TAG, "dxp-- enc thread sleep exception");
}
}
}
}
public void EncoderFrame(byte[] input) {
//Log.d(TAG, "dxp-- EncoderFrame");
final long kTimeOutUs = 100;
if(enccodec==null)return;
try {
ByteBuffer[] inputBuffers = enccodec.getInputBuffers();
ByteBuffer[] outputBuffers = enccodec.getOutputBuffers();
int inputBufferIndex = enccodec.dequeueInputBuffer(kTimeOutUs);
//Log.d(TAG, "dxp-- inputBufferIndex:"+inputBufferIndex);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
int wh = width*height/4;
int length = width*height*3/2;
inputBuffer.put(input, 0 , length);
Log.d(TAG, "dxp-- inputBufferIndex:"+inputBufferIndex+",len:"+length);
m_nPacksPutIn ++;
long presentationTimeUs = m_nPacksPutIn * 1000000 / fps;
enccodec.queueInputBuffer(inputBufferIndex, 0, length, presentationTimeUs, 0);
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = enccodec.dequeueOutputBuffer(bufferInfo,kTimeOutUs);
//Log.d(TAG, "dxp-- outputBufferIndex:"+outputBufferIndex);
while (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
//MediaFormat bufferFormat = enccodec.getOutputFormat(); // option A
byte[] outData = new byte[bufferInfo.size];
Log.d(TAG, "dxp-- outputBufferIndex:"+outputBufferIndex+",size:"+bufferInfo.size);
outputBuffer.get(outData);
Log.d(TAG, "dxp-- encode outdata[4]:"+Integer.toHexString(outData[4]));
if(rmode){
synchronized(decqueue){
decqueue.offer(outData);
}
}else{
DecodeFrame(outData);
}
enccodec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = enccodec.dequeueOutputBuffer(bufferInfo, kTimeOutUs);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
///formediacodec encoder end
///==============================================
///for mediacodec decoder start
private MediaCodec deccodec;
private DecThread dt;
public int mediaDecInit()
{
Log.d(TAG,"test-- mediaDecInit ");
deccodec = MediaCodec.createDecoderByType(videoformat264);
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);
//mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
//mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, fps);
//mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
//mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
deccodec.configure(mediaFormat, DecSurfaceHolder.getSurface(), null, 0);
return 0;
}
public void mediaDecStart()
{
Log.d(TAG,"test-- mediaDecStart ");
if(deccodec==null){
Log.d(TAG,"test-- mediaDecStart err!");
return;
}
deccodec.start();
if(rmode){
dt= new DecThread();
dt.start();
}
}
public void mediaDecStop()
{
Log.d(TAG,"test-- mediaDecStop ");
if(deccodec==null)return;
if(rmode){
dt.interrupt();
}
deccodec.stop();
}
public void mediaDecUninit()
{
if(deccodec==null)return;
deccodec.release();
deccodec=null;
}
private class DecThread extends Thread {
public void run() {
byte[] outdata = null;
while(runstate==1){
//Log.d(TAG, "dxp-- dec thread");
synchronized(decqueue){
outdata = decqueue.poll();
}
if(outdata!=null){
DecodeFrame(outdata);
outdata = null;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public void DecodeFrame(byte[] input) {
Log.d(TAG, "dxp-- DecodeFrame input:"+input.length);
if(deccodec==null)return;
try {
int inputBufferIndex = deccodec.dequeueInputBuffer(kTimeOutUs);//-1
if (inputBufferIndex >= 0) {
//fill data to queue
ByteBuffer[] inputBuffer = deccodec.getInputBuffers();
inputBuffer[inputBufferIndex].clear();
inputBuffer[inputBufferIndex].put(input);
//push data to mediacodec
deccodec.queueInputBuffer(inputBufferIndex, 0, input.length, 0, 0);
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = deccodec.dequeueOutputBuffer(bufferInfo,0);
if (outputBufferIndex >= 0) {
ByteBuffer outputBuffer[] = deccodec.getOutputBuffers();
byte[] outData = new byte[bufferInfo.size];
outputBuffer[outputBufferIndex].get(outData);
//outputStream.write(outData, 0, outData.length);
Log.d(TAG, "dxp-- dec out datalen:"+outData.length);
deccodec.releaseOutputBuffer(outputBufferIndex, true/*if render*/);
outputBufferIndex = deccodec.dequeueOutputBuffer(bufferInfo, 0);
bufferInfo = null;
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
ByteBuffer outputBuffer[] = deccodec.getOutputBuffers();
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Subsequent data will conform to new format.
MediaFormat format = deccodec.getOutputFormat();
}
} catch (Throwable t) {
t.printStackTrace();
}
}
private final class DecSufaceListener implements SurfaceHolder.Callback{
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
Log.d(TAG, "dec surfaceChanged");
DecSurfaceHolder = holder;
//mediaEncStart();
//mediaDecStart();
}
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
Log.d(TAG, "dec surfaceCreated");
DecSurfaceHolder = holder;
//mediaEncInit();
//mediaDecInit();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
Log.d(TAG, "dec surfaceDestroyed");
DecSurfaceHolder = null;
//mediaEncStop();
//mediaDecStop();
//mediaEncUninit();
//mediaDecUninit();
}
}
///for mediacodec decoder end
///==============================================
}