pub(crate) fn track_details_from_sdp(s: &SessionDescription) -> Vec<TrackDetails> {
    let mut incoming_tracks = vec![];

    for media in &s.media_descriptions {
        let mut tracks_in_media_section = vec![];
        let mut rtx_repair_flows = HashMap::new();

        // Plan B can have multiple tracks in a signle media section
        let mut stream_id = "";
        let mut track_id = "";

        // If media section is recvonly or inactive skip
        if media.attribute(ATTR_KEY_RECV_ONLY).is_some()
            || media.attribute(ATTR_KEY_INACTIVE).is_some()
        {
            continue;
        }

        let mid_value = match get_mid_value(media) {
            Some(mid_value) => mid_value,
            None => continue,
        };

        let codec_type = RTPCodecType::from(media.media_name.media.as_str());
        if codec_type == RTPCodecType::Unspecified {
            continue;
        }

        for attr in &media.attributes {
            match attr.key.as_str() {
                ATTR_KEY_SSRCGROUP => {
                    if let Some(value) = &attr.value {
                        let split: Vec<&str> = value.split(' ').collect();
                        if split[0] == SEMANTIC_TOKEN_FLOW_IDENTIFICATION {
                            // Add rtx ssrcs to blacklist, to avoid adding them as tracks
                            // Essentially lines like `a=ssrc-group:FID 2231627014 632943048` are processed by this section
                            // as this declares that the second SSRC (632943048) is a rtx repair flow (RFC4588) for the first
                            // (2231627014) as specified in RFC5576
                            if split.len() == 3 {
                                let base_ssrc = match split[1].parse::<u32>() {
                                    Ok(ssrc) => ssrc,
                                    Err(err) => {
                                        log::warn!("Failed to parse SSRC: {}", err);
                                        continue;
                                    }
                                };
                                let rtx_repair_flow = match split[2].parse::<u32>() {
                                    Ok(n) => n,
                                    Err(err) => {
                                        log::warn!("Failed to parse SSRC: {}", err);
                                        continue;
                                    }
                                };
                                rtx_repair_flows.insert(rtx_repair_flow, base_ssrc);
                                // Remove if rtx was added as track before
                                filter_track_with_ssrc(
                                    &mut tracks_in_media_section,
                                    rtx_repair_flow as SSRC,
                                );
                            }
                        }
                    }
                }

                // Handle `a=msid:<stream_id> <track_label>` for Unified plan. The first value is the same as MediaStream.id
                // in the browser and can be used to figure out which tracks belong to the same stream. The browser should
                // figure this out automatically when an ontrack event is emitted on RTCPeerConnection.
                ATTR_KEY_MSID => {
                    if let Some(value) = &attr.value {
                        let split: Vec<&str> = value.split(' ').collect();
                        if split.len() == 2 {
                            stream_id = split[0];
                            track_id = split[1];
                        }
                    }
                }

                ATTR_KEY_SSRC => {
                    if let Some(value) = &attr.value {
                        let split: Vec<&str> = value.split(' ').collect();
                        let ssrc = match split[0].parse::<u32>() {
                            Ok(ssrc) => ssrc,
                            Err(err) => {
                                log::warn!("Failed to parse SSRC: {}", err);
                                continue;
                            }
                        };

                        if rtx_repair_flows.contains_key(&ssrc) {
                            continue; // This ssrc is a RTX repair flow, ignore
                        }

                        if split.len() == 3 && split[1].starts_with("msid:") {
                            stream_id = &split[1]["msid:".len()..];
                            track_id = split[2];
                        }

                        let mut track_idx = tracks_in_media_section.len();

                        for (i, t) in tracks_in_media_section.iter().enumerate() {
                            if t.ssrcs.contains(&ssrc) {
                                track_idx = i;
                                //TODO: no break?
                            }
                        }

                        let mut repair_ssrc = 0;
                        for (repair, base) in &rtx_repair_flows {
                            if *base == ssrc {
                                repair_ssrc = *repair;
                                //TODO: no break?
                            }
                        }

                        if track_idx < tracks_in_media_section.len() {
                            tracks_in_media_section[track_idx].mid = mid_value.to_owned();
                            tracks_in_media_section[track_idx].kind = codec_type;
                            tracks_in_media_section[track_idx].stream_id = stream_id.to_owned();
                            tracks_in_media_section[track_idx].id = track_id.to_owned();
                            tracks_in_media_section[track_idx].ssrcs = vec![ssrc];
                            tracks_in_media_section[track_idx].repair_ssrc = repair_ssrc;
                        } else {
                            let track_details = TrackDetails {
                                mid: mid_value.to_owned(),
                                kind: codec_type,
                                stream_id: stream_id.to_owned(),
                                id: track_id.to_owned(),
                                ssrcs: vec![ssrc],
                                repair_ssrc,
                                ..Default::default()
                            };
                            tracks_in_media_section.push(track_details);
                        }
                    }
                }
                _ => {}
            };
        }

        let rids = get_rids(media);
        if !rids.is_empty() && !track_id.is_empty() && !stream_id.is_empty() {
            let mut simulcast_track = TrackDetails {
                mid: mid_value.to_owned(),
                kind: codec_type,
                stream_id: stream_id.to_owned(),
                id: track_id.to_owned(),
                rids: vec![],
                ..Default::default()
            };
            for rid in rids.keys() {
                simulcast_track.rids.push(rid.to_owned());
            }
            if simulcast_track.rids.len() == tracks_in_media_section.len() {
                for track in &tracks_in_media_section {
                    simulcast_track.ssrcs.extend(&track.ssrcs)
                }
            }

            tracks_in_media_section = vec![simulcast_track];
        }

        incoming_tracks.extend(tracks_in_media_section);
    }

    incoming_tracks
}
/// extract all TrackDetails from an SDP.
extract all TrackDetails from an SDP.
extract all TrackDetails from an SDP .
pub(crate) fn track_details_from_sdp(s: &SessionDescription) -> Vec<TrackDetails> {
    let mut incoming_tracks = vec![];
    for media in &s.media_descriptions {
        let mut tracks_in_media_section = vec![];
        let mut rtx_repair_flows = HashMap::new();
        // Plan B can have multiple tracks in a signle media section
        let mut stream_id = "";
        let mut track_id = "";
        // If media section is recvonly or inactive skip
        if media.attribute(ATTR_KEY_RECV_ONLY).is_some()
            || media.attribute(ATTR_KEY_INACTIVE).is_some()
        {
            continue;
        }
        let mid_value = match get_mid_value(media) {
            Some(mid_value) => mid_value,
            None => continue,
        };
        let codec_type = RTPCodecType::from(media.media_name.media.as_str());
        if codec_type == RTPCodecType::Unspecified {
            continue;
        }
        for attr in &media.attributes {
            match attr.key.as_str() {
                ATTR_KEY_SSRCGROUP => {
                    if let Some(value) = &attr.value {
                        let split: Vec<&str> = value.split(' ').collect();
                        if split[0] == SEMANTIC_TOKEN_FLOW_IDENTIFICATION {
                            // Add rtx ssrcs to blacklist, to avoid adding them as tracks
                            // Essentially lines like `a=ssrc-group:FID 2231627014 632943048` are processed by this section
                            // as this declares that the second SSRC (632943048) is a rtx repair flow (RFC4588) for the first
                            // (2231627014) as specified in RFC5576
                            if split.len() == 3 {
                                let base_ssrc = match split[1].parse::<u32>() {
                                    Ok(ssrc) => ssrc,
                                    Err(err) => {
                                        log::warn!("Failed to parse SSRC: {}", err);
                                        continue;
                                    }
                                };
                                let rtx_repair_flow = match split[2].parse::<u32>() {
                                    Ok(n) => n,
                                    Err(err) => {
                                        log::warn!("Failed to parse SSRC: {}", err);
                                        continue;
                                    }
                                };
                                rtx_repair_flows.insert(rtx_repair_flow, base_ssrc);
                                // Remove if rtx was added as track before
                                filter_track_with_ssrc(
                                    &mut tracks_in_media_section,
                                    rtx_repair_flow as SSRC,
                                );
                            }
                        }
                    }
                }
                // Handle `a=msid:<stream_id> <track_label>` for Unified plan. The first value is the same as MediaStream.id
                // in the browser and can be used to figure out which tracks belong to the same stream. The browser should
                // figure this out automatically when an ontrack event is emitted on RTCPeerConnection.
                ATTR_KEY_MSID => {
                    if let Some(value) = &attr.value {
                        let split: Vec<&str> = value.split(' ').collect();
                        if split.len() == 2 {
                            stream_id = split[0];
                            track_id = split[1];
                        }
                    }
                }
                ATTR_KEY_SSRC => {
                    if let Some(value) = &attr.value {
                        let split: Vec<&str> = value.split(' ').collect();
                        let ssrc = match split[0].parse::<u32>() {
                            Ok(ssrc) => ssrc,
                            Err(err) => {
                                log::warn!("Failed to parse SSRC: {}", err);
                                continue;
                            }
                        };
                        if rtx_repair_flows.contains_key(&ssrc) {
                            continue; // This ssrc is a RTX repair flow, ignore
                        }
                        if split.len() == 3 && split[1].starts_with("msid:") {
                            stream_id = &split[1]["msid:".len()..];
                            track_id = split[2];
                        }
                        let mut track_idx = tracks_in_media_section.len();
                        for (i, t) in tracks_in_media_section.iter().enumerate() {
                            if t.ssrcs.contains(&ssrc) {
                                track_idx = i;
                                //TODO: no break?
                            }
                        }
                        let mut repair_ssrc = 0;
                        for (repair, base) in &rtx_repair_flows {
                            if *base == ssrc {
                                repair_ssrc = *repair;
                                //TODO: no break?
                            }
                        }
                        if track_idx < tracks_in_media_section.len() {
                            tracks_in_media_section[track_idx].mid = mid_value.to_owned();
                            tracks_in_media_section[track_idx].kind = codec_type;
                            tracks_in_media_section[track_idx].stream_id = stream_id.to_owned();
                            tracks_in_media_section[track_idx].id = track_id.to_owned();
                            tracks_in_media_section[track_idx].ssrcs = vec![ssrc];
                            tracks_in_media_section[track_idx].repair_ssrc = repair_ssrc;
                        } else {
                            let track_details = TrackDetails {
                                mid: mid_value.to_owned(),
                                kind: codec_type,
                                stream_id: stream_id.to_owned(),
                                id: track_id.to_owned(),
                                ssrcs: vec![ssrc],
                                repair_ssrc,
                                ..Default::default()
                            };
                            tracks_in_media_section.push(track_details);
                        }
                    }
                }
                _ => {}
            };
        }
        let rids = get_rids(media);
        if !rids.is_empty() && !track_id.is_empty() && !stream_id.is_empty() {
            let mut simulcast_track = TrackDetails {
                mid: mid_value.to_owned(),
                kind: codec_type,
                stream_id: stream_id.to_owned(),
                id: track_id.to_owned(),
                rids: vec![],
                ..Default::default()
            };
            for rid in rids.keys() {
                simulcast_track.rids.push(rid.to_owned());
            }
            if simulcast_track.rids.len() == tracks_in_media_section.len() {
                for track in &tracks_in_media_section {
                    simulcast_track.ssrcs.extend(&track.ssrcs)
                }
            }
            tracks_in_media_section = vec![simulcast_track];
        }
        incoming_tracks.extend(tracks_in_media_section);
    }
    incoming_tracks
}
pub ( crate ) fn track_details_from_sdp ( s : & SessionDescription ) -> Vec < TrackDetails > { let mut incoming_tracks = vec ! [ ] ; for media in & s . media_descriptions { let mut tracks_in_media_section = vec ! [ ] ; let mut rtx_repair_flows = HashMap :: new ( ) ; // Plan B can have multiple tracks in a signle media section let mut stream_id = " " ; let mut track_id = " " ; // If media section is recvonly or inactive skip if media . attribute ( ATTR_KEY_RECV_ONLY ) . is_some ( ) || media . attribute ( ATTR_KEY_INACTIVE ) . is_some ( ) { continue ; } let mid_value = match get_mid_value ( media ) { Some ( mid_value ) => mid_value , None => continue , } ; let codec_type = RTPCodecType :: from ( media . media_name . media . as_str ( ) ) ; if codec_type == RTPCodecType :: Unspecified { continue ; } for attr in & media . attributes { match attr . key . as_str ( ) { ATTR_KEY_SSRCGROUP => { if let Some ( value ) = & attr . value { let split : Vec < & str > = value . split ( ' ' ) . collect ( ) ; if split [ 0 ] == SEMANTIC_TOKEN_FLOW_IDENTIFICATION { // Add rtx ssrcs to blacklist, to avoid adding them as tracks // Essentially lines like `a=ssrc-group:FID 2231627014 632943048` are processed by this section // as this declares that the second SSRC (632943048) is a rtx repair flow (RFC4588) for the first // (2231627014) as specified in RFC5576 if split . len ( ) == 3 { let base_ssrc = match split [ 1 ] . parse :: < u32 > ( ) { Ok ( ssrc ) => ssrc , Err ( err ) => { log :: warn ! ( " " err ) ; continue ; } } ; let rtx_repair_flow = match split [ 2 ] . parse :: < u32 > ( ) { Ok ( n ) => n , Err ( err ) => { log :: warn ! ( " " err ) ; continue ; } } ; rtx_repair_flows . insert ( rtx_repair_flow , base_ssrc ) ; // Remove if rtx was added as track before filter_track_with_ssrc ( & mut tracks_in_media_section , rtx_repair_flow as SSRC , ) ; } } } } // Handle `a=msid:<stream_id> <track_label>` for Unified plan. The first value is the same as MediaStream.id // in the browser and can be used to figure out which tracks belong to the same stream. The browser should // figure this out automatically when an ontrack event is emitted on RTCPeerConnection. ATTR_KEY_MSID => { if let Some ( value ) = & attr . value { let split : Vec < & str > = value . split ( ' ' ) . collect ( ) ; if split . len ( ) == 2 { stream_id = split [ 0 ] ; track_id = split [ 1 ] ; } } } ATTR_KEY_SSRC => { if let Some ( value ) = & attr . value { let split : Vec < & str > = value . split ( ' ' ) . collect ( ) ; let ssrc = match split [ 0 ] . parse :: < u32 > ( ) { Ok ( ssrc ) => ssrc , Err ( err ) => { log :: warn ! ( " " err ) ; continue ; } } ; if rtx_repair_flows . contains_key ( & ssrc ) { continue ; // This ssrc is a RTX repair flow, ignore } if split . len ( ) == 3 && split [ 1 ] . starts_with ( " " ) { stream_id = & split [ 1 ] [ " " . len ( ) .. ] ; track_id = split [ 2 ] ; } let mut track_idx = tracks_in_media_section . len ( ) ; for ( i , t ) in tracks_in_media_section . iter ( ) . enumerate ( ) { if t . ssrcs . contains ( & ssrc ) { track_idx = i ; //TODO: no break? } } let mut repair_ssrc = 0 ; for ( repair , base ) in & rtx_repair_flows { if * base == ssrc { repair_ssrc = * repair ; //TODO: no break? } } if track_idx < tracks_in_media_section . len ( ) { tracks_in_media_section [ track_idx ] . mid = mid_value . to_owned ( ) ; tracks_in_media_section [ track_idx ] . kind = codec_type ; tracks_in_media_section [ track_idx ] . stream_id = stream_id . to_owned ( ) ; tracks_in_media_section [ track_idx ] . id = track_id . to_owned ( ) ; tracks_in_media_section [ track_idx ] . ssrcs = vec ! [ ssrc ] ; tracks_in_media_section [ track_idx ] . repair_ssrc = repair_ssrc ; } else { let track_details = TrackDetails { mid : mid_value . to_owned ( ) , kind : codec_type , stream_id : stream_id . to_owned ( ) , id : track_id . to_owned ( ) , ssrcs : vec ! [ ssrc ] , repair_ssrc , .. Default :: default ( ) } ; tracks_in_media_section . push ( track_details ) ; } } } _ => { } } ; } let rids = get_rids ( media ) ; if ! rids . is_empty ( ) && ! track_id . is_empty ( ) && ! stream_id . is_empty ( ) { let mut simulcast_track = TrackDetails { mid : mid_value . to_owned ( ) , kind : codec_type , stream_id : stream_id . to_owned ( ) , id : track_id . to_owned ( ) , rids : vec ! [ ] , .. Default :: default ( ) } ; for rid in rids . keys ( ) { simulcast_track . rids . push ( rid . to_owned ( ) ) ; } if simulcast_track . rids . len ( ) == tracks_in_media_section . len ( ) { for track in & tracks_in_media_section { simulcast_track . ssrcs . extend ( & track . ssrcs ) } } tracks_in_media_section = vec ! [ simulcast_track ] ; } incoming_tracks . extend ( tracks_in_media_section ) ; } incoming_tracks }
extract all TrackDetails from an SDP.
extract all TrackDetails from an SDP .
s
&SessionDescription