From 4f3852dcbfcbf4087a32d91313d50a9e3bd97371 Mon Sep 17 00:00:00 2001 From: Dima Rostopira Date: Fri, 2 Dec 2022 14:07:09 +0300 Subject: [PATCH] Add rotation and change track for Android MediaRecorder --- .../cloudwebrtc/webrtc/GetUserMediaImpl.java | 12 +++++++++-- .../webrtc/MethodCallHandlerImpl.java | 12 ++++++++++- .../webrtc/record/MediaRecorderImpl.java | 20 ++++++++++++++++--- .../webrtc/record/VideoFileRenderer.java | 4 ++-- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/android/src/main/java/com/cloudwebrtc/webrtc/GetUserMediaImpl.java b/android/src/main/java/com/cloudwebrtc/webrtc/GetUserMediaImpl.java index 21e43322d6..943cf19cfc 100755 --- a/android/src/main/java/com/cloudwebrtc/webrtc/GetUserMediaImpl.java +++ b/android/src/main/java/com/cloudwebrtc/webrtc/GetUserMediaImpl.java @@ -828,10 +828,11 @@ public void onCameraSwitchError(String s) { * @param path to the file for record * @param videoTrack to record or null if only audio needed * @param audioChannel channel for recording or null + * @param rotation additional rotation of the resulting video * @throws Exception lot of different exceptions, pass back to dart layer to print them at least */ void startRecordingToFile( - String path, Integer id, @Nullable VideoTrack videoTrack, @Nullable AudioChannel audioChannel) + String path, Integer id, @Nullable VideoTrack videoTrack, @Nullable AudioChannel audioChannel, @Nullable Integer rotation) throws Exception { AudioSamplesInterceptor interceptor = null; if (audioChannel == AudioChannel.INPUT) { @@ -842,11 +843,18 @@ void startRecordingToFile( } interceptor = outputSamplesInterceptor; } - MediaRecorderImpl mediaRecorder = new MediaRecorderImpl(id, videoTrack, interceptor); + MediaRecorderImpl mediaRecorder = new MediaRecorderImpl(id, videoTrack, interceptor, rotation); mediaRecorder.startRecording(new File(path)); mediaRecorders.append(id, mediaRecorder); } + public void changeRecorderTrack(VideoTrack track, Integer recorderId) { + final MediaRecorderImpl mr = mediaRecorders.get(recorderId); + if (mr != null) { + mr.switchVideoTrack(track); + } + } + void stopRecording(Integer id) { MediaRecorderImpl mediaRecorder = mediaRecorders.get(id); if (mediaRecorder != null) { diff --git a/android/src/main/java/com/cloudwebrtc/webrtc/MethodCallHandlerImpl.java b/android/src/main/java/com/cloudwebrtc/webrtc/MethodCallHandlerImpl.java index 7cb6bb9260..59dbaff98e 100644 --- a/android/src/main/java/com/cloudwebrtc/webrtc/MethodCallHandlerImpl.java +++ b/android/src/main/java/com/cloudwebrtc/webrtc/MethodCallHandlerImpl.java @@ -527,8 +527,9 @@ public void onMethodCall(MethodCall call, @NonNull Result notSafeResult) { audioChannel = AudioChannel.values()[(Integer) call.argument("audioChannel")]; } Integer recorderId = call.argument("recorderId"); + Integer rotation = call.argument("rotation"); if (videoTrack != null || audioChannel != null) { - getUserMediaImpl.startRecordingToFile(path, recorderId, videoTrack, audioChannel); + getUserMediaImpl.startRecordingToFile(path, recorderId, videoTrack, audioChannel, rotation); result.success(null); } else { resultError("startRecordToFile", "No tracks", result); @@ -537,6 +538,15 @@ public void onMethodCall(MethodCall call, @NonNull Result notSafeResult) { resultError("startRecordToFile", e.getMessage(), result); } break; + case "changeRecorderTrack": + String videoTrackId = call.argument("videoTrackId"); + Integer recorderId = call.argument("recorderId"); + MediaStreamTrack track = getTrackForId(videoTrackId); + if (track instanceof VideoTrack) { + getUserMediaImpl.changeRecorderTrack((VideoTrack) track, recorderId); + } + result.success(null); + break; case "stopRecordToFile": Integer recorderId = call.argument("recorderId"); getUserMediaImpl.stopRecording(recorderId); diff --git a/android/src/main/java/com/cloudwebrtc/webrtc/record/MediaRecorderImpl.java b/android/src/main/java/com/cloudwebrtc/webrtc/record/MediaRecorderImpl.java index f1c45357bc..dc3f00bac3 100644 --- a/android/src/main/java/com/cloudwebrtc/webrtc/record/MediaRecorderImpl.java +++ b/android/src/main/java/com/cloudwebrtc/webrtc/record/MediaRecorderImpl.java @@ -12,16 +12,21 @@ public class MediaRecorderImpl { private final Integer id; - private final VideoTrack videoTrack; + private VideoTrack videoTrack; private final AudioSamplesInterceptor audioInterceptor; private VideoFileRenderer videoFileRenderer; private boolean isRunning = false; private File recordFile; + private final int rotation; - public MediaRecorderImpl(Integer id, @Nullable VideoTrack videoTrack, @Nullable AudioSamplesInterceptor audioInterceptor) { + public MediaRecorderImpl(Integer id, @Nullable VideoTrack videoTrack, @Nullable AudioSamplesInterceptor audioInterceptor, @Nullable Integer rotation) { this.id = id; this.videoTrack = videoTrack; this.audioInterceptor = audioInterceptor; + if (rotation != null) + this.rotation = rotation; + else + this.rotation = 0; } public void startRecording(File file) throws Exception { @@ -35,7 +40,8 @@ public void startRecording(File file) throws Exception { videoFileRenderer = new VideoFileRenderer( file.getAbsolutePath(), EglUtils.getRootEglBaseContext(), - audioInterceptor != null + audioInterceptor != null, + rotation ); videoTrack.addSink(videoFileRenderer); if (audioInterceptor != null) @@ -49,6 +55,14 @@ public void startRecording(File file) throws Exception { } } + public void switchVideoTrack(VideoTrack newVideoTrack) { + if (videoTrack != null) { + videoTrack.removeSink(videoFileRenderer); + } + newVideoTrack.addSink(videoFileRenderer); + videoTrack = newVideoTrack; + } + public File getRecordFile() { return recordFile; } public void stopRecording() { diff --git a/android/src/main/java/com/cloudwebrtc/webrtc/record/VideoFileRenderer.java b/android/src/main/java/com/cloudwebrtc/webrtc/record/VideoFileRenderer.java index 3ec5dd3e0c..6baf87b388 100644 --- a/android/src/main/java/com/cloudwebrtc/webrtc/record/VideoFileRenderer.java +++ b/android/src/main/java/com/cloudwebrtc/webrtc/record/VideoFileRenderer.java @@ -50,7 +50,7 @@ class VideoFileRenderer implements VideoSink, SamplesReadyCallback { private Surface surface; private MediaCodec audioEncoder; - VideoFileRenderer(String outputFile, final EglBase.Context sharedContext, boolean withAudio) throws IOException { + VideoFileRenderer(String outputFile, final EglBase.Context sharedContext, boolean withAudio, int rotation) throws IOException { renderThread = new HandlerThread(TAG + "RenderThread"); renderThread.start(); renderThreadHandler = new Handler(renderThread.getLooper()); @@ -70,7 +70,7 @@ class VideoFileRenderer implements VideoSink, SamplesReadyCallback { // obtained from the encoder after it has started processing data. mediaMuxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); - + mediaMuxer.setOrientationHint(rotation % 360); audioTrackIndex = withAudio ? -1 : 0; }