桌面推流ok

This commit is contained in:
li 2024-05-30 16:38:04 +08:00
parent b5df0c6374
commit ff8769f4ac
15 changed files with 699 additions and 25 deletions

View File

@ -50,5 +50,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation project(':zlm')
implementation 'com.guolindev.permissionx:permissionx:1.7.1'
}

View File

@ -1,14 +1,23 @@
package com.zlmediakit.webrtc
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.permissionx.guolindev.PermissionX
import com.permissionx.guolindev.callback.RequestCallback
import com.zlm.rtc.ZLMRTCPusher
import com.zlm.rtc.push.PushMode
import com.zlm.rtc.push.ZLMRTCPusherImpl
import kotlinx.android.synthetic.main.activity_player.surface_view_renderer
import kotlinx.android.synthetic.main.activity_player.tv_app
import kotlinx.android.synthetic.main.activity_player.tv_stream_id
class PusherDemoActivity: AppCompatActivity() {
class PusherDemoActivity : AppCompatActivity() {
private val pusher: ZLMRTCPusher by lazy {
@ -16,23 +25,50 @@ class PusherDemoActivity: AppCompatActivity() {
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pusher)
pusher.bind(surface_view_renderer, true)
}
fun onPushCamera(view: View) {
pusher.push(tv_app.text.toString(), tv_stream_id.text.toString())
PermissionX.init(this)
.permissions(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
.request { allGranted, grantedList, deniedList ->
if (allGranted) {
pusher.push(tv_app.text.toString(), tv_stream_id.text.toString())
}
}
}
fun onPushScreen(view: View) {
PermissionX.init(this)
.permissions(Manifest.permission.RECORD_AUDIO)
.request { allGranted, grantedList, deniedList ->
if (allGranted) {
pusher.push(
tv_app.text.toString(),
tv_stream_id.text.toString(),
PushMode.SCREEN
)
}
}
}
fun onPushFile(view: View) {
}
override fun onDestroy() {
super.onDestroy()
pusher.stop()
}
}

View File

@ -6,7 +6,7 @@
<org.webrtc.SurfaceViewRenderer
android:id="@+id/surface_view_renderer"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="240dp" />
<androidx.appcompat.widget.LinearLayoutCompat

View File

@ -0,0 +1,142 @@
v=0
o=- 6282534540641525368 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1 2
a=extmap-allow-mixed
a=msid-semantic: WMS
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 102 0 8 13 110 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:H2Ms
a=ice-pwd:14ODtTVh5+lT7W0+g9EAlE4j
a=ice-options:trickle renomination
a=fingerprint:sha-256 47:81:90:07:43:95:D7:F2:DA:60:DA:79:E5:88:26:65:29:4A:26:28:7A:B7:AB:D3:DB:CE:C9:09:39:EB:31:91
a=setup:actpass
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=sendrecv
a=msid:- ef1bcbd2-7e69-40d3-9532-0f4a698022a7
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=rtpmap:9 G722/8000
a=rtpmap:102 ILBC/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:126 telephone-event/8000
a=ssrc:2860285610 cname:2EW1hNfQMKxwbRrt
a=ssrc:2860285610 msid:- ef1bcbd2-7e69-40d3-9532-0f4a698022a7
m=video 9 UDP/TLS/RTP/SAVPF 96 97 39 40 98 99 127 103 104 105 106 107 108
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:H2Ms
a=ice-pwd:14ODtTVh5+lT7W0+g9EAlE4j
a=ice-options:trickle renomination
a=fingerprint:sha-256 47:81:90:07:43:95:D7:F2:DA:60:DA:79:E5:88:26:65:29:4A:26:28:7A:B7:AB:D3:DB:CE:C9:09:39:EB:31:91
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendonly
a=msid:- 8b57ef63-835d-4acf-bce9-18e26cebf9b1
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:39 AV1/90000
a=rtcp-fb:39 goog-remb
a=rtcp-fb:39 transport-cc
a=rtcp-fb:39 ccm fir
a=rtcp-fb:39 nack
a=rtcp-fb:39 nack pli
a=rtpmap:40 rtx/90000
a=fmtp:40 apt=39
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:127 H264/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:103 rtx/90000
a=fmtp:103 apt=127
a=rtpmap:104 H265/90000
a=rtcp-fb:104 goog-remb
a=rtcp-fb:104 transport-cc
a=rtcp-fb:104 ccm fir
a=rtcp-fb:104 nack
a=rtcp-fb:104 nack pli
a=rtpmap:105 rtx/90000
a=fmtp:105 apt=104
a=rtpmap:106 red/90000
a=rtpmap:107 rtx/90000
a=fmtp:107 apt=106
a=rtpmap:108 ulpfec/90000
a=ssrc-group:FID 100166069 2038394461
a=ssrc:100166069 cname:2EW1hNfQMKxwbRrt
a=ssrc:100166069 msid:- 8b57ef63-835d-4acf-bce9-18e26cebf9b1
a=ssrc:2038394461 cname:2EW1hNfQMKxwbRrt
a=ssrc:2038394461 msid:- 8b57ef63-835d-4acf-bce9-18e26cebf9b1
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 102 0 8 13 110 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:H2Ms
a=ice-pwd:14ODtTVh5+lT7W0+g9EAlE4j
a=ice-options:trickle renomination
a=fingerprint:sha-256 47:81:90:07:43:95:D7:F2:DA:60:DA:79:E5:88:26:65:29:4A:26:28:7A:B7:AB:D3:DB:CE:C9:09:39:EB:31:91
a=setup:actpass
a=mid:2
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=sendonly
a=msid:- 49d08cb1-a325-4a5f-805a-72a215eb67af
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=rtpmap:9 G722/8000
a=rtpmap:102 ILBC/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:126 telephone-event/8000
a=ssrc:2666757999 cname:2EW1hNfQMKxwbRrt
a=ssrc:2666757999 msid:- 49d08cb1-a325-4a5f-805a-72a215eb67af

View File

@ -0,0 +1,112 @@
v=0
o=- 6005790964057401669 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=extmap-allow-mixed
a=msid-semantic: WMS ARDAMS
m=video 9 UDP/TLS/RTP/SAVPF 96 97 39 40 98 99 127 103 104 105 106 107 108
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:UITA
a=ice-pwd:obg6A6ZHKhr5CGzmmgRfTk/y
a=ice-options:trickle renomination
a=fingerprint:sha-256 5E:23:E2:B2:0F:ED:FC:A2:20:06:24:0F:6A:4B:99:33:91:39:A7:40:A5:20:88:77:D6:99:04:FB:C7:CD:7F:33
a=setup:actpass
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 urn:3gpp:video-orientation
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:ARDAMS ARDAMSv0
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:39 AV1/90000
a=rtcp-fb:39 goog-remb
a=rtcp-fb:39 transport-cc
a=rtcp-fb:39 ccm fir
a=rtcp-fb:39 nack
a=rtcp-fb:39 nack pli
a=rtpmap:40 rtx/90000
a=fmtp:40 apt=39
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:127 H264/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:103 rtx/90000
a=fmtp:103 apt=127
a=rtpmap:104 H265/90000
a=rtcp-fb:104 goog-remb
a=rtcp-fb:104 transport-cc
a=rtcp-fb:104 ccm fir
a=rtcp-fb:104 nack
a=rtcp-fb:104 nack pli
a=rtpmap:105 rtx/90000
a=fmtp:105 apt=104
a=rtpmap:106 red/90000
a=rtpmap:107 rtx/90000
a=fmtp:107 apt=106
a=rtpmap:108 ulpfec/90000
a=ssrc-group:FID 1882091013 3163545249
a=ssrc:1882091013 cname:L6diPFsW7sR8uU/a
a=ssrc:1882091013 msid:ARDAMS ARDAMSv0
a=ssrc:3163545249 cname:L6diPFsW7sR8uU/a
a=ssrc:3163545249 msid:ARDAMS ARDAMSv0
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 102 0 8 13 110 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:UITA
a=ice-pwd:obg6A6ZHKhr5CGzmmgRfTk/y
a=ice-options:trickle renomination
a=fingerprint:sha-256 5E:23:E2:B2:0F:ED:FC:A2:20:06:24:0F:6A:4B:99:33:91:39:A7:40:A5:20:88:77:D6:99:04:FB:C7:CD:7F:33
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
a=sendrecv
a=msid:ARDAMS ARDAMSa0
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=rtpmap:9 G722/8000
a=rtpmap:102 ILBC/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:126 telephone-event/8000
a=ssrc:1776947399 cname:L6diPFsW7sR8uU/a
a=ssrc:1776947399 msid:ARDAMS ARDAMSa0

View File

@ -0,0 +1,119 @@
v=0
o=- 2629706501878304631 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=extmap-allow-mixed
a=msid-semantic: WMS ARDAMS
m=video 9 UDP/TLS/RTP/SAVPF 96 97 39 40 98 99 127 103 104 105 106 107 108 43
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:+zOA
a=ice-pwd:q88Il7adjqdu6SeSqAdsv8lv
a=ice-options:trickle renomination
a=fingerprint:sha-256 8D:0B:86:0B:8C:DF:22:99:85:DD:60:41:AA:FE:F1:98:FE:FB:29:32:5C:11:6E:47:54:82:21:6C:A0:4F:60:B2
a=setup:actpass
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 urn:3gpp:video-orientation
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:ARDAMS ARDAMSv0
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:39 AV1/90000
a=rtcp-fb:39 goog-remb
a=rtcp-fb:39 transport-cc
a=rtcp-fb:39 ccm fir
a=rtcp-fb:39 nack
a=rtcp-fb:39 nack pli
a=rtpmap:40 rtx/90000
a=fmtp:40 apt=39
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:127 H264/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:103 rtx/90000
a=fmtp:103 apt=127
a=rtpmap:104 H265/90000
a=rtcp-fb:104 goog-remb
a=rtcp-fb:104 transport-cc
a=rtcp-fb:104 ccm fir
a=rtcp-fb:104 nack
a=rtcp-fb:104 nack pli
a=rtpmap:105 rtx/90000
a=fmtp:105 apt=104
a=rtpmap:106 red/90000
a=rtpmap:107 rtx/90000
a=fmtp:107 apt=106
a=rtpmap:108 ulpfec/90000
a=rtpmap:43 flexfec-03/90000
a=rtcp-fb:43 goog-remb
a=rtcp-fb:43 transport-cc
a=fmtp:43 repair-window=10000000
a=ssrc-group:FID 129065984 1082217256
a=ssrc-group:FEC-FR 129065984 4071693473
a=ssrc:129065984 cname:T1fpJkmBNuVUtXM8
a=ssrc:129065984 msid:ARDAMS ARDAMSv0
a=ssrc:1082217256 cname:T1fpJkmBNuVUtXM8
a=ssrc:1082217256 msid:ARDAMS ARDAMSv0
a=ssrc:4071693473 cname:T1fpJkmBNuVUtXM8
a=ssrc:4071693473 msid:ARDAMS ARDAMSv0
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 102 0 8 13 110 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:+zOA
a=ice-pwd:q88Il7adjqdu6SeSqAdsv8lv
a=ice-options:trickle renomination
a=fingerprint:sha-256 8D:0B:86:0B:8C:DF:22:99:85:DD:60:41:AA:FE:F1:98:FE:FB:29:32:5C:11:6E:47:54:82:21:6C:A0:4F:60:B2
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
a=sendrecv
a=msid:ARDAMS ARDAMSa0
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=rtpmap:9 G722/8000
a=rtpmap:102 ILBC/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:126 telephone-event/8000
a=ssrc:2019904101 cname:T1fpJkmBNuVUtXM8
a=ssrc:2019904101 msid:ARDAMS ARDAMSa0

View File

@ -12,5 +12,12 @@
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application>
<service android:name="com.zlm.rtc.push.ScreenShareService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="mediaProjection"/>
</application>
</manifest>

View File

@ -1,9 +1,14 @@
package com.zlm.rtc
import android.graphics.Bitmap
import com.zlm.rtc.push.PushMode
import org.webrtc.SurfaceViewRenderer
abstract class ZLMRTCPusher {
abstract fun push(app: String, streamId: String)
public abstract fun bind(surface: SurfaceViewRenderer, localPreview: Boolean)
abstract fun push(app: String, streamId: String, mode: PushMode = PushMode.CAMERA)
abstract fun stop()

View File

@ -0,0 +1,53 @@
package com.zlm.rtc.base;
import android.content.Context;
import android.content.Intent;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
public class ActivityLauncher {
private static final String TAG = "ActivityLauncher";
private Context mContext;
private RouterFragment mRouterFragment;
public static ActivityLauncher init(FragmentActivity activity) {
return new ActivityLauncher(activity);
}
private ActivityLauncher(FragmentActivity activity) {
mContext = activity;
mRouterFragment = getRouterFragment(activity);
}
private RouterFragment getRouterFragment(FragmentActivity activity) {
RouterFragment routerFragment = findRouterFragment(activity);
if (routerFragment == null) {
routerFragment = RouterFragment.newInstance();
FragmentManager fragmentManager = activity.getSupportFragmentManager();
fragmentManager
.beginTransaction()
.add(routerFragment, TAG)
.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
}
return routerFragment;
}
private RouterFragment findRouterFragment(FragmentActivity activity) {
return (RouterFragment) activity.getSupportFragmentManager().findFragmentByTag(TAG);
}
public void startActivityForResult(Class<?> clazz, Callback callback) {
Intent intent = new Intent(mContext, clazz);
startActivityForResult(intent, callback);
}
public void startActivityForResult(Intent intent, Callback callback) {
mRouterFragment.startActivityForResult(intent, callback);
}
public interface Callback {
void onActivityResult(int resultCode, Intent data);
}
}

View File

@ -0,0 +1,60 @@
package com.zlm.rtc.base;
import android.content.Intent;
import android.os.Bundle;
import android.util.SparseArray;
import androidx.fragment.app.Fragment;
import java.util.Random;
public class RouterFragment extends Fragment {
private SparseArray<ActivityLauncher.Callback> mCallbacks = new SparseArray<>();
private Random mCodeGenerator = new Random();
public RouterFragment() {
// Required empty public constructor
}
public static RouterFragment newInstance() {
return new RouterFragment();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void startActivityForResult(Intent intent, ActivityLauncher.Callback callback) {
int requestCode = makeRequestCode();
mCallbacks.put(requestCode, callback);
startActivityForResult(intent, requestCode);
}
/**
* 随机生成唯一的requestCode最多尝试10次
*
* @return
*/
private int makeRequestCode() {
int requestCode;
int tryCount = 0;
do {
requestCode = mCodeGenerator.nextInt(0x0000FFFF);
tryCount++;
} while (mCallbacks.indexOfKey(requestCode) >= 0 && tryCount < 10);
return requestCode;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
ActivityLauncher.Callback callback = mCallbacks.get(requestCode);
mCallbacks.remove(requestCode);
if (callback != null) {
callback.onActivityResult(resultCode, data);
}
}
}

View File

@ -636,7 +636,7 @@ public class PeerConnectionClient {
// Create SDP constraints.
sdpMediaConstraints = new MediaConstraints();
sdpMediaConstraints.mandatory.add(
new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
new MediaConstraints.KeyValuePair("OfferToReceiveAudio", Boolean.toString(true)));
sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
"OfferToReceiveVideo", Boolean.toString(true)));//这里
}
@ -692,8 +692,10 @@ public class PeerConnectionClient {
if (saveVideoFileRecorder == null) {
saveVideoFileRecorder = new VideoFileRecorder();
}
// peerConnection.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO,new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_RECV));
// peerConnection.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO,new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_RECV));
Log.d(TAG, "Peer connection created.");
}

View File

@ -0,0 +1,5 @@
package com.zlm.rtc.push
enum class PushMode {
CAMERA, SCREEN, FILE
}

View File

@ -0,0 +1,66 @@
package com.zlm.rtc.push
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.graphics.BitmapFactory
import android.media.projection.MediaProjectionManager
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import com.zlm.rtc.R
class ScreenShareService :Service(){
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val resultCode = intent?.getIntExtra("resultCode", -1)
val resultData = intent?.getParcelableExtra<Intent>("data")
createNotificationChannel()
resultCode?.let {code->
resultData?.let { data->
val mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
val mediaProjection = mediaProjectionManager.getMediaProjection(code, data);
}
}
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
"ScreenShared", "屏幕录制",
NotificationManager.IMPORTANCE_DEFAULT
)
val manager = getSystemService(
NotificationManager::class.java
)
manager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(this, "ScreenShared")
.setContentTitle("屏幕分享")
.setContentText("分享中...")
.setSmallIcon(R.drawable.icon_screen_share)
.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.icon_screen_share))
.setAutoCancel(true)
.build()
startForeground(1024, notification)
}
}

View File

@ -1,11 +1,16 @@
package com.zlm.rtc.push
import android.app.Activity
import android.content.Context
import android.graphics.Bitmap
import android.media.AudioManager
import android.content.Intent
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import com.zlm.rtc.NativeLib
import com.zlm.rtc.ZLMRTCPusher
import com.zlm.rtc.base.ActivityLauncher
import com.zlm.rtc.client.HttpClient
import com.zlm.rtc.client.PeerConnectionClient
import org.json.JSONObject
@ -15,6 +20,7 @@ import org.webrtc.CameraEnumerator
import org.webrtc.EglBase
import org.webrtc.IceCandidate
import org.webrtc.PeerConnectionFactory
import org.webrtc.ScreenCapturerAndroid
import org.webrtc.SessionDescription
import org.webrtc.StatsReport
import org.webrtc.SurfaceViewRenderer
@ -22,7 +28,7 @@ import org.webrtc.VideoCapturer
import java.math.BigInteger
import kotlin.random.Random
class ZLMRTCPusherImpl(val context:Context) :ZLMRTCPusher(),
class ZLMRTCPusherImpl(val context: FragmentActivity) : ZLMRTCPusher(),
PeerConnectionClient.PeerConnectionEvents {
@ -39,6 +45,10 @@ class ZLMRTCPusherImpl(val context:Context) :ZLMRTCPusher(),
private var app: String = ""
private var streamId: String = ""
private val CAPTURE_PERMISSION_REQUEST_CODE = 1
private fun initPeerConnectionClient(): PeerConnectionClient {
eglBase = EglBase.create()
return PeerConnectionClient(
@ -47,19 +57,19 @@ class ZLMRTCPusherImpl(val context:Context) :ZLMRTCPusher(),
true,
false,
false,
1280,
720,
defaultFps,
1024 * 1000 * 2,
"H264",
true,
0,
0,
0,
0,
"VP8",
true,
false,
0,
"OPUS",
true,
false,
false,
false,
false,
true,
false,
false,
false,
@ -74,6 +84,18 @@ class ZLMRTCPusherImpl(val context:Context) :ZLMRTCPusher(),
} else {
createCameraCapture(Camera1Enumerator(true))
}
return videoCapturer
}
private fun createScreenCapture(context: Context?): VideoCapturer? {
val videoCapturer: VideoCapturer? = if (Camera2Enumerator.isSupported(context)) {
createCameraCapture(Camera2Enumerator(context))
} else {
createCameraCapture(Camera1Enumerator(true))
}
return videoCapturer
}
@ -113,7 +135,12 @@ class ZLMRTCPusherImpl(val context:Context) :ZLMRTCPusher(),
override fun push(app: String, streamId: String) {
override fun bind(surface: SurfaceViewRenderer, localPreview: Boolean) {
this.surfaceViewRenderer = surface
}
override fun push(app: String, streamId: String, mode: PushMode) {
this.app = app
this.streamId = streamId
if (peerConnectionClient == null) peerConnectionClient = initPeerConnectionClient()
@ -121,10 +148,41 @@ class ZLMRTCPusherImpl(val context:Context) :ZLMRTCPusher(),
peerConnectionClient?.setAudioEnabled(true)
peerConnectionClient?.setVideoEnabled(true)
peerConnectionClient?.createPeerConnectionFactory(PeerConnectionFactory.Options())
peerConnectionClient?.createPeerConnection(createVideoCapture(context), localHandleId)
peerConnectionClient?.createOffer(localHandleId)
}
if (mode == PushMode.CAMERA) {
peerConnectionClient?.createPeerConnection(createVideoCapture(context), localHandleId)
peerConnectionClient?.createOffer(localHandleId)
} else if (mode == PushMode.SCREEN) {
val mediaProjectionManager = context.getSystemService(
Context.MEDIA_PROJECTION_SERVICE
) as MediaProjectionManager
ActivityLauncher.init(context).startActivityForResult(
mediaProjectionManager.createScreenCaptureIntent()
) { resultCode, data ->
if (resultCode == Activity.RESULT_OK) {
ContextCompat.startForegroundService(context, Intent(context, ScreenShareService::class.java))
val screenCapturerAndroid =
ScreenCapturerAndroid(data, object : MediaProjection.Callback() {
})
peerConnectionClient?.createPeerConnection(screenCapturerAndroid, localHandleId)
peerConnectionClient?.createOffer(localHandleId)
}
}
} else {
}
}
override fun stop() {
surfaceViewRenderer?.clearImage()
@ -144,6 +202,7 @@ class ZLMRTCPusherImpl(val context:Context) :ZLMRTCPusher(),
mutableMapOf()
)
val result = JSONObject(doPost)
logger(result.toString())
val code = result.getInt("code")
if (code == 0) {
logger("handleId: $doPost")
@ -155,11 +214,12 @@ class ZLMRTCPusherImpl(val context:Context) :ZLMRTCPusher(),
} else {
val msg = result.getString("msg")
logger("handleId: $msg")
peerConnectionClient?.setAudioEnabled(false)
peerConnectionClient?.setVideoEnabled(false)
}
}
override fun onIceCandidate(handleId: BigInteger?, candidate: IceCandidate?) {
}
override fun onIceCandidatesRemoved(

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:viewportHeight="1024"
android:viewportWidth="1024" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#333333" android:pathData="M740.4,709.3H298.7a98.2,98.2 0,0 1,-98.1 -98.1V360.2A98.2,98.2 0,0 1,298.7 262.1h426.7a98.2,98.2 0,0 1,98.1 98.1v266a12.8,12.8 0,0 1,-25.6 0V360.2a72.6,72.6 0,0 0,-72.5 -72.5H298.7a72.6,72.6 0,0 0,-72.5 72.5v251a72.6,72.6 0,0 0,72.5 72.5h441.7a12.8,12.8 0,0 1,0 25.6zM612.2,761.9H401.1a12.8,12.8 0,1 1,0 -25.6h211.1a12.8,12.8 0,0 1,0 25.6z"/>
<path android:fillColor="#0098F0" android:pathData="M649.2,483.3L531.6,390v54.8c-147,0.7 -156.8,136.5 -156.8,136.5 49,-68.3 156.8,-58.9 156.8,-58.9v54.3z"/>
</vector>