From ff8769f4ac1cea05dd125f3d8e2730175b3517e0 Mon Sep 17 00:00:00 2001 From: li <719947897@ qq.com> Date: Thu, 30 May 2024 16:38:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A1=8C=E9=9D=A2=E6=8E=A8=E6=B5=81ok?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webrtc_player/android/app/build.gradle | 2 + .../zlmediakit/webrtc/PusherDemoActivity.kt | 46 +++++- .../src/main/res/layout/activity_pusher.xml | 2 +- webrtc_player/android/demo.sdp | 142 ++++++++++++++++++ webrtc_player/android/success.sdp | 112 ++++++++++++++ webrtc_player/android/this.sdp | 119 +++++++++++++++ .../android/zlm/src/main/AndroidManifest.xml | 7 + .../src/main/java/com/zlm/rtc/ZLMRTCPusher.kt | 7 +- .../com/zlm/rtc/base/ActivityLauncher.java | 53 +++++++ .../java/com/zlm/rtc/base/RouterFragment.java | 60 ++++++++ .../zlm/rtc/client/PeerConnectionClient.java | 6 +- .../main/java/com/zlm/rtc/push/PushMode.kt | 5 + .../com/zlm/rtc/push/ScreenShareService.kt | 66 ++++++++ .../java/com/zlm/rtc/push/ZLMRTCPusherImpl.kt | 92 ++++++++++-- .../main/res/drawable/icon_screen_share.xml | 5 + 15 files changed, 699 insertions(+), 25 deletions(-) create mode 100644 webrtc_player/android/demo.sdp create mode 100644 webrtc_player/android/success.sdp create mode 100644 webrtc_player/android/this.sdp create mode 100644 webrtc_player/android/zlm/src/main/java/com/zlm/rtc/base/ActivityLauncher.java create mode 100644 webrtc_player/android/zlm/src/main/java/com/zlm/rtc/base/RouterFragment.java create mode 100644 webrtc_player/android/zlm/src/main/java/com/zlm/rtc/push/PushMode.kt create mode 100644 webrtc_player/android/zlm/src/main/java/com/zlm/rtc/push/ScreenShareService.kt create mode 100644 webrtc_player/android/zlm/src/main/res/drawable/icon_screen_share.xml diff --git a/webrtc_player/android/app/build.gradle b/webrtc_player/android/app/build.gradle index 04b816f6..6bacd76e 100644 --- a/webrtc_player/android/app/build.gradle +++ b/webrtc_player/android/app/build.gradle @@ -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' + } \ No newline at end of file diff --git a/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PusherDemoActivity.kt b/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PusherDemoActivity.kt index c5ece879..7b9a9cff 100644 --- a/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PusherDemoActivity.kt +++ b/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PusherDemoActivity.kt @@ -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() } + + } \ No newline at end of file diff --git a/webrtc_player/android/app/src/main/res/layout/activity_pusher.xml b/webrtc_player/android/app/src/main/res/layout/activity_pusher.xml index 9793cba8..81bbe808 100644 --- a/webrtc_player/android/app/src/main/res/layout/activity_pusher.xml +++ b/webrtc_player/android/app/src/main/res/layout/activity_pusher.xml @@ -6,7 +6,7 @@ + + + + \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/ZLMRTCPusher.kt b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/ZLMRTCPusher.kt index ec38b886..9a51f223 100644 --- a/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/ZLMRTCPusher.kt +++ b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/ZLMRTCPusher.kt @@ -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() diff --git a/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/base/ActivityLauncher.java b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/base/ActivityLauncher.java new file mode 100644 index 00000000..7cd05613 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/base/ActivityLauncher.java @@ -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); + } +} diff --git a/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/base/RouterFragment.java b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/base/RouterFragment.java new file mode 100644 index 00000000..ff75ccf4 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/base/RouterFragment.java @@ -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 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); + } + } +} diff --git a/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/client/PeerConnectionClient.java b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/client/PeerConnectionClient.java index f8a0dc46..e901cea4 100644 --- a/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/client/PeerConnectionClient.java +++ b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/client/PeerConnectionClient.java @@ -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."); } diff --git a/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/push/PushMode.kt b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/push/PushMode.kt new file mode 100644 index 00000000..feb81715 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/push/PushMode.kt @@ -0,0 +1,5 @@ +package com.zlm.rtc.push + +enum class PushMode { + CAMERA, SCREEN, FILE +} \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/push/ScreenShareService.kt b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/push/ScreenShareService.kt new file mode 100644 index 00000000..984f6617 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/push/ScreenShareService.kt @@ -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("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) + } +} \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/push/ZLMRTCPusherImpl.kt b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/push/ZLMRTCPusherImpl.kt index 8ccaed9f..e2b1b625 100644 --- a/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/push/ZLMRTCPusherImpl.kt +++ b/webrtc_player/android/zlm/src/main/java/com/zlm/rtc/push/ZLMRTCPusherImpl.kt @@ -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( diff --git a/webrtc_player/android/zlm/src/main/res/drawable/icon_screen_share.xml b/webrtc_player/android/zlm/src/main/res/drawable/icon_screen_share.xml new file mode 100644 index 00000000..dffd6c0c --- /dev/null +++ b/webrtc_player/android/zlm/src/main/res/drawable/icon_screen_share.xml @@ -0,0 +1,5 @@ + + + +