Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
53dca96
fu flv
ThibaultBee May 11, 2025
91b856f
fu flv
ThibaultBee May 12, 2025
0378e41
fu flv
ThibaultBee May 12, 2025
8051509
fu flv
ThibaultBee May 12, 2025
36a6caf
fu publication
ThibaultBee May 13, 2025
6d7c900
fu flv
ThibaultBee May 13, 2025
a387a1a
fu flv
ThibaultBee May 14, 2025
bc891f3
fu rtmp
ThibaultBee May 14, 2025
8b7c703
fu flvparser-cli
ThibaultBee May 14, 2025
bb68b0e
fu flvparser-cli
ThibaultBee May 14, 2025
7934bb2
fu flvparser-cli
ThibaultBee May 14, 2025
ee015de
fu flv
ThibaultBee May 14, 2025
16f1b8f
fu flv
ThibaultBee May 14, 2025
2a1a412
fu flv
ThibaultBee May 14, 2025
bf5a9da
fu flv
ThibaultBee May 15, 2025
e9e3bef
fu flv
ThibaultBee May 15, 2025
f303c96
fu flv
ThibaultBee May 15, 2025
1dba2f4
fu flv
ThibaultBee May 19, 2025
1146268
fu flv
ThibaultBee May 20, 2025
6e6419e
fu rtmp
ThibaultBee Jun 1, 2025
8a1a0e1
fu rtmp
ThibaultBee Jun 1, 2025
b50eda3
fu flv
ThibaultBee Jun 1, 2025
55c625c
fu rtmp
ThibaultBee Jun 1, 2025
6b95932
fu rtmp
ThibaultBee Jun 1, 2025
4456c51
fu rtmp
ThibaultBee Jun 6, 2025
e889c80
fu rtmp
ThibaultBee Jun 8, 2025
84ea128
fu rtmp
ThibaultBee Jun 8, 2025
23b5203
fu rtmp
ThibaultBee Jun 8, 2025
1f008e4
fu rtmp
ThibaultBee Jun 9, 2025
645f978
fu rtmp
ThibaultBee Jun 9, 2025
c5cd765
fu rtmp
ThibaultBee Jun 9, 2025
b7ed272
fu rtmp
ThibaultBee Jun 9, 2025
7b5ea0c
fu rtmp
ThibaultBee Jun 9, 2025
771b848
fix rtmp test
ThibaultBee Jun 9, 2025
6b3128d
fix rtmp
ThibaultBee Jun 16, 2025
d559a6e
fix rtmp
ThibaultBee Jun 16, 2025
a4b09a2
fu flv
ThibaultBee Jun 16, 2025
5e4457c
fix rtmp
ThibaultBee Jun 16, 2025
d7004e9
fix rtmp
ThibaultBee Jun 16, 2025
47b3a44
Bump ktor from 3.1.2 to 3.2.0
dependabot[bot] Jun 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ jobs:
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_IN_MEMORY_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_TOKEN }}
ORG_GRADLE_PROJECT_centralPortalToken: ${{ secrets.CENTRALPORTALTOKEN }}
- name: Generate documentation
run: ./gradlew dokkaHtmlMultiModule
- name: Deploy API documentation to Github Pages
Expand Down
74 changes: 41 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ implementation("io.github.thibaultbee.krtmp:rtmp:1.0.0")

## Usage

Creates a RTMP publish client with the Factory `RtmpClientConnectionFactory`:
### Client

Creates a RTMP client with the Factory `RtmpClient`:

```kotlin
val client = RtmpPublishClientConnectionFactory().create(
val client = RtmpClient(
"rtmp://my.server.com/app/streamkey" // Your RTMP server URL (incl app name and stream key)
)
```
Expand All @@ -44,15 +46,6 @@ client.createStream()
client.publish(Command.Publish.Type.LIVE)
```

If you have raw audio and video frames, you need to mux them into FLV tag headers. You can use
the `FlvMuxer` class for that.

```kotlin
val flvMuxer = client.flvMuxer
```

See [FLV](#flv) for more details to write audio and video frames..

If you already have FLV data, write your video/audio data:

```kotlin
Expand All @@ -72,7 +65,21 @@ try {
}
```

For advanced configuration, see `RtmpClientSettings`.
See [FLV](#flv) for more details to write audio and video frames..

### Server

Use the `RtmpServer` to create a RTMP server:

```kotlin
val server = RtmpServer("0.0.0.0:1935") // Listening on port 1935
```

Then start the server:

```kotlin
server.listen()
```

# FLV

Expand All @@ -84,8 +91,9 @@ Features:
- [x] Demuxer for FLV
- [x] AMF0 metadata
- [ ] AMF3 metadata
- [x] Supported audio codec: AAC
- [x] Supported video codec: AVC/H.264 and enhanced RTMP codecs: HEVC/H.265, VP9, AV1
- [x] Support for legacy RTMP
- [x] Support for enhanced RTMP v1: AV1, HEVC, VP8, VP9
- [x] Support for enhanced RTMP v2: Multitrack, Opus,...

## Installation

Expand All @@ -100,36 +108,36 @@ implementation("io.github.thibaultbee.krtmp:flv:1.0.0")
Creates a FLV muxer and add audio/video data:

```kotlin
val muxer = FlvMuxer()
val muxer = FLVMuxer(path = "/path/to/file.flv")

// Write FLV header
flvMuxer.encodeFlvHeader(hasAudio, hasVideo)

// Register audio configurations (if any)
val audioConfig = FlvAudioConfig(
val audioConfig = FLVAudioConfig(
FlvAudioConfig.SoundFormat.AAC,
FlvAudioConfig.SoundRate.KHZ44,
FlvAudioConfig.SoundSize.SND8BIT,
FlvAudioConfig.SoundType.STEREO
)
val audioId = muxer.addStream(audioConfig)

// Register audio configurations (if any)
val videoConfig = FlvVideoConfig(

// Register video configurations (if any)
val videoConfig = FLVVideoConfig(
)
val videoId = muxer.addStream(videoConfig)

// Start the muxer (write FlvTag (if needed) and onMetaData)
muxer.startStream()
// Write onMetadata
muxer.encode(0, OnMetadata(audioConfig, videoConfig))

// Write audio/video data
muxer.write(audioFrame)
muxer.write(videoFrame)
muxer.write(audioFrame)
muxer.write(videoFrame)
// till you're done
muxer.encode(audioFrame)
muxer.encode(videoFrame)
muxer.encode(audioFrame)
muxer.encode(videoFrame)

// Stop the muxer
muxer.stopStream()
// Till you're done, then
muxer.flush()

// Close the output
muxer.close()
```

# AMF
Expand All @@ -140,7 +148,7 @@ Features:

- [x] Serializer for AMF0
- [ ] Serializer for AMF3
- [ ] Deserializer for AMF0
- [x] Deserializer for AMF0
- [ ] Deserializer for AMF3

## Installation
Expand Down Expand Up @@ -172,7 +180,7 @@ val array = Amf.encodeToByteArray(MyData.serializer(), data)

# TODO

- [x] A FLV/RTMP parameter for supported level: (legacy, enhanced v1, enhanced v2,...)
- [ ] More tests (missing tests samples)

# Licence

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,5 @@ class AmfContainer internal constructor(private val elements: MutableList<AmfEle
override fun equals(other: Any?): Boolean = elements == other
override fun hashCode(): Int = elements.hashCode()
override fun toString(): String =
elements.joinToString(prefix = "[", postfix = "]", separator = ",")
elements.joinToString(prefix = "[", postfix = "]", separator = ", ")
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@ class AmfEcmaArray internal constructor(private val elements: MutableMap<String,
override fun hashCode(): Int = elements.hashCode()
override fun toString(): String {
return elements.entries.joinToString(
separator = ",",
separator = ", ",
prefix = "{",
postfix = "}",
transform = { (k, v) ->
buildString {
append(k)
append(':')
append('=')
append(v)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ class AmfObject internal constructor(private val elements: MutableMap<String, Am
override fun hashCode(): Int = elements.hashCode()
override fun toString(): String {
return elements.entries.joinToString(
separator = ",",
separator = ", ",
prefix = "{",
postfix = "}",
transform = { (k, v) ->
buildString {
append(k)
append(':')
append('=')
append(v)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ fun amf0StrictArrayFrom(source: Source): AmfStrictArray {
return amf0StrictArray
}

fun amfStrictArrayOf(initialElements: List<Any?>) = AmfStrictArray().apply { addAll(initialElements) }
fun amfStrictArrayOf(initialElements: List<Any?>) =
AmfStrictArray().apply { addAll(initialElements) }

class AmfStrictArray internal constructor(private val elements: MutableList<AmfElement> = mutableListOf()) :
AmfElement(), MutableList<AmfElement> by elements {
Expand All @@ -59,5 +60,5 @@ class AmfStrictArray internal constructor(private val elements: MutableList<AmfE
override fun equals(other: Any?): Boolean = elements == other
override fun hashCode(): Int = elements.hashCode()
override fun toString(): String =
elements.joinToString(prefix = "[", postfix = "]", separator = ",")
elements.joinToString(prefix = "[", postfix = "]", separator = ", ")
}
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ kover {
reports {
verify {
rule {
minBound(65, CoverageUnit.LINE)
minBound(35, CoverageUnit.LINE)

// we allow lower branch coverage, because not all checks in the internal code lead to errors
minBound(45, CoverageUnit.BRANCH)
minBound(30, CoverageUnit.BRANCH)
}
}
}
Expand Down
9 changes: 4 additions & 5 deletions buildSrc/src/main/kotlin/Utils.kt
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import org.gradle.api.Project
import java.util.Locale

fun Project.loadProperty(
projectPropertyName: String,
envName: String = projectPropertyName.uppercase()
): String {
): String? {
val envValue = System.getenv(envName)?.toString()
if (envValue != null) return envValue

val projectPropertiesValue = project.properties[projectPropertyName]?.toString()
if (projectPropertiesValue != null) return projectPropertiesValue

return ""
return null
}

fun Project.loadFileContents(
projectPropertyName: String,
envName: String = projectPropertyName.uppercase()
): String {
): String? {
val decodeIfNeeded: (String) -> String = {
if (it.startsWith("~/")) {
// the value is a path to file on disk. Read its contents
Expand All @@ -35,5 +34,5 @@ fun Project.loadFileContents(
val projectPropertiesValue = project.properties[projectPropertyName]?.toString()
if (projectPropertiesValue != null) return decodeIfNeeded(projectPropertiesValue)

return ""
return null
}
32 changes: 22 additions & 10 deletions buildSrc/src/main/kotlin/krtmp-publish.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,30 @@ val javadocJar by tasks.registering(Jar::class) {

publishing {
repositories {
maven(url = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") {
name = "mavenCentral"
credentials {
username = project.loadProperty("ossrh_username")
password = project.loadProperty("ossrh_password")
maven(url = "https://central.sonatype.com/api/v1/publisher/deployments/download/") {
name = "centralPortal"
project.loadProperty("centralPortalToken")?.let {
authentication {
create<HttpHeaderAuthentication>("header")
}

credentials(HttpHeaderCredentials::class) {
name = "Authorization"
value = "Bearer $it"
}
}
}
maven(url = "https://s01.oss.sonatype.org/content/repositories/snapshots/") {
name = "mavenCentralSnapshots"
credentials {
username = project.loadProperty("ossrh_username")
password = project.loadProperty("ossrh_password")
maven(url = "https://central.sonatype.com/repository/maven-snapshots/") {
name = "centralPortalSnapshots"
project.loadProperty("centralPortalToken")?.let {
authentication {
create<HttpHeaderAuthentication>("header")
}

credentials(HttpHeaderCredentials::class) {
name = "Authorization"
value = "Bearer $it"
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.github.thibaultbee.krtmp.common.logger
/**
* Default logger implementation.
*/
class DefaultLogger: ILogger {
class DefaultLogger: IKrtmpLogger {
override fun e(tag: String, message: String, tr: Throwable?) {
println("E/$tag: $message")
tr?.printStackTrace()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.github.thibaultbee.krtmp.common.logger

interface ILogger {
interface IKrtmpLogger {
/**
* Logs an error.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package io.github.thibaultbee.krtmp.common.logger

object Logger {
object KrtmpLogger {
/**
* The logger implementation.
* Customize it by setting a new [ILogger] implementation.
* Customize it by setting a new [IKrtmpLogger] implementation.
*/
var logger: ILogger = DefaultLogger()
var logger: IKrtmpLogger = DefaultLogger()

/**
* Logs an error.
Expand Down
2 changes: 1 addition & 1 deletion flv/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ kotlin {
commonMain.dependencies {
implementation(libs.kotlinx.io.core)
implementation(libs.kotlinx.serialization.core)
implementation(libs.kotlinx.coroutines.core)
api(project(":amf"))
api(project(":common"))
}
Expand All @@ -55,7 +56,6 @@ kotlin {
}
jvmTest.dependencies {
implementation(libs.kotlin.test)
implementation(libs.jcodec)
}
}

Expand Down
Loading
Loading