Mercurial > mplayer.hg
annotate libmpdemux/demux_nsv.c @ 15478:868fa16142df
ftp is handled by the modular stream manager
author | nicodvb |
---|---|
date | Sun, 15 May 2005 08:47:55 +0000 |
parents | 6f8fe531dd73 |
children | 3042ee91c7dd |
rev | line source |
---|---|
12175 | 1 |
2 /* | |
3 * Nullsoft Streaming Video demuxer | |
4 * for MPlayer | |
5 * by Reza Jelveh <reza.jelveh@tuhh.de> | |
6 * seeking and PCM audio not yet supported | |
7 * PCM needs extra audio chunk "miniheader" parsing | |
12181
12ddc64011fe
Add license, remove bogus comment as told by Reza Jelveh, the author.
diego
parents:
12179
diff
changeset
|
8 * Based on A'rpis G2 work |
12ddc64011fe
Add license, remove bogus comment as told by Reza Jelveh, the author.
diego
parents:
12179
diff
changeset
|
9 * Licence: GPL |
12175 | 10 */ |
11 | |
12 #include <stdio.h> | |
13 #include <stdlib.h> | |
14 #include <unistd.h> | |
15 | |
16 #include "config.h" | |
17 #include "mp_msg.h" | |
18 #include "help_mp.h" | |
19 #include "stream.h" | |
20 #include "demuxer.h" | |
21 #include "stheader.h" | |
22 | |
23 typedef struct { | |
24 float v_pts; | |
25 int video_pack_no; | |
26 unsigned int a_format; | |
27 unsigned int v_format; | |
28 unsigned char fps; | |
29 } nsv_priv_t; | |
30 | |
31 /** | |
32 * Seeking still to be implemented | |
33 */ | |
34 void demux_seek_nsv ( demuxer_t *demuxer, float rel_seek_secs, int flags ) | |
35 { | |
36 // seeking is not yet implemented | |
37 } | |
38 | |
39 | |
40 int demux_nsv_fill_buffer ( demuxer_t *demuxer ) | |
41 { | |
42 unsigned char hdr[17]; | |
43 // for the extra data | |
44 unsigned char aux[6]; | |
45 int i_aux = 0; | |
46 // videolen = audio chunk length, audiolen = video chunk length | |
47 int videolen,audiolen; | |
48 | |
12272 | 49 sh_video_t *sh_video = demuxer->video->sh; |
50 sh_audio_t *sh_audio = demuxer->audio->sh; | |
12175 | 51 |
52 nsv_priv_t * priv = demuxer->priv; | |
53 | |
54 // if the audio/video chunk has no new header the first 2 bytes will be discarded 0xBEEF | |
55 // or rather 0xEF 0xBE | |
56 stream_read(demuxer->stream,hdr,7); | |
57 if(stream_eof(demuxer->stream)) return 0; | |
58 // sometimes instead of 0xBEEF as described for the next audio/video chunk we get | |
59 // a whole new header | |
60 | |
61 mp_dbg(MSGT_DEMUX,MSGL_DBG2,"demux_nsv: %08X %08X\n",hdr[0]<<8|hdr[1],stream_tell(demuxer->stream)); | |
62 switch(hdr[0]<<8|hdr[1]) { | |
63 case 0x4E53: | |
64 if(hdr[2]==0x56 && hdr[3]==0x73){ | |
65 // NSVs | |
66 // get the header since there is no more metaheader after the first one | |
67 // there is no more need to skip that | |
68 stream_read(demuxer->stream,hdr+7,17-7); | |
69 stream_read(demuxer->stream,hdr,7); | |
70 } | |
71 break; | |
72 | |
73 case 0xEFBE: | |
74 break; | |
75 | |
76 default: | |
12272 | 77 mp_dbg(MSGT_DEMUX,MSGL_WARN,"demux_nsv: sync lost\n"); |
12175 | 78 break; |
79 } | |
80 | |
12272 | 81 if (sh_video) |
12175 | 82 sh_video->pts = priv->v_pts =demuxer->video->pts= priv->video_pack_no * |
83 (float)sh_video->frametime; | |
12272 | 84 else |
85 priv->v_pts = priv->video_pack_no; | |
12175 | 86 |
87 demuxer->filepos=stream_tell(demuxer->stream); | |
88 | |
89 | |
90 mp_dbg(MSGT_DEMUX,MSGL_DBG2,"demux_nsv: %08X: %02X %02X | %02X %02X %02X | %02X %02X \n", | |
91 (int)demuxer->filepos, hdr[0],hdr[1],hdr[2],hdr[3],hdr[4],hdr[5],hdr[6]); | |
92 | |
93 // read video: | |
94 videolen=(hdr[2]>>4)|(hdr[3]<<4)|(hdr[4]<<0xC); | |
95 //check if we got extra data like subtitles here | |
96 if( (hdr[2]&0x0f) != 0x0 ) { | |
97 stream_read( demuxer->stream, aux, 6); | |
98 | |
99 i_aux = aux[0]|aux[1]<<8; | |
100 // We skip this extra data | |
101 stream_skip( demuxer->stream, i_aux ); | |
102 i_aux+=6; | |
103 videolen -= i_aux; | |
104 } | |
105 | |
106 | |
107 // we need to return an empty packet when the | |
108 // video frame is empty otherwise the stream will fasten up | |
12272 | 109 if(sh_video) { |
12175 | 110 if( (hdr[2]&0x0f) != 0x0 ) |
111 ds_read_packet(demuxer->video,demuxer->stream,videolen,priv->v_pts,demuxer->filepos-i_aux,0); | |
112 else | |
113 ds_read_packet(demuxer->video,demuxer->stream,videolen,priv->v_pts,demuxer->filepos,0); | |
114 } | |
115 else | |
116 stream_skip(demuxer->stream,videolen); | |
117 | |
118 // read audio: | |
119 audiolen=(hdr[5])|(hdr[6]<<8); | |
120 // we need to return an empty packet when the | |
121 // audio frame is empty otherwise the stream will fasten up | |
12272 | 122 if(sh_audio) { |
12175 | 123 ds_read_packet(demuxer->audio,demuxer->stream,audiolen,priv->v_pts,demuxer->filepos+videolen,0); |
124 } | |
125 else | |
126 stream_skip(demuxer->stream,audiolen); | |
127 | |
128 ++priv->video_pack_no; | |
129 | |
130 return 1; | |
131 | |
132 } | |
133 | |
134 | |
135 demuxer_t* demux_open_nsv ( demuxer_t* demuxer ) | |
136 { | |
137 // last 2 bytes 17 and 18 are unknown but right after that comes the length | |
138 unsigned char hdr[17]; | |
139 int videolen,audiolen; | |
12272 | 140 unsigned char buf[10]; |
12175 | 141 sh_video_t *sh_video = NULL; |
142 sh_audio_t *sh_audio = NULL; | |
143 | |
144 | |
145 nsv_priv_t * priv = malloc(sizeof(nsv_priv_t)); | |
146 demuxer->priv=priv; | |
147 priv->video_pack_no=0; | |
148 | |
149 /* disable seeking yet to be fixed*/ | |
150 demuxer->seekable = 0; | |
151 | |
152 stream_read(demuxer->stream,hdr,4); | |
153 if(stream_eof(demuxer->stream)) return 0; | |
154 | |
155 /*** if we detected the file to be nsv and there was neither eof nor a header | |
156 **** that means that its most likely a shoutcast stream so we will need to seek | |
157 **** to the first occurance of the NSVs header ****/ | |
158 if(!(hdr[0]==0x4E && hdr[1]==0x53 && hdr[2]==0x56)){ | |
159 // todo: replace this with a decent string search algo | |
160 while(1){ | |
161 stream_read(demuxer->stream,hdr,1); | |
162 if(stream_eof(demuxer->stream)) | |
163 return 0; | |
164 if(hdr[0]!=0x4E) | |
165 continue; | |
166 | |
167 stream_read(demuxer->stream,hdr+1,1); | |
168 | |
169 if(stream_eof(demuxer->stream)) | |
170 return 0; | |
171 if(hdr[1]!=0x53) | |
172 continue; | |
173 | |
174 stream_read(demuxer->stream,hdr+2,1); | |
175 | |
176 if(stream_eof(demuxer->stream)) | |
177 return 0; | |
178 if(hdr[2]!=0x56) | |
179 continue; | |
180 | |
181 stream_read(demuxer->stream,hdr+3,1); | |
182 | |
183 if(stream_eof(demuxer->stream)) | |
184 return 0; | |
185 if(hdr[3]!=0x73) | |
186 continue; | |
187 | |
188 break; | |
189 } | |
190 } | |
191 if(hdr[0]==0x4E && hdr[1]==0x53 && hdr[2]==0x56){ | |
192 // NSV header! | |
193 if(hdr[3]==0x73){ | |
194 // NSVs | |
195 stream_read(demuxer->stream,hdr+4,17-4); | |
196 } | |
197 | |
198 if(hdr[3]==0x66){ | |
199 // NSVf | |
200 int len=stream_read_dword_le(demuxer->stream); | |
201 // TODO: parse out metadata!!!! | |
202 stream_skip(demuxer->stream,len-8); | |
203 | |
204 // NSVs | |
205 stream_read(demuxer->stream,hdr,17); | |
206 } | |
207 | |
208 // dummy debug message | |
209 mp_msg(MSGT_DEMUX,MSGL_V,"demux_nsv: Header: %.12s\n",hdr); | |
210 | |
211 // bytes 8-11 audio codec fourcc | |
212 // PCM fourcc needs extra parsing for every audio chunk, yet to implement | |
12896 | 213 if((demuxer->audio->id != -2) && strncmp(hdr+8,"NONE", 4)){//&&strncmp(hdr+8,"VLB ", 4)){ |
12175 | 214 sh_audio = new_sh_audio ( demuxer, 0 ); |
215 demuxer->audio->sh = sh_audio; | |
216 sh_audio->format=mmioFOURCC(hdr[8],hdr[9],hdr[10],hdr[11]); | |
217 sh_audio->ds = demuxer->audio; | |
12896 | 218 priv->a_format=mmioFOURCC(hdr[8],hdr[9],hdr[10],hdr[11]); |
12175 | 219 } |
220 | |
221 // store hdr fps | |
222 priv->fps=hdr[16]; | |
223 | |
12896 | 224 if ((demuxer->video->id != -2) && strncmp(hdr+4,"NONE", 4)) { |
12198 | 225 /* Create a new video stream header */ |
226 sh_video = new_sh_video ( demuxer, 0 ); | |
227 | |
228 /* Make sure the demuxer knows about the new video stream header | |
229 * (even though new_sh_video() ought to take care of it) | |
230 */ | |
231 demuxer->video->sh = sh_video; | |
232 | |
233 /* Make sure that the video demuxer stream header knows about its | |
234 * parent video demuxer stream (this is getting wacky), or else | |
235 * video_read_properties() will choke | |
236 */ | |
237 sh_video->ds = demuxer->video; | |
238 | |
12175 | 239 // bytes 4-7 video codec fourcc |
240 priv->v_format = sh_video->format=mmioFOURCC(hdr[4],hdr[5],hdr[6],hdr[7]); | |
241 | |
242 // new video stream! parse header | |
243 sh_video->disp_w=hdr[12]|(hdr[13]<<8); | |
244 sh_video->disp_h=hdr[14]|(hdr[15]<<8); | |
245 sh_video->bih=(BITMAPINFOHEADER*)calloc(1,sizeof(BITMAPINFOHEADER)); | |
246 sh_video->bih->biSize=sizeof(BITMAPINFOHEADER); | |
247 sh_video->bih->biPlanes=1; | |
248 sh_video->bih->biBitCount=24; | |
249 sh_video->bih->biWidth=hdr[12]|(hdr[13]<<8); | |
250 sh_video->bih->biHeight=hdr[14]|(hdr[15]<<8); | |
251 memcpy(&sh_video->bih->biCompression,hdr+4,4); | |
252 sh_video->bih->biSizeImage=sh_video->bih->biWidth*sh_video->bih->biHeight*3; | |
253 | |
254 // here we search for the correct keyframe | |
12275 | 255 // vp6 keyframe is when the 2nd byte of the vp6 header is |
256 // 0x36 for VP61 and 0x46 for VP62 | |
12272 | 257 if((priv->v_format==mmioFOURCC('V','P','6','1')) || |
258 (priv->v_format==mmioFOURCC('V','P','6','2')) || | |
259 (priv->v_format==mmioFOURCC('V','P','3','1'))) { | |
260 stream_read(demuxer->stream,buf,10); | |
12277 | 261 if (((((priv->v_format>>16) & 0xff) == '6') && ((buf[8]&0x0e)!=0x06)) || |
12272 | 262 ((((priv->v_format>>16) & 0xff) == '3') && (buf[8]!=0x00 || buf[9]!=0x08))) { |
263 mp_msg(MSGT_DEMUX,MSGL_V,"demux_nsv: searching %.4s keyframe...\n", (char*)&priv->v_format); | |
12277 | 264 while(((((priv->v_format>>16) & 0xff) == '6') && ((buf[8]&0x0e)!=0x06)) || |
12272 | 265 ((((priv->v_format>>16) & 0xff) == '3') && (buf[8]!=0x00 || buf[9]!=0x08))){ |
266 mp_msg(MSGT_DEMUX,MSGL_DBG2,"demux_nsv: %.4s block skip.\n", (char*)&priv->v_format); | |
12179
aeb377ccb110
Fix vp61 keyframe search (do not skip blocks if 1st frame is keyframe)
rtognimp
parents:
12175
diff
changeset
|
267 videolen=(buf[2]>>4)|(buf[3]<<4)|(buf[4]<<0xC); |
aeb377ccb110
Fix vp61 keyframe search (do not skip blocks if 1st frame is keyframe)
rtognimp
parents:
12175
diff
changeset
|
268 audiolen=(buf[5])|(buf[6]<<8); |
12272 | 269 stream_skip(demuxer->stream, videolen+audiolen-3); |
270 stream_read(demuxer->stream,buf,10); | |
12277 | 271 if(stream_eof(demuxer->stream)) return 0; |
12179
aeb377ccb110
Fix vp61 keyframe search (do not skip blocks if 1st frame is keyframe)
rtognimp
parents:
12175
diff
changeset
|
272 if(buf[0]==0x4E){ |
aeb377ccb110
Fix vp61 keyframe search (do not skip blocks if 1st frame is keyframe)
rtognimp
parents:
12175
diff
changeset
|
273 mp_msg(MSGT_DEMUX,MSGL_DBG2,"demux_nsv: Got NSVs block.\n"); |
12272 | 274 stream_skip(demuxer->stream,7); |
275 stream_read(demuxer->stream,buf,10); | |
12175 | 276 } |
277 } | |
278 } | |
279 | |
13299
6f8fe531dd73
avoid always skipping first junk with a "sync lost" message
reimar
parents:
12896
diff
changeset
|
280 // data starts 10 bytes before current pos but later |
6f8fe531dd73
avoid always skipping first junk with a "sync lost" message
reimar
parents:
12896
diff
changeset
|
281 // we seek 17 backwards |
6f8fe531dd73
avoid always skipping first junk with a "sync lost" message
reimar
parents:
12896
diff
changeset
|
282 stream_skip(demuxer->stream,7); |
12175 | 283 } |
284 | |
285 switch(priv->fps){ | |
286 case 0x80: | |
287 sh_video->fps=30; | |
288 break; | |
289 case 0x81: | |
290 sh_video->fps=(float)30000.0/1001.0; | |
291 break; | |
292 case 0x82: | |
293 sh_video->fps=25; | |
294 break; | |
295 case 0x83: | |
296 sh_video->fps=(float)24000.0/1001.0; | |
297 break; | |
298 case 0x85: | |
299 sh_video->fps=(float)15000.0/1001.0; | |
300 break; | |
301 default: | |
302 sh_video->fps = (float)priv->fps; | |
303 } | |
304 sh_video->frametime = (float)1.0 / (float)sh_video->fps; | |
12198 | 305 } |
12175 | 306 } |
307 | |
13299
6f8fe531dd73
avoid always skipping first junk with a "sync lost" message
reimar
parents:
12896
diff
changeset
|
308 // seek to start of NSV header |
6f8fe531dd73
avoid always skipping first junk with a "sync lost" message
reimar
parents:
12896
diff
changeset
|
309 stream_seek(demuxer->stream,stream_tell(demuxer->stream)-17); |
12175 | 310 |
311 return demuxer; | |
312 } | |
313 | |
314 int nsv_check_file ( demuxer_t* demuxer ) | |
315 { | |
316 unsigned int id; | |
317 | |
318 /* Store original position */ | |
319 // off_t orig_pos = stream_tell(demuxer->stream); | |
320 | |
321 mp_msg ( MSGT_DEMUX, MSGL_V, "Checking for Nullsoft Streaming Video\n" ); | |
322 | |
323 //---- check NSVx header: | |
324 id=stream_read_dword_le(demuxer->stream); | |
325 if(id!=mmioFOURCC('N','S','V','f') && id!=mmioFOURCC('N','S','V','s')) | |
326 return 0; // not an NSV file | |
327 | |
328 stream_reset(demuxer->stream); // clear EOF | |
329 stream_seek(demuxer->stream,demuxer->stream->start_pos); | |
330 | |
331 | |
332 return 1; | |
333 } | |
334 | |
335 void demux_close_nsv(demuxer_t* demuxer) { | |
336 nsv_priv_t* priv = demuxer->priv; | |
337 | |
338 if(!priv) | |
339 return; | |
340 free(priv); | |
341 | |
342 } |