@@ -80,22 +80,11 @@ private set
8080 }
8181 }
8282
83- public VideoResolution PublisherTargetResolution
84- {
85- get
86- {
87- if ( _mediaInputProvider . VideoInput != null )
88- {
89- return new VideoResolution ( _mediaInputProvider . VideoInput . width ,
90- _mediaInputProvider . VideoInput . height ) ;
91- }
92-
93- return _publisherVideoSettings . MaxResolution ;
94- }
95- }
96-
9783 public RTCRtpSender VideoSender { get ; private set ; }
9884
85+ // Full: 704×576 -> half: 352×288 -> quarter: 176×144 <- We want the smallest resolution to be above 96x96
86+ public static VideoResolution MinimumSafeTargetResolution => new VideoResolution ( 704 , 576 ) ;
87+
9988 public StreamPeerConnection ( ILogs logs , StreamPeerType peerType , IEnumerable < ICEServer > iceServers ,
10089 IMediaInputProvider mediaInputProvider , IStreamAudioConfig audioConfig ,
10190 PublisherVideoSettings publisherVideoSettings , Tracer tracer )
@@ -237,6 +226,18 @@ public void Update()
237226 }
238227
239228 public Task < RTCStatsReport > GetStatsReportAsync ( ) => _peerConnection . GetStatsAsync ( ) ;
229+
230+ public PublisherVideoSettings GetLatestVideoSettings ( )
231+ {
232+ if ( _publisherVideoSettings == null )
233+ {
234+ throw new ArgumentNullException ( $ "{ nameof ( _publisherVideoSettings ) } is null in { nameof ( GetLatestVideoSettings ) } ") ;
235+ }
236+
237+ _publisherVideoSettings . MaxResolution = PublisherTargetResolution ;
238+ _publisherVideoSettings . FrameRate = PublisherTargetFrameRate ;
239+ return _publisherVideoSettings ;
240+ }
240241
241242 public void Dispose ( )
242243 {
@@ -289,6 +290,43 @@ public void Dispose()
289290 private const string VideoCodecKeyH264 = "h264" ;
290291 private const string VideoCodecKeyVP8 = "vp8" ;
291292 private const string AudioCodecKeyRed = "red" ;
293+
294+ private VideoResolution PublisherTargetResolution
295+ {
296+ get
297+ {
298+ if ( _mediaInputProvider . VideoInput != null )
299+ {
300+ var preferred = new VideoResolution ( _mediaInputProvider . VideoInput . width ,
301+ _mediaInputProvider . VideoInput . height ) ;
302+
303+ // Requesting too small resolution can cause crashes in the Android video encoder
304+ // The target resolution is used to calculate 3 layers of video encoding (full, half, quarter)
305+ // For very small values of quarter resolution (not sure exact but ~100x100) the encoder crashes
306+
307+ if ( preferred . Width < MinimumSafeTargetResolution . Width ||
308+ preferred . Height < MinimumSafeTargetResolution . Height )
309+ {
310+ return MinimumSafeTargetResolution ;
311+ }
312+ }
313+
314+ return _publisherVideoSettings . MaxResolution ;
315+ }
316+ }
317+
318+ private uint PublisherTargetFrameRate
319+ {
320+ get
321+ {
322+ if ( _mediaInputProvider . VideoInput != null )
323+ {
324+ return ( uint ) _mediaInputProvider . VideoInput . requestedFPS ;
325+ }
326+
327+ return 30 ;
328+ }
329+ }
292330
293331 private readonly RTCPeerConnection _peerConnection ;
294332
@@ -604,6 +642,10 @@ private static IEnumerable<RTCRtpEncodingParameters> GetVideoEncodingParameters(
604642
605643 break ;
606644 case TrackKind . Video :
645+
646+ //StreamTodo: construct fewer layer when the target resolution is small. Android video encoder crashes when requesting very small resolution
647+ // We're currently forcing the smallest safe resolution that the user can request so that the quarter layer doesn't reach too small resolution
648+ // But we should allow setting small resolution and just produce fewer layers in that case
607649
608650 var fullQuality = new RTCRtpEncodingParameters
609651 {
0 commit comments