android - mp4文件使用mediacodec和mediamuxer时的音频和视频轨道同步问题

标签 android audio video android-mediacodec mediamuxer

我想通过多路复用来自mic的音频(覆盖didGetAudioData)和来自摄像机的视频(覆盖onpreviewframe)来产生mp4文件。但是,我遇到了声音和视频同步问题,视频显示的速度会比音频快。我想知道问题是否与不兼容的配置或presentationTimeUs有关,有人可以指导我如何解决此问题。以下是我的软件。

视频配置

formatVideo = MediaFormat.createVideoFormat(MIME_TYPE_VIDEO, 640, 360);
formatVideo.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
formatVideo.setInteger(MediaFormat.KEY_BIT_RATE, 2000000);
formatVideo.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
formatVideo.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);

视频演示PTS如下
if(generateIndex == 0) {
    videoAbsolutePtsUs = 132;
    StartVideoAbsolutePtsUs = System.nanoTime() / 1000L;
}else {
    CurrentVideoAbsolutePtsUs = System.nanoTime() / 1000L;
    videoAbsolutePtsUs =132+ CurrentVideoAbsolutePtsUs-StartVideoAbsolutePtsUs;
}
generateIndex++;

音频配置
format = MediaFormat.createAudioFormat(MIME_TYPE, 48000/*sample rate*/, AudioFormat.CHANNEL_IN_MONO /*Channel config*/);
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE,48000);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT,1);
format.setInteger(MediaFormat.KEY_BIT_RATE,64000);

收到了如下的音频演示文稿PTS,
if(generateIndex == 0) {
   audioAbsolutePtsUs = 132;
   StartAudioAbsolutePtsUs = System.nanoTime() / 1000L;
}else {
   CurrentAudioAbsolutePtsUs = System.nanoTime() / 1000L;
   audioAbsolutePtsUs =CurrentAudioAbsolutePtsUs - StartAudioAbsolutePtsUs;
}

generateIndex++;
audioAbsolutePtsUs = getJitterFreePTS(audioAbsolutePtsUs, audioInputLength / 2);

long startPTS = 0;
long totalSamplesNum = 0;
private long getJitterFreePTS(long bufferPts, long bufferSamplesNum) {
    long correctedPts = 0;
    long bufferDuration = (1000000 * bufferSamplesNum) / 48000;
    bufferPts -= bufferDuration; // accounts for the delay of acquiring the audio buffer
    if (totalSamplesNum == 0) {
        // reset
        startPTS = bufferPts;
        totalSamplesNum = 0;
    }
    correctedPts = startPTS +  (1000000 * totalSamplesNum) / 48000;
    if(bufferPts - correctedPts >= 2*bufferDuration) {
        // reset
        startPTS = bufferPts;
        totalSamplesNum = 0;
        correctedPts = startPTS;
    }
    totalSamplesNum += bufferSamplesNum;
    return correctedPts;
}

我的问题是仅对音频应用抖动功能引起的吗?如果是,如何为视频应用抖动功能?我还尝试通过https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java找到正确的音频和视频演示文稿PTS。但是encodeecodeTest仅提供视频PTS。这就是我的实现对音频和视频都使用系统nanotime的原因。如果要在encodedecodetest中使用视频presentationPTS,如何构造兼容的音频presentationPTS?感谢帮助!

以下是我如何将yuv帧排队到视频mediacodec以供引用。对于音频部分,除了不同的presentationPTS外,其余部分相同。
int videoInputBufferIndex;
int videoInputLength;
long videoAbsolutePtsUs;
long StartVideoAbsolutePtsUs, CurrentVideoAbsolutePtsUs;

int put_v =0;
int get_v =0;
int generateIndex = 0;

public void setByteBufferVideo(byte[] buffer, boolean isUsingFrontCamera, boolean Input_endOfStream){
    if(Build.VERSION.SDK_INT >=18){
        try{

            endOfStream = Input_endOfStream;
            if(!Input_endOfStream){
            ByteBuffer[] inputBuffers = mVideoCodec.getInputBuffers();
            videoInputBufferIndex = mVideoCodec.dequeueInputBuffer(-1);

                if (VERBOSE) {
                    Log.w(TAG,"[put_v]:"+(put_v)+"; videoInputBufferIndex = "+videoInputBufferIndex+"; endOfStream = "+endOfStream);
                }

                if(videoInputBufferIndex>=0) {
                    ByteBuffer inputBuffer = inputBuffers[videoInputBufferIndex];
                    inputBuffer.clear();

                    inputBuffer.put(mNV21Convertor.convert(buffer));
                    videoInputLength = buffer.length;

                    if(generateIndex == 0) {
                        videoAbsolutePtsUs = 132;
                        StartVideoAbsolutePtsUs = System.nanoTime() / 1000L;
                    }else {
                        CurrentVideoAbsolutePtsUs = System.nanoTime() / 1000L;
                        videoAbsolutePtsUs =132+ CurrentVideoAbsolutePtsUs - StartVideoAbsolutePtsUs;
                    }

                    generateIndex++;

                    if (VERBOSE) {
                        Log.w(TAG, "[put_v]:"+(put_v)+"; videoAbsolutePtsUs = " + videoAbsolutePtsUs + "; CurrentVideoAbsolutePtsUs = "+CurrentVideoAbsolutePtsUs);
                    }

                    if (videoInputLength == AudioRecord.ERROR_INVALID_OPERATION) {
                        Log.w(TAG, "[put_v]ERROR_INVALID_OPERATION");
                    } else if (videoInputLength == AudioRecord.ERROR_BAD_VALUE) {
                        Log.w(TAG, "[put_v]ERROR_ERROR_BAD_VALUE");
                    }
                    if (endOfStream) {
                        Log.w(TAG, "[put_v]:"+(put_v++)+"; [get] receive endOfStream");
                        mVideoCodec.queueInputBuffer(videoInputBufferIndex, 0, videoInputLength, videoAbsolutePtsUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    } else {
                        Log.w(TAG, "[put_v]:"+(put_v++)+"; receive videoInputLength :" + videoInputLength);
                        mVideoCodec.queueInputBuffer(videoInputBufferIndex, 0, videoInputLength, videoAbsolutePtsUs, 0);
                    }
                }
            }
        }catch (Exception x) {
            x.printStackTrace();
        }
    }
}

最佳答案

我如何在应用程序中解决此问题的方法是将所有视频和音频帧的PTS设置为使用共享的“同步时钟”(请注意 sync 也意味着它是线程安全的),该时间从第一个视频帧(具有PTS 0)开始本身)可用。因此,如果音频录制的开始时间早于视频,则音频数据将一直被丢弃(不会进入编码器),直到视频开始播放为止;如果音频录制的开始时间较晚,则第一个音频PTS将相对于整个视频的开始位置。

当然,您可以自由地允许音频首先开始,但是播放器通常无论如何都会跳过或等待第一个视频帧。还要注意,编码的音频帧将“乱序”到达,MediaMuxer迟早会失败并出现错误。我的解决方案是像这样对所有队列进行排队:当有新的队列时,按pt对它们进行排序,然后将500ms以上(相对于最新的)的所有内容都写入MediaMuxer,但仅将PTS高于最新的内容写入书面框架。理想情况下,这意味着将数据平滑地写入MediaMuxer,延迟为500 ms。最坏的情况是,您将丢失一些音频帧。

关于android - mp4文件使用mediacodec和mediamuxer时的音频和视频轨道同步问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36135282/

相关文章:

android - 有什么地方可以让我从 Android 设计指南中获得典型的 XML 样式吗?

android - 将maven编写的项目导入Eclipse

audio - Transcodeit 将元数据添加到音频文件

android - 在电话上发送流音频

java - Xuggler 和直播

android - 我可以将 debug.keystore 从一台电脑复制到另一台电脑吗?

Android:在 Continuous ScrollView 上绘图

python - Pygame中的声音问题

Python数组3D转视频

video - 计算视频视频的比特率