peertube-live-streaming/app/src/main/java/fr/mobdev/peertubelive/activity/StreamActivity.kt

336 lines
12 KiB
Kotlin

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<StreamData>(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<out String>, 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<String> {
val permissions: ArrayList<String> = ArrayList<String>()
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
}
}