diff --git a/README.md b/README.md index 4d1cedc..1bb3f80 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@   [![badge](https://img.shields.io/github/stars/apivideo/api.video-android-live-stream?style=social)](https://github.com/apivideo/api.video-android-live-stream)   [![badge](https://img.shields.io/discourse/topics?server=https%3A%2F%2Fcommunity.api.video)](https://community.api.video) ![](https://github.com/apivideo/.github/blob/main/assets/apivideo_banner.png) -

Android RTMP live stream client

+

Android RTMP and SRT live stream client

[api.video](https://api.video) is the video infrastructure for product builders. Lightning fast video APIs for integrating, scaling, and managing on-demand & low latency live streaming features in @@ -154,8 +154,8 @@ You can check device supported configurations by using the helper: `Helper` We are using external library -| Plugin | README | -| ------ | ------ | +| Plugin | README | +|---------------------------------------------------------|------------------------------------------------------------------------------| | [StreamPack](https://github.com/ThibaultBee/StreamPack) | [README.md](https://github.com/ThibaultBee/StreamPack/blob/master/README.md) | ## Sample application diff --git a/example/src/main/java/video/api/livestream/example/ui/utils/Configuration.kt b/example/src/main/java/video/api/livestream/example/ui/utils/Configuration.kt index 5accac4..9f72a7d 100644 --- a/example/src/main/java/video/api/livestream/example/ui/utils/Configuration.kt +++ b/example/src/main/java/video/api/livestream/example/ui/utils/Configuration.kt @@ -76,11 +76,11 @@ class Configuration(context: Context) { class Endpoint(private val sharedPref: SharedPreferences, private val resources: Resources) { var url: String = "" get() = sharedPref.getString( - resources.getString(R.string.rtmp_endpoint_url_key), + resources.getString(R.string.server_url_key), field )!! var streamKey: String = "" - get() = sharedPref.getString(resources.getString(R.string.rtmp_stream_key_key), field)!! + get() = sharedPref.getString(resources.getString(R.string.stream_key_key), field)!! } } \ No newline at end of file diff --git a/example/src/main/res/values/strings.xml b/example/src/main/res/values/strings.xml index 451b16e..8234e2d 100644 --- a/example/src/main/res/values/strings.xml +++ b/example/src/main/res/values/strings.xml @@ -31,11 +31,11 @@ audio_enable_echo_canceler_key audio_enable_noise_suppressor_key Enable noise suppressor - rtmp://broadcast.api.video/s/ - rtmp_address_key - RTMP endpoint - rtmp_stream_key_key - Stream key + rtmp://broadcast.api.video/s/ + server_url_key + RTMP or SRT server URL + stream_key_key + RTMP Stream key or SRT stream id Permissions Permissions not granted: leaving! \ No newline at end of file diff --git a/example/src/main/res/xml/preferences.xml b/example/src/main/res/xml/preferences.xml index 6538188..9700112 100644 --- a/example/src/main/res/xml/preferences.xml +++ b/example/src/main/res/xml/preferences.xml @@ -62,15 +62,15 @@ \ No newline at end of file diff --git a/livestream/build.gradle b/livestream/build.gradle index fd3664c..c4b4aa8 100644 --- a/livestream/build.gradle +++ b/livestream/build.gradle @@ -59,6 +59,7 @@ dependencies { implementation "io.github.thibaultbee.streampack:streampack-core:$streamPackVersion" implementation "io.github.thibaultbee.streampack:streampack-ui:$streamPackVersion" implementation "io.github.thibaultbee.streampack:streampack-extension-rtmp:$streamPackVersion" + implementation "io.github.thibaultbee.streampack:streampack-extension-srt:$streamPackVersion" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.2.1' diff --git a/livestream/src/main/java/video/api/livestream/ApiVideoLiveStream.kt b/livestream/src/main/java/video/api/livestream/ApiVideoLiveStream.kt index 346846e..40e2993 100644 --- a/livestream/src/main/java/video/api/livestream/ApiVideoLiveStream.kt +++ b/livestream/src/main/java/video/api/livestream/ApiVideoLiveStream.kt @@ -2,15 +2,17 @@ package video.api.livestream import android.Manifest import android.content.Context +import android.net.Uri import android.util.Log import androidx.annotation.RequiresPermission +import io.github.thibaultbee.streampack.core.data.mediadescriptor.MediaDescriptor import io.github.thibaultbee.streampack.core.data.mediadescriptor.UriMediaDescriptor import io.github.thibaultbee.streampack.core.internal.endpoints.MediaSinkType import io.github.thibaultbee.streampack.core.streamers.callbacks.DefaultCameraCallbackStreamer import io.github.thibaultbee.streampack.core.streamers.interfaces.ICallbackStreamer -import io.github.thibaultbee.streampack.core.streamers.interfaces.startStream import io.github.thibaultbee.streampack.core.utils.extensions.isBackCamera import io.github.thibaultbee.streampack.core.utils.extensions.isFrontCamera +import io.github.thibaultbee.streampack.ext.srt.data.mediadescriptor.SrtMediaDescriptor import kotlinx.coroutines.* import video.api.livestream.enums.CameraFacingDirection import video.api.livestream.interfaces.IConnectionListener @@ -287,31 +289,96 @@ constructor( } /** - * Start a new RTMP stream. + * Start a new RTMP or SRT stream. * - * @param streamKey RTMP stream key. For security purpose, you must not expose it. - * @param url RTMP Url. Default value (not set or null) is api.video RTMP broadcast url. + * Example of RTMP server url: + * ``` + * rtmp://broadcast.api.video/s + * ``` + * + * Example of SRT server url: + * ``` + * srt://broadcast.api.video:6200 + * ``` + * Other query parameters will be ignored. + * + * @param streamKey The RTMP stream key or SRT stream id. For security purpose, you must not expose it. + * @param url The RTMP or SRT server Url. Default value (not set or null) is api.video RTMP broadcast url. * @see [stopStreaming] */ fun startStreaming( streamKey: String, - url: String = context.getString(R.string.default_rtmp_url), + url: String = context.getString(R.string.default_server_url), ) { require(!isStreaming) { "Stream is already running" } require(streamKey.isNotEmpty()) { "Stream key must not be empty" } require(url.isNotEmpty()) { "Url must not be empty" } - require(audioConfig != null) { "Audio config must be set" } - require(videoConfig != null) { "Video config must be set" } + requireNotNull(audioConfig) { "Audio config must be set" } + requireNotNull(videoConfig) { "Video config must be set" } + + val descriptor = if (url.startsWith("srt://")) { + val uri = Uri.parse(url) + SrtMediaDescriptor( + host = requireNotNull(uri.host), + port = uri.port, + streamId = streamKey + ) + } else { + UriMediaDescriptor(url.addTrailingSlashIfNeeded() + streamKey) + } - val descriptor = UriMediaDescriptor(url.addTrailingSlashIfNeeded() + streamKey) - require(descriptor.type.sinkType == MediaSinkType.RTMP) { "URL must be RTMP" } + startStreaming(descriptor) + } + + /** + * Start a new RTMP or SRT stream. + * + * Example of RTMP url: + * ``` + * rtmp://broadcast.api.video/s/{streamKey} + * ``` + * + * Example of SRT url: + * ``` + * srt://broadcast.api.video:6200?streamid={streamKey} + * ``` + * + * Get the stream key from the api.video dashboard or through the API. + * + * @param url The RTMP or SRT server Url with stream key (RTMP) or stream id (SRT). + * @see [stopStreaming] + */ + fun startStreaming( + url: String + ) { + require(!isStreaming) { "Stream is already running" } + require(url.isNotEmpty()) { "Url must not be empty" } + requireNotNull(audioConfig) { "Audio config must be set" } + requireNotNull(videoConfig) { "Video config must be set" } + + startStreaming(UriMediaDescriptor(url)) + } + + /** + * Start a new RTMP or SRT stream. + * + * @param descriptor The media descriptor + * @see [stopStreaming] + */ + private fun startStreaming( + descriptor: MediaDescriptor + ) { + require(!isStreaming) { "Stream is already running" } + requireNotNull(audioConfig) { "Audio config must be set" } + requireNotNull(videoConfig) { "Video config must be set" } + + require((descriptor.type.sinkType == MediaSinkType.RTMP) || (descriptor.type.sinkType == MediaSinkType.SRT)) { "URL must be RTMP or SRT" } try { - streamer.startStream(url) - } catch (e: Exception) { - streamer.close() - connectionListener.onConnectionFailed("$e") - throw e + streamer.startStream(descriptor) + } catch (t: Throwable) { + connectionListener.onConnectionFailed("$t") + throw t } } diff --git a/livestream/src/main/res/values/strings.xml b/livestream/src/main/res/values/strings.xml index b8b32ec..58e4d27 100644 --- a/livestream/src/main/res/values/strings.xml +++ b/livestream/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ - rtmp://broadcast.api.video/s/ + rtmp://broadcast.api.video/s/ \ No newline at end of file