6

I currently have two separate media extractors and codecs that I am using to break down each individual sample into ByteBuffers. Then I store each sample into two short arrays. Then I call my mix function which combines the two samples into one short[] and if I play this short[] with AudioTrack it plays the result great with both sounds playing at the same time as desired.

However, what I really want to do is use MediaMuxer to convert my new short[] back to an mp4 audio file and store it to the device for later playback. Can someone please help me figure out what Im doing wrong?

Here is some of the code I have... As you can see, I commented out the AudioTrack and that is where Im trying to turn the short[] back into the ByteBuffer so I can use the media muxer to create the audio file. The result this gives me is a new audio file that is the correct name and overall length stored on my device... but when I try to play it... it jumps from begining to end in a fraction of a second and doesnt actually play and audio.

        songOutPath = Environment.getExternalStorageDirectory()+"/My Folder/song.raw";
        muxer = new MediaMuxer(songOutPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        mTrackIndex = muxer.addTrack(format);
        muxer.start();
        mMuxerStarted = true;

        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        final long timeoutUs = 5000;
        boolean sawInputEOS = false;
        boolean sawOutputEOS = false;
        int noOutputCounter = 0;

        MediaCodec.BufferInfo info2 = new MediaCodec.BufferInfo();
        final long timeoutUs2 = 5000;
        boolean sawInputEOS2 = false;
        boolean sawOutputEOS2 = false;
        int noOutputCounter2 = 0;

        while (!sawOutputEOS && !sawOutputEOS2 && noOutputCounter < 50 && noOutputCounter2 < 50) {
            noOutputCounter++;
            if (!sawInputEOS) {
                int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs);
                if (inputBufferIndex >= 0) {
                    ByteBuffer buffer = codecInputBuffers[inputBufferIndex];
                    int sampleSize = extractor.readSampleData(buffer, 0);
                    long presentationTimeUs = 0;
                    if (sampleSize < 0) {
                        sawInputEOS = true;
                        sampleSize = 0;
                    } else {
                        presentationTimeUs = extractor.getSampleTime();
                    }
                    codec.queueInputBuffer(inputBufferIndex, 0, sampleSize,
                            presentationTimeUs,
                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
                    if (!sawInputEOS) {
                        extractor.advance();
                    }
                }
            }
            noOutputCounter2++;
            if (!sawInputEOS2) {
                int inputBufferIndex2 = codec2.dequeueInputBuffer(timeoutUs2);
                if (inputBufferIndex2 >= 0) {
                    ByteBuffer buffer2 = codecInputBuffers2[inputBufferIndex2];
                    int sampleSize2 = extractor2.readSampleData(buffer2, 0);
                    long presentationTimeUs2 = 0;
                    if (sampleSize2 < 0) {
                        sawInputEOS2 = true;
                        sampleSize2 = 0;
                    } else {
                        presentationTimeUs2 = extractor2.getSampleTime();
                    }
                    codec2.queueInputBuffer(inputBufferIndex2, 0, sampleSize2,
                            presentationTimeUs2,
                            sawInputEOS2 ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
                    if (!sawInputEOS2) {
                        extractor2.advance();
                    }
                }
            }

            int outputBufferIndex = codec.dequeueOutputBuffer(info, timeoutUs);
            int outputBufferIndex2 = codec2.dequeueOutputBuffer(info2, timeoutUs2);
            if (outputBufferIndex >= 0) {
                if (info.size > 0) {
                    noOutputCounter = 0;
                }
                if (info2.size > 0) {
                    noOutputCounter2 = 0;
                }
                ByteBuffer buffer = codecOutputBuffers[outputBufferIndex];
                ByteBuffer buffer2 = codecOutputBuffers2[outputBufferIndex2];

                shortArrayOne = new short[info.size/2];
                shortArrayTwo = new short[info2.size/2];

                buffer.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shortArrayOne);
                buffer.clear();
                buffer2.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shortArrayTwo);
                buffer2.clear();

                shortArrayThree = mix(shortArrayOne, shortArrayTwo);
                if(shortArrayThree.length > 0){
                    //audioTrack.write(shortArrayThree,0,shortArrayThree.length);
                    ByteBuffer byteBuf = ByteBuffer.allocate(2*shortArrayThree.length);
                    int index;
                    for(index = 0; index != shortArrayThree.length; index++)
                    {
                        byteBuf.putShort(shortArrayThree[index]);
                    }
                    muxer.writeSampleData(mTrackIndex, byteBuf, info);
                }

                codec.releaseOutputBuffer(outputBufferIndex, false);
                codec2.releaseOutputBuffer(outputBufferIndex2, false);
                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    sawOutputEOS = true;
                }
                if ((info2.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    sawOutputEOS2 = true;
                }
            } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                codecOutputBuffers = codec.getOutputBuffers();
                if (outputBufferIndex2 == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                    codecOutputBuffers2 = codec2.getOutputBuffers();
                }
            }
        }

Update

I added a method to try and encode the pcm file to an audio file for playback. This creates a new file but now when I play it it is twice the length as it is supposed to be and it just plays static the whole time. I think there is either something wrong with the way im creating the song.raw file or some thing wrong with the encoding of that file to a mp4. Here is a look at the rest of my code to encode the pcm file to mp4. Can someone tell me where I may be going wrong? Thank You.

    public void encodeToAAC(){
        final String LOGTAG = "Encode";
        final String COMPRESSED_AUDIO_FILE_MIME_TYPE = "audio/mp4a-latm";
        final int KEY_CHANNEL_COUNT = 2;
        final int COMPRESSED_AUDIO_FILE_BIT_RATE = 96000; // 96kbps
        final int SAMPLING_RATE = 44100;
        final int CODEC_TIMEOUT_IN_MS = 5000;
        final int BUFFER_SIZE = 88200;
        Boolean mStop = false;
        MediaCodec codec;


        try {
            String filePath = Environment.getExternalStorageDirectory()+"/My Folder/song.raw";
            File inputFile = new File(filePath);
            FileInputStream fis = new FileInputStream(inputFile);

            File outputFile = new File(Environment.getExternalStorageDirectory()+"/My Folder/song.mp4");
            if (outputFile.exists()) outputFile.delete();

            MediaMuxer mux = new MediaMuxer(outputFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

            MediaFormat outputFormat = MediaFormat.createAudioFormat(COMPRESSED_AUDIO_FILE_MIME_TYPE, SAMPLING_RATE, KEY_CHANNEL_COUNT);
            outputFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, COMPRESSED_AUDIO_FILE_BIT_RATE);

            codec = MediaCodec.createEncoderByType(COMPRESSED_AUDIO_FILE_MIME_TYPE);
            codec.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            codec.start();
            ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
            ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();

            MediaCodec.BufferInfo outBuffInfo = new MediaCodec.BufferInfo();

            byte[] tempBuffer = new byte[BUFFER_SIZE];
            boolean hasMoreData = true;
            double presentationTimeUs = 0;
            int audioTrackIdx = 0;
            int totalBytesRead = 0;
            int percentComplete;

            do {

                int inputBufIndex = 0;
                while (inputBufIndex != -1 && hasMoreData) {
                    inputBufIndex = codec.dequeueInputBuffer(CODEC_TIMEOUT_IN_MS);

                    if (inputBufIndex >= 0) {
                        ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
                        dstBuf.clear();

                        int bytesRead = fis.read(tempBuffer, 0, dstBuf.limit());
                        if (bytesRead == -1) { // -1 implies EOS
                            hasMoreData = false;
                            codec.queueInputBuffer(inputBufIndex, 0, 0, (long) presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        } else {
                            totalBytesRead += bytesRead;
                            dstBuf.put(tempBuffer, 0, bytesRead);
                            codec.queueInputBuffer(inputBufIndex, 0, bytesRead, (long) presentationTimeUs, 0);
                            presentationTimeUs = 1000000l * (totalBytesRead / 2) / SAMPLING_RATE;
                        }
                    }
                }

                // Drain audio
                int outputBufIndex = 0;
                while (outputBufIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {

                    outputBufIndex = codec.dequeueOutputBuffer(outBuffInfo, CODEC_TIMEOUT_IN_MS);
                    if (outputBufIndex >= 0) {
                        ByteBuffer encodedData = codecOutputBuffers[outputBufIndex];
                        encodedData.position(outBuffInfo.offset);
                        encodedData.limit(outBuffInfo.offset + outBuffInfo.size);

                        if ((outBuffInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0 && outBuffInfo.size != 0) {
                            codec.releaseOutputBuffer(outputBufIndex, false);
                        } else {
                            mux.writeSampleData(audioTrackIdx, codecOutputBuffers[outputBufIndex], outBuffInfo);
                            codec.releaseOutputBuffer(outputBufIndex, false);
                        }
                    } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                        outputFormat = codec.getOutputFormat();
                        Log.v(LOGTAG, "Output format changed - " + outputFormat);
                        audioTrackIdx = mux.addTrack(outputFormat);
                        mux.start();
                    } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                        Log.e(LOGTAG, "Output buffers changed during encode!");
                    } else if (outputBufIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
                        // NO OP
                    } else {
                        Log.e(LOGTAG, "Unknown return code from dequeueOutputBuffer - " + outputBufIndex);
                    }
                }
                percentComplete = (int) Math.round(((float) totalBytesRead / (float) inputFile.length()) * 100.0);
                Log.v(LOGTAG, "Conversion % - " + percentComplete);
            } while (outBuffInfo.flags != MediaCodec.BUFFER_FLAG_END_OF_STREAM && !mStop);

            fis.close();
            mux.stop();
            mux.release();
            Log.v(LOGTAG, "Compression done ...");
        } catch (FileNotFoundException e) {
            Log.e(LOGTAG, "File not found!", e);
        } catch (IOException e) {
            Log.e(LOGTAG, "IO exception!", e);
        }

        mStop = false;
        // Notify UI thread...
    }
AMG
  • 117
  • 7
  • 1
    Wouldn't you need a third `MediaCodec` that encodes the mixed audio back to AAC or something before you write it to the `MediaMuxer`? I don't think having uncompressed PCM audio in an MP4 container is supported. – Michael Jul 07 '15 at 14:20
  • I think you may be right... maybe I can try something like that – AMG Jul 07 '15 at 14:23
  • I tried to execute what @Michael said and I think it is the way to go but I am still having some struggles as I explain in my update. – AMG Jul 08 '15 at 17:45
  • Any time I see "twice as long as it should be" I immediately wonder if the mono vs. stereo setting is wrong. Is your audio stereo? You don't appear to be setting `KEY_CHANNEL_COUNT`. – fadden Jul 08 '15 at 18:25
  • Thats what I was thinking @fadden but i declare the channel count to be 2. Its the third parameter when I call MediaFormat.createAudioFormat(COMPRESSED_AUDIO_FILE_MIME_TYPE, SAMPLING_RATE, 2) to set the output format. Any other suggestions? I updated the code to show where I set Channel count – AMG Jul 08 '15 at 19:15
  • How is the mix() function implemented ? I am interested in understanding the logic used to mix two audio short based arrays – SathyaKrishna Prabhu Tadepalli May 11 '21 at 22:46

1 Answers1

0

You have to put the KEY_CHANNEL_COUNT to 2. But more importantly make sure your presentation time is calculated correctly for stereo;

presentationTimeUs = 1000000l * (totalBytesRead / 4) / SAMPLING_RATE;
Tunaki
  • 132,869
  • 46
  • 340
  • 423
Patrick
  • 1
  • 1