In Android, I'm trying to model `MediaExtractor` and `MediaCodec` working in tandem with `Flows`, the first as a producer, and the later as a consumer-producer. They will be used exclusively to process audio data in mono, most possibly with 16 bit depth and 8000 samples per second. This is the code of the `MediaCodec` part. Please check possible bugs:

```
package com.example.alarma.utils.speechrecognizer.flowed.impl

import android.media.MediaCodec
import android.media.MediaFormat
import com.example.alarma.utils.speechrecognizer.flowed.DecoderResult
import com.example.alarma.utils.speechrecognizer.flowed.DecoderResult.AudioData
import com.example.alarma.utils.speechrecognizer.flowed.DecoderResult.EndOfStream
import com.example.alarma.utils.speechrecognizer.flowed.DecoderResult.Error
import com.example.alarma.utils.speechrecognizer.flowed.DecoderResult.Format
import com.example.alarma.utils.speechrecognizer.flowed.impl.ExtractorResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn

/**
 * Creates a Flow of decoded audio events from an encoded audio Flow.
 * @param encodedFlow Flow emitting [ExtractorResult] (from MediaExtractor).
 * @return Flow emitting [com.example.alarma.utils.speechrecognizer.flowed.DecoderResult] (decoded data, format, EOS, errors).
 */
internal fun decodeAudio(encodedFlow: Flow<ExtractorResult>): Flow<DecoderResult> = flow {
    var codec: MediaCodec? = null
    var hasEnded = false

    try {
        encodedFlow.collect { event ->
            when (event) {
                is ExtractorResult.Format -> {
                    codec = createAndConfigureCodec(event.format)
                    // Emit the codec's OUTPUT format (may differ from input)
                    //emit(Format(codec.outputFormat))
                }
                is ExtractorResult.Sample -> {
                    val buffer = event.buffer
                    val bufferSize = event.bufferSize
                    val presentationTimeUs = event.presentationTimeUs
                    val flags = event.flags

                    codec?.let { it ->
                        // Always drain output first before feeding new input
                        drainCodecOutput(it, MediaCodec.BufferInfo()) { result ->
                            emit(result)
                        }

                        // Feed input buffer
                        val inputBufferIndex = it.dequeueInputBuffer(-1)
                        if (inputBufferIndex >= 0) {
                            it.getInputBuffer(inputBufferIndex)?.apply {
                                clear()
                                put(buffer)
                                it.queueInputBuffer(inputBufferIndex, 0, bufferSize, presentationTimeUs, flags)
                            }
                        }

                        // Drain output buffers immediately after queueing input
                        drainCodecOutput(it, MediaCodec.BufferInfo()) { decodedEvent ->
                            emit(decodedEvent)
                        }
                    }
                }
                is ExtractorResult.EndOfStream -> {
                    hasEnded = true
                    codec?.let {
                        // signalInputEos
                        val inputBufferIndex = it.dequeueInputBuffer(-1) // Wait indefinitely for a buffer
                        if (inputBufferIndex >= 0) {
                            it.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
                        }
                        // Now drain all remaining output until EOS flag appears
                        drainCodecOutput(it, MediaCodec.BufferInfo()) { result ->
                            emit(result)
                        }
                    }
                }
                is ExtractorResult.Error -> emit(Error(event.exception))
            }
        }
    } catch (e: Exception) {
        emit(Error(e))
    } finally {
        codec?.let {
            it.stop()
            it.release()
        }
    }
}
    .flowOn(Dispatchers.IO)


/**
 * Creates and configures a MediaCodec decoder for the given format.
 */
private fun createAndConfigureCodec(format: MediaFormat): MediaCodec {
    val mime = format.getString(MediaFormat.KEY_MIME)
        ?: throw IllegalArgumentException("MIME type missing from format")
    return MediaCodec.createDecoderByType(mime).apply {
        configure(format, null, null, 0)
        start()
    }
}

/**
 * Drains MediaCodec's output buffers and emits decoded events.
 * @return True if more output is available, false otherwise.
 */
private suspend fun drainCodecOutput(
    codec: MediaCodec,
    bufferInfo: MediaCodec.BufferInfo,
    emit: suspend (DecoderResult) -> Unit
) {
    var hasMoreOutput = false
    do {
        val outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, -1)
        when (outputBufferIndex) {
            MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> {
                // Emit new format if codec changes it (rare for audio)
                emit(Format(codec.outputFormat))
            }
            MediaCodec.INFO_TRY_AGAIN_LATER -> {
                // No output available now
                hasMoreOutput = false
            }
            else -> {
                if (outputBufferIndex >= 0) {
                    val outputBuffer = codec.getOutputBuffer(outputBufferIndex)
                    if (outputBuffer != null && bufferInfo.size > 0) {
                        // Copy decoded data to ByteArray (codec reuses buffers)
                        val decodedData = ByteArray(bufferInfo.size)
                        outputBuffer.get(decodedData)
                        emit(AudioData(decodedData, bufferInfo.presentationTimeUs, bufferInfo.flags))
                    }
                    // Release the buffer back to the codec
                    codec.releaseOutputBuffer(outputBufferIndex, false)
                    // Check if this is the last buffer (EOS)
                    hasMoreOutput = bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM == 0
                    if (!hasMoreOutput) {
                        emit(EndOfStream)
                    }
                }
            }
        }
    } while (hasMoreOutput)
}
```