#include "flv-parser.h" #include "flv-header.h" #include "flv-proto.h" #include #include #include #include #define N_TAG_SIZE 4 // previous tag size #define FLV_HEADER_SIZE 9 // DataOffset included #define FLV_TAG_HEADER_SIZE 11 // StreamID included #define FLV_VIDEO_CODEC_NAME(codecid) (FLV_VIDEO_H264==(codecid) ? FLV_VIDEO_AVCC : (FLV_VIDEO_H265==(codecid) ? FLV_VIDEO_HVCC : (FLV_VIDEO_H266==(codecid) ? FLV_VIDEO_VVCC : FLV_VIDEO_AV1C))) static int flv_parser_audio(struct flv_audio_tag_header_t* audio, const uint8_t* data, size_t bytes, uint32_t timestamp, flv_parser_handler handler, void* param) { if (FLV_SEQUENCE_HEADER == audio->avpacket) return handler(param, FLV_AUDIO_AAC == audio->codecid ? FLV_AUDIO_ASC : FLV_AUDIO_OPUS_HEAD, data, bytes, timestamp, timestamp, 0); else return handler(param, audio->codecid, data, bytes, timestamp, timestamp, 0); } static int flv_parser_video(struct flv_video_tag_header_t* video, const uint8_t* data, size_t bytes, uint32_t timestamp, flv_parser_handler handler, void* param) { if (FLV_VIDEO_H264 == video->codecid || FLV_VIDEO_H265 == video->codecid || FLV_VIDEO_H266 == video->codecid || FLV_VIDEO_AV1 == video->codecid) { if (FLV_SEQUENCE_HEADER == video->avpacket) { return handler(param, FLV_VIDEO_CODEC_NAME(video->codecid), data, bytes, timestamp, timestamp, 0); } else if (FLV_AVPACKET == video->avpacket) { return handler(param, video->codecid, data, bytes, timestamp + video->cts, timestamp, (FLV_VIDEO_KEY_FRAME == video->keyframe) ? 1 : 0); } else if (FLV_END_OF_SEQUENCE == video->avpacket) { return 0; // AVC end of sequence (lower level NALU sequence ender is not required or supported) } else { assert(0); return -EINVAL; } } else { // Video frame data return handler(param, video->codecid, data, bytes, timestamp, timestamp, (FLV_VIDEO_KEY_FRAME == video->keyframe) ? 1 : 0); } } // http://www.cnblogs.com/musicfans/archive/2012/11/07/2819291.html // metadata keyframes/filepositions static int flv_parser_script(const uint8_t* data, size_t bytes, uint32_t timestamp, flv_parser_handler handler, void* param) { return handler(param, FLV_SCRIPT_METADATA, data, bytes, timestamp, timestamp, 0); } int flv_parser_tag(int type, const void* data, size_t bytes, uint32_t timestamp, flv_parser_handler handler, void* param) { int n; struct flv_audio_tag_header_t audio; struct flv_video_tag_header_t video; if (bytes < 1) return -EINVAL; switch (type) { case FLV_TYPE_AUDIO: n = flv_audio_tag_header_read(&audio, data, bytes); if (n < 0) return n; return flv_parser_audio(&audio, (const uint8_t*)data + n, (int)bytes - n, timestamp, handler, param); case FLV_TYPE_VIDEO: n = flv_video_tag_header_read(&video, data, bytes); if (n < 0) return n; return flv_parser_video(&video, (const uint8_t*)data + n, (int)bytes - n, timestamp, handler, param); case FLV_TYPE_SCRIPT: n = flv_data_tag_header_read(data, bytes); if (n < 0) return n; return flv_parser_script((const uint8_t*)data + n, (int)bytes - n, timestamp, handler, param); default: assert(0); return -1; } } static size_t flv_parser_append(struct flv_parser_t* parser, const uint8_t* data, size_t bytes, size_t expect) { size_t n; if (parser->bytes > expect || expect > sizeof(parser->ptr)) { // invalid status, consume all assert(0); parser->bytes = expect; return bytes; } n = parser->bytes + bytes >= expect ? expect - parser->bytes : bytes; if (n > 0) { memcpy(parser->ptr + parser->bytes, data, n); parser->bytes += n; } return n; } int flv_parser_input(struct flv_parser_t* parser, const uint8_t* data, size_t bytes, flv_parser_handler handler, void* param) { int r; size_t n; uint8_t codec; uint32_t size; enum {FLV_HEADER=0, FLV_HEADER_OFFSET, FLV_PREVIOUS_SIZE, FLV_TAG_HEADER, FLV_AVHEADER_CODEC, FLV_AVHEADER_EXTRA, FLV_TAG_BODY}; for (n = r = 0; bytes > 0 && n >= 0 && 0 == r; data += n, bytes -= n) { switch (parser->state) { case FLV_HEADER: n = flv_parser_append(parser, data, bytes, FLV_HEADER_SIZE); if (FLV_HEADER_SIZE == parser->bytes) { flv_header_read(&parser->header, parser->ptr, parser->bytes); if (parser->header.offset < 9 || parser->header.offset > sizeof(parser->ptr)) return -1; parser->header.offset -= 9; parser->state = parser->header.offset > 0 ? FLV_HEADER_OFFSET : FLV_PREVIOUS_SIZE; parser->bytes = 0; } break; case FLV_HEADER_OFFSET: n = flv_parser_append(parser, data, bytes, parser->header.offset); if (parser->header.offset == (uint32_t)parser->bytes) { parser->bytes = 0; parser->state = FLV_PREVIOUS_SIZE; } break; case FLV_PREVIOUS_SIZE: n = flv_parser_append(parser, data, bytes, N_TAG_SIZE); if (N_TAG_SIZE == parser->bytes) { flv_tag_size_read(parser->ptr, parser->bytes, &size); assert(size == 0 || size == parser->tag.size + FLV_TAG_HEADER_SIZE); parser->bytes = 0; parser->state = FLV_TAG_HEADER; } break; case FLV_TAG_HEADER: n = flv_parser_append(parser, data, bytes, FLV_TAG_HEADER_SIZE); if (FLV_TAG_HEADER_SIZE == parser->bytes) { flv_tag_header_read(&parser->tag, parser->ptr, parser->bytes); parser->bytes = 0; parser->expect = 0; parser->state = FLV_AVHEADER_CODEC; } break; case FLV_AVHEADER_CODEC: switch (parser->tag.type) { case FLV_TYPE_AUDIO: parser->expect = 1; n = flv_parser_append(parser, data, bytes, 1); codec = (parser->ptr[0] & 0xF0) /*>> 4*/; if (FLV_AUDIO_AAC == codec || FLV_AUDIO_OPUS == codec) parser->expect = 2; break; case FLV_TYPE_VIDEO: parser->expect = 1; n = flv_parser_append(parser, data, bytes, 1); codec = (parser->ptr[0] & 0x0F); if (FLV_VIDEO_H264 == codec || FLV_VIDEO_H265 == codec || FLV_VIDEO_H266 == codec || FLV_VIDEO_AV1 == codec) parser->expect = 5; break; case FLV_TYPE_SCRIPT: parser->expect = 0; n = 0; // noops break; default: assert(0); return -1; // invalid flv file } parser->state = FLV_AVHEADER_EXTRA; break; case FLV_AVHEADER_EXTRA: n = flv_parser_append(parser, data, bytes, parser->expect); if (parser->expect == parser->bytes) { if(FLV_TYPE_AUDIO == parser->tag.type) flv_audio_tag_header_read(&parser->audio, parser->ptr, parser->bytes); else if(FLV_TYPE_VIDEO == parser->tag.type) flv_video_tag_header_read(&parser->video, parser->ptr, parser->bytes); parser->bytes = 0; parser->state = FLV_TAG_BODY; parser->expect = parser->tag.size - parser->expect; parser->body = parser->alloc ? parser->alloc(param, parser->expect) : malloc(parser->expect); if (!parser->body) return -1; } break; case FLV_TAG_BODY: assert(parser->body && parser->bytes <= parser->expect); n = parser->bytes + bytes >= parser->expect ? parser->expect - parser->bytes : bytes; if(n > 0) { memmove(parser->body + parser->bytes, data, n); parser->bytes += n; } if (parser->expect == parser->bytes) { parser->bytes = 0; parser->state = FLV_PREVIOUS_SIZE; switch (parser->tag.type) { case FLV_TYPE_AUDIO: r = flv_parser_audio(&parser->audio, parser->body, parser->expect, parser->tag.timestamp, handler, param); break; case FLV_TYPE_VIDEO: r = flv_parser_video(&parser->video, parser->body, parser->expect, parser->tag.timestamp, handler, param); break; case FLV_TYPE_SCRIPT: r = flv_parser_script(parser->body, parser->expect, parser->tag.timestamp, handler, param); break; default: assert(0); r = -1; break; } parser->free ? parser->free(param, parser->body) : free(parser->body); } break; default: assert(0); return -1; } } return r; }