├── .gitignore ├── CCAudioPlayer ├── CCAudioPlayer.h └── CCAudioPlayer.m ├── Demo ├── CCAudioPlayer.xcodeproj │ └── project.pbxproj ├── CCAudioPlayer │ ├── CCAppDelegate.h │ ├── CCAppDelegate.m │ ├── CCAudioPlayer-Info.plist │ ├── CCAudioPlayer-Prefix.pch │ ├── DemoViewController.h │ ├── DemoViewController.m │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── Track.h │ ├── Track.m │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── CCAudioPlayerTests │ ├── CCAudioPlayerTests-Info.plist │ ├── CCAudioPlayerTests.m │ └── en.lproj │ └── InfoPlist.strings ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /CCAudioPlayer/CCAudioPlayer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CCAudioPlayer - Objective-C Audio player support remote and local files, Sitting on top of AVPlayer: 3 | * 4 | * https://github.com/yechunjun/CCAudioPlayer 5 | * 6 | * This code is distributed under the terms and conditions of the MIT license. 7 | * 8 | * Author: 9 | * Chun Ye 10 | * 11 | */ 12 | 13 | #import 14 | 15 | #define CCLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); 16 | 17 | typedef NS_ENUM(NSInteger, CCAudioPlayerState) { 18 | CCAudioPlayerStateNone = 0, 19 | CCAudioPlayerStateRunning = 1, // Player and audio file get ready for playing 20 | CCAudioPlayerStateBuffering = (1 << 1), // Buffering the audio content. If player status is playing but no buffer to play, player will be this state 21 | CCAudioPlayerStatePlaying = (1 << 2), // Playing 22 | CCAudioPlayerStatePaused = (1 << 3), // Paused 23 | CCAudioPlayerStateStopped = (1 << 4), // Raised when an audio file has finished playing 24 | CCAudioPlayerStateError = (1 << 5), // Raised when an unexpected and possibly unrecoverable error has occured 25 | CCAudioPlayerStateDisposed = (1 << 6) // Audio player is disposed 26 | }; 27 | 28 | typedef NS_ENUM(NSInteger, CCAudioPlayerErrorCode) { 29 | CCAudioPlayerErrorNone = 0, 30 | CCAudioPlayerErrorPlayerInitializeFailed, // Player initialize failed 31 | CCAudioPlayerErrorBytesInitializeFailed, // Audio file initialize failed 32 | CCAudioPlayerErrorUnknow //Audio item play failed, but unknow reason 33 | }; 34 | 35 | @interface CCAudioPlayer : NSObject 36 | 37 | /// Initializes a new CCAudioPlayer with the given url, the url supports remote and local audio file. 38 | - (instancetype)initWithContentsOfURL:(NSURL *)url; 39 | 40 | + (instancetype)audioPlayerWithContentsOfURL:(NSURL *)url; 41 | 42 | @property (nonatomic, readonly) NSTimeInterval progress; // Gets the current item progress in seconds, Support KVO 43 | 44 | @property (nonatomic, readonly) CCAudioPlayerState playerState; // Gets the current player state, Support KVO 45 | 46 | @property (nonatomic, strong, readonly) NSURL *url; // Gets the current playing url 47 | 48 | @property (nonatomic, readonly) NSTimeInterval duration; // Gets the current item duration in seconds 49 | 50 | @property (nonatomic, readonly) CCAudioPlayerErrorCode errorCode; // Gets the error code when the player state is error 51 | 52 | @property (nonatomic, readonly) BOOL isPlaying; // Gets the current player is playing or not 53 | 54 | // Track Player Status Block, update progress and player state 55 | - (void)trackPlayerProgress:(void (^)(NSTimeInterval progress))progressHandler playerState:(void(^)(CCAudioPlayerState playerState))playerStateHandler; 56 | 57 | - (void)play; 58 | 59 | - (void)pause; 60 | 61 | - (void)seekToTime:(NSTimeInterval)toTime; 62 | 63 | // Disposes the CCAudioPlayer and frees up all resources. 64 | // Make sure that call this method when you don't need current player. 65 | // If you want to play next audio file, you need call [xxx dispose] firstly, then create a new CCAudioPlayer. 66 | - (void)dispose; 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /CCAudioPlayer/CCAudioPlayer.m: -------------------------------------------------------------------------------- 1 | /* 2 | * CCAudioPlayer - Objective-C Audio player support remote and local files, Sitting on top of AVPlayer: 3 | * 4 | * https://github.com/yechunjun/CCAudioPlayer 5 | * 6 | * This code is distributed under the terms and conditions of the MIT license. 7 | * 8 | * Author: 9 | * Chun Ye 10 | * 11 | */ 12 | 13 | #import "CCAudioPlayer.h" 14 | 15 | @import AVFoundation; 16 | 17 | typedef NS_ENUM(NSInteger, CCAudioPlayerPauseReason) { 18 | CCAudioPlayerPauseReasonNotPause = 0, 19 | CCAudioPlayerPauseReasonUserPause, 20 | CCAudioPlayerPauseReasonForcePause 21 | }; 22 | 23 | @interface CCAudioPlayer () 24 | { 25 | @private 26 | AVQueuePlayer *_player; 27 | AVPlayerItem *_playerItem; 28 | 29 | UIBackgroundTaskIdentifier _backgroundTaskId; 30 | UIBackgroundTaskIdentifier _removedTaskId; 31 | 32 | id _timeObserver; 33 | 34 | CCAudioPlayerPauseReason _pauseReason; 35 | 36 | BOOL _routeChangedWhilePlaying; 37 | BOOL _interruptedWhilePlaying; 38 | 39 | BOOL _alreadyBeginPlay; 40 | 41 | BOOL _seeking; 42 | } 43 | 44 | @property (nonatomic, strong) NSURL *url; 45 | 46 | @property (nonatomic) NSTimeInterval duration; 47 | 48 | @property (nonatomic) NSTimeInterval progress; 49 | 50 | @property (nonatomic) CCAudioPlayerState playerState; 51 | 52 | @property (nonatomic) CCAudioPlayerErrorCode errorCode; 53 | 54 | @property (nonatomic, copy) void (^trackProgressBlock)(NSTimeInterval progress); 55 | 56 | @property (nonatomic, copy) void (^trackPlayerStateBlock)(CCAudioPlayerState playerState); 57 | 58 | @end 59 | 60 | @implementation CCAudioPlayer 61 | 62 | #pragma mark - Initialize & Life 63 | 64 | - (instancetype)initWithContentsOfURL:(NSURL *)url 65 | { 66 | self = [super init]; 67 | if (self) { 68 | NSParameterAssert(url); 69 | 70 | [self setupPlayBackAudioSession]; 71 | 72 | self.url = url; 73 | 74 | self.playerState = CCAudioPlayerStateNone; 75 | 76 | [self setupNotifications]; 77 | 78 | [self longTimeBufferAtBackground]; 79 | 80 | [self initializePlayer]; 81 | } 82 | return self; 83 | } 84 | 85 | + (instancetype)audioPlayerWithContentsOfURL:(NSURL *)url 86 | { 87 | return [[CCAudioPlayer alloc] initWithContentsOfURL:url]; 88 | } 89 | 90 | - (void)dealloc 91 | { 92 | CCLog(@""); 93 | } 94 | 95 | #pragma mark - Public 96 | 97 | - (void)play 98 | { 99 | if (_playerItem) { 100 | 101 | [_player play]; 102 | 103 | if (self.playerState == CCAudioPlayerStateRunning) { 104 | self.playerState = CCAudioPlayerStateBuffering; 105 | } else if (self.playerState == CCAudioPlayerStatePaused) { 106 | self.playerState = CCAudioPlayerStatePlaying; 107 | } 108 | 109 | _alreadyBeginPlay = YES; 110 | 111 | _pauseReason = CCAudioPlayerPauseReasonNotPause; 112 | } 113 | } 114 | 115 | - (void)pause 116 | { 117 | if (_playerItem) { 118 | 119 | [_player pause]; 120 | 121 | if (self.playerState == CCAudioPlayerStatePlaying) { 122 | self.playerState = CCAudioPlayerStatePaused; 123 | } 124 | 125 | _pauseReason = CCAudioPlayerPauseReasonUserPause; 126 | } 127 | } 128 | 129 | - (void)seekToTime:(NSTimeInterval)toTime 130 | { 131 | if (_playerItem) { 132 | if (toTime < 0.0) { 133 | toTime = 0.0; 134 | } 135 | 136 | self.progress = toTime; 137 | 138 | _seeking = YES; 139 | 140 | typeof(self) __weak weakSelf = self; 141 | [_playerItem seekToTime:CMTimeMakeWithSeconds(toTime, 1.0) completionHandler:^(BOOL finished) { 142 | CCAudioPlayer *strongSelf = weakSelf; 143 | strongSelf->_seeking = NO; 144 | }]; 145 | 146 | if (CMTimeGetSeconds(_playerItem.duration) < toTime) { 147 | [self play]; 148 | } 149 | } 150 | } 151 | 152 | - (void)dispose 153 | { 154 | [_player pause]; 155 | 156 | [self clearTimeObserver]; 157 | 158 | if (_playerItem) { 159 | [self cleanupPlayerItem:_playerItem]; 160 | _playerItem = nil; 161 | } 162 | 163 | if (_player) { 164 | [_player removeObserver:self forKeyPath:@"status"]; 165 | _player = nil; 166 | } 167 | 168 | [self longTimeBufferBackgroundCompleted]; 169 | 170 | self.playerState = CCAudioPlayerStateDisposed; 171 | 172 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 173 | 174 | if (self.trackProgressBlock) { 175 | self.trackProgressBlock = nil; 176 | } 177 | 178 | if (self.trackPlayerStateBlock) { 179 | self.trackPlayerStateBlock = nil; 180 | } 181 | } 182 | 183 | - (NSTimeInterval)duration 184 | { 185 | if (_playerItem) { 186 | NSArray *loadedRanges = _playerItem.seekableTimeRanges; 187 | if (loadedRanges.count > 0) { 188 | CMTimeRange range = [loadedRanges[0] CMTimeRangeValue]; 189 | return CMTimeGetSeconds((range.duration)); 190 | } else { 191 | return 0.0f; 192 | } 193 | } else { 194 | return 0.0f; 195 | } 196 | } 197 | 198 | - (BOOL)isPlaying 199 | { 200 | return (nil != _playerItem) ? (0.0 != _player.rate) : NO; 201 | } 202 | 203 | - (void)trackPlayerProgress:(void (^)(NSTimeInterval))progressHandler playerState:(void (^)(CCAudioPlayerState))playerStateHandler 204 | { 205 | if (progressHandler) { 206 | self.trackProgressBlock = progressHandler; 207 | } 208 | if (playerStateHandler) { 209 | self.trackPlayerStateBlock = playerStateHandler; 210 | } 211 | } 212 | 213 | #pragma mark - Private 214 | 215 | - (void)setPlayerState:(CCAudioPlayerState)playerState 216 | { 217 | _playerState = playerState; 218 | 219 | if (self.trackPlayerStateBlock) { 220 | self.trackPlayerStateBlock(_playerState); 221 | } 222 | } 223 | 224 | - (void)setProgress:(NSTimeInterval)progress 225 | { 226 | _progress = progress; 227 | if (self.trackProgressBlock) { 228 | self.trackProgressBlock(_progress); 229 | } 230 | } 231 | 232 | - (void)initializePlayer 233 | { 234 | _player = [[AVQueuePlayer alloc] init]; 235 | [_player addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:NULL]; 236 | 237 | [self addItemToPlayer]; 238 | } 239 | 240 | - (void)addItemToPlayer 241 | { 242 | _playerItem = [self createPlayerItemWithUrl:self.url]; 243 | if (_playerItem) { 244 | [_player insertItem:_playerItem afterItem:nil]; 245 | } 246 | } 247 | 248 | - (void)setupNotifications 249 | { 250 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruptionNotification:) name:AVAudioSessionInterruptionNotification object:nil]; 251 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleRouteChangeNotification:) name:AVAudioSessionRouteChangeNotification object:nil]; 252 | } 253 | 254 | - (void)longTimeBufferAtBackground 255 | { 256 | typeof(self) __weak weakSelf = self; 257 | _backgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 258 | CCAudioPlayer *strongSelf = weakSelf; 259 | if (strongSelf) { 260 | [[UIApplication sharedApplication] endBackgroundTask:strongSelf->_removedTaskId]; 261 | strongSelf->_backgroundTaskId = UIBackgroundTaskInvalid; 262 | } 263 | }]; 264 | 265 | if (_backgroundTaskId != UIBackgroundTaskInvalid && _removedTaskId == 0 ? YES : (_removedTaskId != UIBackgroundTaskInvalid)) { 266 | [[UIApplication sharedApplication] endBackgroundTask: _removedTaskId]; 267 | } 268 | _removedTaskId = _backgroundTaskId; 269 | } 270 | 271 | - (void)longTimeBufferBackgroundCompleted 272 | { 273 | if (_backgroundTaskId != UIBackgroundTaskInvalid && _removedTaskId != _backgroundTaskId) { 274 | [[UIApplication sharedApplication] endBackgroundTask: _backgroundTaskId]; 275 | _removedTaskId = _backgroundTaskId; 276 | } 277 | } 278 | 279 | - (void)setErrorCode:(CCAudioPlayerErrorCode)errorCode 280 | { 281 | _errorCode = errorCode; 282 | 283 | if (errorCode == CCAudioPlayerErrorNone) { 284 | CCLog(@"CCAudioPlayerErrorNone"); 285 | } else if (errorCode == CCAudioPlayerErrorPlayerInitializeFailed) { 286 | CCLog(@"CCAudioPlayerErrorPlayerInitializeFailed"); 287 | } else if (errorCode == CCAudioPlayerErrorBytesInitializeFailed) { 288 | CCLog(@"CCAudioPlayerErrorBytesInitializeFailed"); 289 | } else if (errorCode == CCAudioPlayerErrorUnknow) { 290 | CCLog(@"CCAudioPlayerErrorUnknow"); 291 | } 292 | } 293 | 294 | - (void)clearTimeObserver 295 | { 296 | if (_timeObserver) { 297 | [_player removeTimeObserver:_timeObserver]; 298 | _timeObserver = nil; 299 | } 300 | } 301 | 302 | - (void)removeCurrentItem 303 | { 304 | if (_playerItem) { 305 | [_player pause]; 306 | 307 | _pauseReason = CCAudioPlayerPauseReasonForcePause; 308 | 309 | [self cleanupPlayerItem:_playerItem]; 310 | 311 | _playerItem = nil; 312 | } 313 | } 314 | 315 | - (void)replayCurrentPlayItemToTime:(NSTimeInterval)seekTime 316 | { 317 | [self removeCurrentItem]; 318 | [self addItemToPlayer]; 319 | [self seekToTime:seekTime]; 320 | } 321 | 322 | #pragma mark - AVPlayerItem 323 | 324 | - (AVPlayerItem *)createPlayerItemWithUrl:(NSURL *)url 325 | { 326 | AVPlayerItem *item = [AVPlayerItem playerItemWithURL:url]; 327 | if (item) { 328 | [item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:NULL]; 329 | [item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; 330 | [item addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil]; 331 | 332 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemCompleted:) name:AVPlayerItemDidPlayToEndTimeNotification object:item]; 333 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemFailedToComplete:) name:AVPlayerItemFailedToPlayToEndTimeNotification object:item]; 334 | } 335 | return item; 336 | } 337 | 338 | - (void)cleanupPlayerItem:(AVPlayerItem *)item 339 | { 340 | [self clearTimeObserver]; 341 | 342 | [item removeObserver:self forKeyPath:@"status" context:NULL]; 343 | 344 | [item removeObserver:self forKeyPath:@"loadedTimeRanges" context:NULL]; 345 | 346 | [item removeObserver:self forKeyPath:@"playbackLikelyToKeepUp" context:NULL]; 347 | 348 | [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:item]; 349 | 350 | [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemFailedToPlayToEndTimeNotification object:item]; 351 | } 352 | 353 | - (void)playerItemCompleted:(NSNotification *)notification 354 | { 355 | self.playerState = CCAudioPlayerStateStopped; 356 | } 357 | 358 | - (void)playerItemFailedToComplete:(NSNotification *)notification 359 | { 360 | self.playerState = CCAudioPlayerStateError; 361 | self.errorCode = CCAudioPlayerErrorUnknow; 362 | } 363 | 364 | #pragma mark - Interruption & Route changed 365 | 366 | - (void)handleInterruptionNotification:(NSNotification *)notification 367 | { 368 | NSDictionary *interuptionDict = notification.userInfo; 369 | NSUInteger interuptionType = [[interuptionDict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue]; 370 | 371 | if (interuptionType == AVAudioSessionInterruptionTypeBegan && _pauseReason != CCAudioPlayerPauseReasonForcePause && _pauseReason != CCAudioPlayerPauseReasonUserPause) { 372 | _interruptedWhilePlaying = YES; 373 | [self pause]; 374 | _pauseReason = CCAudioPlayerPauseReasonForcePause; 375 | } else if (interuptionType == AVAudioSessionInterruptionTypeEnded && _interruptedWhilePlaying && _pauseReason != CCAudioPlayerPauseReasonUserPause) { 376 | [self setupPlayBackAudioSession]; 377 | _interruptedWhilePlaying = NO; 378 | [self play]; 379 | } 380 | } 381 | 382 | - (void)handleRouteChangeNotification:(NSNotification *)notification 383 | { 384 | NSDictionary *routeChangeDict = notification.userInfo; 385 | NSUInteger routeChangeType = [[routeChangeDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; 386 | 387 | if (routeChangeType == AVAudioSessionRouteChangeReasonOldDeviceUnavailable && _pauseReason != CCAudioPlayerPauseReasonForcePause && _pauseReason != CCAudioPlayerPauseReasonUserPause) { 388 | _routeChangedWhilePlaying = YES; 389 | [self pause]; 390 | _pauseReason = CCAudioPlayerPauseReasonForcePause; 391 | } else if (routeChangeType == AVAudioSessionRouteChangeReasonNewDeviceAvailable && _routeChangedWhilePlaying) { 392 | _routeChangedWhilePlaying = NO; 393 | [self play]; 394 | } 395 | 396 | } 397 | 398 | #pragma mark - KVO 399 | 400 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 401 | { 402 | if (object == _player && [keyPath isEqualToString:@"status"]) { 403 | NSNumber *changeKind = change[NSKeyValueChangeKindKey]; 404 | if ([@(NSKeyValueChangeSetting) isEqual:changeKind]) { 405 | if (_player.status == AVPlayerStatusReadyToPlay) { 406 | if (_playerItem) { 407 | self.playerState = CCAudioPlayerStateBuffering; 408 | } else { 409 | self.playerState = CCAudioPlayerStateRunning; 410 | } 411 | } else { 412 | self.playerState = CCAudioPlayerStateError; 413 | self.errorCode = CCAudioPlayerErrorPlayerInitializeFailed; 414 | } 415 | } 416 | } else if (object == _playerItem && [keyPath isEqualToString:@"status"]) { 417 | NSNumber *changeKind = [change objectForKey:NSKeyValueChangeKindKey]; 418 | if ([@(NSKeyValueChangeSetting) isEqual:changeKind]) { 419 | 420 | NSUInteger playerItemIndex = [_player.items indexOfObject:_playerItem]; 421 | AVPlayerItemStatus playerItemStatus = AVPlayerItemStatusUnknown; 422 | id updateStatus = [change objectForKey:NSKeyValueChangeNewKey]; 423 | 424 | if (updateStatus) { 425 | playerItemStatus = [updateStatus integerValue]; 426 | } else { 427 | if (playerItemIndex == NSNotFound) { 428 | playerItemStatus = AVPlayerItemStatusFailed; 429 | } else { 430 | playerItemStatus = _playerItem.status; 431 | } 432 | } 433 | 434 | if (playerItemStatus == AVPlayerItemStatusReadyToPlay) { 435 | [self clearTimeObserver]; 436 | typeof(self) __weak weakSelf = self; 437 | _timeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1.0, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { 438 | CCAudioPlayer *strongSelf = weakSelf; 439 | if (strongSelf) { 440 | if (!strongSelf->_seeking) { 441 | strongSelf.progress = CMTimeGetSeconds(time); 442 | } 443 | } 444 | }]; 445 | 446 | if (self.isPlaying) { 447 | self.playerState = CCAudioPlayerStatePlaying; 448 | } 449 | } else { 450 | self.playerState = CCAudioPlayerStateError; 451 | self.errorCode = CCAudioPlayerErrorBytesInitializeFailed; 452 | } 453 | } 454 | } else if (object == _playerItem && [keyPath isEqualToString:@"loadedTimeRanges"]) { 455 | NSArray *timeRanges = (NSArray *)[change objectForKey:NSKeyValueChangeNewKey]; 456 | if (timeRanges && timeRanges.count > 0) { 457 | CMTimeRange timeRange = [timeRanges[0] CMTimeRangeValue]; 458 | if (_player.rate == 0 && _pauseReason != CCAudioPlayerPauseReasonForcePause) { 459 | if (self.isPlaying) { 460 | self.playerState = CCAudioPlayerStateBuffering; 461 | } 462 | [self longTimeBufferAtBackground]; 463 | 464 | CMTime bufferedTime = CMTimeAdd(timeRange.start, timeRange.duration); 465 | CMTime milestone = CMTimeAdd(_player.currentTime, CMTimeMakeWithSeconds(2.0f, timeRange.duration.timescale)); 466 | if (CMTIME_COMPARE_INLINE(bufferedTime , >, milestone) && _player.currentItem.status == AVPlayerItemStatusReadyToPlay && !_interruptedWhilePlaying && !_routeChangedWhilePlaying) { 467 | if (_pauseReason == CCAudioPlayerPauseReasonForcePause) { 468 | if (!self.isPlaying && _alreadyBeginPlay) { 469 | [_player play]; 470 | self.playerState = CCAudioPlayerStatePlaying; 471 | [self longTimeBufferBackgroundCompleted]; 472 | } 473 | } 474 | } 475 | } 476 | } 477 | } else if (object == _playerItem && [keyPath isEqualToString:@"playbackLikelyToKeepUp"]) { 478 | if (!_playerItem.playbackLikelyToKeepUp) { 479 | if (_pauseReason == CCAudioPlayerPauseReasonForcePause) { 480 | NSTimeInterval currentTime = self.progress; 481 | [self replayCurrentPlayItemToTime:currentTime]; 482 | } 483 | } 484 | } 485 | } 486 | 487 | #pragma mark - AudioSession Setup 488 | 489 | - (void)setupPlayBackAudioSession 490 | { 491 | AVAudioSession *audioSession = [AVAudioSession sharedInstance]; 492 | [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; 493 | if (audioSession.category != AVAudioSessionCategoryPlayback) { 494 | UIDevice *device = [UIDevice currentDevice]; 495 | if ([device respondsToSelector:@selector(isMultitaskingSupported)]) { 496 | if (device.multitaskingSupported) { 497 | 498 | NSError *setCategoryError = nil; 499 | [audioSession setCategory:AVAudioSessionCategoryPlayback 500 | withOptions:AVAudioSessionCategoryOptionAllowBluetooth 501 | error:&setCategoryError]; 502 | 503 | NSError *activationError = nil; 504 | [audioSession setActive:YES error:&activationError]; 505 | } 506 | } 507 | } 508 | } 509 | 510 | @end 511 | -------------------------------------------------------------------------------- /Demo/CCAudioPlayer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 34D82425198A1C30002746E7 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34D82424198A1C30002746E7 /* Foundation.framework */; }; 11 | 34D82427198A1C30002746E7 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34D82426198A1C30002746E7 /* CoreGraphics.framework */; }; 12 | 34D82429198A1C30002746E7 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34D82428198A1C30002746E7 /* UIKit.framework */; }; 13 | 34D8242F198A1C30002746E7 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 34D8242D198A1C30002746E7 /* InfoPlist.strings */; }; 14 | 34D82431198A1C30002746E7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D82430198A1C30002746E7 /* main.m */; }; 15 | 34D82435198A1C30002746E7 /* CCAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D82434198A1C30002746E7 /* CCAppDelegate.m */; }; 16 | 34D82437198A1C30002746E7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 34D82436198A1C30002746E7 /* Images.xcassets */; }; 17 | 34D8243E198A1C30002746E7 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34D8243D198A1C30002746E7 /* XCTest.framework */; }; 18 | 34D8243F198A1C30002746E7 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34D82424198A1C30002746E7 /* Foundation.framework */; }; 19 | 34D82440198A1C30002746E7 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34D82428198A1C30002746E7 /* UIKit.framework */; }; 20 | 34D82448198A1C30002746E7 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 34D82446198A1C30002746E7 /* InfoPlist.strings */; }; 21 | 34D8244A198A1C30002746E7 /* CCAudioPlayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D82449198A1C30002746E7 /* CCAudioPlayerTests.m */; }; 22 | 34D82459198A1E44002746E7 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34D82458198A1E44002746E7 /* AVFoundation.framework */; }; 23 | 34D8245D198A40CE002746E7 /* DemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8245C198A40CE002746E7 /* DemoViewController.m */; }; 24 | 34D82460198A4191002746E7 /* Track.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8245F198A4191002746E7 /* Track.m */; }; 25 | 34D82463198A75ED002746E7 /* CCAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D82462198A75ED002746E7 /* CCAudioPlayer.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 34D82441198A1C30002746E7 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 34D82419198A1C30002746E7 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 34D82420198A1C30002746E7; 34 | remoteInfo = CCAudioPlayer; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 34D82421198A1C30002746E7 /* CCAudioPlayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CCAudioPlayer.app; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 34D82424198A1C30002746E7 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 41 | 34D82426198A1C30002746E7 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 42 | 34D82428198A1C30002746E7 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 43 | 34D8242C198A1C30002746E7 /* CCAudioPlayer-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CCAudioPlayer-Info.plist"; sourceTree = ""; }; 44 | 34D8242E198A1C30002746E7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 45 | 34D82430198A1C30002746E7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 46 | 34D82432198A1C30002746E7 /* CCAudioPlayer-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CCAudioPlayer-Prefix.pch"; sourceTree = ""; }; 47 | 34D82433198A1C30002746E7 /* CCAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CCAppDelegate.h; sourceTree = ""; }; 48 | 34D82434198A1C30002746E7 /* CCAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CCAppDelegate.m; sourceTree = ""; }; 49 | 34D82436198A1C30002746E7 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 50 | 34D8243C198A1C30002746E7 /* CCAudioPlayerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CCAudioPlayerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 34D8243D198A1C30002746E7 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 52 | 34D82445198A1C30002746E7 /* CCAudioPlayerTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CCAudioPlayerTests-Info.plist"; sourceTree = ""; }; 53 | 34D82447198A1C30002746E7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 54 | 34D82449198A1C30002746E7 /* CCAudioPlayerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CCAudioPlayerTests.m; sourceTree = ""; }; 55 | 34D82458198A1E44002746E7 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 56 | 34D8245B198A40CE002746E7 /* DemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoViewController.h; sourceTree = ""; }; 57 | 34D8245C198A40CE002746E7 /* DemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoViewController.m; sourceTree = ""; }; 58 | 34D8245E198A4191002746E7 /* Track.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Track.h; sourceTree = ""; }; 59 | 34D8245F198A4191002746E7 /* Track.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Track.m; sourceTree = ""; }; 60 | 34D82461198A75ED002746E7 /* CCAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CCAudioPlayer.h; path = ../../CCAudioPlayer/CCAudioPlayer.h; sourceTree = ""; }; 61 | 34D82462198A75ED002746E7 /* CCAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CCAudioPlayer.m; path = ../../CCAudioPlayer/CCAudioPlayer.m; sourceTree = ""; }; 62 | /* End PBXFileReference section */ 63 | 64 | /* Begin PBXFrameworksBuildPhase section */ 65 | 34D8241E198A1C30002746E7 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | 34D82459198A1E44002746E7 /* AVFoundation.framework in Frameworks */, 70 | 34D82427198A1C30002746E7 /* CoreGraphics.framework in Frameworks */, 71 | 34D82429198A1C30002746E7 /* UIKit.framework in Frameworks */, 72 | 34D82425198A1C30002746E7 /* Foundation.framework in Frameworks */, 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | 34D82439198A1C30002746E7 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | 34D8243E198A1C30002746E7 /* XCTest.framework in Frameworks */, 81 | 34D82440198A1C30002746E7 /* UIKit.framework in Frameworks */, 82 | 34D8243F198A1C30002746E7 /* Foundation.framework in Frameworks */, 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | /* End PBXFrameworksBuildPhase section */ 87 | 88 | /* Begin PBXGroup section */ 89 | 34D82418198A1C30002746E7 = { 90 | isa = PBXGroup; 91 | children = ( 92 | 34D8242A198A1C30002746E7 /* CCAudioPlayer */, 93 | 34D82443198A1C30002746E7 /* CCAudioPlayerTests */, 94 | 34D82423198A1C30002746E7 /* Frameworks */, 95 | 34D82422198A1C30002746E7 /* Products */, 96 | ); 97 | sourceTree = ""; 98 | }; 99 | 34D82422198A1C30002746E7 /* Products */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 34D82421198A1C30002746E7 /* CCAudioPlayer.app */, 103 | 34D8243C198A1C30002746E7 /* CCAudioPlayerTests.xctest */, 104 | ); 105 | name = Products; 106 | sourceTree = ""; 107 | }; 108 | 34D82423198A1C30002746E7 /* Frameworks */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 34D82458198A1E44002746E7 /* AVFoundation.framework */, 112 | 34D82424198A1C30002746E7 /* Foundation.framework */, 113 | 34D82426198A1C30002746E7 /* CoreGraphics.framework */, 114 | 34D82428198A1C30002746E7 /* UIKit.framework */, 115 | 34D8243D198A1C30002746E7 /* XCTest.framework */, 116 | ); 117 | name = Frameworks; 118 | sourceTree = ""; 119 | }; 120 | 34D8242A198A1C30002746E7 /* CCAudioPlayer */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 34D82457198A1CCE002746E7 /* AudioPlayer */, 124 | 34D82433198A1C30002746E7 /* CCAppDelegate.h */, 125 | 34D82434198A1C30002746E7 /* CCAppDelegate.m */, 126 | 34D8245B198A40CE002746E7 /* DemoViewController.h */, 127 | 34D8245C198A40CE002746E7 /* DemoViewController.m */, 128 | 34D8245E198A4191002746E7 /* Track.h */, 129 | 34D8245F198A4191002746E7 /* Track.m */, 130 | 34D82436198A1C30002746E7 /* Images.xcassets */, 131 | 34D8242B198A1C30002746E7 /* Supporting Files */, 132 | ); 133 | path = CCAudioPlayer; 134 | sourceTree = ""; 135 | }; 136 | 34D8242B198A1C30002746E7 /* Supporting Files */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 34D8242C198A1C30002746E7 /* CCAudioPlayer-Info.plist */, 140 | 34D8242D198A1C30002746E7 /* InfoPlist.strings */, 141 | 34D82430198A1C30002746E7 /* main.m */, 142 | 34D82432198A1C30002746E7 /* CCAudioPlayer-Prefix.pch */, 143 | ); 144 | name = "Supporting Files"; 145 | sourceTree = ""; 146 | }; 147 | 34D82443198A1C30002746E7 /* CCAudioPlayerTests */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 34D82449198A1C30002746E7 /* CCAudioPlayerTests.m */, 151 | 34D82444198A1C30002746E7 /* Supporting Files */, 152 | ); 153 | path = CCAudioPlayerTests; 154 | sourceTree = ""; 155 | }; 156 | 34D82444198A1C30002746E7 /* Supporting Files */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 34D82445198A1C30002746E7 /* CCAudioPlayerTests-Info.plist */, 160 | 34D82446198A1C30002746E7 /* InfoPlist.strings */, 161 | ); 162 | name = "Supporting Files"; 163 | sourceTree = ""; 164 | }; 165 | 34D82457198A1CCE002746E7 /* AudioPlayer */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 34D82461198A75ED002746E7 /* CCAudioPlayer.h */, 169 | 34D82462198A75ED002746E7 /* CCAudioPlayer.m */, 170 | ); 171 | name = AudioPlayer; 172 | sourceTree = ""; 173 | }; 174 | /* End PBXGroup section */ 175 | 176 | /* Begin PBXNativeTarget section */ 177 | 34D82420198A1C30002746E7 /* CCAudioPlayer */ = { 178 | isa = PBXNativeTarget; 179 | buildConfigurationList = 34D8244D198A1C30002746E7 /* Build configuration list for PBXNativeTarget "CCAudioPlayer" */; 180 | buildPhases = ( 181 | 34D8241D198A1C30002746E7 /* Sources */, 182 | 34D8241E198A1C30002746E7 /* Frameworks */, 183 | 34D8241F198A1C30002746E7 /* Resources */, 184 | ); 185 | buildRules = ( 186 | ); 187 | dependencies = ( 188 | ); 189 | name = CCAudioPlayer; 190 | productName = CCAudioPlayer; 191 | productReference = 34D82421198A1C30002746E7 /* CCAudioPlayer.app */; 192 | productType = "com.apple.product-type.application"; 193 | }; 194 | 34D8243B198A1C30002746E7 /* CCAudioPlayerTests */ = { 195 | isa = PBXNativeTarget; 196 | buildConfigurationList = 34D82450198A1C30002746E7 /* Build configuration list for PBXNativeTarget "CCAudioPlayerTests" */; 197 | buildPhases = ( 198 | 34D82438198A1C30002746E7 /* Sources */, 199 | 34D82439198A1C30002746E7 /* Frameworks */, 200 | 34D8243A198A1C30002746E7 /* Resources */, 201 | ); 202 | buildRules = ( 203 | ); 204 | dependencies = ( 205 | 34D82442198A1C30002746E7 /* PBXTargetDependency */, 206 | ); 207 | name = CCAudioPlayerTests; 208 | productName = CCAudioPlayerTests; 209 | productReference = 34D8243C198A1C30002746E7 /* CCAudioPlayerTests.xctest */; 210 | productType = "com.apple.product-type.bundle.unit-test"; 211 | }; 212 | /* End PBXNativeTarget section */ 213 | 214 | /* Begin PBXProject section */ 215 | 34D82419198A1C30002746E7 /* Project object */ = { 216 | isa = PBXProject; 217 | attributes = { 218 | CLASSPREFIX = CC; 219 | LastUpgradeCheck = 0510; 220 | ORGANIZATIONNAME = Chun; 221 | TargetAttributes = { 222 | 34D82420198A1C30002746E7 = { 223 | SystemCapabilities = { 224 | com.apple.BackgroundModes = { 225 | enabled = 1; 226 | }; 227 | }; 228 | }; 229 | 34D8243B198A1C30002746E7 = { 230 | TestTargetID = 34D82420198A1C30002746E7; 231 | }; 232 | }; 233 | }; 234 | buildConfigurationList = 34D8241C198A1C30002746E7 /* Build configuration list for PBXProject "CCAudioPlayer" */; 235 | compatibilityVersion = "Xcode 3.2"; 236 | developmentRegion = English; 237 | hasScannedForEncodings = 0; 238 | knownRegions = ( 239 | en, 240 | ); 241 | mainGroup = 34D82418198A1C30002746E7; 242 | productRefGroup = 34D82422198A1C30002746E7 /* Products */; 243 | projectDirPath = ""; 244 | projectRoot = ""; 245 | targets = ( 246 | 34D82420198A1C30002746E7 /* CCAudioPlayer */, 247 | 34D8243B198A1C30002746E7 /* CCAudioPlayerTests */, 248 | ); 249 | }; 250 | /* End PBXProject section */ 251 | 252 | /* Begin PBXResourcesBuildPhase section */ 253 | 34D8241F198A1C30002746E7 /* Resources */ = { 254 | isa = PBXResourcesBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | 34D8242F198A1C30002746E7 /* InfoPlist.strings in Resources */, 258 | 34D82437198A1C30002746E7 /* Images.xcassets in Resources */, 259 | ); 260 | runOnlyForDeploymentPostprocessing = 0; 261 | }; 262 | 34D8243A198A1C30002746E7 /* Resources */ = { 263 | isa = PBXResourcesBuildPhase; 264 | buildActionMask = 2147483647; 265 | files = ( 266 | 34D82448198A1C30002746E7 /* InfoPlist.strings in Resources */, 267 | ); 268 | runOnlyForDeploymentPostprocessing = 0; 269 | }; 270 | /* End PBXResourcesBuildPhase section */ 271 | 272 | /* Begin PBXSourcesBuildPhase section */ 273 | 34D8241D198A1C30002746E7 /* Sources */ = { 274 | isa = PBXSourcesBuildPhase; 275 | buildActionMask = 2147483647; 276 | files = ( 277 | 34D82435198A1C30002746E7 /* CCAppDelegate.m in Sources */, 278 | 34D82431198A1C30002746E7 /* main.m in Sources */, 279 | 34D82463198A75ED002746E7 /* CCAudioPlayer.m in Sources */, 280 | 34D82460198A4191002746E7 /* Track.m in Sources */, 281 | 34D8245D198A40CE002746E7 /* DemoViewController.m in Sources */, 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | }; 285 | 34D82438198A1C30002746E7 /* Sources */ = { 286 | isa = PBXSourcesBuildPhase; 287 | buildActionMask = 2147483647; 288 | files = ( 289 | 34D8244A198A1C30002746E7 /* CCAudioPlayerTests.m in Sources */, 290 | ); 291 | runOnlyForDeploymentPostprocessing = 0; 292 | }; 293 | /* End PBXSourcesBuildPhase section */ 294 | 295 | /* Begin PBXTargetDependency section */ 296 | 34D82442198A1C30002746E7 /* PBXTargetDependency */ = { 297 | isa = PBXTargetDependency; 298 | target = 34D82420198A1C30002746E7 /* CCAudioPlayer */; 299 | targetProxy = 34D82441198A1C30002746E7 /* PBXContainerItemProxy */; 300 | }; 301 | /* End PBXTargetDependency section */ 302 | 303 | /* Begin PBXVariantGroup section */ 304 | 34D8242D198A1C30002746E7 /* InfoPlist.strings */ = { 305 | isa = PBXVariantGroup; 306 | children = ( 307 | 34D8242E198A1C30002746E7 /* en */, 308 | ); 309 | name = InfoPlist.strings; 310 | sourceTree = ""; 311 | }; 312 | 34D82446198A1C30002746E7 /* InfoPlist.strings */ = { 313 | isa = PBXVariantGroup; 314 | children = ( 315 | 34D82447198A1C30002746E7 /* en */, 316 | ); 317 | name = InfoPlist.strings; 318 | sourceTree = ""; 319 | }; 320 | /* End PBXVariantGroup section */ 321 | 322 | /* Begin XCBuildConfiguration section */ 323 | 34D8244B198A1C30002746E7 /* Debug */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 328 | CLANG_CXX_LIBRARY = "libc++"; 329 | CLANG_ENABLE_MODULES = YES; 330 | CLANG_ENABLE_OBJC_ARC = YES; 331 | CLANG_WARN_BOOL_CONVERSION = YES; 332 | CLANG_WARN_CONSTANT_CONVERSION = YES; 333 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | COPY_PHASE_STRIP = NO; 341 | GCC_C_LANGUAGE_STANDARD = gnu99; 342 | GCC_DYNAMIC_NO_PIC = NO; 343 | GCC_OPTIMIZATION_LEVEL = 0; 344 | GCC_PREPROCESSOR_DEFINITIONS = ( 345 | "DEBUG=1", 346 | "$(inherited)", 347 | ); 348 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 349 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 350 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 351 | GCC_WARN_UNDECLARED_SELECTOR = YES; 352 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 353 | GCC_WARN_UNUSED_FUNCTION = YES; 354 | GCC_WARN_UNUSED_VARIABLE = YES; 355 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 356 | ONLY_ACTIVE_ARCH = YES; 357 | SDKROOT = iphoneos; 358 | }; 359 | name = Debug; 360 | }; 361 | 34D8244C198A1C30002746E7 /* Release */ = { 362 | isa = XCBuildConfiguration; 363 | buildSettings = { 364 | ALWAYS_SEARCH_USER_PATHS = NO; 365 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 366 | CLANG_CXX_LIBRARY = "libc++"; 367 | CLANG_ENABLE_MODULES = YES; 368 | CLANG_ENABLE_OBJC_ARC = YES; 369 | CLANG_WARN_BOOL_CONVERSION = YES; 370 | CLANG_WARN_CONSTANT_CONVERSION = YES; 371 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 372 | CLANG_WARN_EMPTY_BODY = YES; 373 | CLANG_WARN_ENUM_CONVERSION = YES; 374 | CLANG_WARN_INT_CONVERSION = YES; 375 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 376 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 377 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 378 | COPY_PHASE_STRIP = YES; 379 | ENABLE_NS_ASSERTIONS = NO; 380 | GCC_C_LANGUAGE_STANDARD = gnu99; 381 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 382 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 383 | GCC_WARN_UNDECLARED_SELECTOR = YES; 384 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 385 | GCC_WARN_UNUSED_FUNCTION = YES; 386 | GCC_WARN_UNUSED_VARIABLE = YES; 387 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 388 | SDKROOT = iphoneos; 389 | VALIDATE_PRODUCT = YES; 390 | }; 391 | name = Release; 392 | }; 393 | 34D8244E198A1C30002746E7 /* Debug */ = { 394 | isa = XCBuildConfiguration; 395 | buildSettings = { 396 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 397 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 398 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 399 | GCC_PREFIX_HEADER = "CCAudioPlayer/CCAudioPlayer-Prefix.pch"; 400 | INFOPLIST_FILE = "CCAudioPlayer/CCAudioPlayer-Info.plist"; 401 | PRODUCT_NAME = "$(TARGET_NAME)"; 402 | WRAPPER_EXTENSION = app; 403 | }; 404 | name = Debug; 405 | }; 406 | 34D8244F198A1C30002746E7 /* Release */ = { 407 | isa = XCBuildConfiguration; 408 | buildSettings = { 409 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 410 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 411 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 412 | GCC_PREFIX_HEADER = "CCAudioPlayer/CCAudioPlayer-Prefix.pch"; 413 | INFOPLIST_FILE = "CCAudioPlayer/CCAudioPlayer-Info.plist"; 414 | PRODUCT_NAME = "$(TARGET_NAME)"; 415 | WRAPPER_EXTENSION = app; 416 | }; 417 | name = Release; 418 | }; 419 | 34D82451198A1C30002746E7 /* Debug */ = { 420 | isa = XCBuildConfiguration; 421 | buildSettings = { 422 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/CCAudioPlayer.app/CCAudioPlayer"; 423 | FRAMEWORK_SEARCH_PATHS = ( 424 | "$(SDKROOT)/Developer/Library/Frameworks", 425 | "$(inherited)", 426 | "$(DEVELOPER_FRAMEWORKS_DIR)", 427 | ); 428 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 429 | GCC_PREFIX_HEADER = "CCAudioPlayer/CCAudioPlayer-Prefix.pch"; 430 | GCC_PREPROCESSOR_DEFINITIONS = ( 431 | "DEBUG=1", 432 | "$(inherited)", 433 | ); 434 | INFOPLIST_FILE = "CCAudioPlayerTests/CCAudioPlayerTests-Info.plist"; 435 | PRODUCT_NAME = "$(TARGET_NAME)"; 436 | TEST_HOST = "$(BUNDLE_LOADER)"; 437 | WRAPPER_EXTENSION = xctest; 438 | }; 439 | name = Debug; 440 | }; 441 | 34D82452198A1C30002746E7 /* Release */ = { 442 | isa = XCBuildConfiguration; 443 | buildSettings = { 444 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/CCAudioPlayer.app/CCAudioPlayer"; 445 | FRAMEWORK_SEARCH_PATHS = ( 446 | "$(SDKROOT)/Developer/Library/Frameworks", 447 | "$(inherited)", 448 | "$(DEVELOPER_FRAMEWORKS_DIR)", 449 | ); 450 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 451 | GCC_PREFIX_HEADER = "CCAudioPlayer/CCAudioPlayer-Prefix.pch"; 452 | INFOPLIST_FILE = "CCAudioPlayerTests/CCAudioPlayerTests-Info.plist"; 453 | PRODUCT_NAME = "$(TARGET_NAME)"; 454 | TEST_HOST = "$(BUNDLE_LOADER)"; 455 | WRAPPER_EXTENSION = xctest; 456 | }; 457 | name = Release; 458 | }; 459 | /* End XCBuildConfiguration section */ 460 | 461 | /* Begin XCConfigurationList section */ 462 | 34D8241C198A1C30002746E7 /* Build configuration list for PBXProject "CCAudioPlayer" */ = { 463 | isa = XCConfigurationList; 464 | buildConfigurations = ( 465 | 34D8244B198A1C30002746E7 /* Debug */, 466 | 34D8244C198A1C30002746E7 /* Release */, 467 | ); 468 | defaultConfigurationIsVisible = 0; 469 | defaultConfigurationName = Release; 470 | }; 471 | 34D8244D198A1C30002746E7 /* Build configuration list for PBXNativeTarget "CCAudioPlayer" */ = { 472 | isa = XCConfigurationList; 473 | buildConfigurations = ( 474 | 34D8244E198A1C30002746E7 /* Debug */, 475 | 34D8244F198A1C30002746E7 /* Release */, 476 | ); 477 | defaultConfigurationIsVisible = 0; 478 | defaultConfigurationName = Release; 479 | }; 480 | 34D82450198A1C30002746E7 /* Build configuration list for PBXNativeTarget "CCAudioPlayerTests" */ = { 481 | isa = XCConfigurationList; 482 | buildConfigurations = ( 483 | 34D82451198A1C30002746E7 /* Debug */, 484 | 34D82452198A1C30002746E7 /* Release */, 485 | ); 486 | defaultConfigurationIsVisible = 0; 487 | defaultConfigurationName = Release; 488 | }; 489 | /* End XCConfigurationList section */ 490 | }; 491 | rootObject = 34D82419198A1C30002746E7 /* Project object */; 492 | } 493 | -------------------------------------------------------------------------------- /Demo/CCAudioPlayer/CCAppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CCAudioPlayer - Objective-C Audio player support remote and local files, Sitting on top of AVPlayer: 3 | * 4 | * https://github.com/yechunjun/CCAudioPlayer 5 | * 6 | * This code is distributed under the terms and conditions of the MIT license. 7 | * 8 | * Author: 9 | * Chun Ye 10 | * 11 | */ 12 | 13 | #import 14 | 15 | @interface CCAppDelegate : UIResponder 16 | 17 | @property (strong, nonatomic) UIWindow *window; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Demo/CCAudioPlayer/CCAppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | * CCAudioPlayer - Objective-C Audio player support remote and local files, Sitting on top of AVPlayer: 3 | * 4 | * https://github.com/yechunjun/CCAudioPlayer 5 | * 6 | * This code is distributed under the terms and conditions of the MIT license. 7 | * 8 | * Author: 9 | * Chun Ye 10 | * 11 | */ 12 | 13 | #import "CCAppDelegate.h" 14 | #import "DemoViewController.h" 15 | 16 | @implementation CCAppDelegate 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 19 | { 20 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 21 | self.window.backgroundColor = [UIColor whiteColor]; 22 | [self.window makeKeyAndVisible]; 23 | 24 | DemoViewController *demoViewController = [[DemoViewController alloc] init]; 25 | self.window.rootViewController = demoViewController; 26 | 27 | return YES; 28 | } 29 | 30 | - (void)applicationWillResignActive:(UIApplication *)application 31 | { 32 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 33 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 34 | } 35 | 36 | - (void)applicationDidEnterBackground:(UIApplication *)application 37 | { 38 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 39 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 40 | } 41 | 42 | - (void)applicationWillEnterForeground:(UIApplication *)application 43 | { 44 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 45 | } 46 | 47 | - (void)applicationDidBecomeActive:(UIApplication *)application 48 | { 49 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 50 | } 51 | 52 | - (void)applicationWillTerminate:(UIApplication *)application 53 | { 54 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Demo/CCAudioPlayer/CCAudioPlayer-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | Chun.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0.0 25 | LSRequiresIPhoneOS 26 | 27 | UIBackgroundModes 28 | 29 | audio 30 | 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Demo/CCAudioPlayer/CCAudioPlayer-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Demo/CCAudioPlayer/DemoViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CCAudioPlayer - Objective-C Audio player support remote and local files, Sitting on top of AVPlayer: 3 | * 4 | * https://github.com/yechunjun/CCAudioPlayer 5 | * 6 | * This code is distributed under the terms and conditions of the MIT license. 7 | * 8 | * Author: 9 | * Chun Ye 10 | * 11 | */ 12 | 13 | #import 14 | 15 | @interface DemoViewController : UIViewController 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Demo/CCAudioPlayer/DemoViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | * CCAudioPlayer - Objective-C Audio player support remote and local files, Sitting on top of AVPlayer: 3 | * 4 | * https://github.com/yechunjun/CCAudioPlayer 5 | * 6 | * This code is distributed under the terms and conditions of the MIT license. 7 | * 8 | * Author: 9 | * Chun Ye 10 | * 11 | */ 12 | 13 | #import "DemoViewController.h" 14 | #import "CCAudioPlayer.h" 15 | #import "Track.h" 16 | 17 | #define kUseBlockAPIToTrackPlayerStatus 1 18 | 19 | @interface DemoViewController () 20 | { 21 | @private 22 | UILabel *_titleLabel; 23 | UILabel *_statusLabel; 24 | 25 | UIButton *_buttonPlayPause; 26 | UIButton *_buttonNext; 27 | 28 | UISlider *_progressSlider; 29 | 30 | CCAudioPlayer *_audioPlayer; 31 | 32 | NSArray *_tracks; 33 | NSUInteger _currentTrackIndex; 34 | } 35 | 36 | @end 37 | 38 | @implementation DemoViewController 39 | 40 | - (void)viewDidLoad 41 | { 42 | [super viewDidLoad]; 43 | 44 | _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 64.0, CGRectGetWidth([self.view bounds]), 30.0)]; 45 | [_titleLabel setFont:[UIFont systemFontOfSize:20.0]]; 46 | [_titleLabel setTextColor:[UIColor blackColor]]; 47 | [_titleLabel setTextAlignment:NSTextAlignmentCenter]; 48 | [_titleLabel setLineBreakMode:NSLineBreakByTruncatingTail]; 49 | [self.view addSubview:_titleLabel]; 50 | 51 | _statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, CGRectGetMaxY([_titleLabel frame]) + 10.0, CGRectGetWidth([self.view bounds]), 30.0)]; 52 | [_statusLabel setFont:[UIFont systemFontOfSize:16.0]]; 53 | [_statusLabel setTextColor:[UIColor colorWithWhite:0.4 alpha:1.0]]; 54 | [_statusLabel setTextAlignment:NSTextAlignmentCenter]; 55 | [_statusLabel setLineBreakMode:NSLineBreakByTruncatingTail]; 56 | [self.view addSubview:_statusLabel]; 57 | 58 | _buttonPlayPause = [UIButton buttonWithType:UIButtonTypeSystem]; 59 | [_buttonPlayPause setFrame:CGRectMake(80.0, CGRectGetMaxY([_statusLabel frame]) + 20.0, 60.0, 20.0)]; 60 | [_buttonPlayPause setTitle:@"Play" forState:UIControlStateNormal]; 61 | [_buttonPlayPause addTarget:self action:@selector(_actionPlayPause:) forControlEvents:UIControlEventTouchDown]; 62 | [self.view addSubview:_buttonPlayPause]; 63 | 64 | _buttonNext = [UIButton buttonWithType:UIButtonTypeSystem]; 65 | [_buttonNext setFrame:CGRectMake(CGRectGetWidth([self.view bounds]) - 80.0 - 60.0, CGRectGetMinY([_buttonPlayPause frame]), 60.0, 20.0)]; 66 | [_buttonNext setTitle:@"Next" forState:UIControlStateNormal]; 67 | [_buttonNext addTarget:self action:@selector(_actionNext:) forControlEvents:UIControlEventTouchDown]; 68 | [self.view addSubview:_buttonNext]; 69 | 70 | _progressSlider = [[UISlider alloc] initWithFrame:CGRectMake(20.0, CGRectGetMaxY([_buttonNext frame]) + 20.0, CGRectGetWidth([self.view bounds]) - 20.0 * 2.0, 40.0)]; 71 | _progressSlider.continuous = NO; 72 | [_progressSlider addTarget:self action:@selector(_actionSliderProgress:) forControlEvents:UIControlEventValueChanged]; 73 | [self.view addSubview:_progressSlider]; 74 | 75 | _tracks = [Track remoteTracks]; 76 | 77 | [self _resetStreamer]; 78 | } 79 | 80 | #pragma mark - Private 81 | 82 | - (void)updateProgressView 83 | { 84 | [_progressSlider setValue:_audioPlayer.progress / _audioPlayer.duration animated:YES]; 85 | } 86 | 87 | - (void)updateStatusViews 88 | { 89 | switch (_audioPlayer.playerState) { 90 | case CCAudioPlayerStatePlaying: 91 | { 92 | _statusLabel.text = @"Playing"; 93 | [_buttonPlayPause setTitle:@"Pause" forState:UIControlStateNormal]; 94 | } 95 | break; 96 | case CCAudioPlayerStateBuffering: 97 | { 98 | _statusLabel.text = @"Buffering"; 99 | } 100 | break; 101 | 102 | case CCAudioPlayerStatePaused: 103 | { 104 | _statusLabel.text = @"Paused"; 105 | [_buttonPlayPause setTitle:@"Play" forState:UIControlStateNormal]; 106 | } 107 | break; 108 | 109 | case CCAudioPlayerStateStopped: 110 | { 111 | _statusLabel.text = @"Play to End"; 112 | 113 | [self _actionNext:nil]; 114 | } 115 | break; 116 | default: 117 | break; 118 | } 119 | } 120 | 121 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 122 | { 123 | if ([keyPath isEqualToString:@"progress"]) { 124 | [self updateProgressView]; 125 | } else { 126 | [self updateStatusViews]; 127 | } 128 | } 129 | 130 | - (void)_actionSliderProgress:(id)sender 131 | { 132 | [_audioPlayer seekToTime:_audioPlayer.duration * _progressSlider.value]; 133 | } 134 | 135 | - (void)_actionPlayPause:(id)sender 136 | { 137 | if (_audioPlayer.isPlaying) { 138 | [_audioPlayer pause]; 139 | [_buttonPlayPause setTitle:@"Play" forState:UIControlStateNormal]; 140 | } else { 141 | [_audioPlayer play]; 142 | [_buttonPlayPause setTitle:@"Pause" forState:UIControlStateNormal]; 143 | } 144 | } 145 | 146 | - (void)_actionNext:(id)sender 147 | { 148 | if (++_currentTrackIndex >= [_tracks count]) { 149 | _currentTrackIndex = 0; 150 | } 151 | 152 | [self _resetStreamer]; 153 | } 154 | 155 | - (void)_resetStreamer 156 | { 157 | if (_audioPlayer) { 158 | [_audioPlayer dispose]; 159 | if (!kUseBlockAPIToTrackPlayerStatus) { 160 | [_audioPlayer removeObserver:self forKeyPath:@"progress"]; 161 | [_audioPlayer removeObserver:self forKeyPath:@"playerState"]; 162 | } 163 | _audioPlayer = nil; 164 | } 165 | 166 | [_progressSlider setValue:0.0 animated:NO]; 167 | 168 | if (_tracks.count != 0) { 169 | Track *track = [_tracks objectAtIndex:_currentTrackIndex]; 170 | NSString *title = [NSString stringWithFormat:@"%@ - %@", track.artist, track.title]; 171 | [_titleLabel setText:title]; 172 | 173 | _audioPlayer = [CCAudioPlayer audioPlayerWithContentsOfURL:track.audioFileURL]; 174 | if (kUseBlockAPIToTrackPlayerStatus) { 175 | typeof(self) __weak weakSelf = self; 176 | [_audioPlayer trackPlayerProgress:^(NSTimeInterval progress) { 177 | DemoViewController *strongSelf = weakSelf; 178 | [strongSelf updateProgressView]; 179 | } playerState:^(CCAudioPlayerState playerState) { 180 | DemoViewController *strongSelf = weakSelf; 181 | [strongSelf updateStatusViews]; 182 | }]; 183 | } else { 184 | [_audioPlayer addObserver:self forKeyPath:@"progress" options:NSKeyValueObservingOptionNew context:NULL]; 185 | [_audioPlayer addObserver:self forKeyPath:@"playerState" options:NSKeyValueObservingOptionNew context:NULL]; 186 | } 187 | [_audioPlayer play]; 188 | } else { 189 | NSLog(@"No tracks available"); 190 | } 191 | } 192 | 193 | @end 194 | -------------------------------------------------------------------------------- /Demo/CCAudioPlayer/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Demo/CCAudioPlayer/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Demo/CCAudioPlayer/Track.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CCAudioPlayer - Objective-C Audio player support remote and local files, Sitting on top of AVPlayer: 3 | * 4 | * https://github.com/yechunjun/CCAudioPlayer 5 | * 6 | * This code is distributed under the terms and conditions of the MIT license. 7 | * 8 | * Author: 9 | * Chun Ye 10 | * 11 | */ 12 | 13 | #import 14 | 15 | @interface Track : NSObject 16 | 17 | @property (nonatomic, strong) NSString *artist; 18 | @property (nonatomic, strong) NSString *title; 19 | @property (nonatomic, strong) NSURL *audioFileURL; 20 | 21 | @end 22 | 23 | @interface Track (Provider) 24 | 25 | + (NSArray *)remoteTracks; 26 | 27 | @end -------------------------------------------------------------------------------- /Demo/CCAudioPlayer/Track.m: -------------------------------------------------------------------------------- 1 | /* 2 | * CCAudioPlayer - Objective-C Audio player support remote and local files, Sitting on top of AVPlayer: 3 | * 4 | * https://github.com/yechunjun/CCAudioPlayer 5 | * 6 | * This code is distributed under the terms and conditions of the MIT license. 7 | * 8 | * Author: 9 | * Chun Ye 10 | * 11 | */ 12 | 13 | #import "Track.h" 14 | 15 | @implementation Track 16 | 17 | @end 18 | 19 | @implementation Track (Provider) 20 | 21 | + (void)load 22 | { 23 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 24 | [self remoteTracks]; 25 | }); 26 | } 27 | 28 | + (NSArray *)remoteTracks 29 | { 30 | static NSArray *tracks = nil; 31 | 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^{ 34 | NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://douban.fm/j/mine/playlist?type=n&channel=1004693&from=mainsite"]]; 35 | NSData *data = [NSURLConnection sendSynchronousRequest:request 36 | returningResponse:NULL 37 | error:NULL]; 38 | NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 39 | NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:[string dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL]; 40 | 41 | NSMutableArray *allTracks = [NSMutableArray array]; 42 | for (NSDictionary *song in [dict objectForKey:@"song"]) { 43 | Track *track = [[Track alloc] init]; 44 | [track setArtist:[song objectForKey:@"artist"]]; 45 | [track setTitle:[song objectForKey:@"title"]]; 46 | [track setAudioFileURL:[NSURL URLWithString:[song objectForKey:@"url"]]]; 47 | [allTracks addObject:track]; 48 | } 49 | 50 | tracks = [allTracks copy]; 51 | NSLog(@"%@", tracks); 52 | }); 53 | return tracks; 54 | } 55 | 56 | @end -------------------------------------------------------------------------------- /Demo/CCAudioPlayer/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Demo/CCAudioPlayer/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CCAudioPlayer 4 | // 5 | // Created by Chun Ye on 7/31/14. 6 | // Copyright (c) 2014 Chun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "CCAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([CCAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Demo/CCAudioPlayerTests/CCAudioPlayerTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | Chun.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Demo/CCAudioPlayerTests/CCAudioPlayerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // CCAudioPlayerTests.m 3 | // CCAudioPlayerTests 4 | // 5 | // Created by Chun Ye on 7/31/14. 6 | // Copyright (c) 2014 Chun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CCAudioPlayerTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation CCAudioPlayerTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Demo/CCAudioPlayerTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Chun Ye 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CCAudioPlayer 2 | ============= 3 | 4 | * CCAudioPlayer support remote and local audio files, Sitting on top of AVPlayer. 5 | * And It provides useful basic player functionalities. 6 | 7 | ### Features 8 | 9 | * Supporting both local and remote media. 10 | * Simple APIs. 11 | * Handle Interruption and Route change. 12 | * If player suspended bacause of buffering issue, auto-resume the playback when buffered size reached 2 secs. 13 | 14 | ### Manually install 15 | 16 | * Download [CCAudioPlayer](https://github.com/yechunjun/CCAudioPlayer.git), drag _CCAudioPlayer.h_ and _CCAudioPlayer.m_ into your Xcode project and you are ready to go. 17 | 18 | * XCode providing GUI checkbox to enable various background modes. Enable **Audio and AirPlay**, you can find this section from _Project -> Capabilities -> Background Modes_. 19 | 20 | ### How to Use 21 | 22 | * A working demonstration is included inside example folder. You can get a simple music app.(Example uses [Douban.fm](http://www.douban.com) music apis to test.) 23 | 24 | #### Setup 25 | NSURL *audioURL = [NSURL URLWithString:urlString]; // Support Local or remote 26 | CCAudioPlayer *audioPlayer = [CCAudioPlayer audioPlayerWithContentsOfURL:audioURL]; 27 | 28 | #### Player Status 29 | 30 | CCAudioPlayerStateRunning // Player and audio file get ready for playing 31 | CCAudioPlayerStateBuffering // Buffering the audio content 32 | CCAudioPlayerStatePlaying // Playing 33 | CCAudioPlayerStatePaused // Paused 34 | CCAudioPlayerStateStopped // Raised when an audio file has finished playing 35 | CCAudioPlayerStateError // Raised when an unexpected and possibly unrecoverable error has occured 36 | CCAudioPlayerStateDisposed // Audio player is disposed 37 | 38 | // Use KVO 39 | [audioPlayer addObserver:self forKeyPath:@"progress" options:NSKeyValueObservingOptionNew context:NULL]; 40 | [audioPlayer addObserver:self forKeyPath:@"playerState" options:NSKeyValueObservingOptionNew context:NULL]; 41 | 42 | // Or Use Block API 43 | [audioPlayer trackPlayerProgress:^(NSTimeInterval progress) { 44 | NSLog(@"Player progress update."); 45 | } playerState:^(CCAudioPlayerState playerState) { 46 | NSLog(@"Player state update."); 47 | }]; 48 | 49 | #### Player Control 50 | [audioPlayer play]; 51 | [audioPlayer pause]; 52 | [audioPlayer seekToTime:1.0f]; 53 | 54 | ### License 55 | * All source code is licensed under the MIT License. 56 | --------------------------------------------------------------------------------