Created
August 8, 2025 16:39
-
-
Save zhuker/a1ec1afba79cc31a93a9c2e71a8013a4 to your computer and use it in GitHub Desktop.
simple h264 nal unit parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <iostream> | |
| #include <vector> | |
| #include <cstdint> | |
| #include <cstring> | |
| bool isStartCode3(const uint8_t* p) | |
| { | |
| return p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01; | |
| } | |
| bool isStartCode4(const uint8_t* p) | |
| { | |
| return p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x00 && p[3] == 0x01; | |
| } | |
| struct Nal | |
| { | |
| const uint8_t* buffer; | |
| size_t size; | |
| size_t startCodeLength; | |
| }; | |
| // Returns a vector of pointers to NAL units (without start codes) | |
| std::vector<Nal> parseNALUnits(const uint8_t* buffer, size_t size) | |
| { | |
| std::vector<Nal> nalUnits; | |
| size_t i = 0; | |
| while (i + 3 < size) | |
| { | |
| size_t start = 0, end = 0; | |
| size_t startCodeLength = 0; | |
| // Find the start code | |
| if (isStartCode4(buffer + i)) | |
| { | |
| start = i + 4; | |
| startCodeLength = 4; | |
| } | |
| else if (isStartCode3(buffer + i)) | |
| { | |
| start = i + 3; | |
| startCodeLength = 3; | |
| } | |
| else | |
| { | |
| ++i; | |
| continue; | |
| } | |
| // Find the next start code | |
| i = start; | |
| while (i + 3 < size && !isStartCode3(buffer + i) && !isStartCode4(buffer + i)) | |
| { | |
| ++i; | |
| } | |
| end = i; | |
| if (i + 3 >= size) | |
| { | |
| end = size; | |
| } | |
| Nal nal{buffer + start - startCodeLength, end - start + startCodeLength, startCodeLength}; | |
| nalUnits.emplace_back(nal); | |
| } | |
| return nalUnits; | |
| } | |
| static const char* nalUnitTypeString(uint8_t nalType) | |
| { | |
| switch (nalType) | |
| { | |
| case 1: return "non-IDR"; | |
| case 2: return "Coded slice data partition A"; | |
| case 3: return "Coded slice data partition B"; | |
| case 4: return "Coded slice data partition C"; | |
| case 5: return "IDR"; | |
| case 6: return "SEI"; | |
| case 7: return "SPS"; | |
| case 8: return "PPS"; | |
| default: return "Unknown"; | |
| } | |
| } | |
| int main(void) { | |
| FILE* f = fopen("video.h264", "rb"); | |
| struct stat fileInfo{}; | |
| ASSERT_EQ(0, stat("video.h264", &fileInfo)); | |
| void* data = mmap(nullptr, fileInfo.st_size, PROT_READ, MAP_SHARED | MAP_NORESERVE, fileno(f), 0); | |
| ASSERT_NE(MAP_FAILED, data); | |
| auto nalUnits = parseNALUnits(static_cast<const uint8_t*>(data), fileInfo.st_size); | |
| for (const auto& nal_unit : nalUnits) | |
| { | |
| printf("%lu\n", nal_unit.size); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment