Skip to content

Commit 3553291

Browse files
committed
Merge remote-tracking branch 'upstream/master' into dl/upstream_merge_july2025
2 parents f3cb1a0 + 0f1a265 commit 3553291

File tree

8 files changed

+137
-16
lines changed

8 files changed

+137
-16
lines changed

Documentation/BasicUsage.md

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,9 @@ That will allow you to enable and disable video streams on demand while a call i
232232

233233
```javascript
234234
let sessionConstraints = {
235-
mandatory: {
236-
OfferToReceiveAudio: true,
237-
OfferToReceiveVideo: true,
238-
VoiceActivityDetection: true
239-
}
235+
offerToReceiveAudio: true,
236+
offerToReceiveVideo: true,
237+
voiceActivityDetection: true
240238
};
241239
```
242240

@@ -341,6 +339,30 @@ Don't forget, the user facing camera is usually mirrored.
341339
| objectFit | string | 'contain' | Can be `'contain'` or `'cover'` nothing more or less. |
342340
| streamURL | string | 'streamURL' | Required to have an actual video stream rendering. |
343341
| zOrder | number | 0 | Similar to zIndex. |
342+
| onDimensionsChange | function | undefined | Callback fired when video dimensions change. Receives event with nativeEvent containing width and height. |
343+
344+
## Handling Video Dimension Changes
345+
346+
You can listen for changes in video dimensions using the onDimensionsChange callback.
347+
This is useful for adapting your UI based on the video's aspect ratio or for analytics.
348+
349+
```javascript
350+
import React, { useState } from 'react';
351+
352+
const [videoDimensions, setVideoDimensions] = useState({ width: 0, height: 0 });
353+
354+
<RTCView
355+
mirror={true}
356+
objectFit={'cover'}
357+
streamURL={localMediaStream.toURL()}
358+
zOrder={0}
359+
onDimensionsChange={(event) => {
360+
const { width, height } = event.nativeEvent;
361+
setVideoDimensions({ width, height });
362+
console.log(`Video dimensions changed: ${width}x${height}`);
363+
}}
364+
/>
365+
```
344366

345367
## Controlling remote audio tracks
346368

Documentation/CallGuide.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,9 @@ So you can now start creating an offer which then needs sending send off to the
143143

144144
```javascript
145145
let sessionConstraints = {
146-
mandatory: {
147-
OfferToReceiveAudio: true,
148-
OfferToReceiveVideo: true,
149-
VoiceActivityDetection: true
150-
}
146+
offerToReceiveAudio: true,
147+
offerToReceiveVideo: true,
148+
voiceActivityDetection: true
151149
};
152150

153151
try {

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
package com.oney.WebRTCModule;
22

3+
import com.facebook.react.bridge.ReadableMap;
34
import com.facebook.react.uimanager.SimpleViewManager;
45
import com.facebook.react.uimanager.ThemedReactContext;
56
import com.facebook.react.uimanager.annotations.ReactProp;
7+
import com.facebook.react.uimanager.events.RCTEventEmitter;
8+
9+
import java.util.HashMap;
10+
import java.util.Map;
611

712
public class RTCVideoViewManager extends SimpleViewManager<WebRTCView> {
813
private static final String REACT_CLASS = "RTCVideoView";
@@ -70,4 +75,24 @@ public void setStreamURL(WebRTCView view, String streamURL) {
7075
public void setZOrder(WebRTCView view, int zOrder) {
7176
view.setZOrder(zOrder);
7277
}
78+
79+
/**
80+
* Sets the callback for when video dimensions change.
81+
*
82+
* @param view The {@code WebRTCView} on which the callback is to be set.
83+
* @param onDimensionsChange The callback to be called when video dimensions change.
84+
*/
85+
@ReactProp(name = "onDimensionsChange")
86+
public void setOnDimensionsChange(WebRTCView view, boolean onDimensionsChange) {
87+
view.setOnDimensionsChange(onDimensionsChange);
88+
}
89+
90+
@Override
91+
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
92+
Map<String, Object> eventTypeConstants = new HashMap<>();
93+
Map<String, String> dimensionsChangeEvent = new HashMap<>();
94+
dimensionsChangeEvent.put("registrationName", "onDimensionsChange");
95+
eventTypeConstants.put("onDimensionsChange", dimensionsChangeEvent);
96+
return eventTypeConstants;
97+
}
7398
}

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@
1010

1111
import androidx.core.view.ViewCompat;
1212

13+
import com.facebook.react.bridge.Arguments;
14+
import com.facebook.react.bridge.Callback;
1315
import com.facebook.react.bridge.ReactContext;
16+
import com.facebook.react.bridge.ReadableMap;
17+
import com.facebook.react.bridge.WritableMap;
18+
import com.facebook.react.uimanager.events.RCTEventEmitter;
1419

1520
import org.webrtc.EglBase;
1621
import org.webrtc.Logging;
@@ -144,6 +149,11 @@ public void run() {
144149
*/
145150
private VideoTrack videoTrack;
146151

152+
/**
153+
* The callback to be called when video dimensions change.
154+
*/
155+
private boolean onDimensionsChangeEnabled = false;
156+
147157
public WebRTCView(Context context) {
148158
super(context);
149159

@@ -257,6 +267,24 @@ private void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotat
257267
// The onFrameResolutionChanged method call executes on the
258268
// surfaceViewRenderer's render Thread.
259269
post(requestSurfaceViewRendererLayoutRunnable);
270+
271+
// Call the onDimensionsChange callback if it's enabled
272+
if (onDimensionsChangeEnabled) {
273+
post(() -> {
274+
try {
275+
ReactContext reactContext = (ReactContext) getContext();
276+
WritableMap params = Arguments.createMap();
277+
params.putInt("width", videoWidth);
278+
params.putInt("height", videoHeight);
279+
280+
// Send the event through React Native's event system
281+
reactContext.getJSModule(RCTEventEmitter.class)
282+
.receiveEvent(getId(), "onDimensionsChange", params);
283+
} catch (Exception e) {
284+
Log.e(TAG, "Error calling onDimensionsChange callback", e);
285+
}
286+
});
287+
}
260288
}
261289
}
262290

@@ -543,4 +571,13 @@ private void tryAddRendererToVideoTrack() {
543571
rendererAttached = true;
544572
}
545573
}
574+
575+
/**
576+
* Sets whether the onDimensionsChange callback should be called.
577+
*
578+
* @param enabled Whether the callback should be enabled.
579+
*/
580+
public void setOnDimensionsChange(boolean enabled) {
581+
this.onDimensionsChangeEnabled = enabled;
582+
}
546583
}

ios/RCTWebRTC/RTCVideoViewManager.m

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
* Implements an equivalent of {@code HTMLVideoElement} i.e. Web's video
2424
* element.
2525
*/
26-
@interface RTCVideoView : RCTView
26+
@interface RTCVideoView : RCTView<RTCVideoViewDelegate>
2727

2828
/**
2929
* The indicator which determines whether this {@code RTCVideoView} is to mirror
@@ -53,6 +53,9 @@ @interface RTCVideoView : RCTView
5353
@property(nonatomic, readonly) RTCMTLVideoView *videoView;
5454
#endif
5555

56+
// Add a reference to the view manager
57+
@property(nonatomic, weak) RTCVideoViewManager *viewManager;
58+
5659
/**
5760
* The {@link RTCVideoTrack}, if any, which this instance renders.
5861
*/
@@ -63,6 +66,8 @@ @interface RTCVideoView : RCTView
6366
*/
6467
@property(nonatomic, weak) WebRTCModule *module;
6568

69+
@property(nonatomic, copy) RCTDirectEventBlock onDimensionsChange;
70+
6671
@end
6772

6873
@implementation RTCVideoView
@@ -113,6 +118,7 @@ - (instancetype)initWithFrame:(CGRect)frame {
113118
#endif
114119
_objectFit = RTCVideoViewObjectFitCover;
115120
[self addSubview:self.videoView];
121+
self.videoView.delegate = self;
116122
}
117123

118124
return self;
@@ -292,6 +298,18 @@ - (void)setVideoTrack:(RTCVideoTrack *)videoTrack {
292298
}
293299
}
294300

301+
- (void)videoView:(id)videoView didChangeVideoSize:(CGSize)size {
302+
// Capture the callback block to avoid accessing it across threads
303+
RCTDirectEventBlock callback = self.onDimensionsChange;
304+
if (callback) {
305+
NSDictionary *eventData = @{@"width" : @(size.width), @"height" : @(size.height)};
306+
307+
dispatch_async(dispatch_get_main_queue(), ^{
308+
callback(eventData);
309+
});
310+
}
311+
}
312+
295313
@end
296314

297315
@implementation RTCVideoViewManager
@@ -301,6 +319,7 @@ @implementation RTCVideoViewManager
301319
- (RCTView *)view {
302320
RTCVideoView *v = [[RTCVideoView alloc] init];
303321
v.module = [self.bridge moduleForName:@"WebRTCModule"];
322+
v.viewManager = self;
304323
v.clipsToBounds = YES;
305324
return v;
306325
}
@@ -327,6 +346,8 @@ - (dispatch_queue_t)methodQueue {
327346
view.objectFit = fit;
328347
}
329348

349+
RCT_EXPORT_VIEW_PROPERTY(onDimensionsChange, RCTDirectEventBlock)
350+
330351
RCT_CUSTOM_VIEW_PROPERTY(streamURL, NSString *, RTCVideoView) {
331352
if (!json) {
332353
view.videoTrack = nil;

src/RTCPeerConnection.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import RTCRtpTransceiver from './RTCRtpTransceiver';
1818
import RTCSessionDescription, { RTCSessionDescriptionInit } from './RTCSessionDescription';
1919
import RTCTrackEvent from './RTCTrackEvent';
2020
import * as RTCUtil from './RTCUtil';
21+
import { RTCOfferOptions } from './RTCUtil';
2122

2223
const log = new Logger('pc');
2324
const { WebRTCModule } = NativeModules;
@@ -132,7 +133,7 @@ export default class RTCPeerConnection extends EventTarget<RTCPeerConnectionEven
132133
log.debug(`${this._pcId} ctor`);
133134
}
134135

135-
async createOffer(options) {
136+
async createOffer(options?:RTCOfferOptions) {
136137
log.debug(`${this._pcId} createOffer`);
137138

138139
const {

src/RTCUtil.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ const FACING_MODES = [ 'user', 'environment' ];
1212

1313
const ASPECT_RATIO = 16 / 9;
1414

15+
export type RTCOfferOptions = {
16+
iceRestart?:boolean;
17+
offerToReceiveAudio?: boolean;
18+
offerToReceiveVideo?: boolean;
19+
voiceActivityDetection?:boolean
20+
};
21+
1522
const STANDARD_OFFER_OPTIONS = {
1623
icerestart: 'IceRestart',
1724
offertoreceiveaudio: 'OfferToReceiveAudio',
@@ -157,10 +164,10 @@ export function isSdpTypeValid(type: string): boolean {
157164
* @param options - user supplied options
158165
* @return Normalized options
159166
*/
160-
export function normalizeOfferOptions(options: object = {}): object {
161-
const newOptions = {};
167+
export function normalizeOfferOptions(options?: RTCOfferOptions) {
168+
const newOptions: Record<string,string> = {};
162169

163-
if (!options) {
170+
if (typeof options !== 'object') {
164171
return newOptions;
165172
}
166173

src/RTCView.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,17 @@ export interface RTCVideoViewProps extends ViewProps {
7070
*
7171
* iOS only. Requires iOS 15.0 or above, and the PIP background mode capability.
7272
*/
73-
iosPIP?: RTCIOSPIPOptions
73+
iosPIP?: RTCIOSPIPOptions;
74+
75+
/**
76+
* Callback function that is called when the dimensions of the video change.
77+
*
78+
* @param {Object} event - The event object containing the new dimensions.
79+
* @param {Object} event.nativeEvent - The native event data.
80+
* @param {number} event.nativeEvent.width - The width of the video.
81+
* @param {number} event.nativeEvent.height - The height of the video.
82+
*/
83+
onDimensionsChange?: (event: { nativeEvent: { width: number; height: number } }) => void;
7484
}
7585

7686
export interface RTCIOSPIPOptions {

0 commit comments

Comments
 (0)