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",hdr
[0]<<8|hdr
[1],stream_tell(demuxer
->stream
));
78 switch(hdr
[0]<<8|hdr
[1]) {
80 if(hdr
[2]==0x56 && hdr
[3]==0x73){
82 // get the header since there is no more metaheader after the first one
83 // there is no more need to skip that
84 stream_read(demuxer
->stream
,hdr
+7,17-7);
85 stream_read(demuxer
->stream
,hdr
,7);
93 mp_dbg(MSGT_DEMUX
,MSGL_WARN
,"demux_nsv: sync lost\n");
98 sh_video
->pts
= priv
->v_pts
=demuxer
->video
->pts
= priv
->video_pack_no
*
99 (float)sh_video
->frametime
;
101 priv
->v_pts
= priv
->video_pack_no
;
103 demuxer
->filepos
=stream_tell(demuxer
->stream
);
106 mp_dbg(MSGT_DEMUX
,MSGL_DBG2
,"demux_nsv: %08X: %02X %02X | %02X %02X %02X | %02X %02X \n",
107 (int)demuxer
->filepos
, hdr
[0],hdr
[1],hdr
[2],hdr
[3],hdr
[4],hdr
[5],hdr
[6]);
110 videolen
=(hdr
[2]>>4)|(hdr
[3]<<4)|(hdr
[4]<<0xC);
111 //check if we got extra data like subtitles here
112 if( (hdr
[2]&0x0f) != 0x0 ) {
113 stream_read( demuxer
->stream
, aux
, 6);
115 i_aux
= aux
[0]|aux
[1]<<8;
116 // We skip this extra data
117 stream_skip( demuxer
->stream
, i_aux
);
123 // we need to return an empty packet when the
124 // video frame is empty otherwise the stream will fasten up
126 if( (hdr
[2]&0x0f) != 0x0 )
127 ds_read_packet(demuxer
->video
,demuxer
->stream
,videolen
,priv
->v_pts
,demuxer
->filepos
-i_aux
,0);
129 ds_read_packet(demuxer
->video
,demuxer
->stream
,videolen
,priv
->v_pts
,demuxer
->filepos
,0);
132 stream_skip(demuxer
->stream
,videolen
);
135 audiolen
=(hdr
[5])|(hdr
[6]<<8);
136 // we need to return an empty packet when the
137 // audio frame is empty otherwise the stream will fasten up
139 ds_read_packet(demuxer
->audio
,demuxer
->stream
,audiolen
,priv
->v_pts
,demuxer
->filepos
+videolen
,0);
142 stream_skip(demuxer
->stream
,audiolen
);
144 ++priv
->video_pack_no
;
151 static demuxer_t
* demux_open_nsv ( demuxer_t
* demuxer
)
153 // last 2 bytes 17 and 18 are unknown but right after that comes the length
154 unsigned char hdr
[17];
155 int videolen
,audiolen
;
156 unsigned char buf
[10];
157 sh_video_t
*sh_video
= NULL
;
158 sh_audio_t
*sh_audio
= NULL
;
161 nsv_priv_t
* priv
= malloc(sizeof(nsv_priv_t
));
163 priv
->video_pack_no
=0;
165 /* disable seeking yet to be fixed*/
166 demuxer
->seekable
= 0;
168 stream_read(demuxer
->stream
,hdr
,4);
169 if(stream_eof(demuxer
->stream
)) return 0;
171 if(hdr
[0]==0x4E && hdr
[1]==0x53 && hdr
[2]==0x56){
175 stream_read(demuxer
->stream
,hdr
+4,17-4);
180 int len
=stream_read_dword_le(demuxer
->stream
);
181 // TODO: parse out metadata!!!!
182 stream_skip(demuxer
->stream
,len
-8);
185 stream_read(demuxer
->stream
,hdr
,17);
186 if (stream_eof(demuxer
->stream
) || strncmp(hdr
, "NSVs", 4))
190 // dummy debug message
191 mp_msg(MSGT_DEMUX
,MSGL_V
,"demux_nsv: Header: %.12s\n",hdr
);
193 // bytes 8-11 audio codec fourcc
194 // PCM fourcc needs extra parsing for every audio chunk, yet to implement
195 if((demuxer
->audio
->id
!= -2) && strncmp(hdr
+8,"NONE", 4)){//&&strncmp(hdr+8,"VLB ", 4)){
196 sh_audio
= new_sh_audio ( demuxer
, 0 );
197 demuxer
->audio
->id
= 0;
198 demuxer
->audio
->sh
= sh_audio
;
199 sh_audio
->format
=mmioFOURCC(hdr
[8],hdr
[9],hdr
[10],hdr
[11]);
200 sh_audio
->ds
= demuxer
->audio
;
201 priv
->a_format
=mmioFOURCC(hdr
[8],hdr
[9],hdr
[10],hdr
[11]);
207 if ((demuxer
->video
->id
!= -2) && strncmp(hdr
+4,"NONE", 4)) {
208 /* Create a new video stream header */
209 sh_video
= new_sh_video ( demuxer
, 0 );
211 /* Make sure the demuxer knows about the new video stream header
212 * (even though new_sh_video() ought to take care of it)
214 demuxer
->video
->sh
= sh_video
;
216 /* Make sure that the video demuxer stream header knows about its
217 * parent video demuxer stream (this is getting wacky), or else
218 * video_read_properties() will choke
220 sh_video
->ds
= demuxer
->video
;
222 // bytes 4-7 video codec fourcc
223 priv
->v_format
= sh_video
->format
=mmioFOURCC(hdr
[4],hdr
[5],hdr
[6],hdr
[7]);
225 // new video stream! parse header
226 sh_video
->disp_w
=hdr
[12]|(hdr
[13]<<8);
227 sh_video
->disp_h
=hdr
[14]|(hdr
[15]<<8);
228 sh_video
->bih
=calloc(1,sizeof(*sh_video
->bih
));
229 sh_video
->bih
->biSize
=sizeof(*sh_video
->bih
);
230 sh_video
->bih
->biPlanes
=1;
231 sh_video
->bih
->biBitCount
=24;
232 sh_video
->bih
->biWidth
=hdr
[12]|(hdr
[13]<<8);
233 sh_video
->bih
->biHeight
=hdr
[14]|(hdr
[15]<<8);
234 memcpy(&sh_video
->bih
->biCompression
,hdr
+4,4);
235 sh_video
->bih
->biSizeImage
=sh_video
->bih
->biWidth
*sh_video
->bih
->biHeight
*3;
237 // here we search for the correct keyframe
238 // vp6 keyframe is when the 2nd byte of the vp6 header is
239 // 0x36 for VP61 and 0x46 for VP62
240 if((priv
->v_format
==mmioFOURCC('V','P','6','1')) ||
241 (priv
->v_format
==mmioFOURCC('V','P','6','2')) ||
242 (priv
->v_format
==mmioFOURCC('V','P','3','1'))) {
243 stream_read(demuxer
->stream
,buf
,10);
244 if (((((priv
->v_format
>>16) & 0xff) == '6') && ((buf
[8]&0x0e)!=0x06)) ||
245 ((((priv
->v_format
>>16) & 0xff) == '3') && (buf
[8]!=0x00 || buf
[9]!=0x08))) {
246 mp_msg(MSGT_DEMUX
,MSGL_V
,"demux_nsv: searching %.4s keyframe...\n", (char*)&priv
->v_format
);
247 while(((((priv
->v_format
>>16) & 0xff) == '6') && ((buf
[8]&0x0e)!=0x06)) ||
248 ((((priv
->v_format
>>16) & 0xff) == '3') && (buf
[8]!=0x00 || buf
[9]!=0x08))){
249 mp_msg(MSGT_DEMUX
,MSGL_DBG2
,"demux_nsv: %.4s block skip.\n", (char*)&priv
->v_format
);
250 videolen
=(buf
[2]>>4)|(buf
[3]<<4)|(buf
[4]<<0xC);
251 audiolen
=(buf
[5])|(buf
[6]<<8);
252 stream_skip(demuxer
->stream
, videolen
+audiolen
-3);
253 stream_read(demuxer
->stream
,buf
,10);
254 if(stream_eof(demuxer
->stream
)) return 0;
256 mp_msg(MSGT_DEMUX
,MSGL_DBG2
,"demux_nsv: Got NSVs block.\n");
257 stream_skip(demuxer
->stream
,7);
258 stream_read(demuxer
->stream
,buf
,10);
263 // data starts 10 bytes before current pos but later
264 // we seek 17 backwards
265 stream_skip(demuxer
->stream
,7);
273 sh_video
->fps
=(float)30000.0/1001.0;
279 sh_video
->fps
=(float)24000.0/1001.0;
282 sh_video
->fps
=(float)15000.0/1001.0;
285 sh_video
->fps
=(float)10000.0/1001.0;
288 sh_video
->fps
= (float)priv
->fps
;
290 sh_video
->frametime
= (float)1.0 / (float)sh_video
->fps
;
294 // seek to start of NSV header
295 stream_seek(demuxer
->stream
,stream_tell(demuxer
->stream
)-17);
300 static int nsv_check_file ( demuxer_t
* demuxer
)
305 mp_msg ( MSGT_DEMUX
, MSGL_V
, "Checking for Nullsoft Streaming Video\n" );
307 for (i
= 0; i
< HEADER_SEARCH_SIZE
; i
++) {
308 uint8_t c
= stream_read_char(demuxer
->stream
);
309 if (stream_eof(demuxer
->stream
))
311 if (hdr
== mmioFOURCC('s', 'V', 'S', 'N') ||
312 (hdr
== mmioFOURCC('f', 'V', 'S', 'N') && !c
)) {
313 stream_seek(demuxer
->stream
,stream_tell(demuxer
->stream
)-5);
314 return DEMUXER_TYPE_NSV
;
316 hdr
= (hdr
<< 8) | c
;
322 static void demux_close_nsv(demuxer_t
* demuxer
) {
323 nsv_priv_t
* priv
= demuxer
->priv
;
332 const demuxer_desc_t demuxer_desc_nsv
= {
333 "NullsoftVideo demuxer",
335 "Nullsoft Streaming Video",
337 "nsv and nsa streaming files",
339 0, // safe but expensive autodetect
341 demux_nsv_fill_buffer
,