How to fix video & audio out of sync issue on android Video Recording using Camera api 2?

北城余情 提交于 2020-04-16 05:49:44

问题


I am implementing video recording application using Camera2 api. I've used Google samples for video recording. However, there's an audio, video out of sync issue on some devices, such as Samsung J5, J6. I have changed MediaRecorder.AudioEncoder, MediaRecorder.VideoEncoder, VideoEncodingBitrate, but it could not help me. How to handle audio, video sync issue?


回答1:


I've found a solution from this article. It mightn't be the best solution but it works. To solve the out of sync issue, mp4parserlibrary is utilized. First, video recording workflow is the same as just normal video recording, but , there's an extra step for the problematic devices. I will below provide my answer. First step of video recording is to prepare MediaRecorder, to shorten my answer I'll omit some steps.

 private void setupMediaRecorder(){
    mMediaRecorder = new MediaRecorder();

    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);

    mMediaRecorder.setOutputFile(mVideoFilePath);
    mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());

    CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
    mMediaRecorder.setVideoFrameRate(profile.videoFrameRate);
    mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
    mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate);
    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);

    mMediaRecorder.setAudioChannels(2);
    mMediaRecorder.setAudioEncodingBitRate(profile.audioBitRate);
    mMediaRecorder.setAudioSamplingRate(profile.audioSampleRate);

    int rotation = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();

    switch (getRotation()) {
        case SENSOR_ORIENTATION_DEFAULT_DEGREES:

            mMediaRecorder.setOrientationHint(ORIENTATIONS.get(rotation));
            break;
        case SENSOR_ORIENTATION_INVERSE_DEGREES:

            mMediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation));
            break;
        case SENSOR_ORIENTATION_DEFAULT_LAND_DEGREES:

            mMediaRecorder.setOrientationHint((ORIENTATIONS.get(rotation)+270)%360);
            break;
        case SENSOR_ORIENTATION_INVERSE_LAND_DEGREES:

            mMediaRecorder.setOrientationHint((INVERSE_ORIENTATIONS.get(rotation)+270)%360);
            break;
    }
    try{
        mMediaRecorder.prepare();
    }catch (IllegalStateException | IOException exc){
        exc.printStackTrace();
    }
}

To stop video recording

public void stopVideo(){
    //Stop recording
    try {
       mMediaRecorder.stop();
       mMediaRecorder.release();
       parseVideo(mVideoFilePath);
    }catch (RuntimeException e){
    e.printStackTrace();
   }
   closePreviewSession();
   createCameraPreviewSession();
   if (mListener!=null){
    mListener.onPrepareRecorder();
   }
}

The last and important step is to call parseVideo function

 private String parseVideo(String mFilePath) {
    try {
        DataSource channel = new FileDataSourceImpl(mFilePath);
        IsoFile isoFile = new IsoFile(channel);
        List<TrackBox> trackBoxes = isoFile.getMovieBox().getBoxes(TrackBox.class);
        boolean isError = false;
        for (TrackBox trackBox : trackBoxes) {
            TimeToSampleBox.Entry firstEntry = trackBox.getMediaBox().getMediaInformationBox().getSampleTableBox().getTimeToSampleBox().getEntries().get(0);
            // Detect if first sample is a problem and fix it in isoFile
            // This is a hack. The audio deltas are 1024 for my files, and video deltas about 3000
            // 10000 seems sufficient since for 30 fps the normal delta is about 3000
            if (firstEntry.getDelta() > 10000) {
                isError = true;
                firstEntry.setDelta(3000);
            }
        }

        if (isError) {
            Movie movie = new Movie();
            for (TrackBox trackBox : trackBoxes) {
                movie.addTrack(new Mp4TrackImpl(channel.toString() + "[" + trackBox.getTrackHeaderBox().getTrackId() + "]", trackBox));
            }
            movie.setMatrix(isoFile.getMovieBox().getMovieHeaderBox().getMatrix());
            Container out = new DefaultMp4Builder().build(movie);

            //delete file first!
            FileChannel fc = new RandomAccessFile(mPostProcessingFilePath, "rw").getChannel();
            out.writeContainer(fc);
            fc.close();
            deleteFile(mVideoFilePath);
            mListener.onVideoStop(mPostProcessingFilePath);
            return mPostProcessingFilePath;
        }
        mListener.onVideoStop(mVideoFilePath);
        return mFilePath;
    }catch (IOException e){
        mListener.onVideoError("");
        return mPostProcessingFilePath;
    }
}

In the parser function it checks the delta value, if it's bigger than 10000, it processes the recorded video and returns the result. In case if the delta value is less than 10000, it just returns video without processing. For more details, please refer to the link. Hope it helps you.



来源:https://stackoverflow.com/questions/60503030/how-to-fix-video-audio-out-of-sync-issue-on-android-video-recording-using-came

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!