//
// XMGDownLoader.h
// XMGDownLoader
//
// Created by 小码哥 on 2017/1/8.
// Copyright © 2017年 xmg. All rights reserved.
//
#import
@protocol XMGDownLoaderDelegate <NSObject>
- (void)downloadData:(NSData*)datapath:(NSString*)path;
@end
@interface XMGDownLoader : NSObject
- (void)downLoader:(NSURL*)url;
@property (nonatomic, weak) id<XMGDownLoaderDelegate> delegate;
@end
//
// XMGDownLoader.m
// XMGDownLoader
//
// Created by 小码哥 on 2017/1/8.
// Copyright © 2017年 xmg. All rights reserved.
//
#import "XMGDownLoader.h"
#import "XMGFileTool.h"
#define kTmpPath NSTemporaryDirectory()
@interface XMGDownLoader () <NSURLSessionDataDelegate>
@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, copy) NSString *downLoadingPath;
@property (nonatomic, strong) NSOutputStream *outputStream;
//是否开始有拉取到第一段数据了
@property (nonatomic, assign) BOOL isStarDownloadData;
@end
@implementation XMGDownLoader
- (instancetype)init {
if(self= [superinit]) {
}
return self;
}
- (NSURLSession *)session {
if(!_session) {
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
- (void)downLoader:(NSURL*)url {
self.isStarDownloadData = NO;
NSString*fileName = url.lastPathComponent;
self.downLoadingPath = [kTmpPath stringByAppendingPathComponent:fileName];
//这里为了方便测试,再次点击下载,如果发现downLoadingPath存在,那就删除掉
if ([XMGFileTool fileExists:self.downLoadingPath]) {
[XMGFileTool removeFile:self.downLoadingPath];
}
// 从0字节开始请求资源
[self downLoadWithURL:url offset:0];
}
#pragma mark- 协议方法
// 第一次接受到相应的时候调用(响应头, 并没有具体的资源内容)
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
// 继续接受数据
// 确定开始下载数据
self.outputStream = [NSOutputStream outputStreamToFileAtPath:self.downLoadingPath append:YES];
[self.outputStream open];
completionHandler(NSURLSessionResponseAllow);
}
static NSInteger i = 0;
//下载到资源了,多次调用,直到下载完毕
- (void)URLSession:(NSURLSession*)sessiondataTask:(NSURLSessionDataTask*)dataTaskdidReceiveData:(NSData*)data
{
//下载数据写进downLoadingPath
[self.outputStream write:data.bytes maxLength:data.length];
if(self.delegate&& [self.delegaterespondsToSelector:@selector(downloadData:path:)]) {
if(self.isStarDownloadData == NO) {
self.isStarDownloadData = YES;
//这里我也不知道为啥调用一次,就能把那么长一首歌全播下来,按理来说,这里会多次收到下载数据,应该是一小段一小段数据下载下来的.理论上应该一开始只播放几秒. 所以猜测可能和NSOutputStream这种流的方式写入文件有关系. 如果用[data WriteToFile]这种就不行
[self.delegate downloadData:data path:self.downLoadingPath];
}
}
NSLog(@"在接受后续数据%ld",data.length);
}
// 请求完成的时候调用( != 请求成功/失败)
- (void)URLSession:(NSURLSession*)sessiontask:(NSURLSessionTask*)taskdidCompleteWithError:(NSError*)error {
NSLog(@"请求完成");
if(error ==nil) {
// 不一定是成功
// 数据是肯定可以请求完毕
// 判断, 本地缓存 == 文件总大小 {filename: filesize: md5:xxx}
// 如果等于 => 验证, 是否文件完整(file md5 )
}else{
NSLog(@"有问题");
}
[self.outputStream close];
}
#pragma mark- 私有方法
/**
根据开始字节, 请求资源
@param url url
@param offset 开始字节
*/
- (void)downLoadWithURL:(NSURL*)urloffset:(longlong)offset {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:0];
[requestsetValue:[NSString stringWithFormat:@"bytes=%lld-", offset] forHTTPHeaderField:@"Range"];
// session 分配的task, 默认情况, 挂起状态
NSURLSessionDataTask*dataTask = [self.sessiondataTaskWithRequest:request];
[dataTaskresume];
}
@end
//
// XMGFileTool.h
// XMGDownLoader
//
// Created by 小码哥 on 2017/1/8.
// Copyright © 2017年 xmg. All rights reserved.
//
#import
@interface XMGFileTool : NSObject
+ (BOOL)fileExists:(NSString*)filePath;
+ (longlong)fileSize:(NSString*)filePath;
+ (void)moveFile:(NSString*)fromPathtoPath:(NSString*)toPath;
+ (void)removeFile:(NSString*)filePath;
@end
//
// XMGFileTool.m
// XMGDownLoader
//
// Created by 小码哥 on 2017/1/8.
// Copyright © 2017年 xmg. All rights reserved.
//
#import "XMGFileTool.h"
@implementation XMGFileTool
+ (BOOL)fileExists:(NSString*)filePath {
if(filePath.length==0) {
returnNO;
}
return [[NSFileManager defaultManager] fileExistsAtPath:filePath];
}
+ (longlong)fileSize:(NSString*)filePath {
if(![selffileExists:filePath]) {
return0;
}
NSDictionary *fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
return[fileInfo[NSFileSize]longLongValue];
}
+ (void)moveFile:(NSString*)fromPathtoPath:(NSString*)toPath {
if(![selffileSize:fromPath]) {
return;
}
[[NSFileManager defaultManager] moveItemAtPath:fromPath toPath:toPath error:nil];
}
+ (void)removeFile:(NSString*)filePath {
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}
@end
//
// ViewController.m
// XMGDownLoader
//
// Created by 小码哥 on 2017/1/8.
// Copyright © 2017年 xmg. All rights reserved.
//
#import "ViewController.h"
#import "XMGDownLoader.h"
#import
@interface ViewController ()<XMGDownLoaderDelegate>
@property (nonatomic, strong) XMGDownLoader *downLoader;
@property (nonatomic, strong) AVAudioEngine *engine;
@property (nonatomic, strong) AVAudioPlayerNode *playerNode;
@end
@implementation ViewController
- (XMGDownLoader *)downLoader {
if (!_downLoader) {
_downLoader= [XMGDownLoadernew];
_downLoader.delegate = self;
}
return _downLoader;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.engine= [[AVAudioEnginealloc]init];
self.playerNode = [[AVAudioPlayerNode alloc] init];
[self.engine attachNode:self.playerNode];
[self.engine connect:self.playerNode to:self.engine.mainMixerNode format:nil];
// 安装音频缓冲区的处理
AVAudioFormat *format = [self.engine.mainMixerNode inputFormatForBus:0];
[self.playerNodeinstallTapOnBus:0bufferSize:4096format:formatblock:^(AVAudioPCMBuffer*buffer,AVAudioTime*when) {
[selfprocessAudioBuffer:buffer];
}];
NSError*error =nil;
if(![self.enginestartAndReturnError:&error]) {
NSLog(@"Audio Engine error: %@", error.localizedDescription);
}
[self.playerNode play];
// Do any additional setup after loading the view.
UIButton*btn = [[UIButtonalloc]initWithFrame:CGRectMake(0,100,100,100)];
btn.backgroundColor = [UIColor redColor];
[btnsetTitle:@"下载播放" forState:UIControlStateNormal];
[btnaddTarget:self action:@selector(downAndPlayAudio) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
- (void)downAndPlayAudio {
NSURL *url1 = [NSURL URLWithString:@"http://cdnringhs.shoujiduoduo.com/ringres/userv1/m96/482/317324482.mp3"];//短mp3音频
NSURL *url2 = [NSURL URLWithString:@"http://cdnringhs.shoujiduoduo.com/ringres/userv1/m96/290/315379290.mp3"];//长mp3音频
[self.downLoader downLoader:url2];
}
- (void)playWithUrl:(NSURL*)url {
NSError*error =nil;
AVAudioFile*file = [[AVAudioFilealloc]initForReading:urlerror:&error];
if(error) {
NSLog(@"Audio file error: %@", error.localizedDescription);
return;
}
[self.playerNode scheduleFile:file atTime:nil completionHandler:^{
NSLog(@"播放完成");
}];
}
- (void)processAudioBuffer:(AVAudioPCMBuffer *)buffer {
AVAudioChannelCountchannelCount = buffer.format.channelCount;
if(channelCount >0) {
float*channelData = buffer.floatChannelData[0];
NSUIntegerframeLength = buffer.frameLength;
NSMutableArray *channelDataArray = [NSMutableArrayarrayWithCapacity:frameLength];
for(NSUIntegeri =0; i < frameLength; i++) {
[channelDataArrayaddObject:@(channelData[i])];
}
floatrms =0;
for(NSNumber*valueinchannelDataArray) {
rms += value.floatValue* value.floatValue;
}
rms =sqrt(rms / frameLength);
floatavgPower =20*log10(rms);
dispatch_async(dispatch_get_main_queue(), ^{
// NSLog(@"Current audio power: %f dB", avgPower);
// 你可以在这里更新UI,如进度条或波动图
});
}
}
#pragma mark - XMGDownLoaderDelegate
- (void)downloadData:(NSData*)datapath:(NSString*)path {
NSURL*url = [NSURLfileURLWithPath:path];
[selfplayWithUrl:url];
}
@end
网友评论