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