使用NSURLSession播放远程音频,代码点评

Using NSURLSession to play remote audio, code critique

首先,为了避免在此处发布大量代码,我创建了一个非常基本的示例 - https://github.com/opheliadesign/AudioTest

我是 iOS 开发的新手,我在理解在远程服务器上加载和播放静态 MP3 文件的最佳方式时遇到了一些困难。音频 不是 直播,不到 1 兆字节 - 平均长约 30 秒(它们是无线电调度)。

有人建议我使用 NSURLSession 加载 MP3 文件,然后在完成块中播放它。这似乎可行,但我觉得我可以更好地处理它,只是不知道该怎么做。这是我抓取音频并开始播放的块:

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:YES];
    NSLog(@"View loaded");
    // Register to receive notification from AppDelegate when entering background
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(stopPlayingAudio) name:@"stopPlayingAudio" object:nil];

    // Assign timer to update progress
    self.updateTimer =     [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateSeekBar) userInfo:nil repeats:YES];

    // Demo recording URL
    NSString *recordingUrl = @"https://s3.amazonaws.com/tevfd-recording/All_Fire_and_EMS_2015-02-0214_48_33_630819.mp3";

    NSURLSession *session = [NSURLSession sharedSession];
    [[session dataTaskWithURL:[NSURL URLWithString:recordingUrl] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (!error) {
            NSLog(@"No error..");
            self.player = [[AVAudioPlayer alloc]initWithData:data error:nil];
            // Setup slider for track position
            self.slider.minimumValue = 0;
            self.slider.maximumValue = self.player.duration;
            [self.player prepareToPlay];
            [self.player play];
        }
    }]resume];

}

例如,有一个在移动时更新玩家位置的滑块。我在完成块中初始化 AVAudioPlayer,但在 header 中为其创建了一个 属性。如果播放器还没有初始化,移动滑块会导致崩溃吗?如果是这样,我应该如何更好地处理它?

另外,我有一个计时器,它可以在 AVAudioPlayer 播放时更新滑块位置。当轨道到达终点时,计时器继续 - 显然这是一个潜在的内存问题。不确定处理此问题的最佳方法,我还希望用户能够在完成后再次开始播放录音,例如,如果他们将滑块向右移动。

我找了又找,似乎找不到任何与我正在做的事情相关的东西。任何帮助将不胜感激。

更新 这是我首先开始的,但我在单击 UITableView 单元格和呈现 loaded/played 音频的视图之间遇到了一些延迟。几个建议的 NSURLSession.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Get the URL
    NSURL *callAudioURL = [NSURL URLWithString:[self.call objectForKey:@"url"]];
    NSData *callAudioData = [NSData dataWithContentsOfURL:callAudioURL];
    AVAudioPlayer *audio = [[AVAudioPlayer alloc] initWithData: callAudioData error:nil];
    self.player = audio;

    self.slider.minimumValue = 0;
    self.slider.maximumValue = self.player.duration;
    [[self player] play];
    self.updateTimer =     [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateSeekBar) userInfo:nil repeats:YES];
}

如果您担心会有小的延迟,那么下载并缓存它最好的解决方案。您需要下载音频文件,然后在本地播放。通常只有当音频非常独特以至于您无法在本地存储时,从网络播放或流式传输才有用。

要实现这样的东西(代码未经测试):

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSFileHandle *hFile = [NSFileHandle fileHandleForWritingAtPath:self.localPath];
//did we succeed in opening the existing file?
if (!hFile) {   //nope->create that file!
   [[NSFileManager defaultManager] createFileAtPath:self.localPath contents:nil attributes:nil];
   //try to open it again...
   hFile = [NSFileHandle fileHandleForWritingAtPath:self.localPath];
}
//did we finally get an accessable file?
if (!hFile) {
   NSLog("could not write to file %@", self.localPath); 
   return;
}

@try {
   //seek to the end of the file
   [hFile seekToEndOfFile];
   //finally write our data to it
   [hFile writeData:data];
} @catch (NSException * e) {
   NSLog("exception when writing to file %@", self.localPath); 
   result = NO;
}
   [hFile closeFile];
}

收到所有数据后,开始播放音频:

self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:self.localPath error:nil];