me
me copied to clipboard
学习 rtmp 开发 (Part 3: FLV Format)
FLV: Flash Video.
FLV Layout:
- The FLV header
- The FLV file body
- PreviousTagSize0
- Tag1
- PreviousTagSize1
- Tag2
- ...
- PreviousTagSizeN-1
- TagN
- PreviousTagSizeN
main ()
int main(int argc, char *argv[]) {
RTMP_LogSetLevel(RTMP_LOGINFO);
char *prog = argv[0];
int c;
while ((c = getopt(argc, argv, "vV")) != -1) {
switch (c) {
case 'v':
RTMP_LogSetLevel(RTMP_LOGDEBUG);
break;
case 'V':
RTMP_LogSetLevel(RTMP_LOGDEBUG2);
break;
default:
usage(prog);
break;
}
}
RTMP_Log(RTMP_LOGDEBUG, "input: %s", argv[1]);
infile = fopen(argv[optind], "r");
if (!infile) {
usage(argv[0]);
}
flv_read_header();
flv_tag_t *tag;
while ((tag = flv_read_tag()) != NULL) {
push_tag(tag);
print_tag(tag);
if (feof(infile)) break;
}
printf("flv tag count: %lu\n", get_tag_count());
// video tags
int i = 0;
flv_tag_t *current = flv_tag_list_head;
if (current != NULL) {
do {
if (TAGTYPE_VIDEODATA == current->tag_type) {
++i;
RTMP_Log(RTMP_LOGINFO, "%s, t: %d, data size: %d", flv_tag_types[current->tag_type], current->timestamp, current->data_size);
}
} while ((current = (flv_tag_t *) current->next) != NULL && i < 10);
}
RTMP_Log(RTMP_LOGDEBUG, "the end.");
release();
return 0;
}
说明
- linux c真的是啥都没有, 比如list, hash map, lambda 😖
- linux c一个c就是一个世界,一个module,要充分理解这个概念,一个是linus/git, 一个是rtmpdump,每个.c对应到.o,然后形成lib(.a/.so),然后外部直接用.o去实现模块组合,形成多个target,一组script, git/ffmpeg都是这个套路
-
size_t fread(void *buffer, size_t size, size_t count, FILE *stream);
,size是每个读取单元的大小,而count是读的批次数- 读取10个char, 则size: sizeof(char), count: 10
- 读取1个struct header,则size: sizeof(header), count:1
- 内存和文件如果都采用LE则通过struct和buffer直接mapping,但需要设置
#pragma pack(1)
, gcc则更多用__attribute__((__packed__)) __
- flv则采用BE,所以很多时候不能直接mapping,需要通过重置byte order来实现
- RTMP_Log不错,可以实现error, debug, info多个level
AVCC and AnnexB
void generate_h264_file() {
// open outfile
FILE *outfile = fopen("out.h264", "wb");
static const byte startcode[] = {0x00, 0x00, 0x00, 0x01};
// write pps/sps
flv_tag_t *current = flv_tag_list_head;
assert(current);
do {
if (TAGTYPE_VIDEODATA == current->tag_type) {
video_tag_t *video_tag = (video_tag_t *) current->data;
avc_video_packet_t *packet = (avc_video_packet_t *) video_tag->data;
if (AVC_SEQUENCE_HEADER == packet->avc_packet_type) {
avc_decoder_configuration_record_t *record = (avc_decoder_configuration_record_t *) packet->data;
RTMP_LogHex(RTMP_LOGINFO, record->sps, record->sequenceParameterSetLength);
fwrite(&startcode, sizeof(startcode), 1, outfile);
fwrite(record->sps, record->sequenceParameterSetLength, 1, outfile);
RTMP_LogHex(RTMP_LOGINFO, record->pps, record->pictureParameterSetLength);
fwrite(&startcode, sizeof(startcode), 1, outfile);
fwrite(record->pps, record->pictureParameterSetLength, 1, outfile);
} else if (AVC_NALU == packet->avc_packet_type) {
avc_nalus_t *nalus = (avc_nalus_t *) packet->data;
// AVCC to AnnexB
uint32_t offset = 0;
uint32_t nalu_len = 0;
while (offset < (nalus->size - 4)) {
memcpy(&nalu_len, nalus->data + offset, 4);
nalu_len = ntohl(nalu_len);
printf(".");
fwrite(&startcode, sizeof(startcode), 1, outfile);
fwrite(nalus->data + offset + 4, nalu_len, 1, outfile);
offset += nalu_len + 4;
}
}
}
} while ((current = (flv_tag_t *) current->next) != NULL);
printf("\n");
fclose(outfile);
}
- VideoToolBox和flv都采用AVCC,而h264则采用AnnexB,所以需要将每个NALU拆开后通过start code隔开保存
- Akagi201的代码有点问题,没有考虑多个NALU的情况,struct中不能直接写nalu_len,因为会有多个的
最后
- 完整代码在这里: nonocast/hello-rtmp
- 下载: hello-rtmp.zip
- 感谢 Akagi201/flv-parser, 学到很多
参考文档
- Video File Format Specification Version 10
- [Adobe Flash Video File Format Specification 10.1.2.01 - GitHub]
- 视音频编解码学习工程:FLV封装格式分析器_雷霄骅的博客-CSDN博客
- 视音频数据处理入门:FLV封装格式解析_雷霄骅的博客-CSDN博客_flv封装格式
- FLV 文件格式分析 - 知乎
- FLV文件格式解析_Dillon2015的博客-CSDN博客_flv文件格式解析
- 也说FLV格式分析(C语言从0开始,详解,完整版)_spygg的博客-CSDN博客
- 一张图看懂FLV文件格式 | 彩色世界
- FLV封装格式介绍及解析 - 掘金
- 重要 FLV格式分析 - 简书
- Ffmpeg对sps/pps的解析和格式转换 - 云+社区 - 腾讯云
- 将h.264视频流封装成flv格式文件(二.开始动手)_yeyumin89的博客-CSDN博客
- Live RTMP Java Publisher: H264 AVCVIDEOPACKET
- 重要 h264和aac封装flv - Z--Y - 博客园
- Possible Locations for Sequence/Picture Parameter Set(s) for H.264 Stream - Stack Overflow