Mercurial > SDL_sound_CoreAudio
annotate decoders/quicktime.c @ 426:72383155bfac
Updated.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Wed, 09 Oct 2002 16:09:57 +0000 |
parents | cbb15ecf423a |
children | c66080364dff |
rev | line source |
---|---|
318 | 1 /* |
2 * SDL_sound -- An abstract sound format decoding API. | |
3 * Copyright (C) 2001 Ryan C. Gordon. | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2.1 of the License, or (at your option) any later version. | |
9 * | |
10 * This library is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Lesser General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Lesser General Public | |
16 * License along with this library; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 */ | |
19 | |
20 /* | |
21 * QuickTime decoder for sound formats that QuickTime supports. | |
22 * April 28, 2002 | |
23 * | |
24 * This driver handles .mov files with a sound track. In | |
25 * theory, it could handle any format that QuickTime supports. | |
26 * In practice, it may only handle a select few of these formats. | |
27 * | |
28 * It seems able to play back AIFF and other standard Mac formats. | |
29 * Rewinding is not supported yet. | |
30 * | |
31 * The routine QT_create_data_ref() needs to be | |
32 * tweaked to support different media types. | |
33 * This code was originally written to get MP3 support, | |
34 * as it turns out, this isn't possible using this method. | |
35 * | |
36 * The only way to get streaming MP3 support through QuickTime, | |
37 * and hence support for SDL_RWops, is to write | |
38 * a DataHandler component, which suddenly gets much more difficult :-( | |
39 * | |
40 * This file was written by Darrell Walisser (walisser@mac.com) | |
41 * Portions have been borrowed from the "MP3Player" sample code, | |
42 * courtesy of Apple. | |
43 */ | |
44 | |
45 #if HAVE_CONFIG_H | |
46 # include <config.h> | |
47 #endif | |
48 | |
49 #ifdef SOUND_SUPPORTS_QUICKTIME | |
50 #ifdef macintosh | |
51 typedef long int32_t; | |
52 # define OPAQUE_UPP_TYPES 0 | |
53 # include <QuickTime.h> | |
54 #else | |
55 # include <QuickTime/QuickTime.h> | |
56 # include <Carbon/Carbon.h> | |
57 #endif | |
58 | |
59 #include <stdio.h> | |
60 #include <stdlib.h> | |
61 #include <stdint.h> | |
62 #include <string.h> | |
63 | |
64 #include "SDL_sound.h" | |
65 | |
66 #define __SDL_SOUND_INTERNAL__ | |
67 #include "SDL_sound_internal.h" | |
68 | |
69 static int QT_init(void); | |
70 static void QT_quit(void); | |
71 static int QT_open(Sound_Sample *sample, const char *ext); | |
72 static void QT_close(Sound_Sample *sample); | |
73 static Uint32 QT_read(Sound_Sample *sample); | |
74 static int QT_rewind(Sound_Sample *sample); | |
328
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
75 static int QT_seek(Sound_Sample *sample, Uint32 ms); |
318 | 76 |
77 #define QT_MAX_INPUT_BUFFER (32*1024) /* Maximum size of internal buffer (internal->buffer_size) */ | |
78 | |
79 static const char *extensions_quicktime[] = { "mov", NULL }; | |
80 const Sound_DecoderFunctions __Sound_DecoderFunctions_QuickTime = | |
81 { | |
82 { | |
83 extensions_quicktime, | |
84 "QuickTime format", | |
85 "Darrell Walisser <dwaliss1@purdue.edu>", | |
86 "http://www.icculus.org/SDL_sound/" | |
87 }, | |
88 | |
328
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
89 QT_init, /* init() method */ |
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
90 QT_quit, /* quit() method */ |
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
91 QT_open, /* open() method */ |
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
92 QT_close, /* close() method */ |
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
93 QT_read, /* read() method */ |
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
94 QT_rewind, /* rewind() method */ |
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
95 QT_seek /* seek() method */ |
318 | 96 }; |
97 | |
98 typedef struct { | |
99 | |
100 ExtendedSoundComponentData compData; | |
101 Handle hSource; /* source media buffer */ | |
102 Media sourceMedia; /* sound media identifier */ | |
103 TimeValue getMediaAtThisTime; | |
104 TimeValue sourceDuration; | |
105 Boolean isThereMoreSource; | |
106 UInt32 maxBufferSize; | |
107 | |
108 } SCFillBufferData, *SCFillBufferDataPtr; | |
109 | |
110 typedef struct { | |
111 | |
112 Movie movie; | |
113 Track track; | |
114 Media media; | |
115 AudioFormatAtomPtr atom; | |
116 SoundComponentData source_format; | |
117 SoundComponentData dest_format; | |
118 SoundConverter converter; | |
119 SCFillBufferData buffer_data; | |
120 SoundConverterFillBufferDataUPP fill_buffer_proc; | |
121 | |
122 } qt_t; | |
123 | |
124 | |
125 | |
126 | |
127 /* | |
128 * This procedure creates a description of the raw data | |
129 * read from SDL_RWops so that QuickTime can identify | |
130 * the codec it needs to use to decompress it. | |
131 */ | |
132 static Handle QT_create_data_ref (const char *file_extension) { | |
133 | |
134 Handle tmp_handle, data_ref; | |
135 StringPtr file_name = "\p"; /* empty since we don't know the file name! */ | |
136 OSType file_type; | |
137 StringPtr mime_type; | |
138 long atoms[3]; | |
139 | |
140 /* | |
141 if (__Sound_strcasecmp (file_extension, "mp3")==0) { | |
142 file_type = 'MPEG'; | |
143 mime_type = "\pvideo/mpeg"; | |
144 } | |
145 else { | |
146 | |
147 return NULL; | |
148 } | |
149 */ | |
150 | |
151 if (__Sound_strcasecmp (file_extension, "mov") == 0) { | |
152 | |
153 file_type = 'MooV'; | |
154 mime_type = "\pvideo/quicktime"; | |
155 } | |
156 else { | |
157 | |
158 return NULL; | |
159 } | |
160 | |
161 tmp_handle = NewHandle(0); | |
162 assert (tmp_handle != NULL); | |
163 assert (noErr == PtrToHand (&tmp_handle, &data_ref, sizeof(Handle))); | |
164 assert (noErr == PtrAndHand (file_name, data_ref, file_name[0]+1)); | |
165 | |
166 atoms[0] = EndianU32_NtoB (sizeof(long) * 3); | |
167 atoms[1] = EndianU32_NtoB (kDataRefExtensionMacOSFileType); | |
168 atoms[2] = EndianU32_NtoB (file_type); | |
169 | |
170 assert (noErr == PtrAndHand (atoms, data_ref, sizeof(long)*3)); | |
171 | |
172 atoms[0] = EndianU32_NtoB (sizeof(long)*2 + mime_type[0]+1); | |
173 atoms[1] = EndianU32_NtoB (kDataRefExtensionMIMEType); | |
174 | |
175 assert (noErr == PtrAndHand (atoms, data_ref, sizeof(long)*2)); | |
176 assert (noErr == PtrAndHand (mime_type, data_ref, mime_type[0]+1)); | |
177 | |
178 return data_ref; | |
179 } | |
180 | |
181 /* | |
182 * This procedure is a hook for QuickTime to grab data from the | |
183 * SDL_RWOps data structure when it needs it | |
184 */ | |
185 static pascal OSErr QT_get_movie_data_proc (long offset, long size, | |
186 void *data, void *user_data) | |
187 { | |
188 SDL_RWops* rw = (SDL_RWops*)user_data; | |
189 OSErr error; | |
190 | |
191 if (offset == SDL_RWseek (rw, offset, SEEK_SET)) { | |
192 | |
193 if (size == SDL_RWread (rw, data, 1, size)) { | |
194 error = noErr; | |
195 } | |
196 else { | |
197 error = notEnoughDataErr; | |
198 } | |
199 } | |
200 else { | |
201 error = fileOffsetTooBigErr; | |
202 } | |
203 | |
204 return (error); | |
205 } | |
206 | |
207 /* * ---------------------------- | |
208 * SoundConverterFillBufferDataProc | |
209 * | |
210 * the callback routine that provides the source data for conversion - | |
211 * it provides data by setting outData to a pointer to a properly | |
212 * filled out ExtendedSoundComponentData structure | |
213 */ | |
214 static pascal Boolean QT_sound_converter_fill_buffer_data_proc (SoundComponentDataPtr *outData, void *inRefCon) | |
215 { | |
216 SCFillBufferDataPtr pFillData = (SCFillBufferDataPtr)inRefCon; | |
217 | |
218 OSErr err = noErr; | |
219 | |
220 /* if after getting the last chunk of data the total time is over | |
221 * the duration, we're done | |
222 */ | |
223 if (pFillData->getMediaAtThisTime >= pFillData->sourceDuration) { | |
224 pFillData->isThereMoreSource = false; | |
225 pFillData->compData.desc.buffer = NULL; | |
226 pFillData->compData.desc.sampleCount = 0; | |
227 pFillData->compData.bufferSize = 0; | |
228 } | |
229 | |
230 if (pFillData->isThereMoreSource) { | |
231 | |
232 long sourceBytesReturned; | |
233 long numberOfSamples; | |
234 TimeValue sourceReturnedTime, durationPerSample; | |
235 | |
236 HUnlock(pFillData->hSource); | |
237 | |
238 err = GetMediaSample | |
239 (pFillData->sourceMedia,/* specifies the media for this operation */ | |
240 pFillData->hSource, /* function returns the sample data into this handle */ | |
241 pFillData->maxBufferSize, /* maximum number of bytes of sample data to be returned */ | |
242 &sourceBytesReturned, /* the number of bytes of sample data returned */ | |
243 pFillData->getMediaAtThisTime,/* starting time of the sample to | |
244 be retrieved (must be in | |
245 Media's TimeScale) */ | |
246 &sourceReturnedTime,/* indicates the actual time of the returned sample data */ | |
247 &durationPerSample, /* duration of each sample in the media */ | |
248 NULL, /* sample description corresponding to the returned sample data (NULL to ignore) */ | |
249 NULL, /* index value to the sample description that corresponds | |
250 to the returned sample data (NULL to ignore) */ | |
251 0, /* maximum number of samples to be returned (0 to use a | |
252 value that is appropriate for the media) */ | |
253 &numberOfSamples, /* number of samples it actually returned */ | |
254 NULL); /* flags that describe the sample (NULL to ignore) */ | |
255 | |
256 HLock(pFillData->hSource); | |
257 | |
258 if ((noErr != err) || (sourceBytesReturned == 0)) { | |
259 pFillData->isThereMoreSource = false; | |
260 pFillData->compData.desc.buffer = NULL; | |
261 pFillData->compData.desc.sampleCount = 0; | |
262 | |
263 if ((err != noErr) && (sourceBytesReturned > 0)) | |
264 DebugStr("\pGetMediaSample - Failed in FillBufferDataProc"); | |
265 } | |
266 | |
267 pFillData->getMediaAtThisTime = sourceReturnedTime + (durationPerSample * numberOfSamples); | |
268 pFillData->compData.bufferSize = sourceBytesReturned; | |
269 } | |
270 | |
271 /* set outData to a properly filled out ExtendedSoundComponentData struct */ | |
272 *outData = (SoundComponentDataPtr)&pFillData->compData; | |
273 | |
274 return (pFillData->isThereMoreSource); | |
275 } | |
276 | |
277 | |
278 static int QT_init_internal () { | |
279 | |
280 OSErr error; | |
281 | |
282 error = EnterMovies(); /* initialize the movie toolbox */ | |
283 | |
284 return (error == noErr); | |
285 } | |
286 | |
287 static void QT_quit_internal () { | |
288 | |
289 ExitMovies(); | |
290 } | |
291 | |
292 static qt_t* QT_open_internal (Sound_Sample *sample, const char *extension) | |
293 { | |
294 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
295 | |
296 qt_t *instance; | |
297 OSErr error; | |
298 Movie movie; | |
299 Track sound_track; | |
300 Media sound_track_media; | |
301 AudioFormatAtomPtr source_sound_decomp_atom; | |
302 | |
303 SoundDescriptionV1Handle source_sound_description; | |
304 Handle source_sound_description_extension; | |
305 Size source_sound_description_extension_size; | |
306 Handle data_ref; | |
307 | |
308 data_ref = QT_create_data_ref (extension); | |
309 | |
310 /* create a movie that will read data using SDL_RWops */ | |
311 error = NewMovieFromUserProc | |
312 (&movie, | |
313 0, | |
314 NULL, | |
315 NewGetMovieUPP(QT_get_movie_data_proc), | |
316 (void*) internal->rw, | |
317 data_ref, | |
318 'hndl'); | |
319 | |
320 if (error != noErr) { | |
321 | |
322 return NULL; | |
323 } | |
324 | |
325 /* get the first sound track of the movie; other tracks will be ignored */ | |
326 sound_track = GetMovieIndTrackType (movie, 1, SoundMediaType, movieTrackMediaType); | |
327 if (sound_track == NULL) { | |
328 | |
329 /* movie needs a sound track! */ | |
330 | |
331 return NULL; | |
332 } | |
333 | |
334 /* get and return the sound track media */ | |
335 sound_track_media = GetTrackMedia (sound_track); | |
336 if (sound_track_media == NULL) { | |
337 | |
338 return NULL; | |
339 } | |
340 | |
341 /* create a description of the source sound so we can convert it later */ | |
342 source_sound_description = (SoundDescriptionV1Handle)NewHandle(0); | |
343 assert (source_sound_description != NULL); /* out of memory */ | |
344 | |
345 GetMediaSampleDescription (sound_track_media, 1, | |
346 (SampleDescriptionHandle)source_sound_description); | |
347 error = GetMoviesError(); | |
348 if (error != noErr) { | |
349 | |
350 return NULL; | |
351 } | |
352 | |
353 source_sound_description_extension = NewHandle(0); | |
354 assert (source_sound_description_extension != NULL); /* out of memory */ | |
355 | |
356 error = GetSoundDescriptionExtension ((SoundDescriptionHandle) source_sound_description, | |
357 &source_sound_description_extension, | |
358 siDecompressionParams); | |
359 | |
360 if (error == noErr) { | |
361 | |
362 /* copy extension to atom format description if we have an extension */ | |
363 | |
364 source_sound_description_extension_size = | |
365 GetHandleSize (source_sound_description_extension); | |
366 HLock (source_sound_description_extension); | |
367 | |
368 source_sound_decomp_atom = (AudioFormatAtom*) | |
369 NewPtr (source_sound_description_extension_size); | |
370 assert (source_sound_decomp_atom != NULL); /* out of memory */ | |
371 | |
372 BlockMoveData (*source_sound_description_extension, | |
373 source_sound_decomp_atom, | |
374 source_sound_description_extension_size); | |
375 | |
376 HUnlock (source_sound_description_extension); | |
377 } | |
378 | |
379 else { | |
380 | |
381 source_sound_decomp_atom = NULL; | |
382 } | |
383 | |
384 instance = (qt_t*) malloc (sizeof(*instance)); | |
385 assert (instance != NULL); /* out of memory */ | |
386 | |
387 instance->movie = movie; | |
388 instance->track = sound_track; | |
389 instance->media = sound_track_media; | |
390 instance->atom = source_sound_decomp_atom; | |
391 | |
392 instance->source_format.flags = 0; | |
393 instance->source_format.format = (*source_sound_description)->desc.dataFormat; | |
394 instance->source_format.numChannels = (*source_sound_description)->desc.numChannels; | |
395 instance->source_format.sampleSize = (*source_sound_description)->desc.sampleSize; | |
396 instance->source_format.sampleRate = (*source_sound_description)->desc.sampleRate; | |
397 instance->source_format.sampleCount = 0; | |
398 instance->source_format.buffer = NULL; | |
399 instance->source_format.reserved = 0; | |
400 | |
401 instance->dest_format.flags = 0; | |
402 instance->dest_format.format = kSoundNotCompressed; | |
403 instance->dest_format.numChannels = (*source_sound_description)->desc.numChannels; | |
404 instance->dest_format.sampleSize = (*source_sound_description)->desc.sampleSize; | |
405 instance->dest_format.sampleRate = (*source_sound_description)->desc.sampleRate; | |
406 instance->dest_format.sampleCount = 0; | |
407 instance->dest_format.buffer = NULL; | |
408 instance->dest_format.reserved = 0; | |
409 | |
410 sample->actual.channels = (*source_sound_description)->desc.numChannels; | |
411 sample->actual.rate = (*source_sound_description)->desc.sampleRate >> 16; | |
412 | |
413 if ((*source_sound_description)->desc.sampleSize == 16) { | |
414 | |
415 sample->actual.format = AUDIO_S16SYS; | |
416 } | |
417 else if ((*source_sound_description)->desc.sampleSize == 8) { | |
418 | |
419 sample->actual.format = AUDIO_U8; | |
420 } | |
421 else { | |
422 | |
423 /* 24-bit or others... (which SDL can't handle) */ | |
424 return NULL; | |
425 } | |
426 | |
427 DisposeHandle (source_sound_description_extension); | |
428 DisposeHandle ((Handle)source_sound_description); | |
429 | |
430 /* This next code sets up the SoundConverter component */ | |
431 error = SoundConverterOpen (&instance->source_format, &instance->dest_format, | |
432 &instance->converter); | |
433 | |
434 if (error != noErr) { | |
435 | |
436 return NULL; | |
437 } | |
438 | |
439 error = SoundConverterSetInfo (instance->converter, siDecompressionParams, | |
440 instance->atom); | |
441 if (error == siUnknownInfoType) { | |
442 | |
443 /* ignore */ | |
444 } | |
445 else if (error != noErr) { | |
446 | |
447 /* reall error */ | |
448 return NULL; | |
449 } | |
450 | |
451 error = SoundConverterBeginConversion (instance->converter); | |
452 if (error != noErr) { | |
453 | |
454 return NULL; | |
455 } | |
456 | |
457 instance->buffer_data.sourceMedia = instance->media; | |
458 instance->buffer_data.getMediaAtThisTime = 0; | |
459 instance->buffer_data.sourceDuration = GetMediaDuration(instance->media); | |
460 instance->buffer_data.isThereMoreSource = true; | |
461 instance->buffer_data.maxBufferSize = QT_MAX_INPUT_BUFFER; | |
462 /* allocate source media buffer */ | |
463 instance->buffer_data.hSource = NewHandle((long)instance->buffer_data.maxBufferSize); | |
464 assert (instance->buffer_data.hSource != NULL); /* out of memory */ | |
465 | |
466 instance->buffer_data.compData.desc = instance->source_format; | |
467 instance->buffer_data.compData.desc.buffer = (Byte *)*instance->buffer_data.hSource; | |
468 instance->buffer_data.compData.desc.flags = kExtendedSoundData; | |
469 instance->buffer_data.compData.recordSize = sizeof(ExtendedSoundComponentData); | |
470 instance->buffer_data.compData.extendedFlags = | |
471 kExtendedSoundSampleCountNotValid | kExtendedSoundBufferSizeValid; | |
472 instance->buffer_data.compData.bufferSize = 0; | |
473 | |
474 instance->fill_buffer_proc = | |
475 NewSoundConverterFillBufferDataUPP (QT_sound_converter_fill_buffer_data_proc); | |
476 | |
477 return (instance); | |
478 | |
479 } /* QT_open_internal */ | |
480 | |
481 static void QT_close_internal (qt_t *instance) | |
482 { | |
483 | |
484 } /* QT_close_internal */ | |
485 | |
486 static Uint32 QT_read_internal(Sound_Sample *sample) | |
487 { | |
488 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
489 qt_t *instance = (qt_t*) internal->decoder_private; | |
490 long output_bytes, output_frames, output_flags; | |
491 OSErr error; | |
492 | |
493 error = SoundConverterFillBuffer | |
494 (instance->converter, /* a sound converter */ | |
495 instance->fill_buffer_proc, /* the callback UPP */ | |
496 &instance->buffer_data, /* refCon passed to FillDataProc */ | |
497 internal->buffer, /* the decompressed data 'play' buffer */ | |
498 internal->buffer_size, /* size of the 'play' buffer */ | |
499 &output_bytes, /* number of output bytes */ | |
500 &output_frames, /* number of output frames */ | |
501 &output_flags); /* fillbuffer retured advisor flags */ | |
502 | |
503 if (output_flags & kSoundConverterHasLeftOverData) { | |
504 | |
505 sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; | |
506 } | |
507 else { | |
508 | |
509 sample->flags |= SOUND_SAMPLEFLAG_EOF; | |
510 } | |
511 | |
512 if (error != noErr) { | |
513 | |
514 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
515 } | |
516 | |
517 return (output_bytes); | |
518 | |
519 } /* QT_read_internal */ | |
520 | |
521 static int QT_rewind_internal (Sound_Sample *sample) | |
522 { | |
523 | |
524 return 0; | |
525 | |
526 } /* QT_rewind_internal */ | |
527 | |
528 | |
529 | |
530 static int QT_init(void) | |
531 { | |
532 return (QT_init_internal()); | |
533 | |
534 } /* QT_init */ | |
535 | |
536 static void QT_quit(void) | |
537 { | |
538 QT_quit_internal(); | |
539 | |
540 } /* QT_quit */ | |
541 | |
542 static int QT_open(Sound_Sample *sample, const char *ext) | |
543 { | |
544 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
545 qt_t *instance; | |
546 | |
547 instance = QT_open_internal(sample, ext); | |
548 internal->decoder_private = (void*)instance; | |
549 | |
550 return(instance != NULL); | |
551 | |
552 } /* QT_open */ | |
553 | |
554 | |
555 static void QT_close(Sound_Sample *sample) | |
556 { | |
557 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
558 qt_t *instance = (qt_t *) internal->decoder_private; | |
559 | |
560 QT_close_internal (instance); | |
561 | |
562 free (instance); | |
563 | |
564 } /* QT_close */ | |
565 | |
566 | |
567 static Uint32 QT_read(Sound_Sample *sample) | |
568 { | |
569 return(QT_read_internal(sample)); | |
570 | |
571 } /* QT_read */ | |
572 | |
573 | |
574 static int QT_rewind(Sound_Sample *sample) | |
575 { | |
576 | |
577 return(QT_rewind_internal(sample)); | |
578 | |
579 } /* QT_rewind */ | |
580 | |
328
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
581 |
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
582 static int QT_seek(Sound_Sample *sample, Uint32 ms) |
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
583 { |
351 | 584 BAIL_MACRO("QUICKTIME: Seeking not implemented", 0); |
328
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
585 } /* QT_seek */ |
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
586 |
a7c1dbcf5e00
Added a seek method stub.
Ryan C. Gordon <icculus@icculus.org>
parents:
318
diff
changeset
|
587 |
318 | 588 #endif /* SOUND_SUPPORTS_QUICKTIME */ |
589 | |
590 /* end of quicktime.c ... */ | |
591 |