在某些设备上使用MediaCodec编码器后,音频中会产生噪声

时间:2020-07-29 12:20:16

标签: android audio android-mediacodec

它仅在某些设备(例如 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编码器/解码器测试。

有人知道某些设备上的噪音音频问题吗?

0 个答案:

没有答案