Bug 1926249 - Implement support for desired width/height with WebCodecs ImageDecoder. r=media-playback-reviewers,padenot

Differential Revision: https://phabricator.services.mozilla.com/D226498
This commit is contained in:
Andrew Osmond
2024-10-22 13:45:36 +00:00
parent 00e0a758fc
commit 509fefbffd
7 changed files with 115 additions and 13 deletions

View File

@@ -45,11 +45,14 @@ class ImageDecoder::ControlMessage {
class ImageDecoder::ConfigureMessage final class ImageDecoder::ConfigureMessage final
: public ImageDecoder::ControlMessage { : public ImageDecoder::ControlMessage {
public: public:
explicit ConfigureMessage(ColorSpaceConversion aColorSpaceConversion) explicit ConfigureMessage(const Maybe<gfx::IntSize>& aOutputSize,
: mColorSpaceConversion(aColorSpaceConversion) {} ColorSpaceConversion aColorSpaceConversion)
: mOutputSize(aOutputSize),
mColorSpaceConversion(aColorSpaceConversion) {}
ConfigureMessage* AsConfigureMessage() override { return this; } ConfigureMessage* AsConfigureMessage() override { return this; }
const Maybe<gfx::IntSize> mOutputSize;
const ColorSpaceConversion mColorSpaceConversion; const ColorSpaceConversion mColorSpaceConversion;
}; };
@@ -152,9 +155,10 @@ void ImageDecoder::Destroy() {
} }
void ImageDecoder::QueueConfigureMessage( void ImageDecoder::QueueConfigureMessage(
const Maybe<gfx::IntSize>& aOutputSize,
ColorSpaceConversion aColorSpaceConversion) { ColorSpaceConversion aColorSpaceConversion) {
mControlMessageQueue.push( mControlMessageQueue.push(
MakeUnique<ConfigureMessage>(aColorSpaceConversion)); MakeUnique<ConfigureMessage>(aOutputSize, aColorSpaceConversion));
} }
void ImageDecoder::QueueDecodeMetadataMessage() { void ImageDecoder::QueueDecodeMetadataMessage() {
@@ -239,8 +243,8 @@ MessageProcessedResult ImageDecoder::ProcessConfigureMessage(
// 3. Otherwise, assign the [[codec implementation]] internal slot with an // 3. Otherwise, assign the [[codec implementation]] internal slot with an
// implementation supporting init.type // implementation supporting init.type
mDecoder = mDecoder = image::ImageUtils::CreateDecoder(mSourceBuffer, type,
image::ImageUtils::CreateDecoder(mSourceBuffer, type, surfaceFlags); aMsg->mOutputSize, surfaceFlags);
if (NS_WARN_IF(!mDecoder)) { if (NS_WARN_IF(!mDecoder)) {
MOZ_LOG(gWebCodecsLog, LogLevel::Error, MOZ_LOG(gWebCodecsLog, LogLevel::Error,
("ImageDecoder %p Initialize -- failed to create platform decoder", ("ImageDecoder %p Initialize -- failed to create platform decoder",
@@ -680,9 +684,17 @@ void ImageDecoder::Initialize(const GlobalObject& aGlobal,
return; return;
} }
Maybe<gfx::IntSize> desiredSize;
if (aInit.mDesiredWidth.WasPassed() && aInit.mDesiredHeight.WasPassed()) {
desiredSize.emplace(
std::min(aInit.mDesiredWidth.Value(), static_cast<uint32_t>(INT32_MAX)),
std::min(aInit.mDesiredHeight.Value(),
static_cast<uint32_t>(INT32_MAX)));
}
// 10.2.2.17.3 / 10.2.2.18.6. // 10.2.2.17.3 / 10.2.2.18.6.
// Queue a control message to configure the image decoder with init. // Queue a control message to configure the image decoder with init.
QueueConfigureMessage(aInit.mColorSpaceConversion); QueueConfigureMessage(desiredSize, aInit.mColorSpaceConversion);
// 10.2.10.2.2.18.7. Queue a control message to decode track metadata. // 10.2.10.2.2.18.7. Queue a control message to decode track metadata.
// //

View File

@@ -14,6 +14,7 @@
#include "mozilla/UniquePtr.h" #include "mozilla/UniquePtr.h"
#include "mozilla/dom/ImageDecoderBinding.h" #include "mozilla/dom/ImageDecoderBinding.h"
#include "mozilla/dom/WebCodecsUtils.h" #include "mozilla/dom/WebCodecsUtils.h"
#include "mozilla/gfx/Point.h"
#include "nsCycleCollectionParticipant.h" #include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h" #include "nsWrapperCache.h"
@@ -113,7 +114,8 @@ class ImageDecoder final : public nsISupports, public nsWrapperCache {
void Reset(const MediaResult& aResult); void Reset(const MediaResult& aResult);
void Close(const MediaResult& aResult); void Close(const MediaResult& aResult);
void QueueConfigureMessage(ColorSpaceConversion aColorSpaceConversion); void QueueConfigureMessage(const Maybe<gfx::IntSize>& aOutputSize,
ColorSpaceConversion aColorSpaceConversion);
void QueueDecodeMetadataMessage(); void QueueDecodeMetadataMessage();
void QueueDecodeFrameMessage(); void QueueDecodeFrameMessage();

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

View File

@@ -5,9 +5,15 @@ prefs = [
"dom.media.webcodecs.enabled=true", "dom.media.webcodecs.enabled=true",
"dom.media.webcodecs.image-decoder.enabled=true", "dom.media.webcodecs.image-decoder.enabled=true",
] ]
support-files = ["bug1921817.jpg"] support-files = [
"bug1921817.jpg",
"green.png",
]
["test_videoFrame_mismatched_codedSize.html"] ["test_videoFrame_mismatched_codedSize.html"]
["test_bug1921817.html"] ["test_bug1921817.html"]
scheme = "https" scheme = "https"
["test_imageDecoder_desiredSize.html"]
scheme = "https"

View File

@@ -0,0 +1,57 @@
<!DOCTYPE HTML>
<html>
<head>
<title></title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script>
// Bug 1924775 - ESLint doesn't yet know about `ImageDecoder`.
/* globals ImageDecoder:false */
async function test(desiredWidth, desiredHeight, expectedWidth, expectedHeight) {
const imgResponse = await fetch("green.png");
const decoder = new ImageDecoder({
data: imgResponse.body,
type: "image/png",
desiredWidth,
desiredHeight,
});
// Should download all the data and decode metadata just fine.
await decoder.completed;
await decoder.tracks.ready;
is(decoder.tracks.length, 1, "Should have one track");
is(decoder.tracks[0].frameCount, 1, "Should have a single frame");
try {
const result = await decoder.decode();
ok(result.complete, "Should have complete image");
is(result.image.codedWidth, expectedWidth, "Should have expected width");
is(result.image.codedHeight, expectedHeight, "Should have expected height");
} catch (e) {
ok(false, "Decode image failed with " + e)
}
return Promise.resolve();
}
async function initTest() {
SimpleTest.waitForExplicitFinish();
try {
await test(10, 20, 10, 20);
await test(100, 300, 100, 100);
await test(500, 40, 100, 100);
await test(500, 300, 100, 100);
} catch (e) {
ok(false, "Unexpected error " + e);
} finally {
SimpleTest.finish();
}
}
initTest();
</script>
</body>
</html>

View File

@@ -171,6 +171,12 @@ class AnonymousFramesDecoderTask final : public AnonymousDecoderTask {
ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner) ThreadSafeWeakPtr<AnonymousDecoder>&& aOwner)
: AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {} : AnonymousDecoderTask(std::move(aDecoder), std::move(aOwner)) {}
void SetOutputSize(const OrientedIntSize& aSize) {
if (mDecoder) {
mDecoder->SetOutputSize(aSize);
}
}
protected: protected:
bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame, bool OnFrameAvailable(RefPtr<imgFrame>&& aFrame,
RefPtr<gfx::SourceSurface>&& aSurface) override { RefPtr<gfx::SourceSurface>&& aSurface) override {
@@ -194,8 +200,9 @@ class AnonymousFramesDecoderTask final : public AnonymousDecoderTask {
class AnonymousDecoderImpl final : public AnonymousDecoder { class AnonymousDecoderImpl final : public AnonymousDecoder {
public: public:
AnonymousDecoderImpl() explicit AnonymousDecoderImpl(const Maybe<gfx::IntSize>& aOutputSize)
: mMutex("mozilla::image::AnonymousDecoderImpl::mMutex") {} : mMutex("mozilla::image::AnonymousDecoderImpl::mMutex"),
mOutputSize(aOutputSize) {}
~AnonymousDecoderImpl() override { Destroy(); } ~AnonymousDecoderImpl() override { Destroy(); }
@@ -298,6 +305,23 @@ class AnonymousDecoderImpl final : public AnonymousDecoder {
this, size.width, size.height, mMetadataResult.mRepetitions, this, size.width, size.height, mMetadataResult.mRepetitions,
mMetadataResult.mAnimated)); mMetadataResult.mAnimated));
if (mOutputSize && !mMetadataResult.mAnimated && mFramesTask) {
if (mOutputSize->width <= size.width &&
mOutputSize->height <= size.height) {
MOZ_LOG(
sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::OnMetadata -- use output size %dx%d",
this, mOutputSize->width, mOutputSize->height));
mFramesTask->SetOutputSize(
OrientedIntSize::FromUnknownSize(*mOutputSize));
} else {
MOZ_LOG(sLog, LogLevel::Debug,
("[%p] AnonymousDecoderImpl::OnMetadata -- cannot use output "
"size %dx%d, exceeds metadata size",
this, mOutputSize->width, mOutputSize->height));
}
}
if (!mMetadataResult.mAnimated) { if (!mMetadataResult.mAnimated) {
mMetadataResult.mFrameCount = 1; mMetadataResult.mFrameCount = 1;
mMetadataResult.mFrameCountComplete = true; mMetadataResult.mFrameCountComplete = true;
@@ -526,6 +550,7 @@ class AnonymousDecoderImpl final : public AnonymousDecoder {
RefPtr<imgFrame> mLastFrame MOZ_GUARDED_BY(mMutex); RefPtr<imgFrame> mLastFrame MOZ_GUARDED_BY(mMutex);
DecodeMetadataResult mMetadataResult MOZ_GUARDED_BY(mMutex); DecodeMetadataResult mMetadataResult MOZ_GUARDED_BY(mMutex);
DecodeFramesResult mPendingFramesResult MOZ_GUARDED_BY(mMutex); DecodeFramesResult mPendingFramesResult MOZ_GUARDED_BY(mMutex);
Maybe<gfx::IntSize> mOutputSize MOZ_GUARDED_BY(mMutex);
size_t mFramesToDecode MOZ_GUARDED_BY(mMutex) = 1; size_t mFramesToDecode MOZ_GUARDED_BY(mMutex) = 1;
uint32_t mFrameCount MOZ_GUARDED_BY(mMutex) = 0; uint32_t mFrameCount MOZ_GUARDED_BY(mMutex) = 0;
bool mMetadataTaskRunning MOZ_GUARDED_BY(mMutex) = false; bool mMetadataTaskRunning MOZ_GUARDED_BY(mMutex) = false;
@@ -535,7 +560,7 @@ class AnonymousDecoderImpl final : public AnonymousDecoder {
/* static */ already_AddRefed<AnonymousDecoder> ImageUtils::CreateDecoder( /* static */ already_AddRefed<AnonymousDecoder> ImageUtils::CreateDecoder(
SourceBuffer* aSourceBuffer, DecoderType aType, SourceBuffer* aSourceBuffer, DecoderType aType,
SurfaceFlags aSurfaceFlags) { const Maybe<gfx::IntSize>& aOutputSize, SurfaceFlags aSurfaceFlags) {
if (NS_WARN_IF(!aSourceBuffer)) { if (NS_WARN_IF(!aSourceBuffer)) {
return nullptr; return nullptr;
} }
@@ -551,7 +576,7 @@ class AnonymousDecoderImpl final : public AnonymousDecoder {
return nullptr; return nullptr;
} }
auto anonymousDecoder = MakeRefPtr<AnonymousDecoderImpl>(); auto anonymousDecoder = MakeRefPtr<AnonymousDecoderImpl>(aOutputSize);
if (NS_WARN_IF(!anonymousDecoder->Initialize(std::move(decoder)))) { if (NS_WARN_IF(!anonymousDecoder->Initialize(std::move(decoder)))) {
return nullptr; return nullptr;
} }

View File

@@ -126,7 +126,7 @@ class ImageUtils {
public: public:
static already_AddRefed<AnonymousDecoder> CreateDecoder( static already_AddRefed<AnonymousDecoder> CreateDecoder(
SourceBuffer* aSourceBuffer, DecoderType aType, SourceBuffer* aSourceBuffer, DecoderType aType,
SurfaceFlags aSurfaceFlags); const Maybe<gfx::IntSize>& aOutputSize, SurfaceFlags aSurfaceFlags);
static DecoderType GetDecoderType(const nsACString& aMimeType); static DecoderType GetDecoderType(const nsACString& aMimeType);