22
33import android .content .Context ;
44import android .content .Intent ;
5+ import android .content .res .AssetManager ;
56import android .os .Bundle ;
67import android .os .Handler ;
78import android .os .HandlerThread ;
1415import android .widget .Button ;
1516import android .widget .FrameLayout ;
1617import androidx .appcompat .app .AppCompatActivity ;
18+
19+ import com .piasy .avconf .AudioMixer ;
1720import com .piasy .avconf .view .FreezeAwareRenderer ;
1821
22+ import java .io .File ;
23+ import java .io .FileOutputStream ;
24+ import java .io .IOException ;
25+ import java .io .InputStream ;
1926import java .util .Collections ;
2027import java .util .List ;
2128
4754import static com .piasy .kmp .webrtc .android .HallActivity .EXTRA_VIDEO_HEIGHT ;
4855import static com .piasy .kmp .webrtc .android .HallActivity .EXTRA_VIDEO_WIDTH ;
4956
50- public class CallActivity extends AppCompatActivity implements PeerConnectionClientCallback {
57+ public class CallActivity extends AppCompatActivity implements PeerConnectionClientCallback , AudioMixer . MixerCallback {
5158
5259 private static final String TAG = "CallActivity" ;
5360
5461 private static final boolean SAVE_YUV_FRAME = false ;
55- private static final boolean LOCAL_RECORD = false ;
5662 private static final boolean PAUSE_STREAMING = true ;
5763
5864 private boolean isPublisher ;
@@ -66,8 +72,8 @@ public class CallActivity extends AppCompatActivity implements PeerConnectionCli
6672 private Button recordButton ;
6773 private boolean recording ;
6874
69- private Button recordLButton ;
70- private boolean localRecording = false ;
75+ private Button mixerButton ;
76+ private boolean mixing ;
7177
7278 private Button pauseButton ;
7379 private boolean streamingPaused = false ;
@@ -82,6 +88,7 @@ public class CallActivity extends AppCompatActivity implements PeerConnectionCli
8288 private EglBase eglBase ;
8389 private PeerConnectionClientFactory pcClientFactory ;
8490 private PeerConnectionClient pcClient ;
91+ private AudioMixer mixer ;
8592
8693 private static int getSystemUiVisibility () {
8794 return View .SYSTEM_UI_FLAG_HIDE_NAVIGATION | View .SYSTEM_UI_FLAG_FULLSCREEN | View .SYSTEM_UI_FLAG_IMMERSIVE_STICKY ;
@@ -129,26 +136,28 @@ protected void onCreate(Bundle savedInstanceState) {
129136 recordButton .setOnClickListener (v -> {
130137 recording = !recording ;
131138 recordButton .setText (recording ? "stop record" : "start record" );
132- // if (recording) {
133- // avConf.startRecording(new StartRecordingCallback() {
134- // @Override
135- // public void onStartSuccess(final String filename) {
136- // Toast.makeText(CallActivity.this,
137- // "start record success: " + filename + ".mkv",
138- // Toast.LENGTH_SHORT).show();
139- // }
140- //
141- // @Override
142- // public void onStartFail() {
143- // Toast.makeText(CallActivity.this, "start record fail",
144- // Toast.LENGTH_SHORT).show();
145- // recording = false;
146- // recordButton.setText("start record");
147- // }
148- // });
149- // } else {
150- // avConf.stopRecording();
151- // }
139+ if (recording ) {
140+ File path = new File (getExternalFilesDir (null ), "send.mkv" );
141+ pcClient .startRecorder (PeerConnectionClient .DIR_SEND_ONLY , path .getAbsolutePath ());
142+ } else {
143+ pcClient .stopRecorder (PeerConnectionClient .DIR_SEND_ONLY );
144+ }
145+ });
146+
147+ mixerButton = findViewById (R .id .btnMixer );
148+ mixerButton .setText (mixing ? "stop mixer" : "start mixer" );
149+ mixerButton .setOnClickListener (v -> {
150+ mixing = !mixing ;
151+ mixerButton .setText (mixing ? "stop mixer" : "start mixer" );
152+ if (mixing ) {
153+ File path = new File (getExternalFilesDir (null ), "mozart.mp3" );
154+ mixer = new AudioMixer (path .getAbsolutePath (), 48000 , 1 , 10000 , false , 5 , this );
155+ mixer .startMixer ();
156+ mixer .toggleMusicStreaming (true );
157+ } else {
158+ mixer .stopMixer ();
159+ mixer = null ;
160+ }
152161 });
153162
154163 Button switchCamera = findViewById (R .id .btnCamera );
@@ -175,22 +184,6 @@ public void run() {
175184// avConf.setVideoEnabled(!audioOnly);
176185 });
177186
178- recordLButton = findViewById (R .id .btnRecordL );
179- if (LOCAL_RECORD ) {
180- recordLButton .setOnClickListener (v -> {
181- localRecording = !localRecording ;
182- recordLButton .setText (localRecording ? "stop record L" : "start record L" );
183- // if (localRecording) {
184- // avConf.startLocalRecording(selfUid, PeerConnectionClient.DIR_SEND_ONLY,
185- // "/sdcard/avconf/record_" + System.currentTimeMillis() + ".mkv");
186- // } else {
187- // avConf.stopLocalRecording(selfUid, PeerConnectionClient.DIR_SEND_ONLY);
188- // }
189- });
190- } else {
191- recordButton .setVisibility (View .GONE );
192- }
193-
194187 pauseButton = findViewById (R .id .btnPauseStreaming );
195188 if (PAUSE_STREAMING ) {
196189 pauseButton .setOnClickListener (v -> {
@@ -216,9 +209,33 @@ public void run() {
216209 localRenderer = createRenderer (true );
217210 remoteRenderer = createRenderer (false );
218211
212+ prepareMusic ();
213+
219214 startLoopback ();
220215 }
221216
217+ private void prepareMusic () {
218+ try {
219+ File path = new File (getExternalFilesDir (null ), "mozart.mp3" );
220+ if (path .exists ()) {
221+ return ;
222+ }
223+ InputStream inputStream = getAssets ().open ("mozart.mp3" );
224+ FileOutputStream outputStream = new FileOutputStream (path );
225+
226+ byte [] buffer = new byte [1024 ];
227+ int length ;
228+ while ((length = inputStream .read (buffer )) > 0 ) {
229+ outputStream .write (buffer , 0 , length );
230+ }
231+ inputStream .close ();
232+ outputStream .close ();
233+ Logging .INSTANCE .info (TAG , "copy music success" );
234+ } catch (IOException e ) {
235+ e .printStackTrace ();
236+ }
237+ }
238+
222239 private FreezeAwareRenderer createRenderer (boolean local ) {
223240 WindowManager windowManager = (WindowManager ) getSystemService (Context .WINDOW_SERVICE );
224241 DisplayMetrics displayMetrics = new DisplayMetrics ();
@@ -237,6 +254,7 @@ private FreezeAwareRenderer createRenderer(boolean local) {
237254 local ? "loopback_local" : "loopback_remote" , FreezeAwareRenderer .SCALE_TYPE_CENTER_CROP );
238255 renderer .init (eglBase .getEglBaseContext (), null );
239256 renderer .setScalingType (RendererCommon .ScalingType .SCALE_ASPECT_FIT );
257+ renderer .setMirror (local );
240258
241259 wrapper .addView (renderer , ViewGroup .LayoutParams .MATCH_PARENT ,
242260 ViewGroup .LayoutParams .MATCH_PARENT );
@@ -318,6 +336,10 @@ public void run() {
318336
319337 // Disconnect from remote resources, dispose of local resources, and exit.
320338 private void disconnect () {
339+ if (mixer != null ) {
340+ mixer .stopMixer ();
341+ mixer = null ;
342+ }
321343 if (pcClientFactory != null ) {
322344 pcClientFactory .stopVideoCapture ();
323345 pcClient .close ();
@@ -339,21 +361,41 @@ private void disconnect() {
339361
340362 @ Override
341363 public void onIceCandidatesRemoved (@ NotNull String peerUid , @ NotNull List <@ NotNull IceCandidate > candidates ) {
342-
343364 }
344365
345366 @ Override
346367 public void onPeerConnectionStatsReady (@ NotNull String peerUid , @ NotNull RtcStatsReport report ) {
347-
348368 }
349369
350370 @ Override
351371 public void onIceDisconnected (@ NotNull String peerUid ) {
352-
353372 }
354373
355374 @ Override
356375 public void onError (@ NotNull String peerUid , int code ) {
376+ }
377+
378+ @ Override
379+ public void onMixerSsrcFinished (int ssrc ) {
380+ Logging .INSTANCE .info (TAG , "onMixerSsrcFinished " + ssrc );
381+ onMixerStopped ();
382+ }
357383
384+ @ Override
385+ public void onMixerSsrcError (int ssrc , int error ) {
386+ Logging .INSTANCE .error (TAG , "onMixerSsrcError " + ssrc + ", " + error );
387+ onMixerStopped ();
388+ }
389+
390+ private void onMixerStopped () {
391+ runOnUiThread (new Runnable () {
392+ @ Override
393+ public void run () {
394+ mixing = false ;
395+ mixerButton .setText ("start mixer" );
396+ mixer .stopMixer ();
397+ mixer = null ;
398+ }
399+ });
358400 }
359401}
0 commit comments