关于使用MediaCodec
进行解码时有关音频和视频同步的所有问题,建议我们应使用“ AV同步”机制使用其时间戳来同步视频和音频。
这是我要做的事情:
我有2个线程,一个用于解码视频,一个用于音频。要同步我正在使用Extractor.getSampleTime()
确定是否应释放音频或视频缓冲区的视频和音频,请参见以下内容:
//This is called after configuring MediaCodec(both audio and video)
private void startPlaybackThreads(){
//Audio playback thread
mAudioWorkerThread = new Thread("AudioThread") {
@Override
public void run() {
if (!Thread.interrupted()) {
try {
//Check info below
if (shouldPushAudio()) {
workLoopAudio();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
mAudioWorkerThread.start();
//Video playback thread
mVideoWorkerThread = new Thread("VideoThread") {
@Override
public void run() {
if (!Thread.interrupted()) {
try {
//Check info below
if (shouldPushVideo()) {
workLoopVideo();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
mVideoWorkerThread.start();
}
//Check if more buffers should be sent to the audio decoder
private boolean shouldPushAudio(){
int audioTime =(int) mAudioExtractor.getSampleTime();
int videoTime = (int) mExtractor.getSampleTime();
return audioTime <= videoTime;
}
//Check if more buffers should be sent to the video decoder
private boolean shouldPushVideo(){
int audioTime =(int) mAudioExtractor.getSampleTime();
int videoTime = (int) mExtractor.getSampleTime();
return audioTime > videoTime;
}
在workLoopAudio()
和workLoopVideo()
内部是我的所有MediaCodec
逻辑(我决定不发布它,因为它不相关)。
所以我要做的是,获得视频和音频轨道的采样时间,然后检查哪个更大(更远)。如果视频“超前”,那么我会将更多缓冲区传递给音频解码器,反之亦然。
这似乎工作正常-视频和音频正在同步播放。
我的问题:] >>
我想知道我的方法是否正确(这是我们应该怎么做,还是有另一种/更好的方法)?我找不到任何可用的工作示例(用java / kotlin编写),因此出现了问题。
[我发现解码/播放使用FFmpeg
编码的视频时,音频落后于视频(非常轻微)。如果我使用未使用FFmpeg
编码的视频,则视频和音频会完美同步。
FFmpeg
命令与众不同:
-i inputPath -crf 18 -c:v libx264 -preset ultrafast OutputPath
我将在下面提供更多信息:
我像这样初始化/创建AudioTrack
:
//Audio mAudioExtractor = new MediaExtractor(); mAudioExtractor.setDataSource(mSource); int audioTrackIndex = selectAudioTrack(mAudioExtractor); if (audioTrackIndex < 0){ throw new IOException("Can't find Audio info!"); } mAudioExtractor.selectTrack(audioTrackIndex); mAudioFormat = mAudioExtractor.getTrackFormat(audioTrackIndex); mAudioMime = mAudioFormat.getString(MediaFormat.KEY_MIME); mAudioChannels = mAudioFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT); mAudioSampleRate = mAudioFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE); final int min_buf_size = AudioTrack.getMinBufferSize(mAudioSampleRate, (mAudioChannels == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO), AudioFormat.ENCODING_PCM_16BIT); final int max_input_size = mAudioFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE); mAudioInputBufSize = min_buf_size > 0 ? min_buf_size * 4 : max_input_size; if (mAudioInputBufSize > max_input_size) mAudioInputBufSize = max_input_size; final int frameSizeInBytes = mAudioChannels * 2; mAudioInputBufSize = (mAudioInputBufSize / frameSizeInBytes) * frameSizeInBytes; mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, mAudioSampleRate, (mAudioChannels == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO), AudioFormat.ENCODING_PCM_16BIT, AudioTrack.getMinBufferSize(mAudioSampleRate, mAudioChannels == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT), AudioTrack.MODE_STREAM); try { mAudioTrack.play(); } catch (final Exception e) { Log.e(TAG, "failed to start audio track playing", e); mAudioTrack.release(); mAudioTrack = null; }
并且我这样写到
AudioTrack
:
//Called from within workLoopAudio, when releasing audio buffers if (bufferAudioIndex >= 0) { if (mAudioBufferInfo.size > 0) { internalWriteAudio(mAudioOutputBuffers[bufferAudioIndex], mAudioBufferInfo.size); } mAudioDecoder.releaseOutputBuffer(bufferAudioIndex, false); } private boolean internalWriteAudio(final ByteBuffer buffer, final int size) { if (mAudioOutTempBuf.length < size) { mAudioOutTempBuf = new byte[size]; } buffer.position(0); buffer.get(mAudioOutTempBuf, 0, size); buffer.clear(); if (mAudioTrack != null) mAudioTrack.write(mAudioOutTempBuf, 0, size); return true; }
“ NEW”问题:
如果我使用通过FFmpeg
编码的视频,音频会落后于视频约200ms,请问为什么会发生这种情况?
关于使用MediaCodec进行解码时有关音频和视频同步的所有问题,建议我们应使用“ AV同步”机制使用其时间戳同步视频和音频。这是...
似乎现在正在工作。我使用与上述相同的逻辑,但是现在在调用presentationTimeUs
以检查是否应该继续我的视频或音频工作循环之前,请保留从MediaCodec.BufferInfo()
返回的dequeueOutputBuffer
的引用: