diff --git a/build.gradle b/build.gradle index 3edca3f..0e9ae5d 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { versionName = "${properties['VERSION_NAME']}" // StreamPack - streamPackVersion = "2.6.1" + streamPackVersion = "3.0.0" } } plugins { diff --git a/livestream/build.gradle b/livestream/build.gradle index 39b6ed8..0655ca1 100644 --- a/livestream/build.gradle +++ b/livestream/build.gradle @@ -56,9 +56,9 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.appcompat:appcompat:1.6.1' - implementation "io.github.thibaultbee:streampack:$streamPackVersion" - implementation "io.github.thibaultbee:streampack-extension-rtmp:$streamPackVersion" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3' + 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" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' diff --git a/livestream/src/main/java/video/api/livestream/ApiVideoLiveStream.kt b/livestream/src/main/java/video/api/livestream/ApiVideoLiveStream.kt index 289ed4e..f209c9b 100644 --- a/livestream/src/main/java/video/api/livestream/ApiVideoLiveStream.kt +++ b/livestream/src/main/java/video/api/livestream/ApiVideoLiveStream.kt @@ -4,11 +4,13 @@ import android.Manifest import android.content.Context import android.util.Log import androidx.annotation.RequiresPermission -import io.github.thibaultbee.streampack.error.StreamPackError -import io.github.thibaultbee.streampack.ext.rtmp.streamers.CameraRtmpLiveStreamer -import io.github.thibaultbee.streampack.listeners.OnConnectionListener -import io.github.thibaultbee.streampack.listeners.OnErrorListener -import io.github.thibaultbee.streampack.utils.* +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 kotlinx.coroutines.* import video.api.livestream.enums.CameraFacingDirection import video.api.livestream.interfaces.IConnectionListener @@ -69,8 +71,6 @@ constructor( private const val TAG = "ApiVideoLiveStream" } - private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) - /** * Sets/gets audio configuration once you have created the a [ApiVideoLiveStream] instance. */ @@ -136,33 +136,30 @@ constructor( } } - private val internalConnectionListener = object : OnConnectionListener { - override fun onFailed(message: String) { - connectionListener.onConnectionFailed(message) - } - - override fun onLost(message: String) { - connectionListener.onDisconnect() + private val internalListener = object : ICallbackStreamer.Listener { + override fun onOpenFailed(t: Throwable) { + connectionListener.onConnectionFailed(t.message ?: "Unknown error") } - override fun onSuccess() { - connectionListener.onConnectionSuccess() + override fun onIsOpenChanged(isOpen: Boolean) { + if (isOpen) { + connectionListener.onConnectionSuccess() + } else { + connectionListener.onDisconnect() + } } - } - private val errorListener = object : OnErrorListener { - override fun onError(error: StreamPackError) { - _isStreaming = false - Log.e(TAG, "An error happened", error) + override fun onError(throwable: Throwable) { + Log.e(TAG, "An error happened", throwable) } } - private val streamer = CameraRtmpLiveStreamer( + private val streamer = DefaultCameraCallbackStreamer( context = context, - enableAudio = true, - initialOnErrorListener = errorListener, - initialOnConnectionListener = internalConnectionListener - ) + enableMicrophone = true + ).apply { + addListener(internalListener) + } /** * Get/set video bitrate during a streaming in bps. @@ -174,14 +171,14 @@ constructor( * * @return video bitrate in bps */ - get() = streamer.settings.video.bitrate + get() = streamer.videoEncoder?.bitrate ?: 0 /** * Set video bitrate. * * @param value video bitrate in bps */ set(value) { - streamer.settings.video.bitrate = value + streamer.videoEncoder?.bitrate = value } /** @@ -250,17 +247,16 @@ constructor( * * @return [Boolean.true] if audio is muted, [Boolean.false] if audio is not muted. */ - get() = streamer.settings.audio.isMuted + get() = streamer.audioSource?.isMuted ?: true /** * Set mute value. * * @param value [Boolean.true] to mute audio, [Boolean.false] to unmute audio. */ set(value) { - streamer.settings.audio.isMuted = value + streamer.audioSource?.isMuted = value } - /** * Set/get the zoom ratio. */ @@ -270,14 +266,14 @@ constructor( * * @return the zoom ratio */ - get() = streamer.settings.camera.zoom.zoomRatio + get() = streamer.videoSource.settings.zoom.zoomRatio /** * Set the zoom ratio. * * @param value the zoom ratio */ set(value) { - streamer.settings.camera.zoom.zoomRatio = value + streamer.videoSource.settings.zoom.zoomRatio = value } /** @@ -297,22 +293,15 @@ constructor( require(audioConfig != null) { "Audio config must be set" } require(videoConfig != null) { "Video config must be set" } - scope.launch { - withContext(context = Dispatchers.IO) { - try { - streamer.connect(url.addTrailingSlashIfNeeded() + streamKey) - try { - streamer.startStream() - _isStreaming = true - } catch (e: Exception) { - streamer.disconnect() - connectionListener.onConnectionFailed("$e") - throw e - } - } catch (e: Exception) { - Log.e(TAG, "Failed to start stream", e) - } - } + val descriptor = UriMediaDescriptor(url.addTrailingSlashIfNeeded() + streamKey) + require(descriptor.type.sinkType == MediaSinkType.RTMP) { "URL must be RTMP" } + + try { + streamer.startStream(url) + } catch (e: Exception) { + streamer.close() + connectionListener.onConnectionFailed("$e") + throw e } } @@ -322,25 +311,10 @@ constructor( * @see [startStreaming] */ fun stopStreaming() { - val isConnected = streamer.isConnected - scope.launch { - withContext(context = Dispatchers.IO) { - streamer.stopStream() - streamer.disconnect() - if (isConnected) { - connectionListener.onDisconnect() - } - _isStreaming = false - } - } + streamer.stopStream() + streamer.close() } - - /** - * Hack for private setter of [isStreaming]. - */ - private var _isStreaming: Boolean = false - /** * Check the streaming state. * @@ -349,7 +323,7 @@ constructor( * @see [stopStreaming] */ val isStreaming: Boolean - get() = _isStreaming + get() = streamer.isStreaming /** * Starts camera preview of [cameraPosition]. @@ -391,6 +365,5 @@ constructor( */ fun release() { streamer.release() - scope.cancel() } } \ No newline at end of file diff --git a/livestream/src/main/java/video/api/livestream/ConfigurationHelper.kt b/livestream/src/main/java/video/api/livestream/ConfigurationHelper.kt index 9cef249..5837a5c 100644 --- a/livestream/src/main/java/video/api/livestream/ConfigurationHelper.kt +++ b/livestream/src/main/java/video/api/livestream/ConfigurationHelper.kt @@ -2,27 +2,30 @@ package video.api.livestream import android.content.Context import android.media.MediaFormat -import io.github.thibaultbee.streampack.streamers.helpers.AudioStreamerConfigurationHelper -import io.github.thibaultbee.streampack.streamers.helpers.CameraStreamerConfigurationHelper -import io.github.thibaultbee.streampack.streamers.helpers.VideoCameraStreamerConfigurationHelper -import io.github.thibaultbee.streampack.utils.backCameraList -import io.github.thibaultbee.streampack.utils.cameraList -import io.github.thibaultbee.streampack.utils.frontCameraList +import io.github.thibaultbee.streampack.core.internal.endpoints.composites.CompositeEndpoint +import io.github.thibaultbee.streampack.core.internal.endpoints.composites.muxers.flv.FlvMuxerInfo +import io.github.thibaultbee.streampack.core.streamers.infos.AudioStreamerConfigurationInfo +import io.github.thibaultbee.streampack.core.streamers.infos.CameraStreamerConfigurationInfo +import io.github.thibaultbee.streampack.core.streamers.infos.VideoCameraStreamerConfigurationInfo +import io.github.thibaultbee.streampack.core.utils.extensions.backCameras +import io.github.thibaultbee.streampack.core.utils.extensions.cameras +import io.github.thibaultbee.streampack.core.utils.extensions.frontCameras object ConfigurationHelper { - private val helper = CameraStreamerConfigurationHelper.flvHelper + private val helper = + CameraStreamerConfigurationInfo(CompositeEndpoint.EndpointInfo(FlvMuxerInfo)) val audio = AudioConfigurationHelper(helper.audio) val video = VideoStreamerConfigurationHelper(helper.video) } -class AudioConfigurationHelper(private val audioHelper: AudioStreamerConfigurationHelper) { +class AudioConfigurationHelper(private val audioInfo: AudioStreamerConfigurationInfo) { /** * Get supported bitrate range. * * @return bitrate range */ fun getSupportedBitrates() = - audioHelper.getSupportedBitrates(MediaFormat.MIMETYPE_AUDIO_AAC) + audioInfo.getSupportedBitrates(MediaFormat.MIMETYPE_AUDIO_AAC) /** * Get maximum supported number of channel by encoder. @@ -30,7 +33,7 @@ class AudioConfigurationHelper(private val audioHelper: AudioStreamerConfigurati * @return maximum number of channel supported by the encoder */ fun getSupportedInputChannelRange() = - audioHelper.getSupportedInputChannelRange(MediaFormat.MIMETYPE_AUDIO_AAC) + audioInfo.getSupportedInputChannelRange(MediaFormat.MIMETYPE_AUDIO_AAC) /** * Get audio supported sample rates. @@ -38,10 +41,10 @@ class AudioConfigurationHelper(private val audioHelper: AudioStreamerConfigurati * @return sample rates list in Hz. */ fun getSupportedSampleRates() = - audioHelper.getSupportedSampleRates(MediaFormat.MIMETYPE_AUDIO_AAC) + audioInfo.getSupportedSampleRates(MediaFormat.MIMETYPE_AUDIO_AAC) } -class VideoStreamerConfigurationHelper(private val videoHelper: VideoCameraStreamerConfigurationHelper) { +class VideoStreamerConfigurationHelper(private val videoInfo: VideoCameraStreamerConfigurationInfo) { /** * Get supported bitrate range. @@ -49,7 +52,7 @@ class VideoStreamerConfigurationHelper(private val videoHelper: VideoCameraStrea * @return bitrate range */ fun getSupportedBitrates() = - videoHelper.getSupportedBitrates(MediaFormat.MIMETYPE_VIDEO_AVC) + videoInfo.getSupportedBitrates(MediaFormat.MIMETYPE_VIDEO_AVC) /** * Get encoder supported resolutions range. @@ -57,7 +60,7 @@ class VideoStreamerConfigurationHelper(private val videoHelper: VideoCameraStrea * @return pair that contains supported width ([Pair.first]) and supported height ([Pair.second]). */ fun getSupportedResolutions() = - videoHelper.getSupportedResolutions(MediaFormat.MIMETYPE_VIDEO_AVC) + videoInfo.getSupportedResolutions(MediaFormat.MIMETYPE_VIDEO_AVC) /** * Get camera supported resolutions that are also supported by the encoder. @@ -66,7 +69,7 @@ class VideoStreamerConfigurationHelper(private val videoHelper: VideoCameraStrea * @return list of resolutions */ fun getCameraSupportedResolutions(context: Context) = - videoHelper.getSupportedResolutions(context, MediaFormat.MIMETYPE_VIDEO_AVC) + videoInfo.getSupportedResolutions(context, MediaFormat.MIMETYPE_VIDEO_AVC) /** @@ -79,7 +82,7 @@ class VideoStreamerConfigurationHelper(private val videoHelper: VideoCameraStrea fun getSupportedFrameRates( context: Context, cameraId: String - ) = videoHelper.getSupportedFramerates(context, MediaFormat.MIMETYPE_VIDEO_AVC, cameraId) + ) = videoInfo.getSupportedFramerates(context, MediaFormat.MIMETYPE_VIDEO_AVC, cameraId) /** * Get cameras list @@ -87,7 +90,7 @@ class VideoStreamerConfigurationHelper(private val videoHelper: VideoCameraStrea * @param context application context * @return list of camera */ - fun getCamerasList(context: Context) = context.cameraList + fun getCamerasList(context: Context) = context.cameras /** * Get back cameras list @@ -95,7 +98,7 @@ class VideoStreamerConfigurationHelper(private val videoHelper: VideoCameraStrea * @param context application context * @return list of back camera */ - fun getBackCamerasList(context: Context) = context.backCameraList + fun getBackCamerasList(context: Context) = context.backCameras /** * Get front cameras list @@ -103,5 +106,5 @@ class VideoStreamerConfigurationHelper(private val videoHelper: VideoCameraStrea * @param context application context * @return list of front camera */ - fun getFrontCamerasList(context: Context) = context.frontCameraList + fun getFrontCamerasList(context: Context) = context.frontCameras } diff --git a/livestream/src/main/java/video/api/livestream/enums/CameraFacingDirection.kt b/livestream/src/main/java/video/api/livestream/enums/CameraFacingDirection.kt index e7164d2..97fe1c2 100644 --- a/livestream/src/main/java/video/api/livestream/enums/CameraFacingDirection.kt +++ b/livestream/src/main/java/video/api/livestream/enums/CameraFacingDirection.kt @@ -1,10 +1,10 @@ package video.api.livestream.enums import android.content.Context -import io.github.thibaultbee.streampack.utils.backCameraList -import io.github.thibaultbee.streampack.utils.frontCameraList -import io.github.thibaultbee.streampack.utils.isBackCamera -import io.github.thibaultbee.streampack.utils.isFrontCamera +import io.github.thibaultbee.streampack.core.utils.extensions.backCameras +import io.github.thibaultbee.streampack.core.utils.extensions.frontCameras +import io.github.thibaultbee.streampack.core.utils.extensions.isBackCamera +import io.github.thibaultbee.streampack.core.utils.extensions.isFrontCamera /** * Represents camera facing direction. @@ -28,9 +28,9 @@ enum class CameraFacingDirection { */ fun toCameraId(context: Context): String { val cameraList = if (this == BACK) { - context.backCameraList + context.backCameras } else { - context.frontCameraList + context.frontCameras } return cameraList[0] } diff --git a/livestream/src/main/java/video/api/livestream/models/AudioConfig.kt b/livestream/src/main/java/video/api/livestream/models/AudioConfig.kt index 6917df3..538c701 100644 --- a/livestream/src/main/java/video/api/livestream/models/AudioConfig.kt +++ b/livestream/src/main/java/video/api/livestream/models/AudioConfig.kt @@ -34,8 +34,8 @@ data class AudioConfig( */ val noiseSuppressor: Boolean = true ) { - internal fun toSdkConfig(): io.github.thibaultbee.streampack.data.AudioConfig { - return io.github.thibaultbee.streampack.data.AudioConfig( + internal fun toSdkConfig(): io.github.thibaultbee.streampack.core.data.AudioConfig { + return io.github.thibaultbee.streampack.core.data.AudioConfig( startBitrate = bitrate, sampleRate = sampleRate, channelConfig = if (stereo) AudioFormat.CHANNEL_IN_STEREO else AudioFormat.CHANNEL_IN_MONO, @@ -45,7 +45,7 @@ data class AudioConfig( } companion object { - internal fun fromSdkConfig(config: io.github.thibaultbee.streampack.data.AudioConfig): AudioConfig { + internal fun fromSdkConfig(config: io.github.thibaultbee.streampack.core.data.AudioConfig): AudioConfig { return AudioConfig( bitrate = config.startBitrate, sampleRate = config.sampleRate, diff --git a/livestream/src/main/java/video/api/livestream/models/VideoConfig.kt b/livestream/src/main/java/video/api/livestream/models/VideoConfig.kt index c7a4dae..da9b1fb 100644 --- a/livestream/src/main/java/video/api/livestream/models/VideoConfig.kt +++ b/livestream/src/main/java/video/api/livestream/models/VideoConfig.kt @@ -35,8 +35,8 @@ class VideoConfig( gopDuration: Float ) : this(resolution.size, bitrate, fps, gopDuration) - internal fun toSdkConfig(): io.github.thibaultbee.streampack.data.VideoConfig { - return io.github.thibaultbee.streampack.data.VideoConfig( + internal fun toSdkConfig(): io.github.thibaultbee.streampack.core.data.VideoConfig { + return io.github.thibaultbee.streampack.core.data.VideoConfig( startBitrate = bitrate, resolution = resolution, fps = fps, @@ -45,7 +45,7 @@ class VideoConfig( } companion object { - internal fun fromSdkConfig(config: io.github.thibaultbee.streampack.data.VideoConfig): VideoConfig { + internal fun fromSdkConfig(config: io.github.thibaultbee.streampack.core.data.VideoConfig): VideoConfig { return VideoConfig( bitrate = config.startBitrate, resolution = config.resolution, diff --git a/livestream/src/main/java/video/api/livestream/views/ApiVideoView.kt b/livestream/src/main/java/video/api/livestream/views/ApiVideoView.kt index fc08a0d..fa8cb73 100644 --- a/livestream/src/main/java/video/api/livestream/views/ApiVideoView.kt +++ b/livestream/src/main/java/video/api/livestream/views/ApiVideoView.kt @@ -4,8 +4,8 @@ import android.content.Context import android.util.AttributeSet import android.view.ViewGroup import android.widget.FrameLayout -import io.github.thibaultbee.streampack.streamers.interfaces.ICameraStreamer -import io.github.thibaultbee.streampack.views.PreviewView +import io.github.thibaultbee.streampack.core.streamers.interfaces.ICameraStreamer +import io.github.thibaultbee.streampack.ui.views.PreviewView /** * View where to display camera preview.