它仅在某些设备(例如 Huawei P40 pro 或 Honor P20 )上发生。 在其他设备上,一切正常。
这是我从视频中提取音频的方式:
/**
* Work loop.
*/
void doExtract(MediaExtractor extractor, int trackIndex, MediaCodec decoder) throws Throwable {
final int TIMEOUT_USEC = 10000;
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
int inputChunk = 0;
int decodeCount = 0;
boolean inputDone = false;
ByteBuffer inputBuf;
while (!Thread.interrupted()) {
if (VERBOSE) Log.d(TAG, "loop");
// Feed more data to the decoder.
if (!inputDone) {
int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
if (inputBufIndex >= 0) {
inputBuf = decoder.getInputBuffer(inputBufIndex);
int chunkSize = extractor.readSampleData(inputBuf, 0);
if (chunkSize < 0) {
// End of stream -- send empty frame with EOS flag set.
decoder.queueInputBuffer(inputBufIndex, 0, 0, 0L,
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
inputDone = true;
if (VERBOSE) Log.d(TAG, "sent input EOS");
} else {
if (extractor.getSampleTrackIndex() != trackIndex) {
Log.w(TAG, "WEIRD: got sample from track " +
extractor.getSampleTrackIndex() + ", expected " + trackIndex);
}
decoder.queueInputBuffer(inputBufIndex, 0, chunkSize,
extractor.getSampleTime(), 0 /*flags*/);
if (VERBOSE) {
Log.d(TAG, "submitted frame " + inputChunk + " to dec, size=" +
chunkSize);
}
inputChunk++;
extractor.advance();
}
} else {
if (VERBOSE) Log.d(TAG, "input buffer not available");
}
}
int decoderStatus = decoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
// no output available yet
if (VERBOSE) Log.d(TAG, "no output from decoder available");
} else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
// not important for us, since we're using Surface
if (VERBOSE) Log.d(TAG, "decoder output buffers changed");
} else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat newFormat = decoder.getOutputFormat();
if (VERBOSE) Log.d(TAG, "decoder output format changed: " + newFormat);
} else if (decoderStatus < 0) {
listener.failed(new Throwable("unexpected result from decoder.dequeueOutputBuffer: " + decoderStatus), this);
break;
} else { // decoderStatus >= 0
if (VERBOSE) Log.d(TAG, "surface decoder given buffer " + decoderStatus +
" (size=" + info.size + ")");
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
if (VERBOSE) Log.d(TAG, "output EOS");
listener.finished(this);
break;
}
boolean doRender = info.size != 0;
ByteBuffer byteBuffer = null;
if (doRender) {
ByteBuffer outputByteBuffer = decoder.getOutputBuffer(decoderStatus);
if (outputByteBuffer != null) {
byteBuffer = outputByteBuffer;
}
}
decoder.releaseOutputBuffer(decoderStatus, false);
if (doRender && byteBuffer != null) {
frameTimeUs = info.presentationTimeUs;
if (VERBOSE) Log.d(TAG, "awaiting decode of frame " + decodeCount);
try {
synchronized (syncObject) {
this.byteBuffer = byteBuffer;
this.size = info.size;
this.offset = info.offset;
this.flags = info.flags;
listener.frameReady(AudioExtractor.this);
syncObject.wait();
}
} catch (InterruptedException exception) {
Timber.e(exception);
break;
}
decodeCount++;
}
}
}
}
enter code here
这是我使用编码器的方式:
public void encodeAudio(@NonNull AudioExtractor audioExtractor) {
MediaCodec audioEncoder = audioEncoders.get(audioExtractor);
while (true) {
int encoderInputBufferIndex = audioEncoder.dequeueInputBuffer(TIMEOUT_USEC);
if (encoderInputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
if (VERBOSE) Log.d(TAG, "no audio encoder input buffer");
break;
}
if (VERBOSE) {
Log.d(TAG, "audio encoder: returned input buffer: " + encoderInputBufferIndex);
}
ByteBuffer encoderInputBuffer = audioEncoder.getInputBuffer(encoderInputBufferIndex);
if (encoderInputBuffer != null && audioExtractor.byteBuffer != null) {
try {
encoderInputBuffer.put(audioExtractor.byteBuffer);
audioEncoder.queueInputBuffer(
encoderInputBufferIndex,
audioExtractor.offset,
audioExtractor.size,
audioExtractor.getFrameTimeUs(),
audioExtractor.flags
);
drainAudioEncoder(audioExtractor);
} catch (IllegalStateException exception) {
Timber.e(exception);
}
break;
}
}
}
private void drainAudioEncoder(@NonNull AudioExtractor audioExtractor) {
ByteBuffer byteBuffer;
MediaCodec audioEncoder = audioEncoders.get(audioExtractor);
while (true) {
int encoderStatus = audioEncoder.dequeueOutputBuffer(audioBufferInfo, TIMEOUT_USEC);
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
break; // out of while
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// should happen before receiving buffers, and should only happen once
if (videoMuxer.isAudioTrackAdded(audioExtractor)) {
throw new RuntimeException("format changed twice");
}
MediaFormat newFormat = audioEncoder.getOutputFormat();
Log.d(TAG, "encoder output format changed: " + newFormat);
// Start the muxer
videoMuxer.addAudioTrack(audioExtractor, audioEncoder);
} else if (encoderStatus < 0) {
Log.w(TAG, "unexpected result from encoder.dequeueOutputBuffer: " +
encoderStatus);
// let's ignore it
} else {
byteBuffer = audioEncoder.getOutputBuffer(encoderStatus);
if (byteBuffer == null) {
throw new RuntimeException("encoderOutputBuffer " + encoderStatus +
" was null");
}
if ((audioBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
// The codec config data was pulled out and fed to the muxer when we got
// the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");
audioBufferInfo.size = 0;
}
if (audioBufferInfo.size != 0) {
if (!videoMuxer.isStarted()) {
throw new RuntimeException("muxer hasn't started");
}
videoMuxer.muxAudio(audioExtractor, byteBuffer, audioBufferInfo);
audioEncoder.releaseOutputBuffer(encoderStatus, false);
break;
}
if ((audioBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.w(TAG, "reached end of stream unexpectedly");
}
}
}
}
输入和输出音频编解码器为MIMETYPE_AUDIO_AAC =“ audio / mp4a-latm”
上面的代码取自经过修改的Google编码器/解码器测试。
有人知道某些设备上的噪音音频问题吗?