|
5 | 5 | #include <cstdint> |
6 | 6 | #include <iostream> |
7 | 7 | #include <map> |
| 8 | +#include <array> |
8 | 9 | #include <sstream> |
9 | 10 | #include <string> |
10 | 11 | #include <vector> |
@@ -236,6 +237,45 @@ std::vector<std::uint8_t> make_multitrack_init_mp4() { |
236 | 237 | return concat({ftyp, moov}); |
237 | 238 | } |
238 | 239 |
|
| 240 | +std::vector<std::uint8_t> make_hevc_init_mp4(std::uint8_t general_profile_byte, |
| 241 | + std::uint32_t compatibility_flags, |
| 242 | + std::array<std::uint8_t, 6> constraint_bytes, |
| 243 | + std::uint8_t level_idc) { |
| 244 | + const auto ftyp = make_box("ftyp", {'i', 's', 'o', '6', 0, 0, 0, 1, 'i', 's', 'o', '6', 'c', 'm', 'f', 'c'}); |
| 245 | + const auto tkhd = make_full_box("tkhd", |
| 246 | + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}); |
| 247 | + const auto hdlr = make_full_box("hdlr", {0, 0, 0, 0, 'v', 'i', 'd', 'e', 0, 0, 0, 0}); |
| 248 | + auto video_header = std::vector<std::uint8_t>(70, 0); |
| 249 | + video_header[24] = 0x01; |
| 250 | + video_header[25] = 0x40; |
| 251 | + video_header[26] = 0x00; |
| 252 | + video_header[27] = 0xf0; |
| 253 | + |
| 254 | + std::vector<std::uint8_t> hvcc_payload = { |
| 255 | + 0x01, |
| 256 | + general_profile_byte, |
| 257 | + static_cast<std::uint8_t>((compatibility_flags >> 24U) & 0xFFU), |
| 258 | + static_cast<std::uint8_t>((compatibility_flags >> 16U) & 0xFFU), |
| 259 | + static_cast<std::uint8_t>((compatibility_flags >> 8U) & 0xFFU), |
| 260 | + static_cast<std::uint8_t>(compatibility_flags & 0xFFU), |
| 261 | + constraint_bytes[0], |
| 262 | + constraint_bytes[1], |
| 263 | + constraint_bytes[2], |
| 264 | + constraint_bytes[3], |
| 265 | + constraint_bytes[4], |
| 266 | + constraint_bytes[5], |
| 267 | + level_idc, |
| 268 | + }; |
| 269 | + const auto sample_entry = make_box("hev1", concat({video_header, make_box("hvcC", hvcc_payload)})); |
| 270 | + const auto stsd = make_full_box("stsd", concat({std::vector<std::uint8_t>{0, 0, 0, 1}, sample_entry})); |
| 271 | + const auto stbl = make_box("stbl", stsd); |
| 272 | + const auto minf = make_box("minf", stbl); |
| 273 | + const auto mdia = make_box("mdia", concat({hdlr, minf})); |
| 274 | + const auto trak = make_box("trak", concat({tkhd, mdia})); |
| 275 | + const auto moov = make_box("moov", trak); |
| 276 | + return concat({ftyp, moov}); |
| 277 | +} |
| 278 | + |
239 | 279 | std::size_t find_after(std::string_view haystack, std::string_view needle, std::size_t start = 0) { |
240 | 280 | const std::size_t pos = haystack.find(needle, start); |
241 | 281 | return pos == std::string_view::npos ? pos : pos + needle.size(); |
@@ -485,5 +525,16 @@ int main() { |
485 | 525 | ok &= expect(audio_init_boxes[0].type == "ftyp" && audio_init_boxes[1].type == "moov", |
486 | 526 | "expected audio initData to contain ftyp and moov boxes"); |
487 | 527 |
|
| 528 | + const auto hevc_main_bytes = make_hevc_init_mp4(0x01, 0x60000000, {0xB0, 0x00, 0x00, 0x00, 0x00, 0x00}, 90); |
| 529 | + const auto hevc_high_bytes = make_hevc_init_mp4(0x22, 0x40000000, {0xB0, 0x00, 0x00, 0x00, 0x00, 0x00}, 150); |
| 530 | + const auto hevc_main_tracks = extract_tracks(parse_mp4_boxes(hevc_main_bytes), hevc_main_bytes); |
| 531 | + const auto hevc_high_tracks = extract_tracks(parse_mp4_boxes(hevc_high_bytes), hevc_high_bytes); |
| 532 | + ok &= expect(hevc_main_tracks.size() == 1, "expected one HEVC main-profile track"); |
| 533 | + ok &= expect(hevc_high_tracks.size() == 1, "expected one HEVC high-tier track"); |
| 534 | + ok &= expect(hevc_main_tracks.front().codec == "hev1.1.6.L90.B0", |
| 535 | + "expected compact RFC 6381 HEVC main-profile codec string"); |
| 536 | + ok &= expect(hevc_high_tracks.front().codec == "hev1.2.4.H150.B0", |
| 537 | + "expected compact RFC 6381 HEVC high-tier codec string"); |
| 538 | + |
488 | 539 | return ok ? 0 : 1; |
489 | 540 | } |
0 commit comments