Mercurial > libavformat.hg
annotate nsvdec.c @ 4273:ed5196e6e112 libavformat
Move av_read_frame/parser related buffer variables from AVFormatContext
to AVStream, this is required if we want to allow the user to pull frames
from specific streams at some point in the future.
author | michael |
---|---|
date | Thu, 22 Jan 2009 23:35:31 +0000 |
parents | 77e0c7511d41 |
children | 042e9a2fdda2 |
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 |
4251
77e0c7511d41
cosmetics: Remove pointless period after copyright statement non-sentences.
diego
parents:
3908
diff
changeset
|
3 * Copyright (c) 2004 The FFmpeg Project |
594 | 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; | |
2164 | 94 uint32_t file_size; /* max 4GB ??? no one learns anything it seems :^) */ |
594 | 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 | |
3365 | 143 /* hardcoded stream indexes */ |
594 | 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; |
3586
e421492a6e27
Try to fix FATE after my time_base simplification change.
michael
parents:
3424
diff
changeset
|
183 AVRational framerate; |
594 | 184 //DVDemuxContext* dv_demux; |
185 } NSVContext; | |
186 | |
1677
2a85c82b8538
add codec_id <-> codec_tag tables to AVIn/OutputFormat
michael
parents:
1593
diff
changeset
|
187 static const AVCodecTag nsv_codec_video_tags[] = { |
594 | 188 { CODEC_ID_VP3, MKTAG('V', 'P', '3', ' ') }, |
189 { CODEC_ID_VP3, MKTAG('V', 'P', '3', '0') }, | |
190 { CODEC_ID_VP3, MKTAG('V', 'P', '3', '1') }, | |
1305 | 191 { CODEC_ID_VP5, MKTAG('V', 'P', '5', ' ') }, |
192 { CODEC_ID_VP5, MKTAG('V', 'P', '5', '0') }, | |
1593 | 193 { CODEC_ID_VP6, MKTAG('V', 'P', '6', ' ') }, |
194 { CODEC_ID_VP6, MKTAG('V', 'P', '6', '0') }, | |
195 { CODEC_ID_VP6, MKTAG('V', 'P', '6', '1') }, | |
1305 | 196 { CODEC_ID_VP6, MKTAG('V', 'P', '6', '2') }, |
594 | 197 /* |
198 { CODEC_ID_VP4, MKTAG('V', 'P', '4', ' ') }, | |
199 { CODEC_ID_VP4, MKTAG('V', 'P', '4', '0') }, | |
200 */ | |
2164 | 201 { CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'D') }, /* cf sample xvid decoder from nsv_codec_sdk.zip */ |
594 | 202 { CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', '3') }, |
203 { 0, 0 }, | |
204 }; | |
205 | |
1677
2a85c82b8538
add codec_id <-> codec_tag tables to AVIn/OutputFormat
michael
parents:
1593
diff
changeset
|
206 static const AVCodecTag nsv_codec_audio_tags[] = { |
594 | 207 { CODEC_ID_MP3, MKTAG('M', 'P', '3', ' ') }, |
208 { CODEC_ID_AAC, MKTAG('A', 'A', 'C', ' ') }, | |
209 { CODEC_ID_AAC, MKTAG('A', 'A', 'C', 'P') }, /* _CUTTED__MUXED_2 Heads - Out Of The City.nsv */ | |
210 { CODEC_ID_PCM_U16LE, MKTAG('P', 'C', 'M', ' ') }, | |
211 { 0, 0 }, | |
212 }; | |
213 | |
214 //static int nsv_load_index(AVFormatContext *s); | |
215 static int nsv_read_chunk(AVFormatContext *s, int fill_header); | |
216 | |
217 #ifdef DEBUG | |
218 static void print_tag(const char *str, unsigned int tag, int size) | |
219 { | |
220 printf("%s: tag=%c%c%c%c\n", | |
221 str, tag & 0xff, | |
222 (tag >> 8) & 0xff, | |
223 (tag >> 16) & 0xff, | |
224 (tag >> 24) & 0xff); | |
225 } | |
226 #endif | |
227 | |
228 /* try to find something we recognize, and set the state accordingly */ | |
229 static int nsv_resync(AVFormatContext *s) | |
230 { | |
231 NSVContext *nsv = s->priv_data; | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2567
diff
changeset
|
232 ByteIOContext *pb = s->pb; |
595 | 233 uint32_t v = 0; |
594 | 234 int i; |
885 | 235 |
881 | 236 PRINT(("%s(), offset = %"PRId64", state = %d\n", __FUNCTION__, url_ftell(pb), nsv->state)); |
885 | 237 |
594 | 238 //nsv->state = NSV_UNSYNC; |
885 | 239 |
594 | 240 for (i = 0; i < NSV_MAX_RESYNC; i++) { |
241 if (url_feof(pb)) { | |
242 PRINT(("NSV EOF\n")); | |
243 nsv->state = NSV_UNSYNC; | |
244 return -1; | |
245 } | |
246 v <<= 8; | |
247 v |= get_byte(pb); | |
248 /* | |
249 if (i < 8) { | |
250 PRINT(("NSV resync: [%d] = %02x\n", i, v & 0x0FF)); | |
251 } | |
252 */ | |
885 | 253 |
594 | 254 if ((v & 0x0000ffff) == 0xefbe) { /* BEEF */ |
255 PRINT(("NSV resynced on BEEF after %d bytes\n", i+1)); | |
256 nsv->state = NSV_FOUND_BEEF; | |
257 return 0; | |
258 } | |
259 /* we read as big endian, thus the MK*BE* */ | |
260 if (v == TB_NSVF) { /* NSVf */ | |
261 PRINT(("NSV resynced on NSVf after %d bytes\n", i+1)); | |
262 nsv->state = NSV_FOUND_NSVF; | |
263 return 0; | |
264 } | |
265 if (v == MKBETAG('N', 'S', 'V', 's')) { /* NSVs */ | |
266 PRINT(("NSV resynced on NSVs after %d bytes\n", i+1)); | |
267 nsv->state = NSV_FOUND_NSVS; | |
268 return 0; | |
269 } | |
885 | 270 |
594 | 271 } |
272 PRINT(("NSV sync lost\n")); | |
273 return -1; | |
274 } | |
275 | |
276 static int nsv_parse_NSVf_header(AVFormatContext *s, AVFormatParameters *ap) | |
277 { | |
278 NSVContext *nsv = s->priv_data; | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2567
diff
changeset
|
279 ByteIOContext *pb = s->pb; |
683
095009fc2f35
kill warnings patch by (Mns Rullgrd <mru inprovide com>)
michael
parents:
639
diff
changeset
|
280 unsigned int file_size, size; |
594 | 281 int64_t duration; |
282 int strings_size; | |
283 int table_entries; | |
284 int table_entries_used; | |
285 | |
286 PRINT(("%s()\n", __FUNCTION__)); | |
287 | |
288 nsv->state = NSV_UNSYNC; /* in case we fail */ | |
885 | 289 |
594 | 290 size = get_le32(pb); |
291 if (size < 28) | |
292 return -1; | |
293 nsv->NSVf_end = size; | |
294 | |
295 //s->file_size = (uint32_t)get_le32(pb); | |
296 file_size = (uint32_t)get_le32(pb); | |
838 | 297 PRINT(("NSV NSVf chunk_size %u\n", size)); |
298 PRINT(("NSV NSVf file_size %u\n", file_size)); | |
594 | 299 |
743 | 300 nsv->duration = duration = get_le32(pb); /* in ms */ |
881 | 301 PRINT(("NSV NSVf duration %"PRId64" ms\n", duration)); |
594 | 302 // XXX: store it in AVStreams |
303 | |
304 strings_size = get_le32(pb); | |
305 table_entries = get_le32(pb); | |
306 table_entries_used = get_le32(pb); | |
885 | 307 PRINT(("NSV NSVf info-strings size: %d, table entries: %d, bis %d\n", |
594 | 308 strings_size, table_entries, table_entries_used)); |
309 if (url_feof(pb)) | |
310 return -1; | |
885 | 311 |
881 | 312 PRINT(("NSV got header; filepos %"PRId64"\n", url_ftell(pb))); |
594 | 313 |
314 if (strings_size > 0) { | |
315 char *strings; /* last byte will be '\0' to play safe with str*() */ | |
316 char *p, *endp; | |
317 char *token, *value; | |
318 char quote; | |
319 | |
320 p = strings = av_mallocz(strings_size + 1); | |
321 endp = strings + strings_size; | |
322 get_buffer(pb, strings, strings_size); | |
323 while (p < endp) { | |
324 while (*p == ' ') | |
325 p++; /* strip out spaces */ | |
326 if (p >= endp-2) | |
327 break; | |
328 token = p; | |
329 p = strchr(p, '='); | |
330 if (!p || p >= endp-2) | |
331 break; | |
332 *p++ = '\0'; | |
333 quote = *p++; | |
334 value = p; | |
335 p = strchr(p, quote); | |
336 if (!p || p >= endp) | |
337 break; | |
338 *p++ = '\0'; | |
339 PRINT(("NSV NSVf INFO: %s='%s'\n", token, value)); | |
340 if (!strcmp(token, "ASPECT")) { | |
341 /* don't care */ | |
342 } else if (!strcmp(token, "CREATOR") || !strcmp(token, "Author")) { | |
343 strncpy(s->author, value, 512-1); | |
344 } else if (!strcmp(token, "Copyright")) { | |
345 strncpy(s->copyright, value, 512-1); | |
346 } else if (!strcmp(token, "TITLE") || !strcmp(token, "Title")) { | |
347 strncpy(s->title, value, 512-1); | |
348 } | |
349 } | |
350 av_free(strings); | |
351 } | |
352 if (url_feof(pb)) | |
353 return -1; | |
885 | 354 |
881 | 355 PRINT(("NSV got infos; filepos %"PRId64"\n", url_ftell(pb))); |
594 | 356 |
357 if (table_entries_used > 0) { | |
358 nsv->index_entries = table_entries_used; | |
639 | 359 if((unsigned)table_entries >= UINT_MAX / sizeof(uint32_t)) |
360 return -1; | |
594 | 361 nsv->nsvf_index_data = av_malloc(table_entries * sizeof(uint32_t)); |
881 | 362 #warning "FIXME: Byteswap buffer as needed" |
363 get_buffer(pb, (unsigned char *)nsv->nsvf_index_data, table_entries * sizeof(uint32_t)); | |
594 | 364 } |
365 | |
881 | 366 PRINT(("NSV got index; filepos %"PRId64"\n", url_ftell(pb))); |
885 | 367 |
594 | 368 #ifdef DEBUG_DUMP_INDEX |
369 #define V(v) ((v<0x20 || v > 127)?'.':v) | |
370 /* dump index */ | |
371 PRINT(("NSV %d INDEX ENTRIES:\n", table_entries)); | |
372 PRINT(("NSV [dataoffset][fileoffset]\n", table_entries)); | |
373 for (i = 0; i < table_entries; i++) { | |
374 unsigned char b[8]; | |
375 url_fseek(pb, size + nsv->nsvf_index_data[i], SEEK_SET); | |
376 get_buffer(pb, b, 8); | |
377 PRINT(("NSV [0x%08lx][0x%08lx]: %02x %02x %02x %02x %02x %02x %02x %02x" | |
378 "%c%c%c%c%c%c%c%c\n", | |
379 nsv->nsvf_index_data[i], size + nsv->nsvf_index_data[i], | |
885 | 380 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], |
594 | 381 V(b[0]), V(b[1]), V(b[2]), V(b[3]), V(b[4]), V(b[5]), V(b[6]), V(b[7]) )); |
382 } | |
383 //url_fseek(pb, size, SEEK_SET); /* go back to end of header */ | |
384 #undef V | |
385 #endif | |
885 | 386 |
594 | 387 url_fseek(pb, nsv->base_offset + size, SEEK_SET); /* required for dumbdriving-271.nsv (2 extra bytes) */ |
885 | 388 |
594 | 389 if (url_feof(pb)) |
390 return -1; | |
391 nsv->state = NSV_HAS_READ_NSVF; | |
392 return 0; | |
393 } | |
394 | |
395 static int nsv_parse_NSVs_header(AVFormatContext *s, AVFormatParameters *ap) | |
396 { | |
397 NSVContext *nsv = s->priv_data; | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2567
diff
changeset
|
398 ByteIOContext *pb = s->pb; |
594 | 399 uint32_t vtag, atag; |
400 uint16_t vwidth, vheight; | |
742 | 401 AVRational framerate; |
402 int i; | |
594 | 403 AVStream *st; |
404 NSVStream *nst; | |
405 PRINT(("%s()\n", __FUNCTION__)); | |
406 | |
407 vtag = get_le32(pb); | |
408 atag = get_le32(pb); | |
409 vwidth = get_le16(pb); | |
410 vheight = get_le16(pb); | |
742 | 411 i = get_byte(pb); |
1430
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
412 |
742 | 413 PRINT(("NSV NSVs framerate code %2x\n", i)); |
1430
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
414 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
|
415 int t=(i & 0x7F)>>2; |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
416 if(t<16) framerate = (AVRational){1, t+1}; |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
417 else framerate = (AVRational){t-15, 1}; |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
418 |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
419 if(i&1){ |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
420 framerate.num *= 1000; |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
421 framerate.den *= 1001; |
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 |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
424 if((i&3)==3) framerate.num *= 24; |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
425 else if((i&3)==2) framerate.num *= 25; |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
426 else framerate.num *= 30; |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
427 } |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
428 else |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
429 framerate= (AVRational){i, 1}; |
f72de3879e6c
add support for all framerates specified by the standard
gpoirier
parents:
1429
diff
changeset
|
430 |
1429 | 431 nsv->avsync = get_le16(pb); |
3586
e421492a6e27
Try to fix FATE after my time_base simplification change.
michael
parents:
3424
diff
changeset
|
432 nsv->framerate = framerate; |
594 | 433 #ifdef DEBUG |
434 print_tag("NSV NSVs vtag", vtag, 0); | |
435 print_tag("NSV NSVs atag", atag, 0); | |
436 PRINT(("NSV NSVs vsize %dx%d\n", vwidth, vheight)); | |
437 #endif | |
885 | 438 |
594 | 439 /* XXX change to ap != NULL ? */ |
440 if (s->nb_streams == 0) { /* streams not yet published, let's do that */ | |
441 nsv->vtag = vtag; | |
442 nsv->atag = atag; | |
443 nsv->vwidth = vwidth; | |
444 nsv->vheight = vwidth; | |
445 if (vtag != T_NONE) { | |
446 st = av_new_stream(s, NSV_ST_VIDEO); | |
447 if (!st) | |
448 goto fail; | |
449 | |
450 nst = av_mallocz(sizeof(NSVStream)); | |
451 if (!nst) | |
452 goto fail; | |
453 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
|
454 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
|
455 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
|
456 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
|
457 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
|
458 st->codec->height = vheight; |
3908
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3586
diff
changeset
|
459 st->codec->bits_per_coded_sample = 24; /* depth XXX */ |
594 | 460 |
742 | 461 av_set_pts_info(st, 64, framerate.den, framerate.num); |
594 | 462 st->start_time = 0; |
743 | 463 st->duration = av_rescale(nsv->duration, framerate.num, 1000*framerate.den); |
594 | 464 } |
465 if (atag != T_NONE) { | |
466 #ifndef DISABLE_AUDIO | |
467 st = av_new_stream(s, NSV_ST_AUDIO); | |
468 if (!st) | |
469 goto fail; | |
470 | |
471 nst = av_mallocz(sizeof(NSVStream)); | |
472 if (!nst) | |
473 goto fail; | |
474 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
|
475 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
|
476 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
|
477 st->codec->codec_id = codec_get_id(nsv_codec_audio_tags, atag); |
885 | 478 |
2023 | 479 st->need_parsing = AVSTREAM_PARSE_FULL; /* for PCM we will read a chunk later and put correct info */ |
594 | 480 |
1429 | 481 /* set timebase to common denominator of ms and framerate */ |
482 av_set_pts_info(st, 64, 1, framerate.num*1000); | |
483 st->start_time = 0; | |
484 st->duration = (int64_t)nsv->duration * framerate.num; | |
594 | 485 #endif |
486 } | |
487 #ifdef CHECK_SUBSEQUENT_NSVS | |
488 } else { | |
489 if (nsv->vtag != vtag || nsv->atag != atag || nsv->vwidth != vwidth || nsv->vheight != vwidth) { | |
490 PRINT(("NSV NSVs header values differ from the first one!!!\n")); | |
491 //return -1; | |
492 } | |
493 #endif /* CHECK_SUBSEQUENT_NSVS */ | |
494 } | |
495 | |
496 nsv->state = NSV_HAS_READ_NSVS; | |
497 return 0; | |
498 fail: | |
499 /* XXX */ | |
500 nsv->state = NSV_UNSYNC; | |
501 return -1; | |
502 } | |
503 | |
504 static int nsv_read_header(AVFormatContext *s, AVFormatParameters *ap) | |
505 { | |
506 NSVContext *nsv = s->priv_data; | |
683
095009fc2f35
kill warnings patch by (Mns Rullgrd <mru inprovide com>)
michael
parents:
639
diff
changeset
|
507 int i, err; |
594 | 508 |
509 PRINT(("%s()\n", __FUNCTION__)); | |
510 PRINT(("filename '%s'\n", s->filename)); | |
511 | |
512 nsv->state = NSV_UNSYNC; | |
513 nsv->ahead[0].data = nsv->ahead[1].data = NULL; | |
885 | 514 |
594 | 515 for (i = 0; i < NSV_MAX_RESYNC_TRIES; i++) { |
516 if (nsv_resync(s) < 0) | |
517 return -1; | |
518 if (nsv->state == NSV_FOUND_NSVF) | |
519 err = nsv_parse_NSVf_header(s, ap); | |
520 /* we need the first NSVs also... */ | |
521 if (nsv->state == NSV_FOUND_NSVS) { | |
522 err = nsv_parse_NSVs_header(s, ap); | |
523 break; /* we just want the first one */ | |
524 } | |
525 } | |
526 if (s->nb_streams < 1) /* no luck so far */ | |
527 return -1; | |
528 /* now read the first chunk, so we can attempt to decode more info */ | |
529 err = nsv_read_chunk(s, 1); | |
885 | 530 |
594 | 531 PRINT(("parsed header\n")); |
532 return 0; | |
533 } | |
534 | |
535 static int nsv_read_chunk(AVFormatContext *s, int fill_header) | |
536 { | |
537 NSVContext *nsv = s->priv_data; | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2567
diff
changeset
|
538 ByteIOContext *pb = s->pb; |
594 | 539 AVStream *st[2] = {NULL, NULL}; |
540 NSVStream *nst; | |
541 AVPacket *pkt; | |
542 int i, err = 0; | |
595 | 543 uint8_t auxcount; /* number of aux metadata, also 4 bits of vsize */ |
544 uint32_t vsize; | |
545 uint16_t asize; | |
546 uint16_t auxsize; | |
547 uint32_t auxtag; | |
885 | 548 |
594 | 549 PRINT(("%s(%d)\n", __FUNCTION__, fill_header)); |
885 | 550 |
594 | 551 if (nsv->ahead[0].data || nsv->ahead[1].data) |
552 return 0; //-1; /* hey! eat what you've in your plate first! */ | |
553 | |
554 null_chunk_retry: | |
555 if (url_feof(pb)) | |
556 return -1; | |
885 | 557 |
594 | 558 for (i = 0; i < NSV_MAX_RESYNC_TRIES && nsv->state < NSV_FOUND_NSVS && !err; i++) |
559 err = nsv_resync(s); | |
560 if (err < 0) | |
561 return err; | |
562 if (nsv->state == NSV_FOUND_NSVS) | |
563 err = nsv_parse_NSVs_header(s, NULL); | |
564 if (err < 0) | |
565 return err; | |
566 if (nsv->state != NSV_HAS_READ_NSVS && nsv->state != NSV_FOUND_BEEF) | |
567 return -1; | |
885 | 568 |
594 | 569 auxcount = get_byte(pb); |
570 vsize = get_le16(pb); | |
571 asize = get_le16(pb); | |
572 vsize = (vsize << 4) | (auxcount >> 4); | |
573 auxcount &= 0x0f; | |
838 | 574 PRINT(("NSV CHUNK %d aux, %u bytes video, %d bytes audio\n", auxcount, vsize, asize)); |
594 | 575 /* skip aux stuff */ |
576 for (i = 0; i < auxcount; i++) { | |
577 auxsize = get_le16(pb); | |
578 auxtag = get_le32(pb); | |
885 | 579 PRINT(("NSV aux data: '%c%c%c%c', %d bytes\n", |
580 (auxtag & 0x0ff), | |
581 ((auxtag >> 8) & 0x0ff), | |
594 | 582 ((auxtag >> 16) & 0x0ff), |
583 ((auxtag >> 24) & 0x0ff), | |
584 auxsize)); | |
585 url_fskip(pb, auxsize); | |
595 | 586 vsize -= auxsize + sizeof(uint16_t) + sizeof(uint32_t); /* that's becoming braindead */ |
594 | 587 } |
885 | 588 |
594 | 589 if (url_feof(pb)) |
590 return -1; | |
591 if (!vsize && !asize) { | |
592 nsv->state = NSV_UNSYNC; | |
593 goto null_chunk_retry; | |
594 } | |
885 | 595 |
594 | 596 /* map back streams to v,a */ |
597 if (s->streams[0]) | |
598 st[s->streams[0]->id] = s->streams[0]; | |
599 if (s->streams[1]) | |
600 st[s->streams[1]->id] = s->streams[1]; | |
885 | 601 |
594 | 602 if (vsize/* && st[NSV_ST_VIDEO]*/) { |
603 nst = st[NSV_ST_VIDEO]->priv_data; | |
604 pkt = &nsv->ahead[NSV_ST_VIDEO]; | |
775 | 605 av_get_packet(pb, pkt, vsize); |
594 | 606 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
|
607 pkt->dts = nst->frame_offset; |
1429 | 608 pkt->flags |= nsv->state == NSV_HAS_READ_NSVS ? PKT_FLAG_KEY : 0; /* keyframe only likely on a sync frame */ |
594 | 609 /* |
610 for (i = 0; i < MIN(8, vsize); i++) | |
611 PRINT(("NSV video: [%d] = %02x\n", i, pkt->data[i])); | |
612 */ | |
613 } | |
2566
347da1903d92
fix segfault with http://samples.mplayerhq.hu/nsv/nsv_samples/ot171_novideo.nsv.bz2
michael
parents:
2164
diff
changeset
|
614 if(st[NSV_ST_VIDEO]) |
2567 | 615 ((NSVStream*)st[NSV_ST_VIDEO]->priv_data)->frame_offset++; |
1942
70b741fa63eb
The NSV demuxer assumes that a video frame's timestamp increases by one on each
diego
parents:
1677
diff
changeset
|
616 |
594 | 617 if (asize/*st[NSV_ST_AUDIO]*/) { |
618 nst = st[NSV_ST_AUDIO]->priv_data; | |
619 pkt = &nsv->ahead[NSV_ST_AUDIO]; | |
620 /* read raw audio specific header on the first audio chunk... */ | |
621 /* 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
|
622 if (asize && st[NSV_ST_AUDIO]->codec->codec_tag == MKTAG('P', 'C', 'M', ' ')/* && fill_header*/) { |
594 | 623 uint8_t bps; |
624 uint8_t channels; | |
625 uint16_t samplerate; | |
626 bps = get_byte(pb); | |
627 channels = get_byte(pb); | |
628 samplerate = get_le16(pb); | |
629 asize-=4; | |
838 | 630 PRINT(("NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate)); |
594 | 631 if (fill_header) { |
2023 | 632 st[NSV_ST_AUDIO]->need_parsing = AVSTREAM_PARSE_NONE; /* we know everything */ |
594 | 633 if (bps != 16) { |
634 PRINT(("NSV AUDIO bit/sample != 16 (%d)!!!\n", bps)); | |
635 } | |
636 bps /= channels; // ??? | |
637 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
|
638 st[NSV_ST_AUDIO]->codec->codec_id = CODEC_ID_PCM_U8; |
594 | 639 samplerate /= 4;/* UGH ??? XXX */ |
640 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
|
641 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
|
642 st[NSV_ST_AUDIO]->codec->sample_rate = samplerate; |
838 | 643 PRINT(("NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate)); |
594 | 644 } |
645 } | |
775 | 646 av_get_packet(pb, pkt, asize); |
594 | 647 pkt->stream_index = st[NSV_ST_AUDIO]->index;//NSV_ST_AUDIO; |
1429 | 648 pkt->flags |= nsv->state == NSV_HAS_READ_NSVS ? PKT_FLAG_KEY : 0; /* keyframe only likely on a sync frame */ |
649 if( nsv->state == NSV_HAS_READ_NSVS && st[NSV_ST_VIDEO] ) { | |
650 /* on a nsvs frame we have new information on a/v sync */ | |
651 pkt->dts = (((NSVStream*)st[NSV_ST_VIDEO]->priv_data)->frame_offset-1); | |
3586
e421492a6e27
Try to fix FATE after my time_base simplification change.
michael
parents:
3424
diff
changeset
|
652 pkt->dts *= (int64_t)1000 * nsv->framerate.den; |
e421492a6e27
Try to fix FATE after my time_base simplification change.
michael
parents:
3424
diff
changeset
|
653 pkt->dts += (int64_t)nsv->avsync * nsv->framerate.num; |
1429 | 654 PRINT(("NSV AUDIO: sync:%d, dts:%"PRId64, nsv->avsync, pkt->dts)); |
655 } | |
656 nst->frame_offset++; | |
594 | 657 } |
885 | 658 |
594 | 659 nsv->state = NSV_UNSYNC; |
660 return 0; | |
661 } | |
662 | |
663 | |
664 static int nsv_read_packet(AVFormatContext *s, AVPacket *pkt) | |
665 { | |
666 NSVContext *nsv = s->priv_data; | |
667 int i, err = 0; | |
668 | |
669 PRINT(("%s()\n", __FUNCTION__)); | |
885 | 670 |
594 | 671 /* in case we don't already have something to eat ... */ |
672 if (nsv->ahead[0].data == NULL && nsv->ahead[1].data == NULL) | |
673 err = nsv_read_chunk(s, 0); | |
674 if (err < 0) | |
675 return err; | |
885 | 676 |
594 | 677 /* now pick one of the plates */ |
678 for (i = 0; i < 2; i++) { | |
679 if (nsv->ahead[i].data) { | |
887 | 680 PRINT(("%s: using cached packet[%d]\n", __FUNCTION__, i)); |
594 | 681 /* avoid the cost of new_packet + memcpy(->data) */ |
682 memcpy(pkt, &nsv->ahead[i], sizeof(AVPacket)); | |
683 nsv->ahead[i].data = NULL; /* we ate that one */ | |
684 return pkt->size; | |
685 } | |
686 } | |
885 | 687 |
594 | 688 /* this restaurant is not approvisionned :^] */ |
689 return -1; | |
690 } | |
691 | |
692 static int nsv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) | |
693 { | |
683
095009fc2f35
kill warnings patch by (Mns Rullgrd <mru inprovide com>)
michael
parents:
639
diff
changeset
|
694 #if 0 |
594 | 695 NSVContext *avi = s->priv_data; |
696 AVStream *st; | |
697 NSVStream *ast; | |
698 int frame_number, i; | |
699 int64_t pos; | |
683
095009fc2f35
kill warnings patch by (Mns Rullgrd <mru inprovide com>)
michael
parents:
639
diff
changeset
|
700 #endif |
594 | 701 |
702 return -1; | |
703 } | |
704 | |
705 static int nsv_read_close(AVFormatContext *s) | |
706 { | |
683
095009fc2f35
kill warnings patch by (Mns Rullgrd <mru inprovide com>)
michael
parents:
639
diff
changeset
|
707 /* int i; */ |
594 | 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 } | |
820
feca73904e67
changing AVCodecContext codec -> *codec in AVStream so additions to AVCodecContext dont randomize AVStream and break binary compatibility
michael
parents:
775
diff
changeset
|
722 av_free(st->codec->palctrl); |
594 | 723 } |
724 | |
725 #endif | |
726 return 0; | |
727 } | |
728 | |
729 static int nsv_probe(AVProbeData *p) | |
730 { | |
731 int i; | |
601 | 732 // PRINT(("nsv_probe(), buf_size %d\n", p->buf_size)); |
594 | 733 /* check file header */ |
734 /* streamed files might not have any header */ | |
735 if (p->buf[0] == 'N' && p->buf[1] == 'S' && | |
2002 | 736 p->buf[2] == 'V' && (p->buf[3] == 'f' || p->buf[3] == 's')) |
594 | 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")) | |
2003 | 749 return AVPROBE_SCORE_MAX/2; |
594 | 750 /* FIXME: add mime-type check */ |
751 return 0; | |
752 } | |
753 | |
1169 | 754 AVInputFormat nsv_demuxer = { |
594 | 755 "nsv", |
3424
7a0230981402
Make long_names in lavf/lavdev optional depending on CONFIG_SMALL.
diego
parents:
3365
diff
changeset
|
756 NULL_IF_CONFIG_SMALL("NullSoft Video format"), |
594 | 757 sizeof(NSVContext), |
758 nsv_probe, | |
759 nsv_read_header, | |
760 nsv_read_packet, | |
761 nsv_read_close, | |
762 nsv_read_seek, | |
763 }; |