165 lines
4.0 KiB
C++
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;
|
|
}
|