feat: parse flv header,body,tag
This commit is contained in:
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.flv filter=lfs diff=lfs merge=lfs -text
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.cache
|
||||
out/
|
||||
compile_commands.json
|
||||
6
CMakeLists.txt
Normal file
6
CMakeLists.txt
Normal file
@@ -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")
|
||||
|
||||
BIN
flv_parser/Big_Buck_Bunny_1080_10s_h264.flv
LFS
Normal file
BIN
flv_parser/Big_Buck_Bunny_1080_10s_h264.flv
LFS
Normal file
Binary file not shown.
164
flv_parser/main.cc
Normal file
164
flv_parser/main.cc
Normal file
@@ -0,0 +1,164 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user