changeset 318:df024df56996

Initial add.
author Ryan C. Gordon <icculus@icculus.org>
date Mon, 29 Apr 2002 06:01:52 +0000
parents 132d303b3ef0
children 4a2cd7d4aa06
files decoders/quicktime.c
diffstat 1 files changed, 583 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/decoders/quicktime.c	Mon Apr 29 06:01:52 2002 +0000
@@ -0,0 +1,583 @@
+/*
+ * 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 <assert.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);
+
+#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 */
+  };
+
+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;
+
+  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 */
+
+#endif /* SOUND_SUPPORTS_QUICKTIME */
+
+/* end of quicktime.c ... */
+