Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1097)

Unified Diff: media/filters/jpeg_parser.cc

Issue 748023002: Add JPEG parser for JPEG decode acceleration (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/filters/jpeg_parser.h ('k') | media/filters/jpeg_parser_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/filters/jpeg_parser.cc
diff --git a/media/filters/jpeg_parser.cc b/media/filters/jpeg_parser.cc
new file mode 100644
index 0000000000000000000000000000000000000000..66a3651210dc1add056c0b11dd71aea572b0f3ea
--- /dev/null
+++ b/media/filters/jpeg_parser.cc
@@ -0,0 +1,373 @@
+// 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/filters/jpeg_parser.h"
+
+#include "base/big_endian.h"
+#include "base/logging.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 {
+
+namespace {
+enum JpegMarker {
+ SOF0 = 0xC0, // start of frame (baseline)
+ DHT = 0xC4, // define huffman table
+ SOI = 0xD8, // start of image
+ SOS = 0xDA, // start of scan
+ DQT = 0xDB, // define quantization table
+ DRI = 0xDD, // define restart internal
+ MARKER1 = 0xFF, // jpeg marker prefix
+};
+}
+
+static bool InRange(int value, int a, int b) {
+ return a <= value && value <= b;
+}
+
+// |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 (frame_header->num_components >= arraysize(frame_header->components)) {
+ DLOG(ERROR) << "num_components="
+ << static_cast<int>(frame_header->num_components)
+ << " is not supported";
+ return false;
+ }
+
+ 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 (!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);
+ }
+
+ 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 < arraysize(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;
+}
+
+// |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 != MARKER1)
+ return false;
+
+ do {
+ READ_U8_OR_RETURN_FALSE(&marker2);
+ } while (marker2 == MARKER1); // skip fill bytes
+
+ uint16_t size;
+ READ_U16_OR_RETURN_FALSE(&size);
+ if (reader.remaining() < size) {
+ DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" << reader.remaining()
+ << ") is smaller than header specified (" << size << ")";
+ return false;
+ }
+
+ // 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);
+
+ switch (marker2) {
+ case SOF0:
+ if (!ParseSOF(reader.ptr(), size, &result->frame_header)) {
+ DLOG(ERROR) << "ParseSOF failed";
+ return false;
+ }
+ break;
+ case DQT:
+ if (!ParseDQT(reader.ptr(), size, result->q_table)) {
+ DLOG(ERROR) << "ParseDQT failed";
+ return false;
+ }
+ has_marker_dqt = true;
+ break;
+ case DHT:
+ if (!ParseDHT(reader.ptr(), size, result->dc_table, result->ac_table)) {
+ DLOG(ERROR) << "ParseDHT failed";
+ return false;
+ }
+ break;
+ case DRI:
+ if (!ParseDRI(reader.ptr(), size, &result->restart_interval)) {
+ DLOG(ERROR) << "ParseDRI failed";
+ return false;
+ }
+ break;
+ case 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 != MARKER1 || marker2 != SOI) {
+ DLOG(ERROR) << "Not a JPEG";
+ return false;
+ }
+
+ return ParseSOI(reader.ptr(), reader.remaining(), result);
+}
+
+} // namespace media
« no previous file with comments | « media/filters/jpeg_parser.h ('k') | media/filters/jpeg_parser_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698