Mercurial > mplayer.hg
annotate libmpdemux/demux_nsv.c @ 17892:2a37ab5a913e
1.226: Print the mute state on the OSD.
1.225: Fix up libaf unknown-format printing
1.224: last print on libaf to af_msg
1.223: [done by diego]
1.222: dwStart support for mencoder
author | kraymer |
---|---|
date | Sat, 18 Mar 2006 19:22:13 +0000 |
parents | b849a99cdc3c |
children | fdb789f1a99a |
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 | |
16318 | 31 #define HEADER_SEARCH_SIZE 65000 |
32 | |
33 | |
12175 | 34 /** |
35 * Seeking still to be implemented | |
36 */ | |
17636 | 37 static void demux_seek_nsv ( demuxer_t *demuxer, float rel_seek_secs, float audio_delay, int flags ) |
12175 | 38 { |
39 // seeking is not yet implemented | |
40 } | |
41 | |
42 | |
16175 | 43 static int demux_nsv_fill_buffer ( demuxer_t *demuxer, demux_stream_t *ds ) |
12175 | 44 { |
45 unsigned char hdr[17]; | |
46 // for the extra data | |
47 unsigned char aux[6]; | |
48 int i_aux = 0; | |
49 // videolen = audio chunk length, audiolen = video chunk length | |
50 int videolen,audiolen; | |
51 | |
12272 | 52 sh_video_t *sh_video = demuxer->video->sh; |
53 sh_audio_t *sh_audio = demuxer->audio->sh; | |
12175 | 54 |
55 nsv_priv_t * priv = demuxer->priv; | |
56 | |
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 | |
62 // a whole new header | |
63 | |
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]) { | |
66 case 0x4E53: | |
67 if(hdr[2]==0x56 && hdr[3]==0x73){ | |
68 // NSVs | |
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); | |
73 } | |
74 break; | |
75 | |
76 case 0xEFBE: | |
77 break; | |
78 | |
79 default: | |
12272 | 80 mp_dbg(MSGT_DEMUX,MSGL_WARN,"demux_nsv: sync lost\n"); |
12175 | 81 break; |
82 } | |
83 | |
12272 | 84 if (sh_video) |
12175 | 85 sh_video->pts = priv->v_pts =demuxer->video->pts= priv->video_pack_no * |
86 (float)sh_video->frametime; | |
12272 | 87 else |
88 priv->v_pts = priv->video_pack_no; | |
12175 | 89 |
90 demuxer->filepos=stream_tell(demuxer->stream); | |
91 | |
92 | |
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]); | |
95 | |
96 // read video: | |
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); | |
101 | |
102 i_aux = aux[0]|aux[1]<<8; | |
103 // We skip this extra data | |
104 stream_skip( demuxer->stream, i_aux ); | |
105 i_aux+=6; | |
106 videolen -= i_aux; | |
107 } | |
108 | |
109 | |
110 // we need to return an empty packet when the | |
111 // video frame is empty otherwise the stream will fasten up | |
12272 | 112 if(sh_video) { |
12175 | 113 if( (hdr[2]&0x0f) != 0x0 ) |
114 ds_read_packet(demuxer->video,demuxer->stream,videolen,priv->v_pts,demuxer->filepos-i_aux,0); | |
115 else | |
116 ds_read_packet(demuxer->video,demuxer->stream,videolen,priv->v_pts,demuxer->filepos,0); | |
117 } | |
118 else | |
119 stream_skip(demuxer->stream,videolen); | |
120 | |
121 // read audio: | |
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 | |
12272 | 125 if(sh_audio) { |
12175 | 126 ds_read_packet(demuxer->audio,demuxer->stream,audiolen,priv->v_pts,demuxer->filepos+videolen,0); |
127 } | |
128 else | |
129 stream_skip(demuxer->stream,audiolen); | |
130 | |
131 ++priv->video_pack_no; | |
132 | |
133 return 1; | |
134 | |
135 } | |
136 | |
137 | |
16175 | 138 static demuxer_t* demux_open_nsv ( demuxer_t* demuxer ) |
12175 | 139 { |
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; | |
12272 | 143 unsigned char buf[10]; |
12175 | 144 sh_video_t *sh_video = NULL; |
145 sh_audio_t *sh_audio = NULL; | |
146 | |
147 | |
148 nsv_priv_t * priv = malloc(sizeof(nsv_priv_t)); | |
149 demuxer->priv=priv; | |
150 priv->video_pack_no=0; | |
151 | |
152 /* disable seeking yet to be fixed*/ | |
153 demuxer->seekable = 0; | |
154 | |
155 stream_read(demuxer->stream,hdr,4); | |
156 if(stream_eof(demuxer->stream)) return 0; | |
157 | |
158 if(hdr[0]==0x4E && hdr[1]==0x53 && hdr[2]==0x56){ | |
159 // NSV header! | |
160 if(hdr[3]==0x73){ | |
161 // NSVs | |
162 stream_read(demuxer->stream,hdr+4,17-4); | |
163 } | |
164 | |
165 if(hdr[3]==0x66){ | |
166 // NSVf | |
167 int len=stream_read_dword_le(demuxer->stream); | |
168 // TODO: parse out metadata!!!! | |
169 stream_skip(demuxer->stream,len-8); | |
170 | |
171 // NSVs | |
172 stream_read(demuxer->stream,hdr,17); | |
173 } | |
174 | |
175 // dummy debug message | |
176 mp_msg(MSGT_DEMUX,MSGL_V,"demux_nsv: Header: %.12s\n",hdr); | |
177 | |
178 // bytes 8-11 audio codec fourcc | |
179 // PCM fourcc needs extra parsing for every audio chunk, yet to implement | |
12896 | 180 if((demuxer->audio->id != -2) && strncmp(hdr+8,"NONE", 4)){//&&strncmp(hdr+8,"VLB ", 4)){ |
12175 | 181 sh_audio = new_sh_audio ( demuxer, 0 ); |
182 demuxer->audio->sh = sh_audio; | |
183 sh_audio->format=mmioFOURCC(hdr[8],hdr[9],hdr[10],hdr[11]); | |
184 sh_audio->ds = demuxer->audio; | |
12896 | 185 priv->a_format=mmioFOURCC(hdr[8],hdr[9],hdr[10],hdr[11]); |
12175 | 186 } |
187 | |
188 // store hdr fps | |
189 priv->fps=hdr[16]; | |
190 | |
12896 | 191 if ((demuxer->video->id != -2) && strncmp(hdr+4,"NONE", 4)) { |
12198 | 192 /* Create a new video stream header */ |
193 sh_video = new_sh_video ( demuxer, 0 ); | |
194 | |
195 /* Make sure the demuxer knows about the new video stream header | |
196 * (even though new_sh_video() ought to take care of it) | |
197 */ | |
198 demuxer->video->sh = sh_video; | |
199 | |
200 /* Make sure that the video demuxer stream header knows about its | |
201 * parent video demuxer stream (this is getting wacky), or else | |
202 * video_read_properties() will choke | |
203 */ | |
204 sh_video->ds = demuxer->video; | |
205 | |
12175 | 206 // bytes 4-7 video codec fourcc |
207 priv->v_format = sh_video->format=mmioFOURCC(hdr[4],hdr[5],hdr[6],hdr[7]); | |
208 | |
209 // new video stream! parse header | |
210 sh_video->disp_w=hdr[12]|(hdr[13]<<8); | |
211 sh_video->disp_h=hdr[14]|(hdr[15]<<8); | |
212 sh_video->bih=(BITMAPINFOHEADER*)calloc(1,sizeof(BITMAPINFOHEADER)); | |
213 sh_video->bih->biSize=sizeof(BITMAPINFOHEADER); | |
214 sh_video->bih->biPlanes=1; | |
215 sh_video->bih->biBitCount=24; | |
216 sh_video->bih->biWidth=hdr[12]|(hdr[13]<<8); | |
217 sh_video->bih->biHeight=hdr[14]|(hdr[15]<<8); | |
218 memcpy(&sh_video->bih->biCompression,hdr+4,4); | |
219 sh_video->bih->biSizeImage=sh_video->bih->biWidth*sh_video->bih->biHeight*3; | |
220 | |
221 // here we search for the correct keyframe | |
12275 | 222 // vp6 keyframe is when the 2nd byte of the vp6 header is |
223 // 0x36 for VP61 and 0x46 for VP62 | |
12272 | 224 if((priv->v_format==mmioFOURCC('V','P','6','1')) || |
225 (priv->v_format==mmioFOURCC('V','P','6','2')) || | |
226 (priv->v_format==mmioFOURCC('V','P','3','1'))) { | |
227 stream_read(demuxer->stream,buf,10); | |
12277 | 228 if (((((priv->v_format>>16) & 0xff) == '6') && ((buf[8]&0x0e)!=0x06)) || |
12272 | 229 ((((priv->v_format>>16) & 0xff) == '3') && (buf[8]!=0x00 || buf[9]!=0x08))) { |
230 mp_msg(MSGT_DEMUX,MSGL_V,"demux_nsv: searching %.4s keyframe...\n", (char*)&priv->v_format); | |
12277 | 231 while(((((priv->v_format>>16) & 0xff) == '6') && ((buf[8]&0x0e)!=0x06)) || |
12272 | 232 ((((priv->v_format>>16) & 0xff) == '3') && (buf[8]!=0x00 || buf[9]!=0x08))){ |
233 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
|
234 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
|
235 audiolen=(buf[5])|(buf[6]<<8); |
12272 | 236 stream_skip(demuxer->stream, videolen+audiolen-3); |
237 stream_read(demuxer->stream,buf,10); | |
12277 | 238 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
|
239 if(buf[0]==0x4E){ |
aeb377ccb110
Fix vp61 keyframe search (do not skip blocks if 1st frame is keyframe)
rtognimp
parents:
12175
diff
changeset
|
240 mp_msg(MSGT_DEMUX,MSGL_DBG2,"demux_nsv: Got NSVs block.\n"); |
12272 | 241 stream_skip(demuxer->stream,7); |
242 stream_read(demuxer->stream,buf,10); | |
12175 | 243 } |
244 } | |
245 } | |
246 | |
13299
6f8fe531dd73
avoid always skipping first junk with a "sync lost" message
reimar
parents:
12896
diff
changeset
|
247 // 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
|
248 // we seek 17 backwards |
6f8fe531dd73
avoid always skipping first junk with a "sync lost" message
reimar
parents:
12896
diff
changeset
|
249 stream_skip(demuxer->stream,7); |
12175 | 250 } |
251 | |
252 switch(priv->fps){ | |
253 case 0x80: | |
254 sh_video->fps=30; | |
255 break; | |
256 case 0x81: | |
257 sh_video->fps=(float)30000.0/1001.0; | |
258 break; | |
259 case 0x82: | |
260 sh_video->fps=25; | |
261 break; | |
262 case 0x83: | |
263 sh_video->fps=(float)24000.0/1001.0; | |
264 break; | |
265 case 0x85: | |
266 sh_video->fps=(float)15000.0/1001.0; | |
267 break; | |
15991 | 268 case 0x89: |
269 sh_video->fps=(float)10000.0/1001.0; | |
270 break; | |
12175 | 271 default: |
272 sh_video->fps = (float)priv->fps; | |
273 } | |
274 sh_video->frametime = (float)1.0 / (float)sh_video->fps; | |
12198 | 275 } |
12175 | 276 } |
277 | |
13299
6f8fe531dd73
avoid always skipping first junk with a "sync lost" message
reimar
parents:
12896
diff
changeset
|
278 // seek to start of NSV header |
6f8fe531dd73
avoid always skipping first junk with a "sync lost" message
reimar
parents:
12896
diff
changeset
|
279 stream_seek(demuxer->stream,stream_tell(demuxer->stream)-17); |
12175 | 280 |
281 return demuxer; | |
282 } | |
283 | |
16175 | 284 static int nsv_check_file ( demuxer_t* demuxer ) |
12175 | 285 { |
16318 | 286 unsigned char hdr; |
287 int i; | |
12175 | 288 |
289 /* Store original position */ | |
290 // off_t orig_pos = stream_tell(demuxer->stream); | |
291 | |
292 mp_msg ( MSGT_DEMUX, MSGL_V, "Checking for Nullsoft Streaming Video\n" ); | |
293 | |
16318 | 294 for (i = 0; i < HEADER_SEARCH_SIZE; i++) { |
295 if (stream_read_char(demuxer->stream) != 'N') | |
296 continue; | |
297 if(stream_eof(demuxer->stream)) | |
298 return 0; | |
12175 | 299 |
16318 | 300 if (stream_read_char(demuxer->stream) != 'S') |
301 continue; | |
302 if(stream_eof(demuxer->stream)) | |
303 return 0; | |
304 if (stream_read_char(demuxer->stream) != 'V') | |
305 continue; | |
306 if(stream_eof(demuxer->stream)) | |
307 return 0; | |
308 | |
309 hdr = stream_read_char(demuxer->stream); | |
310 if(stream_eof(demuxer->stream)) | |
311 return 0; | |
312 if((hdr == 'f') || (hdr == 's')) { | |
313 stream_seek(demuxer->stream,stream_tell(demuxer->stream)-4); | |
314 return DEMUXER_TYPE_NSV; | |
315 } | |
316 } | |
317 | |
318 return 0; | |
12175 | 319 } |
320 | |
16175 | 321 static void demux_close_nsv(demuxer_t* demuxer) { |
12175 | 322 nsv_priv_t* priv = demuxer->priv; |
323 | |
324 if(!priv) | |
325 return; | |
326 free(priv); | |
327 | |
328 } | |
16175 | 329 |
330 | |
331 demuxer_desc_t demuxer_desc_nsv = { | |
332 "NullsoftVideo demuxer", | |
333 "nsv", | |
334 "Nullsoft Streaming Video", | |
335 "Reza Jelveh", | |
336 "nsv and nsa streaming files", | |
337 DEMUXER_TYPE_NSV, | |
16318 | 338 0, // safe but expensive autodetect |
16175 | 339 nsv_check_file, |
340 demux_nsv_fill_buffer, | |
341 demux_open_nsv, | |
342 demux_close_nsv, | |
343 demux_seek_nsv, | |
344 NULL | |
345 }; |