Files
luban-lite/packages/artinchip/mpp/middle_media/base/parser/flv/flv.c

958 lines
30 KiB
C
Raw Normal View History

2025-01-08 19:12:06 +08:00
/*
* Copyright (C) 2020-2024 ArtInChip Technology Co. Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Author: <che.jiang@artinchip.com>
* Desc: flv file demuxer
*/
#include "flv.h"
#include "aic_parser.h"
#include "aic_stream.h"
#include "aic_tag.h"
#include "mpp_log.h"
#include "mpp_mem.h"
#include <inttypes.h>
#include <stdlib.h>
#define FLV_MAX_DEPTH 16
/* TagType(1) + DataSize(3) + TimeStamp(3) + TimeStampExtended(1) + StremId(3) */
#define FLV_TAG_HEADER_SIZE 11
/* offsets for packed values */
#define FLV_AUDIO_SAMPLERATE_OFFSET 2
#define FLV_AUDIO_CODECID_OFFSET 4
#define FLV_VIDEO_FRAMETYPE_OFFSET 4
/* bitmasks to isolate specific values */
#define FLV_VIDEO_FRAMETYPE_MASK 0xf0
#define AMF_END_OF_OBJECT 0x09
/* AMF1 Packet: AMFType(1) + AMFLen(2) + AMFValue(10)*/
#define AMF1_TAG_HEADER_SIZE 13
/* AMF2 Packet: AMFType(1) + AMFLen(4)*/
#define AMF2_TAG_HEADER_SIZE 5
#define FLV_TYPE_ONTEXTDATA 1
#define FLV_TYPE_ONCAPTION 2
#define FLV_TYPE_ONCAPTIONINFO 3
#define FLV_TYPE_UNKNOWN 9
typedef enum {
FLV_HEADER_FLAG_HASVIDEO = 1,
FLV_HEADER_FLAG_HASAUDIO = 4,
} FLV_HEADER_FLAG;
typedef enum {
FLV_TAG_TYPE_AUDIO = 0x08,
FLV_TAG_TYPE_VIDEO = 0x09,
FLV_TAG_TYPE_META = 0x12,
} FLV_TAG_TYPE;
typedef enum {
FLV_STREAM_TYPE_VIDEO,
FLV_STREAM_TYPE_AUDIO,
FLV_STREAM_TYPE_SUBTITLE,
FLV_STREAM_TYPE_DATA,
FLV_STREAM_TYPE_NB,
} FLV_STREAM_TYPE;
typedef enum {
FLV_MONO = 0,
FLV_STEREO = 1,
} FLV_CHANNEL;
typedef enum {
FLV_CODECID_PCM = 0,
FLV_CODECID_ADPCM = 1 << FLV_AUDIO_CODECID_OFFSET,
FLV_CODECID_MP3 = 2 << FLV_AUDIO_CODECID_OFFSET,
FLV_CODECID_PCM_LE = 3 << FLV_AUDIO_CODECID_OFFSET,
FLV_CODECID_NELLYMOSER_16KHZ_MONO = 4 << FLV_AUDIO_CODECID_OFFSET,
FLV_CODECID_NELLYMOSER_8KHZ_MONO = 5 << FLV_AUDIO_CODECID_OFFSET,
FLV_CODECID_NELLYMOSER = 6 << FLV_AUDIO_CODECID_OFFSET,
FLV_CODECID_PCM_ALAW = 7 << FLV_AUDIO_CODECID_OFFSET,
FLV_CODECID_PCM_MULAW = 8 << FLV_AUDIO_CODECID_OFFSET,
FLV_CODECID_AAC = 10 << FLV_AUDIO_CODECID_OFFSET,
FLV_CODECID_SPEEX = 11 << FLV_AUDIO_CODECID_OFFSET,
} FLV_CODECID_AUDIO;
typedef enum {
FLV_CODECID_H263 = 2,
FLV_CODECID_SCREEN = 3,
FLV_CODECID_VP6 = 4,
FLV_CODECID_VP6A = 5,
FLV_CODECID_SCREEN2 = 6,
FLV_CODECID_H264 = 7,
FLV_CODECID_REALH263 = 8,
FLV_CODECID_MPEG4 = 9,
} FLV_CODECID_VIDEO;
typedef enum {
FLV_AVC_PACKET_HEADER = 0,
FLV_AVC_PACKET_NALU = 1,
FLV_AVC_PACKET_TAIL = 2,
} FLV_AVC_PACKET_TYPE;
typedef enum {
/* key frame (for AVC, a seekable frame) */
FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET,
/* inter frame (for AVC, a non-seekable frame) */
FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET,
/* disposable inter frame (H.263 only) */
FLV_FRAME_DISP_INTER = 3 << FLV_VIDEO_FRAMETYPE_OFFSET,
/* generated key frame (reserved for server use only)*/
FLV_FRAME_GENERATED_KEY = 4 << FLV_VIDEO_FRAMETYPE_OFFSET,
/* video info/command frame*/
FLV_FRAME_VIDEO_INFO_CMD = 5 << FLV_VIDEO_FRAMETYPE_OFFSET,
} FLV_FRAME;
typedef enum {
AMF_DATA_TYPE_NUMBER = 0x00,
AMF_DATA_TYPE_BOOL = 0x01,
AMF_DATA_TYPE_STRING = 0x02,
AMF_DATA_TYPE_OBJECT = 0x03,
AMF_DATA_TYPE_NULL = 0x05,
AMF_DATA_TYPE_UNDEFINED = 0x06,
AMF_DATA_TYPE_REFERENCE = 0x07,
AMF_DATA_TYPE_MIXEDARRAY = 0x08,
AMF_DATA_TYPE_OBJECT_END = 0x09,
AMF_DATA_TYPE_ARRAY = 0x0a,
AMF_DATA_TYPE_DATE = 0x0b,
AMF_DATA_TYPE_LONG_STRING = 0x0c,
AMF_DATA_TYPE_UNSUPPORTED = 0x0d,
} FLV_AMF_DATA_TYPE;
typedef struct flv_context {
int trust_metadata; // configure streams according onMetaData
int wrong_dts; // wrong dts due to negative cts
int framerate;
unsigned int packet_cnt;
int64_t video_bit_rate;
int64_t audio_bit_rate;
int64_t seek_pts;
int64_t last_pts;
int64_t cur_pts;
int64_t cur_pos;
int64_t last_pos;
int64_t pre_key_pts;
int64_t pre_key_pos;
int64_t last_key_pts;
int64_t last_key_pos;
int64_t next_key_pos;
char has_video;
char has_audio;
char find_key_frame;
char seek_dir; // 0 - none, 1 - forward, 2 - backward
int orig_size;
int packet_size;
int pre_tag_size;
int cur_stream_type;
} flv_context;
union flv_intfloat64 {
uint64_t i;
double f;
};
static double flv_int2double(uint64_t i)
{
union flv_intfloat64 v;
v.i = i;
return v.f;
}
static struct flv_stream_ctx *flv_new_stream(struct aic_flv_parser *s)
{
struct flv_stream_ctx *sc;
sc = (struct flv_stream_ctx *)mpp_alloc(sizeof(struct flv_stream_ctx));
if (sc == NULL) {
return NULL;
}
memset(sc, 0, sizeof(struct flv_stream_ctx));
sc->index = s->nb_streams;
s->streams[s->nb_streams++] = sc;
return sc;
}
static struct flv_stream_ctx *flv_find_stream(struct aic_flv_parser *s, int stream_type)
{
int i = 0;
struct flv_stream_ctx *st = NULL;
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
if (stream_type == FLV_STREAM_TYPE_AUDIO) {
if (st->codecpar.codec_type == MPP_MEDIA_TYPE_AUDIO)
break;
} else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
if (st->codecpar.codec_type == MPP_MEDIA_TYPE_VIDEO)
break;
} else if (stream_type == FLV_STREAM_TYPE_SUBTITLE ||
stream_type == FLV_STREAM_TYPE_DATA) {
if (st->codecpar.codec_type == MPP_MEDIA_TYPE_OTHER)
break;
}
}
if (i == s->nb_streams) {
logw("unsupport another stream_type %d.", stream_type);
return NULL;
}
return st;
}
static void flv_set_audio_codec(struct aic_flv_parser *s, struct flv_stream_ctx *astream,
struct aic_codec_param *apar, int flv_codecid)
{
switch (flv_codecid) {
// no distinction between S16 and S8 PCM codec flags
case FLV_CODECID_PCM:
apar->codec_id = apar->bits_per_coded_sample == 8
? CODEC_ID_PCM_U8
#if HAVE_BIGENDIAN
: CODEC_ID_PCM_S16BE;
#else
: CODEC_ID_PCM_S16LE;
#endif
break;
case FLV_CODECID_AAC:
apar->codec_id = CODEC_ID_AAC;
break;
case FLV_CODECID_MP3:
apar->codec_id = CODEC_ID_MP3;
break;
default:
loge("unsupport audio codec (%x)",
flv_codecid >> FLV_AUDIO_CODECID_OFFSET);
apar->codec_tag = flv_codecid >> FLV_AUDIO_CODECID_OFFSET;
}
}
static int flv_set_video_codec(struct aic_flv_parser *s, struct flv_stream_ctx *vstream,
int flv_codecid, int read)
{
int ret = 0;
struct aic_codec_param *par = &vstream->codecpar;
switch (flv_codecid) {
case FLV_CODECID_H264:
par->codec_id = CODEC_ID_H264;
ret = 3; // not 4, reading packet type will consume one byte
break;
case FLV_CODECID_MPEG4:
par->codec_id = CODEC_ID_MPEG4;
ret = 3;
break;
default:
loge("unsupport video codec (%x)", flv_codecid);
par->codec_tag = flv_codecid;
}
return ret;
}
static int amf_get_string(struct aic_stream *ioc, char *buffer, int buffsize)
{
int ret;
int length = aic_stream_rb16(ioc);
if (length >= buffsize) {
aic_stream_skip(ioc, length);
return PARSER_ERROR;
}
ret = aic_stream_read(ioc, buffer, length);
if (ret < 0)
return ret;
if (ret < length)
return -PARSER_INVALIDDATA;
buffer[length] = '\0';
return length;
}
static int amf_get_object(struct aic_flv_parser *s, struct flv_stream_ctx *astream,
struct flv_stream_ctx *vstream, const char *key,
FLV_AMF_DATA_TYPE amf_type, int depth, double num_val)
{
if (!key)
return PARSER_ERROR;
if (depth != 1)
return PARSER_OK;
if (amf_type != AMF_DATA_TYPE_NUMBER && amf_type != AMF_DATA_TYPE_BOOL) {
return PARSER_OK;
}
flv_context *flv = s->priv_data;
struct aic_codec_param *apar, *vpar;
apar = astream ? &astream->codecpar : NULL;
vpar = vstream ? &vstream->codecpar : NULL;
if (!strcmp(key, "duration")) {
s->duration = num_val * TIME_BASE;
} else if (!strcmp(key, "videodatarate") && 0 <= (int)(num_val * 1024.0)) {
flv->video_bit_rate = num_val * 1024.0;
} else if (!strcmp(key, "audiodatarate") && 0 <= (int)(num_val * 1024.0)) {
flv->audio_bit_rate = num_val * 1024.0;
} else if (!strcmp(key, "framerate")) {
flv->framerate = num_val;
} else if (flv->trust_metadata) {
if (!strcmp(key, "videocodecid") && vpar) {
int ret = flv_set_video_codec(s, vstream, (int)num_val, 0);
if (ret < 0)
return ret;
} else if (!strcmp(key, "audiocodecid") && apar) {
int id = ((int)num_val) << FLV_AUDIO_CODECID_OFFSET;
flv_set_audio_codec(s, astream, apar, id);
} else if (!strcmp(key, "audiosamplerate") && apar) {
apar->sample_rate = num_val;
} else if (!strcmp(key, "audiosamplesize") && apar) {
apar->bits_per_coded_sample = num_val;
} else if (!strcmp(key, "stereo") && apar) {
apar->channels = num_val + 1;
} else if (!strcmp(key, "width") && vpar) {
vpar->width = num_val;
} else if (!strcmp(key, "height") && vpar) {
vpar->height = num_val;
}
}
return PARSER_OK;
}
static int amf_parse_object(struct aic_flv_parser *s, struct flv_stream_ctx *astream,
struct flv_stream_ctx *vstream, const char *key,
int64_t max_pos, int depth)
{
struct aic_stream *ioc;
FLV_AMF_DATA_TYPE amf_type;
char str_val[512];
double num_val;
if (depth > FLV_MAX_DEPTH)
return PARSER_ERROR;
num_val = 0;
ioc = s->stream;
amf_type = aic_stream_r8(ioc);
switch (amf_type) {
case AMF_DATA_TYPE_NUMBER:
num_val = flv_int2double(aic_stream_rb64(ioc));
logi("amf_type = %d key:%s num_val = %f.", amf_type, key, num_val);
break;
case AMF_DATA_TYPE_BOOL:
num_val = aic_stream_r8(ioc);
break;
case AMF_DATA_TYPE_STRING:
if (amf_get_string(ioc, str_val, sizeof(str_val)) < 0) {
loge("AMF_DATA_TYPE_STRING parsing failed\n");
return PARSER_ERROR;
}
break;
case AMF_DATA_TYPE_OBJECT:
while (aic_stream_tell(ioc) < max_pos - 2 &&
amf_get_string(ioc, str_val, sizeof(str_val)) > 0)
if (amf_parse_object(s, astream, vstream, str_val, max_pos,
depth + 1) < 0)
return PARSER_ERROR; // if we couldn't skip, bomb out.
if (aic_stream_r8(ioc) != AMF_END_OF_OBJECT) {
loge("Missing AMF_END_OF_OBJECT in AMF_DATA_TYPE_OBJECT\n");
return PARSER_ERROR;
}
break;
case AMF_DATA_TYPE_NULL:
case AMF_DATA_TYPE_UNDEFINED:
case AMF_DATA_TYPE_UNSUPPORTED:
break; // these take up no additional space
case AMF_DATA_TYPE_MIXEDARRAY: {
unsigned v;
aic_stream_skip(ioc, 4); // skip 32-bit max array index
while (aic_stream_tell(ioc) < max_pos - 2 &&
amf_get_string(ioc, str_val, sizeof(str_val)) > 0)
/*this is the only case in which we would want a nested
parse to not skip over the object */
if (amf_parse_object(s, astream, vstream, str_val, max_pos,
depth + 1) < 0)
return PARSER_ERROR;
v = aic_stream_r8(ioc);
if (v != AMF_END_OF_OBJECT) {
loge("Missing AMF_END_OF_OBJECT in AMF_DATA_TYPE_MIXEDARRAY, found %d\n", v);
return PARSER_ERROR;
}
break;
}
case AMF_DATA_TYPE_ARRAY: {
unsigned int arraylen, i;
arraylen = aic_stream_rb32(ioc);
for (i = 0; i < arraylen && aic_stream_tell(ioc) < max_pos - 1; i++)
if (amf_parse_object(s, NULL, NULL, NULL, max_pos,
depth + 1) < 0)
return PARSER_ERROR; // if we couldn't skip, bomb out.
} break;
case AMF_DATA_TYPE_DATE:
aic_stream_rb64(ioc);
aic_stream_rb16(ioc);
break;
default: // unsupported type, we couldn't skip
loge("unsupported amf type %d\n", amf_type);
return PARSER_ERROR;
}
amf_get_object(s, astream, vstream, key, amf_type, depth, num_val);
return PARSER_OK;
}
static int flv_read_metabody(struct aic_flv_parser *s, int64_t next_pos)
{
int ret = PARSER_OK;
int i = 0;
char buffer[32];
FLV_AMF_DATA_TYPE type;
struct aic_stream *ioc = NULL;
struct flv_stream_ctx *stream = NULL;
struct flv_stream_ctx *astream = NULL;
struct flv_stream_ctx *vstream = NULL;
ioc = s->stream;
/* first object needs to be "onMetaData" string */
type = aic_stream_r8(ioc);
if (type != AMF_DATA_TYPE_STRING) {
loge("type %d is not support or amf.", type);
return FLV_TYPE_UNKNOWN;
}
ret = amf_get_string(ioc, buffer, sizeof(buffer));
if (ret < 0) {
loge("amf_get_string failed %d.", ret);
return FLV_TYPE_UNKNOWN;
}
if (!strcmp(buffer, "onTextData")) {
loge("the tag is %s, not metadata.", buffer);
return FLV_TYPE_ONTEXTDATA;
}
if (!strcmp(buffer, "onCaption")) {
loge("the tag is %s, not metadata.", buffer);
return FLV_TYPE_ONCAPTION;
}
if (!strcmp(buffer, "onCaptionInfo")) {
loge("the tag is %s, not metadata.", buffer);
return FLV_TYPE_ONCAPTIONINFO;
}
if (strcmp(buffer, "onMetaData") && strcmp(buffer, "onCuePoint")) {
logi("Unknown type %s.", buffer);
return FLV_TYPE_UNKNOWN;
}
logi("type = 0x%x, buffer:%s.", type, buffer);
/* find the streams now so that amf_parse_object doesn't need to do
the lookup every time it is called.*/
for (i = 0; i < s->nb_streams; i++) {
stream = s->streams[i];
if (stream->codecpar.codec_type == MPP_MEDIA_TYPE_VIDEO) {
vstream = stream;
} else if (stream->codecpar.codec_type == MPP_MEDIA_TYPE_AUDIO) {
astream = stream;
} else {
loge("can not find stream type %d.", stream->codecpar.codec_type);
return PARSER_NODATA;
}
}
/* parse the second object (we want a mixed array)*/
ret = amf_parse_object(s, astream, vstream, buffer, next_pos, 0);
if (ret < 0) {
loge("metadata amf parse object failed 0x%x.", ret);
return PARSER_ERROR;
}
return PARSER_OK;
}
int flv_read_metadata(struct aic_flv_parser *s)
{
int ret, size;
FLV_TAG_TYPE type;
int64_t next;
int64_t dts = 0;
int pre_tag_size = 0;
/* pkt size is repeated at end. skip it */
aic_stream_tell(s->stream);
type = (aic_stream_r8(s->stream) & 0x1F); /*constant value 18*/
size = aic_stream_rb24(s->stream);
dts = aic_stream_rb24(s->stream); /* millseconds */
dts |= aic_stream_r8(s->stream) << 28;
logi("type:%d, size:%d, pos:%" PRId64 "\n",
type, size, (int64_t)aic_stream_tell(s->stream));
if (aic_stream_tell(s->stream) >= aic_stream_size(s->stream))
return PARSER_EOS;
aic_stream_skip(s->stream, 3); /* stream id, always 0 */
next = size + aic_stream_tell(s->stream);
if (type != FLV_TAG_TYPE_META) {
loge("Not include the first metadata tag.\n");
return PARSER_INVALIDDATA;
}
/* Header-type metadata stuff check*/
if (size <= AMF1_TAG_HEADER_SIZE + AMF2_TAG_HEADER_SIZE) {
loge("Metadata tag size is %d.\n", size);
return PARSER_INVALIDDATA;
}
/* Read metabody to get audio and video params*/
aic_stream_tell(s->stream);
ret = flv_read_metabody(s, next);
if (ret != PARSER_OK) {
loge("metabody mismatch err = %d\n", ret);
return PARSER_INVALIDDATA;
}
pre_tag_size = aic_stream_rb32(s->stream);
if (pre_tag_size != size + FLV_TAG_HEADER_SIZE) {
logw("Metadata size %d != %d check failed\n",
pre_tag_size, size + FLV_TAG_HEADER_SIZE);
}
return PARSER_OK;
}
int flv_read_header(struct aic_flv_parser *s)
{
int flags;
int offset;
int pre_tag_size = 0;
flv_context *flv = NULL;
struct flv_stream_ctx *st = NULL;
s->priv_data = mpp_alloc(sizeof(struct flv_context));
if (!s->priv_data) {
loge("malloc for flv_context failed.\n");
return PARSER_NOMEM;
}
memset(s->priv_data, 0, sizeof(struct flv_context));
aic_stream_skip(s->stream, 4);
flags = aic_stream_r8(s->stream);
logi("read flv header flags = 0x%x.", flags);
flv = s->priv_data;
flv->trust_metadata = 1;
offset = aic_stream_rb32(s->stream);
aic_stream_seek(s->stream, offset, SEEK_SET);
pre_tag_size = aic_stream_rb32(s->stream);
if (pre_tag_size) {
logw("Unstandard flv format, pre_tag_size(%d)\n", pre_tag_size);
}
/*Has video and audio, then create the stream in advanced*/
if (flags & FLV_HEADER_FLAG_HASVIDEO) {
flv->has_video = 1;
st = flv_new_stream(s);
if (!st) {
loge("create stream for video failed.");
return PARSER_NOMEM;
}
st->codecpar.codec_type = MPP_MEDIA_TYPE_VIDEO;
}
if (flags & FLV_HEADER_FLAG_HASAUDIO) {
flv->has_audio = 1;
st = flv_new_stream(s);
if (!st) {
loge("create stream for audio failed.");
return PARSER_NOMEM;
}
st->codecpar.codec_type = MPP_MEDIA_TYPE_AUDIO;
}
/*Read data of metadata, then get audio and video parametes*/
if (flv_read_metadata(s)) {
loge("flv read metadata failed.");
return PARSER_INVALIDDATA;
}
logi("flv read header success.");
return PARSER_OK;
}
int flv_read_close(struct aic_flv_parser *s)
{
int i = 0;
struct flv_stream_ctx *st = NULL;
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
if (st)
mpp_free(st);
}
if (s->priv_data)
mpp_free(s->priv_data);
return PARSER_OK;
}
static int64_t flv_sat_add64(int64_t a, int64_t b)
{
int64_t s = a + (uint64_t)b;
if ((int64_t)((a ^ b) | (~s ^ b)) >= 0)
return INT64_MAX ^ (b >> 63);
return s;
}
int flv_seek_audio_pos(struct aic_flv_parser *s, int64_t next)
{
flv_context *flv = s->priv_data;
if (flv->seek_dir == 1) { //forward
if (flv->seek_pts > flv->cur_pts * 1000) {
aic_stream_seek(s->stream, next + 4, SEEK_SET);
} else {
flv->seek_dir = 0; // reset direction
return 0;
}
} else if (flv->seek_dir == 2) { //backward
if (flv->seek_pts < flv->cur_pts * 1000) {
/*Get the pre frame size then seek to the pre frame*/
aic_stream_seek(s->stream, flv->cur_pos - 4, SEEK_SET);
int pre_tag_size = aic_stream_rb32(s->stream);
int64_t pre_tag_pos = flv->cur_pos - (pre_tag_size + 4);
if (pre_tag_pos >= 0) {
aic_stream_seek(s->stream, pre_tag_pos, SEEK_SET);
} else {
aic_stream_seek(s->stream, 0, SEEK_SET);
flv->seek_dir = 0;
return 0;
}
} else {
flv->seek_dir = 0;
return 0;
}
} else {
flv->seek_dir = 0; // reset direction
return 0;
}
return 1;
}
int flv_seek_video_pos(struct aic_flv_parser *s, int stream_type, int flags, int64_t next)
{
int64_t diff = 0;
static int64_t min_diff = INT64_MAX;
flv_context *flv = s->priv_data;
if (flv->seek_dir == 1) { //forward
logd("seek flv packet: seek_pts %"PRId64", cur_pts %"PRId64".\n", flv->seek_pts, next);
if (flv->seek_pts >= flv->cur_pts * 1000) {
diff = MPP_ABS(flv->seek_pts, flv->cur_pts * 1000);
if (min_diff == INT64_MAX) {
flv->pre_key_pos = flv->last_key_pos;
flv->pre_key_pts = flv->last_key_pts;
}
if (diff < min_diff) {
min_diff = diff;
if (stream_type == FLV_STREAM_TYPE_VIDEO &&
((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY)) {
flv->find_key_frame = 1;
flv->last_key_pos = flv->cur_pos;
}
}
/*Seek next packet need skip pre_tag_size*/
aic_stream_seek(s->stream, next + 4, SEEK_SET);
} else {
/*If find key frame then seek to key frame postion,
otherwise seek to the start postion */
if (flv->find_key_frame) {
aic_stream_seek(s->stream, flv->last_key_pos, SEEK_SET);
logi("find key frame, key_pos: %"PRId64"", flv->last_key_pos);
flv->find_key_frame = 0;
flv->seek_dir = 0; // reset direction
} else {
/*Forward to find the next key frame, compare with the pre key pos
and get the min diff time, finally get the position*/
if (stream_type == FLV_STREAM_TYPE_VIDEO &&
((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY)) {
logw("cur_pts = %"PRId64", pre_key_pts = %"PRId64", seek_pts = %"PRId64"",
flv->cur_pts * 1000, flv->pre_key_pts * 1000, flv->seek_pts);
diff = MPP_ABS(flv->seek_pts, flv->cur_pts * 1000);
if (diff <= MPP_ABS(flv->seek_pts, flv->pre_key_pts * 1000)) {
flv->find_key_frame = 1;
flv->last_key_pos = flv->cur_pos;
flv->seek_dir = 0; // reset direction
min_diff = INT64_MAX;
logi("find key frame, key_pos: %"PRId64", key_pts: %"PRId64"",
flv->cur_pos, flv->cur_pts * 1000);
return 0;
} else {
aic_stream_seek(s->stream, flv->pre_key_pos, SEEK_SET);
}
} else {
aic_stream_seek(s->stream, next + 4, SEEK_SET);
logd("can not find key frame, then find next frame");
}
}
}
} else if (flv->seek_dir == 2) { //backward
if (flv->seek_pts < flv->cur_pts * 1000) {
/*Get the pre frame size then seek to the pre frame*/
aic_stream_seek(s->stream, flv->cur_pos - 4, SEEK_SET);
int pre_tag_size = aic_stream_rb32(s->stream);
int64_t pre_tag_pos = flv->cur_pos - (pre_tag_size + 4);
if (pre_tag_pos >= 0) {
aic_stream_seek(s->stream, pre_tag_pos, SEEK_SET);
} else {
aic_stream_seek(s->stream, 0, SEEK_SET);
flv->seek_dir = 0;
return 0;
}
} else {
/*If not find key frame need to find the pre frame*/
if (stream_type == FLV_STREAM_TYPE_VIDEO &&
((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY)) {
flv->find_key_frame = 1;
flv->last_key_pos = flv->cur_pos;
flv->seek_dir = 0;
return 0;
} else {
if (flv->cur_pts == 0) {
flv->seek_dir = 0;
return 0;
}
/*Get the pre frame size then seek to the pre frame*/
aic_stream_seek(s->stream, flv->cur_pos - 4, SEEK_SET);
int pre_tag_size = aic_stream_rb32(s->stream);
int64_t pre_tag_pos = flv->cur_pos - (pre_tag_size + 4);
if (pre_tag_pos >= 0) {
aic_stream_seek(s->stream, pre_tag_pos, SEEK_SET);
} else {
aic_stream_seek(s->stream, 0, SEEK_SET);
flv->seek_dir = 0;
return 0;
}
}
}
} else {
flv->seek_dir = 0; // reset direction
return 0;
}
return 1; // need try again
}
int flv_get_packet_data(struct aic_flv_parser *s, struct flv_stream_ctx *st,
struct aic_parser_packet *pkt)
{
flv_context *flv = s->priv_data;
if (st->codecpar.codec_id == CODEC_ID_AAC ||
st->codecpar.codec_id == CODEC_ID_H264 ||
st->codecpar.codec_id == CODEC_ID_MPEG4) {
uint8_t packet_type = aic_stream_r8(s->stream);
flv->packet_size--;
if (flv->packet_size < 0) {
return PARSER_INVALIDDATA;
}
if ((st->codecpar.codec_id == CODEC_ID_H264 || st->codecpar.codec_id == CODEC_ID_MPEG4)) {
if (packet_type == FLV_AVC_PACKET_HEADER) {
pkt->flag = PACKET_EXTRA_DATA;
} else if (packet_type == FLV_AVC_PACKET_TAIL) {
pkt->flag = PACKET_EOS;
pkt->size = 0;
return PARSER_EOS;
}
// sign extension
int32_t cts = (aic_stream_rb24(s->stream) + 0xff800000) ^ 0xff800000;
int64_t pts = flv_sat_add64(flv->cur_pts, cts);
if (cts < 0) { // dts might be wrong
if (!flv->wrong_dts)
logw("Negative cts, previous timestamps might be wrong.\n");
flv->wrong_dts = 1;
} else if (MPP_ABS(flv->cur_pts, pts) > 1000 * 60 * 15) {
logw("invalid timestamps %" PRId64 " %" PRId64 "\n", flv->cur_pts, pts);
flv->cur_pts = 0;
return PARSER_INVALIDDATA;
}
flv->packet_size -= 3;
if (flv->packet_size < 0) {
return PARSER_INVALIDDATA;
}
flv->cur_pts = pts;
}
}
return PARSER_OK;
}
int flv_peek_packet(struct aic_flv_parser *s, struct aic_parser_packet *pkt)
{
flv_context *flv = s->priv_data;
int ret = 0, size, flags;
int stream_type = -1;
FLV_TAG_TYPE type;
int64_t next, pos;
int64_t dts = 0;
struct flv_stream_ctx *st = NULL;
int last = -1;
retry:
/* pkt size is repeated at end. skip it */
pos = aic_stream_tell(s->stream);
type = (aic_stream_r8(s->stream) & 0x1F);
size = aic_stream_rb24(s->stream);
dts = aic_stream_rb24(s->stream); /* millseconds */
dts |= (unsigned)aic_stream_r8(s->stream) << 24;
flv->cur_pts = dts;
if (aic_stream_tell(s->stream) >= aic_stream_size(s->stream)) {
pkt->flag = PACKET_EOS;
pkt->size = 0;
return PARSER_EOS;
}
flv->orig_size = size;
flv->cur_pos = pos;
logd("type:%d, size:%d, last:%d, dts:%"PRId64", pos:%"PRId64", packet_cnt:%u\n",
type, size, last, dts, pos, flv->packet_cnt);
aic_stream_skip(s->stream, 3); /* stream id, always 0 */
if (size == 0) {
ret = PARSER_ERROR;
goto out;
}
flags = 0;
next = size + aic_stream_tell(s->stream);
if (type == FLV_TAG_TYPE_AUDIO) {
stream_type = FLV_STREAM_TYPE_AUDIO;
flags = aic_stream_r8(s->stream);
size--;
pkt->type = MPP_MEDIA_TYPE_AUDIO;
} else if (type == FLV_TAG_TYPE_VIDEO) {
stream_type = FLV_STREAM_TYPE_VIDEO;
flags = aic_stream_r8(s->stream);
size--;
pkt->type = MPP_MEDIA_TYPE_VIDEO;
if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY) {
flv->last_key_pts = flv->cur_pts;
flv->last_key_pos = flv->cur_pos;
}
if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD)
goto skip;
} else {
loge("Skipping flv packet: type %d, size %d, flags %d.\n", type, size, flags);
skip:
/* May be happend err in this situation. */
if (aic_stream_seek(s->stream, next, SEEK_SET) != next) {
loge("Unable to seek to the next packet\n");
return PARSER_INVALIDDATA;
}
ret = PARSER_ERROR;
goto out;
}
/* skip empty data packets */
if (!size) {
ret = PARSER_ERROR;
goto out;
}
st = flv_find_stream(s, stream_type);
if (!st) {
loge("find stream_type %d failed.", stream_type);
return PARSER_NOMEM;
}
/* seek packet by pts */
flv->packet_size = size;
if (flv->seek_dir && (flv->seek_pts >= 0)) {
logd("seek flv packet: type %d, flags %d, next %"PRId64".\n", stream_type, flags, next);
if (!flv->has_video && flv->has_audio) {
if (flv_seek_audio_pos(s, next))
goto retry;
} else if (flv->has_video) {
if (flv_seek_video_pos(s, stream_type, flags, next))
goto retry;
}
}
pkt->flag = 0;
ret = flv_get_packet_data(s, st, pkt);
if (ret != PARSER_OK) {
goto out;
}
pkt->pts = flv->cur_pts * 1000;
pkt->size = flv->packet_size;
flv->last_pts = flv->cur_pts;
flv->last_pos = pos;
flv->cur_pos = aic_stream_tell(s->stream);
flv->packet_cnt++;
logd("peek flv packet: type = %d, size = %d, pts = %lld.\n",
pkt->type, pkt->size, pkt->pts);
out:
return ret;
}
int flv_read_packet(struct aic_flv_parser *s, struct aic_parser_packet *pkt)
{
flv_context *flv = s->priv_data;
int pre_tag_size = -1;
aic_stream_seek(s->stream, flv->cur_pos, SEEK_SET);
aic_stream_read(s->stream, pkt->data, pkt->size);
pre_tag_size = aic_stream_rb32(s->stream);
if (pre_tag_size != flv->orig_size + FLV_TAG_HEADER_SIZE) {
loge("Packet mismatch %d %d\n", pre_tag_size, flv->orig_size + FLV_TAG_HEADER_SIZE);
}
logd("read flv packet: type %d, size %d, cur_pos %"PRId64", pts %lld\n",
pkt->type, pkt->size, flv->cur_pos, pkt->pts);
return PARSER_OK;
}
int flv_seek_packet(struct aic_flv_parser *s, s64 pts)
{
flv_context *flv = s->priv_data;
flv->seek_pts = pts;
flv->seek_dir = (pts > flv->cur_pts * 1000) ? 1 : 2;
if (flv->seek_pts < 0)
flv->seek_dir = 0;
/*Reset to cur position avoid some err happend*/
if (flv->seek_dir && flv->seek_pts >= 0) {
aic_stream_seek(s->stream, flv->last_pos, SEEK_SET);
}
logi("seek_pts:%lld, cur_pts:%"PRId64", last_pos:%"PRId64", seek_dir:%d, packet_cnt:%u",
pts, flv->cur_pts * 1000, flv->last_pos, flv->seek_dir, flv->packet_cnt);
return PARSER_OK;
}