Quick way to extract AMR-audio from Android 3GP files

If you need to record audio from microphone of your Android-device to a file, it is better to use MediaRecorder;. On the current Android platforms (we can be sure for 1.6, 1.5 and earlier versions) you can record only to 3GPP AMR-NB file, but not to the raw AMR. If you will try to use MediaRecorder.OutputFormat.RAW_AMR you will get native error that will not come to Java layer. We’re pretty sure that it will be fixed in the future, but what to do now if you need simple raw AMR-NB file without extra 3GP-container?

Simple Google search about how to extract raw AMR audio from 3GPP-file recorded by your Android return us a link to the isobox4j library that can convert between all kinds of 3gp, amr, mp4, etc. files. It is perfect, but it is 400Kb. Do you really need to have such huge library in your project only for converting one type of file to another?

I will show you the easier way that works fine only with Android 3GP-files. What you need to do – just remove 3GP-container and store AMR-data with appropriate header.

3GP-header fields are stored in 32-bits, big-endian format. So, you need correct functions to read 32-bits values from ByteArrayInputStream.

public long readUint32(ByteArrayInputStream bis) {
    long result = 0;
    result += ((long) readUInt16(bis)) << 16;
    result += readUInt16(bis);
    return result;
}
public int readUInt16(ByteArrayInputStream bis) {
    int result = 0;
    result += bis.read() << 8;
    result += bis.read();
    return result;
}

Now you need to skip 3gp-headers. First header is file header (‘ftyp’) and next is MediaData (‘mdat’). I’ve made it according to 3GP and Iso file structure specifications, using classes. Base Box contains two fields: size and type.

class Box {
    protected long size;
    protected long type;
 
    protected long boxSize = 0;
 
    public Box(ByteArrayInputStream bis) {
        size = readUint32(bis);
        boxSize += 4;
        type = readUint32(bis);
        boxSize += 4;
    }
}

Now classes for Filetype header and MediaData header:

class FileTypeBox extends Box {
    private static char[] HEADER_TYPE = {'f', 't', 'y', 'p'};
 
    protected long brand;
    protected long minorVersion;
    protected long[] compatibleBrands;
 
    public FileTypeBox(ByteArrayInputStream bis) {
        super(bis);
        brand = readUint32(bis);
        boxSize += 4;
        minorVersion = readUint32(bis);
        boxSize += 4;
        int remainSize = (int)(size - boxSize);
        if (remainSize > 0) {
            compatibleBrands = new long[remainSize / 4];
            for (int i = 0; i < compatibleBrands.length; i++) {
                compatibleBrands[i] = readUint32(bis);
            }
        }
    }
}
class MediaDataBox extends Box {
    private static char[] HEADER_TYPE = {'m', 'd', 'a', 't'};
 
    protected byte[] data;
 
    public MediaDataBox(ByteArrayInputStream bis) {
        super(bis);
        // you can read data[] here, but we need only size
    }
 
    public int getDataLength() {
        return (int)(size - boxSize);
    }
}

I add HEADER_TYPE to each Box-class. You can add checking that class field type has the appropriate HEADER_TYPE value just to be sure that you read and parse correct file.
So, we’re ready to convert 3GP to AMR. 3GP-structure is as follows:
1. FileType header
2. MediaDataHeader. Data inside it is raw AMR data.
3. MovieData box (‘moov’ type) that presents, but is empty and we don’t need to copy it to destination AMR-file.
Converting steps is as follows:
1. Skip FileType header
2. Skip MediaData Header size and type fields, get raw AMR data only.
3. Create new file
4. Add AMR-header magic number – “#!AMR\n” (according to RFC-3267)
5. Copy all raw AMR data to file.
That’s it!

// #!AMR\n
private static byte[] AMR_MAGIC_HEADER = {0x23, 0x21, 0x41, 0x4d, 0x52, 0x0a};
 
public byte[] convert3gpDataToAmr(byte[] data) {
    if (data == null) {
        return null;
    }
 
    ByteArrayInputStream bis = new ByteArrayInputStream(data);
    // read FileTypeHeader
    FileTypeBox ftypHeader = new FileTypeBox(bis);
    // You can check if it is correct here
    // read MediaDataHeader
    MediaDataBox mdatHeader = new MediaDataBox(bis);
    // You can check if it is correct here
    int rawAmrDataLength = mdatHeader.getDataLength();
    int fullAmrDataLength = AMR_MAGIC_HEADER.length + rawAmrDataLength;
    byte[] amrData = new byte[fullAmrDataLength];
    System.arraycopy(AMR_MAGIC_HEADER, 0, amrData, 0, AMR_MAGIC_HEADER.length);
    bis.read(amrData, AMR_MAGIC_HEADER.length, rawAmrDataLength);
    return amrData;
}

Enjoy!

  • Share/Bookmark

Comments (9)

vigneshNovember 17th, 2009 at 12:33 pm

brilliant..thanks so much…
Can you also give me pointers on how to convert the AMR data to WAV?
And How to increase the Gain/volume/amplitude of AMR data..like peak normalisation

goalstudioNovember 17th, 2009 at 12:52 pm

What is the reason to convert AMR to WAV? You can use another way to record audio on Android via AudioRecord class. It writes data in raw PCM format that can be simply converted to WAV.
The same thing is for audio processing, it is better to use PCM for it because AMR uses lossy compression method for audio encoding.

vigneshNovember 17th, 2009 at 8:05 pm

I dont really need to convert AMR to WAV..but i need to manipulate the volume of 3gp/amr files…the input for my application is a 3gp/amr file and this file was recorded with a poor mic or maybe from a long distance…

goalstudioNovember 18th, 2009 at 3:28 pm

I see. To add any sound processing, I think, you should convert AMR to PCM and then change amplitudes. But it can cost you a lot of memory, because PCM is about 5 times bigger than AMR.

man910February 23rd, 2010 at 8:27 pm

Do you have any code or links that can convert AMR to PCM? I’m doing this on the server once I get the AMR from the device, so memory is not a concern.

yessikaJune 20th, 2010 at 1:58 am

hello I need to do the reverse process of amr to 3gp how to add headers Help me please

programmingJuly 29th, 2010 at 5:43 pm

What does “<< 16;” mean? I am getting an error with that

villain_dmJuly 29th, 2010 at 5:48 pm

LOL) you answered your question:)

cantimattAugust 30th, 2010 at 3:10 am

this is fantastic :) For the audio processing newbie, could you explain how to convert a file of another format (say, mp3?) to wav? Is it as simple as this process? Basically I want to get sample values from a user’s mp3.

Leave a comment

Your comment