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:
@@ -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.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
BIN
dom/media/webcodecs/test/green.png
Normal file
BIN
dom/media/webcodecs/test/green.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 255 B |
@@ -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"
|
||||||
|
|||||||
57
dom/media/webcodecs/test/test_imageDecoder_desiredSize.html
Normal file
57
dom/media/webcodecs/test/test_imageDecoder_desiredSize.html
Normal 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>
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user