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

Use constrainable pattern for ImageCapture (#124) #146

Closed
wants to merge 5 commits into from

Conversation

yell0wd0g
Copy link
Member

First draft of using ConstrainablePattern for ImageCapture capabilities and settings management.

This PR:

  • Adds an Extensions section, where MediaTrackSupportedConstraints, MediaTrackCapabilities, MediaTrackConstraintSet and MediaTrackSettings dictionaries are extended to include our set of capabilities/settings.
  • The previous partial dictionaries are also explained in relation to a new 'Constrainable properties' section (since these dictionaries always refer to the same set of constraints/settings, it's easier to define them all together).
  • moves getPhotoCapabilities(), setOptions(), PhotoCapabilities and PhotoSettings to a legacy non-normative section, for the time being.

A rendering of the contents of this PR can be found in https://rawgit.com/Miguelao/mediacapture-image/pr001_constrainable_pattern/index.html

@yell0wd0g
Copy link
Member Author

@alvestrand can you please take a first coarse look?

@beaufortfrancois
Copy link
Contributor

@miguelao Can you update examples as well?

Copy link
Contributor

@alvestrand alvestrand left a comment

Choose a reason for hiding this comment

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

I like this change. A number of details are noted in review.

</dd>
<dl class="domintro">
<dt><dfn dict-member for="MediaTrackSupportedConstraints"><code>whiteBalanceMode</code></dfn></dt>
<dd>Whether <a>white balance mode</a> constraining is supported.</dd>
Copy link
Contributor

Choose a reason for hiding this comment

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

I suggest to use the term "recognized" here as well as in the intro, to lessen the (high!) chance that people think that it's guaranteed to work when this member is true. It's not guaranteed to work, it's just guaranteed to be recognized.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

<pre class="idl">
partial dictionary MediaTrackConstraintSet {
MeteringMode whiteBalanceMode;
ConstraintDouble colorTemperature;
Copy link
Contributor

Choose a reason for hiding this comment

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

The type is named "ConstrainDouble". Somehow the link works, but the spelling is wrong.

Copy link
Member Author

Choose a reason for hiding this comment

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

Corrected. The link was working because I defined it at the bottom of the file, and the text section was wrong while the url was right:

urlPrefix: https://www.w3.org/TR/mediacapture-streams/#; type: interface; text: ConstrainDouble ; url: idl-def-ConstrainDouble

<li><dfn>Fill light mode</dfn> describes the flash setting of the capture device (e.g. |auto|, |off|, |on|). </li>
</ol>

# {{MediaSettingsRange}} # {#mediasettingsrange-section}

<pre class="idl">
interface MediaSettingsRange {
Copy link
Contributor

Choose a reason for hiding this comment

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

All my previous comments on the "step" still apply. I'm sketptical.
IDL-wise, I'd like you to make this a subclass of DoubleRange, to emphasize that that's what it is.

Copy link
Member Author

Choose a reason for hiding this comment

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

MediaSettingsRange is an interface

<pre class="idl">
  interface MediaSettingsRange {
      readonly attribute double max;
      readonly attribute double min;
      readonly attribute double step;
  };
</pre>

whereas DoubleRange is a dictionary.

dictionary DoubleRange {
             double max;
             double min;
};

It makes sense to have it as a Dictionary since it's used for configuring constraints in the track; in ImageCapture it makes sense to have it as an interface because settings ranges are always composed of min-max and step. So, they're different ways to represent related concepts and both are OK.

@@ -40,7 +40,7 @@ table td, table th {

The API defined in this document captures images from a photographic device referenced through a valid {{MediaStreamTrack}}. The produced image can be in the form of a {{Blob}} (see {{takePhoto()}} method) or as a {{ImageBitmap}} (see {{grabFrame()}}). Picture-specific settings can be optionally provided as arguments that can be applied to the device for the capture.

# Image Capture API # {#imagecaptureapi"}
# Image Capture API # {#imagecaptureapi}
Copy link
Contributor

Choose a reason for hiding this comment

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

You need an explanaition up here to explain where the constraints fit in. Something like:

"This document defines a number of new constraints for a MediaStreamTrack that can be applied in order to make its behavior more suitable for taking pictures. The use of these constraints is via the standard getUserMedia() and applyConstraints() methods for a MediaStreamTrack. They will modify the behavior of the track and its source independently of the usage of the track with the ImageCapture object."

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@yell0wd0g
Copy link
Member Author

@beaufortfrancois the examples as they are would still work assuming that ImageCapture is also a LegacyImageCapture, which still has getPhotoCapabilities() and setOptions(). I propose making new examples and making evident that the current one is legacy-oriented in another, subsequent CL (if we could only make GitHub issues "blocked" on others as we do in crbug.com... then it'd be clearer). Any way I filed #148 for it.

@yell0wd0g
Copy link
Member Author

@alvestrand comments addressed except the MediaSettingsRange one. PTAL.

@gmandyam I think you can take a look now, PTAL.

@yell0wd0g
Copy link
Member Author

ping @alvestrand

@yell0wd0g
Copy link
Member Author

@alvestrand @gmandyam PTAL

@gmandyam
Copy link
Collaborator

gmandyam commented Feb 9, 2017

@miguelao
Why are the ConstrainDouble and ConstraintBoolean types necessary? It is hard to trace exactly what data types the corresponding constraints must follow.

@yell0wd0g
Copy link
Member Author

yell0wd0g commented Feb 9, 2017

ConstrainBla allows for things like e.g. { zoom : 215 }, { zoom : { min : 200, max : 225}}, { zoom : { exact : 213 }} etc which are part of the constrainable pattern definition. (Note that there's a typo, ConstraintBoolean should be ConstrainBoolean).

@gmandyam
Copy link
Collaborator

gmandyam commented Feb 9, 2017

Sorry - that was my mistake. I thought you had redefined ConstrainBoolean, etc. in the Image Capture spec. Turns out you had just linked to the Media Capture and Streams spec.

@gmandyam
Copy link
Collaborator

gmandyam commented Feb 10, 2017

Assume that the MediaStream is currently sourcing a video element. Upon a successful call of applyConstraints(), is the expectation that all the media track constraints defined in Image Capture will be reflected in the video being rendered in the video element (e.g. contrast, sharpness, etc.)? Or will they only be applied when one of the ImageCapture methods (e.g. takePhoto) are invoked?

@yell0wd0g
Copy link
Member Author

I believe they will be applied and visible at the application moment (plus any hardware delays, e.g. zoom) since we'll be modifying the Track constraints.

@yell0wd0g
Copy link
Member Author

@alvestrand ping
@gmandyam all clear now from your side?

@gmandyam
Copy link
Collaborator

@miguelao, @alvestrand

I think we can go forward with this change.

@yell0wd0g
Copy link
Member Author

@alvestrand please take another look otherwise it's good to go.

# MediaSettingsRange
## Constrainable Properties ## {#constrainable-properties}

Many of these fields mirror hardware capabilities that are hard to define since can be implemented in a number of ways. Moreover, hardware manufacturers tend to publish vague definitions to protect their intellectual property. The following definitions are assumed for individual settings and are provided for information purposes:
Copy link

Choose a reason for hiding this comment

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

nit: should there be a they or something similar inserted between since and can?

@stefhak
Copy link

stefhak commented Feb 11, 2017

@miguelao Harald is on vacation for the next two weeks. Unless @burnburn can review and comment quickly I propose you merge and that we take it from there. @burnburn would you have the cycles to go over this in the next few days?

@stefhak
Copy link

stefhak commented Feb 14, 2017

It seems @burnburn does not have the time to review now, I think this PR should be merged now.

@jan-ivar
Copy link
Member

jan-ivar commented Feb 14, 2017

@miguelao I thought we were only going to move "live" settings to constraints? This appears to move all of them.

@jan-ivar
Copy link
Member

That may be fine, just checking if I missed the rationale.

@yell0wd0g
Copy link
Member Author

@jan-ivar I thought about it, but it might be too complex to have two ways to configure the device; the quintessential example are imageHeight and imageWidth: they are not (visually) reflected in the Track, but seems logical that the user would expect them listed when calling getSettings() or getCapabilities(), etc. IOW, I think it might be better to have them all together than spread across two interfaces. wdyt?

@jan-ivar
Copy link
Member

@miguelao Users would be wrong. :-) What we call "delayed" properties aren't really applied to the device at all at the time of applyConstraints, but later. Thus getSettings() would reveal no current hardware settings, rather just mirror getConstraints(). Doesn't seem very useful.

Also, there'd never be any conflict; no need for min, max or exact except for purposely going outside the deterministic getCapabilities() envelope.

The alternative seems more logical to me:

capture.takePhoto({imageWidth: 3264, imageHeight: 2448});

I.e. fess up to these not really being video track properties, but properties of the takePhoto method.

Of course, logic isn't everything. Expecting users to remember which settings are video settings and which are photo settings, may frustrate more than the functional clarity helps.

@jan-ivar
Copy link
Member

But if imageWidth, imageHeight were takePhoto options, they wouldn't belong in getCapabilities.

@yell0wd0g
Copy link
Member Author

@jan-ivar I understand your last 2 comments; they'd apply to imageWidth, imageHeight and redEyeReduction since those are the only ones not "visible" / "live" (only applied when the photo is actually taken and not reflected on the MSVideoTrack).

But since those 3 still have ranges/supported sets (e.g. imageWidth might support any value between 400px and 4000px in steps of 100px, redEyeReduction can support {true, false}, how would we convey those ranges to the WebApp? Via getCapabilities()? Via some reduced getPhotoCapabilities()? (And a similar argument goes for configuring them). Just double checking that I understood the concrete proposal.

@yell0wd0g
Copy link
Member Author

ping @jan-ivar

@jan-ivar
Copy link
Member

jan-ivar commented Mar 2, 2017

@miguelao Isn't there also exposureMode, iso, focusMode, and fillLightMode from your list?

I don't have a concrete proposal, merely challenging what is most logical:

  • Separate video and photo settings is more logical, less convenient.
  • Single mechanism for settings is more convenient, less logical.

If we chose the former, any ranges for values of takePhoto arguments would not belong in getCapabilities, and would need their own thing IMHO, e.g. getPhotoCapabilities().

e.g. imageWidth might support any value between 400px and 4000px in steps of 100px

In a vacuum where constraints didn't exist, I'd say the most logical way to represent "delayed" (photo) settings would be an array of values for each thing, and then have takePhoto fail if called with values not in the array. No need for a separate "configuring" step, since you know all the valid settings. You're not configuring the device, you're configuring the shot.

If we chose the latter, for "delayed" (photo) properties which never collide, the exact keyword seems to exist solely to compensate for the lack of specificity in getCapabilities().

I'm curious, how do we imagine a user "configuring" their device in the single constraints model? What would the UI look like? How would the app learn the discrete settings to offer as choices?

  • Call applyConstraints({imageWidth: {exact: w}, imageHeight: {exact: h}}) until it succeeds?
  • Call applyConstraints({imageWidth: {min: minWidthICanAccept, max: maxWidthICanAccept}, imageHeight: {min: minHeightICanAccept, max: maxHeightICanAccept}}) and hope it doesn't fail?

What's the ideal UI here? Maybe we should start there.

@yell0wd0g
Copy link
Member Author

Let's bring the list here for conveniency (apologies, iso can be seen immediately in the video feed).

Setting
whiteBalanceMode immediate
colorTemperature immediate
exposureMode delayed
exposureCompensation immediate
iso immediate
redEyeReduction delayed
focusMode delayed
pointsOfInterest probably immediate
brightness immediate
contrast immediate
saturation immediate
sharpness immediate
imageHeight delayed
imageWidth delayed
zoom immediate
fillLightMode both

Code wise we could

  1. keep the current Promise<PhotoCapabilities> getPhotoCapabilities(); returning the "delayed" capabilities (might need some tweaking), i.e.
interface PhotoCapabilities {
  readonly attribute MeteringMode       exposureMode;
  readonly attribute boolean            redEyeReduction;
  readonly attribute MeteringMode       focusMode;
  readonly attribute MediaSettingsRange imageHeight;
  readonly attribute MediaSettingsRange imageWidth;
  readonly attribute FillLightMode      fillLightMode;
};
  1. nuke the current setOptions() and instead pass the mentioned Capabilities directly into takePhoto(), i.e. takePhoto({redEyeReduction : yes, fillLightMode : "auto"});

  2. All the other settings/capabilities would be inspected and/or set via the MediaStreamTrack, e.g. ( iso only for clarity):
    track.getCapabilities() ==> { iso : { min:1000, max : 8000, step : 100, default : 3000}}
    then
    track.applyConstraints({ iso : { ideal:1234}}); ==> works or not
    track.getSettings() ==> {iso : 1234}

So essentially this is the Separate video and photo settings is more logical, less convenient. case of yours. If you agree, I will make a separate PR with this approach extended so we can decide on actualities, wdyt?

@alvestrand
Copy link
Contributor

This makes sense to me - from the viewpoint of "are these constraints usable for video streams", they all seem usable. Agree that "pointsOfInterest" is likely to be immediate & useful for video.

WRT the odd "fillLightMode" case (both), would it make sense to separate this into two capabilities, "light" (a constraint) and "flash" (a photo property)?

@yell0wd0g
Copy link
Member Author

Two updates: I just noticed that exposureMode andfocusMode are also 'live' capabilities, since from the moment the user configures them to any of their values (e.g. off, single-shot, continuous) the effect is immediately visible (as is e.g. whiteBalanceMode). Apologies for any confusion.

I like @alvestrand suggestion, so I'll split torch mode out of fillLightMode, the latter being a photo-capability and the former being a video constraint.

So the only remaining photo capabilities will be:

interface PhotoCapabilities {
  readonly attribute FillLightMode      fillLightMode;  //not including 'torch'
  readonly attribute MediaSettingsRange imageHeight;
  readonly attribute MediaSettingsRange imageWidth;
  readonly attribute FillLightMode      fillLightMode;
};

Finalising a new pull request so we can continue the conversation there.

@yell0wd0g
Copy link
Member Author

#150 is the new pull request.

@jan-ivar
Copy link
Member

jan-ivar commented Mar 3, 2017

track.getCapabilities() ==> { iso : { min:1000, max : 8000, step : 100, default :

👍 except note that there's no such thing as step: in constrainable capabilities (e.g. LongRange).

@yell0wd0g
Copy link
Member Author

Closing this in favour of #150 .

@yell0wd0g yell0wd0g closed this Mar 11, 2017
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.

6 participants