/src/ffmpeg/libavformat/nsvdec.c
Line | Count | Source |
1 | | /* |
2 | | * NSV demuxer |
3 | | * Copyright (c) 2004 The FFmpeg Project |
4 | | * |
5 | | * first version by Francois Revol <revol@free.fr> |
6 | | * |
7 | | * This file is part of FFmpeg. |
8 | | * |
9 | | * FFmpeg is free software; you can redistribute it and/or |
10 | | * modify it under the terms of the GNU Lesser General Public |
11 | | * License as published by the Free Software Foundation; either |
12 | | * version 2.1 of the License, or (at your option) any later version. |
13 | | * |
14 | | * FFmpeg is distributed in the hope that it will be useful, |
15 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | | * Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General Public |
20 | | * License along with FFmpeg; if not, write to the Free Software |
21 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | | */ |
23 | | |
24 | | #include "libavutil/attributes.h" |
25 | | #include "libavutil/mathematics.h" |
26 | | #include "libavutil/mem.h" |
27 | | #include "avformat.h" |
28 | | #include "demux.h" |
29 | | #include "internal.h" |
30 | | #include "libavutil/dict.h" |
31 | | #include "libavutil/intreadwrite.h" |
32 | | |
33 | | /* max bytes to crawl for trying to resync |
34 | | * stupid streaming servers don't start at chunk boundaries... |
35 | | */ |
36 | 23.8M | #define NSV_MAX_RESYNC (500*1024) |
37 | 7.24M | #define NSV_MAX_RESYNC_TRIES 300 |
38 | | |
39 | | /* |
40 | | * References: |
41 | | * (1) http://www.multimedia.cx/nsv-format.txt |
42 | | * seems someone came to the same conclusions as me, and updated it: |
43 | | * (2) http://www.stud.ktu.lt/~vitslav/nsv/nsv-format.txt |
44 | | * http://www.stud.ktu.lt/~vitslav/nsv/ |
45 | | * official docs |
46 | | * (3) http://ultravox.aol.com/NSVFormat.rtf |
47 | | * Sample files: |
48 | | * (S1) http://www.nullsoft.com/nsv/samples/ |
49 | | * http://www.nullsoft.com/nsv/samples/faster.nsv |
50 | | * http://streamripper.sourceforge.net/openbb/read.php?TID=492&page=4 |
51 | | */ |
52 | | |
53 | | /* |
54 | | * notes on the header (Francois Revol): |
55 | | * |
56 | | * It is followed by strings, then a table, but nothing tells |
57 | | * where the table begins according to (1). After checking faster.nsv, |
58 | | * I believe NVSf[16-19] gives the size of the strings data |
59 | | * (that is the offset of the data table after the header). |
60 | | * After checking all samples from (S1) all confirms this. |
61 | | * |
62 | | * Then, about NSVf[12-15], faster.nsf has 179700. When viewing it in VLC, |
63 | | * I noticed there was about 1 NVSs chunk/s, so I ran |
64 | | * strings faster.nsv | grep NSVs | wc -l |
65 | | * which gave me 180. That leads me to think that NSVf[12-15] might be the |
66 | | * file length in milliseconds. |
67 | | * Let's try that: |
68 | | * 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 |
69 | | * except for nsvtrailer (which doesn't have an NSVf header), it reports correct time. |
70 | | * |
71 | | * nsvtrailer.nsv (S1) does not have any NSVf header, only NSVs chunks, |
72 | | * so the header seems to not be mandatory. (for streaming). |
73 | | * |
74 | | * index slice duration check (excepts nsvtrailer.nsv): |
75 | | * 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, slice time $(($DUR/$IC))"; done |
76 | | */ |
77 | | |
78 | | /* |
79 | | * TODO: |
80 | | * - handle timestamps !!! |
81 | | * - use index |
82 | | * - mime-type in probe() |
83 | | * - seek |
84 | | */ |
85 | | |
86 | | #if 0 |
87 | | struct NSVf_header { |
88 | | uint32_t chunk_tag; /* 'NSVf' */ |
89 | | uint32_t chunk_size; |
90 | | uint32_t file_size; /* max 4GB ??? no one learns anything it seems :^) */ |
91 | | uint32_t file_length; //unknown1; /* what about MSB of file_size ? */ |
92 | | uint32_t info_strings_size; /* size of the info strings */ //unknown2; |
93 | | uint32_t table_entries; |
94 | | uint32_t table_entries_used; /* the left ones should be -1 */ |
95 | | }; |
96 | | |
97 | | struct NSVs_header { |
98 | | uint32_t chunk_tag; /* 'NSVs' */ |
99 | | uint32_t v4cc; /* or 'NONE' */ |
100 | | uint32_t a4cc; /* or 'NONE' */ |
101 | | uint16_t vwidth; /* av_assert0(vwidth%16==0) */ |
102 | | uint16_t vheight; /* av_assert0(vheight%16==0) */ |
103 | | uint8_t framerate; /* value = (framerate&0x80)?frtable[frameratex0x7f]:framerate */ |
104 | | uint16_t unknown; |
105 | | }; |
106 | | |
107 | | struct nsv_avchunk_header { |
108 | | uint8_t vchunk_size_lsb; |
109 | | uint16_t vchunk_size_msb; /* value = (vchunk_size_msb << 4) | (vchunk_size_lsb >> 4) */ |
110 | | uint16_t achunk_size; |
111 | | }; |
112 | | |
113 | | struct nsv_pcm_header { |
114 | | uint8_t bits_per_sample; |
115 | | uint8_t channel_count; |
116 | | uint16_t sample_rate; |
117 | | }; |
118 | | #endif |
119 | | |
120 | | /* variation from avi.h */ |
121 | | /*typedef struct CodecTag { |
122 | | int id; |
123 | | unsigned int tag; |
124 | | } CodecTag;*/ |
125 | | |
126 | | /* tags */ |
127 | | |
128 | | #define T_NSVF MKTAG('N', 'S', 'V', 'f') /* file header */ |
129 | | #define T_NSVS MKTAG('N', 'S', 'V', 's') /* chunk header */ |
130 | | #define T_TOC2 MKTAG('T', 'O', 'C', '2') /* extra index marker */ |
131 | 964 | #define T_NONE MKTAG('N', 'O', 'N', 'E') /* null a/v 4CC */ |
132 | | #define T_SUBT MKTAG('S', 'U', 'B', 'T') /* subtitle aux data */ |
133 | | #define T_ASYN MKTAG('A', 'S', 'Y', 'N') /* async a/v aux marker */ |
134 | | #define T_KEYF MKTAG('K', 'E', 'Y', 'F') /* video keyframe aux marker (addition) */ |
135 | | |
136 | 22.1M | #define TB_NSVF MKBETAG('N', 'S', 'V', 'f') |
137 | | #define TB_NSVS MKBETAG('N', 'S', 'V', 's') |
138 | | |
139 | | /* hardcoded stream indexes */ |
140 | 10.6M | #define NSV_ST_VIDEO 0 |
141 | 758k | #define NSV_ST_AUDIO 1 |
142 | | #define NSV_ST_SUBT 2 |
143 | | |
144 | | enum NSVStatus { |
145 | | NSV_UNSYNC, |
146 | | NSV_FOUND_NSVF, |
147 | | NSV_HAS_READ_NSVF, |
148 | | NSV_FOUND_NSVS, |
149 | | NSV_HAS_READ_NSVS, |
150 | | NSV_FOUND_BEEF, |
151 | | NSV_GOT_VIDEO, |
152 | | NSV_GOT_AUDIO, |
153 | | }; |
154 | | |
155 | | typedef struct NSVStream { |
156 | | int frame_offset; /* current frame (video) or byte (audio) counter |
157 | | (used to compute the pts) */ |
158 | | int scale; |
159 | | int rate; |
160 | | int sample_size; /* audio only data */ |
161 | | int start; |
162 | | |
163 | | int new_frame_offset; /* temporary storage (used during seek) */ |
164 | | int cum_len; /* temporary storage (used during seek) */ |
165 | | } NSVStream; |
166 | | |
167 | | typedef struct NSVContext { |
168 | | int base_offset; |
169 | | int NSVf_end; |
170 | | uint32_t *nsvs_file_offset; |
171 | | int index_entries; |
172 | | enum NSVStatus state; |
173 | | AVPacket ahead[2]; /* [v, a] if .data is !NULL there is something */ |
174 | | /* cached */ |
175 | | int64_t duration; |
176 | | uint32_t vtag, atag; |
177 | | uint16_t vwidth, vheight; |
178 | | int16_t avsync; |
179 | | AVRational framerate; |
180 | | uint32_t *nsvs_timestamps; |
181 | | int nsvf; |
182 | | } NSVContext; |
183 | | |
184 | | static const AVCodecTag nsv_codec_video_tags[] = { |
185 | | { AV_CODEC_ID_VP3, MKTAG('V', 'P', '3', ' ') }, |
186 | | { AV_CODEC_ID_VP3, MKTAG('V', 'P', '3', '0') }, |
187 | | { AV_CODEC_ID_VP3, MKTAG('V', 'P', '3', '1') }, |
188 | | { AV_CODEC_ID_VP5, MKTAG('V', 'P', '5', ' ') }, |
189 | | { AV_CODEC_ID_VP5, MKTAG('V', 'P', '5', '0') }, |
190 | | { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', ' ') }, |
191 | | { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '0') }, |
192 | | { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '1') }, |
193 | | { AV_CODEC_ID_VP6, MKTAG('V', 'P', '6', '2') }, |
194 | | { AV_CODEC_ID_VP8, MKTAG('V', 'P', '8', '0') }, |
195 | | /* |
196 | | { AV_CODEC_ID_VP4, MKTAG('V', 'P', '4', ' ') }, |
197 | | { AV_CODEC_ID_VP4, MKTAG('V', 'P', '4', '0') }, |
198 | | */ |
199 | | { AV_CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'D') }, /* cf sample xvid decoder from nsv_codec_sdk.zip */ |
200 | | { AV_CODEC_ID_H264, MKTAG('H', '2', '6', '4') }, |
201 | | { AV_CODEC_ID_RAWVIDEO, MKTAG('R', 'G', 'B', '3') }, |
202 | | { AV_CODEC_ID_NONE, 0 }, |
203 | | }; |
204 | | |
205 | | static const AVCodecTag nsv_codec_audio_tags[] = { |
206 | | { AV_CODEC_ID_MP3, MKTAG('M', 'P', '3', ' ') }, |
207 | | { AV_CODEC_ID_AAC, MKTAG('A', 'A', 'C', ' ') }, |
208 | | { AV_CODEC_ID_AAC, MKTAG('A', 'A', 'C', 'P') }, |
209 | | { AV_CODEC_ID_AAC, MKTAG('A', 'A', 'V', ' ') }, |
210 | | { AV_CODEC_ID_AAC, MKTAG('V', 'L', 'B', ' ') }, |
211 | | { AV_CODEC_ID_SPEEX, MKTAG('S', 'P', 'X', ' ') }, |
212 | | { AV_CODEC_ID_PCM_U16LE, MKTAG('P', 'C', 'M', ' ') }, |
213 | | { AV_CODEC_ID_NONE, 0 }, |
214 | | }; |
215 | | |
216 | | //static int nsv_load_index(AVFormatContext *s); |
217 | | static int nsv_read_chunk(AVFormatContext *s, int fill_header); |
218 | | |
219 | | /* try to find something we recognize, and set the state accordingly */ |
220 | | static int nsv_resync(AVFormatContext *s) |
221 | 1.84M | { |
222 | 1.84M | NSVContext *nsv = s->priv_data; |
223 | 1.84M | AVIOContext *pb = s->pb; |
224 | 1.84M | uint32_t v = 0; |
225 | 1.84M | int i; |
226 | | |
227 | 23.8M | for (i = 0; i < NSV_MAX_RESYNC; i++) { |
228 | 23.8M | if (avio_feof(pb)) { |
229 | 120 | av_log(s, AV_LOG_TRACE, "NSV EOF\n"); |
230 | 120 | nsv->state = NSV_UNSYNC; |
231 | 120 | return -1; |
232 | 120 | } |
233 | 23.8M | v <<= 8; |
234 | 23.8M | v |= avio_r8(pb); |
235 | 23.8M | if (i < 8) { |
236 | 6.33M | av_log(s, AV_LOG_TRACE, "NSV resync: [%d] = %02"PRIx32"\n", i, v & 0x0FF); |
237 | 6.33M | } |
238 | | |
239 | 23.8M | if ((v & 0x0000ffff) == 0xefbe) { /* BEEF */ |
240 | 1.75M | av_log(s, AV_LOG_TRACE, "NSV resynced on BEEF after %d bytes\n", i+1); |
241 | 1.75M | nsv->state = NSV_FOUND_BEEF; |
242 | 1.75M | return 0; |
243 | 1.75M | } |
244 | | /* we read as big-endian, thus the MK*BE* */ |
245 | 22.1M | if (v == TB_NSVF) { /* NSVf */ |
246 | 63.0k | av_log(s, AV_LOG_TRACE, "NSV resynced on NSVf after %d bytes\n", i+1); |
247 | 63.0k | nsv->state = NSV_FOUND_NSVF; |
248 | 63.0k | return 0; |
249 | 63.0k | } |
250 | 22.0M | if (v == MKBETAG('N', 'S', 'V', 's')) { /* NSVs */ |
251 | 26.9k | av_log(s, AV_LOG_TRACE, "NSV resynced on NSVs after %d bytes\n", i+1); |
252 | 26.9k | nsv->state = NSV_FOUND_NSVS; |
253 | 26.9k | return 0; |
254 | 26.9k | } |
255 | | |
256 | 22.0M | } |
257 | 14 | av_log(s, AV_LOG_TRACE, "NSV sync lost\n"); |
258 | 14 | return -1; |
259 | 1.84M | } |
260 | | |
261 | | static int nsv_parse_NSVf_header(AVFormatContext *s) |
262 | 126 | { |
263 | 126 | NSVContext *nsv = s->priv_data; |
264 | 126 | AVIOContext *pb = s->pb; |
265 | 126 | av_unused unsigned int file_size; |
266 | 126 | unsigned int size; |
267 | 126 | int64_t duration; |
268 | 126 | int strings_size; |
269 | 126 | int table_entries; |
270 | 126 | int table_entries_used; |
271 | | |
272 | 126 | nsv->state = NSV_UNSYNC; /* in case we fail */ |
273 | | |
274 | 126 | if (nsv->nsvf) { |
275 | 16 | av_log(s, AV_LOG_TRACE, "Multiple NSVf\n"); |
276 | 16 | return 0; |
277 | 16 | } |
278 | 110 | nsv->nsvf = 1; |
279 | | |
280 | 110 | size = avio_rl32(pb); |
281 | 110 | if (size < 28) |
282 | 2 | return -1; |
283 | 108 | nsv->NSVf_end = size; |
284 | | |
285 | 108 | file_size = (uint32_t)avio_rl32(pb); |
286 | 108 | av_log(s, AV_LOG_TRACE, "NSV NSVf chunk_size %u\n", size); |
287 | 108 | av_log(s, AV_LOG_TRACE, "NSV NSVf file_size %u\n", file_size); |
288 | | |
289 | 108 | nsv->duration = duration = avio_rl32(pb); /* in ms */ |
290 | 108 | av_log(s, AV_LOG_TRACE, "NSV NSVf duration %"PRId64" ms\n", duration); |
291 | | // XXX: store it in AVStreams |
292 | | |
293 | 108 | strings_size = avio_rl32(pb); |
294 | 108 | table_entries = avio_rl32(pb); |
295 | 108 | table_entries_used = avio_rl32(pb); |
296 | 108 | av_log(s, AV_LOG_TRACE, "NSV NSVf info-strings size: %d, table entries: %d, bis %d\n", |
297 | 108 | strings_size, table_entries, table_entries_used); |
298 | 108 | if (avio_feof(pb)) |
299 | 2 | return -1; |
300 | | |
301 | 106 | av_log(s, AV_LOG_TRACE, "NSV got header; filepos %"PRId64"\n", avio_tell(pb)); |
302 | | |
303 | 106 | if (strings_size > 0) { |
304 | 39 | char *strings; /* last byte will be '\0' to play safe with str*() */ |
305 | 39 | char *p, *endp; |
306 | 39 | char *token, *value; |
307 | 39 | char quote; |
308 | | |
309 | 39 | p = strings = av_mallocz((size_t)strings_size + 1); |
310 | 39 | if (!p) |
311 | 1 | return AVERROR(ENOMEM); |
312 | 38 | endp = strings + strings_size; |
313 | 38 | avio_read(pb, strings, strings_size); |
314 | 319 | while (p < endp) { |
315 | 382 | while (*p == ' ') |
316 | 70 | p++; /* strip out spaces */ |
317 | 312 | if (p >= endp-2) |
318 | 4 | break; |
319 | 308 | token = p; |
320 | 308 | p = strchr(p, '='); |
321 | 308 | if (!p || p >= endp-2) |
322 | 20 | break; |
323 | 288 | *p++ = '\0'; |
324 | 288 | quote = *p++; |
325 | 288 | value = p; |
326 | 288 | p = strchr(p, quote); |
327 | 288 | if (!p || p >= endp) |
328 | 7 | break; |
329 | 281 | *p++ = '\0'; |
330 | 281 | av_log(s, AV_LOG_TRACE, "NSV NSVf INFO: %s='%s'\n", token, value); |
331 | 281 | av_dict_set(&s->metadata, token, value, 0); |
332 | 281 | } |
333 | 38 | av_free(strings); |
334 | 38 | } |
335 | 105 | if (avio_feof(pb)) |
336 | 26 | return -1; |
337 | | |
338 | 79 | av_log(s, AV_LOG_TRACE, "NSV got infos; filepos %"PRId64"\n", avio_tell(pb)); |
339 | | |
340 | 79 | if (table_entries_used > 0) { |
341 | 58 | int i; |
342 | 58 | nsv->index_entries = table_entries_used; |
343 | 58 | if((unsigned)table_entries_used >= UINT_MAX / sizeof(uint32_t)) |
344 | 2 | return -1; |
345 | 56 | nsv->nsvs_file_offset = av_malloc_array((unsigned)table_entries_used, sizeof(uint32_t)); |
346 | 56 | if (!nsv->nsvs_file_offset) |
347 | 2 | return AVERROR(ENOMEM); |
348 | | |
349 | 465k | for(i=0;i<table_entries_used;i++) { |
350 | 465k | if (avio_feof(pb)) |
351 | 16 | return AVERROR_INVALIDDATA; |
352 | 465k | nsv->nsvs_file_offset[i] = avio_rl32(pb) + size; |
353 | 465k | } |
354 | | |
355 | 38 | if(table_entries > table_entries_used && |
356 | 16 | avio_rl32(pb) == MKTAG('T','O','C','2')) { |
357 | 0 | nsv->nsvs_timestamps = av_malloc_array((unsigned)table_entries_used, sizeof(uint32_t)); |
358 | 0 | if (!nsv->nsvs_timestamps) |
359 | 0 | return AVERROR(ENOMEM); |
360 | 0 | for(i=0;i<table_entries_used;i++) { |
361 | 0 | nsv->nsvs_timestamps[i] = avio_rl32(pb); |
362 | 0 | } |
363 | 0 | } |
364 | 38 | } |
365 | | |
366 | 59 | av_log(s, AV_LOG_TRACE, "NSV got index; filepos %"PRId64"\n", avio_tell(pb)); |
367 | | |
368 | 59 | avio_seek(pb, nsv->base_offset + size, SEEK_SET); /* required for dumbdriving-271.nsv (2 extra bytes) */ |
369 | | |
370 | 59 | if (avio_feof(pb)) |
371 | 4 | return -1; |
372 | 55 | nsv->state = NSV_HAS_READ_NSVF; |
373 | 55 | return 0; |
374 | 59 | } |
375 | | |
376 | | static int nsv_parse_NSVs_header(AVFormatContext *s) |
377 | 26.9k | { |
378 | 26.9k | NSVContext *nsv = s->priv_data; |
379 | 26.9k | AVIOContext *pb = s->pb; |
380 | 26.9k | uint32_t vtag, atag; |
381 | 26.9k | uint16_t vwidth, vheight; |
382 | 26.9k | AVRational framerate; |
383 | 26.9k | int i; |
384 | 26.9k | AVStream *st; |
385 | 26.9k | NSVStream *nst; |
386 | | |
387 | 26.9k | vtag = avio_rl32(pb); |
388 | 26.9k | atag = avio_rl32(pb); |
389 | 26.9k | vwidth = avio_rl16(pb); |
390 | 26.9k | vheight = avio_rl16(pb); |
391 | 26.9k | i = avio_r8(pb); |
392 | | |
393 | 26.9k | av_log(s, AV_LOG_TRACE, "NSV NSVs framerate code %2x\n", i); |
394 | 26.9k | if(i&0x80) { /* odd way of giving native framerates from docs */ |
395 | 9.27k | int t=(i & 0x7F)>>2; |
396 | 9.27k | if(t<16) framerate = (AVRational){1, t+1}; |
397 | 6.41k | else framerate = (AVRational){t-15, 1}; |
398 | | |
399 | 9.27k | if(i&1){ |
400 | 3.66k | framerate.num *= 1000; |
401 | 3.66k | framerate.den *= 1001; |
402 | 3.66k | } |
403 | | |
404 | 9.27k | if((i&3)==3) framerate.num *= 24; |
405 | 8.77k | else if((i&3)==2) framerate.num *= 25; |
406 | 3.19k | else framerate.num *= 30; |
407 | 9.27k | } |
408 | 17.7k | else |
409 | 17.7k | framerate= (AVRational){i, 1}; |
410 | | |
411 | 26.9k | nsv->avsync = avio_rl16(pb); |
412 | 26.9k | nsv->framerate = framerate; |
413 | | |
414 | 26.9k | av_log(s, AV_LOG_TRACE, "NSV NSVs vsize %dx%d\n", vwidth, vheight); |
415 | | |
416 | | /* XXX change to ap != NULL ? */ |
417 | 26.9k | if (s->nb_streams == 0) { /* streams not yet published, let's do that */ |
418 | 482 | nsv->vtag = vtag; |
419 | 482 | nsv->atag = atag; |
420 | 482 | nsv->vwidth = vwidth; |
421 | 482 | nsv->vheight = vwidth; |
422 | 482 | if (vtag != T_NONE) { |
423 | 463 | int i; |
424 | 463 | st = avformat_new_stream(s, NULL); |
425 | 463 | if (!st) |
426 | 0 | goto fail; |
427 | | |
428 | 463 | st->id = NSV_ST_VIDEO; |
429 | 463 | nst = av_mallocz(sizeof(NSVStream)); |
430 | 463 | if (!nst) |
431 | 0 | goto fail; |
432 | 463 | st->priv_data = nst; |
433 | 463 | st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; |
434 | 463 | st->codecpar->codec_tag = vtag; |
435 | 463 | st->codecpar->codec_id = ff_codec_get_id(nsv_codec_video_tags, vtag); |
436 | 463 | st->codecpar->width = vwidth; |
437 | 463 | st->codecpar->height = vheight; |
438 | 463 | st->codecpar->bits_per_coded_sample = 24; /* depth XXX */ |
439 | | |
440 | 463 | avpriv_set_pts_info(st, 64, framerate.den, framerate.num); |
441 | 463 | st->start_time = 0; |
442 | 463 | st->duration = av_rescale(nsv->duration, framerate.num, 1000*framerate.den); |
443 | | |
444 | 398k | for(i=0;i<nsv->index_entries;i++) { |
445 | 397k | if(nsv->nsvs_timestamps) { |
446 | 0 | av_add_index_entry(st, nsv->nsvs_file_offset[i], nsv->nsvs_timestamps[i], |
447 | 0 | 0, 0, AVINDEX_KEYFRAME); |
448 | 397k | } else { |
449 | 397k | int64_t ts = av_rescale(i*nsv->duration/nsv->index_entries, framerate.num, 1000*framerate.den); |
450 | 397k | av_add_index_entry(st, nsv->nsvs_file_offset[i], ts, 0, 0, AVINDEX_KEYFRAME); |
451 | 397k | } |
452 | 397k | } |
453 | 463 | } |
454 | 482 | if (atag != T_NONE) { |
455 | 471 | st = avformat_new_stream(s, NULL); |
456 | 471 | if (!st) |
457 | 0 | goto fail; |
458 | | |
459 | 471 | st->id = NSV_ST_AUDIO; |
460 | 471 | nst = av_mallocz(sizeof(NSVStream)); |
461 | 471 | if (!nst) |
462 | 0 | goto fail; |
463 | 471 | st->priv_data = nst; |
464 | 471 | st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; |
465 | 471 | st->codecpar->codec_tag = atag; |
466 | 471 | st->codecpar->codec_id = ff_codec_get_id(nsv_codec_audio_tags, atag); |
467 | | |
468 | 471 | if (atag == MKTAG('A', 'A', 'V', ' ')) { |
469 | 0 | static const uint8_t aav_pce[] = { |
470 | 0 | 0x12, 0x00, 0x05, 0x08, 0x48, 0x00, |
471 | 0 | 0x20, 0x00, 0xC6, 0x40, 0x04, 0x4C, |
472 | 0 | 0x61, 0x76, 0x63, 0x56, 0xE5, 0x00, |
473 | 0 | 0x00, 0x00, |
474 | 0 | }; |
475 | 0 | int ret; |
476 | |
|
477 | 0 | if ((ret = ff_alloc_extradata(st->codecpar, sizeof(aav_pce))) < 0) |
478 | 0 | return ret; |
479 | | |
480 | 0 | st->codecpar->sample_rate = 44100; |
481 | 0 | memcpy(st->codecpar->extradata, aav_pce, sizeof(aav_pce)); |
482 | 0 | } |
483 | | |
484 | 471 | ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL; /* for PCM we will read a chunk later and put correct info */ |
485 | | |
486 | | /* set timebase to common denominator of ms and framerate */ |
487 | 471 | avpriv_set_pts_info(st, 64, 1, framerate.num*1000); |
488 | 471 | st->start_time = 0; |
489 | 471 | st->duration = (int64_t)nsv->duration * framerate.num; |
490 | 471 | } |
491 | 26.4k | } else { |
492 | 26.4k | if (nsv->vtag != vtag || nsv->atag != atag || nsv->vwidth != vwidth || nsv->vheight != vwidth) { |
493 | 25.5k | av_log(s, AV_LOG_TRACE, "NSV NSVs header values differ from the first one!!!\n"); |
494 | | //return -1; |
495 | 25.5k | } |
496 | 26.4k | } |
497 | | |
498 | 26.9k | nsv->state = NSV_HAS_READ_NSVS; |
499 | 26.9k | return 0; |
500 | 0 | fail: |
501 | | /* XXX */ |
502 | 0 | nsv->state = NSV_UNSYNC; |
503 | 0 | return -1; |
504 | 26.9k | } |
505 | | |
506 | | static int nsv_read_header(AVFormatContext *s) |
507 | 575 | { |
508 | 575 | NSVContext *nsv = s->priv_data; |
509 | 575 | int i, err; |
510 | | |
511 | 575 | nsv->state = NSV_UNSYNC; |
512 | 575 | nsv->ahead[0].data = nsv->ahead[1].data = NULL; |
513 | | |
514 | 2.67k | for (i = 0; i < NSV_MAX_RESYNC_TRIES; i++) { |
515 | 2.67k | err = nsv_resync(s); |
516 | 2.67k | if (err < 0) |
517 | 33 | return err; |
518 | 2.63k | if (nsv->state == NSV_FOUND_NSVF) { |
519 | 126 | err = nsv_parse_NSVf_header(s); |
520 | 126 | if (err < 0) |
521 | 55 | return err; |
522 | 126 | } |
523 | | /* we need the first NSVs also... */ |
524 | 2.58k | if (nsv->state == NSV_FOUND_NSVS) { |
525 | 482 | err = nsv_parse_NSVs_header(s); |
526 | 482 | if (err < 0) |
527 | 0 | return err; |
528 | 482 | break; /* we just want the first one */ |
529 | 482 | } |
530 | 2.58k | } |
531 | 487 | if (s->nb_streams < 1) /* no luck so far */ |
532 | 5 | return AVERROR_INVALIDDATA; |
533 | | |
534 | | /* now read the first chunk, so we can attempt to decode more info */ |
535 | 482 | err = nsv_read_chunk(s, 1); |
536 | | |
537 | 482 | av_log(s, AV_LOG_TRACE, "parsed header\n"); |
538 | 482 | return err; |
539 | 487 | } |
540 | | |
541 | | static int nsv_read_chunk(AVFormatContext *s, int fill_header) |
542 | 1.77M | { |
543 | 1.77M | NSVContext *nsv = s->priv_data; |
544 | 1.77M | AVIOContext *pb = s->pb; |
545 | 1.77M | AVStream *st[2] = {NULL, NULL}; |
546 | 1.77M | NSVStream *nst; |
547 | 1.77M | AVPacket *pkt; |
548 | 1.77M | int i, err = 0; |
549 | 1.77M | uint8_t auxcount; /* number of aux metadata, also 4 bits of vsize */ |
550 | 1.77M | uint32_t vsize; |
551 | 1.77M | uint16_t asize; |
552 | 1.77M | uint16_t auxsize; |
553 | 1.77M | int ret; |
554 | | |
555 | 1.77M | if (nsv->ahead[0].data || nsv->ahead[1].data) |
556 | 0 | return 0; //-1; /* hey! eat what you've in your plate first! */ |
557 | | |
558 | 1.78M | null_chunk_retry: |
559 | 1.78M | if (avio_feof(pb)) |
560 | 511 | return -1; |
561 | | |
562 | 3.62M | for (i = 0; i < NSV_MAX_RESYNC_TRIES && nsv->state < NSV_FOUND_NSVS && !err; i++) |
563 | 1.84M | err = nsv_resync(s); |
564 | 1.78M | if (err < 0) |
565 | 101 | return err; |
566 | 1.78M | if (nsv->state == NSV_FOUND_NSVS) |
567 | 26.4k | err = nsv_parse_NSVs_header(s); |
568 | 1.78M | if (err < 0) |
569 | 0 | return err; |
570 | 1.78M | if (nsv->state != NSV_HAS_READ_NSVS && nsv->state != NSV_FOUND_BEEF) |
571 | 177 | return -1; |
572 | | |
573 | 1.78M | auxcount = avio_r8(pb); |
574 | 1.78M | vsize = avio_rl16(pb); |
575 | 1.78M | asize = avio_rl16(pb); |
576 | 1.78M | vsize = (vsize << 4) | (auxcount >> 4); |
577 | 1.78M | auxcount &= 0x0f; |
578 | 1.78M | av_log(s, AV_LOG_TRACE, "NSV CHUNK %d aux, %"PRIu32" bytes video, %d bytes audio\n", auxcount, vsize, asize); |
579 | | /* skip aux stuff */ |
580 | 1.78M | for (i = 0; i < auxcount; i++) { |
581 | 1.97k | av_unused uint32_t auxtag; |
582 | 1.97k | auxsize = avio_rl16(pb); |
583 | 1.97k | auxtag = avio_rl32(pb); |
584 | 1.97k | avio_skip(pb, auxsize); |
585 | 1.97k | vsize -= auxsize + sizeof(uint16_t) + sizeof(uint32_t); /* that's becoming brain-dead */ |
586 | 1.97k | } |
587 | | |
588 | 1.78M | if (avio_feof(pb)) |
589 | 93 | return -1; |
590 | 1.78M | if (!vsize && !asize) { |
591 | 2.88k | nsv->state = NSV_UNSYNC; |
592 | 2.88k | goto null_chunk_retry; |
593 | 2.88k | } |
594 | | |
595 | | /* map back streams to v,a */ |
596 | 1.77M | if (s->nb_streams > 0) |
597 | 1.77M | st[s->streams[0]->id] = s->streams[0]; |
598 | 1.77M | if (s->nb_streams > 1) |
599 | 1.71M | st[s->streams[1]->id] = s->streams[1]; |
600 | | |
601 | 1.77M | if (vsize && st[NSV_ST_VIDEO]) { |
602 | 1.74M | nst = st[NSV_ST_VIDEO]->priv_data; |
603 | 1.74M | pkt = &nsv->ahead[NSV_ST_VIDEO]; |
604 | 1.74M | if ((ret = av_get_packet(pb, pkt, vsize)) < 0) |
605 | 324 | return ret; |
606 | 1.74M | pkt->stream_index = st[NSV_ST_VIDEO]->index;//NSV_ST_VIDEO; |
607 | 1.74M | pkt->dts = nst->frame_offset; |
608 | 1.74M | pkt->flags |= nsv->state == NSV_HAS_READ_NSVS ? AV_PKT_FLAG_KEY : 0; /* keyframe only likely on a sync frame */ |
609 | 3.52M | for (i = 0; i < FFMIN(8, vsize); i++) |
610 | 1.77M | av_log(s, AV_LOG_TRACE, "NSV video: [%d] = %02x\n", i, pkt->data[i]); |
611 | 1.74M | } |
612 | 1.77M | if(st[NSV_ST_VIDEO]) |
613 | 1.77M | ((NSVStream*)st[NSV_ST_VIDEO]->priv_data)->frame_offset++; |
614 | | |
615 | 1.77M | if (asize && st[NSV_ST_AUDIO]) { |
616 | 162k | nst = st[NSV_ST_AUDIO]->priv_data; |
617 | 162k | pkt = &nsv->ahead[NSV_ST_AUDIO]; |
618 | | /* read raw audio specific header on the first audio chunk... */ |
619 | | /* on ALL audio chunks ?? seems so! */ |
620 | 162k | if (asize >= 4 && st[NSV_ST_AUDIO]->codecpar->codec_tag == MKTAG('P', 'C', 'M', ' ')/* && fill_header*/) { |
621 | 12.1k | uint8_t bps; |
622 | 12.1k | uint8_t channels; |
623 | 12.1k | uint16_t samplerate; |
624 | 12.1k | bps = avio_r8(pb); |
625 | 12.1k | channels = avio_r8(pb); |
626 | 12.1k | samplerate = avio_rl16(pb); |
627 | 12.1k | if (!channels || !samplerate) |
628 | 18 | return AVERROR_INVALIDDATA; |
629 | 12.1k | asize-=4; |
630 | 12.1k | av_log(s, AV_LOG_TRACE, "NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate); |
631 | 12.1k | if (fill_header) { |
632 | 70 | ffstream(st[NSV_ST_AUDIO])->need_parsing = AVSTREAM_PARSE_NONE; /* we know everything */ |
633 | 70 | if (bps != 16) { |
634 | 60 | av_log(s, AV_LOG_TRACE, "NSV AUDIO bit/sample != 16 (%d)!!!\n", bps); |
635 | 60 | } |
636 | 70 | bps /= channels; // ??? |
637 | 70 | if (bps == 8) |
638 | 13 | st[NSV_ST_AUDIO]->codecpar->codec_id = AV_CODEC_ID_PCM_U8; |
639 | 70 | samplerate /= 4;/* UGH ??? XXX */ |
640 | 70 | channels = 1; |
641 | 70 | st[NSV_ST_AUDIO]->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; |
642 | 70 | st[NSV_ST_AUDIO]->codecpar->sample_rate = samplerate; |
643 | 70 | av_log(s, AV_LOG_TRACE, "NSV RAWAUDIO: bps %d, nchan %d, srate %d\n", bps, channels, samplerate); |
644 | 70 | } |
645 | 12.1k | } |
646 | 162k | if ((ret = av_get_packet(pb, pkt, asize)) < 0) |
647 | 64 | return ret; |
648 | 162k | pkt->stream_index = st[NSV_ST_AUDIO]->index;//NSV_ST_AUDIO; |
649 | 162k | pkt->flags |= nsv->state == NSV_HAS_READ_NSVS ? AV_PKT_FLAG_KEY : 0; /* keyframe only likely on a sync frame */ |
650 | 162k | if( nsv->state == NSV_HAS_READ_NSVS && st[NSV_ST_VIDEO] ) { |
651 | | /* on a nsvs frame we have new information on a/v sync */ |
652 | 25.5k | pkt->dts = (((NSVStream*)st[NSV_ST_VIDEO]->priv_data)->frame_offset-1); |
653 | 25.5k | pkt->dts *= (int64_t)1000 * nsv->framerate.den; |
654 | 25.5k | pkt->dts += (int64_t)nsv->avsync * nsv->framerate.num; |
655 | 25.5k | av_log(s, AV_LOG_TRACE, "NSV AUDIO: sync:%d, dts:%"PRId64, nsv->avsync, pkt->dts); |
656 | 25.5k | } |
657 | 162k | nst->frame_offset++; |
658 | 162k | } |
659 | | |
660 | 1.77M | nsv->state = NSV_UNSYNC; |
661 | 1.77M | return 0; |
662 | 1.77M | } |
663 | | |
664 | | |
665 | | static int nsv_read_packet(AVFormatContext *s, AVPacket *pkt) |
666 | 1.91M | { |
667 | 1.91M | NSVContext *nsv = s->priv_data; |
668 | 1.91M | int i, err = 0; |
669 | | |
670 | | /* in case we don't already have something to eat ... */ |
671 | 1.91M | if (!nsv->ahead[0].data && !nsv->ahead[1].data) |
672 | 1.77M | err = nsv_read_chunk(s, 0); |
673 | 1.91M | if (err < 0) |
674 | 1.19k | return err; |
675 | | |
676 | | /* now pick one of the plates */ |
677 | 2.07M | for (i = 0; i < 2; i++) { |
678 | 2.07M | if (nsv->ahead[i].data) { |
679 | 1.91M | av_packet_move_ref(pkt, &nsv->ahead[i]); |
680 | 1.91M | return 0; |
681 | 1.91M | } |
682 | 2.07M | } |
683 | | |
684 | | /* this restaurant is not provisioned :^] */ |
685 | 4 | return -1; |
686 | 1.91M | } |
687 | | |
688 | | static int nsv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) |
689 | 0 | { |
690 | 0 | NSVContext *nsv = s->priv_data; |
691 | 0 | AVStream *st = s->streams[stream_index]; |
692 | 0 | FFStream *const sti = ffstream(st); |
693 | 0 | NSVStream *nst = st->priv_data; |
694 | 0 | int index; |
695 | |
|
696 | 0 | index = av_index_search_timestamp(st, timestamp, flags); |
697 | 0 | if(index < 0) |
698 | 0 | return -1; |
699 | | |
700 | 0 | if (avio_seek(s->pb, sti->index_entries[index].pos, SEEK_SET) < 0) |
701 | 0 | return -1; |
702 | | |
703 | 0 | nst->frame_offset = sti->index_entries[index].timestamp; |
704 | 0 | nsv->state = NSV_UNSYNC; |
705 | 0 | return 0; |
706 | 0 | } |
707 | | |
708 | | static int nsv_read_close(AVFormatContext *s) |
709 | 575 | { |
710 | 575 | NSVContext *nsv = s->priv_data; |
711 | | |
712 | 575 | av_freep(&nsv->nsvs_file_offset); |
713 | 575 | av_freep(&nsv->nsvs_timestamps); |
714 | 575 | if (nsv->ahead[0].data) |
715 | 28 | av_packet_unref(&nsv->ahead[0]); |
716 | 575 | if (nsv->ahead[1].data) |
717 | 1 | av_packet_unref(&nsv->ahead[1]); |
718 | 575 | return 0; |
719 | 575 | } |
720 | | |
721 | | static int nsv_probe(const AVProbeData *p) |
722 | 958k | { |
723 | 958k | int i, score = 0; |
724 | | |
725 | | /* check file header */ |
726 | | /* streamed files might not have any header */ |
727 | 958k | if (p->buf[0] == 'N' && p->buf[1] == 'S' && |
728 | 1.01k | p->buf[2] == 'V' && (p->buf[3] == 'f' || p->buf[3] == 's')) |
729 | 409 | return AVPROBE_SCORE_MAX; |
730 | | /* XXX: do streamed files always start at chunk boundary ?? */ |
731 | | /* or do we need to search NSVs in the byte stream ? */ |
732 | | /* seems the servers don't bother starting clean chunks... */ |
733 | | /* sometimes even the first header is at 9KB or something :^) */ |
734 | 3.58G | for (i = 1; i < p->buf_size - 3; i++) { |
735 | 3.58G | if (AV_RL32(p->buf + i) == AV_RL32("NSVs")) { |
736 | | /* Get the chunk size and check if at the end we are getting 0xBEEF */ |
737 | 76.3k | int vsize = AV_RL24(p->buf+i+19) >> 4; |
738 | 76.3k | int asize = AV_RL16(p->buf+i+22); |
739 | 76.3k | int offset = i + 23 + asize + vsize + 1; |
740 | 76.3k | if (offset <= p->buf_size - 2 && AV_RL16(p->buf + offset) == 0xBEEF) |
741 | 300 | return 4*AVPROBE_SCORE_MAX/5; |
742 | 76.0k | score = AVPROBE_SCORE_MAX/5; |
743 | 76.0k | } |
744 | 3.58G | } |
745 | | /* so we'll have more luck on extension... */ |
746 | 957k | if (av_match_ext(p->filename, "nsv")) |
747 | 0 | return AVPROBE_SCORE_EXTENSION; |
748 | | /* FIXME: add mime-type check */ |
749 | 957k | return score; |
750 | 957k | } |
751 | | |
752 | | const FFInputFormat ff_nsv_demuxer = { |
753 | | .p.name = "nsv", |
754 | | .p.long_name = NULL_IF_CONFIG_SMALL("Nullsoft Streaming Video"), |
755 | | .priv_data_size = sizeof(NSVContext), |
756 | | .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, |
757 | | .read_probe = nsv_probe, |
758 | | .read_header = nsv_read_header, |
759 | | .read_packet = nsv_read_packet, |
760 | | .read_close = nsv_read_close, |
761 | | .read_seek = nsv_read_seek, |
762 | | }; |