1

I am decoding an mp3 file, first I convert the mp3 file into a chunks of byteArray of size 1000 and put it in a circularArray and then pass it to mediaCodec callback for decoding (decode one byteArray at a time), I follow this link. It is working fine for Samsung devices, but if I use other than Samsung devices (Vivo, pixel 3a) it crashes at the mediaCodec.getInputBuffer(index) in the callback of onInputBufferAvailable by giving the exception IllegalStateException. My code is as follows:

var decoder: MediaCodec = MediaCodec.createDecoderByType("audio/mpeg")
decoder.configure(format, null, null, 0)
decoder.setCallback(object : MediaCodec.Callback() {
        override fun onInputBufferAvailable(mediaCodec: MediaCodec, i: Int) {

            while (true) {
                if (circularArray!!.size() > 0) {
                    val data: ByteArray = circularArray.popFirst()
                    val info = MediaCodec.BufferInfo()
                    val buffer = mediaCodec.getInputBuffer(i)
                    buffer!!.put(data, 0, data.size)
                    mediaCodec.queueInputBuffer(i, 0, data.size, 0, 0)
                    break
                }
            }
        }

        override fun onOutputBufferAvailable(mediaCodec: MediaCodec, i: Int, info: MediaCodec.BufferInfo) {
            //DECODING PACKET ENDED
            val outBuffer = mediaCodec.getOutputBuffer(i)
            val chunk = ByteArray(info.size)
            outBuffer!![chunk] // Read the buffer all at once
            outBuffer!!.clear()
            Log.d(TAG, "onOutputBufferAvailable: ${info.size}")
            audioTrack!!.write(chunk, info.offset, info.offset + info.size) 
            mediaCodec.releaseOutputBuffer(i, false)
        }

        override fun onError(mediaCodec: MediaCodec, e: MediaCodec.CodecException) {}
        override fun onOutputFormatChanged(mediaCodec: MediaCodec, mediaFormat: MediaFormat) {}
    })

    decoder!!.start()

I converted my file like this

val tempBuf = ByteArray(1000)
var byteRead: Int
    try {

        val bufferedInputStream = BufferedInputStream(FileInputStream(mp3File))
        while (bufferedInputStream.read(tempBuf).also { byteRead = it } != -1) {
            circularArray.addLast(tempBuf.copyOf())
        }
        bufferedInputStream.close()
        Thread(aacDecoderAndPlayRunnable).start()

    } catch (e: java.lang.Exception) {
        Log.d(TAG, "fileToInputStream: ${e.message}")
        e.printStackTrace()
        null
    }

The exception where the app crashes is

Crash

Even if I try to get the format form mediaCodec in the callback, it gives an exception and crashes anyway. I also checked supportedTypes from the codec it supports audio/mpeg.

Shan Ahmad
  • 31
  • 4

1 Answers1

0

First of all, the MediaCodec works with a queue of input buffers. And you can read more about it in the docs.

The second parameter of the onInputBufferAvailable callback is the index of the buffer. When calling getInputBuffer() you must pass this index instead of 0:

val buffer = mediaCodec.getInputBuffer(i)

Second, consider using the MediaExtractor instead of reading the file yourself. It supplies you will presentation timestamps and flags to pass into queueInputBuffer().

Third, you need to remove the while (true) loop. You can only queue one buffer per callback.

dev.bmax
  • 8,998
  • 3
  • 30
  • 41
  • I wrote 0 instead of index mistakenly while copy pasting on stack overflow, although I am using index instead of 0. I also tried MediaExtractor but the result is same, I had the same exception (works on Samsung Device with MediaExtractor) . And also keep in consideration my code is working fine when I use Samsung devices. If I use other devices like Xiaomi, Pixel or Vivo I am having an exception by using mediaCodec which I get in the callback, even if I try to get anything form this mediaCodec like mediaCodec.codecInfo I get the exception. – Shan Ahmad Dec 08 '22 at 10:43
  • OK, you also need to remove the `while (true)` loop. You can only queue one buffer at at time. – dev.bmax Dec 08 '22 at 12:05
  • Additionally, you must pass the presentation timestamp of each buffer to `queueInputBuffer()`. You can get it from `MediaExtractor`. – dev.bmax Dec 08 '22 at 12:15
  • [link](https://github.com/taehwandev/MediaCodecExample/blob/master/app/src/main/java/tech/thdev/mediacodecexample/audio/AACAudioDecoderThread.kt) – Shan Ahmad Dec 08 '22 at 13:38
  • The linked example uses the `MediaCodec` in the synchronous mode. This is the old and less convenient way. While you have callbacks and you can only process one buffer in your callback. – dev.bmax Dec 08 '22 at 13:42
  • I also tried this [example](https://github.com/taehwandev/MediaCodecExample/blob/master/app/src/main/java/tech/thdev/mediacodecexample/audio/AACAudioDecoderThread.kt), but instead of aac file I used **mp3 file** and also I changed my decoder to **audio/mpeg**. And in this example he uses mediaExtractor. Regardless of any change, it is working on Samsung device and not working on any other device. I also tested this on OlePlus 8t but result is the same it crashes at line 128 by calling _decoder.outputBuffers_ with same exception **IllegalStateException**. – Shan Ahmad Dec 08 '22 at 13:46
  • But did you remove the `while (true)` loop and are you sending the timestamps? – dev.bmax Dec 08 '22 at 14:06
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/250280/discussion-between-shan-ahmad-and-dev-bmax). – Shan Ahmad Dec 09 '22 at 05:56