OSStatus GetMovieAudioExtractionASBD(MovieAudioExtractionRef inSessionRef,
                                        QTPropertyValuePtr *outASBDPtr)
{
	QTPropertyValueType outPropType;
	ByteCount			outPropValueSize;
	UInt32              outPropertyFlags;
	OSStatus			status = noErr;
	
	*outASBDPtr = nil;
	
	// first get the size of the property so we know how
	// much memory to allocate for our property buffer
	status = MovieAudioExtractionGetPropertyInfo(	inSessionRef,
                                                    kQTPropertyClass_MovieAudioExtraction_Audio,
                                                    kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
                                                    &outPropType,
                                                    &outPropValueSize,
                                                    &outPropertyFlags);
	assert(noErr == status);
	
	// allocate a buffer for the property using the size value just obtained
	*outASBDPtr = malloc(outPropValueSize);
	assert(*outASBDPtr != nil);

	// now get the actual property and put it into our buffer
	ByteCount outPropValueSizeUsed;
	status = MovieAudioExtractionGetProperty( inSessionRef,
                                              kQTPropertyClass_MovieAudioExtraction_Audio,
                                              kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
                                              outPropValueSize,
                                              *outASBDPtr,
                                              &outPropValueSizeUsed);

	return status;
}
// Get the default extraction layout for this movie, expanded into individual channel descriptions.
// The channel layout returned by this routine must be deallocated by the client.
// If 'asbd' is non-NULL, fill it with the default extraction asbd, which contains the
// highest sample rate among the sound tracks that will be contributing.
//	'outLayoutSize' and 'asbd' may be nil.
OSStatus GetDefaultExtractionLayout(Movie movie, UInt32* outLayoutSize,
                            AudioChannelLayout** outLayout, AudioStreamBasicDescription *asbd)
{
	OSStatus                    err = noErr;
	AudioChannelLayout          *layout = NULL;
	UInt32                      size = 0;
	MovieAudioExtractionRef     extractionSessionRef = nil;

	if (!outLayout)
	{
		err = paramErr;
		goto bail;
	}

	// Initiate a dummy audio extraction here, in order to get the resulting default channel layout.
	err = MovieAudioExtractionBegin(movie, 0, &extractionSessionRef);
	require(err == noErr, bail);	
	
	// Get the size of the extraction output layout
	err = MovieAudioExtractionGetPropertyInfo(extractionSessionRef, kQTPropertyClass_MovieAudioExtraction_Audio,
                                                kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
                                                NULL, &size, NULL);
	require(err == noErr, bail);	

	// Allocate memory for the layout
	layout = (AudioChannelLayout *) calloc(1, size);
	if (layout == nil) 
	{
		err = memFullErr;
		goto bail;
	}

	// Get the layout for the current extraction configuration.
	// This will have already been expanded into channel descriptions.
	err = MovieAudioExtractionGetProperty(extractionSessionRef, kQTPropertyClass_MovieAudioExtraction_Audio,
										  kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
										  size, layout, nil);
	require(err == noErr, bail);	
	
	// Return the layout and size, if requested
	*outLayout = layout;
	if (outLayoutSize)	
		*outLayoutSize = size;

	// If the ASBD was requested, get that also.
	if (asbd != nil)
	{
		// Get the layout for the current extraction configuration.
		// This will have already been expanded into channel descriptions.
		size = sizeof (*asbd);
		err = MovieAudioExtractionGetProperty(extractionSessionRef,
											  kQTPropertyClass_MovieAudioExtraction_Audio,
											  kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
											  size, asbd, nil);
        require(err == noErr, bail);	
	}

bail:
	if (err != noErr)
	{
		if (layout)
			free(layout);
		if (outLayoutSize)	
			*outLayoutSize = 0;	
	}

	// Throw away the temporary audio extraction session
	if (extractionSessionRef)
	{
		MovieAudioExtractionEnd(extractionSessionRef);
	}
	
	return err;				
}
    QTAudioReader (InputStream* const input_, const int trackNum_)
        : AudioFormatReader (input_, TRANS (quickTimeFormatName)),
          ok (false),
          movie (0),
          trackNum (trackNum_),
          lastSampleRead (0),
          lastThreadId (0),
          extractor (0),
          dataHandle (0)
    {
        JUCE_AUTORELEASEPOOL
        bufferList.calloc (256, 1);

       #if JUCE_WINDOWS
        if (InitializeQTML (0) != noErr)
            return;
       #endif

        if (EnterMovies() != noErr)
            return;

        bool opened = juce_OpenQuickTimeMovieFromStream (input_, movie, dataHandle);

        if (! opened)
            return;

        {
            const int numTracks = GetMovieTrackCount (movie);
            int trackCount = 0;

            for (int i = 1; i <= numTracks; ++i)
            {
                track = GetMovieIndTrack (movie, i);
                media = GetTrackMedia (track);

                OSType mediaType;
                GetMediaHandlerDescription (media, &mediaType, 0, 0);

                if (mediaType == SoundMediaType
                     && trackCount++ == trackNum_)
                {
                    ok = true;
                    break;
                }
            }
        }

        if (! ok)
            return;

        ok = false;

        lengthInSamples = GetMediaDecodeDuration (media);
        usesFloatingPointData = false;

        samplesPerFrame = (int) (GetMediaDecodeDuration (media) / GetMediaSampleCount (media));

        trackUnitsPerFrame = GetMovieTimeScale (movie) * samplesPerFrame
                                / GetMediaTimeScale (media);

        OSStatus err = MovieAudioExtractionBegin (movie, 0, &extractor);

        unsigned long output_layout_size;
        err = MovieAudioExtractionGetPropertyInfo (extractor,
                                                   kQTPropertyClass_MovieAudioExtraction_Audio,
                                                   kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
                                                   0, &output_layout_size, 0);
        if (err != noErr)
            return;

        HeapBlock <AudioChannelLayout> qt_audio_channel_layout;
        qt_audio_channel_layout.calloc (output_layout_size, 1);

        err = MovieAudioExtractionGetProperty (extractor,
                                               kQTPropertyClass_MovieAudioExtraction_Audio,
                                               kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
                                               output_layout_size, qt_audio_channel_layout, 0);

        qt_audio_channel_layout[0].mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;

        err = MovieAudioExtractionSetProperty (extractor,
                                               kQTPropertyClass_MovieAudioExtraction_Audio,
                                               kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
                                               output_layout_size,
                                               qt_audio_channel_layout);

        err = MovieAudioExtractionGetProperty (extractor,
                                               kQTPropertyClass_MovieAudioExtraction_Audio,
                                               kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
                                               sizeof (inputStreamDesc),
                                               &inputStreamDesc, 0);
        if (err != noErr)
            return;

        inputStreamDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger
                                        | kAudioFormatFlagIsPacked
                                        | kAudioFormatFlagsNativeEndian;
        inputStreamDesc.mBitsPerChannel = sizeof (SInt16) * 8;
        inputStreamDesc.mChannelsPerFrame = jmin ((UInt32) 2, inputStreamDesc.mChannelsPerFrame);
        inputStreamDesc.mBytesPerFrame = sizeof (SInt16) * inputStreamDesc.mChannelsPerFrame;
        inputStreamDesc.mBytesPerPacket = inputStreamDesc.mBytesPerFrame;

        err = MovieAudioExtractionSetProperty (extractor,
                                               kQTPropertyClass_MovieAudioExtraction_Audio,
                                               kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
                                               sizeof (inputStreamDesc),
                                               &inputStreamDesc);
        if (err != noErr)
            return;

        Boolean allChannelsDiscrete = false;
        err = MovieAudioExtractionSetProperty (extractor,
                                               kQTPropertyClass_MovieAudioExtraction_Movie,
                                               kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete,
                                               sizeof (allChannelsDiscrete),
                                               &allChannelsDiscrete);

        if (err != noErr)
            return;

        bufferList->mNumberBuffers = 1;
        bufferList->mBuffers[0].mNumberChannels = inputStreamDesc.mChannelsPerFrame;
        bufferList->mBuffers[0].mDataByteSize =  jmax ((UInt32) 4096, (UInt32) (samplesPerFrame * inputStreamDesc.mBytesPerFrame) + 16);

        dataBuffer.malloc (bufferList->mBuffers[0].mDataByteSize);
        bufferList->mBuffers[0].mData = dataBuffer;

        sampleRate = inputStreamDesc.mSampleRate;
        bitsPerSample = 16;
        numChannels = inputStreamDesc.mChannelsPerFrame;

        detachThread();
        ok = true;
    }