w3c / tvcontrol-api

TV Control API specification - https://w3c.github.io/tvcontrol-api/
10 stars 11 forks source link

Management of underlying resources #3

Open chrisn opened 7 years ago

chrisn commented 7 years ago

As raised on the mailing list here:

We at Broadcom have implemented TV stack following TV control API Specification (https://www.w3.org/2015/tvapi/Overview.html).

We have usecases involving multiple modules that requires TVTuner. We need some clarification on how to use the apis to handle the following scenario.

Scenario: There are two modules A and B. There is one tuner, tuner1. Module A uses tuner1 and performs its functionality. Module A is finished with its function and release tuner1. Module B now wants to be able to use same tuner1 to acheive a different functionality.

Module A could find tuner1 using TVManager::getTuners() But, once it is done using tuner1, how can moduleA inform TVManager that tuner1 is free so that moduleB can find tuner1 using TVManager::getTuners().

We see there is ontunerchanged event. But how can TVManager be aware when module A has finished using tuner1 in order to fire ontunerchanged event. Is there any api ModuleA can call to inform TVManager that tuner1 is free?

Are changes needed to the specification to support this scenario?

tidoust commented 7 years ago

Are changes needed to the specification to support this scenario?

Trying to throw some thoughts to trigger discussion here...

The scenario seems to assume that moduleB would only see tuner1 when moduleA is done with it. Another possibility would be for getTuners to return tuner1 regardless of whether the tuner is in use. This does not solve the problem though.

If getTuners only returns tuners that are free to use, these tuners would need to be immediately locked and hidden away from other callers as long as the app/module that called the method first does not explicitly release them, e.g. through some hypothetical unlock method. It seems weird to lock resources by default: the app may use only one of the tuners returned for instance and requiring it to start by releasing tuners it does not need seems bad design: a spec can only constrain user agents, it cannot require Web apps to behave in a certain way, and thus should no depend on apps doing certain actions to unlock resources.

If getTuners returns all tuners regardless of their lock state, the tuner could be locked once the app starts using it, typically when it calls setCurrentSource. Unlocking could then be either explicit (e.g. a call to setCurrentSource(null) would release the tuner), or implicit once there are no more references to the tuner instance. In that scenario, tuner.currentSource and tuner.stream must return null as long as the app has not called setCurrentSource once (an app should not be able to see what source another app might have used). Similarly, the currentsourcechanged event should only be triggered for source changes that occur after the initial call to setCurrentSource.

The lock/unlock mechanism could be more explicit, for instance as done in the current draft of the WebVR API for the VRDisplay interface with the requestPresent and exitPresent methods.

Now, setting the source does not necessarily mean that the app is going to use the tuner. It seems a bit heavy to lock the tuner right away. Wouldn't it be better to lock the tuner once the app actually consumes content from it, in other words when it accesses the TVMediaStream?

That would require a change of API, somewhat similar to the discussion started in #4 "Why retrieve a list of tuners instead of just one tuner". In particular, this would suggest a model à la getUserMedia, defined in Media Capture and Streams. In other words, this becomes possible provided the API does not expose tuners and sources as objects on which the app can operate.

In terms of WebIDL, something like:

interface TVManager {
  Promise<sequence<TVChannel>> enumerateChannels();
  Promise<TVMediaStream> getTunerChannel(TVChannel channel);
};

In this model, the notion of sources and tuners may still appear at the TVChannel level, although as information, not as instances of objects that the app may control (no way to switch the tuner to a given source, no way to switch a source to another channel). From the perspective of this issue, that API would behave the same way as getUserMedia and release the underlying resource when the TVMediaStream instance is no longer used. The user agent would be responsible for resource management.

Obviously, among other things, I left channels scanning out of the discussion there. It could be re-introduced although not at the source level, which may be problematic (see issue #7).

stevem-tw commented 7 years ago

In general, I prefer the model where getTuners() returns all tuners, regardless of whether they are currently available, and the resource is locked when the app starts using it (more on this below).

I also agree that an app should not be able to see what another app is using a tuner for, and so TVTuner.currentSource and TVTuner.stream should both return null until TVTuner.setCurrentSource() and TVSource.setCurrentChannel() are called respectively. I would argue that TVSource.setCurrentChannel() is probably the call that locks the resources - this would not require changes to the API.

The question then becomes what happens when a resource is in use and another request tries to claim it (whether from the same app or a different app). Should the request fail, or should it pre-empt the resources? There are valid arguments for both cases.

Steve.

tidoust commented 7 years ago

I would argue that TVSource.setCurrentChannel() is probably the call that locks the resources

I think that model would allow the following scenario:

  1. App 1 - calls TVTuner.setCurrentSource, succeeds
  2. App 2 - calls TVTuner.setCurrentSource with a different source, succeeds
  3. App 1 - calls TVSource.setCurrentChannel. Either the call fails because the source is no longer the current source, or the call succeeds but App 1 cannot read TVTuner.stream since the source is no longer the current source. In both cases, App 1 did not do anything wrong, and if App 2 does not do anything with the tuner in the meantime, it can fix the problem by re-issuing a call to setCurrentSource. It seems strange to design an API that basically encourages apps to retry in case something fails.

Locking the tuner when a call to setCurrentSource is issued would solve that problem.

stevem-tw commented 7 years ago

I agree that an API which encourages apps to retry when something fails is not a good design choice.

This is an issue that caused much debate in other organisations I've worked with. There usually seems to end up being two schools of thought:

  1. Have resources be explicitly locked and unlocked. This causes problems when another app locks a resource and doesn't release it, but does make the expected behaviour clear, as you mention.
  2. Take the approach that the last page/app to request a resource pre-empts others, because that's probably the page that the user is currently interacting with. While it solves the problem of apps not unlocking a resource (i.e. in step 3, the call to setCurrentChannel() would implicitly set the source as well), it can lead to apps having resources taken away from them and having to handle that gracefully.

I don't think either approach is perfect, and there are valid reasons for choosing both approaches. I'm not sure there is a "right" answer to this - it's more about choosing the one that fits best with the philosophy of the other Web APIs.

mg4news commented 7 years ago

The notion of exposing underlying resources always surprises me. I would assume one of the goals of this working group would be to be aligned with video tag syntax. My organisation has taken the position that you need to know the answer to two questions:

Either tuners are irrelevant, OR all possible types of resources are relevant, in which case exposing tuner APIs is simply the start of a long trip down the rabbit hole.

The approach we take is to expose a simple play( SRC, DST ) command. The URI of the SRC & DST defines what needs to happen, the underlying system manages the allocation, yielding, locking, unlocking, etc of resources, and the application remains logically simple. Priority rules are applied [configurably] in the system (application hierarchy, most recent wins, etc) and request types themselves become the resources (i.e. LIVE, HN share, PLAYBACK, etc)

This ends up with a system completely compatibly with video tags, and something that does not start from the perspective of trying to wrap a legacy STB in W3C syntax.

stevem-tw commented 7 years ago

Although I was saying I could see good reasons for supporting both approaches earlier in the thread, on the whole I prefer an implicit resource model, because there will almost inevitably be some implementations where that do not match the resource model you define (for a variety of very good reasons).

I can agree that your two queries are the ones we really need to expose, along with the ability to get a MediaStream object for a piece of content that can be passed to an HTML5 media element.

The complicating factor is that you may not actually know if you can play a piece of content until you've acquired some of the resources, but I think that is an asynchronous failure mode.