@@ -2066,7 +2066,7 @@ class RTCSession extends EventManager implements Owner {
2066
2066
Map <String , dynamic > mediaConstraints = < String , dynamic > {
2067
2067
'audio' : true ,
2068
2068
'video' : < String , dynamic > {
2069
- 'mandatory' : < String ,dynamic > {
2069
+ 'mandatory' : < String , dynamic > {
2070
2070
'minWidth' : '320' ,
2071
2071
'minHeight' : '240' ,
2072
2072
'maxWidth' : '320' ,
@@ -2115,7 +2115,7 @@ class RTCSession extends EventManager implements Owner {
2115
2115
'Remote wants to upgrade to video but no camera available to send' );
2116
2116
}
2117
2117
}
2118
- print (" \n\n\n\n\n\n\n ABOUT TO CALL SDP OFFER TO WEB RTTC \n\n\n\n\n\n " );
2118
+ print (' \n\n\n\n\n\n\n ABOUT TO CALL SDP OFFER TO WEB RTTC \n\n\n\n\n\n ' );
2119
2119
logger.d ('emit "sdp"' );
2120
2120
final String ? processedSDP = _sdpOfferToWebRTC (request.body);
2121
2121
emit (EventSdp (
@@ -2764,6 +2764,7 @@ class RTCSession extends EventManager implements Owner {
2764
2764
options['pcConfig' ]? ['sdpSemantics' ] ?? 'unified-plan' ;
2765
2765
2766
2766
try {
2767
+ print ('MediaConstraints going into upgrade: $mediaConstraints ' );
2767
2768
MediaStream localStream =
2768
2769
await navigator.mediaDevices.getUserMedia (mediaConstraints);
2769
2770
_localMediaStreamLocallyGenerated = true ;
@@ -3154,76 +3155,238 @@ class RTCSession extends EventManager implements Owner {
3154
3155
}
3155
3156
3156
3157
void _optimizeVideoCodecs (Map <String , dynamic > sdp) {
3157
- print (" \n\n\n\n\n OPTIMIZING VIDEO CODECS \n\n\n\n\n " );
3158
+ print ('Attempting STRATEGY B v2: Remove H264/VP8 and DEPENDENT codecs...' );
3158
3159
3159
- bool isAvaDevice = true ; // Set based on your device detection logic
3160
+ // final bool applyOptimizations =
3161
+ // Platform.isAndroid; // Or more specific check
3162
+ // if (!applyOptimizations) {
3163
+ // print("Skipping codec removal (not required for this platform/device).");
3164
+ // return;
3165
+ // }
3160
3166
3161
- for (Map <String , dynamic > media in sdp['media' ]) {
3162
- if (media['type' ] == 'video' ) {
3163
- // Get all codec entries with proper casting
3164
- List <dynamic > rtpDynamic = media['rtp' ] ?? < dynamic > [];
3165
- List <dynamic > fmtpDynamic = media['fmtp' ] ?? < dynamic > [];
3167
+ final List <dynamic >? mediaList = sdp['media' ] as List <dynamic >? ;
3168
+ if (mediaList == null ) return ;
3166
3169
3167
- // Find payload types for different codecs
3168
- List <int > h264PayloadTypes = < int > [];
3169
- List <int > vpPayloadTypes = < int > [];
3170
+ final List <dynamic > newMediaList = []; // Build a new list
3170
3171
3171
- for (dynamic item in rtpDynamic) {
3172
- if (item is Map ) {
3173
- String codec = item['codec' ]? .toString ().toLowerCase () ?? '' ;
3172
+ for (dynamic mediaDynamic in mediaList) {
3173
+ if (mediaDynamic is ! Map <String , dynamic >) {
3174
+ newMediaList.add (mediaDynamic);
3175
+ continue ;
3176
+ }
3177
+ final Map <String , dynamic > media = mediaDynamic;
3178
+
3179
+ if (media['type' ] != 'video' ) {
3180
+ newMediaList.add (media); // Keep non-video sections
3181
+ continue ;
3182
+ }
3183
+
3184
+ print (
3185
+ 'Processing video media section to remove H264/VP8 + dependencies...' );
3186
+
3187
+ final List <dynamic > rtpDynamic = media['rtp' ] ?? < dynamic > [];
3188
+ final List <dynamic > fmtpDynamic =
3189
+ media['fmtp' ] ?? < dynamic > []; // Get fmtp lines
3190
+
3191
+ final Set <String > initialPayloadsToRemove =
3192
+ < String > {}; // Use a Set for efficiency
3193
+ final List <String > h264Payloads =
3194
+ < String > []; // Keep for logging if needed
3195
+ final List <String > vp8Payloads = < String > []; // Keep for logging if needed
3196
+
3197
+ // 1. Identify initial H264 and VP8 payloads
3198
+ for (dynamic item in rtpDynamic) {
3199
+ if (item is Map ) {
3200
+ final String ? payload = item['payload' ]? .toString ();
3201
+ final String codec = item['codec' ]? .toString ().toLowerCase () ?? '' ;
3202
+ if (payload != null ) {
3174
3203
if (codec == 'h264' ) {
3175
- h264PayloadTypes.add (item['payload' ]);
3176
- } else if (codec == 'vp8' || codec == 'vp9' ) {
3177
- vpPayloadTypes.add (item['payload' ]);
3204
+ initialPayloadsToRemove.add (payload);
3205
+ h264Payloads.add (payload);
3206
+ } else if (codec == 'vp8' ) {
3207
+ initialPayloadsToRemove.add (payload);
3208
+ vp8Payloads.add (payload);
3178
3209
}
3179
3210
}
3180
3211
}
3212
+ }
3181
3213
3182
- if (isAvaDevice) {
3183
- // APPROACH 1: For AVA devices - Try to modify H.264 profiles for better compatibility
3184
- for (dynamic item in fmtpDynamic) {
3185
- if (item is Map && h264PayloadTypes.contains (item['payload' ])) {
3186
- String config = item['config' ]? .toString () ?? '' ;
3187
- if (config.contains ('profile-level-id' )) {
3188
- // Try a more basic/compatible H.264 profile
3189
- // 42001f = Baseline Profile, Level 3.1 with constraint set
3190
- config = config.replaceAll (RegExp (r'profile-level-id=[^;]+' ),
3191
- 'profile-level-id=42001f' );
3192
- item['config' ] = config;
3193
- }
3194
- }
3195
- }
3214
+ if (initialPayloadsToRemove.isEmpty) {
3215
+ print ('No H264 or VP8 codecs found to remove.' );
3216
+ newMediaList.add (media);
3217
+ continue ;
3218
+ }
3219
+ print (
3220
+ 'Initial H264/VP8 payloads targeted: ${initialPayloadsToRemove .toList ()}' );
3196
3221
3197
- // APPROACH 2: Prioritize VP8/VP9 over H.264
3198
- if (vpPayloadTypes.isNotEmpty) {
3199
- List <String > payloads = media['payloads' ].toString ().split (' ' );
3200
- List <String > reorderedPayloads = < String > [];
3222
+ // 2. Identify dependent payloads (like RTX via 'apt')
3223
+ final Set <String > allPayloadsToRemove =
3224
+ Set <String >.from (initialPayloadsToRemove);
3225
+ bool foundNewDependent = true ; // Flag to loop if needed (usually not)
3226
+
3227
+ while (foundNewDependent) {
3228
+ foundNewDependent = false ;
3229
+ for (dynamic item in fmtpDynamic) {
3230
+ if (item is Map ) {
3231
+ final String ? fmtpPayload = item['payload' ]? .toString ();
3232
+ final String config = item['config' ]? .toString () ?? '' ;
3233
+
3234
+ if (fmtpPayload == null ) continue ;
3201
3235
3202
- // Add VP codecs first
3203
- for ( int payload in vpPayloadTypes ) {
3204
- reorderedPayloads. add (payload. toString ()) ;
3236
+ // Check if this fmtp line's payload is already marked for removal
3237
+ if (allPayloadsToRemove. contains (fmtpPayload) ) {
3238
+ continue ;
3205
3239
}
3206
3240
3207
- // Add H.264 last
3208
- for (String payload in payloads) {
3209
- int ? payloadInt = int .tryParse (payload);
3210
- if (payloadInt != null &&
3211
- ! vpPayloadTypes.contains (payloadInt) &&
3212
- ! h264PayloadTypes.contains (payloadInt)) {
3213
- reorderedPayloads.add (payload);
3241
+ // Simple check for 'apt=XXX'
3242
+ final aptMatch = RegExp (r'apt=(\d+)' ).firstMatch (config);
3243
+ if (aptMatch != null ) {
3244
+ final String ? associatedPayload = aptMatch.group (1 );
3245
+ // If the associated payload 'apt' is one we are removing,
3246
+ // then we MUST also remove the payload defined for this fmtp line.
3247
+ if (associatedPayload != null &&
3248
+ allPayloadsToRemove.contains (associatedPayload)) {
3249
+ if (allPayloadsToRemove.add (fmtpPayload)) {
3250
+ // Add returns true if it was added
3251
+ print (
3252
+ 'Identified dependent payload $fmtpPayload (apt=$associatedPayload ) for removal.' );
3253
+ foundNewDependent =
3254
+ true ; // May need another pass if dependencies chain
3255
+ }
3214
3256
}
3215
3257
}
3258
+ // Add checks for other dependency types if necessary (e.g., FEC)
3259
+ }
3260
+ }
3261
+ }
3216
3262
3217
- // Add H.264 at the end (lowest priority)
3218
- for (int payload in h264PayloadTypes) {
3219
- reorderedPayloads.add (payload.toString ());
3220
- }
3263
+ print (
3264
+ 'Final set of ALL payloads to remove (H264/VP8 + dependents): ${allPayloadsToRemove .toList ()}' );
3221
3265
3222
- media['payloads' ] = reorderedPayloads.join (' ' );
3223
- }
3266
+ // --- Filter the media section using allPayloadsToRemove ---
3267
+
3268
+ Map <String , dynamic > modifiedMedia = Map <String , dynamic >.from (media);
3224
3269
3225
- // APPROACH 3 (uncomment if needed): Complete removal of H.264
3226
- /*
3270
+ // 3. Filter Payloads line (m=video ...)
3271
+ final String ? currentPayloadsStr = modifiedMedia['payloads' ]? .toString ();
3272
+ if (currentPayloadsStr != null ) {
3273
+ final List <String > originalPayloads = currentPayloadsStr.split (' ' );
3274
+ final List <String > keptPayloads = originalPayloads
3275
+ .where ((p) => ! allPayloadsToRemove.contains (p))
3276
+ .toList ();
3277
+ modifiedMedia['payloads' ] = keptPayloads.join (' ' );
3278
+ print ("Filtered 'payloads': ${modifiedMedia ['payloads' ]}" );
3279
+
3280
+ if (keptPayloads.isEmpty) {
3281
+ print (
3282
+ 'Warning: Removing codecs and dependents left no video codecs! Video might fail.' );
3283
+ // Optional: skip adding this media section entirely
3284
+ // continue;
3285
+ }
3286
+ } else {
3287
+ print ("Warning: 'payloads' field missing." );
3288
+ }
3289
+
3290
+ // 4. Filter rtp array (a=rtpmap:...)
3291
+ modifiedMedia['rtp' ] = (modifiedMedia['rtp' ] as List <dynamic >? )
3292
+ ? .where ((item) =>
3293
+ item is Map &&
3294
+ ! allPayloadsToRemove.contains (item['payload' ]? .toString ()))
3295
+ .toList () ??
3296
+ [];
3297
+
3298
+ // 5. Filter fmtp array (a=fmtp:...)
3299
+ modifiedMedia['fmtp' ] = (modifiedMedia['fmtp' ] as List <dynamic >? )
3300
+ ? .where ((item) =>
3301
+ item is Map &&
3302
+ ! allPayloadsToRemove.contains (item['payload' ]? .toString ()))
3303
+ .toList () ??
3304
+ [];
3305
+
3306
+ // 6. Filter rtcpFb array (a=rtcp-fb:...)
3307
+ modifiedMedia['rtcpFb' ] = (modifiedMedia['rtcpFb' ] as List <dynamic >? )
3308
+ ? .where ((item) =>
3309
+ item is Map &&
3310
+ ! allPayloadsToRemove.contains (item['payload' ]? .toString ()))
3311
+ .toList () ??
3312
+ [];
3313
+
3314
+ newMediaList.add (modifiedMedia);
3315
+ } // end iterating media sections
3316
+
3317
+ sdp['media' ] = newMediaList;
3318
+ print ('Finished Strategy B v2: Removing H264/VP8 + dependents.' );
3319
+
3320
+ // print("\n\n\n\n\n OPTIMIZING VIDEO CODECS \n\n\n\n\n");
3321
+ //
3322
+ // bool isAvaDevice = true; // Set based on your device detection logic
3323
+ //
3324
+ // for (Map<String, dynamic> media in sdp['media']) {
3325
+ // if (media['type'] == 'video') {
3326
+ // // Get all codec entries with proper casting
3327
+ // List<dynamic> rtpDynamic = media['rtp'] ?? <dynamic>[];
3328
+ // List<dynamic> fmtpDynamic = media['fmtp'] ?? <dynamic>[];
3329
+ //
3330
+ // // Find payload types for different codecs
3331
+ // List<int> h264PayloadTypes = <int>[];
3332
+ // List<int> vpPayloadTypes = <int>[];
3333
+ //
3334
+ // for (dynamic item in rtpDynamic) {
3335
+ // if (item is Map) {
3336
+ // String codec = item['codec']?.toString().toLowerCase() ?? '';
3337
+ // if (codec == 'h264') {
3338
+ // h264PayloadTypes.add(item['payload']);
3339
+ // } else if (codec == 'vp8' || codec == 'vp9') {
3340
+ // vpPayloadTypes.add(item['payload']);
3341
+ // }
3342
+ // }
3343
+ // }
3344
+ //
3345
+ // if (isAvaDevice) {
3346
+ // // APPROACH 1: For AVA devices - Try to modify H.264 profiles for better compatibility
3347
+ // for (dynamic item in fmtpDynamic) {
3348
+ // if (item is Map && h264PayloadTypes.contains(item['payload'])) {
3349
+ // String config = item['config']?.toString() ?? '';
3350
+ // if (config.contains('profile-level-id')) {
3351
+ // // Try a more basic/compatible H.264 profile
3352
+ // // 42001f = Baseline Profile, Level 3.1 with constraint set
3353
+ // config = config.replaceAll(RegExp(r'profile-level-id=[^;]+'),
3354
+ // 'profile-level-id=42001f');
3355
+ // item['config'] = config;
3356
+ // }
3357
+ // }
3358
+ // }
3359
+ //
3360
+ // // APPROACH 2: Prioritize VP8/VP9 over H.264
3361
+ // if (vpPayloadTypes.isNotEmpty) {
3362
+ // List<String> payloads = media['payloads'].toString().split(' ');
3363
+ // List<String> reorderedPayloads = <String>[];
3364
+ //
3365
+ // // Add VP codecs first
3366
+ // for (int payload in vpPayloadTypes) {
3367
+ // reorderedPayloads.add(payload.toString());
3368
+ // }
3369
+ //
3370
+ // // Add H.264 last
3371
+ // for (String payload in payloads) {
3372
+ // int? payloadInt = int.tryParse(payload);
3373
+ // if (payloadInt != null &&
3374
+ // !vpPayloadTypes.contains(payloadInt) &&
3375
+ // !h264PayloadTypes.contains(payloadInt)) {
3376
+ // reorderedPayloads.add(payload);
3377
+ // }
3378
+ // }
3379
+ //
3380
+ // // Add H.264 at the end (lowest priority)
3381
+ // for (int payload in h264PayloadTypes) {
3382
+ // reorderedPayloads.add(payload.toString());
3383
+ // }
3384
+ //
3385
+ // media['payloads'] = reorderedPayloads.join(' ');
3386
+ // }
3387
+
3388
+ // APPROACH 3 (uncomment if needed): Complete removal of H.264
3389
+ /*
3227
3390
if (h264PayloadTypes.isNotEmpty) {
3228
3391
// Remove H.264 from RTP entries
3229
3392
media['rtp'] = rtpDynamic.where((dynamic item) {
@@ -3246,13 +3409,13 @@ class RTCSession extends EventManager implements Owner {
3246
3409
}
3247
3410
}
3248
3411
*/
3249
- }
3250
- }
3251
- }
3412
+ // }
3413
+ // }
3414
+ // }
3252
3415
}
3253
3416
3254
3417
void _filterH264Codec (Map <String , dynamic > sdp) {
3255
- print (" \n\n\n\n\n\n FILTERING h264 \n\n\n\n\n " );
3418
+ print (' \n\n\n\n\n\n FILTERING h264 \n\n\n\n\n ' );
3256
3419
// Go through each media section
3257
3420
for (Map <String , dynamic > m in sdp['media' ]) {
3258
3421
if (m['type' ] == 'video' ) {
0 commit comments