|
|
|
@ -66,6 +66,17 @@ static int flv_probe(AVProbeData *p) |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static AVStream *create_stream(AVFormatContext *s, int tag, int codec_type) |
|
|
|
|
{ |
|
|
|
|
AVStream *st = avformat_new_stream(s, NULL); |
|
|
|
|
if (!st) |
|
|
|
|
return NULL; |
|
|
|
|
st->id = tag; |
|
|
|
|
st->codec->codec_type = codec_type; |
|
|
|
|
avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */ |
|
|
|
|
return st; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void flv_set_audio_codec(AVFormatContext *s, AVStream *astream, AVCodecContext *acodec, int flv_codecid) { |
|
|
|
|
switch(flv_codecid) { |
|
|
|
|
//no distinction between S16 and S8 PCM codec flags
|
|
|
|
@ -302,6 +313,12 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, AVStream *vst |
|
|
|
|
vcodec->bit_rate = num_val * 1024.0; |
|
|
|
|
else if (!strcmp(key, "audiodatarate") && acodec && 0 <= (int)(num_val * 1024.0)) |
|
|
|
|
acodec->bit_rate = num_val * 1024.0; |
|
|
|
|
else if (!strcmp(key, "datastream")) { |
|
|
|
|
AVStream *st = create_stream(s, 2, AVMEDIA_TYPE_DATA); |
|
|
|
|
if (!st) |
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
st->codec->codec_id = CODEC_ID_TEXT; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!strcmp(key, "duration") || |
|
|
|
@ -344,7 +361,14 @@ static int flv_read_metabody(AVFormatContext *s, int64_t next_pos) { |
|
|
|
|
|
|
|
|
|
//first object needs to be "onMetaData" string
|
|
|
|
|
type = avio_r8(ioc); |
|
|
|
|
if(type != AMF_DATA_TYPE_STRING || amf_get_string(ioc, buffer, sizeof(buffer)) < 0 || strcmp(buffer, "onMetaData")) |
|
|
|
|
if (type != AMF_DATA_TYPE_STRING || |
|
|
|
|
amf_get_string(ioc, buffer, sizeof(buffer)) < 0) |
|
|
|
|
return -1; |
|
|
|
|
|
|
|
|
|
if (!strcmp(buffer, "onTextData")) |
|
|
|
|
return 1; |
|
|
|
|
|
|
|
|
|
if (strcmp(buffer, "onMetaData")) |
|
|
|
|
return -1; |
|
|
|
|
|
|
|
|
|
//find the streams now so that amf_parse_object doesn't need to do the lookup every time it is called.
|
|
|
|
@ -361,16 +385,6 @@ static int flv_read_metabody(AVFormatContext *s, int64_t next_pos) { |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static AVStream *create_stream(AVFormatContext *s, int is_audio){ |
|
|
|
|
AVStream *st = avformat_new_stream(s, NULL); |
|
|
|
|
if (!st) |
|
|
|
|
return NULL; |
|
|
|
|
st->id = is_audio; |
|
|
|
|
st->codec->codec_type = is_audio ? AVMEDIA_TYPE_AUDIO : AVMEDIA_TYPE_VIDEO; |
|
|
|
|
avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */ |
|
|
|
|
return st; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int flv_read_header(AVFormatContext *s) |
|
|
|
|
{ |
|
|
|
|
int offset, flags; |
|
|
|
@ -389,11 +403,11 @@ static int flv_read_header(AVFormatContext *s) |
|
|
|
|
s->ctx_flags |= AVFMTCTX_NOHEADER; |
|
|
|
|
|
|
|
|
|
if(flags & FLV_HEADER_FLAG_HASVIDEO){ |
|
|
|
|
if(!create_stream(s, 0)) |
|
|
|
|
if(!create_stream(s, 0, AVMEDIA_TYPE_VIDEO)) |
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
} |
|
|
|
|
if(flags & FLV_HEADER_FLAG_HASAUDIO){ |
|
|
|
|
if(!create_stream(s, 1)) |
|
|
|
|
if(!create_stream(s, 1, AVMEDIA_TYPE_AUDIO)) |
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -453,6 +467,65 @@ static void clear_index_entries(AVFormatContext *s, int64_t pos) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int flv_data_packet(AVFormatContext *s, AVPacket *pkt, |
|
|
|
|
int64_t dts, int64_t next) |
|
|
|
|
{ |
|
|
|
|
int ret = AVERROR_INVALIDDATA, i; |
|
|
|
|
AVIOContext *pb = s->pb; |
|
|
|
|
AVStream *st = NULL; |
|
|
|
|
AMFDataType type; |
|
|
|
|
char buf[20]; |
|
|
|
|
int length; |
|
|
|
|
|
|
|
|
|
type = avio_r8(pb); |
|
|
|
|
if (type == AMF_DATA_TYPE_MIXEDARRAY) |
|
|
|
|
avio_seek(pb, 4, SEEK_CUR); |
|
|
|
|
else if (type != AMF_DATA_TYPE_OBJECT) |
|
|
|
|
goto out; |
|
|
|
|
|
|
|
|
|
amf_get_string(pb, buf, sizeof(buf)); |
|
|
|
|
if (strcmp(buf, "type") || avio_r8(pb) != AMF_DATA_TYPE_STRING) |
|
|
|
|
goto out; |
|
|
|
|
|
|
|
|
|
amf_get_string(pb, buf, sizeof(buf)); |
|
|
|
|
//FIXME parse it as codec_id
|
|
|
|
|
amf_get_string(pb, buf, sizeof(buf)); |
|
|
|
|
if (strcmp(buf, "text") || avio_r8(pb) != AMF_DATA_TYPE_STRING) |
|
|
|
|
goto out; |
|
|
|
|
|
|
|
|
|
length = avio_rb16(pb); |
|
|
|
|
ret = av_get_packet(s->pb, pkt, length); |
|
|
|
|
if (ret < 0) { |
|
|
|
|
ret = AVERROR(EIO); |
|
|
|
|
goto out; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (i = 0; i < s->nb_streams; i++) { |
|
|
|
|
st = s->streams[i]; |
|
|
|
|
if (st->id == 2) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (i == s->nb_streams) { |
|
|
|
|
st = create_stream(s, 2, AVMEDIA_TYPE_DATA); |
|
|
|
|
if (!st) |
|
|
|
|
goto out; |
|
|
|
|
st->codec->codec_id = CODEC_ID_TEXT; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pkt->dts = dts; |
|
|
|
|
pkt->pts = dts; |
|
|
|
|
pkt->size = ret; |
|
|
|
|
|
|
|
|
|
pkt->stream_index = st->index; |
|
|
|
|
pkt->flags |= AV_PKT_FLAG_KEY; |
|
|
|
|
|
|
|
|
|
avio_seek(s->pb, next + 4, SEEK_SET); |
|
|
|
|
out: |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
|
{ |
|
|
|
|
FLVContext *flv = s->priv_data; |
|
|
|
@ -507,7 +580,9 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
|
goto skip; |
|
|
|
|
} else { |
|
|
|
|
if (type == FLV_TAG_TYPE_META && size > 13+1+4) |
|
|
|
|
flv_read_metabody(s, next); |
|
|
|
|
if (flv_read_metabody(s, next) > 0) { |
|
|
|
|
return flv_data_packet(s, pkt, dts, next); |
|
|
|
|
} |
|
|
|
|
else /* skip packet */ |
|
|
|
|
av_log(s, AV_LOG_DEBUG, "skipping flv packet: type %d, size %d, flags %d\n", type, size, flags); |
|
|
|
|
skip: |
|
|
|
@ -527,7 +602,8 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
|
} |
|
|
|
|
if(i == s->nb_streams){ |
|
|
|
|
av_log(s, AV_LOG_ERROR, "invalid stream\n"); |
|
|
|
|
st= create_stream(s, is_audio); |
|
|
|
|
st = create_stream(s, is_audio, |
|
|
|
|
is_audio ? AVMEDIA_TYPE_AUDIO : AVMEDIA_TYPE_VIDEO); |
|
|
|
|
s->ctx_flags &= ~AVFMTCTX_NOHEADER; |
|
|
|
|
} |
|
|
|
|
av_dlog(s, "%d %X %d \n", is_audio, flags, st->discard); |
|
|
|
|