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 @@
+
+
+
+