標籤:avaudioplayer mpmovieplayerviewcon nsnotificationcenter 音樂播放器 媒體播放
概要
本章主要接到上次的文章講音頻播放,因為音頻播放沒有對應的播放視圖控制項,所以本例子自繪了一個簡單的音樂播放介面,包括返回、暫停/播放、進度控制等。
結果展示
(暫停/播放 播放進度拖拉 返回)
流程概要
1.接上次的流程建立一個音樂播放類,類比MPMoviePlayerViewController的實現;
2.播放音訊時使用IOS內建的類AVAudioPlayer,該類支援播放wav.mp3等格式的音樂,其對應的代理類為AVAudioPlayerDelegate;
3.對於這種初始化賦值代碼:self._buttonPlayPause = [[UIButton alloc] init];突然想到這種賦值會存在記憶體泄露 ,因為分配記憶體直接賦值給屬性變數,變數retain兩次最後卻只release一次。所以以前的好多代碼都有問題。汗!!!
4.NSNotificationCenter添加監視時注意object不要為空白而要設為nil,不能會導致難發現的記憶體奔潰問題。
主要代碼h檔案
//// MusicPlayerViewController.h// MediaPlayer//// Created by God Lin on 14/12/15.// Copyright (c) 2014年 arbboter. All rights reserved.//#import <UIKit/UIKit.h>#import <AVFoundation/AVFoundation.h>@interface MusicPlayerViewController : UIViewController <AVAudioPlayerDelegate>{ AVAudioPlayer* _audioPlayer; UIButton* _buttonPlayPause; UIButton* _buttonReturn; UIImage* _imagePause; UIImage* _imagePlay; UIImage* _imageReturn; UISlider* _sliderPlayer; NSURL* _urlMusic; NSTimer* _timerPlayProcess; BOOL _bPlay; UITextView* _textViewInfo; NSString* _titleMusic;}@property (nonatomic, retain) AVAudioPlayer* _audioPlayer;@property (nonatomic, retain) UIButton* _buttonPlayPause;@property (nonatomic, retain) UIButton* _buttonReturn;@property (nonatomic, retain) UISlider* _sliderPlayer;@property (nonatomic, retain) NSURL* _urlMusic;@property (nonatomic, retain) UIImage* _imagePause;@property (nonatomic, retain) UIImage* _imagePlay;@property (nonatomic, retain) UIImage* _imageReturn;@property (nonatomic, readwrite) BOOL _bPlay;@property (nonatomic, retain) NSTimer* _timerPlayProcess;@property (nonatomic, retain) UITextView* _textViewInfo;@property (nonatomic, retain) NSString* _titleMusic;-(id)initWithContentURL:(NSURL*)url;-(void)setContentURL:(NSURL*)url;@endvoid AudioFinishedCallBack(SystemSoundID idSound, void* data);
m檔案
//// MusicPlayerViewController.m// MediaPlayer//// Created by God Lin on 14/12/15.// Copyright (c) 2014年 arbboter. All rights reserved.//#import "MusicPlayerViewController.h"@interface MusicPlayerViewController ()@end@implementation MusicPlayerViewController@synthesize _audioPlayer, _urlMusic;@synthesize _buttonPlayPause, _buttonReturn;@synthesize _sliderPlayer, _titleMusic;@synthesize _imagePause, _imageReturn, _imagePlay;@synthesize _bPlay, _timerPlayProcess, _textViewInfo;// 布局-(void) doRotateAction:(NSNotification *) notification{ [self relayout];}-(void)relayout{ CGFloat x = self.view.frame.origin.x; CGFloat y = self.view.frame.origin.y + 20; CGFloat w = self.view.frame.size.width; CGFloat h = self.view.frame.size.height - 20; CGFloat x1 = 0; CGFloat y1 = 0; CGFloat w1 = 0; CGFloat h1 = 0; UIImage* image = self._imagePause; if(w < h) { image = [UIImage imageNamed:@"oca.jpeg"]; } else { image = [UIImage imageNamed:@"flower.jpg"]; } self.view.backgroundColor = [UIColor colorWithPatternImage:image]; image = self._imageReturn; x1 = x + 5; y1 = y + 5; w1 = image.size.width; h1 = image.size.height; self._buttonReturn.frame = CGRectMake(x1, y1, w1, h1); image = self._imagePause; x1 = x + 5; y1 = (y + h) - (image.size.height + 5); w1 = image.size.width; h1 = image.size.height; self._buttonPlayPause.frame = CGRectMake(x1, y1, w1, h1); x1 += image.size.width ; w1 = x + w - 5 - x1; self._sliderPlayer.frame = CGRectMake(x1, y1, w1, h1); x1 = x + 5; y1 = y + h/4; w1 = w - 10; h1 = h/2; NSArray* fontArray = [UIFont familyNames]; NSString* fontName = [fontArray objectAtIndex:arc4random()%[fontArray count]]; _textViewInfo.font = [UIFont fontWithName:fontName size:h1/10]; _textViewInfo.frame = CGRectMake(x1, y1, w1, h1); _textViewInfo.editable = NO; _textViewInfo.textAlignment = NSTextAlignmentCenter; _textViewInfo.backgroundColor = [UIColor clearColor];}// 播放介面控制項-(void)onDragPlayProcess{ [self._audioPlayer setCurrentTime:self._sliderPlayer.value];}-(void)onPlayProcess{ self._sliderPlayer.value = self._audioPlayer.currentTime; NSInteger nTime = _sliderPlayer.value; NSInteger nMaxTime = _sliderPlayer.maximumValue; NSString* info = [[NSString alloc] initWithFormat:@"%@\n%02ld:%02ld - %02ld:%02ld", _titleMusic, nTime/60, nTime%60, nMaxTime/60, nMaxTime%60]; _textViewInfo.text = info; [info release];}// 設定播放的檔案-(id)initWithContentURL:(NSURL*)url{ self = [super init]; if(self) { [self setContentURL:url]; } return self;}-(void)setContentURL:(NSURL*)url{ self._urlMusic = url;}// 啟動播放,畫面出現前-(void)viewWillAppear:(BOOL)animated{ [self onPlay];}- (void)viewDidLoad{ [super viewDidLoad]; self._imagePlay = [UIImage imageNamed:@"play.png"]; self._imagePause = [UIImage imageNamed:@"pause.png"]; self._imageReturn = [UIImage imageNamed:@"return.png"]; // 突然想到這種賦值會存在記憶體泄露 -> 分配記憶體直接賦值給屬性變數 // 因為retain兩次最後卻只release一次,所以以前的好多都有問題 // 汗!!! UIButton* button = nil; button = [[UIButton alloc] init]; self._buttonPlayPause = button; [button release]; [self.view addSubview:self._buttonPlayPause]; [self._buttonPlayPause setImage:self._imagePause forState:UIControlStateNormal]; [self._buttonPlayPause addTarget:self action:@selector(onPlayPause) forControlEvents:UIControlEventTouchUpInside]; button = [[UIButton alloc] init]; self._buttonReturn = button; [button release]; [self.view addSubview:self._buttonReturn]; [self._buttonReturn setImage:self._imageReturn forState:UIControlStateNormal]; [self._buttonReturn addTarget:self action:@selector(onStop) forControlEvents:UIControlEventTouchUpInside]; UISlider* slider = [[UISlider alloc] init]; self._sliderPlayer = slider; [slider release]; [self.view addSubview:self._sliderPlayer]; [self._sliderPlayer addTarget:self action:@selector(onDragPlayProcess) forControlEvents:UIControlEventValueChanged]; UITextView* textView = [[UITextView alloc] init]; self._textViewInfo = textView; [textView release]; [self.view addSubview:_textViewInfo]; [self relayout]; // 切屏 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doRotateAction:) name:UIDeviceOrientationDidChangeNotification object:self];}-(void)onPlay{ if(!_bPlay) { NSError* error; self._bPlay = YES; AVAudioPlayer* player = [[AVAudioPlayer alloc] initWithContentsOfURL:self._urlMusic error:&error]; self._audioPlayer = player; [player release]; self._audioPlayer.delegate = self; self._timerPlayProcess = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(onPlayProcess) userInfo:nil repeats:YES]; //self._audioPlayer; self._sliderPlayer.maximumValue = 0.0f; // 設定播放時間長度 self._sliderPlayer.maximumValue = self._audioPlayer.duration; [self._audioPlayer play]; // 非法音頻 if(_sliderPlayer.maximumValue == 0) { [self onStop]; } [self onPlayProcess]; }}-(void)onStop{ [self dismissViewControllerAnimated:YES completion:nil]; [self._audioPlayer stop]; [self._timerPlayProcess invalidate]; _bPlay = NO;}-(void)onPlayPause{ UIImage* image = nil; _bPlay = !_bPlay; if(_bPlay) { image = self._imagePause; [self._audioPlayer play]; } else { image = self._imagePlay; [self._audioPlayer pause]; } [self._buttonPlayPause setImage:image forState:UIControlStateNormal];}- (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}-(void)dealloc{ [_buttonReturn release]; [_buttonPlayPause release]; [_imageReturn release]; [_imagePause release]; [_imagePlay release]; [_audioPlayer release]; [_urlMusic release]; [_sliderPlayer release]; [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc];}#pragma mark - AVAudioPlayerDelegate- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{ NSLog(@"Music play over"); [self onStop];}/* if an error occurs while decoding it will be reported to the delegate. */- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error{ NSLog(@"Music play occured error"); [self onStop];}@end// 音頻播放回呼函數void AudioFinishedCallBack(SystemSoundID idSound, void* data){ AudioServicesDisposeSystemSoundID(idSound);}
項目工程
媒體播放器(AVAudioPlayer,MPMoviePlayerViewController)