#include "media/parsers/webp_parser.h"
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include "base/bits.h"
#include "base/check_op.h"
#include "base/containers/span_reader.h"
#include "base/numerics/safe_conversions.h"
#include "build/build_config.h"
#include "media/parsers/vp8_parser.h"
namespace media {
namespace {
constexpr size_t kFileSizeBytePosition = 4u;
constexpr size_t kWebPFileBeginBytePosition = 8u;
constexpr size_t kVp8FrameSizePosition = 16u;
constexpr size_t kWebPFileHeaderByteSize = 12u;
constexpr size_t kWebPFileAndVp8ChunkHeaderSizeInBytes = 20u;
constexpr uint32_t kMaxWebPFileSize = (1ull << 32) - 10u;
constexpr std::string_view kRiffHeader = "RIFF";
constexpr std::string_view kLossyHeader = "WEBPVP8 ";
}
bool IsLossyWebPImage(base::span<const uint8_t> encoded_data) {
if (encoded_data.size() < kWebPFileAndVp8ChunkHeaderSizeInBytes) {
return false;
}
auto riff_header = base::as_bytes(base::span(kRiffHeader));
auto lossy_header = base::as_bytes(base::span(kLossyHeader));
return encoded_data.first(4u) == riff_header &&
encoded_data.subspan(kWebPFileBeginBytePosition, 8u) == lossy_header;
}
std::unique_ptr<Vp8FrameHeader> ParseWebPImage(
base::span<const uint8_t> encoded_data) {
if (!IsLossyWebPImage(encoded_data))
return nullptr;
static_assert(CHAR_BIT == 8, "Size of a char is not 8 bits.");
uint32_t file_size = 0;
if (!base::SpanReader(encoded_data.subspan(kFileSizeBytePosition))
.ReadU32LittleEndian(file_size)) {
return nullptr;
}
if (file_size % 2 != 0)
return nullptr;
if (file_size > kMaxWebPFileSize)
return nullptr;
if (base::strict_cast<size_t>(file_size) !=
encoded_data.size() - kWebPFileBeginBytePosition) {
return nullptr;
}
uint32_t vp8_frame_size = 0;
if (!base::SpanReader(encoded_data.subspan(kVp8FrameSizePosition))
.ReadU32LittleEndian(vp8_frame_size)) {
return nullptr;
}
if (base::strict_cast<size_t>(file_size) - kWebPFileHeaderByteSize <
base::strict_cast<size_t>(vp8_frame_size)) {
return nullptr;
}
const size_t vp8_padded_frame_size =
base::bits::AlignUp(size_t{vp8_frame_size}, size_t{2});
if (encoded_data.size() - kWebPFileAndVp8ChunkHeaderSizeInBytes !=
vp8_padded_frame_size) {
return nullptr;
}
if (vp8_frame_size % 2 && encoded_data[encoded_data.size() - 1] != 0u) {
return nullptr;
}
Vp8Parser vp8_parser;
auto result = std::make_unique<Vp8FrameHeader>();
if (vp8_parser.ParseFrame(
encoded_data.subspan(kWebPFileAndVp8ChunkHeaderSizeInBytes).data(),
base::strict_cast<size_t>(vp8_frame_size), result.get())) {
return result;
}
return nullptr;
}
}