Mercurial > mplayer.hg
comparison libmpdemux/demux_ogg.c @ 5732:6586448b5243
Seeking implemented
author | albeu |
---|---|
date | Sat, 20 Apr 2002 17:50:11 +0000 |
parents | 779ebb242aec |
children | 4b24942acdbb |
comparison
equal
deleted
inserted
replaced
5731:56983ecb89aa | 5732:6586448b5243 |
---|---|
81 } sh; | 81 } sh; |
82 } stream_header; | 82 } stream_header; |
83 | 83 |
84 /// Our private datas | 84 /// Our private datas |
85 | 85 |
86 typedef struct ogg_syncpoint { | |
87 int64_t granulepos; | |
88 off_t page_pos; | |
89 } ogg_syncpoint_t; | |
90 | |
86 /// A logical stream | 91 /// A logical stream |
87 typedef struct ogg_stream { | 92 typedef struct ogg_stream { |
88 /// Timestamping stuff | 93 /// Timestamping stuff |
89 float samplerate; /// granulpos 2 time | 94 float samplerate; /// granulpos 2 time |
90 int64_t lastpos; | 95 int64_t lastpos; |
91 int32_t lastsize; | 96 int32_t lastsize; |
92 | 97 |
93 // Logical stream state | 98 // Logical stream state |
94 ogg_stream_state stream; | 99 ogg_stream_state stream; |
100 int hdr_packets; | |
101 int vorbis; | |
95 } ogg_stream_t; | 102 } ogg_stream_t; |
96 | 103 |
97 typedef struct ogg_demuxer { | 104 typedef struct ogg_demuxer { |
98 /// Physical stream state | 105 /// Physical stream state |
99 ogg_sync_state sync; | 106 ogg_sync_state sync; |
100 /// Current page | 107 /// Current page |
101 ogg_page page; | 108 ogg_page page; |
102 /// Logical streams | 109 /// Logical streams |
103 ogg_stream_t *subs; | 110 ogg_stream_t *subs; |
104 int num_sub; | 111 int num_sub; |
112 ogg_syncpoint_t* syncpoints; | |
113 int num_syncpoint; | |
105 } ogg_demuxer_t; | 114 } ogg_demuxer_t; |
115 | |
116 #define NUM_VORBIS_HDR_PACKETS 3 | |
106 | 117 |
107 /// Some defines from OggDS | 118 /// Some defines from OggDS |
108 #define PACKET_TYPE_HEADER 0x01 | 119 #define PACKET_TYPE_HEADER 0x01 |
109 #define PACKET_TYPE_BITS 0x07 | 120 #define PACKET_TYPE_BITS 0x07 |
110 #define PACKET_LEN_BITS01 0xc0 | 121 #define PACKET_LEN_BITS01 0xc0 |
133 | 144 |
134 return id; | 145 return id; |
135 | 146 |
136 } | 147 } |
137 | 148 |
138 /// Calculate the timestamp and add the packet to the demux stream | 149 static unsigned char* demux_ogg_read_packet(ogg_stream_t* os,ogg_packet* pack,vorbis_info* vi,float* pts,int* flags) { |
139 // return 1 if the packet was added, 0 otherwise | |
140 static int demux_ogg_add_packet(demux_stream_t* ds,ogg_stream_t* os,ogg_packet* pack) { | |
141 demuxer_t* d = ds->demuxer; | |
142 demux_packet_t* dp; | |
143 unsigned char* data; | 150 unsigned char* data; |
144 float pts = 0; | 151 |
145 int flags = 0; | 152 *pts = 0; |
146 | 153 *flags = 0; |
147 // If packet is an header we jump it except for vorbis | 154 |
148 if((*pack->packet & PACKET_TYPE_HEADER) && | 155 if(os->vorbis) { |
149 (ds == d->video || (ds == d->audio && ((sh_audio_t*)ds->sh)->format != 0xFFFE ))) | |
150 return 0; | |
151 | |
152 // For vorbis packet the packet is the data, for other codec we must jump the header | |
153 if(ds == d->audio && ((sh_audio_t*)ds->sh)->format == 0xFFFE) { | |
154 data = pack->packet; | 156 data = pack->packet; |
155 if(*pack->packet & PACKET_TYPE_HEADER) | 157 if(*pack->packet & PACKET_TYPE_HEADER) |
156 pts = 0; | 158 os->hdr_packets++; |
157 else { | 159 else if(vi) { |
158 vorbis_info* vi = &((ov_struct_t*)((sh_audio_t*)ds->sh)->context)->vi; | |
159 // When we dump the audio, there is no vi, but we dont care of timestamp in this case | 160 // When we dump the audio, there is no vi, but we dont care of timestamp in this case |
160 if(vi) { | 161 int32_t blocksize = vorbis_packet_blocksize(vi,pack) / vi->channels; |
161 int32_t blocksize = vorbis_packet_blocksize(vi,pack) / vi->channels; | 162 // Calculate the timestamp if the packet don't have any |
162 // Calculate the timestamp if the packet don't have any | 163 if(pack->granulepos == -1) { |
163 if(pack->granulepos == -1) { | 164 pack->granulepos = os->lastpos; |
164 pack->granulepos = os->lastpos; | 165 if(os->lastsize > 0) |
165 if(os->lastsize > 0) | 166 pack->granulepos += os->lastsize; |
166 pack->granulepos += os->lastsize; | |
167 } | |
168 pts = pack->granulepos / (float)vi->rate; | |
169 os->lastsize = blocksize; | |
170 os->lastpos = pack->granulepos; | |
171 } | 167 } |
168 *pts = pack->granulepos / (float)vi->rate; | |
169 os->lastsize = blocksize; | |
170 os->lastpos = pack->granulepos; | |
172 } | 171 } |
173 } else { | 172 } else { |
174 // Find data start | 173 // Find data start |
175 int16_t hdrlen = (*pack->packet & PACKET_LEN_BITS01)>>6; | 174 int16_t hdrlen = (*pack->packet & PACKET_LEN_BITS01)>>6; |
176 hdrlen |= (*pack->packet & PACKET_LEN_BITS2) <<1; | 175 hdrlen |= (*pack->packet & PACKET_LEN_BITS2) <<1; |
177 data = pack->packet + 1 + hdrlen; | 176 data = pack->packet + 1 + hdrlen; |
178 // Calculate the timestamp | 177 // Calculate the timestamp |
179 if(pack->granulepos == -1) | 178 if(pack->granulepos == -1) |
180 pack->granulepos = os->lastpos + os->lastsize; | 179 pack->granulepos = os->lastpos + os->lastsize; |
181 // If we alredy have a timestamp it can be a syncpoint | 180 // If we alredy have a timestamp it can be a syncpoint |
182 else if(*pack->packet & PACKET_IS_SYNCPOINT) | 181 if(*pack->packet & PACKET_IS_SYNCPOINT) |
183 flags = 1; | 182 *flags = 1; |
184 pts = pack->granulepos/os->samplerate; | 183 *pts = pack->granulepos/os->samplerate; |
185 // Save the packet length and timestamp | 184 // Save the packet length and timestamp |
186 os->lastsize = 0; | 185 os->lastsize = 0; |
187 while(hdrlen) { | 186 while(hdrlen) { |
188 os->lastsize <<= 8; | 187 os->lastsize <<= 8; |
189 os->lastsize |= pack->packet[hdrlen]; | 188 os->lastsize |= pack->packet[hdrlen]; |
190 hdrlen--; | 189 hdrlen--; |
191 } | 190 } |
192 os->lastpos = pack->granulepos; | 191 os->lastpos = pack->granulepos; |
193 } | 192 } |
193 return data; | |
194 } | |
195 | |
196 /// Calculate the timestamp and add the packet to the demux stream | |
197 // return 1 if the packet was added, 0 otherwise | |
198 static int demux_ogg_add_packet(demux_stream_t* ds,ogg_stream_t* os,ogg_packet* pack) { | |
199 demuxer_t* d = ds->demuxer; | |
200 demux_packet_t* dp; | |
201 unsigned char* data; | |
202 float pts = 0; | |
203 int flags = 0; | |
204 | |
205 // If packet is an header we jump it except for vorbis | |
206 if((*pack->packet & PACKET_TYPE_HEADER) && | |
207 (ds == d->video || (ds == d->audio && ( ((sh_audio_t*)ds->sh)->format != 0xFFFE || os->hdr_packets >= NUM_VORBIS_HDR_PACKETS ) ) )) | |
208 return 0; | |
209 | |
210 // For vorbis packet the packet is the data, for other codec we must jump the header | |
211 if(ds == d->audio && ((sh_audio_t*)ds->sh)->format == 0xFFFE) | |
212 data = demux_ogg_read_packet(os,pack,&((ov_struct_t*)((sh_audio_t*)ds->sh)->context)->vi, | |
213 &pts,&flags); | |
214 else | |
215 data = demux_ogg_read_packet(os,pack,NULL,&pts,&flags); | |
216 | |
194 | 217 |
195 /// Send the packet | 218 /// Send the packet |
196 dp = new_demux_packet(pack->bytes-(data-pack->packet)); | 219 dp = new_demux_packet(pack->bytes-(data-pack->packet)); |
197 memcpy(dp->buffer,data,pack->bytes-(data-pack->packet)); | 220 memcpy(dp->buffer,data,pack->bytes-(data-pack->packet)); |
198 dp->pts = pts; | 221 dp->pts = pts; |
199 dp->flags = flags; | 222 dp->flags = flags; |
200 ds_add_packet(ds,dp); | 223 ds_add_packet(ds,dp); |
201 if(verbose>1)printf("New dp: %p ds=%p pts=%5.3f len=%d flag=%d \n", | 224 if(verbose>1)printf("New dp: %p ds=%p pts=%5.3f len=%d flag=%d \n", |
202 dp, ds, pts, dp->len, flags); | 225 dp, ds, pts, dp->len, flags); |
203 return 1; | 226 return 1; |
227 } | |
228 | |
229 /// Build a table of all syncpoints to make seeking easier | |
230 void demux_ogg_build_syncpoints_table(demuxer_t* demuxer) { | |
231 ogg_demuxer_t* ogg_d = demuxer->priv; | |
232 stream_t *s = demuxer->stream; | |
233 ogg_sync_state* sync = &ogg_d->sync; | |
234 ogg_page* page= &ogg_d->page; | |
235 ogg_stream_state* oss; | |
236 ogg_stream_t* os; | |
237 ogg_packet op; | |
238 int np,sid,p; | |
239 vorbis_info* vi = NULL; | |
240 off_t pos, last_pos; | |
241 pos = last_pos = demuxer->movi_start; | |
242 | |
243 // Reset the stream | |
244 stream_seek(s,demuxer->movi_start); | |
245 ogg_sync_reset(sync); | |
246 | |
247 // Get the serial number of the stream we use | |
248 if(demuxer->video->id >= 0) | |
249 sid = demuxer->video->id; | |
250 else { | |
251 sid = demuxer->audio->id; | |
252 if(((sh_audio_t*)demuxer->audio->sh)->format == 0xFFFE) | |
253 vi = &((ov_struct_t*)((sh_audio_t*)demuxer->audio->sh)->context)->vi; | |
254 } | |
255 os = &ogg_d->subs[sid]; | |
256 oss = &os->stream; | |
257 | |
258 while(1) { | |
259 np = ogg_sync_pageseek(sync,page); | |
260 if(np < 0) { // We had to skip some bytes | |
261 mp_msg(MSGT_DEMUX,MSGL_ERR,"Bad page sync while building syncpoints table (%ld)\n",-np); | |
262 pos += -np; | |
263 continue; | |
264 } | |
265 if(np <= 0) { // We need more data | |
266 char* buf = ogg_sync_buffer(sync,BLOCK_SIZE); | |
267 int len = stream_read(s,buf,BLOCK_SIZE); | |
268 if(len == 0 && s->eof) | |
269 break; | |
270 ogg_sync_wrote(sync,len); | |
271 continue; | |
272 } | |
273 // The page is ready | |
274 //ogg_sync_pageout(sync,page); | |
275 if(ogg_page_serialno(page) != os->stream.serialno) { // It isn't a page from the stream we want | |
276 pos += np; | |
277 continue; | |
278 } | |
279 if(ogg_stream_pagein(oss,page) != 0) { | |
280 mp_msg(MSGT_DEMUX,MSGL_ERR,"Pagein error ????\n"); | |
281 pos += np; | |
282 continue; | |
283 } | |
284 p = 0; | |
285 while(ogg_stream_packetout(oss,&op) == 1) { | |
286 float pts; | |
287 int flags; | |
288 demux_ogg_read_packet(os,&op,vi,&pts,&flags); | |
289 if(flags || (os->vorbis && op.granulepos >= 0)) { | |
290 ogg_d->syncpoints = (ogg_syncpoint_t*)realloc(ogg_d->syncpoints,(ogg_d->num_syncpoint+1)*sizeof(ogg_syncpoint_t)); | |
291 ogg_d->syncpoints[ogg_d->num_syncpoint].granulepos = op.granulepos; | |
292 ogg_d->syncpoints[ogg_d->num_syncpoint].page_pos = (ogg_page_continued(page) && p == 0) ? last_pos : pos; | |
293 ogg_d->num_syncpoint++; | |
294 } | |
295 p++; | |
296 } | |
297 if(p > 0) | |
298 last_pos = pos; | |
299 pos += np; | |
300 } | |
301 | |
302 mp_msg(MSGT_DEMUX,MSGL_V,"Ogg syncpoints table builed: %d syncpoints\n",ogg_d->num_syncpoint); | |
303 | |
304 stream_reset(s); | |
305 stream_seek(s,demuxer->movi_start); | |
306 ogg_sync_reset(sync); | |
307 for(np = 0 ; np < ogg_d->num_sub ; np++) { | |
308 ogg_stream_reset(&ogg_d->subs[np].stream); | |
309 ogg_d->subs[np].lastpos = ogg_d->subs[np].lastsize = ogg_d->subs[np].hdr_packets = 0; | |
310 } | |
311 | |
312 | |
313 // Get the first page | |
314 while(1) { | |
315 np = ogg_sync_pageout(sync,page); | |
316 if(np <= 0) { // We need more data | |
317 char* buf = ogg_sync_buffer(sync,BLOCK_SIZE); | |
318 int len = stream_read(s,buf,BLOCK_SIZE); | |
319 if(len == 0 && s->eof) { | |
320 mp_msg(MSGT_DEMUX,MSGL_ERR,"EOF while trying to get the first page !!!!\n"); | |
321 break; | |
322 } | |
323 | |
324 ogg_sync_wrote(sync,len); | |
325 continue; | |
326 } | |
327 demux_ogg_get_page_stream(ogg_d,&oss); | |
328 ogg_stream_pagein(oss,page); | |
329 break; | |
330 } | |
331 | |
204 } | 332 } |
205 | 333 |
206 /// Open an ogg physical stream | 334 /// Open an ogg physical stream |
207 int demux_ogg_open(demuxer_t* demuxer) { | 335 int demux_ogg_open(demuxer_t* demuxer) { |
208 ogg_demuxer_t* ogg_d; | 336 ogg_demuxer_t* ogg_d; |
274 | 402 |
275 // Check for Vorbis | 403 // Check for Vorbis |
276 if(pack.bytes >= 7 && ! strncmp(&pack.packet[1],"vorbis", 6) ) { | 404 if(pack.bytes >= 7 && ! strncmp(&pack.packet[1],"vorbis", 6) ) { |
277 sh_a = new_sh_audio(demuxer,ogg_d->num_sub); | 405 sh_a = new_sh_audio(demuxer,ogg_d->num_sub); |
278 sh_a->format = 0xFFFE; | 406 sh_a->format = 0xFFFE; |
407 ogg_d->subs[ogg_d->num_sub].vorbis = 1; | |
279 n_audio++; | 408 n_audio++; |
280 mp_msg(MSGT_DEMUX,MSGL_V,"OGG : stream %d is vorbis\n",ogg_d->num_sub); | 409 mp_msg(MSGT_DEMUX,MSGL_V,"OGG : stream %d is vorbis\n",ogg_d->num_sub); |
281 | 410 |
282 /// Check for old header | 411 /// Check for old header |
283 } else if(pack.bytes >= 142 && ! strncmp(&pack.packet[1],"Direct Show Samples embedded in Ogg",35) ) { | 412 } else if(pack.bytes >= 142 && ! strncmp(&pack.packet[1],"Direct Show Samples embedded in Ogg",35) ) { |
409 // if(sh_v->bih) print_video_header(sh_v->bih); | 538 // if(sh_v->bih) print_video_header(sh_v->bih); |
410 } | 539 } |
411 if(demuxer->video->id == ogg_d->num_sub) | 540 if(demuxer->video->id == ogg_d->num_sub) |
412 ds = demuxer->video; | 541 ds = demuxer->video; |
413 } | 542 } |
414 /// Add the packet contained in this page | 543 /// Add the header packets if the stream isn't seekable |
415 if(ds) { | 544 if(ds && !s->end_pos) { |
416 /// Finish the page, otherwise packets will be lost | 545 /// Finish the page, otherwise packets will be lost |
417 do { | 546 do { |
418 demux_ogg_add_packet(ds,&ogg_d->subs[ogg_d->num_sub],&pack); | 547 demux_ogg_add_packet(ds,&ogg_d->subs[ogg_d->num_sub],&pack); |
419 } while(ogg_stream_packetout(&ogg_d->subs[ogg_d->num_sub].stream,&pack) == 1); | 548 } while(ogg_stream_packetout(&ogg_d->subs[ogg_d->num_sub].stream,&pack) == 1); |
420 | 549 } |
421 } | |
422 } | 550 } |
423 ogg_d->num_sub++; | 551 ogg_d->num_sub++; |
424 } | 552 } |
425 | 553 |
426 /// Finish to setup the demuxer | 554 /// Finish to setup the demuxer |
427 demuxer->priv = ogg_d; | 555 demuxer->priv = ogg_d; |
428 /// We can't seek :( | |
429 demuxer->seekable = 0; | |
430 | 556 |
431 if(!n_video) | 557 if(!n_video) |
432 demuxer->video->id = -2; | 558 demuxer->video->id = -2; |
433 if(!n_audio) | 559 if(!n_audio) |
434 demuxer->audio->id = -2; | 560 demuxer->audio->id = -2; |
561 | |
562 if(!s->end_pos) | |
563 demuxer->seekable = 0; | |
564 else { | |
565 demuxer->movi_start = 0; | |
566 demuxer->movi_end = s->end_pos; | |
567 demux_ogg_build_syncpoints_table(demuxer); | |
568 } | |
435 | 569 |
436 mp_msg(MSGT_DEMUX,MSGL_V,"OGG demuxer : found %d audio stream and %d video stream\n",n_audio,n_video); | 570 mp_msg(MSGT_DEMUX,MSGL_V,"OGG demuxer : found %d audio stream and %d video stream\n",n_audio,n_video); |
437 | 571 |
438 return 1; | 572 return 1; |
439 } | 573 } |
606 demuxer->audio->id = -2; | 740 demuxer->audio->id = -2; |
607 return demuxer; | 741 return demuxer; |
608 | 742 |
609 } | 743 } |
610 | 744 |
611 /// TODO : Seeking 8-) | 745 void demux_ogg_seek(demuxer_t *demuxer,float rel_seek_secs,int flags) { |
612 | 746 ogg_demuxer_t* ogg_d = demuxer->priv; |
747 ogg_sync_state* sync = &ogg_d->sync; | |
748 ogg_page* page= &ogg_d->page; | |
749 ogg_stream_state* oss; | |
750 ogg_stream_t* os; | |
751 demux_stream_t* ds; | |
752 sh_audio_t* sh_audio = demuxer->audio->sh; | |
753 //sh_video_t* sh_video = demuxer->video->sh; | |
754 ogg_packet op; | |
755 float time_pos,rate; | |
756 int i,sp; | |
757 vorbis_info* vi = NULL; | |
758 int64_t gp; | |
759 | |
760 if(!ogg_d->syncpoints) | |
761 return; | |
762 | |
763 if(demuxer->video->id >= 0) { | |
764 ds = demuxer->video; | |
765 rate = ogg_d->subs[ds->id].samplerate; | |
766 } else { | |
767 ds = demuxer->audio; | |
768 vi = &((ov_struct_t*)((sh_audio_t*)ds->sh)->context)->vi; | |
769 rate = (float)vi->rate; | |
770 } | |
771 | |
772 os = &ogg_d->subs[ds->id]; | |
773 oss = &os->stream; | |
774 | |
775 time_pos = flags & 1 ? 0 : os->lastpos/ rate; | |
776 if(flags & 2) | |
777 time_pos += ogg_d->syncpoints[ogg_d->num_syncpoint].granulepos / rate * rel_seek_secs; | |
778 else | |
779 time_pos += rel_seek_secs; | |
780 | |
781 gp = time_pos * rate; | |
782 | |
783 for(sp = 0; sp < ogg_d->num_syncpoint ; sp++) { | |
784 if(ogg_d->syncpoints[sp].granulepos >= gp) | |
785 break; | |
786 } | |
787 | |
788 if(sp >= ogg_d->num_syncpoint) | |
789 return; | |
790 | |
791 stream_seek(demuxer->stream,ogg_d->syncpoints[sp].page_pos); | |
792 ogg_sync_reset(sync); | |
793 for(i = 0 ; i < ogg_d->num_sub ; i++) | |
794 ogg_stream_reset(&ogg_d->subs[i].stream); | |
795 | |
796 while(1) { | |
797 int np = ogg_sync_pageout(sync,page); | |
798 if(np <= 0) { // We need more data | |
799 char* buf = ogg_sync_buffer(sync,BLOCK_SIZE); | |
800 int len = stream_read(demuxer->stream,buf,BLOCK_SIZE); | |
801 if(len == 0 && demuxer->stream->eof) { | |
802 mp_msg(MSGT_DEMUX,MSGL_ERR,"EOF while trying to seek !!!!\n"); | |
803 break; | |
804 } | |
805 ogg_sync_wrote(sync,len); | |
806 continue; | |
807 } | |
808 | |
809 if(ogg_page_serialno(page) != os->stream.serialno) | |
810 continue; | |
811 if(ogg_stream_pagein(oss,page) != 0) | |
812 continue; | |
813 | |
814 while(1) { | |
815 np = ogg_stream_packetpeek(oss,&op); | |
816 if(np < 0) { | |
817 ogg_stream_packetout(oss,&op); | |
818 continue; | |
819 } else if(np == 0) | |
820 break; | |
821 else { | |
822 float pts; | |
823 int f; | |
824 demux_ogg_read_packet(os,&op,vi,&pts,&f); | |
825 if(f || (os->vorbis && op.granulepos >= gp)) { | |
826 if(sh_audio) | |
827 resync_audio_stream(sh_audio); | |
828 return; | |
829 } | |
830 ogg_stream_packetout(oss,&op); | |
831 demux_ogg_read_packet(os,&op,vi,&pts,&f); | |
832 } | |
833 } | |
834 } | |
835 | |
836 mp_msg(MSGT_DEMUX,MSGL_ERR,"Can't find the good packet :(\n"); | |
837 | |
838 } | |
839 | |
613 #endif | 840 #endif |