]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/flvdec.c
avformat: Constify all muxer/demuxers
[ffmpeg] / libavformat / flvdec.c
index 445d58d8f7eb01b925da3893278d20901084fe43..718d690421987ed960b114a1332c703318de1de1 100644 (file)
@@ -32,7 +32,6 @@
 #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"
@@ -42,6 +41,8 @@
 
 #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
@@ -113,6 +114,20 @@ static int live_flv_probe(const AVProbeData *p)
     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;
@@ -127,7 +142,7 @@ static void add_keyframes_index(AVFormatContext *s)
     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);
@@ -369,13 +384,18 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream,
 
 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';
 
@@ -433,9 +453,13 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, int64_t m
         }
 
         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
@@ -480,8 +504,13 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
     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) {
@@ -501,8 +530,7 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
         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);
@@ -703,7 +731,7 @@ static int flv_read_metabody(AVFormatContext *s, int64_t next_pos)
     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;
     }
@@ -719,8 +747,7 @@ static int flv_read_metabody(AVFormatContext *s, int64_t next_pos)
             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;
     }
 
@@ -738,6 +765,10 @@ static int flv_read_header(AVFormatContext *s)
     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);
 
@@ -778,12 +809,12 @@ static int flv_read_close(AVFormatContext *s)
 
 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;
 }
@@ -813,17 +844,23 @@ static void clear_index_entries(AVFormatContext *s, int64_t pos)
         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);
@@ -838,6 +875,8 @@ static int amf_skip_tag(AVIOContext *pb, AMFDataType type)
         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) {
@@ -848,7 +887,7 @@ static int amf_skip_tag(AVIOContext *pb, AMFDataType type)
                 }
                 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;
@@ -892,7 +931,7 @@ static int flv_data_packet(AVFormatContext *s, AVPacket *pkt,
             else
                 break;
         } else {
-            if ((ret = amf_skip_tag(pb, type)) < 0)
+            if ((ret = amf_skip_tag(pb, type, 0)) < 0)
                 goto skip;
         }
     }
@@ -1041,8 +1080,7 @@ retry:
             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
-                ) {
+                    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;
                 }
@@ -1103,7 +1141,6 @@ skip:
         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);
 
@@ -1116,10 +1153,9 @@ skip:
          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
-    ) {
+    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;
@@ -1144,7 +1180,7 @@ retry_duration:
             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) {
@@ -1217,7 +1253,7 @@ retry_duration:
         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,
@@ -1247,22 +1283,6 @@ retry_duration:
             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;
         }
@@ -1282,12 +1302,11 @@ retry_duration:
     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;
         }
     }
@@ -1299,10 +1318,10 @@ retry_duration:
         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:
@@ -1353,7 +1372,7 @@ static const AVClass flv_class = {
     .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),
@@ -1373,7 +1392,7 @@ static const AVClass live_flv_class = {
     .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),
@@ -1386,3 +1405,23 @@ AVInputFormat ff_live_flv_demuxer = {
     .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,
+};