簡體   English   中英

Android:MediaCodec + MediaMuxer編碼的音頻MP4無法播放

[英]Android: MediaCodec+MediaMuxer encoded audio MP4 won't play

我有以下函數,該函數需要一個WAV(PCM)文件,並使用Android的MediaCode和MediaMuxer類將其編碼為AAC編碼的MP4文件。 這僅是音頻。 該函數成功運行,並輸出一個公認的AAC編碼的.mp4。 但是它不能在Android,Web或iOS播放器上播放,並且會使Audacity崩潰。 我有什么想念的嗎? 代碼如下所示。

public void encode(final String from, final String to, final Callback callback) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                extractor.setDataSource(from);
                int numTracks = extractor.getTrackCount();
                for (int i = 0; i < numTracks; ++i) {
                    MediaFormat format = extractor.getTrackFormat(i);
                    String mime = format.getString(MediaFormat.KEY_MIME);
                    Log.d(TAG, "Track " + i + " mime-type: " + mime);
                    if (true) {
                        extractor.selectTrack(i);
                    }
                }

                MediaCodec codec = MediaCodec.createEncoderByType("audio/mp4a-latm");
                MediaFormat format = new MediaFormat();
                format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
                format.setInteger(MediaFormat.KEY_BIT_RATE, 128 * 1024);
                format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
                format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
                format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
                codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
                final MediaMuxer muxer = new MediaMuxer(to, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
                final ByteBuffer byteBuffer = ByteBuffer.allocate(65536);
                codec.setCallback(new MediaCodec.Callback() {
                    @Override
                    public void onInputBufferAvailable(MediaCodec codec, int bufferIndex) {
                        ByteBuffer inputBuffer = codec.getInputBuffer(bufferIndex);
                        if (isEndOfStream) {
                            return;
                        }
                        int sampleCapacity = inputBuffer.capacity() / 8;
                        if (numAvailable == 0) {
                            numAvailable = extractor.readSampleData(byteBuffer, 0);
                            if (numAvailable <= 0) {
                                codec.queueInputBuffer(bufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                                isEndOfStream = true;
                                return;
                            }
                            long presentationTimeUs = extractor.getSampleTime();
                            extractor.advance();
                        }
                        if (numAvailable < sampleCapacity) {
                            codec.queueInputBuffer(bufferIndex, 0, numAvailable * 8, 0, 0);
                            numAvailable = 0;
                        } else {
                            codec.queueInputBuffer(bufferIndex, 0, sampleCapacity * 8, 0, 0);
                            numAvailable -= sampleCapacity;
                        }
                    }

                    @Override
                    public void onOutputBufferAvailable(MediaCodec codec, int index, MediaCodec.BufferInfo info) {
                        ByteBuffer outputBuffer = codec.getOutputBuffer(index);
                        muxer.writeSampleData(audioTrackIndex,outputBuffer,info);
                        codec.releaseOutputBuffer(index, true);
                        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                            Log.d(TAG, "end of encoding!");
                            codec.stop();
                            codec.release();
                            extractor.release();
                            extractor = null;
                            muxer.stop();
                            muxer.release();
                            callback.run(true);
                        }
                    }

                    @Override
                    public void onError(MediaCodec codec, MediaCodec.CodecException e) {
                        Log.e(TAG, "codec error", e);

                    }

                    @Override
                    public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
                        audioTrackIndex = muxer.addTrack(format);
                        muxer.start();

                    }
                });
                codec.start();
            } catch (IOException e) {
                Log.e(TAG,"Unable to encode",e);
                callback.run(false);
            }

        }
    }).run();

您似乎將AAC編碼為LATM 格式 ,這種格式不太流行。 也許這就是玩家不玩的原因。 嘗試使用其他媒體類型, audio/mp4audio/3gpp

請參閱AAC容器格式

你需要:

  1. 由於媒體復用器需要使用它來標記數據包時間信息,因此請正確添加時間戳信息。
  2. 添加邏輯以將數據緩沖區從提取器數據緩沖區(PCM)復制到媒體編解碼器輸入緩沖區,僅引用緩沖區索引將僅對隨機數據緩沖區進行編碼而無需初始。
  3. 添加代碼以將輸入源屬性(例如頻道和采樣率)應用於mediacodec。 不知道您是否打算使用不同的通道和采樣率進行編碼!

示例代碼如下:

MediaExtractor extractor = null;
int numAvailable = 0;
boolean isEndOfStream = false;
int audioTrackIndex = 0;
long totalen = 0;
int channels = 0;
int sampleRate = 0;
public void encode(final String from, final String to) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                extractor = new MediaExtractor();
                extractor.setDataSource(from);
                int numTracks = extractor.getTrackCount();
                for (int i = 0; i < numTracks; ++i) {
                    MediaFormat format = extractor.getTrackFormat(i);
                    String mime = format.getString(MediaFormat.KEY_MIME);
                    Log.d(TAG, "Track " + i + " mime-type: " + mime);
                    if (true) {
                        extractor.selectTrack(i);
                        channels = extractor.getTrackFormat(i).getInteger(MediaFormat.KEY_CHANNEL_COUNT);
                        sampleRate = extractor.getTrackFormat(i).getInteger(MediaFormat.KEY_SAMPLE_RATE);
                        Log.e(TAG,"sampleRate:" + sampleRate + " channels:" + channels);
                    }
                }
                String mimeType = "audio/mp4a-latm";
                MediaCodec codec = MediaCodec.createEncoderByType(mimeType);
                MediaFormat format = new MediaFormat();
                format.setString(MediaFormat.KEY_MIME, mimeType);
                format.setInteger(MediaFormat.KEY_BIT_RATE, 128 * 1024);
                format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, channels);
                format.setInteger(MediaFormat.KEY_SAMPLE_RATE, sampleRate);
                format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
                codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
                final MediaMuxer muxer = new MediaMuxer(to, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
                final ByteBuffer byteBuffer = ByteBuffer.allocate(65536);
                codec.setCallback(new MediaCodec.Callback() {
                    @Override
                    public void onInputBufferAvailable(MediaCodec codec, int bufferIndex) {
                        ByteBuffer inputBuffer = codec.getInputBuffer(bufferIndex);
                        inputBuffer.clear();
                        if (isEndOfStream) {
                            return;
                        }
                        int sampleCapacity = inputBuffer.capacity();
                        if (numAvailable == 0) {
                            numAvailable = extractor.readSampleData(byteBuffer, 0);
                            if (numAvailable <= 0) {
                                codec.queueInputBuffer(bufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                                isEndOfStream = true;
                                return;
                            }
                            extractor.advance();
                        }
                        long timestampUs = 1000000l * totalen / (2 * channels * sampleRate);
                        if (numAvailable < sampleCapacity) {
                            byte[] byteArray = new byte[numAvailable];
                            byteBuffer.get(byteArray);
                            inputBuffer.put(byteArray, 0, (int)numAvailable);
                            totalen += numAvailable;
                            codec.queueInputBuffer(bufferIndex, 0, numAvailable, timestampUs, 0);
                            numAvailable = 0;
                        } else {
                            byte[] byteArray = new byte[sampleCapacity];
                            byteBuffer.get(byteArray);
                            inputBuffer.put(byteArray, 0, (int)sampleCapacity);
                            totalen += sampleCapacity;
                            codec.queueInputBuffer(bufferIndex, 0, sampleCapacity, timestampUs, 0);
                            numAvailable -= sampleCapacity;
                        }
                    }
                    @Override
                    public void onOutputBufferAvailable(MediaCodec codec, int index, MediaCodec.BufferInfo info) {
                        ByteBuffer outputBuffer = codec.getOutputBuffer(index);
                        muxer.writeSampleData(audioTrackIndex,outputBuffer,info);
                        codec.releaseOutputBuffer(index, true);
                        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                            Log.d(TAG, "end of encoding!");
                            codec.stop();
                            codec.release();
                            extractor.release();
                            extractor = null;
                            muxer.stop();
                            muxer.release();
                            //callback.run(true);
                        }
                    }
                    @Override
                    public void onError(MediaCodec codec, MediaCodec.CodecException e) {
                        Log.e(TAG, "codec error", e);

                    }
                    @Override
                    public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
                        audioTrackIndex = muxer.addTrack(format);
                        muxer.start();
                    }
                });
                codec.start();
            } catch (IOException e) {
                Log.e(TAG,"Unable to encode",e);
                //callback.run(false);
            }
        }
    }).run();
}

順便說一句,為什么您需要將8除以緩沖區長度? 什么是回調類? 請共享導入模塊! 我幾乎無法通過帶有回調參數的構建,因此將其注釋掉!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM