// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h"
#include <algorithm>
#include <string>
#include "base/containers/span.h"
#include "base/functional/callback_helpers.h"
#include "media/base/limits.h"
#include "media/base/sample_format.h"
#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_rect_init.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_aac_encoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_data_copy_to_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_data_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_decoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_audio_chunk_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_chunk_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_opus_encoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_plane_layout.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_cssimagevalue_htmlcanvaselement_htmlimageelement_htmlvideoelement_imagebitmap_offscreencanvas_svgimageelement_videoframe.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_color_space_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_encode_options_for_av_1.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_encode_options_for_vp_9.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_frame_buffer_init.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_video_frame_init.h"
#include "third_party/blink/renderer/core/html/canvas/image_data.h"
#include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_data_view.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.h"
#include "third_party/blink/renderer/modules/webaudio/audio_buffer.h"
#include "third_party/blink/renderer/modules/webcodecs/fuzzer_inputs.pb.h"
#include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
#include "third_party/blink/renderer/platform/audio/audio_bus.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
namespace {
// 16 MiB ought to be enough for anybody.
constexpr size_t kMaxBufferLength = 16 * 1024 * 1024;
// Override for maximum frame dimensions to avoid huge allocations.
constexpr uint32_t kMaxVideoFrameDimension = 1024;
} // namespace
base::ScopedClosureRunner MakeScopedGarbageCollectionRequest(
v8::Isolate* isolate) {
return base::ScopedClosureRunner(WTF::BindOnce(
[](v8::Isolate* isolate) {
// Request a V8 GC. Oilpan will be invoked by the GC epilogue.
//
// Multiple GCs may be required to ensure everything is collected (due
// to a chain of persistent handles), so some objects may not be
// collected until a subsequent iteration. This is slow enough as is, so
// we compromise on one major GC, as opposed to the 5 used in
// V8GCController for unit tests.
isolate->RequestGarbageCollectionForTesting(
v8::Isolate::kFullGarbageCollection);
},
WTF::Unretained(isolate)));
}
FakeFunction::FakeFunction(std::string name) : name_(std::move(name)) {}
ScriptValue FakeFunction::Call(ScriptState*, ScriptValue) {
return ScriptValue();
}
VideoDecoderConfig* MakeVideoDecoderConfig(
const wc_fuzzer::ConfigureVideoDecoder& proto) {
auto* config = VideoDecoderConfig::Create();
config->setCodec(proto.codec().c_str());
DOMArrayBuffer* data_copy = DOMArrayBuffer::Create(
proto.description().data(), proto.description().size());
config->setDescription(
MakeGarbageCollected<AllowSharedBufferSource>(data_copy));
return config;
}
AudioDecoderConfig* MakeAudioDecoderConfig(
const wc_fuzzer::ConfigureAudioDecoder& proto) {
AudioDecoderConfig* config = AudioDecoderConfig::Create();
config->setCodec(proto.codec().c_str());
config->setSampleRate(proto.sample_rate());
config->setNumberOfChannels(proto.number_of_channels());
DOMArrayBuffer* data_copy = DOMArrayBuffer::Create(
proto.description().data(), proto.description().size());
config->setDescription(
MakeGarbageCollected<AllowSharedBufferSource>(data_copy));
return config;
}
VideoEncoderConfig* MakeVideoEncoderConfig(
const wc_fuzzer::ConfigureVideoEncoder& proto) {
VideoEncoderConfig* config = VideoEncoderConfig::Create();
config->setCodec(proto.codec().c_str());
config->setHardwareAcceleration(ToAccelerationType(proto.acceleration()));
config->setFramerate(proto.framerate());
config->setWidth(std::min(proto.width(), kMaxVideoFrameDimension));
config->setHeight(std::min(proto.height(), kMaxVideoFrameDimension));
config->setDisplayWidth(proto.display_width());
config->setDisplayHeight(proto.display_height());
if (proto.has_alpha()) {
config->setAlpha(ToAlphaOption(proto.alpha()));
}
if (proto.has_bitrate_mode()) {
config->setBitrateMode(ToBitrateMode(proto.bitrate_mode()));
}
if (proto.has_scalability_mode()) {
config->setScalabilityMode(ToScalabilityMode(proto.scalability_mode()));
}
if (proto.has_latency_mode()) {
config->setLatencyMode(ToLatencyMode(proto.latency_mode()));
}
if (proto.has_content_hint()) {
config->setContentHint(ToContentHint(proto.content_hint()));
}
// Bitrate is truly optional, so don't just take the proto default value.
if (proto.has_bitrate())
config->setBitrate(proto.bitrate());
return config;
}
AudioEncoderConfig* MakeAudioEncoderConfig(
const wc_fuzzer::ConfigureAudioEncoder& proto) {
auto* config = AudioEncoderConfig::Create();
config->setCodec(proto.codec().c_str());
config->setBitrate(proto.bitrate());
config->setNumberOfChannels(proto.number_of_channels());
config->setSampleRate(proto.sample_rate());
if (proto.has_bitrate_mode()) {
config->setBitrateMode(ToBitrateMode(proto.bitrate_mode()));
}
if (proto.has_aac()) {
auto* aac = AacEncoderConfig::Create();
config->setAac(aac);
if (proto.aac().has_format()) {
aac->setFormat(ToAacFormat(proto.aac().format()));
}
}
if (proto.has_opus()) {
auto* opus = OpusEncoderConfig::Create();
config->setOpus(opus);
if (proto.opus().has_frame_duration()) {
opus->setFrameDuration(proto.opus().frame_duration());
}
if (proto.opus().has_complexity()) {
opus->setComplexity(proto.opus().complexity());
}
if (proto.opus().has_packetlossperc()) {
opus->setPacketlossperc(proto.opus().packetlossperc());
}
if (proto.opus().has_useinbandfec()) {
opus->setUseinbandfec(proto.opus().useinbandfec());
}
if (proto.opus().has_usedtx()) {
opus->setUsedtx(proto.opus().usedtx());
}
if (proto.opus().has_signal()) {
opus->setSignal(ToOpusSignal(proto.opus().signal()));
}
if (proto.opus().has_application()) {
opus->setApplication(ToOpusApplication(proto.opus().application()));
}
}
return config;
}
String ToAccelerationType(
wc_fuzzer::ConfigureVideoEncoder_EncoderAccelerationPreference type) {
switch (type) {
case wc_fuzzer::ConfigureVideoEncoder_EncoderAccelerationPreference_ALLOW:
return "no-preference";
case wc_fuzzer::ConfigureVideoEncoder_EncoderAccelerationPreference_DENY:
return "prefer-software";
case wc_fuzzer::ConfigureVideoEncoder_EncoderAccelerationPreference_REQUIRE:
return "prefer-hardware";
}
}
String ToBitrateMode(
wc_fuzzer::ConfigureVideoEncoder_VideoEncoderBitrateMode mode) {
switch (mode) {
case wc_fuzzer::ConfigureVideoEncoder_VideoEncoderBitrateMode_CONSTANT:
return "constant";
case wc_fuzzer::ConfigureVideoEncoder_VideoEncoderBitrateMode_VARIABLE:
return "variable";
case wc_fuzzer::ConfigureVideoEncoder_VideoEncoderBitrateMode_QUANTIZER:
return "quantizer";
}
}
String ToScalabilityMode(
wc_fuzzer::ConfigureVideoEncoder_ScalabilityMode mode) {
switch (mode) {
case wc_fuzzer::ConfigureVideoEncoder_ScalabilityMode_L1T1:
return "L1T1";
case wc_fuzzer::ConfigureVideoEncoder_ScalabilityMode_L1T2:
return "L1T2";
case wc_fuzzer::ConfigureVideoEncoder_ScalabilityMode_L1T3:
return "L1T3";
}
}
String ToLatencyMode(wc_fuzzer::ConfigureVideoEncoder_LatencyMode mode) {
switch (mode) {
case wc_fuzzer::ConfigureVideoEncoder_LatencyMode_QUALITY:
return "quality";
case wc_fuzzer::ConfigureVideoEncoder_LatencyMode_REALTIME:
return "realtime";
}
}
String ToContentHint(wc_fuzzer::ConfigureVideoEncoder_ContentHint hint) {
switch (hint) {
case wc_fuzzer::ConfigureVideoEncoder_ContentHint_NONE:
return "";
case wc_fuzzer::ConfigureVideoEncoder_ContentHint_TEXT:
return "text";
case wc_fuzzer::ConfigureVideoEncoder_ContentHint_MOTION:
return "motion";
case wc_fuzzer::ConfigureVideoEncoder_ContentHint_DETAIL:
return "detail";
}
}
String ToAlphaOption(wc_fuzzer::ConfigureVideoEncoder_AlphaOption option) {
switch (option) {
case wc_fuzzer::ConfigureVideoEncoder_AlphaOption_KEEP:
return "keep";
case wc_fuzzer::ConfigureVideoEncoder_AlphaOption_DISCARD:
return "discard";
}
}
String ToAacFormat(wc_fuzzer::AacFormat format) {
switch (format) {
case wc_fuzzer::AAC:
return "aac";
case wc_fuzzer::ADTS:
return "adts";
}
}
String ToBitrateMode(wc_fuzzer::BitrateMode bitrate_mode) {
switch (bitrate_mode) {
case wc_fuzzer::VARIABLE:
return "variable";
case wc_fuzzer::CONSTANT:
return "constant";
}
}
String ToOpusSignal(wc_fuzzer::OpusSignal opus_signal) {
switch (opus_signal) {
case wc_fuzzer::AUTO:
return "auto";
case wc_fuzzer::MUSIC:
return "music";
case wc_fuzzer::VOICE:
return "voice";
}
}
String ToOpusApplication(wc_fuzzer::OpusApplication opus_application) {
switch (opus_application) {
case wc_fuzzer::VOIP:
return "voip";
case wc_fuzzer::AUDIO:
return "audio";
case wc_fuzzer::LOWDELAY:
return "lowdelay";
}
}
String ToChunkType(wc_fuzzer::EncodedChunkType type) {
switch (type) {
case wc_fuzzer::EncodedChunkType::KEY:
return "key";
case wc_fuzzer::EncodedChunkType::DELTA:
return "delta";
}
}
String ToAudioSampleFormat(wc_fuzzer::AudioSampleFormat format) {
switch (format) {
case wc_fuzzer::AudioSampleFormat::U8:
return "u8";
case wc_fuzzer::AudioSampleFormat::S16:
return "s16";
case wc_fuzzer::AudioSampleFormat::S32:
return "s32";
case wc_fuzzer::AudioSampleFormat::F32:
return "f32";
case wc_fuzzer::AudioSampleFormat::U8_PLANAR:
return "u8-planar";
case wc_fuzzer::AudioSampleFormat::S16_PLANAR:
return "s16-planar";
case wc_fuzzer::AudioSampleFormat::S32_PLANAR:
return "s32-planar";
case wc_fuzzer::AudioSampleFormat::F32_PLANAR:
return "f32-planar";
}
}
int SampleFormatToSampleSize(V8AudioSampleFormat format) {
using FormatEnum = V8AudioSampleFormat::Enum;
switch (format.AsEnum()) {
case FormatEnum::kU8:
case FormatEnum::kU8Planar:
return 1;
case FormatEnum::kS16:
case FormatEnum::kS16Planar:
return 2;
case FormatEnum::kS32:
case FormatEnum::kS32Planar:
case FormatEnum::kF32:
case FormatEnum::kF32Planar:
return 4;
}
}
EncodedVideoChunk* MakeEncodedVideoChunk(
ScriptState* script_state,
const wc_fuzzer::EncodedVideoChunk& proto) {
auto* data = MakeGarbageCollected<AllowSharedBufferSource>(
DOMArrayBuffer::Create(proto.data().data(), proto.data().size()));
auto* init = EncodedVideoChunkInit::Create();
init->setTimestamp(proto.timestamp());
init->setType(ToChunkType(proto.type()));
init->setData(data);
if (proto.has_duration())
init->setDuration(proto.duration());
return EncodedVideoChunk::Create(script_state, init,
IGNORE_EXCEPTION_FOR_TESTING);
}
EncodedAudioChunk* MakeEncodedAudioChunk(
ScriptState* script_state,
const wc_fuzzer::EncodedAudioChunk& proto) {
auto* data = MakeGarbageCollected<AllowSharedBufferSource>(
DOMArrayBuffer::Create(proto.data().data(), proto.data().size()));
auto* init = EncodedAudioChunkInit::Create();
init->setTimestamp(proto.timestamp());
init->setType(ToChunkType(proto.type()));
init->setData(data);
if (proto.has_duration())
init->setDuration(proto.duration());
return EncodedAudioChunk::Create(script_state, init,
IGNORE_EXCEPTION_FOR_TESTING);
}
VideoEncoderEncodeOptions* MakeEncodeOptions(
const wc_fuzzer::EncodeVideo_EncodeOptions& proto) {
VideoEncoderEncodeOptions* options = VideoEncoderEncodeOptions::Create();
// Truly optional, so don't set it if its just a proto default value.
if (proto.has_key_frame())
options->setKeyFrame(proto.key_frame());
if (proto.has_av1() && proto.av1().has_quantizer()) {
auto* av1 = VideoEncoderEncodeOptionsForAv1::Create();
av1->setQuantizer(proto.av1().quantizer());
options->setAv1(av1);
}
if (proto.has_vp9() && proto.vp9().has_quantizer()) {
auto* vp9 = VideoEncoderEncodeOptionsForVp9::Create();
vp9->setQuantizer(proto.vp9().quantizer());
options->setVp9(vp9);
}
return options;
}
BufferAndSource MakeAllowSharedBufferSource(
const wc_fuzzer::AllowSharedBufferSource& proto) {
BufferAndSource result = {};
size_t length =
std::min(static_cast<size_t>(proto.length()), kMaxBufferLength);
DOMArrayBufferBase* buffer = nullptr;
if (proto.shared()) {
buffer = DOMSharedArrayBuffer::Create(static_cast<unsigned>(length), 1);
} else {
auto* array_buffer = DOMArrayBuffer::Create(length, 1);
buffer = array_buffer;
if (proto.transfer()) {
result.buffer = array_buffer;
}
}
DCHECK(buffer);
size_t view_offset =
std::min(static_cast<size_t>(proto.view_offset()), length);
size_t view_length =
std::min(static_cast<size_t>(proto.view_length()), length - view_offset);
switch (proto.view_type()) {
case wc_fuzzer::AllowSharedBufferSource_ViewType_NONE:
result.source = MakeGarbageCollected<AllowSharedBufferSource>(buffer);
break;
case wc_fuzzer::AllowSharedBufferSource_ViewType_INT8:
result.source = MakeGarbageCollected<AllowSharedBufferSource>(
MaybeShared<DOMInt8Array>(
DOMInt8Array::Create(buffer, view_offset, view_length)));
break;
case wc_fuzzer::AllowSharedBufferSource_ViewType_UINT32:
// View must be element-aligned and is sized by element count.
view_offset = std::min(view_offset, length / 4) * 4;
view_length = std::min(view_length, length / 4 - view_offset / 4);
result.source = MakeGarbageCollected<AllowSharedBufferSource>(
MaybeShared<DOMUint32Array>(
DOMUint32Array::Create(buffer, view_offset, view_length)));
break;
case wc_fuzzer::AllowSharedBufferSource_ViewType_DATA:
result.source = MakeGarbageCollected<AllowSharedBufferSource>(
MaybeShared<DOMDataView>(
DOMDataView::Create(buffer, view_offset, view_length)));
}
return result;
}
PlaneLayout* MakePlaneLayout(const wc_fuzzer::PlaneLayout& proto) {
PlaneLayout* plane_layout = PlaneLayout::Create();
plane_layout->setOffset(proto.offset());
plane_layout->setStride(proto.stride());
return plane_layout;
}
DOMRectInit* MakeDOMRectInit(const wc_fuzzer::DOMRectInit& proto) {
DOMRectInit* init = DOMRectInit::Create();
init->setX(proto.x());
init->setY(proto.y());
init->setWidth(proto.width());
init->setHeight(proto.height());
return init;
}
VideoColorSpaceInit* MakeVideoColorSpaceInit(
const wc_fuzzer::VideoColorSpaceInit& proto) {
VideoColorSpaceInit* init = VideoColorSpaceInit::Create();
if (proto.has_primaries()) {
switch (proto.primaries()) {
case wc_fuzzer::VideoColorSpaceInit_VideoColorPrimaries_VCP_BT709:
init->setPrimaries("bt709");
break;
case wc_fuzzer::VideoColorSpaceInit_VideoColorPrimaries_VCP_BT470BG:
init->setPrimaries("bt470bg");
break;
case wc_fuzzer::VideoColorSpaceInit_VideoColorPrimaries_VCP_SMPTE170M:
init->setPrimaries("smpte170m");
break;
case wc_fuzzer::VideoColorSpaceInit_VideoColorPrimaries_VCP_BT2020:
init->setPrimaries("bt2020");
break;
case wc_fuzzer::VideoColorSpaceInit_VideoColorPrimaries_VCP_SMPTE432:
init->setPrimaries("smpte432");
break;
}
}
if (proto.has_transfer()) {
switch (proto.transfer()) {
case wc_fuzzer::
VideoColorSpaceInit_VideoTransferCharacteristics_VTC_BT709:
init->setTransfer("bt709");
break;
case wc_fuzzer::
VideoColorSpaceInit_VideoTransferCharacteristics_VTC_SMPTE170M:
init->setTransfer("smpte170m");
break;
case wc_fuzzer::
VideoColorSpaceInit_VideoTransferCharacteristics_VTC_IEC61966_2_1:
init->setTransfer("iec61966-2-1");
break;
case wc_fuzzer::
VideoColorSpaceInit_VideoTransferCharacteristics_VTC_LINEAR:
init->setTransfer("linear");
break;
case wc_fuzzer::VideoColorSpaceInit_VideoTransferCharacteristics_VTC_PQ:
init->setTransfer("pq");
break;
case wc_fuzzer::VideoColorSpaceInit_VideoTransferCharacteristics_VTC_HLG:
init->setTransfer("hlg");
break;
}
}
if (proto.has_matrix()) {
switch (proto.matrix()) {
case wc_fuzzer::VideoColorSpaceInit_VideoMatrixCoefficients_VMC_RGB:
init->setMatrix("rgb");
break;
case wc_fuzzer::VideoColorSpaceInit_VideoMatrixCoefficients_VMC_BT709:
init->setMatrix("bt709");
break;
case wc_fuzzer::VideoColorSpaceInit_VideoMatrixCoefficients_VMC_BT470BG:
init->setMatrix("bt470bg");
break;
case wc_fuzzer::VideoColorSpaceInit_VideoMatrixCoefficients_VMC_SMPTE170M:
init->setMatrix("smpte170m");
break;
case wc_fuzzer::
VideoColorSpaceInit_VideoMatrixCoefficients_VMC_BT2020_NCL:
init->setMatrix("bt2020-ncl");
break;
}
}
if (proto.has_full_range()) {
init->setFullRange(proto.full_range());
}
return init;
}
VideoFrame* MakeVideoFrame(
ScriptState* script_state,
const wc_fuzzer::VideoFrameBufferInitInvocation& proto) {
BufferAndSource data = MakeAllowSharedBufferSource(proto.data());
VideoFrameBufferInit* init = VideoFrameBufferInit::Create();
switch (proto.init().format()) {
case wc_fuzzer::VideoFrameBufferInit_VideoPixelFormat_I420:
init->setFormat("I420");
break;
case wc_fuzzer::VideoFrameBufferInit_VideoPixelFormat_I420A:
init->setFormat("I420A");
break;
case wc_fuzzer::VideoFrameBufferInit_VideoPixelFormat_I444:
init->setFormat("I444");
break;
case wc_fuzzer::VideoFrameBufferInit_VideoPixelFormat_NV12:
init->setFormat("NV12");
break;
case wc_fuzzer::VideoFrameBufferInit_VideoPixelFormat_RGBA:
init->setFormat("RGBA");
break;
case wc_fuzzer::VideoFrameBufferInit_VideoPixelFormat_RGBX:
init->setFormat("RGBX");
break;
case wc_fuzzer::VideoFrameBufferInit_VideoPixelFormat_BGRA:
init->setFormat("BGRA");
break;
case wc_fuzzer::VideoFrameBufferInit_VideoPixelFormat_BGRX:
init->setFormat("BGRX");
break;
}
if (proto.init().layout_size()) {
HeapVector<Member<PlaneLayout>> layout{};
for (const auto& plane_proto : proto.init().layout())
layout.push_back(MakePlaneLayout(plane_proto));
init->setLayout(layout);
}
init->setTimestamp(proto.init().timestamp());
if (proto.init().has_duration())
init->setDuration(proto.init().duration());
init->setCodedWidth(
std::min(proto.init().coded_width(), kMaxVideoFrameDimension));
init->setCodedHeight(
std::min(proto.init().coded_height(), kMaxVideoFrameDimension));
if (proto.init().has_visible_rect())
init->setVisibleRect(MakeDOMRectInit(proto.init().visible_rect()));
if (proto.init().has_display_width())
init->setDisplayWidth(proto.init().display_width());
if (proto.init().has_display_height())
init->setDisplayHeight(proto.init().display_height());
if (proto.init().has_color_space()) {
init->setColorSpace(MakeVideoColorSpaceInit(proto.init().color_space()));
}
if (data.buffer) {
HeapVector<Member<DOMArrayBuffer>> transfer;
transfer.push_back(data.buffer);
init->setTransfer(std::move(transfer));
}
return VideoFrame::Create(script_state, data.source, init,
IGNORE_EXCEPTION_FOR_TESTING);
}
VideoFrame* MakeVideoFrame(ScriptState* script_state,
const wc_fuzzer::VideoFrameBitmapInit& proto) {
constexpr size_t kBytesPerPixel = 4;
auto bitmap_size = proto.rgb_bitmap().size();
// ImageData::Create() rejects inputs if data size is not a multiple of
// width * 4.
// Round down bitmap size to width * 4, it makes more fuzzer inputs
// acceptable and incresease fuzzing penetration.
if (proto.bitmap_width() > 0 && proto.bitmap_width() < bitmap_size)
bitmap_size -= bitmap_size % (proto.bitmap_width() * kBytesPerPixel);
NotShared<DOMUint8ClampedArray> data_u8(DOMUint8ClampedArray::Create(
base::as_byte_span(proto.rgb_bitmap()).first(bitmap_size)));
ImageData* image_data = ImageData::Create(data_u8, proto.bitmap_width(),
IGNORE_EXCEPTION_FOR_TESTING);
if (!image_data)
return nullptr;
ImageBitmap* image_bitmap = MakeGarbageCollected<ImageBitmap>(
image_data, std::nullopt, ImageBitmapOptions::Create());
VideoFrameInit* video_frame_init = VideoFrameInit::Create();
video_frame_init->setTimestamp(proto.timestamp());
video_frame_init->setDuration(proto.duration());
auto* source = MakeGarbageCollected<V8CanvasImageSource>(image_bitmap);
return VideoFrame::Create(script_state, source, video_frame_init,
IGNORE_EXCEPTION_FOR_TESTING);
}
AudioData* MakeAudioData(ScriptState* script_state,
const wc_fuzzer::AudioDataInit& proto) {
if (!proto.channels().size() ||
proto.channels().size() > media::limits::kMaxChannels)
return nullptr;
if (!proto.length() ||
proto.length() > media::limits::kMaxSamplesPerPacket / 4) {
return nullptr;
}
V8AudioSampleFormat format =
V8AudioSampleFormat::Create(ToAudioSampleFormat(proto.format())).value();
int size_per_sample = SampleFormatToSampleSize(format);
int number_of_samples = proto.channels().size() * proto.length();
auto* buffer = DOMArrayBuffer::Create(number_of_samples, size_per_sample);
memset(buffer->Data(), 0, number_of_samples * size_per_sample);
for (int i = 0; i < proto.channels().size(); i++) {
size_t max_plane_size = proto.length() * size_per_sample;
auto* data = proto.channels().Get(i).data();
auto size = std::min(proto.channels().Get(i).size(), max_plane_size);
void* plane_start =
reinterpret_cast<uint8_t*>(buffer->Data()) + i * max_plane_size;
memcpy(plane_start, data, size);
}
auto* init = AudioDataInit::Create();
init->setTimestamp(proto.timestamp());
init->setNumberOfFrames(proto.length());
init->setNumberOfChannels(proto.channels().size());
init->setSampleRate(proto.sample_rate());
init->setFormat(format);
init->setData(MakeGarbageCollected<AllowSharedBufferSource>(buffer));
if (proto.transfer()) {
HeapVector<Member<DOMArrayBuffer>> transfer;
transfer.push_back(buffer);
init->setTransfer(std::move(transfer));
}
return AudioData::Create(script_state, init, IGNORE_EXCEPTION_FOR_TESTING);
}
AudioDataCopyToOptions* MakeAudioDataCopyToOptions(
const wc_fuzzer::AudioDataCopyToOptions& options_proto) {
AudioDataCopyToOptions* options = AudioDataCopyToOptions::Create();
options->setPlaneIndex(options_proto.plane_index());
if (options_proto.has_frame_offset())
options->setFrameOffset(options_proto.frame_offset());
if (options_proto.has_frame_count())
options->setFrameCount(options_proto.frame_count());
if (options_proto.has_format())
options->setFormat(ToAudioSampleFormat(options_proto.format()));
return options;
}
} // namespace blink