Older/MediaServer/libflv/source/flv-parser.c

262 lines
7.7 KiB
C
Raw Normal View History

2024-10-01 00:12:57 +08:00
#include "flv-parser.h"
#include "flv-header.h"
#include "flv-proto.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#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;
}