]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/flvdec.c
Merge commit '97c9a5084479eeb66f4beb100cc7589a2c8bfe81'
[ffmpeg] / libavformat / flvdec.c
index 975641757adc0e4a6a94bfec420511816d685c89..972e3333133f530ce85f47b33ce2c63b02ca791c 100644 (file)
@@ -44,6 +44,8 @@
 typedef struct FLVContext {
     const AVClass *class; ///< Class for private options.
     int trust_metadata;   ///< configure streams according onMetaData
+    int trust_datasize;   ///< trust data size of FLVTag
+    int dump_full_metadata;   ///< Dump full metadata of the onMetadata
     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];
@@ -70,6 +72,9 @@ typedef struct FLVContext {
     int64_t *keyframe_filepositions;
     int missing_streams;
     AVRational framerate;
+    int64_t last_ts;
+    int64_t time_offset;
+    int64_t time_pos;
 } FLVContext;
 
 static int probe(AVProbeData *p, int live)
@@ -141,7 +146,9 @@ static AVStream *create_stream(AVFormatContext *s, int codec_type)
     st->codecpar->codec_type = codec_type;
     if (s->nb_streams>=3 ||(   s->nb_streams==2
                            && s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE
-                           && s->streams[1]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE))
+                           && s->streams[1]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE
+                           && s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_DATA
+                           && s->streams[1]->codecpar->codec_type != AVMEDIA_TYPE_DATA))
         s->ctx_flags &= ~AVFMTCTX_NOHEADER;
     if (codec_type == AVMEDIA_TYPE_AUDIO) {
         st->codecpar->bit_rate = flv->audio_bit_rate;
@@ -611,7 +618,7 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
             (!vpar && !strcmp(key, "videocodecid"))))
                 s->ctx_flags &= ~AVFMTCTX_NOHEADER; //If there is either audio/video missing, codecid will be an empty object
 
-        if (!strcmp(key, "duration")        ||
+        if ((!strcmp(key, "duration")        ||
             !strcmp(key, "filesize")        ||
             !strcmp(key, "width")           ||
             !strcmp(key, "height")          ||
@@ -623,7 +630,7 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
             !strcmp(key, "audiosamplesize") ||
             !strcmp(key, "stereo")          ||
             !strcmp(key, "audiocodecid")    ||
-            !strcmp(key, "datastream"))
+            !strcmp(key, "datastream")) && !flv->dump_full_metadata)
             return 0;
 
         s->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
@@ -751,6 +758,9 @@ static int flv_read_close(AVFormatContext *s)
 
 static int flv_get_extradata(AVFormatContext *s, AVStream *st, int size)
 {
+    if (!size)
+        return 0;
+
     av_freep(&st->codecpar->extradata);
     if (ff_get_extradata(s, st->codecpar, s->pb, size) < 0)
         return AVERROR(ENOMEM);
@@ -761,6 +771,9 @@ static int flv_get_extradata(AVFormatContext *s, AVStream *st, int size)
 static int flv_queue_extradata(FLVContext *flv, AVIOContext *pb, int stream,
                                int size)
 {
+    if (!size)
+        return 0;
+
     av_free(flv->new_extradata[stream]);
     flv->new_extradata[stream] = av_mallocz(size +
                                             AV_INPUT_BUFFER_PADDING_SIZE);
@@ -907,6 +920,18 @@ static int resync(AVFormatContext *s)
         flv->resync_buffer[j ] =
         flv->resync_buffer[j1] = avio_r8(s->pb);
 
+        if (i >= 8 && pos) {
+            uint8_t *d = flv->resync_buffer + j1 - 8;
+            if (d[0] == 'F' &&
+                d[1] == 'L' &&
+                d[2] == 'V' &&
+                d[3] < 5 && d[5] == 0) {
+                av_log(s, AV_LOG_WARNING, "Concatenated FLV detected, might fail to demux, decode and seek %"PRId64"\n", flv->last_ts);
+                flv->time_offset = flv->last_ts + 1;
+                flv->time_pos    = avio_tell(s->pb);
+            }
+        }
+
         if (i > 22) {
             unsigned lsize2 = AV_RB32(flv->resync_buffer + j1 - 4);
             if (lsize2 >= 11 && lsize2 + 8LL < FFMIN(i, RESYNC_BUFFER_SIZE)) {
@@ -988,12 +1013,12 @@ retry:
             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_DATA;
+            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 || type == TYPE_UNKNOWN) {
+                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
@@ -1007,6 +1032,8 @@ retry:
                     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;
                 }
                 avio_seek(s->pb, meta_pos, SEEK_SET);
             }
@@ -1043,13 +1070,16 @@ skip:
                 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_DATA) {
+            } 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};
+            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);
@@ -1057,6 +1087,10 @@ skip:
         }
         av_log(s, AV_LOG_TRACE, "%d %X %d \n", stream_type, flags, st->discard);
 
+        if (flv->time_pos <= pos) {
+            dts += flv->time_offset;
+        }
+
         if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) &&
             ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
               stream_type == FLV_STREAM_TYPE_AUDIO))
@@ -1143,8 +1177,10 @@ retry_duration:
         if (ret < 0)
             return ret;
         size -= ret;
-    } else if (stream_type == FLV_STREAM_TYPE_DATA) {
+    } else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) {
         st->codecpar->codec_id = AV_CODEC_ID_TEXT;
+    } else if (stream_type == FLV_STREAM_TYPE_DATA) {
+        st->codecpar->codec_id = AV_CODEC_ID_NONE; // Opaque AMF data
     }
 
     if (st->codecpar->codec_id == AV_CODEC_ID_AAC ||
@@ -1245,23 +1281,30 @@ retry_duration:
 
     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:
     last = avio_rb32(s->pb);
-    if (last != orig_size + 11 && last != orig_size + 10 &&
-        !avio_feof(s->pb) &&
-        (last != orig_size || !last) && last != flv->sum_flv_tag_size &&
-        !flv->broken_sizes) {
-        av_log(s, AV_LOG_ERROR, "Packet mismatch %d %d %d\n", last, orig_size + 11, flv->sum_flv_tag_size);
-        avio_seek(s->pb, pos + 1, SEEK_SET);
-        ret = resync(s);
-        av_packet_unref(pkt);
-        if (ret >= 0) {
-            goto retry;
+    if (!flv->trust_datasize) {
+        if (last != orig_size + 11 && last != orig_size + 10 &&
+            !avio_feof(s->pb) &&
+            (last != orig_size || !last) && last != flv->sum_flv_tag_size &&
+            !flv->broken_sizes) {
+            av_log(s, AV_LOG_ERROR, "Packet mismatch %d %d %d\n", last, orig_size + 11, flv->sum_flv_tag_size);
+            avio_seek(s->pb, pos + 1, SEEK_SET);
+            ret = resync(s);
+            av_packet_unref(pkt);
+            if (ret >= 0) {
+                goto retry;
+            }
         }
     }
+
+    if (ret >= 0)
+        flv->last_ts = pkt->dts;
+
     return ret;
 }
 
@@ -1277,6 +1320,8 @@ static int flv_read_seek(AVFormatContext *s, int stream_index,
 #define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
 static const AVOption options[] = {
     { "flv_metadata", "Allocate streams according to the onMetaData array", OFFSET(trust_metadata), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD },
+    { "flv_full_metadata", "Dump full metadata of the onMetadata", OFFSET(dump_full_metadata), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD },
+    { "flv_ignore_prevtag", "Ignore the Size of previous tag", OFFSET(trust_datasize), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD },
     { "missing_streams", "", OFFSET(missing_streams), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 0xFF, VD | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY },
     { NULL }
 };