package fr.mobdev.peertubelive.activity import android.Manifest.permission import android.content.* import android.content.pm.PackageManager import android.hardware.SensorManager import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.Settings import android.view.OrientationEventListener import android.view.SurfaceHolder import android.view.View import android.view.WindowManager import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import com.pedro.encoder.input.video.CameraHelper import com.pedro.rtplibrary.rtmp.RtmpCamera1 import net.ossrs.rtmp.ConnectCheckerRtmp import fr.mobdev.peertubelive.R import fr.mobdev.peertubelive.databinding.StreamBinding import fr.mobdev.peertubelive.manager.InstanceManager.EXTRA_DATA import fr.mobdev.peertubelive.objects.StreamData class StreamActivity : AppCompatActivity() { private lateinit var binding: StreamBinding private lateinit var rtmpCamera1 : RtmpCamera1 private lateinit var streamData: StreamData private lateinit var orientationEventListener: OrientationEventListener private lateinit var lockReceiver: BroadcastReceiver private var surfaceInit: Boolean = false private var permissionGiven: Boolean = false private var streamIsActive: Boolean = false private var screenOrientation: Int = 0 private var lastScreenOrientation: Int = 0 private var orientationCounter: Int = 0 private var rotationIsLanternEnabled: Boolean = true companion object { const val BACKGROUND :Int = 1 const val LOCK :Int = 2 const val BACK :Int = 3 } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) binding = DataBindingUtil.setContentView(this, R.layout.stream) orientationEventListener = object: OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL){ override fun onOrientationChanged(orientation: Int) { if(orientation < 0 || !rotationIsLanternEnabled) return var localOrientation: Int localOrientation = when (orientation) { in 45..135 -> { 90 } in 135..225 -> { 180 } in 225..315 -> { 270 } else -> { 0 } } if(localOrientation != lastScreenOrientation) { lastScreenOrientation = localOrientation orientationCounter = 0 } else { orientationCounter++ } if (lastScreenOrientation != screenOrientation && orientationCounter > 30) { screenOrientation = lastScreenOrientation rtmpCamera1.glInterface.setStreamRotation(screenOrientation) if (screenOrientation == 90) { localOrientation = 270 } else if(screenOrientation == 270) { localOrientation = 90 } binding.flash.rotation = localOrientation.toFloat() binding.muteMicro.rotation = localOrientation.toFloat() binding.switchCamera.rotation = localOrientation.toFloat() } } } orientationEventListener.enable() streamData = intent.getParcelableExtra(EXTRA_DATA)!! binding.switchCamera.setOnClickListener { rtmpCamera1.switchCamera() } binding.muteMicro.setOnClickListener { if (rtmpCamera1.isAudioMuted) { rtmpCamera1.enableAudio() binding.muteMicro.setImageResource(R.drawable.baseline_volume_up_24) } else { rtmpCamera1.disableAudio() binding.muteMicro.setImageResource(R.drawable.baseline_volume_off_24) } } binding.flash.setOnClickListener { if (rtmpCamera1.isLanternEnabled) { rtmpCamera1.disableLantern() binding.flash.setImageResource(R.drawable.baseline_flash_off_24) } else { rtmpCamera1.enableLantern() binding.flash.setImageResource(R.drawable.baseline_flash_on_24) } } binding.rotation.setOnClickListener { if (rotationIsLanternEnabled) { rotationIsLanternEnabled = !rotationIsLanternEnabled binding.rotation.setImageResource(R.drawable.baseline_screen_lock_rotation_24) } else { rotationIsLanternEnabled = !rotationIsLanternEnabled binding.rotation.setImageResource(R.drawable.baseline_screen_rotation_24) } } binding.surfaceView.holder.addCallback(object: SurfaceHolder.Callback { override fun surfaceCreated(p0: SurfaceHolder) { surfaceInit = true if (permissionGiven) startStream() } override fun surfaceChanged(p0: SurfaceHolder, p1: Int, p2: Int, p3: Int) { } override fun surfaceDestroyed(p0: SurfaceHolder) { if(rtmpCamera1.isStreaming) { rtmpCamera1.stopStream() } rtmpCamera1.stopPreview() } } ) lockReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action.equals(Intent.ACTION_SCREEN_OFF)){ setResult(LOCK) finish() } else if (intent?.action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)){ val reason = intent?.getStringExtra("reason") if(reason.equals("homekey")){ setResult(BACKGROUND) finish() } } } } val filter = IntentFilter(Intent.ACTION_SCREEN_OFF) filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) registerReceiver(lockReceiver, filter) } override fun onResume() { super.onResume() val permissions = getUnAllowedPermissions() if(permissions.isNotEmpty()) { var shouldShowRequest = true for(perm in permissions){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) shouldShowRequest = shouldShowRequest && shouldShowRequestPermissionRationale(perm) } if(shouldShowRequest) { binding.permissionInfo.visibility = View.VISIBLE binding.gotoPermission.visibility = View.VISIBLE binding.surfaceView.visibility = View.GONE binding.gotoPermission.setOnClickListener { val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) intent.data = Uri.fromParts("package", packageName, null) startActivity(intent) } } else { ActivityCompat.requestPermissions(this, permissions.toTypedArray(), 1) } } else { permissionGiven = true if(surfaceInit && !streamIsActive) startStream() } } override fun onStop() { super.onStop() if (!hasWindowFocus()) { unregisterReceiver(lockReceiver) setResult(BACKGROUND) finish() } } override fun onBackPressed() { val alertBuilder = AlertDialog.Builder(this) alertBuilder.setTitle(R.string.end_stream) alertBuilder.setMessage(R.string.ask_end_stream) alertBuilder.setPositiveButton(R.string.yes) { _: DialogInterface, _: Int -> setResult(BACK) finish() } alertBuilder.setNegativeButton(R.string.no,null) alertBuilder.show() } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) var allPermissionGranted = true for (result in grantResults) { allPermissionGranted = allPermissionGranted && (result == PackageManager.PERMISSION_GRANTED) } if (allPermissionGranted) { permissionGiven = true if(surfaceInit && !streamIsActive) startStream() } else { binding.permissionInfo.visibility = View.VISIBLE binding.gotoPermission.visibility = View.VISIBLE binding.surfaceView.visibility = View.GONE } } private fun startStream() { streamIsActive = true binding.permissionInfo.visibility = View.GONE binding.gotoPermission.visibility = View.GONE binding.surfaceView.visibility = View.VISIBLE val connectChecker : ConnectCheckerRtmp = object : ConnectCheckerRtmp { override fun onConnectionSuccessRtmp() { runOnUiThread { Toast.makeText(binding.root.context, "Connection success", Toast.LENGTH_SHORT).show(); } } override fun onConnectionFailedRtmp(reason: String) { runOnUiThread { Toast.makeText(binding.root.context, "Connection failed", Toast.LENGTH_SHORT).show(); rtmpCamera1.stopStream() } } override fun onNewBitrateRtmp(bitrate: Long) { } override fun onDisconnectRtmp() { runOnUiThread { Toast.makeText(binding.root.context, "Disconnect", Toast.LENGTH_SHORT).show(); } } override fun onAuthErrorRtmp() { runOnUiThread { Toast.makeText(binding.root.context, "Auth Error", Toast.LENGTH_SHORT).show(); } } override fun onAuthSuccessRtmp() { runOnUiThread { Toast.makeText(binding.root.context, "Auth Success", Toast.LENGTH_SHORT).show(); } } } rtmpCamera1 = RtmpCamera1(binding.surfaceView, connectChecker) var resolutions = rtmpCamera1.resolutionsBack var width: Int var height: Int if(resolutions[0].width > resolutions[0].height) { width = resolutions[0].width height = resolutions[0].height } else { width = resolutions[0].height height = resolutions[0].width } for(res in resolutions) { if (width * height < res.width * res.width) { if(res.width > res.height) { width = res.width height = res.height } else { width = res.height height = res.width } } } rtmpCamera1.startPreview(width,height) //start stream if (rtmpCamera1.prepareAudio() && rtmpCamera1.prepareVideo(width,height,30,3000*1024,false,CameraHelper.getCameraOrientation(this))) { rtmpCamera1.startStream(streamData.url+"/"+streamData.key) } else { /**This device cant init encoders, this could be for 2 reasons: The encoder selected doesnt support any configuration setted or your device hasnt a H264 or AAC encoder (in this case you can see log error valid encoder not found) */ } } private fun getUnAllowedPermissions(): List { val permissions: ArrayList = ArrayList() if (ContextCompat.checkSelfPermission(this, permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { permissions.add(permission.CAMERA) } if (ContextCompat.checkSelfPermission(this, permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { permissions.add(permission.RECORD_AUDIO) } return permissions } }