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

[Proposal] Update mediaCapabilitiesProber API #1472

Open
wants to merge 14 commits into
base: dev
Choose a base branch
from

Conversation

peaBerberian
Copy link
Collaborator

After some brainstorming related to #1470, we noticed that as is, our old mediaCapabilitiesProber experimental tool could gain by being more flexible.

Especially, the getCompatibleDRMConfigurations method checked multiple EME MediaKeySystemConfiguration at once and returned information on actual configurations compatible to them once all of them have been checked.

We wondered whether this method would not be more useful by making it less powerful: we'll now only check for a single MediaKeySystemConfiguration per method call (and rename that method to checkDrmConfiguration).
Doing this signals to an application that it can just combine multiple calls if it wants the old behavior (e.g. through a Promise.all), use Promise.race to just obtain the first one etc. (this would still be possible with the previous API, but seemed unnatural to application developers).

I also added a timeout argument to it, but realized that it might be very complex to maintain, as theoretically checkDrmConfiguration could call multiple browser API sequentially in the future (as other mediaCapabilitiesProber's methods are doing).
In that case, where are we supposed to apply the timeout: to each browser API call or globally on the method?


I also profited from this work to rewrite the implementation of other mediaCapabilitiesProber's method without changing their API, as I found that a lot of its data-processing code was unneeded and difficult to follow.

@peaBerberian peaBerberian added the proposal This Pull Request or Issue is only a proposal for a change with the expectation of a debate on it label Jun 26, 2024
@peaBerberian peaBerberian added this to the 4.2.0 milestone Jun 27, 2024
@peaBerberian peaBerberian added the Priority: 2 (Medium) This issue or PR has a medium priority. label Jul 26, 2024
@peaBerberian peaBerberian force-pushed the misc/mcp-refacto branch 3 times, most recently from 99059eb to 538aff1 Compare September 4, 2024 16:57
@peaBerberian peaBerberian modified the milestones: 4.2.0, 4.3.0 Oct 4, 2024
Comment on lines +131 to +133
console.log("This device is compatible with the given configuration!");
console.log("Wanted configuration:", configuration);
console.log("Compatible configuration:", compatibleConfiguration);
Copy link
Collaborator

Choose a reason for hiding this comment

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

this example is not working, configuration and compatibleConfiguration are not defined

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You're right, it's directly the compatible one instead. I'll update.

Comment on lines +81 to +83
const result: ISupportWithFeatures = (
globalScope as IGlobalScopeWithMSMediaKeysFeatures
).MSMediaKeys.isTypeSupportedWithFeatures(type, features);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure how this API works, do you have any documentation on this one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It was an old edge feature only present on MSMediaKeys and was able to check for more precize policy support.

I also have trouble finding documentation on it today, my searches for now only lead to the RxPlayer and complaints from Netflix users :p

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

TypeScript at some point had it in its type definitions: https://raw.githubusercontent.com/Microsoft/TypeScript/v2.9.2/lib/lib.es6.d.ts

Though not anymore

}
} catch (err) {
const error = err instanceof Error ? err : new Error("Unknown Error");
log.error("MCP: `probeHDCPPolicy` didn't succeed to create a MediaKeys", error);
Copy link
Collaborator

Choose a reason for hiding this comment

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

From what I read, the error log here is not accurate:
We can get here if the API getStatusForPolicy does not exist, not because the MediaKeys failed to be created.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You're right requestMediaKeySystemAccess and createMediaKeys don't even have their try/catch.

I'll fix that

Comment on lines +61 to +70
try {
await probeDecodingInfos(configuration);
resetDecodingInfos();
} catch (error) {
const message = error instanceof Error ? error.message : "";
thrownException = true;
expect(message).toEqual("Not enough arguments for calling mediaCapabilites.");
expect(decodingInfoStub).not.toHaveBeenCalled();
resetDecodingInfos();
}
Copy link
Collaborator

@Florent-Bouisset Florent-Bouisset Oct 22, 2024

Choose a reason for hiding this comment

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

remark about resetDecodingInfos(), it can be done only once outside the try..catch

try {
  await probeDecodingInfos(configuration);
} catch (error) {
  const message = error instanceof Error ? error.message : "";
  thrownException = true;
  expect(message).toEqual("Not enough arguments for calling mediaCapabilites.");
  expect(decodingInfoStub).not.toHaveBeenCalled();
}
resetDecodingInfos();

But a similar code is runned in the afterEach() hook.
So we can delete that code from every test since it's already done in the hook and check that it works well

@@ -82,22 +80,21 @@ describe("MediaCapabilitiesProber probers - decodingInfo", () => {
},
};

expect.assertions(2);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I just learned about expect.assertions() is it something that should be avoided?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't like using it because it's something easy to forget about and it's not really a meaningful test beside ensuring that a code path with assertions has been taken, in which case I prefer a more explicit check like a boolean.

// eslint-disable-next-line @typescript-eslint/no-floating-promises
expect(probeDecodingInfos({})).rejects.toThrowError(
"MediaCapabilitiesProber >>> API_CALL: MediaCapabilities API not available",
return probeDecodingInfos({}).then(
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is there a return ?
Also there is jest modifier .rejects that help with this kind of test:

await expect(probeDecodingInfos({})).rejects.toThrowError("navigator.mediaCapabilites.decodingInfo is not available");

Copy link
Collaborator Author

@peaBerberian peaBerberian Oct 22, 2024

Choose a reason for hiding this comment

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

When tests return a promise in most testing frameworks, the test will reject when the promise rejects and resolve when the promise resolve.

I know about rejects, and that was what was there before, but for some reason I found returning the promise more readable.

Comment on lines +186 to +195
return probeDecodingInfos({}).then(
() => {
throw new Error("Should not succeed");
},
(err: Error) => {
expect(err).toBeInstanceOf(Error);
expect(err.message).toEqual(
"navigator.mediaCapabilites.decodingInfo is not available",
);
},
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here

expect(probeMediaContentType({})).rejects.toThrowError(
"MediaCapabilitiesProber >>> API_CALL: " + "MediaSource API not available",
);
expect(() => probeMediaContentType({})).toThrowError("MediaSource API not available");
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

the eslint error could have been fixed by adding await before expect

Comment on lines +29 to 31
expect(() => probeMediaContentType({})).toThrowError(
"MediaSource.isTypeSupported API not available",
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here

globalScope as { matchMedia: typeof globalScope.matchMedia | undefined }
).matchMedia = origMatchMedia;
expect(() => probeMediaDisplayInfos({})).toThrowError("matchMedia API not available");
globalScope.matchMedia = origMatchMedia;
Copy link
Collaborator

Choose a reason for hiding this comment

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

setting the matchMedia API should be done in the afterEach / beforeEach hooks

support: ISupportWithFeatures,
): "NotSupported" | "Unknown" | "Supported" {
if (support === "") {
throw new Error("Bad arguments for calling isTypeSupportedWithFeatures");
Copy link
Collaborator

Choose a reason for hiding this comment

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

The API returns empty string if arguments are invalid ? Weird one

* @param {Object} mediaConfig
* @returns {Promise.<string>}
*/
async getDecodingCapabilities(mediaConfig: IMediaConfiguration): Promise<string> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

don't the type should be narrowed to "Supported" / "Unknown" / "NotSupported" instead of being a string ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Priority: 2 (Medium) This issue or PR has a medium priority. proposal This Pull Request or Issue is only a proposal for a change with the expectation of a debate on it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants