Skip to content

Commit 2ec6d5f

Browse files
committed
Add SRT Listener Mode support
1 parent 749929e commit 2ec6d5f

44 files changed

Lines changed: 4747 additions & 599 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/src/main/AndroidManifest.xml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,22 +56,28 @@
5656
<activity
5757
android:name="com.pedro.streamer.file.FromFileActivity"
5858
android:label="@string/from_file"
59-
/>
59+
tools:targetApi="18" />
6060

6161
<activity
6262
android:name="com.pedro.streamer.screen.ScreenActivity"
6363
android:label="@string/display"
64-
/>
64+
tools:targetApi="21" />
6565

6666
<activity android:name="com.pedro.streamer.rotation.RotationActivity"
6767
android:label="@string/rotation_rtmp"
6868
android:screenOrientation="fullSensor"
6969
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize"
70-
tools:ignore="DiscouragedApi" />
70+
tools:ignore="DiscouragedApi"
71+
tools:targetApi="21" />
72+
73+
<activity
74+
android:name="com.pedro.streamer.srtreceiver.SrtReceiverActivity"
75+
android:label="@string/srt_receiver"
76+
/>
7177

7278
<service android:name="com.pedro.streamer.screen.ScreenService"
7379
android:foregroundServiceType="mediaProjection|microphone|camera"
74-
/>
80+
tools:targetApi="21" />
7581
</application>
7682

7783
</manifest>

app/src/main/java/com/pedro/streamer/MainActivity.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ class MainActivity : AppCompatActivity() {
105105
getString(R.string.rotation_rtmp), VERSION_CODES.LOLLIPOP
106106
)
107107
)
108+
activities.add(
109+
ActivityLink(
110+
Intent(this, com.pedro.streamer.srtreceiver.SrtReceiverActivity::class.java),
111+
getString(R.string.srt_receiver), VERSION_CODES.N
112+
)
113+
)
108114
}
109115

110116
private fun setListAdapter(activities: List<ActivityLink>) {

app/src/main/java/com/pedro/streamer/rotation/CameraFragment.kt

Lines changed: 175 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package com.pedro.streamer.rotation
1919
import android.annotation.SuppressLint
2020
import android.os.Build
2121
import android.os.Bundle
22+
import android.util.Log
2223
import android.view.LayoutInflater
2324
import android.view.SurfaceHolder
2425
import 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

Comments
 (0)