]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/flvdec.c
lavf: move AVStream.*index_entries* to AVStreamInternal
[ffmpeg] / libavformat / flvdec.c
index 972e3333133f530ce85f47b33ce2c63b02ca791c..ad6e7a3ca5ace4d0a3f1f935b41d40b705dac837 100644 (file)
@@ -30,8 +30,8 @@
 #include "libavutil/opt.h"
 #include "libavutil/intfloat.h"
 #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"
@@ -77,7 +77,13 @@ typedef struct FLVContext {
     int64_t time_pos;
 } FLVContext;
 
-static int probe(AVProbeData *p, int live)
+/* AMF date type */
+typedef struct amf_date {
+    double   milliseconds;
+    int16_t  timezone;
+} amf_date;
+
+static int probe(const AVProbeData *p, int live)
 {
     const uint8_t *d = p->buf;
     unsigned offset = AV_RB32(d + 5);
@@ -96,16 +102,30 @@ static int probe(AVProbeData *p, int live)
     return 0;
 }
 
-static int flv_probe(AVProbeData *p)
+static int flv_probe(const AVProbeData *p)
 {
     return probe(p, 0);
 }
 
-static int live_flv_probe(AVProbeData *p)
+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;
@@ -120,7 +140,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);
@@ -386,7 +406,7 @@ static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, int64_t m
     int64_t initial_pos    = avio_tell(ioc);
 
     if (flv->keyframe_count > 0) {
-        av_log(s, AV_LOG_DEBUG, "keyframes have been paresed\n");
+        av_log(s, AV_LOG_DEBUG, "keyframes have been parsed\n");
         return 0;
     }
     av_assert0(!flv->keyframe_times);
@@ -471,9 +491,12 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
     AMFDataType amf_type;
     char str_val[1024];
     double num_val;
+    amf_date date;
 
     num_val  = 0;
     ioc      = s->pb;
+    if (avio_feof(ioc))
+        return AVERROR_EOF;
     amf_type = avio_r8(ioc);
 
     switch (amf_type) {
@@ -493,8 +516,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);
@@ -542,7 +564,9 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
     }
     break;
     case AMF_DATA_TYPE_DATE:
-        avio_skip(ioc, 8 + 2);  // timestamp (double) and UTC offset (int16)
+        // timestamp (double) and UTC offset (int16)
+        date.milliseconds = av_int2double(avio_rb64(ioc));
+        date.timezone = avio_rb16(ioc);
         break;
     default:                    // unsupported type, we couldn't skip
         av_log(s, AV_LOG_ERROR, "unsupported amf type %d\n", amf_type);
@@ -641,8 +665,18 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
         } else if (amf_type == AMF_DATA_TYPE_NUMBER) {
             snprintf(str_val, sizeof(str_val), "%.f", num_val);
             av_dict_set(&s->metadata, key, str_val, 0);
-        } else if (amf_type == AMF_DATA_TYPE_STRING)
+        } else if (amf_type == AMF_DATA_TYPE_STRING) {
             av_dict_set(&s->metadata, key, str_val, 0);
+        } else if (amf_type == AMF_DATA_TYPE_DATE) {
+            time_t time;
+            struct tm t;
+            char datestr[128];
+            time =  date.milliseconds / 1000; // to seconds
+            localtime_r(&time, &t);
+            strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S %z", &t);
+
+            av_dict_set(&s->metadata, key, datestr, 0);
+        }
     }
 
     return 0;
@@ -683,7 +717,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;
     }
@@ -699,8 +733,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;
     }
 
@@ -718,6 +751,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);
 
@@ -758,12 +795,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;
 }
@@ -793,10 +830,10 @@ 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;
     }
 }
 
@@ -966,144 +1003,141 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
 
 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
@@ -1227,22 +1261,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;
         }
@@ -1262,12 +1280,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;
         }
     }
@@ -1279,10 +1296,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:
@@ -1366,3 +1383,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,
+};
+
+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,
+};