Mercurial > libavformat.hg
comparison nsvdec.c @ 594:89a09ede50ad libavformat
First implementation of nsv demuxer.
Get libavformat linked with lavc and lmp3lame is required. BeOS requires no undefined syms on link! (besides it's bad to leave undef syms)
author | mmu_man |
---|---|
date | Sat, 20 Nov 2004 23:10:07 +0000 |
parents | |
children | f0066a7fba8a |
comparison
equal
deleted
inserted
replaced
593:a1f354e84965 | 594:89a09ede50ad |
---|---|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 - revol@free.fr | |
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 uint64_t nsv_framerate_table[] = { | |
209 ((uint64_t)AV_TIME_BASE * 30), | |
210 ((uint64_t)AV_TIME_BASE * 30000 / 1001), /* 29.97 */ | |
211 ((uint64_t)AV_TIME_BASE * 25), | |
212 ((uint64_t)AV_TIME_BASE * 24000 / 1001), /* 23.98 */ | |
213 ((uint64_t)AV_TIME_BASE * 30), /* ?? */ | |
214 ((uint64_t)AV_TIME_BASE * 15000 / 1001), /* 14.98 */ | |
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 v = 0; | |
237 int i; | |
238 | |
239 PRINT(("%s(), offset = %Ld, 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 uint32_t tag, tag1, handler; | |
284 int codec_type, stream_index, frame_period, bit_rate, scale, rate; | |
285 unsigned int file_size, size, nb_frames; | |
286 int64_t duration; | |
287 int strings_size; | |
288 int table_entries; | |
289 int table_entries_used; | |
290 int i, n; | |
291 AVStream *st; | |
292 NSVStream *ast; | |
293 | |
294 PRINT(("%s()\n", __FUNCTION__)); | |
295 | |
296 nsv->state = NSV_UNSYNC; /* in case we fail */ | |
297 | |
298 size = get_le32(pb); | |
299 if (size < 28) | |
300 return -1; | |
301 nsv->NSVf_end = size; | |
302 | |
303 //s->file_size = (uint32_t)get_le32(pb); | |
304 file_size = (uint32_t)get_le32(pb); | |
305 PRINT(("NSV NSVf chunk_size %ld\n", size)); | |
306 PRINT(("NSV NSVf file_size %Ld\n", file_size)); | |
307 | |
308 duration = get_le32(pb); /* in ms */ | |
309 nsv->duration = duration * AV_TIME_BASE / 1000; /* convert */ | |
310 PRINT(("NSV NSVf duration %Ld ms\n", duration)); | |
311 // XXX: store it in AVStreams | |
312 | |
313 strings_size = get_le32(pb); | |
314 table_entries = get_le32(pb); | |
315 table_entries_used = get_le32(pb); | |
316 PRINT(("NSV NSVf info-strings size: %d, table entries: %d, bis %d\n", | |
317 strings_size, table_entries, table_entries_used)); | |
318 if (url_feof(pb)) | |
319 return -1; | |
320 | |
321 PRINT(("NSV got header; filepos %Ld\n", url_ftell(pb))); | |
322 | |
323 if (strings_size > 0) { | |
324 char *strings; /* last byte will be '\0' to play safe with str*() */ | |
325 char *p, *endp; | |
326 char *token, *value; | |
327 char quote; | |
328 | |
329 p = strings = av_mallocz(strings_size + 1); | |
330 endp = strings + strings_size; | |
331 get_buffer(pb, strings, strings_size); | |
332 while (p < endp) { | |
333 while (*p == ' ') | |
334 p++; /* strip out spaces */ | |
335 if (p >= endp-2) | |
336 break; | |
337 token = p; | |
338 p = strchr(p, '='); | |
339 if (!p || p >= endp-2) | |
340 break; | |
341 *p++ = '\0'; | |
342 quote = *p++; | |
343 value = p; | |
344 p = strchr(p, quote); | |
345 if (!p || p >= endp) | |
346 break; | |
347 *p++ = '\0'; | |
348 PRINT(("NSV NSVf INFO: %s='%s'\n", token, value)); | |
349 if (!strcmp(token, "ASPECT")) { | |
350 /* don't care */ | |
351 } else if (!strcmp(token, "CREATOR") || !strcmp(token, "Author")) { | |
352 strncpy(s->author, value, 512-1); | |
353 } else if (!strcmp(token, "Copyright")) { | |
354 strncpy(s->copyright, value, 512-1); | |
355 } else if (!strcmp(token, "TITLE") || !strcmp(token, "Title")) { | |
356 strncpy(s->title, value, 512-1); | |
357 } | |
358 } | |
359 av_free(strings); | |
360 } | |
361 if (url_feof(pb)) | |
362 return -1; | |
363 | |
364 PRINT(("NSV got infos; filepos %Ld\n", url_ftell(pb))); | |
365 | |
366 if (table_entries_used > 0) { | |
367 nsv->index_entries = table_entries_used; | |
368 nsv->nsvf_index_data = av_malloc(table_entries * sizeof(uint32_t)); | |
369 get_buffer(pb, nsv->nsvf_index_data, table_entries * sizeof(uint32_t)); | |
370 } | |
371 | |
372 PRINT(("NSV got index; filepos %Ld\n", url_ftell(pb))); | |
373 | |
374 #ifdef DEBUG_DUMP_INDEX | |
375 #define V(v) ((v<0x20 || v > 127)?'.':v) | |
376 /* dump index */ | |
377 PRINT(("NSV %d INDEX ENTRIES:\n", table_entries)); | |
378 PRINT(("NSV [dataoffset][fileoffset]\n", table_entries)); | |
379 for (i = 0; i < table_entries; i++) { | |
380 unsigned char b[8]; | |
381 url_fseek(pb, size + nsv->nsvf_index_data[i], SEEK_SET); | |
382 get_buffer(pb, b, 8); | |
383 PRINT(("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->nsvf_index_data[i], size + nsv->nsvf_index_data[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 //url_fseek(pb, size, SEEK_SET); /* go back to end of header */ | |
390 #undef V | |
391 #endif | |
392 | |
393 url_fseek(pb, nsv->base_offset + size, SEEK_SET); /* required for dumbdriving-271.nsv (2 extra bytes) */ | |
394 | |
395 if (url_feof(pb)) | |
396 return -1; | |
397 nsv->state = NSV_HAS_READ_NSVF; | |
398 return 0; | |
399 } | |
400 | |
401 static int nsv_parse_NSVs_header(AVFormatContext *s, AVFormatParameters *ap) | |
402 { | |
403 NSVContext *nsv = s->priv_data; | |
404 ByteIOContext *pb = &s->pb; | |
405 uint32_t vtag, atag; | |
406 uint16_t vwidth, vheight; | |
407 uint32_t framerate; | |
408 uint16_t unknown; | |
409 AVStream *st; | |
410 NSVStream *nst; | |
411 PRINT(("%s()\n", __FUNCTION__)); | |
412 | |
413 vtag = get_le32(pb); | |
414 atag = get_le32(pb); | |
415 vwidth = get_le16(pb); | |
416 vheight = get_le16(pb); | |
417 framerate = (uint8_t)get_byte(pb); | |
418 /* XXX how big must the table be ? */ | |
419 /* seems there is more to that... */ | |
420 PRINT(("NSV NSVs framerate code %2x\n", framerate)); | |
421 framerate = (framerate & 0x80)?(nsv_framerate_table[framerate & 0x7F]):(framerate*AV_TIME_BASE); | |
422 unknown = get_le16(pb); | |
423 #ifdef DEBUG | |
424 print_tag("NSV NSVs vtag", vtag, 0); | |
425 print_tag("NSV NSVs atag", atag, 0); | |
426 PRINT(("NSV NSVs vsize %dx%d\n", vwidth, vheight)); | |
427 PRINT(("NSV NSVs framerate %2x\n", framerate)); | |
428 #endif | |
429 | |
430 /* XXX change to ap != NULL ? */ | |
431 if (s->nb_streams == 0) { /* streams not yet published, let's do that */ | |
432 nsv->vtag = vtag; | |
433 nsv->atag = atag; | |
434 nsv->vwidth = vwidth; | |
435 nsv->vheight = vwidth; | |
436 if (vtag != T_NONE) { | |
437 st = av_new_stream(s, NSV_ST_VIDEO); | |
438 if (!st) | |
439 goto fail; | |
440 | |
441 nst = av_mallocz(sizeof(NSVStream)); | |
442 if (!nst) | |
443 goto fail; | |
444 st->priv_data = nst; | |
445 st->codec.codec_type = CODEC_TYPE_VIDEO; | |
446 st->codec.codec_tag = vtag; | |
447 st->codec.codec_id = codec_get_id(nsv_codec_video_tags, vtag); | |
448 st->codec.width = vwidth; | |
449 st->codec.height = vheight; | |
450 st->codec.bits_per_sample = 24; /* depth XXX */ | |
451 | |
452 st->codec.frame_rate = framerate; | |
453 st->codec.frame_rate_base = AV_TIME_BASE; | |
454 av_set_pts_info(st, 64, AV_TIME_BASE, framerate); | |
455 st->start_time = 0; | |
456 st->duration = nsv->duration; | |
457 } | |
458 if (atag != T_NONE) { | |
459 #ifndef DISABLE_AUDIO | |
460 st = av_new_stream(s, NSV_ST_AUDIO); | |
461 if (!st) | |
462 goto fail; | |
463 | |
464 nst = av_mallocz(sizeof(NSVStream)); | |
465 if (!nst) | |
466 goto fail; | |
467 st->priv_data = nst; | |
468 st->codec.codec_type = CODEC_TYPE_AUDIO; | |
469 st->codec.codec_tag = atag; | |
470 st->codec.codec_id = codec_get_id(nsv_codec_audio_tags, atag); | |
471 st->start_time = 0; | |
472 st->duration = nsv->duration; | |
473 | |
474 st->need_parsing = 1; /* for PCM we will read a chunk later and put correct info */ | |
475 /* XXX:FIXME */ | |
476 //st->codec.channels = 2; //XXX:channels; | |
477 //st->codec.sample_rate = 1000; | |
478 //av_set_pts_info(st, 64, 1, st->codec.sample_rate); | |
479 | |
480 #endif | |
481 } | |
482 #ifdef CHECK_SUBSEQUENT_NSVS | |
483 } else { | |
484 if (nsv->vtag != vtag || nsv->atag != atag || nsv->vwidth != vwidth || nsv->vheight != vwidth) { | |
485 PRINT(("NSV NSVs header values differ from the first one!!!\n")); | |
486 //return -1; | |
487 } | |
488 #endif /* CHECK_SUBSEQUENT_NSVS */ | |
489 } | |
490 | |
491 nsv->state = NSV_HAS_READ_NSVS; | |
492 return 0; | |
493 fail: | |
494 /* XXX */ | |
495 nsv->state = NSV_UNSYNC; | |
496 return -1; | |
497 } | |
498 | |
499 static int nsv_read_header(AVFormatContext *s, AVFormatParameters *ap) | |
500 { | |
501 NSVContext *nsv = s->priv_data; | |
502 ByteIOContext *pb = &s->pb; | |
503 uint32_t tag, tag1, handler; | |
504 int codec_type, stream_index, frame_period, bit_rate, scale, rate; | |
505 unsigned int size, nb_frames; | |
506 int table_entries; | |
507 int i, n, err; | |
508 AVStream *st; | |
509 NSVStream *ast; | |
510 | |
511 PRINT(("%s()\n", __FUNCTION__)); | |
512 PRINT(("filename '%s'\n", s->filename)); | |
513 | |
514 nsv->state = NSV_UNSYNC; | |
515 nsv->ahead[0].data = nsv->ahead[1].data = NULL; | |
516 | |
517 for (i = 0; i < NSV_MAX_RESYNC_TRIES; i++) { | |
518 if (nsv_resync(s) < 0) | |
519 return -1; | |
520 if (nsv->state == NSV_FOUND_NSVF) | |
521 err = nsv_parse_NSVf_header(s, ap); | |
522 /* we need the first NSVs also... */ | |
523 if (nsv->state == NSV_FOUND_NSVS) { | |
524 err = nsv_parse_NSVs_header(s, ap); | |
525 break; /* we just want the first one */ | |
526 } | |
527 } | |
528 if (s->nb_streams < 1) /* no luck so far */ | |
529 return -1; | |
530 /* now read the first chunk, so we can attempt to decode more info */ | |
531 err = nsv_read_chunk(s, 1); | |
532 | |
533 PRINT(("parsed header\n")); | |
534 return 0; | |
535 } | |
536 | |
537 static int nsv_read_chunk(AVFormatContext *s, int fill_header) | |
538 { | |
539 NSVContext *nsv = s->priv_data; | |
540 ByteIOContext *pb = &s->pb; | |
541 AVStream *st[2] = {NULL, NULL}; | |
542 NSVStream *nst; | |
543 AVPacket *pkt; | |
544 uint32 v = 0; | |
545 int i, err = 0; | |
546 uint8 auxcount; /* number of aux metadata, also 4 bits of vsize */ | |
547 uint32 vsize; | |
548 uint16 asize; | |
549 uint16 auxsize; | |
550 uint32 auxtag; | |
551 | |
552 PRINT(("%s(%d)\n", __FUNCTION__, fill_header)); | |
553 | |
554 if (nsv->ahead[0].data || nsv->ahead[1].data) | |
555 return 0; //-1; /* hey! eat what you've in your plate first! */ | |
556 | |
557 null_chunk_retry: | |
558 if (url_feof(pb)) | |
559 return -1; | |
560 | |
561 for (i = 0; i < NSV_MAX_RESYNC_TRIES && nsv->state < NSV_FOUND_NSVS && !err; i++) | |
562 err = nsv_resync(s); | |
563 if (err < 0) | |
564 return err; | |
565 if (nsv->state == NSV_FOUND_NSVS) | |
566 err = nsv_parse_NSVs_header(s, NULL); | |
567 if (err < 0) | |
568 return err; | |
569 if (nsv->state != NSV_HAS_READ_NSVS && nsv->state != NSV_FOUND_BEEF) | |
570 return -1; | |
571 | |
572 auxcount = get_byte(pb); | |
573 vsize = get_le16(pb); | |
574 asize = get_le16(pb); | |
575 vsize = (vsize << 4) | (auxcount >> 4); | |
576 auxcount &= 0x0f; | |
577 PRINT(("NSV CHUNK %d aux, %ld bytes video, %d bytes audio\n", auxcount, vsize, asize)); | |
578 /* skip aux stuff */ | |
579 for (i = 0; i < auxcount; i++) { | |
580 auxsize = get_le16(pb); | |
581 auxtag = get_le32(pb); | |
582 PRINT(("NSV aux data: '%c%c%c%c', %d bytes\n", | |
583 (auxtag & 0x0ff), | |
584 ((auxtag >> 8) & 0x0ff), | |
585 ((auxtag >> 16) & 0x0ff), | |
586 ((auxtag >> 24) & 0x0ff), | |
587 auxsize)); | |
588 url_fskip(pb, auxsize); | |
589 vsize -= auxsize + sizeof(uint16) + sizeof(uint32); /* that's becoming braindead */ | |
590 } | |
591 | |
592 if (url_feof(pb)) | |
593 return -1; | |
594 if (!vsize && !asize) { | |
595 nsv->state = NSV_UNSYNC; | |
596 goto null_chunk_retry; | |
597 } | |
598 | |
599 /* map back streams to v,a */ | |
600 if (s->streams[0]) | |
601 st[s->streams[0]->id] = s->streams[0]; | |
602 if (s->streams[1]) | |
603 st[s->streams[1]->id] = s->streams[1]; | |
604 | |
605 if (vsize/* && st[NSV_ST_VIDEO]*/) { | |
606 nst = st[NSV_ST_VIDEO]->priv_data; | |
607 pkt = &nsv->ahead[NSV_ST_VIDEO]; | |
608 av_new_packet(pkt, vsize); | |
609 get_buffer(pb, pkt->data, vsize); | |
610 pkt->stream_index = st[NSV_ST_VIDEO]->index;//NSV_ST_VIDEO; | |
611 pkt->dts = nst->frame_offset++; | |
612 pkt->flags |= PKT_FLAG_KEY; /* stupid format has no way to tell XXX: try the index */ | |
613 /* | |
614 for (i = 0; i < MIN(8, vsize); i++) | |
615 PRINT(("NSV video: [%d] = %02x\n", i, pkt->data[i])); | |
616 */ | |
617 } | |
618 if (asize/*st[NSV_ST_AUDIO]*/) { | |
619 nst = st[NSV_ST_AUDIO]->priv_data; | |
620 pkt = &nsv->ahead[NSV_ST_AUDIO]; | |
621 /* read raw audio specific header on the first audio chunk... */ | |
622 /* on ALL audio chunks ?? seems so! */ | |
623 if (asize && st[NSV_ST_AUDIO]->codec.codec_tag == MKTAG('P', 'C', 'M', ' ')/* && fill_header*/) { | |
624 uint8_t bps; | |
625 uint8_t channels; | |
626 uint16_t samplerate; | |
627 bps = get_byte(pb); | |
628 channels = get_byte(pb); | |
629 samplerate = get_le16(pb); | |
630 asize-=4; | |
631 PRINT(("NSV RAWAUDIO: bps %d, nchan %d, srate %ld\n", bps, channels, samplerate)); | |
632 if (fill_header) { | |
633 st[NSV_ST_AUDIO]->need_parsing = 0; /* we know everything */ | |
634 if (bps != 16) { | |
635 PRINT(("NSV AUDIO bit/sample != 16 (%d)!!!\n", bps)); | |
636 } | |
637 bps /= channels; // ??? | |
638 if (bps == 8) | |
639 st[NSV_ST_AUDIO]->codec.codec_id = CODEC_ID_PCM_U8; | |
640 samplerate /= 4;/* UGH ??? XXX */ | |
641 channels = 1; | |
642 st[NSV_ST_AUDIO]->codec.channels = channels; | |
643 st[NSV_ST_AUDIO]->codec.sample_rate = samplerate; | |
644 av_set_pts_info(st[NSV_ST_AUDIO], 64, 1, | |
645 st[NSV_ST_AUDIO]->codec.sample_rate); | |
646 PRINT(("NSV RAWAUDIO: bps %d, nchan %d, srate %ld\n", bps, channels, samplerate)); | |
647 } | |
648 } | |
649 av_new_packet(pkt, asize); | |
650 if (asize) | |
651 get_buffer(pb, pkt->data, asize); | |
652 pkt->stream_index = st[NSV_ST_AUDIO]->index;//NSV_ST_AUDIO; | |
653 //pkt->dts = nst->frame_offset; | |
654 //if (nst->sample_size) | |
655 // pkt->dts /= nst->sample_size; | |
656 nst->frame_offset += asize; // XXX: that's valid only for PCM !? | |
657 } | |
658 | |
659 //pkt->flags |= PKT_FLAG_KEY; | |
660 nsv->state = NSV_UNSYNC; | |
661 return 0; | |
662 } | |
663 | |
664 | |
665 static int nsv_read_packet(AVFormatContext *s, AVPacket *pkt) | |
666 { | |
667 NSVContext *nsv = s->priv_data; | |
668 ByteIOContext *pb = &s->pb; | |
669 int i, err = 0; | |
670 | |
671 PRINT(("%s()\n", __FUNCTION__)); | |
672 | |
673 /* in case we don't already have something to eat ... */ | |
674 if (nsv->ahead[0].data == NULL && nsv->ahead[1].data == NULL) | |
675 err = nsv_read_chunk(s, 0); | |
676 if (err < 0) | |
677 return err; | |
678 | |
679 /* now pick one of the plates */ | |
680 for (i = 0; i < 2; i++) { | |
681 if (nsv->ahead[i].data) { | |
682 PRINT(("%s: using cached packet[%d]\n", __FUNCTION__, i)); | |
683 /* avoid the cost of new_packet + memcpy(->data) */ | |
684 memcpy(pkt, &nsv->ahead[i], sizeof(AVPacket)); | |
685 nsv->ahead[i].data = NULL; /* we ate that one */ | |
686 return pkt->size; | |
687 } | |
688 } | |
689 | |
690 /* this restaurant is not approvisionned :^] */ | |
691 return -1; | |
692 } | |
693 | |
694 static int nsv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) | |
695 { | |
696 NSVContext *avi = s->priv_data; | |
697 AVStream *st; | |
698 NSVStream *ast; | |
699 int frame_number, i; | |
700 int64_t pos; | |
701 | |
702 return -1; | |
703 } | |
704 | |
705 static int nsv_read_close(AVFormatContext *s) | |
706 { | |
707 int i; | |
708 NSVContext *nsv = s->priv_data; | |
709 | |
710 if (nsv->index_entries) | |
711 av_free(nsv->nsvf_index_data); | |
712 | |
713 #if 0 | |
714 | |
715 for(i=0;i<s->nb_streams;i++) { | |
716 AVStream *st = s->streams[i]; | |
717 NSVStream *ast = st->priv_data; | |
718 if(ast){ | |
719 av_free(ast->index_entries); | |
720 av_free(ast); | |
721 } | |
722 av_free(st->codec.extradata); | |
723 av_free(st->codec.palctrl); | |
724 } | |
725 | |
726 #endif | |
727 return 0; | |
728 } | |
729 | |
730 static int nsv_probe(AVProbeData *p) | |
731 { | |
732 int i; | |
733 PRINT(("nsv_probe(), buf_size %d\n", p->buf_size)); | |
734 /* check file header */ | |
735 if (p->buf_size <= 32) | |
736 return 0; | |
737 if (p->buf[0] == 'N' && p->buf[1] == 'S' && | |
738 p->buf[2] == 'V' && p->buf[3] == 'f') | |
739 return AVPROBE_SCORE_MAX; | |
740 /* streamed files might not have any header */ | |
741 if (p->buf[0] == 'N' && p->buf[1] == 'S' && | |
742 p->buf[2] == 'V' && p->buf[3] == 's') | |
743 return AVPROBE_SCORE_MAX; | |
744 /* XXX: do streamed files always start at chunk boundary ?? */ | |
745 /* or do we need to search NSVs in the byte stream ? */ | |
746 /* seems the servers don't bother starting clean chunks... */ | |
747 /* sometimes even the first header is at 9KB or something :^) */ | |
748 for (i = 1; i < p->buf_size - 3; i++) { | |
749 if (p->buf[i+0] == 'N' && p->buf[i+1] == 'S' && | |
750 p->buf[i+2] == 'V' && p->buf[i+3] == 's') | |
751 return AVPROBE_SCORE_MAX-20; | |
752 } | |
753 /* so we'll have more luck on extension... */ | |
754 if (match_ext(p->filename, "nsv")) | |
755 return AVPROBE_SCORE_MAX-20; | |
756 /* FIXME: add mime-type check */ | |
757 return 0; | |
758 } | |
759 | |
760 static AVInputFormat nsv_iformat = { | |
761 "nsv", | |
762 "NullSoft Video format", | |
763 sizeof(NSVContext), | |
764 nsv_probe, | |
765 nsv_read_header, | |
766 nsv_read_packet, | |
767 nsv_read_close, | |
768 nsv_read_seek, | |
769 }; | |
770 | |
771 int nsvdec_init(void) | |
772 { | |
773 av_register_input_format(&nsv_iformat); | |
774 return 0; | |
775 } |