#include "media/parsers/webp_parser.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include "base/base_paths.h"
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
#include "base/path_service.h"
#include "media/parsers/vp8_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
constexpr size_t kWebPFileAndVp8ChunkHeaderSizeInBytes = 20u;
constexpr uint8_t kLossyWebPFileHeader[] = {
'R', 'I', 'F', 'F',
0x0c, 0x00, 0x00, 0x00,
'W', 'E', 'B', 'P',
'V', 'P', '8', ' ',
0x00, 0x00, 0x00, 0x00
};
constexpr base::span<const uint8_t> kLossyWebPEncodedData(
kLossyWebPFileHeader,
kWebPFileAndVp8ChunkHeaderSizeInBytes);
constexpr base::span<const uint8_t> kInvalidWebPEncodedDataSize(
kLossyWebPFileHeader,
kWebPFileAndVp8ChunkHeaderSizeInBytes - 5u);
constexpr uint8_t kLosslessWebPFileHeader[] = {
'R', 'I', 'F', 'F',
0x0c, 0x00, 0x00, 0x00,
'W', 'E', 'B', 'P',
'V', 'P', '8', 'L',
0x00, 0x00, 0x00, 0x00
};
constexpr base::span<const uint8_t> kLosslessWebPEncodedData(
kLosslessWebPFileHeader,
kWebPFileAndVp8ChunkHeaderSizeInBytes);
constexpr uint8_t kExtendedWebPFileHeader[] = {
'R', 'I', 'F', 'F',
0x0c, 0x00, 0x00, 0x00,
'W', 'E', 'B', 'P',
'V', 'P', '8', 'X',
0x00, 0x00, 0x00, 0x00
};
constexpr base::span<const uint8_t> kExtendedWebPEncodedData(
kExtendedWebPFileHeader,
kWebPFileAndVp8ChunkHeaderSizeInBytes);
constexpr uint8_t kUnknownWebPFileHeader[] = {
'R', 'I', 'F', 'F',
0x0c, 0x00, 0x00, 0x00,
'W', 'E', 'B', 'P',
'V', 'P', '8', '~',
0x00, 0x00, 0x00, 0x00
};
constexpr base::span<const uint8_t> kUnknownWebPEncodedData(
kUnknownWebPFileHeader,
kWebPFileAndVp8ChunkHeaderSizeInBytes);
constexpr uint8_t kInvalidRiffWebPFileHeader[] = {
'X', 'I', 'F', 'F',
0x0c, 0x00, 0x00, 0x00,
'W', 'E', 'B', 'P',
'V', 'P', '8', ' ',
0x00, 0x00, 0x00, 0x00
};
constexpr base::span<const uint8_t> kInvalidRiffWebPEncodedData(
kInvalidRiffWebPFileHeader,
kWebPFileAndVp8ChunkHeaderSizeInBytes);
constexpr uint8_t kInvalidOddFileSizeInWebPFileHeader[] = {
'R', 'I', 'F', 'F',
0x0d, 0x00, 0x00, 0x00,
'W', 'E', 'B', 'P',
'V', 'P', '8', ' ',
0x00, 0x00, 0x00, 0x00,
0x00
};
constexpr base::span<const uint8_t> kInvalidOddFileSizeInHeaderWebPEncodedData(
kInvalidOddFileSizeInWebPFileHeader,
kWebPFileAndVp8ChunkHeaderSizeInBytes + 1u);
constexpr uint8_t kInvalidLargerThanLimitFileSizeInWebPFileHeader[] = {
'R', 'I', 'F', 'F',
0xfe, 0xff, 0xff, 0xff,
'W', 'E', 'B', 'P',
'V', 'P', '8', ' ',
0x00, 0x00, 0x00, 0x00
};
constexpr base::span<const uint8_t>
kInvalidLargerThanLimitFileSizeInHeaderWebPEncodedData(
kInvalidLargerThanLimitFileSizeInWebPFileHeader,
kWebPFileAndVp8ChunkHeaderSizeInBytes);
constexpr uint8_t kInvalidLargerFileSizeInWebPFileHeader[] = {
'R', 'I', 'F', 'F',
0x10, 0x00, 0x00, 0x00,
'W', 'E', 'B', 'P',
'V', 'P', '8', ' ',
0x00, 0x00, 0x00, 0x00
};
constexpr base::span<const uint8_t>
kInvalidLargerFileSizeInHeaderWebPEncodedData(
kInvalidLargerFileSizeInWebPFileHeader,
kWebPFileAndVp8ChunkHeaderSizeInBytes);
constexpr uint8_t kInvalidKeyFrameSizeInWebPFileHeader[] = {
'R', 'I', 'F', 'F',
0x0c, 0x00, 0x00, 0x00,
'W', 'E', 'B', 'P',
'V', 'P', '8', ' ',
0xc8, 0x00, 0x00, 0x00
};
constexpr base::span<const uint8_t> kInvalidKeyFrameSizeInWebPEncodedData(
kInvalidKeyFrameSizeInWebPFileHeader,
kWebPFileAndVp8ChunkHeaderSizeInBytes);
constexpr uint8_t kMismatchingOddVp8FrameSizeAndDataSize[] = {
'R', 'I', 'F', 'F',
0x12, 0x00, 0x00, 0x00,
'W', 'E', 'B', 'P',
'V', 'P', '8', ' ',
0x03, 0x00, 0x00, 0x00,
0x11, 0xa0, 0x23, 0x00,
0xfa, 0xcc
};
constexpr base::span<const uint8_t>
kMismatchingOddVp8FrameSizeAndDataSizeEncodedData(
kMismatchingOddVp8FrameSizeAndDataSize,
kWebPFileAndVp8ChunkHeaderSizeInBytes + 6u);
constexpr uint8_t kMismatchingEvenVp8FrameSizeAndDataSize[] = {
'R', 'I', 'F', 'F',
0x12, 0x00, 0x00, 0x00,
'W', 'E', 'B', 'P',
'V', 'P', '8', ' ',
0x04, 0x00, 0x00, 0x00,
0x11, 0xa0, 0x23, 0x12,
0xfc, 0xcd
};
constexpr base::span<const uint8_t>
kMismatchingEvenVp8FrameSizeAndDataSizeEncodedData(
kMismatchingEvenVp8FrameSizeAndDataSize,
kWebPFileAndVp8ChunkHeaderSizeInBytes + 6u);
constexpr uint8_t kInvalidPaddingByteInVp8DataChunk[] = {
'R', 'I', 'F', 'F',
0x10, 0x00, 0x00, 0x00,
'W', 'E', 'B', 'P',
'V', 'P', '8', ' ',
0x03, 0x00, 0x00, 0x00,
0x11, 0xa0, 0x23, 0xff
};
constexpr base::span<const uint8_t>
kInvalidPaddingByteInVp8DataChunkEncodedData(
kInvalidPaddingByteInVp8DataChunk,
kWebPFileAndVp8ChunkHeaderSizeInBytes + 4u);
}
TEST(WebPParserTest, WebPImageFileValidator) {
ASSERT_TRUE(IsLossyWebPImage(kLossyWebPEncodedData));
ASSERT_FALSE(IsLossyWebPImage(kLosslessWebPEncodedData));
ASSERT_FALSE(IsLossyWebPImage(kExtendedWebPEncodedData));
ASSERT_FALSE(IsLossyWebPImage(kUnknownWebPEncodedData));
ASSERT_FALSE(IsLossyWebPImage(kInvalidRiffWebPEncodedData));
ASSERT_FALSE(IsLossyWebPImage(kInvalidWebPEncodedDataSize));
}
TEST(WebPParserTest, ParseLossyWebP) {
base::FilePath data_dir;
ASSERT_TRUE(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &data_dir));
base::FilePath file_path = data_dir.AppendASCII("media")
.AppendASCII("test")
.AppendASCII("data")
.AppendASCII("red_green_gradient_lossy.webp");
base::MemoryMappedFile stream;
ASSERT_TRUE(stream.Initialize(file_path))
<< "Couldn't open stream file: " << file_path.MaybeAsASCII();
std::unique_ptr<Vp8FrameHeader> result = ParseWebPImage(
UNSAFE_TODO(base::span<const uint8_t>(stream.data(), stream.length())));
ASSERT_TRUE(result);
ASSERT_TRUE(result->IsKeyframe());
ASSERT_TRUE(result->data);
ASSERT_EQ(3000u, result->width);
ASSERT_EQ(3000u, result->height);
}
TEST(WebPParserTest, ParseLosslessWebP) {
base::FilePath data_dir;
ASSERT_TRUE(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &data_dir));
base::FilePath file_path =
data_dir.AppendASCII("media")
.AppendASCII("test")
.AppendASCII("data")
.AppendASCII("yellow_pink_gradient_lossless.webp");
base::MemoryMappedFile stream;
ASSERT_TRUE(stream.Initialize(file_path))
<< "Couldn't open stream file: " << file_path.MaybeAsASCII();
std::unique_ptr<Vp8FrameHeader> result = ParseWebPImage(
UNSAFE_TODO(base::span<const uint8_t>(stream.data(), stream.length())));
ASSERT_FALSE(result);
}
TEST(WebPParserTest, ParseExtendedWebP) {
base::FilePath data_dir;
ASSERT_TRUE(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &data_dir));
base::FilePath file_path = data_dir.AppendASCII("media")
.AppendASCII("test")
.AppendASCII("data")
.AppendASCII("bouncy_ball.webp");
base::MemoryMappedFile stream;
ASSERT_TRUE(stream.Initialize(file_path))
<< "Couldn't open stream file: " << file_path.MaybeAsASCII();
std::unique_ptr<Vp8FrameHeader> result = ParseWebPImage(
UNSAFE_TODO(base::span<const uint8_t>(stream.data(), stream.length())));
ASSERT_FALSE(result);
}
TEST(WebPParserTest, ParseWebPWithUnknownFormat) {
std::unique_ptr<Vp8FrameHeader> result =
ParseWebPImage(kUnknownWebPEncodedData);
ASSERT_FALSE(result);
}
TEST(WebPParserTest, ParseWebPWithInvalidHeaders) {
std::unique_ptr<Vp8FrameHeader> result =
ParseWebPImage(kInvalidRiffWebPEncodedData);
ASSERT_FALSE(result);
result = ParseWebPImage(kInvalidWebPEncodedDataSize);
ASSERT_FALSE(result);
}
TEST(WebPParserTest, ParseWebPWithInvalidOddSizeInHeader) {
std::unique_ptr<Vp8FrameHeader> result =
ParseWebPImage(kInvalidOddFileSizeInHeaderWebPEncodedData);
ASSERT_FALSE(result);
}
TEST(WebPParserTest, ParseWebPWithInvalidLargerThanLimitSizeInHeader) {
std::unique_ptr<Vp8FrameHeader> result =
ParseWebPImage(kInvalidLargerThanLimitFileSizeInHeaderWebPEncodedData);
ASSERT_FALSE(result);
}
TEST(WebPParserTest, ParseWebPWithInvalidFileSizeInHeader) {
std::unique_ptr<Vp8FrameHeader> result =
ParseWebPImage(kInvalidLargerFileSizeInHeaderWebPEncodedData);
ASSERT_FALSE(result);
}
TEST(WebPParserTest, ParseWebPWithEmptyVp8KeyFrameAndIncorrectKeyFrameSize) {
std::unique_ptr<Vp8FrameHeader> result =
ParseWebPImage(kInvalidKeyFrameSizeInWebPEncodedData);
ASSERT_FALSE(result);
}
TEST(WebPParserTest, ParseWebPWithMismatchingVp8FrameAndDataSize) {
std::unique_ptr<Vp8FrameHeader> result =
ParseWebPImage(kMismatchingOddVp8FrameSizeAndDataSizeEncodedData);
ASSERT_FALSE(result);
result = ParseWebPImage(kMismatchingEvenVp8FrameSizeAndDataSizeEncodedData);
ASSERT_FALSE(result);
}
TEST(WebPParserTest, ParseWebPWithInvalidPaddingByteInVp8DataChunk) {
std::unique_ptr<Vp8FrameHeader> result =
ParseWebPImage(kInvalidPaddingByteInVp8DataChunkEncodedData);
ASSERT_FALSE(result);
}
TEST(WebPParserTest, ParseWebPWithEmptyVp8KeyFrame) {
std::unique_ptr<Vp8FrameHeader> result =
ParseWebPImage(kLossyWebPEncodedData);
ASSERT_FALSE(result);
}
}