it is since a lot of time that I´m experiencing a weird seek slider behavior when seeking forward or backwards on streamed movies using MPMoviePlayerController.
The symptoms are:
. You begin the seek gesture on slider and the slider button follows the finger.
. When slider is released, it jumps back to the gesture starting point and slingers to the point where the gesture was finished.
. Then playback continues from the selected movie time.
This is of course visually annoying, although the functionality is OK.
This is even worst on iOS 7.
Thanks for the welcome help.
Perhaps a bit late to answer this, but since I stumbled upon this on a Google search, maybe this will be of help to someone else.
I ran into this behavior with AVPlayer and found the reason to be because of a time observer that kept running during scrubbing. The time observer was in charge of updating the scrubber position based on what the current playback time was. So, once the gesture finished moving the slider, the time observer would move the slider knob back to where it thought playback was before the scrubbing action updated the play clock.
My solution was to temporarily remove this time observer right when scrubbing began, and recreate it once scrubbing ended.
When scrubbing begins:
if (scrubberTimeObserver) {
[player removeTimeObserver:scrubberTimeObserver];
scrubberTimeObserver = nil;
}
Note the you will want to know the name of your time observer so you make sure you're removing the correct one.
When scrubbing ends:
scrubberTimeObserver = [player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(interval, NSEC_PER_SEC)
queue:dispatch_get_main_queue()
usingBlock:^(CMTime time) {
[weakSelf syncScrubber];
}
];
Related
So here is the story.
I have a timer and timer.setEventHandler renders animation (something like adding 10 circles in the UI every 125ms) when I tap a button. The animation runs smoothly and so far so good.
Now I want to add sound (the sound plays 10mm like a beep but from a mp3 file) every time when the circle is added. I tried 2 approaches:
AudioServicesPlaySystemSound
The Animation is smooth, but the sound is not played evenly. At least the sound playing doesn't lag the UI whatsoever.
Use AVAudioPlay
I prepared the AVAudioPlay instance in a singleton class and every time the animation logic calls play() in a DispatchedQueue to make sure the sound is played in a separate thread. The first time when the button is pressed, the animation is lagged. However, when I press the button again shortly (within 2 seconds) when the animation is done, the animation and sound will be played perfectly. However, if I wait for longer time like 10 seconds since the last sound was played, the animation will be lagged and sound is kind of bursting out.
I am wondering if iOS (I am running iOS 10 on iPad Air 2) actually put AVAudioPlay instances on sleep when it is not active. Unless I press the button within the short time (2 seconds) since the last sound was played, the AVAudioPlay thread will be active all the time.
Any idea?
I figured it out.
Use a timer to play sound at 0 volumn every 1 second forever to keep the audio session alive. It sounds stupid but it works like a charm!
Hey I have a couple of AVAudioPlayers containing one sound each. If I press the same button a couple of times, it should repeat the sound from the beginning. If I press another button afterwards, the running sound shall be stopped in order to "make room" for the new one.
The code I am using for that:
-(void) plays:(int)p{ // p is the index of the sound being triggered
if([players[p] isPlaying])
{ // setting the time back to 0 makes
players[p].currentTime = 0.0; // the player automatically play again
}
else
{
[players[p] play]; // if not playing, start playing
}
if(last!=p)
{ // if the last sound is different from the current
[players[last] stop]; // stop the last one
players[last].currentTime = 0.0;} // put its position back to 0
last=p; // set the 'last' variable
}
However, hitting the same button again ends up in a little delay (maybe 20ms) in which no sound is heard. This is the time, the AVAudioPlayer seems to need to "rewind" the track in order to play it again. One Idea to get around this would be to create multiple objects of AVAudioPlayer for each sound but that'd make some awful code! Any ideas on how to make this process quicker?
Thanks, Alex
EDIT: playing 2 different sounds works perfectly fine, I can't hear any delay in between them as I prepareToPlay all the sounds beforehand.
I know how to eliminate the 20ms gap, but first consider if you want to.
Imagine if you jumped immediately from the mid-point of the sound file to the beginning with no gap at all. Better yet, download Audacity and hear how it sounds. Because of the discontinuity, you are going to get an unpleasant crackling or pop sound. Perhaps that 1 fiftieth of a second of silence actually sounds better than immediately restarting.
If you want an uninterrupted audio stream, you're going to have to put away the easy AVAudioPlayer interface and build an AUGraph. Of course, this means learning a complex audio interface and learning all about audio formats. And then you have to figure out what data you're going to stuff into your audio stream.
How would you make your loop sounds nice? You might try fading out at the touch point, and then fading back in. Or you could search for the zero crossings at the beginning and end of your loop (zero crossings are the place where the value of your sound wave is 0. They happen all the time in a mono output, but might be harder to find if your output is stereo.) In the end will this sound nicer than the 20 ms of silence? Get out Audacity and experiment with looping before you enter the long road of learning the AUGraph interace.
To the same song, how about stopping it, then prepareToPlay and play?
I have a bit of a strange problem. I have a music app that uses the [MPMusicPlayerController iPodMusicPlayer]. Everything is fine, notifications are fired for track changes and changes in playback state.
I have one screen where the user needs to review one single song, I don't want him to go on to the next song in his queue. Since there is no delegate method for when a track WILL change (only DID change), to prevent the music player from continuing to the next track I use a new [MPMusicPlayerController applicationMusicPlayer], give it iPodMusicPlayer's currently playing song and all is well. No new tracks to continue to, and I'm not touching the original iPodMusicPlayer queue so in theory, when I close this screen and use the iPodMusicPlayer again, all should be perfectly fine.
However, when the user is done on this screen and closes it, iPodMusicPlayer is now suddenly broken, notifications are not called and when I put the app to the background, music stops playing, causing me to believe that iPodMusicPlayer is now actually applicationMusicPlayer.
Okay so my question is basically: I need a way to prevent the music player to continue on to the next track in the queue. Switching to applicationMusicPlayer with one track seems to break stuff, as explained above. What's the best solution?
EDIT: because this might be a bit difficult to understand, I created a small project to show the problem: https://github.com/kevinrenskers/MPMusicPlayerControllerTest. Open the app while music is playing, see that the play button behaves correctly. Now open the popup, close it again and the play button is broken.
I found a solution to my problem: set the repeatMode to MPMusicRepeatModeOne and then catch the MPMusicPlayerControllerNowPlayingItemDidChangeNotification notification. You can stop the playback and you never continue to the next track. Once I'm done with the second screen I reset the repeatMode to the original value.
I've written custom control which allows the user to change the currentPlaybackTime of a MPMoviePlayerController by clicking or dragging on a custom UIButton that displays an image of some musical notation. The code uses the x position of the touch event to update currentPlaybackTime and it all works fine. I also have a CALayer (a vertical red line) that shows the user where they have touched on the control.
What I'd like to do now is have the red line's (CALayer object) position update if the user changes the currentPlaybackTime property of the movie player using the scrubber (slider) control.
Obviously currentPlaybackTime is always updating as the movie plays, but is there a way to detect if the user interferes with this by skipping forward or backwards?
Many thanks.
You could use Key-Value-Observing to track changes of currentPlaybackTime in your MPMoviePlayerController, but that might generate a lot of events and thus negatively affect your performance. Maybe you can also use KVO with the playbackState property of your MPMoviePlayerController, I believe it changes when the user starts seeking.
Another alternative is that you just setup a repeating timer that updates quite frequently, and everytime it fires you check the new value of currentPlaybackTime. But that might look choppy and it could also affect performance.
I have a weird issue with the MPMoviePlayerController in iOS.
I have a MPMoviePlayerController with control style none.
Added my own UISlider to seek forward/backward in the movie.
Set up notifications to see a change in the playback state.
After setting player.currentPlaybackTime to a new value the playbackstate goes to MPMoviePlaybackStateSeekingForward or MPMoviePlaybackStateSeekingBackward and stays in that state.
I would expect that when the seeking stops (new position found), and the player continues to play normally the playbackstate would go back to MPMoviePlaybachStatePlaying but it doesn't.
Is this normal behaviour or am I missing something?
The problem is with UISlider's property continuous. Set it to no and you will be notified when the state will change from Forward/Backward to Playing.
self.playerControlsView.slider.continuous = NO;
The issue i guess is that when the continuous property is set to YES. The slider will continue the action of sliding the user is providing it and not trigger the state change. And hence the state remains forward/backward. I am not very sure about this but the answer is somewhere on these lines.
Hope this helps someone :)