AVPlayer seekToTime not correct With UISlider - ios

- (void)viewDidLoad {
[super viewDidLoad];
self.slider.maximumValue = 30;
}
- (void)slide:(UISlider *)slider {
NSLog(#"....progress slider value:%d", (int)slider.value);
CMTime showingTime = CMTimeMakeWithSeconds((int)slider.value, 1);
[self.player seekToTime:showingTime];
}
Everytime I slide the UISlider, The console log the right number as I slided. like 6 seconds. But the slider would jump back to 0; If I slide it to value bigger than 10, like 14, It would slide back to 10.
Can anybody help me ? Thanks

Try this.
You gotta init a CMTime object based on UISlider's value at first. Then you call AVPlayer object's seek to time method. Pass the CMTime object as parameter.
CMTime showingTime = CMTimeMake(slider.value *1000, 1000);
[self.player seekToTime:showingTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];

Related

Issue while scrubbing video using AVPlayer

I am reading video from local using AVPlayer. When I scrub through the video, sometimes I am getting pixelated video for few second or green screen appears for a while.
Following is my code for scrubbing:
- (void)playerControlView:(PlayerControlView *)playerControlView beganScrubbingAtPercent:(float)timePercent
{
if (_avPlayer.rate > 0) {
_resumePlayAfterScrub = YES;
} else {
_resumePlayAfterScrub = NO;
}
}
- (void)playerControlView:(PlayerControlView *)playerControlView scrubbedToPercent:(float)timePercent
{
if (_avPlayer.rate > 0) {
[_avPlayer pause];
[_loadingView startAnimating];
}
float durationSeconds = CMTimeGetSeconds(_avPlayerItem.duration);
CMTime cmTime = CMTimeMakeWithSeconds(durationSeconds * timePercent, NSEC_PER_SEC);
[_avPlayer seekToTime:cmTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
[_hideControlsTimer invalidate];
}
- (void)playerControlView:(PlayerControlView *)playerControlView finishedScrubbingAtPercent:(float)timePercent
{
if (_resumePlayAfterScrub) {
[_avPlayer play];
}
[self resetCurrentEventWithCopy:YES];
}
what can I optimize on above code?
It could be a case wherein the gap between consecutive IDR frames is high so when you seek, it takes a while to hit the closest IDR frame and clear picture buffer. Till the player hits an IDR frame, it will not be able to decode the stream properly which will result in you seeing green screens and pixelations.

Make time interval calculation in -[SKScene update:] account for "paused" status

When I pause the game and wait some time, then resume the game, an alien ship is immediately added. How do I make it so that the 'timer' doesn't continue once the game is paused?
My Code:
#implementation GamePlayScene
-(void) didMoveToView:(SKView *)view {
...
self.addAlienTimeInterval = [Util randomWithMin:10 max:25];
self.timeSinceLastAlien = 0;
...
}
-(void) update:(NSTimeInterval)currentTime{
if (!_isGamePaused){
if (self.lastUpdateTimeInterval){
self.timeSinceLastAlien += currentTime - self.lastUpdateTimeInterval;
}
if (self.timeSinceLastAlien > self.addAlienTimeInterval && !self.gameOver){
[self addAlienShip];
self.timeSinceLastAlien = 0;
}
self.lastUpdateTimeInterval = currentTime;
...
}
}
#end
Your update: process isn't assigning lastUpdateTimeInterval while the "paused" condition is true, so when you resume, timeSinceLastAlien is augmented based on the lastUpdateTimeInterval that was seen way back when you first hit the pause button.
You need to keep updating the lastUpdateTimeInterval property while _isGamePaused is true in order for the interval currentTime - self.lastUpdateTimeInterval to reflect un-paused game time.

In AVAudioplayer multiple songs play at once.how to fix single song play at a time

I am trying to create AVAudioplayer programatically.The audio player successfully playing.But i have some issues.I am displaying 12 audio items in UITableview.If i click first item that should navigate audio player view controller.And i click the play button the song will play.If i click back button the song play continuously.But if i click again same item the Audio view controller the view will display initial state like progress bar should not move and current time and song duration are empty.but song play continuously.
And another issue is there. If i click first item that corresponding song will play.I should not click any pass button.i click back button and click second item.if i click second item the audio view controller will display.I click play button the song display.In background first song play and second song also play.This is my second issue.how to solve these two issues.please help me any body.
-(void)playOrPauseButtonPressed:(id)sender
{
if(playing==NO)
{
[playButton setBackgroundImage:[UIImage imageNamed:#"Pause.png"] forState:UIControlStateNormal];
// Here Pause.png is a image showing Pause Button.
NSError *err=nil;
AVAudioSession *audioSession=[AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
NSLog(#"%# %d",urlsArray,selectedIndex);
NSString *sourcePath=[urlsArray objectAtIndex:selectedIndex];
NSData *objectData=[NSData dataWithContentsOfURL:[NSURL URLWithString:sourcePath]];
audioPlayer = [[AVAudioPlayer alloc] initWithData:objectData error:&err];
audioPlayer.numberOfLoops = 0;
[audioPlayer prepareToPlay];
audioPlayer.delegate=self;
[audioPlayer play];
playing=YES;
}
else if (playing==YES)
{
[playButton setBackgroundImage:[UIImage imageNamed:#"play.png"] forState:UIControlStateNormal];
[audioPlayer pause];
playing=NO;
}
if (self.audioPlayer)
{
[self updateViewForPlayerInfo];
[self updateViewForPlayerState];
[self.audioPlayer setDelegate:self];
}
}
-(void)updateViewForPlayerInfo
{
self.songDuration.text = [NSString stringWithFormat:#"%d:%02d", (int)self.audioPlayer.duration / 60, (int)self.audioPlayer.duration % 60, nil];
NSLog(#"%f", self.audioPlayer.duration);
self.progressBar.maximumValue = self.audioPlayer.duration;
self.volumeSlider.value = self.audioPlayer.volume;
}
-(void)updateViewForPlayerState
{
[self updateCurrentTime];
if (self.updatedTimer)
{
[self.updatedTimer invalidate];
}
if (self.audioPlayer.playing)
{
self.updatedTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(updateCurrentTime) userInfo:self.audioPlayer repeats:YES];
}
}
-(void)updateCurrentTime
{
//NSLog(#"self.audioPlayer.currentTime = %f", self.audioPlayer.currentTime);
self.currentTime.text = [NSString stringWithFormat:#"%d:%02d", (int)self.audioPlayer.currentTime / 60, (int)self.audioPlayer.currentTime % 60, nil];
self.progressBar.value = self.audioPlayer.currentTime;
}
-(void)volumeSliderMoved:(UISlider *)sender
{
self.audioPlayer.volume=[sender value];
}
I think the problem is here
audioPlayer = [[AVAudioPlayer alloc] initWithData:objectData error:&err];
Each time you are creating the audioPlayer object. So my suggestion is check for audioPlayer before creating the object like this
if(audioPlayer)
{
audioPlayer.delegate=nil;
audioPlayer=nil;
}
Then create the object.
In short what's happening is the following:
you click on the table cell
you create an audio player view controller
You play the audio - your audio player itself is retained for some reason (Unclear why - are you using ARC?)
You click the back button - the Audio player view controller is released
you click the table cell again - and create a new audio player view controller with it's own player!
Basically - if you want the previous song continue playing even when your exiting the Audio player view controller - and to have only a single player for the audio - I would hold it in a singleton class that will manage the player , or as a property of the app delegate.
This way - when you enter the Audio Player view controller - It will check the following:
If a player doesn't exist - create it and assign it the item - ready to play
If a player exists but it's the same item - just update the controls to show it's current playing status.
If a player exists but it's another item - either don't update the controls - and when the user hits play - replace the item in the player and start updating the controls - or stop the song as you display the Audio player view controller. (Up to you - I don't know the design of your app obviously)
In working with AVAudioPlayer, I use a property to keep up with the player. Based on looking at your code it isn't clear to me that you are keeping the player around, or that you aren't instantiating a new instance every time. Based on your description, it sounds like you ARE instantiating a new instance - don't think you want that.
So in your .h file, something like this:
#property (nonatomic, strong) AVAudioPlayer *myAVAudioPlayer;
and then the corresponding #synthesize in your .m file.
When you want to move from one song to the next, simply stop playing current - then reload a new URL using an init for your self.myAVAudioPlayer property. You MUST then call the prepareToPlay method again for the AVAudioPlayer option.
You probably will make it easier on yourself by not managing the variables regarding playing or not (it looked like you were doing that from your post). Use your property and check by accessing the setter/getter for the property for state of the player:
if(!self.myAVAudioPlayer)
{
// allocate and initialize using the code you have
[self.myAVAudioPlayer prepareToPlay];
} else
if ([self.myAVAudioPlayer isPlaying])
{
//do whatever you need for isPlaying - looks like pause
} else
if (![self.myAVAudioPlayer isPlaying])
{
// do whatever you need to do if the player isn't playing when the button is pressed
// at this point you will likely just re-init with a new URL and reset other items
// don't forget to call -prepareToPlay again
}
Here is a link to another approach which takes advantage of AVPlayerItem --> click https://stackoverflow.com/a/22254588/3563814

Objc (IOS) , simple free fall animation gone bad

I'm currently taking my first shaky steps in ios development. I'm trying to animate a free falling ball. I have a button connected to an IBAction, and and UIImageView containing an image of my ball.
Inside my action, I have a while loop that's timed using NSTimeInterval, and based on the time it takes it calculates a new position until the ball reaches 'the ground'(Yes, I realize this is probably the least optimal way to do it. But I'm having a hard time grasping the syntax (and therein my problem probably lays), the optimisation will have to come later). From what I can understand, NSTimeInterval returns the elapsed time in seconds, so even though it will increment incredibly small steps, it should work. But I may have a serious case of brain fart.
So far so good. But when I tap the button, the ball moves straight from it's starting point to it's finishing point without an animation.
-(IBAction)doshit:(id)sender{
int G = 10;
CGPoint center = [myImage center];
NSDate *startTime = [NSDate date];
NSTimeInterval T;
T = fabs([startTime timeIntervalSinceNow]);
while (center.y<480)
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:T];
center.y=center.y+((G*T*T)/2);
[myImage setCenter:center];
T = fabs([startTime timeIntervalSinceNow]);
[UIView commitAnimations];
}
}
I welcome all suggestions! =)
One way to do it is to use a CADisplayLink to provide the timing -- it is tied to the display refresh rate of 1/60 of a second. If you 're using auto layout (which is on by default now), it is better to animate a constraint, rather then set a frame. So, this example uses a button at the top of the screen, whose constraint to the top of the view is connected to the IBOutlet topCon.
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface ViewController ()
#property (nonatomic, strong) CADisplayLink *displayLink;
#property (weak,nonatomic) IBOutlet NSLayoutConstraint *topCon;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelector:#selector(startDisplayLink) withObject:nil afterDelay:2];
}
- (void)startDisplayLink {
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink {
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink {
static BOOL first = YES;
static double startTime = 0;
if (first) {
startTime = displayLink.timestamp;
}
first = NO;
double T = (double)displayLink.timestamp - startTime;
self.topCon.constant += ((10 * T * T)/2);
if (self.topCon.constant > 420) {
[self stopDisplayLink];
}
}
As Carl notes, you cannot perform animations by repeatedly changing things in the middle of a method call. See What is the most robust way to force a UIView to redraw? for more discussion on that.
As you may suspect, not only is this non-optimal, it actively fights iOS. iOS has many easy-to-use techniques for performing smooth animations without resorting to timers of any kind. Here is one simple approach:
- (IBAction)dropAnimate:(id)sender {
[UIView animateWithDuration:3 animations:^{
self.circleView.center = CGPointMake(100, 300);
}];
}
In the animations block, you change the thing you want to change to the final value (myImage.center in your case). UIKit will sample the current value and the final value, and figure out a path to get you there in the time you requested. For a full, runnable example with a few other features (like chained animations), see the example code from iOS:PTL Chapter 9.
The above code will use the default timing function. Once you understand that, you can move on to customizing the timing function. See Animation Pacing in the Animation Types and Timing Programming Guide. Also How to create custom easing function with Core Animation? and Parametric acceleration curves in Core Animation. But I would get your head around simple, default animations first.

Execute a piece of code for few seconds/minutes with the button?

I just want to run a certain method for 20 seconds when user presses start button and stop it by it's own rather than using a button to do it. But it should also trigger when ever the user press start button after 20 seconds in next turn.
How can i use NStimer to implement this?
Sorry for not posting any codes
Thanks in advance!
// Assume ARC - otherwise do the proper retains/releases
{
NSTimer *timer; // keep a reference around so you can cancel the timer if you need to
NSDate *timerDate;
}
- (IBAction)buttonPressed:(id)sender
{
// disable button til the time has passed
[sender setEnabled:NO];
// make sure its diabled til we're done here
timerDate = [NSDate date];
NSTimeInterval interval = 1; // 1 gets you a callback every second. If you just wnat to wait 20 seconds change it
// schedule it
timer = [NSTimer ]scheduledTimerWithTimeInterval:time target:self selector:#selector(myTimeRoutine:) userInfo:nil repeats:YES];
}
- (void)myTimeRoutine:(NSTimer *)timer
{
// do something ...
NSTimeInterval interval = -[timerDate timeIntervalSinceNow]; // return value will be negative, so change sign
if(interval >= 20) {
// I'm done now
myButton.enabled = YES; // so it can be tapped again
[timer cancel]; // dont want to get any more messages
timer = nil; // ARC way to release it
timerDate = nil;
}
}

Resources