Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 0 additions & 27 deletions .github/workflows/documentation.yml

This file was deleted.

4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ 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_centralPortalUsername: ${{ secrets.CENTRAL_PORTAL_USERNAME }}
ORG_GRADLE_PROJECT_centralPortalPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }}
- 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
4 changes: 2 additions & 2 deletions amf/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ kotlin {
compilations.all {
compileTaskProvider.configure {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_1_8)
jvmTarget.set(JvmTarget.JVM_18)
}
}
}
Expand Down Expand Up @@ -68,7 +68,7 @@ kotlin {

android {
namespace = "io.github.thibaultbee.krtmp.amf"
compileSdk = 34
compileSdk = 36
defaultConfig {
minSdk = 21
}
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
}
22 changes: 11 additions & 11 deletions buildSrc/src/main/kotlin/krtmp-publish.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ val javadocJar by tasks.registering(Jar::class) {

publishing {
repositories {
maven(url = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") {
name = "mavenCentral"
maven(url = "https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/") {
name = "centralPortal"
credentials {
username = project.loadProperty("ossrh_username")
password = project.loadProperty("ossrh_password")
username = project.loadProperty("centralPortalUsername")
password = project.loadProperty("centralPortalPassword")
}
}
maven(url = "https://s01.oss.sonatype.org/content/repositories/snapshots/") {
name = "mavenCentralSnapshots"
maven(url = "https://central.sonatype.com/repository/maven-snapshots/") {
name = "centralPortalSnapshots"
credentials {
username = project.loadProperty("ossrh_username")
password = project.loadProperty("ossrh_password")
username = project.loadProperty("centralPortalUsername")
password = project.loadProperty("centralPortalPassword")
}
}
}
Expand Down Expand Up @@ -68,9 +68,9 @@ if (project.hasProperty("signing_key_id") && project.hasProperty("signing_key")
) {
signing {
useInMemoryPgpKeys(
project.loadProperty("signing_key_id"),
project.loadFileContents("signing_key"),
project.loadProperty("signing_password")
project.loadProperty("signingInMemoryKeyId"),
project.loadFileContents("signingInMemoryKey"),
project.loadProperty("signingInMemoryKeyPassword")
)
sign(publishing.publications)
}
Expand Down
4 changes: 2 additions & 2 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ kotlin {
compilations.all {
compileTaskProvider.configure {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_1_8)
jvmTarget.set(JvmTarget.JVM_18)
}
}
}
Expand Down Expand Up @@ -63,7 +63,7 @@ kotlin {

android {
namespace = "io.github.thibaultbee.krtmp.common"
compileSdk = 34
compileSdk = 36
defaultConfig {
minSdk = 21
}
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
Loading
Loading