2020-10-05 22:39:59 +00:00
|
|
|
// Copyright 2020 Google LLC. All rights reserved.
|
|
|
|
//
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file or at
|
|
|
|
// https://developers.google.com/open-source/licenses/bsd
|
|
|
|
|
2023-12-01 17:32:19 +00:00
|
|
|
#include <packager/media/formats/dvb/dvb_sub_parser.h>
|
2020-10-05 22:39:59 +00:00
|
|
|
|
2021-03-23 19:04:07 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2023-12-01 17:32:19 +00:00
|
|
|
#include <absl/log/check.h>
|
|
|
|
#include <absl/log/log.h>
|
|
|
|
|
|
|
|
#include <packager/media/formats/mp2t/mp2t_common.h>
|
2020-10-05 22:39:59 +00:00
|
|
|
|
|
|
|
namespace shaka {
|
|
|
|
namespace media {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
RgbaColor ConvertYuv(uint8_t Y, uint8_t Cr, uint8_t Cb, uint8_t T) {
|
2021-03-23 19:04:07 +00:00
|
|
|
// Converts based on ITU-R BT.601.
|
2020-10-05 22:39:59 +00:00
|
|
|
// See https://en.wikipedia.org/wiki/YCbCr
|
2021-03-23 19:04:07 +00:00
|
|
|
//
|
|
|
|
// Note that the T value should be interpolated based on a full transparency
|
|
|
|
// being 256. This means that T=255 should not be fully transparent. Y=0 is
|
|
|
|
// used to signal full transparency.
|
|
|
|
// Values for Y<16 (except Y=0) are invalid, so clamp to 16.
|
2020-10-05 22:39:59 +00:00
|
|
|
RgbaColor color;
|
2021-03-23 19:04:07 +00:00
|
|
|
const double y_transform = 255.0 / 219 * (std::max<uint8_t>(Y, 16) - 16);
|
2020-10-05 22:39:59 +00:00
|
|
|
const double cb_transform = 255.0 / 244 * 1.772 * (Cb - 128);
|
|
|
|
const double cr_transform = 255.0 / 244 * 1.402 * (Cr - 128);
|
|
|
|
const double f1 = 0.114 / 0.587;
|
|
|
|
const double f2 = 0.299 / 0.587;
|
|
|
|
color.r = static_cast<uint8_t>(y_transform + cr_transform);
|
|
|
|
color.g =
|
|
|
|
static_cast<uint8_t>(y_transform - cb_transform * f1 - cr_transform * f2);
|
|
|
|
color.b = static_cast<uint8_t>(y_transform + cb_transform);
|
2021-03-23 19:04:07 +00:00
|
|
|
color.a = Y == 0 ? 0 : (T == 0 ? 255 : 256 - T);
|
2020-10-05 22:39:59 +00:00
|
|
|
return color;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
DvbSubParser::DvbSubParser() : last_pts_(0), timeout_(0) {}
|
|
|
|
|
|
|
|
DvbSubParser::~DvbSubParser() {}
|
|
|
|
|
|
|
|
bool DvbSubParser::Parse(DvbSubSegmentType segment_type,
|
|
|
|
int64_t pts,
|
|
|
|
const uint8_t* payload,
|
|
|
|
size_t size,
|
|
|
|
std::vector<std::shared_ptr<TextSample>>* samples) {
|
|
|
|
switch (segment_type) {
|
|
|
|
case DvbSubSegmentType::kPageComposition:
|
|
|
|
return ParsePageComposition(pts, payload, size, samples);
|
|
|
|
case DvbSubSegmentType::kRegionComposition:
|
|
|
|
return ParseRegionComposition(payload, size);
|
|
|
|
case DvbSubSegmentType::kClutDefinition:
|
|
|
|
return ParseClutDefinition(payload, size);
|
|
|
|
case DvbSubSegmentType::kObjectData:
|
|
|
|
return ParseObjectData(pts, payload, size);
|
|
|
|
case DvbSubSegmentType::kDisplayDefinition:
|
|
|
|
return ParseDisplayDefinition(payload, size);
|
|
|
|
case DvbSubSegmentType::kEndOfDisplay:
|
|
|
|
// This signals all the current objects are available. But we need to
|
|
|
|
// know the end time, so we do nothing for now.
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
LOG(WARNING) << "Unknown DVB-sub segment_type=0x" << std::hex
|
|
|
|
<< static_cast<uint32_t>(segment_type);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DvbSubParser::Flush(std::vector<std::shared_ptr<TextSample>>* samples) {
|
|
|
|
RCHECK(composer_.GetSamples(last_pts_, last_pts_ + timeout_ * kMpeg2Timescale,
|
|
|
|
samples));
|
|
|
|
composer_.ClearObjects();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const DvbImageColorSpace* DvbSubParser::GetColorSpace(uint8_t clut_id) {
|
|
|
|
return composer_.GetColorSpace(clut_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
const DvbImageBuilder* DvbSubParser::GetImageForObject(uint16_t object_id) {
|
|
|
|
return composer_.GetObjectImage(object_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DvbSubParser::ParsePageComposition(
|
|
|
|
int64_t pts,
|
|
|
|
const uint8_t* data,
|
|
|
|
size_t size,
|
|
|
|
std::vector<std::shared_ptr<TextSample>>* samples) {
|
|
|
|
// See ETSI EN 300 743 Section 7.2.2.
|
|
|
|
BitReader reader(data, size);
|
|
|
|
|
|
|
|
uint8_t page_state;
|
|
|
|
RCHECK(reader.ReadBits(8, &timeout_));
|
|
|
|
RCHECK(reader.SkipBits(4)); // page_version_number
|
|
|
|
RCHECK(reader.ReadBits(2, &page_state));
|
|
|
|
RCHECK(reader.SkipBits(2)); // reserved
|
|
|
|
if (page_state == 0x1 || page_state == 0x2) {
|
|
|
|
// If this is a "acquisition point" or a "mode change", then this is a new
|
|
|
|
// page and we should clear the old data.
|
|
|
|
RCHECK(composer_.GetSamples(last_pts_, pts, samples));
|
|
|
|
composer_.ClearObjects();
|
|
|
|
last_pts_ = pts;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (reader.bits_available() > 0u) {
|
|
|
|
uint8_t region_id;
|
|
|
|
uint16_t x, y;
|
|
|
|
RCHECK(reader.ReadBits(8, ®ion_id));
|
|
|
|
RCHECK(reader.SkipBits(8)); // reserved
|
|
|
|
RCHECK(reader.ReadBits(16, &x));
|
|
|
|
RCHECK(reader.ReadBits(16, &y));
|
|
|
|
|
|
|
|
RCHECK(composer_.SetRegionPosition(region_id, x, y));
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DvbSubParser::ParseRegionComposition(const uint8_t* data, size_t size) {
|
|
|
|
// See ETSI EN 300 743 Section 7.2.3.
|
|
|
|
BitReader reader(data, size);
|
|
|
|
|
|
|
|
uint8_t region_id, clut_id;
|
|
|
|
uint16_t region_width, region_height;
|
|
|
|
bool region_fill_flag;
|
|
|
|
int background_pixel_code;
|
|
|
|
RCHECK(reader.ReadBits(8, ®ion_id));
|
|
|
|
RCHECK(reader.SkipBits(4)); // region_version_number
|
|
|
|
RCHECK(reader.ReadBits(1, ®ion_fill_flag));
|
|
|
|
RCHECK(reader.SkipBits(3)); // reserved
|
|
|
|
RCHECK(reader.ReadBits(16, ®ion_width));
|
|
|
|
RCHECK(reader.ReadBits(16, ®ion_height));
|
|
|
|
RCHECK(reader.SkipBits(3)); // region_level_of_compatibility
|
|
|
|
RCHECK(reader.SkipBits(3)); // region_depth
|
|
|
|
RCHECK(reader.SkipBits(2)); // reserved
|
|
|
|
RCHECK(reader.ReadBits(8, &clut_id));
|
|
|
|
RCHECK(reader.ReadBits(8, &background_pixel_code));
|
|
|
|
RCHECK(reader.SkipBits(4)); // region_4-bit_pixel_code
|
|
|
|
RCHECK(reader.SkipBits(2)); // region_2-bit_pixel_code
|
|
|
|
RCHECK(reader.SkipBits(2)); // reserved
|
|
|
|
RCHECK(
|
|
|
|
composer_.SetRegionInfo(region_id, clut_id, region_width, region_height));
|
|
|
|
if (!region_fill_flag)
|
|
|
|
background_pixel_code = -1;
|
|
|
|
|
|
|
|
while (reader.bits_available() > 0) {
|
|
|
|
uint16_t object_id, x, y;
|
|
|
|
uint8_t object_type;
|
|
|
|
RCHECK(reader.ReadBits(16, &object_id));
|
|
|
|
RCHECK(reader.ReadBits(2, &object_type));
|
|
|
|
RCHECK(reader.SkipBits(2)); // object_provider_flag
|
|
|
|
RCHECK(reader.ReadBits(12, &x));
|
|
|
|
RCHECK(reader.SkipBits(4)); // reserved
|
|
|
|
RCHECK(reader.ReadBits(12, &y));
|
|
|
|
|
|
|
|
if (object_type == 0x01 || object_type == 0x02) {
|
|
|
|
RCHECK(reader.SkipBits(8)); // foreground_pixel_code
|
|
|
|
RCHECK(reader.SkipBits(8)); // background_pixel_code
|
|
|
|
}
|
|
|
|
RCHECK(composer_.SetObjectInfo(object_id, region_id, x, y,
|
|
|
|
background_pixel_code));
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DvbSubParser::ParseClutDefinition(const uint8_t* data, size_t size) {
|
|
|
|
// See ETSI EN 300 743 Section 7.2.4.
|
|
|
|
BitReader reader(data, size);
|
|
|
|
|
|
|
|
uint8_t clut_id;
|
|
|
|
RCHECK(reader.ReadBits(8, &clut_id));
|
|
|
|
auto* color_space = composer_.GetColorSpace(clut_id);
|
|
|
|
RCHECK(reader.SkipBits(4)); // CLUT_version_number
|
|
|
|
RCHECK(reader.SkipBits(4)); // reserved
|
|
|
|
while (reader.bits_available() > 0) {
|
|
|
|
uint8_t clut_entry_id;
|
|
|
|
uint8_t has_2_bit;
|
|
|
|
uint8_t has_4_bit;
|
|
|
|
uint8_t has_8_bit;
|
|
|
|
uint8_t full_range_flag;
|
|
|
|
RCHECK(reader.ReadBits(8, &clut_entry_id));
|
|
|
|
RCHECK(reader.ReadBits(1, &has_2_bit));
|
|
|
|
RCHECK(reader.ReadBits(1, &has_4_bit));
|
|
|
|
RCHECK(reader.ReadBits(1, &has_8_bit));
|
|
|
|
RCHECK(reader.SkipBits(4)); // reserved
|
|
|
|
RCHECK(reader.ReadBits(1, &full_range_flag));
|
|
|
|
|
|
|
|
if (has_2_bit + has_4_bit + has_8_bit != 1) {
|
|
|
|
LOG(ERROR) << "Must specify exactly one bit depth in CLUT definition";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const BitDepth bit_depth =
|
|
|
|
has_2_bit ? BitDepth::k2Bit
|
|
|
|
: (has_4_bit ? BitDepth::k4Bit : BitDepth::k8Bit);
|
|
|
|
|
|
|
|
uint8_t Y, Cr, Cb, T;
|
|
|
|
if (full_range_flag) {
|
|
|
|
RCHECK(reader.ReadBits(8, &Y));
|
|
|
|
RCHECK(reader.ReadBits(8, &Cr));
|
|
|
|
RCHECK(reader.ReadBits(8, &Cb));
|
|
|
|
RCHECK(reader.ReadBits(8, &T));
|
|
|
|
} else {
|
|
|
|
// These store the most-significant bits, so shift them up.
|
|
|
|
RCHECK(reader.ReadBits(6, &Y));
|
|
|
|
Y <<= 2;
|
|
|
|
RCHECK(reader.ReadBits(4, &Cr));
|
|
|
|
Cr <<= 4;
|
|
|
|
RCHECK(reader.ReadBits(4, &Cb));
|
|
|
|
Cb <<= 4;
|
|
|
|
RCHECK(reader.ReadBits(2, &T));
|
|
|
|
T <<= 6;
|
|
|
|
}
|
|
|
|
color_space->SetColor(bit_depth, clut_entry_id, ConvertYuv(Y, Cr, Cb, T));
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DvbSubParser::ParseObjectData(int64_t pts,
|
|
|
|
const uint8_t* data,
|
|
|
|
size_t size) {
|
|
|
|
// See ETSI EN 300 743 Section 7.2.5 Table 17.
|
|
|
|
BitReader reader(data, size);
|
|
|
|
|
|
|
|
uint16_t object_id;
|
|
|
|
uint8_t object_coding_method;
|
|
|
|
RCHECK(reader.ReadBits(16, &object_id));
|
|
|
|
RCHECK(reader.SkipBits(4)); // object_version_number
|
|
|
|
RCHECK(reader.ReadBits(2, &object_coding_method));
|
|
|
|
RCHECK(reader.SkipBits(1)); // non_modifying_colour_flag
|
|
|
|
RCHECK(reader.SkipBits(1)); // reserved
|
|
|
|
|
|
|
|
auto* image = composer_.GetObjectImage(object_id);
|
|
|
|
auto* color_space = composer_.GetColorSpaceForObject(object_id);
|
|
|
|
if (!image || !color_space)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (object_coding_method == 0) {
|
|
|
|
uint16_t top_field_length;
|
|
|
|
uint16_t bottom_field_length;
|
|
|
|
RCHECK(reader.ReadBits(16, &top_field_length));
|
|
|
|
RCHECK(reader.ReadBits(16, &bottom_field_length));
|
|
|
|
|
|
|
|
RCHECK(ParsePixelDataSubObject(top_field_length, true, &reader, color_space,
|
|
|
|
image));
|
|
|
|
RCHECK(ParsePixelDataSubObject(bottom_field_length, false, &reader,
|
|
|
|
color_space, image));
|
|
|
|
// Ignore 8_stuff_bits since we don't need to read to the end.
|
|
|
|
|
|
|
|
if (bottom_field_length == 0) {
|
|
|
|
// If there are no bottom rows, then the top rows are used instead. See
|
|
|
|
// beginning of section 7.2.5.1.
|
|
|
|
image->MirrorToBottomRows();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
LOG(ERROR) << "Unsupported DVB-sub object coding method: "
|
|
|
|
<< static_cast<int>(object_coding_method);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DvbSubParser::ParseDisplayDefinition(const uint8_t* data, size_t size) {
|
|
|
|
// See ETSI EN 300 743 Section 7.2.1.
|
|
|
|
BitReader reader(data, size);
|
|
|
|
|
|
|
|
uint16_t width, height;
|
|
|
|
RCHECK(reader.SkipBits(4)); // dds_version_number
|
|
|
|
RCHECK(reader.SkipBits(1)); // display_window_flag
|
|
|
|
RCHECK(reader.SkipBits(3)); // reserved
|
|
|
|
RCHECK(reader.ReadBits(16, &width));
|
|
|
|
RCHECK(reader.ReadBits(16, &height));
|
|
|
|
// Size is stored as -1.
|
|
|
|
composer_.SetDisplaySize(width + 1, height + 1);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DvbSubParser::ParsePixelDataSubObject(size_t sub_object_length,
|
|
|
|
bool is_top_fields,
|
|
|
|
BitReader* reader,
|
|
|
|
DvbImageColorSpace* color_space,
|
|
|
|
DvbImageBuilder* image) {
|
|
|
|
const size_t start = reader->bit_position() / 8;
|
|
|
|
while (reader->bit_position() / 8 < start + sub_object_length) {
|
|
|
|
// See ETSI EN 300 743 Section 7.2.5.1 Table 20
|
|
|
|
uint8_t data_type;
|
|
|
|
RCHECK(reader->ReadBits(8, &data_type));
|
|
|
|
uint8_t temp[16];
|
|
|
|
switch (data_type) {
|
|
|
|
case 0x10:
|
|
|
|
RCHECK(Parse2BitPixelData(is_top_fields, reader, image));
|
|
|
|
reader->SkipToNextByte();
|
|
|
|
break;
|
|
|
|
case 0x11:
|
|
|
|
RCHECK(Parse4BitPixelData(is_top_fields, reader, image));
|
|
|
|
reader->SkipToNextByte();
|
|
|
|
break;
|
|
|
|
case 0x12:
|
|
|
|
RCHECK(Parse8BitPixelData(is_top_fields, reader, image));
|
|
|
|
break;
|
|
|
|
case 0x20:
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
RCHECK(reader->ReadBits(4, &temp[i]));
|
|
|
|
}
|
|
|
|
color_space->Set2To4BitDepthMap(temp);
|
|
|
|
break;
|
|
|
|
case 0x21:
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
RCHECK(reader->ReadBits(8, &temp[i]));
|
|
|
|
}
|
|
|
|
color_space->Set2To8BitDepthMap(temp);
|
|
|
|
break;
|
|
|
|
case 0x22:
|
|
|
|
for (int i = 0; i < 16; i++) {
|
|
|
|
RCHECK(reader->ReadBits(8, &temp[i]));
|
|
|
|
}
|
|
|
|
color_space->Set4To8BitDepthMap(temp);
|
|
|
|
break;
|
|
|
|
case 0xf0:
|
|
|
|
image->NewRow(is_top_fields);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG(ERROR) << "Unsupported DVB-sub pixel data format: 0x" << std::hex
|
|
|
|
<< static_cast<int>(data_type);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DvbSubParser::Parse2BitPixelData(bool is_top_fields,
|
|
|
|
BitReader* reader,
|
|
|
|
DvbImageBuilder* image) {
|
|
|
|
// 2-bit/pixel code string, Section 7.2.5.2.1, Table 22.
|
|
|
|
while (true) {
|
|
|
|
uint8_t peek;
|
|
|
|
RCHECK(reader->ReadBits(2, &peek));
|
|
|
|
if (peek != 0) {
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k2Bit, peek, is_top_fields));
|
|
|
|
} else {
|
|
|
|
uint8_t switch_1;
|
|
|
|
RCHECK(reader->ReadBits(1, &switch_1));
|
|
|
|
if (switch_1 == 1) {
|
|
|
|
uint8_t count_minus_3;
|
|
|
|
RCHECK(reader->ReadBits(3, &count_minus_3));
|
|
|
|
RCHECK(reader->ReadBits(2, &peek));
|
|
|
|
for (uint8_t i = 0; i < count_minus_3 + 3; i++)
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k2Bit, peek, is_top_fields));
|
|
|
|
} else {
|
|
|
|
uint8_t switch_2;
|
|
|
|
RCHECK(reader->ReadBits(1, &switch_2));
|
|
|
|
if (switch_2 == 1) {
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k2Bit, 0, is_top_fields));
|
|
|
|
} else {
|
|
|
|
uint8_t switch_3;
|
|
|
|
RCHECK(reader->ReadBits(2, &switch_3));
|
|
|
|
if (switch_3 == 0) {
|
|
|
|
break;
|
|
|
|
} else if (switch_3 == 1) {
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k2Bit, 0, is_top_fields));
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k2Bit, 0, is_top_fields));
|
|
|
|
} else if (switch_3 == 2) {
|
|
|
|
uint8_t count_minus_12;
|
|
|
|
RCHECK(reader->ReadBits(4, &count_minus_12));
|
|
|
|
RCHECK(reader->ReadBits(2, &peek));
|
|
|
|
for (uint8_t i = 0; i < count_minus_12 + 12; i++)
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k2Bit, peek, is_top_fields));
|
|
|
|
} else if (switch_3 == 3) {
|
|
|
|
uint8_t count_minus_29;
|
|
|
|
RCHECK(reader->ReadBits(8, &count_minus_29));
|
|
|
|
RCHECK(reader->ReadBits(2, &peek));
|
|
|
|
for (uint8_t i = 0; i < count_minus_29 + 29; i++)
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k2Bit, peek, is_top_fields));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DvbSubParser::Parse4BitPixelData(bool is_top_fields,
|
|
|
|
BitReader* reader,
|
|
|
|
DvbImageBuilder* image) {
|
|
|
|
// 4-bit/pixel code string, Section 7.2.5.2.2, Table 24.
|
|
|
|
DCHECK(reader->bits_available() % 8 == 0);
|
|
|
|
while (true) {
|
|
|
|
uint8_t peek;
|
|
|
|
RCHECK(reader->ReadBits(4, &peek));
|
|
|
|
if (peek != 0) {
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k4Bit, peek, is_top_fields));
|
|
|
|
} else {
|
|
|
|
uint8_t switch_1;
|
|
|
|
RCHECK(reader->ReadBits(1, &switch_1));
|
|
|
|
if (switch_1 == 0) {
|
|
|
|
RCHECK(reader->ReadBits(3, &peek));
|
|
|
|
if (peek != 0) {
|
|
|
|
for (int i = 0; i < peek + 2; i++)
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k4Bit, 0, is_top_fields));
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uint8_t switch_2;
|
|
|
|
RCHECK(reader->ReadBits(1, &switch_2));
|
|
|
|
if (switch_2 == 0) {
|
|
|
|
RCHECK(reader->ReadBits(2, &peek)); // run_length_4-7
|
|
|
|
uint8_t code;
|
|
|
|
RCHECK(reader->ReadBits(4, &code));
|
|
|
|
for (int i = 0; i < peek + 4; i++)
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k4Bit, code, is_top_fields));
|
|
|
|
} else {
|
|
|
|
uint8_t switch_3;
|
|
|
|
RCHECK(reader->ReadBits(2, &switch_3));
|
|
|
|
if (switch_3 == 0) {
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k4Bit, 0, is_top_fields));
|
|
|
|
} else if (switch_3 == 1) {
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k4Bit, 0, is_top_fields));
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k4Bit, 0, is_top_fields));
|
|
|
|
} else if (switch_3 == 2) {
|
|
|
|
RCHECK(reader->ReadBits(4, &peek)); // run_length_9-24
|
|
|
|
uint8_t code;
|
|
|
|
RCHECK(reader->ReadBits(4, &code));
|
|
|
|
for (int i = 0; i < peek + 9; i++)
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k4Bit, code, is_top_fields));
|
|
|
|
} else {
|
|
|
|
// switch_3 == 3
|
|
|
|
RCHECK(reader->ReadBits(8, &peek)); // run_length_25-280
|
|
|
|
uint8_t code;
|
|
|
|
RCHECK(reader->ReadBits(4, &code));
|
|
|
|
for (int i = 0; i < peek + 25; i++)
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k4Bit, code, is_top_fields));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DvbSubParser::Parse8BitPixelData(bool is_top_fields,
|
|
|
|
BitReader* reader,
|
|
|
|
DvbImageBuilder* image) {
|
|
|
|
// 8-bit/pixel code string, Section 7.2.5.2.3, Table 26.
|
|
|
|
while (true) {
|
|
|
|
uint8_t peek;
|
|
|
|
RCHECK(reader->ReadBits(8, &peek));
|
|
|
|
if (peek != 0) {
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k8Bit, peek, is_top_fields));
|
|
|
|
} else {
|
|
|
|
uint8_t switch_1;
|
|
|
|
RCHECK(reader->ReadBits(1, &switch_1));
|
|
|
|
if (switch_1 == 0) {
|
|
|
|
RCHECK(reader->ReadBits(7, &peek));
|
|
|
|
if (peek != 0) {
|
|
|
|
for (uint8_t i = 0; i < peek; i++)
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k8Bit, 0, is_top_fields));
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uint8_t count;
|
|
|
|
RCHECK(reader->ReadBits(7, &count));
|
|
|
|
RCHECK(reader->ReadBits(8, &peek));
|
|
|
|
for (uint8_t i = 0; i < count; i++)
|
|
|
|
RCHECK(image->AddPixel(BitDepth::k8Bit, peek, is_top_fields));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace media
|
|
|
|
} // namespace shaka
|