Android adding AAC ADTS to Mediarecorder PCM

990 Views Asked by At

My Mediarecorder gives me a PCM File as an output when I record the phone's microphone. Now when trying to listen to this File that it created all I hear is static and I think, if I have understood correctly, I get a PCM file from Mediarecorder not AAC and I need to add ADTS header to the PCM to be able to listen to it.

I have seen threads with custom Encoders but I can not seem to figure out where and what I need to do with them.

I make an output File from microphone recoridng like this:

private static final int CHANNEL = AudioFormat.CHANNEL_IN_MONO;
private static final int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private static final int SAMPLE_RATE = 44100; //44.1kHz
private static final int BUFFER_SIZE = 2048;

public Status status = Status.IDLE;
private AudioRecordingHandler arh;
private File outputFile;
private Context context;


/**
 * Starts script for running. Needs output file to work!
 */
public void start() {
    if (outputFile == null) { return; }
    System.out.println("Start reading stream...");
    aacEncoder = new AACEncoder(SAMPLE_RATE, micOutputPCM);
    new Thread(new Runnable() {
        @Override
        public void run() {
            record.startRecording();
            byte[] data = new byte[BUFFER_SIZE];
            float[] audioFloatBuffer = new float[BUFFER_SIZE/2];
            Yin y = new Yin(SAMPLE_RATE, BUFFER_SIZE/2);

            while(status == Status.RECORDING) {
                record.read(data, 0, BUFFER_SIZE);
                audioFloatBuffer = ConverterUtil.toFloatArray(data, 0, audioFloatBuffer,
                        0, audioFloatBuffer.length);
                PitchDetectionResult pdr = y.getPitch(audioFloatBuffer);
                aacEncoder.writeIntoOutputfile(data);
                arh.handlePitch(pdr.getPitch());
            }
            aacEncoder.stopEncoding();
        }
    }).start();
}

/**
 * Stops script
 */
public void stop() {
    status = Status.IDLE;
    record.stop();
    arh.finishedRecording(micOutputPCM);
}

Here is how I get the byte[] from the File and where I try to encode the ADTS header to them.

 public static File addHeaderToAac(File micOutputPCM, File output) throws IOException {

    byte[] pcmFile = fullyReadFileToBytes(micOutputPCM);
    int bufferSize = 2048;

    //addADTSHeader to byte[] and return a File object


    return fileWithADTSHeader;
}


public static byte[] fullyReadFileToBytes(File f) throws IOException {
    int size = (int) f.length();
    byte bytes[] = new byte[size];
    byte tmpBuff[] = new byte[size];
    FileInputStream fis= new FileInputStream(f);;
    try {

        int read = fis.read(bytes, 0, size);
        if (read < size) {
            int remain = size - read;
            while (remain > 0) {
                read = fis.read(tmpBuff, 0, remain);
                System.arraycopy(tmpBuff, 0, bytes, size - remain, read);
                remain -= read;
            }
        }
    }  catch (IOException e){
        throw e;
    } finally {
        fis.close();
    }

    return bytes;
}

My question is, does anyone have an Encoder that can accept a File or byte[] or ByteStream as an input and return a File.

Because ultimately I want to make a mp4parser AACTrackImpl, which can be found here : https://github.com/sannies/mp4parser

AACTrackImpl aacTrack2 = new MP3TrackImpl(new FileDataSourceImpl(micOutputPCM));

Also If I am missing some important details about how to convert and what I should do to be able to play it then that information will also be useful.

If I need provide more information in order to answer this question, then I will gladly do so.

Edit:

I've been trying to make an encoder that would do what I need, but so far I have had no success.

    public static File addHeaderToAac(File pcmFile1, File output, Context context) throws IOException {

    byte[] pcmFile = fullyReadFileToBytes(pcmFile1);
    int bufferSize = 2048;


    AACEncoder encoder = new AACEncoder(44100, output);
    encoder.encodeAudioFrameToAAC(pcmFile);

    return output;
}

I am trying to encode the PCM to AAC with this encoder, but this encoder writes the output file to memory, but I need an object. And when I give it my byte[] it also gives me an error :

W/System.err:     at java.nio.ByteBuffer.put(ByteBuffer.java:642)

And the error is coming from this line :

inputBuf.put(frameData);

Finally, my encoder:

public class AACEncoder {
    final String TAG = "UEncoder Processor";

    final int sampleRate;
    File outputFile;

    FileOutputStream fos;

    final int TIMEOUT_USEC = 10000 ;

    MediaCodec encoder;
    boolean isEncoderRunning = false;
    boolean outputDone = false;

    MediaCodec.BufferInfo info;

    public AACEncoder(final int sampleRate, File outputFile) {
        this.sampleRate = sampleRate;
        this.info = new MediaCodec.BufferInfo();
        this.outputFile = outputFile;

        openFileStream();
        initEncoder();
    }

    /**
     * Initializes CrappyEncoder for AAC-LC (Low complexity)
     * @throws Exception
     */
    public void initEncoder() {
        try {
            encoder = MediaCodec.createEncoderByType("audio/mp4a-latm");
            MediaFormat format = new MediaFormat();
            format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
            format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
            format.setInteger(MediaFormat.KEY_SAMPLE_RATE, sampleRate);
            format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
            format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        } catch (IOException ex) {
            Log.e(TAG, "Failed to create CrappyEncoder");
            ex.printStackTrace();
        }
    }

    int generateIndex = 0;

    public void encodeAudioFrameToAAC(byte[] frameData) {

        if (encoder == null) return;

        if (!isEncoderRunning) {
            encoder.start();
            isEncoderRunning = true;
        }

        ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers();
        if (fos != null) {
            int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);

            if (inputBufIndex >= 0) {
                long ptsUsec = (System.currentTimeMillis() * 1000) / 10000;
                if (outputDone) {
                    encoder.queueInputBuffer(inputBufIndex, 0, 0, ptsUsec,
                            MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                } else {
                    ByteBuffer inputBuf = encoderInputBuffers[inputBufIndex];
                    inputBuf.clear();
                    inputBuf.put(frameData);
                    encoder.queueInputBuffer(inputBufIndex, 0, frameData.length, ptsUsec, 0);
                }
                generateIndex++;
            }
            tryEncodeOutputBuffer();
        }
        checkIfOutputDone();
    }

    /**
     * Gets data from output buffer and encodes it to
     * AAC-LC encoding with ADTS header attached before every frame
     */
    private void tryEncodeOutputBuffer() {

        ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();
        //If >= 0 then valid response
        int encoderStatus = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
        if (encoderStatus >= 0) {
            ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];

            encodedData.position(info.offset);
            encodedData.limit(info.offset + info.size + 7);

            byte[] data = new byte[info.size + 7];
            addADTStoPacket(data, info.size + 7);
            encodedData.get(data, 7, info.size);

            encodedData.position(info.offset);
            writeIntoOutputfile(data);
            encoder.releaseOutputBuffer(encoderStatus, false);
        }
    }

    private void checkIfOutputDone() {
        if (outputDone) {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException ioe) {
                    Log.w(TAG, "failed closing debug file");
                    throw new RuntimeException(ioe);
                }
                fos = null;
            }
        }
    }

    /**
     *  Add ADTS header at the beginning of each and every AAC packet.
     *  This is needed as MediaCodec CrappyEncoder generates a packet of raw
     *  AAC data.
     *
     *  Note the packetLen must count in the ADTS header itself.
     **/
    private void addADTStoPacket(byte[] packet, int packetLen) {
        int profile = 2;  //AAC LC
        //39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
        int freqIdx = 4;  //44.1KHz
        int chanCfg = 2;  //CPE

        // fill in ADTS data
        packet[0] = (byte)0xFF;
        packet[1] = (byte)0xF9;
        packet[2] = (byte)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
        packet[3] = (byte)(((chanCfg&3)<<6) + (packetLen>>11));
        packet[4] = (byte)((packetLen&0x7FF) >> 3);
        packet[5] = (byte)(((packetLen&7)<<5) + 0x1F);
        packet[6] = (byte)0xFC;
    }

    private void openFileStream() {
        fos = null;
        try {
            fos = new FileOutputStream(outputFile, false);
        } catch (FileNotFoundException e) {
            Log.e("AudioRecorder", e.getMessage());
        }
    }

    /**
     * Writes data into file
     * @param data
     */
    public void writeIntoOutputfile(byte[] data) {
        try {
            fos.write(data);
        } catch (IOException ioe) {
            Log.w(TAG, "failed writing debug data to file");
            throw new RuntimeException(ioe);
        }
    }

    public void stopEncoding() {
        isEncoderRunning = false;
        encoder.stop();
        closeStream();
    }

    private void closeStream() {
        try {
            if (fos != null) {
                fos.close();
            }
        } catch (IOException e) {
            Log.e("AudioRecorder", e.getMessage());
        }
    }
}
0

There are 0 best solutions below