-
Notifications
You must be signed in to change notification settings - Fork 16
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
Add changes to configurationchange MediaStreamTrack event #83
base: main
Are you sure you want to change the base?
Conversation
83fe9e5
to
da7d291
Compare
da7d291
to
6c0aa31
Compare
Gentle ping @youennf |
index.html
Outdated
<li> | ||
<p>[=Fire an event=] named <var>configurationchange</var> on <var>track</var>.</p> | ||
<li><p>Let <var>changes</var> be an empty [=sequence=].</p></li> | ||
<li><p>For each change in <var>track</var>'s capabilities and settings, add its name to <var>changes</var>.</p></li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"For each change... add its name"
What is the name of a change? Should that be clarified? (Depending on the answer - the issue was raised of distinguishing capabilities and settings with a similar name. Have I missed the resolution of that discussion?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We tentatively settled on having a single array.
One reason is that both capabilities and settings might change at the same time.
Say bgblur is made mandatory, bgblur capabiliy becomes [true]
and settings become true
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with Elad, we can probably tidy a bit the language.
Source settings are dictionaries, ditto for capabilities, we could use for describing the matching algorithm, something like:
- for each key-value pair of source settings, if track settings does not have the same pair, append key to the list if not already in the list.
- for each key-value pair of track settings, if source settings does not have the same pair, append key to the list if not already in the list.
- for each key-value pair of source capabilities, if track capabilities does not have the same pair, append key to the list if not already in the list.
- for each key-value pair of track capabilities, if source set capabilities ings does not have the same pair, append key to the list if not already in the list.
- If list is empty, abort these steps
- Sort the list by lexicographical order.
- Fire the event
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! It is much better now!
const [track] = stream.getVideoTracks(); | ||
track.addEventListener("configurationchange", (event) => { | ||
if (event.changes.includes("backgroundBlur")) { | ||
// Background blur configuration has changed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, and completely up to you, but maybe a clearer example would also indicate why an app might care about this? Maybe the comment could mention it would toggle its own blurring to be the inverse, so as to always be blurred but never double-blurred?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion!
index.html
Outdated
<p> | ||
<p>When the [=User Agent=] detects <dfn data-export id="change-track-configuration">a change of configuration</dfn> | ||
in a <var>track</var>'s underlying source, the [=User Agent=] MUST run the following steps:</p> | ||
<ol> | ||
<li><p>If <var>track</var>.{{MediaStreamTrack/muted}} is <code>true</code>, wait for <var>track</var>.{{MediaStreamTrack/muted}} | ||
to become <code>false</code> or <var>track</var>.{{MediaStreamTrack/readyState}} to be "ended".</p></li> | ||
<li><p>If <var>track</var>.{{MediaStreamTrack/readyState}} is "ended", abort these steps.</p></li> | ||
<li><p>If <var>track</var>'s capabilities, constraints and settings are matching <var>source</var> configuration, abort these steps. | ||
<li><p>If <var>track</var>'s capabilities, constraints and settings are matching <var>source</var> configuration, abort these steps.</li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking more about it, if capabilities and settings are matching, why should constraints change?
Maybe we can remove constraints here (or we could leave that to a follow-up)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely agree.
index.html
Outdated
<li> | ||
<p>[=Fire an event=] named <var>configurationchange</var> on <var>track</var>.</p> | ||
<li><p>Let <var>changes</var> be an empty [=sequence=].</p></li> | ||
<li><p>For each change in <var>track</var>'s capabilities and settings, add its name to <var>changes</var>.</p></li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We tentatively settled on having a single array.
One reason is that both capabilities and settings might change at the same time.
Say bgblur is made mandatory, bgblur capabiliy becomes [true]
and settings become true
.
index.html
Outdated
<li> | ||
<p>[=Fire an event=] named <var>configurationchange</var> on <var>track</var>.</p> | ||
<li><p>Let <var>changes</var> be an empty [=sequence=].</p></li> | ||
<li><p>For each change in <var>track</var>'s capabilities and settings, add its name to <var>changes</var>.</p></li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with Elad, we can probably tidy a bit the language.
Source settings are dictionaries, ditto for capabilities, we could use for describing the matching algorithm, something like:
- for each key-value pair of source settings, if track settings does not have the same pair, append key to the list if not already in the list.
- for each key-value pair of track settings, if source settings does not have the same pair, append key to the list if not already in the list.
- for each key-value pair of source capabilities, if track capabilities does not have the same pair, append key to the list if not already in the list.
- for each key-value pair of track capabilities, if source set capabilities ings does not have the same pair, append key to the list if not already in the list.
- If list is empty, abort these steps
- Sort the list by lexicographical order.
- Fire the event
index.html
Outdated
is an [=event handler IDL attribute=] for the `onconfigurationchange` | ||
[=event handler=], whose [=event handler event type=] is | ||
<dfn event for=MediaStreamTrack>configurationchange</dfn>. | ||
</p> | ||
<p> | ||
<p>When the [=User Agent=] detects <dfn data-export id="change-track-configuration">a change of configuration</dfn> | ||
in a <var>track</var>'s underlying source, the [=User Agent=] MUST run the following steps:</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a pre-existing issue but it is not very clear whether each clone track will fire in its own event loop task or in the same event loop task.
We might want to file an issue about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've filed an issue at #86.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@youennf @eladalon1983 Thanks for your review! I've addressed your feedback.
index.html
Outdated
<p> | ||
<p>When the [=User Agent=] detects <dfn data-export id="change-track-configuration">a change of configuration</dfn> | ||
in a <var>track</var>'s underlying source, the [=User Agent=] MUST run the following steps:</p> | ||
<ol> | ||
<li><p>If <var>track</var>.{{MediaStreamTrack/muted}} is <code>true</code>, wait for <var>track</var>.{{MediaStreamTrack/muted}} | ||
to become <code>false</code> or <var>track</var>.{{MediaStreamTrack/readyState}} to be "ended".</p></li> | ||
<li><p>If <var>track</var>.{{MediaStreamTrack/readyState}} is "ended", abort these steps.</p></li> | ||
<li><p>If <var>track</var>'s capabilities, constraints and settings are matching <var>source</var> configuration, abort these steps. | ||
<li><p>If <var>track</var>'s capabilities, constraints and settings are matching <var>source</var> configuration, abort these steps.</li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely agree.
const [track] = stream.getVideoTracks(); | ||
track.addEventListener("configurationchange", (event) => { | ||
if (event.changes.includes("backgroundBlur")) { | ||
// Background blur configuration has changed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion!
index.html
Outdated
is an [=event handler IDL attribute=] for the `onconfigurationchange` | ||
[=event handler=], whose [=event handler event type=] is | ||
<dfn event for=MediaStreamTrack>configurationchange</dfn>. | ||
</p> | ||
<p> | ||
<p>When the [=User Agent=] detects <dfn data-export id="change-track-configuration">a change of configuration</dfn> | ||
in a <var>track</var>'s underlying source, the [=User Agent=] MUST run the following steps:</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've filed an issue at #86.
index.html
Outdated
<li> | ||
<p>[=Fire an event=] named <var>configurationchange</var> on <var>track</var>.</p> | ||
<li><p>Let <var>changes</var> be an empty [=sequence=].</p></li> | ||
<li><p>For each change in <var>track</var>'s capabilities and settings, add its name to <var>changes</var>.</p></li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! It is much better now!
<li><p>If <var>changes</var> [=list/is empty=], abort these steps.</p></li> | ||
<li><p>[=list/Sort=] <var>changes</var> in lexicographical order.</p></li> | ||
<li> | ||
<p>[=Fire an event=] named {{MediaStreamTrack/configurationchange}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should also specify the boolean values of bubbles
and cancellable
for this event. I'd say they should be both false
. Do you agree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the default values in EventInit so we might not need it.
Let's put this on the agenda for next interim. |
Not clear to me what adopting this change means for other attributes than background blur - for instance, if a captured tab is resized, its size attributes change - does the event fire? and when? |
§ 7.7. Use plain Events for state says we generally want to fire plain events and not add state to events. It seems to me, the app can figure out what changed without this information. For example, on devicechange we DON'T have information on the event about which device(s) were added or which were removed, as this is simple enough to figure out and would get complex real fast. |
Arguably, an app can more efficiently and elegantly bail early on unimportant changes if it can query the event for "is x among the properties changed" rather than locally store state and compare it to the new state, which it needs to call Contrast: if (!event.changes.includes("backgroundBlur")) {
return;
}
respondToNewState(event.target.getSettings().backgroundBlur); With: const settings = event.target.getSettings();
if (settings.backgroundBlur == previousState.backgroundBlur) {
return;
}
previousState = settings;
respondToNewState(settings.backgroundBlur); |
Co-authored-by: youennf <[email protected]>
It is true the app can figure this out on its own.
The analogy would be more about whether exposing a device-added/device-removed enumeration to the event. |
I agree. Overall, this change seems good to me. |
@jan-ivar I agree with #83 (comment) as well. |
This is an event with one name returning a list of other names basically. I think this is the sort of APIs the design guides explicitly tell us to avoid, because it creates new and disparate/novel patterns that are inherently redundant with each other and creates entropy. I think it's a false choice to say the alternative is an event name per change. This seems easy enough for JS and similar enough to devicechange which does NOT have an event for add and a separate for remove, because JS simply compares the result before and after to learn that. Same here. For example: let lastSettings = track.getSettings();
track.onconfigurationchange = () => {
const settings = track.getSettings();
if (settings.backgroundBlur != lastSettings.backgroundBlur) {
// background blur changed
}
lastSettings = track.getSettings();
}; We should not create new platform APIs for this simple thing. That is my position as well as my interpretation of TAG advice in § 7.7. Use plain Events for state. cc @annevk |
In the interest of progress, isn't this something we can add later if needed rather than block on? |
IIUC we now have two options:
track.onbackgroundblurchange = () => {
// Background blur has changed.
if (track.getSettings().backgroundBlur) {
// Turn off web app own blurring so that video doesn't get double-blurred.
}
};
let lastSettings = track.getSettings();
track.onconfigurationchange = () => {
const settings = track.getSettings();
if (settings.backgroundBlur != lastSettings.backgroundBlur) {
// Turn off web app own blurring so that video doesn't get double-blurred.
}
lastSettings = track.getSettings();
}; Am I missing another option? |
I am not very fond of this approach. It either gets really complex (there are 37 constrainable properties in total defined in
I am quite fine with not including changes in configurationchange event. It adds some avoidable work to event handlers but it is still managable. It might also require some clarification, such as that the event should not be triggered if the frameRate setting is not a real setting but an estimate which is about to change on every or always every frame or if the color temperature changes during auto white balance mode, etc.
In #82 (comment) you suggested a |
FWIW, this API-shape doesn't look unreasonable to me. Events can come with accompanying data that explains the nature of the change. E.g., the recently-added HTML popover feature exposes the old and new states in its |
According to #83 (comment), it seems like we could actually expose |
For discussion in the WG in March? |
MST changes its settings inside the queued task whereas https://html.spec.whatwg.org/#queue-a-popover-toggle-event-task appears to have a different event firing model where the event fires AFTER the state transition (which isn't in the queued task). So oldState in that case isn't trivially available on the event target like in our case, if I read it right. Getting git blame on html is a pain, so I haven't yet dug out whether that was part of its rationale, but it seems to exist not solely for developer ergonomics, and also highlights why e.g. adding oldState to the event target would have been weird, since the point there seems to be to disambiguate state transitions that effectively have already happened. In our case, I've seen no evidence that disambiguating the state transitions of blur is important. It seems apps are mostly interested in the most recent state, which seems trivial to ascertain from the event target, by comparing to the app's tracking of its state of interest. |
That's true for I think your suggestion above about adding this once web developers have had a chance to write some code against a plainer version is reasonable though. Start with the minimal thing that works and then build up based on demand and patterns that emerge. |
Makes sense to me. |
FYI I've started #89 to improve the configuration change section. |
@youennf @eladalon1983 Refer : https://jsfiddle.net/abhijeetk/k9qso14g/5/ |
This issue was discussed in WebRTC Feb 2023 Meeting – (onConfigurationChange 🎞︎) |
This issue was mentioned in WebRTC Feb 2023 Meeting – (onConfigurationChange 🎞︎) |
Nearly a year old. Is there still interest in proceeding with this approach? |
Prematurely closed, sorry. |
Following #80 (comment), here's my naive attempt to expose changes in the configurationchange MediaStreamTrack event.
Live preview: https://pr-preview.s3.amazonaws.com/beaufortfrancois/mediacapture-extensions/pull/83.html#exposing-change-of-mediastreamtrack-configuration
@youennf PTAL
Preview | Diff