flvdec.c 17.6 KB
Newer Older
1
/*
2
 * FLV demuxer
3
 * Copyright (c) 2003 The FFmpeg Project
4
 *
5 6 7 8 9
 * This demuxer will generate a 1 byte extradata for VP6F content.
 * It is composed of:
 *  - upper 4bits: difference between encoded width and visible width
 *  - lower 4bits: difference between encoded height and visible height
 *
10 11 12
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
13 14
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
15
 * version 2.1 of the License, or (at your option) any later version.
16
 *
17
 * FFmpeg is distributed in the hope that it will be useful,
18 19 20 21 22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
23
 * License along with FFmpeg; if not, write to the Free Software
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25
 */
26

27
#include "libavutil/avstring.h"
28
#include "libavcodec/bytestream.h"
29
#include "libavcodec/mpeg4audio.h"
30
#include "avformat.h"
31
#include "flv.h"
32

Baptiste Coudurier's avatar
Baptiste Coudurier committed
33 34 35 36
typedef struct {
    int wrong_dts; ///< wrong dts due to negative cts
} FLVContext;

37 38 39 40 41
static int flv_probe(AVProbeData *p)
{
    const uint8_t *d;

    d = p->buf;
42
    if (d[0] == 'F' && d[1] == 'L' && d[2] == 'V' && d[3] < 5 && d[5]==0 && AV_RB32(d+5)>8) {
43
        return AVPROBE_SCORE_MAX;
44 45 46 47
    }
    return 0;
}

Allan Hsu's avatar
Allan Hsu committed
48 49 50 51
static void flv_set_audio_codec(AVFormatContext *s, AVStream *astream, int flv_codecid) {
    AVCodecContext *acodec = astream->codec;
    switch(flv_codecid) {
        //no distinction between S16 and S8 PCM codec flags
52
        case FLV_CODECID_PCM:
53
            acodec->codec_id = acodec->bits_per_coded_sample == 8 ? CODEC_ID_PCM_U8 :
54
#if HAVE_BIGENDIAN
Michael Niedermayer's avatar
Michael Niedermayer committed
55 56 57 58 59
                                CODEC_ID_PCM_S16BE;
#else
                                CODEC_ID_PCM_S16LE;
#endif
            break;
Allan Hsu's avatar
Allan Hsu committed
60
        case FLV_CODECID_PCM_LE:
61
            acodec->codec_id = acodec->bits_per_coded_sample == 8 ? CODEC_ID_PCM_U8 : CODEC_ID_PCM_S16LE; break;
62
        case FLV_CODECID_AAC  : acodec->codec_id = CODEC_ID_AAC;                                    break;
Allan Hsu's avatar
Allan Hsu committed
63
        case FLV_CODECID_ADPCM: acodec->codec_id = CODEC_ID_ADPCM_SWF;                              break;
64 65 66 67
        case FLV_CODECID_SPEEX:
            acodec->codec_id = CODEC_ID_SPEEX;
            acodec->sample_rate = 16000;
            break;
Aurelien Jacobs's avatar
Aurelien Jacobs committed
68
        case FLV_CODECID_MP3  : acodec->codec_id = CODEC_ID_MP3      ; astream->need_parsing = AVSTREAM_PARSE_FULL; break;
69
        case FLV_CODECID_NELLYMOSER_8KHZ_MONO:
Allan Hsu's avatar
Allan Hsu committed
70
            acodec->sample_rate = 8000; //in case metadata does not otherwise declare samplerate
71 72 73 74 75 76
            acodec->codec_id = CODEC_ID_NELLYMOSER;
            break;
        case FLV_CODECID_NELLYMOSER_16KHZ_MONO:
            acodec->sample_rate = 16000;
            acodec->codec_id = CODEC_ID_NELLYMOSER;
            break;
Allan Hsu's avatar
Allan Hsu committed
77
        case FLV_CODECID_NELLYMOSER:
Benjamin Larsson's avatar
Benjamin Larsson committed
78 79
            acodec->codec_id = CODEC_ID_NELLYMOSER;
            break;
Allan Hsu's avatar
Allan Hsu committed
80 81 82 83 84 85 86 87 88 89 90
        default:
            av_log(s, AV_LOG_INFO, "Unsupported audio codec (%x)\n", flv_codecid >> FLV_AUDIO_CODECID_OFFSET);
            acodec->codec_tag = flv_codecid >> FLV_AUDIO_CODECID_OFFSET;
    }
}

static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, int flv_codecid) {
    AVCodecContext *vcodec = vstream->codec;
    switch(flv_codecid) {
        case FLV_CODECID_H263  : vcodec->codec_id = CODEC_ID_FLV1   ; break;
        case FLV_CODECID_SCREEN: vcodec->codec_id = CODEC_ID_FLASHSV; break;
91
        case FLV_CODECID_SCREEN2: vcodec->codec_id = CODEC_ID_FLASHSV2; break;
Allan Hsu's avatar
Allan Hsu committed
92
        case FLV_CODECID_VP6   : vcodec->codec_id = CODEC_ID_VP6F   ;
Aurelien Jacobs's avatar
Aurelien Jacobs committed
93 94 95
        case FLV_CODECID_VP6A  :
            if(flv_codecid == FLV_CODECID_VP6A)
                vcodec->codec_id = CODEC_ID_VP6A;
Allan Hsu's avatar
Allan Hsu committed
96 97 98 99
            if(vcodec->extradata_size != 1) {
                vcodec->extradata_size = 1;
                vcodec->extradata = av_malloc(1);
            }
100
            vcodec->extradata[0] = avio_r8(s->pb);
Allan Hsu's avatar
Allan Hsu committed
101
            return 1; // 1 byte body size adjustment for flv_read_packet()
102 103 104
        case FLV_CODECID_H264:
            vcodec->codec_id = CODEC_ID_H264;
            return 3; // not 4, reading packet type will consume one byte
Allan Hsu's avatar
Allan Hsu committed
105 106 107 108 109 110 111 112
        default:
            av_log(s, AV_LOG_INFO, "Unsupported video codec (%x)\n", flv_codecid);
            vcodec->codec_tag = flv_codecid;
    }

    return 0;
}

113
static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize) {
114
    int length = avio_rb16(ioc);
115 116 117 118
    if(length >= buffsize) {
        url_fskip(ioc, length);
        return -1;
    }
119

120
    avio_read(ioc, buffer, length);
121

122
    buffer[length] = '\0';
123

124
    return length;
125 126
}

Pascal Massimino's avatar
Pascal Massimino committed
127
static int amf_parse_object(AVFormatContext *s, AVStream *astream, AVStream *vstream, const char *key, int64_t max_pos, int depth) {
Allan Hsu's avatar
Allan Hsu committed
128
    AVCodecContext *acodec, *vcodec;
129
    AVIOContext *ioc;
Allan Hsu's avatar
Allan Hsu committed
130
    AMFDataType amf_type;
131
    char str_val[256];
Allan Hsu's avatar
Allan Hsu committed
132 133 134
    double num_val;

    num_val = 0;
135
    ioc = s->pb;
Allan Hsu's avatar
Allan Hsu committed
136

137
    amf_type = avio_r8(ioc);
Allan Hsu's avatar
Allan Hsu committed
138 139 140

    switch(amf_type) {
        case AMF_DATA_TYPE_NUMBER:
141
            num_val = av_int2dbl(avio_rb64(ioc)); break;
Allan Hsu's avatar
Allan Hsu committed
142
        case AMF_DATA_TYPE_BOOL:
143
            num_val = avio_r8(ioc); break;
Allan Hsu's avatar
Allan Hsu committed
144
        case AMF_DATA_TYPE_STRING:
145
            if(amf_get_string(ioc, str_val, sizeof(str_val)) < 0)
Allan Hsu's avatar
Allan Hsu committed
146 147 148 149 150
                return -1;
            break;
        case AMF_DATA_TYPE_OBJECT: {
            unsigned int keylen;

151
            while(url_ftell(ioc) < max_pos - 2 && (keylen = avio_rb16(ioc))) {
Allan Hsu's avatar
Allan Hsu committed
152 153 154 155
                url_fskip(ioc, keylen); //skip key string
                if(amf_parse_object(s, NULL, NULL, NULL, max_pos, depth + 1) < 0)
                    return -1; //if we couldn't skip, bomb out.
            }
156
            if(avio_r8(ioc) != AMF_END_OF_OBJECT)
Allan Hsu's avatar
Allan Hsu committed
157 158 159 160 161 162 163 164 165
                return -1;
        }
            break;
        case AMF_DATA_TYPE_NULL:
        case AMF_DATA_TYPE_UNDEFINED:
        case AMF_DATA_TYPE_UNSUPPORTED:
            break; //these take up no additional space
        case AMF_DATA_TYPE_MIXEDARRAY:
            url_fskip(ioc, 4); //skip 32-bit max array index
166
            while(url_ftell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) {
Allan Hsu's avatar
Allan Hsu committed
167
                //this is the only case in which we would want a nested parse to not skip over the object
168
                if(amf_parse_object(s, astream, vstream, str_val, max_pos, depth + 1) < 0)
Allan Hsu's avatar
Allan Hsu committed
169 170
                    return -1;
            }
171
            if(avio_r8(ioc) != AMF_END_OF_OBJECT)
Allan Hsu's avatar
Allan Hsu committed
172 173 174 175 176
                return -1;
            break;
        case AMF_DATA_TYPE_ARRAY: {
            unsigned int arraylen, i;

177
            arraylen = avio_rb32(ioc);
Allan Hsu's avatar
Allan Hsu committed
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
            for(i = 0; i < arraylen && url_ftell(ioc) < max_pos - 1; i++) {
                if(amf_parse_object(s, NULL, NULL, NULL, max_pos, depth + 1) < 0)
                    return -1; //if we couldn't skip, bomb out.
            }
        }
            break;
        case AMF_DATA_TYPE_DATE:
            url_fskip(ioc, 8 + 2); //timestamp (double) and UTC offset (int16)
            break;
        default: //unsupported type, we couldn't skip
            return -1;
    }

    if(depth == 1 && key) { //only look for metadata values when we are not nested and key != NULL
        acodec = astream ? astream->codec : NULL;
        vcodec = vstream ? vstream->codec : NULL;

        if(amf_type == AMF_DATA_TYPE_BOOL) {
196
            av_strlcpy(str_val, num_val > 0 ? "true" : "false", sizeof(str_val));
197
            av_metadata_set2(&s->metadata, key, str_val, 0);
Allan Hsu's avatar
Allan Hsu committed
198
        } else if(amf_type == AMF_DATA_TYPE_NUMBER) {
199
            snprintf(str_val, sizeof(str_val), "%.f", num_val);
200
            av_metadata_set2(&s->metadata, key, str_val, 0);
Allan Hsu's avatar
Allan Hsu committed
201
            if(!strcmp(key, "duration")) s->duration = num_val * AV_TIME_BASE;
202 203
            else if(!strcmp(key, "videodatarate") && vcodec && 0 <= (int)(num_val * 1024.0))
                vcodec->bit_rate = num_val * 1024.0;
Howard Chu's avatar
Howard Chu committed
204 205
            else if(!strcmp(key, "audiodatarate") && acodec && 0 <= (int)(num_val * 1024.0))
                acodec->bit_rate = num_val * 1024.0;
206
        } else if (amf_type == AMF_DATA_TYPE_STRING)
207
            av_metadata_set2(&s->metadata, key, str_val, 0);
Allan Hsu's avatar
Allan Hsu committed
208 209 210 211 212
    }

    return 0;
}

Pascal Massimino's avatar
Pascal Massimino committed
213
static int flv_read_metabody(AVFormatContext *s, int64_t next_pos) {
Allan Hsu's avatar
Allan Hsu committed
214 215
    AMFDataType type;
    AVStream *stream, *astream, *vstream;
216
    AVIOContext *ioc;
217
    int i;
218
    char buffer[11]; //only needs to hold the string "onMetaData". Anything longer is something we don't want.
Allan Hsu's avatar
Allan Hsu committed
219 220 221

    astream = NULL;
    vstream = NULL;
222
    ioc = s->pb;
Allan Hsu's avatar
Allan Hsu committed
223 224

    //first object needs to be "onMetaData" string
225
    type = avio_r8(ioc);
226
    if(type != AMF_DATA_TYPE_STRING || amf_get_string(ioc, buffer, sizeof(buffer)) < 0 || strcmp(buffer, "onMetaData"))
Allan Hsu's avatar
Allan Hsu committed
227 228 229 230 231
        return -1;

    //find the streams now so that amf_parse_object doesn't need to do the lookup every time it is called.
    for(i = 0; i < s->nb_streams; i++) {
        stream = s->streams[i];
232 233
        if     (stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) astream = stream;
        else if(stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) vstream = stream;
Allan Hsu's avatar
Allan Hsu committed
234 235 236
    }

    //parse the second object (we want a mixed array)
237
    if(amf_parse_object(s, astream, vstream, buffer, next_pos, 0) < 0)
Allan Hsu's avatar
Allan Hsu committed
238 239 240 241 242
        return -1;

    return 0;
}

243 244 245 246
static AVStream *create_stream(AVFormatContext *s, int is_audio){
    AVStream *st = av_new_stream(s, is_audio);
    if (!st)
        return NULL;
247
    st->codec->codec_type = is_audio ? AVMEDIA_TYPE_AUDIO : AVMEDIA_TYPE_VIDEO;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
248
    av_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
249 250 251
    return st;
}

252 253 254
static int flv_read_header(AVFormatContext *s,
                           AVFormatParameters *ap)
{
255
    int offset, flags;
256

257
    url_fskip(s->pb, 4);
258
    flags = avio_r8(s->pb);
259 260 261 262 263 264
    /* old flvtool cleared this field */
    /* FIXME: better fix needed */
    if (!flags) {
        flags = FLV_HEADER_FLAG_HASVIDEO | FLV_HEADER_FLAG_HASAUDIO;
        av_log(s, AV_LOG_WARNING, "Broken FLV file, which says no streams present, this might fail\n");
    }
265

Michael Niedermayer's avatar
Michael Niedermayer committed
266 267 268 269
    if((flags & (FLV_HEADER_FLAG_HASVIDEO|FLV_HEADER_FLAG_HASAUDIO))
             != (FLV_HEADER_FLAG_HASVIDEO|FLV_HEADER_FLAG_HASAUDIO))
        s->ctx_flags |= AVFMTCTX_NOHEADER;

270
    if(flags & FLV_HEADER_FLAG_HASVIDEO){
271
        if(!create_stream(s, 0))
272
            return AVERROR(ENOMEM);
273 274
    }
    if(flags & FLV_HEADER_FLAG_HASAUDIO){
275
        if(!create_stream(s, 1))
276
            return AVERROR(ENOMEM);
277 278
    }

279
    offset = avio_rb32(s->pb);
280
    url_fseek(s->pb, offset, SEEK_SET);
Howard Chu's avatar
Howard Chu committed
281
    url_fskip(s->pb, 4);
282

283 284
    s->start_time = 0;

285 286 287
    return 0;
}

288 289 290 291 292 293 294
static int flv_get_extradata(AVFormatContext *s, AVStream *st, int size)
{
    av_free(st->codec->extradata);
    st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
    if (!st->codec->extradata)
        return AVERROR(ENOMEM);
    st->codec->extradata_size = size;
295
    avio_read(s->pb, st->codec->extradata, st->codec->extradata_size);
296 297 298
    return 0;
}

299 300
static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
{
Baptiste Coudurier's avatar
Baptiste Coudurier committed
301
    FLVContext *flv = s->priv_data;
Pascal Massimino's avatar
Pascal Massimino committed
302 303
    int ret, i, type, size, flags, is_audio;
    int64_t next, pos;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
304
    int64_t dts, pts = AV_NOPTS_VALUE;
Alex Beregszaszi's avatar
Alex Beregszaszi committed
305
    AVStream *st = NULL;
306

Howard Chu's avatar
Howard Chu committed
307
 for(;;url_fskip(s->pb, 4)){ /* pkt size is repeated at end. skip it */
308
    pos = url_ftell(s->pb);
309 310 311 312
    type = avio_r8(s->pb);
    size = avio_rb24(s->pb);
    dts = avio_rb24(s->pb);
    dts |= avio_r8(s->pb) << 24;
313
//    av_log(s, AV_LOG_DEBUG, "type:%d, size:%d, dts:%d\n", type, size, dts);
314
    if (url_feof(s->pb))
315
        return AVERROR_EOF;
316
    url_fskip(s->pb, 3); /* stream id, always 0 */
317
    flags = 0;
318

Michael Niedermayer's avatar
cleanup  
Michael Niedermayer committed
319 320
    if(size == 0)
        continue;
321

322
    next= size + url_ftell(s->pb);
323

324
    if (type == FLV_TAG_TYPE_AUDIO) {
Michael Niedermayer's avatar
cleanup  
Michael Niedermayer committed
325
        is_audio=1;
326
        flags = avio_r8(s->pb);
327
        size--;
328
    } else if (type == FLV_TAG_TYPE_VIDEO) {
Michael Niedermayer's avatar
cleanup  
Michael Niedermayer committed
329
        is_audio=0;
330
        flags = avio_r8(s->pb);
331
        size--;
332 333
        if ((flags & 0xf0) == 0x50) /* video info / command frame */
            goto skip;
334
    } else {
335
        if (type == FLV_TAG_TYPE_META && size > 13+1+4)
Allan Hsu's avatar
Allan Hsu committed
336 337
            flv_read_metabody(s, next);
        else /* skip packet */
338
            av_log(s, AV_LOG_DEBUG, "skipping flv packet: type %d, size %d, flags %d\n", type, size, flags);
339
    skip:
340
        url_fseek(s->pb, next, SEEK_SET);
341
        continue;
342 343
    }

344 345 346 347
    /* skip empty data packets */
    if (!size)
        continue;

348 349 350
    /* now find stream */
    for(i=0;i<s->nb_streams;i++) {
        st = s->streams[i];
Michael Niedermayer's avatar
cleanup  
Michael Niedermayer committed
351 352
        if (st->id == is_audio)
            break;
353
    }
Michael Niedermayer's avatar
cleanup  
Michael Niedermayer committed
354
    if(i == s->nb_streams){
355
        av_log(s, AV_LOG_ERROR, "invalid stream\n");
356
        st= create_stream(s, is_audio);
357
        s->ctx_flags &= ~AVFMTCTX_NOHEADER;
358
    }
359
//    av_log(s, AV_LOG_DEBUG, "%d %X %d \n", is_audio, flags, st->discard);
360 361
    if(  (st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||         is_audio))
       ||(st->discard >= AVDISCARD_BIDIR  &&  ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && !is_audio))
362 363
       || st->discard >= AVDISCARD_ALL
       ){
364
        url_fseek(s->pb, next, SEEK_SET);
365
        continue;
366
    }
367
    if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY)
368
        av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME);
Michael Niedermayer's avatar
cleanup  
Michael Niedermayer committed
369 370 371
    break;
 }

372
    // if not streamed and no duration from metadata then seek to end to find the duration from the timestamps
373
    if(!url_is_streamed(s->pb) && (!s->duration || s->duration==AV_NOPTS_VALUE)){
374
        int size;
Pascal Massimino's avatar
Pascal Massimino committed
375 376
        const int64_t pos= url_ftell(s->pb);
        const int64_t fsize= url_fsize(s->pb);
377
        url_fseek(s->pb, fsize-4, SEEK_SET);
378
        size= avio_rb32(s->pb);
379
        url_fseek(s->pb, fsize-3-size, SEEK_SET);
380 381 382
        if(size == avio_rb24(s->pb) + 11){
            uint32_t ts = avio_rb24(s->pb);
            ts |= avio_r8(s->pb) << 24;
383
            s->duration = ts * (int64_t)AV_TIME_BASE / 1000;
384
        }
385
        url_fseek(s->pb, pos, SEEK_SET);
386 387
    }

Michael Niedermayer's avatar
cleanup  
Michael Niedermayer committed
388
    if(is_audio){
389
        if(!st->codec->channels || !st->codec->sample_rate || !st->codec->bits_per_coded_sample) {
390
            st->codec->channels = (flags & FLV_AUDIO_CHANNEL_MASK) == FLV_STEREO ? 2 : 1;
391
            st->codec->sample_rate = (44100 << ((flags & FLV_AUDIO_SAMPLERATE_MASK) >> FLV_AUDIO_SAMPLERATE_OFFSET) >> 3);
392
            st->codec->bits_per_coded_sample = (flags & FLV_AUDIO_SAMPLESIZE_MASK) ? 16 : 8;
393 394
        }
        if(!st->codec->codec_id){
Allan Hsu's avatar
Allan Hsu committed
395
            flv_set_audio_codec(s, st, flags & FLV_AUDIO_CODECID_MASK);
Michael Niedermayer's avatar
cleanup  
Michael Niedermayer committed
396 397
        }
    }else{
Allan Hsu's avatar
Allan Hsu committed
398
        size -= flv_set_video_codec(s, st, flags & FLV_VIDEO_CODECID_MASK);
Michael Niedermayer's avatar
Michael Niedermayer committed
399 400
    }

401 402
    if (st->codec->codec_id == CODEC_ID_AAC ||
        st->codec->codec_id == CODEC_ID_H264) {
403
        int type = avio_r8(s->pb);
404 405
        size--;
        if (st->codec->codec_id == CODEC_ID_H264) {
406
            int32_t cts = (avio_rb24(s->pb)+0xff800000)^0xff800000; // sign extension
Baptiste Coudurier's avatar
Baptiste Coudurier committed
407 408 409 410 411 412 413
            pts = dts + cts;
            if (cts < 0) { // dts are wrong
                flv->wrong_dts = 1;
                av_log(s, AV_LOG_WARNING, "negative cts, previous timestamps might be wrong\n");
            }
            if (flv->wrong_dts)
                dts = AV_NOPTS_VALUE;
414 415
        }
        if (type == 0) {
416
            if ((ret = flv_get_extradata(s, st, size)) < 0)
417
                return ret;
418 419 420 421
            if (st->codec->codec_id == CODEC_ID_AAC) {
                MPEG4AudioConfig cfg;
                ff_mpeg4audio_get_config(&cfg, st->codec->extradata,
                                         st->codec->extradata_size);
422
                st->codec->channels = cfg.channels;
423 424 425 426
                if (cfg.ext_sample_rate)
                    st->codec->sample_rate = cfg.ext_sample_rate;
                else
                    st->codec->sample_rate = cfg.sample_rate;
Luca Barbato's avatar
Luca Barbato committed
427
                av_dlog(s, "mp4a config channels %d sample rate %d\n",
428 429 430
                        st->codec->channels, st->codec->sample_rate);
            }

Howard Chu's avatar
Howard Chu committed
431 432
            ret = AVERROR(EAGAIN);
            goto leave;
433 434 435
        }
    }

436
    /* skip empty data packets */
Howard Chu's avatar
Howard Chu committed
437 438 439 440
    if (!size) {
        ret = AVERROR(EAGAIN);
        goto leave;
    }
441

442
    ret= av_get_packet(s->pb, pkt, size);
443
    if (ret < 0) {
444
        return AVERROR(EIO);
445 446 447 448
    }
    /* note: we need to modify the packet size here to handle the last
       packet */
    pkt->size = ret;
449
    pkt->dts = dts;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
450
    pkt->pts = pts == AV_NOPTS_VALUE ? dts : pts;
451
    pkt->stream_index = st->index;
452

453
    if (is_audio || ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY))
454
        pkt->flags |= AV_PKT_FLAG_KEY;
455

Howard Chu's avatar
Howard Chu committed
456 457
leave:
    url_fskip(s->pb, 4);
458 459 460
    return ret;
}

Howard Chu's avatar
Howard Chu committed
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
static int flv_read_seek(AVFormatContext *s, int stream_index,
    int64_t ts, int flags)
{
    return av_url_read_fseek(s->pb, stream_index, ts, flags);
}

#if 0 /* don't know enough to implement this */
static int flv_read_seek2(AVFormatContext *s, int stream_index,
    int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
{
    int ret = AVERROR(ENOSYS);

    if (ts - min_ts > (uint64_t)(max_ts - ts)) flags |= AVSEEK_FLAG_BACKWARD;

    if (url_is_streamed(s->pb)) {
        if (stream_index < 0) {
            stream_index = av_find_default_stream_index(s);
            if (stream_index < 0)
                return -1;

            /* timestamp for default must be expressed in AV_TIME_BASE units */
            ts = av_rescale_rnd(ts, 1000, AV_TIME_BASE,
                flags & AVSEEK_FLAG_BACKWARD ? AV_ROUND_DOWN : AV_ROUND_UP);
        }
        ret = av_url_read_fseek(s->pb, stream_index, ts, flags);
    }

    if (ret == AVERROR(ENOSYS))
        ret = av_seek_frame(s, stream_index, ts, flags);
    return ret;
}
#endif

494
AVInputFormat ff_flv_demuxer = {
495
    "flv",
496
    NULL_IF_CONFIG_SMALL("FLV format"),
Baptiste Coudurier's avatar
Baptiste Coudurier committed
497
    sizeof(FLVContext),
498 499 500
    flv_probe,
    flv_read_header,
    flv_read_packet,
Howard Chu's avatar
Howard Chu committed
501 502 503 504
    .read_seek = flv_read_seek,
#if 0
    .read_seek2 = flv_read_seek2,
#endif
505 506 507
    .extensions = "flv",
    .value = CODEC_ID_FLV1,
};