-
-
Save a-m-s/1991ab18fbcb0fcc2cf9 to your computer and use it in GitHub Desktop.
/* MediaDecoder | |
Author: Andrew Stubbs (based on some examples from the docs) | |
This class opens a file, reads the first audio channel it finds, and returns raw audio data. | |
Usage: | |
MediaDecoder decoder = new MediaDecoder("myfile.m4a"); | |
short[] data; | |
while ((data = decoder.readShortData()) != null) { | |
// process data here | |
} | |
*/ | |
import java.nio.ByteBuffer; | |
import android.media.MediaCodec; | |
import android.media.MediaCodec.BufferInfo; | |
import android.media.MediaExtractor; | |
import android.media.MediaFormat; | |
public class MediaDecoder { | |
private MediaExtractor extractor = new MediaExtractor(); | |
private MediaCodec decoder; | |
private MediaFormat inputFormat; | |
private ByteBuffer[] inputBuffers; | |
private boolean end_of_input_file; | |
private ByteBuffer[] outputBuffers; | |
private int outputBufferIndex = -1; | |
public MediaDecoder(String inputFilename) { | |
extractor.setDataSource(inputFilename); | |
// Select the first audio track we find. | |
int numTracks = extractor.getTrackCount(); | |
for (int i = 0; i < numTracks; ++i) { | |
MediaFormat format = extractor.getTrackFormat(i); | |
String mime = format.getString(MediaFormat.KEY_MIME); | |
if (mime.startsWith("audio/")) { | |
extractor.selectTrack(i); | |
decoder = MediaCodec.createDecoderByType(mime); | |
decoder.configure(format, null, null, 0); | |
inputFormat = format; | |
break; | |
} | |
} | |
if (decoder == null) { | |
throw new IllegalArgumentException("No decoder for file format"); | |
} | |
decoder.start(); | |
inputBuffers = decoder.getInputBuffers(); | |
outputBuffers = decoder.getOutputBuffers(); | |
end_of_input_file = false; | |
} | |
// Read the raw data from MediaCodec. | |
// The caller should copy the data out of the ByteBuffer before calling this again | |
// or else it may get overwritten. | |
private ByteBuffer readData(BufferInfo info) { | |
if (decoder == null) | |
return null; | |
for (;;) { | |
// Read data from the file into the codec. | |
if (!end_of_input_file) { | |
int inputBufferIndex = decoder.dequeueInputBuffer(10000); | |
if (inputBufferIndex >= 0) { | |
int size = extractor.readSampleData(inputBuffers[inputBufferIndex], 0); | |
if (size < 0) { | |
// End Of File | |
decoder.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); | |
end_of_input_file = true; | |
} else { | |
decoder.queueInputBuffer(inputBufferIndex, 0, size, extractor.getSampleTime(), 0); | |
extractor.advance(); | |
} | |
} | |
} | |
// Read the output from the codec. | |
if (outputBufferIndex >= 0) | |
// Ensure that the data is placed at the start of the buffer | |
outputBuffers[outputBufferIndex].position(0); | |
outputBufferIndex = decoder.dequeueOutputBuffer(info, 10000); | |
if (outputBufferIndex >= 0) { | |
// Handle EOF | |
if (info.flags != 0) { | |
decoder.stop(); | |
decoder.release(); | |
decoder = null; | |
return null; | |
} | |
// Release the buffer so MediaCodec can use it again. | |
// The data should stay there until the next time we are called. | |
decoder.releaseOutputBuffer(outputBufferIndex, false); | |
return outputBuffers[outputBufferIndex]; | |
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { | |
// This usually happens once at the start of the file. | |
outputBuffers = decoder.getOutputBuffers(); | |
} | |
} | |
} | |
// Return the Audio sample rate, in samples/sec. | |
public int getSampleRate() { | |
return inputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE); | |
} | |
// Read the raw audio data in 16-bit format | |
// Returns null on EOF | |
public short[] readShortData() { | |
BufferInfo info = new BufferInfo(); | |
ByteBuffer data = readData(info); | |
if (data == null) | |
return null; | |
int samplesRead = info.size/2; | |
short[] returnData = new short[samplesRead]; | |
// Converting the ByteBuffer to an array doesn't actually make a copy | |
// so we must do so or it will be overwritten later. | |
System.arraycopy(data.asShortBuffer().array(), 0, returnData, 0, samplesRead); | |
return returnData; | |
} | |
} |
I love your example, but there are some errors.
IllegalStateException and UnsupportedOperationException on Line 132 "System.arraycopy(data.asShortBuffer().array(), 0, returnData, 0, samplesRead);"
The IllegalStateException occurs because access to "ByteBuffer data" which has been released on Line 102 "decoder.releaseOutputBuffer(outputBufferIndex, false);"
The UnsupportedOperationException occurs because the "ShortBuffer" from "ByteBuffer.asShortBuffer" cannot use "array" function.
So I changed this line to "outputBuffers[outputBufferIndex].order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(returnData);" and it works like a charm!
Thank you for example code!
Is there any other way? There is an array error
I did what Riaxter said, but I could not solve it.
Is there any other way? There is an array error
I did what Riaxter said, but I could not solve it.
Me too. Another issue:
java.lang.IllegalStateException: buffer is inaccessible
at java.nio.DirectByteBuffer.getUnchecked(DirectByteBuffer.java:475)
at java.nio.ByteBufferAsShortBuffer.get(ByteBufferAsShortBuffer.java:102)
at java.nio.ShortBuffer.get(ShortBuffer.java:417)
at org.peace.allinone.ui.MediaDecoder.readShortData(MediaDecoder.java:138)
How can I save the audio track to a playable audio file?