LibWeb: Implement MediaCapabilities.decodingInfo()

This commit is contained in:
Psychpsyo 2025-02-12 17:29:29 +01:00 committed by Andrew Kaster
parent 3b577e6135
commit 0206697d36
Notes: github-actions[bot] 2025-02-18 17:19:43 +00:00
10 changed files with 1047 additions and 1 deletions

View file

@ -1,16 +1,114 @@
/*
* Copyright (c) 2024, Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (c) 2025, Psychpsyo <psychpsyo@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/BooleanObject.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/MediaCapabilitiesPrototype.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/MediaCapabilitiesAPI/MediaCapabilities.h>
#include <LibWeb/Platform/EventLoopPlugin.h>
namespace Web::MediaCapabilitiesAPI {
// https://w3c.github.io/media-capabilities/#valid-mediaconfiguration
bool MediaConfiguration::is_valid_media_configuration() const
{
// For a MediaConfiguration to be a valid MediaConfiguration, all of the following conditions MUST be true:
// 1. audio and/or video MUST exist.
if (!audio.has_value() && !video.has_value())
return false;
// 2. audio MUST be a valid audio configuration if it exists.
if (audio.has_value() && !audio.value().is_valid_audio_configuration())
return false;
// 3. video MUST be a valid video configuration if it exists.
if (video.has_value() && !video.value().is_valid_video_configuration())
return false;
return true;
}
// https://w3c.github.io/media-capabilities/#valid-mediadecodingconfiguration
bool MediaDecodingConfiguration::is_valid_media_decoding_configuration() const
{
// For a MediaDecodingConfiguration to be a valid MediaDecodingConfiguration, all of the following
// conditions MUST be true:
// 1. It MUST be a valid MediaConfiguration.
if (!is_valid_media_configuration())
return false;
// 2. If keySystemConfiguration exists:
// FIXME: Implement this.
return true;
}
// https://w3c.github.io/media-capabilities/#valid-audio-mime-type
bool is_valid_audio_mime_type(StringView string)
{
// A valid audio MIME type is a string that is a valid media MIME type and for which the type per [RFC9110] is
// either audio or application.
auto mime_type = MimeSniff::MimeType::parse(string);
if (!mime_type.has_value())
return false;
return mime_type->type() == "audio"sv || mime_type->type() == "application"sv;
}
// https://w3c.github.io/media-capabilities/#valid-video-mime-type
bool is_valid_video_mime_type(StringView string)
{
// A valid video MIME type is a string that is a valid media MIME type and for which the type per [RFC9110] is
// either video or application.
auto mime_type = MimeSniff::MimeType::parse(string);
if (!mime_type.has_value())
return false;
return mime_type->type() == "video"sv || mime_type->type() == "application"sv;
}
// https://w3c.github.io/media-capabilities/#valid-video-configuration
bool VideoConfiguration::is_valid_video_configuration() const
{
// To check if a VideoConfiguration configuration is a valid video configuration, the following steps MUST be
// run:
// 1. If configurations contentType is not a valid video MIME type, return false and abort these steps.
if (!is_valid_video_mime_type(content_type))
return false;
// 2. If framerate is not finite or is not greater than 0, return false and abort these steps.
if (!isfinite(framerate) || framerate <= 0)
return false;
// 3. If an optional member is specified for a MediaDecodingType or MediaEncodingType to which its not
// applicable, return false and abort these steps. See applicability rules in the member definitions below.
// FIXME: Implement this.
// 4. Return true.
return true;
}
// https://w3c.github.io/media-capabilities/#valid-video-configuration
bool AudioConfiguration::is_valid_audio_configuration() const
{
// To check if a AudioConfiguration configuration is a valid audio configuration, the following steps MUST be
// run:
// 1. If configurations contentType is not a valid audio MIME type, return false and abort these steps.
if (!is_valid_audio_mime_type(content_type))
return false;
// 2. Return true.
return true;
}
GC_DEFINE_ALLOCATOR(MediaCapabilities);
GC::Ref<MediaCapabilities> MediaCapabilities::create(JS::Realm& realm)
@ -29,4 +127,120 @@ void MediaCapabilities::initialize(JS::Realm& realm)
WEB_SET_PROTOTYPE_FOR_INTERFACE(MediaCapabilities);
}
// https://w3c.github.io/media-capabilities/#queue-a-media-capabilities-task
void queue_a_media_capabilities_task(JS::VM& vm, Function<void()> steps)
{
// When an algorithm queues a Media Capabilities task T, the user agent MUST queue a global task T on the
// media capabilities task source using the global object of the the current realm record.
queue_global_task(HTML::Task::Source::MediaCapabilities, vm.current_realm()->global_object(), GC::create_function(vm.current_realm()->heap(), move(steps)));
}
// https://w3c.github.io/media-capabilities/#dom-mediacapabilities-decodinginfo
GC::Ref<WebIDL::Promise> MediaCapabilities::decoding_info(MediaDecodingConfiguration const& configuration)
{
auto& realm = this->realm();
// The decodingInfo() method MUST run the following steps:
// 1. If configuration is not a valid MediaDecodingConfiguration, return a Promise rejected with a newly created
// TypeError.
if (!configuration.is_valid_media_decoding_configuration()) {
return WebIDL::create_rejected_promise_from_exception(realm, vm().throw_completion<JS::TypeError>("The given configuration is not a valid MediaDecodingConfiguration"sv));
}
// 2. If configuration.keySystemConfiguration exists, run the following substeps:
// FIXME: Implement this.
// 3. Let p be a new Promise.
auto p = WebIDL::create_promise(realm);
// 4. Run the following steps in parallel:
auto& vm = this->vm();
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [&vm, &realm, p, configuration]() mutable {
HTML::TemporaryExecutionContext context(realm);
// 1. Run the Create a MediaCapabilitiesDecodingInfo algorithm with configuration.
auto result = create_a_media_capabilities_decoding_info(configuration).to_object(realm);
// Queue a Media Capabilities task to resolve p with its result.
queue_a_media_capabilities_task(vm, [&realm, p, result] {
HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
WebIDL::resolve_promise(realm, p, JS::Value(result));
});
}));
// 5. Return p.
return p;
}
// https://w3c.github.io/media-capabilities/#create-a-mediacapabilitiesdecodinginfo
MediaCapabilitiesDecodingInfo create_a_media_capabilities_decoding_info(MediaDecodingConfiguration configuration)
{
// 1. Let info be a new MediaCapabilitiesDecodingInfo instance. Unless stated otherwise, reading and
// writing apply to info for the next steps.
MediaCapabilitiesDecodingInfo info = {};
// 2. Set configuration to be a new MediaDecodingConfiguration. For every property in configuration create
// a new property with the same name and value in configuration.
info.configuration = { { configuration.video, configuration.audio }, configuration.type, configuration.key_system_configuration };
// 3. If configuration.keySystemConfiguration exists:
if (false) {
// FIXME: Implement this.
}
// 4. Otherwise, run the following steps:
else {
// 1. Set keySystemAccess to null.
// FIXME: Implement this.
// 2. If the user agent is able to decode the media represented by configuration, set supported to true.
// 3. Otherwise, set it to false.
info.supported = is_able_to_decode_media(configuration);
}
// 5. If the user agent is able to decode the media represented by configuration at the indicated framerate without
// dropping frames, set smooth to true. Otherwise set it to false.
// FIXME: Actually check this.
info.smooth = false;
// 6. If the user agent is able to decode the media represented by configuration in a power efficient manner, set
// powerEfficient to true. Otherwise set it to false.
// FIXME: Actually check this... somehow.
info.power_efficient = false;
// 7. Return info.
return info;
}
bool is_able_to_decode_media(MediaDecodingConfiguration configuration)
{
if (configuration.type != Bindings::MediaDecodingType::MediaSource)
return false;
if (configuration.video.has_value()) {
auto video_mime_type = MimeSniff::MimeType::parse(configuration.video.value().content_type);
if (!video_mime_type.has_value() || !Web::HTML::HTMLMediaElement::supported_video_subtypes.contains_slow(video_mime_type->subtype()))
return false;
}
if (configuration.audio.has_value()) {
auto audio_mime_type = MimeSniff::MimeType::parse(configuration.audio.value().content_type);
if (!audio_mime_type.has_value() || !Web::HTML::HTMLMediaElement::supported_audio_subtypes.contains_slow(audio_mime_type->subtype()))
return false;
}
return true;
}
GC::Ref<JS::Object> MediaCapabilitiesDecodingInfo::to_object(JS::Realm& realm)
{
auto object = JS::Object::create(realm, realm.intrinsics().object_prototype());
// FIXME: Also include configuration in this object.
MUST(object->create_data_property("supported", JS::BooleanObject::create(realm, supported)));
MUST(object->create_data_property("smooth", JS::BooleanObject::create(realm, smooth)));
MUST(object->create_data_property("powerEfficent", JS::BooleanObject::create(realm, power_efficient)));
return object;
}
}