使用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];
首先,为了避免在此处发布大量代码,我创建了一个非常基本的示例 - 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];