• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Streaming using coreaudio
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Streaming using coreaudio


  • Subject: Re: Streaming using coreaudio
  • From: "Hamish Allan" <email@hidden>
  • Date: Tue, 13 Jan 2009 22:57:52 +0000

On Tue, Jan 13, 2009 at 10:22 PM, John Michael Zorko <email@hidden> wrote:

>  If NSURLConnection is reading as fast as it can (and caching all that it's
> read until I consume it), and I can only consume it at a certain rate (say,
> for a 128K MP3 stream), NSURLConnection's ease of use ends up costing more
> than one might expect (and for low-memory platforms like the iPhone, quite
> possibly more than it's worth -- the iPhone OS kept sending my app memory
> warnings when I tried to stream a long-ish piece of audio).

Ah, sorry, I should have been clearer: the OP mentioned shoutcast, so
I assumed we were talking about a stream of indeterminate length
(expectedContentLength == -1), in which production and consumption
rates are necessarily equal.

For streaming finite length audio, it might be easier to download the
data into a file but start playing the file before the download is
finished. An unfinished and unpolished piece of code I wrote for this
purpose is included below; feel free to use this under a BSD license.

Best wishes,
Hamish

//
//  FiniteStreamAudioPlayer.h
//  FiniteStreamAudioPlayer
//
//  Created by Hamish on 18/12/2008.
//  Copyright 2008 Hamish Allan. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@class FiniteStreamAudioPlayer;

@protocol FiniteStreamAudioPlayerDelegate

- (void)finiteStreamAudioPlayerDidFinishPlaying:(FiniteStreamAudioPlayer
*)player successfully:(BOOL)flag;
- (void)finiteStreamAudioPlayerDecodeErrorDidOccur:(FiniteStreamAudioPlayer
*)player error:(NSError *)error;
- (void)finiteStreamAudioPlayerBeginInterruption:(FiniteStreamAudioPlayer
*)player;
- (void)finiteStreamAudioPlayerEndInterruption:(FiniteStreamAudioPlayer
*)player;

@end

@interface FiniteStreamAudioPlayer : NSObject <AVAudioPlayerDelegate>
{
    NSURL *myUrl;
    id myDelegate;

    NSURLConnection *myConnection;

    NSString *myTemporaryFilePath;
    NSFileHandle *myTemporaryFileHandle;

    long long myReceivedContentLength;
    long long myExpectedContentLength;
    NSTimeInterval myAvailableDuration;

    BOOL myAutostart;

    AVAudioPlayer *myAudioPlayer;
}

@property(assign) id<FiniteStreamAudioPlayerDelegate> delegate;

@property(readonly, getter=isPlaying) BOOL playing;

@property NSTimeInterval currentTime;
@property(readonly) NSTimeInterval minimumAvailableDurationForAutostart;
@property(readonly) NSTimeInterval availableDuration;
@property(readonly) NSTimeInterval duration;
@property BOOL autostart;

@property(readonly) NSURL *url;

- (id)initWithContentsOfURL:(NSURL *)url error:(NSError **)errorPtr;

- (BOOL)prepareToPlay;
- (BOOL)play;
- (void)pause;
- (void)stop;

@end

@interface NSString (SHA256Additions)

- (NSString *)sha256Hash;

@end



//
//  FiniteStreamAudioPlayer.m
//  FiniteStreamAudioPlayer
//
//  Created by Hamish on 18/12/2008.
//  Copyright 2008 Hamish Allan. All rights reserved.
//

#import "FiniteStreamAudioPlayer.h"

#import <CommonCrypto/CommonDigest.h>
#import <AVFoundation/AVFoundation.h>

@implementation FiniteStreamAudioPlayer

@synthesize autostart = myAutostart;

- (id)initWithContentsOfURL:(NSURL *)url error:(NSError **)errorPtr
{
    self = [super init];
    if (self)
    {
        NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
        myConnection = [[NSURLConnection alloc]
initWithRequest:urlRequest delegate:self startImmediately:YES];
        if (!myConnection)
        {
            [self release];
            return nil;
        }
        myUrl = [url retain];
        self.autostart = YES;
    }
    return self;
}

- (void)removeTemporaryFile
{
    if (myTemporaryFilePath)
        [[NSFileManager defaultManager]
removeItemAtPath:myTemporaryFilePath error:NULL];
}

- (void)dealloc
{
    [myConnection cancel];
    [myConnection autorelease];
    [self removeTemporaryFile];

    [myUrl release];
    [myTemporaryFilePath release];
    [myTemporaryFileHandle release];
    [myAudioPlayer release];

    [super dealloc];
}

- (void)cancelWithError:(NSError *)error
{
    [myConnection cancel];
    [myConnection release];
    myConnection = nil;

    [self.delegate finiteStreamAudioPlayerDecodeErrorDidOccur:self error:error];
}

- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
    NSString *urlString = [[response URL] absoluteString];
    myExpectedContentLength = [response expectedContentLength];

    if (myExpectedContentLength < 0)
    {
        NSString *errorDescription = NSLocalizedString(@"Stream not
finite", @"FiniteStreamAudioPlayer.m");
        NSString *errorFormat = NSLocalizedString(@"The stream at %@
is of indeterminate length", @"FiniteStreamAudioPlayer.m");
        NSString *errorReason = [NSString
stringWithFormat:errorFormat, urlString];

        NSError *error = [NSError errorWithDomain:NSURLErrorDomain
code:NSURLErrorUnsupportedURL userInfo:
                          [NSDictionary dictionaryWithObjectsAndKeys:
                           errorDescription, NSLocalizedDescriptionKey,
                           errorReason, NSLocalizedFailureReasonErrorKey,
                           urlString, NSErrorFailingURLStringKey, nil]];

        [self cancelWithError:error];
        return;
    }

    NSString *tmpName = [NSString stringWithFormat:@"%@.%@",
[urlString sha256Hash], [urlString pathExtension]];
    myTemporaryFilePath = [[NSTemporaryDirectory()
stringByAppendingPathComponent:tmpName] retain];
    NSLog(@"myTemporaryFilePath %@", myTemporaryFilePath);

    if (![[NSFileManager defaultManager]
createFileAtPath:myTemporaryFilePath contents:[NSData data]
attributes:nil])
    {
        NSString *errorDescription = NSLocalizedString(@"Unable to
create file", @"FiniteStreamAudioPlayer.m");
        NSString *errorReason = NSLocalizedString(@"The audio cache
file cannot be created", @"FiniteStreamAudioPlayer.m");

        NSError *error = [NSError errorWithDomain:NSURLErrorDomain
code:NSURLErrorCannotCreateFile userInfo:
                          [NSDictionary dictionaryWithObjectsAndKeys:
                           errorDescription, NSLocalizedDescriptionKey,
                           errorReason, NSLocalizedFailureReasonErrorKey,
                           urlString, NSErrorFailingURLStringKey, nil]];

        [self cancelWithError:error];
        return;
    }

    myTemporaryFileHandle = [[NSFileHandle
fileHandleForWritingAtPath:myTemporaryFilePath] retain];

    [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillTerminate:)

name:UIApplicationWillTerminateNotification object:nil];

    [myTemporaryFileHandle truncateFileAtOffset:myExpectedContentLength];
    [myTemporaryFileHandle seekToFileOffset:0];
}

- (void)applicationWillTerminate:(NSNotification *)notification
{
    [self removeTemporaryFile];
}

- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
{
    NSLog(@"didFailWithError %@", error);
}

- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
    NSLog(@"didReceiveData length %d", [data length]);
    [myTemporaryFileHandle writeData:data];

    myReceivedContentLength += [data length];
    if (!myAudioPlayer && myReceivedContentLength > 10000) // TODO:
rationalise this
    {
        NSError *error = nil;
        myAudioPlayer = [(AVAudioPlayer *)[AVAudioPlayer alloc]
initWithContentsOfURL:[NSURL fileURLWithPath:myTemporaryFilePath]
error:&error];
        if (error)
            [self cancelWithError:error];
        myAudioPlayer.delegate = self;
    }

    [self willChangeValueForKey:@"availableDuration"];
    myAvailableDuration = (CGFloat)myReceivedContentLength /
(CGFloat)myExpectedContentLength * myAudioPlayer.duration;
    if (myAvailableDuration > self.minimumAvailableDurationForAutostart)
        [myAudioPlayer play];
    [self didChangeValueForKey:@"availableDuration"];
}

- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
    NSLog(@"didFinishLoading");
    [myTemporaryFileHandle closeFile];
}

- (id<FiniteStreamAudioPlayerDelegate>)delegate
{
    return myDelegate;
}

- (void)setDelegate:(id<FiniteStreamAudioPlayerDelegate>)delegate
{
    myDelegate = delegate;
}

- (BOOL)isPlaying
{
    return myAudioPlayer.isPlaying;
}

- (NSTimeInterval)currentTime
{
    return myAudioPlayer.currentTime;
}

- (void)setCurrentTime:(NSTimeInterval)currentTime
{
    myAudioPlayer.currentTime = currentTime;
}

- (NSTimeInterval)minimumAvailableDurationForAutostart
{
    // TODO: make this take into account loading speed

    if (self.duration < 5.0)
        return self.duration;
    else
        return 5.0;
}

- (NSTimeInterval)availableDuration
{
    return myAvailableDuration;
}

- (NSTimeInterval)duration
{
    return myAudioPlayer.duration;
}

- (NSURL *)url
{
    return myUrl;
}

- (BOOL)prepareToPlay
{
    return [myAudioPlayer prepareToPlay];
}

- (BOOL)play
{
    return [myAudioPlayer play];
}

- (void)pause
{
    [myAudioPlayer pause];
}

- (void)stop
{
    [myAudioPlayer stop];
    if (myConnection)
        [myConnection cancel];
}

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player
successfully:(BOOL)flag
{
    [myDelegate finiteStreamAudioPlayerDidFinishPlaying:self successfully:flag];
}

- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player
error:(NSError *)error;
{
    [myDelegate finiteStreamAudioPlayerDecodeErrorDidOccur:self error:error];
}

- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{
    [myDelegate finiteStreamAudioPlayerBeginInterruption:self];
}

- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player
{
    [myDelegate finiteStreamAudioPlayerEndInterruption:self];
}

@end

@implementation NSString (SHA256Additions)

- (NSString *)sha256Hash
{
    unsigned char hash[CC_SHA256_DIGEST_LENGTH];

    CC_SHA256([self UTF8String], [self
lengthOfBytesUsingEncoding:NSUTF8StringEncoding], hash);
    NSMutableString *result = [NSMutableString
stringWithCapacity:CC_SHA256_DIGEST_LENGTH];

    int i;
    for (i = 0; i < CC_SHA256_DIGEST_LENGTH; ++i)
        [result appendString:[NSString stringWithFormat:@"x", hash[i]]];

    return result;
}

@end
 _______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:

This email sent to email@hidden

  • Follow-Ups:
    • Re: Streaming using coreaudio
      • From: John Michael Zorko <email@hidden>
References: 
 >Streaming using coreaudio (From: "Arnab Ganguly" <email@hidden>)
 >Re: Streaming using coreaudio (From: John Michael Zorko <email@hidden>)
 >Re: Streaming using coreaudio (From: Christopher Atlan <email@hidden>)
 >Re: Streaming using coreaudio (From: John Michael Zorko <email@hidden>)
 >Re: Streaming using coreaudio (From: "Hamish Allan" <email@hidden>)
 >Re: Streaming using coreaudio (From: John Michael Zorko <email@hidden>)

  • Prev by Date: Audio Playback Latency
  • Next by Date: Two projects I can't figure out- help?
  • Previous by thread: Re: Streaming using coreaudio
  • Next by thread: Re: Streaming using coreaudio
  • Index(es):
    • Date
    • Thread