commit 258ba09cdf2fc46fa85e6d0de46b5cebe7a055c0 Author: tqcq <99722391+tqcq@users.noreply.github.com> Date: Tue Jan 6 22:11:06 2026 +0800 feat: parse flv header,body,tag diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d3e8898 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.flv filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..09bd551 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.cache +out/ +compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..33304f7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.10) +project(av LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 17) +add_executable(flv_parser "flv_parser/main.cc") + diff --git a/flv_parser/Big_Buck_Bunny_1080_10s_h264.flv b/flv_parser/Big_Buck_Bunny_1080_10s_h264.flv new file mode 100644 index 0000000..4cf1480 --- /dev/null +++ b/flv_parser/Big_Buck_Bunny_1080_10s_h264.flv @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f604aa9646734c105362577c2f594c3e36c096c07cdaf74b16903f26b75c47f2 +size 7463950 diff --git a/flv_parser/main.cc b/flv_parser/main.cc new file mode 100644 index 0000000..4144090 --- /dev/null +++ b/flv_parser/main.cc @@ -0,0 +1,164 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace flv { +#pragma pack(push, 1) + +struct FLVHeader { + FLVHeader() + { + memcpy(magic, "FLV", 3); + version = 1; +#ifdef SINGLE_FLAG + // flag = 0; +#else + dataOffset = 9; + _reserved = 0; + _typeFlagsReserved = 0; + typeFlagsVideo = 0; + typeFlagsAudio = 0; +#endif + } + + char magic[3]; + uint8_t version; +#ifdef SINGLE_FLAG + uint8_t flag; +#else + uint8_t typeFlagsVideo : 1; + uint8_t _typeFlagsReserved : 1; + uint8_t typeFlagsAudio : 1; + uint8_t _reserved : 5; +#endif + uint32_t dataOffset; +}; + +struct TagHeader { + uint8_t tagType; + uint8_t dataSize[3]; + uint8_t timestamp[4]; + uint8_t streamId[3]; +}; + +#pragma pack(pop) + +static_assert(sizeof(FLVHeader) == 9, "sizeof(FLVHeader) MUST be 9 bytes"); + +class Parser { +public: + Parser(const char *filepath) + { + if (openFile(filepath)) { + _m_flv_hdr = parseHeader(); + if (_m_flv_hdr) { parseBody(); } + } else { + std::cout << "openFile [" << filepath << "] failed." << std::endl; + } + } + + bool ok() { return _m_ifs.is_open() && _m_flv_hdr.has_value(); } + + std::string toString() const + { + if (!_m_flv_hdr) { return "Invalid FLV"; } + + std::stringstream ss; + + ss << "VideoFlag: " << (_m_flv_hdr->typeFlagsVideo ? "yes" : "no") << std::endl; + ss << "AudioFlag: " << (_m_flv_hdr->typeFlagsAudio ? "yes" : "no") << std::endl; + for (const auto &tag : _m_tag_list) { + ss << " Tag:" << std::endl; + ss << " >type=" << (int) tag.type << std::endl; + ss << " >dataLen=" << tag.dataLen << std::endl; + ss << " >timestamp=" << tag.timestamp << std::endl; + ss << " >streamId=" << tag.streamId << std::endl; + } + + return ss.str(); + } + +private: + bool openFile(const char *filepath) + { + _m_ifs.open(filepath, std::iostream::binary | std::iostream::in); + return _m_ifs.is_open(); + } + + std::optional parseHeader() + { + FLVHeader hdr; + _m_ifs.read(reinterpret_cast(&hdr), sizeof(FLVHeader)); + if (_m_ifs.gcount() != sizeof(hdr)) { + std::cout << "read FLVHeader failed." << std::endl; + return std::nullopt; + } + if (hdr._reserved || hdr._typeFlagsReserved) { return std::nullopt; } + return hdr; + } + + void parseBody() + { + uint32_t prevTagSize; + TagHeader hdr; + while (true) { + // read prevTagSize + _m_ifs.read(reinterpret_cast(&prevTagSize), sizeof(prevTagSize)); + if (_m_ifs.gcount() != sizeof(prevTagSize)) { break; } + + // read tagHeader + _m_ifs.read(reinterpret_cast(&hdr), sizeof(hdr)); + if (_m_ifs.gcount() != sizeof(hdr)) { break; } + + Tag tag; + tag.type = hdr.tagType; + tag.dataLen = (hdr.dataSize[0] << 16) | (hdr.dataSize[1] << 8) | hdr.dataSize[2]; + tag.timestamp = + (hdr.timestamp[3] << 24) | (hdr.timestamp[0] << 16) | (hdr.timestamp[1] << 8) | hdr.timestamp[2]; + tag.streamId = (hdr.streamId[0] << 16) | (hdr.streamId[1] << 8) | hdr.streamId[2]; + + _m_tag_list.push_back(std::move(tag)); + _m_ifs.seekg(static_cast(tag.dataLen), std::ios::cur); + } + } + +private: + struct Tag { + uint8_t type = 0; + size_t dataLen = 0; + uint32_t timestamp = 0; + uint32_t streamId; + }; + + std::ifstream _m_ifs; + std::optional _m_flv_hdr; + std::vector _m_tag_list; +}; + +}// namespace flv + +int +main(int argc, char *argv[]) + +{ + if (argc < 2) { + printf("flv_parser \n"); + return -1; + } + + flv::Parser parser(argv[1]); + if (!parser.ok()) { + printf("Invalid flv\n"); + return -1; + } + + std::cout << parser.toString() << std::endl; + + return 0; +}