Mercurial > SDL_sound_CoreAudio
view decoders/quicktime.c @ 477:3e705c9180e5
Fixed binary compatibility, added Sound_GetDuration().
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Wed, 12 May 2004 02:15:00 +0000 |
parents | c66080364dff |
children | a737d89af74e |
line wrap: on
line source
/* * SDL_sound -- An abstract sound format decoding API. * Copyright (C) 2001 Ryan C. Gordon. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * QuickTime decoder for sound formats that QuickTime supports. * April 28, 2002 * * This driver handles .mov files with a sound track. In * theory, it could handle any format that QuickTime supports. * In practice, it may only handle a select few of these formats. * * It seems able to play back AIFF and other standard Mac formats. * Rewinding is not supported yet. * * The routine QT_create_data_ref() needs to be * tweaked to support different media types. * This code was originally written to get MP3 support, * as it turns out, this isn't possible using this method. * * The only way to get streaming MP3 support through QuickTime, * and hence support for SDL_RWops, is to write * a DataHandler component, which suddenly gets much more difficult :-( * * This file was written by Darrell Walisser (walisser@mac.com) * Portions have been borrowed from the "MP3Player" sample code, * courtesy of Apple. */ #if HAVE_CONFIG_H # include <config.h> #endif #ifdef SOUND_SUPPORTS_QUICKTIME #ifdef macintosh typedef long int32_t; # define OPAQUE_UPP_TYPES 0 # include <QuickTime.h> #else # include <QuickTime/QuickTime.h> # include <Carbon/Carbon.h> #endif #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include "SDL_sound.h" #define __SDL_SOUND_INTERNAL__ #include "SDL_sound_internal.h" static int QT_init(void); static void QT_quit(void); static int QT_open(Sound_Sample *sample, const char *ext); static void QT_close(Sound_Sample *sample); static Uint32 QT_read(Sound_Sample *sample); static int QT_rewind(Sound_Sample *sample); static int QT_seek(Sound_Sample *sample, Uint32 ms); #define QT_MAX_INPUT_BUFFER (32*1024) /* Maximum size of internal buffer (internal->buffer_size) */ static const char *extensions_quicktime[] = { "mov", NULL }; const Sound_DecoderFunctions __Sound_DecoderFunctions_QuickTime = { { extensions_quicktime, "QuickTime format", "Darrell Walisser <dwaliss1@purdue.edu>", "http://www.icculus.org/SDL_sound/" }, QT_init, /* init() method */ QT_quit, /* quit() method */ QT_open, /* open() method */ QT_close, /* close() method */ QT_read, /* read() method */ QT_rewind, /* rewind() method */ QT_seek /* seek() method */ }; typedef struct { ExtendedSoundComponentData compData; Handle hSource; /* source media buffer */ Media sourceMedia; /* sound media identifier */ TimeValue getMediaAtThisTime; TimeValue sourceDuration; Boolean isThereMoreSource; UInt32 maxBufferSize; } SCFillBufferData, *SCFillBufferDataPtr; typedef struct { Movie movie; Track track; Media media; AudioFormatAtomPtr atom; SoundComponentData source_format; SoundComponentData dest_format; SoundConverter converter; SCFillBufferData buffer_data; SoundConverterFillBufferDataUPP fill_buffer_proc; } qt_t; /* * This procedure creates a description of the raw data * read from SDL_RWops so that QuickTime can identify * the codec it needs to use to decompress it. */ static Handle QT_create_data_ref (const char *file_extension) { Handle tmp_handle, data_ref; StringPtr file_name = "\p"; /* empty since we don't know the file name! */ OSType file_type; StringPtr mime_type; long atoms[3]; /* if (__Sound_strcasecmp (file_extension, "mp3")==0) { file_type = 'MPEG'; mime_type = "\pvideo/mpeg"; } else { return NULL; } */ if (__Sound_strcasecmp (file_extension, "mov") == 0) { file_type = 'MooV'; mime_type = "\pvideo/quicktime"; } else { return NULL; } tmp_handle = NewHandle(0); assert (tmp_handle != NULL); assert (noErr == PtrToHand (&tmp_handle, &data_ref, sizeof(Handle))); assert (noErr == PtrAndHand (file_name, data_ref, file_name[0]+1)); atoms[0] = EndianU32_NtoB (sizeof(long) * 3); atoms[1] = EndianU32_NtoB (kDataRefExtensionMacOSFileType); atoms[2] = EndianU32_NtoB (file_type); assert (noErr == PtrAndHand (atoms, data_ref, sizeof(long)*3)); atoms[0] = EndianU32_NtoB (sizeof(long)*2 + mime_type[0]+1); atoms[1] = EndianU32_NtoB (kDataRefExtensionMIMEType); assert (noErr == PtrAndHand (atoms, data_ref, sizeof(long)*2)); assert (noErr == PtrAndHand (mime_type, data_ref, mime_type[0]+1)); return data_ref; } /* * This procedure is a hook for QuickTime to grab data from the * SDL_RWOps data structure when it needs it */ static pascal OSErr QT_get_movie_data_proc (long offset, long size, void *data, void *user_data) { SDL_RWops* rw = (SDL_RWops*)user_data; OSErr error; if (offset == SDL_RWseek (rw, offset, SEEK_SET)) { if (size == SDL_RWread (rw, data, 1, size)) { error = noErr; } else { error = notEnoughDataErr; } } else { error = fileOffsetTooBigErr; } return (error); } /* * ---------------------------- * SoundConverterFillBufferDataProc * * the callback routine that provides the source data for conversion - * it provides data by setting outData to a pointer to a properly * filled out ExtendedSoundComponentData structure */ static pascal Boolean QT_sound_converter_fill_buffer_data_proc (SoundComponentDataPtr *outData, void *inRefCon) { SCFillBufferDataPtr pFillData = (SCFillBufferDataPtr)inRefCon; OSErr err = noErr; /* if after getting the last chunk of data the total time is over * the duration, we're done */ if (pFillData->getMediaAtThisTime >= pFillData->sourceDuration) { pFillData->isThereMoreSource = false; pFillData->compData.desc.buffer = NULL; pFillData->compData.desc.sampleCount = 0; pFillData->compData.bufferSize = 0; } if (pFillData->isThereMoreSource) { long sourceBytesReturned; long numberOfSamples; TimeValue sourceReturnedTime, durationPerSample; HUnlock(pFillData->hSource); err = GetMediaSample (pFillData->sourceMedia,/* specifies the media for this operation */ pFillData->hSource, /* function returns the sample data into this handle */ pFillData->maxBufferSize, /* maximum number of bytes of sample data to be returned */ &sourceBytesReturned, /* the number of bytes of sample data returned */ pFillData->getMediaAtThisTime,/* starting time of the sample to be retrieved (must be in Media's TimeScale) */ &sourceReturnedTime,/* indicates the actual time of the returned sample data */ &durationPerSample, /* duration of each sample in the media */ NULL, /* sample description corresponding to the returned sample data (NULL to ignore) */ NULL, /* index value to the sample description that corresponds to the returned sample data (NULL to ignore) */ 0, /* maximum number of samples to be returned (0 to use a value that is appropriate for the media) */ &numberOfSamples, /* number of samples it actually returned */ NULL); /* flags that describe the sample (NULL to ignore) */ HLock(pFillData->hSource); if ((noErr != err) || (sourceBytesReturned == 0)) { pFillData->isThereMoreSource = false; pFillData->compData.desc.buffer = NULL; pFillData->compData.desc.sampleCount = 0; if ((err != noErr) && (sourceBytesReturned > 0)) DebugStr("\pGetMediaSample - Failed in FillBufferDataProc"); } pFillData->getMediaAtThisTime = sourceReturnedTime + (durationPerSample * numberOfSamples); pFillData->compData.bufferSize = sourceBytesReturned; } /* set outData to a properly filled out ExtendedSoundComponentData struct */ *outData = (SoundComponentDataPtr)&pFillData->compData; return (pFillData->isThereMoreSource); } static int QT_init_internal () { OSErr error; error = EnterMovies(); /* initialize the movie toolbox */ return (error == noErr); } static void QT_quit_internal () { ExitMovies(); } static qt_t* QT_open_internal (Sound_Sample *sample, const char *extension) { Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; qt_t *instance; OSErr error; Movie movie; Track sound_track; Media sound_track_media; AudioFormatAtomPtr source_sound_decomp_atom; SoundDescriptionV1Handle source_sound_description; Handle source_sound_description_extension; Size source_sound_description_extension_size; Handle data_ref; data_ref = QT_create_data_ref (extension); /* create a movie that will read data using SDL_RWops */ error = NewMovieFromUserProc (&movie, 0, NULL, NewGetMovieUPP(QT_get_movie_data_proc), (void*) internal->rw, data_ref, 'hndl'); if (error != noErr) { return NULL; } /* get the first sound track of the movie; other tracks will be ignored */ sound_track = GetMovieIndTrackType (movie, 1, SoundMediaType, movieTrackMediaType); if (sound_track == NULL) { /* movie needs a sound track! */ return NULL; } /* get and return the sound track media */ sound_track_media = GetTrackMedia (sound_track); if (sound_track_media == NULL) { return NULL; } /* create a description of the source sound so we can convert it later */ source_sound_description = (SoundDescriptionV1Handle)NewHandle(0); assert (source_sound_description != NULL); /* out of memory */ GetMediaSampleDescription (sound_track_media, 1, (SampleDescriptionHandle)source_sound_description); error = GetMoviesError(); if (error != noErr) { return NULL; } source_sound_description_extension = NewHandle(0); assert (source_sound_description_extension != NULL); /* out of memory */ error = GetSoundDescriptionExtension ((SoundDescriptionHandle) source_sound_description, &source_sound_description_extension, siDecompressionParams); if (error == noErr) { /* copy extension to atom format description if we have an extension */ source_sound_description_extension_size = GetHandleSize (source_sound_description_extension); HLock (source_sound_description_extension); source_sound_decomp_atom = (AudioFormatAtom*) NewPtr (source_sound_description_extension_size); assert (source_sound_decomp_atom != NULL); /* out of memory */ BlockMoveData (*source_sound_description_extension, source_sound_decomp_atom, source_sound_description_extension_size); HUnlock (source_sound_description_extension); } else { source_sound_decomp_atom = NULL; } instance = (qt_t*) malloc (sizeof(*instance)); assert (instance != NULL); /* out of memory */ instance->movie = movie; instance->track = sound_track; instance->media = sound_track_media; instance->atom = source_sound_decomp_atom; instance->source_format.flags = 0; instance->source_format.format = (*source_sound_description)->desc.dataFormat; instance->source_format.numChannels = (*source_sound_description)->desc.numChannels; instance->source_format.sampleSize = (*source_sound_description)->desc.sampleSize; instance->source_format.sampleRate = (*source_sound_description)->desc.sampleRate; instance->source_format.sampleCount = 0; instance->source_format.buffer = NULL; instance->source_format.reserved = 0; instance->dest_format.flags = 0; instance->dest_format.format = kSoundNotCompressed; instance->dest_format.numChannels = (*source_sound_description)->desc.numChannels; instance->dest_format.sampleSize = (*source_sound_description)->desc.sampleSize; instance->dest_format.sampleRate = (*source_sound_description)->desc.sampleRate; instance->dest_format.sampleCount = 0; instance->dest_format.buffer = NULL; instance->dest_format.reserved = 0; sample->actual.channels = (*source_sound_description)->desc.numChannels; sample->actual.rate = (*source_sound_description)->desc.sampleRate >> 16; if ((*source_sound_description)->desc.sampleSize == 16) { sample->actual.format = AUDIO_S16SYS; } else if ((*source_sound_description)->desc.sampleSize == 8) { sample->actual.format = AUDIO_U8; } else { /* 24-bit or others... (which SDL can't handle) */ return NULL; } DisposeHandle (source_sound_description_extension); DisposeHandle ((Handle)source_sound_description); /* This next code sets up the SoundConverter component */ error = SoundConverterOpen (&instance->source_format, &instance->dest_format, &instance->converter); if (error != noErr) { return NULL; } error = SoundConverterSetInfo (instance->converter, siDecompressionParams, instance->atom); if (error == siUnknownInfoType) { /* ignore */ } else if (error != noErr) { /* reall error */ return NULL; } error = SoundConverterBeginConversion (instance->converter); if (error != noErr) { return NULL; } instance->buffer_data.sourceMedia = instance->media; instance->buffer_data.getMediaAtThisTime = 0; instance->buffer_data.sourceDuration = GetMediaDuration(instance->media); instance->buffer_data.isThereMoreSource = true; instance->buffer_data.maxBufferSize = QT_MAX_INPUT_BUFFER; /* allocate source media buffer */ instance->buffer_data.hSource = NewHandle((long)instance->buffer_data.maxBufferSize); assert (instance->buffer_data.hSource != NULL); /* out of memory */ instance->buffer_data.compData.desc = instance->source_format; instance->buffer_data.compData.desc.buffer = (Byte *)*instance->buffer_data.hSource; instance->buffer_data.compData.desc.flags = kExtendedSoundData; instance->buffer_data.compData.recordSize = sizeof(ExtendedSoundComponentData); instance->buffer_data.compData.extendedFlags = kExtendedSoundSampleCountNotValid | kExtendedSoundBufferSizeValid; instance->buffer_data.compData.bufferSize = 0; instance->fill_buffer_proc = NewSoundConverterFillBufferDataUPP (QT_sound_converter_fill_buffer_data_proc); return (instance); } /* QT_open_internal */ static void QT_close_internal (qt_t *instance) { } /* QT_close_internal */ static Uint32 QT_read_internal(Sound_Sample *sample) { Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; qt_t *instance = (qt_t*) internal->decoder_private; long output_bytes, output_frames, output_flags; OSErr error; error = SoundConverterFillBuffer (instance->converter, /* a sound converter */ instance->fill_buffer_proc, /* the callback UPP */ &instance->buffer_data, /* refCon passed to FillDataProc */ internal->buffer, /* the decompressed data 'play' buffer */ internal->buffer_size, /* size of the 'play' buffer */ &output_bytes, /* number of output bytes */ &output_frames, /* number of output frames */ &output_flags); /* fillbuffer retured advisor flags */ if (output_flags & kSoundConverterHasLeftOverData) { sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; } else { sample->flags |= SOUND_SAMPLEFLAG_EOF; } if (error != noErr) { sample->flags |= SOUND_SAMPLEFLAG_ERROR; } return (output_bytes); } /* QT_read_internal */ static int QT_rewind_internal (Sound_Sample *sample) { return 0; } /* QT_rewind_internal */ static int QT_init(void) { return (QT_init_internal()); } /* QT_init */ static void QT_quit(void) { QT_quit_internal(); } /* QT_quit */ static int QT_open(Sound_Sample *sample, const char *ext) { Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; qt_t *instance; instance = QT_open_internal(sample, ext); internal->decoder_private = (void*)instance; internal->total_time = -1; /* return -1 for total time of song for now */ return(instance != NULL); } /* QT_open */ static void QT_close(Sound_Sample *sample) { Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; qt_t *instance = (qt_t *) internal->decoder_private; QT_close_internal (instance); free (instance); } /* QT_close */ static Uint32 QT_read(Sound_Sample *sample) { return(QT_read_internal(sample)); } /* QT_read */ static int QT_rewind(Sound_Sample *sample) { return(QT_rewind_internal(sample)); } /* QT_rewind */ static int QT_seek(Sound_Sample *sample, Uint32 ms) { BAIL_MACRO("QUICKTIME: Seeking not implemented", 0); } /* QT_seek */ #endif /* SOUND_SUPPORTS_QUICKTIME */ /* end of quicktime.c ... */