Files
av/flv_parser/main.cc
2026-01-06 22:11:06 +08:00

165 lines
4.0 KiB
C++

#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <optional>
#include <sstream>
#include <vector>
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<FLVHeader> parseHeader()
{
FLVHeader hdr;
_m_ifs.read(reinterpret_cast<char *>(&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<char *>(&prevTagSize), sizeof(prevTagSize));
if (_m_ifs.gcount() != sizeof(prevTagSize)) { break; }
// read tagHeader
_m_ifs.read(reinterpret_cast<char *>(&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<std::streamoff>(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<FLVHeader> _m_flv_hdr;
std::vector<Tag> _m_tag_list;
};
}// namespace flv
int
main(int argc, char *argv[])
{
if (argc < 2) {
printf("flv_parser <flv_path_path>\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;
}