Skip to content

Commit 5adbf4c

Browse files
committed
evc: initial parsing for evcC box
1 parent 47e00f2 commit 5adbf4c

File tree

6 files changed

+361
-0
lines changed

6 files changed

+361
-0
lines changed

libheif/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ set(libheif_sources
8787
codecs/avc_boxes.cc
8888
codecs/avc_dec.h
8989
codecs/avc_dec.cc
90+
codecs/evc_boxes.cc
91+
codecs/evc_boxes.h
9092
image-items/mask_image.h
9193
image-items/mask_image.cc
9294
image-items/image_item.h

libheif/box.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "codecs/avc_boxes.h"
3232
#include "codecs/avif_boxes.h"
3333
#include "image-items/tiled.h"
34+
#include "codecs/evc_boxes.h"
3435

3536
#include <iomanip>
3637
#include <utility>
@@ -690,6 +691,12 @@ Error Box::read(BitstreamRange& range, std::shared_ptr<Box>* result, const heif_
690691
box = std::make_shared<Box_avcC>();
691692
break;
692693

694+
// --- EVC
695+
696+
case fourcc("evcC"):
697+
box = std::make_shared<Box_evcC>();
698+
break;
699+
693700
#if WITH_EXPERIMENTAL_FEATURES
694701
case fourcc("tilC"):
695702
box = std::make_shared<Box_tilC>();

libheif/codecs/evc_boxes.cc

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* HEIF EVC codec.
3+
* Copyright (c) 2024 Brad Hards <[email protected]>
4+
*
5+
* This file is part of libheif.
6+
*
7+
* libheif is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU Lesser General Public License as
9+
* published by the Free Software Foundation, either version 3 of
10+
* the License, or (at your option) any later version.
11+
*
12+
* libheif is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with libheif. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
#include "evc_boxes.h"
22+
#include "bitstream.h"
23+
#include "error.h"
24+
#include "file.h"
25+
26+
#include <iomanip>
27+
#include <libheif/api_structs.h>
28+
29+
30+
31+
Error Box_evcC::parse(BitstreamRange& range, const heif_security_limits* limits)
32+
{
33+
m_configuration.configurationVersion = range.read8();
34+
m_configuration.profile_idc = range.read8();
35+
m_configuration.level_idc = range.read8();
36+
m_configuration.toolset_idc_h = range.read32();
37+
m_configuration.toolset_idc_l = range.read32();
38+
uint8_t b = range.read8();
39+
m_configuration.chroma_format_idc = (b >> 6) & 0b11;
40+
uint8_t bit_depth_luma_minus8 = (b >> 3) & 0b111;
41+
m_configuration.bit_depth_luma = bit_depth_luma_minus8 + 8;
42+
uint8_t bit_depth_chroma_minus8 = b & 0b111;
43+
m_configuration.bit_depth_chroma = bit_depth_chroma_minus8 + 8;
44+
m_configuration.pic_width_in_luma_samples = range.read16();
45+
m_configuration.pic_height_in_luma_samples = range.read16();
46+
b = range.read8();
47+
uint8_t lengthSizeMinus1 = b & 0b11;
48+
m_configuration.lengthSize = lengthSizeMinus1 + 1;
49+
uint8_t num_of_arrays = range.read8();
50+
for (uint8_t j = 0; j < num_of_arrays && !range.error(); j++) {
51+
NalArray array;
52+
b = range.read8();
53+
array.array_completeness = ((b & 0x80) == 0x80);
54+
array.NAL_unit_type = (b & 0b00111111);
55+
uint16_t num_nalus = range.read16();
56+
for (int i = 0; i < num_nalus && !range.error(); i++) {
57+
uint16_t nal_unit_length = range.read16();
58+
if (nal_unit_length == 0) {
59+
// Ignore empty NAL units.
60+
continue;
61+
}
62+
std::vector<uint8_t> nal_unit;
63+
if (range.prepare_read(nal_unit_length)) {
64+
nal_unit.resize(nal_unit_length);
65+
bool success = range.get_istream()->read((char*) nal_unit.data(), nal_unit_length);
66+
if (!success) {
67+
return Error{heif_error_Invalid_input, heif_suberror_End_of_data, "error while reading evcC box"};
68+
}
69+
}
70+
71+
array.nal_units.push_back(std::move(nal_unit));
72+
}
73+
m_nal_array.push_back(array);
74+
}
75+
76+
return range.get_error();
77+
}
78+
79+
80+
std::string Box_evcC::dump(Indent& indent) const
81+
{
82+
std::ostringstream sstr;
83+
sstr << Box::dump(indent);
84+
// TODO: decode more of this
85+
sstr << indent << "configurationVersion: " << ((int)m_configuration.configurationVersion) << "\n";
86+
sstr << indent << "profile_idc: " << ((int)m_configuration.profile_idc) << "\n";
87+
sstr << indent << "level_idc: " << ((int)m_configuration.level_idc) << "\n";
88+
sstr << indent << "toolset_idc_h: " << m_configuration.toolset_idc_h << "\n";
89+
sstr << indent << "toolset_idc_l: " << m_configuration.toolset_idc_l << "\n";
90+
sstr << indent << "chroma_format_idc: " << ((int)m_configuration.chroma_format_idc)
91+
<< " (" << get_chroma_format_as_text() << ")\n";
92+
sstr << indent << "bit_depth_luma: " << ((int)m_configuration.bit_depth_luma) << "\n";
93+
sstr << indent << "bit_depth_chroma: " << ((int)m_configuration.bit_depth_chroma) << "\n";
94+
sstr << indent << "pic_width_in_luma_samples: " << m_configuration.pic_width_in_luma_samples << "\n";
95+
sstr << indent << "pic_height_in_luma_samples: " << m_configuration.pic_height_in_luma_samples << "\n";
96+
sstr << indent << "length_size: " << ((int)m_configuration.lengthSize) << "\n";
97+
for (const auto &array : m_nal_array)
98+
{
99+
sstr << indent << "<array>\n";
100+
101+
indent++;
102+
sstr << indent << "array_completeness: " << (array.array_completeness ? "true" : "false") << "\n"
103+
<< indent << "NAL_unit_type: " << ((int) array.NAL_unit_type) << "\n";
104+
105+
for (const auto& unit : array.nal_units) {
106+
sstr << indent;
107+
for (uint8_t b : unit) {
108+
sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " ";
109+
}
110+
sstr << "\n";
111+
sstr << std::dec;
112+
}
113+
114+
indent--;
115+
}
116+
return sstr.str();
117+
}
118+
119+
std::string Box_evcC::get_chroma_format_as_text() const
120+
{
121+
switch (m_configuration.chroma_format_idc)
122+
{
123+
case 0:
124+
return std::string("Monochrome");
125+
case 1:
126+
return std::string("4:2:0");
127+
case 2:
128+
return std::string("4:2:2");
129+
case 3:
130+
return std::string("4:4:4");
131+
default:
132+
return std::string("Invalid");
133+
}
134+
}
135+
136+
Error Box_evcC::write(StreamWriter& writer) const
137+
{
138+
size_t box_start = reserve_box_header_space(writer);
139+
140+
writer.write8(m_configuration.configurationVersion);
141+
writer.write8(m_configuration.profile_idc);
142+
writer.write8(m_configuration.level_idc);
143+
writer.write32(m_configuration.toolset_idc_h);
144+
writer.write32(m_configuration.toolset_idc_l);
145+
uint8_t bit_depth_luma_minus8 = ((m_configuration.bit_depth_luma - 8) & 0b111);
146+
uint8_t bit_depth_chroma_minus8 = ((m_configuration.bit_depth_chroma - 8) & 0b111);
147+
uint8_t b = (m_configuration.chroma_format_idc << 6) | (bit_depth_luma_minus8 << 3) | bit_depth_chroma_minus8;
148+
writer.write8(b);
149+
writer.write16(m_configuration.pic_width_in_luma_samples);
150+
writer.write16(m_configuration.pic_height_in_luma_samples);
151+
writer.write8(m_configuration.lengthSize - 1);
152+
writer.write8((uint8_t)m_nal_array.size());
153+
for (const NalArray& array : m_nal_array) {
154+
155+
writer.write8((uint8_t) ((array.array_completeness? 0x80: 0x00) |
156+
(array.NAL_unit_type & 0b00111111)));
157+
158+
writer.write16((uint16_t)array.nal_units.size());
159+
160+
for (const std::vector<uint8_t>& nal_unit : array.nal_units) {
161+
writer.write16((uint16_t) nal_unit.size());
162+
writer.write(nal_unit);
163+
}
164+
}
165+
prepend_header(writer, box_start);
166+
167+
return Error::Ok;
168+
}

libheif/codecs/evc_boxes.h

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* HEIF EVC codec.
3+
* Copyright (c) 2024 Brad Hards <[email protected]>
4+
*
5+
* This file is part of libheif.
6+
*
7+
* libheif is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU Lesser General Public License as
9+
* published by the Free Software Foundation, either version 3 of
10+
* the License, or (at your option) any later version.
11+
*
12+
* libheif is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with libheif. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
#ifndef HEIF_EVC_BOXES_H
22+
#define HEIF_EVC_BOXES_H
23+
24+
#include "box.h"
25+
#include "error.h"
26+
// #include <cstdint>
27+
// #include <vector>
28+
// #include <string>
29+
// #include <memory>
30+
// #include "image-items/image_item.h"
31+
32+
33+
class Box_evcC : public Box {
34+
public:
35+
Box_evcC() { set_short_type(fourcc("evcC")); }
36+
37+
bool is_essential() const override { return true; }
38+
39+
struct configuration {
40+
uint8_t configurationVersion = 1;
41+
uint8_t profile_idc;
42+
uint8_t level_idc;
43+
uint32_t toolset_idc_h;
44+
uint32_t toolset_idc_l;
45+
uint8_t chroma_format_idc;
46+
uint8_t bit_depth_luma;
47+
uint8_t bit_depth_chroma;
48+
uint16_t pic_width_in_luma_samples;
49+
uint16_t pic_height_in_luma_samples;
50+
uint8_t lengthSize = 0;
51+
};
52+
53+
void set_configuration(const configuration& config)
54+
{
55+
m_configuration = config;
56+
}
57+
58+
const configuration& get_configuration() const
59+
{
60+
return m_configuration;
61+
}
62+
63+
std::string dump(Indent &) const override;
64+
65+
Error write(StreamWriter &writer) const override;
66+
67+
protected:
68+
Error parse(BitstreamRange &range, const heif_security_limits* limits) override;
69+
70+
private:
71+
configuration m_configuration;
72+
struct NalArray
73+
{
74+
bool array_completeness;
75+
uint8_t NAL_unit_type;
76+
77+
std::vector<std::vector<uint8_t> > nal_units;
78+
};
79+
80+
std::vector<NalArray> m_nal_array;
81+
82+
std::string get_chroma_format_as_text() const;
83+
};
84+
85+
#endif

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ else()
3434
add_libheif_test(jpeg2000)
3535
add_libheif_test(avc_box)
3636
add_libheif_test(file_layout)
37+
add_libheif_test(evc_box)
3738
endif()
3839

3940
if (WITH_EXPERIMENTAL_FEATURS AND WITH_REDUCED_VISIBILITY)

tests/evc_box.cc

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
libheif EVC unit tests
3+
4+
MIT License
5+
6+
Copyright (c) 2024 Brad Hards <[email protected]>
7+
8+
Permission is hereby granted, free of charge, to any person obtaining a copy
9+
of this software and associated documentation files (the "Software"), to deal
10+
in the Software without restriction, including without limitation the rights
11+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
copies of the Software, and to permit persons to whom the Software is
13+
furnished to do so, subject to the following conditions:
14+
15+
The above copyright notice and this permission notice shall be included in all
16+
copies or substantial portions of the Software.
17+
18+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
SOFTWARE.
25+
*/
26+
27+
#include "catch.hpp"
28+
#include "codecs/evc_boxes.h"
29+
#include "error.h"
30+
#include <cstdint>
31+
#include <iostream>
32+
#include <memory>
33+
34+
TEST_CASE("evcC") {
35+
std::vector<uint8_t> byteArray{
36+
0x00, 0x00, 0x00, 0x3d, 0x65, 0x76, 0x63, 0x43,
37+
0x01, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00,
38+
0x00, 0x00, 0x00, 0x52, 0x01, 0x40, 0x00, 0xf0,
39+
0x03, 0x02, 0x98, 0x00, 0x01, 0x00, 0x15, 0x32,
40+
0x00, 0x80, 0x6b, 0x80, 0x00, 0x00, 0x00, 0x00,
41+
0x00, 0x00, 0x00, 0x20, 0x0a, 0x08, 0x0f, 0x16,
42+
0xc0, 0x00, 0x54, 0x00, 0x99, 0x00, 0x01, 0x00,
43+
0x04, 0x34, 0x00, 0xfb, 0x00};
44+
45+
auto reader = std::make_shared<StreamReader_memory>(byteArray.data(),
46+
byteArray.size(), false);
47+
48+
BitstreamRange range(reader, byteArray.size());
49+
std::shared_ptr<Box> box;
50+
Error error = Box::read(range, &box, heif_get_global_security_limits());
51+
REQUIRE(error == Error::Ok);
52+
REQUIRE(range.error() == 0);
53+
54+
REQUIRE(box->get_short_type() == fourcc("evcC"));
55+
REQUIRE(box->get_type_string() == "evcC");
56+
std::shared_ptr<Box_evcC> evcC = std::dynamic_pointer_cast<Box_evcC>(box);
57+
Box_evcC::configuration configuration = evcC->get_configuration();
58+
REQUIRE(configuration.configurationVersion == 1);
59+
REQUIRE(configuration.profile_idc == 0);
60+
REQUIRE(configuration.level_idc == 215);
61+
REQUIRE(configuration.toolset_idc_h == 0);
62+
REQUIRE(configuration.toolset_idc_l == 0);
63+
REQUIRE(configuration.chroma_format_idc == 1);
64+
REQUIRE(configuration.bit_depth_luma == 10);
65+
REQUIRE(configuration.bit_depth_chroma == 10);
66+
REQUIRE(configuration.pic_width_in_luma_samples == 320);
67+
REQUIRE(configuration.pic_height_in_luma_samples == 240);
68+
REQUIRE(configuration.lengthSize == 4);
69+
Indent indent;
70+
std::string dumpResult = box->dump(indent);
71+
REQUIRE(dumpResult == "Box: evcC -----\n"
72+
"size: 61 (header size: 8)\n"
73+
"configurationVersion: 1\n"
74+
"profile_idc: 0\n"
75+
"level_idc: 215\n"
76+
"toolset_idc_h: 0\n"
77+
"toolset_idc_l: 0\n"
78+
"chroma_format_idc: 1 (4:2:0)\n"
79+
"bit_depth_luma: 10\n"
80+
"bit_depth_chroma: 10\n"
81+
"pic_width_in_luma_samples: 320\n"
82+
"pic_height_in_luma_samples: 240\n"
83+
"length_size: 4\n"
84+
"<array>\n"
85+
"| array_completeness: true\n"
86+
"| NAL_unit_type: 24\n"
87+
"| 32 00 80 6b 80 00 00 00 00 00 00 00 20 0a 08 0f 16 c0 00 54 00 \n"
88+
"<array>\n"
89+
"| array_completeness: true\n"
90+
"| NAL_unit_type: 25\n"
91+
"| 34 00 fb 00 \n");
92+
93+
StreamWriter writer;
94+
Error err = evcC->write(writer);
95+
REQUIRE(err.error_code == heif_error_Ok);
96+
const std::vector<uint8_t> bytes = writer.get_data();
97+
REQUIRE(bytes == byteArray);
98+
}

0 commit comments

Comments
 (0)