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