android - 用于音频的 MediaExtractor,得到意外的音频

标签 android ffmpeg exoplayer mediaextractor android-mediacodec

使用 MediaExtractor 类,我可以从保存的 mp4 视频中获取编码的音频样本数据,如下所示:

ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 256);
MediaExtractor audioExtractor = new MediaExtractor();
try {
    int trackIndex = -1;
    audioExtractor.setDataSource(originalMediaItem.getFilePath());

    for (int i = 0; i < audioExtractor.getTrackCount(); i++) {
        MediaFormat format = audioExtractor.getTrackFormat(i);
        String mime = format.getString(MediaFormat.KEY_MIME);

        if (mime.startsWith("audio/")) {
            trackIndex = i;
            break;
        }
    }

    audioExtractor.selectTrack(trackIndex);

    mAudioFormatMedia = audioExtractor.getTrackFormat(trackIndex);
    mAudioTrackIndex = mMediaMuxer.addTrack(mAudioFormatMedia);

    int size = audioExtractor.readSampleData(byteBuffer, 0);
    do {
        if (audioExtractor.getSampleTrackIndex() == 1) {
            long presentationTime = audioExtractor.getSampleTime();
            mInputBufferHashMap.put(presentationTime, byteBuffer);
            audioExtractor.advance();
            size = audioExtractor.readSampleData(byteBuffer, 0);
        }
    } while (size >= 0);
    audioExtractor.release();
    audioExtractor = null;
} catch (IOException e) {
    e.printStackTrace();
}

我有一个来自 GlSurface 的视频源,然后想使用 MediaMuxer 将此视频与前面提到的音频提取进行混合。在处理视频时,使用 hashmap 将音频交错到复用器中。我成功地混合了视频和音频并创建了可播放的 mp4 视频,但是音频听起来不像原始 mp4 的原始音频。

当我写入复用器时,我确实看到了预期的 bufferinfo.size 和 bufferInfo.presentationTimeUs:
mMediaMuxer.writeSampleData(mAudioTrackIndex, buffer, mAudioBufferInfo);
Log.d(TAG, String.format("Wrote %d audio bytes at %d", mAudioBufferInfo.size, mAudioBufferInfo.presentationTimeUs));

我尝试使用标准的 inputBuffer、outputBuffer 和 MediaCodec,就像这样 https://gist.github.com/a-m-s/1991ab18fbcb0fcc2cf9 ,但这会产生相同的音频,据我了解,MediaExtractor 应该已经是编码的音频数据,因此数据应该能够直接通过管道传输。

同样有趣的是,当我在最初提取时检查标志时:
if( (audioExtractor.getSampleFlags() & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) 
   Log.d(TAG, "BUFFER_FLAG_END_OF_STREAM")

以上内容均未针对原始 mp4 视频打印。我现在质疑原始 mp4 视频,以及是否有可能为 mp4 提供不可提取的音轨,以及我如何才能确认这一点。

我相信我已经看过了关于 stackoverflow 的大多数 MediaExtractor 问题以及 github 上的很多 MediaExtractor 单例解决方案。有谁知道以另一种方式提取音频的方法,即使用 ExoPlayer(最好不是 ffmpeg,因为它在 android 项目上增加了大量开销)。如果我当前的实现中有任何错误,任何见解都会有所帮助!

编辑 1:这是 audioExtractor.getTrackFormat(trackIndex) 的格式:

{max-bitrate=512000, sample-rate=48000, track-id=2, durationUs=22373187, mime=audio/mp4a-latm, profile=2, channel-count=4, language=```, aac-profile=2, bitrate=512000, max-input-size=1764, csd-0=java.nio.HeapByteBuffer[pos=0 lim=2 cap=2]}

最佳答案

问题是尝试为音频数据创建 map 。 AudioData 不正确。我可以通过在使用如下方法编写 videoData 时批处理音频样本数据来解决这个问题:

    private void writeAudioSampleData(
                MediaExtractor audioExtractor, MediaMuxer muxer, int filterStart, int filterEnd) {
        mFilterStart = filterEnd;
        MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();
        boolean audioExtractorDone = false;
        audioExtractor.seekTo(filterStart, MediaExtractor.SEEK_TO_CLOSEST_SYNC);

        synchronized (mAudioLockObject) {
            while (!audioExtractorDone) {
                try {
                    audioBufferInfo.size =
                            audioExtractor.readSampleData(audioInputBuffer, 0);
                } catch (Exception e) {
                    e.printStackTrace();
                }

                if (DEBUG) {
                    Log.d(TAG, "audioBufferInfo.size: " + audioBufferInfo.size);
                }

                if (audioBufferInfo.size < 0) {
                    audioBufferInfo.size = 0;
                    audioExtractorDone = true;
                } else {
                    audioBufferInfo.presentationTimeUs = audioExtractor.getSampleTime();
                    if (audioBufferInfo.presentationTimeUs > filterEnd) {
                        break;      //out of while
                    }
                    if (audioBufferInfo.presentationTimeUs >= filterStart &&
                            audioBufferInfo.presentationTimeUs <= filterEnd) {
                        audioBufferInfo.presentationTimeUs -= mOriginalMediaItem.mRecordingStartTs;
                        audioBufferInfo.flags = audioExtractor.getSampleFlags();
                        try {
                            muxer.writeSampleData(mAudioTrackIndex, audioInputBuffer,
                                    audioBufferInfo);
                            if (DEBUG)Log.d(TAG, String.format("Wrote %d audio bytes at %d",
                                    audioBufferInfo.size, audioBufferInfo.presentationTimeUs));
                        } catch(IllegalArgumentException | IllegalStateException |
                                NullPointerException ignore) {}
                    }

                    audioExtractor.advance();
                }
            }
        }

关于android - 用于音频的 MediaExtractor,得到意外的音频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48594218/

相关文章:

java - 将 Exoplayer 演示与 Firebase 实时数据库结合使用

Android - ExoPlayer 2 离线播放 DRM (widevine)

java - 将 Serilizable 转换为 HashMap 的派生

android - ExoPlayer 下载后从缓存/离线播放文件

android - Volley 内存不足错误

ubuntu - 如何组合两个 ffmpeg 命令 - 添加模糊条和添加 Logo 和字幕?

swift - 如何在 Swift 中从 UnsafeMutablePointer<UInt8> 获取 CVPixelBuffer 句柄?

python - ffmpeg xstack 过滤器布局限制

java - 设置 TextView 的文本时出现 NullPointerException

android - 当移动设备离开特定区域时发送短信