Android 分段录制丢帧保持在100ms作用(不掉帧)(没带代码,有空后续copy出来)

Android 分段录制不掉帧

项目需求过检项目要控制,分段录制时间间隔需要控制在100ms以内
原理:在使用原生的编解码MediaMuxer合成录制时,创建两个MediaMuxer(检测在new MediaMuxer时间已经超过了100ms),在分段的时候停止当前的MediaMuxer.stop(),立马把另外一个addTrack开始录制

1,在初始化编码器的时候创建两个Muxer

mMuxer[0] = new MediaMuxer(path1,   	MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mMuxer[1] = new MediaMuxer(path2,   	MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

2,在音频解码的方法中判断是否到了分段时间进行分段操作,在MediaCodec.INFO_TRY_AGAIN_LATER中进行分段,因为进入MediaCodec.INFO_TRY_AGAIN_LATER说明完成一段音频的解码写入,注意有个提前100ms在结束的时候给个关键帧,这样确保接下来的帧是关键帧,因为一个MP4的开头必须要关键帧

    public void drainAudioEncoder(boolean endOfStream) {
//        Logger.getLogger(TAG).e("drainAudioEncoder");
//        Logger.getLogger(TAG).e("drainAudioEncoder:endOfStream    "+endOfStream);
        if (!isMicSound) return;
        if (endOfStream) return;
        final int TIMEOUT_USEC = 0;
        ByteBuffer[] encoderOutputBuffers = mAudioEncoder.getOutputBuffers();//该方法返回一个 output 缓冲区,包含解码或编码后的数据。
        while( true ){
            int encoderStatus = mAudioEncoder.dequeueOutputBuffer(mAudioBufferInfo, TIMEOUT_USEC);//获取可用的输出缓冲区
            if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
                // no output available yet
                if (!endOfStream) {
                    if (DEBUG) Log.d(TAG, "Audio no output available, out of while(true) ");{
                        if (!stopRecording && System.currentTimeMillis() - curTime>= 50000){
                            curTime = System.currentTimeMillis();
                            changeFilePath();
                        }else if (System.currentTimeMillis() - curTime>= (50000-100)){//提前100ms在结束的时候给个关键帧
                            Bundle params = new Bundle();
                            params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
                            mVideoEncoder.setParameters(params);
                        }
                    }
                    break;      // out of while(true){}
                } else {
                    if (DEBUG) Log.d(TAG, "Audio no output available, spinning to await EOS");
                }
            } 
                mAudioEncoder.releaseOutputBuffer(encoderStatus, false);//释放缓冲区
                if ((mAudioBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    if (!endOfStream) {
                        Log.w(TAG, "reached end of stream unexpectedly");
                    } else {
                        if (DEBUG) Log.d(TAG, "Audio end of stream reached");
                    }
                    break;      // out of while
                }
            }
        }
    }

3,changeFile实现

   public void changeFilePath(){
        //循环存储机制
        try {
            contoryMuxer++;
            Logger.getLogger(TAG).e("changeFilePath stop:"+getWitchOneMuxer()+"   start:"+getNewWitchOneMuxer());
//            mVideoBufferInfo.presentationTimeUs = 0;
            if (mMuxer[getWitchOneMuxer()] !=null) {
                // stop() throws an exception if you haven't fed it any data.
                // Keep track of frames submitted, and don't call stop() if we haven't written anything.
                mAudioTrackIndex =-1;
                mVideoTrackIndex =-1;
                mMuxer[getWitchOneMuxer()].stop();//关闭当前正在写数据的Muxer
                mMuxer[getWitchOneMuxer()].release();
                mMuxer[getWitchOneMuxer()] = null;
                
                new Thread(new Runnable() {
                    @Override
                    public void run() {//此时mMuxer[getWitchOneMuxer()]已经为null,然后在线程里面重新new MediaMuxer()为下一个分段的文件做准备
                        try {//结束了  直接start  new muxer实例需要100ms以上 所以放在线程里面
                            int checkValue = CameraSettings.RECROD_DEFAULT.RECORD_TIME_INDEX_VALUE;
                            mCurFile2 = FileUtils.getOutputFile(FileUtils.MEDIA_TYPE_VIDEO,System.currentTimeMillis()+CameraSettings.RECROD_DEFAULT.RECORD_DEFAULT_DURATION_VALUES[checkValue],CameraSettings.RECROD_DEFAULT.FILE_NAME_FORMAT,false,false);
                            mMuxer[getWitchOneMuxer()] = new MediaMuxer(mCurFile2.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
                //此时当前mMuxer[getNewWitchOneMuxer()]已经在上一次分段中实例化好了然后直接addTrack
                mVideoTrackIndex = mMuxer[getNewWitchOneMuxer()].addTrack(videoFormatUsering);
                mAudioTrackIndex = mMuxer[getNewWitchOneMuxer()].addTrack(audioFormatUsering);
                if(mVideoTrackIndex!=-1 && mAudioTrackIndex!=-1){//确保再次申请关键帧
                    mMuxer[getNewWitchOneMuxer()].start();
                    Bundle params = new Bundle();
                    params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
                    mVideoEncoder.setParameters(params);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

原理其实很简单,就是新建两个Muxer 第一次的时候同时实例化,当第一个录制完成时,第二个因为已经实例化可以直接addTarck(audio),addTarck(video),同时开线程又new MediaMuxer为下一段做准备。注意因为是预先生成MediaMuxer在SDcard中会先生成一个0KB的.MP4文件记得及时删除。
最后我测试的结果也不是很理想一般是在100MS以内,但是偶尔会超过100ms

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值