Skip to content

Instantly share code, notes, and snippets.

@depthlove
Forked from pgodino/GPUImageMovie.h
Created October 11, 2017 17:31
Show Gist options
  • Save depthlove/df6b646db3a5af3cfdc411c8af9d65c3 to your computer and use it in GitHub Desktop.
Save depthlove/df6b646db3a5af3cfdc411c8af9d65c3 to your computer and use it in GitHub Desktop.

Revisions

  1. @pgodino pgodino created this gist Oct 2, 2012.
    49 changes: 49 additions & 0 deletions GPUImageMovie.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,49 @@
    #import <Foundation/Foundation.h>
    #import <AVFoundation/AVFoundation.h>
    #import "GPUImageOpenGLESContext.h"
    #import "GPUImageOutput.h"
    #import "TPCircularBuffer.h"

    #ifndef max
    #define max( a, b ) ( ((a) > (b)) ? (a) : (b) )
    #endif

    #ifndef min
    #define min( a, b ) ( ((a) < (b)) ? (a) : (b) )
    #endif

    /** Source object for filtering movies
    */
    @interface GPUImageMovie : GPUImageOutput

    @property (readwrite, retain) AVAsset *asset;
    @property(readwrite, retain) NSURL *url;

    /** This enables the benchmarking mode, which logs out instantaneous and average frame times to the console
    */
    @property(readwrite, nonatomic) BOOL runBenchmark;

    /** This determines whether to play back a movie as fast as the frames can be processed, or if the original speed of the movie should be respected. Defaults to NO.
    */
    @property(readwrite, nonatomic) BOOL playAtActualSpeed;

    @property(readwrite, nonatomic) AudioBuffer aBuffer;

    @property (nonatomic) TPCircularBuffer tpCircularBuffer;

    /// @name Initialization and teardown
    - (id)initWithAsset:(AVAsset *)asset;
    - (id)initWithURL:(NSURL *)url;
    - (void)textureCacheSetup;

    /// @name Movie processing
    - (void)enableSynchronizedEncodingUsingMovieWriter:(GPUImageMovieWriter *)movieWriter;
    - (void)readNextVideoFrameFromOutput:(AVAssetReaderTrackOutput *)readerVideoTrackOutput;
    - (void)readNextAudioSampleFromOutput:(AVAssetReaderTrackOutput *)readerAudioTrackOutput;
    - (void)startProcessing;
    - (void)endProcessing;
    - (void)processMovieFrame:(CMSampleBufferRef)movieSampleBuffer;

    @property(nonatomic, copy) void(^completionBlock)(void);

    @end
    658 changes: 658 additions & 0 deletions GPUImageMovie.m
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,658 @@
    #import "GPUImageMovie.h"
    #import "GPUImageMovieWriter.h"
    #import "TPCircularBuffer+AudioBufferList.h"

    #define kOutputBus 0

    TPCircularBuffer tpCircularBuffer1;

    void checkStatus(int status);
    static OSStatus playbackCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);

    void checkStatus(int status)
    {
    if (status) {
    printf("Status not 0! %d\n", status);
    // exit(1);
    }
    }

    /**
    This callback is called when the audioUnit needs new data to play through the
    speakers. If you don't have any, just don't write anything in the buffers
    */
    static OSStatus playbackCallback(void *inRefCon,
    AudioUnitRenderActionFlags *ioActionFlags,
    const AudioTimeStamp *inTimeStamp,
    UInt32 inBusNumber,
    UInt32 inNumberFrames,
    AudioBufferList *ioData) {

    // GPUImageMovie* iosAudio = (__bridge GPUImageMovie *)inRefCon;

    // Notes: ioData contains buffers (may be more than one!)
    // Fill them up as much as you can. Remember to set the size value in each buffer to match how
    // much data is in the buffer.
    // &tpCircularBuffer, &audioBufferList, NULL, kTPCircularBufferCopyAll, NULL))

    // AudioBufferList outputBufferList;
    // TPCircularBufferDequeueBufferListFrames(TPCircularBuffer *buffer, UInt32 *ioLengthInFrames, AudioBufferList *outputBufferList, AudioTimeStamp *outTimestamp, AudioStreamBasicDescription *audioFormat);

    UInt32 ioLengthInFrames = inNumberFrames;

    AudioStreamBasicDescription audioFormat;
    audioFormat.mSampleRate = 44100.00;
    audioFormat.mFormatID = kAudioFormatLinearPCM;
    audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    audioFormat.mFramesPerPacket = 1;
    audioFormat.mChannelsPerFrame = 2;
    audioFormat.mBitsPerChannel = 16;
    audioFormat.mBytesPerPacket = 4;
    audioFormat.mBytesPerFrame = 4;

    AudioTimeStamp outTimestamp;


    UInt32 retVal = TPCircularBufferPeek(&tpCircularBuffer1, &outTimestamp, &audioFormat);
    NSLog (@"Pippo %ld\n", retVal);

    if (retVal > 0)
    // TPCircularBuffer tpCirc = [iosAudio tpCircularBuffer];
    TPCircularBufferDequeueBufferListFrames(&tpCircularBuffer1, &ioLengthInFrames, ioData, &outTimestamp, &audioFormat);



    /*
    AudioBuffer aBuffer = outputBufferList.mBuffers[0];
    AudioBuffer buffer = ioData->mBuffers[0];
    UInt32 size = min(buffer.mDataByteSize, aBuffer.mDataByteSize); // dont copy more data then we have, or then fits
    memcpy(buffer.mData, aBuffer.mData, size);
    buffer.mDataByteSize = size; // indicate how much data we wrote in the buffer
    */
    /*
    AudioBuffer *pBuffer = [iosAudio.sampleBuffer getAudioBuffer];
    if (pBuffer != NULL) {
    AudioBuffer aBuffer = *pBuffer ;
    AudioBuffer buffer = ioData->mBuffers[0];
    // [[iosAudio.array objectAtIndex:[iosAudio.array count]-1] getValue:&aBuffer];
    // [iosAudio.array removeObjectAtIndex:[iosAudio.array count]-1];
    UInt32 size = min(buffer.mDataByteSize, aBuffer.mDataByteSize); // dont copy more data then we have, or then fits
    memcpy(buffer.mData, aBuffer.mData, size);
    buffer.mDataByteSize = size; // indicate how much data we wrote in the buffer
    }
    */

    /*for (int i=0; i < ioData->mNumberBuffers; i++) { // in practice we will only ever have 1 buffer, since audio format is mono
    AudioBuffer buffer = ioData->mBuffers[i];
    // NSLog(@" Buffer %d has %d channels and wants %d bytes of data.", i, buffer.mNumberChannels, buffer.mDataByteSize);
    // copy temporary buffer data to output buffer
    UInt32 size = min(buffer.mDataByteSize, [iosAudio aBuffer].mDataByteSize); // dont copy more data then we have, or then fits
    memcpy(buffer.mData, [iosAudio aBuffer].mData, size);
    buffer.mDataByteSize = size; // indicate how much data we wrote in the buffer
    } */

    return noErr;
    }

    @interface GPUImageMovie ()
    {
    BOOL audioEncodingIsFinished, videoEncodingIsFinished;
    GPUImageMovieWriter *synchronizedMovieWriter;
    CVOpenGLESTextureCacheRef coreVideoTextureCache;
    AVAssetReader *reader;
    CMTime previousFrameTime, previousAudioFrameTime;
    CFAbsoluteTime previousActualFrameTime, previousAudioActualFrameTime;

    AudioComponentInstance audioUnit;

    TPCircularBuffer tpCircularBuffer;
    }

    - (void)processAsset;

    @end

    @implementation GPUImageMovie

    @synthesize url = _url;
    @synthesize asset = _asset;
    @synthesize runBenchmark = _runBenchmark;
    @synthesize playAtActualSpeed = _playAtActualSpeed;
    @synthesize completionBlock;
    @synthesize tpCircularBuffer;

    #pragma mark -
    #pragma mark Initialization and teardown

    - (id)initWithURL:(NSURL *)url;
    {
    if (!(self = [super init]))
    {
    return nil;
    }

    [self textureCacheSetup];

    self.url = url;
    self.asset = nil;

    TPCircularBufferInit(&tpCircularBuffer1, 4096*500);

    return self;
    }

    - (id)initWithAsset:(AVAsset *)asset;
    {
    if (!(self = [super init]))
    {
    return nil;
    }

    [self textureCacheSetup];

    self.url = nil;
    self.asset = asset;

    TPCircularBufferInit(&tpCircularBuffer1, 4096*500);

    return self;
    }

    - (void)textureCacheSetup;
    {
    if ([GPUImageOpenGLESContext supportsFastTextureUpload])
    {
    runSynchronouslyOnVideoProcessingQueue(^{
    [GPUImageOpenGLESContext useImageProcessingContext];
    #if defined(__IPHONE_6_0)
    CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, [[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context], NULL, &coreVideoTextureCache);
    #else
    CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, (__bridge void *)[[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context], NULL, &coreVideoTextureCache);
    #endif
    if (err)
    {
    NSAssert(NO, @"Error at CVOpenGLESTextureCacheCreate %d", err);
    }

    // Need to remove the initially created texture
    [self deleteOutputTexture];
    });
    }
    }

    - (void)dealloc
    {
    if ([GPUImageOpenGLESContext supportsFastTextureUpload])
    {
    CFRelease(coreVideoTextureCache);
    }

    TPCircularBufferCleanup(&tpCircularBuffer1);
    }
    #pragma mark -
    #pragma mark Movie processing

    - (void)enableSynchronizedEncodingUsingMovieWriter:(GPUImageMovieWriter *)movieWriter;
    {
    synchronizedMovieWriter = movieWriter;
    movieWriter.encodingLiveVideo = NO;
    }

    - (void)startProcessing
    {
    if(self.url == nil)
    {
    [self processAsset];
    return;
    }

    previousFrameTime = kCMTimeZero;
    previousActualFrameTime = CFAbsoluteTimeGetCurrent();

    previousAudioFrameTime = kCMTimeZero;
    previousAudioActualFrameTime = CFAbsoluteTimeGetCurrent();

    NSDictionary *inputOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
    AVURLAsset *inputAsset = [[AVURLAsset alloc] initWithURL:self.url options:inputOptions];
    [inputAsset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"tracks"] completionHandler: ^{
    NSError *error = nil;
    AVKeyValueStatus tracksStatus = [inputAsset statusOfValueForKey:@"tracks" error:&error];
    if (!tracksStatus == AVKeyValueStatusLoaded)
    {
    return;
    }
    self.asset = inputAsset;
    [self setupAudio];
    [self processAsset];
    }];
    }

    - (void)processAsset
    {
    __unsafe_unretained GPUImageMovie *weakSelf = self;
    NSError *error = nil;
    reader = [AVAssetReader assetReaderWithAsset:self.asset error:&error];

    NSMutableDictionary *outputSettings = [NSMutableDictionary dictionary];
    [outputSettings setObject: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey: (NSString*)kCVPixelBufferPixelFormatTypeKey];
    // Maybe set alwaysCopiesSampleData to NO on iOS 5.0 for faster video decoding
    AVAssetReaderTrackOutput *readerVideoTrackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:[[self.asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] outputSettings:outputSettings];
    [reader addOutput:readerVideoTrackOutput];

    NSArray *audioTracks = [self.asset tracksWithMediaType:AVMediaTypeAudio];
    // BOOL shouldRecordAudioTrack = (([audioTracks count] > 0) && (weakSelf.audioEncodingTarget != nil) );
    AVAssetReaderTrackOutput *readerAudioTrackOutput = nil;

    //if (shouldRecordAudioTrack)
    if ([audioTracks count] > 0)
    {
    audioEncodingIsFinished = NO;

    // AudioChannelLayout channelLayout;
    // memset(&channelLayout, 0, sizeof(AudioChannelLayout));
    // channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;

    NSMutableDictionary *audioOutputSettings = [NSMutableDictionary dictionary];
    [audioOutputSettings setObject:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
    [audioOutputSettings setObject:[NSNumber numberWithInt:44100] forKey:AVSampleRateKey];
    [audioOutputSettings setObject:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
    // [audioOutputSettings setObject:[NSData dataWithBytes:&channelLayout length:sizeof(AudioChannelLayout)] forKey:AVChannelLayoutKey];
    [audioOutputSettings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
    [audioOutputSettings setObject:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey];
    [audioOutputSettings setObject:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsFloatKey];
    [audioOutputSettings setObject:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsNonInterleaved];

    // This might need to be extended to handle movies with more than one audio track
    AVAssetTrack* audioTrack = [audioTracks objectAtIndex:0];
    readerAudioTrackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:audioOutputSettings];
    [reader addOutput:readerAudioTrackOutput];
    }

    if ([reader startReading] == NO)
    {
    NSLog(@"Error reading from file at URL: %@", weakSelf.url);
    return;
    }

    if (synchronizedMovieWriter != nil)
    {
    [synchronizedMovieWriter setVideoInputReadyCallback:^{
    [weakSelf readNextVideoFrameFromOutput:readerVideoTrackOutput];
    }];

    [synchronizedMovieWriter setAudioInputReadyCallback:^{
    [weakSelf readNextAudioSampleFromOutput:readerAudioTrackOutput];
    }];

    [synchronizedMovieWriter enableSynchronizationCallbacks];
    }
    else
    {
    [self startPlay];

    while (reader.status == AVAssetReaderStatusReading)
    {
    [weakSelf readNextVideoFrameFromOutput:readerVideoTrackOutput];

    // if ( (shouldRecordAudioTrack) && (!audioEncodingIsFinished) )
    if (!audioEncodingIsFinished)
    {
    [weakSelf readNextAudioSampleFromOutput:readerAudioTrackOutput];
    }

    }

    [self stopPlay];

    if (reader.status == AVAssetWriterStatusCompleted) {
    [weakSelf endProcessing];
    }
    }
    }

    - (void)readNextVideoFrameFromOutput:(AVAssetReaderTrackOutput *)readerVideoTrackOutput;
    {
    if (reader.status == AVAssetReaderStatusReading)
    {
    CMSampleBufferRef sampleBufferRef = [readerVideoTrackOutput copyNextSampleBuffer];
    if (sampleBufferRef)
    {
    if (_playAtActualSpeed)
    {
    // Do this outside of the video processing queue to not slow that down while waiting
    CMTime currentSampleTime = CMSampleBufferGetOutputPresentationTimeStamp(sampleBufferRef);
    CMTime differenceFromLastFrame = CMTimeSubtract(currentSampleTime, previousFrameTime);
    CFAbsoluteTime currentActualTime = CFAbsoluteTimeGetCurrent();

    CGFloat frameTimeDifference = CMTimeGetSeconds(differenceFromLastFrame);
    CGFloat actualTimeDifference = currentActualTime - previousActualFrameTime;

    if (frameTimeDifference > actualTimeDifference)
    {
    usleep(1000000.0 * (frameTimeDifference - actualTimeDifference));
    }

    previousFrameTime = currentSampleTime;
    previousActualFrameTime = CFAbsoluteTimeGetCurrent();
    }

    __unsafe_unretained GPUImageMovie *weakSelf = self;
    runSynchronouslyOnVideoProcessingQueue(^{
    [weakSelf processMovieFrame:sampleBufferRef];
    });

    CMSampleBufferInvalidate(sampleBufferRef);
    CFRelease(sampleBufferRef);
    }
    else
    {
    videoEncodingIsFinished = YES;
    [self endProcessing];
    }
    }
    else if (synchronizedMovieWriter != nil)
    {
    if (reader.status == AVAssetWriterStatusCompleted)
    {
    [self endProcessing];
    }
    }
    }

    - (void)readNextAudioSampleFromOutput:(AVAssetReaderTrackOutput *)readerAudioTrackOutput;
    {
    if (audioEncodingIsFinished)
    {
    return;
    }

    CMSampleBufferRef audioSampleBufferRef = [readerAudioTrackOutput copyNextSampleBuffer];

    if (audioSampleBufferRef)
    {
    __unsafe_unretained GPUImageMovie *weakSelf = self;
    runSynchronouslyOnVideoProcessingQueue(^{
    [self.audioEncodingTarget processAudioBuffer:audioSampleBufferRef];
    [weakSelf processAudioFrame:audioSampleBufferRef];

    CMSampleBufferInvalidate(audioSampleBufferRef);
    CFRelease(audioSampleBufferRef);
    });
    }
    else
    {
    audioEncodingIsFinished = YES;

    }
    }

    - (void)processMovieFrame:(CMSampleBufferRef)movieSampleBuffer;
    {
    // CMTimeGetSeconds
    // CMTimeSubtract

    CMTime currentSampleTime = CMSampleBufferGetOutputPresentationTimeStamp(movieSampleBuffer);
    CVImageBufferRef movieFrame = CMSampleBufferGetImageBuffer(movieSampleBuffer);

    int bufferHeight = CVPixelBufferGetHeight(movieFrame);
    #if TARGET_IPHONE_SIMULATOR
    int bufferWidth = CVPixelBufferGetBytesPerRow(movieFrame) / 4; // This works around certain movie frame types on the Simulator (see https://github.com/BradLarson/GPUImage/issues/424)
    #else
    int bufferWidth = CVPixelBufferGetWidth(movieFrame);
    #endif

    CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();

    if ([GPUImageOpenGLESContext supportsFastTextureUpload])
    {
    CVPixelBufferLockBaseAddress(movieFrame, 0);

    [GPUImageOpenGLESContext useImageProcessingContext];
    CVOpenGLESTextureRef texture = NULL;
    CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, coreVideoTextureCache, movieFrame, NULL, GL_TEXTURE_2D, GL_RGBA, bufferWidth, bufferHeight, GL_BGRA, GL_UNSIGNED_BYTE, 0, &texture);

    if (!texture || err) {
    NSLog(@"Movie CVOpenGLESTextureCacheCreateTextureFromImage failed (error: %d)", err);
    return;
    }

    outputTexture = CVOpenGLESTextureGetName(texture);
    // glBindTexture(CVOpenGLESTextureGetTarget(texture), outputTexture);
    glBindTexture(GL_TEXTURE_2D, outputTexture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    for (id<GPUImageInput> currentTarget in targets)
    {
    NSInteger indexOfObject = [targets indexOfObject:currentTarget];
    NSInteger targetTextureIndex = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];

    [currentTarget setInputSize:CGSizeMake(bufferWidth, bufferHeight) atIndex:targetTextureIndex];
    [currentTarget setInputTexture:outputTexture atIndex:targetTextureIndex];

    [currentTarget newFrameReadyAtTime:currentSampleTime atIndex:targetTextureIndex];
    }

    CVPixelBufferUnlockBaseAddress(movieFrame, 0);

    // Flush the CVOpenGLESTexture cache and release the texture
    CVOpenGLESTextureCacheFlush(coreVideoTextureCache, 0);
    CFRelease(texture);
    outputTexture = 0;
    }
    else
    {
    // Upload to texture
    CVPixelBufferLockBaseAddress(movieFrame, 0);

    glBindTexture(GL_TEXTURE_2D, outputTexture);
    // Using BGRA extension to pull in video frame data directly
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferWidth, bufferHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, CVPixelBufferGetBaseAddress(movieFrame));

    CGSize currentSize = CGSizeMake(bufferWidth, bufferHeight);
    for (id<GPUImageInput> currentTarget in targets)
    {
    NSInteger indexOfObject = [targets indexOfObject:currentTarget];
    NSInteger targetTextureIndex = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];

    [currentTarget setInputSize:currentSize atIndex:targetTextureIndex];
    [currentTarget newFrameReadyAtTime:currentSampleTime atIndex:targetTextureIndex];
    }
    CVPixelBufferUnlockBaseAddress(movieFrame, 0);
    }

    if (_runBenchmark)
    {
    CFAbsoluteTime currentFrameTime = (CFAbsoluteTimeGetCurrent() - startTime);
    NSLog(@"Current frame time : %f ms", 1000.0 * currentFrameTime);
    }
    }

    - (void)endProcessing;
    {
    for (id<GPUImageInput> currentTarget in targets)
    {
    [currentTarget endProcessing];
    }

    if (synchronizedMovieWriter != nil)
    {
    [synchronizedMovieWriter setVideoInputReadyCallback:^{}];
    [synchronizedMovieWriter setAudioInputReadyCallback:^{}];
    }

    if (completionBlock)
    {
    completionBlock();
    }
    }

    - (void)processAudioFrame:(CMSampleBufferRef)movieSampleBuffer;
    {
    // CMTime currentSampleTime = CMSampleBufferGetOutputPresentationTimeStamp(movieSampleBuffer);
    // NSLog(@"%f\n", CMTimeGetSeconds(currentSampleTime));
    // CMItemCount numSamplesInBuffer = CMSampleBufferGetNumSamples(movieSampleBuffer);

    AudioBufferList audioBufferList;
    CMBlockBufferRef blockBuffer;

    CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(movieSampleBuffer,NULL,&audioBufferList,sizeof(audioBufferList),NULL,NULL,kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,&blockBuffer);


    // _audio_buffer = audioBufferList.mBuffers[0];


    // self.aBuffer = audioBufferList.mBuffers[0];
    // AudioBuffer aBuf = audioBufferList.mBuffers[0];

    // [sampleBuffer addAudioBuffer:&aBuf];

    /* AudioStreamBasicDescription audioFormat;
    audioFormat.mSampleRate = 44100.00;
    audioFormat.mFormatID = kAudioFormatLinearPCM;
    audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    audioFormat.mFramesPerPacket = 1;
    audioFormat.mChannelsPerFrame = 2;
    audioFormat.mBitsPerChannel = 16;
    audioFormat.mBytesPerPacket = 4;
    audioFormat.mBytesPerFrame = 4;
    */

    if (!TPCircularBufferCopyAudioBufferList(&tpCircularBuffer1, &audioBufferList, NULL, kTPCircularBufferCopyAll, NULL))
    NSLog(@"ahhhhhhh\n");


    CFRelease(blockBuffer);

    /* int j=0;
    float* buffer111 = (float*)malloc(numSamplesInBuffer*sizeof(float));
    for (int i = 0; i < numSamplesInBuffer; i+=2)
    {
    // buffer[i] = (float)(audioBufferList.mBuffers[0].(mData+(i*4)) + audioBufferList.mBuffers[0].mData+((i*4)+1) +audioBufferList.mBuffers[0].(mData+(i*4)+2) +audioBufferList.mBuffers[0].(mData+(i*4)+3) );
    // buffer[i] = (float)(*(char*)(aBuffer.mData+i) + *(char*)(aBuffer.mData+i+1) + *(char*)(aBuffer.mData+i+2) + *(char*)(aBuffer.mData+i+3));
    // int32_t* samples = (int32_t*)(aBuffer.mData);
    SInt16* samples = (SInt16*)(audioBufferList.mBuffers[0].mData);
    // buffer111[i] = (float)(*((int32_t*)aBuffer.mData+(i*sizeof(int32_t))));
    buffer111[j++] = (*(samples+(i*sizeof(SInt16)))) * SHRT_MAX;
    // NSLog(@"%f\n", *(samples+(i*sizeof(float))));
    // buffer[i] = (float)(*(aBuffer.mData+i));
    }
    // NSLog(@"%f ", buffer[0]);
    player = [[AVBufferPlayer alloc] initWithBuffer:buffer111 frames:numSamplesInBuffer/2];
    free(buffer111);
    [player play];
    */

    }

    - (void) setupAudio {
    OSStatus status;

    SInt32 ambient = kAudioSessionCategory_SoloAmbientSound;
    if (AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (ambient), &ambient)) {
    NSLog(@"Error setting ambient property");
    }

    // Describe audio component
    AudioComponentDescription desc;
    desc.componentType = kAudioUnitType_Output;
    desc.componentSubType = kAudioUnitSubType_RemoteIO;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;

    // Get component
    AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);

    // Get audio units
    status = AudioComponentInstanceNew(inputComponent, &audioUnit);
    checkStatus(status);

    UInt32 flag = 1;
    // Enable IO for playback
    status = AudioUnitSetProperty(audioUnit,
    kAudioOutputUnitProperty_EnableIO,
    kAudioUnitScope_Output,
    kOutputBus,
    &flag,
    sizeof(flag));
    checkStatus(status);

    // Describe format
    AudioStreamBasicDescription audioFormat;
    audioFormat.mSampleRate = 44100.00;
    audioFormat.mFormatID = kAudioFormatLinearPCM;
    audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    audioFormat.mFramesPerPacket = 1;
    audioFormat.mChannelsPerFrame = 2;
    audioFormat.mBitsPerChannel = 16;
    audioFormat.mBytesPerPacket = 4;
    audioFormat.mBytesPerFrame = 4;


    // Apply format
    status = AudioUnitSetProperty(audioUnit,
    kAudioUnitProperty_StreamFormat,
    kAudioUnitScope_Input,
    kOutputBus,
    &audioFormat,
    sizeof(audioFormat));
    checkStatus(status);

    // Set output callback
    AURenderCallbackStruct callbackStruct;
    callbackStruct.inputProc = playbackCallback;
    callbackStruct.inputProcRefCon = (__bridge void *)(self);
    status = AudioUnitSetProperty(audioUnit,
    kAudioUnitProperty_SetRenderCallback,
    kAudioUnitScope_Global,
    kOutputBus,
    &callbackStruct,
    sizeof(callbackStruct));
    checkStatus(status);

    // Allocate our own buffers (1 channel, 16 bits per sample, thus 16 bits per frame, thus 2 bytes per frame).
    // Practice learns the buffers used contain 512 frames, if this changes it will be fixed in processAudio.
    //tempBuffer.mNumberChannels = 1;
    //tempBuffer.mDataByteSize = 512 * 2;
    //tempBuffer.mData = malloc( 512 * 2 );

    // Initialise
    status = AudioUnitInitialize(audioUnit);
    checkStatus(status);
    }

    - (void) startPlay {
    OSStatus status = AudioOutputUnitStart(audioUnit);
    checkStatus(status);
    }

    - (void) stopPlay {
    OSStatus status = AudioOutputUnitStop(audioUnit);
    checkStatus(status);
    }



    @end