Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,11 @@ public void applyConstraints(ReadableMap constraints, @Nullable Consumer<Excepti
// Find target camera to switch to.
String[] deviceNames = cameraEnumerator.getDeviceNames();

// Use the initial deviceId/facingMode. It is a constraint violation to change these through applyConstraints.
final String deviceId = constraintDeviceId;
final String facingMode = constraintFacingMode;
// Re-read from the incoming constraints so `MediaStreamTrack._switchCamera()`
// can flip the camera via `applyConstraints({facingMode})` — the documented
// W3C pattern that browsers also implement.
final String deviceId = ReactBridgeUtil.getMapStrValue(constraints, "deviceId");
final String facingMode = ReactBridgeUtil.getMapStrValue(constraints, "facingMode");
int cameraIndex = -1;
String cameraName = null;

Expand Down
20 changes: 20 additions & 0 deletions android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,11 @@ void setVideoEffects(String trackId, ReadableArray names) {
VideoSource videoSource = (VideoSource) track.mediaSource;
SurfaceTextureHelper surfaceTextureHelper = track.surfaceTextureHelper;

// Swap first, dispose last — otherwise a frame in flight could hit a freed
// processor. onCapturerStopped can't replace this; it also fires on pauses.
VideoEffectProcessor previousProcessor = track.videoEffectProcessor;
track.videoEffectProcessor = null;

if (names != null) {
List<VideoFrameProcessor> processors =
names.toArrayList()
Expand All @@ -602,10 +607,15 @@ void setVideoEffects(String trackId, ReadableArray names) {

VideoEffectProcessor videoEffectProcessor = new VideoEffectProcessor(processors, surfaceTextureHelper);
videoSource.setVideoProcessor(videoEffectProcessor);
track.videoEffectProcessor = videoEffectProcessor;

} else {
videoSource.setVideoProcessor(null);
}

if (previousProcessor != null) {
previousProcessor.dispose();
}
}
}

Expand Down Expand Up @@ -643,6 +653,9 @@ private static class TrackPrivate {
*/
public final VideoTrackAdapter videoTrackAdapter;

/** Current effect processor, disposed on filter switch and on track teardown. */
public VideoEffectProcessor videoEffectProcessor;

/**
* Whether this object has been disposed or not.
*/
Expand Down Expand Up @@ -693,6 +706,13 @@ public void dispose() {
}
}

// After stopCapture so no frame can still reach it; before
// surfaceTextureHelper dispose so GL is still alive for cleanup.
if (!isClone && videoEffectProcessor != null) {
videoEffectProcessor.dispose();
videoEffectProcessor = null;
}

// Clean up VideoTrackAdapter for video tracks (each TrackPrivate, incl. clones, has its own)
if (videoTrackAdapter != null && track instanceof VideoTrack) {
videoTrackAdapter.removeDimensionDetector((VideoTrack) track);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class VideoEffectProcessor implements VideoProcessor {
private VideoSink mSink;
final private SurfaceTextureHelper textureHelper;
final private List<VideoFrameProcessor> videoFrameProcessors;
private boolean disposed = false;

public VideoEffectProcessor(List<VideoFrameProcessor> processors, SurfaceTextureHelper textureHelper) {
this.textureHelper = textureHelper;
Expand All @@ -27,6 +28,21 @@ public void onCapturerStarted(boolean success) {}
@Override
public void onCapturerStopped() {}

/**
* Disposes each wrapped processor. Posted to the capturer handler so it runs
* after any in-flight frame, so implementations can clean up GL state inline.
* Idempotent. Not wired to {@link #onCapturerStopped} because that also fires on pause.
*/
public void dispose() {
textureHelper.getHandler().post(() -> {
if (disposed) return;
disposed = true;
for (VideoFrameProcessor processor : this.videoFrameProcessors) {
processor.dispose();
}
});
}

@Override
public void setSink(VideoSink sink) {
mSink = sink;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,10 @@ public interface VideoFrameProcessor {
* @return processed videoframe which will rendered
*/
public VideoFrame process(VideoFrame frame, SurfaceTextureHelper textureHelper);

/**
* Called once when this processor leaves the pipeline. Release any native or
* GL resources here. Runs on the capturer handler. Default no-op.
*/
default void dispose() {}
}
26 changes: 26 additions & 0 deletions ios/RCTWebRTC/VideoCaptureController.m
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,32 @@ - (void)determineDevice:(NSDictionary *)constraints {
- (void)applyConstraints:(NSDictionary *)constraints error:(NSError **)outError {
BOOL hasChanged = NO;

// Re-read device-selecting constraints so `MediaStreamTrack._switchCamera()`
// can flip the camera via `applyConstraints({facingMode})` — the documented
// W3C pattern that browsers also implement.
NSString *deviceId = constraints[@"deviceId"];
id facingMode = constraints[@"facingMode"];

BOOL targetFront = self.usingFrontCamera;
if ([facingMode isKindOfClass:[NSString class]]) {
if ([facingMode isEqualToString:@"environment"]) {
targetFront = NO;
} else if ([facingMode isEqualToString:@"user"]) {
targetFront = YES;
}
}
if (targetFront != self.usingFrontCamera) {
self.usingFrontCamera = targetFront;
// Clear deviceId so `startCapture` re-resolves it from the new position.
self.deviceId = nil;
hasChanged = YES;
}

if (deviceId && ![deviceId isEqualToString:self.deviceId]) {
self.deviceId = deviceId;
hasChanged = YES;
}

int width = [constraints[@"width"] intValue];
int height = [constraints[@"height"] intValue];
int frameRate = [constraints[@"frameRate"] intValue];
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@stream-io/react-native-webrtc",
"version": "137.2.0-alpha.3",
"version": "137.2.0-alpha.5",
"repository": {
"type": "git",
"url": "git+https://github.com/GetStream/react-native-webrtc.git"
Expand Down
Loading