2021-09-23 14:55:50 +02:00
/ * *
* Copyright ( C ) 2021 Anthony Chomienne
* This program is free software : you can redistribute it and / or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation , version 3.
*
* This program is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
* See the GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License along with this program .
* If not , see < https : //www.gnu.org/licenses/>
* /
2021-06-22 16:05:02 +02:00
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
2022-06-16 12:34:41 +02:00
import com.pedro.rtmp.utils.ConnectCheckerRtmp
2021-07-27 15:45:12 +02:00
import com.pedro.rtplibrary.rtmp.RtmpCamera2
2021-06-22 16:05:02 +02:00
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
2021-07-27 15:45:12 +02:00
private lateinit var rtmpCamera2 : RtmpCamera2
2021-06-22 16:05:02 +02:00
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
2022-07-14 13:33:30 +02:00
private var rotationIsEnabled : Boolean = true
2021-06-22 16:05:02 +02:00
companion object {
2021-07-26 20:38:57 +02:00
const val BACKGROUND : Int = 1
const val LOCK : Int = 2
const val BACK : Int = 3
const val STOP : Int = 4
const val NETWORK _ERROR : Int = 5
2021-06-22 16:05:02 +02:00
}
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 ) {
2022-07-14 13:33:30 +02:00
if ( orientation < 0 || ! rotationIsEnabled ) {
2021-06-22 16:05:02 +02:00
return
2022-07-14 13:33:30 +02:00
}
2021-06-22 16:05:02 +02:00
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
2021-07-27 15:45:12 +02:00
rtmpCamera2 . glInterface . setStreamRotation ( screenOrientation )
2021-06-22 16:05:02 +02:00
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 ( )
2022-07-14 13:33:30 +02:00
binding . rotation . rotation = localOrientation . toFloat ( )
2021-06-22 16:05:02 +02:00
}
}
}
orientationEventListener . enable ( )
streamData = intent . getParcelableExtra < StreamData > ( EXTRA _DATA ) !!
2021-07-07 17:29:37 +02:00
binding . stop . setOnClickListener {
askStopLive ( STOP )
}
2021-07-27 15:45:12 +02:00
binding . switchCamera . setOnClickListener { rtmpCamera2 . switchCamera ( ) }
2021-10-18 18:11:54 +02:00
binding . switchCamera . visibility = View . GONE
2021-06-22 16:05:02 +02:00
binding . muteMicro . setOnClickListener {
2021-07-27 15:45:12 +02:00
if ( rtmpCamera2 . isAudioMuted ) {
rtmpCamera2 . enableAudio ( )
2021-06-22 16:05:02 +02:00
binding . muteMicro . setImageResource ( R . drawable . baseline _volume _up _24 )
}
else {
2021-07-27 15:45:12 +02:00
rtmpCamera2 . disableAudio ( )
2021-06-22 16:05:02 +02:00
binding . muteMicro . setImageResource ( R . drawable . baseline _volume _off _24 )
}
}
2021-10-18 18:11:54 +02:00
binding . muteMicro . visibility = View . GONE
2021-06-22 16:05:02 +02:00
binding . flash . setOnClickListener {
2021-07-27 15:45:12 +02:00
if ( rtmpCamera2 . isLanternEnabled ) {
rtmpCamera2 . disableLantern ( )
2021-06-22 16:05:02 +02:00
binding . flash . setImageResource ( R . drawable . baseline _flash _off _24 )
}
else {
2021-07-27 15:45:12 +02:00
rtmpCamera2 . enableLantern ( )
2021-06-22 16:05:02 +02:00
binding . flash . setImageResource ( R . drawable . baseline _flash _on _24 )
}
}
2021-10-18 18:11:54 +02:00
binding . flash . visibility = View . GONE
2021-06-22 16:05:02 +02:00
binding . rotation . setOnClickListener {
2022-07-14 13:33:30 +02:00
if ( rotationIsEnabled ) {
rotationIsEnabled = ! rotationIsEnabled
2021-06-22 16:05:02 +02:00
binding . rotation . setImageResource ( R . drawable . baseline _screen _lock _rotation _24 )
}
else {
2022-07-14 13:33:30 +02:00
rotationIsEnabled = ! rotationIsEnabled
2021-06-22 16:05:02 +02:00
binding . rotation . setImageResource ( R . drawable . baseline _screen _rotation _24 )
}
}
2021-10-18 18:11:54 +02:00
binding . rotation . visibility = View . GONE
2021-06-22 16:05:02 +02:00
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 ) {
2021-07-27 15:45:12 +02:00
if ( this @StreamActivity :: rtmpCamera2 . isInitialized ) {
if ( rtmpCamera2 . isStreaming ) {
rtmpCamera2 . stopStream ( )
2021-07-25 21:06:51 +02:00
}
2021-07-27 15:45:12 +02:00
rtmpCamera2 . stopPreview ( )
2021-06-22 16:05:02 +02:00
}
}
}
)
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
2021-10-18 18:11:54 +02:00
2021-06-22 16:05:02 +02:00
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 {
2021-10-18 18:11:54 +02:00
binding . surfaceView . visibility = View . VISIBLE
2021-06-22 16:05:02 +02:00
permissionGiven = true
if ( surfaceInit && ! streamIsActive )
startStream ( )
}
}
override fun onStop ( ) {
super . onStop ( )
2021-10-18 18:11:54 +02:00
if ( this @StreamActivity :: rtmpCamera2 . isInitialized && ! hasWindowFocus ( ) ) {
2021-06-22 16:05:02 +02:00
unregisterReceiver ( lockReceiver )
setResult ( BACKGROUND )
finish ( )
}
}
override fun onBackPressed ( ) {
2021-07-07 17:29:37 +02:00
askStopLive ( BACK )
2021-06-22 16:05:02 +02:00
}
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
2021-10-18 18:11:54 +02:00
binding . muteMicro . visibility = View . VISIBLE
binding . rotation . visibility = View . VISIBLE
binding . switchCamera . visibility = View . VISIBLE
binding . flash . visibility = View . VISIBLE
2021-06-22 16:05:02 +02:00
val connectChecker : ConnectCheckerRtmp = object : ConnectCheckerRtmp {
2022-06-16 12:34:41 +02:00
override fun onConnectionStartedRtmp ( rtmpUrl : String ) {
}
2021-06-22 16:05:02 +02:00
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 ( ) ;
2021-07-27 15:45:12 +02:00
rtmpCamera2 . stopStream ( )
2021-07-26 20:38:57 +02:00
setResult ( NETWORK _ERROR )
finish ( )
2021-06-22 16:05:02 +02:00
}
}
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 ( ) ;
}
}
}
2021-07-27 15:45:12 +02:00
rtmpCamera2 = RtmpCamera2 ( binding . surfaceView , connectChecker )
2021-06-22 16:05:02 +02:00
2021-07-27 15:45:12 +02:00
var width = 1920
var height = 1080
when ( streamData . resolution ) {
StreamData . STREAM_RESOLUTION . p2160 -> {
width = 3840
height = 2160
}
StreamData . STREAM_RESOLUTION . p1440 -> {
width = 2560
height = 1440
}
StreamData . STREAM_RESOLUTION . p1080 -> {
width = 1920
height = 1080
}
StreamData . STREAM_RESOLUTION . p720 -> {
width = 1280
height = 720
}
StreamData . STREAM_RESOLUTION . p480 -> {
width = 640
height = 480
}
StreamData . STREAM_RESOLUTION . p360 -> {
width = 480
height = 360
2021-06-22 16:05:02 +02:00
}
}
2021-07-27 15:45:12 +02:00
rtmpCamera2 . startPreview ( CameraHelper . Facing . BACK , width , height )
2021-06-22 16:05:02 +02:00
//start stream
2022-07-14 13:33:30 +02:00
if ( rtmpCamera2 . prepareAudio ( ) && rtmpCamera2 . prepareVideo ( width , height , 30 , ( width * height * 30 * 0.076 ) . toInt ( ) , CameraHelper . getCameraOrientation ( this ) , true ) ) {
println ( " peertubeurl " + streamData . url + " / " + streamData . key )
2021-07-27 15:45:12 +02:00
rtmpCamera2 . startStream ( streamData . url + " / " + streamData . key )
2021-06-22 16:05:02 +02:00
} 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
}
2021-07-07 17:29:37 +02:00
private fun askStopLive ( reason : Int ) {
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 ( reason )
finish ( )
}
alertBuilder . setNegativeButton ( R . string . no , null )
alertBuilder . show ( )
}
2021-06-22 16:05:02 +02:00
}