Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Play audio with movie
#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
#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
@shruthimr

This comment has been minimized.

Copy link

shruthimr commented Oct 3, 2012

Hi ,

Thanks for putting this up... :)
I was tryign to use your code. But I get an error "Problem appending pixel buffer at time: 40"

This is the error message in the function newFrameReadyAtTime of GPUImageMovieWriter

  • (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;

Do you have any clue want it means?

Thanks,
Shruthi.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.