Skip to content

Commit 5160108

Browse files
committed
fu flv
1 parent 0d2509b commit 5160108

File tree

190 files changed

+9435
-4715
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

190 files changed

+9435
-4715
lines changed

.github/workflows/documentation.yml

Lines changed: 0 additions & 27 deletions
This file was deleted.

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ jobs:
2121
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_IN_MEMORY_KEY }}
2222
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_KEY_ID }}
2323
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}
24-
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USERNAME }}
25-
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_TOKEN }}
24+
ORG_GRADLE_PROJECT_centralPortalUsername: ${{ secrets.CENTRAL_PORTAL_USERNAME }}
25+
ORG_GRADLE_PROJECT_centralPortalPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }}
2626
- name: Generate documentation
2727
run: ./gradlew dokkaHtmlMultiModule
2828
- name: Deploy API documentation to Github Pages

README.md

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ A RTMP client and server (soon) library for Kotlin Multiplatform.
66

77
Features:
88

9-
- [x] RTMP publish client
10-
- [ ] RTMP play client
11-
- [ ] RTMP play2 client
12-
- [ ] RTMP server
9+
- [x] RTMP client
10+
- [x] RTMP server
1311
- [ ] Statistics
12+
- [x] Support for legacy RTMP
13+
- [x] Support for enhanced RTMP v2: AV1, HEVC, VP8, VP9
1414

1515
Supported protocols:
1616

@@ -23,15 +23,17 @@ Supported protocols:
2323
Adds the following dependency to your project:
2424

2525
```kotlin
26-
implementation("io.github.thibaultbee.krtmp:rtmp:1.0.0")
26+
implementation("io.github.thibaultbee.krtmp:rtmp:0.9.0")
2727
```
2828

2929
## Usage
3030

31-
Creates a RTMP publish client with the Factory `RtmpClientConnectionFactory`:
31+
### Client
32+
33+
Creates a RTMP client with the Factory `RtmpClient`:
3234

3335
```kotlin
34-
val client = RtmpPublishClientConnectionFactory().create(
36+
val client = RtmpClient(
3537
"rtmp://my.server.com/app/streamkey" // Your RTMP server URL (incl app name and stream key)
3638
)
3739
```
@@ -41,18 +43,9 @@ Then prepare your live by sending these messages to the server:
4143
```kotlin
4244
client.connect()
4345
client.createStream()
44-
client.publish(Command.Publish.Type.LIVE)
45-
```
46-
47-
If you have raw audio and video frames, you need to mux them into FLV tag headers. You can use
48-
the `FlvMuxer` class for that.
49-
50-
```kotlin
51-
val flvMuxer = client.flvMuxer
46+
client.publish(StreamPublishType.LIVE)
5247
```
5348

54-
See [FLV](#flv) for more details to write audio and video frames..
55-
5649
If you already have FLV data, write your video/audio data:
5750

5851
```kotlin
@@ -72,7 +65,21 @@ try {
7265
}
7366
```
7467

75-
For advanced configuration, see `RtmpClientSettings`.
68+
See [FLV](#flv) for more details to write audio and video frames..
69+
70+
### Server
71+
72+
Use the `RtmpServer` to create a RTMP server:
73+
74+
```kotlin
75+
val server = RtmpServer("0.0.0.0:1935") // Listening on port 1935
76+
```
77+
78+
Then start the server:
79+
80+
```kotlin
81+
server.listen()
82+
```
7683

7784
# FLV
7885

@@ -84,52 +91,53 @@ Features:
8491
- [x] Demuxer for FLV
8592
- [x] AMF0 metadata
8693
- [ ] AMF3 metadata
87-
- [x] Supported audio codec: AAC
88-
- [x] Supported video codec: AVC/H.264 and enhanced RTMP codecs: HEVC/H.265, VP9, AV1
94+
- [x] Support for legacy RTMP
95+
- [x] Support for enhanced RTMP v1: AV1, HEVC, VP8, VP9
96+
- [x] Support for enhanced RTMP v2: Multitrack, Opus,...
8997

9098
## Installation
9199

92100
Adds the following dependencies to your project:
93101

94102
```kotlin
95-
implementation("io.github.thibaultbee.krtmp:flv:1.0.0")
103+
implementation("io.github.thibaultbee.krtmp:flv:0.9.0")
96104
```
97105

98106
## Usage
99107

100108
Creates a FLV muxer and add audio/video data:
101109

102110
```kotlin
103-
val muxer = FlvMuxer()
111+
val muxer = FLVMuxer(path = "/path/to/file.flv")
112+
113+
// Write FLV header
114+
flvMuxer.encodeFlvHeader(hasAudio, hasVideo)
104115

105116
// Register audio configurations (if any)
106-
val audioConfig = FlvAudioConfig(
117+
val audioConfig = FLVAudioConfig(
107118
FlvAudioConfig.SoundFormat.AAC,
108119
FlvAudioConfig.SoundRate.KHZ44,
109120
FlvAudioConfig.SoundSize.SND8BIT,
110121
FlvAudioConfig.SoundType.STEREO
111122
)
112-
val audioId = muxer.addStream(audioConfig)
113-
114-
// Register audio configurations (if any)
115-
val videoConfig = FlvVideoConfig(
116-
123+
// Register video configurations (if any)
124+
val videoConfig = FLVVideoConfig(
117125
)
118-
val videoId = muxer.addStream(videoConfig)
119126

120-
// Start the muxer (write FlvTag (if needed) and onMetaData)
121-
muxer.startStream()
127+
// Write onMetadata
128+
muxer.encode(0, OnMetadata(audioConfig, videoConfig))
122129

123130
// Write audio/video data
124-
muxer.write(audioFrame)
125-
muxer.write(videoFrame)
126-
muxer.write(audioFrame)
127-
muxer.write(videoFrame)
128-
// till you're done
131+
muxer.encode(audioFrame)
132+
muxer.encode(videoFrame)
133+
muxer.encode(audioFrame)
134+
muxer.encode(videoFrame)
129135

130-
// Stop the muxer
131-
muxer.stopStream()
136+
// Till you're done, then
137+
muxer.flush()
132138

139+
// Close the output
140+
muxer.close()
133141
```
134142

135143
# AMF
@@ -140,7 +148,7 @@ Features:
140148

141149
- [x] Serializer for AMF0
142150
- [ ] Serializer for AMF3
143-
- [ ] Deserializer for AMF0
151+
- [x] Deserializer for AMF0
144152
- [ ] Deserializer for AMF3
145153

146154
## Installation
@@ -151,7 +159,7 @@ details.
151159
Then, adds the following dependencies to your project:
152160

153161
```kotlin
154-
implementation("io.github.thibaultbee.krtmp:amf:1.0.0")
162+
implementation("io.github.thibaultbee.krtmp:amf:0.9.0")
155163
```
156164

157165
## Usage
@@ -172,7 +180,7 @@ val array = Amf.encodeToByteArray(MyData.serializer(), data)
172180

173181
# TODO
174182

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

177185
# Licence
178186

amf/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ kotlin {
2727
compilations.all {
2828
compileTaskProvider.configure {
2929
compilerOptions {
30-
jvmTarget.set(JvmTarget.JVM_1_8)
30+
jvmTarget.set(JvmTarget.JVM_18)
3131
}
3232
}
3333
}
@@ -68,7 +68,7 @@ kotlin {
6868

6969
android {
7070
namespace = "io.github.thibaultbee.krtmp.amf"
71-
compileSdk = 34
71+
compileSdk = 36
7272
defaultConfig {
7373
minSdk = 21
7474
}

amf/src/commonMain/kotlin/io/github/thibaultbee/krtmp/amf/Amf.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,19 +163,19 @@ data class AmfConfiguration(
163163
/**
164164
* Specifies whether default values of Kotlin properties should be encoded.
165165
*/
166-
val encodeDefaults: Boolean = false,
166+
val encodeDefaults: Boolean = true,
167167

168168
/**
169169
* Specifies whether encounters of unknown properties in the input AMF
170170
* should be ignored instead of throwing [SerializationException].
171171
* `false` by default.
172172
*/
173-
val ignoreUnknownKeys: Boolean = false,
173+
val ignoreUnknownKeys: Boolean = true,
174174

175175
/**
176176
* Specifies whether nulls should be encoded as explicit AMF nulls.
177177
*
178178
* When this flag is disabled properties with `null` values without default are not encoded.
179179
*/
180-
val explicitNulls: Boolean = true
180+
val explicitNulls: Boolean = false
181181
)

amf/src/commonMain/kotlin/io/github/thibaultbee/krtmp/amf/elements/AmfElementFactory.kt

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,29 @@
1515
*/
1616
package io.github.thibaultbee.krtmp.amf.elements
1717

18-
import io.github.thibaultbee.krtmp.amf.elements.containers.AmfContainer
19-
import io.github.thibaultbee.krtmp.amf.elements.containers.AmfEcmaArray
20-
import io.github.thibaultbee.krtmp.amf.elements.containers.AmfObject
21-
import io.github.thibaultbee.krtmp.amf.elements.containers.AmfStrictArray
18+
import io.github.thibaultbee.krtmp.amf.elements.containers.amfContainerOf
19+
import io.github.thibaultbee.krtmp.amf.elements.containers.amfEcmaArrayOf
20+
import io.github.thibaultbee.krtmp.amf.elements.containers.amfObjectOf
21+
import io.github.thibaultbee.krtmp.amf.elements.containers.amfStrictArrayOf
2222
import io.github.thibaultbee.krtmp.amf.elements.primitives.AmfBoolean
23-
import io.github.thibaultbee.krtmp.amf.elements.primitives.AmfDate
2423
import io.github.thibaultbee.krtmp.amf.elements.primitives.AmfNull
2524
import io.github.thibaultbee.krtmp.amf.elements.primitives.AmfNumber
2625
import io.github.thibaultbee.krtmp.amf.elements.primitives.AmfString
27-
import kotlinx.datetime.Instant
26+
import io.github.thibaultbee.krtmp.amf.elements.primitives.amfDateOf
2827
import kotlinx.io.IOException
28+
import kotlin.time.ExperimentalTime
29+
import kotlin.time.Instant
2930

3031
object AmfElementFactory {
31-
fun buildContainer(value: List<Any?>) = AmfContainer(value)
32+
fun buildContainer(value: List<Any?>) = amfContainerOf(value)
3233

33-
fun buildEcmaArray(value: Map<String, Any?>) = AmfEcmaArray(value)
34+
fun buildEcmaArray(value: Map<String, Any?>) = amfEcmaArrayOf(value)
3435

35-
fun buildObject(value: Map<String, Any?>) = AmfObject(value)
36+
fun buildObject(value: Map<String, Any?>) = amfObjectOf(value)
3637

37-
fun buildStrictArray(value: List<Any?>) = AmfStrictArray(value)
38+
fun buildStrictArray(value: List<Any?>) = amfStrictArrayOf(value)
3839

40+
@OptIn(ExperimentalTime::class)
3941
fun build(value: Any?): AmfElement {
4042
if (value == null) {
4143
return AmfNull()
@@ -45,15 +47,16 @@ object AmfElementFactory {
4547
is Boolean -> AmfBoolean(value)
4648
is Double -> AmfNumber(value)
4749
is String -> AmfString(value)
48-
is List<*> -> AmfStrictArray(value)
49-
is Instant -> AmfDate(value)
50+
is List<*> -> amfStrictArrayOf(value)
51+
is Instant -> amfDateOf(value)
5052
is Map<*, *> -> {
5153
if (value.keys.any { it !is String }) {
5254
throw IOException("AMF ECMA array keys must be String. At least one is not a String in ${value.keys}")
5355
}
5456
@Suppress("UNCHECKED_CAST")
55-
AmfEcmaArray(value as Map<String, Any?>)
57+
amfEcmaArrayOf(value as Map<String, Any?>)
5658
}
59+
5760
else -> throw IOException("Can't build an AmfParameter for ${value::class.simpleName}: $value")
5861
}
5962
}

amf/src/commonMain/kotlin/io/github/thibaultbee/krtmp/amf/elements/AmfElementReader.kt

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
*/
1616
package io.github.thibaultbee.krtmp.amf.elements
1717

18-
import io.github.thibaultbee.krtmp.amf.elements.containers.Amf0Container
19-
import io.github.thibaultbee.krtmp.amf.elements.containers.Amf0EcmaArray
20-
import io.github.thibaultbee.krtmp.amf.elements.containers.Amf0Object
21-
import io.github.thibaultbee.krtmp.amf.elements.containers.Amf0StrictArray
22-
import io.github.thibaultbee.krtmp.amf.elements.primitives.Amf0Boolean
23-
import io.github.thibaultbee.krtmp.amf.elements.primitives.Amf0Date
24-
import io.github.thibaultbee.krtmp.amf.elements.primitives.Amf0Null
25-
import io.github.thibaultbee.krtmp.amf.elements.primitives.Amf0Number
26-
import io.github.thibaultbee.krtmp.amf.elements.primitives.Amf0String
18+
import io.github.thibaultbee.krtmp.amf.elements.containers.amf0ContainerFrom
19+
import io.github.thibaultbee.krtmp.amf.elements.containers.amf0EcmaArrayFrom
20+
import io.github.thibaultbee.krtmp.amf.elements.containers.amf0ObjectFrom
21+
import io.github.thibaultbee.krtmp.amf.elements.containers.amf0StrictArrayFrom
22+
import io.github.thibaultbee.krtmp.amf.elements.primitives.amf0BooleanFrom
23+
import io.github.thibaultbee.krtmp.amf.elements.primitives.amf0DateFrom
24+
import io.github.thibaultbee.krtmp.amf.elements.primitives.amf0NullFrom
25+
import io.github.thibaultbee.krtmp.amf.elements.primitives.amf0NumberFrom
26+
import io.github.thibaultbee.krtmp.amf.elements.primitives.amf0StringFrom
2727
import kotlinx.io.IOException
2828
import kotlinx.io.Source
2929

@@ -41,25 +41,25 @@ interface AmfElementReader {
4141

4242
object Amf0ElementReader : AmfElementReader {
4343
override fun readContainer(numOfElements: Int, source: Source) =
44-
Amf0Container(numOfElements, source)
44+
amf0ContainerFrom(numOfElements, source)
4545

46-
override fun readEcmaArray(source: Source) = Amf0EcmaArray(source)
46+
override fun readEcmaArray(source: Source) = amf0EcmaArrayFrom(source)
4747

48-
override fun readObject(source: Source) = Amf0Object(source)
48+
override fun readObject(source: Source) = amf0ObjectFrom(source)
4949

50-
override fun buildStrictArray(source: Source) = Amf0StrictArray(source)
50+
override fun buildStrictArray(source: Source) = amf0StrictArrayFrom(source)
5151

5252
override fun read(source: Source): AmfElement {
5353
return when (val type = source.peek().readByte()) {
54-
Amf0Type.NUMBER.value -> Amf0Number(source)
55-
Amf0Type.BOOLEAN.value -> Amf0Boolean(source)
56-
Amf0Type.STRING.value -> Amf0String(source)
57-
Amf0Type.LONG_STRING.value -> Amf0String(source)
58-
Amf0Type.OBJECT.value -> Amf0Object(source)
59-
Amf0Type.NULL.value -> Amf0Null(source)
60-
Amf0Type.ECMA_ARRAY.value -> Amf0EcmaArray(source)
61-
Amf0Type.STRICT_ARRAY.value -> Amf0StrictArray(source)
62-
Amf0Type.DATE.value -> Amf0Date(source)
54+
Amf0Type.NUMBER.value -> amf0NumberFrom(source)
55+
Amf0Type.BOOLEAN.value -> amf0BooleanFrom(source)
56+
Amf0Type.STRING.value -> amf0StringFrom(source)
57+
Amf0Type.LONG_STRING.value -> amf0StringFrom(source)
58+
Amf0Type.OBJECT.value -> amf0ObjectFrom(source)
59+
Amf0Type.NULL.value -> amf0NullFrom(source)
60+
Amf0Type.ECMA_ARRAY.value -> amf0EcmaArrayFrom(source)
61+
Amf0Type.STRICT_ARRAY.value -> amf0StrictArrayFrom(source)
62+
Amf0Type.DATE.value -> amf0DateFrom(source)
6363
else -> throw IOException("Invalid AMF0 type: $type")
6464
}
6565
}

0 commit comments

Comments
 (0)