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