0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 12:02:23 +02:00

Seeking implemented

git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@5733 b3059339-0415-0410-9bf9-f77b7e298cf2
This commit is contained in:
albeu 2002-04-20 17:50:11 +00:00
parent 291c078ade
commit 29a46375a8
2 changed files with 284 additions and 54 deletions

View File

@ -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,6 +146,53 @@ static int demux_ogg_get_page_stream(ogg_demuxer_t* ogg_d,ogg_stream_state** os
}
static unsigned char* demux_ogg_read_packet(ogg_stream_t* os,ogg_packet* pack,vorbis_info* vi,float* pts,int* flags) {
unsigned char* data;
*pts = 0;
*flags = 0;
if(os->vorbis) {
data = pack->packet;
if(*pack->packet & PACKET_TYPE_HEADER)
os->hdr_packets++;
else if(vi) {
// When we dump the audio, there is no vi, but we dont care of timestamp in this case
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
int16_t hdrlen = (*pack->packet & PACKET_LEN_BITS01)>>6;
hdrlen |= (*pack->packet & PACKET_LEN_BITS2) <<1;
data = pack->packet + 1 + hdrlen;
// Calculate the timestamp
if(pack->granulepos == -1)
pack->granulepos = os->lastpos + os->lastsize;
// If we alredy have a timestamp it can be a syncpoint
if(*pack->packet & PACKET_IS_SYNCPOINT)
*flags = 1;
*pts = pack->granulepos/os->samplerate;
// Save the packet length and timestamp
os->lastsize = 0;
while(hdrlen) {
os->lastsize <<= 8;
os->lastsize |= pack->packet[hdrlen];
hdrlen--;
}
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) {
@ -146,51 +204,16 @@ static int demux_ogg_add_packet(demux_stream_t* ds,ogg_stream_t* os,ogg_packet*
// 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 )))
(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 = pack->packet;
if(*pack->packet & PACKET_TYPE_HEADER)
pts = 0;
else {
vorbis_info* vi = &((ov_struct_t*)((sh_audio_t*)ds->sh)->context)->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;
}
}
} else {
// Find data start
int16_t hdrlen = (*pack->packet & PACKET_LEN_BITS01)>>6;
hdrlen |= (*pack->packet & PACKET_LEN_BITS2) <<1;
data = pack->packet + 1 + hdrlen;
// Calculate the timestamp
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;
// Save the packet length and timestamp
os->lastsize = 0;
while(hdrlen) {
os->lastsize <<= 8;
os->lastsize |= pack->packet[hdrlen];
hdrlen--;
}
os->lastpos = pack->granulepos;
}
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

View File

@ -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)