Mercurial > libavformat.hg
annotate nsvdec.c @ 1779:de2cf54eb68f libavformat
mxf aes decryption support, patch by Reimar, simplified to only look for first crypto context, will be extended once we get files with multiple cryptocontext, and hope that they won't have broken container ul
author | bcoudurier |
---|---|
date | Sun, 11 Feb 2007 12:50:33 +0000 |
parents | 2a85c82b8538 |
children | 70b741fa63eb |
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; |
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 } | |
612 if (asize/*st[NSV_ST_AUDIO]*/) { | |
613 nst = st[NSV_ST_AUDIO]->priv_data; | |
614 pkt = &nsv->ahead[NSV_ST_AUDIO]; | |
615 /* read raw audio specific header on the first audio chunk... */ | |
616 /* 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
|
617 if (asize && st[NSV_ST_AUDIO]->codec->codec_tag == MKTAG('P', 'C', 'M', ' ')/* && fill_header*/) { |
594 | 618 uint8_t bps; |
619 uint8_t channels; | |
620 uint16_t samplerate; | |
621 bps = get_byte(pb); | |
622 channels = get_byte(pb); | |
623 samplerate = get_le16(pb); | |
624 asize-=4; | |
838 | 625 PRINT(("NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate)); |
594 | 626 if (fill_header) { |
627 st[NSV_ST_AUDIO]->need_parsing = 0; /* we know everything */ | |
628 if (bps != 16) { | |
629 PRINT(("NSV AUDIO bit/sample != 16 (%d)!!!\n", bps)); | |
630 } | |
631 bps /= channels; // ??? | |
632 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
|
633 st[NSV_ST_AUDIO]->codec->codec_id = CODEC_ID_PCM_U8; |
594 | 634 samplerate /= 4;/* UGH ??? XXX */ |
635 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
|
636 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
|
637 st[NSV_ST_AUDIO]->codec->sample_rate = samplerate; |
838 | 638 PRINT(("NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate)); |
594 | 639 } |
640 } | |
775 | 641 av_get_packet(pb, pkt, asize); |
594 | 642 pkt->stream_index = st[NSV_ST_AUDIO]->index;//NSV_ST_AUDIO; |
1429 | 643 pkt->flags |= nsv->state == NSV_HAS_READ_NSVS ? PKT_FLAG_KEY : 0; /* keyframe only likely on a sync frame */ |
644 if( nsv->state == NSV_HAS_READ_NSVS && st[NSV_ST_VIDEO] ) { | |
645 /* on a nsvs frame we have new information on a/v sync */ | |
646 pkt->dts = (((NSVStream*)st[NSV_ST_VIDEO]->priv_data)->frame_offset-1); | |
647 pkt->dts *= (int64_t)1000 * st[NSV_ST_VIDEO]->time_base.num; | |
648 pkt->dts += (int64_t)nsv->avsync * st[NSV_ST_VIDEO]->time_base.den; | |
649 PRINT(("NSV AUDIO: sync:%d, dts:%"PRId64, nsv->avsync, pkt->dts)); | |
650 } | |
651 nst->frame_offset++; | |
594 | 652 } |
885 | 653 |
594 | 654 nsv->state = NSV_UNSYNC; |
655 return 0; | |
656 } | |
657 | |
658 | |
659 static int nsv_read_packet(AVFormatContext *s, AVPacket *pkt) | |
660 { | |
661 NSVContext *nsv = s->priv_data; | |
662 int i, err = 0; | |
663 | |
664 PRINT(("%s()\n", __FUNCTION__)); | |
885 | 665 |
594 | 666 /* in case we don't already have something to eat ... */ |
667 if (nsv->ahead[0].data == NULL && nsv->ahead[1].data == NULL) | |
668 err = nsv_read_chunk(s, 0); | |
669 if (err < 0) | |
670 return err; | |
885 | 671 |
594 | 672 /* now pick one of the plates */ |
673 for (i = 0; i < 2; i++) { | |
674 if (nsv->ahead[i].data) { | |
887 | 675 PRINT(("%s: using cached packet[%d]\n", __FUNCTION__, i)); |
594 | 676 /* avoid the cost of new_packet + memcpy(->data) */ |
677 memcpy(pkt, &nsv->ahead[i], sizeof(AVPacket)); | |
678 nsv->ahead[i].data = NULL; /* we ate that one */ | |
679 return pkt->size; | |
680 } | |
681 } | |
885 | 682 |
594 | 683 /* this restaurant is not approvisionned :^] */ |
684 return -1; | |
685 } | |
686 | |
687 static int nsv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) | |
688 { | |
683
095009fc2f35
kill warnings patch by (Mns Rullgrd <mru inprovide com>)
michael
parents:
639
diff
changeset
|
689 #if 0 |
594 | 690 NSVContext *avi = s->priv_data; |
691 AVStream *st; | |
692 NSVStream *ast; | |
693 int frame_number, i; | |
694 int64_t pos; | |
683
095009fc2f35
kill warnings patch by (Mns Rullgrd <mru inprovide com>)
michael
parents:
639
diff
changeset
|
695 #endif |
594 | 696 |
697 return -1; | |
698 } | |
699 | |
700 static int nsv_read_close(AVFormatContext *s) | |
701 { | |
683
095009fc2f35
kill warnings patch by (Mns Rullgrd <mru inprovide com>)
michael
parents:
639
diff
changeset
|
702 /* int i; */ |
594 | 703 NSVContext *nsv = s->priv_data; |
704 | |
705 if (nsv->index_entries) | |
706 av_free(nsv->nsvf_index_data); | |
707 | |
708 #if 0 | |
709 | |
710 for(i=0;i<s->nb_streams;i++) { | |
711 AVStream *st = s->streams[i]; | |
712 NSVStream *ast = st->priv_data; | |
713 if(ast){ | |
714 av_free(ast->index_entries); | |
715 av_free(ast); | |
716 } | |
820
feca73904e67
changing AVCodecContext codec -> *codec in AVStream so additions to AVCodecContext dont randomize AVStream and break binary compatibility
michael
parents:
775
diff
changeset
|
717 av_free(st->codec->palctrl); |
594 | 718 } |
719 | |
720 #endif | |
721 return 0; | |
722 } | |
723 | |
724 static int nsv_probe(AVProbeData *p) | |
725 { | |
726 int i; | |
601 | 727 // PRINT(("nsv_probe(), buf_size %d\n", p->buf_size)); |
594 | 728 /* check file header */ |
729 if (p->buf_size <= 32) | |
730 return 0; | |
731 if (p->buf[0] == 'N' && p->buf[1] == 'S' && | |
732 p->buf[2] == 'V' && p->buf[3] == 'f') | |
733 return AVPROBE_SCORE_MAX; | |
734 /* streamed files might not have any header */ | |
735 if (p->buf[0] == 'N' && p->buf[1] == 'S' && | |
736 p->buf[2] == 'V' && p->buf[3] == 's') | |
737 return AVPROBE_SCORE_MAX; | |
738 /* XXX: do streamed files always start at chunk boundary ?? */ | |
739 /* or do we need to search NSVs in the byte stream ? */ | |
740 /* seems the servers don't bother starting clean chunks... */ | |
741 /* sometimes even the first header is at 9KB or something :^) */ | |
742 for (i = 1; i < p->buf_size - 3; i++) { | |
743 if (p->buf[i+0] == 'N' && p->buf[i+1] == 'S' && | |
744 p->buf[i+2] == 'V' && p->buf[i+3] == 's') | |
745 return AVPROBE_SCORE_MAX-20; | |
746 } | |
747 /* so we'll have more luck on extension... */ | |
748 if (match_ext(p->filename, "nsv")) | |
749 return AVPROBE_SCORE_MAX-20; | |
750 /* FIXME: add mime-type check */ | |
751 return 0; | |
752 } | |
753 | |
1169 | 754 AVInputFormat nsv_demuxer = { |
594 | 755 "nsv", |
756 "NullSoft Video format", | |
757 sizeof(NSVContext), | |
758 nsv_probe, | |
759 nsv_read_header, | |
760 nsv_read_packet, | |
761 nsv_read_close, | |
762 nsv_read_seek, | |
763 }; |