Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decoding audio files and re-encoding to desired PCM format: 44,100 kHz, 2 Channels, 16 Bit

I want to decode audio files to raw PCM data to stream it to a playback device in the local network. I use the new MediaExtractor and MediaCodec classes, introduced with API level 16, for that. The device requires the PCM data to be in 44,100 kHz, have 2 channels and a 16 bit sample size. This is working fine as long as the input file roughly matches these requirements. However whenever I'm decoding a MP3 file that uses - for example - a sample rate of 32,000 kHz and maybe has only one channel then I don't get the required output from the MediaCodec class.

As it seems I can't specify the output format of the MediaCodec class. So I decided to instantiate another MediaCodec object to re-encode the raw data into my desired format. According to the list of supported media formats Android supports encoding to PCM/Wave since Android 4.1. However I'm unable to create a MediaCodec object that encodes to PCM/Wave. I tried passing all kinds of MIME types to MediaCodec.createEncoderByType(type); but I always failed with an IOException:

java.io.IOException: Failed to allocate component instance
at android.media.MediaCodec.native_setup(Native Method)
at android.media.MediaCodec.<init>(MediaCodec.java:210)
at android.media.MediaCodec.createEncoderByType(MediaCodec.java:194)
[..]

Has anyone of you been able to successfully create a MediaCodec instance that encodes to PCM/Wave and can provide me with a working example?

like image 974
pocmo Avatar asked Dec 17 '13 13:12

pocmo


1 Answers

As per our conversation in the comments, this answer pertains to decoding audio data to PCM using OpenSL. Unfortunately, I cannot provide a similar answer using the MediaCodec class.

To start, set up an Android NDK project (using Eclipse: right-click project, Android Tools -> Add Native support...). Then, in the Android.mk file that is created, at the very least you will need to link against the OpenSL library:

LOCAL_LDLIBS += -lOpenSLES

If you aren't yet comfortable with an NDK project, there are tutorials aplenty on the old interweb, e.g., here and here.

Once you have an NDK project working, the general goal will be to set up an audio player that acts as a decoder to PCM. There is actually an example in the NDK samples that does exactly that. It's a bit more complex than if you were to make a minimally functional tool, but it should get you going. There is a pretty easy-to-understand description of what is happening at this link (search for "Decode audio to PCM"). I provided you the link to the relevant OpenSL spec in the comments, but there it is again. The SLDataFormat_PCM structure you would need to use when specifying the data sink would look something like:

SLDataFormat_PCM pcm = {
    SL_DATAFORMAT_PCM,
    2,                            // numChannels
    SL_SAMPLINGRATE_44_1,         // samplesPerSec
    SL_PCMSAMPLEFORMAT_FIXED_16,  // bitsPerSample
    SL_PCMSAMPLEFORMAT_FIXED_16,  // containerSize
    SL_SPEAKER_FRONT_LEFT |
        SL_SPEAKER_FRONT_RIGHT,   // channelMask
    SL_BYTEORDER_LITTLEENDIAN     // endianness
};

Following the example and descriptive guide, you would define an SLDataSink with an Android simple buffer queue data locator and the aforementioned data format. The player should then provide you a series of buffers containing data in the correct format. You can then pass that data back to Java, or (better) stream the data from native code.

like image 82
Dave Avatar answered Oct 03 '22 08:10

Dave