ㄷㅣㅆㅣ's Amusement
[iOS/Objective-C] Audio Player tutorial using AVPlayer (AVFoundation) -- 1 본문
[iOS/Objective-C] Audio Player tutorial using AVPlayer (AVFoundation) -- 1
ㄷㅣㅆㅣ 2016. 2. 24. 12:10[iOS/Objective-C] Audio Player tutorial using AVPlayer (AVFoundation) -- 1
이전에 비디오 플레이어 튜토리얼을 포스팅 했으니(2016/02/23 - [프로그래밍/iOS] - [iOS/Objective-C] Video Player tutorial using AVPlayer (AVFoundation)), 여세를 몰아 오디오 플레이어도 만들어보자.
Last posting is Video Player Tutorial(2016/02/23 - [프로그래밍/iOS] - [iOS/Objective-C] Video Player tutorial using AVPlayer (AVFoundation)). so We'll make a simple audio player this time.
- 요구사항
1) 백그라운드에서 플레이 되어야 한다.
2) iOS의 Now Playing Info Center에 현재상태가 보여야 하고, 조정도 가능해야한다.
3) 알림음과 다른 볼륨으로 가져가야한다.
4) 벨소리/전화와 같은 인터럽트가 발생한 이후 다시 재생되어야 한다.
- Requirement
1) It can play music background
2) It can display current status on Now Playing Info Center(You can check if you swipe bottom to middle on your iPhone/iPad screen) and control music on it.
3) The music volume must be different from Alarm sound
4) It can be resumed after bell-ringing or phone call interrupt.
[1편에서는 일단 play하는 것 까지만 다룬다]
[At this time, I will post Just How to play music using AVPlayer/AVFoundation]
1. AudioManager
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | @import AVFoundation; @import MediaPlayer; @import AudioToolbox; #import <Foundation/Foundation.h> #import "CFile.h" @protocol AudioManagerDelegate <NSObject> @optional - (void)metadataExtracted; - (void)playerStateChanged:(BOOL)isPlaying; @end @interface AudioManager : NSObject <AVAudioPlayerDelegate> @property (nonatomic) NSInteger curTrackIndex; @property (strong, nonatomic) NSArray<CFile *> *playlist; @property (strong, nonatomic) NSString *curTitle; @property (strong, nonatomic) NSString *curAlbumName; @property (strong, nonatomic) NSString *curArtist; @property (strong, nonatomic) UIImage *curAlbumImg; @property (nonatomic, assign) id<AudioManagerDelegate> delegate; + (instancetype)sharedInstance; - (void)prepareToPlay:(void (^)(BOOL bSuccess))complete; - (void)playAudio; - (void)pauseAudio; // For auto playing prev/next music. (looping playlist) - (void)playPrevAudioAutoStart:(BOOL)bAutoStart; - (void)playNextAudioAutoStart:(BOOL)bAutoStart; // Common control function - (void)seekTo:(NSTimeInterval)value; - (float)curAudioDuration; - (NSTimeInterval)curAudioTime; - (NSString *)curTrackName; // Event from Now Playing Center - (void)handleControlReceivedWithEvent:(UIEvent *)event; @end | cs |
- If you want to play music when the view-controller dismissed, you can make AudioManager and use it in view-controller.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | - (void)prepareToPlay:(void (^)(BOOL bSuccess))complete { __block BOOL bPrepared = NO; if (filePath == nil) { // download audio files [self downloadAudioFile:cFile complete:^(BOOL bSuccess) { if (bSuccess) { NSURL *audioFileUrl = [NSURL fileURLWithPath:[cFile.filePath fullPath]]; bPrepared = [self prepareAudioPlayerWithUrl:audioFileUrl]; } if (complete) { complete (bSuccess && bPrepared); } }]; } else { NSURL *audioFileUrl = [NSURL fileURLWithPath:filePath fullPath]; bPrepared = [self prepareAudioPlayerWithUrl:audioFileUrl]; if (complete) { complete(bPrepared); } } } - (BOOL)prepareAudioPlayerWithUrl:(NSURL *)audioFileUrl { BOOL bPrepared = NO; NSError *error; self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileUrl error:&error]; if (error == nil) { self.audioPlayer.delegate = self; bPrepared = [self.audioPlayer prepareToPlay]; // Get metadata cuncurrently [self metadataFromFileUrl:audioFileUrl]; } else { NSLog(@"====================== error : %@", error); } return bPrepared; } - (void)metadataFromFileUrl:(NSURL *)audioFileUrl { AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:audioFileUrl options:nil]; NSArray *keys = @[@"commonMetadata"]; self.curTitle = @""; self.curAlbumName = @""; self.curArtist = @""; self.curAlbumImg = nil; [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^() { NSError *error = nil; AVKeyValueStatus metadataStatus = [asset statusOfValueForKey:@"commonMetadata" error:&error]; switch (metadataStatus) { case AVKeyValueStatusLoaded:{ for (AVMetadataItem *item in [asset commonMetadata]) { if ([item.commonKey isEqualToString:AVMetadataCommonKeyTitle]) { self.curTitle = item.stringValue; } else if ([item.commonKey isEqualToString:AVMetadataCommonKeyAlbumName]) { self.curAlbumName = item.stringValue; } else if ([item.commonKey isEqualToString:AVMetadataCommonKeyArtist]) { self.curArtist = item.stringValue; } else if ([item.commonKey isEqualToString:AVMetadataCommonKeyArtwork]) { self.curAlbumImg = [UIImage imageWithData:[item.value copyWithZone:nil]]; } } if ([self.delegate respondsToSelector:@selector(metadataExtracted)]) { dispatch_async(dispatch_get_main_queue(), ^{ // Metadata notification for view update. [self.delegate metadataExtracted]; }); } dispatch_async(dispatch_get_main_queue(), ^{ [self updatePlayingInfoCenter]; }); break; } default: NSLog(@"error - status:%ld, error:%@", (long)metadataStatus, error); break; } }]; } - (void)updatePlayingInfoCenter { NSMutableDictionary *playingInfo = [NSMutableDictionary dictionary]; playingInfo[MPMediaItemPropertyAlbumTitle] = self.curAlbumName; playingInfo[MPMediaItemPropertyArtist] = self.curArtist; playingInfo[MPMediaItemPropertyTitle] = (0<self.curTitle.length?self.curTitle:self.curTrackName); playingInfo[MPMediaItemPropertyPlaybackDuration] = @(ceil(self.curAudioDuration)); playingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = @(self.curAudioTime); playingInfo[MPNowPlayingInfoPropertyPlaybackRate] = @(1); playingInfo[MPNowPlayingInfoPropertyPlaybackQueueIndex] = @(self.curTrackIndex); if (self.curAlbumImg != nil) { // You can see the album art on lock screen if you set this. MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:self.curAlbumImg]; playingInfo[MPMediaItemPropertyArtwork] = artwork; } [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = playingInfo; } - (void)playAudio { [[AVAudioSession sharedInstance] setActive:YES error:nil]; if ([self.audioPlayer play]) { MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter]; // Set current time to Now Playing Center. // mutable로 안하면 기존 정보가 지워짐. 문서에 안나옴. 왜그런지 모름. // You should use mutable dictionary. otherwise, all information you set before will be lost. NSMutableDictionary *playingInfo = [NSMutableDictionary dictionaryWithDictionary:center.nowPlayingInfo]; playingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = @(self.audioPlayer.currentTime); center.nowPlayingInfo = playingInfo; } if ([self.delegate respondsToSelector:@selector(playerStateChanged:)]) { [self.delegate playerStateChanged:self.audioPlayer.isPlaying]; } } | cs |
2. View Controller.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | - (void)viewDidLoad { [super viewDidLoad]; // running time slide bar setting. [self.runningTimeSlider setThumbImage:[UIImage imageNamed:@"YOUR_HANDLE_ICON"] forState:UIControlStateNormal]; [self.runningTimeSlider setThumbImage:[UIImage imageNamed:@"YOUR_HANDLE_ICON_HIGHLIGHTED"] forState:UIControlStateHighlighted]; [self.runningTimeSlider setMaximumValue:[[AudioManager sharedInstance] curAudioDuration]]; [self.runningTimeSlider setValue:0.0]; // play music automatically [[AudioManager sharedInstance] setDelegate:self]; [[AudioManager sharedInstance] setPlaylist:self.playlist]; [[AudioManager sharedInstance] prepareToPlay:^(BOOL bSuccess) { [[AudioManager sharedInstance] playAudio]; }]; } | cs |
- Just Use Audio Manager :)
'Programming > iOS' 카테고리의 다른 글
[Swift] How to convert from void* to char* (0) | 2017.09.05 |
---|---|
How to use private git repository under carthage. on Mac OS (0) | 2017.08.22 |
[iOS/Objective-C] Video Player tutorial using AVPlayer (AVFoundation) (2) | 2016.02.23 |
[iOS/Objective-C] 이미지 자르기 (Image crop), CGContextSetBlendMode usage (0) | 2016.02.05 |
[iOS/Objective-C] To run threads sequentialy or concurrently (0) | 2016.02.04 |