Mercurial > mplayer.hg
comparison libmpdemux/demux_pva.c @ 7364:b2445802532c
.PVA (mpeg-like fileformat used by MultiDec && WinTV) demuxer
slightly modified patch, original by Matteo Giani <matgiani@ctonet.it>
author | arpi |
---|---|
date | Tue, 10 Sep 2002 21:50:03 +0000 |
parents | |
children | c4434bdf6e51 |
comparison
equal
deleted
inserted
replaced
7363:8acd8cb8fd52 | 7364:b2445802532c |
---|---|
1 /* | |
2 * demuxer for PVA files, such as the ones produced by software to manage DVB boards | |
3 * like the Hauppauge WinTV DVBs, for MPlayer. | |
4 * | |
5 * Uses info from the PVA file specifications found at | |
6 * | |
7 * http://www.technotrend.de/download/av_format_v1.pdf | |
8 * | |
9 * Known issue: | |
10 * - Does not seem to correctly demux files produced by MultiDec (on some channels). | |
11 * They seem not to be correctly formatted. In fact, other DVB software which uses | |
12 * the DVB board for playback plays them fine, but existing software which should | |
13 * convert PVAs to MPEG-PS streams fails on them as well. | |
14 * Feedback would be appreciated. | |
15 * | |
16 * | |
17 * written by Matteo Giani | |
18 */ | |
19 | |
20 | |
21 | |
22 | |
23 #include "config.h" | |
24 #include "mp_msg.h" | |
25 #include "help_mp.h" | |
26 | |
27 #include "stream.h" | |
28 #include "demuxer.h" | |
29 #include "stheader.h" | |
30 | |
31 #include "bswap.h" | |
32 | |
33 /* | |
34 * #defines below taken from PVA spec (see URL above) | |
35 */ | |
36 | |
37 #define PVA_MAX_VIDEO_PACK_LEN 6*1024 | |
38 | |
39 #define VIDEOSTREAM 0x01 | |
40 #define MAINAUDIOSTREAM 0x02 | |
41 | |
42 typedef struct { | |
43 unsigned long offset; | |
44 long size; | |
45 uint8_t type; | |
46 uint8_t is_packet_start; | |
47 uint64_t pts; | |
48 } pva_payload_t; | |
49 | |
50 | |
51 typedef struct { | |
52 float last_audio_pts; | |
53 float last_video_pts; | |
54 uint8_t just_synced; | |
55 uint8_t synced_stream_id; | |
56 } pva_priv_t; | |
57 | |
58 | |
59 int pva_sync(demuxer_t * demuxer) | |
60 { | |
61 uint8_t buffer[5]={0,0,0,0,0}; | |
62 int count; | |
63 pva_priv_t * priv = (pva_priv_t *) demuxer->priv; | |
64 | |
65 /* This is a hack. Since this function is called both for actual syncing and by | |
66 * pva_check_file to check file type, we must check whether the priv structure has | |
67 * already been allocated, otherwise we will dereference NULL and segfault. | |
68 * So, if priv is NULL (not yet allocated) use a local variable, otherwise use priv->just_synced. | |
69 * This field is in the priv structure so that pva_get_payload knows that pva_sync has already | |
70 * read (part of) the PVA header. This way we can avoid to seek back and (hopefully) be able to read | |
71 * from pipes and such. | |
72 */ | |
73 | |
74 uint8_t local_synced=0; | |
75 uint8_t * syncedptr; | |
76 | |
77 syncedptr=(priv==NULL)?&local_synced:&priv->just_synced; | |
78 | |
79 | |
80 for(count=0 ; count<PVA_MAX_VIDEO_PACK_LEN && !demuxer->stream->eof && !*syncedptr ; count++) | |
81 { | |
82 buffer[0]=buffer[1]; | |
83 buffer[1]=buffer[2]; | |
84 buffer[2]=buffer[3]; | |
85 buffer[3]=buffer[4]; | |
86 buffer[4]=stream_read_char(demuxer->stream); | |
87 /* | |
88 * Check for a PVA packet beginning sequence: we check both the "AV" word at the | |
89 * very beginning and the "0x55" reserved byte (which is unused and set to 0x55 by spec) | |
90 */ | |
91 if(buffer[0]=='A' && buffer[1] == 'V' && buffer[4] == 0x55) *syncedptr=1; | |
92 //printf("demux_pva: pva_sync() current offset: %d\n",stream_tell(demuxer->stream)); | |
93 } | |
94 if(*syncedptr) | |
95 { | |
96 if(priv!=NULL) priv->synced_stream_id=buffer[2]; | |
97 return 1; | |
98 } | |
99 else | |
100 { | |
101 return 0; | |
102 } | |
103 } | |
104 | |
105 int pva_check_file(demuxer_t * demuxer) | |
106 { | |
107 uint8_t buffer[5]={0,0,0,0,0}; | |
108 mp_msg(MSGT_DEMUX, MSGL_V, "Checking for PVA\n"); | |
109 stream_read(demuxer->stream,buffer,5); | |
110 if(buffer[0]=='A' && buffer[1] == 'V' && buffer[4] == 0x55) | |
111 { | |
112 mp_msg(MSGT_DEMUX,MSGL_DBG2, "Success: PVA\n"); | |
113 return 1; | |
114 } | |
115 else | |
116 { | |
117 mp_msg(MSGT_DEMUX,MSGL_DBG2, "Failed: PVA\n"); | |
118 return 0; | |
119 } | |
120 } | |
121 | |
122 demuxer_t * demux_open_pva (demuxer_t * demuxer) | |
123 { | |
124 sh_video_t *sh_video = new_sh_video(demuxer,0); | |
125 sh_audio_t * sh_audio = new_sh_audio(demuxer,0); | |
126 pva_priv_t * priv; | |
127 unsigned char * buffer; | |
128 | |
129 | |
130 | |
131 stream_reset(demuxer->stream); | |
132 stream_seek(demuxer->stream,0); | |
133 | |
134 | |
135 | |
136 priv=malloc(sizeof(pva_priv_t)); | |
137 | |
138 if(demuxer->stream->type!=STREAMTYPE_FILE) demuxer->seekable=0; | |
139 else demuxer->seekable=1; | |
140 | |
141 demuxer->priv=priv; | |
142 memset(demuxer->priv,0,sizeof(pva_priv_t)); | |
143 | |
144 if(!pva_sync(demuxer)) | |
145 { | |
146 mp_msg(MSGT_DEMUX,MSGL_ERR,"Not a PVA file.\n"); | |
147 return NULL; | |
148 } | |
149 | |
150 //printf("priv->just_synced %s after initial sync!\n",priv->just_synced?"set":"UNSET"); | |
151 | |
152 demuxer->video->sh=sh_video; | |
153 demuxer->audio->sh=sh_audio; | |
154 mp_msg(MSGT_DEMUXER,MSGL_INFO,"Opened PVA demuxer...\n"); | |
155 | |
156 /* | |
157 * Audio and Video codecs: | |
158 * the PVA spec only allows MPEG2 video and MPEG layer II audio. No need to check the formats then. | |
159 * Moreover, there would be no way to do that since the PVA stream format has no fields to describe | |
160 * the used codecs. | |
161 */ | |
162 | |
163 sh_video->format=0x10000002; | |
164 sh_video->ds=demuxer->video; | |
165 | |
166 sh_audio->format=0x50; | |
167 sh_audio->ds=demuxer->audio; | |
168 | |
169 priv->last_video_pts=-1; | |
170 priv->last_audio_pts=-1; | |
171 | |
172 return demuxer; | |
173 } | |
174 | |
175 // 0 = EOF or no stream found | |
176 // 1 = successfully read a packet | |
177 int demux_pva_fill_buffer (demuxer_t * demux) | |
178 { | |
179 uint8_t done=0; | |
180 demux_packet_t * dp; | |
181 pva_priv_t * priv=demux->priv; | |
182 pva_payload_t current_payload; | |
183 | |
184 while(!done) | |
185 { | |
186 if(!pva_get_payload(demux,¤t_payload)) return 0; | |
187 switch(current_payload.type) | |
188 { | |
189 case VIDEOSTREAM: | |
190 if(!current_payload.is_packet_start && priv->last_video_pts==-1) | |
191 { | |
192 /* We should only be here at the beginning of a stream, when we have | |
193 * not yet encountered a valid Video PTS, or after a seek. | |
194 * So, skip these starting packets in order not to deliver the | |
195 * player a bogus PTS. | |
196 */ | |
197 done=0; | |
198 } | |
199 else | |
200 { | |
201 /* | |
202 * In every other condition, we are delivering the payload. Set this | |
203 * so that the following code knows whether to skip it or read it. | |
204 */ | |
205 done=1; | |
206 } | |
207 if(current_payload.is_packet_start) | |
208 { | |
209 priv->last_video_pts=((float)current_payload.pts)/90000; | |
210 //mp_msg(MSGT_DEMUXER,MSGL_DBG2,"demux_pva: Video PTS=%llu , delivered %f\n",current_payload.pts,priv->last_video_pts); | |
211 } | |
212 if(done) | |
213 { | |
214 dp=new_demux_packet(current_payload.size); | |
215 dp->pts=priv->last_video_pts; | |
216 stream_read(demux->stream,dp->buffer,current_payload.size); | |
217 ds_add_packet(demux->video,dp); | |
218 } | |
219 else | |
220 { | |
221 //printf("Skipping %u video bytes\n",current_payload.size); | |
222 stream_skip(demux->stream,current_payload.size); | |
223 } | |
224 break; | |
225 case MAINAUDIOSTREAM: | |
226 if(!current_payload.is_packet_start && priv->last_audio_pts==-1) | |
227 { | |
228 /* Same as above for invalid video PTS, just for audio. */ | |
229 done=0; | |
230 } | |
231 else | |
232 { | |
233 done=1; | |
234 } | |
235 if(current_payload.is_packet_start) | |
236 { | |
237 priv->last_audio_pts=(float)current_payload.pts/90000; | |
238 } | |
239 if(done) | |
240 { | |
241 dp=new_demux_packet(current_payload.size); | |
242 dp->pts=priv->last_audio_pts; | |
243 stream_read(demux->stream,dp->buffer,current_payload.size); | |
244 ds_add_packet(demux->audio,dp); | |
245 } | |
246 else | |
247 { | |
248 stream_skip(demux->stream,current_payload.size); | |
249 } | |
250 break; | |
251 } | |
252 } | |
253 return 1; | |
254 } | |
255 | |
256 int pva_get_payload(demuxer_t * d,pva_payload_t * payload) | |
257 { | |
258 uint8_t flags,pes_head_len; | |
259 uint16_t pack_size; | |
260 long long next_offset,pva_payload_start; | |
261 unsigned char buffer[256]; | |
262 demux_packet_t * dp; //hack to deliver the preBytes (see PVA doc) | |
263 pva_priv_t * priv=(pva_priv_t *) d->priv; | |
264 | |
265 | |
266 if(d==NULL) | |
267 { | |
268 printf("demux_pva: pva_get_payload got passed a NULL pointer!\n"); | |
269 return 0; | |
270 } | |
271 | |
272 | |
273 if(d->stream->eof) | |
274 { | |
275 mp_msg(MSGT_DEMUX,MSGL_V,"demux_pva: pva_get_payload() detected stream->eof!!!\n"); | |
276 return 0; | |
277 } | |
278 | |
279 //printf("priv->just_synced %s\n",priv->just_synced?"SET":"UNSET"); | |
280 if(!priv->just_synced) | |
281 { | |
282 if(stream_read_word(d->stream) != (('A'<<8)|'V')) | |
283 { | |
284 mp_msg(MSGT_DEMUX,MSGL_V,"demux_pva: pva_get_payload() missed a SyncWord at %ld!! Trying to sync...\n",stream_tell(d->stream)); | |
285 if(!pva_sync(d)) return 0; | |
286 } | |
287 } | |
288 if(priv->just_synced) | |
289 { | |
290 payload->type=priv->synced_stream_id; | |
291 priv->just_synced=0; | |
292 } | |
293 else | |
294 { | |
295 payload->type=stream_read_char(d->stream); | |
296 stream_skip(d->stream,2); //counter and reserved | |
297 } | |
298 flags=stream_read_char(d->stream); | |
299 payload->is_packet_start=flags & 0x10; | |
300 pack_size=le2me_16(stream_read_word(d->stream)); | |
301 mp_msg(MSGT_DEMUX,MSGL_DBG2,"demux_pva::pva_get_payload(): pack_size=%u field read at offset %lu\n",pack_size,stream_tell(d->stream)-2); | |
302 pva_payload_start=stream_tell(d->stream); | |
303 next_offset=pva_payload_start+pack_size; | |
304 if(!payload->is_packet_start) | |
305 { | |
306 payload->offset=stream_tell(d->stream); | |
307 payload->size=pack_size; | |
308 } | |
309 else | |
310 { //here comes the good part... | |
311 switch(payload->type) | |
312 { | |
313 case VIDEOSTREAM: | |
314 payload->pts=le2me_32(stream_read_dword(d->stream)); | |
315 //printf("Video PTS: %llu\n",payload->pts); | |
316 if((flags&0x03) && ((pva_priv_t *)d->priv)->last_video_pts!=-1) | |
317 { | |
318 dp=new_demux_packet(flags&0x03); | |
319 stream_read(d->stream,dp->buffer,flags & 0x03); //read PreBytes | |
320 dp->pts=((pva_priv_t *)d->priv)->last_video_pts; | |
321 ds_add_packet(d->video,dp); | |
322 } | |
323 else | |
324 { | |
325 stream_skip(d->stream,flags&0x03); | |
326 } | |
327 | |
328 //now we are at real beginning of payload. | |
329 payload->offset=stream_tell(d->stream); | |
330 //size is pack_size minus PTS size minus padding size. | |
331 payload->size=pack_size - 4 - (flags & 0x03); | |
332 break; | |
333 case MAINAUDIOSTREAM: | |
334 stream_skip(d->stream,8); //FIXME properly parse PES header. | |
335 //assuming byte 8 is PES residual header length. | |
336 pes_head_len=stream_read_char(d->stream); | |
337 stream_read(d->stream,buffer,pes_head_len); | |
338 //we should now be on start of real payload. | |
339 //let's now parse the PTS... | |
340 if((buffer[0] & 0xf0)!=0x20) | |
341 { | |
342 mp_msg(MSGT_DEMUX,MSGL_ERR,"demux_pva: expected audio PTS but badly formatted... (read 0x%02X)\n",buffer[0]); | |
343 return; | |
344 } | |
345 payload->pts=0LL; | |
346 payload->pts|=((uint64_t)(buffer[0] & 0x0e) << 29); | |
347 payload->pts|=buffer[1]<<22; | |
348 payload->pts|=(buffer[2] & 0xfe) << 14; | |
349 payload->pts|=buffer[3]<<7; | |
350 payload->pts|=(buffer[4] & 0xfe) >> 1; | |
351 /* | |
352 * PTS parsing is hopefully finished. | |
353 * Let's now fill in offset and size. | |
354 */ | |
355 payload->pts=le2me_64(payload->pts); | |
356 payload->offset=stream_tell(d->stream); | |
357 payload->size=pack_size-stream_tell(d->stream)+pva_payload_start; | |
358 break; | |
359 } | |
360 } | |
361 } | |
362 | |
363 int demux_seek_pva(demuxer_t * demuxer,float rel_seek_secs,int flags) | |
364 { | |
365 int total_bitrate=0; | |
366 long dest_offset; | |
367 pva_priv_t * priv=demuxer->priv; | |
368 | |
369 total_bitrate=((sh_audio_t *)demuxer->audio->sh)->i_bps + ((sh_video_t *)demuxer->video->sh)->i_bps; | |
370 | |
371 /* | |
372 * Compute absolute offset inside the stream. Approximate total bitrate with sum of bitrates | |
373 * reported by the audio and video codecs. The seek is not accurate because, just like | |
374 * with MPEG streams, the bitrate is not constant. Moreover, we do not take into account | |
375 * the overhead caused by PVA and PES headers. | |
376 * If the calculated absolute offset is negative, seek to the beginning of the file. | |
377 */ | |
378 | |
379 dest_offset=stream_tell(demuxer->stream)+rel_seek_secs*total_bitrate; | |
380 if(dest_offset<0) dest_offset=0; | |
381 | |
382 stream_seek(demuxer->stream,dest_offset); | |
383 | |
384 if(!pva_sync(demuxer)) | |
385 { | |
386 mp_msg(MSGT_DEMUX,MSGL_V,"demux_pva: Couldn't seek!\n"); | |
387 return 0; | |
388 } | |
389 | |
390 /* | |
391 * Reset the PTS info inside the pva_priv_t structure. This way we don't deliver | |
392 * data with the wrong PTSs (the ones we had before seeking). | |
393 * | |
394 */ | |
395 | |
396 priv->last_video_pts=-1; | |
397 priv->last_audio_pts=-1; | |
398 | |
399 return 1; | |
400 } | |
401 | |
402 | |
403 | |
404 void demux_close_pva(demuxer_t * demuxer) | |
405 { | |
406 if(demuxer->priv) | |
407 { | |
408 free(demuxer->priv); | |
409 demuxer->priv=NULL; | |
410 } | |
411 } | |
412 |