#include "libavutil/mathematics.h"
#include "libavutil/time_internal.h"
#include "libavcodec/bytestream.h"
-#include "libavcodec/mpeg4audio.h"
#include "avformat.h"
#include "internal.h"
#include "avio_internal.h"
#define RESYNC_BUFFER_SIZE (1<<20)
+#define MAX_DEPTH 16 ///< arbitrary limit to prevent unbounded recursion
+
typedef struct FLVContext {
const AVClass *class; ///< Class for private options.
int trust_metadata; ///< configure streams according onMetaData
return probe(p, 1);
}
+static int kux_probe(const AVProbeData *p)
+{
+ const uint8_t *d = p->buf;
+
+ if (d[0] == 'K' &&
+ d[1] == 'D' &&
+ d[2] == 'K' &&
+ d[3] == 0 &&
+ d[4] == 0) {
+ return AVPROBE_SCORE_EXTENSION + 1;
+ }
+ return 0;
+}
+
static void add_keyframes_index(AVFormatContext *s)
{
FLVContext *flv = s->priv_data;
av_assert0(flv->last_keyframe_stream_index <= s->nb_streams);
stream = s->streams[flv->last_keyframe_stream_index];
- if (stream->nb_index_entries == 0) {
+ if (stream->internal->nb_index_entries == 0) {
for (i = 0; i < flv->keyframe_count; i++) {
av_log(s, AV_LOG_TRACE, "keyframe filepositions = %"PRId64" times = %"PRId64"\n",
flv->keyframe_filepositions[i], flv->keyframe_times[i] * 1000);
static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize)
{
+ int ret;
int length = avio_rb16(ioc);
if (length >= buffsize) {
avio_skip(ioc, length);
return -1;
}
- avio_read(ioc, buffer, length);
+ ret = avio_read(ioc, buffer, length);
+ if (ret < 0)
+ return ret;
+ if (ret < length)
+ return AVERROR_INVALIDDATA;
buffer[length] = '\0';
}
for (i = 0; i < arraylen && avio_tell(ioc) < max_pos - 1; i++) {
+ double d;
if (avio_r8(ioc) != AMF_DATA_TYPE_NUMBER)
goto invalid;
- current_array[0][i] = av_int2double(avio_rb64(ioc));
+ d = av_int2double(avio_rb64(ioc));
+ if (isnan(d) || d < INT64_MIN || d > INT64_MAX)
+ goto invalid;
+ current_array[0][i] = d;
}
if (times && filepositions) {
// All done, exiting at a position allowing amf_parse_object
double num_val;
amf_date date;
+ if (depth > MAX_DEPTH)
+ return AVERROR_PATCHWELCOME;
+
num_val = 0;
ioc = s->pb;
+ if (avio_feof(ioc))
+ return AVERROR_EOF;
amf_type = avio_r8(ioc);
switch (amf_type) {
if (key &&
(ioc->seekable & AVIO_SEEKABLE_NORMAL) &&
!strcmp(KEYFRAMES_TAG, key) && depth == 1)
- if (parse_keyframes_index(s, ioc,
- max_pos) < 0)
+ if (parse_keyframes_index(s, ioc, max_pos) < 0)
av_log(s, AV_LOG_ERROR, "Keyframe index parsing failed\n");
else
add_keyframes_index(s);
if (!strcmp(buffer, "onCaptionInfo"))
return TYPE_ONCAPTIONINFO;
- if (strcmp(buffer, "onMetaData") && strcmp(buffer, "onCuePoint")) {
+ if (strcmp(buffer, "onMetaData") && strcmp(buffer, "onCuePoint") && strcmp(buffer, "|RtmpSampleAccess")) {
av_log(s, AV_LOG_DEBUG, "Unknown type %s\n", buffer);
return TYPE_UNKNOWN;
}
astream = stream;
if (flv->last_keyframe_stream_index == -1)
flv->last_keyframe_stream_index = i;
- }
- else if (stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
+ } else if (stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
dstream = stream;
}
int offset;
int pre_tag_size = 0;
+ /* Actual FLV data at 0xe40000 in KUX file */
+ if(!strcmp(s->iformat->name, "kux"))
+ avio_skip(s->pb, 0xe40000);
+
avio_skip(s->pb, 4);
flags = avio_r8(s->pb);
static int flv_get_extradata(AVFormatContext *s, AVStream *st, int size)
{
+ int ret;
if (!size)
return 0;
- av_freep(&st->codecpar->extradata);
- if (ff_get_extradata(s, st->codecpar, s->pb, size) < 0)
- return AVERROR(ENOMEM);
+ if ((ret = ff_get_extradata(s, st->codecpar, s->pb, size)) < 0)
+ return ret;
st->internal->need_context_update = 1;
return 0;
}
AVStream *st = s->streams[i];
/* Remove all index entries that point to >= pos */
out = 0;
- for (j = 0; j < st->nb_index_entries; j++)
- if (st->index_entries[j].pos < pos)
- st->index_entries[out++] = st->index_entries[j];
- st->nb_index_entries = out;
+ for (j = 0; j < st->internal->nb_index_entries; j++)
+ if (st->internal->index_entries[j].pos < pos)
+ st->internal->index_entries[out++] = st->internal->index_entries[j];
+ st->internal->nb_index_entries = out;
}
}
-static int amf_skip_tag(AVIOContext *pb, AMFDataType type)
+static int amf_skip_tag(AVIOContext *pb, AMFDataType type, int depth)
{
int nb = -1, ret, parse_name = 1;
+ if (depth > MAX_DEPTH)
+ return AVERROR_PATCHWELCOME;
+
+ if (avio_feof(pb))
+ return AVERROR_EOF;
+
switch (type) {
case AMF_DATA_TYPE_NUMBER:
avio_skip(pb, 8);
parse_name = 0;
case AMF_DATA_TYPE_MIXEDARRAY:
nb = avio_rb32(pb);
+ if (nb < 0)
+ return AVERROR_INVALIDDATA;
case AMF_DATA_TYPE_OBJECT:
while(!pb->eof_reached && (nb-- > 0 || type != AMF_DATA_TYPE_ARRAY)) {
if (parse_name) {
}
avio_skip(pb, size);
}
- if ((ret = amf_skip_tag(pb, avio_r8(pb))) < 0)
+ if ((ret = amf_skip_tag(pb, avio_r8(pb), depth + 1)) < 0)
return ret;
}
break;
else
break;
} else {
- if ((ret = amf_skip_tag(pb, type)) < 0)
+ if ((ret = amf_skip_tag(pb, type, 0)) < 0)
goto skip;
}
}
retry:
/* pkt size is repeated at end. skip it */
- pos = avio_tell(s->pb);
- type = (avio_r8(s->pb) & 0x1F);
- orig_size =
- size = avio_rb24(s->pb);
- flv->sum_flv_tag_size += size + 11;
- dts = avio_rb24(s->pb);
- dts |= (unsigned)avio_r8(s->pb) << 24;
- av_log(s, AV_LOG_TRACE, "type:%d, size:%d, last:%d, dts:%"PRId64" pos:%"PRId64"\n", type, size, last, dts, avio_tell(s->pb));
- if (avio_feof(s->pb))
- return AVERROR_EOF;
- avio_skip(s->pb, 3); /* stream id, always 0 */
- flags = 0;
-
- if (flv->validate_next < flv->validate_count) {
- int64_t validate_pos = flv->validate_index[flv->validate_next].pos;
- if (pos == validate_pos) {
- if (FFABS(dts - flv->validate_index[flv->validate_next].dts) <=
- VALIDATE_INDEX_TS_THRESH) {
- flv->validate_next++;
- } else {
- clear_index_entries(s, validate_pos);
- flv->validate_count = 0;
- }
- } else if (pos > validate_pos) {
+ pos = avio_tell(s->pb);
+ type = (avio_r8(s->pb) & 0x1F);
+ orig_size =
+ size = avio_rb24(s->pb);
+ flv->sum_flv_tag_size += size + 11;
+ dts = avio_rb24(s->pb);
+ dts |= (unsigned)avio_r8(s->pb) << 24;
+ av_log(s, AV_LOG_TRACE, "type:%d, size:%d, last:%d, dts:%"PRId64" pos:%"PRId64"\n", type, size, last, dts, avio_tell(s->pb));
+ if (avio_feof(s->pb))
+ return AVERROR_EOF;
+ avio_skip(s->pb, 3); /* stream id, always 0 */
+ flags = 0;
+
+ if (flv->validate_next < flv->validate_count) {
+ int64_t validate_pos = flv->validate_index[flv->validate_next].pos;
+ if (pos == validate_pos) {
+ if (FFABS(dts - flv->validate_index[flv->validate_next].dts) <=
+ VALIDATE_INDEX_TS_THRESH) {
+ flv->validate_next++;
+ } else {
clear_index_entries(s, validate_pos);
flv->validate_count = 0;
}
+ } else if (pos > validate_pos) {
+ clear_index_entries(s, validate_pos);
+ flv->validate_count = 0;
}
+ }
- if (size == 0) {
- ret = FFERROR_REDO;
- goto leave;
- }
+ if (size == 0) {
+ ret = FFERROR_REDO;
+ goto leave;
+ }
- next = size + avio_tell(s->pb);
-
- if (type == FLV_TAG_TYPE_AUDIO) {
- stream_type = FLV_STREAM_TYPE_AUDIO;
- flags = avio_r8(s->pb);
- size--;
- } else if (type == FLV_TAG_TYPE_VIDEO) {
- stream_type = FLV_STREAM_TYPE_VIDEO;
- flags = avio_r8(s->pb);
- size--;
- if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD)
- goto skip;
- } else if (type == FLV_TAG_TYPE_META) {
- stream_type=FLV_STREAM_TYPE_SUBTITLE;
- if (size > 13 + 1 + 4) { // Header-type metadata stuff
- int type;
- meta_pos = avio_tell(s->pb);
- type = flv_read_metabody(s, next);
- if (type == 0 && dts == 0 || type < 0) {
- if (type < 0 && flv->validate_count &&
- flv->validate_index[0].pos > next &&
- flv->validate_index[0].pos - 4 < next
- ) {
- av_log(s, AV_LOG_WARNING, "Adjusting next position due to index mismatch\n");
- next = flv->validate_index[0].pos - 4;
- }
- goto skip;
- } else if (type == TYPE_ONTEXTDATA) {
- avpriv_request_sample(s, "OnTextData packet");
- return flv_data_packet(s, pkt, dts, next);
- } else if (type == TYPE_ONCAPTION) {
- return flv_data_packet(s, pkt, dts, next);
- } else if (type == TYPE_UNKNOWN) {
- stream_type = FLV_STREAM_TYPE_DATA;
+ next = size + avio_tell(s->pb);
+
+ if (type == FLV_TAG_TYPE_AUDIO) {
+ stream_type = FLV_STREAM_TYPE_AUDIO;
+ flags = avio_r8(s->pb);
+ size--;
+ } else if (type == FLV_TAG_TYPE_VIDEO) {
+ stream_type = FLV_STREAM_TYPE_VIDEO;
+ flags = avio_r8(s->pb);
+ size--;
+ if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD)
+ goto skip;
+ } else if (type == FLV_TAG_TYPE_META) {
+ stream_type=FLV_STREAM_TYPE_SUBTITLE;
+ if (size > 13 + 1 + 4) { // Header-type metadata stuff
+ int type;
+ meta_pos = avio_tell(s->pb);
+ type = flv_read_metabody(s, next);
+ if (type == 0 && dts == 0 || type < 0) {
+ if (type < 0 && flv->validate_count &&
+ flv->validate_index[0].pos > next &&
+ flv->validate_index[0].pos - 4 < next) {
+ av_log(s, AV_LOG_WARNING, "Adjusting next position due to index mismatch\n");
+ next = flv->validate_index[0].pos - 4;
}
- avio_seek(s->pb, meta_pos, SEEK_SET);
- }
- } else {
- av_log(s, AV_LOG_DEBUG,
- "Skipping flv packet: type %d, size %d, flags %d.\n",
- type, size, flags);
-skip:
- if (avio_seek(s->pb, next, SEEK_SET) != next) {
- // This can happen if flv_read_metabody above read past
- // next, on a non-seekable input, and the preceding data has
- // been flushed out from the IO buffer.
- av_log(s, AV_LOG_ERROR, "Unable to seek to the next packet\n");
- return AVERROR_INVALIDDATA;
+ goto skip;
+ } else if (type == TYPE_ONTEXTDATA) {
+ avpriv_request_sample(s, "OnTextData packet");
+ return flv_data_packet(s, pkt, dts, next);
+ } else if (type == TYPE_ONCAPTION) {
+ return flv_data_packet(s, pkt, dts, next);
+ } else if (type == TYPE_UNKNOWN) {
+ stream_type = FLV_STREAM_TYPE_DATA;
}
- ret = FFERROR_REDO;
- goto leave;
- }
-
- /* skip empty data packets */
- if (!size) {
- ret = FFERROR_REDO;
- goto leave;
+ avio_seek(s->pb, meta_pos, SEEK_SET);
}
-
- /* now find stream */
- for (i = 0; i < s->nb_streams; i++) {
- st = s->streams[i];
- if (stream_type == FLV_STREAM_TYPE_AUDIO) {
- if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
- (s->audio_codec_id || flv_same_audio_codec(st->codecpar, flags)))
- break;
- } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
- if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
- (s->video_codec_id || flv_same_video_codec(st->codecpar, flags)))
- break;
- } else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) {
- if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
- break;
- } else if (stream_type == FLV_STREAM_TYPE_DATA) {
- if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA)
- break;
- }
+ } else {
+ av_log(s, AV_LOG_DEBUG,
+ "Skipping flv packet: type %d, size %d, flags %d.\n",
+ type, size, flags);
+skip:
+ if (avio_seek(s->pb, next, SEEK_SET) != next) {
+ // This can happen if flv_read_metabody above read past
+ // next, on a non-seekable input, and the preceding data has
+ // been flushed out from the IO buffer.
+ av_log(s, AV_LOG_ERROR, "Unable to seek to the next packet\n");
+ return AVERROR_INVALIDDATA;
}
- if (i == s->nb_streams) {
- static const enum AVMediaType stream_types[] = {AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_SUBTITLE, AVMEDIA_TYPE_DATA};
- st = create_stream(s, stream_types[stream_type]);
- if (!st)
- return AVERROR(ENOMEM);
+ ret = FFERROR_REDO;
+ goto leave;
+ }
- }
- av_log(s, AV_LOG_TRACE, "%d %X %d \n", stream_type, flags, st->discard);
+ /* skip empty data packets */
+ if (!size) {
+ ret = FFERROR_REDO;
+ goto leave;
+ }
- if (flv->time_pos <= pos) {
- dts += flv->time_offset;
+ /* now find stream */
+ for (i = 0; i < s->nb_streams; i++) {
+ st = s->streams[i];
+ if (stream_type == FLV_STREAM_TYPE_AUDIO) {
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
+ (s->audio_codec_id || flv_same_audio_codec(st->codecpar, flags)))
+ break;
+ } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+ (s->video_codec_id || flv_same_video_codec(st->codecpar, flags)))
+ break;
+ } else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) {
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
+ break;
+ } else if (stream_type == FLV_STREAM_TYPE_DATA) {
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA)
+ break;
}
+ }
+ if (i == s->nb_streams) {
+ static const enum AVMediaType stream_types[] = {AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_SUBTITLE, AVMEDIA_TYPE_DATA};
+ st = create_stream(s, stream_types[stream_type]);
+ if (!st)
+ return AVERROR(ENOMEM);
+ }
+ av_log(s, AV_LOG_TRACE, "%d %X %d \n", stream_type, flags, st->discard);
- if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) &&
- ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
- stream_type == FLV_STREAM_TYPE_AUDIO))
- av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME);
+ if (flv->time_pos <= pos) {
+ dts += flv->time_offset;
+ }
- if ( (st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || (stream_type == FLV_STREAM_TYPE_AUDIO)))
- ||(st->discard >= AVDISCARD_BIDIR && ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && (stream_type == FLV_STREAM_TYPE_VIDEO)))
- || st->discard >= AVDISCARD_ALL
- ) {
- avio_seek(s->pb, next, SEEK_SET);
- ret = FFERROR_REDO;
- goto leave;
- }
+ if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) &&
+ ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
+ stream_type == FLV_STREAM_TYPE_AUDIO))
+ av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME);
+
+ if ((st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || stream_type == FLV_STREAM_TYPE_AUDIO)) ||
+ (st->discard >= AVDISCARD_BIDIR && ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && stream_type == FLV_STREAM_TYPE_VIDEO)) ||
+ st->discard >= AVDISCARD_ALL) {
+ avio_seek(s->pb, next, SEEK_SET);
+ ret = FFERROR_REDO;
+ goto leave;
+ }
// if not streamed and no duration from metadata then seek to end to find
// the duration from the timestamps
avio_seek(s->pb, fsize - 3 - size, SEEK_SET);
if (size == avio_rb24(s->pb) + 11) {
uint32_t ts = avio_rb24(s->pb);
- ts |= avio_r8(s->pb) << 24;
+ ts |= (unsigned)avio_r8(s->pb) << 24;
if (ts)
s->duration = ts * (int64_t)AV_TIME_BASE / 1000;
else if (fsize >= 8 && fsize - 8 >= size) {
if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {
// sign extension
int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000;
- pts = dts + cts;
+ pts = av_sat_add64(dts, cts);
if (cts < 0) { // dts might be wrong
if (!flv->wrong_dts)
av_log(s, AV_LOG_WARNING,
if (st->codecpar->codec_id == AV_CODEC_ID_AAC && t && !strcmp(t->value, "Omnia A/XE"))
st->codecpar->extradata_size = 2;
- if (st->codecpar->codec_id == AV_CODEC_ID_AAC && 0) {
- MPEG4AudioConfig cfg;
-
- if (avpriv_mpeg4audio_get_config(&cfg, st->codecpar->extradata,
- st->codecpar->extradata_size * 8, 1) >= 0) {
- st->codecpar->channels = cfg.channels;
- st->codecpar->channel_layout = 0;
- if (cfg.ext_sample_rate)
- st->codecpar->sample_rate = cfg.ext_sample_rate;
- else
- st->codecpar->sample_rate = cfg.sample_rate;
- av_log(s, AV_LOG_TRACE, "mp4a config channels %d sample rate %d\n",
- st->codecpar->channels, st->codecpar->sample_rate);
- }
- }
-
ret = FFERROR_REDO;
goto leave;
}
pkt->stream_index = st->index;
pkt->pos = pos;
if (flv->new_extradata[stream_type]) {
- uint8_t *side = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
- flv->new_extradata_size[stream_type]);
- if (side) {
- memcpy(side, flv->new_extradata[stream_type],
- flv->new_extradata_size[stream_type]);
- av_freep(&flv->new_extradata[stream_type]);
+ int ret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
+ flv->new_extradata[stream_type],
+ flv->new_extradata_size[stream_type]);
+ if (ret >= 0) {
+ flv->new_extradata[stream_type] = NULL;
flv->new_extradata_size[stream_type] = 0;
}
}
ff_add_param_change(pkt, channels, 0, sample_rate, 0, 0);
}
- if ( stream_type == FLV_STREAM_TYPE_AUDIO ||
- ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY) ||
- stream_type == FLV_STREAM_TYPE_SUBTITLE ||
- stream_type == FLV_STREAM_TYPE_DATA)
+ if (stream_type == FLV_STREAM_TYPE_AUDIO ||
+ (flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
+ stream_type == FLV_STREAM_TYPE_SUBTITLE ||
+ stream_type == FLV_STREAM_TYPE_DATA)
pkt->flags |= AV_PKT_FLAG_KEY;
leave:
.version = LIBAVUTIL_VERSION_INT,
};
-AVInputFormat ff_flv_demuxer = {
+const AVInputFormat ff_flv_demuxer = {
.name = "flv",
.long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
.priv_data_size = sizeof(FLVContext),
.version = LIBAVUTIL_VERSION_INT,
};
-AVInputFormat ff_live_flv_demuxer = {
+const AVInputFormat ff_live_flv_demuxer = {
.name = "live_flv",
.long_name = NULL_IF_CONFIG_SMALL("live RTMP FLV (Flash Video)"),
.priv_data_size = sizeof(FLVContext),
.priv_class = &live_flv_class,
.flags = AVFMT_TS_DISCONT
};
+
+static const AVClass kux_class = {
+ .class_name = "kuxdec",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const AVInputFormat ff_kux_demuxer = {
+ .name = "kux",
+ .long_name = NULL_IF_CONFIG_SMALL("KUX (YouKu)"),
+ .priv_data_size = sizeof(FLVContext),
+ .read_probe = kux_probe,
+ .read_header = flv_read_header,
+ .read_packet = flv_read_packet,
+ .read_seek = flv_read_seek,
+ .read_close = flv_read_close,
+ .extensions = "kux",
+ .priv_class = &kux_class,
+};