summaryrefslogtreecommitdiff
path: root/chromium/media/parsers
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2019-07-31 15:50:41 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2019-08-30 12:35:23 +0000
commit7b2ffa587235a47d4094787d72f38102089f402a (patch)
tree30e82af9cbab08a7fa028bb18f4f2987a3f74dfa /chromium/media/parsers
parentd94af01c90575348c4e81a418257f254b6f8d225 (diff)
downloadqtwebengine-chromium-7b2ffa587235a47d4094787d72f38102089f402a.tar.gz
BASELINE: Update Chromium to 76.0.3809.94
Change-Id: I321c3f5f929c105aec0f98c5091ef6108822e647 Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/media/parsers')
-rw-r--r--chromium/media/parsers/BUILD.gn82
-rw-r--r--chromium/media/parsers/jpeg_parser.cc574
-rw-r--r--chromium/media/parsers/jpeg_parser.h164
-rw-r--r--chromium/media/parsers/jpeg_parser_picture_fuzzertest.cc22
-rw-r--r--chromium/media/parsers/jpeg_parser_unittest.cc138
-rw-r--r--chromium/media/parsers/media_parsers_export.h12
-rw-r--r--chromium/media/parsers/vp8_bool_decoder.cc209
-rw-r--r--chromium/media/parsers/vp8_bool_decoder.h135
-rw-r--r--chromium/media/parsers/vp8_bool_decoder_unittest.cc126
-rw-r--r--chromium/media/parsers/vp8_parser.cc876
-rw-r--r--chromium/media/parsers/vp8_parser.h208
-rw-r--r--chromium/media/parsers/vp8_parser_fuzzertest.cc31
-rw-r--r--chromium/media/parsers/vp8_parser_unittest.cc50
-rw-r--r--chromium/media/parsers/webp_parser.cc131
-rw-r--r--chromium/media/parsers/webp_parser.h38
-rw-r--r--chromium/media/parsers/webp_parser_fuzzertest.cc24
-rw-r--r--chromium/media/parsers/webp_parser_unittest.cc327
17 files changed, 3147 insertions, 0 deletions
diff --git a/chromium/media/parsers/BUILD.gn b/chromium/media/parsers/BUILD.gn
new file mode 100644
index 00000000000..140d1aae981
--- /dev/null
+++ b/chromium/media/parsers/BUILD.gn
@@ -0,0 +1,82 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//media/media_options.gni")
+
+component("parsers") {
+ sources = [
+ "jpeg_parser.cc",
+ "jpeg_parser.h",
+ "media_parsers_export.h",
+ "vp8_bool_decoder.cc",
+ "vp8_bool_decoder.h",
+ "vp8_parser.cc",
+ "vp8_parser.h",
+ "webp_parser.cc",
+ "webp_parser.h",
+ ]
+ defines = [ "IS_MEDIA_PARSER_IMPL" ]
+ deps = [
+ "//base",
+ ]
+
+ # This target is used in GPU IPC code and cannot depend on any //media code.
+ assert_no_deps = [
+ "//media",
+ "//media:shared_memory_support",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "jpeg_parser_unittest.cc",
+ "vp8_bool_decoder_unittest.cc",
+ "vp8_parser_unittest.cc",
+ "webp_parser_unittest.cc",
+ ]
+ deps = [
+ ":parsers",
+ "//base",
+ "//media:test_support",
+ "//testing/gtest",
+ ]
+}
+
+fuzzer_test("media_jpeg_parser_picture_fuzzer") {
+ sources = [
+ "jpeg_parser_picture_fuzzertest.cc",
+ ]
+ deps = [
+ ":parsers",
+ "//base",
+ ]
+ seed_corpus = "//media/test/data"
+ dict = "//media/test/jpeg.dict"
+}
+
+fuzzer_test("media_vp8_parser_fuzzer") {
+ sources = [
+ "vp8_parser_fuzzertest.cc",
+ ]
+ deps = [
+ ":parsers",
+ "//base",
+ "//media:test_support",
+ ]
+ libfuzzer_options = [ "max_len = 400000" ]
+ dict = "//media/test/vp8.dict"
+}
+
+fuzzer_test("media_webp_parser_fuzzer") {
+ sources = [
+ "webp_parser_fuzzertest.cc",
+ ]
+ deps = [
+ ":parsers",
+ "//base",
+ ]
+ seed_corpus = "//media/test/data"
+ dict = "//media/test/webp.dict"
+}
diff --git a/chromium/media/parsers/jpeg_parser.cc b/chromium/media/parsers/jpeg_parser.cc
new file mode 100644
index 00000000000..344f7c36995
--- /dev/null
+++ b/chromium/media/parsers/jpeg_parser.cc
@@ -0,0 +1,574 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/parsers/jpeg_parser.h"
+
+#include "base/big_endian.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+using base::BigEndianReader;
+
+#define READ_U8_OR_RETURN_FALSE(out) \
+ do { \
+ uint8_t _out; \
+ if (!reader.ReadU8(&_out)) { \
+ DVLOG(1) \
+ << "Error in stream: unexpected EOS while trying to read " #out; \
+ return false; \
+ } \
+ *(out) = _out; \
+ } while (0)
+
+#define READ_U16_OR_RETURN_FALSE(out) \
+ do { \
+ uint16_t _out; \
+ if (!reader.ReadU16(&_out)) { \
+ DVLOG(1) \
+ << "Error in stream: unexpected EOS while trying to read " #out; \
+ return false; \
+ } \
+ *(out) = _out; \
+ } while (0)
+
+namespace media {
+
+const JpegHuffmanTable kDefaultDcTable[kJpegMaxHuffmanTableNumBaseline] = {
+ // luminance DC coefficients
+ {
+ true,
+ {0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0},
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+ 0x0b},
+ },
+ // chrominance DC coefficients
+ {
+ true,
+ {0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb},
+ },
+};
+
+const JpegHuffmanTable kDefaultAcTable[kJpegMaxHuffmanTableNumBaseline] = {
+ // luminance AC coefficients
+ {
+ true,
+ {0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d},
+ {0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
+ 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+ 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72,
+ 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
+ 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4,
+ 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa},
+ },
+ // chrominance AC coefficients
+ {
+ true,
+ {0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77},
+ {0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41,
+ 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
+ 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
+ 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74,
+ 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
+ 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
+ 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+ 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
+ 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa},
+ },
+};
+
+constexpr uint8_t kZigZag8x8[64] = {
+ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63};
+
+constexpr JpegQuantizationTable kDefaultQuantTable[2] = {
+ // Table K.1 Luminance quantization table values.
+ {
+ true,
+ {16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55,
+ 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62,
+ 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92,
+ 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99},
+ },
+ // Table K.2 Chrominance quantization table values.
+ {
+ true,
+ {17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99,
+ 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99},
+ },
+};
+
+static bool InRange(int value, int a, int b) {
+ return a <= value && value <= b;
+}
+
+// Round up |value| to multiple of |mul|. |value| must be non-negative.
+// |mul| must be positive.
+static int RoundUp(int value, int mul) {
+ DCHECK_GE(value, 0);
+ DCHECK_GE(mul, 1);
+ return (value + mul - 1) / mul * mul;
+}
+
+// |frame_header| is already initialized to 0 in ParseJpegPicture.
+static bool ParseSOF(const char* buffer,
+ size_t length,
+ JpegFrameHeader* frame_header) {
+ // Spec B.2.2 Frame header syntax
+ DCHECK(buffer);
+ DCHECK(frame_header);
+ BigEndianReader reader(buffer, length);
+
+ uint8_t precision;
+ READ_U8_OR_RETURN_FALSE(&precision);
+ READ_U16_OR_RETURN_FALSE(&frame_header->visible_height);
+ READ_U16_OR_RETURN_FALSE(&frame_header->visible_width);
+ READ_U8_OR_RETURN_FALSE(&frame_header->num_components);
+
+ if (precision != 8) {
+ DLOG(ERROR) << "Only support 8-bit precision, not "
+ << static_cast<int>(precision) << " bit for baseline";
+ return false;
+ }
+ if (!InRange(frame_header->num_components, 1,
+ base::size(frame_header->components))) {
+ DLOG(ERROR) << "num_components="
+ << static_cast<int>(frame_header->num_components)
+ << " is not supported";
+ return false;
+ }
+
+ int max_h_factor = 0;
+ int max_v_factor = 0;
+ for (size_t i = 0; i < frame_header->num_components; i++) {
+ JpegComponent& component = frame_header->components[i];
+ READ_U8_OR_RETURN_FALSE(&component.id);
+ if (component.id > frame_header->num_components) {
+ DLOG(ERROR) << "component id (" << static_cast<int>(component.id)
+ << ") should be <= num_components ("
+ << static_cast<int>(frame_header->num_components) << ")";
+ return false;
+ }
+ uint8_t hv;
+ READ_U8_OR_RETURN_FALSE(&hv);
+ component.horizontal_sampling_factor = hv / 16;
+ component.vertical_sampling_factor = hv % 16;
+ if (component.horizontal_sampling_factor > max_h_factor)
+ max_h_factor = component.horizontal_sampling_factor;
+ if (component.vertical_sampling_factor > max_v_factor)
+ max_v_factor = component.vertical_sampling_factor;
+ if (!InRange(component.horizontal_sampling_factor, 1, 4)) {
+ DVLOG(1) << "Invalid horizontal sampling factor "
+ << static_cast<int>(component.horizontal_sampling_factor);
+ return false;
+ }
+ if (!InRange(component.vertical_sampling_factor, 1, 4)) {
+ DVLOG(1) << "Invalid vertical sampling factor "
+ << static_cast<int>(component.horizontal_sampling_factor);
+ return false;
+ }
+ READ_U8_OR_RETURN_FALSE(&component.quantization_table_selector);
+ }
+
+ // The size of data unit is 8*8 and the coded size should be extended
+ // to complete minimum coded unit, MCU. See Spec A.2.
+ frame_header->coded_width =
+ RoundUp(frame_header->visible_width, max_h_factor * 8);
+ frame_header->coded_height =
+ RoundUp(frame_header->visible_height, max_v_factor * 8);
+
+ return true;
+}
+
+// |q_table| is already initialized to 0 in ParseJpegPicture.
+static bool ParseDQT(const char* buffer,
+ size_t length,
+ JpegQuantizationTable* q_table) {
+ // Spec B.2.4.1 Quantization table-specification syntax
+ DCHECK(buffer);
+ DCHECK(q_table);
+ BigEndianReader reader(buffer, length);
+ while (reader.remaining() > 0) {
+ uint8_t precision_and_table_id;
+ READ_U8_OR_RETURN_FALSE(&precision_and_table_id);
+ uint8_t precision = precision_and_table_id / 16;
+ uint8_t table_id = precision_and_table_id % 16;
+ if (!InRange(precision, 0, 1)) {
+ DVLOG(1) << "Invalid precision " << static_cast<int>(precision);
+ return false;
+ }
+ if (precision == 1) { // 1 means 16-bit precision
+ DLOG(ERROR) << "An 8-bit DCT-based process shall not use a 16-bit "
+ << "precision quantization table";
+ return false;
+ }
+ if (table_id >= kJpegMaxQuantizationTableNum) {
+ DLOG(ERROR) << "Quantization table id (" << static_cast<int>(table_id)
+ << ") exceeded " << kJpegMaxQuantizationTableNum;
+ return false;
+ }
+
+ if (!reader.ReadBytes(&q_table[table_id].value,
+ sizeof(q_table[table_id].value)))
+ return false;
+ q_table[table_id].valid = true;
+ }
+ return true;
+}
+
+// |dc_table| and |ac_table| are already initialized to 0 in ParseJpegPicture.
+static bool ParseDHT(const char* buffer,
+ size_t length,
+ JpegHuffmanTable* dc_table,
+ JpegHuffmanTable* ac_table) {
+ // Spec B.2.4.2 Huffman table-specification syntax
+ DCHECK(buffer);
+ DCHECK(dc_table);
+ DCHECK(ac_table);
+ BigEndianReader reader(buffer, length);
+ while (reader.remaining() > 0) {
+ uint8_t table_class_and_id;
+ READ_U8_OR_RETURN_FALSE(&table_class_and_id);
+ int table_class = table_class_and_id / 16;
+ int table_id = table_class_and_id % 16;
+ if (!InRange(table_class, 0, 1)) {
+ DVLOG(1) << "Invalid table class " << table_class;
+ return false;
+ }
+ if (table_id >= 2) {
+ DLOG(ERROR) << "Table id(" << table_id
+ << ") >= 2 is invalid for baseline profile";
+ return false;
+ }
+
+ JpegHuffmanTable* table;
+ if (table_class == 1)
+ table = &ac_table[table_id];
+ else
+ table = &dc_table[table_id];
+
+ size_t count = 0;
+ if (!reader.ReadBytes(&table->code_length, sizeof(table->code_length)))
+ return false;
+ for (size_t i = 0; i < base::size(table->code_length); i++)
+ count += table->code_length[i];
+
+ if (!InRange(count, 0, sizeof(table->code_value))) {
+ DVLOG(1) << "Invalid code count " << count;
+ return false;
+ }
+ if (!reader.ReadBytes(&table->code_value, count))
+ return false;
+ table->valid = true;
+ }
+ return true;
+}
+
+static bool ParseDRI(const char* buffer,
+ size_t length,
+ uint16_t* restart_interval) {
+ // Spec B.2.4.4 Restart interval definition syntax
+ DCHECK(buffer);
+ DCHECK(restart_interval);
+ BigEndianReader reader(buffer, length);
+ return reader.ReadU16(restart_interval) && reader.remaining() == 0;
+}
+
+// |scan| is already initialized to 0 in ParseJpegPicture.
+static bool ParseSOS(const char* buffer,
+ size_t length,
+ const JpegFrameHeader& frame_header,
+ JpegScanHeader* scan) {
+ // Spec B.2.3 Scan header syntax
+ DCHECK(buffer);
+ DCHECK(scan);
+ BigEndianReader reader(buffer, length);
+ READ_U8_OR_RETURN_FALSE(&scan->num_components);
+ if (scan->num_components != frame_header.num_components) {
+ DLOG(ERROR) << "The number of scan components ("
+ << static_cast<int>(scan->num_components)
+ << ") mismatches the number of image components ("
+ << static_cast<int>(frame_header.num_components) << ")";
+ return false;
+ }
+
+ for (int i = 0; i < scan->num_components; i++) {
+ JpegScanHeader::Component* component = &scan->components[i];
+ READ_U8_OR_RETURN_FALSE(&component->component_selector);
+ uint8_t dc_and_ac_selector;
+ READ_U8_OR_RETURN_FALSE(&dc_and_ac_selector);
+ component->dc_selector = dc_and_ac_selector / 16;
+ component->ac_selector = dc_and_ac_selector % 16;
+ if (component->component_selector != frame_header.components[i].id) {
+ DLOG(ERROR) << "component selector mismatches image component id";
+ return false;
+ }
+ if (component->dc_selector >= kJpegMaxHuffmanTableNumBaseline) {
+ DLOG(ERROR) << "DC selector (" << static_cast<int>(component->dc_selector)
+ << ") should be 0 or 1 for baseline mode";
+ return false;
+ }
+ if (component->ac_selector >= kJpegMaxHuffmanTableNumBaseline) {
+ DLOG(ERROR) << "AC selector (" << static_cast<int>(component->ac_selector)
+ << ") should be 0 or 1 for baseline mode";
+ return false;
+ }
+ }
+
+ // Unused fields, only for value checking.
+ uint8_t spectral_selection_start;
+ uint8_t spectral_selection_end;
+ uint8_t point_transform;
+ READ_U8_OR_RETURN_FALSE(&spectral_selection_start);
+ READ_U8_OR_RETURN_FALSE(&spectral_selection_end);
+ READ_U8_OR_RETURN_FALSE(&point_transform);
+ if (spectral_selection_start != 0 || spectral_selection_end != 63) {
+ DLOG(ERROR) << "Spectral selection should be 0,63 for baseline mode";
+ return false;
+ }
+ if (point_transform != 0) {
+ DLOG(ERROR) << "Point transform should be 0 for baseline mode";
+ return false;
+ }
+
+ return true;
+}
+
+// |eoi_begin_ptr| will point to the beginning of the EOI marker (the FF byte)
+// and |eoi_end_ptr| will point to the end of image (right after the end of the
+// EOI marker) after search succeeds. Returns true on EOI marker found, or false
+// otherwise.
+static bool SearchEOI(const char* buffer,
+ size_t length,
+ const char** eoi_begin_ptr,
+ const char** eoi_end_ptr) {
+ DCHECK(buffer);
+ DCHECK(eoi_begin_ptr);
+ DCHECK(eoi_end_ptr);
+ BigEndianReader reader(buffer, length);
+ uint8_t marker2;
+
+ while (reader.remaining() > 0) {
+ const char* marker1_ptr = static_cast<const char*>(
+ memchr(reader.ptr(), JPEG_MARKER_PREFIX, reader.remaining()));
+ if (!marker1_ptr)
+ return false;
+ reader.Skip(marker1_ptr - reader.ptr() + 1);
+
+ do {
+ READ_U8_OR_RETURN_FALSE(&marker2);
+ } while (marker2 == JPEG_MARKER_PREFIX); // skip fill bytes
+
+ switch (marker2) {
+ // Compressed data escape.
+ case 0x00:
+ break;
+ // Restart
+ case JPEG_RST0:
+ case JPEG_RST1:
+ case JPEG_RST2:
+ case JPEG_RST3:
+ case JPEG_RST4:
+ case JPEG_RST5:
+ case JPEG_RST6:
+ case JPEG_RST7:
+ break;
+ case JPEG_EOI:
+ *eoi_begin_ptr = marker1_ptr;
+ *eoi_end_ptr = reader.ptr();
+ return true;
+ default:
+ // Skip for other markers.
+ uint16_t size;
+ READ_U16_OR_RETURN_FALSE(&size);
+ if (size < sizeof(size)) {
+ DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size
+ << ") is smaller than size field (" << sizeof(size)
+ << ")";
+ return false;
+ }
+ size -= sizeof(size);
+
+ if (!reader.Skip(size)) {
+ DLOG(ERROR) << "Ill-formed JPEG. Remaining size ("
+ << reader.remaining()
+ << ") is smaller than header specified (" << size << ")";
+ return false;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+// |result| is already initialized to 0 in ParseJpegPicture.
+static bool ParseSOI(const char* buffer,
+ size_t length,
+ JpegParseResult* result) {
+ // Spec B.2.1 High-level syntax
+ DCHECK(buffer);
+ DCHECK(result);
+ BigEndianReader reader(buffer, length);
+ uint8_t marker1;
+ uint8_t marker2;
+ bool has_marker_dqt = false;
+ bool has_marker_sos = false;
+
+ // Once reached SOS, all neccesary data are parsed.
+ while (!has_marker_sos) {
+ READ_U8_OR_RETURN_FALSE(&marker1);
+ if (marker1 != JPEG_MARKER_PREFIX)
+ return false;
+
+ do {
+ READ_U8_OR_RETURN_FALSE(&marker2);
+ } while (marker2 == JPEG_MARKER_PREFIX); // skip fill bytes
+
+ uint16_t size;
+ READ_U16_OR_RETURN_FALSE(&size);
+ // The size includes the size field itself.
+ if (size < sizeof(size)) {
+ DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size
+ << ") is smaller than size field (" << sizeof(size) << ")";
+ return false;
+ }
+ size -= sizeof(size);
+
+ if (reader.remaining() < size) {
+ DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" << reader.remaining()
+ << ") is smaller than header specified (" << size << ")";
+ return false;
+ }
+
+ switch (marker2) {
+ case JPEG_SOF0:
+ if (!ParseSOF(reader.ptr(), size, &result->frame_header)) {
+ DLOG(ERROR) << "ParseSOF failed";
+ return false;
+ }
+ break;
+ case JPEG_SOF1:
+ case JPEG_SOF2:
+ case JPEG_SOF3:
+ case JPEG_SOF5:
+ case JPEG_SOF6:
+ case JPEG_SOF7:
+ case JPEG_SOF9:
+ case JPEG_SOF10:
+ case JPEG_SOF11:
+ case JPEG_SOF13:
+ case JPEG_SOF14:
+ case JPEG_SOF15:
+ DLOG(ERROR) << "Only SOF0 (baseline) is supported, but got SOF"
+ << (marker2 - JPEG_SOF0);
+ return false;
+ case JPEG_DQT:
+ if (!ParseDQT(reader.ptr(), size, result->q_table)) {
+ DLOG(ERROR) << "ParseDQT failed";
+ return false;
+ }
+ has_marker_dqt = true;
+ break;
+ case JPEG_DHT:
+ if (!ParseDHT(reader.ptr(), size, result->dc_table, result->ac_table)) {
+ DLOG(ERROR) << "ParseDHT failed";
+ return false;
+ }
+ break;
+ case JPEG_DRI:
+ if (!ParseDRI(reader.ptr(), size, &result->restart_interval)) {
+ DLOG(ERROR) << "ParseDRI failed";
+ return false;
+ }
+ break;
+ case JPEG_SOS:
+ if (!ParseSOS(reader.ptr(), size, result->frame_header,
+ &result->scan)) {
+ DLOG(ERROR) << "ParseSOS failed";
+ return false;
+ }
+ has_marker_sos = true;
+ break;
+ default:
+ DVLOG(4) << "unknown marker " << static_cast<int>(marker2);
+ break;
+ }
+ reader.Skip(size);
+ }
+
+ if (!has_marker_dqt) {
+ DLOG(ERROR) << "No DQT marker found";
+ return false;
+ }
+
+ // Scan data follows scan header immediately.
+ result->data = reader.ptr();
+ result->data_size = reader.remaining();
+ return true;
+}
+
+bool ParseJpegPicture(const uint8_t* buffer,
+ size_t length,
+ JpegParseResult* result) {
+ DCHECK(buffer);
+ DCHECK(result);
+ BigEndianReader reader(reinterpret_cast<const char*>(buffer), length);
+ memset(result, 0, sizeof(JpegParseResult));
+
+ uint8_t marker1, marker2;
+ READ_U8_OR_RETURN_FALSE(&marker1);
+ READ_U8_OR_RETURN_FALSE(&marker2);
+ if (marker1 != JPEG_MARKER_PREFIX || marker2 != JPEG_SOI) {
+ DLOG(ERROR) << "Not a JPEG";
+ return false;
+ }
+
+ if (!ParseSOI(reader.ptr(), reader.remaining(), result))
+ return false;
+
+ // Update the sizes: |result->data_size| should not include the EOI marker or
+ // beyond.
+ BigEndianReader eoi_reader(result->data, result->data_size);
+ const char* eoi_begin_ptr = nullptr;
+ const char* eoi_end_ptr = nullptr;
+ if (!SearchEOI(eoi_reader.ptr(), eoi_reader.remaining(), &eoi_begin_ptr,
+ &eoi_end_ptr)) {
+ DLOG(ERROR) << "SearchEOI failed";
+ return false;
+ }
+ DCHECK(eoi_begin_ptr);
+ DCHECK(eoi_end_ptr);
+ result->data_size = eoi_begin_ptr - result->data;
+ result->image_size = eoi_end_ptr - reinterpret_cast<const char*>(buffer);
+ return true;
+}
+
+// TODO(andrescj): this function no longer seems necessary. Fix call sites to
+// use ParseJpegPicture() directly.
+bool ParseJpegStream(const uint8_t* buffer,
+ size_t length,
+ JpegParseResult* result) {
+ DCHECK(buffer);
+ DCHECK(result);
+ return ParseJpegPicture(buffer, length, result);
+}
+
+} // namespace media
diff --git a/chromium/media/parsers/jpeg_parser.h b/chromium/media/parsers/jpeg_parser.h
new file mode 100644
index 00000000000..bd054f555b8
--- /dev/null
+++ b/chromium/media/parsers/jpeg_parser.h
@@ -0,0 +1,164 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_PARSERS_JPEG_PARSER_H_
+#define MEDIA_PARSERS_JPEG_PARSER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "media/parsers/media_parsers_export.h"
+
+namespace media {
+
+// It's not a full featured JPEG parser implementation. It only parses JPEG
+// baseline sequential process (invalid or progressive JPEGs should fail but not
+// crash). For explanations of each struct and its members, see JPEG
+// specification at http://www.w3.org/Graphics/JPEG/itu-t81.pdf.
+
+enum JpegMarker {
+ JPEG_SOF0 = 0xC0, // start of frame (baseline)
+ JPEG_SOF1 = 0xC1, // start of frame (extended sequential)
+ JPEG_SOF2 = 0xC2, // start of frame (progressive)
+ JPEG_SOF3 = 0xC3, // start of frame (lossless))
+ JPEG_DHT = 0xC4, // define huffman table
+ JPEG_SOF5 = 0xC5, // start of frame (differential, sequential)
+ JPEG_SOF6 = 0xC6, // start of frame (differential, progressive)
+ JPEG_SOF7 = 0xC7, // start of frame (differential, lossless)
+ JPEG_SOF9 = 0xC9, // start of frame (arithmetic coding, extended)
+ JPEG_SOF10 = 0xCA, // start of frame (arithmetic coding, progressive)
+ JPEG_SOF11 = 0xCB, // start of frame (arithmetic coding, lossless)
+ JPEG_SOF13 = 0xCD, // start of frame (differential, arithmetic, sequential)
+ JPEG_SOF14 = 0xCE, // start of frame (differential, arithmetic, progressive)
+ JPEG_SOF15 = 0xCF, // start of frame (differential, arithmetic, lossless)
+ JPEG_RST0 = 0xD0, // restart
+ JPEG_RST1 = 0xD1, // restart
+ JPEG_RST2 = 0xD2, // restart
+ JPEG_RST3 = 0xD3, // restart
+ JPEG_RST4 = 0xD4, // restart
+ JPEG_RST5 = 0xD5, // restart
+ JPEG_RST6 = 0xD6, // restart
+ JPEG_RST7 = 0xD7, // restart
+ JPEG_SOI = 0xD8, // start of image
+ JPEG_EOI = 0xD9, // end of image
+ JPEG_SOS = 0xDA, // start of scan
+ JPEG_DQT = 0xDB, // define quantization table
+ JPEG_DRI = 0xDD, // define restart internal
+ JPEG_APP0 = 0xE0, // start of application segment (APP0)
+ JPEG_APP1 = 0xE1, // start of application segment (APP1)
+ JPEG_MARKER_PREFIX = 0xFF, // jpeg marker prefix
+};
+
+// JPEG format uses 2 bytes to denote the size of a segment, and the size
+// includes the 2 bytes used for specifying it. Therefore, maximum data size
+// allowed is: 65535 - 2 = 65533.
+constexpr size_t kMaxMarkerSizeAllowed = 65533;
+
+// JPEG header only uses 2 bytes to represent width and height.
+constexpr int kMaxDimension = 65535;
+
+constexpr size_t kDctSize = 64;
+constexpr size_t kNumDcRunSizeBits = 16;
+constexpr size_t kNumAcRunSizeBits = 16;
+constexpr size_t kNumDcCodeWordsHuffVal = 12;
+constexpr size_t kNumAcCodeWordsHuffVal = 162;
+constexpr size_t kJpegDefaultHeaderSize =
+ 67 + (kDctSize * 2) + (kNumDcRunSizeBits * 2) +
+ (kNumDcCodeWordsHuffVal * 2) + (kNumAcRunSizeBits * 2) +
+ (kNumAcCodeWordsHuffVal * 2);
+constexpr size_t kJFIFApp0Size = 16;
+
+const size_t kJpegMaxHuffmanTableNumBaseline = 2;
+const size_t kJpegMaxComponents = 4;
+const size_t kJpegMaxQuantizationTableNum = 4;
+
+// Parsing result of JPEG DHT marker.
+struct JpegHuffmanTable {
+ bool valid;
+ uint8_t code_length[16];
+ uint8_t code_value[162];
+};
+
+// K.3.3.1 "Specification of typical tables for DC difference coding"
+MEDIA_PARSERS_EXPORT
+extern const JpegHuffmanTable kDefaultDcTable[kJpegMaxHuffmanTableNumBaseline];
+
+// K.3.3.2 "Specification of typical tables for AC coefficient coding"
+MEDIA_PARSERS_EXPORT
+extern const JpegHuffmanTable kDefaultAcTable[kJpegMaxHuffmanTableNumBaseline];
+
+// Parsing result of JPEG DQT marker.
+struct JpegQuantizationTable {
+ bool valid;
+ uint8_t value[kDctSize]; // baseline only supports 8 bits quantization table
+};
+
+MEDIA_PARSERS_EXPORT extern const uint8_t kZigZag8x8[64];
+
+// Table K.1 Luminance quantization table
+// Table K.2 Chrominance quantization table
+MEDIA_PARSERS_EXPORT
+extern const JpegQuantizationTable kDefaultQuantTable[2];
+
+// Parsing result of a JPEG component.
+struct JpegComponent {
+ uint8_t id;
+ uint8_t horizontal_sampling_factor;
+ uint8_t vertical_sampling_factor;
+ uint8_t quantization_table_selector;
+};
+
+// Parsing result of a JPEG SOF marker.
+struct JpegFrameHeader {
+ uint16_t visible_width;
+ uint16_t visible_height;
+ uint16_t coded_width;
+ uint16_t coded_height;
+ uint8_t num_components;
+ JpegComponent components[kJpegMaxComponents];
+};
+
+// Parsing result of JPEG SOS marker.
+struct JpegScanHeader {
+ uint8_t num_components;
+ struct Component {
+ uint8_t component_selector;
+ uint8_t dc_selector;
+ uint8_t ac_selector;
+ } components[kJpegMaxComponents];
+};
+
+struct JpegParseResult {
+ JpegFrameHeader frame_header;
+ JpegHuffmanTable dc_table[kJpegMaxHuffmanTableNumBaseline];
+ JpegHuffmanTable ac_table[kJpegMaxHuffmanTableNumBaseline];
+ JpegQuantizationTable q_table[kJpegMaxQuantizationTableNum];
+ uint16_t restart_interval;
+ JpegScanHeader scan;
+ const char* data;
+ // The size of compressed data of the first image.
+ size_t data_size;
+ // The size of the first entire image including header.
+ size_t image_size;
+};
+
+// Parses JPEG picture in |buffer| with |length|. Returns true iff header is
+// valid and JPEG baseline sequential process is present. If parsed
+// successfully, |result| is the parsed result.
+MEDIA_PARSERS_EXPORT
+bool ParseJpegPicture(const uint8_t* buffer,
+ size_t length,
+ JpegParseResult* result);
+
+// Parses the first image of JPEG stream in |buffer| with |length|. Returns
+// true iff header is valid and JPEG baseline sequential process is present.
+// If parsed successfully, |result| is the parsed result.
+MEDIA_PARSERS_EXPORT
+bool ParseJpegStream(const uint8_t* buffer,
+ size_t length,
+ JpegParseResult* result);
+
+} // namespace media
+
+#endif // MEDIA_PARSERS_JPEG_PARSER_H_
diff --git a/chromium/media/parsers/jpeg_parser_picture_fuzzertest.cc b/chromium/media/parsers/jpeg_parser_picture_fuzzertest.cc
new file mode 100644
index 00000000000..31d9cae7e0f
--- /dev/null
+++ b/chromium/media/parsers/jpeg_parser_picture_fuzzertest.cc
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "media/parsers/jpeg_parser.h"
+
+struct Environment {
+ Environment() { logging::SetMinLogLevel(logging::LOG_FATAL); }
+};
+
+Environment* env = new Environment();
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ media::JpegParseResult result;
+ ParseJpegPicture(data, size, &result);
+ return 0;
+}
diff --git a/chromium/media/parsers/jpeg_parser_unittest.cc b/chromium/media/parsers/jpeg_parser_unittest.cc
new file mode 100644
index 00000000000..72b9ae957aa
--- /dev/null
+++ b/chromium/media/parsers/jpeg_parser_unittest.cc
@@ -0,0 +1,138 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "base/at_exit.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/path_service.h"
+#include "media/base/test_data_util.h"
+#include "media/parsers/jpeg_parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+TEST(JpegParserTest, Parsing) {
+ base::FilePath data_dir;
+ ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
+
+ // This sample frame is captured from Chromebook Pixel
+ base::FilePath file_path = data_dir.AppendASCII("media")
+ .AppendASCII("test")
+ .AppendASCII("data")
+ .AppendASCII("pixel-1280x720.jpg");
+
+ base::MemoryMappedFile stream;
+ ASSERT_TRUE(stream.Initialize(file_path))
+ << "Couldn't open stream file: " << file_path.MaybeAsASCII();
+
+ JpegParseResult result;
+ ASSERT_TRUE(ParseJpegPicture(stream.data(), stream.length(), &result));
+
+ // Verify selected fields
+
+ // SOF fields
+ EXPECT_EQ(1280, result.frame_header.visible_width);
+ EXPECT_EQ(720, result.frame_header.visible_height);
+ EXPECT_EQ(1280, result.frame_header.coded_width);
+ EXPECT_EQ(720, result.frame_header.coded_height);
+ EXPECT_EQ(3, result.frame_header.num_components);
+ EXPECT_EQ(1, result.frame_header.components[0].id);
+ EXPECT_EQ(2, result.frame_header.components[0].horizontal_sampling_factor);
+ EXPECT_EQ(1, result.frame_header.components[0].vertical_sampling_factor);
+ EXPECT_EQ(0, result.frame_header.components[0].quantization_table_selector);
+ EXPECT_EQ(2, result.frame_header.components[1].id);
+ EXPECT_EQ(1, result.frame_header.components[1].horizontal_sampling_factor);
+ EXPECT_EQ(1, result.frame_header.components[1].vertical_sampling_factor);
+ EXPECT_EQ(1, result.frame_header.components[1].quantization_table_selector);
+ EXPECT_EQ(3, result.frame_header.components[2].id);
+ EXPECT_EQ(1, result.frame_header.components[2].horizontal_sampling_factor);
+ EXPECT_EQ(1, result.frame_header.components[2].vertical_sampling_factor);
+ EXPECT_EQ(1, result.frame_header.components[2].quantization_table_selector);
+
+ // DRI fields
+ EXPECT_EQ(0, result.restart_interval);
+
+ // DQT fields
+ EXPECT_TRUE(result.q_table[0].valid);
+ EXPECT_TRUE(result.q_table[1].valid);
+ EXPECT_FALSE(result.q_table[2].valid);
+ EXPECT_FALSE(result.q_table[3].valid);
+
+ // DHT fields (no DHT marker)
+ EXPECT_FALSE(result.dc_table[0].valid);
+ EXPECT_FALSE(result.ac_table[0].valid);
+ EXPECT_FALSE(result.dc_table[1].valid);
+ EXPECT_FALSE(result.ac_table[1].valid);
+
+ // SOS fields
+ EXPECT_EQ(3, result.scan.num_components);
+ EXPECT_EQ(1, result.scan.components[0].component_selector);
+ EXPECT_EQ(0, result.scan.components[0].dc_selector);
+ EXPECT_EQ(0, result.scan.components[0].ac_selector);
+ EXPECT_EQ(2, result.scan.components[1].component_selector);
+ EXPECT_EQ(1, result.scan.components[1].dc_selector);
+ EXPECT_EQ(1, result.scan.components[1].ac_selector);
+ EXPECT_EQ(3, result.scan.components[2].component_selector);
+ EXPECT_EQ(1, result.scan.components[2].dc_selector);
+ EXPECT_EQ(1, result.scan.components[2].ac_selector);
+ EXPECT_EQ(121148u, result.data_size);
+ EXPECT_EQ(121358u, result.image_size);
+}
+
+TEST(JpegParserTest, TrailingZerosShouldBeIgnored) {
+ base::FilePath data_dir;
+ ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
+ base::FilePath file_path =
+ data_dir.AppendASCII("media")
+ .AppendASCII("test")
+ .AppendASCII("data")
+ .AppendASCII("pixel-1280x720-trailing-zeros.jpg");
+
+ base::MemoryMappedFile stream;
+ ASSERT_TRUE(stream.Initialize(file_path))
+ << "Couldn't open stream file: " << file_path.MaybeAsASCII();
+
+ JpegParseResult result;
+ ASSERT_TRUE(ParseJpegPicture(stream.data(), stream.length(), &result));
+
+ // Verify selected fields
+
+ // SOS fields
+ EXPECT_EQ(121148u, result.data_size);
+ EXPECT_EQ(121358u, result.image_size);
+}
+
+TEST(JpegParserTest, CodedSizeNotEqualVisibleSize) {
+ base::FilePath data_dir;
+ ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
+
+ base::FilePath file_path = data_dir.AppendASCII("media")
+ .AppendASCII("test")
+ .AppendASCII("data")
+ .AppendASCII("blank-1x1.jpg");
+
+ base::MemoryMappedFile stream;
+ ASSERT_TRUE(stream.Initialize(file_path))
+ << "Couldn't open stream file: " << file_path.MaybeAsASCII();
+
+ JpegParseResult result;
+ ASSERT_TRUE(ParseJpegPicture(stream.data(), stream.length(), &result));
+
+ EXPECT_EQ(1, result.frame_header.visible_width);
+ EXPECT_EQ(1, result.frame_header.visible_height);
+ // The sampling factor of the given image is 2:2, so coded size is 16x16
+ EXPECT_EQ(16, result.frame_header.coded_width);
+ EXPECT_EQ(16, result.frame_header.coded_height);
+ EXPECT_EQ(2, result.frame_header.components[0].horizontal_sampling_factor);
+ EXPECT_EQ(2, result.frame_header.components[0].vertical_sampling_factor);
+}
+
+TEST(JpegParserTest, ParsingFail) {
+ const uint8_t data[] = {0, 1, 2, 3}; // not jpeg
+ JpegParseResult result;
+ ASSERT_FALSE(ParseJpegPicture(data, sizeof(data), &result));
+}
+
+} // namespace media
diff --git a/chromium/media/parsers/media_parsers_export.h b/chromium/media/parsers/media_parsers_export.h
new file mode 100644
index 00000000000..72277ea7a92
--- /dev/null
+++ b/chromium/media/parsers/media_parsers_export.h
@@ -0,0 +1,12 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_PARSERS_MEDIA_PARSERS_EXPORT_H_
+#define MEDIA_PARSERS_MEDIA_PARSERS_EXPORT_H_
+
+#include "base/component_export.h"
+
+#define MEDIA_PARSERS_EXPORT COMPONENT_EXPORT(MEDIA_PARSER)
+
+#endif // MEDIA_PARSERS_MEDIA_PARSERS_EXPORT_H_
diff --git a/chromium/media/parsers/vp8_bool_decoder.cc b/chromium/media/parsers/vp8_bool_decoder.cc
new file mode 100644
index 00000000000..4f156ad8daa
--- /dev/null
+++ b/chromium/media/parsers/vp8_bool_decoder.cc
@@ -0,0 +1,209 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+/*
+ * Copyright (c) 2010, The WebM Project authors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of Google, nor the WebM Project, nor the names
+ * of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This file is modified from the dboolhuff.{c,h} from the WebM's libvpx
+// project. (http://www.webmproject.org/code)
+// It is used to decode bits from a vp8 stream.
+
+#include "media/parsers/vp8_bool_decoder.h"
+
+#include <limits.h>
+
+#include <algorithm>
+
+#include "base/numerics/safe_conversions.h"
+
+namespace media {
+
+#define VP8_BD_VALUE_BIT \
+ static_cast<int>(sizeof(Vp8BoolDecoder::value_) * CHAR_BIT)
+
+static const int kDefaultProbability = 0x80; // 0x80 / 256 = 0.5
+
+// This is meant to be a large, positive constant that can still be efficiently
+// loaded as an immediate (on platforms like ARM, for example). Even relatively
+// modest values like 100 would work fine.
+#define VP8_LOTS_OF_BITS (0x40000000)
+
+// The number of leading zeros.
+static const unsigned char kVp8Norm[256] = {
+ 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+Vp8BoolDecoder::Vp8BoolDecoder()
+ : user_buffer_(NULL),
+ user_buffer_end_(NULL),
+ value_(0),
+ count_(-8),
+ range_(255) {
+}
+
+bool Vp8BoolDecoder::Initialize(const uint8_t* data, size_t size) {
+ if (data == NULL || size == 0)
+ return false;
+ user_buffer_start_ = data;
+ user_buffer_ = data;
+ user_buffer_end_ = data + size;
+ value_ = 0;
+ count_ = -8;
+ range_ = 255;
+ return true;
+}
+
+void Vp8BoolDecoder::FillDecoder() {
+ DCHECK(user_buffer_ != NULL);
+ int shift = VP8_BD_VALUE_BIT - CHAR_BIT - (count_ + CHAR_BIT);
+ size_t bytes_left = user_buffer_end_ - user_buffer_;
+ size_t bits_left = bytes_left * CHAR_BIT;
+ int x = static_cast<int>(shift + CHAR_BIT - bits_left);
+ int loop_end = 0;
+
+ if (x >= 0) {
+ count_ += VP8_LOTS_OF_BITS;
+ loop_end = x;
+ }
+
+ if (x < 0 || bits_left) {
+ while (shift >= loop_end) {
+ count_ += CHAR_BIT;
+ value_ |= static_cast<size_t>(*user_buffer_) << shift;
+ ++user_buffer_;
+ shift -= CHAR_BIT;
+ }
+ }
+}
+
+int Vp8BoolDecoder::ReadBit(int probability) {
+ int bit = 0;
+ size_t split = 1 + (((range_ - 1) * probability) >> 8);
+ if (count_ < 0)
+ FillDecoder();
+ size_t bigsplit = static_cast<size_t>(split) << (VP8_BD_VALUE_BIT - 8);
+
+ if (value_ >= bigsplit) {
+ range_ -= split;
+ value_ -= bigsplit;
+ bit = 1;
+ } else {
+ range_ = split;
+ }
+
+ size_t shift = kVp8Norm[range_];
+ range_ <<= shift;
+ value_ <<= shift;
+ count_ -= shift;
+
+ DCHECK_EQ(1U, (range_ >> 7)); // In the range [128, 255].
+
+ return bit;
+}
+
+bool Vp8BoolDecoder::ReadLiteral(size_t num_bits, int* out) {
+ DCHECK_LE(num_bits, sizeof(int) * CHAR_BIT);
+ *out = 0;
+ for (; num_bits > 0; --num_bits)
+ *out = (*out << 1) | ReadBit(kDefaultProbability);
+ return !OutOfBuffer();
+}
+
+bool Vp8BoolDecoder::ReadBool(bool* out, uint8_t probability) {
+ *out = !!ReadBit(probability);
+ return !OutOfBuffer();
+}
+
+bool Vp8BoolDecoder::ReadBool(bool* out) {
+ return ReadBool(out, kDefaultProbability);
+}
+
+bool Vp8BoolDecoder::ReadLiteralWithSign(size_t num_bits, int* out) {
+ ReadLiteral(num_bits, out);
+ // Read sign.
+ if (ReadBit(kDefaultProbability))
+ *out = -*out;
+ return !OutOfBuffer();
+}
+
+size_t Vp8BoolDecoder::BitOffset() {
+ int bit_count = count_ + 8;
+ if (bit_count > VP8_BD_VALUE_BIT)
+ // Capped at 0 to ignore buffer underrun.
+ bit_count = std::max(0, bit_count - VP8_LOTS_OF_BITS);
+ return (user_buffer_ - user_buffer_start_) * 8 - bit_count;
+}
+
+uint8_t Vp8BoolDecoder::GetRange() {
+ return base::checked_cast<uint8_t>(range_);
+}
+
+uint8_t Vp8BoolDecoder::GetBottom() {
+ if (count_ < 0)
+ FillDecoder();
+ return static_cast<uint8_t>(value_ >> (VP8_BD_VALUE_BIT - 8));
+}
+
+inline bool Vp8BoolDecoder::OutOfBuffer() {
+ // Check if we have reached the end of the buffer.
+ //
+ // Variable |count_| stores the number of bits in the |value_| buffer, minus
+ // 8. The top byte is part of the algorithm and the remainder is buffered to
+ // be shifted into it. So, if |count_| == 8, the top 16 bits of |value_| are
+ // occupied, 8 for the algorithm and 8 in the buffer.
+ //
+ // When reading a byte from the user's buffer, |count_| is filled with 8 and
+ // one byte is filled into the |value_| buffer. When we reach the end of the
+ // data, |count_| is additionally filled with VP8_LOTS_OF_BITS. So when
+ // |count_| == VP8_LOTS_OF_BITS - 1, the user's data has been exhausted.
+ return (count_ > VP8_BD_VALUE_BIT) && (count_ < VP8_LOTS_OF_BITS);
+}
+
+} // namespace media
diff --git a/chromium/media/parsers/vp8_bool_decoder.h b/chromium/media/parsers/vp8_bool_decoder.h
new file mode 100644
index 00000000000..0f407cfbcc9
--- /dev/null
+++ b/chromium/media/parsers/vp8_bool_decoder.h
@@ -0,0 +1,135 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+/*
+ * Copyright (c) 2010, The WebM Project authors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of Google, nor the WebM Project, nor the names
+ * of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This file is modified from the dboolhuff.{c,h} from the WebM's libvpx
+// project. (http://www.webmproject.org/code)
+// It is used to decode bits from a vp8 stream.
+
+#ifndef MEDIA_PARSERS_VP8_BOOL_DECODER_H_
+#define MEDIA_PARSERS_VP8_BOOL_DECODER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "media/parsers/media_parsers_export.h"
+
+namespace media {
+
+// A class to decode the VP8's boolean entropy coded stream. It's a variant of
+// arithmetic coding. See RFC 6386 - Chapter 7. Boolean Entropy Decoder.
+class MEDIA_PARSERS_EXPORT Vp8BoolDecoder {
+ public:
+ Vp8BoolDecoder();
+
+ // Initializes the decoder to start decoding |data|, |size| being size
+ // of |data| in bytes. Returns false if |data| is NULL or empty.
+ bool Initialize(const uint8_t* data, size_t size);
+
+ // Reads a boolean from the coded stream. Returns false if it has reached the
+ // end of |data| and failed to read the boolean. The probability of |out| to
+ // be true is |probability| / 256, e.g., when |probability| is 0x80, the
+ // chance is 1/2 (i.e., 0x80 / 256).
+ bool ReadBool(bool* out, uint8_t probability);
+
+ // Reads a boolean from the coded stream with the default probability 1/2.
+ // Returns false if it has reached the end of |data| and failed to read the
+ // boolean.
+ bool ReadBool(bool* out);
+
+ // Reads a "literal", that is, a "num_bits"-wide unsigned value whose bits
+ // come high- to low-order, with each bit encoded at probability 1/2.
+ // Returns false if it has reached the end of |data| and failed to read the
+ // literal.
+ bool ReadLiteral(size_t num_bits, int* out);
+
+ // Reads a literal with sign from the coded stream. This is similar to
+ // the ReadListeral(), it first read a "num_bits"-wide unsigned value, and
+ // then read an extra bit as the sign of the literal. Returns false if it has
+ // reached the end of |data| and failed to read the literal or the sign.
+ // This is different from the "read_signed_literal(d, n)" defined in RFC 6386.
+ bool ReadLiteralWithSign(size_t num_bits, int* out);
+
+ // The following methods are used to get the internal states of the decoder.
+
+ // Returns the bit offset to the current top bit of the coded stream. It is
+ // also the number of bits that have been written in the corresponding
+ // encoding state. More specifically, we have the following constraint:
+ // w + (bottom * S) <= v < w + (bottom + range) * S,
+ // where "w" is for the bits already written,
+ // "v" is for the possible values of the coded number.
+ // "S" is the scale for the current bit position,
+ // i.e., S = pow(2, -(n + 8)), where "n" is the bit number of "w".
+ // BitOffset() returns the bit count of "w", i.e., "n".
+ size_t BitOffset();
+
+ // Gets the "bottom" of the current coded value. See BitOffset() for
+ // more details.
+ uint8_t GetBottom();
+
+ // Gets the "range" of the current coded value. See BitOffset() for
+ // more details.
+ uint8_t GetRange();
+
+ private:
+ // Reads the next bit from the coded stream. The probability of the bit to
+ // be one is |probability| / 256.
+ int ReadBit(int probability);
+
+ // Fills more bits from |user_buffer_| to |value_|. We shall keep at least 8
+ // bits of the current |user_buffer_| in |value_|.
+ void FillDecoder();
+
+ // Returns true iff we have ran out of bits.
+ bool OutOfBuffer();
+
+ const uint8_t* user_buffer_;
+ const uint8_t* user_buffer_start_;
+ const uint8_t* user_buffer_end_;
+ size_t value_;
+ int count_;
+ size_t range_;
+
+ DISALLOW_COPY_AND_ASSIGN(Vp8BoolDecoder);
+};
+
+} // namespace media
+
+#endif // MEDIA_PARSERS_VP8_BOOL_DECODER_H_
diff --git a/chromium/media/parsers/vp8_bool_decoder_unittest.cc b/chromium/media/parsers/vp8_bool_decoder_unittest.cc
new file mode 100644
index 00000000000..87968f4fc26
--- /dev/null
+++ b/chromium/media/parsers/vp8_bool_decoder_unittest.cc
@@ -0,0 +1,126 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/parsers/vp8_bool_decoder.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+const static size_t NUM_BITS_TO_TEST = 100;
+
+namespace {
+
+// 100 zeros with probability of 0x80.
+const uint8_t kDataZerosAndEvenProbabilities[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00};
+
+// 100 ones with probability of 0x80.
+const uint8_t kDataOnesAndEvenProbabilities[] = {
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf0, 0x20};
+
+// [0, 1, 0, 1, ..., 1] with probability [0, 1, 2, 3, ..., 99].
+const uint8_t kDataParitiesAndIncreasingProbabilities[] = {
+ 0x00, 0x02, 0x08, 0x31, 0x8e, 0xca, 0xab, 0xe2, 0xc8, 0x31, 0x12,
+ 0xb3, 0x2c, 0x19, 0x90, 0xc6, 0x6a, 0xeb, 0x17, 0x52, 0x30};
+
+} // namespace
+
+class Vp8BoolDecoderTest : public ::testing::Test {
+ public:
+ Vp8BoolDecoderTest() = default;
+
+ protected:
+ // Fixture member, the bool decoder to be tested.
+ Vp8BoolDecoder bd_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Vp8BoolDecoderTest);
+};
+
+#define INITIALIZE(data) ASSERT_TRUE(bd_.Initialize(data, sizeof(data)));
+
+TEST_F(Vp8BoolDecoderTest, DecodeBoolsWithZerosAndEvenProbabilities) {
+ INITIALIZE(kDataZerosAndEvenProbabilities);
+ ASSERT_EQ(0u, bd_.BitOffset());
+ for (size_t i = 0; i < NUM_BITS_TO_TEST; ++i) {
+ bool out = true;
+ ASSERT_TRUE(bd_.ReadBool(&out, 0x80));
+ ASSERT_FALSE(out);
+ ASSERT_EQ(i, bd_.BitOffset());
+ }
+}
+
+TEST_F(Vp8BoolDecoderTest, DecodeLiteralsWithZerosAndEvenProbabilities) {
+ INITIALIZE(kDataZerosAndEvenProbabilities);
+
+ int value = 1;
+ ASSERT_TRUE(bd_.ReadLiteral(1, &value));
+ ASSERT_EQ(0, value);
+
+ value = 1;
+ ASSERT_TRUE(bd_.ReadLiteral(32, &value));
+ ASSERT_EQ(0, value);
+
+ value = 1;
+ ASSERT_TRUE(bd_.ReadLiteralWithSign(1, &value));
+ ASSERT_EQ(0, value);
+
+ value = 1;
+ ASSERT_TRUE(bd_.ReadLiteralWithSign(31, &value));
+ ASSERT_EQ(0, value);
+}
+
+TEST_F(Vp8BoolDecoderTest, DecodeBoolsWithOnesAndEvenProbabilities) {
+ INITIALIZE(kDataOnesAndEvenProbabilities);
+
+ ASSERT_EQ(0u, bd_.BitOffset());
+ for (size_t i = 0; i < NUM_BITS_TO_TEST; ++i) {
+ bool out = false;
+ ASSERT_TRUE(bd_.ReadBool(&out, 0x80));
+ ASSERT_TRUE(out);
+ ASSERT_EQ(i + 1, bd_.BitOffset());
+ }
+}
+
+TEST_F(Vp8BoolDecoderTest, DecodeLiteralsWithOnesAndEvenProbabilities) {
+ INITIALIZE(kDataOnesAndEvenProbabilities);
+
+ int value = 0;
+ ASSERT_TRUE(bd_.ReadLiteral(1, &value));
+ EXPECT_EQ(1, value);
+
+ value = 0;
+ ASSERT_TRUE(bd_.ReadLiteral(31, &value));
+ EXPECT_EQ(0x7FFFFFFF, value);
+
+ value = 0;
+ ASSERT_TRUE(bd_.ReadLiteralWithSign(1, &value));
+ EXPECT_EQ(-1, value);
+
+ value = 0;
+ ASSERT_TRUE(bd_.ReadLiteralWithSign(31, &value));
+ EXPECT_EQ(-0x7FFFFFFF, value);
+}
+
+TEST_F(Vp8BoolDecoderTest, DecodeBoolsWithParitiesAndIncreasingProbabilities) {
+ INITIALIZE(kDataParitiesAndIncreasingProbabilities);
+
+ for (size_t i = 0; i < NUM_BITS_TO_TEST; ++i) {
+ bool out = !(i & 1);
+ ASSERT_LE(i, std::numeric_limits<uint8_t>::max());
+ ASSERT_TRUE(bd_.ReadBool(&out, static_cast<uint8_t>(i)));
+ EXPECT_EQ(out, !!(i & 1));
+ }
+}
+
+} // namespace media
diff --git a/chromium/media/parsers/vp8_parser.cc b/chromium/media/parsers/vp8_parser.cc
new file mode 100644
index 00000000000..04345b5f110
--- /dev/null
+++ b/chromium/media/parsers/vp8_parser.cc
@@ -0,0 +1,876 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains an implementation of a VP8 raw stream parser,
+// as defined in RFC 6386.
+
+#include "media/parsers/vp8_parser.h"
+
+#include "base/logging.h"
+
+namespace media {
+
+#define ERROR_RETURN(what) \
+ do { \
+ DVLOG(1) << "Error while trying to read " #what; \
+ return false; \
+ } while (0)
+
+#define BD_READ_BOOL_OR_RETURN(out) \
+ do { \
+ if (!bd_.ReadBool(out)) \
+ ERROR_RETURN(out); \
+ } while (0)
+
+#define BD_READ_BOOL_WITH_PROB_OR_RETURN(out, prob) \
+ do { \
+ if (!bd_.ReadBool(out, prob)) \
+ ERROR_RETURN(out); \
+ } while (0)
+
+#define BD_READ_UNSIGNED_OR_RETURN(num_bits, out) \
+ do { \
+ int _out; \
+ if (!bd_.ReadLiteral(num_bits, &_out)) \
+ ERROR_RETURN(out); \
+ *out = _out; \
+ } while (0)
+
+#define BD_READ_SIGNED_OR_RETURN(num_bits, out) \
+ do { \
+ int _out; \
+ if (!bd_.ReadLiteralWithSign(num_bits, &_out)) \
+ ERROR_RETURN(out); \
+ *out = _out; \
+ } while (0)
+
+Vp8FrameHeader::Vp8FrameHeader() {
+ memset(this, 0, sizeof(*this));
+}
+
+Vp8Parser::Vp8Parser() : stream_(nullptr), bytes_left_(0) {
+}
+
+Vp8Parser::~Vp8Parser() = default;
+
+bool Vp8Parser::ParseFrame(const uint8_t* ptr,
+ size_t frame_size,
+ Vp8FrameHeader* fhdr) {
+ stream_ = ptr;
+ bytes_left_ = frame_size;
+
+ memset(fhdr, 0, sizeof(*fhdr));
+ fhdr->data = stream_;
+ fhdr->frame_size = bytes_left_;
+
+ if (!ParseFrameTag(fhdr))
+ return false;
+
+ fhdr->first_part_offset = stream_ - fhdr->data;
+
+ if (!ParseFrameHeader(fhdr))
+ return false;
+
+ if (!ParsePartitions(fhdr))
+ return false;
+
+ DVLOG(4) << "Frame parsed, start: " << static_cast<const void*>(ptr)
+ << ", size: " << frame_size
+ << ", offsets: to first_part=" << fhdr->first_part_offset
+ << ", to macroblock data (in bits)=" << fhdr->macroblock_bit_offset;
+
+ return true;
+}
+
+static inline uint32_t GetBitsAt(uint32_t data, size_t shift, size_t num_bits) {
+ return ((data >> shift) & ((1 << num_bits) - 1));
+}
+
+bool Vp8Parser::ParseFrameTag(Vp8FrameHeader* fhdr) {
+ const size_t kFrameTagSize = 3;
+ if (bytes_left_ < kFrameTagSize)
+ return false;
+
+ uint32_t frame_tag = (stream_[2] << 16) | (stream_[1] << 8) | stream_[0];
+ fhdr->frame_type =
+ static_cast<Vp8FrameHeader::FrameType>(GetBitsAt(frame_tag, 0, 1));
+ fhdr->version = GetBitsAt(frame_tag, 1, 2);
+ fhdr->is_experimental = !!GetBitsAt(frame_tag, 3, 1);
+ fhdr->show_frame =!!GetBitsAt(frame_tag, 4, 1);
+ fhdr->first_part_size = GetBitsAt(frame_tag, 5, 19);
+
+ stream_ += kFrameTagSize;
+ bytes_left_ -= kFrameTagSize;
+
+ if (fhdr->IsKeyframe()) {
+ const size_t kKeyframeTagSize = 7;
+ if (bytes_left_ < kKeyframeTagSize)
+ return false;
+
+ static const uint8_t kVp8StartCode[] = {0x9d, 0x01, 0x2a};
+ if (memcmp(stream_, kVp8StartCode, sizeof(kVp8StartCode)) != 0)
+ return false;
+
+ stream_ += sizeof(kVp8StartCode);
+ bytes_left_ -= sizeof(kVp8StartCode);
+
+ uint16_t data = (stream_[1] << 8) | stream_[0];
+ fhdr->width = data & 0x3fff;
+ fhdr->horizontal_scale = data >> 14;
+
+ data = (stream_[3] << 8) | stream_[2];
+ fhdr->height = data & 0x3fff;
+ fhdr->vertical_scale = data >> 14;
+
+ stream_ += 4;
+ bytes_left_ -= 4;
+ }
+
+ return true;
+}
+
+bool Vp8Parser::ParseFrameHeader(Vp8FrameHeader* fhdr) {
+ if (!bd_.Initialize(stream_, bytes_left_))
+ return false;
+
+ bool keyframe = fhdr->IsKeyframe();
+ if (keyframe) {
+ unsigned int data;
+ BD_READ_UNSIGNED_OR_RETURN(1, &data); // color_space
+ BD_READ_UNSIGNED_OR_RETURN(1, &data); // clamping_type
+ }
+
+ if (!ParseSegmentationHeader(keyframe))
+ return false;
+
+ fhdr->segmentation_hdr = curr_segmentation_hdr_;
+
+ if (!ParseLoopFilterHeader(keyframe))
+ return false;
+
+ fhdr->loopfilter_hdr = curr_loopfilter_hdr_;
+
+ int log2_nbr_of_dct_partitions;
+ BD_READ_UNSIGNED_OR_RETURN(2, &log2_nbr_of_dct_partitions);
+ fhdr->num_of_dct_partitions = static_cast<size_t>(1)
+ << log2_nbr_of_dct_partitions;
+
+ if (!ParseQuantizationHeader(&fhdr->quantization_hdr))
+ return false;
+
+ if (keyframe) {
+ BD_READ_BOOL_OR_RETURN(&fhdr->refresh_entropy_probs);
+ } else {
+ BD_READ_BOOL_OR_RETURN(&fhdr->refresh_golden_frame);
+ BD_READ_BOOL_OR_RETURN(&fhdr->refresh_alternate_frame);
+
+ int refresh_mode;
+ if (!fhdr->refresh_golden_frame) {
+ BD_READ_UNSIGNED_OR_RETURN(2, &refresh_mode);
+ fhdr->copy_buffer_to_golden =
+ static_cast<Vp8FrameHeader::GoldenRefreshMode>(refresh_mode);
+ }
+
+ if (!fhdr->refresh_alternate_frame) {
+ BD_READ_UNSIGNED_OR_RETURN(2, &refresh_mode);
+ fhdr->copy_buffer_to_alternate =
+ static_cast<Vp8FrameHeader::AltRefreshMode>(refresh_mode);
+ }
+
+ BD_READ_UNSIGNED_OR_RETURN(1, &fhdr->sign_bias_golden);
+ BD_READ_UNSIGNED_OR_RETURN(1, &fhdr->sign_bias_alternate);
+ BD_READ_BOOL_OR_RETURN(&fhdr->refresh_entropy_probs);
+ BD_READ_BOOL_OR_RETURN(&fhdr->refresh_last);
+ }
+
+ if (keyframe)
+ ResetProbs();
+
+ fhdr->entropy_hdr = curr_entropy_hdr_;
+
+ if (!ParseTokenProbs(&fhdr->entropy_hdr, fhdr->refresh_entropy_probs))
+ return false;
+
+ BD_READ_BOOL_OR_RETURN(&fhdr->mb_no_skip_coeff);
+ if (fhdr->mb_no_skip_coeff)
+ BD_READ_UNSIGNED_OR_RETURN(8, &fhdr->prob_skip_false);
+
+ if (!keyframe) {
+ BD_READ_UNSIGNED_OR_RETURN(8, &fhdr->prob_intra);
+ BD_READ_UNSIGNED_OR_RETURN(8, &fhdr->prob_last);
+ BD_READ_UNSIGNED_OR_RETURN(8, &fhdr->prob_gf);
+ }
+
+ if (!ParseIntraProbs(&fhdr->entropy_hdr, fhdr->refresh_entropy_probs,
+ keyframe))
+ return false;
+
+ if (!keyframe) {
+ if (!ParseMVProbs(&fhdr->entropy_hdr, fhdr->refresh_entropy_probs))
+ return false;
+ }
+
+ fhdr->macroblock_bit_offset = bd_.BitOffset();
+ fhdr->bool_dec_range = bd_.GetRange();
+ fhdr->bool_dec_value = bd_.GetBottom();
+ fhdr->bool_dec_count = 7 - (bd_.BitOffset() + 7) % 8;
+
+ return true;
+}
+
+bool Vp8Parser::ParseSegmentationHeader(bool keyframe) {
+ Vp8SegmentationHeader* shdr = &curr_segmentation_hdr_;
+
+ if (keyframe)
+ memset(shdr, 0, sizeof(*shdr));
+
+ BD_READ_BOOL_OR_RETURN(&shdr->segmentation_enabled);
+ if (!shdr->segmentation_enabled)
+ return true;
+
+ BD_READ_BOOL_OR_RETURN(&shdr->update_mb_segmentation_map);
+ BD_READ_BOOL_OR_RETURN(&shdr->update_segment_feature_data);
+ if (shdr->update_segment_feature_data) {
+ int mode;
+ BD_READ_UNSIGNED_OR_RETURN(1, &mode);
+ shdr->segment_feature_mode =
+ static_cast<Vp8SegmentationHeader::SegmentFeatureMode>(mode);
+
+ for (size_t i = 0; i < kMaxMBSegments; ++i) {
+ bool quantizer_update;
+ BD_READ_BOOL_OR_RETURN(&quantizer_update);
+ if (quantizer_update)
+ BD_READ_SIGNED_OR_RETURN(7, &shdr->quantizer_update_value[i]);
+ else
+ shdr->quantizer_update_value[i] = 0;
+ }
+
+ for (size_t i = 0; i < kMaxMBSegments; ++i) {
+ bool loop_filter_update;
+ BD_READ_BOOL_OR_RETURN(&loop_filter_update);
+ if (loop_filter_update)
+ BD_READ_SIGNED_OR_RETURN(6, &shdr->lf_update_value[i]);
+ else
+ shdr->lf_update_value[i] = 0;
+ }
+ }
+
+ if (shdr->update_mb_segmentation_map) {
+ for (size_t i = 0; i < kNumMBFeatureTreeProbs; ++i) {
+ bool segment_prob_update;
+ BD_READ_BOOL_OR_RETURN(&segment_prob_update);
+ if (segment_prob_update)
+ BD_READ_UNSIGNED_OR_RETURN(8, &shdr->segment_prob[i]);
+ else
+ shdr->segment_prob[i] = Vp8SegmentationHeader::kDefaultSegmentProb;
+ }
+ }
+
+ return true;
+}
+
+bool Vp8Parser::ParseLoopFilterHeader(bool keyframe) {
+ Vp8LoopFilterHeader* lfhdr = &curr_loopfilter_hdr_;
+
+ if (keyframe)
+ memset(lfhdr, 0, sizeof(*lfhdr));
+
+ int type;
+ BD_READ_UNSIGNED_OR_RETURN(1, &type);
+ lfhdr->type = static_cast<Vp8LoopFilterHeader::Type>(type);
+ BD_READ_UNSIGNED_OR_RETURN(6, &lfhdr->level);
+ BD_READ_UNSIGNED_OR_RETURN(3, &lfhdr->sharpness_level);
+ BD_READ_BOOL_OR_RETURN(&lfhdr->loop_filter_adj_enable);
+
+ if (lfhdr->loop_filter_adj_enable) {
+ BD_READ_BOOL_OR_RETURN(&lfhdr->mode_ref_lf_delta_update);
+ if (lfhdr->mode_ref_lf_delta_update) {
+ for (size_t i = 0; i < kNumBlockContexts; ++i) {
+ bool ref_frame_delta_update_flag;
+ BD_READ_BOOL_OR_RETURN(&ref_frame_delta_update_flag);
+ if (ref_frame_delta_update_flag)
+ BD_READ_SIGNED_OR_RETURN(6, &lfhdr->ref_frame_delta[i]);
+ }
+
+ for (size_t i = 0; i < kNumBlockContexts; ++i) {
+ bool mb_mode_delta_update_flag;
+ BD_READ_BOOL_OR_RETURN(&mb_mode_delta_update_flag);
+ if (mb_mode_delta_update_flag)
+ BD_READ_SIGNED_OR_RETURN(6, &lfhdr->mb_mode_delta[i]);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool Vp8Parser::ParseQuantizationHeader(Vp8QuantizationHeader* qhdr) {
+ // If any of the delta values is not present, the delta should be zero.
+ memset(qhdr, 0, sizeof(*qhdr));
+
+ BD_READ_UNSIGNED_OR_RETURN(7, &qhdr->y_ac_qi);
+
+ bool delta_present;
+
+ BD_READ_BOOL_OR_RETURN(&delta_present);
+ if (delta_present)
+ BD_READ_SIGNED_OR_RETURN(4, &qhdr->y_dc_delta);
+
+ BD_READ_BOOL_OR_RETURN(&delta_present);
+ if (delta_present)
+ BD_READ_SIGNED_OR_RETURN(4, &qhdr->y2_dc_delta);
+
+ BD_READ_BOOL_OR_RETURN(&delta_present);
+ if (delta_present)
+ BD_READ_SIGNED_OR_RETURN(4, &qhdr->y2_ac_delta);
+
+ BD_READ_BOOL_OR_RETURN(&delta_present);
+ if (delta_present)
+ BD_READ_SIGNED_OR_RETURN(4, &qhdr->uv_dc_delta);
+
+ BD_READ_BOOL_OR_RETURN(&delta_present);
+ if (delta_present)
+ BD_READ_SIGNED_OR_RETURN(4, &qhdr->uv_ac_delta);
+
+ return true;
+}
+
+// See spec for details on these values.
+const uint8_t kCoeffUpdateProbs[kNumBlockTypes][kNumCoeffBands]
+ [kNumPrevCoeffContexts][kNumEntropyNodes] = {
+ {
+ {
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255},
+ {249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255},
+ {234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255},
+ {250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255},
+ {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ },
+ {
+ {
+ {217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255},
+ {234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255},
+ },
+ {
+ {255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+ {250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ },
+ {
+ {
+ {186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255},
+ {234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255},
+ {251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255},
+ },
+ {
+ {255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ },
+ {
+ {
+ {248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255},
+ {248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+ {246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+ {252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255},
+ {248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+ {253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+ {252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+ {250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ {
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ },
+};
+
+const uint8_t kKeyframeYModeProbs[kNumYModeProbs] = {145, 156, 163, 128};
+const uint8_t kKeyframeUVModeProbs[kNumUVModeProbs] = {142, 114, 183};
+
+const uint8_t kDefaultYModeProbs[kNumYModeProbs] = {112, 86, 140, 37};
+const uint8_t kDefaultUVModeProbs[kNumUVModeProbs] = {162, 101, 204};
+
+const uint8_t kDefaultCoeffProbs[kNumBlockTypes][kNumCoeffBands]
+ [kNumPrevCoeffContexts][kNumEntropyNodes] = {
+ {
+ {
+ {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+ {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+ {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+ },
+ {
+ {253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128},
+ {189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128},
+ {106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128},
+ },
+ {
+ { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128},
+ {181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128},
+ { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128},
+ },
+ {
+ { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128},
+ {184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128},
+ { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128},
+ },
+ {
+ { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128},
+ {170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128},
+ { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128},
+ },
+ {
+ { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128},
+ {207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128},
+ {102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128},
+ },
+ {
+ { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128},
+ {177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128},
+ { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128},
+ },
+ {
+ { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+ {246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+ {255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+ }
+ },
+ {
+ {
+ {198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62},
+ {131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1},
+ { 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128},
+ },
+ {
+ { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128},
+ {184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128},
+ { 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128},
+ },
+ {
+ { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128},
+ { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128},
+ { 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128},
+ },
+ {
+ { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128},
+ {109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128},
+ { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128},
+ },
+ {
+ { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128},
+ { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128},
+ { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128},
+ },
+ {
+ { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128},
+ {124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128},
+ { 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128},
+ },
+ {
+ { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128},
+ {121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128},
+ { 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128},
+ },
+ {
+ { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128},
+ {203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128},
+ {137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128},
+ }
+ },
+ {
+ {
+ {253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128},
+ {175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128},
+ { 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128},
+ },
+ {
+ { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128},
+ {239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128},
+ {155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128},
+ },
+ {
+ { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128},
+ {201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128},
+ { 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128},
+ },
+ {
+ { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128},
+ {223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128},
+ {141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128},
+ },
+ {
+ { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128},
+ {190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128},
+ {149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+ },
+ {
+ { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+ {247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+ {240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+ },
+ {
+ { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128},
+ {213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128},
+ { 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+ },
+ {
+ {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+ {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+ {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+ }
+ },
+ {
+ {
+ {202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255},
+ {126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128},
+ { 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128},
+ },
+ {
+ { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128},
+ {166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128},
+ { 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128},
+ },
+ {
+ { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128},
+ {124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128},
+ { 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128},
+ },
+ {
+ { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128},
+ {149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128},
+ { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128}
+ },
+ {
+ { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128},
+ {123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128},
+ { 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128},
+ },
+ {
+ { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128},
+ {168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128},
+ { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128},
+ },
+ {
+ { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128},
+ {141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128},
+ { 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128},
+ },
+ {
+ { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+ {244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+ {238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+ },
+ },
+};
+
+const uint8_t kMVUpdateProbs[kNumMVContexts][kNumMVProbs] =
+{
+ {
+ 237, 246, 253, 253, 254, 254, 254, 254, 254,
+ 254, 254, 254, 254, 254, 250, 250, 252, 254, 254,
+ },
+ {
+ 231, 243, 245, 253, 254, 254, 254, 254, 254,
+ 254, 254, 254, 254, 254, 251, 251, 254, 254, 254,
+ },
+};
+
+const uint8_t kDefaultMVProbs[kNumMVContexts][kNumMVProbs] = {
+ {
+ 162, 128, 225, 146, 172, 147, 214, 39, 156,
+ 128, 129, 132, 75, 145, 178, 206, 239, 254, 254,
+ },
+ {
+ 164, 128, 204, 170, 119, 235, 140, 230, 228,
+ 128, 130, 130, 74, 148, 180, 203, 236, 254, 254,
+ },
+};
+
+void Vp8Parser::ResetProbs() {
+ static_assert(
+ sizeof(curr_entropy_hdr_.coeff_probs) == sizeof(kDefaultCoeffProbs),
+ "coeff_probs_arrays_must_be_of_correct_size");
+ memcpy(curr_entropy_hdr_.coeff_probs, kDefaultCoeffProbs,
+ sizeof(curr_entropy_hdr_.coeff_probs));
+
+ static_assert(sizeof(curr_entropy_hdr_.mv_probs) == sizeof(kDefaultMVProbs),
+ "mv_probs_arrays_must_be_of_correct_size");
+ memcpy(curr_entropy_hdr_.mv_probs, kDefaultMVProbs,
+ sizeof(curr_entropy_hdr_.mv_probs));
+
+ static_assert(
+ sizeof(curr_entropy_hdr_.y_mode_probs) == sizeof(kDefaultYModeProbs),
+ "y_probs_arrays_must_be_of_correct_size");
+ memcpy(curr_entropy_hdr_.y_mode_probs, kDefaultYModeProbs,
+ sizeof(curr_entropy_hdr_.y_mode_probs));
+
+ static_assert(
+ sizeof(curr_entropy_hdr_.uv_mode_probs) == sizeof(kDefaultUVModeProbs),
+ "uv_probs_arrays_must_be_of_correct_size");
+ memcpy(curr_entropy_hdr_.uv_mode_probs, kDefaultUVModeProbs,
+ sizeof(curr_entropy_hdr_.uv_mode_probs));
+}
+
+bool Vp8Parser::ParseTokenProbs(Vp8EntropyHeader* ehdr,
+ bool update_curr_probs) {
+ for (size_t i = 0; i < kNumBlockTypes; ++i) {
+ for (size_t j = 0; j < kNumCoeffBands; ++j) {
+ for (size_t k = 0; k < kNumPrevCoeffContexts; ++k) {
+ for (size_t l = 0; l < kNumEntropyNodes; ++l) {
+ bool coeff_prob_update_flag;
+ BD_READ_BOOL_WITH_PROB_OR_RETURN(&coeff_prob_update_flag,
+ kCoeffUpdateProbs[i][j][k][l]);
+ if (coeff_prob_update_flag)
+ BD_READ_UNSIGNED_OR_RETURN(8, &ehdr->coeff_probs[i][j][k][l]);
+ }
+ }
+ }
+ }
+
+ if (update_curr_probs) {
+ memcpy(curr_entropy_hdr_.coeff_probs, ehdr->coeff_probs,
+ sizeof(curr_entropy_hdr_.coeff_probs));
+ }
+
+ return true;
+}
+
+bool Vp8Parser::ParseIntraProbs(Vp8EntropyHeader* ehdr,
+ bool update_curr_probs,
+ bool keyframe) {
+ if (keyframe) {
+ static_assert(
+ sizeof(ehdr->y_mode_probs) == sizeof(kKeyframeYModeProbs),
+ "y_probs_arrays_must_be_of_correct_size");
+ memcpy(ehdr->y_mode_probs, kKeyframeYModeProbs,
+ sizeof(ehdr->y_mode_probs));
+
+ static_assert(
+ sizeof(ehdr->uv_mode_probs) == sizeof(kKeyframeUVModeProbs),
+ "uv_probs_arrays_must_be_of_correct_size");
+ memcpy(ehdr->uv_mode_probs, kKeyframeUVModeProbs,
+ sizeof(ehdr->uv_mode_probs));
+ } else {
+ bool intra_16x16_prob_update_flag;
+ BD_READ_BOOL_OR_RETURN(&intra_16x16_prob_update_flag);
+ if (intra_16x16_prob_update_flag) {
+ for (size_t i = 0; i < kNumYModeProbs; ++i)
+ BD_READ_UNSIGNED_OR_RETURN(8, &ehdr->y_mode_probs[i]);
+
+ if (update_curr_probs) {
+ memcpy(curr_entropy_hdr_.y_mode_probs, ehdr->y_mode_probs,
+ sizeof(curr_entropy_hdr_.y_mode_probs));
+ }
+ }
+
+ bool intra_chroma_prob_update_flag;
+ BD_READ_BOOL_OR_RETURN(&intra_chroma_prob_update_flag);
+ if (intra_chroma_prob_update_flag) {
+ for (size_t i = 0; i < kNumUVModeProbs; ++i)
+ BD_READ_UNSIGNED_OR_RETURN(8, &ehdr->uv_mode_probs[i]);
+
+ if (update_curr_probs) {
+ memcpy(curr_entropy_hdr_.uv_mode_probs, ehdr->uv_mode_probs,
+ sizeof(curr_entropy_hdr_.uv_mode_probs));
+ }
+ }
+ }
+
+ return true;
+}
+
+bool Vp8Parser::ParseMVProbs(Vp8EntropyHeader* ehdr, bool update_curr_probs) {
+ for (size_t mv_ctx = 0; mv_ctx < kNumMVContexts; ++mv_ctx) {
+ for (size_t p = 0; p < kNumMVProbs; ++p) {
+ bool mv_prob_update_flag;
+ BD_READ_BOOL_WITH_PROB_OR_RETURN(&mv_prob_update_flag,
+ kMVUpdateProbs[mv_ctx][p]);
+ if (mv_prob_update_flag) {
+ uint8_t prob;
+ BD_READ_UNSIGNED_OR_RETURN(7, &prob);
+ ehdr->mv_probs[mv_ctx][p] = prob ? (prob << 1) : 1;
+ }
+ }
+ }
+
+ if (update_curr_probs) {
+ memcpy(curr_entropy_hdr_.mv_probs, ehdr->mv_probs,
+ sizeof(curr_entropy_hdr_.mv_probs));
+ }
+
+ return true;
+}
+
+bool Vp8Parser::ParsePartitions(Vp8FrameHeader* fhdr) {
+ CHECK_GE(fhdr->num_of_dct_partitions, 1u);
+ CHECK_LE(fhdr->num_of_dct_partitions, kMaxDCTPartitions);
+
+ // DCT partitions start after the first partition and partition size values
+ // that follow it. There are num_of_dct_partitions - 1 sizes stored in the
+ // stream after the first partition, each 3 bytes long. The size of last
+ // DCT partition is not stored in the stream, but is instead calculated by
+ // taking the remainder of the frame size after the penultimate DCT partition.
+ size_t first_dct_pos = fhdr->first_part_offset + fhdr->first_part_size +
+ (fhdr->num_of_dct_partitions - 1) * 3;
+
+ // Make sure we have enough data for the first partition and partition sizes.
+ if (fhdr->frame_size < first_dct_pos)
+ return false;
+
+ // Total size of all DCT partitions.
+ size_t bytes_left = fhdr->frame_size - first_dct_pos;
+
+ // Position ourselves at the beginning of partition size values.
+ const uint8_t* ptr =
+ fhdr->data + fhdr->first_part_offset + fhdr->first_part_size;
+
+ // Read sizes from the stream (if present).
+ for (size_t i = 0; i < fhdr->num_of_dct_partitions - 1; ++i) {
+ fhdr->dct_partition_sizes[i] = (ptr[2] << 16) | (ptr[1] << 8) | ptr[0];
+
+ // Make sure we have enough data in the stream for ith partition and
+ // subtract its size from total.
+ if (bytes_left < fhdr->dct_partition_sizes[i])
+ return false;
+
+ bytes_left -= fhdr->dct_partition_sizes[i];
+
+ // Move to the position of the next partition size value.
+ ptr += 3;
+ }
+
+ // The remainder of the data belongs to the last DCT partition.
+ fhdr->dct_partition_sizes[fhdr->num_of_dct_partitions - 1] = bytes_left;
+
+ DVLOG(4) << "Control part size: " << fhdr->first_part_size;
+ for (size_t i = 0; i < fhdr->num_of_dct_partitions; ++i)
+ DVLOG(4) << "DCT part " << i << " size: " << fhdr->dct_partition_sizes[i];
+
+ return true;
+}
+
+} // namespace media
diff --git a/chromium/media/parsers/vp8_parser.h b/chromium/media/parsers/vp8_parser.h
new file mode 100644
index 00000000000..11585bc04e1
--- /dev/null
+++ b/chromium/media/parsers/vp8_parser.h
@@ -0,0 +1,208 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains an implementation of a VP8 raw stream parser,
+// as defined in RFC 6386.
+
+#ifndef MEDIA_PARSERS_VP8_PARSER_H_
+#define MEDIA_PARSERS_VP8_PARSER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "media/parsers/media_parsers_export.h"
+#include "media/parsers/vp8_bool_decoder.h"
+
+namespace media {
+
+// See spec for definitions of values/fields.
+const size_t kMaxMBSegments = 4;
+const size_t kNumMBFeatureTreeProbs = 3;
+
+// Member of Vp8FrameHeader and will be 0-initialized
+// in Vp8FrameHeader's constructor.
+struct Vp8SegmentationHeader {
+ enum SegmentFeatureMode { FEATURE_MODE_DELTA = 0, FEATURE_MODE_ABSOLUTE = 1 };
+
+ bool segmentation_enabled;
+ bool update_mb_segmentation_map;
+ bool update_segment_feature_data;
+ SegmentFeatureMode segment_feature_mode;
+
+ int8_t quantizer_update_value[kMaxMBSegments];
+ int8_t lf_update_value[kMaxMBSegments];
+ static const int kDefaultSegmentProb = 255;
+ uint8_t segment_prob[kNumMBFeatureTreeProbs];
+};
+
+const size_t kNumBlockContexts = 4;
+
+// Member of Vp8FrameHeader and will be 0-initialized
+// in Vp8FrameHeader's constructor.
+struct Vp8LoopFilterHeader {
+ enum Type { LOOP_FILTER_TYPE_NORMAL = 0, LOOP_FILTER_TYPE_SIMPLE = 1 };
+ Type type;
+ uint8_t level;
+ uint8_t sharpness_level;
+ bool loop_filter_adj_enable;
+ bool mode_ref_lf_delta_update;
+
+ int8_t ref_frame_delta[kNumBlockContexts];
+ int8_t mb_mode_delta[kNumBlockContexts];
+};
+
+// Member of Vp8FrameHeader and will be 0-initialized
+// in Vp8FrameHeader's constructor.
+struct Vp8QuantizationHeader {
+ uint8_t y_ac_qi;
+ int8_t y_dc_delta;
+ int8_t y2_dc_delta;
+ int8_t y2_ac_delta;
+ int8_t uv_dc_delta;
+ int8_t uv_ac_delta;
+};
+
+const size_t kNumBlockTypes = 4;
+const size_t kNumCoeffBands = 8;
+const size_t kNumPrevCoeffContexts = 3;
+const size_t kNumEntropyNodes = 11;
+
+const size_t kNumMVContexts = 2;
+const size_t kNumMVProbs = 19;
+
+const size_t kNumYModeProbs = 4;
+const size_t kNumUVModeProbs = 3;
+
+// Member of Vp8FrameHeader and will be 0-initialized
+// in Vp8FrameHeader's constructor.
+struct Vp8EntropyHeader {
+ uint8_t coeff_probs[kNumBlockTypes][kNumCoeffBands][kNumPrevCoeffContexts]
+ [kNumEntropyNodes];
+
+ uint8_t y_mode_probs[kNumYModeProbs];
+ uint8_t uv_mode_probs[kNumUVModeProbs];
+
+ uint8_t mv_probs[kNumMVContexts][kNumMVProbs];
+};
+
+const size_t kMaxDCTPartitions = 8;
+const size_t kNumVp8ReferenceBuffers = 3;
+
+enum Vp8RefType : size_t {
+ VP8_FRAME_LAST = 0,
+ VP8_FRAME_GOLDEN = 1,
+ VP8_FRAME_ALTREF = 2,
+};
+
+struct MEDIA_PARSERS_EXPORT Vp8FrameHeader {
+ Vp8FrameHeader();
+
+ enum FrameType { KEYFRAME = 0, INTERFRAME = 1 };
+ bool IsKeyframe() const { return frame_type == KEYFRAME; }
+
+ enum GoldenRefreshMode {
+ NO_GOLDEN_REFRESH = 0,
+ COPY_LAST_TO_GOLDEN = 1,
+ COPY_ALT_TO_GOLDEN = 2,
+ };
+
+ enum AltRefreshMode {
+ NO_ALT_REFRESH = 0,
+ COPY_LAST_TO_ALT = 1,
+ COPY_GOLDEN_TO_ALT = 2,
+ };
+
+ FrameType frame_type;
+ uint8_t version;
+ bool is_experimental;
+ bool show_frame;
+ size_t first_part_size;
+
+ uint16_t width;
+ uint8_t horizontal_scale;
+ uint16_t height;
+ uint8_t vertical_scale;
+
+ Vp8SegmentationHeader segmentation_hdr;
+ Vp8LoopFilterHeader loopfilter_hdr;
+ Vp8QuantizationHeader quantization_hdr;
+
+ size_t num_of_dct_partitions;
+
+ Vp8EntropyHeader entropy_hdr;
+
+ bool refresh_entropy_probs;
+ bool refresh_golden_frame;
+ bool refresh_alternate_frame;
+ GoldenRefreshMode copy_buffer_to_golden;
+ AltRefreshMode copy_buffer_to_alternate;
+ uint8_t sign_bias_golden;
+ uint8_t sign_bias_alternate;
+ bool refresh_last;
+
+ bool mb_no_skip_coeff;
+ uint8_t prob_skip_false;
+ uint8_t prob_intra;
+ uint8_t prob_last;
+ uint8_t prob_gf;
+
+ const uint8_t* data;
+ size_t frame_size;
+
+ size_t dct_partition_sizes[kMaxDCTPartitions];
+ // Offset in bytes from data.
+ off_t first_part_offset;
+ // Offset in bits from first_part_offset.
+ off_t macroblock_bit_offset;
+
+ // Bool decoder state
+ uint8_t bool_dec_range;
+ uint8_t bool_dec_value;
+ uint8_t bool_dec_count;
+};
+
+// A parser for raw VP8 streams as specified in RFC 6386.
+class MEDIA_PARSERS_EXPORT Vp8Parser {
+ public:
+ Vp8Parser();
+ ~Vp8Parser();
+
+ // Try to parse exactly one VP8 frame starting at |ptr| and of size |size|,
+ // filling the parsed data in |fhdr|. Return true on success.
+ // Size has to be exactly the size of the frame and coming from the caller,
+ // who needs to acquire it from elsewhere (normally from a container).
+ bool ParseFrame(const uint8_t* ptr, size_t size, Vp8FrameHeader* fhdr);
+
+ private:
+ bool ParseFrameTag(Vp8FrameHeader* fhdr);
+ bool ParseFrameHeader(Vp8FrameHeader* fhdr);
+
+ bool ParseSegmentationHeader(bool keyframe);
+ bool ParseLoopFilterHeader(bool keyframe);
+ bool ParseQuantizationHeader(Vp8QuantizationHeader* qhdr);
+ bool ParseTokenProbs(Vp8EntropyHeader* ehdr, bool update_curr_probs);
+ bool ParseIntraProbs(Vp8EntropyHeader* ehdr,
+ bool update_curr_probs,
+ bool keyframe);
+ bool ParseMVProbs(Vp8EntropyHeader* ehdr, bool update_curr_probs);
+ bool ParsePartitions(Vp8FrameHeader* fhdr);
+ void ResetProbs();
+
+ // These persist across calls to ParseFrame() and may be used and/or updated
+ // for subsequent frames if the stream instructs us to do so.
+ Vp8SegmentationHeader curr_segmentation_hdr_;
+ Vp8LoopFilterHeader curr_loopfilter_hdr_;
+ Vp8EntropyHeader curr_entropy_hdr_;
+
+ const uint8_t* stream_;
+ size_t bytes_left_;
+ Vp8BoolDecoder bd_;
+
+ DISALLOW_COPY_AND_ASSIGN(Vp8Parser);
+};
+
+} // namespace media
+
+#endif // MEDIA_PARSERS_VP8_PARSER_H_
diff --git a/chromium/media/parsers/vp8_parser_fuzzertest.cc b/chromium/media/parsers/vp8_parser_fuzzertest.cc
new file mode 100644
index 00000000000..75cc6bb317b
--- /dev/null
+++ b/chromium/media/parsers/vp8_parser_fuzzertest.cc
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/numerics/safe_conversions.h"
+#include "media/filters/ivf_parser.h"
+#include "media/parsers/vp8_parser.h"
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ const uint8_t* ivf_payload = nullptr;
+ media::IvfParser ivf_parser;
+ media::IvfFileHeader ivf_file_header;
+ media::IvfFrameHeader ivf_frame_header;
+
+ if (!ivf_parser.Initialize(data, size, &ivf_file_header))
+ return 0;
+
+ // Parse until the end of stream/unsupported stream/error in stream is found.
+ while (ivf_parser.ParseNextFrame(&ivf_frame_header, &ivf_payload)) {
+ media::Vp8Parser vp8_parser;
+ media::Vp8FrameHeader vp8_frame_header;
+ vp8_parser.ParseFrame(ivf_payload, ivf_frame_header.frame_size,
+ &vp8_frame_header);
+ }
+
+ return 0;
+}
diff --git a/chromium/media/parsers/vp8_parser_unittest.cc b/chromium/media/parsers/vp8_parser_unittest.cc
new file mode 100644
index 00000000000..cbe4ff07b5f
--- /dev/null
+++ b/chromium/media/parsers/vp8_parser_unittest.cc
@@ -0,0 +1,50 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/files/memory_mapped_file.h"
+#include "base/logging.h"
+#include "media/base/test_data_util.h"
+#include "media/filters/ivf_parser.h"
+#include "media/parsers/vp8_parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+TEST(Vp8ParserTest, StreamFileParsing) {
+ base::FilePath file_path = GetTestDataFilePath("test-25fps.vp8");
+ base::MemoryMappedFile stream;
+ ASSERT_TRUE(stream.Initialize(file_path))
+ << "Couldn't open stream file: " << file_path.MaybeAsASCII();
+
+ IvfParser ivf_parser;
+ IvfFileHeader ivf_file_header = {};
+ ASSERT_TRUE(
+ ivf_parser.Initialize(stream.data(), stream.length(), &ivf_file_header));
+ ASSERT_EQ(ivf_file_header.fourcc, 0x30385056u); // VP80
+
+ Vp8Parser vp8_parser;
+ IvfFrameHeader ivf_frame_header = {};
+ size_t num_parsed_frames = 0;
+
+ // Parse until the end of stream/unsupported stream/error in stream is found.
+ const uint8_t* payload = nullptr;
+ while (ivf_parser.ParseNextFrame(&ivf_frame_header, &payload)) {
+ Vp8FrameHeader fhdr;
+
+ ASSERT_TRUE(
+ vp8_parser.ParseFrame(payload, ivf_frame_header.frame_size, &fhdr));
+
+ ++num_parsed_frames;
+ }
+
+ DVLOG(1) << "Number of successfully parsed frames before EOS: "
+ << num_parsed_frames;
+
+ EXPECT_EQ(ivf_file_header.num_frames, num_parsed_frames);
+}
+
+} // namespace media
diff --git a/chromium/media/parsers/webp_parser.cc b/chromium/media/parsers/webp_parser.cc
new file mode 100644
index 00000000000..b0348037e79
--- /dev/null
+++ b/chromium/media/parsers/webp_parser.cc
@@ -0,0 +1,131 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/parsers/webp_parser.h"
+
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "base/bits.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "build/build_config.h"
+#include "media/parsers/vp8_parser.h"
+
+#if !defined(ARCH_CPU_LITTLE_ENDIAN)
+#error Big-Endian architecture not supported.
+#endif
+
+namespace media {
+
+namespace {
+
+// The byte position storing the size of the file.
+constexpr size_t kFileSizeBytePosition = 4u;
+
+// The byte position in which the WebP image data begins.
+constexpr size_t kWebPFileBeginBytePosition = 8u;
+
+// The byte position storing the size of the VP8 frame.
+constexpr size_t kVp8FrameSizePosition = 16u;
+
+// The 12 bytes that include the FourCC "WEBPVP8 " plus the VP8 chunk size info.
+constexpr size_t kWebPFileHeaderByteSize = 12u;
+
+// A valid WebP image header and VP8 chunk header require 20 bytes.
+// The VP8 Key Frame's payload also begins at byte 20.
+constexpr size_t kWebPFileAndVp8ChunkHeaderSizeInBytes = 20u;
+
+// The max WebP file size is (2^32 - 10) per the WebP spec:
+// https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
+constexpr uint32_t kMaxWebPFileSize = (1ull << 32) - 10u;
+
+constexpr size_t kSizeOfUint32t = sizeof(uint32_t);
+
+} // namespace
+
+bool IsLossyWebPImage(base::span<const uint8_t> encoded_data) {
+ if (encoded_data.size() < kWebPFileAndVp8ChunkHeaderSizeInBytes)
+ return false;
+
+ DCHECK(encoded_data.data());
+
+ return !memcmp(encoded_data.data(), "RIFF", 4) &&
+ !memcmp(encoded_data.data() + kWebPFileBeginBytePosition, "WEBPVP8 ",
+ 8);
+}
+
+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.");
+ static_assert(kSizeOfUint32t == 4u, "Size of uint32_t is not 4 bytes.");
+
+ // Try to acquire the WebP file size. IsLossyWebPImage() has ensured
+ // that we have enough data to read the file size.
+ DCHECK_GE(encoded_data.size(), kFileSizeBytePosition + kSizeOfUint32t);
+
+ // No need to worry about endianness because we assert little-endianness.
+ const uint32_t file_size = *reinterpret_cast<const uint32_t*>(
+ encoded_data.data() + kFileSizeBytePosition);
+
+ // Check that |file_size| is even, per the WebP spec:
+ // https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
+ if (file_size % 2 != 0)
+ return nullptr;
+
+ // Check that |file_size| <= 2^32 - 10, per the WebP spec:
+ // https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
+ if (file_size > kMaxWebPFileSize)
+ return nullptr;
+
+ // Check that the file size in the header matches the encoded data's size.
+ if (base::strict_cast<size_t>(file_size) !=
+ encoded_data.size() - kWebPFileBeginBytePosition) {
+ return nullptr;
+ }
+
+ // Try to acquire the VP8 key frame size and validate that it fits within the
+ // encoded data's size.
+ DCHECK_GE(encoded_data.size(), kVp8FrameSizePosition + kSizeOfUint32t);
+
+ const uint32_t vp8_frame_size = *reinterpret_cast<const uint32_t*>(
+ encoded_data.data() + kVp8FrameSizePosition);
+
+ // Check that the VP8 frame size is bounded by the WebP size.
+ if (base::strict_cast<size_t>(file_size) - kWebPFileHeaderByteSize <
+ base::strict_cast<size_t>(vp8_frame_size)) {
+ return nullptr;
+ }
+
+ // Check that the size of the encoded data is consistent.
+ const size_t vp8_padded_frame_size =
+ base::bits::Align(base::strict_cast<size_t>(vp8_frame_size), 2u);
+ if (encoded_data.size() - kWebPFileAndVp8ChunkHeaderSizeInBytes !=
+ vp8_padded_frame_size) {
+ return nullptr;
+ }
+
+ // Check that the last byte is 0 if |vp8_frame_size| is odd per WebP specs:
+ // https://developers.google.com/speed/webp/docs/riff_container#riff_file_format
+ if (vp8_frame_size % 2 &&
+ encoded_data.data()[encoded_data.size() - 1] != 0u) {
+ return nullptr;
+ }
+
+ // Attempt to parse the VP8 frame.
+ Vp8Parser vp8_parser;
+ auto result = std::make_unique<Vp8FrameHeader>();
+ if (vp8_parser.ParseFrame(
+ encoded_data.data() + kWebPFileAndVp8ChunkHeaderSizeInBytes,
+ base::strict_cast<size_t>(vp8_frame_size), result.get())) {
+ return result;
+ }
+ return nullptr;
+}
+
+} // namespace media
diff --git a/chromium/media/parsers/webp_parser.h b/chromium/media/parsers/webp_parser.h
new file mode 100644
index 00000000000..436d6c74210
--- /dev/null
+++ b/chromium/media/parsers/webp_parser.h
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_PARSERS_WEBP_PARSER_H_
+#define MEDIA_PARSERS_WEBP_PARSER_H_
+
+#include <stdint.h>
+#include <memory>
+
+#include "base/containers/span.h"
+#include "media/parsers/media_parsers_export.h"
+
+namespace media {
+
+struct Vp8FrameHeader;
+
+// A lightweight WebP file header parser to extract feature and size
+// information. It validates that a given data stream encodes a simple lossy
+// WebP image and populates a Vp8FrameHeader upon successful parsing.
+// For more information, see the WebP Container Specification:
+// https://developers.google.com/speed/webp/docs/riff_container
+
+// Returns true if |encoded_data| claims to encode a simple (non-extended) lossy
+// WebP image. Returns false otherwise.
+MEDIA_PARSERS_EXPORT
+bool IsLossyWebPImage(base::span<const uint8_t> encoded_data);
+
+// Parses a simple (non-extended) lossy WebP image and returns a Vp8FrameHeader
+// containing the parsed VP8 frame contained by the image. Returns nullptr on
+// failure.
+MEDIA_PARSERS_EXPORT
+std::unique_ptr<Vp8FrameHeader> ParseWebPImage(
+ base::span<const uint8_t> encoded_data);
+
+} // namespace media
+
+#endif // MEDIA_PARSERS_WEBP_PARSER_H_
diff --git a/chromium/media/parsers/webp_parser_fuzzertest.cc b/chromium/media/parsers/webp_parser_fuzzertest.cc
new file mode 100644
index 00000000000..d10c830c0af
--- /dev/null
+++ b/chromium/media/parsers/webp_parser_fuzzertest.cc
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/containers/span.h"
+#include "base/logging.h"
+#include "media/parsers/vp8_parser.h"
+#include "media/parsers/webp_parser.h"
+
+struct Environment {
+ Environment() { logging::SetMinLogLevel(logging::LOG_FATAL); }
+};
+
+Environment* env = new Environment();
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ base::span<const uint8_t> encoded_data(data, size);
+ media::ParseWebPImage(encoded_data);
+ return 0;
+}
diff --git a/chromium/media/parsers/webp_parser_unittest.cc b/chromium/media/parsers/webp_parser_unittest.cc
new file mode 100644
index 00000000000..4c9d99765b1
--- /dev/null
+++ b/chromium/media/parsers/webp_parser_unittest.cc
@@ -0,0 +1,327 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/base_paths.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 "media/parsers/webp_parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+
+constexpr size_t kWebPFileAndVp8ChunkHeaderSizeInBytes = 20u;
+// clang-format off
+constexpr uint8_t kLossyWebPFileHeader[] = {
+ 'R', 'I', 'F', 'F',
+ 0x0c, 0x00, 0x00, 0x00, // == 12 (little endian)
+ 'W', 'E', 'B', 'P',
+ 'V', 'P', '8', ' ',
+ 0x00, 0x00, 0x00, 0x00 // == 0
+};
+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, // == 13 (Invalid: should be even)
+ 'W', 'E', 'B', 'P',
+ 'V', 'P', '8', ' ',
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00
+};
+constexpr base::span<const uint8_t> kInvalidOddFileSizeInHeaderWebPEncodedData(
+ kInvalidOddFileSizeInWebPFileHeader,
+ kWebPFileAndVp8ChunkHeaderSizeInBytes + 1u); // Match the reported size
+
+constexpr uint8_t kInvalidLargerThanLimitFileSizeInWebPFileHeader[] = {
+ 'R', 'I', 'F', 'F',
+ 0xfe, 0xff, 0xff, 0xff, // == 2^32 - 2 (Invalid: should be <= 2^32 - 10)
+ '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, // == 16 (Invalid: should be 12)
+ '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, // == 12
+ 'W', 'E', 'B', 'P',
+ 'V', 'P', '8', ' ',
+ 0xc8, 0x00, 0x00, 0x00 // == 200 (Invalid: should be 0)
+};
+constexpr base::span<const uint8_t> kInvalidKeyFrameSizeInWebPEncodedData(
+ kInvalidKeyFrameSizeInWebPFileHeader,
+ kWebPFileAndVp8ChunkHeaderSizeInBytes);
+
+constexpr uint8_t kMismatchingOddVp8FrameSizeAndDataSize[] = {
+ 'R', 'I', 'F', 'F',
+ 0x12, 0x00, 0x00, 0x00, // == 18
+ 'W', 'E', 'B', 'P',
+ 'V', 'P', '8', ' ',
+ 0x03, 0x00, 0x00, 0x00, // == 3
+ 0x11, 0xa0, 0x23, 0x00, // Valid padding byte
+ 0xfa, 0xcc // Should not exist.
+};
+constexpr base::span<const uint8_t>
+kMismatchingOddVp8FrameSizeAndDataSizeEncodedData(
+ kMismatchingOddVp8FrameSizeAndDataSize,
+ kWebPFileAndVp8ChunkHeaderSizeInBytes + 6u);
+
+constexpr uint8_t kMismatchingEvenVp8FrameSizeAndDataSize[] = {
+ 'R', 'I', 'F', 'F',
+ 0x12, 0x00, 0x00, 0x00, // == 18
+ 'W', 'E', 'B', 'P',
+ 'V', 'P', '8', ' ',
+ 0x04, 0x00, 0x00, 0x00, // == 4
+ 0x11, 0xa0, 0x23, 0x12,
+ 0xfc, 0xcd // Should not exist.
+};
+constexpr base::span<const uint8_t>
+kMismatchingEvenVp8FrameSizeAndDataSizeEncodedData(
+ kMismatchingEvenVp8FrameSizeAndDataSize,
+ kWebPFileAndVp8ChunkHeaderSizeInBytes + 6u);
+
+constexpr uint8_t kInvalidPaddingByteInVp8DataChunk[] = {
+ 'R', 'I', 'F', 'F',
+ 0x10, 0x00, 0x00, 0x00, // == 16
+ 'W', 'E', 'B', 'P',
+ 'V', 'P', '8', ' ',
+ 0x03, 0x00, 0x00, 0x00, // == 3
+ 0x11, 0xa0, 0x23, 0xff // Invalid: last byte should be 0
+};
+constexpr base::span<const uint8_t>
+kInvalidPaddingByteInVp8DataChunkEncodedData(
+ kInvalidPaddingByteInVp8DataChunk,
+ kWebPFileAndVp8ChunkHeaderSizeInBytes + 4u);
+// clang-format on
+
+} // namespace
+
+TEST(WebPParserTest, WebPImageFileValidator) {
+ // Verify that only lossy WebP formats pass.
+ ASSERT_TRUE(IsLossyWebPImage(kLossyWebPEncodedData));
+
+ // Verify that lossless, extended, and unknown WebP formats fail.
+ ASSERT_FALSE(IsLossyWebPImage(kLosslessWebPEncodedData));
+ ASSERT_FALSE(IsLossyWebPImage(kExtendedWebPEncodedData));
+ ASSERT_FALSE(IsLossyWebPImage(kUnknownWebPEncodedData));
+
+ // Verify that invalid WebP file headers and sizes fail.
+ ASSERT_FALSE(IsLossyWebPImage(kInvalidRiffWebPEncodedData));
+ ASSERT_FALSE(IsLossyWebPImage(kInvalidWebPEncodedDataSize));
+}
+
+TEST(WebPParserTest, ParseLossyWebP) {
+ base::FilePath data_dir;
+ ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_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(base::span<const uint8_t>(stream.data(), stream.length()));
+ ASSERT_TRUE(result);
+
+ ASSERT_TRUE(result->IsKeyframe());
+ ASSERT_TRUE(result->data);
+
+ // Original image is 3000x3000.
+ ASSERT_EQ(3000u, result->width);
+ ASSERT_EQ(3000u, result->height);
+}
+
+TEST(WebPParserTest, ParseLosslessWebP) {
+ base::FilePath data_dir;
+ ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_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();
+
+ // Should fail because WebP parser does not parse lossless webp images.
+ std::unique_ptr<Vp8FrameHeader> result =
+ ParseWebPImage(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_SOURCE_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();
+
+ // Should fail because WebP parser does not parse extended webp images.
+ std::unique_ptr<Vp8FrameHeader> result =
+ ParseWebPImage(base::span<const uint8_t>(stream.data(), stream.length()));
+ ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithUnknownFormat) {
+ // Should fail when the specifier byte at position 16 holds anything but ' '.
+ std::unique_ptr<Vp8FrameHeader> result =
+ ParseWebPImage(kUnknownWebPEncodedData);
+ ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithInvalidHeaders) {
+ // Should fail because the header is an invalid WebP container.
+ std::unique_ptr<Vp8FrameHeader> result =
+ ParseWebPImage(kInvalidRiffWebPEncodedData);
+ ASSERT_FALSE(result);
+
+ // Should fail because the header has an invalid size.
+ result = ParseWebPImage(kInvalidWebPEncodedDataSize);
+ ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithInvalidOddSizeInHeader) {
+ // Should fail because the size reported in the header is odd.
+ std::unique_ptr<Vp8FrameHeader> result =
+ ParseWebPImage(kInvalidOddFileSizeInHeaderWebPEncodedData);
+ ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithInvalidLargerThanLimitSizeInHeader) {
+ // Should fail because the size reported in the header is larger than
+ // 2^32 - 10 per the WebP spec.
+ std::unique_ptr<Vp8FrameHeader> result =
+ ParseWebPImage(kInvalidLargerThanLimitFileSizeInHeaderWebPEncodedData);
+ ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithInvalidFileSizeInHeader) {
+ // Should fail because the size reported in the header does not match the
+ // actual data size.
+ std::unique_ptr<Vp8FrameHeader> result =
+ ParseWebPImage(kInvalidLargerFileSizeInHeaderWebPEncodedData);
+ ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithEmptyVp8KeyFrameAndIncorrectKeyFrameSize) {
+ // Should fail because the reported VP8 key frame size is larger than the
+ // the existing data.
+ std::unique_ptr<Vp8FrameHeader> result =
+ ParseWebPImage(kInvalidKeyFrameSizeInWebPEncodedData);
+ ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithMismatchingVp8FrameAndDataSize) {
+ // Should fail because the reported VP8 key frame size (even or odd) does not
+ // match the encoded data's size.
+ std::unique_ptr<Vp8FrameHeader> result =
+ ParseWebPImage(kMismatchingOddVp8FrameSizeAndDataSizeEncodedData);
+ ASSERT_FALSE(result);
+
+ result = ParseWebPImage(kMismatchingEvenVp8FrameSizeAndDataSizeEncodedData);
+ ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithInvalidPaddingByteInVp8DataChunk) {
+ // Should fail because the reported VP8 key frame size is odd and the added
+ // padding byte is not 0.
+ std::unique_ptr<Vp8FrameHeader> result =
+ ParseWebPImage(kInvalidPaddingByteInVp8DataChunkEncodedData);
+ ASSERT_FALSE(result);
+}
+
+TEST(WebPParserTest, ParseWebPWithEmptyVp8KeyFrame) {
+ // Should fail because the VP8 parser is passed a data chunk of size 0.
+ std::unique_ptr<Vp8FrameHeader> result =
+ ParseWebPImage(kLossyWebPEncodedData);
+ ASSERT_FALSE(result);
+}
+
+} // namespace media