]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/flvdec.c
Merge remote-tracking branch 'qatar/master'
[ffmpeg] / libavformat / flvdec.c
index 7705c480a956583f90e4b70503c8b4f2520c91a7..94fd76d666d71206ac238ffc0c649874260bcd8e 100644 (file)
 #include "avio_internal.h"
 #include "flv.h"
 
+#define VALIDATE_INDEX_TS_THRESH 2500
+
 typedef struct {
     int wrong_dts; ///< wrong dts due to negative cts
     uint8_t *new_extradata[FLV_STREAM_TYPE_NB];
     int      new_extradata_size[FLV_STREAM_TYPE_NB];
     int      last_sample_rate;
     int      last_channels;
+    struct {
+        int64_t dts;
+        int64_t pos;
+    } validate_index[2];
+    int validate_next;
+    int validate_count;
 } FLVContext;
 
 static int flv_probe(AVProbeData *p)
@@ -137,6 +145,7 @@ static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize) {
 }
 
 static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, AVStream *vstream, int64_t max_pos) {
+    FLVContext *flv = s->priv_data;
     unsigned int timeslen = 0, fileposlen = 0, i;
     char str_val[256];
     int64_t *times = NULL;
@@ -149,6 +158,9 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, AVStream
         return 0;
     }
 
+    if (s->flags & AVFMT_FLAG_IGNIDX)
+        return 0;
+
     while (avio_tell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) {
         int64_t** current_array;
         unsigned int arraylen;
@@ -189,17 +201,15 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, AVStream
     }
 
     if (timeslen == fileposlen && fileposlen>1 && max_pos <= filepositions[0]) {
-        int64_t dts, size0, size1;
-        avio_seek(ioc, filepositions[1]-4, SEEK_SET);
-        size0 = avio_rb32(ioc);
-                avio_r8(ioc);
-        size1 = avio_rb24(ioc);
-        dts   = avio_rb24(ioc);
-        dts  |= avio_r8(ioc) << 24;
-        if (size0 > filepositions[1] || FFABS(dts - times[1]*1000)>5000/*arbitraray threshold to detect invalid index*/)
-            goto invalid;
-         for(i = 0; i < timeslen; i++)
-             av_add_index_entry(vstream, filepositions[i], times[i]*1000, 0, 0, AVINDEX_KEYFRAME);
+        for (i = 0; i < fileposlen; i++) {
+            av_add_index_entry(vstream, filepositions[i], times[i]*1000,
+                               0, 0, AVINDEX_KEYFRAME);
+            if (i < 2) {
+                flv->validate_index[i].pos = filepositions[i];
+                flv->validate_index[i].dts = times[i] * 1000;
+                flv->validate_count = i + 1;
+            }
+        }
     } else {
 invalid:
         av_log(s, AV_LOG_WARNING, "Invalid keyframes object, skipping.\n");
@@ -233,22 +243,18 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, AVStream *vst
             if(amf_get_string(ioc, str_val, sizeof(str_val)) < 0)
                 return -1;
             break;
-        case AMF_DATA_TYPE_OBJECT: {
-            unsigned int keylen;
-
+        case AMF_DATA_TYPE_OBJECT:
             if ((vstream || astream) && ioc->seekable && key && !strcmp(KEYFRAMES_TAG, key) && depth == 1)
                 if (parse_keyframes_index(s, ioc, vstream ? vstream : astream,
                                           max_pos) < 0)
                     av_log(s, AV_LOG_ERROR, "Keyframe index parsing failed\n");
 
-            while(avio_tell(ioc) < max_pos - 2 && (keylen = avio_rb16(ioc))) {
-                avio_skip(ioc, keylen); //skip key string
-                if(amf_parse_object(s, NULL, NULL, NULL, max_pos, depth + 1) < 0)
+            while (avio_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 -1; //if we couldn't skip, bomb out.
             }
             if(avio_r8(ioc) != AMF_END_OF_OBJECT)
                 return -1;
-        }
             break;
         case AMF_DATA_TYPE_NULL:
         case AMF_DATA_TYPE_UNDEFINED:
@@ -378,8 +384,7 @@ static AVStream *create_stream(AVFormatContext *s, int stream_type){
     return st;
 }
 
-static int flv_read_header(AVFormatContext *s,
-                           AVFormatParameters *ap)
+static int flv_read_header(AVFormatContext *s)
 {
     int offset, flags;
 
@@ -445,6 +450,22 @@ static int flv_queue_extradata(FLVContext *flv, AVIOContext *pb, int stream,
     return 0;
 }
 
+static void clear_index_entries(AVFormatContext *s, int64_t pos)
+{
+    int i, j, out;
+    av_log(s, AV_LOG_WARNING, "Found invalid index entries, clearing the index.\n");
+    for (i = 0; i < s->nb_streams; i++) {
+        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;
+    }
+}
+
 static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
 {
     FLVContext *flv = s->priv_data;
@@ -468,6 +489,22 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
     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)
         continue;
 
@@ -481,7 +518,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
         stream_type=FLV_STREAM_TYPE_VIDEO;
         flags = avio_r8(s->pb);
         size--;
-        if ((flags & 0xf0) == 0x50) /* video info / command frame */
+        if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD)
             goto skip;
     } else if (type == FLV_TAG_TYPE_META) {
         if (size > 13+1+4 && dts == 0) { // Header-type metadata stuff
@@ -655,6 +692,8 @@ leave:
 static int flv_read_seek(AVFormatContext *s, int stream_index,
     int64_t ts, int flags)
 {
+    FLVContext *flv = s->priv_data;
+    flv->validate_count = 0;
     return avio_seek_time(s->pb, stream_index, ts, flags);
 }
 
@@ -698,5 +737,4 @@ AVInputFormat ff_flv_demuxer = {
 #endif
     .read_close = flv_read_close,
     .extensions = "flv",
-    .value = CODEC_ID_FLV1,
 };