AVPlayer Statement for buffering - ios

I am using the AVPlayer to play a live stream from the Internet, and need to show than the player is buffering :
I am using an NStimer :
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0) target:self selector:#selector(buffering) userInfo:nil repeats:YES];
-(void)buffering {
if(radiosound.rate == 1.0)
[activityIndicator stopAnimating];
else
[activityIndicator startAnimating];
}
For sure rate property is not working properly to show !
Is it an other statement to know if the AVPlayer is buffering ?

You need to look at setting up key-value observers for the loaded times, and use that to figure out if you are waiting for data at the current play point.
Observers are setup using addObserver:self forKeyPath: options: context: method on AVPlayerItem and then in the observeValueForKeyPath: ofObject: change: context: callback you can figure out what times have been loaded compared with where in the item the player is playing.
You won't see the rate variable drop to zero when buffering, as this is the desired playback rate, not the actual rate being achieved.

Use the NSURLConnection class in conjunction with the NSURLConnectionDataDelegate protocol.

Related

AVPlayer play for exact amount of time

Using an AVPlayer I would like to play a mov file for exactly 1 second then pause it.
Currently I'm playing the mov then setting a timer to pause it after 1 second as below. Unfortunately, this does not appear to be exactly accurate and the mov is sometimes playing for slightly shorter or longer than 1 second. Is there a more accurate way of doing this please?
[self.player4 play];
[self performSelector:#selector(pausePlayer4:) withObject:nil afterDelay:1.0];
- (void)pausePlayer4:(NSTimer *)timer
{
[self.player4 pause];
}
Even if you can get an event to fire precisely enough, media playback on iOS devices happens in an entirely different process (a daemon) and there's always latency when doing IPC.
Depending on your needs it might be best to build an AVMutableComposition that plays exactly one second of content from your AVURLAsset, and then assign the composition to your player.
The best way wold be to add a boundary observer to trigger after a second
NSValue *endBoundary = [NSValue valueWithCMTime:CMTimeMakeWithSeconds(1.0, 300)];
[self.player4 addBoundaryTimeObserverForTimes:#[endBoundary]
queue:NULL
usingBlock:^{
[self.player4 stop];
}];
[self.player4 play];

How to set timeout for AVPlayer [duplicate]

I want to play a specified duration within a sound file on IOS. I found a method in AVAudioPlayer that seeks to the begining of the playing (playAtTime:) but i cannot find a direct way to specify an end time before the end of the sound file.
Is there is a way to achieve this?
If you don't need much precision and you want to stick with AVAudioPlayer, this is one option:
- (void)playAtTime:(NSTimeInterval)time withDuration:(NSTimeInterval)duration {
NSTimeInterval shortStartDelay = 0.01;
NSTimeInterval now = player.deviceCurrentTime;
[self.audioPlayer playAtTime:now + shortStartDelay];
self.stopTimer = [NSTimer scheduledTimerWithTimeInterval:shortStartDelay + duration
target:self
selector:#selector(stopPlaying:)
userInfo:nil
repeats:NO];
}
- (void)stopPlaying:(NSTimer *)theTimer {
[self.audioPlayer pause];
}
Bear in mind that stopTimer will fire on the thread's run loop, so there will be some variability in how long the audio plays, depending on what else the app is doing at the time. If you need a higher level of precision, consider using AVPlayer instead of AVAudioPlayer. AVPlayer plays AVPlayerItem objects, which let you specify a forwardPlaybackEndTime.

start playing audio from a background task via AVAudioPlayer in Xcode

I am trying to start playing a sound from a background task via an AVAudioPlayer that is instantiated then, so here's what I've got.
For readability I cut out all user selected values.
- (void)restartHandler {
bg = 0;
bg = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bg];
}];
tim = [NSTimer timerWithTimeInterval:.1 target:self selector:#selector(upd) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:tim forMode:NSRunLoopCommonModes];
}
- (void)upd {
if ([[NSDate date] timeIntervalSinceDate:startDate] >= difference) {
[self playSoundFile];
[tim invalidate];
[[UIApplication sharedApplication] endBackgroundTask:bg];
}
}
- (void)playSoundFile {
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&sessionError];
// new player object
_player = [[AVQueuePlayer alloc] init];
[_player insertItem:[AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:#"Manamana" withExtension:#"m4a"]] afterItem:nil];
[_player setVolume:.7];
[_player play];
NSLog(#"Testing");
}
Explanation: bg, tim, startDate, difference and _player are globally declared variables. I call - (void)restartHandler from a user-fired method and inside it start a 10Hz repeating timer for - (void)upd. When a pre-set difference is reached, - (void)playSoundFile gets called and initiates and starts the player. The testing NSLog at the end of the method gets called.
Now the strange thing is if I call - (void)playSoundFile when the app is active, everything works out just fine, but if I start it from the background task it just won't play any sound.
Edit
So I tried using different threads at runtime and as it seems, if the Player is not instantiated on the Main Thread this same problem will appear.
I also tried using AVPlayer and AVAudioPlayer where the AVAudioPlayer's .playing property did return YES and still no sound was playing.
From what I've learned after writing an player App, it seems that you can not start playing audio when your App is already in background for longer than X seconds, even if you have configured everything right.
To fix it, you have to use background task wisely.
The most important thing is that you must NOT call endBackgroundTask immediately after playSoundFile. Delay it for about 5-10 seconds.
Here is how I managed doing it for iOS6 and iOS7.
1, Add audio UIBackgroundModes in plist.
2, Set audio session category:
3, Create an AVQueuePlayer in the main thread and enqueue an audio asset before the App enter background.
*For continues playing in background (like playing a playlist)*
4, Make sure the audio queue in AVQueuePlayer never become empty, otherwise your App will be suspended when it finishes playing the last asset.
5, If 5 seconds is not enough for you to get the next asset you can employ background task to delay the suspend. When the asset is ready you should call endBackgroundTask after 10 seconds.
include your playSoundFile call in the below, this should always run it in main thread
dispatch_async(dispatch_get_main_queue(), ^{
});
add this if to your - (void) playSoundFile to check if player is created, if not, then create it
if(!_player) {
_player = [[AVQueuePlayer alloc] init];
}
Seems to me as if you do not have the appropriate background mode set?
Also, looks like another user had the same issue and fixed it with an Apple Docs post. Link to Post
You may need to just do the step to set the audio session as active:
[audioSession setActive:YES error:&activationError];
Hope it helps!
If you need to start playing a sound in the background (without a trick such as keeping on playing at volume zero until you need the sound):
Set Background mode Audio in your p.list;
Set AudioSession category to AVAudioSessionCategoryPlayback
Set AudioSession to active
When your backgrounded app becomes running, start a background task before playing the sound (apparently when the remaining background time is less than 10 seconds, the sound is not played)
This now works for me.
What fixed it for me was to start playing some audio just as application quits, so I added 1sec blank audio in applicationWillResignActive in the AppDelegate
func applicationWillResignActive(_ application: UIApplication) {
self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
self.endBackgroundUpdateTask(true)
})
let secSilence = URL(fileURLWithPath: Bundle.main.path(forResource: "1secSilence", ofType: "mp3")!)
do {
audioPlayer = try AVAudioPlayer(contentsOf: secSilence)
}
catch {
print("Error in loading sound file")
}
audioPlayer.prepareToPlay()
audioPlayer.play()
}

Weird behavior running MPMoviePlayerController in viewDidLoad

I'm using the MediaPlayer framework to play a pretty sizeable movie (~200 MB) as soon as my application is launched. When I attempt to play the video in my viewDidLoad, breakpoints indicated that the view was added however the video did not show up on the device.
I then set up a button to confirm that the video worked at all. Running the IBAction from the button showed no problems.
So then I was stumped. And I wondered if it had anything to do with the fact that the video was being called as soon as the application was launched. So I added a NSTimer with a delay of five seconds to the viewDidLoad call. Lo and behold, it worked fine. Can anyone shed any light on to why the video won't play unless there is an initial delay? Code below.
- (void)viewDidLoad {
NSTimer *currentTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(playMovie) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:currentTimer forMode:NSRunLoopCommonModes];
[super viewDidLoad];
}
-(IBAction)playMovie
{
NSString *filepath = [[NSBundle mainBundle] pathForResource:#"Speed" ofType:#"mov"];
NSURL *fileURL = [NSURL fileURLWithPath:filepath];
MPMoviePlayerController *moviePlayerController = [[MPMoviePlayerController alloc] initWithContentURL:fileURL];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlaybackComplete:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayerController];
[self.view addSubview:moviePlayerController.view];
moviePlayerController.fullscreen = YES;
[moviePlayerController play];
}
Try playing the movie in viewDidAppear: instead of viewDidLoad. At the point viewDidLoad is called, the view is not yet in the view hierarchy. You could still create the MPMoviePlayerController in viewDidLoad, just don't play it until the view actually appears.
I think the size of the video has something to do here. I tried doing the same thing with a smaller video and it works fine. It seems the video is loaded first and then played ( see this stack over flow question: MPMoviePlayerController not showing controls until video is loaded). Apparently, the 200 MB video takes 4-5 seconds to preload. Try the code that was'nt working with a video of smaller size. If it works fine, then it's definitely the size problem.
Edit: I found this in MPMoviePlayerController Class Reference "If you know the source type of the movie, setting the value of this property before playback begins can improve the load times for the movie content. If you do not set the source type explicitly before playback, the movie player controller must gather this information, which might delay playback." I notice you haven't declared source type. Add to that the size of the video, and you've got yourself 4-5 seconds of load time.
Also try this before playing [moviePlayerController prepareToPlay];
I had a different experience but moving the code in viewDidAppear: helped my case as well. I have IOS code that segues from one storyboard to another and once the segue is complete - in the viewDidLoad: I was initializing a periodic timer. The code for the timer is below [I was 'calling' startTimer in viewDidLoad:]:
// Schedules a new timer, adds it to the current run loop and waits forever.
- (void) startTimer
{
_timer = [NSTimer scheduledTimerWithTimeInterval: 10.0
target:self
selector:#selector(request)
userInfo:nil
repeats:YES];
// [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
// [[NSRunLoop mainRunLoop] runUntilDate:[NSDate distantFuture]];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
}
What is interesting is that the when I call the startTimer in viewDidLoad: I can never observe the view on the iPhone change to the next controller - I can see the NSLog statement that shows the control came to
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
but the control does not go to the ViewController.
This behavior is independent of the timing of the periodic timer - setting low or high does not impact the problem. The observed problem goes away when I move the startTimer invocation to viewDidAppear:
This is followed by another set of challenge even when I move the timer code to viewDidAppear:. This code of having a regular timer - I have created this so that I can get the RSSI value for the Bluetooth Low Energy Connection. What I have observed is that I stop getting delegate callbacks for the Bluetooth delegate.

Play any audio for given time

I want to play any file for 6 seconds. Also suppose the audio is bigger then 6 sec the application will play only for 6 sec.and if it is less then 6 sec then play continuously. So is there any inbuilt option from any framework?
A Simple Way To Play MP3 Audio Files
Assuming you use an AVAudioPlayer, send a -stop message after 6 seconds:
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:6
target:thePlayer selector:#selector(stop)
userInfo:nil repeats:NO];
(Remember to invalidate the timer if the player stops early.)
If you are using AVAudioPlayer you can make use of an NSTimer as mentioned by #Kenny. Just to add to add to that answer.
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:6
target:thePlayer selector:#selector(stopPlayer)
userInfo:nil repeats:NO];
- (void)stopPlayer
{
[audioPlayer stop];
}
In case the duration of your audio file is less than 6 seconds or your desired limit you should continue to play right? so you must set the numberOfLoops for the audio player instance to continue playback. Here is the reference

Resources