1

I have some trouble extracting raw AMR audio frames from a .3gp file. I followed the link: "http://android.amberfog.com/?p=181" but instead of "mdat" box type I've got "moov". I read somewhere that "moov" box and "mdat" box location differ from device to device. Does anyone knows how to correctly skip the .3gp headers and extract raw AMR data? Below is a code snippet:

public AMRFile convert3GPDataToAmr(String rawAmrFilePath) throws IOException {
        if (_3gpFile == null) {
            return null;
        }

        System.out.println("3GP file length: "+_3gpFile.getRawFile().length());
        //FileInputStream is = new FileInputStream(_3gpFile.getRawFile());
        ByteArrayInputStream bis = new ByteArrayInputStream(FileUtils.readFileToByteArray(_3gpFile.getRawFile()));
        // read FileTypeHeader
        System.out.println("Available1: "+bis.available());
        FileTypeBox ftypHeader = new FileTypeBox(bis);
        System.out.println("Available2: "+bis.available());
        String header = ftypHeader.getHeaderAsString();
        if(!FileTypeBox.HEADER_TYPE.equalsIgnoreCase(header)){
            throw new IOException("File is not 3GP. ftyp header missing.");
        }
        // You can check if it is correct here
        // read MediaDataHeader
        MediaDataBox mdatHeader = new MediaDataBox(bis);
        System.out.println("Available3: "+bis.available());
        header = mdatHeader.getHeaderAsString();
        if(!MediaDataBox.HEADER_TYPE.equalsIgnoreCase(header)){
            //here is THE PROBLEM!!!!! - header is "moov" instead of "mdat" !!!!!!!!!!!!!
            throw new IOException("File is not 3GP. mdat header missing.");
        }
        // You can check if it is correct here
        int rawAmrDataLength = mdatHeader.getDataLength();
        System.out.println("RAW Amr length: "+bis.available());
        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);
        bis.close();

        //create raw amr file
        File rawAmrFile = new File(rawAmrFilePath);
        FileOutputStream fos = new FileOutputStream(rawAmrFile);    
        AMRFile amrFile = null;

        try {
            fos.write(amrData);
        } catch (Exception e) {
            Log.e(getClass().getName(), e.getMessage(), e);         
        }
        finally{
            fos.close();
            amrFile = new AMRFile(rawAmrFile);
        }

        System.out.println("AMR file length: "+amrFile.getRawFile().length());
        return amrFile;
    }
Uwe Plonus
  • 9,803
  • 4
  • 41
  • 48
Alexandru Circus
  • 5,478
  • 7
  • 52
  • 89
  • Where is this failing? – Shark Jul 10 '13 at 12:24
  • if(!MediaDataBox.HEADER_TYPE.equalsIgnoreCase(header)){ //here is THE PROBLEM!!!!! - header is "moov" instead if(!MediaDataBox.HEADER_TYPE.equalsIgnoreCase(header)){ – Alexandru Circus Jul 10 '13 at 12:26
  • Instead of mdat the header is moov – Alexandru Circus Jul 10 '13 at 12:27
  • and the raw AMR data is not correct(maybe because of the changed location of the mdat box) – Alexandru Circus Jul 10 '13 at 12:28
  • Are you sure the failure is not file-specific? Maybe you're just using a broken test 3gp file? I am unsure whether i see a problem with the algorithm. If you parse a non-3gp header (`moov` instead of `mdat`) either your parser needs fixing or you don't know your testfiles. – Shark Jul 10 '13 at 12:31
  • @Shark - I use .3gp file from android recorder. I prepare the recorder like this:mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); – Alexandru Circus Jul 10 '13 at 12:34
  • after the recording I try to extract raw AMR data. – Alexandru Circus Jul 10 '13 at 12:35
  • from as I know moov box is for video, but it appears as empty? box in audio file too – Alexandru Circus Jul 10 '13 at 12:43

1 Answers1

1

I used some HEX Viewer tool to look into my .3gp file and I saw that mdat box wasn't in the place where the algorithm has looked for, so I decided to read from the stream until I find the mdat box. Now the extraction works ok. I modified MediaDataBox a little bit:

public MediaDataBox(FileInputStream is) throws IOException {
        super(is);

        //check the mdat header - if not read until mdat if found
        long last32Int = 0;
        long curr32Int = 0;
        long temp;
        while (is.available()>=8) {
            temp = curr32Int = readUint32(is);          

            //test like this to avoid low memory issues
            if((HEADER_TYPE.charAt(0) == (byte)(temp >>> 24)) && 
                    (HEADER_TYPE.charAt(1) == (byte)(temp >>> 16)) &&
                        (HEADER_TYPE.charAt(2) == (byte)(temp >>> 8)) &&
                            (HEADER_TYPE.charAt(3) == (byte)temp)){

                size = last32Int;
                type = curr32Int;
                boxSize = 8;
                break;
            }
            last32Int = curr32Int;
        }
    }

and super class:

public _3GPBox(FileInputStream is) throws IOException{
        size = readUint32(is);
        boxSize += 4;
        type = readUint32(is);
        boxSize += 4;
    }
Alexandru Circus
  • 5,478
  • 7
  • 52
  • 89