Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tentative first pass at making simulcast egest possible #312

Draft
wants to merge 38 commits into
base: master
Choose a base branch
from

Conversation

robashton
Copy link
Member

@robashton robashton commented Oct 7, 2022

#289 (comment)

Forgot to make a PR and stick my long-winded text there, but whatever, is is the draft PR :)

(I'll fix the tests momentarily, forgot I'd changed a structural thing for the API!)

@k0nserv
Copy link
Member

k0nserv commented Oct 7, 2022

@robashton This looks great! I'm going to look closer next week. I would advise that you review the changes that undo work from #217 however. These fixed bugs and made us more spec compliant and should definitely remain(might need changing though)

@robashton
Copy link
Member Author

Ah, if I have a source as to "where it came from" then I can do my best to restore it

@robashton
Copy link
Member Author

ah, yes - now I see where it was being set. By the time I realised that code wasn't working any more I'd lost that line.

Fine, that's easy to restore, will sort that along with the pesky Mutex that shouldn't be a Mutex.

@k0nserv
Copy link
Member

k0nserv commented Oct 7, 2022

Great, I think it was only #217 that introduced these changes, but you if you're in doubt it should be clear from git blame. Any part of JSEP that talk about msid and the parts of the spec that discuss RTCRtpSender.[[AssociatedMediaStreamIds]] are relevant to read to make sense of this

@robashton
Copy link
Member Author

That should be a bit closer to the finish line - I think I got the placement right for the msid / simulcast directives in the SDP

Copy link
Member

@k0nserv k0nserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be better to leverage an enum for track_encodings, i.e.

enum TrackEncodings {
  Simulcast(Vec<TrackEncoding>),
  Single(TrackEncoding)
}

webrtc/src/peer_connection/mod.rs Outdated Show resolved Hide resolved
webrtc/src/error.rs Outdated Show resolved Hide resolved
interceptor/src/stream_info.rs Outdated Show resolved Hide resolved
webrtc/src/peer_connection/peer_connection_internal.rs Outdated Show resolved Hide resolved
webrtc/src/peer_connection/sdp/mod.rs Outdated Show resolved Hide resolved
webrtc/src/rtp_transceiver/rtp_sender/mod.rs Outdated Show resolved Hide resolved
webrtc/src/rtp_transceiver/rtp_sender/mod.rs Outdated Show resolved Hide resolved
webrtc/src/rtp_transceiver/rtp_sender/mod.rs Outdated Show resolved Hide resolved
webrtc/src/track/track_local/mod.rs Show resolved Hide resolved
webrtc/src/track/track_local/mod.rs Outdated Show resolved Hide resolved
@robashton
Copy link
Member Author

I think it might be better to leverage an enum for track_encodings, i.e.

enum TrackEncodings {
  Simulcast(Vec<TrackEncoding>),
  Single(TrackEncoding)
}

I'm not against this, let's get ourselves happy with the rest of the minor bits and I'll have a look at it.

@robashton
Copy link
Member Author

Just tidying up this thread so I can see what is outstanding, quite keen to get this pushed over the finish line so I can move onto the next task in my queue (not webrtc related).

The SRTP Reader stuff seems the most pressing, I've re-looked at this in the whole and it looks as though it's not 'wrong' per se, we've got independent streams on a shared transport and you want separate binds/interceptors for each of those (or that shared state will get hairy quickly).

The comment about the SRTP Stream being shared I don't think is correct, as we create the SRTP stream per encoding at the top of the function

        let ssrc = rand::random::<u32>();
        let srtp_stream = Arc::new(SrtpWriterFuture {
            closed: AtomicBool::new(false),
            ssrc,
            rtp_sender: Arc::downgrade(&self.internal),
            rtp_transport: Arc::clone(&self.transport),
            rtcp_read_stream: Mutex::new(None),
            rtp_write_session: Mutex::new(None),
        });

        let srtp_rtcp_reader = Arc::clone(&srtp_stream) as Arc<dyn RTCPReader + Send + Sync>;
        let rtcp_reader = self.interceptor.bind_rtcp_reader(srtp_rtcp_reader).await;

Calling close on each of these in sequence is fine (I think). This is spiritually quite close to what Pion is doing with its stack (although I appreciate a lot of that has changd and it may well have fixes in to deal with bugs that might arise with this decisio that I haven't implemented here)

@k0nserv
Copy link
Member

k0nserv commented Oct 12, 2022

The comment about the SRTP Stream being shared I don't think is correct, as we create the SRTP stream per encoding at the top of the function

Aight, I misread.

What about RTCP?

webrtc/src/peer_connection/sdp/mod.rs Outdated Show resolved Hide resolved
webrtc/src/peer_connection/sdp/mod.rs Outdated Show resolved Hide resolved
@@ -198,7 +198,7 @@ impl RTPReceiverInternal {

let tracks = self.tracks.read().await;
for t in &*tracks {
if t.track.rid() == rid {
if t.track.rid().map_or(false, |r| r == rid) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think when comparing to an option option = Some(value) might be easier to read than map_or

Copy link
Member Author

@robashton robashton Oct 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, you can't just do a Some(value) because the types don't line up, track.rid() is Option<String> (I toyed with having it be something else, I don't know what the rust convention is and it seemed to match the other types across the codebase) and rid is a &str, so I'd have to make it owned in order to do the comparison which seems even more of a stretch than doing a map_or, which is fundamentally equivalent to the oft-used fromMaybe in a functional language like haskell/purescript.

If the left hand type can be changed then this would go away. If you have a suggestion for this then please do tell (I did start with &Option<String> to avoid the clone but that just pushed the clone to the consumers which was 🤮 )

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do track.rid().as_deref() == Some(rid) but I guess that's not much better than this. By using as_deref the Deref coercion mechanism kicks in, resulting in a left hand side type of Option<&str>

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a neat little feature - would there be benefit in changing the type of track.rid()? It's mostly used in checks just like this anyway

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point actually, it should be Option<&str> to avoid the allocation each time it's called. The caller can still clone it if they want

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah okay, Option<String> on the struct and Option<&str> on the trait is much nicer (and analogous to how you'd do a String anyway), I note that there are plenty of inconsistencies in how strings are passed around in the codebase in general and I've left most of them alone in this change so there are a few unnecessary String::from's here, but on the whole it's much nicer


/// RTPSender allows an application to control how a given Track is encoded and transmitted to a remote peer
pub struct RTCRtpSender {
pub(crate) track_encodings: RwLock<Vec<Arc<TrackEncoding>>>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't expect you need to use an async lock here. As long as the await point isn't being held across awaits a regular sync lock is preferred. See https://tokio.rs/tokio/tutorial/shared-state#on-using-stdsyncmutex

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hadn't even occured to me which RwLock I'd imported, I'll watch for that in the future

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is another of these on for the list of tracks on RTCRtpReceiver which I added a few months ago, I guess the same applies for that too

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah generally there are a lot of asynchronous locks in this project that should be reconsidered. I suspect a big chunk of these can become sync

rtp_write_session: Mutex::new(None),
});

let srtp_rtcp_reader = Arc::clone(&srtp_stream) as Arc<dyn RTCPReader + Send + Sync>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, if you are leaving it like this can we add a TODO about this point?

let mut send_parameters = RTCRtpSendParameters {
rtp_parameters: self
.media_engine
.get_rtp_parameters_by_kind(self.kind, &[RTCRtpTransceiverDirection::Sendonly])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will conflict with #321 FYI

webrtc/src/rtp_transceiver/rtp_sender/mod.rs Show resolved Hide resolved
webrtc/src/rtp_transceiver/rtp_sender/mod.rs Outdated Show resolved Hide resolved
webrtc/src/rtp_transceiver/rtp_sender/mod.rs Outdated Show resolved Hide resolved
@robashton
Copy link
Member Author

Urgh, I didn't mean to push that rebase, I was just looking at what had changed upstream and forgot I'd pulled it when I came back from coffee. What a mess.

@codecov
Copy link

codecov bot commented Oct 13, 2022

Codecov Report

Base: 56.62% // Head: 56.80% // Increases project coverage by +0.17% 🎉

Coverage data is based on head (7c03a11) compared to base (a000977).
Patch coverage: 59.07% of modified lines in pull request are covered.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #312      +/-   ##
==========================================
+ Coverage   56.62%   56.80%   +0.17%     
==========================================
  Files         500      500              
  Lines       47517    47714     +197     
  Branches    12850    12861      +11     
==========================================
+ Hits        26907    27103     +196     
+ Misses       9943     9920      -23     
- Partials    10667    10691      +24     
Impacted Files Coverage Δ
...hannels-flow-control/data-channels-flow-control.rs 0.00% <0.00%> (ø)
examples/examples/simulcast/simulcast.rs 0.00% <0.00%> (ø)
ice/src/util/util_test.rs 42.85% <0.00%> (ø)
.../src/io/sample_builder/sample_sequence_location.rs 92.30% <ø> (-4.99%) ⬇️
turn/examples/turn_client_udp.rs 0.00% <ø> (ø)
turn/examples/turn_server_udp.rs 0.00% <ø> (ø)
turn/src/relay/relay_range.rs 0.00% <0.00%> (ø)
webrtc/src/error.rs 10.52% <ø> (ø)
.../src/rtp_transceiver/rtp_sender/rtp_sender_test.rs 53.60% <0.00%> (-0.22%) ⬇️
...c/src/rtp_transceiver/rtp_transceiver_direction.rs 95.74% <ø> (+3.15%) ⬆️
... and 31 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@robashton
Copy link
Member Author

The comment about the SRTP Stream being shared I don't think is correct, as we create the SRTP stream per encoding at the top of the function

Aight, I misread.

What about RTCP?

I've re-read the code around this, and as far as I can see, the infrastructure-per-encoding model works because each encoding has its own SSRC and that is how the SrtpWriterFuture discriminates when it sets itself up. (The SrtpWriterfuture being where the RTCP reader comes from) and that being how read_simulcast then works.

It's a bit of a janky model, but it's the one we've got - I don't know how you'd avoid setting this up per-encoding without changing a heap of other code?

Copy link
Member

@k0nserv k0nserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you try and clean up the git strangeness and I can do a final pass

ice/CHANGELOG.md Outdated
@@ -1,7 +1,8 @@
# webrtc-ice changelog

## Unreleased
* Add IP filter to ICE `AgentConfig` [#306](https://github.com/webrtc-rs/webrtc/pull/306)

* Add IP filter to ICE `AgentConfig` [#306](https://github.com/webrtc-rs/webrtc/pull/306) and [#318](https://github.com/webrtc-rs/webrtc/pull/318).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something is off here this change shouldn't be in this branch

@@ -118,17 +118,13 @@ pub async fn local_interfaces(

if !ipaddr.is_loopback()
&& ((ipv4requested && ipaddr.is_ipv4()) || (ipv6requested && ipaddr.is_ipv6()))
&& ip_filter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this diff here, this is already the code on master, something must've gone wrong with rebasing

@robashton
Copy link
Member Author

Could you try and clean up the git strangeness and I can do a final pass

If I can work out how, I totally messed this one up, usually I'd just do a git fetch and a rebase on final pass (and from the other side). That I did a pull to check what had changed upstream and then accidentally pushed it is a total cluster.

I imagine my best bet might be to do an interactive rebase locally and re-apply my changes without any of that noise in it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants