MediaCodec - 缺少编解码器特定数据

MediaCodec - Missing codec specific data

我们的项目使用设备的麦克风作为音频输入录制视频,并将 openGL 场景作为视频 - openGL 场景是音频的可视化(频谱仪等)。在 Android 11 更新之前,它在设备上运行良好,但现在更新设备上的输出视频没有声音。该问题并非特定于任何制造商,例如Google Pixel 3/4 和 Samsung Galaxy S20 均受到影响。没有运行时错误或崩溃,使用什么设备播放文件也没有关系——它绝对没有声音。视频中的音频可视化很好,所以 microphone/input 不是问题。我们使用 MediaMuxer 和 MediaCodec 将录音输出到 mp4 文件。

是否有其他人在使用最新的 Android 11 设备更新时遇到此问题?有人对我们如何调试有什么建议吗?

这是我们配置视频和音频编解码器的方式:

//VIDEO OUTPUT (MP4)
public static final String VIDEO_MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
public static final int VIDEO_FRAME_RATE = 30; 
public static final int VIDEO_IFRAME_INTERVAL = 5;
public static final String AUDIO_MIME_TYPE = MediaFormat.MIMETYPE_AUDIO_AAC;Video Coding

//AUDIO INPUT (MIC)
public static final int RECORDER_SAMPLE_RATE = 44100;
public static final int RECORDER_CHANNELS = 1;
public static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;

//AUDIO OUTPUT (MP4)
public static final String ENCODER_AUDIO_FORMAT = MediaFormat.MIMETYPE_AUDIO_AAC;
public static final int ENCODER_AAC_PROFILE = MediaCodecInfo.CodecProfileLevel.AACObjectLC;
public static final int AUDIO_BIT_RATE = 128000; 
public static final int AUDIO_SAMPLE_RATE = 44100;
public static final int AUDIO_SAMPLES_PER_FRAME = 1024;
public static final int AUDIO_FRAMES_PER_BUFFER = 25;
public static final int AUDIO_BYTES_PER_ELEMENT = 2;
public static final int AUDIO_NUMBER_OF_SPECTRUM_BINS = 20;

//...

//Initialise Video Encoder
MediaFormat format = MediaFormat.createVideoFormat(Config.VIDEO_MIME_TYPE, width, height);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
format.setInteger(MediaFormat.KEY_FRAME_RATE, Config.VIDEO_FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, Config.VIDEO_IFRAME_INTERVAL);
videoEncoder = MediaCodec.createEncoderByType(Config.VIDEO_MIME_TYPE);
videoEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mInputSurface = videoEncoder.createInputSurface();
videoEncoder.start();

//Initialise Audio Encoder
audioEncoder = MediaCodec.createEncoderByType(Config.AUDIO_MIME_TYPE);
MediaFormat audioFormat = MediaFormat.createAudioFormat(Config.ENCODER_AUDIO_FORMAT,Config.AUDIO_SAMPLE_RATE,Config.RECORDER_CHANNELS);
audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, Config.ENCODER_AAC_PROFILE);
audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, Config.AUDIO_BIT_RATE);
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, Config.RECORDER_CHANNELS);
audioEncoder.configure(audioFormat, null, null,MediaCodec.CONFIGURE_FLAG_ENCODE);
audioEncoder.start();

//Initiate Muxer
mMuxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mMuxer.setOrientationHint(orientationHint);

我们在收到第一帧时开始复用

//...
else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
    MediaFormat videoFormat = videoEncoder.getOutputFormat();
    MediaFormat audioFormat = audioEncoder.getOutputFormat();
    videoTrackIndex = mMuxer.addTrack(videoFormat);
    audioTrackIndex = mMuxer.addTrack(audioFormat);
    mMuxer.start();
}

检查受影响设备上的详细日志,在点击“停止记录”按钮后,我能够找到以下感兴趣的行:

2021-03-08 14:52:12.658 20015-20241/com.xx.xx V/Audio:VideoEncoderCore: Sending EOS to encoder!
2021-03-08 14:52:12.670 20015-20241/com.xx.xx D/MPEG4Writer: reset()
**2021-03-08 14:52:12.670 20015-20259/com.xx.xx E/MPEG4Writer: Missing codec specific data**
2021-03-08 14:52:12.670 20015-20259/com.xx.xx E/MPEG4Writer: 0 frames to dump timeStamps in Audio track 
**2021-03-08 14:52:12.670 20015-20258/com.xx.xx V/MediaWriter: Track event err/info msg:101, trackId:1, type:1000,val:-1011 **
2021-03-08 14:52:12.670 20015-20258/com.xx.xx I/MPEG4Writer: Received total/0-length (69/0) buffers and encoded 69 frames. - Video
2021-03-08 14:52:12.670 20015-20241/com.xx.xx D/MPEG4Writer: Video track stopping. Stop source
2021-03-08 14:52:12.670 20015-20241/com.xx.xx D/MPEG4Writer: Video track source stopping
2021-03-08 14:52:12.670 20015-20241/com.xx.xx D/MPEG4Writer: Video track source stopped
2021-03-08 14:52:12.670 20015-20241/com.xx.xx D/MPEG4Writer: Video track stopped. Status:0. Stop source
2021-03-08 14:52:12.670 20015-20241/com.xx.xx D/MPEG4Writer: Audio track stopping. Stop source
2021-03-08 14:52:12.670 20015-20241/com.xx.xx D/MPEG4Writer: Audio track source stopping
2021-03-08 14:52:12.670 20015-20241/com.xx.xx D/MPEG4Writer: Audio track source stopped
2021-03-08 14:52:12.670 20015-20259/com.xx.xx V/MediaWriter: Track event err/info msg:100, trackId:2, type:100,val:-1007
2021-03-08 14:52:12.670 20015-20259/com.xx.xx I/MPEG4Writer: Received total/0-length (87/0) buffers and encoded 87 frames. - Audio
2021-03-08 14:52:12.670 20015-20259/com.xx.xx I/MPEG4Writer: Audio track drift time: 0 us
2021-03-08 14:52:12.670 20015-20262/com.xx.xx I/hw-BpHwBinder: onLastStrongRef automatically unlinking death recipients
**2021-03-08 14:52:12.671 20015-20241/com.xx.xx D/MPEG4Writer: Audio track stopped. Status:-1007. Stop source **
**2021-03-08 14:52:12.671 20015-20241/com.xx.xx W/MPEG4Writer: Condition trackErr == OK failed Audio track stopped with an error **
2021-03-08 14:52:12.671 20015-20241/com.xx.xx D/MPEG4Writer: Duration from tracks range is [2050653, 2293393] us
2021-03-08 14:52:12.671 20015-20256/com.xx.xx D/MPEG4Writer: 0 chunks are written in the last batch
2021-03-08 14:52:12.671 20015-20241/com.xx.xx D/MPEG4Writer: WriterThread stopped. Status:0
2021-03-08 14:52:12.671 20015-20241/com.xx.xx I/MPEG4Writer: Adjust the moov start time from 0 us -> 0 us
2021-03-08 14:52:12.671 20015-20241/com.xx.xx I/MPEG4Writer: MOOV atom was written to the file
2021-03-08 14:52:12.671 20015-20241/com.xx.xx D/MPEG4Writer: release()

以下错误帮助我解决了这个问题:

2021-03-08 14:52:12.670 20015-20259/com.xx.xx E/MPEG4Writer: Missing codec specific data

有关 codec specific data 及其内容的更多信息。

我仍然不知道为什么这只在 Android 11 推送后才开始,但我知道原因以及如何解决它。最初,我会在收到第一帧视频后从视频线程中的音频和视频编码器中提取 MediaFormat,然后调用 MediaMuxer.start()。直到 Android 11,这种方法工作得很好。但是,更新后,我的音频格式附加了内容特定数据,需要将其提供给混合器。不幸的是,此数据仅在接收到第一帧声音(而非第一帧视频)后才能从音频编码器获得。因此,我之前的方法行不通,因为在接收到第一帧视频之前我没有传递音频帧,并且在从我的音频编码器接收到编解码器特定数据之前我启动了我的复用器。

解决方案

等待接收第一帧声音并在您的音频线程中调用 MediaCodec.getMediaFormat()。对视频线程中的视频执行相同的操作。包含逻辑以检查是否已首先接收到音频和视频格式,然后在您的视频线程中启动复用器。我所有的输出文件现在都有视频和音频。

我在处理一些混合问题时遇到了同样的问题。你的解决方案的坏处是你会丢失所有由相机提供给 mediamuxer 的视频帧,直到你收到第一个音频帧。我想在编码开始时提供一些虚拟音频帧可能会更好?