Skip to content

Commit a03dd2c

Browse files
committed
New RTMP module.
1 parent e9dcd38 commit a03dd2c

File tree

88 files changed

+460
-206
lines changed

Some content is hidden

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

88 files changed

+460
-206
lines changed

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ android {
3939

4040
dependencies {
4141
implementation(project(":haishinkit"))
42+
implementation(project(":rtmp"))
4243
implementation(project(":lottie"))
4344
implementation(project(":compose"))
4445
implementation(libs.accompanist.permissions)

app/src/main/java/com/haishinkit/app/MainActivity.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ import androidx.activity.compose.setContent
55
import androidx.activity.enableEdgeToEdge
66
import androidx.appcompat.app.AppCompatActivity
77
import androidx.compose.material3.MaterialTheme
8+
import com.haishinkit.rtmp.RtmpStreamSessionFactory
9+
import com.haishinkit.stream.StreamSession
810

911
class MainActivity : AppCompatActivity() {
1012
override fun onCreate(savedInstanceState: Bundle?) {
1113
super.onCreate(savedInstanceState)
1214
enableEdgeToEdge()
15+
StreamSession.Builder.registerFactory(RtmpStreamSessionFactory)
1316
setContent {
1417
MaterialTheme {
1518
MainScreen()

app/src/main/java/com/haishinkit/app/MediaProjectionService.kt

Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,17 @@ import android.os.Messenger
1919
import android.util.Log
2020
import androidx.annotation.RequiresApi
2121
import androidx.core.app.NotificationCompat
22+
import androidx.core.net.toUri
2223
import androidx.localbroadcastmanager.content.LocalBroadcastManager
23-
import com.haishinkit.event.Event
24-
import com.haishinkit.event.EventUtils
25-
import com.haishinkit.event.IEventListener
2624
import com.haishinkit.graphics.effect.LanczosVideoEffect
2725
import com.haishinkit.graphics.effect.VideoEffect
2826
import com.haishinkit.media.MediaMixer
2927
import com.haishinkit.media.source.MediaProjectionSource
30-
import com.haishinkit.rtmp.RtmpConnection
31-
import com.haishinkit.rtmp.RtmpStream
28+
import com.haishinkit.stream.StreamSession
3229

33-
class MediaProjectionService :
34-
Service(),
35-
IEventListener {
30+
class MediaProjectionService : Service() {
3631
private lateinit var mixer: MediaMixer
37-
private lateinit var stream: RtmpStream
38-
private lateinit var connection: RtmpConnection
32+
private lateinit var session: StreamSession
3933
private lateinit var videoSource: MediaProjectionSource
4034
private val localBroadcastManager: LocalBroadcastManager by lazy {
4135
LocalBroadcastManager.getInstance(this)
@@ -54,12 +48,12 @@ class MediaProjectionService :
5448
override fun handleMessage(msg: Message) {
5549
when (msg.what) {
5650
MSG_CONNECT -> {
57-
connection.connect(Preference.shared.rtmpURL)
51+
// session.connect(StreamSession.Method.INGEST)
5852
}
5953

6054
MSG_CLOSE -> {
6155
Log.i(TAG, "MSG_CLOSE")
62-
connection.close()
56+
// session.close()
6357
stopSelf()
6458
}
6559

@@ -116,33 +110,27 @@ class MediaProjectionService :
116110
getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
117111

118112
// mixer.attachAudio(AudioRecordSource(this))
119-
mixer.registerOutput(stream)
120-
121-
stream.listener = listener
113+
mixer.registerOutput(session.stream)
122114
data?.let {
123115
val source =
124116
MediaProjectionSource(
125117
this,
126118
mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, it),
127119
)
128120
// mixer.attachVideo(0, source)
129-
stream.videoSetting.width = source.video.videoSize.width shr 2
130-
stream.videoSetting.height = source.video.videoSize.height shr 2
121+
// stream.videoSetting.width = source.video.videoSize.width shr 2
122+
// stream.videoSetting.height = source.video.videoSize.height shr 2
131123
videoSource = source
132-
Log.e(TAG, "${stream.videoSetting.width}:${stream.videoSetting.height}")
133124
}
134-
135-
connection.connect(Preference.shared.rtmpURL)
136125
return START_NOT_STICKY
137126
}
138127

139128
override fun onCreate() {
140129
super.onCreate()
141130
mixer = MediaMixer(applicationContext)
142131
messenger = Messenger(handler)
143-
connection = RtmpConnection()
144-
connection.addEventListener(Event.RTMP_STATUS, this)
145-
stream = RtmpStream(applicationContext, connection)
132+
session =
133+
StreamSession.Builder(applicationContext, Preference.shared.rtmpURL.toUri()).build()
146134
localBroadcastManager.registerReceiver(
147135
isRunningReceiver,
148136
IntentFilter(ACTION_SERVICE_RUNNING),
@@ -152,20 +140,9 @@ class MediaProjectionService :
152140
override fun onDestroy() {
153141
super.onDestroy()
154142
mixer.dispose()
155-
connection.dispose()
156143
localBroadcastManager.unregisterReceiver(isRunningReceiver)
157144
}
158145

159-
override fun handleEvent(event: Event) {
160-
Log.i(TAG, event.toString())
161-
val data = EventUtils.toMap(event)
162-
val code = data["code"].toString()
163-
if (code == RtmpConnection.Code.CONNECT_SUCCESS.rawValue) {
164-
stream.publish(Preference.shared.streamName)
165-
}
166-
Log.i(TAG, code)
167-
}
168-
169146
companion object {
170147
private const val ACTION_SERVICE_RUNNING: String = "ACTION_SERVICE_RUNNING"
171148

@@ -176,8 +153,6 @@ class MediaProjectionService :
176153
const val NOTIFY_TITLE = "Recording."
177154

178155
var data: Intent? = null
179-
var listener: RtmpStream.Listener? = null
180-
181156
const val MSG_CONNECT = 0
182157
const val MSG_CLOSE = 1
183158
const val MSG_SET_VIDEO_EFFECT = 2

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ dependencies {
3333
dokka(project(":haishinkit:"))
3434
dokka(project(":compose:"))
3535
dokka(project(":lottie:"))
36+
dokka(project(":rtmp:"))
3637
}

haishinkit/src/main/java/com/haishinkit/media/BufferController.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import android.util.Log
55
import java.util.concurrent.LinkedBlockingDeque
66
import java.util.concurrent.atomic.AtomicInteger
77

8-
internal class BufferController<T>(
8+
class BufferController<T>(
99
suffix: String,
1010
) : Object() {
1111
interface Listener {

haishinkit/src/main/java/com/haishinkit/media/MediaLink.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import kotlin.coroutines.CoroutineContext
2121
/**
2222
* The MediaLink class can be used to synchronously play audio and video streams.
2323
*/
24-
internal class MediaLink(
24+
class MediaLink(
2525
val stream: Stream,
2626
) : Running,
2727
CoroutineScope,
@@ -54,7 +54,7 @@ internal class MediaLink(
5454
override val coroutineContext: CoroutineContext
5555
get() = Dispatchers.Default
5656

57-
internal var audioTrack: AudioTrack? = null
57+
var audioTrack: AudioTrack? = null
5858
set(value) {
5959
syncMode =
6060
if (value == null) {

haishinkit/src/main/java/com/haishinkit/metrics/FrameTracker.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import android.util.Log
44
import kotlin.math.pow
55
import kotlin.math.sqrt
66

7-
internal class FrameTracker {
7+
class FrameTracker {
88
private class Frame(
99
val type: String,
1010
) {

haishinkit/src/main/java/com/haishinkit/net/NetSocketImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import javax.net.ssl.SSLSocket
1818
import javax.net.ssl.SSLSocketFactory
1919
import kotlin.coroutines.CoroutineContext
2020

21-
internal class NetSocketImpl :
21+
class NetSocketImpl :
2222
NetSocket,
2323
CoroutineScope {
2424
override var timeout = DEFAULT_TIMEOUT

haishinkit/src/main/java/com/haishinkit/stream/Stream.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,14 @@ import com.haishinkit.media.MediaLink
1313
import com.haishinkit.media.MediaOutput
1414
import com.haishinkit.media.MediaOutputDataSource
1515
import com.haishinkit.media.MediaType
16-
import com.haishinkit.rtmp.RtmpStream
1716
import com.haishinkit.screen.Screen
1817
import com.haishinkit.screen.VideoScreenObject
1918
import java.lang.ref.WeakReference
2019
import java.nio.ByteBuffer
2120
import java.util.concurrent.atomic.AtomicBoolean
2221

2322
/**
24-
* The Stream class is the foundation of a [RtmpStream].
23+
* The Stream class is the foundation of a [com.haishinkit.rtmp.RtmpStream].
2524
*/
2625
@Suppress("UNUSED")
2726
abstract class Stream(
@@ -75,7 +74,7 @@ abstract class Stream(
7574
return field
7675
}
7776

78-
internal val mediaLink: MediaLink by lazy {
77+
val mediaLink: MediaLink by lazy {
7978
MediaLink(this)
8079
}
8180

@@ -155,7 +154,7 @@ abstract class Stream(
155154
videoCodec.surface = surface
156155
}
157156

158-
internal fun queueOutputBuffer(
157+
fun queueOutputBuffer(
159158
type: MediaType,
160159
index: Int,
161160
payload: ByteBuffer?,

haishinkit/src/main/java/com/haishinkit/stream/StreamSession.kt

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ package com.haishinkit.stream
22

33
import android.content.Context
44
import android.net.Uri
5-
import com.haishinkit.rtmp.RtmpConnection
6-
import com.haishinkit.rtmp.RtmpSession
75

86
/**
97
* [StreamSession] is a session for live streaming.
108
*
11-
* Streaming with [RtmpConnection] is difficult to use because it requires many idioms.
9+
* Streaming with [com.haishinkit.rtmp.RtmpConnection] is difficult to use because it requires many idioms.
1210
* This is a helper class specialized for a one-connection, one-stream setup.
1311
*/
1412
interface StreamSession {
@@ -24,7 +22,28 @@ interface StreamSession {
2422
private val context: Context,
2523
private val uri: Uri,
2624
) {
27-
fun build(): StreamSession = RtmpSession(context, uri)
25+
companion object {
26+
private var factoryMap = mutableMapOf<String, StreamSessionFactory>()
27+
28+
/**
29+
* Registers a stream session factory.
30+
*/
31+
fun registerFactory(factory: StreamSessionFactory) {
32+
factory.protocols.forEach {
33+
factoryMap[it] = factory
34+
}
35+
}
36+
}
37+
38+
fun build(): StreamSession {
39+
val scheme = uri.scheme
40+
for (factory in factoryMap) {
41+
if (factory.key == scheme) {
42+
return factory.value.create(context, uri)
43+
}
44+
}
45+
throw NullPointerException()
46+
}
2847
}
2948

3049
/**

0 commit comments

Comments
 (0)