Closed MinhMinh8722 closed 2 months ago
Hello,
Can you show me your code?
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?) {
...
}
}
This is weird, the code looks fine.
Are you using proguard? try to disable it Also, try clear your build and recompile the project
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! 🙇
I'm not able to reproduce it. Can you share me a project with a code example to reproduce it?
@pedroSG94 I did it. This is my error. Thanks for your support!!!
Hi @pedroSG94 , I got this error in
onCreate()
at init GenericStream. Could you help me resolve this? Thank you!!!I used ScreenService