Skip to content

Commit 3e80e4f

Browse files
wjaykim8BallBomBom
authored andcommitted
Multiple video frame processors (+ implementation for ios) (react-native-webrtc#1331)
* feat(ios): add video frame processor * fix(ios): add missing capturer property * feat(android): support multiple VideoFrameProcessors * Update MediaStreamTrack.ts Quick lint fix. --------- Co-authored-by: Johnathon Weaver <weaver123_johnathon@hotmail.com>
1 parent ad71581 commit 3e80e4f

12 files changed

+164
-28
lines changed

android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@
2525

2626
import java.util.ArrayList;
2727
import java.util.HashMap;
28+
import java.util.List;
2829
import java.util.Map;
30+
import java.util.Objects;
2931
import java.util.UUID;
32+
import java.util.stream.Collectors;
3033

3134
/**
3235
* The implementation of {@code getUserMedia} extracted into a separate file in
@@ -388,28 +391,33 @@ VideoTrack createVideoTrack(AbstractVideoCaptureController videoCaptureControlle
388391
}
389392

390393
/**
391-
* Set video effect to the TrackPrivate corresponding to the trackId with the help of VideoEffectProcessor
392-
* corresponding to the name.
394+
* Set video effects to the TrackPrivate corresponding to the trackId with the help of VideoEffectProcessor
395+
* corresponding to the names.
393396
* @param trackId TrackPrivate id
394-
* @param name VideoEffectProcessor name
397+
* @param names VideoEffectProcessor names
395398
*/
396-
void setVideoEffect(String trackId, String name) {
399+
void setVideoEffects(String trackId, ReadableArray names) {
397400
TrackPrivate track = tracks.get(trackId);
398401

399402
if (track != null && track.videoCaptureController instanceof CameraCaptureController) {
400403
VideoSource videoSource = (VideoSource) track.mediaSource;
401404
SurfaceTextureHelper surfaceTextureHelper = track.surfaceTextureHelper;
402405

403-
if (name != null) {
404-
VideoFrameProcessor videoFrameProcessor = ProcessorProvider.getProcessor(name);
405-
406-
if (videoFrameProcessor == null) {
407-
Log.e(TAG, "no videoFrameProcessor associated with this name");
408-
return;
409-
}
406+
if (names != null) {
407+
List<VideoFrameProcessor> processors = names.toArrayList().stream()
408+
.filter(name -> name instanceof String)
409+
.map(name -> {
410+
VideoFrameProcessor videoFrameProcessor = ProcessorProvider.getProcessor((String) name);
411+
if (videoFrameProcessor == null) {
412+
Log.e(TAG, "no videoFrameProcessor associated with this name: " + name);
413+
}
414+
return videoFrameProcessor;
415+
})
416+
.filter(Objects::nonNull)
417+
.collect(Collectors.toList());
410418

411419
VideoEffectProcessor videoEffectProcessor =
412-
new VideoEffectProcessor(videoFrameProcessor, surfaceTextureHelper);
420+
new VideoEffectProcessor(processors, surfaceTextureHelper);
413421
videoSource.setVideoProcessor(videoEffectProcessor);
414422

415423
} else {

android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -942,9 +942,12 @@ private ReadableArray getTransceiversInfo(PeerConnection peerConnection) {
942942
return transceiverUpdates;
943943
}
944944

945+
945946
@ReactMethod
946-
public void mediaStreamTrackSetVideoEffect(String id, String name) {
947-
ThreadUtils.runOnExecutor(() -> { getUserMediaImpl.setVideoEffect(id, name); });
947+
public void mediaStreamTrackSetVideoEffects(String id, ReadableArray names) {
948+
ThreadUtils.runOnExecutor(() -> {
949+
getUserMediaImpl.setVideoEffects(id, names);
950+
});
948951
}
949952

950953
@ReactMethod

android/src/main/java/com/oney/WebRTCModule/videoEffects/VideoEffectProcessor.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@
55
import org.webrtc.VideoProcessor;
66
import org.webrtc.VideoSink;
77

8+
import java.util.List;
9+
810
/**
911
* Lightweight abstraction for an object that can receive video frames, process and add effects in
1012
* them, and pass them on to another object.
1113
*/
1214
public class VideoEffectProcessor implements VideoProcessor {
1315
private VideoSink mSink;
1416
final private SurfaceTextureHelper textureHelper;
15-
final private VideoFrameProcessor videoFrameProcessor;
17+
final private List<VideoFrameProcessor> videoFrameProcessors;
1618

17-
public VideoEffectProcessor(VideoFrameProcessor processor, SurfaceTextureHelper textureHelper) {
19+
public VideoEffectProcessor(List<VideoFrameProcessor> processors, SurfaceTextureHelper textureHelper) {
1820
this.textureHelper = textureHelper;
19-
this.videoFrameProcessor = processor;
21+
this.videoFrameProcessors = processors;
2022
}
2123

2224
@Override
@@ -39,15 +41,12 @@ public void setSink(VideoSink sink) {
3941
@Override
4042
public void onFrameCaptured(VideoFrame frame) {
4143
frame.retain();
42-
VideoFrame outputFrame = videoFrameProcessor.process(frame, textureHelper);
43-
44-
if (outputFrame == null) {
45-
mSink.onFrame(frame);
46-
frame.release();
47-
return;
44+
VideoFrame outputFrame = frame;
45+
for (VideoFrameProcessor processor : this.videoFrameProcessors) {
46+
outputFrame = processor.process(outputFrame, textureHelper);
4847
}
48+
4949
mSink.onFrame(outputFrame);
5050
outputFrame.release();
51-
frame.release();
5251
}
5352
}

ios/RCTWebRTC/VideoCaptureController.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
#import "CaptureController.h"
77

88
@interface VideoCaptureController : CaptureController
9-
@property(nonatomic, readonly, strong) AVCaptureDeviceFormat *selectedFormat;
10-
@property(nonatomic, readonly, assign) int frameRate;
9+
10+
@property (nonatomic, readonly, strong) RTCCameraVideoCapturer *capturer;
11+
@property (nonatomic, readonly, strong) AVCaptureDeviceFormat *selectedFormat;
12+
@property (nonatomic, readonly, assign) int frameRate;
1113

1214
- (instancetype)initWithCapturer:(RTCCameraVideoCapturer *)capturer andConstraints:(NSDictionary *)constraints;
1315
- (void)startCapture;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
#import "CaptureController.h"
22
#import "WebRTCModule.h"
3+
#import "VideoEffectProcessor.h"
34

45
@interface WebRTCModule (RTCMediaStream)
6+
7+
@property (nonatomic, strong) VideoEffectProcessor *videoEffectProcessor;
8+
59
- (RTCVideoTrack *)createVideoTrackWithCaptureController:
610
(CaptureController * (^)(RTCVideoSource *))captureControllerCreator;
711
- (NSArray *)createMediaStream:(NSArray<RTCMediaStreamTrack *> *)tracks;
12+
813
@end

ios/RCTWebRTC/WebRTCModule+RTCMediaStream.m

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,24 @@
99
#import "WebRTCModule+RTCMediaStream.h"
1010
#import "WebRTCModule+RTCPeerConnection.h"
1111

12+
#import "ProcessorProvider.h"
1213
#import "ScreenCaptureController.h"
1314
#import "ScreenCapturer.h"
1415
#import "TrackCapturerEventsEmitter.h"
1516
#import "VideoCaptureController.h"
1617

1718
@implementation WebRTCModule (RTCMediaStream)
1819

20+
- (VideoEffectProcessor *)videoEffectProcessor
21+
{
22+
return objc_getAssociatedObject(self, _cmd);
23+
}
24+
25+
- (void)setVideoEffectProcessor:(VideoEffectProcessor *)videoEffectProcessor
26+
{
27+
objc_setAssociatedObject(self, @selector(videoEffectProcessor), videoEffectProcessor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
28+
}
29+
1930
#pragma mark - getUserMedia
2031

2132
/**
@@ -411,6 +422,31 @@ - (RTCVideoTrack *)createScreenCaptureVideoTrack {
411422
}
412423
}
413424

425+
RCT_EXPORT_METHOD(mediaStreamTrackSetVideoEffects:(nonnull NSString *)trackID names:(nonnull NSArray<NSString *> *)names)
426+
{
427+
RTCMediaStreamTrack *track = self.localTracks[trackID];
428+
if (track) {
429+
RTCVideoTrack *videoTrack = (RTCVideoTrack *)track;
430+
RTCVideoSource *videoSource = videoTrack.source;
431+
432+
NSMutableArray *processors = [[NSMutableArray alloc] init];
433+
for (NSString *name in names) {
434+
NSObject<VideoFrameProcessorDelegate> *processor = [ProcessorProvider getProcessor:name];
435+
if (processor != nil) {
436+
[processors addObject:processor];
437+
}
438+
}
439+
440+
self.videoEffectProcessor = [[VideoEffectProcessor alloc] initWithProcessors:processors
441+
videoSource:videoSource];
442+
443+
VideoCaptureController *vcc = (VideoCaptureController *)videoTrack.captureController;
444+
RTCVideoCapturer *capturer = vcc.capturer;
445+
446+
capturer.delegate = self.videoEffectProcessor;
447+
}
448+
}
449+
414450
#pragma mark - Helpers
415451

416452
- (RTCMediaStreamTrack *)trackForId:(nonnull NSString *)trackId pcId:(nonnull NSNumber *)pcId {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#import "VideoFrameProcessor.h"
2+
3+
@interface ProcessorProvider : NSObject
4+
5+
+ (NSObject<VideoFrameProcessorDelegate> *)getProcessor:(NSString *)name;
6+
+ (void)addProcessor:(NSObject<VideoFrameProcessorDelegate> *)processor
7+
forName:(NSString *)name;
8+
+ (void)removeProcessor:(NSString *)name;
9+
10+
@end
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#import "ProcessorProvider.h"
2+
3+
@implementation ProcessorProvider
4+
5+
static NSMutableDictionary<NSString *, NSObject<VideoFrameProcessorDelegate> *> *processorMap;
6+
7+
+ (void)initialize {
8+
processorMap = [[NSMutableDictionary alloc] init];
9+
}
10+
11+
+ (NSObject<VideoFrameProcessorDelegate> *)getProcessor:(NSString *)name {
12+
return [processorMap objectForKey:name];
13+
}
14+
15+
+ (void)addProcessor:(NSObject<VideoFrameProcessorDelegate> *)processor
16+
forName:(NSString *)name {
17+
[processorMap setObject:processor forKey:name];
18+
}
19+
20+
+ (void)removeProcessor:(NSString *)name {
21+
[processorMap removeObjectForKey:name];
22+
}
23+
24+
@end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#import <WebRTC/RTCVideoSource.h>
2+
3+
#import "VideoFrameProcessor.h"
4+
5+
@interface VideoEffectProcessor : NSObject<RTCVideoCapturerDelegate>
6+
7+
@property (nonatomic, strong) NSArray<NSObject<VideoFrameProcessorDelegate> *> *videoFrameProcessors;
8+
@property (nonatomic, strong) RTCVideoSource *videoSource;
9+
10+
- (instancetype)initWithProcessors:(NSArray<NSObject<VideoFrameProcessorDelegate> *> *)videoFrameProcessors
11+
videoSource:(RTCVideoSource *)videoSource;
12+
13+
@end
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#import <WebRTC/RTCVideoCapturer.h>
2+
3+
#import "VideoEffectProcessor.h"
4+
5+
@implementation VideoEffectProcessor
6+
7+
- (instancetype)initWithProcessors:(NSArray<NSObject<VideoFrameProcessorDelegate> *> *)videoFrameProcessors
8+
videoSource:(RTCVideoSource *)videoSource {
9+
self = [super init];
10+
_videoFrameProcessors = videoFrameProcessors;
11+
_videoSource = videoSource;
12+
return self;
13+
}
14+
15+
- (void)capturer:(nonnull RTCVideoCapturer *)capturer didCaptureVideoFrame:(nonnull RTCVideoFrame *)frame {
16+
RTCVideoFrame *processedFrame = frame;
17+
for (NSObject<VideoFrameProcessorDelegate> *processor in _videoFrameProcessors) {
18+
processedFrame = [processor capturer:capturer didCaptureVideoFrame:processedFrame];
19+
}
20+
[self.videoSource capturer:capturer didCaptureVideoFrame:processedFrame];
21+
}
22+
23+
@end

0 commit comments

Comments
 (0)