2 Copyright (C) 2003 Commonwealth Scientific and Industrial Research
3 Organisation (CSIRO) Australia
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
9 - Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
12 - Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 - Neither the name of CSIRO Australia nor the names of its
17 contributors may be used to endorse or promote products derived from
18 this software without specific prior written permission.
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR
24 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 * Shane Stephens <shane.stephens@annodex.net>
40 #include "oggplay_private.h"
41 #include "oggplay_buffer.h"
46 #define OGGZ_READ_CHUNK_SIZE 8192
49 oggplay_new_with_reader(OggPlayReader
*reader
) {
53 /* check whether the reader is valid. */
57 me
= (OggPlay
*)oggplay_malloc (sizeof(OggPlay
));
62 me
->decode_data
= NULL
;
63 me
->callback_info
= NULL
;
65 me
->all_tracks_initialised
= 0;
66 me
->callback_period
= 0;
69 me
->active_tracks
= 0;
74 me
->pt_update_valid
= 1;
82 oggplay_initialise(OggPlay
*me
, int block
) {
84 OggPlayErrorCode return_val
;
88 return E_OGGPLAY_BAD_OGGPLAY
;
91 return_val
= me
->reader
->initialise(me
->reader
, block
);
93 if (return_val
!= E_OGGPLAY_OK
) {
98 * this is the cut-off time value below which packets will be ignored. Initialise it to 0 here.
99 * We'll reinitialise it when/if we encounter a skeleton header
101 me
->presentation_time
= 0;
104 * start to retrieve data, until we get all of the track info. We need
105 * to do this now so that the user can query us for this info before entering
108 me
->oggz
= oggz_new(OGGZ_READ
| OGGZ_AUTO
);
109 if (me
->oggz
== NULL
)
110 return E_OGGPLAY_OGGZ_UNHAPPY
;
112 if (oggz_io_set_read(me
->oggz
, me
->reader
->io_read
, me
->reader
) != 0)
113 return E_OGGPLAY_OGGZ_UNHAPPY
;
115 if (oggz_io_set_seek(me
->oggz
, me
->reader
->io_seek
, me
->reader
) != 0)
116 return E_OGGPLAY_OGGZ_UNHAPPY
;
118 if (oggz_io_set_tell(me
->oggz
, me
->reader
->io_tell
, me
->reader
) != 0)
119 return E_OGGPLAY_OGGZ_UNHAPPY
;
121 if (oggz_set_read_callback(me
->oggz
, -1, oggplay_callback_predetected
, me
))
122 return E_OGGPLAY_OGGZ_UNHAPPY
;
125 i
= oggz_read (me
->oggz
, OGGZ_READ_CHUNK_SIZE
);
130 * EOF reached while processing headers,
131 * possible erroneous file, mark it as such.
133 case OGGZ_ERR_HOLE_IN_DATA
:
134 /* there was a whole in the data */
135 return E_OGGPLAY_BAD_INPUT
;
137 case OGGZ_ERR_OUT_OF_MEMORY
:
138 /* ran out of memory during decoding! */
139 return E_OGGPLAY_OUT_OF_MEMORY
;
141 case OGGZ_ERR_STOP_ERR
:
142 /* error occured while initialising headers
143 - only occures with bad OggPlay handle */
144 return E_OGGPLAY_BAD_OGGPLAY
;
146 case OGGZ_ERR_BAD_OGGZ
:
147 /* bad Oggz handle */
148 return E_OGGPLAY_OGGZ_UNHAPPY
;
150 case OGGZ_ERR_STOP_OK
:
151 /* while initialising the streams, we read the
152 header packets in a packet-by-packet manner */
158 return E_OGGPLAY_BAD_INPUT
;
161 if (me
->all_tracks_initialised
) {
167 * set all the tracks to inactive
169 for (i
= 0; i
< me
->num_tracks
; i
++) {
170 me
->decode_data
[i
]->active
= 0;
174 * if the buffer was set up before initialisation, prepare it now
176 if (me
->buffer
!= NULL
) {
177 oggplay_buffer_prepare(me
);
185 oggplay_open_with_reader(OggPlayReader
*reader
) {
188 int r
= E_OGGPLAY_TIMEOUT
;
190 if ((me
= oggplay_new_with_reader(reader
)) == NULL
)
193 while (r
== E_OGGPLAY_TIMEOUT
) {
194 r
= oggplay_initialise(me
, 0);
197 if (r
!= E_OGGPLAY_OK
) {
199 /* in case of error close the OggPlay handle */
209 * API function to prevent bad input, and to prevent data callbacks being registered
213 oggplay_set_data_callback(OggPlay
*me
, OggPlayDataCallback callback
,
217 return E_OGGPLAY_BAD_OGGPLAY
;
220 if (me
->buffer
!= NULL
) {
221 return E_OGGPLAY_BUFFER_MODE
;
224 oggplay_set_data_callback_force(me
, callback
, user
);
230 * internal function that doesn't perform error checking. Used so the buffer
231 * can register a callback!
234 oggplay_set_data_callback_force(OggPlay
*me
, OggPlayDataCallback callback
,
237 me
->callback
= callback
;
238 me
->callback_user_ptr
= user
;
244 oggplay_set_callback_num_frames(OggPlay
*me
, int track
, int frames
) {
247 return E_OGGPLAY_BAD_OGGPLAY
;
250 if (track
< 0 || track
>= me
->num_tracks
) {
251 return E_OGGPLAY_BAD_TRACK
;
254 me
->callback_period
= me
->decode_data
[track
]->granuleperiod
* frames
;
255 me
->target
= me
->presentation_time
+ me
->callback_period
- 1;
261 oggplay_set_callback_period(OggPlay
*me
, int track
, int milliseconds
) {
264 return E_OGGPLAY_BAD_OGGPLAY
;
267 if (track
< 0 || track
>= me
->num_tracks
) {
268 return E_OGGPLAY_BAD_TRACK
;
271 me
->callback_period
= OGGPLAY_TIME_INT_TO_FP(((ogg_int64_t
)milliseconds
))/1000;
272 me
->target
= me
->presentation_time
+ me
->callback_period
- 1;
278 oggplay_set_offset(OggPlay
*me
, int track
, ogg_int64_t offset
) {
281 return E_OGGPLAY_BAD_OGGPLAY
;
284 if (track
< 0 || track
>= me
->num_tracks
) {
285 return E_OGGPLAY_BAD_TRACK
;
288 me
->decode_data
[track
]->offset
= OGGPLAY_TIME_INT_TO_FP(offset
) / 1000;
295 oggplay_get_video_fps(OggPlay
*me
, int track
, int* fps_denom
, int* fps_num
) {
296 OggPlayTheoraDecode
*decode
;
299 return E_OGGPLAY_BAD_OGGPLAY
;
302 if (track
< 0 || track
>= me
->num_tracks
) {
303 return E_OGGPLAY_BAD_TRACK
;
306 if (me
->decode_data
[track
]->decoded_type
!= OGGPLAY_YUV_VIDEO
) {
307 return E_OGGPLAY_WRONG_TRACK_TYPE
;
310 decode
= (OggPlayTheoraDecode
*)(me
->decode_data
[track
]);
312 if ((decode
->video_info
.fps_denominator
== 0)
313 || (decode
->video_info
.fps_numerator
== 0)) {
314 return E_OGGPLAY_UNINITIALISED
;
317 (*fps_denom
) = decode
->video_info
.fps_denominator
;
318 (*fps_num
) = decode
->video_info
.fps_numerator
;
324 oggplay_get_video_aspect_ratio(OggPlay
*me
, int track
, int* aspect_denom
, int* aspect_num
) {
325 OggPlayTheoraDecode
*decode
;
328 return E_OGGPLAY_BAD_OGGPLAY
;
331 if (track
< 0 || track
>= me
->num_tracks
) {
332 return E_OGGPLAY_BAD_TRACK
;
335 if (me
->decode_data
[track
]->decoded_type
!= OGGPLAY_YUV_VIDEO
) {
336 return E_OGGPLAY_WRONG_TRACK_TYPE
;
339 decode
= (OggPlayTheoraDecode
*)(me
->decode_data
[track
]);
341 if ((decode
->video_info
.aspect_denominator
== 0)
342 || (decode
->video_info
.aspect_numerator
== 0)) {
343 return E_OGGPLAY_UNINITIALISED
;
346 (*aspect_denom
) = decode
->video_info
.aspect_denominator
;
347 (*aspect_num
) = decode
->video_info
.aspect_numerator
;
353 oggplay_convert_video_to_rgb(OggPlay
*me
, int track
, int convert
, int swap_rgb
) {
354 OggPlayTheoraDecode
*decode
;
357 return E_OGGPLAY_BAD_OGGPLAY
;
360 if (track
< 0 || track
>= me
->num_tracks
) {
361 return E_OGGPLAY_BAD_TRACK
;
364 if (me
->decode_data
[track
]->content_type
!= OGGZ_CONTENT_THEORA
) {
365 return E_OGGPLAY_WRONG_TRACK_TYPE
;
368 decode
= (OggPlayTheoraDecode
*)(me
->decode_data
[track
]);
370 if (decode
->convert_to_rgb
!= convert
|| decode
->swap_rgb
!= swap_rgb
) {
371 decode
->convert_to_rgb
= convert
;
372 decode
->swap_rgb
= swap_rgb
;
373 me
->decode_data
[track
]->decoded_type
= convert
? OGGPLAY_RGBA_VIDEO
: OGGPLAY_YUV_VIDEO
;
375 /* flush any records created with previous type */
376 oggplay_data_free_list(me
->decode_data
[track
]->data_list
);
377 me
->decode_data
[track
]->data_list
= NULL
;
384 oggplay_get_video_y_size(OggPlay
*me
, int track
, int *y_width
, int *y_height
) {
386 OggPlayTheoraDecode
*decode
;
389 return E_OGGPLAY_BAD_OGGPLAY
;
392 if (track
< 0 || track
>= me
->num_tracks
) {
393 return E_OGGPLAY_BAD_TRACK
;
396 if (me
->decode_data
[track
]->decoded_type
!= OGGPLAY_YUV_VIDEO
) {
397 return E_OGGPLAY_WRONG_TRACK_TYPE
;
400 decode
= (OggPlayTheoraDecode
*)(me
->decode_data
[track
]);
402 if (decode
->y_width
== 0) {
403 return E_OGGPLAY_UNINITIALISED
;
406 (*y_width
) = decode
->y_width
;
407 (*y_height
) = decode
->y_height
;
414 oggplay_get_video_uv_size(OggPlay
*me
, int track
, int *uv_width
, int *uv_height
)
417 OggPlayTheoraDecode
*decode
;
420 return E_OGGPLAY_BAD_OGGPLAY
;
423 if (track
< 0 || track
>= me
->num_tracks
) {
424 return E_OGGPLAY_BAD_TRACK
;
427 if (me
->decode_data
[track
]->decoded_type
!= OGGPLAY_YUV_VIDEO
) {
428 return E_OGGPLAY_WRONG_TRACK_TYPE
;
431 decode
= (OggPlayTheoraDecode
*)(me
->decode_data
[track
]);
433 if (decode
->y_width
== 0) {
434 return E_OGGPLAY_UNINITIALISED
;
437 (*uv_width
) = decode
->uv_width
;
438 (*uv_height
) = decode
->uv_height
;
445 oggplay_get_audio_channels(OggPlay
*me
, int track
, int* channels
) {
447 OggPlayAudioDecode
*decode
;
450 return E_OGGPLAY_BAD_OGGPLAY
;
453 if (track
< 0 || track
>= me
->num_tracks
) {
454 return E_OGGPLAY_BAD_TRACK
;
457 if (me
->decode_data
[track
]->decoded_type
!= OGGPLAY_FLOATS_AUDIO
) {
458 return E_OGGPLAY_WRONG_TRACK_TYPE
;
461 decode
= (OggPlayAudioDecode
*)(me
->decode_data
[track
]);
463 if (decode
->sound_info
.channels
== 0) {
464 return E_OGGPLAY_UNINITIALISED
;
466 (*channels
) = decode
->sound_info
.channels
;
472 oggplay_get_audio_samplerate(OggPlay
*me
, int track
, int* rate
) {
474 OggPlayAudioDecode
* decode
;
477 return E_OGGPLAY_BAD_OGGPLAY
;
480 if (track
< 0 || track
>= me
->num_tracks
) {
481 return E_OGGPLAY_BAD_TRACK
;
484 if (me
->decode_data
[track
]->decoded_type
!= OGGPLAY_FLOATS_AUDIO
) {
485 return E_OGGPLAY_WRONG_TRACK_TYPE
;
488 decode
= (OggPlayAudioDecode
*)(me
->decode_data
[track
]);
490 if (decode
->sound_info
.channels
== 0) {
491 return E_OGGPLAY_UNINITIALISED
;
493 (*rate
) = decode
->sound_info
.samplerate
;
499 oggplay_get_kate_category(OggPlay
*me
, int track
, const char** category
) {
501 OggPlayKateDecode
* decode
;
504 return E_OGGPLAY_BAD_OGGPLAY
;
507 if (track
< 0 || track
>= me
->num_tracks
) {
508 return E_OGGPLAY_BAD_TRACK
;
511 if (me
->decode_data
[track
]->content_type
!= OGGZ_CONTENT_KATE
) {
512 return E_OGGPLAY_WRONG_TRACK_TYPE
;
515 decode
= (OggPlayKateDecode
*)(me
->decode_data
[track
]);
518 if (decode
->decoder
.initialised
== 1) {
519 (*category
) = decode
->k_state
.ki
->category
;
522 else return E_OGGPLAY_UNINITIALISED
;
524 return E_OGGPLAY_NO_KATE_SUPPORT
;
529 oggplay_get_kate_language(OggPlay
*me
, int track
, const char** language
) {
531 OggPlayKateDecode
* decode
;
534 return E_OGGPLAY_BAD_OGGPLAY
;
537 if (track
< 0 || track
>= me
->num_tracks
) {
538 return E_OGGPLAY_BAD_TRACK
;
541 if (me
->decode_data
[track
]->content_type
!= OGGZ_CONTENT_KATE
) {
542 return E_OGGPLAY_WRONG_TRACK_TYPE
;
545 decode
= (OggPlayKateDecode
*)(me
->decode_data
[track
]);
548 if (decode
->decoder
.initialised
== 1) {
549 (*language
) = decode
->k_state
.ki
->language
;
552 else return E_OGGPLAY_UNINITIALISED
;
554 return E_OGGPLAY_NO_KATE_SUPPORT
;
559 oggplay_set_kate_tiger_rendering(OggPlay
*me
, int track
, int use_tiger
, int swap_rgb
, int default_width
, int default_height
) {
561 OggPlayKateDecode
* decode
;
564 return E_OGGPLAY_BAD_OGGPLAY
;
567 if (track
< 0 || track
>= me
->num_tracks
) {
568 return E_OGGPLAY_BAD_TRACK
;
571 if (me
->decode_data
[track
]->content_type
!= OGGZ_CONTENT_KATE
) {
572 return E_OGGPLAY_WRONG_TRACK_TYPE
;
575 decode
= (OggPlayKateDecode
*)(me
->decode_data
[track
]);
579 if (decode
->decoder
.initialised
== 1 && decode
->tr
) {
580 decode
->use_tiger
= use_tiger
;
581 decode
->swap_rgb
= swap_rgb
;
582 decode
->default_width
= default_width
;
583 decode
->default_height
= default_height
;
584 decode
->decoder
.decoded_type
= use_tiger
? OGGPLAY_RGBA_VIDEO
: OGGPLAY_KATE
;
587 else return E_OGGPLAY_UNINITIALISED
;
589 return E_OGGPLAY_NO_TIGER_SUPPORT
;
592 return E_OGGPLAY_NO_KATE_SUPPORT
;
597 oggplay_overlay_kate_track_on_video(OggPlay
*me
, int kate_track
, int video_track
) {
599 OggPlayKateDecode
* decode
;
602 return E_OGGPLAY_BAD_OGGPLAY
;
605 if (kate_track
< 0 || kate_track
>= me
->num_tracks
) {
606 return E_OGGPLAY_BAD_TRACK
;
608 if (video_track
< 0 || video_track
>= me
->num_tracks
) {
609 return E_OGGPLAY_BAD_TRACK
;
612 if (me
->decode_data
[kate_track
]->content_type
!= OGGZ_CONTENT_KATE
) {
613 return E_OGGPLAY_WRONG_TRACK_TYPE
;
616 if (me
->decode_data
[kate_track
]->decoded_type
!= OGGPLAY_RGBA_VIDEO
) {
617 return E_OGGPLAY_WRONG_TRACK_TYPE
;
620 if (me
->decode_data
[video_track
]->content_type
!= OGGZ_CONTENT_THEORA
) {
621 return E_OGGPLAY_WRONG_TRACK_TYPE
;
624 if (me
->decode_data
[video_track
]->decoded_type
!= OGGPLAY_RGBA_VIDEO
) {
625 return E_OGGPLAY_WRONG_TRACK_TYPE
;
628 decode
= (OggPlayKateDecode
*)(me
->decode_data
[kate_track
]);
632 decode
->overlay_dest
= video_track
;
635 return E_OGGPLAY_NO_TIGER_SUPPORT
;
638 return E_OGGPLAY_NO_KATE_SUPPORT
;
642 #define MAX_CHUNK_COUNT 10
645 oggplay_step_decoding(OggPlay
*me
) {
647 OggPlayCallbackInfo
** info
;
655 return E_OGGPLAY_BAD_OGGPLAY
;
659 * check whether the OggPlayDataCallback is set for the given
660 * OggPlay handle. If not return with error as there's no callback
661 * function processing the decoded data.
663 if (me
->callback
== NULL
) {
664 return E_OGGPLAY_UNINITIALISED
;
668 * clean up any trash pointers. As soon as the current buffer has a
669 * frame taken out, we know the old buffer will no longer be used.
676 (me
->buffer
== NULL
|| me
->buffer
->last_emptied
> -1)
679 oggplay_take_out_trash(me
, me
->trash
);
687 * if there are no active tracks, we might need to return some data
688 * left over at the end of a once-active track that has had all of its
689 * data processed. Look through the tracks to find these overhangs
693 if (me
->active_tracks
== 0) {
695 for (i
= 0; i
< me
->num_tracks
; i
++) {
696 if (me
->decode_data
[i
]->current_loc
+
697 me
->decode_data
[i
]->granuleperiod
>= me
->target
+ me
->decode_data
[i
]->offset
) {
701 if (remaining
== 0) {
707 * if any of the tracks have not yet met the target (modified by that
708 * track's offset), then retrieve more data
711 for (i
= 0; i
< me
->num_tracks
; i
++) {
712 if (me
->decode_data
[i
]->active
== 0)
714 if (me
->decode_data
[i
]->content_type
== OGGZ_CONTENT_CMML
)
716 if (me
->decode_data
[i
]->content_type
== OGGZ_CONTENT_KATE
)
720 me
->decode_data
[i
]->current_loc
722 me
->target
+ me
->decode_data
[i
]->offset
735 * get a chunk of data. If we're at the end of the file, then we must
736 * have some final frames to render (?). E_OGGPLAY_END_OF_FILE is
737 * only returned if there is *no* more data.
740 if (chunk_count
> MAX_CHUNK_COUNT
) {
741 return E_OGGPLAY_TIMEOUT
;
746 r
= oggz_read(me
->oggz
, OGGZ_READ_CHUNK_SIZE
);
752 num_records
= oggplay_callback_info_prepare(me
, &info
);
754 * set all of the tracks to inactive
756 for (i
= 0; i
< me
->num_tracks
; i
++) {
757 me
->decode_data
[i
]->active
= 0;
759 me
->active_tracks
= 0;
762 me
->callback (me
, num_records
, info
, me
->callback_user_ptr
);
763 oggplay_callback_info_destroy(me
, info
);
767 * ensure all tracks have their final data packet set to end_of_stream
768 * But skip doing this if we're shutting down --- me->buffer may not
769 * be in a safe state.
771 if (me
->buffer
!= NULL
&& !me
->shutdown
) {
772 oggplay_buffer_set_last_data(me
, me
->buffer
);
775 /* we reached the end of the stream */
778 case OGGZ_ERR_HOLE_IN_DATA
:
779 /* there was a whole in the data */
780 return E_OGGPLAY_BAD_INPUT
;
782 case OGGZ_ERR_STOP_ERR
:
784 * one of the callback functions requested us to stop.
785 * as this currently happens only when one of the
786 * OggzReadPacket callback functions does not receive
787 * the user provided data, i.e. the OggPlayDecode struct
788 * for the track mark it as a memory problem, since this
789 * could happen only if something is wrong with the memory,
790 * e.g. some buffer overflow.
793 case OGGZ_ERR_OUT_OF_MEMORY
:
794 /* ran out of memory during decoding! */
795 return E_OGGPLAY_OUT_OF_MEMORY
;
804 num_records
= oggplay_callback_info_prepare (me
, &info
);
806 r
= me
->callback (me
, num_records
, info
, me
->callback_user_ptr
);
807 oggplay_callback_info_destroy (me
, info
);
813 * clean the data lists
815 for (i
= 0; i
< me
->num_tracks
; i
++) {
816 oggplay_data_clean_list (me
->decode_data
[i
]);
820 * there was an error during info prepare!
823 if (num_records
< 0) {
827 /* if we received an shutdown event, dont try to read more data...*/
832 /* we require more data for decoding */
837 me
->target
+= me
->callback_period
;
839 return E_OGGPLAY_USER_INTERRUPT
;
842 return E_OGGPLAY_CONTINUE
;
847 oggplay_start_decoding(OggPlay
*me
) {
852 r
= oggplay_step_decoding(me
);
853 if (r
== E_OGGPLAY_CONTINUE
|| r
== E_OGGPLAY_TIMEOUT
) {
856 return (OggPlayErrorCode
)r
;
861 oggplay_close(OggPlay
*me
) {
866 return E_OGGPLAY_BAD_OGGPLAY
;
869 if (me
->reader
!= NULL
) {
870 me
->reader
->destroy(me
->reader
);
874 if (me
->decode_data
!= NULL
) {
875 for (i
= 0; i
< me
->num_tracks
; i
++) {
876 oggplay_callback_shutdown(me
->decode_data
[i
]);
881 oggz_close(me
->oggz
);
883 if (me
->buffer
!= NULL
) {
884 oggplay_buffer_shutdown(me
, me
->buffer
);
887 if (me
->callback_info
!= NULL
) {
888 oggplay_free(me
->callback_info
);
891 oggplay_free(me
->decode_data
);
898 * this function is required to release the frame_sem in the buffer, if
899 * the buffer is being used.
902 oggplay_prepare_for_close(OggPlay
*me
) {
905 if (me
->buffer
!= NULL
) {
906 SEM_SIGNAL(((OggPlayBuffer
*)(me
->buffer
))->frame_sem
);
911 oggplay_get_available(OggPlay
*me
) {
913 ogg_int64_t current_time
, current_byte
;
916 return E_OGGPLAY_BAD_OGGPLAY
;
919 current_time
= oggz_tell_units(me
->oggz
);
920 current_byte
= (ogg_int64_t
)oggz_tell(me
->oggz
);
922 return me
->reader
->available(me
->reader
, current_byte
, current_time
);
927 oggplay_get_duration(OggPlay
*me
) {
930 return E_OGGPLAY_BAD_OGGPLAY
;
933 /* If the reader has a duration function we always call that
934 * function to find the duration. We never cache the result
937 * If there is no reader duration function we use our cached
938 * duration value, or do a liboggz seek to find it and cache
941 if (me
->reader
->duration
) {
942 ogg_int64_t d
= me
->reader
->duration(me
->reader
);
948 if (me
->duration
< 0) {
950 pos
= oggz_tell_units(me
->oggz
);
951 me
->duration
= oggz_seek_units(me
->oggz
, 0, SEEK_END
);
952 oggz_seek_units(me
->oggz
, pos
, SEEK_SET
);
953 oggplay_seek_cleanup(me
, pos
);
960 oggplay_media_finished_retrieving(OggPlay
*me
) {
963 return E_OGGPLAY_BAD_OGGPLAY
;
966 if (me
->reader
== NULL
) {
967 return E_OGGPLAY_BAD_READER
;
970 return me
->reader
->finished_retrieving(me
->reader
);