gpt4 book ai didi

Android:MediaCodec+MediaMuxer 编码的音频 MP4 无法播放

转载 作者:行者123 更新时间:2023-12-03 01:55:28 37 4
gpt4 key购买 nike

我有以下函数,它采用 WAV (PCM) 文件并使用 Android 的 MediaCode 和 MediaMuxer 类将其编码为 AAC 编码的 MP4 文件。这只是音频。该函数成功运行并输出一个合理的 .mp4 文件,该文件被识别为 AAC 编码。但它不能在 Android、网络或 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();

最佳答案

你需要:

  • 正确添加时间戳信息,因为媒体复用器需要使用它来标记数据包时间信息。
  • 添加逻辑以将数据缓冲区从提取器数据缓冲区(PCM)复制到媒体编解码器输入缓冲区,仅引用缓冲区索引将仅编码随机数据缓冲区而没有初始值。
  • 添加代码以将输入源属性(例如 channel 和采样率)应用于媒体编解码器。不确定您是否打算使用不同的 channel 和采样率进行编码!

  • 示例代码如下:
    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 除以缓冲区长度?什么是回调类?请分享导入模块!我几乎无法使用回调参数传递构建,所以将其注释掉!

    关于Android:MediaCodec+MediaMuxer 编码的音频 MP4 无法播放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35907516/

    37 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com