我正在尝试将任何音频文件转换为 AAC、128kbps 比特率、32000Hz 采样率。
我期待一个具有上述规格的转换后的音频文件。
我得到了一个输出文件,但它是垃圾音频,ExoPlayer 中存在大量
Audio Sink Error
错误,Codec2Client 中存在大量 query -- param skipped
警告。
这是我的代码:
info -
outputFile
和 sourceUri
分别属于 AtomicReference<File>
和 AtomicReference<Uri>
类型
info - 我假设
sourceUri
是单个音频文件的 uri
我先尝试只编码:
startEncoding.setOnClickListener(v ->
{
try {
outputFile.set(File.createTempFile("temp", null));
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(this, sourceUri.get(), null);
extractor.selectTrack(0);
MediaFormat outputFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 32000, 2);
outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
MediaMuxer muxer = new MediaMuxer(outputFile.get().getPath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
int trackIndex = muxer.addTrack(outputFormat);
MediaCodec.Callback encoderCallbacks = new MediaCodec.Callback() {
int sampleSize;
long sampleTime;
boolean EOSFlag = false;
boolean muxerStarted = false;
@Override
public void onInputBufferAvailable(@NonNull MediaCodec encoder, int index) {
Log.d(Tag.DEBUG.toString(), "onInputBufferAvailable: encoding...");
if(EOSFlag){
encoder.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
return;
}
ByteBuffer inputBuffer = encoder.getInputBuffer(index);
sampleSize = extractor.readSampleData(inputBuffer, 0);
if(sampleSize >= 0){
sampleTime = extractor.getSampleTime();
encoder.queueInputBuffer(index, 0, sampleSize, sampleTime, 0);
Log.d(Tag.DEBUG.toString(), "onInputBufferAvailable: decoding...");
} else {
EOSFlag = true;
encoder.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
extractor.release();
Log.d(Tag.DEBUG.toString(), "onInputBufferAvailable: decoding completed by extractor.getSampleSize() < 0");
return;
}
if(!extractor.advance()){
EOSFlag = true;
Log.d(Tag.DEBUG.toString(), "onInputBufferAvailable: decoding completed by extractor.advance() == false");
extractor.release();
}
}
@Override
public void onOutputBufferAvailable(@NonNull MediaCodec encoder, int index, @NonNull MediaCodec.BufferInfo info) {
if(!muxerStarted){
muxer.start();
muxerStarted = true;
}
if (info.flags != MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
muxer.writeSampleData(trackIndex, encoder.getOutputBuffer(index), info);
Log.d(Tag.DEBUG.toString(), "onOutputBufferAvailable: writing to file...");
encoder.releaseOutputBuffer(index, false);
} else {
Log.d(Tag.DEBUG.toString(), "onOutputBufferAvailable: Writing completed.");
encoder.stop();
encoder.release();
muxer.stop();
muxer.release();
muxerStarted = false;
Log.d(Tag.DEBUG.toString(), "onOutputBufferAvailable: Resources released");
Log.d(Tag.DEBUG.toString(),
"Source file size: " + new File(sourceUri.get().getPath()).length() +
"\nTarget file size: " + outputFile.get().length());
}
}
@Override
public void onError(@NonNull MediaCodec encoder, @NonNull MediaCodec.CodecException e) {
encoder.stop();
encoder.release();
muxer.stop();
muxer.release();
muxerStarted = false;
extractor.release();
Log.e(Tag.DEBUG.toString(), "onError: ", e);
}
@Override
public void onOutputFormatChanged(@NonNull MediaCodec encoder, @NonNull MediaFormat format) {
}
};
MediaCodec encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
encoder.setCallback(encoderCallbacks);
encoder.start();
} catch (Exception e) {
Log.e(Tag.DEBUG.toString(), "onCreate: ", e);
}
});
然后我尝试在编码之前解码:
(我知道我无法使用此代码对大文件进行编码,否则
byteBuffersWithInfo
将会溢出。
所以我使用小音频文件来测试此代码)
startDecodingAndEncoding.setOnClickListener(
v -> {
try {
outputFile.set(File.createTempFile("temp", null));
LinkedList<HashMap<String, Object>> byteBuffersWithInfo = new LinkedList<>();
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(this, sourceUri.get(), null);
extractor.selectTrack(0);
MediaFormat outputFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 32000, 2);
outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
MediaMuxer muxer = new MediaMuxer(outputFile.get().getPath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
int trackIndex = muxer.addTrack(outputFormat);
MediaCodec.Callback encoderCallbacks = new MediaCodec.Callback() {
boolean muxerStarted = false;
@Override
public void onInputBufferAvailable(@NonNull MediaCodec encoder, int index) {
Log.d(Tag.DEBUG.toString(), "onInputBufferAvailable: encoding...");
HashMap<String, Object> byteBufferWithInfo = byteBuffersWithInfo.poll();
if(byteBufferWithInfo != null) {
ByteBuffer buffer = (ByteBuffer) byteBufferWithInfo.get("buffer");
encoder.getInputBuffer(index).put(buffer);
MediaCodec.BufferInfo info = (MediaCodec.BufferInfo) byteBufferWithInfo.get("info");
encoder.queueInputBuffer(index, info.offset, info.size, info.presentationTimeUs, info.flags);
} else {
Log.d(Tag.DEBUG.toString(), "onInputBufferAvailable: encoded.");
}
}
@Override
public void onOutputBufferAvailable(@NonNull MediaCodec encoder, int index, @NonNull MediaCodec.BufferInfo info) {
if(!muxerStarted){
muxer.start();
muxerStarted = true;
}
if (info.flags != MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
muxer.writeSampleData(trackIndex, encoder.getOutputBuffer(index), info);
Log.d(Tag.DEBUG.toString(), "onOutputBufferAvailable: writing to file...");
encoder.releaseOutputBuffer(index, false);
} else {
Log.d(Tag.DEBUG.toString(), "onOutputBufferAvailable: Writing completed.");
encoder.stop();
encoder.release();
muxer.stop();
muxer.release();
muxerStarted = false;
Log.d(Tag.DEBUG.toString(), "onOutputBufferAvailable: Resources released");
Log.d(Tag.DEBUG.toString(),
"Source file size: " + new File(sourceUri.get().getPath()).length() +
"\nTarget file size: " + outputFile.get().length());
}
}
@Override
public void onError(@NonNull MediaCodec encoder, @NonNull MediaCodec.CodecException e) {
encoder.stop();
encoder.release();
muxer.stop();
muxer.release();
muxerStarted = false;
Log.e(Tag.DEBUG.toString(), "onError: ", e);
}
@Override
public void onOutputFormatChanged(@NonNull MediaCodec encoder, @NonNull MediaFormat format) {
}
};
MediaCodec encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
encoder.setCallback(encoderCallbacks);
MediaCodec.Callback decoderCallbacks = new MediaCodec.Callback() {
int sampleSize;
long sampleTime;
boolean EOSFlag = false;
boolean encoderStarted = false;
@Override
public void onInputBufferAvailable(@NonNull MediaCodec decoder, int index) {
if(EOSFlag){
decoder.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
return;
}
ByteBuffer inputBuffer = decoder.getInputBuffer(index);
sampleSize = extractor.readSampleData(inputBuffer, 0);
if(sampleSize >= 0){
sampleTime = extractor.getSampleTime();
decoder.queueInputBuffer(index, 0, sampleSize, sampleTime, 0);
Log.d(Tag.DEBUG.toString(), "onInputBufferAvailable: decoding...");
} else {
EOSFlag = true;
decoder.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
extractor.release();
Log.d(Tag.DEBUG.toString(), "onInputBufferAvailable: decoding completed by extractor.getSampleSize() < 0");
return;
}
if(!extractor.advance()){
EOSFlag = true;
Log.d(Tag.DEBUG.toString(), "onInputBufferAvailable: decoding completed by extractor.advance() == false");
extractor.release();
}
}
@Override
public void onOutputBufferAvailable(@NonNull MediaCodec decoder, int index, @NonNull MediaCodec.BufferInfo info) {
Log.d(Tag.DEBUG.toString(), "onOutputBufferAvailable: filling middle buffer...");
HashMap<String, Object> byteBufferWithInfo = new HashMap<>();
byteBufferWithInfo.put("buffer", decoder.getOutputBuffer(index));
byteBufferWithInfo.put("info", info);
byteBuffersWithInfo.offer(byteBufferWithInfo);
decoder.releaseOutputBuffer(index, false);
if(info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM){
Log.d(Tag.DEBUG.toString(), "onOutputBufferAvailable: middle buffer filled.");
if(!encoderStarted){
encoder.start();
encoderStarted = true;
decoder.stop();
}
}
}
@Override
public void onError(@NonNull MediaCodec decoder, @NonNull MediaCodec.CodecException e) {
decoder.stop();
decoder.release();
extractor.release();
encoderStarted = false;
Log.e(Tag.DEBUG.toString(), "onError: ", e);
}
@Override
public void onOutputFormatChanged(@NonNull MediaCodec decoder, @NonNull MediaFormat format) {
}
};
MediaCodec decoder = MediaCodec.createDecoderByType(extractor.getTrackFormat(0).getString(MediaFormat.KEY_MIME));
decoder.configure(extractor.getTrackFormat(0), null, null, 0);
decoder.setCallback(decoderCallbacks);
decoder.start();
} catch (Exception e) {
Log.e(Tag.DEBUG.toString(), "onCreate: ", e);
}
}
);
但是当我尝试使用 Media3 ExoPlayer 播放两者中的任何一个时,我得到:
垃圾音频
ExoPlayer 中出现此错误
Audio sink error
androidx.media3.exoplayer.audio.AudioSink$UnexpectedDiscontinuityException: Unexpected audio track timestamp discontinuity: expected 1000000480000, got 1000000689343
at androidx.media3.exoplayer.audio.DefaultAudioSink.handleBuffer(DefaultAudioSink.java:956)
at androidx.media3.exoplayer.audio.MediaCodecAudioRenderer.processOutputBuffer(MediaCodecAudioRenderer.java:739)
at androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.drainOutputBuffer(MediaCodecRenderer.java:1998)
at androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:827)
at androidx.media3.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:1079)
at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:529)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loopOnce(Looper.java:230)
at android.os.Looper.loop(Looper.java:319)
at android.os.HandlerThread.run(HandlerThread.java:67)
日志中有很多这样的警告:
Codec2Client: query -- param skipped: index = 1342179345.
请告诉我我错过了什么。
这是我在社区的第一篇帖子,所以请原谅我的任何不当行为。
首先,是的,您必须在将文件输入编码器之前对其进行解码。
其次,
MediaCodec
API 不处理采样率转换(例如从 48,000 Hz 到 32,000 Hz)。您需要为此使用第三方库或编写自己的代码。
关于您的代码。当您从解码器获得输出缓冲区时,将其保存在链接列表中。在下一行中你释放它。这是一个错误。
解码器管理可重用缓冲区的队列。一旦输出缓冲区被释放到编解码器,就不得使用它。当您从列表中获取缓冲区时,它可能已被其他数据覆盖。解决此问题的一种方法是复制缓冲区。