Index: libmpdemux/demux_pva.c =================================================================== RCS file: /cvsroot/mplayer/main/libmpdemux/demux_pva.c,v retrieving revision 1.2 diff -u -r1.2 demux_pva.c --- libmpdemux/demux_pva.c 22 Sep 2002 02:33:26 -0000 1.2 +++ libmpdemux/demux_pva.c 7 Oct 2002 21:53:12 -0000 @@ -6,17 +6,17 @@ * * http://www.technotrend.de/download/av_format_v1.pdf * - * Known issue: - * - Does not seem to correctly demux files produced by MultiDec (on some channels). - * They seem not to be correctly formatted. In fact, other DVB software which uses - * the DVB board for playback plays them fine, but existing software which should - * convert PVAs to MPEG-PS streams fails on them as well. - * Feedback would be appreciated. + * WARNING: Quite a hack was required in order to get files by MultiDec played back correctly. + * If it breaks anything else, just comment out the "#define DEMUX_PVA_MULTIDEC_HACK" below + * and it will not be compiled in. * + * Feedback is appreciated. * * written by Matteo Giani */ +#define DEMUX_PVA_MULTIDEC_HACK +#define PVA_NEW_PREBYTES_CODE #include #include @@ -42,44 +42,44 @@ #define MAINAUDIOSTREAM 0x02 typedef struct { - unsigned long offset; + off_t offset; long size; uint8_t type; uint8_t is_packet_start; - uint64_t pts; + float pts; } pva_payload_t; typedef struct { float last_audio_pts; float last_video_pts; +#ifdef PVA_NEW_PREBYTES_CODE + float video_pts_after_prebytes; + long video_size_after_prebytes; + uint8_t prebytes_delivered; +#endif uint8_t just_synced; uint8_t synced_stream_id; } pva_priv_t; + int pva_sync(demuxer_t * demuxer) { uint8_t buffer[5]={0,0,0,0,0}; int count; pva_priv_t * priv = (pva_priv_t *) demuxer->priv; + - /* This is a hack. Since this function is called both for actual syncing and by - * pva_check_file to check file type, we must check whether the priv structure has - * already been allocated, otherwise we will dereference NULL and segfault. - * So, if priv is NULL (not yet allocated) use a local variable, otherwise use priv->just_synced. - * This field is in the priv structure so that pva_get_payload knows that pva_sync has already - * read (part of) the PVA header. This way we can avoid to seek back and (hopefully) be able to read - * from pipes and such. - */ + /* This function is used to find the next nearest PVA packet start after a seek, since a PVA file + * is not indexed. + * The just_synced field is in the priv structure so that pva_get_payload knows pva_sync + * has already read (part of) the PVA header. This way we can avoid to seek back and (hopefully) + * be able to read from pipes and such. + */ - uint8_t local_synced=0; - uint8_t * syncedptr; - - syncedptr=(priv==NULL)?&local_synced:&priv->just_synced; - - for(count=0 ; countstream->eof && !*syncedptr ; count++) + for(count=0 ; countstream->eof && !priv->just_synced ; count++) { buffer[0]=buffer[1]; buffer[1]=buffer[2]; @@ -90,10 +90,10 @@ * Check for a PVA packet beginning sequence: we check both the "AV" word at the * very beginning and the "0x55" reserved byte (which is unused and set to 0x55 by spec) */ - if(buffer[0]=='A' && buffer[1] == 'V' && buffer[4] == 0x55) *syncedptr=1; - //printf("demux_pva: pva_sync() current offset: %d\n",stream_tell(demuxer->stream)); + if(buffer[0]=='A' && buffer[1] == 'V' && buffer[4] == 0x55) priv->just_synced=1; + //printf("demux_pva: pva_sync(): current offset= %ld\n",stream_tell(demuxer->stream)); } - if(*syncedptr) + if(priv->just_synced) { if(priv!=NULL) priv->synced_stream_id=buffer[2]; return 1; @@ -124,7 +124,9 @@ demuxer_t * demux_open_pva (demuxer_t * demuxer) { sh_video_t *sh_video = new_sh_video(demuxer,0); - sh_audio_t * sh_audio = new_sh_audio(demuxer,0); + sh_audio_t *sh_audio = new_sh_audio(demuxer,0); + + pva_priv_t * priv; stream_reset(demuxer->stream); @@ -149,7 +151,10 @@ //printf("priv->just_synced %s after initial sync!\n",priv->just_synced?"set":"UNSET"); demuxer->video->sh=sh_video; - demuxer->audio->sh=sh_audio; + + //printf("demuxer->stream->end_pos= %d\n",demuxer->stream->end_pos); + + mp_msg(MSGT_DEMUXER,MSGL_INFO,"Opened PVA demuxer...\n"); /* @@ -162,9 +167,18 @@ sh_video->format=0x10000002; sh_video->ds=demuxer->video; + /* + printf("demuxer->video->id==%d\n",demuxer->video->id); + printf("demuxer->audio->id==%d\n",demuxer->audio->id); + */ + + demuxer->audio->sh=sh_audio; sh_audio->format=0x50; sh_audio->ds=demuxer->audio; - + + demuxer->movi_start=0; + demuxer->movi_end=demuxer->stream->end_pos; + priv->last_video_pts=-1; priv->last_audio_pts=-1; @@ -188,6 +202,7 @@ switch(current_payload.type) { case VIDEOSTREAM: + if(demux->video->id==-1) demux->video->id=0; if(!current_payload.is_packet_start && priv->last_video_pts==-1) { /* We should only be here at the beginning of a stream, when we have @@ -205,9 +220,10 @@ */ done=1; } + if(demux->video->id!=0) done=0; if(current_payload.is_packet_start) { - priv->last_video_pts=((float)current_payload.pts)/90000; + priv->last_video_pts=current_payload.pts; //mp_msg(MSGT_DEMUXER,MSGL_DBG2,"demux_pva: Video PTS=%llu , delivered %f\n",current_payload.pts,priv->last_video_pts); } if(done) @@ -224,6 +240,7 @@ } break; case MAINAUDIOSTREAM: + if(demux->audio->id==-1) demux->audio->id=0; if(!current_payload.is_packet_start && priv->last_audio_pts==-1) { /* Same as above for invalid video PTS, just for audio. */ @@ -235,12 +252,15 @@ } if(current_payload.is_packet_start) { - priv->last_audio_pts=(float)current_payload.pts/90000; + priv->last_audio_pts=current_payload.pts; } + if(demux->audio->id!=0) done=0; if(done) { dp=new_demux_packet(current_payload.size); dp->pts=priv->last_audio_pts; + if(current_payload.offset != stream_tell(demux->stream)) + stream_seek(demux->stream,current_payload.offset); stream_read(demux->stream,dp->buffer,current_payload.size); ds_add_packet(demux->audio,dp); } @@ -258,7 +278,7 @@ { uint8_t flags,pes_head_len; uint16_t pack_size; - long long next_offset,pva_payload_start; + off_t next_offset,pva_payload_start; unsigned char buffer[256]; demux_packet_t * dp; //hack to deliver the preBytes (see PVA doc) pva_priv_t * priv=(pva_priv_t *) d->priv; @@ -266,10 +286,14 @@ if(d==NULL) { - printf("demux_pva: pva_get_payload got passed a NULL pointer!\n"); + mp_msg(MSGT_DEMUX,MSGL_ERR,"demux_pva: pva_get_payload got passed a NULL pointer!\n"); return 0; } + d->filepos=stream_tell(d->stream); + + + if(d->stream->eof) { @@ -278,12 +302,37 @@ } //printf("priv->just_synced %s\n",priv->just_synced?"SET":"UNSET"); + +#ifdef PVA_NEW_PREBYTES_CODE + if(priv->prebytes_delivered) + /* The previous call to this fn has delivered the preBytes. Then we are already inside + * the payload. Let's just deliver the video along with its right PTS, the one we stored + * in the priv structure and was in the PVA header before the PreBytes. + */ + { + //printf("prebytes_delivered=1. Resetting.\n"); + payload->size = priv->video_size_after_prebytes; + payload->pts = priv->video_pts_after_prebytes; + payload->is_packet_start = 1; + payload->offset = stream_tell(d->stream); + payload->type = VIDEOSTREAM; + priv->prebytes_delivered = 0; + return 1; + } +#endif if(!priv->just_synced) { if(stream_read_word(d->stream) != (('A'<<8)|'V')) { mp_msg(MSGT_DEMUX,MSGL_V,"demux_pva: pva_get_payload() missed a SyncWord at %ld!! Trying to sync...\n",stream_tell(d->stream)); - if(!pva_sync(d)) return 0; + if(!pva_sync(d)) + { + if (!d->stream->eof) + { + mp_msg(MSGT_DEMUX,MSGL_ERR,"demux_pva: couldn't sync! (broken file?)"); + } + return 0; + } } } if(priv->just_synced) @@ -302,6 +351,32 @@ 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); pva_payload_start=stream_tell(d->stream); next_offset=pva_payload_start+pack_size; + + + /* + * The code in the #ifdef directive below is a hack needed to get badly formatted PVA files + * such as the ones written by MultiDec played back correctly. + * Basically, it works like this: if the PVA packet does not signal a PES header, but the + * payload looks like one, let's assume it IS one. It has worked for me up to now. + * It can be disabled since it's quite an ugly hack and could potentially break things up + * if the PVA audio payload happens to start with 0x000001 even without being a non signalled + * PES header start. + * Though it's quite unlikely, it potentially could (AFAIK). + */ +#ifdef DEMUX_PVA_MULTIDEC_HACK + if(payload->type==MAINAUDIOSTREAM) + { + stream_read(d->stream,buffer,3); + if(buffer[0]==0x00 && buffer[1]==0x00 && buffer[2]==0x01 && !payload->is_packet_start) + { + mp_msg(MSGT_DEMUX,MSGL_V,"demux_pva: suspecting non signaled audio PES packet start. Maybe file by MultiDec?\n"); + payload->is_packet_start=1; + } + stream_seek(d->stream,stream_tell(d->stream)-3); + } +#endif + + if(!payload->is_packet_start) { payload->offset=stream_tell(d->stream); @@ -312,48 +387,75 @@ switch(payload->type) { case VIDEOSTREAM: - payload->pts=le2me_32(stream_read_dword(d->stream)); - //printf("Video PTS: %llu\n",payload->pts); - if((flags&0x03) && ((pva_priv_t *)d->priv)->last_video_pts!=-1) + payload->pts=(float)(le2me_32(stream_read_dword(d->stream)))/90000; + //printf("Video PTS: %f\n",payload->pts); + if((flags&0x03) +#ifdef PVA_NEW_PREBYTES_CODE + && !priv->prebytes_delivered +#endif + ) { +#ifndef PVA_NEW_PREBYTES_CODE dp=new_demux_packet(flags&0x03); stream_read(d->stream,dp->buffer,flags & 0x03); //read PreBytes - dp->pts=((pva_priv_t *)d->priv)->last_video_pts; ds_add_packet(d->video,dp); +#else + //printf("Delivering prebytes. Setting prebytes_delivered."); + payload->offset=stream_tell(d->stream); + payload->size = flags & 0x03; + priv->video_pts_after_prebytes = payload->pts; + priv->video_size_after_prebytes = pack_size - 4 - (flags & 0x03); + payload->pts=priv->last_video_pts; + payload->is_packet_start=0; + priv->prebytes_delivered=1; + return 1; +#endif } - else - { - stream_skip(d->stream,flags&0x03); - } + //now we are at real beginning of payload. payload->offset=stream_tell(d->stream); - //size is pack_size minus PTS size minus padding size. + //size is pack_size minus PTS size minus PreBytes size. payload->size=pack_size - 4 - (flags & 0x03); break; case MAINAUDIOSTREAM: - stream_skip(d->stream,8); //FIXME properly parse PES header. - //assuming byte 8 is PES residual header length. + stream_skip(d->stream,3); //FIXME properly parse PES header. + //printf("StreamID in audio PES header: 0x%2X\n",stream_read_char(d->stream)); + stream_skip(d->stream,4); + + buffer[255]=stream_read_char(d->stream); pes_head_len=stream_read_char(d->stream); stream_read(d->stream,buffer,pes_head_len); - //we should now be on start of real payload. - //let's now parse the PTS... - if((buffer[0] & 0xf0)!=0x20) - { - mp_msg(MSGT_DEMUX,MSGL_ERR,"demux_pva: expected audio PTS but badly formatted... (read 0x%02X)\n",buffer[0]); - return 0; - } - payload->pts=0LL; - payload->pts|=((uint64_t)(buffer[0] & 0x0e) << 29); - payload->pts|=buffer[1]<<22; - payload->pts|=(buffer[2] & 0xfe) << 14; - payload->pts|=buffer[3]<<7; - payload->pts|=(buffer[4] & 0xfe) >> 1; - /* - * PTS parsing is hopefully finished. - * Let's now fill in offset and size. - */ - payload->pts=le2me_64(payload->pts); + if(!buffer[255]&0x80) //PES header does not contain PTS. + { + mp_msg(MSGT_DEMUX,MSGL_V,"Audio PES packet does not contain PTS. (pes_head_len=%d)\n",pes_head_len); + payload->pts=priv->last_audio_pts; + break; + } + else //PES header DOES contain PTS + { + if((buffer[0] & 0xf0)!=0x20) // PTS badly formatted + { + mp_msg(MSGT_DEMUX,MSGL_V,"demux_pva: expected audio PTS but badly formatted... (read 0x%02X). Falling back to previous PTS (hack).\n",buffer[0]); + payload->pts=priv->last_audio_pts; + // return 0; + } + else + { + uint64_t temp_pts; + + temp_pts=0LL; + temp_pts|=((uint64_t)(buffer[0] & 0x0e) << 29); + temp_pts|=buffer[1]<<22; + temp_pts|=(buffer[2] & 0xfe) << 14; + temp_pts|=buffer[3]<<7; + temp_pts|=(buffer[4] & 0xfe) >> 1; + /* + * PTS parsing is hopefully finished. + */ + payload->pts=(float)le2me_64(temp_pts)/90000; + } + } payload->offset=stream_tell(d->stream); payload->size=pack_size-stream_tell(d->stream)+pva_payload_start; break; @@ -365,7 +467,7 @@ int demux_seek_pva(demuxer_t * demuxer,float rel_seek_secs,int flags) { int total_bitrate=0; - long dest_offset; + off_t dest_offset; pva_priv_t * priv=demuxer->priv; total_bitrate=((sh_audio_t *)demuxer->audio->sh)->i_bps + ((sh_video_t *)demuxer->video->sh)->i_bps;