pedroSG94 / RootEncoder

RootEncoder for Android (rtmp-rtsp-stream-client-java) is a stream encoder to push video/audio to media servers using protocols RTMP, RTSP, SRT and UDP with all code written in Java/Kotlin
Apache License 2.0
2.53k stars 771 forks source link

Occur error at onCreate() #1518

Closed MinhMinh8722 closed 2 months ago

MinhMinh8722 commented 2 months ago

Hi @pedroSG94 , I got this error in onCreate() at init GenericStream. Could you help me resolve this? Thank you!!!

Process: com.abc.abc, PID: 10526
    java.lang.NoSuchMethodError: No direct method <init>(Lcom/pedro/common/ConnectChecker;)V in class Lcom/pedro/rtsp/rtsp/RtspClient; or its super classes (declaration of 'com.pedro.rtsp.rtsp.RtspClient' appears in /data/app/com.abc.abc==/base.apk!classes6.dex)
    at com.pedro.library.generic.GenericStream.<init>(GenericStream.kt:64)
    at com.abc.abc.service.ScreenService.onCreate(ScreenService.kt:73)
    at android.app.ActivityThread.handleCreateService(ActivityThread.java:4281)
    at android.app.ActivityThread.access$1500(ActivityThread.java:270)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2067)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loop(Looper.java:237)
    at android.app.ActivityThread.main(ActivityThread.java:7948)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1075)

I used ScreenService

pedroSG94 commented 2 months ago

Hello,

Can you show me your code?

MinhMinh8722 commented 2 months ago

Hi, this is my code.

ScreenService

class ScreenService: Service(), ConnectChecker {
    companion object {
        private const val TAG = "ScreenService"
        private const val CHANNEL_ID = "rtpDisplayStreamChannel"
        const val NOTIFY_ID = 101
        var INSTANCE: ScreenService? = null

        fun startService(context: Context) =
            Intent(AppLauncher.instance(), ScreenService::class.java).also {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
                    context.startService(it)
                } else {
                    ContextCompat.startForegroundService(context, it)
                }
            }
    }

    private val binder: IBinder by lazy { LocaleBinder() }
    private var notificationManager: NotificationManager? = null
    private lateinit var genericStream: GenericStream
    private val mediaProjectionManager: MediaProjectionManager by lazy {
        applicationContext.getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
    }
    private var callback: ConnectChecker? = null
    private var stateChangeCallback: StateChangeCallback? = null
    private val width = 640
    private val height = 480
    private val vBitrate = 1200 * 1000
    private var rotation = 0 //0 for landscape or 90 for portrait
    private val sampleRate = 32000
    private val isStereo = true
    private val aBitrate = 128 * 1000
    private var prepared = 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(CHANNEL_ID, CHANNEL_ID, NotificationManager.IMPORTANCE_HIGH)
            notificationManager?.createNotificationChannel(channel)
        }
        genericStream = GenericStream(baseContext, this, NoVideoSource(), MicrophoneSource()).apply {
            getGlInterface().setForceRender(true, 15)
        }
        prepared = try {
            genericStream.prepareVideo(width, height, vBitrate, rotation = rotation) &&
                    genericStream.prepareAudio(sampleRate, isStereo, aBitrate)
        } catch (e: IllegalArgumentException) {
            false
        }
        if (prepared) INSTANCE = this
        else Toast.makeText(this, "Invalid audio or video parameters, prepare failed", Toast.LENGTH_SHORT).show()
    }

    private fun keepAliveTrick() {
        val notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.livenow)
            .setSilent(true)
            .setOngoing(false)
            .build()
        startForeground(1, notification)
    }

    override fun onBind(p0: Intent?): IBinder = binder

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

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

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

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

    fun stopRecord() {
        if (genericStream.isStreaming) {
            genericStream.stopRecord()
            notificationManager?.cancel(NOTIFY_ID)
        }
    }

    fun setCallback(connectChecker: ConnectChecker?, stateChangeCallback: StateChangeCallback) {
        callback = connectChecker
        this.stateChangeCallback = stateChangeCallback
    }

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

    fun prepareStream(resultCode: Int, data: Intent): Boolean {
        keepAliveTrick()
        stopRecord()
        val mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data)
        val screenSource = ScreenSource(applicationContext, mediaProjection)
        return try {
            genericStream.getGlInterface().setCameraOrientation(0)
            genericStream.changeVideoSource(screenSource)
            true
        } catch (ignored: IllegalArgumentException) {
            false
        }
    }

    fun startRecord(endpoint: String, listener: Listener) {
        if (!genericStream.isStreaming) genericStream.startRecord(endpoint, listener)
    }

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

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

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

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

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

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

    override fun onNewBitrate(bitrate: Long) {
        callback?.onNewBitrate(bitrate)
    }

    inner class LocaleBinder : Binder() {
        fun getService() = this@ScreenService
    }

    interface StateChangeCallback {
        fun getHandler(): Handler
        fun onStateChanged(isBroadcasting: Boolean)

        fun onSavedVideosComplete(uri: Uri?);
    }
}

And in ScreenFragment

class ScreenRecordFragment: Fragment(), ConnectChecker, RecordController.Listener {

    companion object {
        fun newInstance(): Fragment {
            return ScreenRecordFragment()
        }
    }

    private var _viewBinding: FragmentScreenRecordBinding? = null
    private val binding get() = _viewBinding!!

    private val handler: Handler = Handler(Looper.getMainLooper())
    private var mServiceIntent: Intent? = null
    private var mService: ScreenService? = null

    private val activityResultContract = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        val data = result.data
        if (data != null && result.resultCode == RESULT_OK) {
            val screenService = ScreenService.INSTANCE
            if (screenService != null) {
//                val endpoint = etUrl.text.toString()
                if (screenService.prepareStream(result.resultCode, data)) {
                    screenService.startRecord("LiveNow/", this)
                } else {
                    Toast.makeText(this.requireContext(), "Prepare stream failed", Toast.LENGTH_SHORT).show()
                }
            }
        } else {
            Toast.makeText(this.requireContext(), "No permissions available", Toast.LENGTH_SHORT).show()
        }
    }

    private val serviceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            val binder = service as? ScreenService.LocaleBinder
            mService = binder?.getService()
            mService?.setCallback(this@ScreenRecordFragment, streamCallback)
            activityResultContract.launch(mService!!.sendIntent())
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            mService = null
        }
    }

    override fun onStart() {
        super.onStart()
        mServiceIntent = Intent(...)
        if (mService == null) {
            mServiceIntent = ScreenService.startService(requireContext())
        }
        requireContext().bindService(mServiceIntent, serviceConnection, 0)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _viewBinding = FragmentScreenRecordBinding.inflate(layoutInflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.btnStartStop.setOnClickListener {
            val mService = ScreenService.INSTANCE
            if (!mService!!.isRecording()) {
                mServiceIntent = ScreenService.startService(requireContext())
                requireContext().bindService(mServiceIntent, serviceConnection, 0)
            } else {
                stopRecord()
            }
        }
    }

    private fun stopRecord() {
        val screenService = ScreenService.INSTANCE
        screenService?.stopRecord()
        ...
    }

    override fun onAuthError() {
        stopRecord()
    }

    override fun onAuthSuccess() {
        ...
    }

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

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

    override fun onConnectionSuccess() {
        ...
    }

    override fun onDisconnect() {
        ...
    }

    override fun onNewBitrate(bitrate: Long) {
        ...
    }

    override fun onStatusChange(status: RecordController.Status?) {
        ...
    }
}
pedroSG94 commented 2 months ago

This is weird, the code looks fine.

Are you using proguard? try to disable it Also, try clear your build and recompile the project

MinhMinh8722 commented 2 months ago

I removed proguard. This error also happened.

Declare at AndroidManifest.xml:

<service android:name=".service.ScreenService"
            android:enabled="true"
            android:exported="false"
            android:foregroundServiceType="mediaProjection" />

Please help me check it! 🙇

pedroSG94 commented 2 months ago

I'm not able to reproduce it. Can you share me a project with a code example to reproduce it?

MinhMinh8722 commented 2 months ago

@pedroSG94 I did it. This is my error. Thanks for your support!!!