Skip to content

Require help to resolve this crash , can you please guide on this to resolve the below crashes #2014

@bhavinatharva

Description

@bhavinatharva

Library : implementation("com.github.pedroSG94.RootEncoder:library:2.6.6")

Android Manifest :

<uses-permission
        android:name="android.permission.FOREGROUND_SERVICE"
        tools:ignore="ForegroundServicesPolicy" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<service
            android:name=".services.ShareScreenService"
            android:exported="false"
            android:foregroundServiceType="mediaProjection|microphone|camera" />
  1. Caused by android.app.ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false: service

  2. Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.os.Looper.quit()' on a null object reference

at com.pedro.encoder.BaseEncoder.stop(BaseEncoder.java:181)
       at com.pedro.encoder.BaseEncoder.stop(BaseEncoder.java:167)
       at com.pedro.library.base.StreamBase.stopSources(StreamBase.kt:526)
       at com.pedro.library.base.StreamBase.stopStream(StreamBase.kt:251)
       at com.atharvasystem.quickscreenrecorder.services.ShareScreenService$stopStream$1$1.invokeSuspend(ShareScreenService.kt:148)
  1. Fatal Exception: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1863891168, result=-1, data=Intent { (has extras) }} to activity {com.atharvasystem.quickscreenrecorder/com.atharvasystem.quickscreenrecorder.ui.streams.StreamSourceActivity}: java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
class ShareScreenService : Service(), ConnectChecker {
    companion object {
        private const val TAG = "ShareScreenService"
        private const val channelId = "rtpDisplayStreamChannel"
        private const val defaultAudioSource = 0 // 0 = MicroPhone, 1 = Internal, 2 = Mix Audio
        const val notifyId = 123456
        var INSTANCE: ShareScreenService? = null
    }

    private lateinit var notificationManager: NotificationManager
    private lateinit var genericStream: GenericStream
    private val mediaProjectionManager: MediaProjectionManager by lazy {
        applicationContext.getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
    }
    private var mediaProjection: MediaProjection? = null
    private var callback: ConnectChecker? = null
    private var width = 1920
    private var height = 1080
    private var vBitrate = 4000 * 1000
    private var rotation = 0 //0 for landscape or 90 for portrait
    private var framePerSecond = 15
    private var sampleRate = 32000
    private var isStereo = true
    private var aBitrate = 128 * 1000
    private var prepared = false
    private var selectedAudioSource: Int = defaultAudioSource
    private val isForegroundStarted = AtomicBoolean(false)

    override fun onCreate() {
        super.onCreate()
        notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                channelId,
                channelId, // Add this string resource
                NotificationManager.IMPORTANCE_LOW
            )
            notificationManager.createNotificationChannel(channel)
        }
        genericStream =
            GenericStream(baseContext, this, NoVideoSource(), MicrophoneSource()).apply {
                //This is important to keep a constant fps because media projection only produce fps if the screen change
                getGlInterface().setForceRender(true, framePerSecond)
            }
        INSTANCE = this
    }

    private fun keepAliveTrick() {
        if (isForegroundStarted.get()) return
        val notification = NotificationCompat.Builder(this, channelId)
            .setSmallIcon(R.drawable.ic_notification)
            .setSilent(true)
            .setOngoing(true)
            .setColor(Color.BLACK)
            .setAutoCancel(false)
            .setContentTitle(getString(R.string.live_broadcast_progress))
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build()

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
            val serviceTypes = ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION

            startForeground(
                notifyId,
                notification,
                serviceTypes
            )
        } else {
            startForeground(notifyId, notification)
        }
        isForegroundStarted.set(true)
    }

    override fun onBind(p0: Intent?): IBinder? {

        return null
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return START_STICKY
    }

    fun sendIntent(): Intent {
        return mediaProjectionManager.createScreenCaptureIntent()
    }

    fun isStreaming(): Boolean {
        return genericStream.isStreaming
    }

    fun getGenericStream(): GenericStream {
        return genericStream
    }

    fun isRecording(): Boolean {
        return genericStream.isRecording
    }

    fun startRecording(outputPath: String) {
        genericStream.startRecord(outputPath) {

        }
    }

    fun stopRecording(): Boolean {
        return genericStream.stopRecord()
    }

    fun stopStreamingService(context: Context) {
        val intent = Intent(context, ShareScreenService::class.java)
        context.stopService(intent)
    }

    fun stopStream() {
        try {
            if (genericStream.isStreaming || genericStream.isRecording) {
                if (genericStream.isRecording) {
                    stopRecording()
                }

                if (genericStream.isStreaming) {
                    GlobalScope.launch(Dispatchers.Main) { // Or a custom scope
                        withContext(Dispatchers.IO) { // Perform blocking operation on IO dispatcher
                            genericStream.stopStream()
                        }
                    }
                }

                notificationManager.cancel(notifyId)
                stopForeground(true)  // Add this to remove foreground state
                stopSelf()            // Uncomment this to stop the service
            }
        } catch (e: Exception) {
            e.printStackTrace()
            try {
                stopRecording()
                stopStream()
            } catch (e: Exception) {
                e.printStackTrace()
            }
            stopSelf()
        }
    }

    fun setCallback(connectChecker: ConnectChecker?) {
        callback = connectChecker
    }

    override fun onDestroy() {
        super.onDestroy()
        stopStream()
        INSTANCE = null
    }

    fun prepareStream(resultCode: Int, data: Intent): Boolean {
        keepAliveTrick()
        stopStream()
        val mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data)
        if (mediaProjection == null) {
            Log.e(TAG, "MediaProjection is null")
            return false
        }
        this.mediaProjection = mediaProjection
        val screenSource = ScreenSource(applicationContext, mediaProjection)
        return try {
            //ScreenSource need use always setCameraOrientation(0) because the MediaProjection handle orientation.
            //You also need remove autoHandleOrientation if you are using it.
            //You need to call it after prepareVideo to override the default value.
            screenSource.let {
                genericStream.let {
                    genericStream.getGlInterface().let {
                        genericStream.getGlInterface().setCameraOrientation(0)
                    }
                    genericStream.changeVideoSource(screenSource)
                    genericStream.getStreamClient().setReTries(5)
                    prepareVideoAudio()
                }
            }
        } catch (ignored: IllegalArgumentException) {
            false
        }
    }

    fun startStream(endpoint: String) {
        if (!genericStream.isStreaming) {
            genericStream.startStream(endpoint)
        }
    }

    override fun onConnectionStarted(url: String) {
        callback?.onConnectionStarted(url)
    }

    override fun onConnectionSuccess() {
        callback?.onConnectionSuccess()
    }

    override fun onNewBitrate(bitrate: Long) {
        callback?.onNewBitrate(bitrate)
        if (SharePrefUtil.isAdaptiveBitrateEnabled(this)) {
            genericStream.setVideoBitrateOnFly(bitrate.toInt())
        }
    }

    override fun onConnectionFailed(reason: String) {
        callback?.onConnectionFailed(reason)
    }

    override fun onDisconnect() {
        callback?.onDisconnect()
    }

    override fun onAuthError() {
        callback?.onAuthError()
    }

    override fun onAuthSuccess() {
        callback?.onAuthSuccess()
    }

    private fun prepareVideoAudio(): Boolean {
        if (genericStream.isStreaming || genericStream.isRecording || genericStream.isOnPreview) {
            genericStream.stopStream()
        }
        if (SharePrefUtil.isPurchased(this)) {
            val width1 = SharePrefUtil.getResolution(this, getString(R.string.broadcastScreen))
            val height1 = ViewUtil.getResolutionHeight(
                SharePrefUtil.getResolution(
                    this, getString(R.string.broadcastScreen)
                )
            )
            width = height1
            height = width1
            vBitrate = ViewUtil.getVideoBitrate(1080)
        } else {
            val width1 = SharePrefUtil.getResolution(this, getString(R.string.broadcastScreen))
            val height1 = ViewUtil.getResolutionHeight(
                SharePrefUtil.getResolution(
                    this, getString(R.string.broadcastScreen)
                )
            )
            width = height1
            height = width1
            vBitrate = ViewUtil.getVideoBitrate(720)
        }
        prepared = try {
            genericStream.prepareVideo(
                width = width,
                height = height,
                bitrate = vBitrate,
                rotation = rotation,
                fps = SharePrefUtil.getFrameRate(this, getString(R.string.broadcastScreen)),
            ) && genericStream.prepareAudio(
                sampleRate, isStereo, aBitrate, echoCanceler = true, noiseSuppressor = true
            )
        } catch (e: IllegalArgumentException) {
            false
        }
        return prepared
    }
    fun getCurrentAudioSource(): AudioSource = genericStream.audioSource
    fun toggleAudioSource(itemId: Int) {
        when (itemId) {
            0 -> {
                selectedAudioSource = 0
                if (genericStream.audioSource is MicrophoneSource) return
                genericStream.changeAudioSource(MicrophoneSource())
            }
            1-> {
                selectedAudioSource = 1
                if (genericStream.audioSource is InternalAudioSource) return
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    mediaProjection?.let { genericStream.changeAudioSource(InternalAudioSource(it)) }
                } else {
                    throw IllegalArgumentException("You need min API 29+")
                }
            }
            2 -> {
                selectedAudioSource = 2
                if (genericStream.audioSource is MixAudioSource) return
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    mediaProjection?.let { genericStream.changeAudioSource(MixAudioSource(it)) }
                } else {
                    throw IllegalArgumentException("You need min API 29+")
                }
            }
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions