Android Camera预览时输出的帧率控制

来源:互联网 发布:外贸网络推广 编辑:程序博客网 时间:2024/04/29 17:55

如果使用MediaCodec硬编码H264,可以使用下面的方法控制编码输出的帧率:

        MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);          mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);              mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, width*height*5);          mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);          mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);          try {              mediaCodec = MediaCodec.createEncoderByType("video/avc");          } catch (IOException e) {              // TODO Auto-generated catch block              e.printStackTrace();          }          mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);          mediaCodec.start();  

但如果是采用预览模式下得到预览的BITMAP,然后通过x264编码的来实现的方式,则需要应用层控制预览帧的帧帧率,Camera本来提供了两个接口来控制预览帧率,但从多个平台的适配发现,基本上都不能有效的控制Camera预览输出的帧率:

setPreviewFrameRate是在api level1就开始使用了,然后不是简单地设置这个方法就可以让摄像头每秒捕获多少帧数的。 比如我设置2,它一秒不会只捕获2帧数据的,从日志记录来看,相当糟糕,不会是预期的2帧,于是我查找文档,发现这个方法已经废除了。 在api level9时加入了一个方法setPreviewFpsRange (int min, int max) 预览帧数从min到max,这个值再*1000. 这个方法已经在高版本的sdk中取代了旧的setPreviewFrameRate。 

上面是文章: Android camera 预览帧数和视频通话图片缓存 中提到为什么预览帧设置失效的问题,并且也给出了一个控制预览帧的方式,这里提供另外一种类似的实现:无锁队列的实现。

下面 RingBuffer的定义:

final byte STATU_INIT = 0; final byte STATU_WAIT_DEQEUE = 1; class UserDefinedBuffer{ByteBuffer mVideoFrame;byte status;}//the ring queueclass RingBuffer{int r_index;int w_index;int size;UserDefinedBuffer[] mUserDefinedBuffer; long last_time;public RingBuffer(int max_size, int capacity){mUserDefinedBuffer = new UserDefinedBuffer[max_size]; r_index = w_index = 0;size = max_size;for(int i=0 ;i<max_size; i++){mUserDefinedBuffer[i] = new UserDefinedBuffer();mUserDefinedBuffer[i].mVideoFrame = ByteBuffer.allocateDirect(capacity); }}public UserDefinedBuffer getUserDefinedBuffer(int index){return mUserDefinedBuffer[index];}int getRingW(){return w_index;}int getRingR(){return r_index;}int getRingSize(){return size;}  void  setUserDefinedBufferStatus(int index, byte status){synchronized(mUserDefinedBuffer[index]){mUserDefinedBuffer[index].status = status;}}byte getUserDefinedBufferStatus(int index){synchronized(mUserDefinedBuffer[index]){return mUserDefinedBuffer[index].status;} }void enqueue(byte[] _data){ int index = w_index & (size -1);Log.i(TAG, "#enqueue, index:"+index);if (index >= size){index = 0;}if (getUserDefinedBufferStatus(index) != STATU_INIT){Log.i(TAG, "i enqueue, index:"+index+", not dequeue" + ", STATUS:"+getUserDefinedBufferStatus(index));return;}setUserDefinedBufferStatus(index, STATU_WAIT_DEQEUE);mUserDefinedBuffer[index].mVideoFrame.rewind();mUserDefinedBuffer[index].mVideoFrame.put(_data);w_index += 1; }void enqueue(ByteBuffer data){ int index = w_index & (size -1);Log.i(TAG, "enqueue, index:"+index);if (index >= size){index = 0;}if (getUserDefinedBufferStatus(index) != STATU_INIT){Log.i(TAG, "ii enqueue, index:"+index+", not dequeue" + ", STATUS:"+getUserDefinedBufferStatus(index));return;}setUserDefinedBufferStatus(index, STATU_WAIT_DEQEUE);mUserDefinedBuffer[index].mVideoFrame.rewind();mUserDefinedBuffer[index].mVideoFrame.put(data); w_index += 1; //last_time = System.currentTimeMillis();}     long getLastTime(){return last_time;}int dequeue(){ int index = r_index & (size -1); if (index == (w_index & (size -1))){Log.i(TAG, "dequeue, w_index:"+w_index + ", r_index:"+r_index);return -1;}Log.i(TAG, "dequeue, index:"+index);r_index += 1; //ByteBuffer data =  mUserDefinedBuffer[index].mVideoFrame;//mUserDefinedBuffer[index].mVideoFrame.rewind();return index;  } };

 出队线程: 

class PushVideoThread extends Thread{  boolean mExitFlag = false;  public void setExitFlg(boolean bFlag){mExitFlag = bFlag;}     @Override    public void run() {android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);    Log.i(TAG, "PushVideoThread() run start.");final int delay = (100/mFps);//    while(!mExitFlag){            long start=System.currentTimeMillis();              if (mRingBuffer == null){try {Thread.sleep(delay);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}  continue;            }int index = mRingBuffer.dequeue(); if (index == -1){try {Thread.sleep(delay);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}  continue;}if (STATU_WAIT_DEQEUE != mRingBuffer.getUserDefinedBufferStatus(index)){    Log.i(TAG, "Ana  dequeue mRingBuffer.getUserDefinedBufferStatus(index):"+mRingBuffer.getUserDefinedBufferStatus(index));try {Thread.sleep(delay);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}  continue;}UserDefinedBuffer userDefindedBuffer = mRingBuffer.getUserDefinedBuffer(index);    ByteBuffer byteBuffer = userDefindedBuffer.mVideoFrame;    if (byteBuffer != null){framesRendered++;if ((framesRendered % 100) == 0) {logStatistics();framesRendered = 0;startTimeNs= System.nanoTime();}//    Log.i(TAG, "Ana  dequeue getRingW:"+ write +",getRingR:"+ read);    mProducer.push(byteBuffer, mVideoFrame.capacity());    mRingBuffer.setUserDefinedBufferStatus(index, STATU_INIT);    }                long end=System.currentTimeMillis();                  if ((end - start) < delay && (end - start) > 0){        try {        long value = delay - (end -start);        if (value > 0){        Thread.sleep(value);        }    } catch (Exception e) {    // TODO Auto-generated catch block    e.printStackTrace();    }                 }    }    Log.i(TAG, "PushVideoThread() run End.");    }}

RingBuffer的初始化:

static final int MAX_SIZE = 64;//must 2 mulRingBuffer mRingBuffer; void initRingBuffer(){   mRingBuffer = new RingBuffer(MAX_SIZE, mVideoFrame.capacity()); }
入队:
public void onPreviewFrame(byte[] _data, Camera _camera) {mRingBuffer.enqueue(_data);}





1 0