Avplayer video playing Method - ios

I am working on App in which I want to display current playing time and total time of video. I got the total time. Now for showing current playing time Which method will get called. Can anyone help? I have used avplayer. This is the code:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.avplayer pause];
self.avplayer = [AVQueuePlayer playerWithURL:[NSURL URLWithString:#""]];
self.avplayer = nil;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:NO];
AVPlayerItem *currentItem = self.avplayer.currentItem;
CMTime duration = currentItem.duration; //total time
CMTime currentTime = currentItem.currentTime; //playing time
NSUInteger durationSeconds = (long)CMTimeGetSeconds(duration);
NSUInteger minutes = floor(durationSeconds % 3600 / 60);
NSUInteger seconds = floor(durationSeconds % 3600 % 60);
NSString *time = [NSString stringWithFormat:#"%02ld:%02ld", (unsigned long)minutes, (unsigned long)seconds];
NSLog(#"Time|%#", time);
lblTotaltime.text = time;
NSUInteger durationSeconds1 = (long)CMTimeGetSeconds(currentTime);
NSUInteger minutes1 = floor(durationSeconds1 % 3600 / 60);
NSUInteger seconds1 = floor(durationSeconds1 % 3600 % 60);
NSString *time1 = [NSString stringWithFormat:#"%02ld:%02ld", (unsigned long)minutes1, (unsigned long)seconds1];
NSLog(#"Time|%#", time1);
lblRemaningTime.text = time1;
}
#pragma mark PlayerMethods
- (void)itemDidFinishPlaying:(NSNotification *)notification {
AVPlayerItem *player = [notification object];
[player seekToTime:kCMTimeZero];
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
[p seekToTime:CMTimeMake(0, 3)];
}
- (void)playerStartPlaying
{
[self.avplayer play];
}

You can get the current played time by using currentItem property using AVPlayerItem
AVPlayerItem *getcurrentItem = yourAVPlayerName.currentItem;
for get total Duration
CMTime fullDuration = getcurrentItem.duration;
for get current Time
CMTime playercurrentTime = getcurrentItem.currentTime;
alternate
NSTimeInterval playercurrentTime = CMTimeGetSeconds(getcurrentItem.currentTime);
NSLog(#" get current Time of video :%f ",playercurrentTime);

Related

iOS get audio duration from avplayer

I'm new to ios, i have an app which contain online audio player. i need to get total duration for the audio. i have tried lot but all codes returns NaN or 0 duration. What is the best way to get total duration for the audio..?
MY CODE
NSString *songUrl = #"http://9xmusiq.com/songs2/tamil/Kaatru%20Veliyidai/Azhagiye%20%5bStarmusiq.cc%5d.mp3"
AVURLAsset *asset = [AVURLAsset assetWithURL:[NSURL URLWithString:songUrl]];
AVPlayerItem *playerItem1 = [AVPlayerItem playerItemWithAsset:asset];
AVPlayer *player1 = [AVPlayer playerWithPlayerItem:playerItem1];
AVPlayerLayer *playerLayer1 = [AVPlayerLayer playerLayerWithPlayer:player1];
playerLayer1.videoGravity = AVLayerVideoGravityResizeAspectFill;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
playerLayer1.frame = self.view.frame;
});
[self.view.layer insertSublayer:playerLayer1 atIndex:1];
[player1 play];
[playerItem1 addObserver:self forKeyPath:#"status" options:0 context:nil];
[playerItem1 addObserver:self forKeyPath:#"playbackBufferEmpty" options:0 context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if ([object isKindOfClass:[AVPlayerItem class]]){
AVPlayerItem *item = (AVPlayerItem *)object;
if ([keyPath isEqualToString:#"status"]){
switch(item.status){
case AVPlayerItemStatusFailed:
NSLog(#"player item status failed");
break;
case AVPlayerItemStatusReadyToPlay:
NSLog(#"player item status is ready to play");
Float64 duration = CMTimeGetSeconds(self.avPlayer.currentItem.duration);
NSLog(#"Duration--> %f",duration) // NaN returns
break;
case AVPlayerItemStatusUnknown:
NSLog(#"player item status is unknown");
break;
}
}else if ([keyPath isEqualToString:#"playbackBufferEmpty"]){
if (item.playbackBufferEmpty){
NSLog(#"player item playback buffer is empty");
}
}
}
}
Thanks for your support friends, Finally i find the solution for my problem instead of using AVPlayer i used AVAudioPlayer and i fixed the problem and i got the audio duration.
NSString* resourcePath = #"http://9xmusiq.com/songs2/tamil/Kaatru%20Veliyidai/Azhagiye%20%5bStarmusiq.cc%5d.mp3"; //your url
NSData *_objectData = [NSData dataWithContentsOfURL:[NSURL URLWithString:resourcePath]];
NSError *error;
AVAudioPlayer *player1 = [[AVAudioPlayer alloc] initWithData:_objectData error:&error];
player1.numberOfLoops = 0;
player1.volume = 1.0f;
[player1 prepareToPlay];
NSLog(#"Total Duration : %f",player1.duration);
if (player1 == nil){
NSLog(#"%#", [error description]);
}else{
[player1 play];
}
I'm not that familiar with AVPlayer, but in digging around in the docs it looks like the AVAsset (or in your case AVURLAsset) is the object that holds a duration.
Try querying the asset:
AVURLAsset *asset = [AVURLAsset assetWithURL:[NSURL
URLWithString:songUrl]];
CMTime durationCMTime = asset.duration;
Float64 duration =
CMTimeGetSeconds(durationCMTime);
NSLog(#"Duration of asset is %f", duration);
When your AVPlayer ready to play (under the case of AVPlayerItemStatusReadyToPlay), you can use
CMTime duration = self.player.currentItem.asset.duration;
float seconds = CMTimeGetSeconds(duration);
You can access the duration of an AVPlayerItem's asset using duration property. If you need precise seconds with decimals, use Float64 to receive time from CMTimeGetSeconds. For regular use cases, I guess int would be sufficient.
CMTime duration = playerItem1.asset.duration;
int durationTotalSeconds = CMTimeGetSeconds(duration);
int durationHours = floor(durationTotalSeconds / 3600);
int durationMinutes = floor(durationTotalSeconds % 3600 / 60);
int durationSeconds = floor(durationTotalSeconds % 3600 % 60);
NSString *audioDurationString = [NSString stringWithFormat:#"%d:%d:%d",durationHours, durationMinutes, durationSeconds];
you can get duration - (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block;
#property(nonatomic,strong) AVPlayer *player;
#property(nonatomic,strong) id obsever;
self.player = [[AVPlayer alloc]initWithURL:URL];
[self.player play];
self.obsever = [self.player addPeriodicTimeObserverForInterval:interval queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {//you can get duration in block
CMTimeGetSeconds(theItem.currentTime);
CMTimeGetSeconds(theItem.duration)
}];

AVPlayer (or AVAudioSession?) plays after I leave the ViewController, and when I return two are then playing

I have a viewController that streams audio from the web. When I leave that page of my app, the audio keeps playing (this is good). However, when I go back, the viewdidload method creates a second audioplayer. I can do this over and over until I have so many AVplayers. It sounds incredibly echo-y. Here is all my relevant code.
#import "ListenViewController.h"
#import <AVFoundation/AVAudioPlayer.h>
#import <AVFoundation/AVPlayerItem.h>
#import <AVFoundation/AVPlayer.h>
#import <AVFoundation/AVFoundation.h>
#import "RSPlayPauseButton.h"
#interface ListenViewController () <AVAudioPlayerDelegate, AVAudioSessionDelegate>
#property (nonatomic, strong) AVPlayer *player;
#property (nonatomic, strong) AVPlayerItem *playerItem;
#property (nonatomic, strong) RSPlayPauseButton *playPauseButton;
#property (nonatomic, copy) NSDate *now;
#end
#implementation ListenViewController
static int min = -2;
-(void) viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
//[self.player pause];
min = -2;
}
-(void) viewDidLoad
{
[super viewDidLoad];
/*
if ([AVAudioSession sharedInstance].isInputAvailable) {
NSLog(#"XXXXXXXXX");
}
*/
NSURL *streamingURL = [NSURL URLWithString:#"http://URL.m3u"];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:streamingURL];
self.playerItem = playerItem;
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
[player setAllowsExternalPlayback:NO];
CMTime tm = CMTimeMake(1, 1);
[player addPeriodicTimeObserverForInterval:tm
queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
/*
NSDateFormatter *DateFormatter=[[NSDateFormatter alloc] init];
[DateFormatter setDateFormat:#"yyyy-MM-dd hh:mm:ss:mmm"];
NSLog(#"%#",[DateFormatter stringFromDate:[NSDate date]]);
*/
if (!self.now) {
self.now = [NSDate date];
}
/*
NSDate *now = [NSDate date];
NSTimeInterval interval = [now timeIntervalSinceDate:self.now];
self.interval += 0.01;
interval -= (10 * self.rw2);
interval -= (60 * self.rw3);
interval += (10 * self.ff2);
self.timeLabel.text = [NSString stringWithFormat:#"%f", interval];
NSLog(#"INTERVAL: %#", [NSString stringWithFormat:#"%f", self.interval]);
*/
if (CMTimeGetSeconds(self.player.currentTime) >= 0) {
self.timeLabel.text = [NSString stringWithFormat:#"%.0f", CMTimeGetSeconds(self.player.currentTime)];
NSTimeInterval time = CMTimeGetSeconds(self.player.currentTime);
//int min = time/60;
int sec = lroundf(time) % 60;
if (sec == 0) {
++min;
}
NSLog(#"sec: %d", sec);
// update your UI with timeLeft
self. timeLabel.text = [NSString stringWithFormat:#"%.2d:%.2d", min,sec];
}
}];
[self setPlayer:player];
[player play];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
if (!self.playPauseButton) {
self.playPauseButton = [[RSPlayPauseButton alloc] initWithFrame:self.view.frame];
self.playPauseButton.tintColor = [UIColor colorWithRed:1.0 green:155.0/255.0 blue:0.0 alpha:1.0];
self.playPauseButton.animationStyle = RSPlayPauseButtonAnimationStyleSplitAndRotate;
self.playPauseButton.paused = NO;
[self.playPauseButton addTarget:self action:#selector(playPauseButtonDidPress:) forControlEvents:UIControlEventTouchUpInside];
}
[self.view addSubview:self.playPauseButton];
}
- (IBAction)rewind:(id)sender
{
//NSLog(#"currenttime: %f", CMTimeGetSeconds(self.player.currentTime));
//NSLog(#"timescale: %d", self.player.currentTime.timescale);
CMTime cmTime = CMTimeMake(CMTimeGetSeconds(self.player.currentTime) - 1.0, 1);
CMTime zero = CMTimeMake(1, 2);
if (CMTimeGetSeconds(cmTime) > CMTimeGetSeconds(zero)) {
[self.player.currentItem seekToTime:cmTime];
NSLog(#"!");
}
NSLog(#"RW");
}
- (IBAction)rewind2:(id)sender
{
/*
CMTime cmTime = CMTimeMakeWithSeconds(CMTimeGetSeconds(self.player.currentTime) - 10.0, self.player.currentTime.timescale);
CMTime zero = CMTimeMake(0, 10000);
if (CMTimeGetSeconds(cmTime) > CMTimeGetSeconds(zero)) {
[self.player.currentItem seekToTime:cmTime];
self.rw2 += 1;
NSLog(#"RW2!");
}
*/
NSLog(#"RW2");
CMTime cmTime = CMTimeMakeWithSeconds(CMTimeGetSeconds(self.player.currentTime) - 10.0, self.player.currentTime.timescale);
CMTime zero = CMTimeMake(1, 2);
NSLog(#"cmTime: %f", CMTimeGetSeconds(cmTime));
NSLog(#"zero: %f", CMTimeGetSeconds(zero));
if (CMTimeGetSeconds(cmTime) > CMTimeGetSeconds(zero)) {
[self.player.currentItem seekToTime:cmTime];
NSLog(#"!");
} else {
[self rewindAll];
}
}
- (IBAction)rewind3:(id)sender
{
CMTime cmTime = CMTimeMakeWithSeconds(CMTimeGetSeconds(self.player.currentTime) - 60.0, self.player.currentTime.timescale);
CMTime zero = CMTimeMake(0, 10000);
if (CMTimeGetSeconds(cmTime) > CMTimeGetSeconds(zero)) {
[self.player.currentItem seekToTime:cmTime];
NSLog(#"RW3!");
}
NSLog(#"RW3");
}
- (void) rewindAll
{
CMTime one = CMTimeMake(1, 1);
[self.player.currentItem seekToTime:one];
}
- (IBAction)fastForward:(id)sender
{
CMTime cmTime = CMTimeMakeWithSeconds(CMTimeGetSeconds(self.player.currentTime) + 5.0, self.player.currentTime.timescale);
NSDate *now = [NSDate date];
NSTimeInterval interval = [now timeIntervalSinceDate:self.now];
CMTime seekingCM = CMTimeMake(interval, 1);
if (CMTimeGetSeconds(cmTime) < CMTimeGetSeconds(seekingCM)) {
[self.player.currentItem seekToTime:cmTime];
}
}
- (IBAction)fastForward2:(id)sender
{
CMTime cmTime = CMTimeMakeWithSeconds(CMTimeGetSeconds(self.player.currentTime) + 10.0, self.player.currentTime.timescale);
NSDate *now = [NSDate date];
NSTimeInterval interval = [now timeIntervalSinceDate:self.now];
CMTime seekingCM = CMTimeMake(interval, 1);
if (CMTimeGetSeconds(cmTime) < CMTimeGetSeconds(seekingCM)) {
[self.player.currentItem seekToTime:cmTime];
NSLog(#"FF2!");
} else {
[self fastForward3:nil];
}
}
- (IBAction)fastForward3:(id)sender
{
NSDate *now = [NSDate date];
NSLog(#"FIRSTDATE: %#", self.now);
NSTimeInterval interval = [now timeIntervalSinceDate:self.now];
interval -= 0.5f;
NSLog(#"INT: %f", interval);
CMTime seekingCM = CMTimeMake(interval, 1);
/*
NSTimeInterval interval = self.interval - 1.0;
CMTime seekingCM = CMTimeMake(interval, 1);
[self.player.currentItem seekToTime:seekingCM];
*/
//CMTime seekingCM = CMTimeMake(self.interval, 1);
[self.player.currentItem seekToTime:seekingCM];
NSLog(#"FF3!");
}
- (NSTimeInterval)currentPlaybackTime
{
return CMTimeGetSeconds(self.player.currentItem.currentTime);
}
- (void)setCurrentPlaybackTime:(NSTimeInterval)time
{
CMTime cmTime = CMTimeMakeWithSeconds(time, NSEC_PER_SEC);
[self.player.currentItem seekToTime:cmTime];
}
- (void)viewDidLayoutSubviews
{
[self.playPauseButton sizeToFit];
self.playPauseButton.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 1.2);
}
- (void)playPauseButtonDidPress:(RSPlayPauseButton *)playPauseButton
{
[playPauseButton setPaused:!playPauseButton.isPaused animated:YES];
if (playPauseButton.isPaused) {
[self.player pause];
}
else {
[self.player play];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
By the way, there is a UIlabel that gives the current time of the player, but if I leave the page and go back, the label automatically starts again at 00:00. This is because it's giving the current time of the new AVPlayer. Is there a way to make it give the current time of the original AVPlayer?
I would create a singleton object to control a AVPlayer, so no matter how many view controllers you have they all would communicate with this one player controller. So basically I'd advise you to move all the code you have in viewDidLoad to some MYPlayerController singleton class.
here is my view controller from swift, if it can help you, will be fine, I have implemented all radio stuff there:
swift view controller

why am i getting progress slider exception error

I am making an application which will play audio.I have used a slider to show the time elapsed and time remaining graphically in the slider.But i am not getting how to get the value programitcically.I have used the following code but its throwing an exception.
-(void) updateMyProgress
{
float progress = [avPlayer currentTime]/[avPlayer duration];
self.myProgressView.progress = progress;
}
The code im my viewcontoller.m file is
#import "ViewController.h"
#interface ViewController ()
{
AVAudioPlayer *avPlayer;
AVPlayer *avp;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *stringPath = [[NSBundle mainBundle]pathForResource:#"naat" ofType:#"mp3"];
NSURL *url = [NSURL fileURLWithPath:stringPath];
NSError *error;
avPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
[avPlayer setNumberOfLoops:2];
[avPlayer setVolume:self.sliderVolumeOutlet.value];
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateMyProgress) userInfo:nil repeats:YES];
}
/*-(void) updateMyProgress
{
CGFloat progress = [avPlayer currentTime]/[avPlayer duration];
self.myProgressView.progress = progress;
}
*/
-(void) updateMyProgress
{
AVPlayerItem *currentItem = avp.currentItem ;
CMTime duration = currentItem.duration; //total time
CMTime currentTime = currentItem.currentTime; //playing time
CGFloat progress = currentTime.value/duration.value;
self.myProgressView.progress = progress;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)sliderVolumeAction:(id)sender
{
UISlider *myslider= sender;
[avPlayer setVolume:myslider.value];
}
- (IBAction)stopButton:(id)sender
{
[avPlayer stop];
[avPlayer setCurrentTime:0];
}
- (IBAction)pauseButton:(id)sender
{
[avPlayer pause];
}
- (IBAction)playButton:(id)sender
{
[avPlayer play];
}
#end![enter image description here][1]
The attached image shows the screenshot of the exception
! [1]: http://i.stack.imgur.com/WsxtI.png
AVPlayer has no property called "duration".
"duration" is the property of AVPlayerItem.
And both duration and currentTime are of "CMTime" dataType
We have to get the value of CMTime then update the progress.
-(void) updateMyProgress
{
AVPlayerItem *currentItem = avPlayer.currentItem;
CMTime duration = currentItem.duration; //total time
CMTime currentTime = currentItem.currentTime; //playing time
CGFloat progress = currentTime.value/duration.value;
self.myProgressView.progress = progress;
}

Scrubber (UISlider) in AVPlayer?

When you play remote video via AVPlayer and start rewinding, the scrubber is buggy.
I'm doing a player based on this Apple example.
How to implement it smoothly?
Code from my project follows - https://github.com/nullproduction/Player
- (void)initScrubberTimer
{
double interval = .1f;
CMTime playerDuration = [self playerItemDuration];
if (CMTIME_IS_INVALID(playerDuration))
{
return;
}
double duration = CMTimeGetSeconds(playerDuration);
if (isfinite(duration))
{
CGFloat width = CGRectGetWidth([scrubberSlider bounds]);
interval = 0.5f * duration / width;
}
__weak id weakSelf = self;
CMTime intervalSeconds = CMTimeMakeWithSeconds(interval, NSEC_PER_SEC);
mTimeObserver = [self.player addPeriodicTimeObserverForInterval:intervalSeconds
queue:dispatch_get_main_queue()
usingBlock:^(CMTime time) {
[weakSelf syncScrubber];
}];
}
- (void)syncScrubber
{
CMTime playerDuration = [self playerItemDuration];
if (CMTIME_IS_INVALID(playerDuration))
{
scrubberSlider.minimumValue = 0.0;
return;
}
double duration = CMTimeGetSeconds(playerDuration);
if (isfinite(duration))
{
float minValue = [scrubberSlider minimumValue];
float maxValue = [scrubberSlider maximumValue];
double time = CMTimeGetSeconds([self.player currentTime]);
[scrubberSlider setValue:(maxValue - minValue) * time / duration + minValue];
}
}
- (IBAction)beginScrubbing:(id)sender
{
mRestoreAfterScrubbingRate = [self.player rate];
[self.player setRate:0.f];
[self removePlayerTimeObserver];
}
- (IBAction)scrub:(id)sender
{
if ([sender isKindOfClass:[UISlider class]])
{
UISlider* slider = sender;
CMTime playerDuration = [self playerItemDuration];
if (CMTIME_IS_INVALID(playerDuration))
{
return;
}
double duration = CMTimeGetSeconds(playerDuration);
if (isfinite(duration))
{
float minValue = [slider minimumValue];
float maxValue = [slider maximumValue];
float value = [slider value];
double time = duration * (value - minValue) / (maxValue - minValue);
[self.player seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC)];
}
}
}
- (IBAction)endScrubbing:(id)sender
{
if (!mTimeObserver)
{
CMTime playerDuration = [self playerItemDuration];
if (CMTIME_IS_INVALID(playerDuration))
{
return;
}
double duration = CMTimeGetSeconds(playerDuration);
if (isfinite(duration))
{
CGFloat width = CGRectGetWidth([scrubberSlider bounds]);
double tolerance = 0.5f * duration / width;
__weak id weakSelf = self;
CMTime intervalSeconds = CMTimeMakeWithSeconds(tolerance, NSEC_PER_SEC);
mTimeObserver = [self.player addPeriodicTimeObserverForInterval:intervalSeconds
queue:dispatch_get_main_queue()
usingBlock: ^(CMTime time) {
[weakSelf syncScrubber];
}];
}
}
if (mRestoreAfterScrubbingRate)
{
[self.player setRate:mRestoreAfterScrubbingRate];
mRestoreAfterScrubbingRate = 0.f;
}
}
I guess the problem is, that your scrubber is still updating from the video, while you are using the seekbar. Implement it in a way, that you pause the player during the scrubbing, and you won't have this bug anymore. Checkout my solution:
The function to update your player:
- (IBAction)seekbarAction:(UISlider *)sender {
CMTime videoLength = playerItem1.duration; //gets the video duration
float videoLengthInSeconds = videoLength.value/videoLength.timescale; //transfers the CMTime duration into seconds
[player1 seekToTime:CMTimeMakeWithSeconds(videoLengthInSeconds*sender.value, 1)];
}
And another seekbar action with "Touch down" in order to pause the video:
- (IBAction)pauseSeek:(id)sender {
[player1 pause];
}
And another seekbar action with "Touch up" in order to resume the video when you release the scrubber. Hope this helps.

AVPlayer causes crash while updating progress slider with Assertion failure in -[AVPlayerPeriodicCaller initWithPlayer:interval:queue:block:]

I am getting the error within the app
" Assertion failure in -[AVPlayerPeriodicCaller initWithPlayer:interval:queue:block:], /SourceCache/EmbeddedAVFoundation/EmbeddedAVFoundation-461.12/Fig/AVPlayer.m:3993
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: CMTIME_COMPARE_INLINE(interval, >, kCMTimeZero)'
Any ideas why this may be happening?
I am limited to use AvPlayer only because I need to stream large file .AVaudioplayer is not an option. So I have also implemented a scrubber slider. My implementation of AVplayer is as follows:
-(void)playAudioForFile:(NSString *)fileName{
[self.suraPlayer removeTimeObserver:playbackObserver];
[self.suraPlayer removeTimeObserver:mTimeObserver];
self.suraPlayer = nil;
NSURL* url = [self getURLStringForFileName:fileName];
AVPlayer *player = [[AVPlayer alloc]initWithURL:url];
self.suraPlayer = player;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[suraPlayer currentItem]];
[self.suraPlayer play];
[self.suraPlayer addObserver:self forKeyPath:#"status" options:0 context:nil];
[self initScrubberTimer];
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(updateProgress) userInfo:nil repeats:YES];
//[self metadataForAsset:self.suraPlayer.currentItem.asset];
}
-(void)updateProgress {
// NSLog(#"self.mScrubber.value %f", self.mScrubber.value);
//THIS IS THE LINE WHERE THE DEBUGGER SOMETIME STUMPS
float duration = CMTimeGetSeconds(self.suraPlayer.currentItem.duration);
if (self.suraPlayer.currentItem.status==AVPlayerStatusReadyToPlay )
[self.mScrubber setMaximumValue:duration];
self.mScrubber.value = CMTimeGetSeconds(self.suraPlayer.currentTime);
int seconds = self.mScrubber.value, minutes = seconds/60,hours = minutes/60;
int secondsRemain = self.mScrubber.maximumValue - seconds,minutesRemain = secondsRemain/60,hoursRemain = minutesRemain/60;
seconds = seconds-minutes*60;
minutes = minutes-hours*60;
secondsRemain = secondsRemain - minutesRemain*60;
minutesRemain = minutesRemain - hoursRemain*60;
NSString *hourStr,*minuteStr,*secondStr,*hourStrRemain,*minuteStrRemain,*secondStrRemain;
hourStr = hours > 9 ? [NSString stringWithFormat:#"%d",hours] : [NSString stringWithFormat:#"0%d",hours];
minuteStr = minutes > 9 ? [NSString stringWithFormat:#"%d",minutes] : [NSString stringWithFormat:#"0%d",minutes];
secondStr = seconds > 9 ? [NSString stringWithFormat:#"%d",seconds] : [NSString stringWithFormat:#"0%d",seconds];
hourStrRemain = hoursRemain > 9 ? [NSString stringWithFormat:#"%d",hoursRemain] : [NSString stringWithFormat:#"0%d",hoursRemain];
minuteStrRemain = minutesRemain > 9 ? [NSString stringWithFormat:#"%d",minutesRemain] : [NSString stringWithFormat:#"0%d",minutesRemain];
secondStrRemain = secondsRemain > 9 ? [NSString stringWithFormat:#"%d",secondsRemain] : [NSString stringWithFormat:#"0%d",secondsRemain];
self.timePlayerLabel.text = [NSString stringWithFormat:#"%#:%#:%#",hourStr,minuteStr,secondStr];
self.timeRemainingLabel.text = [NSString stringWithFormat:#"-%#:%#:%#",hourStrRemain,minuteStrRemain,secondStrRemain];
}
-(void)initScrubberTimer
{
double interval = .1f;
CMTime playerDuration = self.suraPlayer.currentItem.duration;
if (CMTIME_IS_INVALID(playerDuration))
{
return;
}
double duration = CMTimeGetSeconds(playerDuration);
if (isfinite(duration))
{
CGFloat width = CGRectGetWidth([self.mScrubber bounds]);
interval = 0.5f * duration / width;
}
/* Update the scrubber during normal playback. */
mTimeObserver = [self.suraPlayer addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(interval, NSEC_PER_SEC)
queue:NULL /* If you pass NULL, the main queue is used. */
usingBlock:^(CMTime time)
{
}];
[self syncScrubber];
}
/* Set the scrubber based on the player current time. */
- (void)syncScrubber
{
CMTime playerDuration = self.suraPlayer.currentItem.duration;
if (CMTIME_IS_INVALID(playerDuration))
{
self.mScrubber.minimumValue = 0.0;
return;
}
double duration = CMTimeGetSeconds(playerDuration);
if (isfinite(duration))
{
float minValue = [self.mScrubber minimumValue];
float maxValue = [self.mScrubber maximumValue];
double time = CMTimeGetSeconds([self.suraPlayer currentTime]);
[self.mScrubber setValue:(maxValue - minValue) * time / duration + minValue];
}
}
/* The user is dragging the movie controller thumb to scrub through the movie. */
/* Set the player current time to match the scrubber position. */
- (IBAction)scrub:(id)sender
{
if ([sender isKindOfClass:[UISlider class]])
{
UISlider* slider = sender;
CMTime playerDuration = self.suraPlayer.currentItem.duration;
if (CMTIME_IS_INVALID(playerDuration)) {
return;
}
double duration = CMTimeGetSeconds(playerDuration);
if (isfinite(duration))
{
float minValue = [slider minimumValue];
float maxValue = [slider maximumValue];
float value = [slider value];
double time = duration * (value - minValue) / (maxValue - minValue);
[self.suraPlayer seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC)];
}
}
}
- (IBAction)beginScrubbing:(id)sender {
mRestoreAfterScrubbingRate = [self.suraPlayer rate];
[self.suraPlayer setRate:0.f];
/* Remove previous timer. */
[self removePlayerTimeObserver];
}
/* The user has released the movie thumb control to stop scrubbing through the movie. */
- (IBAction)endScrubbing:(id)sender {
__weak typeof(self) weakSelf = self;
if (!mTimeObserver)
{
CMTime playerDuration = self.suraPlayer.currentItem.duration;
if (CMTIME_IS_INVALID(playerDuration))
{
return;
}
double duration = CMTimeGetSeconds(playerDuration);
if (isfinite(duration))
{
CGFloat width = CGRectGetWidth([self.mScrubber bounds]);
double tolerance = 0.5f * duration / width;
mTimeObserver = [self.suraPlayer addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(tolerance, NSEC_PER_SEC) queue:NULL usingBlock:
^(CMTime time)
{
[weakSelf syncScrubber];
}];
}
}
if (mRestoreAfterScrubbingRate)
{
[self.suraPlayer setRate:mRestoreAfterScrubbingRate];
mRestoreAfterScrubbingRate = 0.f;
}
}
- (BOOL)isScrubbing
{
return mRestoreAfterScrubbingRate != 0.f;
}
-(void)enableScrubber
{
self.mScrubber.enabled = YES;
}
-(void)disableScrubber
{
self.mScrubber.enabled = NO;
}
-(void)removePlayerTimeObserver
{
if (mTimeObserver)
{
[self.suraPlayer removeTimeObserver:mTimeObserver];
mTimeObserver = nil;
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (object == suraPlayer && [keyPath isEqualToString:#"status"]) {
if (suraPlayer.status == AVPlayerStatusFailed) {
NSLog(#"AVPlayer Failed");
} else if (suraPlayer.status == AVPlayerStatusReadyToPlay) {
NSLog(#"AVPlayerStatusReadyToPlay");
// NSLog(#"Common MetaData %#", self.suraPlayer.currentItem.asset.commonMetadata);
AVAsset * asset = self.suraPlayer.currentItem.asset;
[self metadataForAsset:asset];
} else if (suraPlayer.status == AVPlayerItemStatusUnknown) {
NSLog(#"AVPlayer Unknown");
}
}
}
- (NSString *)titleForAsset:(AVAsset *)asset{
NSArray *titles = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata withKey:AVMetadataCommonKeyTitle keySpace:AVMetadataKeySpaceCommon];
AVMetadataItem *title;
NSString * currentTitle;
if(titles.count>0)
{
title= [titles objectAtIndex:0];
currentTitle= [title.value copyWithZone:nil];
}
if (self.suraPlayer.currentItem.asset != asset)
asset = nil;
return currentTitle;
}
- (void)metadataForAsset:(AVAsset *)asset{
self.artWorkImage.image = [UIImage imageNamed:#"Colorful-Burst-iPad-wallpaper-ilikewallpaper_com.jpg"];
NSArray *titles = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata withKey:AVMetadataCommonKeyTitle keySpace:AVMetadataKeySpaceCommon];
NSArray *artists = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata withKey:AVMetadataCommonKeyArtist keySpace:AVMetadataKeySpaceCommon];
NSArray *albumNames = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata withKey:AVMetadataCommonKeyAlbumName keySpace:AVMetadataKeySpaceCommon];
AVMetadataItem *artist;
AVMetadataItem *title;
AVMetadataItem *albumName;
NSString * currentTitle;
NSString * currentArtist;
NSString * currentAlbumName;
if(titles.count>0)
{
title= [titles objectAtIndex:0];
currentTitle= [title.value copyWithZone:nil];
self.audioTitle.text = currentTitle;
}
if(artists.count>0)
{
artist= [artists objectAtIndex:0];
currentArtist = [artist.value copyWithZone:nil];
}
if (albumNames.count>0){
albumName= [albumNames objectAtIndex:0];
currentAlbumName = [albumName.value copyWithZone:nil];
}
NSArray *keys = [NSArray arrayWithObjects:#"commonMetadata", nil];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
NSArray *artworks = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata
withKey:AVMetadataCommonKeyArtwork
keySpace:AVMetadataKeySpaceCommon];
for (AVMetadataItem *item in artworks) {
if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) {
NSDictionary *d = [item.value copyWithZone:nil];
self.artWorkImage.image = [UIImage imageWithData:[d objectForKey:#"data"]];
} else if ([item.keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
self.artWorkImage.image = [UIImage imageWithData:[item.value copyWithZone:nil]];
}
}
}];
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
// code here to play next sound file
if (indexOfSelectedItem<self.filteredAudiosArray.count){
indexOfSelectedItem = indexOfSelectedItem+1;
[self playAudioForFile:[self.filteredAudiosArray objectAtIndex:indexOfSelectedItem]];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:indexOfSelectedItem inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
- (IBAction)sliderValueChangedForDuration:(id)sender {
if ([sender isKindOfClass:[UISlider class]])
{
UISlider* slider = sender;
CMTime playerDuration = self.suraPlayer.currentItem.duration;
if (CMTIME_IS_INVALID(playerDuration)) {
return;
}
double duration = CMTimeGetSeconds(playerDuration);
if (isfinite(duration))
{
float minValue = [slider minimumValue];
float maxValue = [slider maximumValue];
float value = [slider value];
double time = duration * (value - minValue) / (maxValue - minValue);
[self.suraPlayer seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC)];
}
}
}
Late to the game but in case someone else runs into this, I had this issue because of the way I was setting the time interval to the periodic observer.
Changing to:
CMTime interval = CMTimeMake(33, 1000);
Made things work for me.

Resources