1 | /*
|
---|
2 | * NSV decoder.
|
---|
3 | * Copyright (c) 2004 The FFmpeg Project.
|
---|
4 | *
|
---|
5 | * This library is free software; you can redistribute it and/or
|
---|
6 | * modify it under the terms of the GNU Lesser General Public
|
---|
7 | * License as published by the Free Software Foundation; either
|
---|
8 | * version 2 of the License, or (at your option) any later version.
|
---|
9 | *
|
---|
10 | * This library is distributed in the hope that it will be useful,
|
---|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
13 | * Lesser General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU Lesser General Public
|
---|
16 | * License along with this library; if not, write to the Free Software
|
---|
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
---|
18 | */
|
---|
19 | #include "avformat.h"
|
---|
20 | #include "avi.h"
|
---|
21 |
|
---|
22 | #define DEBUG
|
---|
23 | //#define DEBUG_DUMP_INDEX // XXX dumbdriving-271.nsv breaks with it commented!!
|
---|
24 | //#define DEBUG_SEEK
|
---|
25 | #define CHECK_SUBSEQUENT_NSVS
|
---|
26 | //#define DISABLE_AUDIO
|
---|
27 |
|
---|
28 | /* max bytes to crawl for trying to resync
|
---|
29 | * stupid streaming servers don't start at chunk boundaries...
|
---|
30 | */
|
---|
31 | #define NSV_MAX_RESYNC (500*1024)
|
---|
32 | #define NSV_MAX_RESYNC_TRIES 300
|
---|
33 |
|
---|
34 | /*
|
---|
35 | * First version by Francois Revol - [email protected]
|
---|
36 | * References:
|
---|
37 | * (1) http://www.multimedia.cx/nsv-format.txt
|
---|
38 | * seems someone came to the same conclusions as me, and updated it:
|
---|
39 | * (2) http://www.stud.ktu.lt/~vitslav/nsv/nsv-format.txt
|
---|
40 | * http://www.stud.ktu.lt/~vitslav/nsv/
|
---|
41 | * Sample files:
|
---|
42 | * (S1) http://www.nullsoft.com/nsv/samples/
|
---|
43 | * http://www.nullsoft.com/nsv/samples/faster.nsv
|
---|
44 | * http://streamripper.sourceforge.net/openbb/read.php?TID=492&page=4
|
---|
45 | */
|
---|
46 |
|
---|
47 | /*
|
---|
48 | * notes on the header (Francois Revol):
|
---|
49 | *
|
---|
50 | * It is followed by strings, then a table, but nothing tells
|
---|
51 | * where the table begins according to (1). After checking faster.nsv,
|
---|
52 | * I believe NVSf[16-19] gives the size of the strings data
|
---|
53 | * (that is the offset of the data table after the header).
|
---|
54 | * After checking all samples from (S1) all confirms this.
|
---|
55 | *
|
---|
56 | * Then, about NSVf[12-15], faster.nsf has 179700. When veiwing it in VLC,
|
---|
57 | * I noticed there was about 1 NVSs chunk/s, so I ran
|
---|
58 | * strings faster.nsv | grep NSVs | wc -l
|
---|
59 | * which gave me 180. That leads me to think that NSVf[12-15] might be the
|
---|
60 | * file length in milliseconds.
|
---|
61 | * Let's try that:
|
---|
62 | * 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
|
---|
63 | * except for nstrailer (which doesn't have an NSVf header), it repports correct time.
|
---|
64 | *
|
---|
65 | * nsvtrailer.nsv (S1) does not have any NSVf header, only NSVs chunks,
|
---|
66 | * so the header seems to not be mandatory. (for streaming).
|
---|
67 | *
|
---|
68 | * index slice duration check (excepts nsvtrailer.nsv):
|
---|
69 | * for f in [^n]*.nsv; do DUR="$(ffmpeg -i "$f" 2>/dev/null | grep 'NSVf duration' | cut -d ' ' -f 4)"; IC="$(ffmpeg -i "$f" 2>/dev/null | grep 'INDEX ENTRIES' | cut -d ' ' -f 2)"; echo "duration $DUR, slite time $(($DUR/$IC))"; done
|
---|
70 | */
|
---|
71 |
|
---|
72 | /*
|
---|
73 | * TODO:
|
---|
74 | * - handle timestamps !!!
|
---|
75 | * - use index
|
---|
76 | * - mime-type in probe()
|
---|
77 | * - seek
|
---|
78 | */
|
---|
79 |
|
---|
80 | #ifdef DEBUG
|
---|
81 | #define PRINT(_v) printf _v
|
---|
82 | #else
|
---|
83 | #define PRINT(_v)
|
---|
84 | #endif
|
---|
85 |
|
---|
86 | #if 0
|
---|
87 | struct NSVf_header {
|
---|
88 | uint32_t chunk_tag; /* 'NSVf' */
|
---|
89 | uint32_t chunk_size;
|
---|
90 | uint32_t file_size; /* max 4GB ??? noone learns anything it seems :^) */
|
---|
91 | uint32_t file_length; //unknown1; /* what about MSB of file_size ? */
|
---|
92 | uint32_t info_strings_size; /* size of the info strings */ //unknown2;
|
---|
93 | uint32_t table_entries;
|
---|
94 | uint32_t table_entries_used; /* the left ones should be -1 */
|
---|
95 | };
|
---|
96 |
|
---|
97 | struct NSVs_header {
|
---|
98 | uint32_t chunk_tag; /* 'NSVs' */
|
---|
99 | uint32_t v4cc; /* or 'NONE' */
|
---|
100 | uint32_t a4cc; /* or 'NONE' */
|
---|
101 | uint16_t vwidth; /* assert(vwidth%16==0) */
|
---|
102 | uint16_t vheight; /* assert(vheight%16==0) */
|
---|
103 | uint8_t framerate; /* value = (framerate&0x80)?frtable[frameratex0x7f]:framerate */
|
---|
104 | uint16_t unknown;
|
---|
105 | };
|
---|
106 |
|
---|
107 | struct nsv_avchunk_header {
|
---|
108 | uint8_t vchunk_size_lsb;
|
---|
109 | uint16_t vchunk_size_msb; /* value = (vchunk_size_msb << 4) | (vchunk_size_lsb >> 4) */
|
---|
110 | uint16_t achunk_size;
|
---|
111 | };
|
---|
112 |
|
---|
113 | struct nsv_pcm_header {
|
---|
114 | uint8_t bits_per_sample;
|
---|
115 | uint8_t channel_count;
|
---|
116 | uint16_t sample_rate;
|
---|
117 | };
|
---|
118 | #endif
|
---|
119 |
|
---|
120 | /* variation from avi.h */
|
---|
121 | /*typedef struct CodecTag {
|
---|
122 | int id;
|
---|
123 | unsigned int tag;
|
---|
124 | } CodecTag;*/
|
---|
125 |
|
---|
126 | /* tags */
|
---|
127 |
|
---|
128 | #define T_NSVF MKTAG('N', 'S', 'V', 'f') /* file header */
|
---|
129 | #define T_NSVS MKTAG('N', 'S', 'V', 's') /* chunk header */
|
---|
130 | #define T_TOC2 MKTAG('T', 'O', 'C', '2') /* extra index marker */
|
---|
131 | #define T_NONE MKTAG('N', 'O', 'N', 'E') /* null a/v 4CC */
|
---|
132 | #define T_SUBT MKTAG('S', 'U', 'B', 'T') /* subtitle aux data */
|
---|
133 | #define T_ASYN MKTAG('A', 'S', 'Y', 'N') /* async a/v aux marker */
|
---|
134 | #define T_KEYF MKTAG('K', 'E', 'Y', 'F') /* video keyframe aux marker (addition) */
|
---|
135 |
|
---|
136 | #define TB_NSVF MKBETAG('N', 'S', 'V', 'f')
|
---|
137 | #define TB_NSVS MKBETAG('N', 'S', 'V', 's')
|
---|
138 |
|
---|
139 | /* hardcoded stream indices */
|
---|
140 | #define NSV_ST_VIDEO 0
|
---|
141 | #define NSV_ST_AUDIO 1
|
---|
142 | #define NSV_ST_SUBT 2
|
---|
143 |
|
---|
144 | enum NSVStatus {
|
---|
145 | NSV_UNSYNC,
|
---|
146 | NSV_FOUND_NSVF,
|
---|
147 | NSV_HAS_READ_NSVF,
|
---|
148 | NSV_FOUND_NSVS,
|
---|
149 | NSV_HAS_READ_NSVS,
|
---|
150 | NSV_FOUND_BEEF,
|
---|
151 | NSV_GOT_VIDEO,
|
---|
152 | NSV_GOT_AUDIO,
|
---|
153 | };
|
---|
154 |
|
---|
155 | typedef struct NSVStream {
|
---|
156 | int frame_offset; /* current frame (video) or byte (audio) counter
|
---|
157 | (used to compute the pts) */
|
---|
158 | int scale;
|
---|
159 | int rate;
|
---|
160 | int sample_size; /* audio only data */
|
---|
161 | int start;
|
---|
162 |
|
---|
163 | int new_frame_offset; /* temporary storage (used during seek) */
|
---|
164 | int cum_len; /* temporary storage (used during seek) */
|
---|
165 | } NSVStream;
|
---|
166 |
|
---|
167 | typedef struct {
|
---|
168 | int base_offset;
|
---|
169 | int NSVf_end;
|
---|
170 | uint32_t *nsvf_index_data;
|
---|
171 | int index_entries;
|
---|
172 | enum NSVStatus state;
|
---|
173 | AVPacket ahead[2]; /* [v, a] if .data is !NULL there is something */
|
---|
174 | /* cached */
|
---|
175 | int64_t duration;
|
---|
176 | uint32_t vtag, atag;
|
---|
177 | uint16_t vwidth, vheight;
|
---|
178 | //DVDemuxContext* dv_demux;
|
---|
179 | } NSVContext;
|
---|
180 |
|
---|
181 | static const CodecTag nsv_codec_video_tags[] = {
|
---|
182 | { CODEC_ID_VP3, MKTAG('V', 'P', '3', ' ') },
|
---|
183 | { CODEC_ID_VP3, MKTAG('V', 'P', '3', '0') },
|
---|
184 | { CODEC_ID_VP3, MKTAG('V', 'P', '3', '1') },
|
---|
185 | /*
|
---|
186 | { CODEC_ID_VP4, MKTAG('V', 'P', '4', ' ') },
|
---|
187 | { CODEC_ID_VP4, MKTAG('V', 'P', '4', '0') },
|
---|
188 | { CODEC_ID_VP5, MKTAG('V', 'P', '5', ' ') },
|
---|
189 | { CODEC_ID_VP5, MKTAG('V', 'P', '5', '0') },
|
---|
190 | { CODEC_ID_VP6, MKTAG('V', 'P', '6', ' ') },
|
---|
191 | { CODEC_ID_VP6, MKTAG('V', 'P', '6', '0') },
|
---|
192 | { CODEC_ID_VP6, MKTAG('V', 'P', '6', '1') },
|
---|
193 | { CODEC_ID_VP6, MKTAG('V', 'P', '6', '2') },
|
---|
194 | */
|
---|
195 | { CODEC_ID_XVID, MKTAG('X', 'V', 'I', 'D') }, /* cf sample xvid decoder from nsv_codec_sdk.zip */
|
---|
196 | { CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', '3') },
|
---|
197 | { 0, 0 },
|
---|
198 | };
|
---|
199 |
|
---|
200 | static const CodecTag nsv_codec_audio_tags[] = {
|
---|
201 | { CODEC_ID_MP3, MKTAG('M', 'P', '3', ' ') },
|
---|
202 | { CODEC_ID_AAC, MKTAG('A', 'A', 'C', ' ') },
|
---|
203 | { CODEC_ID_AAC, MKTAG('A', 'A', 'C', 'P') }, /* _CUTTED__MUXED_2 Heads - Out Of The City.nsv */
|
---|
204 | { CODEC_ID_PCM_U16LE, MKTAG('P', 'C', 'M', ' ') },
|
---|
205 | { 0, 0 },
|
---|
206 | };
|
---|
207 |
|
---|
208 | static const AVRational nsv_framerate_table[] = {
|
---|
209 | {30,1},
|
---|
210 | {30000,1001},
|
---|
211 | {25,1},
|
---|
212 | {24000,1001},
|
---|
213 | {30,1},
|
---|
214 | {15000,1001},
|
---|
215 | };
|
---|
216 |
|
---|
217 | //static int nsv_load_index(AVFormatContext *s);
|
---|
218 | static int nsv_read_chunk(AVFormatContext *s, int fill_header);
|
---|
219 |
|
---|
220 | #ifdef DEBUG
|
---|
221 | static void print_tag(const char *str, unsigned int tag, int size)
|
---|
222 | {
|
---|
223 | printf("%s: tag=%c%c%c%c\n",
|
---|
224 | str, tag & 0xff,
|
---|
225 | (tag >> 8) & 0xff,
|
---|
226 | (tag >> 16) & 0xff,
|
---|
227 | (tag >> 24) & 0xff);
|
---|
228 | }
|
---|
229 | #endif
|
---|
230 |
|
---|
231 | /* try to find something we recognize, and set the state accordingly */
|
---|
232 | static int nsv_resync(AVFormatContext *s)
|
---|
233 | {
|
---|
234 | NSVContext *nsv = s->priv_data;
|
---|
235 | ByteIOContext *pb = &s->pb;
|
---|
236 | uint32_t v = 0;
|
---|
237 | int i;
|
---|
238 |
|
---|
239 | PRINT(("%s(), offset = %"PRId64", state = %d\n", __FUNCTION__, url_ftell(pb), nsv->state));
|
---|
240 |
|
---|
241 | //nsv->state = NSV_UNSYNC;
|
---|
242 |
|
---|
243 | for (i = 0; i < NSV_MAX_RESYNC; i++) {
|
---|
244 | if (url_feof(pb)) {
|
---|
245 | PRINT(("NSV EOF\n"));
|
---|
246 | nsv->state = NSV_UNSYNC;
|
---|
247 | return -1;
|
---|
248 | }
|
---|
249 | v <<= 8;
|
---|
250 | v |= get_byte(pb);
|
---|
251 | /*
|
---|
252 | if (i < 8) {
|
---|
253 | PRINT(("NSV resync: [%d] = %02x\n", i, v & 0x0FF));
|
---|
254 | }
|
---|
255 | */
|
---|
256 |
|
---|
257 | if ((v & 0x0000ffff) == 0xefbe) { /* BEEF */
|
---|
258 | PRINT(("NSV resynced on BEEF after %d bytes\n", i+1));
|
---|
259 | nsv->state = NSV_FOUND_BEEF;
|
---|
260 | return 0;
|
---|
261 | }
|
---|
262 | /* we read as big endian, thus the MK*BE* */
|
---|
263 | if (v == TB_NSVF) { /* NSVf */
|
---|
264 | PRINT(("NSV resynced on NSVf after %d bytes\n", i+1));
|
---|
265 | nsv->state = NSV_FOUND_NSVF;
|
---|
266 | return 0;
|
---|
267 | }
|
---|
268 | if (v == MKBETAG('N', 'S', 'V', 's')) { /* NSVs */
|
---|
269 | PRINT(("NSV resynced on NSVs after %d bytes\n", i+1));
|
---|
270 | nsv->state = NSV_FOUND_NSVS;
|
---|
271 | return 0;
|
---|
272 | }
|
---|
273 |
|
---|
274 | }
|
---|
275 | PRINT(("NSV sync lost\n"));
|
---|
276 | return -1;
|
---|
277 | }
|
---|
278 |
|
---|
279 | static int nsv_parse_NSVf_header(AVFormatContext *s, AVFormatParameters *ap)
|
---|
280 | {
|
---|
281 | NSVContext *nsv = s->priv_data;
|
---|
282 | ByteIOContext *pb = &s->pb;
|
---|
283 | unsigned int file_size, size;
|
---|
284 | int64_t duration;
|
---|
285 | int strings_size;
|
---|
286 | int table_entries;
|
---|
287 | int table_entries_used;
|
---|
288 |
|
---|
289 | PRINT(("%s()\n", __FUNCTION__));
|
---|
290 |
|
---|
291 | nsv->state = NSV_UNSYNC; /* in case we fail */
|
---|
292 |
|
---|
293 | size = get_le32(pb);
|
---|
294 | if (size < 28)
|
---|
295 | return -1;
|
---|
296 | nsv->NSVf_end = size;
|
---|
297 |
|
---|
298 | //s->file_size = (uint32_t)get_le32(pb);
|
---|
299 | file_size = (uint32_t)get_le32(pb);
|
---|
300 | PRINT(("NSV NSVf chunk_size %u\n", size));
|
---|
301 | PRINT(("NSV NSVf file_size %u\n", file_size));
|
---|
302 |
|
---|
303 | nsv->duration = duration = get_le32(pb); /* in ms */
|
---|
304 | PRINT(("NSV NSVf duration %"PRId64" ms\n", duration));
|
---|
305 | // XXX: store it in AVStreams
|
---|
306 |
|
---|
307 | strings_size = get_le32(pb);
|
---|
308 | table_entries = get_le32(pb);
|
---|
309 | table_entries_used = get_le32(pb);
|
---|
310 | PRINT(("NSV NSVf info-strings size: %d, table entries: %d, bis %d\n",
|
---|
311 | strings_size, table_entries, table_entries_used));
|
---|
312 | if (url_feof(pb))
|
---|
313 | return -1;
|
---|
314 |
|
---|
315 | PRINT(("NSV got header; filepos %"PRId64"\n", url_ftell(pb)));
|
---|
316 |
|
---|
317 | if (strings_size > 0) {
|
---|
318 | char *strings; /* last byte will be '\0' to play safe with str*() */
|
---|
319 | char *p, *endp;
|
---|
320 | char *token, *value;
|
---|
321 | char quote;
|
---|
322 |
|
---|
323 | p = strings = av_mallocz(strings_size + 1);
|
---|
324 | endp = strings + strings_size;
|
---|
325 | get_buffer(pb, strings, strings_size);
|
---|
326 | while (p < endp) {
|
---|
327 | while (*p == ' ')
|
---|
328 | p++; /* strip out spaces */
|
---|
329 | if (p >= endp-2)
|
---|
330 | break;
|
---|
331 | token = p;
|
---|
332 | p = strchr(p, '=');
|
---|
333 | if (!p || p >= endp-2)
|
---|
334 | break;
|
---|
335 | *p++ = '\0';
|
---|
336 | quote = *p++;
|
---|
337 | value = p;
|
---|
338 | p = strchr(p, quote);
|
---|
339 | if (!p || p >= endp)
|
---|
340 | break;
|
---|
341 | *p++ = '\0';
|
---|
342 | PRINT(("NSV NSVf INFO: %s='%s'\n", token, value));
|
---|
343 | if (!strcmp(token, "ASPECT")) {
|
---|
344 | /* don't care */
|
---|
345 | } else if (!strcmp(token, "CREATOR") || !strcmp(token, "Author")) {
|
---|
346 | strncpy(s->author, value, 512-1);
|
---|
347 | } else if (!strcmp(token, "Copyright")) {
|
---|
348 | strncpy(s->copyright, value, 512-1);
|
---|
349 | } else if (!strcmp(token, "TITLE") || !strcmp(token, "Title")) {
|
---|
350 | strncpy(s->title, value, 512-1);
|
---|
351 | }
|
---|
352 | }
|
---|
353 | av_free(strings);
|
---|
354 | }
|
---|
355 | if (url_feof(pb))
|
---|
356 | return -1;
|
---|
357 |
|
---|
358 | PRINT(("NSV got infos; filepos %"PRId64"\n", url_ftell(pb)));
|
---|
359 |
|
---|
360 | if (table_entries_used > 0) {
|
---|
361 | nsv->index_entries = table_entries_used;
|
---|
362 | if((unsigned)table_entries >= UINT_MAX / sizeof(uint32_t))
|
---|
363 | return -1;
|
---|
364 | nsv->nsvf_index_data = av_malloc(table_entries * sizeof(uint32_t));
|
---|
365 | #warning "FIXME: Byteswap buffer as needed"
|
---|
366 | get_buffer(pb, (unsigned char *)nsv->nsvf_index_data, table_entries * sizeof(uint32_t));
|
---|
367 | }
|
---|
368 |
|
---|
369 | PRINT(("NSV got index; filepos %"PRId64"\n", url_ftell(pb)));
|
---|
370 |
|
---|
371 | #ifdef DEBUG_DUMP_INDEX
|
---|
372 | #define V(v) ((v<0x20 || v > 127)?'.':v)
|
---|
373 | /* dump index */
|
---|
374 | PRINT(("NSV %d INDEX ENTRIES:\n", table_entries));
|
---|
375 | PRINT(("NSV [dataoffset][fileoffset]\n", table_entries));
|
---|
376 | for (i = 0; i < table_entries; i++) {
|
---|
377 | unsigned char b[8];
|
---|
378 | url_fseek(pb, size + nsv->nsvf_index_data[i], SEEK_SET);
|
---|
379 | get_buffer(pb, b, 8);
|
---|
380 | PRINT(("NSV [0x%08lx][0x%08lx]: %02x %02x %02x %02x %02x %02x %02x %02x"
|
---|
381 | "%c%c%c%c%c%c%c%c\n",
|
---|
382 | nsv->nsvf_index_data[i], size + nsv->nsvf_index_data[i],
|
---|
383 | b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
|
---|
384 | V(b[0]), V(b[1]), V(b[2]), V(b[3]), V(b[4]), V(b[5]), V(b[6]), V(b[7]) ));
|
---|
385 | }
|
---|
386 | //url_fseek(pb, size, SEEK_SET); /* go back to end of header */
|
---|
387 | #undef V
|
---|
388 | #endif
|
---|
389 |
|
---|
390 | url_fseek(pb, nsv->base_offset + size, SEEK_SET); /* required for dumbdriving-271.nsv (2 extra bytes) */
|
---|
391 |
|
---|
392 | if (url_feof(pb))
|
---|
393 | return -1;
|
---|
394 | nsv->state = NSV_HAS_READ_NSVF;
|
---|
395 | return 0;
|
---|
396 | }
|
---|
397 |
|
---|
398 | static int nsv_parse_NSVs_header(AVFormatContext *s, AVFormatParameters *ap)
|
---|
399 | {
|
---|
400 | NSVContext *nsv = s->priv_data;
|
---|
401 | ByteIOContext *pb = &s->pb;
|
---|
402 | uint32_t vtag, atag;
|
---|
403 | uint16_t vwidth, vheight;
|
---|
404 | AVRational framerate;
|
---|
405 | int i;
|
---|
406 | uint16_t unknown;
|
---|
407 | AVStream *st;
|
---|
408 | NSVStream *nst;
|
---|
409 | PRINT(("%s()\n", __FUNCTION__));
|
---|
410 |
|
---|
411 | vtag = get_le32(pb);
|
---|
412 | atag = get_le32(pb);
|
---|
413 | vwidth = get_le16(pb);
|
---|
414 | vheight = get_le16(pb);
|
---|
415 | i = get_byte(pb);
|
---|
416 | /* XXX how big must the table be ? */
|
---|
417 | /* seems there is more to that... */
|
---|
418 | PRINT(("NSV NSVs framerate code %2x\n", i));
|
---|
419 | if(i&0x80) framerate= nsv_framerate_table[i & 0x7F];
|
---|
420 | else framerate= (AVRational){i, 1};
|
---|
421 | unknown = get_le16(pb);
|
---|
422 | #ifdef DEBUG
|
---|
423 | print_tag("NSV NSVs vtag", vtag, 0);
|
---|
424 | print_tag("NSV NSVs atag", atag, 0);
|
---|
425 | PRINT(("NSV NSVs vsize %dx%d\n", vwidth, vheight));
|
---|
426 | #endif
|
---|
427 |
|
---|
428 | /* XXX change to ap != NULL ? */
|
---|
429 | if (s->nb_streams == 0) { /* streams not yet published, let's do that */
|
---|
430 | nsv->vtag = vtag;
|
---|
431 | nsv->atag = atag;
|
---|
432 | nsv->vwidth = vwidth;
|
---|
433 | nsv->vheight = vwidth;
|
---|
434 | if (vtag != T_NONE) {
|
---|
435 | st = av_new_stream(s, NSV_ST_VIDEO);
|
---|
436 | if (!st)
|
---|
437 | goto fail;
|
---|
438 |
|
---|
439 | nst = av_mallocz(sizeof(NSVStream));
|
---|
440 | if (!nst)
|
---|
441 | goto fail;
|
---|
442 | st->priv_data = nst;
|
---|
443 | st->codec->codec_type = CODEC_TYPE_VIDEO;
|
---|
444 | st->codec->codec_tag = vtag;
|
---|
445 | st->codec->codec_id = codec_get_id(nsv_codec_video_tags, vtag);
|
---|
446 | st->codec->width = vwidth;
|
---|
447 | st->codec->height = vheight;
|
---|
448 | st->codec->bits_per_sample = 24; /* depth XXX */
|
---|
449 |
|
---|
450 | av_set_pts_info(st, 64, framerate.den, framerate.num);
|
---|
451 | st->start_time = 0;
|
---|
452 | st->duration = av_rescale(nsv->duration, framerate.num, 1000*framerate.den);
|
---|
453 | }
|
---|
454 | if (atag != T_NONE) {
|
---|
455 | #ifndef DISABLE_AUDIO
|
---|
456 | st = av_new_stream(s, NSV_ST_AUDIO);
|
---|
457 | if (!st)
|
---|
458 | goto fail;
|
---|
459 |
|
---|
460 | nst = av_mallocz(sizeof(NSVStream));
|
---|
461 | if (!nst)
|
---|
462 | goto fail;
|
---|
463 | st->priv_data = nst;
|
---|
464 | st->codec->codec_type = CODEC_TYPE_AUDIO;
|
---|
465 | st->codec->codec_tag = atag;
|
---|
466 | st->codec->codec_id = codec_get_id(nsv_codec_audio_tags, atag);
|
---|
467 | st->start_time = 0;
|
---|
468 | // st->duration = nsv->duration; //FIXME
|
---|
469 |
|
---|
470 | st->need_parsing = 1; /* for PCM we will read a chunk later and put correct info */
|
---|
471 | /* XXX:FIXME */
|
---|
472 | //st->codec->channels = 2; //XXX:channels;
|
---|
473 | //st->codec->sample_rate = 1000;
|
---|
474 | //av_set_pts_info(st, 64, 1, st->codec->sample_rate);
|
---|
475 |
|
---|
476 | #endif
|
---|
477 | }
|
---|
478 | #ifdef CHECK_SUBSEQUENT_NSVS
|
---|
479 | } else {
|
---|
480 | if (nsv->vtag != vtag || nsv->atag != atag || nsv->vwidth != vwidth || nsv->vheight != vwidth) {
|
---|
481 | PRINT(("NSV NSVs header values differ from the first one!!!\n"));
|
---|
482 | //return -1;
|
---|
483 | }
|
---|
484 | #endif /* CHECK_SUBSEQUENT_NSVS */
|
---|
485 | }
|
---|
486 |
|
---|
487 | nsv->state = NSV_HAS_READ_NSVS;
|
---|
488 | return 0;
|
---|
489 | fail:
|
---|
490 | /* XXX */
|
---|
491 | nsv->state = NSV_UNSYNC;
|
---|
492 | return -1;
|
---|
493 | }
|
---|
494 |
|
---|
495 | static int nsv_read_header(AVFormatContext *s, AVFormatParameters *ap)
|
---|
496 | {
|
---|
497 | NSVContext *nsv = s->priv_data;
|
---|
498 | int i, err;
|
---|
499 |
|
---|
500 | PRINT(("%s()\n", __FUNCTION__));
|
---|
501 | PRINT(("filename '%s'\n", s->filename));
|
---|
502 |
|
---|
503 | nsv->state = NSV_UNSYNC;
|
---|
504 | nsv->ahead[0].data = nsv->ahead[1].data = NULL;
|
---|
505 |
|
---|
506 | for (i = 0; i < NSV_MAX_RESYNC_TRIES; i++) {
|
---|
507 | if (nsv_resync(s) < 0)
|
---|
508 | return -1;
|
---|
509 | if (nsv->state == NSV_FOUND_NSVF)
|
---|
510 | err = nsv_parse_NSVf_header(s, ap);
|
---|
511 | /* we need the first NSVs also... */
|
---|
512 | if (nsv->state == NSV_FOUND_NSVS) {
|
---|
513 | err = nsv_parse_NSVs_header(s, ap);
|
---|
514 | break; /* we just want the first one */
|
---|
515 | }
|
---|
516 | }
|
---|
517 | if (s->nb_streams < 1) /* no luck so far */
|
---|
518 | return -1;
|
---|
519 | /* now read the first chunk, so we can attempt to decode more info */
|
---|
520 | err = nsv_read_chunk(s, 1);
|
---|
521 |
|
---|
522 | PRINT(("parsed header\n"));
|
---|
523 | return 0;
|
---|
524 | }
|
---|
525 |
|
---|
526 | static int nsv_read_chunk(AVFormatContext *s, int fill_header)
|
---|
527 | {
|
---|
528 | NSVContext *nsv = s->priv_data;
|
---|
529 | ByteIOContext *pb = &s->pb;
|
---|
530 | AVStream *st[2] = {NULL, NULL};
|
---|
531 | NSVStream *nst;
|
---|
532 | AVPacket *pkt;
|
---|
533 | int i, err = 0;
|
---|
534 | uint8_t auxcount; /* number of aux metadata, also 4 bits of vsize */
|
---|
535 | uint32_t vsize;
|
---|
536 | uint16_t asize;
|
---|
537 | uint16_t auxsize;
|
---|
538 | uint32_t auxtag;
|
---|
539 |
|
---|
540 | PRINT(("%s(%d)\n", __FUNCTION__, fill_header));
|
---|
541 |
|
---|
542 | if (nsv->ahead[0].data || nsv->ahead[1].data)
|
---|
543 | return 0; //-1; /* hey! eat what you've in your plate first! */
|
---|
544 |
|
---|
545 | null_chunk_retry:
|
---|
546 | if (url_feof(pb))
|
---|
547 | return -1;
|
---|
548 |
|
---|
549 | for (i = 0; i < NSV_MAX_RESYNC_TRIES && nsv->state < NSV_FOUND_NSVS && !err; i++)
|
---|
550 | err = nsv_resync(s);
|
---|
551 | if (err < 0)
|
---|
552 | return err;
|
---|
553 | if (nsv->state == NSV_FOUND_NSVS)
|
---|
554 | err = nsv_parse_NSVs_header(s, NULL);
|
---|
555 | if (err < 0)
|
---|
556 | return err;
|
---|
557 | if (nsv->state != NSV_HAS_READ_NSVS && nsv->state != NSV_FOUND_BEEF)
|
---|
558 | return -1;
|
---|
559 |
|
---|
560 | auxcount = get_byte(pb);
|
---|
561 | vsize = get_le16(pb);
|
---|
562 | asize = get_le16(pb);
|
---|
563 | vsize = (vsize << 4) | (auxcount >> 4);
|
---|
564 | auxcount &= 0x0f;
|
---|
565 | PRINT(("NSV CHUNK %d aux, %u bytes video, %d bytes audio\n", auxcount, vsize, asize));
|
---|
566 | /* skip aux stuff */
|
---|
567 | for (i = 0; i < auxcount; i++) {
|
---|
568 | auxsize = get_le16(pb);
|
---|
569 | auxtag = get_le32(pb);
|
---|
570 | PRINT(("NSV aux data: '%c%c%c%c', %d bytes\n",
|
---|
571 | (auxtag & 0x0ff),
|
---|
572 | ((auxtag >> 8) & 0x0ff),
|
---|
573 | ((auxtag >> 16) & 0x0ff),
|
---|
574 | ((auxtag >> 24) & 0x0ff),
|
---|
575 | auxsize));
|
---|
576 | url_fskip(pb, auxsize);
|
---|
577 | vsize -= auxsize + sizeof(uint16_t) + sizeof(uint32_t); /* that's becoming braindead */
|
---|
578 | }
|
---|
579 |
|
---|
580 | if (url_feof(pb))
|
---|
581 | return -1;
|
---|
582 | if (!vsize && !asize) {
|
---|
583 | nsv->state = NSV_UNSYNC;
|
---|
584 | goto null_chunk_retry;
|
---|
585 | }
|
---|
586 |
|
---|
587 | /* map back streams to v,a */
|
---|
588 | if (s->streams[0])
|
---|
589 | st[s->streams[0]->id] = s->streams[0];
|
---|
590 | if (s->streams[1])
|
---|
591 | st[s->streams[1]->id] = s->streams[1];
|
---|
592 |
|
---|
593 | if (vsize/* && st[NSV_ST_VIDEO]*/) {
|
---|
594 | nst = st[NSV_ST_VIDEO]->priv_data;
|
---|
595 | pkt = &nsv->ahead[NSV_ST_VIDEO];
|
---|
596 | av_get_packet(pb, pkt, vsize);
|
---|
597 | pkt->stream_index = st[NSV_ST_VIDEO]->index;//NSV_ST_VIDEO;
|
---|
598 | pkt->dts = nst->frame_offset++;
|
---|
599 | pkt->flags |= PKT_FLAG_KEY; /* stupid format has no way to tell XXX: try the index */
|
---|
600 | /*
|
---|
601 | for (i = 0; i < MIN(8, vsize); i++)
|
---|
602 | PRINT(("NSV video: [%d] = %02x\n", i, pkt->data[i]));
|
---|
603 | */
|
---|
604 | }
|
---|
605 | if (asize/*st[NSV_ST_AUDIO]*/) {
|
---|
606 | nst = st[NSV_ST_AUDIO]->priv_data;
|
---|
607 | pkt = &nsv->ahead[NSV_ST_AUDIO];
|
---|
608 | /* read raw audio specific header on the first audio chunk... */
|
---|
609 | /* on ALL audio chunks ?? seems so! */
|
---|
610 | if (asize && st[NSV_ST_AUDIO]->codec->codec_tag == MKTAG('P', 'C', 'M', ' ')/* && fill_header*/) {
|
---|
611 | uint8_t bps;
|
---|
612 | uint8_t channels;
|
---|
613 | uint16_t samplerate;
|
---|
614 | bps = get_byte(pb);
|
---|
615 | channels = get_byte(pb);
|
---|
616 | samplerate = get_le16(pb);
|
---|
617 | asize-=4;
|
---|
618 | PRINT(("NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate));
|
---|
619 | if (fill_header) {
|
---|
620 | st[NSV_ST_AUDIO]->need_parsing = 0; /* we know everything */
|
---|
621 | if (bps != 16) {
|
---|
622 | PRINT(("NSV AUDIO bit/sample != 16 (%d)!!!\n", bps));
|
---|
623 | }
|
---|
624 | bps /= channels; // ???
|
---|
625 | if (bps == 8)
|
---|
626 | st[NSV_ST_AUDIO]->codec->codec_id = CODEC_ID_PCM_U8;
|
---|
627 | samplerate /= 4;/* UGH ??? XXX */
|
---|
628 | channels = 1;
|
---|
629 | st[NSV_ST_AUDIO]->codec->channels = channels;
|
---|
630 | st[NSV_ST_AUDIO]->codec->sample_rate = samplerate;
|
---|
631 | av_set_pts_info(st[NSV_ST_AUDIO], 64, 1,
|
---|
632 | st[NSV_ST_AUDIO]->codec->sample_rate);
|
---|
633 | PRINT(("NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate));
|
---|
634 | }
|
---|
635 | }
|
---|
636 | av_get_packet(pb, pkt, asize);
|
---|
637 | pkt->stream_index = st[NSV_ST_AUDIO]->index;//NSV_ST_AUDIO;
|
---|
638 | //pkt->dts = nst->frame_offset;
|
---|
639 | //if (nst->sample_size)
|
---|
640 | // pkt->dts /= nst->sample_size;
|
---|
641 | nst->frame_offset += asize; // XXX: that's valid only for PCM !?
|
---|
642 | }
|
---|
643 |
|
---|
644 | //pkt->flags |= PKT_FLAG_KEY;
|
---|
645 | nsv->state = NSV_UNSYNC;
|
---|
646 | return 0;
|
---|
647 | }
|
---|
648 |
|
---|
649 |
|
---|
650 | static int nsv_read_packet(AVFormatContext *s, AVPacket *pkt)
|
---|
651 | {
|
---|
652 | NSVContext *nsv = s->priv_data;
|
---|
653 | int i, err = 0;
|
---|
654 |
|
---|
655 | PRINT(("%s()\n", __FUNCTION__));
|
---|
656 |
|
---|
657 | /* in case we don't already have something to eat ... */
|
---|
658 | if (nsv->ahead[0].data == NULL && nsv->ahead[1].data == NULL)
|
---|
659 | err = nsv_read_chunk(s, 0);
|
---|
660 | if (err < 0)
|
---|
661 | return err;
|
---|
662 |
|
---|
663 | /* now pick one of the plates */
|
---|
664 | for (i = 0; i < 2; i++) {
|
---|
665 | if (nsv->ahead[i].data) {
|
---|
666 | PRINT(("%s: using cached packet[%d]\n", __FUNCTION__, i));
|
---|
667 | /* avoid the cost of new_packet + memcpy(->data) */
|
---|
668 | memcpy(pkt, &nsv->ahead[i], sizeof(AVPacket));
|
---|
669 | nsv->ahead[i].data = NULL; /* we ate that one */
|
---|
670 | return pkt->size;
|
---|
671 | }
|
---|
672 | }
|
---|
673 |
|
---|
674 | /* this restaurant is not approvisionned :^] */
|
---|
675 | return -1;
|
---|
676 | }
|
---|
677 |
|
---|
678 | static int nsv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
|
---|
679 | {
|
---|
680 | #if 0
|
---|
681 | NSVContext *avi = s->priv_data;
|
---|
682 | AVStream *st;
|
---|
683 | NSVStream *ast;
|
---|
684 | int frame_number, i;
|
---|
685 | int64_t pos;
|
---|
686 | #endif
|
---|
687 |
|
---|
688 | return -1;
|
---|
689 | }
|
---|
690 |
|
---|
691 | static int nsv_read_close(AVFormatContext *s)
|
---|
692 | {
|
---|
693 | /* int i; */
|
---|
694 | NSVContext *nsv = s->priv_data;
|
---|
695 |
|
---|
696 | if (nsv->index_entries)
|
---|
697 | av_free(nsv->nsvf_index_data);
|
---|
698 |
|
---|
699 | #if 0
|
---|
700 |
|
---|
701 | for(i=0;i<s->nb_streams;i++) {
|
---|
702 | AVStream *st = s->streams[i];
|
---|
703 | NSVStream *ast = st->priv_data;
|
---|
704 | if(ast){
|
---|
705 | av_free(ast->index_entries);
|
---|
706 | av_free(ast);
|
---|
707 | }
|
---|
708 | av_free(st->codec->palctrl);
|
---|
709 | }
|
---|
710 |
|
---|
711 | #endif
|
---|
712 | return 0;
|
---|
713 | }
|
---|
714 |
|
---|
715 | static int nsv_probe(AVProbeData *p)
|
---|
716 | {
|
---|
717 | int i;
|
---|
718 | // PRINT(("nsv_probe(), buf_size %d\n", p->buf_size));
|
---|
719 | /* check file header */
|
---|
720 | if (p->buf_size <= 32)
|
---|
721 | return 0;
|
---|
722 | if (p->buf[0] == 'N' && p->buf[1] == 'S' &&
|
---|
723 | p->buf[2] == 'V' && p->buf[3] == 'f')
|
---|
724 | return AVPROBE_SCORE_MAX;
|
---|
725 | /* streamed files might not have any header */
|
---|
726 | if (p->buf[0] == 'N' && p->buf[1] == 'S' &&
|
---|
727 | p->buf[2] == 'V' && p->buf[3] == 's')
|
---|
728 | return AVPROBE_SCORE_MAX;
|
---|
729 | /* XXX: do streamed files always start at chunk boundary ?? */
|
---|
730 | /* or do we need to search NSVs in the byte stream ? */
|
---|
731 | /* seems the servers don't bother starting clean chunks... */
|
---|
732 | /* sometimes even the first header is at 9KB or something :^) */
|
---|
733 | for (i = 1; i < p->buf_size - 3; i++) {
|
---|
734 | if (p->buf[i+0] == 'N' && p->buf[i+1] == 'S' &&
|
---|
735 | p->buf[i+2] == 'V' && p->buf[i+3] == 's')
|
---|
736 | return AVPROBE_SCORE_MAX-20;
|
---|
737 | }
|
---|
738 | /* so we'll have more luck on extension... */
|
---|
739 | if (match_ext(p->filename, "nsv"))
|
---|
740 | return AVPROBE_SCORE_MAX-20;
|
---|
741 | /* FIXME: add mime-type check */
|
---|
742 | return 0;
|
---|
743 | }
|
---|
744 |
|
---|
745 | static AVInputFormat nsv_demuxer = {
|
---|
746 | "nsv",
|
---|
747 | "NullSoft Video format",
|
---|
748 | sizeof(NSVContext),
|
---|
749 | nsv_probe,
|
---|
750 | nsv_read_header,
|
---|
751 | nsv_read_packet,
|
---|
752 | nsv_read_close,
|
---|
753 | nsv_read_seek,
|
---|
754 | };
|
---|
755 |
|
---|
756 | int nsvdec_init(void)
|
---|
757 | {
|
---|
758 | av_register_input_format(&nsv_demuxer);
|
---|
759 | return 0;
|
---|
760 | }
|
---|