我只想对音频文件进行一些处理而不播放它们。我怀疑我做得对吗,还有几个问题。我读了一些示例,但其中大多数都是关于视频流的,根本没有处理原始数据。

  • 我准备了一个具有2个相同通道的mp3文件,即,它是立体声的,但左侧和右侧相同。解码后,我期望获得具有相等对数的缓冲区,因为PCM-16交替存储通道样本,例如{L R L R L R ...},对吗?例如。:

    {105 105 601 601 -243 -243 -484 -484 ...}。

    但是我得到一对接近的数字,但不相等:

    {-308 -264 -1628 -1667 -2568 -2550 -4396 -4389}

    mp3算法对相同值的编码是否不同?为什么?
  • 我想处理1024个样本的数据包。如果没有足够的样品用于另一个包装,我想将其余样品保存到下一批原始数据为止(请参见代码中的mExcess)。是否可以保证订单会得到保留?
  • 我过去将“样本”理解为音频数据的每个单个值。在这里,我看到了MediaExtractor::readSampleDataMediaExtractor::advance方法。第一个返回〜2000的值,第二个描述为“前进到下一个样本”。这只是命名的重叠吗?我看到了几个示例,其中这些方法是成对循环调用的。我的用法正确吗?

  • 这是我的代码:
    public static void foo(String filepath) throws IOException {
        final int SAMPLES_PER_CHUNK = 1024;
    
        MediaExtractor mediaExtractor = new MediaExtractor();
        mediaExtractor.setDataSource(filepath);
        MediaFormat mediaFormat = mediaExtractor.getTrackFormat(0);
        mediaExtractor.release();
    
        MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
        mediaFormat.setString(MediaFormat.KEY_FRAME_RATE, null);
        String codecName = mediaCodecList.findDecoderForFormat(mediaFormat);
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 0);  // MediaCodec crashes with JNI
                                                                // error if FRAME_RATE is null
        MediaCodec mediaCodec = MediaCodec.createByCodecName(codecName);
        mediaCodec.setCallback(new MediaCodec.Callback() {
            private MediaExtractor mExtractor;
            private short[] mExcess;
    
            @Override
            public void onInputBufferAvailable(MediaCodec codec, int index) {
                if (mExtractor == null) {
                    mExtractor = new MediaExtractor();
                    try {
                        mExtractor.setDataSource(filepath);
                        mExtractor.selectTrack(0);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    mExcess = new short[0];
                }
                ByteBuffer in = codec.getInputBuffer(index);
                in.clear();
                int sampleSize = mExtractor.readSampleData(in, 0);
                if (sampleSize > 0) {
                    boolean isOver = !mExtractor.advance();
                    codec.queueInputBuffer(
                            index,
                            0,
                            sampleSize,
                            mExtractor.getSampleTime(),
                            isOver ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
                } else {
                    int helloAmaBreakpoint = 1;
                }
            }
    
            @Override
            public void onOutputBufferAvailable(
                    MediaCodec codec,
                    int index,
                    MediaCodec.BufferInfo info) {
                ByteBuffer tmp = codec.getOutputBuffer(index);
                if (tmp.limit() == 0) return;
    
                ShortBuffer out = tmp.order(ByteOrder.nativeOrder()).asShortBuffer();
                // Prepend the remainder from previous batch to the new data
                short[] buf = new short[mExcess.length + out.limit()];
                System.arraycopy(mExcess, 0, buf, 0, mExcess.length);
                out.get(buf, mExcess.length, out.limit());
    
                final int channelCount
                        = codec.getOutputFormat().getInteger(MediaFormat.KEY_CHANNEL_COUNT);
                for (
                        int offset  = 0;
                        offset + SAMPLES_PER_CHUNK * channelCount < buf.length;
                        offset += SAMPLES_PER_CHUNK * channelCount) {
    
                    double[] x = new double[SAMPLES_PER_CHUNK];  // left channel
                    double[] y = new double[SAMPLES_PER_CHUNK];  // right channel
                    switch (channelCount) {
                        case 1:  // if 1 channel then make 2 identical arrays
                            for (int i = 0; i < SAMPLES_PER_CHUNK; ++i) {
                                x[i] = (double) buf[offset + i];
                                y[i] = (double) buf[offset + i];
                            }
                            break;
                        case 2:  // if 2 channels then read values alternately
                            for (int i = 0; i < SAMPLES_PER_CHUNK; ++i) {
                                x[i] = (double) buf[offset + i * 2];
                                y[i] = (double) buf[offset + i * 2 + 1];
                            }
                            break;
                        default:
                            throw new IllegalStateException("No algorithm for " + channelCount + " channels");
                    }
    
                    /// ... some processing ... ///
                }
    
                // Save the rest until next batch of raw data
                int samplesLeft = buf.length % (SAMPLES_PER_CHUNK * channelCount);
                mExcess = new short[samplesLeft];
                System.arraycopy(
                        buf,
                        buf.length - samplesLeft,
                        mExcess,
                        0,
                        samplesLeft);
    
                codec.releaseOutputBuffer(index, false);
                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) > 0) {
                    codec.stop();
                    codec.release();
                    mExtractor.release();
                }
            }
    
            @Override
            public void onError(MediaCodec codec, MediaCodec.CodecException e) {
    
            }
    
            @Override
            public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
    
            }
        });
    
        mediaFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_16BIT);
        mediaCodec.configure(mediaFormat, null, null, 0);
        mediaCodec.start();
    }
    

    也欢迎快速代码审查。

    最佳答案

  • 我完全确定为什么会用这种方式对它们进行编码,但是我认为较小的方差在预期的公差范围内。请记住,mp3是有损编解码器,只要可听见的表示足够接近,解码器的输出值就不会与输入相同。但这并不能说明为什么两个 channel 最终会有所不同。
  • 是,已解码帧的各个顺序将相同。确切的值不匹配,但听起来应该相似。
  • 在MediaExtractor中,样本是一个编码的数据包,您应该将其馈送到解码器。对于mp3,通常为1152个样本(每个通道)。
  • 08-27 06:06