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

15 篇文章 0 订阅

转:// https://blog.csdn.net/twoconk/article/details/52220338

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


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

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


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



====================2017-05-15 更新 ===================

当初把这个问题复杂化了,其实对预览帧的回调做下控制就能达到该目的,代码:


 
 
  1. private final static int MAX_FPS = 15; //视频通话控制在15帧是足够的
  2. private final static int FRAME_PERIOD = ( 1000 / MAX_FPS); // the frame period
  3. long lastTime = 0;
  4. long timeDiff = 0;
  5. int framesSkipped = 0; // number of frames being skipped
  6. int framesRecevied = 0; // number of frames being skipped
  7. int framesSended = 0; // number of frames being skipped
  8. private PreviewCallback previewCallback = new PreviewCallback() {
  9. public void onPreviewFrame(byte[] _data, Camera _camera) {
  10. timeDiff = System.currentTimeMillis() - lastTime;
  11. framesRecevied++;
  12. if (timeDiff < FRAME_PERIOD){
  13. framesSkipped++;
  14. if (NgnProxyVideoProducer.sAddCallbackBufferSupported) {
  15. // do not use "_data" which could be null (e.g. on GSII)
  16. NgnCameraProducer.addCallbackBuffer(_camera,
  17. _data == null ? mVideoCallbackData : _data);
  18. }
  19. Log.d(TAG, "ii loglized diserved framesSkipped:"+framesSkipped + ",framesRecevied:"+framesRecevied + ", framesSended:"+framesSended);
  20. return;
  21. }
  22. lastTime = System.currentTimeMillis();
  23. framesSended++;
  24. // add end.
  25. //doing other thing.
  26. }




====================2017-05-15 更新 ===================



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

下面 RingBuffer的定义:


 
 
  1. final byte STATU_INIT = 0;
  2. final byte STATU_WAIT_DEQEUE = 1;
  3. class UserDefinedBuffer{
  4. ByteBuffer mVideoFrame;
  5. byte status;
  6. }
  7. //the ring queue
  8. class RingBuffer{
  9. int r_index;
  10. int w_index;
  11. int size;
  12. UserDefinedBuffer[] mUserDefinedBuffer;
  13. long last_time;
  14. public RingBuffer(int max_size, int capacity){
  15. mUserDefinedBuffer = new UserDefinedBuffer[max_size];
  16. r_index = w_index = 0;
  17. size = max_size;
  18. for( int i= 0 ;i<max_size; i++){
  19. mUserDefinedBuffer[i] = new UserDefinedBuffer();
  20. mUserDefinedBuffer[i].mVideoFrame = ByteBuffer.allocateDirect(capacity);
  21. }
  22. }
  23. public UserDefinedBuffer getUserDefinedBuffer(int index){
  24. return mUserDefinedBuffer[index];
  25. }
  26. int getRingW(){
  27. return w_index;
  28. }
  29. int getRingR(){
  30. return r_index;
  31. }
  32. int getRingSize(){
  33. return size;
  34. }
  35. void setUserDefinedBufferStatus(int index, byte status){
  36. synchronized(mUserDefinedBuffer[index]){
  37. mUserDefinedBuffer[index].status = status;
  38. }
  39. }
  40. byte getUserDefinedBufferStatus(int index){
  41. synchronized(mUserDefinedBuffer[index]){
  42. return mUserDefinedBuffer[index].status;
  43. }
  44. }
  45. void enqueue(byte[] _data){
  46. int index = w_index & (size -1);
  47. Log.i(TAG, "#enqueue, index:"+index);
  48. if (index >= size){
  49. index = 0;
  50. }
  51. if (getUserDefinedBufferStatus(index) != STATU_INIT){
  52. Log.i(TAG, "i enqueue, index:"+index+ ", not dequeue" + ", STATUS:"+getUserDefinedBufferStatus(index));
  53. return;
  54. }
  55. setUserDefinedBufferStatus(index, STATU_WAIT_DEQEUE);
  56. mUserDefinedBuffer[index].mVideoFrame.rewind();
  57. mUserDefinedBuffer[index].mVideoFrame.put(_data);
  58. w_index += 1;
  59. }
  60. void enqueue(ByteBuffer data){
  61. int index = w_index & (size -1);
  62. Log.i(TAG, "enqueue, index:"+index);
  63. if (index >= size){
  64. index = 0;
  65. }
  66. if (getUserDefinedBufferStatus(index) != STATU_INIT){
  67. Log.i(TAG, "ii enqueue, index:"+index+ ", not dequeue" + ", STATUS:"+getUserDefinedBufferStatus(index));
  68. return;
  69. }
  70. setUserDefinedBufferStatus(index, STATU_WAIT_DEQEUE);
  71. mUserDefinedBuffer[index].mVideoFrame.rewind();
  72. mUserDefinedBuffer[index].mVideoFrame.put(data);
  73. w_index += 1;
  74. //last_time = System.currentTimeMillis();
  75. }
  76. long getLastTime(){
  77. return last_time;
  78. }
  79. int dequeue(){
  80. int index = r_index & (size -1);
  81. if (index == (w_index & (size -1))){
  82. Log.i(TAG, "dequeue, w_index:"+w_index + ", r_index:"+r_index);
  83. return -1;
  84. }
  85. Log.i(TAG, "dequeue, index:"+index);
  86. r_index += 1;
  87. // ByteBuffer data = mUserDefinedBuffer[index].mVideoFrame;
  88. // mUserDefinedBuffer[index].mVideoFrame.rewind();
  89. return index;
  90. }
  91. };

 出队线程: 


 
 
  1. class PushVideoThread extends Thread{
  2. boolean mExitFlag = false;
  3. public void setExitFlg(boolean bFlag){
  4. mExitFlag = bFlag;
  5. }
  6. @ Override
  7. public void run () {
  8. android.os.Process
  9. .setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
  10. Log.i(TAG, "PushVideoThread() run start.");
  11. final int delay = ( 100/mFps); //
  12. while(!mExitFlag){
  13. long start=System.currentTimeMillis();
  14. if (mRingBuffer == null){
  15. try {
  16. Thread.sleep(delay);
  17. } catch (Exception e) {
  18. // TODO Auto-generated catch block
  19. e.printStackTrace();
  20. }
  21. continue;
  22. }
  23. int index = mRingBuffer.dequeue();
  24. if (index == -1){
  25. try {
  26. Thread.sleep(delay);
  27. } catch (Exception e) {
  28. // TODO Auto-generated catch block
  29. e.printStackTrace();
  30. }
  31. continue;
  32. }
  33. if (STATU_WAIT_DEQEUE != mRingBuffer.getUserDefinedBufferStatus(index)){
  34. Log.i(TAG, "Ana dequeue mRingBuffer.getUserDefinedBufferStatus(index):"+mRingBuffer.getUserDefinedBufferStatus(index));
  35. try {
  36. Thread.sleep(delay);
  37. } catch (Exception e) {
  38. // TODO Auto-generated catch block
  39. e.printStackTrace();
  40. }
  41. continue;
  42. }
  43. UserDefinedBuffer userDefindedBuffer = mRingBuffer.getUserDefinedBuffer(index);
  44. ByteBuffer byteBuffer = userDefindedBuffer.mVideoFrame;
  45. if (byteBuffer != null){
  46. framesRendered++;
  47. if ((framesRendered % 100) == 0) {
  48. logStatistics();
  49. framesRendered = 0;
  50. startTimeNs= System.nanoTime();
  51. }
  52. // Log.i(TAG, "Ana dequeue getRingW:"+ write +",getRingR:"+ read);
  53. mProducer.push(byteBuffer, mVideoFrame.capacity());
  54. mRingBuffer.setUserDefinedBufferStatus(index, STATU_INIT);
  55. }
  56. long end=System.currentTimeMillis();
  57. if ((end - start) < delay && (end - start) > 0){
  58. try {
  59. long value = delay - (end -start);
  60. if (value > 0){
  61. Thread.sleep(value);
  62. }
  63. } catch (Exception e) {
  64. // TODO Auto-generated catch block
  65. e.printStackTrace();
  66. }
  67. }
  68. }
  69. Log.i(TAG, "PushVideoThread() run End.");
  70. }
  71. }

RingBuffer的初始化:


 
 
  1. static final int MAX_SIZE = 64; //must 2 mul
  2. RingBuffer mRingBuffer;
  3. void initRingBuffer(){
  4. mRingBuffer = new RingBuffer(MAX_SIZE, mVideoFrame.capacity());
  5. }
入队:

 
 
  1. public void onPreviewFrame(byte[] _data, Camera _camera) {
  2. mRingBuffer.enqueue(_data);
  3. }





====================2017-05-15 更新 ===================


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值