fun createSession()

in app/src/main/java/com/amazonaws/ivs/basicbroadcast/viewModel/MixerViewModel.kt [49:175]


    fun createSession(logo: Bitmap, content: Uri) {
        session?.release()

        // Create a custom configuration at 720p60.
        val config = BroadcastConfiguration().apply {
            this.video.size = bigSize
            this.video.targetFramerate = 60

            // This slot will hold the camera and start in the bottom left corner of the stream. It will move during the transition.
            cameraSlot = BroadcastConfiguration.Mixer.Slot.with {
                it.size = smallSize
                it.aspect = BroadcastConfiguration.AspectMode.FIT
                it.position = smallPositionBottomLeft
                it.setzIndex(2)
                it.preferredVideoInput = Device.Descriptor.DeviceType.CAMERA
                it.name = CAMERA_SLOT_NAME

                return@with it
            }

            // This slot will hold custom content (in this example, a looping mp4 file) and take up the entire stream. It will move during the transition.
            contentSlot  = BroadcastConfiguration.Mixer.Slot.with {
                it.size = bigSize
                it.position = bigPosition
                it.setzIndex(1)
                it.name = CONTENT_SLOT_NAME

                return@with it
            }

            // This slot will be a logo-based watermark and sit in the bottom right corner of the stream. It will not move around.
            logoSlot  = BroadcastConfiguration.Mixer.Slot.with {
                it.size = BroadcastConfiguration.Vec2(smallSize.y, smallSize.y) // 1:1 aspect
                it.position = BroadcastConfiguration.Vec2(bigSize.x - smallSize.y - borderWidth, smallPositionBottomRight.y)
                it.setzIndex(3)
                it.transparency = 0.3f
                it.name = LOGO_SLOT_NAME

                return@with it
            }

            this.mixer.slots = arrayOf(cameraSlot, contentSlot, logoSlot)
        }

        BroadcastSession(context, null, config, null).apply {
            session = this
            Log.d(TAG, "Broadcast session ready: $isReady")
            if (!isReady) {
                Log.d(TAG, "Broadcast session not ready")
                Toast.makeText(context, context.getString(R.string.error_create_session), Toast.LENGTH_SHORT).show()
                return
            }

            // Attach devices to each slot manually based on the slot names.

            // Find the first front camera.
            val frontCamera = BroadcastSession.listAvailableDevices(context).filter {
                it.position == Device.Descriptor.Position.FRONT && it.type == Device.Descriptor.DeviceType.CAMERA
            }[0]
            frontCamera?.let {
                // Then, we attach the front camera and on completion, bind it to the camera slot.
                // Note that bindToPreference is FALSE, which gives us full control over binding the device to the slot. This also means
                // that we are responsible for binding the device to a slot once the device is attached.
                // (When bindToPreference is TRUE, as part of attaching the device, the broadcast session will also try to bind the device to a
                // slot with a matching type preference.)
                this.attachDevice(frontCamera, false) {
                    val success: Boolean = this.mixer?.bind(it, CAMERA_SLOT_NAME) == true

                    // Error-checking. The most common source of this error is that there is no slot
                    // with the name provided.
                    if (!success) {
                        Toast.makeText(context, context.getString(R.string.error_failed_to_bind_to_slot), Toast.LENGTH_SHORT).show()
                    }

                }
            }

            // Second, create a custom image input source for the logo.
            val logoSurfaceSource = this.createImageInputSource()
            val logoSurface = logoSurfaceSource.inputSurface
            val canvas = logoSurface.lockCanvas(null)
            canvas.drawBitmap(logo, 0f, 0f, null)
            logoSurface.unlockCanvasAndPost(canvas)
            // Bind it to the logo slot.
            this.awaitDeviceChanges {
                val success: Boolean = this.mixer?.bind(logoSurfaceSource, LOGO_SLOT_NAME) == true

                // Error-checking. The most common source of this error is that there is no slot
                // with the name provided.
                if (!success) {
                    Toast.makeText(context, context.getString(R.string.error_failed_to_bind_to_slot), Toast.LENGTH_SHORT).show()
                }
            }

            // Third, create a custom image input source for the looping content.
            val contentSurfaceSource = this.createImageInputSource()
            val contentSurface = contentSurfaceSource.inputSurface
            player = MediaPlayer().apply {
                this.setDataSource(context, content)
                this.prepare()
                this.setDisplay(CustomSurfaceHolder(contentSurface))
                this.setOnPreparedListener {
                    this.start()
                    this.isLooping = true
                }
            }
            // Bind it to the content slot.
            this.awaitDeviceChanges {
                val success: Boolean = this.mixer?.bind(contentSurfaceSource, CONTENT_SLOT_NAME) == true

                // Error-checking. The most common source of this error is that there is no slot
                // with the name provided.
                if (!success) {
                    Toast.makeText(context, context.getString(R.string.error_failed_to_bind_to_slot), Toast.LENGTH_SHORT).show()
                }
            }

            // This creates a preview of the composited output stream, not an individual source. Because of this there is small
            // amount of delay in the preview since it has to go through a render cycle to composite the sources together.
            // It is also important to note that because our configuration is for a landscape stream using the "fit" aspect mode
            // there will be aggressive letterboxing when holding a mobile phone in portrait. Rotating to landscape or using an tablet
            // will provide a larger preview, though the only change is the scaling.
            this.awaitDeviceChanges {
                displayPreview()
            }
        }
    }