@@ -23,31 +23,42 @@ import io.github.thibaultbee.krtmp.flv.tags.FLVTag
2323import io.github.thibaultbee.krtmp.flv.tags.script.OnMetadata
2424import io.github.thibaultbee.streampack.core.configuration.mediadescriptor.MediaDescriptor
2525import io.github.thibaultbee.streampack.core.elements.data.FrameWithCloseable
26- import io.github.thibaultbee.streampack.core.elements.data.useAndUnwrap
2726import io.github.thibaultbee.streampack.core.elements.encoders.CodecConfig
2827import io.github.thibaultbee.streampack.core.elements.endpoints.IEndpointInternal
2928import io.github.thibaultbee.streampack.core.elements.endpoints.MediaSinkType
3029import io.github.thibaultbee.streampack.core.elements.endpoints.composites.CompositeEndpoint.EndpointInfo
3130import io.github.thibaultbee.streampack.core.elements.endpoints.composites.sinks.ContentSink
31+ import io.github.thibaultbee.streampack.core.elements.utils.ChannelWithCloseableData
32+ import io.github.thibaultbee.streampack.core.elements.utils.useConsumeEach
3233import io.github.thibaultbee.streampack.core.logger.Logger
3334import io.github.thibaultbee.streampack.core.pipelines.IDispatcherProvider
3435import io.github.thibaultbee.streampack.ext.flv.elements.endpoints.composites.muxer.FlvMuxerInfo
35- import io.github.thibaultbee.streampack.ext.flv.elements.endpoints.composites.muxer.utils.FlvDataBuilder
36+ import io.github.thibaultbee.streampack.ext.flv.elements.endpoints.composites.muxer.utils.FlvTagBuilder
3637import kotlinx.coroutines.CoroutineDispatcher
38+ import kotlinx.coroutines.CoroutineScope
39+ import kotlinx.coroutines.channels.BufferOverflow
3740import kotlinx.coroutines.flow.MutableStateFlow
3841import kotlinx.coroutines.flow.StateFlow
3942import kotlinx.coroutines.flow.asStateFlow
43+ import kotlinx.coroutines.launch
4044import kotlinx.coroutines.sync.Mutex
4145import kotlinx.coroutines.sync.withLock
46+ import kotlinx.coroutines.withContext
4247
4348/* *
4449 * Writes FLV Data to a file or content.
4550 */
46- sealed class FlvEndpoint (private val coroutineDispatcher : CoroutineDispatcher ) : IEndpointInternal {
47- private val flvDataBuilder = FlvDataBuilder ()
48- private var flvMuxer: FLVMuxer ? = null
51+ sealed class FlvEndpoint (
52+ defaultDispatcher : CoroutineDispatcher ,
53+ private val ioDispatcher : CoroutineDispatcher
54+ ) : IEndpointInternal {
55+ private val coroutineScope = CoroutineScope (defaultDispatcher)
4956 private val mutex = Mutex ()
5057
58+ private val flvTagChannel = ChannelWithCloseableData <FLVTag >(20 , BufferOverflow .DROP_OLDEST )
59+ private val flvTagBuilder = FlvTagBuilder (flvTagChannel)
60+ private var flvMuxer: FLVMuxer ? = null
61+
5162 private var startUpTimestamp = INVALID_TIMESTAMP
5263 private val timestampMutex = Mutex ()
5364
@@ -63,9 +74,21 @@ sealed class FlvEndpoint(private val coroutineDispatcher: CoroutineDispatcher) :
6374
6475 override fun getInfo (type : MediaDescriptor .Type ) = info
6576
77+ init {
78+ coroutineScope.launch {
79+ flvTagChannel.useConsumeEach { flvTag ->
80+ try {
81+ write(flvTag)
82+ } catch (t: Throwable ) {
83+ Logger .e(TAG , " Error while writing FLV data: $t " )
84+ }
85+ }
86+ }
87+ }
88+
6689 private suspend fun safeMuxer (block : suspend (FLVMuxer ) -> Unit ) {
6790 val flvMuxer = requireNotNull(flvMuxer) { " Not opened" }
68- mutex.withLock { block(flvMuxer) }
91+ mutex.withLock { withContext(ioDispatcher) { block(flvMuxer) } }
6992 }
7093
7194 abstract suspend fun openImpl (descriptor : MediaDescriptor ): FLVMuxer
@@ -89,25 +112,28 @@ sealed class FlvEndpoint(private val coroutineDispatcher: CoroutineDispatcher) :
89112 startUpTimestamp
90113 }
91114
115+ private suspend fun write (flvTag : FLVTag ) {
116+ safeMuxer { flvMuxer ->
117+ flvMuxer.encode(flvTag)
118+ }
119+ // Close FLVTag data if needed
120+ (flvTag.data as AutoCloseable ? )?.close()
121+ }
122+
92123 override suspend fun write (
93124 closeableFrame : FrameWithCloseable , streamPid : Int
94125 ) {
95- closeableFrame.useAndUnwrap { frame ->
96- val startUpTimestamp = getStartUpTimestamp(frame.ptsInUs)
97- val ts = (frame.ptsInUs - startUpTimestamp) / 1000
98- flvDataBuilder.write(frame, streamPid).forEach {
99- safeMuxer { flvMuxer ->
100- flvMuxer.encode(FLVTag (ts.toInt(), it))
101- }
102- }
103- }
126+ val frame = closeableFrame.frame
127+ val startUpTimestamp = getStartUpTimestamp(frame.ptsInUs)
128+ val ts = (frame.ptsInUs - startUpTimestamp) / 1000
129+ flvTagBuilder.write(closeableFrame, ts.toInt(), streamPid)
104130 }
105131
106132 override fun addStreams (streamConfigs : List <CodecConfig >): Map <CodecConfig , Int > {
107133 require(streamConfigs.isNotEmpty()) { " At least one stream must be provided" }
108134 mutex.tryLock()
109135 return try {
110- flvDataBuilder .addStreams(streamConfigs)
136+ flvTagBuilder .addStreams(streamConfigs)
111137 } finally {
112138 mutex.unlock()
113139 }
@@ -116,27 +142,29 @@ sealed class FlvEndpoint(private val coroutineDispatcher: CoroutineDispatcher) :
116142 override fun addStream (streamConfig : CodecConfig ): Int {
117143 mutex.tryLock()
118144 return try {
119- flvDataBuilder .addStream(streamConfig)
145+ flvTagBuilder .addStream(streamConfig)
120146 } finally {
121147 mutex.unlock()
122148 }
123149 }
124150
125151 override suspend fun startStream () {
126152 safeMuxer { flvMuxer ->
127- flvMuxer.encodeFLVHeader(flvDataBuilder .hasAudio, flvDataBuilder .hasVideo)
128- flvMuxer.encode(0 , OnMetadata (flvDataBuilder .metadata))
153+ flvMuxer.encodeFLVHeader(flvTagBuilder .hasAudio, flvTagBuilder .hasVideo)
154+ flvMuxer.encode(0 , OnMetadata (flvTagBuilder .metadata))
129155 }
130156 }
131157
132158 override suspend fun stopStream () {
133159 mutex.withLock {
134160 try {
135- flvMuxer?.flush()
161+ withContext(ioDispatcher) {
162+ flvMuxer?.flush()
163+ }
136164 } catch (t: Throwable ) {
137165 Logger .w(TAG , " Error while flushing FLV muxer: $t " )
138166 } finally {
139- flvDataBuilder .clearStreams()
167+ flvTagBuilder .clearStreams()
140168 }
141169 }
142170 timestampMutex.withLock {
@@ -155,6 +183,10 @@ sealed class FlvEndpoint(private val coroutineDispatcher: CoroutineDispatcher) :
155183 }
156184 }
157185
186+ override fun release () {
187+ flvTagChannel.cancel()
188+ }
189+
158190 companion object {
159191 private const val TAG = " FlvEndpoint"
160192
@@ -166,8 +198,12 @@ sealed class FlvEndpoint(private val coroutineDispatcher: CoroutineDispatcher) :
166198/* *
167199 * Writes FLV Data to a content.
168200 */
169- class FlvContentEndpoint (private val context : Context , coroutineDispatcher : CoroutineDispatcher ) :
170- FlvEndpoint (coroutineDispatcher) {
201+ class FlvContentEndpoint (
202+ private val context : Context ,
203+ defaultDispatcher : CoroutineDispatcher ,
204+ ioDispatcher : CoroutineDispatcher
205+ ) :
206+ FlvEndpoint (defaultDispatcher, ioDispatcher) {
171207 override suspend fun openImpl (descriptor : MediaDescriptor ): FLVMuxer {
172208 require(descriptor.type.sinkType == MediaSinkType .CONTENT ) { " Descriptor type must be ${MediaSinkType .CONTENT } " }
173209 return FLVMuxer (ContentSink .openContent(context, descriptor.uri), AmfVersion .AMF0 )
@@ -181,14 +217,18 @@ class FlvContentEndpointFactory : IEndpointInternal.Factory {
181217 override fun create (
182218 context : Context ,
183219 dispatcherProvider : IDispatcherProvider
184- ): IEndpointInternal = FlvContentEndpoint (context, dispatcherProvider.io)
220+ ): IEndpointInternal =
221+ FlvContentEndpoint (context, dispatcherProvider.default, dispatcherProvider.io)
185222}
186223
187224
188225/* *
189226 * Writes FLV Data to a file.
190227 */
191- class FlvFileEndpoint (coroutineDispatcher : CoroutineDispatcher ) : FlvEndpoint(coroutineDispatcher) {
228+ class FlvFileEndpoint (
229+ defaultDispatcher : CoroutineDispatcher ,
230+ ioDispatcher : CoroutineDispatcher
231+ ) : FlvEndpoint(defaultDispatcher, ioDispatcher) {
192232 override suspend fun openImpl (descriptor : MediaDescriptor ): FLVMuxer {
193233 require(descriptor.type.sinkType == MediaSinkType .FILE ) { " Descriptor type must be ${MediaSinkType .FILE } " }
194234 return FLVMuxer (descriptor.uri.path!! , AmfVersion .AMF0 )
@@ -202,7 +242,7 @@ class FlvFileEndpointFactory : IEndpointInternal.Factory {
202242 override fun create (
203243 context : Context ,
204244 dispatcherProvider : IDispatcherProvider
205- ): IEndpointInternal = FlvFileEndpoint (dispatcherProvider.io)
245+ ): IEndpointInternal = FlvFileEndpoint (dispatcherProvider.default, dispatcherProvider. io)
206246}
207247
208248
0 commit comments