nsvdec.c 26.4 KB
Newer Older
1
/*
2
 * NSV demuxer
3
 * Copyright (c) 2004 The Libav Project
4
 *
5
 * This file is part of Libav.
6
 *
7
 * Libav is free software; you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * Libav is distributed in the hope that it will be useful,
13 14 15 16 17
 * 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
18
 * License along with Libav; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21

22
#include "libavutil/attributes.h"
23
#include "libavutil/mathematics.h"
24
#include "avformat.h"
25
#include "internal.h"
26
#include "riff.h"
27
#include "libavutil/dict.h"
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

//#define DEBUG_DUMP_INDEX // XXX dumbdriving-271.nsv breaks with it commented!!
#define CHECK_SUBSEQUENT_NSVS
//#define DISABLE_AUDIO

/* max bytes to crawl for trying to resync
 * stupid streaming servers don't start at chunk boundaries...
 */
#define NSV_MAX_RESYNC (500*1024)
#define NSV_MAX_RESYNC_TRIES 300

/*
 * First version by Francois Revol - revol@free.fr
 * References:
 * (1) http://www.multimedia.cx/nsv-format.txt
 * seems someone came to the same conclusions as me, and updated it:
 * (2) http://www.stud.ktu.lt/~vitslav/nsv/nsv-format.txt
 *     http://www.stud.ktu.lt/~vitslav/nsv/
46 47
 * official docs
 * (3) http://ultravox.aol.com/NSVFormat.rtf
48 49 50 51 52 53 54 55
 * Sample files:
 * (S1) http://www.nullsoft.com/nsv/samples/
 * http://www.nullsoft.com/nsv/samples/faster.nsv
 * http://streamripper.sourceforge.net/openbb/read.php?TID=492&page=4
 */

/*
 * notes on the header (Francois Revol):
56 57
 *
 * It is followed by strings, then a table, but nothing tells
58 59 60 61 62 63 64 65
 * where the table begins according to (1). After checking faster.nsv,
 * I believe NVSf[16-19] gives the size of the strings data
 * (that is the offset of the data table after the header).
 * After checking all samples from (S1) all confirms this.
 *
 * Then, about NSVf[12-15], faster.nsf has 179700. When veiwing it in VLC,
 * I noticed there was about 1 NVSs chunk/s, so I ran
 * strings faster.nsv | grep NSVs | wc -l
66
 * which gave me 180. That leads me to think that NSVf[12-15] might be the
67 68 69 70 71
 * file length in milliseconds.
 * Let's try that:
 * for f in *.nsv; do HTIME="$(od -t x4 "$f" | head -1 | sed 's/.* //')"; echo "'$f' $((0x$HTIME))s = $((0x$HTIME/1000/60)):$((0x$HTIME/1000%60))"; done
 * except for nstrailer (which doesn't have an NSVf header), it repports correct time.
 *
72
 * nsvtrailer.nsv (S1) does not have any NSVf header, only NSVs chunks,
73
 * so the header seems to not be mandatory. (for streaming).
74
 *
75
 * index slice duration check (excepts nsvtrailer.nsv):
76 77 78 79 80
 * for f in [^n]*.nsv; do
 *     DUR="$(avconv -i "$f" 2> /dev/null | grep 'NSVf duration' | cut -d ' ' -f 4)"
 *     IC="$(avconv -i "$f" 2> /dev/null | grep 'INDEX ENTRIES' | cut -d ' ' -f 2)"
 *     echo "duration $DUR, slite time $(($DUR/$IC))"
 * done
81 82 83 84 85 86 87 88 89 90 91 92 93 94
 */

/*
 * TODO:
 * - handle timestamps !!!
 * - use index
 * - mime-type in probe()
 * - seek
 */

#if 0
struct NSVf_header {
    uint32_t chunk_tag; /* 'NSVf' */
    uint32_t chunk_size;
Diego Biurrun's avatar
Diego Biurrun committed
95
    uint32_t file_size; /* max 4GB ??? no one learns anything it seems :^) */
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    uint32_t file_length; //unknown1;  /* what about MSB of file_size ? */
    uint32_t info_strings_size; /* size of the info strings */ //unknown2;
    uint32_t table_entries;
    uint32_t table_entries_used; /* the left ones should be -1 */
};

struct NSVs_header {
    uint32_t chunk_tag; /* 'NSVs' */
    uint32_t v4cc;      /* or 'NONE' */
    uint32_t a4cc;      /* or 'NONE' */
    uint16_t vwidth;    /* assert(vwidth%16==0) */
    uint16_t vheight;   /* assert(vheight%16==0) */
    uint8_t framerate;  /* value = (framerate&0x80)?frtable[frameratex0x7f]:framerate */
    uint16_t unknown;
};

struct nsv_avchunk_header {
    uint8_t vchunk_size_lsb;
    uint16_t vchunk_size_msb; /* value = (vchunk_size_msb << 4) | (vchunk_size_lsb >> 4) */
    uint16_t achunk_size;
};

struct nsv_pcm_header {
    uint8_t bits_per_sample;
    uint8_t channel_count;
    uint16_t sample_rate;
};
#endif

/* variation from avi.h */
/*typedef struct CodecTag {
    int id;
    unsigned int tag;
} CodecTag;*/

/* tags */

#define T_NSVF MKTAG('N', 'S', 'V', 'f') /* file header */
#define T_NSVS MKTAG('N', 'S', 'V', 's') /* chunk header */
#define T_TOC2 MKTAG('T', 'O', 'C', '2') /* extra index marker */
#define T_NONE MKTAG('N', 'O', 'N', 'E') /* null a/v 4CC */
#define T_SUBT MKTAG('S', 'U', 'B', 'T') /* subtitle aux data */
#define T_ASYN MKTAG('A', 'S', 'Y', 'N') /* async a/v aux marker */
#define T_KEYF MKTAG('K', 'E', 'Y', 'F') /* video keyframe aux marker (addition) */

#define TB_NSVF MKBETAG('N', 'S', 'V', 'f')
#define TB_NSVS MKBETAG('N', 'S', 'V', 's')

144
/* hardcoded stream indexes */
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
#define NSV_ST_VIDEO 0
#define NSV_ST_AUDIO 1
#define NSV_ST_SUBT 2

enum NSVStatus {
    NSV_UNSYNC,
    NSV_FOUND_NSVF,
    NSV_HAS_READ_NSVF,
    NSV_FOUND_NSVS,
    NSV_HAS_READ_NSVS,
    NSV_FOUND_BEEF,
    NSV_GOT_VIDEO,
    NSV_GOT_AUDIO,
};

typedef struct NSVStream {
    int frame_offset; /* current frame (video) or byte (audio) counter
                         (used to compute the pts) */
    int scale;
164
    int rate;
165 166
    int sample_size; /* audio only data */
    int start;
167

168 169 170 171 172 173 174
    int new_frame_offset; /* temporary storage (used during seek) */
    int cum_len; /* temporary storage (used during seek) */
} NSVStream;

typedef struct {
    int  base_offset;
    int  NSVf_end;
175
    uint32_t *nsvs_file_offset;
176 177 178 179 180 181 182
    int index_entries;
    enum NSVStatus state;
    AVPacket ahead[2]; /* [v, a] if .data is !NULL there is something */
    /* cached */
    int64_t duration;
    uint32_t vtag, atag;
    uint16_t vwidth, vheight;
183
    int16_t avsync;
184
    AVRational framerate;
185
    uint32_t *nsvs_timestamps;
186 187 188
    //DVDemuxContext* dv_demux;
} NSVContext;

189
static const AVCodecTag nsv_codec_video_tags[] = {
190 191 192 193 194 195 196 197 198
    { AV_CODEC_ID_VP3, MKTAG('V', 'P', '3', ' ') },
    { AV_CODEC_ID_VP3, MKTAG('V', 'P', '3', '0') },
    { AV_CODEC_ID_VP3, MKTAG('V', 'P', '3', '1') },
    { AV_CODEC_ID_VP5, MKTAG('V', 'P', '5', ' ') },
    { AV_CODEC_ID_VP5, MKTAG('V', 'P', '5', '0') },
    { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', ' ') },
    { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '0') },
    { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '1') },
    { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '2') },
199
/*
200 201
    { AV_CODEC_ID_VP4, MKTAG('V', 'P', '4', ' ') },
    { AV_CODEC_ID_VP4, MKTAG('V', 'P', '4', '0') },
202
*/
203 204 205
    { AV_CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'D') }, /* cf sample xvid decoder from nsv_codec_sdk.zip */
    { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', '3') },
    { AV_CODEC_ID_NONE, 0 },
206 207
};

208
static const AVCodecTag nsv_codec_audio_tags[] = {
209 210 211 212 213 214
    { AV_CODEC_ID_MP3,       MKTAG('M', 'P', '3', ' ') },
    { AV_CODEC_ID_AAC,       MKTAG('A', 'A', 'C', ' ') },
    { AV_CODEC_ID_AAC,       MKTAG('A', 'A', 'C', 'P') },
    { AV_CODEC_ID_SPEEX,     MKTAG('S', 'P', 'X', ' ') },
    { AV_CODEC_ID_PCM_U16LE, MKTAG('P', 'C', 'M', ' ') },
    { AV_CODEC_ID_NONE,      0 },
215 216 217 218 219
};

//static int nsv_load_index(AVFormatContext *s);
static int nsv_read_chunk(AVFormatContext *s, int fill_header);

220 221 222 223 224 225
#define print_tag(str, tag, size)       \
    av_dlog(NULL, "%s: tag=%c%c%c%c\n", \
            str, tag & 0xff,            \
            (tag >> 8) & 0xff,          \
            (tag >> 16) & 0xff,         \
            (tag >> 24) & 0xff);
226 227 228 229 230

/* try to find something we recognize, and set the state accordingly */
static int nsv_resync(AVFormatContext *s)
{
    NSVContext *nsv = s->priv_data;
231
    AVIOContext *pb = s->pb;
Mike Melanson's avatar
Mike Melanson committed
232
    uint32_t v = 0;
233
    int i;
234

235
    av_dlog(s, "%s(), offset = %"PRId64", state = %d\n", __FUNCTION__, avio_tell(pb), nsv->state);
236

237
    //nsv->state = NSV_UNSYNC;
238

239
    for (i = 0; i < NSV_MAX_RESYNC; i++) {
Anton Khirnov's avatar
Anton Khirnov committed
240
        if (pb->eof_reached) {
241
            av_dlog(s, "NSV EOF\n");
242 243 244 245
            nsv->state = NSV_UNSYNC;
            return -1;
        }
        v <<= 8;
246
        v |= avio_r8(pb);
247
        if (i < 8) {
248
            av_dlog(s, "NSV resync: [%d] = %02x\n", i, v & 0x0FF);
249
        }
250

251
        if ((v & 0x0000ffff) == 0xefbe) { /* BEEF */
252
            av_dlog(s, "NSV resynced on BEEF after %d bytes\n", i+1);
253 254 255 256 257
            nsv->state = NSV_FOUND_BEEF;
            return 0;
        }
        /* we read as big endian, thus the MK*BE* */
        if (v == TB_NSVF) { /* NSVf */
258
            av_dlog(s, "NSV resynced on NSVf after %d bytes\n", i+1);
259 260 261 262
            nsv->state = NSV_FOUND_NSVF;
            return 0;
        }
        if (v == MKBETAG('N', 'S', 'V', 's')) { /* NSVs */
263
            av_dlog(s, "NSV resynced on NSVs after %d bytes\n", i+1);
264 265 266
            nsv->state = NSV_FOUND_NSVS;
            return 0;
        }
267

268
    }
269
    av_dlog(s, "NSV sync lost\n");
270 271 272
    return -1;
}

273
static int nsv_parse_NSVf_header(AVFormatContext *s)
274 275
{
    NSVContext *nsv = s->priv_data;
276
    AVIOContext *pb = s->pb;
277
    unsigned int av_unused file_size;
278
    unsigned int size;
279 280 281 282 283
    int64_t duration;
    int strings_size;
    int table_entries;
    int table_entries_used;

284
    av_dlog(s, "%s()\n", __FUNCTION__);
285 286

    nsv->state = NSV_UNSYNC; /* in case we fail */
287

288
    size = avio_rl32(pb);
289 290 291 292
    if (size < 28)
        return -1;
    nsv->NSVf_end = size;

293 294
    //s->file_size = (uint32_t)avio_rl32(pb);
    file_size = (uint32_t)avio_rl32(pb);
295 296
    av_dlog(s, "NSV NSVf chunk_size %u\n", size);
    av_dlog(s, "NSV NSVf file_size %u\n", file_size);
297

298
    nsv->duration = duration = avio_rl32(pb); /* in ms */
299
    av_dlog(s, "NSV NSVf duration %"PRId64" ms\n", duration);
300 301
    // XXX: store it in AVStreams

302 303 304
    strings_size = avio_rl32(pb);
    table_entries = avio_rl32(pb);
    table_entries_used = avio_rl32(pb);
305 306
    av_dlog(s, "NSV NSVf info-strings size: %d, table entries: %d, bis %d\n",
            strings_size, table_entries, table_entries_used);
Anton Khirnov's avatar
Anton Khirnov committed
307
    if (pb->eof_reached)
308
        return -1;
309

310
    av_dlog(s, "NSV got header; filepos %"PRId64"\n", avio_tell(pb));
311 312 313 314 315 316 317

    if (strings_size > 0) {
        char *strings; /* last byte will be '\0' to play safe with str*() */
        char *p, *endp;
        char *token, *value;
        char quote;

318 319 320
        p = strings = av_mallocz((size_t)strings_size + 1);
        if (!p)
            return AVERROR(ENOMEM);
321
        endp = strings + strings_size;
322
        avio_read(pb, strings, strings_size);
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
        while (p < endp) {
            while (*p == ' ')
                p++; /* strip out spaces */
            if (p >= endp-2)
                break;
            token = p;
            p = strchr(p, '=');
            if (!p || p >= endp-2)
                break;
            *p++ = '\0';
            quote = *p++;
            value = p;
            p = strchr(p, quote);
            if (!p || p >= endp)
                break;
            *p++ = '\0';
339
            av_dlog(s, "NSV NSVf INFO: %s='%s'\n", token, value);
340
            av_dict_set(&s->metadata, token, value, 0);
341 342 343
        }
        av_free(strings);
    }
Anton Khirnov's avatar
Anton Khirnov committed
344
    if (pb->eof_reached)
345
        return -1;
346

347
    av_dlog(s, "NSV got infos; filepos %"PRId64"\n", avio_tell(pb));
348 349

    if (table_entries_used > 0) {
350
        int i;
351
        nsv->index_entries = table_entries_used;
352
        if((unsigned)table_entries_used >= UINT_MAX / sizeof(uint32_t))
353
            return -1;
354
        nsv->nsvs_file_offset = av_malloc((unsigned)table_entries_used * sizeof(uint32_t));
355 356
        if (!nsv->nsvs_file_offset)
            return AVERROR(ENOMEM);
357 358

        for(i=0;i<table_entries_used;i++)
359
            nsv->nsvs_file_offset[i] = avio_rl32(pb) + size;
360 361

        if(table_entries > table_entries_used &&
362
           avio_rl32(pb) == MKTAG('T','O','C','2')) {
363
            nsv->nsvs_timestamps = av_malloc((unsigned)table_entries_used*sizeof(uint32_t));
364 365
            if (!nsv->nsvs_timestamps)
                return AVERROR(ENOMEM);
366
            for(i=0;i<table_entries_used;i++) {
367
                nsv->nsvs_timestamps[i] = avio_rl32(pb);
368 369
            }
        }
370 371
    }

372
    av_dlog(s, "NSV got index; filepos %"PRId64"\n", avio_tell(pb));
373

374 375 376
#ifdef DEBUG_DUMP_INDEX
#define V(v) ((v<0x20 || v > 127)?'.':v)
    /* dump index */
377 378
    av_dlog(s, "NSV %d INDEX ENTRIES:\n", table_entries);
    av_dlog(s, "NSV [dataoffset][fileoffset]\n", table_entries);
379 380
    for (i = 0; i < table_entries; i++) {
        unsigned char b[8];
381
        avio_seek(pb, size + nsv->nsvs_file_offset[i], SEEK_SET);
382
        avio_read(pb, b, 8);
383
        av_dlog(s, "NSV [0x%08lx][0x%08lx]: %02x %02x %02x %02x %02x %02x %02x %02x"
384
           "%c%c%c%c%c%c%c%c\n",
385
           nsv->nsvs_file_offset[i], size + nsv->nsvs_file_offset[i],
386
           b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
387
           V(b[0]), V(b[1]), V(b[2]), V(b[3]), V(b[4]), V(b[5]), V(b[6]), V(b[7]) );
388
    }
389
    //avio_seek(pb, size, SEEK_SET); /* go back to end of header */
390 391
#undef V
#endif
392

393
    avio_seek(pb, nsv->base_offset + size, SEEK_SET); /* required for dumbdriving-271.nsv (2 extra bytes) */
394

Anton Khirnov's avatar
Anton Khirnov committed
395
    if (pb->eof_reached)
396 397 398 399 400
        return -1;
    nsv->state = NSV_HAS_READ_NSVF;
    return 0;
}

401
static int nsv_parse_NSVs_header(AVFormatContext *s)
402 403
{
    NSVContext *nsv = s->priv_data;
404
    AVIOContext *pb = s->pb;
405 406
    uint32_t vtag, atag;
    uint16_t vwidth, vheight;
407 408
    AVRational framerate;
    int i;
409 410
    AVStream *st;
    NSVStream *nst;
411
    av_dlog(s, "%s()\n", __FUNCTION__);
412

413 414 415 416 417
    vtag = avio_rl32(pb);
    atag = avio_rl32(pb);
    vwidth = avio_rl16(pb);
    vheight = avio_rl16(pb);
    i = avio_r8(pb);
418

419
    av_dlog(s, "NSV NSVs framerate code %2x\n", i);
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
    if(i&0x80) { /* odd way of giving native framerates from docs */
        int t=(i & 0x7F)>>2;
        if(t<16) framerate = (AVRational){1, t+1};
        else     framerate = (AVRational){t-15, 1};

        if(i&1){
            framerate.num *= 1000;
            framerate.den *= 1001;
        }

        if((i&3)==3)      framerate.num *= 24;
        else if((i&3)==2) framerate.num *= 25;
        else              framerate.num *= 30;
    }
    else
        framerate= (AVRational){i, 1};

437
    nsv->avsync = avio_rl16(pb);
438
    nsv->framerate = framerate;
439

440 441
    print_tag("NSV NSVs vtag", vtag, 0);
    print_tag("NSV NSVs atag", atag, 0);
442
    av_dlog(s, "NSV NSVs vsize %dx%d\n", vwidth, vheight);
443

444 445 446 447 448 449 450
    /* XXX change to ap != NULL ? */
    if (s->nb_streams == 0) { /* streams not yet published, let's do that */
        nsv->vtag = vtag;
        nsv->atag = atag;
        nsv->vwidth = vwidth;
        nsv->vheight = vwidth;
        if (vtag != T_NONE) {
451
            int i;
452
            st = avformat_new_stream(s, NULL);
453 454 455
            if (!st)
                goto fail;

456
            st->id = NSV_ST_VIDEO;
457 458 459 460
            nst = av_mallocz(sizeof(NSVStream));
            if (!nst)
                goto fail;
            st->priv_data = nst;
461
            st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
462
            st->codec->codec_tag = vtag;
463
            st->codec->codec_id = ff_codec_get_id(nsv_codec_video_tags, vtag);
464 465
            st->codec->width = vwidth;
            st->codec->height = vheight;
466
            st->codec->bits_per_coded_sample = 24; /* depth XXX */
467

468
            avpriv_set_pts_info(st, 64, framerate.den, framerate.num);
469
            st->start_time = 0;
470
            st->duration = av_rescale(nsv->duration, framerate.num, 1000*framerate.den);
471 472 473 474 475 476 477 478 479 480

            for(i=0;i<nsv->index_entries;i++) {
                if(nsv->nsvs_timestamps) {
                    av_add_index_entry(st, nsv->nsvs_file_offset[i], nsv->nsvs_timestamps[i],
                                       0, 0, AVINDEX_KEYFRAME);
                } else {
                    int64_t ts = av_rescale(i*nsv->duration/nsv->index_entries, framerate.num, 1000*framerate.den);
                    av_add_index_entry(st, nsv->nsvs_file_offset[i], ts, 0, 0, AVINDEX_KEYFRAME);
                }
            }
481 482 483
        }
        if (atag != T_NONE) {
#ifndef DISABLE_AUDIO
484
            st = avformat_new_stream(s, NULL);
485 486 487
            if (!st)
                goto fail;

488
            st->id = NSV_ST_AUDIO;
489 490 491 492
            nst = av_mallocz(sizeof(NSVStream));
            if (!nst)
                goto fail;
            st->priv_data = nst;
493
            st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
494
            st->codec->codec_tag = atag;
495
            st->codec->codec_id = ff_codec_get_id(nsv_codec_audio_tags, atag);
496

497
            st->need_parsing = AVSTREAM_PARSE_FULL; /* for PCM we will read a chunk later and put correct info */
498

499
            /* set timebase to common denominator of ms and framerate */
500
            avpriv_set_pts_info(st, 64, 1, framerate.num*1000);
501 502
            st->start_time = 0;
            st->duration = (int64_t)nsv->duration * framerate.num;
503 504 505 506 507
#endif
        }
#ifdef CHECK_SUBSEQUENT_NSVS
    } else {
        if (nsv->vtag != vtag || nsv->atag != atag || nsv->vwidth != vwidth || nsv->vheight != vwidth) {
508
            av_dlog(s, "NSV NSVs header values differ from the first one!!!\n");
509 510 511 512 513 514 515 516 517 518 519 520 521
            //return -1;
        }
#endif /* CHECK_SUBSEQUENT_NSVS */
    }

    nsv->state = NSV_HAS_READ_NSVS;
    return 0;
fail:
    /* XXX */
    nsv->state = NSV_UNSYNC;
    return -1;
}

522
static int nsv_read_header(AVFormatContext *s)
523 524
{
    NSVContext *nsv = s->priv_data;
525
    int i, err;
526

527 528
    av_dlog(s, "%s()\n", __FUNCTION__);
    av_dlog(s, "filename '%s'\n", s->filename);
529 530 531

    nsv->state = NSV_UNSYNC;
    nsv->ahead[0].data = nsv->ahead[1].data = NULL;
532

533 534 535
    for (i = 0; i < NSV_MAX_RESYNC_TRIES; i++) {
        if (nsv_resync(s) < 0)
            return -1;
Alex Converse's avatar
Alex Converse committed
536
        if (nsv->state == NSV_FOUND_NSVF) {
537
            err = nsv_parse_NSVf_header(s);
Alex Converse's avatar
Alex Converse committed
538 539 540
            if (err < 0)
                return err;
        }
541 542
            /* we need the first NSVs also... */
        if (nsv->state == NSV_FOUND_NSVS) {
543
            err = nsv_parse_NSVs_header(s);
Alex Converse's avatar
Alex Converse committed
544 545
            if (err < 0)
                return err;
546 547 548 549 550 551 552
            break; /* we just want the first one */
        }
    }
    if (s->nb_streams < 1) /* no luck so far */
        return -1;
    /* now read the first chunk, so we can attempt to decode more info */
    err = nsv_read_chunk(s, 1);
553

554
    av_dlog(s, "parsed header\n");
555
    return err;
556 557 558 559 560
}

static int nsv_read_chunk(AVFormatContext *s, int fill_header)
{
    NSVContext *nsv = s->priv_data;
561
    AVIOContext *pb = s->pb;
562 563 564 565
    AVStream *st[2] = {NULL, NULL};
    NSVStream *nst;
    AVPacket *pkt;
    int i, err = 0;
Mike Melanson's avatar
Mike Melanson committed
566 567 568 569
    uint8_t auxcount; /* number of aux metadata, also 4 bits of vsize */
    uint32_t vsize;
    uint16_t asize;
    uint16_t auxsize;
570

571
    av_dlog(s, "%s(%d)\n", __FUNCTION__, fill_header);
572

573 574 575 576
    if (nsv->ahead[0].data || nsv->ahead[1].data)
        return 0; //-1; /* hey! eat what you've in your plate first! */

null_chunk_retry:
Anton Khirnov's avatar
Anton Khirnov committed
577
    if (pb->eof_reached)
578
        return -1;
579

580 581 582 583 584
    for (i = 0; i < NSV_MAX_RESYNC_TRIES && nsv->state < NSV_FOUND_NSVS && !err; i++)
        err = nsv_resync(s);
    if (err < 0)
        return err;
    if (nsv->state == NSV_FOUND_NSVS)
585
        err = nsv_parse_NSVs_header(s);
586 587 588 589
    if (err < 0)
        return err;
    if (nsv->state != NSV_HAS_READ_NSVS && nsv->state != NSV_FOUND_BEEF)
        return -1;
590

591 592 593
    auxcount = avio_r8(pb);
    vsize = avio_rl16(pb);
    asize = avio_rl16(pb);
594 595
    vsize = (vsize << 4) | (auxcount >> 4);
    auxcount &= 0x0f;
596
    av_dlog(s, "NSV CHUNK %d aux, %u bytes video, %d bytes audio\n", auxcount, vsize, asize);
597 598
    /* skip aux stuff */
    for (i = 0; i < auxcount; i++) {
599
        uint32_t av_unused auxtag;
600 601
        auxsize = avio_rl16(pb);
        auxtag = avio_rl32(pb);
602
        av_dlog(s, "NSV aux data: '%c%c%c%c', %d bytes\n",
603 604
              (auxtag & 0x0ff),
              ((auxtag >> 8) & 0x0ff),
605 606
              ((auxtag >> 16) & 0x0ff),
              ((auxtag >> 24) & 0x0ff),
607
              auxsize);
608
        avio_skip(pb, auxsize);
Mike Melanson's avatar
Mike Melanson committed
609
        vsize -= auxsize + sizeof(uint16_t) + sizeof(uint32_t); /* that's becoming braindead */
610
    }
611

Anton Khirnov's avatar
Anton Khirnov committed
612
    if (pb->eof_reached)
613 614 615 616 617
        return -1;
    if (!vsize && !asize) {
        nsv->state = NSV_UNSYNC;
        goto null_chunk_retry;
    }
618

619
    /* map back streams to v,a */
620
    if (s->nb_streams > 0)
621
        st[s->streams[0]->id] = s->streams[0];
622
    if (s->nb_streams > 1)
623
        st[s->streams[1]->id] = s->streams[1];
624

625
    if (vsize && st[NSV_ST_VIDEO]) {
626 627
        nst = st[NSV_ST_VIDEO]->priv_data;
        pkt = &nsv->ahead[NSV_ST_VIDEO];
Michael Niedermayer's avatar
Michael Niedermayer committed
628
        av_get_packet(pb, pkt, vsize);
629
        pkt->stream_index = st[NSV_ST_VIDEO]->index;//NSV_ST_VIDEO;
630
        pkt->dts = nst->frame_offset;
631
        pkt->flags |= nsv->state == NSV_HAS_READ_NSVS ? AV_PKT_FLAG_KEY : 0; /* keyframe only likely on a sync frame */
632 633
        for (i = 0; i < FFMIN(8, vsize); i++)
            av_dlog(s, "NSV video: [%d] = %02x\n", i, pkt->data[i]);
634
    }
635
    if(st[NSV_ST_VIDEO])
Michael Niedermayer's avatar
Michael Niedermayer committed
636
        ((NSVStream*)st[NSV_ST_VIDEO]->priv_data)->frame_offset++;
637

638
    if (asize && st[NSV_ST_AUDIO]) {
639 640 641 642
        nst = st[NSV_ST_AUDIO]->priv_data;
        pkt = &nsv->ahead[NSV_ST_AUDIO];
        /* read raw audio specific header on the first audio chunk... */
        /* on ALL audio chunks ?? seems so! */
643
        if (asize && st[NSV_ST_AUDIO]->codec->codec_tag == MKTAG('P', 'C', 'M', ' ')/* && fill_header*/) {
644 645 646
            uint8_t bps;
            uint8_t channels;
            uint16_t samplerate;
647 648 649
            bps = avio_r8(pb);
            channels = avio_r8(pb);
            samplerate = avio_rl16(pb);
650
            asize-=4;
651
            av_dlog(s, "NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate);
652
            if (fill_header) {
653
                st[NSV_ST_AUDIO]->need_parsing = AVSTREAM_PARSE_NONE; /* we know everything */
654
                if (bps != 16) {
655
                    av_dlog(s, "NSV AUDIO bit/sample != 16 (%d)!!!\n", bps);
656 657 658
                }
                bps /= channels; // ???
                if (bps == 8)
659
                    st[NSV_ST_AUDIO]->codec->codec_id = AV_CODEC_ID_PCM_U8;
660 661
                samplerate /= 4;/* UGH ??? XXX */
                channels = 1;
662 663
                st[NSV_ST_AUDIO]->codec->channels = channels;
                st[NSV_ST_AUDIO]->codec->sample_rate = samplerate;
664
                av_dlog(s, "NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate);
665 666
            }
        }
Michael Niedermayer's avatar
Michael Niedermayer committed
667
        av_get_packet(pb, pkt, asize);
668
        pkt->stream_index = st[NSV_ST_AUDIO]->index;//NSV_ST_AUDIO;
669
        pkt->flags |= nsv->state == NSV_HAS_READ_NSVS ? AV_PKT_FLAG_KEY : 0; /* keyframe only likely on a sync frame */
670 671 672
        if( nsv->state == NSV_HAS_READ_NSVS && st[NSV_ST_VIDEO] ) {
            /* on a nsvs frame we have new information on a/v sync */
            pkt->dts = (((NSVStream*)st[NSV_ST_VIDEO]->priv_data)->frame_offset-1);
673 674
            pkt->dts *= (int64_t)1000        * nsv->framerate.den;
            pkt->dts += (int64_t)nsv->avsync * nsv->framerate.num;
675
            av_dlog(s, "NSV AUDIO: sync:%d, dts:%"PRId64, nsv->avsync, pkt->dts);
676 677
        }
        nst->frame_offset++;
678
    }
679

680 681 682 683 684 685 686 687 688 689
    nsv->state = NSV_UNSYNC;
    return 0;
}


static int nsv_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    NSVContext *nsv = s->priv_data;
    int i, err = 0;

690
    av_dlog(s, "%s()\n", __FUNCTION__);
691

692 693 694 695 696
    /* in case we don't already have something to eat ... */
    if (nsv->ahead[0].data == NULL && nsv->ahead[1].data == NULL)
        err = nsv_read_chunk(s, 0);
    if (err < 0)
        return err;
697

698 699 700
    /* now pick one of the plates */
    for (i = 0; i < 2; i++) {
        if (nsv->ahead[i].data) {
701
            av_dlog(s, "%s: using cached packet[%d]\n", __FUNCTION__, i);
702 703 704 705 706 707
            /* avoid the cost of new_packet + memcpy(->data) */
            memcpy(pkt, &nsv->ahead[i], sizeof(AVPacket));
            nsv->ahead[i].data = NULL; /* we ate that one */
            return pkt->size;
        }
    }
708

709 710 711 712 713 714
    /* this restaurant is not approvisionned :^] */
    return -1;
}

static int nsv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
{
715 716 717 718
    NSVContext *nsv = s->priv_data;
    AVStream *st = s->streams[stream_index];
    NSVStream *nst = st->priv_data;
    int index;
719

720 721 722 723
    index = av_index_search_timestamp(st, timestamp, flags);
    if(index < 0)
        return -1;

724 725 726
    if (avio_seek(s->pb, st->index_entries[index].pos, SEEK_SET) < 0)
        return -1;

727 728 729
    nst->frame_offset = st->index_entries[index].timestamp;
    nsv->state = NSV_UNSYNC;
    return 0;
730 731 732 733
}

static int nsv_read_close(AVFormatContext *s)
{
734
/*     int i; */
735 736
    NSVContext *nsv = s->priv_data;

737 738
    av_freep(&nsv->nsvs_file_offset);
    av_freep(&nsv->nsvs_timestamps);
739 740 741 742
    if (nsv->ahead[0].data)
        av_free_packet(&nsv->ahead[0]);
    if (nsv->ahead[1].data)
        av_free_packet(&nsv->ahead[1]);
743 744 745 746 747 748 749 750 751 752

#if 0

    for(i=0;i<s->nb_streams;i++) {
        AVStream *st = s->streams[i];
        NSVStream *ast = st->priv_data;
        if(ast){
            av_free(ast->index_entries);
            av_free(ast);
        }
753
        av_free(st->codec->palctrl);
754 755 756 757 758 759 760 761 762
    }

#endif
    return 0;
}

static int nsv_probe(AVProbeData *p)
{
    int i;
763 764 765
    int score;
    int vsize, asize, auxcount;
    score = 0;
766
    av_dlog(NULL, "nsv_probe(), buf_size %d\n", p->buf_size);
767 768 769
    /* check file header */
    /* streamed files might not have any header */
    if (p->buf[0] == 'N' && p->buf[1] == 'S' &&
Michael Niedermayer's avatar
Michael Niedermayer committed
770
        p->buf[2] == 'V' && (p->buf[3] == 'f' || p->buf[3] == 's'))
771 772 773 774 775 776 777
        return AVPROBE_SCORE_MAX;
    /* XXX: do streamed files always start at chunk boundary ?? */
    /* or do we need to search NSVs in the byte stream ? */
    /* seems the servers don't bother starting clean chunks... */
    /* sometimes even the first header is at 9KB or something :^) */
    for (i = 1; i < p->buf_size - 3; i++) {
        if (p->buf[i+0] == 'N' && p->buf[i+1] == 'S' &&
778 779 780 781 782 783 784 785 786 787 788 789 790
            p->buf[i+2] == 'V' && p->buf[i+3] == 's') {
            score = AVPROBE_SCORE_MAX/5;
            /* Get the chunk size and check if at the end we are getting 0xBEEF */
            auxcount = p->buf[i+19];
            vsize = p->buf[i+20]  | p->buf[i+21] << 8;
            asize = p->buf[i+22]  | p->buf[i+23] << 8;
            vsize = (vsize << 4) | (auxcount >> 4);
            if ((asize + vsize + i + 23) <  p->buf_size - 2) {
                if (p->buf[i+23+asize+vsize+1] == 0xEF &&
                    p->buf[i+23+asize+vsize+2] == 0xBE)
                    return AVPROBE_SCORE_MAX-20;
            }
        }
791 792
    }
    /* so we'll have more luck on extension... */
793
    if (av_match_ext(p->filename, "nsv"))
794
        return AVPROBE_SCORE_MAX/2;
795
    /* FIXME: add mime-type check */
796
    return score;
797 798
}

799
AVInputFormat ff_nsv_demuxer = {
800 801 802 803 804 805 806 807
    .name           = "nsv",
    .long_name      = NULL_IF_CONFIG_SMALL("Nullsoft Streaming Video"),
    .priv_data_size = sizeof(NSVContext),
    .read_probe     = nsv_probe,
    .read_header    = nsv_read_header,
    .read_packet    = nsv_read_packet,
    .read_close     = nsv_read_close,
    .read_seek      = nsv_read_seek,
808
};