Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // BMHttpAudioStreamManager.m
- // Радио СВЕТ
- // Created by Bogdan Michalchuk on 8/21/14.
- // Copyright (c) 2014 Радио CBET. All rights reserved.
- #import "BMHttpAudioStreamManager.h"
- // static void PlayCallback(void *inUserData, AudioQueueRef inAudioQueue, AudioQueueBufferRef inBuffer) {
- //+ (void) playCallbackWithData:BMHttpAudioStreamManager *) inUserData
- // withAudioQueueRef:inAudioQueue
- // withAudioQueueBufferRef:inBuffer {
- // BMHttpAudioStreamManager *player = (__bridge BMHttpAudioStreamManager *)inUserData;
- // if (player.playing && player.block != nil) {
- // player.block(inBuffer, player.audioFormat);
- // AudioQueueEnqueueBuffer(inAudioQueue, inBuffer, 0, NULL);
- // }
- //}
- @implementation BMHttpAudioStreamManager {
- CFReadStreamRef stream8;
- AudioData *_audioData;
- AudioQueueBufferRef _audioQueueBuffer[kNumAQBufs]; // audio queue buffers
- //Located audioData in the instance variable of ViewController
- // AudioQueueRef audioQueue;
- }
- static const CFOptionFlags kNetworkEvents = kCFStreamEventOpenCompleted |
- kCFStreamEventHasBytesAvailable |
- kCFStreamEventEndEncountered |
- kCFStreamEventErrorOccurred;
- #pragma mark Interruptions
- //static void InterruptionListenerCallback(void *inUserData, UInt32 interruptionState) {
- + (void) interruptionListenerCallbackWithManager:(BMHttpAudioStreamManager *) player
- withInteruptionState:(NSUInteger) interruptionState {
- // BMHttpAudioStreamManager *player = (__bridge BMHttpAudioStreamManager *)inUserData;
- switch (interruptionState) {
- case kAudioSessionBeginInterruption:
- [player tearDownAudio];
- break;
- case kAudioSessionEndInterruption:
- [player setUpAudio];
- [player start];
- break;
- }
- }
- //int MyFindQueueBuffer(AudioData* audioData, AudioQueueBufferRef inBuffer) {
- + (int) myFindQueueBufferWithAudioData:(AudioData *) audioData
- withBuffer:(AudioQueueBufferRef) inBuffer {
- for (unsigned int i = 0; i < kNumAQBufs; ++i) {
- if (inBuffer == audioData->audioQueueBuffer[i]) return i;
- }
- return -1;
- }
- // Called by the system when an audio queue buffer is available for reuse.
- void MyAudioQueueOutputCallback(void* inUserData,
- AudioQueueRef inAQ,
- AudioQueueBufferRef inBuffer) {
- BMHttpAudioStreamManager *manager = (__bridge BMHttpAudioStreamManager*) inUserData;
- [manager myAudioQueueOutputCallback: inAQ buffer: inBuffer];
- }
- - (void) myAudioQueueOutputCallback:(AudioQueueRef) inAQ
- buffer:(AudioQueueBufferRef) inBuffer {
- // this is called by the audio queue when it has finished decoding our data.
- // The buffer is now free to be reused.
- // unsigned int bufIndex = MyFindQueueBuffer(audioData, (AudioQueueBufferRef) inBuffer);
- unsigned int bufIndex = [BMHttpAudioStreamManager myFindQueueBufferWithAudioData: _audioData
- withBuffer: inBuffer];
- // signal waiting thread that the buffer is free.
- pthread_mutex_lock(&(_audioData->mutex));
- _audioData->inuse[bufIndex] = false;
- pthread_cond_signal(&(_audioData->cond));
- pthread_mutex_unlock(&(_audioData->mutex));
- }
- //OSStatus StartQueueIfNeeded(AudioData* audioData) {
- + (OSStatus) startQueueIfNeededWithData:(AudioData *) audioData {
- OSStatus err = noErr;
- if (!audioData->started) { // start the queue if it has not been started already
- if ((err = AudioQueueStart(audioData->audioQueue, NULL))) {
- PRINTERROR("AudioQueueStart");
- audioData->failed = true;
- //return err;
- return err;
- }
- audioData->started = true;
- NSLog(@"started");
- }
- return err;
- }
- /**/
- // OSStatus MyEnqueueBuffer(AudioData* audioData) {
- // Conversion done 10.6.14
- +(OSStatus) myEnqueueBufferWithData:(AudioData *) audioData {
- OSStatus err = noErr;
- audioData->inuse[audioData->fillBufferIndex] = true; // set in use flag
- // enqueue buffer
- AudioQueueBufferRef fillBuf = audioData->audioQueueBuffer[audioData->fillBufferIndex];
- fillBuf->mAudioDataByteSize = audioData->bytesFilled;
- err = AudioQueueEnqueueBuffer(audioData->audioQueue, fillBuf, audioData->packetsFilled, audioData->packetDescs);
- if (err) {
- PRINTERROR("AudioQueueEnqueueBuffer");
- audioData->failed = true;
- return err;
- }
- /* Dont know what I was trying to do with this code here
- AudioQueueStart(audioData->audioQueue, NULL); // Way to bypass
- (audioData);
- */
- [BMHttpAudioStreamManager startQueueIfNeededWithData:audioData];
- /*
- Moved this section to "setUpAudioSession" method
- // Allows Audio to play when phone is put to sleep or working in other apps and silent slider
- UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
- AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (sessionCategory), &sessionCategory);
- [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; // Allows to manage from locked screen
- AudioSessionSetActive(true);
- */
- return err;
- }
- //void WaitForFreeBuffer(AudioData* audioData) {
- + (void) waitForFreeBufferForData:(AudioData *) audioData {
- // go to next buffer
- if (++audioData->fillBufferIndex >= kNumAQBufs) audioData->fillBufferIndex = 0;
- audioData->bytesFilled = 0; // reset bytes filled
- audioData->packetsFilled = 0; // reset packets filled
- /*
- // wait until next buffer is not in use
- printf("->lock\n");
- pthread_mutex_lock(&audioData->mutex);
- while (audioData->inuse[audioData->fillBufferIndex]) // When flag is set for filled
- {
- printf("... WAITING ...\n");
- pthread_cond_wait(&audioData->cond, &audioData->mutex);
- }
- pthread_mutex_unlock(&audioData->mutex);
- printf("<-unlock\n");
- */
- }
- void MyAudioQueueIsRunningCallback(void* inClientData,
- AudioQueueRef inAQ,
- AudioQueuePropertyID inID) {
- BMHttpAudioStreamManager *manager = (__bridge BMHttpAudioStreamManager*) inClientData;
- [manager myAudioQueuePropertyListenerProcWithRef: inAQ withPropertyID: inID];
- }
- - (void) myAudioQueuePropertyListenerProcWithRef:(AudioQueueRef) inAQ
- withPropertyID:(AudioQueuePropertyID) inID {
- UInt32 running, size;
- OSStatus err = AudioQueueGetProperty(inAQ, kAudioQueueProperty_IsRunning, &running, &size);
- if (err) {
- PRINTERROR("get kAudioQueueProperty_IsRunning");
- return;
- }
- if (!running) {
- pthread_mutex_lock(&_audioData->mutex);
- pthread_cond_signal(&_audioData->done);
- pthread_mutex_unlock(&_audioData->mutex);
- }
- }
- void MyPropertyListenerProc(void *inUserData,
- AudioFileStreamID inAudioFileStream,
- AudioFileStreamPropertyID inPropertyID,
- UInt32 *ioFlags) {
- BMHttpAudioStreamManager *manager = (__bridge BMHttpAudioStreamManager*) inUserData;
- [manager myPropertyListenerProcForStreamID:inAudioFileStream forPropertyID:inPropertyID forIOFlags:ioFlags];
- }
- - (void) myPropertyListenerProcForStreamID:(AudioFileStreamID) inAudioFileStream
- forPropertyID:(AudioFileStreamPropertyID) inPropertyID
- forIOFlags:(UInt32 *) ioFlags {
- // this is called by audio file stream when it finds property values
- OSStatus err = noErr;
- printf("found property '%c%c%c%c'\n", (char)(inPropertyID>>24)&255, (char)(inPropertyID>>16)&255, (char)(inPropertyID>>8)&255, (char)inPropertyID&255);
- if (inPropertyID == kAudioFileStreamProperty_ReadyToProducePackets) {
- // the file stream parser is now ready to produce audio packets.
- // get the stream format.
- AudioStreamBasicDescription asbd;
- UInt32 asbdSize = sizeof(asbd);
- err = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &asbdSize, &asbd);
- if (err) {
- PRINTERROR("get kAudioFileStreamProperty_DataFormat");
- _audioData->failed = true;
- return;
- }
- // create the audio queue
- err = AudioQueueNewOutput(&asbd, MyAudioQueueOutputCallback, (__bridge void *)(self), NULL, NULL, 0, &(_audioData->audioQueue));
- if (err) { PRINTERROR("AudioQueueNewOutput");
- _audioData->failed = true;
- return;
- }
- // allocate audio queue buffers
- for (unsigned int i = 0; i < kNumAQBufs; ++i) {
- err = AudioQueueAllocateBuffer(_audioData->audioQueue, kAQBufSize, &_audioData->audioQueueBuffer[i]);
- if (err) { PRINTERROR("AudioQueueAllocateBuffer");
- _audioData->failed = true;
- break;
- }
- }
- // get the cookie size
- UInt32 cookieSize;
- Boolean writable;
- err = AudioFileStreamGetPropertyInfo(inAudioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable);
- if (err) {
- PRINTERROR("info kAudioFileStreamProperty_MagicCookieData");
- return;
- }
- printf("cookieSize %d\n", (unsigned int)cookieSize);
- // get the cookie data
- void* cookieData = calloc(1, cookieSize);
- err = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, cookieData);
- if (err) {
- PRINTERROR("get kAudioFileStreamProperty_MagicCookieData");
- free(cookieData);
- return;
- }
- // set the cookie on the queue.
- err = AudioQueueSetProperty(_audioData->audioQueue, kAudioQueueProperty_MagicCookie, cookieData, cookieSize);
- free(cookieData);
- if (err) {
- PRINTERROR("set kAudioQueueProperty_MagicCookie");
- return;
- }
- // listen for kAudioQueueProperty_IsRunning
- err = AudioQueueAddPropertyListener(_audioData->audioQueue, kAudioQueueProperty_IsRunning, MyAudioQueueIsRunningCallback, _audioData);
- if (err) { PRINTERROR("AudioQueueAddPropertyListener");
- _audioData->failed = true;
- return;
- }
- }
- }
- void ReadStreamClientCallBack(CFReadStreamRef stream,
- CFStreamEventType type,
- void* clientCallBackInfo) {
- BMHttpAudioStreamManager *manager = (__bridge BMHttpAudioStreamManager*) clientCallBackInfo;
- [manager readStreamClientCallBackWithData:stream withType:type];
- }
- - (void) readStreamClientCallBackWithData:(CFReadStreamRef) stream
- withType:(CFStreamEventType) type {
- if(type == kCFStreamEventHasBytesAvailable) {
- UInt8 buffer[2048];
- CFIndex bytesRead = CFReadStreamRead(stream, buffer, sizeof(buffer));
- if (bytesRead < 0) {
- //nothing
- }
- // If zero bytes were read, wait for the EOF to come.
- else if (bytesRead) {
- // parse the data. this will call MyPropertyListenerProc and MyPacketsProc
- // AudioFileStreamParseBytes function called when you have data to pass to the parser.
- // Send the data to the parser sequentially and, if possible, without gaps.
- OSStatus err = AudioFileStreamParseBytes(_audioData->audioFileStream, bytesRead, buffer, 0);
- /* This is what I had before, before I changed it to work 10.13.14
- OSStatus err = AudioFileStreamParseBytes(((AudioData *) audioData)->audioFileStream, bytesRead, buffer, 0);
- */
- if (err) PRINTERROR("AudioFileStreamParseBytes");
- }
- }
- }
- void MyPacketsProc(void * inClientData,
- UInt32 inNumberBytes,
- UInt32 inNumberPackets,
- const void * inInputData,
- AudioStreamPacketDescription *inPacketDescriptions) {
- BMHttpAudioStreamManager *manager = (__bridge BMHttpAudioStreamManager*) inClientData;
- [manager myPacketsProcforNumberBytes: inNumberBytes forNumberPackets: inNumberPackets forInputData: inInputData withPacketDescription: inPacketDescriptions];
- }
- - (void) myPacketsProcforNumberBytes:(UInt32) inNumberBytes
- forNumberPackets:(UInt32) inNumberPackets
- forInputData:(const void *) inInputData
- withPacketDescription:(AudioStreamPacketDescription *) inPacketDescriptions {
- // if (viewController.connectionStopped) {
- // return;
- // }
- // this is called by audio file stream when it finds packets of audio
- printf("got data. bytes: %d packets: %d\n", (unsigned int)inNumberBytes, (unsigned int)inNumberPackets);
- // the following code assumes we're streaming VBR data. for CBR data, you'd need another code branch here.
- for (int i = 0; i < inNumberPackets; ++i) {
- SInt64 packetOffset = inPacketDescriptions[i].mStartOffset;
- SInt64 packetSize = inPacketDescriptions[i].mDataByteSize;
- // if the space remaining in the buffer is not enough for this packet, then enqueue the buffer.
- size_t bufSpaceRemaining = kAQBufSize - _audioData->bytesFilled;
- if (bufSpaceRemaining < packetSize/* && !viewController.connectionStopped*/) {
- // I changed this line of code to objc 10.6.14 MyEnqueueBuffer(audioData);
- [BMHttpAudioStreamManager myEnqueueBufferWithData:_audioData];
- // I changed this line of code to objc 10.6.14 WaitForFreeBuffer(audioData);
- [BMHttpAudioStreamManager waitForFreeBufferForData:_audioData];
- }
- // copy data to the audio queue buffer
- AudioQueueBufferRef fillBuf = _audioData->audioQueueBuffer[_audioData->fillBufferIndex];
- memcpy((char*)fillBuf->mAudioData + _audioData->bytesFilled, (const char*)inInputData + packetOffset, packetSize);
- // fill out packet description
- _audioData->packetDescs[_audioData->packetsFilled] = inPacketDescriptions[i];
- _audioData->packetDescs[_audioData->packetsFilled].mStartOffset = _audioData->bytesFilled;
- // keep track of bytes filled and packets filled
- _audioData->bytesFilled += packetSize;
- _audioData->packetsFilled += 1;
- // if that was the last free packet description, then enqueue the buffer.
- size_t packetsDescsRemaining = kAQMaxPacketDescs - _audioData->packetsFilled;
- if (packetsDescsRemaining == 0/* && !BMHttpAudioStreamManager.connectionStopped*/) {
- // I changed this line of code to objc 10.6.14 MyEnqueueBuffer(audioData);
- [BMHttpAudioStreamManager myEnqueueBufferWithData:_audioData];
- // I changed this line of code to objc 10.6.14 WaitForFreeBuffer(audioData);
- [BMHttpAudioStreamManager waitForFreeBufferForData:_audioData]; }
- }
- }
- #pragma mark connectionStart
- -(void) connectionStart {
- self.playButtonClicked = YES; //this is where it says that stream is playing.
- NSLog(@"%s", "Hello");
- @try {
- // allocate a struct for storing our state
- _audioData = (AudioData*)calloc(1, sizeof(AudioData));
- // initialize a mutex and condition so that we can block on buffers in use.
- pthread_mutex_init(&_audioData->mutex, NULL);
- pthread_cond_init(&_audioData->cond, NULL);
- pthread_cond_init(&_audioData->done, NULL);
- // create an audio file stream parser
- OSStatus err = AudioFileStreamOpen((__bridge void *)(self), MyPropertyListenerProc, MyPacketsProc,
- kAudioFileMP3Type, &_audioData->audioFileStream);
- if (err) { PRINTERROR("AudioFileStreamOpen"); //return 1;
- }
- CFStreamClientContext ctxt = {0, (__bridge void*)self, NULL, NULL, NULL};
- CFStringRef bodyData = CFSTR(""); // Usually used for POST data
- CFStringRef headerFieldName = CFSTR("X-My-Favorite-Field");
- CFStringRef headerFieldValue = CFSTR("Dreams");
- CFStringRef url = CFSTR(RADIO_LOCATION);
- CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, url, NULL);
- CFStringRef requestMethod = CFSTR("GET");
- CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, requestMethod, myURL, kCFHTTPVersion1_1);
- CFHTTPMessageSetBody(myRequest, (CFDataRef)bodyData);
- CFHTTPMessageSetHeaderFieldValue(myRequest, headerFieldName, headerFieldValue);
- //CFDataRef mySerializedRequest = CFHTTPMessageCopySerializedMessage(myRequest);
- // Create the stream for the request.
- // CFReadStreamRef stream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest);
- stream8
- = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest);
- NSLog(@"Stage 3");
- if (!stream8) {
- NSLog(@"Creating the stream failed");
- return;
- }
- // Use persistent conections so connection-based authentications work correctly.
- //CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue);
- // Set the client
- if (!CFReadStreamSetClient(stream8, kNetworkEvents, ReadStreamClientCallBack, &ctxt)) {
- CFRelease(stream8);
- NSLog(@"Setting the stream's client failed.");
- return;
- }
- // Schedule the stream
- CFReadStreamScheduleWithRunLoop(stream8, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
- // Start the HTTP connection
- if (!CFReadStreamOpen(stream8)) {
- CFReadStreamSetClient(stream8, 0, NULL, NULL);
- CFReadStreamUnscheduleFromRunLoop(stream8, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
- CFRelease(stream8);
- NSLog(@"Opening the stream failed.");
- return;
- }
- /*
- // Don't need the old stream any more.
- // if (stream && [self connectionStopped]) {
- if (stream8) {
- CFReadStreamClose(stream8);
- CFRelease(stream8);
- } */
- }
- @catch (NSException *exception) {
- NSLog(@"main: Caught %@: %@", [exception name], [exception reason]);
- }
- }
- #pragma mark - View lifecycle
- /*
- //I enable this section so I could pause
- -(void) resumeRadio { //example for resume radio stream
- NSLog(@"Resuming stream...");
- OSStatus err = AudioQueueStart(audioData->audioQueue, NULL);
- if (err) { PRINTERROR("AudioQueueStart"); audioData->failed = true; //return err;
- }
- }
- -(void) pauseRadio { //example for pause radio stream
- NSLog(@"stream paused");
- OSStatus err = AudioQueuePause(audioData->audioQueue);
- if (err) { PRINTERROR("AudioQueueStart"); audioData->failed = true; //return err;
- }
- if (stream) {
- CFReadStreamClose(stream);
- CFRelease(stream);
- stream = nil;
- }
- }
- */
- #pragma mark stopRadio
- - (void) stopRadio {
- self.connectionStopped = YES; //this is where it says that connection was stopped
- AudioQueueStop(_audioData->audioQueue, TRUE);
- AudioQueueDispose(_audioData->audioQueue, TRUE);
- [self tearDownAudio];
- // [self tearDownPlayQueue];
- }
- -(void)tearDownPlayQueue {
- AudioQueueDispose(_audioData->audioQueue, YES);
- _audioData->audioQueue = NULL;
- }
- #pragma mark - from hollance code
- - (void)tearDownAudioSession {
- [[AVAudioSession sharedInstance] setActive:false error:nil];
- // AudioSessionSetActive(false);
- CFReadStreamClose(stream8);
- CFRelease(stream8);
- stream8 = NULL;
- }
- - (void)tearDownAudio {
- if (_audioData != NULL)
- {
- [self tearDownPlayQueue];
- [self tearDownAudioSession];
- }
- }
- - (void)stopAudio {
- {
- NSLog(@"STOPPED");
- AudioQueueStop(_audioData, TRUE);
- [self tearDownAudio];
- self.playing = NO;
- }
- }
- - (void)start {
- if (!self.playing) {
- self.playing = YES;
- // AudioQueueStart(_audioData->audioQueue, NULL);
- [self connectionStart];
- [self setUpAudio];
- }
- }
- /*- (void)primePlayQueueBuffers
- {
- for (int i = 0; i < kNumAQBufs; ++i)
- {
- MyAudioQueueOutputCallback((__bridge void *)self, _audioData, _audioQueueBuffer[i]);
- }
- }
- */
- - (void)setUpAudio
- {
- if (_audioData == NULL)
- {
- // [self setUpAVAudioSession];
- // [self setUpPlayQueue];
- // [self setUpPlayQueueBuffers];
- }
- }
- #pragma mark - Remote control and AVAudioSession
- - (void)initAVAudioSession {
- //Allows the audio to be played
- AVAudioSession *audioSession = [AVAudioSession sharedInstance];
- NSError *setCategoryError = nil;
- BOOL success = [audioSession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
- if (!success) { /* handle the error condition */ }
- NSError *activationError = nil;
- success = [audioSession setActive:YES error:&activationError];
- if (!success) { /* handle the error condition */ }
- [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; // Allows to manage from locked screen
- NSLog(@"Went through");
- /* ! What code I used for ios7
- // Allows Audio to play when phone is put to sleep or working in other apps and silent slider
- UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
- AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (sessionCategory), &sessionCategory);
- [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; // Allows to manage from locked screen
- AudioSessionSetActive(true);
- */
- }
- - (void)remoteControlReceivedWithEvent:(UIEvent *)theEvent {
- if (theEvent.type == UIEventTypeRemoteControl) {
- switch(theEvent.subtype) {
- case UIEventSubtypeRemoteControlTogglePlayPause:
- //Insert code
- case UIEventSubtypeRemoteControlPlay:
- [self start];
- break;
- case UIEventSubtypeRemoteControlPause:
- [self stopAudio];
- break;
- case UIEventSubtypeRemoteControlStop:
- [self stopAudio];
- break;
- default:
- return;
- }
- }
- }
- /*
- - (void)readyPlayQueueBuffers {
- for (int t = 0; t < kNumAQBufs; ++t) {
- PlayCallback((__bridge void *)self, _audioData, _audioQueueBuffer[kNumAQBufs]);
- }
- }
- */
- @end
Advertisement
Add Comment
Please, Sign In to add comment