diff options
author | albeu <albeu@b3059339-0415-0410-9bf9-f77b7e298cf2> | 2002-04-20 17:50:11 +0000 |
---|---|---|
committer | albeu <albeu@b3059339-0415-0410-9bf9-f77b7e298cf2> | 2002-04-20 17:50:11 +0000 |
commit | 29a46375a8a41d224853e7cd2ab5ddb74f7e1c8b (patch) | |
tree | a1bce5a5229f35a09bb4933f1c099bb9b31b45af /libmpdemux | |
parent | 291c078ade06953b423b2373d706680727d79eb9 (diff) | |
download | mpv-29a46375a8a41d224853e7cd2ab5ddb74f7e1c8b.tar.bz2 mpv-29a46375a8a41d224853e7cd2ab5ddb74f7e1c8b.tar.xz |
Seeking implemented
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@5733 b3059339-0415-0410-9bf9-f77b7e298cf2
Diffstat (limited to 'libmpdemux')
-rw-r--r-- | libmpdemux/demux_ogg.c | 311 | ||||
-rw-r--r-- | libmpdemux/demuxer.c | 3 |
2 files changed, 272 insertions, 42 deletions
diff --git a/libmpdemux/demux_ogg.c b/libmpdemux/demux_ogg.c index 06319f4354..60e2c67442 100644 --- a/libmpdemux/demux_ogg.c +++ b/libmpdemux/demux_ogg.c @@ -83,6 +83,11 @@ typedef struct stream_header /// Our private datas +typedef struct ogg_syncpoint { + int64_t granulepos; + off_t page_pos; +} ogg_syncpoint_t; + /// A logical stream typedef struct ogg_stream { /// Timestamping stuff @@ -92,6 +97,8 @@ typedef struct ogg_stream { // Logical stream state ogg_stream_state stream; + int hdr_packets; + int vorbis; } ogg_stream_t; typedef struct ogg_demuxer { @@ -102,8 +109,12 @@ typedef struct ogg_demuxer { /// Logical streams ogg_stream_t *subs; int num_sub; + ogg_syncpoint_t* syncpoints; + int num_syncpoint; } ogg_demuxer_t; +#define NUM_VORBIS_HDR_PACKETS 3 + /// Some defines from OggDS #define PACKET_TYPE_HEADER 0x01 #define PACKET_TYPE_BITS 0x07 @@ -135,40 +146,28 @@ static int demux_ogg_get_page_stream(ogg_demuxer_t* ogg_d,ogg_stream_state** os } -/// Calculate the timestamp and add the packet to the demux stream -// return 1 if the packet was added, 0 otherwise -static int demux_ogg_add_packet(demux_stream_t* ds,ogg_stream_t* os,ogg_packet* pack) { - demuxer_t* d = ds->demuxer; - demux_packet_t* dp; +static unsigned char* demux_ogg_read_packet(ogg_stream_t* os,ogg_packet* pack,vorbis_info* vi,float* pts,int* flags) { unsigned char* data; - float pts = 0; - int flags = 0; - // If packet is an header we jump it except for vorbis - if((*pack->packet & PACKET_TYPE_HEADER) && - (ds == d->video || (ds == d->audio && ((sh_audio_t*)ds->sh)->format != 0xFFFE ))) - return 0; + *pts = 0; + *flags = 0; - // For vorbis packet the packet is the data, for other codec we must jump the header - if(ds == d->audio && ((sh_audio_t*)ds->sh)->format == 0xFFFE) { + if(os->vorbis) { data = pack->packet; if(*pack->packet & PACKET_TYPE_HEADER) - pts = 0; - else { - vorbis_info* vi = &((ov_struct_t*)((sh_audio_t*)ds->sh)->context)->vi; + os->hdr_packets++; + else if(vi) { // When we dump the audio, there is no vi, but we dont care of timestamp in this case - if(vi) { - int32_t blocksize = vorbis_packet_blocksize(vi,pack) / vi->channels; - // Calculate the timestamp if the packet don't have any - if(pack->granulepos == -1) { - pack->granulepos = os->lastpos; - if(os->lastsize > 0) - pack->granulepos += os->lastsize; - } - pts = pack->granulepos / (float)vi->rate; - os->lastsize = blocksize; - os->lastpos = pack->granulepos; + int32_t blocksize = vorbis_packet_blocksize(vi,pack) / vi->channels; + // Calculate the timestamp if the packet don't have any + if(pack->granulepos == -1) { + pack->granulepos = os->lastpos; + if(os->lastsize > 0) + pack->granulepos += os->lastsize; } + *pts = pack->granulepos / (float)vi->rate; + os->lastsize = blocksize; + os->lastpos = pack->granulepos; } } else { // Find data start @@ -179,9 +178,9 @@ static int demux_ogg_add_packet(demux_stream_t* ds,ogg_stream_t* os,ogg_packet* if(pack->granulepos == -1) pack->granulepos = os->lastpos + os->lastsize; // If we alredy have a timestamp it can be a syncpoint - else if(*pack->packet & PACKET_IS_SYNCPOINT) - flags = 1; - pts = pack->granulepos/os->samplerate; + if(*pack->packet & PACKET_IS_SYNCPOINT) + *flags = 1; + *pts = pack->granulepos/os->samplerate; // Save the packet length and timestamp os->lastsize = 0; while(hdrlen) { @@ -191,6 +190,30 @@ static int demux_ogg_add_packet(demux_stream_t* ds,ogg_stream_t* os,ogg_packet* } os->lastpos = pack->granulepos; } + return data; +} + +/// Calculate the timestamp and add the packet to the demux stream +// return 1 if the packet was added, 0 otherwise +static int demux_ogg_add_packet(demux_stream_t* ds,ogg_stream_t* os,ogg_packet* pack) { + demuxer_t* d = ds->demuxer; + demux_packet_t* dp; + unsigned char* data; + float pts = 0; + int flags = 0; + + // If packet is an header we jump it except for vorbis + if((*pack->packet & PACKET_TYPE_HEADER) && + (ds == d->video || (ds == d->audio && ( ((sh_audio_t*)ds->sh)->format != 0xFFFE || os->hdr_packets >= NUM_VORBIS_HDR_PACKETS ) ) )) + return 0; + + // For vorbis packet the packet is the data, for other codec we must jump the header + if(ds == d->audio && ((sh_audio_t*)ds->sh)->format == 0xFFFE) + data = demux_ogg_read_packet(os,pack,&((ov_struct_t*)((sh_audio_t*)ds->sh)->context)->vi, + &pts,&flags); + else + data = demux_ogg_read_packet(os,pack,NULL,&pts,&flags); + /// Send the packet dp = new_demux_packet(pack->bytes-(data-pack->packet)); @@ -203,6 +226,111 @@ static int demux_ogg_add_packet(demux_stream_t* ds,ogg_stream_t* os,ogg_packet* return 1; } +/// Build a table of all syncpoints to make seeking easier +void demux_ogg_build_syncpoints_table(demuxer_t* demuxer) { + ogg_demuxer_t* ogg_d = demuxer->priv; + stream_t *s = demuxer->stream; + ogg_sync_state* sync = &ogg_d->sync; + ogg_page* page= &ogg_d->page; + ogg_stream_state* oss; + ogg_stream_t* os; + ogg_packet op; + int np,sid,p; + vorbis_info* vi = NULL; + off_t pos, last_pos; + pos = last_pos = demuxer->movi_start; + + // Reset the stream + stream_seek(s,demuxer->movi_start); + ogg_sync_reset(sync); + + // Get the serial number of the stream we use + if(demuxer->video->id >= 0) + sid = demuxer->video->id; + else { + sid = demuxer->audio->id; + if(((sh_audio_t*)demuxer->audio->sh)->format == 0xFFFE) + vi = &((ov_struct_t*)((sh_audio_t*)demuxer->audio->sh)->context)->vi; + } + os = &ogg_d->subs[sid]; + oss = &os->stream; + + while(1) { + np = ogg_sync_pageseek(sync,page); + if(np < 0) { // We had to skip some bytes + mp_msg(MSGT_DEMUX,MSGL_ERR,"Bad page sync while building syncpoints table (%ld)\n",-np); + pos += -np; + continue; + } + if(np <= 0) { // We need more data + char* buf = ogg_sync_buffer(sync,BLOCK_SIZE); + int len = stream_read(s,buf,BLOCK_SIZE); + if(len == 0 && s->eof) + break; + ogg_sync_wrote(sync,len); + continue; + } + // The page is ready + //ogg_sync_pageout(sync,page); + if(ogg_page_serialno(page) != os->stream.serialno) { // It isn't a page from the stream we want + pos += np; + continue; + } + if(ogg_stream_pagein(oss,page) != 0) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"Pagein error ????\n"); + pos += np; + continue; + } + p = 0; + while(ogg_stream_packetout(oss,&op) == 1) { + float pts; + int flags; + demux_ogg_read_packet(os,&op,vi,&pts,&flags); + if(flags || (os->vorbis && op.granulepos >= 0)) { + ogg_d->syncpoints = (ogg_syncpoint_t*)realloc(ogg_d->syncpoints,(ogg_d->num_syncpoint+1)*sizeof(ogg_syncpoint_t)); + ogg_d->syncpoints[ogg_d->num_syncpoint].granulepos = op.granulepos; + ogg_d->syncpoints[ogg_d->num_syncpoint].page_pos = (ogg_page_continued(page) && p == 0) ? last_pos : pos; + ogg_d->num_syncpoint++; + } + p++; + } + if(p > 0) + last_pos = pos; + pos += np; + } + + mp_msg(MSGT_DEMUX,MSGL_V,"Ogg syncpoints table builed: %d syncpoints\n",ogg_d->num_syncpoint); + + stream_reset(s); + stream_seek(s,demuxer->movi_start); + ogg_sync_reset(sync); + for(np = 0 ; np < ogg_d->num_sub ; np++) { + ogg_stream_reset(&ogg_d->subs[np].stream); + ogg_d->subs[np].lastpos = ogg_d->subs[np].lastsize = ogg_d->subs[np].hdr_packets = 0; + } + + + // Get the first page + while(1) { + np = ogg_sync_pageout(sync,page); + if(np <= 0) { // We need more data + char* buf = ogg_sync_buffer(sync,BLOCK_SIZE); + int len = stream_read(s,buf,BLOCK_SIZE); + if(len == 0 && s->eof) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"EOF while trying to get the first page !!!!\n"); + break; + } + + ogg_sync_wrote(sync,len); + continue; + } + demux_ogg_get_page_stream(ogg_d,&oss); + ogg_stream_pagein(oss,page); + break; + } + +} + /// Open an ogg physical stream int demux_ogg_open(demuxer_t* demuxer) { ogg_demuxer_t* ogg_d; @@ -276,6 +404,7 @@ int demux_ogg_open(demuxer_t* demuxer) { if(pack.bytes >= 7 && ! strncmp(&pack.packet[1],"vorbis", 6) ) { sh_a = new_sh_audio(demuxer,ogg_d->num_sub); sh_a->format = 0xFFFE; + ogg_d->subs[ogg_d->num_sub].vorbis = 1; n_audio++; mp_msg(MSGT_DEMUX,MSGL_V,"OGG : stream %d is vorbis\n",ogg_d->num_sub); @@ -411,28 +540,33 @@ int demux_ogg_open(demuxer_t* demuxer) { if(demuxer->video->id == ogg_d->num_sub) ds = demuxer->video; } - /// Add the packet contained in this page - if(ds) { - /// Finish the page, otherwise packets will be lost - do { - demux_ogg_add_packet(ds,&ogg_d->subs[ogg_d->num_sub],&pack); - } while(ogg_stream_packetout(&ogg_d->subs[ogg_d->num_sub].stream,&pack) == 1); - - } + /// Add the header packets if the stream isn't seekable + if(ds && !s->end_pos) { + /// Finish the page, otherwise packets will be lost + do { + demux_ogg_add_packet(ds,&ogg_d->subs[ogg_d->num_sub],&pack); + } while(ogg_stream_packetout(&ogg_d->subs[ogg_d->num_sub].stream,&pack) == 1); + } } ogg_d->num_sub++; } /// Finish to setup the demuxer demuxer->priv = ogg_d; - /// We can't seek :( - demuxer->seekable = 0; if(!n_video) demuxer->video->id = -2; if(!n_audio) demuxer->audio->id = -2; + if(!s->end_pos) + demuxer->seekable = 0; + else { + demuxer->movi_start = 0; + demuxer->movi_end = s->end_pos; + demux_ogg_build_syncpoints_table(demuxer); + } + mp_msg(MSGT_DEMUX,MSGL_V,"OGG demuxer : found %d audio stream and %d video stream\n",n_audio,n_video); return 1; @@ -608,6 +742,99 @@ demuxer_t* init_avi_with_ogg(demuxer_t* demuxer) { } -/// TODO : Seeking 8-) - +void demux_ogg_seek(demuxer_t *demuxer,float rel_seek_secs,int flags) { + ogg_demuxer_t* ogg_d = demuxer->priv; + ogg_sync_state* sync = &ogg_d->sync; + ogg_page* page= &ogg_d->page; + ogg_stream_state* oss; + ogg_stream_t* os; + demux_stream_t* ds; + sh_audio_t* sh_audio = demuxer->audio->sh; + //sh_video_t* sh_video = demuxer->video->sh; + ogg_packet op; + float time_pos,rate; + int i,sp; + vorbis_info* vi = NULL; + int64_t gp; + + if(!ogg_d->syncpoints) + return; + + if(demuxer->video->id >= 0) { + ds = demuxer->video; + rate = ogg_d->subs[ds->id].samplerate; + } else { + ds = demuxer->audio; + vi = &((ov_struct_t*)((sh_audio_t*)ds->sh)->context)->vi; + rate = (float)vi->rate; + } + + os = &ogg_d->subs[ds->id]; + oss = &os->stream; + + time_pos = flags & 1 ? 0 : os->lastpos/ rate; + if(flags & 2) + time_pos += ogg_d->syncpoints[ogg_d->num_syncpoint].granulepos / rate * rel_seek_secs; + else + time_pos += rel_seek_secs; + + gp = time_pos * rate; + + for(sp = 0; sp < ogg_d->num_syncpoint ; sp++) { + if(ogg_d->syncpoints[sp].granulepos >= gp) + break; + } + + if(sp >= ogg_d->num_syncpoint) + return; + + stream_seek(demuxer->stream,ogg_d->syncpoints[sp].page_pos); + ogg_sync_reset(sync); + for(i = 0 ; i < ogg_d->num_sub ; i++) + ogg_stream_reset(&ogg_d->subs[i].stream); + + while(1) { + int np = ogg_sync_pageout(sync,page); + if(np <= 0) { // We need more data + char* buf = ogg_sync_buffer(sync,BLOCK_SIZE); + int len = stream_read(demuxer->stream,buf,BLOCK_SIZE); + if(len == 0 && demuxer->stream->eof) { + mp_msg(MSGT_DEMUX,MSGL_ERR,"EOF while trying to seek !!!!\n"); + break; + } + ogg_sync_wrote(sync,len); + continue; + } + + if(ogg_page_serialno(page) != os->stream.serialno) + continue; + if(ogg_stream_pagein(oss,page) != 0) + continue; + + while(1) { + np = ogg_stream_packetpeek(oss,&op); + if(np < 0) { + ogg_stream_packetout(oss,&op); + continue; + } else if(np == 0) + break; + else { + float pts; + int f; + demux_ogg_read_packet(os,&op,vi,&pts,&f); + if(f || (os->vorbis && op.granulepos >= gp)) { + if(sh_audio) + resync_audio_stream(sh_audio); + return; + } + ogg_stream_packetout(oss,&op); + demux_ogg_read_packet(os,&op,vi,&pts,&f); + } + } + } + + mp_msg(MSGT_DEMUX,MSGL_ERR,"Can't find the good packet :(\n"); + +} + #endif diff --git a/libmpdemux/demuxer.c b/libmpdemux/demuxer.c index 18f1739d42..387bdbaea8 100644 --- a/libmpdemux/demuxer.c +++ b/libmpdemux/demuxer.c @@ -921,6 +921,7 @@ void demux_seek_mov(demuxer_t *demuxer,float pts,int flags); int demux_seek_real(demuxer_t *demuxer,float rel_seek_secs,int flags); extern void demux_audio_seek(demuxer_t *demuxer,float rel_seek_secs,int flags); extern void demux_demuxers_seek(demuxer_t *demuxer,float rel_seek_secs,int flags); +extern void demux_ogg_seek(demuxer_t *demuxer,float rel_seek_secs,int flags); int demux_seek(demuxer_t *demuxer,float rel_seek_secs,int flags){ demux_stream_t *d_audio=demuxer->audio; @@ -989,6 +990,8 @@ switch(demuxer->file_format){ demux_audio_seek(demuxer,rel_seek_secs,flags); break; case DEMUXER_TYPE_DEMUXERS: demux_demuxers_seek(demuxer,rel_seek_secs,flags); break; + case DEMUXER_TYPE_OGG: + demux_ogg_seek(demuxer,rel_seek_secs,flags); break; } // switch(demuxer->file_format) |