3 * Nullsoft Streaming Video demuxer
5 * by Reza Jelveh <reza.jelveh@tuhh.de>
6 * seeking and PCM audio not yet supported
7 * PCM needs extra audio chunk "miniheader" parsing
8 * Based on A'rpis G2 work
19 #include "stream/stream.h"
26 unsigned int a_format
;
27 unsigned int v_format
;
31 #define HEADER_SEARCH_SIZE 256000
35 * Seeking still to be implemented
37 static void demux_seek_nsv ( demuxer_t
*demuxer
, float rel_seek_secs
, float audio_delay
, int flags
)
39 // seeking is not yet implemented
43 static int demux_nsv_fill_buffer ( demuxer_t
*demuxer
, demux_stream_t
*ds
)
45 unsigned char hdr
[17];
49 // videolen = audio chunk length, audiolen = video chunk length
50 int videolen
,audiolen
;
52 sh_video_t
*sh_video
= demuxer
->video
->sh
;
53 sh_audio_t
*sh_audio
= demuxer
->audio
->sh
;
55 nsv_priv_t
* priv
= demuxer
->priv
;
57 // if the audio/video chunk has no new header the first 2 bytes will be discarded 0xBEEF
58 // or rather 0xEF 0xBE
59 stream_read(demuxer
->stream
,hdr
,7);
60 if(stream_eof(demuxer
->stream
)) return 0;
61 // sometimes instead of 0xBEEF as described for the next audio/video chunk we get
64 mp_dbg(MSGT_DEMUX
,MSGL_DBG2
,"demux_nsv: %08X %08X\n",hdr
[0]<<8|hdr
[1],stream_tell(demuxer
->stream
));
65 switch(hdr
[0]<<8|hdr
[1]) {
67 if(hdr
[2]==0x56 && hdr
[3]==0x73){
69 // get the header since there is no more metaheader after the first one
70 // there is no more need to skip that
71 stream_read(demuxer
->stream
,hdr
+7,17-7);
72 stream_read(demuxer
->stream
,hdr
,7);
80 mp_dbg(MSGT_DEMUX
,MSGL_WARN
,"demux_nsv: sync lost\n");
85 sh_video
->pts
= priv
->v_pts
=demuxer
->video
->pts
= priv
->video_pack_no
*
86 (float)sh_video
->frametime
;
88 priv
->v_pts
= priv
->video_pack_no
;
90 demuxer
->filepos
=stream_tell(demuxer
->stream
);
93 mp_dbg(MSGT_DEMUX
,MSGL_DBG2
,"demux_nsv: %08X: %02X %02X | %02X %02X %02X | %02X %02X \n",
94 (int)demuxer
->filepos
, hdr
[0],hdr
[1],hdr
[2],hdr
[3],hdr
[4],hdr
[5],hdr
[6]);
97 videolen
=(hdr
[2]>>4)|(hdr
[3]<<4)|(hdr
[4]<<0xC);
98 //check if we got extra data like subtitles here
99 if( (hdr
[2]&0x0f) != 0x0 ) {
100 stream_read( demuxer
->stream
, aux
, 6);
102 i_aux
= aux
[0]|aux
[1]<<8;
103 // We skip this extra data
104 stream_skip( demuxer
->stream
, i_aux
);
110 // we need to return an empty packet when the
111 // video frame is empty otherwise the stream will fasten up
113 if( (hdr
[2]&0x0f) != 0x0 )
114 ds_read_packet(demuxer
->video
,demuxer
->stream
,videolen
,priv
->v_pts
,demuxer
->filepos
-i_aux
,0);
116 ds_read_packet(demuxer
->video
,demuxer
->stream
,videolen
,priv
->v_pts
,demuxer
->filepos
,0);
119 stream_skip(demuxer
->stream
,videolen
);
122 audiolen
=(hdr
[5])|(hdr
[6]<<8);
123 // we need to return an empty packet when the
124 // audio frame is empty otherwise the stream will fasten up
126 ds_read_packet(demuxer
->audio
,demuxer
->stream
,audiolen
,priv
->v_pts
,demuxer
->filepos
+videolen
,0);
129 stream_skip(demuxer
->stream
,audiolen
);
131 ++priv
->video_pack_no
;
138 static demuxer_t
* demux_open_nsv ( demuxer_t
* demuxer
)
140 // last 2 bytes 17 and 18 are unknown but right after that comes the length
141 unsigned char hdr
[17];
142 int videolen
,audiolen
;
143 unsigned char buf
[10];
144 sh_video_t
*sh_video
= NULL
;
145 sh_audio_t
*sh_audio
= NULL
;
148 nsv_priv_t
* priv
= malloc(sizeof(nsv_priv_t
));
150 priv
->video_pack_no
=0;
152 /* disable seeking yet to be fixed*/
153 demuxer
->seekable
= 0;
155 stream_read(demuxer
->stream
,hdr
,4);
156 if(stream_eof(demuxer
->stream
)) return 0;
158 if(hdr
[0]==0x4E && hdr
[1]==0x53 && hdr
[2]==0x56){
162 stream_read(demuxer
->stream
,hdr
+4,17-4);
167 int len
=stream_read_dword_le(demuxer
->stream
);
168 // TODO: parse out metadata!!!!
169 stream_skip(demuxer
->stream
,len
-8);
172 stream_read(demuxer
->stream
,hdr
,17);
173 if (stream_eof(demuxer
->stream
) || strncmp(hdr
, "NSVs", 4))
177 // dummy debug message
178 mp_msg(MSGT_DEMUX
,MSGL_V
,"demux_nsv: Header: %.12s\n",hdr
);
180 // bytes 8-11 audio codec fourcc
181 // PCM fourcc needs extra parsing for every audio chunk, yet to implement
182 if((demuxer
->audio
->id
!= -2) && strncmp(hdr
+8,"NONE", 4)){//&&strncmp(hdr+8,"VLB ", 4)){
183 sh_audio
= new_sh_audio ( demuxer
, 0 );
184 demuxer
->audio
->sh
= sh_audio
;
185 sh_audio
->format
=mmioFOURCC(hdr
[8],hdr
[9],hdr
[10],hdr
[11]);
186 sh_audio
->ds
= demuxer
->audio
;
187 priv
->a_format
=mmioFOURCC(hdr
[8],hdr
[9],hdr
[10],hdr
[11]);
193 if ((demuxer
->video
->id
!= -2) && strncmp(hdr
+4,"NONE", 4)) {
194 /* Create a new video stream header */
195 sh_video
= new_sh_video ( demuxer
, 0 );
197 /* Make sure the demuxer knows about the new video stream header
198 * (even though new_sh_video() ought to take care of it)
200 demuxer
->video
->sh
= sh_video
;
202 /* Make sure that the video demuxer stream header knows about its
203 * parent video demuxer stream (this is getting wacky), or else
204 * video_read_properties() will choke
206 sh_video
->ds
= demuxer
->video
;
208 // bytes 4-7 video codec fourcc
209 priv
->v_format
= sh_video
->format
=mmioFOURCC(hdr
[4],hdr
[5],hdr
[6],hdr
[7]);
211 // new video stream! parse header
212 sh_video
->disp_w
=hdr
[12]|(hdr
[13]<<8);
213 sh_video
->disp_h
=hdr
[14]|(hdr
[15]<<8);
214 sh_video
->bih
=calloc(1,sizeof(BITMAPINFOHEADER
));
215 sh_video
->bih
->biSize
=sizeof(BITMAPINFOHEADER
);
216 sh_video
->bih
->biPlanes
=1;
217 sh_video
->bih
->biBitCount
=24;
218 sh_video
->bih
->biWidth
=hdr
[12]|(hdr
[13]<<8);
219 sh_video
->bih
->biHeight
=hdr
[14]|(hdr
[15]<<8);
220 memcpy(&sh_video
->bih
->biCompression
,hdr
+4,4);
221 sh_video
->bih
->biSizeImage
=sh_video
->bih
->biWidth
*sh_video
->bih
->biHeight
*3;
223 // here we search for the correct keyframe
224 // vp6 keyframe is when the 2nd byte of the vp6 header is
225 // 0x36 for VP61 and 0x46 for VP62
226 if((priv
->v_format
==mmioFOURCC('V','P','6','1')) ||
227 (priv
->v_format
==mmioFOURCC('V','P','6','2')) ||
228 (priv
->v_format
==mmioFOURCC('V','P','3','1'))) {
229 stream_read(demuxer
->stream
,buf
,10);
230 if (((((priv
->v_format
>>16) & 0xff) == '6') && ((buf
[8]&0x0e)!=0x06)) ||
231 ((((priv
->v_format
>>16) & 0xff) == '3') && (buf
[8]!=0x00 || buf
[9]!=0x08))) {
232 mp_msg(MSGT_DEMUX
,MSGL_V
,"demux_nsv: searching %.4s keyframe...\n", (char*)&priv
->v_format
);
233 while(((((priv
->v_format
>>16) & 0xff) == '6') && ((buf
[8]&0x0e)!=0x06)) ||
234 ((((priv
->v_format
>>16) & 0xff) == '3') && (buf
[8]!=0x00 || buf
[9]!=0x08))){
235 mp_msg(MSGT_DEMUX
,MSGL_DBG2
,"demux_nsv: %.4s block skip.\n", (char*)&priv
->v_format
);
236 videolen
=(buf
[2]>>4)|(buf
[3]<<4)|(buf
[4]<<0xC);
237 audiolen
=(buf
[5])|(buf
[6]<<8);
238 stream_skip(demuxer
->stream
, videolen
+audiolen
-3);
239 stream_read(demuxer
->stream
,buf
,10);
240 if(stream_eof(demuxer
->stream
)) return 0;
242 mp_msg(MSGT_DEMUX
,MSGL_DBG2
,"demux_nsv: Got NSVs block.\n");
243 stream_skip(demuxer
->stream
,7);
244 stream_read(demuxer
->stream
,buf
,10);
249 // data starts 10 bytes before current pos but later
250 // we seek 17 backwards
251 stream_skip(demuxer
->stream
,7);
259 sh_video
->fps
=(float)30000.0/1001.0;
265 sh_video
->fps
=(float)24000.0/1001.0;
268 sh_video
->fps
=(float)15000.0/1001.0;
271 sh_video
->fps
=(float)10000.0/1001.0;
274 sh_video
->fps
= (float)priv
->fps
;
276 sh_video
->frametime
= (float)1.0 / (float)sh_video
->fps
;
280 // seek to start of NSV header
281 stream_seek(demuxer
->stream
,stream_tell(demuxer
->stream
)-17);
286 static int nsv_check_file ( demuxer_t
* demuxer
)
291 mp_msg ( MSGT_DEMUX
, MSGL_V
, "Checking for Nullsoft Streaming Video\n" );
293 for (i
= 0; i
< HEADER_SEARCH_SIZE
; i
++) {
294 uint8_t c
= stream_read_char(demuxer
->stream
);
295 if (stream_eof(demuxer
->stream
))
297 if (hdr
== mmioFOURCC('s', 'V', 'S', 'N') ||
298 (hdr
== mmioFOURCC('f', 'V', 'S', 'N') && !c
)) {
299 stream_seek(demuxer
->stream
,stream_tell(demuxer
->stream
)-5);
300 return DEMUXER_TYPE_NSV
;
302 hdr
= (hdr
<< 8) | c
;
308 static void demux_close_nsv(demuxer_t
* demuxer
) {
309 nsv_priv_t
* priv
= demuxer
->priv
;
318 const demuxer_desc_t demuxer_desc_nsv
= {
319 "NullsoftVideo demuxer",
321 "Nullsoft Streaming Video",
323 "nsv and nsa streaming files",
325 0, // safe but expensive autodetect
327 demux_nsv_fill_buffer
,