2 * Nullsoft Streaming Video demuxer
3 * copyright (c) 2004 by Reza Jelveh <reza.jelveh@tuhh.de>
4 * Based on A'rpis G2 work
6 * seeking and PCM audio not yet supported
7 * PCM needs extra audio chunk "miniheader" parsing
9 * This file is part of MPlayer.
11 * MPlayer is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * MPlayer is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 #include "stream/stream.h"
39 unsigned int a_format
;
40 unsigned int v_format
;
44 #define HEADER_SEARCH_SIZE 256000
48 * Seeking still to be implemented
50 static void demux_seek_nsv ( demuxer_t
*demuxer
, float rel_seek_secs
, float audio_delay
, int flags
)
52 // seeking is not yet implemented
56 static int demux_nsv_fill_buffer ( demuxer_t
*demuxer
, demux_stream_t
*ds
)
58 unsigned char hdr
[17];
62 // videolen = audio chunk length, audiolen = video chunk length
63 int videolen
,audiolen
;
65 sh_video_t
*sh_video
= demuxer
->video
->sh
;
66 sh_audio_t
*sh_audio
= demuxer
->audio
->sh
;
68 nsv_priv_t
* priv
= demuxer
->priv
;
70 // if the audio/video chunk has no new header the first 2 bytes will be discarded 0xBEEF
71 // or rather 0xEF 0xBE
72 stream_read(demuxer
->stream
,hdr
,7);
73 if(stream_eof(demuxer
->stream
)) return 0;
74 // sometimes instead of 0xBEEF as described for the next audio/video chunk we get
77 mp_dbg(MSGT_DEMUX
, MSGL_DBG2
, "demux_nsv: %08X %08X\n",
78 hdr
[0]<<8|hdr
[1], (unsigned int)stream_tell(demuxer
->stream
));
79 switch(hdr
[0]<<8|hdr
[1]) {
81 if(hdr
[2]==0x56 && hdr
[3]==0x73){
83 // get the header since there is no more metaheader after the first one
84 // there is no more need to skip that
85 stream_read(demuxer
->stream
,hdr
+7,17-7);
86 stream_read(demuxer
->stream
,hdr
,7);
94 mp_dbg(MSGT_DEMUX
,MSGL_WARN
,"demux_nsv: sync lost\n");
99 priv
->v_pts
=demuxer
->video
->pts
= priv
->video_pack_no
*
100 (float)sh_video
->frametime
;
102 priv
->v_pts
= priv
->video_pack_no
;
104 demuxer
->filepos
=stream_tell(demuxer
->stream
);
107 mp_dbg(MSGT_DEMUX
,MSGL_DBG2
,"demux_nsv: %08X: %02X %02X | %02X %02X %02X | %02X %02X \n",
108 (int)demuxer
->filepos
, hdr
[0],hdr
[1],hdr
[2],hdr
[3],hdr
[4],hdr
[5],hdr
[6]);
111 videolen
=(hdr
[2]>>4)|(hdr
[3]<<4)|(hdr
[4]<<0xC);
112 //check if we got extra data like subtitles here
113 if( (hdr
[2]&0x0f) != 0x0 ) {
114 stream_read( demuxer
->stream
, aux
, 6);
116 i_aux
= aux
[0]|aux
[1]<<8;
117 // We skip this extra data
118 stream_skip( demuxer
->stream
, i_aux
);
124 // we need to return an empty packet when the
125 // video frame is empty otherwise the stream will fasten up
127 if( (hdr
[2]&0x0f) != 0x0 )
128 ds_read_packet(demuxer
->video
,demuxer
->stream
,videolen
,priv
->v_pts
,demuxer
->filepos
-i_aux
,0);
130 ds_read_packet(demuxer
->video
,demuxer
->stream
,videolen
,priv
->v_pts
,demuxer
->filepos
,0);
133 stream_skip(demuxer
->stream
,videolen
);
136 audiolen
=(hdr
[5])|(hdr
[6]<<8);
137 // we need to return an empty packet when the
138 // audio frame is empty otherwise the stream will fasten up
140 ds_read_packet(demuxer
->audio
,demuxer
->stream
,audiolen
,priv
->v_pts
,demuxer
->filepos
+videolen
,0);
143 stream_skip(demuxer
->stream
,audiolen
);
145 ++priv
->video_pack_no
;
152 static demuxer_t
* demux_open_nsv ( demuxer_t
* demuxer
)
154 // last 2 bytes 17 and 18 are unknown but right after that comes the length
155 unsigned char hdr
[17];
156 int videolen
,audiolen
;
157 unsigned char buf
[10];
158 sh_video_t
*sh_video
= NULL
;
159 sh_audio_t
*sh_audio
= NULL
;
162 nsv_priv_t
* priv
= malloc(sizeof(nsv_priv_t
));
164 priv
->video_pack_no
=0;
166 /* disable seeking yet to be fixed*/
167 demuxer
->seekable
= 0;
169 stream_read(demuxer
->stream
,hdr
,4);
170 if(stream_eof(demuxer
->stream
)) return 0;
172 if(hdr
[0]==0x4E && hdr
[1]==0x53 && hdr
[2]==0x56){
176 stream_read(demuxer
->stream
,hdr
+4,17-4);
181 int len
=stream_read_dword_le(demuxer
->stream
);
182 // TODO: parse out metadata!!!!
183 stream_skip(demuxer
->stream
,len
-8);
186 stream_read(demuxer
->stream
,hdr
,17);
187 if (stream_eof(demuxer
->stream
) || strncmp(hdr
, "NSVs", 4))
191 // dummy debug message
192 mp_msg(MSGT_DEMUX
,MSGL_V
,"demux_nsv: Header: %.12s\n",hdr
);
194 // bytes 8-11 audio codec fourcc
195 // PCM fourcc needs extra parsing for every audio chunk, yet to implement
196 if((demuxer
->audio
->id
!= -2) && strncmp(hdr
+8,"NONE", 4)){//&&strncmp(hdr+8,"VLB ", 4)){
197 sh_audio
= new_sh_audio ( demuxer
, 0 );
198 demuxer
->audio
->id
= 0;
199 demuxer
->audio
->sh
= sh_audio
;
200 sh_audio
->format
=mmioFOURCC(hdr
[8],hdr
[9],hdr
[10],hdr
[11]);
201 sh_audio
->ds
= demuxer
->audio
;
202 priv
->a_format
=mmioFOURCC(hdr
[8],hdr
[9],hdr
[10],hdr
[11]);
208 if ((demuxer
->video
->id
!= -2) && strncmp(hdr
+4,"NONE", 4)) {
209 /* Create a new video stream header */
210 sh_video
= new_sh_video ( demuxer
, 0 );
212 /* Make sure the demuxer knows about the new video stream header
213 * (even though new_sh_video() ought to take care of it)
215 demuxer
->video
->sh
= sh_video
;
217 /* Make sure that the video demuxer stream header knows about its
218 * parent video demuxer stream (this is getting wacky), or else
219 * video_read_properties() will choke
221 sh_video
->ds
= demuxer
->video
;
223 // bytes 4-7 video codec fourcc
224 priv
->v_format
= sh_video
->format
=mmioFOURCC(hdr
[4],hdr
[5],hdr
[6],hdr
[7]);
226 // new video stream! parse header
227 sh_video
->disp_w
=hdr
[12]|(hdr
[13]<<8);
228 sh_video
->disp_h
=hdr
[14]|(hdr
[15]<<8);
229 sh_video
->bih
=calloc(1,sizeof(*sh_video
->bih
));
230 sh_video
->bih
->biSize
=sizeof(*sh_video
->bih
);
231 sh_video
->bih
->biPlanes
=1;
232 sh_video
->bih
->biBitCount
=24;
233 sh_video
->bih
->biWidth
=hdr
[12]|(hdr
[13]<<8);
234 sh_video
->bih
->biHeight
=hdr
[14]|(hdr
[15]<<8);
235 memcpy(&sh_video
->bih
->biCompression
,hdr
+4,4);
236 sh_video
->bih
->biSizeImage
=sh_video
->bih
->biWidth
*sh_video
->bih
->biHeight
*3;
238 // here we search for the correct keyframe
239 // vp6 keyframe is when the 2nd byte of the vp6 header is
240 // 0x36 for VP61 and 0x46 for VP62
241 if((priv
->v_format
==mmioFOURCC('V','P','6','1')) ||
242 (priv
->v_format
==mmioFOURCC('V','P','6','2')) ||
243 (priv
->v_format
==mmioFOURCC('V','P','3','1'))) {
244 stream_read(demuxer
->stream
,buf
,10);
245 if (((((priv
->v_format
>>16) & 0xff) == '6') && ((buf
[8]&0x0e)!=0x06)) ||
246 ((((priv
->v_format
>>16) & 0xff) == '3') && (buf
[8]!=0x00 || buf
[9]!=0x08))) {
247 mp_msg(MSGT_DEMUX
,MSGL_V
,"demux_nsv: searching %.4s keyframe...\n", (char*)&priv
->v_format
);
248 while(((((priv
->v_format
>>16) & 0xff) == '6') && ((buf
[8]&0x0e)!=0x06)) ||
249 ((((priv
->v_format
>>16) & 0xff) == '3') && (buf
[8]!=0x00 || buf
[9]!=0x08))){
250 mp_msg(MSGT_DEMUX
,MSGL_DBG2
,"demux_nsv: %.4s block skip.\n", (char*)&priv
->v_format
);
251 videolen
=(buf
[2]>>4)|(buf
[3]<<4)|(buf
[4]<<0xC);
252 audiolen
=(buf
[5])|(buf
[6]<<8);
253 stream_skip(demuxer
->stream
, videolen
+audiolen
-3);
254 stream_read(demuxer
->stream
,buf
,10);
255 if(stream_eof(demuxer
->stream
)) return 0;
257 mp_msg(MSGT_DEMUX
,MSGL_DBG2
,"demux_nsv: Got NSVs block.\n");
258 stream_skip(demuxer
->stream
,7);
259 stream_read(demuxer
->stream
,buf
,10);
264 // data starts 10 bytes before current pos but later
265 // we seek 17 backwards
266 stream_skip(demuxer
->stream
,7);
274 sh_video
->fps
=(float)30000.0/1001.0;
280 sh_video
->fps
=(float)24000.0/1001.0;
283 sh_video
->fps
=(float)15000.0/1001.0;
286 sh_video
->fps
=(float)10000.0/1001.0;
289 sh_video
->fps
= (float)priv
->fps
;
291 sh_video
->frametime
= (float)1.0 / (float)sh_video
->fps
;
295 // seek to start of NSV header
296 stream_seek(demuxer
->stream
,stream_tell(demuxer
->stream
)-17);
301 static int nsv_check_file ( demuxer_t
* demuxer
)
306 mp_msg ( MSGT_DEMUX
, MSGL_V
, "Checking for Nullsoft Streaming Video\n" );
308 for (i
= 0; i
< HEADER_SEARCH_SIZE
; i
++) {
309 uint8_t c
= stream_read_char(demuxer
->stream
);
310 if (stream_eof(demuxer
->stream
))
312 if (hdr
== mmioFOURCC('s', 'V', 'S', 'N') ||
313 (hdr
== mmioFOURCC('f', 'V', 'S', 'N') && !c
)) {
314 stream_seek(demuxer
->stream
,stream_tell(demuxer
->stream
)-5);
315 return DEMUXER_TYPE_NSV
;
317 hdr
= (hdr
<< 8) | c
;
323 static void demux_close_nsv(demuxer_t
* demuxer
) {
324 nsv_priv_t
* priv
= demuxer
->priv
;
331 const demuxer_desc_t demuxer_desc_nsv
= {
332 "NullsoftVideo demuxer",
334 "Nullsoft Streaming Video",
336 "nsv and nsa streaming files",
338 0, // safe but expensive autodetect
340 demux_nsv_fill_buffer
,