@@ -19,6 +19,7 @@ package com.pedro.streamer.rotation
1919import android.annotation.SuppressLint
2020import android.os.Build
2121import android.os.Bundle
22+ import android.util.Log
2223import android.view.LayoutInflater
2324import android.view.SurfaceHolder
2425import android.view.SurfaceView
@@ -68,168 +69,189 @@ import java.util.Locale
6869 * [com.pedro.library.srt.SrtStream]
6970 */
7071@RequiresApi(Build .VERSION_CODES .LOLLIPOP )
71- class CameraFragment : Fragment (), ConnectChecker {
72+ class CameraFragment : Fragment (), ConnectChecker {
73+ val TAG = " CameraFragment"
7274
73- companion object {
74- fun getInstance (): CameraFragment = CameraFragment ()
75- }
75+ companion object {
76+ fun getInstance (): CameraFragment = CameraFragment ()
77+ }
7678
77- val genericStream: GenericStream by lazy {
78- GenericStream (requireContext(), this ).apply {
79- getGlInterface().autoHandleOrientation = true
79+ val genericStream: GenericStream by lazy {
80+ GenericStream (requireContext(), this ).apply {
81+ getGlInterface().autoHandleOrientation = true
82+ }
8083 }
81- }
82- private lateinit var surfaceView: SurfaceView
83- private lateinit var bStartStop: ImageView
84- private lateinit var txtBitrate: TextView
85- val width = 640
86- val height = 480
87- val vBitrate = 1200 * 1000
88- private var rotation = 0
89- private val sampleRate = 32000
90- private val isStereo = true
91- private val aBitrate = 128 * 1000
92- private var recordPath = " "
93- // Bitrate adapter used to change the bitrate on fly depend of the bandwidth.
94- private val bitrateAdapter = BitrateAdapter {
95- genericStream.setVideoBitrateOnFly(it)
96- }.apply {
97- setMaxBitrate(vBitrate + aBitrate)
98- }
99-
100- @SuppressLint(" ClickableViewAccessibility" )
101- override fun onCreateView (
102- inflater : LayoutInflater , container : ViewGroup ? , savedInstanceState : Bundle ?
103- ): View ? {
104- val view = inflater.inflate(R .layout.fragment_camera, container, false )
105- bStartStop = view.findViewById(R .id.b_start_stop)
106- val bRecord = view.findViewById<ImageView >(R .id.b_record)
107- val bSwitchCamera = view.findViewById<ImageView >(R .id.switch_camera)
108- val etUrl = view.findViewById<EditText >(R .id.et_rtp_url)
109-
110- txtBitrate = view.findViewById(R .id.txt_bitrate)
111- surfaceView = view.findViewById(R .id.surfaceView)
112- (activity as ? RotationActivity )?.let {
113- surfaceView.setOnTouchListener(it)
84+ private lateinit var surfaceView: SurfaceView
85+ private lateinit var bStartStop: ImageView
86+ private lateinit var txtBitrate: TextView
87+ val width = 640
88+ val height = 480
89+ val vBitrate = 1200 * 1000
90+ private var rotation = 0
91+ private val sampleRate = 32000
92+ private val isStereo = true
93+ private val aBitrate = 128 * 1000
94+ private var recordPath = " "
95+
96+ // Bitrate adapter used to change the bitrate on fly depend of the bandwidth.
97+ private val bitrateAdapter = BitrateAdapter {
98+ genericStream.setVideoBitrateOnFly(it)
99+ }.apply {
100+ setMaxBitrate(vBitrate + aBitrate)
114101 }
115- surfaceView.holder.addCallback(object : SurfaceHolder .Callback {
116- override fun surfaceCreated (holder : SurfaceHolder ) {
117- if (! genericStream.isOnPreview) genericStream.startPreview(surfaceView)
118- }
119-
120- override fun surfaceChanged (holder : SurfaceHolder , format : Int , width : Int , height : Int ) {
121- genericStream.getGlInterface().setPreviewResolution(width, height)
122- }
123-
124- override fun surfaceDestroyed (holder : SurfaceHolder ) {
125- if (genericStream.isOnPreview) genericStream.stopPreview()
126- }
127-
128- })
129-
130- bStartStop.setOnClickListener {
131- if (! genericStream.isStreaming) {
132- genericStream.startStream(etUrl.text.toString())
133- bStartStop.setImageResource(R .drawable.stream_stop_icon)
134- } else {
135- genericStream.stopStream()
136- bStartStop.setImageResource(R .drawable.stream_icon)
137- }
102+
103+ @SuppressLint(" ClickableViewAccessibility" )
104+ override fun onCreateView (
105+ inflater : LayoutInflater , container : ViewGroup ? , savedInstanceState : Bundle ?
106+ ): View ? {
107+ val view = inflater.inflate(R .layout.fragment_camera, container, false )
108+ bStartStop = view.findViewById(R .id.b_start_stop)
109+ val bRecord = view.findViewById<ImageView >(R .id.b_record)
110+ val bSwitchCamera = view.findViewById<ImageView >(R .id.switch_camera)
111+ val etUrl = view.findViewById<EditText >(R .id.et_rtp_url)
112+
113+ txtBitrate = view.findViewById(R .id.txt_bitrate)
114+ surfaceView = view.findViewById(R .id.surfaceView)
115+ (activity as ? RotationActivity )?.let {
116+ surfaceView.setOnTouchListener(it)
117+ }
118+ surfaceView.holder.addCallback(object : SurfaceHolder .Callback {
119+ override fun surfaceCreated (holder : SurfaceHolder ) {
120+ if (! genericStream.isOnPreview) genericStream.startPreview(surfaceView)
121+ }
122+
123+ override fun surfaceChanged (
124+ holder : SurfaceHolder ,
125+ format : Int ,
126+ width : Int ,
127+ height : Int
128+ ) {
129+ genericStream.getGlInterface().setPreviewResolution(width, height)
130+ }
131+
132+ override fun surfaceDestroyed (holder : SurfaceHolder ) {
133+ if (genericStream.isOnPreview) genericStream.stopPreview()
134+ }
135+
136+ })
137+
138+ bStartStop.setOnClickListener {
139+ val message = " Link can't be empty," +
140+ " The link must include the IPv4 address and port number in the format, for example: 1.1.1.1:9991"
141+ val link = etUrl.text.trim().toString()
142+
143+ if (link.isEmpty() || ! link.contains(" ." ) || ! link.contains(" :" )) {
144+ toast(message)
145+ return @setOnClickListener
146+ }
147+
148+ if (genericStream.isStreaming) {
149+ genericStream.stopStream()
150+ bStartStop.setImageResource(R .drawable.stream_icon)
151+ } else {
152+ genericStream.startStream(link.plus(" ?mode=listener" ))
153+ // genericStream.startStream("srt://192.168.1.7:9991?mode=listener")
154+ bStartStop.setImageResource(R .drawable.stream_stop_icon)
155+ }
156+ }
157+ bRecord.setOnClickListener {
158+ if (! genericStream.isRecording) {
159+ val folder = PathUtils .getRecordPath()
160+ if (! folder.exists()) folder.mkdir()
161+ val sdf = SimpleDateFormat (" yyyyMMdd_HHmmss" , Locale .getDefault())
162+ recordPath = " ${folder.absolutePath} /${sdf.format(Date ())} .mp4"
163+ bRecord.setImageResource(R .drawable.pause_icon)
164+ genericStream.startRecord(recordPath) { status ->
165+ if (status == RecordController .Status .RECORDING ) {
166+ bRecord.setImageResource(R .drawable.stop_icon)
167+ }
168+ }
169+ } else {
170+ genericStream.stopRecord()
171+ bRecord.setImageResource(R .drawable.record_icon)
172+ PathUtils .updateGallery(requireContext(), recordPath)
173+ }
174+ }
175+ bSwitchCamera.setOnClickListener {
176+ when (val source = genericStream.videoSource) {
177+ is Camera1Source -> source.switchCamera()
178+ is Camera2Source -> source.switchCamera()
179+ is CameraXSource -> source.switchCamera()
180+ }
181+ }
182+ return view
138183 }
139- bRecord.setOnClickListener {
140- if (! genericStream.isRecording) {
141- val folder = PathUtils .getRecordPath()
142- if (! folder.exists()) folder.mkdir()
143- val sdf = SimpleDateFormat (" yyyyMMdd_HHmmss" , Locale .getDefault())
144- recordPath = " ${folder.absolutePath} /${sdf.format(Date ())} .mp4"
145- bRecord.setImageResource(R .drawable.pause_icon)
146- genericStream.startRecord(recordPath) { status ->
147- if (status == RecordController .Status .RECORDING ) {
148- bRecord.setImageResource(R .drawable.stop_icon)
149- }
184+
185+ fun setOrientationMode (isVertical : Boolean ) {
186+ val wasOnPreview = genericStream.isOnPreview
187+ genericStream.release()
188+ rotation = if (isVertical) 90 else 0
189+ prepare()
190+ if (wasOnPreview) genericStream.startPreview(surfaceView)
191+ }
192+
193+ override fun onCreate (savedInstanceState : Bundle ? ) {
194+ super .onCreate(savedInstanceState)
195+ prepare()
196+ genericStream.getStreamClient().setReTries(10 )
197+ }
198+
199+ private fun prepare () {
200+ val prepared = try {
201+ genericStream.prepareVideo(width, height, vBitrate, rotation = rotation)
202+ && genericStream.prepareAudio(sampleRate, isStereo, aBitrate)
203+ } catch (_: IllegalArgumentException ) {
204+ false
205+ }
206+ if (! prepared) {
207+ toast(" Audio or Video configuration failed" )
208+ activity?.finish()
150209 }
151- } else {
152- genericStream.stopRecord()
153- bRecord.setImageResource(R .drawable.record_icon)
154- PathUtils .updateGallery(requireContext(), recordPath)
155- }
156210 }
157- bSwitchCamera.setOnClickListener {
158- when (val source = genericStream.videoSource) {
159- is Camera1Source -> source.switchCamera()
160- is Camera2Source -> source.switchCamera()
161- is CameraXSource -> source.switchCamera()
162- }
211+
212+ override fun onDestroy () {
213+ super .onDestroy()
214+ genericStream.release()
215+ }
216+
217+ override fun onConnectionStarted (url : String ) {
218+
163219 }
164- return view
165- }
166-
167- fun setOrientationMode (isVertical : Boolean ) {
168- val wasOnPreview = genericStream.isOnPreview
169- genericStream.release()
170- rotation = if (isVertical) 90 else 0
171- prepare()
172- if (wasOnPreview) genericStream.startPreview(surfaceView)
173- }
174-
175- override fun onCreate (savedInstanceState : Bundle ? ) {
176- super .onCreate(savedInstanceState)
177- prepare()
178- genericStream.getStreamClient().setReTries(10 )
179- }
180-
181- private fun prepare () {
182- val prepared = try {
183- genericStream.prepareVideo(width, height, vBitrate, rotation = rotation)
184- && genericStream.prepareAudio(sampleRate, isStereo, aBitrate)
185- } catch (_: IllegalArgumentException ) {
186- false
220+
221+ override fun onConnectionSuccess () {
222+ Log .d(TAG , " onConnection: Success" )
223+ toast(" Connected" )
187224 }
188- if (! prepared) {
189- toast(" Audio or Video configuration failed" )
190- activity?.finish()
225+
226+ override fun onConnectionFailed (reason : String ) {
227+ if (genericStream.getStreamClient().reTry(5000 , reason, null )) {
228+ // toast("Retry")
229+ Log .d(TAG , " onConnection: Retry" )
230+ } else {
231+ genericStream.stopStream()
232+ bStartStop.setImageResource(R .drawable.stream_icon)
233+ Log .d(TAG , " onConnection: Failed : $reason " )
234+ toast(" Connection : Failed" )
235+ }
191236 }
192- }
193-
194- override fun onDestroy () {
195- super .onDestroy()
196- genericStream.release()
197- }
198-
199- override fun onConnectionStarted (url : String ) {
200- }
201-
202- override fun onConnectionSuccess () {
203- toast(" Connected" )
204- }
205-
206- override fun onConnectionFailed (reason : String ) {
207- if (genericStream.getStreamClient().reTry(5000 , reason, null )) {
208- toast(" Retry" )
209- } else {
210- genericStream.stopStream()
211- bStartStop.setImageResource(R .drawable.stream_icon)
212- toast(" Failed: $reason " )
237+
238+ override fun onNewBitrate (bitrate : Long ) {
239+ bitrateAdapter.adaptBitrate(bitrate, genericStream.getStreamClient().hasCongestion())
240+ txtBitrate.text = String .format(Locale .getDefault(), " %.1f mb/s" , bitrate / 1000_000f )
241+ }
242+
243+ override fun onDisconnect () {
244+ txtBitrate.text = String ()
245+ toast(" Disconnected" )
246+ }
247+
248+ override fun onAuthError () {
249+ genericStream.stopStream()
250+ bStartStop.setImageResource(R .drawable.stream_icon)
251+ toast(" Auth error" )
252+ }
253+
254+ override fun onAuthSuccess () {
255+ toast(" Auth success" )
213256 }
214- }
215-
216- override fun onNewBitrate (bitrate : Long ) {
217- bitrateAdapter.adaptBitrate(bitrate, genericStream.getStreamClient().hasCongestion())
218- txtBitrate.text = String .format(Locale .getDefault(), " %.1f mb/s" , bitrate / 1000_000f )
219- }
220-
221- override fun onDisconnect () {
222- txtBitrate.text = String ()
223- toast(" Disconnected" )
224- }
225-
226- override fun onAuthError () {
227- genericStream.stopStream()
228- bStartStop.setImageResource(R .drawable.stream_icon)
229- toast(" Auth error" )
230- }
231-
232- override fun onAuthSuccess () {
233- toast(" Auth success" )
234- }
235257}
0 commit comments