<|start_filename|>haishinkit/src/main/java/com/haishinkit/rtmp/message/RtmpSetChunkSizeMessage.kt<|end_filename|>
package com.haishinkit.rtmp.message

import com.haishinkit.rtmp.RtmpConnection
import java.nio.ByteBuffer

/**
 * 5.4.1. Set Chunk Size (1)
 */
internal class RtmpSetChunkSizeMessage : RtmpMessage(TYPE_CHUNK_SIZE) {
    var size: Int = 0
    override var length: Int = CAPACITY

    override fun encode(buffer: ByteBuffer): RtmpMessage {
        buffer.putInt(size)
        return this
    }

    override fun decode(buffer: ByteBuffer): RtmpMessage {
        size = buffer.int
        return this
    }

    override fun execute(connection: RtmpConnection): RtmpMessage {
        connection.socket.chunkSizeC = size
        return this
    }

    companion object {
        private const val CAPACITY = 4
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/rtmp/message/RtmpAcknowledgementMessage.kt<|end_filename|>
package com.haishinkit.rtmp.message

import com.haishinkit.rtmp.RtmpConnection
import java.nio.ByteBuffer

/**
 * 5.4.3 Acknowledgement (3)
 */
internal class RtmpAcknowledgementMessage : RtmpMessage(TYPE_ACK) {
    var sequence = ByteArray(CAPACITY)
        private set
    override var length: Int = CAPACITY

    override fun encode(buffer: ByteBuffer): RtmpMessage {
        buffer.put(sequence)
        return this
    }

    override fun decode(buffer: ByteBuffer): RtmpMessage {
        buffer.get(sequence)
        return this
    }

    override fun execute(connection: RtmpConnection): RtmpMessage {
        return this
    }

    companion object {
        private const val CAPACITY = 4
    }
}


<|start_filename|>haishinkit/src/test/java/com/haishinkit/iso/AudioSpecificConfigTest.kt<|end_filename|>
package com.haishinkit.iso

import junit.framework.TestCase
import java.nio.ByteBuffer

class AudioSpecificConfigTest : TestCase() {
    fun testEncodeAndDecode() {
        val config = AudioSpecificConfig(
            type = AudioSpecificConfig.AudioObjectType.AAC_MAIN,
            frequency = AudioSpecificConfig.SamplingFrequency.HZ44100,
            channel = AudioSpecificConfig.ChannelConfiguration.FRONT_CENTER_AND_FRONT_LEFT_AND_FRONT_RIGHT
        )
        val buffer = ByteBuffer.allocate(2)
        config.encode(buffer)
        buffer.flip()
        val decodeConfig = AudioSpecificConfig().decode(buffer)
        assertEquals(config, decodeConfig)
    }

    fun testEncodeAndDecode2() {
        val config = AudioSpecificConfig(
            type = AudioSpecificConfig.AudioObjectType.AAC_LC,
            frequency = AudioSpecificConfig.SamplingFrequency.HZ48000,
            channel = AudioSpecificConfig.ChannelConfiguration.FRONT_OF_CENTER
        )
        val buffer = ByteBuffer.allocate(2)
        config.encode(buffer)
        buffer.flip()
        val decodeConfig = AudioSpecificConfig().decode(buffer)
        assertEquals(config, decodeConfig)
    }

    fun testEncodeAndDecode3() {
        val config = AudioSpecificConfig(
            type = AudioSpecificConfig.AudioObjectType.AAC_LC,
            frequency = AudioSpecificConfig.SamplingFrequency.HZ48000,
            channel = AudioSpecificConfig.ChannelConfiguration.FRONT_LEFT_AND_FRONT_RIGHT
        )
        val buffer = ByteBuffer.allocate(2)
        config.encode(buffer)
        buffer.flip()
        val decodeConfig = AudioSpecificConfig().decode(buffer)
        assertEquals(config, decodeConfig)
    }

    fun testEncode() {
        val buffer1 = ByteBuffer.wrap(byteArrayOf(18, 8))
        val config = AudioSpecificConfig().decode(buffer1)
        val buffer2 = ByteBuffer.allocate(2)
        config.encode(buffer2)
        print(buffer2.array().contentToString())
        buffer1.flip()
        buffer2.flip()
        assertEquals(buffer1, buffer2)
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/rtmp/message/RtmpAbortMessage.kt<|end_filename|>
package com.haishinkit.rtmp.message

import com.haishinkit.rtmp.RtmpConnection
import java.nio.ByteBuffer

/**
 * 5.4.2. Abort Message (2)
 */
internal class RtmpAbortMessage : RtmpMessage(TYPE_ABORT) {
    var discarded: Int = 0
        private set
    override var length: Int = CAPACITY

    override fun encode(buffer: ByteBuffer): RtmpMessage {
        buffer.putInt(discarded)
        return this
    }

    override fun decode(buffer: ByteBuffer): RtmpMessage {
        discarded = buffer.int
        return this
    }

    override fun execute(connection: RtmpConnection): RtmpMessage {
        return this
    }

    companion object {
        private const val CAPACITY = 4
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/lang/Utilize.kt<|end_filename|>
package com.haishinkit.lang

/**
 * An interface that methods for utilize.
 */
interface Utilize {
    var utilizable: Boolean

    /**
     * Tells the receiver to setUp.
     */
    fun setUp() {
        utilizable = true
    }

    /**
     * Tells the receiver to tearDown.
     */
    fun tearDown() {
        utilizable = false
    }
}


<|start_filename|>haishinkit/src/test/java/com/haishinkit/iso/AvcConfigurationRecordTest.kt<|end_filename|>
package com.haishinkit.iso

import junit.framework.TestCase
import java.nio.ByteBuffer

class AvcConfigurationRecordTest : TestCase() {
    fun testDecode() {
        val byteBuffer = ByteBuffer.wrap(SAMPLE_DATA_0)
        val record = AvcConfigurationRecord()
        record.decode(byteBuffer)
    }

    fun testOptions() {
        val byteBuffer = ByteBuffer.wrap(SAMPLE_DATA_0)
        val record = AvcConfigurationRecord()
        record.decode(byteBuffer)
    }

    companion object {
        private var SAMPLE_DATA_0 = byteArrayOf(1, 100, 0, 31, -1, -31, 0, 27, 103, 100, 0, 31, -84, -39, 64, 72, 5, -70, 106, 2, 2, 2, -128, 0, 0, 3, 0, -128, 0, 0, 30, 71, -116, 24, -53, 1, 0, 4, 104, -17, -68, -80)
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/flv/FlvVideoCodec.kt<|end_filename|>
package com.haishinkit.flv

/**
 * The type of flv supports video codecs.
 */
@Suppress("unused")
object FlvVideoCodec {
    const val SORENSON_H263: Byte = 0x02
    const val SCREEN1: Byte = 0x03
    const val ON2_VP6: Byte = 0x04
    const val ON2_VP6_ALPHA: Byte = 0x05
    const val SCREEN_2: Byte = 0x06
    const val AVC: Byte = 0x07
    const val UNKNOWN: Byte = Byte.MAX_VALUE
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/util/URIUtil.kt<|end_filename|>
package com.haishinkit.util

import java.net.URI

internal object URIUtil {
    fun withoutUserInfo(uri: URI): String {
        val userInfo = uri.userInfo ?: return uri.toString()
        return uri.toString().replace("${uri.rawUserInfo}@", "")
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/net/Responder.kt<|end_filename|>
package com.haishinkit.net

/**
 * A interface that response for a RTMPConnection.call().
 */
interface Responder {
    fun onResult(arguments: List<Any?>)
    fun onStatus(arguments: List<Any?>)
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/event/EventUtils.kt<|end_filename|>
package com.haishinkit.event

import java.util.HashMap

object EventUtils {
    fun toMap(event: Event): Map<String, Any> {
        val data = event.data
        if (data == null || data !is Map<*, *>) {
            return HashMap()
        }
        return data as Map<String, Any>
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/util/MD5Util.kt<|end_filename|>
package com.haishinkit.util

import android.util.Base64
import java.security.MessageDigest
import kotlin.text.Charsets.UTF_8

internal object MD5Util {
    fun base64(str: String, flags: Int): String {
        return Base64.encodeToString(md5(str), flags)
    }

    private fun md5(str: String): ByteArray {
        return MessageDigest.getInstance("MD5").digest(str.toByteArray(UTF_8))
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/event/EventDispatcher.kt<|end_filename|>
package com.haishinkit.event

import androidx.core.util.Pools
import java.util.ArrayList
import java.util.Collections
import java.util.concurrent.ConcurrentHashMap

open class EventDispatcher(private val target: IEventDispatcher?) : IEventDispatcher {
    private val pool = Pools.SynchronizedPool<Event>(MAX_POOL_SIZE)
    private val listeners = ConcurrentHashMap<String, MutableList<IEventListener>>()

    override fun addEventListener(type: String, listener: IEventListener, useCapture: Boolean) {
        val key: String = "$type/$useCapture"
        listeners.putIfAbsent(key, Collections.synchronizedList(mutableListOf<IEventListener>()))
        listeners[key]?.add(listener)
    }

    override fun dispatchEvent(event: Event) {
        if (event.type == null) {
            throw IllegalArgumentException()
        }

        val targets = ArrayList<IEventDispatcher>()
        targets.add(target ?: this)

        for (target in targets) {
            event.currentTarget = target
            val isTargetPhase = target === event.target

            if (isTargetPhase) {
                event.eventPhase = Event.EVENT_PHASE_AT_TARGET
            }

            val isCapturingPhase = (event.eventPhase == Event.EVENT_PHASE_CAPTURING).toString()
            val listeners = this.listeners["${event.type}/$isCapturingPhase"]
            if (listeners != null) {
                for (listener in listeners) {
                    listener.handleEvent(event)
                }
                if (event.propagationStopped) {
                    break
                }
            }

            if (isTargetPhase) {
                event.eventPhase = Event.EVENT_PHASE_BUBBLING
            }
        }

        event.target = null
        event.currentTarget = null
        event.eventPhase = Event.EVENT_PHASE_NONE
        event.propagationStopped = false
    }

    override fun dispatchEventWith(type: String, bubbles: Boolean, data: Any?) {
        val event = pool.acquire() ?: Event(type, bubbles, data)
        event.type = type
        event.isBubbles = bubbles
        event.data = data
        dispatchEvent(event)
        pool.release(event)
    }

    override fun removeEventListener(type: String, listener: IEventListener, useCapture: Boolean) {
        val key = "$type/$useCapture"
        if (!listeners.containsKey(key)) {
            return
        }
        val list = listeners[key]!!
        var i = list.size - 1
        while (0 <= i) {
            if (list[i] === listener) {
                list.removeAt(i)
                return
            }
            --i
        }
    }

    companion object {
        const val MAX_POOL_SIZE = 16
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/net/NetSocket.kt<|end_filename|>
package com.haishinkit.net

import java.nio.ByteBuffer
import java.util.concurrent.atomic.AtomicLong

interface NetSocket {
    interface Listener {
        fun onInput(buffer: ByteBuffer)
        fun onTimeout()
        fun onConnect()
    }

    var timeout: Int
    var listener: Listener?
    val totalBytesIn: AtomicLong
    val totalBytesOut: AtomicLong
    val queueBytesOut: AtomicLong

    fun connect(dstName: String, dstPort: Int, isSecure: Boolean)
    fun doOutput(buffer: ByteBuffer)
    fun close(disconnected: Boolean)
    fun createByteBuffer(capacity: Int): ByteBuffer
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/amf/Amf0Serializer.kt<|end_filename|>
package com.haishinkit.amf

import com.haishinkit.amf.data.AsUndefined
import java.nio.ByteBuffer
import java.util.Date

internal class Amf0Serializer(private val buffer: ByteBuffer) {

    fun putBoolean(value: Boolean): Amf0Serializer {
        buffer.put(Amf0Marker.BOOL.rawValue)
        buffer.put((if (value) 1 else 0).toByte())
        return this
    }

    fun putDouble(value: Double): Amf0Serializer {
        buffer.put(Amf0Marker.NUMBER.rawValue)
        buffer.putDouble(value)
        return this
    }

    fun putString(value: String?): Amf0Serializer {
        if (value == null) {
            buffer.put(Amf0Marker.NULL.rawValue)
            return this
        }
        val length = value.length
        val isShort = length <= java.lang.Short.MAX_VALUE.toInt()
        buffer.put(if (isShort) Amf0Marker.STRING.rawValue else Amf0Marker.LONGSTRING.rawValue)
        return putString(value, isShort)
    }

    fun putMap(value: Map<String, Any?>?): Amf0Serializer {
        if (value == null) {
            buffer.put(Amf0Marker.NULL.rawValue)
            return this
        }
        buffer.put(Amf0Marker.OBJECT.rawValue)
        for ((key, value1) in value) {
            putString(key, true).putObject(value1)
        }
        putString("", true)
        buffer.put(Amf0Marker.OBJECTEND.rawValue)
        return this
    }

    fun putDate(value: Date?): Amf0Serializer {
        if (value == null) {
            buffer.put(Amf0Marker.NULL.rawValue)
            return this
        }
        buffer.put(Amf0Marker.DATE.rawValue)
        buffer.putDouble(value.time.toDouble())
        buffer.put(byteArrayOf(0x00, 0x00))
        return this
    }

    fun putList(value: List<Any>?): Amf0Serializer {
        if (value == null) {
            buffer.put(Amf0Marker.NULL.rawValue)
            return this
        }
        buffer.put(Amf0Marker.ECMAARRAY.rawValue)
        if (value.isEmpty()) {
            buffer.put(byteArrayOf(0x00, 0x00, 0x00, 0x00))
            return this
        }
        buffer.putInt(value.size)
        for (`object` in value) {
            putObject(`object`)
        }
        return this
    }

    fun putObject(value: Any?): Amf0Serializer {
        if (value == null) {
            buffer.put(Amf0Marker.NULL.rawValue)
            return this
        }
        if (value is String) {
            return putString(value as String?)
        }
        if (value is Double) {
            return putDouble((value as Double?)!!)
        }
        if (value is Int) {
            return putDouble(value.toDouble())
        }
        if (value is Short) {
            return putDouble(value.toDouble())
        }
        if (value is Boolean) {
            return putBoolean((value as Boolean?)!!)
        }
        if (value is Date) {
            return putDate(value as Date?)
        }
        if (value is Map<*, *>) {
            return putMap(value as Map<String, Any>?)
        }
        if (value is List<*>) {
            return putList(value as List<Any>?)
        }
        if (value is AsUndefined) {
            buffer.put(Amf0Marker.UNDEFINED.rawValue)
            return this
        }
        return this
    }

    override fun toString(): String {
        return buffer.toString()
    }

    private fun putString(value: String, asShort: Boolean): Amf0Serializer {
        val length = value.length
        if (asShort) {
            buffer.putShort(length.toShort())
        } else {
            buffer.putInt(length)
        }
        buffer.put(value.toByteArray())
        return this
    }
}


<|start_filename|>haishinkit/src/test/java/com/haishinkit/rtmp/RtmpAuthenticatorTest.kt<|end_filename|>
package com.haishinkit.rtmp

import junit.framework.TestCase

class RtmpAuthenticatorTest : TestCase() {
    fun testMain() {
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/media/Timestamp.kt<|end_filename|>
package com.haishinkit.media

internal data class Timestamp(
    val scale: Long = 1L,
    private var start: Long = DEFAULT_TIMESTAMP
) {
    val duration: Long
        get() = (nanoTime - start) / scale

    var nanoTime: Long = DEFAULT_TIMESTAMP
        set(value) {
            if (0 < value && start == DEFAULT_TIMESTAMP) {
                start = value
            }
            field = value
        }

    fun clear() {
        nanoTime = DEFAULT_TIMESTAMP
        start = DEFAULT_TIMESTAMP
    }

    companion object {
        private const val DEFAULT_TIMESTAMP = -1L
    }
}


<|start_filename|>haishinkit/src/test/java/com/haishinkit/util/URIUtilTest.kt<|end_filename|>
package com.haishinkit.util

import junit.framework.TestCase
import java.net.URI

class URIUtilTest : TestCase() {
    fun testGetCompositeTime() {
        val uri = URI.create("rtmp://user:password@localhost:443/live")
        assertEquals(URIUtil.withoutUserInfo(uri), "rtmp://localhost:443/live")
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/util/FileUtil.kt<|end_filename|>
package com.haishinkit.util

import java.io.File

internal object FileUtil {
    fun delete(file: File) {
        if (!file.exists()) return
        if (file.isDirectory) {
            file.listFiles()?.forEach { child ->
                delete(child)
            }
        }
        file.delete()
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/amf/Amf0Deserializer.kt<|end_filename|>
package com.haishinkit.amf

import android.util.Log
import com.haishinkit.amf.data.AsArray
import com.haishinkit.amf.data.AsUndefined
import com.haishinkit.amf.data.AsXmlDocument
import java.io.UnsupportedEncodingException
import java.nio.ByteBuffer
import java.util.Date
import java.util.HashMap
import java.util.IllegalFormatFlagsException

internal class Amf0Deserializer(private val buffer: ByteBuffer) {
    val `object`: Any?
        get() {
            val marker = buffer.get()

            when (marker) {
                Amf0Marker.NUMBER.rawValue -> {
                    buffer.position(buffer.position() - 1)
                    return double
                }
                Amf0Marker.BOOL.rawValue -> {
                    buffer.position(buffer.position() - 1)
                    return boolean
                }
                Amf0Marker.STRING.rawValue -> {
                    buffer.position(buffer.position() - 1)
                    return string
                }
                Amf0Marker.OBJECT.rawValue -> {
                    buffer.position(buffer.position() - 1)
                    return map
                }
                Amf0Marker.MOVIECLIP.rawValue -> throw UnsupportedOperationException()
                Amf0Marker.NULL.rawValue -> return null
                Amf0Marker.UNDEFINED.rawValue -> return AsUndefined.instance
                Amf0Marker.REFERENCE.rawValue -> throw UnsupportedOperationException()
                Amf0Marker.ECMAARRAY.rawValue -> {
                    buffer.position(buffer.position() - 1)
                    return list
                }
                Amf0Marker.OBJECTEND.rawValue -> throw UnsupportedOperationException()
                Amf0Marker.STRICTARRAY.rawValue -> {
                    buffer.position(buffer.position() - 1)
                    return objects
                }
                Amf0Marker.DATE.rawValue -> {
                    buffer.position(buffer.position() - 1)
                    return date
                }
                Amf0Marker.LONGSTRING.rawValue -> {
                    buffer.position(buffer.position() - 1)
                    return string
                }
                Amf0Marker.UNSUPPORTED.rawValue -> throw UnsupportedOperationException()
                Amf0Marker.RECORDSET.rawValue -> throw UnsupportedOperationException()
                Amf0Marker.XMLDOCUMENT.rawValue -> {
                    buffer.position(buffer.position() - 1)
                    return xmlDocument
                }
                Amf0Marker.TYPEDOBJECT.rawValue -> throw UnsupportedOperationException()
                Amf0Marker.AVMPLUSH.rawValue -> throw UnsupportedOperationException()
                else -> {
                }
            }

            return null
        }

    val boolean: Boolean
        get() {
            val marker = buffer.get()
            if (marker != Amf0Marker.BOOL.rawValue) {
                throw IllegalFormatFlagsException(marker.toString())
            }
            return buffer.get().toInt() == 1
        }

    val double: Double
        get() {
            val marker = buffer.get()
            if (marker != Amf0Marker.NUMBER.rawValue) {
                throw IllegalFormatFlagsException(marker.toString())
            }
            return buffer.double
        }

    val string: String
        get() {
            val marker = buffer.get()
            when (marker) {
                Amf0Marker.STRING.rawValue, Amf0Marker.LONGSTRING.rawValue -> {
                }
                else -> throw IllegalFormatFlagsException(marker.toString())
            }
            return getString(Amf0Marker.STRING.rawValue == marker)
        }

    val map: Map<String, Any?>?
        get() {
            val marker = buffer.get()
            if (marker == Amf0Marker.NULL.rawValue) {
                return null
            }
            if (marker != Amf0Marker.OBJECT.rawValue) {
                throw IllegalFormatFlagsException(marker.toString())
            }
            val map = HashMap<String, Any?>()
            while (true) {
                val key = getString(true)
                if (key == "") {
                    buffer.get()
                    break
                }
                map.put(key, `object`)
            }
            return map
        }

    val objects: Array<Any?>?
        get() {
            val marker = buffer.get()
            if (marker == Amf0Marker.NULL.rawValue) {
                return null
            }
            if (marker != Amf0Marker.STRICTARRAY.rawValue) {
                throw IllegalFormatFlagsException(marker.toString())
            }

            val count = buffer.int
            val objects = arrayOfNulls<Any>(count)
            for (i in 0 until count) {
                objects[i] = `object`
            }

            return objects
        }

    val list: List<Any?>?
        get() {
            val marker = buffer.get()
            if (marker == Amf0Marker.NULL.rawValue) {
                return null
            }
            if (marker != Amf0Marker.ECMAARRAY.rawValue) {
                throw IllegalFormatFlagsException(marker.toString())
            }
            val count = buffer.int
            val array = AsArray(count)
            while (true) {
                val key = getString(true)
                if (key == "") {
                    buffer.get()
                    break
                }
                array.put(key, `object`)
            }
            return array
        }

    // timezone
    val date: Date
        get() {
            val marker = buffer.get()
            if (marker != Amf0Marker.DATE.rawValue) {
                throw IllegalFormatFlagsException(marker.toString())
            }
            val value = buffer.double
            buffer.position(buffer.position() + 2)
            val date = Date()
            date.time = value.toLong()
            return date
        }

    val xmlDocument: AsXmlDocument
        get() {
            val marker = buffer.get()
            if (marker != Amf0Marker.XMLDOCUMENT.rawValue) {
                throw IllegalFormatFlagsException(marker.toString())
            }
            return AsXmlDocument(getString(false))
        }

    private fun getString(asShort: Boolean): String {
        val length = if (asShort) buffer.short.toInt() else buffer.int
        return try {
            val bytes = ByteArray(length)
            buffer.get(bytes)
            String(bytes, charset("UTF-8"))
        } catch (e: UnsupportedEncodingException) {
            Log.e(javaClass.name, e.toString())
            ""
        }
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/flv/tag/FlvDataTag.kt<|end_filename|>
package com.haishinkit.flv.tag

import java.nio.ByteBuffer

data class FlvDataTag(
    override val type: Byte = FlvTag.TYPE_DATA,
    override var dataSize: Int,
    override var timestamp: Int,
    override var timestampExtended: Byte,
    override val streamId: Int = 0,
    override var data: ByteBuffer? = null,
) : FlvTag {
    override fun toByteArray(): ByteArray {
        return byteArrayOf()
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/amf/data/AsArray.kt<|end_filename|>
package com.haishinkit.amf.data

import java.util.ArrayList
import java.util.HashMap

class AsArray(capacity: Int) : ArrayList<Any>(capacity) {
    private val properties = HashMap<String, Any?>()

    fun put(k: String, v: Any?) {
        properties.put(k, v)
    }

    override fun toString(): String {
        return "{" + super.toString() + "," + properties.toString() + "}"
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/amf/data/AsUndefined.kt<|end_filename|>
package com.haishinkit.amf.data

class AsUndefined private constructor() {

    override fun toString(): String {
        return "undefined"
    }

    companion object {
        val instance = AsUndefined()
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/amf/data/AsXmlDocument.kt<|end_filename|>
package com.haishinkit.amf.data

class AsXmlDocument(private val document: String) {

    override fun toString(): String {
        return document
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/media/AudioSource.kt<|end_filename|>
package com.haishinkit.media

/**
 * An interface that captures an audio source.
 */
interface AudioSource : Source {
    fun onInputBufferAvailable(codec: android.media.MediaCodec, index: Int)
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/media/camera2/CameraResolver.kt<|end_filename|>
package com.haishinkit.media.camera2

import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.util.Size

class CameraResolver(private val manager: CameraManager) {
    fun getCameraId(facing: Int): String? {
        for (id in manager.cameraIdList) {
            val chars = manager.getCameraCharacteristics(id)
            if (chars.get(CameraCharacteristics.LENS_FACING) == facing) {
                return id
            }
        }
        return null
    }

    fun getFacing(characteristics: CameraCharacteristics): Int? {
        return characteristics.get(CameraCharacteristics.LENS_FACING)
    }

    fun getCameraSize(characteristics: CameraCharacteristics?): Size {
        val scm = characteristics?.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
        val cameraSizes = scm?.getOutputSizes(SurfaceTexture::class.java) ?: return Size(0, 0)
        return cameraSizes[0]
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/rtmp/message/RtmpSetPeerBandwidthMessage.kt<|end_filename|>
package com.haishinkit.rtmp.message

import com.haishinkit.rtmp.RtmpConnection
import java.nio.ByteBuffer

/**
 * 5.4.5. Set Peer Bandwidth (6)
 */
internal class RtmpSetPeerBandwidthMessage : RtmpMessage(TYPE_BANDWIDTH) {
    var size = 0
        private set
    var limit = LIMIT_HARD
        private set
    override var length: Int = CAPACITY

    override fun encode(buffer: ByteBuffer): RtmpMessage {
        buffer.putInt(size)
        buffer.put(limit)
        return this
    }

    override fun decode(buffer: ByteBuffer): RtmpMessage {
        size = buffer.int
        limit = buffer.get()
        return this
    }

    override fun execute(connection: RtmpConnection): RtmpMessage {
        connection.socket.bandWidth = size
        return this
    }

    companion object {
        const val LIMIT_HARD: Byte = 0x00
        const val LIMIT_SOFT: Byte = 0x01
        const val LIMIT_DYNAMIC: Byte = 0x02
        const val LIMIT_UNKNOWN: Byte = Byte.MAX_VALUE

        private const val CAPACITY = 5
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/flv/tag/FlvAudioTag.kt<|end_filename|>
package com.haishinkit.flv.tag

import com.haishinkit.flv.FlvAudioCodec
import com.haishinkit.flv.FlvSoundRate
import com.haishinkit.flv.FlvSoundSize
import com.haishinkit.flv.FlvSoundType
import java.nio.ByteBuffer

data class FlvAudioTag(
    override val type: Byte = FlvTag.TYPE_AUDIO,
    override var dataSize: Int,
    override var timestamp: Int,
    override var timestampExtended: Byte,
    override val streamId: Int = 0,
    override var data: ByteBuffer? = null,
    var codec: Byte = FlvAudioCodec.UNKNOWN,
    var soundRate: Byte = FlvSoundRate.kHz5_5,
    var soundSize: Byte = FlvSoundSize.SOUND_8BIT,
    var soundType: Byte = FlvSoundType.MONO
) : FlvTag {
    override fun toByteArray(): ByteArray {
        return byteArrayOf()
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/flv/FlvSoundType.kt<|end_filename|>
package com.haishinkit.flv

@Suppress("unused")
object FlvSoundType {
    const val MONO: Byte = 0x00
    const val STEREO: Byte = 0x01
}


<|start_filename|>haishinkit/src/test/java/com/haishinkit/iso/AvcFormatUtilsTest.kt<|end_filename|>
package com.haishinkit.iso

import junit.framework.TestCase
import java.nio.ByteBuffer
import java.nio.ByteOrder

class AvcFormatUtilsTest : TestCase() {
    fun testToByteStream() {
        val byteBuffer = ByteBuffer.wrap(SAMPLE_DATA_0).apply {
            order(ByteOrder.BIG_ENDIAN)
        }
        byteBuffer.rewind()
        AvcFormatUtils.toByteStream(byteBuffer, 4)
    }

    companion object {
        private var SAMPLE_DATA_0 = byteArrayOf(0, 0, 0, 0, 0, 0, 0, 28, 65, -18, 1, -16, 3, -88, -27, -21, 4, 28, 20, 123, -17, 114, -69, -81, 0, 39, -36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 65, -18, 1, -16, 3, -88, -27, -21, 4, 28, 20, 123, -17, 114, -69, -81, 0, 39, -36, 0, 0, 0, 0, 0, 0, 0, 0, 0)
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/flv/FlvAacPacketType.kt<|end_filename|>
package com.haishinkit.flv

object FlvAacPacketType {
    const val SEQ: Byte = 0x00
    const val RAW: Byte = 0x01
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/codec/util/FpsController.kt<|end_filename|>
package com.haishinkit.codec.util

interface FpsController {
    fun advanced(timestamp: Long): Boolean
    fun timestamp(timestamp: Long): Long
    fun clear()
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/flv/FlvAudioCodec.kt<|end_filename|>
package com.haishinkit.flv

@Suppress("unused")
object FlvAudioCodec {
    const val ADPCM: Byte = 0x01
    const val MP3: Byte = 0x02
    const val PCMLE: Byte = 0x03
    const val NELLYMOSER16K: Byte = 0x04
    const val NELLYMOSER8K: Byte = 0x05
    const val NELLYMOSER: Byte = 0x06
    const val G711A: Byte = 0x07
    const val G711MU: Byte = 0x08
    const val AAC: Byte = 0x0A
    const val SPEEX: Byte = 0x0B
    const val MP3_8K: Byte = 0x0E
    const val UNKNOWN: Byte = Byte.MAX_VALUE
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/util/MediaFormatUtil.kt<|end_filename|>
package com.haishinkit.util

import android.media.MediaFormat

object MediaFormatUtil {
    private const val CROP_LEFT = "crop-left"
    private const val CROP_RIGHT = "crop-right"
    private const val CROP_TOP = "crop-top"
    private const val CROP_BOTTOM = "crop-bottom"

    fun getWidth(format: MediaFormat): Int {
        var width = format.getInteger(MediaFormat.KEY_WIDTH)
        if (format.containsKey(CROP_LEFT) && format.containsKey(CROP_RIGHT)) {
            width = format.getInteger(CROP_RIGHT) + 1 - format.getInteger(CROP_LEFT)
        }
        return width
    }

    fun getHeight(format: MediaFormat): Int {
        var height = format.getInteger(MediaFormat.KEY_HEIGHT)
        if (format.containsKey(CROP_TOP) && format.containsKey(CROP_BOTTOM)) {
            height = format.getInteger(CROP_BOTTOM) + 1 - format.getInteger(CROP_TOP)
        }
        return height
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/flv/FlvSoundRate.kt<|end_filename|>
package com.haishinkit.flv

/**
 * The type of flv supports audio sound rates.
 */
@Suppress("unused")
object FlvSoundRate {
    /**
     * The sound rate of  5,500.0kHz
     */
    const val kHz5_5: Byte = 0x00

    /**
     * Ths sound rate of 11,000.0kHz.
     */
    const val kHz11: Byte = 0x01

    /**
     * The sound rate of 22,050.0kHz.
     */
    const val kHz22: Byte = 0x02

    /**
     * Ths sound rate of 44,100.0kHz.
     */
    const val kHz44: Byte = 0x03
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/flv/FlvFlameType.kt<|end_filename|>
package com.haishinkit.flv

@Suppress("unused")
object FlvFlameType {
    const val KEY: Byte = 0x01
    const val INTER: Byte = 0x02
    const val DISPOSABLE: Byte = 0x03
    const val GENERATED: Byte = 0x04
    const val COMMAND: Byte = 0x05
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/codec/util/DefaultFpsController.kt<|end_filename|>
package com.haishinkit.codec.util

internal class DefaultFpsController : FpsController {
    override fun advanced(timestamp: Long): Boolean {
        return true
    }

    override fun timestamp(timestamp: Long): Long {
        return timestamp
    }

    override fun clear() {
    }

    companion object {
        var instance = DefaultFpsController()
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/media/Source.kt<|end_filename|>
package com.haishinkit.media

import com.haishinkit.lang.Running
import com.haishinkit.lang.Utilize
import com.haishinkit.net.NetStream

/**
 * An interface that captures a source.
 */
interface Source : Running, Utilize {
    var stream: NetStream?
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/flv/tag/FlvTag.kt<|end_filename|>
package com.haishinkit.flv.tag

import java.nio.ByteBuffer

/**
 * An interface representations FlvTag.
 */
interface FlvTag {
    /**
     * Type of the tag.
     */
    val type: Byte

    /**
     * Length of the data in the Data field.
     */
    var dataSize: Int

    /**
     * Time in milliseconds field.
     */
    var timestamp: Int

    /**
     * Extended time in field.
     */
    var timestampExtended: Byte

    /**
     * Always 0
     */
    val streamId: Int

    /**
     * Body of tag.
     */
    var data: ByteBuffer?

    fun toByteArray(): ByteArray

    companion object {
        var TYPE_AUDIO: Byte = 8
        var TYPE_VIDEO: Byte = 9
        var TYPE_DATA: Byte = 18
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/event/IEventListener.kt<|end_filename|>
package com.haishinkit.event

interface IEventListener {
    fun handleEvent(event: Event)
}


<|start_filename|>haishinkit/src/test/java/com/haishinkit/rtmp/message/RtmpVideoMessageTest.kt<|end_filename|>
package com.haishinkit.rtmp.message

import junit.framework.TestCase
import java.nio.ByteBuffer

class RtmpVideoMessageTest : TestCase() {
    fun testGetCompositeTime() {
        val message1 = RtmpVideoMessage()
        message1.data = ByteBuffer.wrap(byteArrayOf(0, 255.toByte(), 251.toByte(), 160.toByte()))
        assertEquals(-1120, message1.compositeTime)

        val message2 = RtmpVideoMessage()
        message2.data = ByteBuffer.wrap(byteArrayOf(0, 255.toByte(), 252.toByte(), 114.toByte()))
        assertEquals(-910, message2.compositeTime)
    }
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/codec/RecordSetting.kt<|end_filename|>
package com.haishinkit.codec

import java.io.File

class RecordSetting(
    var directory: File? = null
)


<|start_filename|>haishinkit/src/main/java/com/haishinkit/flv/FlvSoundSize.kt<|end_filename|>
package com.haishinkit.flv

@Suppress("unused")
object FlvSoundSize {
    const val SOUND_8BIT: Byte = 0x00
    const val SOUND_16BIT: Byte = 0x01
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/lang/Running.kt<|end_filename|>
package com.haishinkit.lang

import java.util.concurrent.atomic.AtomicBoolean

/**
 * An interface that methods for running.
 */
interface Running {
    /**
     * Indicates whether the receiver is running.
     */
    val isRunning: AtomicBoolean

    /**
     * Tells the receiver to start running.
     */
    fun startRunning()

    /**
     * Tells the receiver to stop running.
     */
    fun stopRunning()
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/flv/FlvAvcPacketType.kt<|end_filename|>
package com.haishinkit.flv

object FlvAvcPacketType {
    const val SEQ: Byte = 0x00
    const val NAL: Byte = 0x01
    const val EOS: Byte = 0x02
}


<|start_filename|>haishinkit/src/main/java/com/haishinkit/util/ByteExt.kt<|end_filename|>
package com.haishinkit.util

internal fun Byte.toPositiveInt() = toInt() and 0xFF


<|start_filename|>haishinkit/src/main/java/com/haishinkit/rtmp/message/RtmpWindowAcknowledgementSizeMessage.kt<|end_filename|>
package com.haishinkit.rtmp.message

import com.haishinkit.rtmp.RtmpChunk
import com.haishinkit.rtmp.RtmpConnection
import java.nio.ByteBuffer

/**
 * 5.4.4. Window Acknowledgement Size (5)
 */
internal class RtmpWindowAcknowledgementSizeMessage : RtmpMessage(TYPE_ACK) {
    var size: Int = 0
        private set
    override var length: Int = CAPACITY

    override fun encode(buffer: ByteBuffer): RtmpMessage {
        buffer.putInt(size)
        return this
    }

    override fun decode(buffer: ByteBuffer): RtmpMessage {
        size = buffer.int
        return this
    }

    override fun execute(connection: RtmpConnection): RtmpMessage {
        val ack = connection.messageFactory.createRtmpWindowAcknowledgementSizeMessage()
        ack.size = size
        ack.chunkStreamID = RtmpChunk.CONTROL
        connection.doOutput(RtmpChunk.ZERO, ack)
        return this
    }

    companion object {
        private const val CAPACITY = 4
    }
}
