blob: 4c9d99765b1f342a0c9959001bb1e168d7fd5775 [file] [log] [blame]
Gil Dekelbf308632019-05-28 22:24:171// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <stddef.h>
6#include <stdint.h>
7
8#include <memory>
9
10#include "base/base_paths.h"
11#include "base/containers/span.h"
12#include "base/files/file_path.h"
13#include "base/files/memory_mapped_file.h"
14#include "base/path_service.h"
15#include "media/parsers/vp8_parser.h"
16#include "media/parsers/webp_parser.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19namespace media {
20
21namespace {
22
23constexpr size_t kWebPFileAndVp8ChunkHeaderSizeInBytes = 20u;
24// clang-format off
25constexpr uint8_t kLossyWebPFileHeader[] = {
26 'R', 'I', 'F', 'F',
27 0x0c, 0x00, 0x00, 0x00, // == 12 (little endian)
28 'W', 'E', 'B', 'P',
29 'V', 'P', '8', ' ',
30 0x00, 0x00, 0x00, 0x00 // == 0
31};
32constexpr base::span<const uint8_t> kLossyWebPEncodedData(
33 kLossyWebPFileHeader,
34 kWebPFileAndVp8ChunkHeaderSizeInBytes);
35constexpr base::span<const uint8_t> kInvalidWebPEncodedDataSize(
36 kLossyWebPFileHeader,
37 kWebPFileAndVp8ChunkHeaderSizeInBytes - 5u);
38
39constexpr uint8_t kLosslessWebPFileHeader[] = {
40 'R', 'I', 'F', 'F',
41 0x0c, 0x00, 0x00, 0x00,
42 'W', 'E', 'B', 'P',
43 'V', 'P', '8', 'L',
44 0x00, 0x00, 0x00, 0x00
45};
46constexpr base::span<const uint8_t> kLosslessWebPEncodedData(
47 kLosslessWebPFileHeader,
48 kWebPFileAndVp8ChunkHeaderSizeInBytes);
49
50constexpr uint8_t kExtendedWebPFileHeader[] = {
51 'R', 'I', 'F', 'F',
52 0x0c, 0x00, 0x00, 0x00,
53 'W', 'E', 'B', 'P',
54 'V', 'P', '8', 'X',
55 0x00, 0x00, 0x00, 0x00
56};
57constexpr base::span<const uint8_t> kExtendedWebPEncodedData(
58 kExtendedWebPFileHeader,
59 kWebPFileAndVp8ChunkHeaderSizeInBytes);
60
61constexpr uint8_t kUnknownWebPFileHeader[] = {
62 'R', 'I', 'F', 'F',
63 0x0c, 0x00, 0x00, 0x00,
64 'W', 'E', 'B', 'P',
65 'V', 'P', '8', '~',
66 0x00, 0x00, 0x00, 0x00
67};
68constexpr base::span<const uint8_t> kUnknownWebPEncodedData(
69 kUnknownWebPFileHeader,
70 kWebPFileAndVp8ChunkHeaderSizeInBytes);
71
72constexpr uint8_t kInvalidRiffWebPFileHeader[] = {
73 'X', 'I', 'F', 'F',
74 0x0c, 0x00, 0x00, 0x00,
75 'W', 'E', 'B', 'P',
76 'V', 'P', '8', ' ',
77 0x00, 0x00, 0x00, 0x00
78};
79constexpr base::span<const uint8_t> kInvalidRiffWebPEncodedData(
80 kInvalidRiffWebPFileHeader,
81 kWebPFileAndVp8ChunkHeaderSizeInBytes);
82
83constexpr uint8_t kInvalidOddFileSizeInWebPFileHeader[] = {
84 'R', 'I', 'F', 'F',
85 0x0d, 0x00, 0x00, 0x00, // == 13 (Invalid: should be even)
86 'W', 'E', 'B', 'P',
87 'V', 'P', '8', ' ',
88 0x00, 0x00, 0x00, 0x00,
89 0x00
90};
91constexpr base::span<const uint8_t> kInvalidOddFileSizeInHeaderWebPEncodedData(
92 kInvalidOddFileSizeInWebPFileHeader,
93 kWebPFileAndVp8ChunkHeaderSizeInBytes + 1u); // Match the reported size
94
95constexpr uint8_t kInvalidLargerThanLimitFileSizeInWebPFileHeader[] = {
96 'R', 'I', 'F', 'F',
97 0xfe, 0xff, 0xff, 0xff, // == 2^32 - 2 (Invalid: should be <= 2^32 - 10)
98 'W', 'E', 'B', 'P',
99 'V', 'P', '8', ' ',
100 0x00, 0x00, 0x00, 0x00
101};
102constexpr base::span<const uint8_t>
103kInvalidLargerThanLimitFileSizeInHeaderWebPEncodedData(
104 kInvalidLargerThanLimitFileSizeInWebPFileHeader,
105 kWebPFileAndVp8ChunkHeaderSizeInBytes);
106
107constexpr uint8_t kInvalidLargerFileSizeInWebPFileHeader[] = {
108 'R', 'I', 'F', 'F',
109 0x10, 0x00, 0x00, 0x00, // == 16 (Invalid: should be 12)
110 'W', 'E', 'B', 'P',
111 'V', 'P', '8', ' ',
112 0x00, 0x00, 0x00, 0x00
113};
114constexpr base::span<const uint8_t>
115kInvalidLargerFileSizeInHeaderWebPEncodedData(
116 kInvalidLargerFileSizeInWebPFileHeader,
117 kWebPFileAndVp8ChunkHeaderSizeInBytes);
118
119constexpr uint8_t kInvalidKeyFrameSizeInWebPFileHeader[] = {
120 'R', 'I', 'F', 'F',
121 0x0c, 0x00, 0x00, 0x00, // == 12
122 'W', 'E', 'B', 'P',
123 'V', 'P', '8', ' ',
124 0xc8, 0x00, 0x00, 0x00 // == 200 (Invalid: should be 0)
125};
126constexpr base::span<const uint8_t> kInvalidKeyFrameSizeInWebPEncodedData(
127 kInvalidKeyFrameSizeInWebPFileHeader,
128 kWebPFileAndVp8ChunkHeaderSizeInBytes);
129
130constexpr uint8_t kMismatchingOddVp8FrameSizeAndDataSize[] = {
131 'R', 'I', 'F', 'F',
132 0x12, 0x00, 0x00, 0x00, // == 18
133 'W', 'E', 'B', 'P',
134 'V', 'P', '8', ' ',
135 0x03, 0x00, 0x00, 0x00, // == 3
136 0x11, 0xa0, 0x23, 0x00, // Valid padding byte
137 0xfa, 0xcc // Should not exist.
138};
139constexpr base::span<const uint8_t>
140kMismatchingOddVp8FrameSizeAndDataSizeEncodedData(
141 kMismatchingOddVp8FrameSizeAndDataSize,
142 kWebPFileAndVp8ChunkHeaderSizeInBytes + 6u);
143
144constexpr uint8_t kMismatchingEvenVp8FrameSizeAndDataSize[] = {
145 'R', 'I', 'F', 'F',
146 0x12, 0x00, 0x00, 0x00, // == 18
147 'W', 'E', 'B', 'P',
148 'V', 'P', '8', ' ',
149 0x04, 0x00, 0x00, 0x00, // == 4
150 0x11, 0xa0, 0x23, 0x12,
151 0xfc, 0xcd // Should not exist.
152};
153constexpr base::span<const uint8_t>
154kMismatchingEvenVp8FrameSizeAndDataSizeEncodedData(
155 kMismatchingEvenVp8FrameSizeAndDataSize,
156 kWebPFileAndVp8ChunkHeaderSizeInBytes + 6u);
157
158constexpr uint8_t kInvalidPaddingByteInVp8DataChunk[] = {
159 'R', 'I', 'F', 'F',
160 0x10, 0x00, 0x00, 0x00, // == 16
161 'W', 'E', 'B', 'P',
162 'V', 'P', '8', ' ',
163 0x03, 0x00, 0x00, 0x00, // == 3
164 0x11, 0xa0, 0x23, 0xff // Invalid: last byte should be 0
165};
166constexpr base::span<const uint8_t>
167kInvalidPaddingByteInVp8DataChunkEncodedData(
168 kInvalidPaddingByteInVp8DataChunk,
169 kWebPFileAndVp8ChunkHeaderSizeInBytes + 4u);
170// clang-format on
171
172} // namespace
173
174TEST(WebPParserTest, WebPImageFileValidator) {
175 // Verify that only lossy WebP formats pass.
176 ASSERT_TRUE(IsLossyWebPImage(kLossyWebPEncodedData));
177
178 // Verify that lossless, extended, and unknown WebP formats fail.
179 ASSERT_FALSE(IsLossyWebPImage(kLosslessWebPEncodedData));
180 ASSERT_FALSE(IsLossyWebPImage(kExtendedWebPEncodedData));
181 ASSERT_FALSE(IsLossyWebPImage(kUnknownWebPEncodedData));
182
183 // Verify that invalid WebP file headers and sizes fail.
184 ASSERT_FALSE(IsLossyWebPImage(kInvalidRiffWebPEncodedData));
185 ASSERT_FALSE(IsLossyWebPImage(kInvalidWebPEncodedDataSize));
186}
187
188TEST(WebPParserTest, ParseLossyWebP) {
189 base::FilePath data_dir;
190 ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
191
192 base::FilePath file_path = data_dir.AppendASCII("media")
193 .AppendASCII("test")
194 .AppendASCII("data")
195 .AppendASCII("red_green_gradient_lossy.webp");
196
197 base::MemoryMappedFile stream;
198 ASSERT_TRUE(stream.Initialize(file_path))
199 << "Couldn't open stream file: " << file_path.MaybeAsASCII();
200
201 std::unique_ptr<Vp8FrameHeader> result =
202 ParseWebPImage(base::span<const uint8_t>(stream.data(), stream.length()));
203 ASSERT_TRUE(result);
204
205 ASSERT_TRUE(result->IsKeyframe());
206 ASSERT_TRUE(result->data);
207
208 // Original image is 3000x3000.
209 ASSERT_EQ(3000u, result->width);
210 ASSERT_EQ(3000u, result->height);
211}
212
213TEST(WebPParserTest, ParseLosslessWebP) {
214 base::FilePath data_dir;
215 ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
216
217 base::FilePath file_path =
218 data_dir.AppendASCII("media")
219 .AppendASCII("test")
220 .AppendASCII("data")
221 .AppendASCII("yellow_pink_gradient_lossless.webp");
222
223 base::MemoryMappedFile stream;
224 ASSERT_TRUE(stream.Initialize(file_path))
225 << "Couldn't open stream file: " << file_path.MaybeAsASCII();
226
227 // Should fail because WebP parser does not parse lossless webp images.
228 std::unique_ptr<Vp8FrameHeader> result =
229 ParseWebPImage(base::span<const uint8_t>(stream.data(), stream.length()));
230 ASSERT_FALSE(result);
231}
232
233TEST(WebPParserTest, ParseExtendedWebP) {
234 base::FilePath data_dir;
235 ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
236
237 base::FilePath file_path = data_dir.AppendASCII("media")
238 .AppendASCII("test")
239 .AppendASCII("data")
240 .AppendASCII("bouncy_ball.webp");
241
242 base::MemoryMappedFile stream;
243 ASSERT_TRUE(stream.Initialize(file_path))
244 << "Couldn't open stream file: " << file_path.MaybeAsASCII();
245
246 // Should fail because WebP parser does not parse extended webp images.
247 std::unique_ptr<Vp8FrameHeader> result =
248 ParseWebPImage(base::span<const uint8_t>(stream.data(), stream.length()));
249 ASSERT_FALSE(result);
250}
251
252TEST(WebPParserTest, ParseWebPWithUnknownFormat) {
253 // Should fail when the specifier byte at position 16 holds anything but ' '.
254 std::unique_ptr<Vp8FrameHeader> result =
255 ParseWebPImage(kUnknownWebPEncodedData);
256 ASSERT_FALSE(result);
257}
258
259TEST(WebPParserTest, ParseWebPWithInvalidHeaders) {
260 // Should fail because the header is an invalid WebP container.
261 std::unique_ptr<Vp8FrameHeader> result =
262 ParseWebPImage(kInvalidRiffWebPEncodedData);
263 ASSERT_FALSE(result);
264
265 // Should fail because the header has an invalid size.
266 result = ParseWebPImage(kInvalidWebPEncodedDataSize);
267 ASSERT_FALSE(result);
268}
269
270TEST(WebPParserTest, ParseWebPWithInvalidOddSizeInHeader) {
271 // Should fail because the size reported in the header is odd.
272 std::unique_ptr<Vp8FrameHeader> result =
273 ParseWebPImage(kInvalidOddFileSizeInHeaderWebPEncodedData);
274 ASSERT_FALSE(result);
275}
276
277TEST(WebPParserTest, ParseWebPWithInvalidLargerThanLimitSizeInHeader) {
278 // Should fail because the size reported in the header is larger than
279 // 2^32 - 10 per the WebP spec.
280 std::unique_ptr<Vp8FrameHeader> result =
281 ParseWebPImage(kInvalidLargerThanLimitFileSizeInHeaderWebPEncodedData);
282 ASSERT_FALSE(result);
283}
284
285TEST(WebPParserTest, ParseWebPWithInvalidFileSizeInHeader) {
286 // Should fail because the size reported in the header does not match the
287 // actual data size.
288 std::unique_ptr<Vp8FrameHeader> result =
289 ParseWebPImage(kInvalidLargerFileSizeInHeaderWebPEncodedData);
290 ASSERT_FALSE(result);
291}
292
293TEST(WebPParserTest, ParseWebPWithEmptyVp8KeyFrameAndIncorrectKeyFrameSize) {
294 // Should fail because the reported VP8 key frame size is larger than the
295 // the existing data.
296 std::unique_ptr<Vp8FrameHeader> result =
297 ParseWebPImage(kInvalidKeyFrameSizeInWebPEncodedData);
298 ASSERT_FALSE(result);
299}
300
301TEST(WebPParserTest, ParseWebPWithMismatchingVp8FrameAndDataSize) {
302 // Should fail because the reported VP8 key frame size (even or odd) does not
303 // match the encoded data's size.
304 std::unique_ptr<Vp8FrameHeader> result =
305 ParseWebPImage(kMismatchingOddVp8FrameSizeAndDataSizeEncodedData);
306 ASSERT_FALSE(result);
307
308 result = ParseWebPImage(kMismatchingEvenVp8FrameSizeAndDataSizeEncodedData);
309 ASSERT_FALSE(result);
310}
311
312TEST(WebPParserTest, ParseWebPWithInvalidPaddingByteInVp8DataChunk) {
313 // Should fail because the reported VP8 key frame size is odd and the added
314 // padding byte is not 0.
315 std::unique_ptr<Vp8FrameHeader> result =
316 ParseWebPImage(kInvalidPaddingByteInVp8DataChunkEncodedData);
317 ASSERT_FALSE(result);
318}
319
320TEST(WebPParserTest, ParseWebPWithEmptyVp8KeyFrame) {
321 // Should fail because the VP8 parser is passed a data chunk of size 0.
322 std::unique_ptr<Vp8FrameHeader> result =
323 ParseWebPImage(kLossyWebPEncodedData);
324 ASSERT_FALSE(result);
325}
326
327} // namespace media