I am using MVMoviePlayer to play videos in the app. Right now, a black screen comes after taping the play button and the video starts playing. But, the black screen is casing some discofort from the user end point of view. So, i want to start the video from a paused state.
In order to do this, i thought of putting the player to paused state before playing it..
Is there a way to do this???
You can hide your MPMoviePlayer until that annoying black flicker is gone.
To ensure that the black flicker is gone, you can check if the MPMoviePlayer's loadState is 3 ( which means MPMovieLoadStatePlayable | MPMovieLoadStatePlaythroughOK ) and playbackState is 1 (which means MPMoviePlaybackStatePlaying)
First hide your MPMoviePlayer:
yourMPMoviePlayer.view.hidden = YES;
Just add an observer to be notified when loadState changes:
[NSNotificationCenter.defaultCenter addObserver:self
selector:#selector(loadStateChanged:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
And make your MPMoviePlayer visible again when you are notified and conditions are met:
- (void)loadStateChanged:(NSNotification *)sentNotification
{
if (player.loadState == (MPMovieLoadStatePlaythroughOK | MPMovieLoadStatePlayable) && player.playbackState == MPMoviePlaybackStatePlaying)
yourMPMoviePlayer.view.hidden = NO;
}
Related
I have an AVPlayer object that I am using to play a video. When the user taps a button, the video is swapped out for a different video, which continues playing. Just using replaceCurrentItem(with:) is resulting in a few tenths of a second of a black frame appearing on my AVPlayerLayer that is displaying the video content.
I have code in place to render an image at the current frame before the AVPlayerItem is swapped out, to bridge the gap before the new AVPlayerItem has a frame ready to display, but the black frame is blocking this image from view. Is there any way to control what the AVPlayerLayer will render before it has actual video data to display?
Alternatively, is there a way to be notified that the AVPlayerLayer (or the AVPlayerItem has actually begun displaying video data? Observing for when the state of the item becomes .readyToPlay triggers too early, hiding the image at that point still leaves the black frame visible.
I had the same flash problem with my macOS app. I resolved it by renewing AVPlayerLayer each time I play a new file.
I have an AVPlayer subclass. The function below removes AVPlayerLayer from its super layer and add a new layer to the view.
class MyVideoPlayer: AVPlayer {
func addLayer(view: NSView) {
view.layer!.sublayers?
.filter { $0 is AVPlayerLayer }
.forEach { $0.removeFromSuperlayer() }
let layer = AVPlayerLayer.init(player: self)
layer.videoGravity = AVLayerVideoGravity.resize
layer.frame = view.bounds
view.layer!.addSublayer(layer)
}
}
In my ViewController, I call this before I play a video content.
myVideoPlayer.addLayer(view: self.view)
Here I have a method that adds the video layer to the UIView that I have setup in IB:
-(void)loadPlayer{
self.asset = [AVAsset assetWithURL:url];
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:self.asset];
self.player = [AVPlayer playerWithPlayerItem:item];
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
self.playerLayer.contentsGravity = AVLayerVideoGravityResizeAspect;
self.player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
item.videoComposition = [AVVideoComposition videoCompositionWithPropertiesOfAsset:self.asset];
[self.player pause];
[self.player addObserver:self forKeyPath:#"status" options:0 context:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[self.videoLayer.layer addSublayer:self.playerLayer];
});
}
The above method gets called in the viewDidAppear of the View Controller, so each time the current View Controller loads, the video should start to play.
Here I check for the player status and play the asset if ready:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if (object == self.player && [keyPath isEqualToString:#"status"]) {
if (self.player.status == AVPlayerStatusReadyToPlay) {
[self.player play];
}
}
}
The problem is that the view did appear, the video starts playing, but only the audio, not video. All I get is black screen in the place of video. Audio is playing back fine, but it does not seem to render the video frames for some reason unknown.
This happens only on iOS 10 devices, the same code when run on a iOS 9 devices, works like a charm. The video shows up as expected.
Is there anything that is changed in the AVFoundation framework for iOS 10 in terms of AVPlayer or so? Or is there anything that I am missing here, coz iOS 9 plays it good.
If anyone has faced this issue, and if you have solved it, please post your solution. It would be helpful.
Thanks.
THE SOLUTION
Thanks for your replies! I just found that, for some reason in iOS 10 the viewDidLayoutSubviews did not get called as it was in iOS 9. Actually I was setting the player layer's frame in the viewDidLayoutSubviews method. Since it didn't get called, I was not able to see the player on screen. What I did was, set the player layer's frame in the loadPlayer method above. And it works fine. Hope this helps someone.
This is the line of code that I moved from viewDidLayoutSubviews to my loadPlayer method and it worked:
self.playerLayer.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
You need to retain your instance of AVPlayerItem i.e. as a property or an instance variable. The reference to the movie player is lost if you do not retain it.
Even some time this rubish happens in ARC as well.Please make it property.
In my case this problem was because i started playing simultaneously with UIView.animate with view.layoutIfNeeded() in animation block.
The fix for that is to not animate layout changes when not needed and not animate when starting playing video.
The solutions on this page did not work for me.
I found another solution to this problem. In my case the video was being played in black but the sound was ok.
I discovered that changing the time slider to scrub the video would make the video appear.
So, my solution was to make the video go the the last frame, then to the first before playing:
Float64 duration = [self.player duration];
[self.player goToTime:duration];
[self.player goToTime:0.0f];
The black color is background color of the AVPlayerViewController. so change the background color of the AVPlayerViewController as superview color. So the black flash will not be visible.
self.view.backgroundColor =[UIColor whiteColor];
avMoviePlayer.view.backgroundColor=[UIColor whiteColor];
[self.view addSubview:avMoviePlayer.view];
Thanks for your replies! I just found that, for some reason in iOS 10 the viewDidLayoutSubviews did not get called as it was in iOS 9. Actually I was setting the player layer's frame in the viewDidLayoutSubviews method. Since it didn't get called, I was not able to see the player on screen. What I did was, set the player layer's frame in the loadPlayer method above.And it works fine. Thanks! Hope this helps someone.
EDIT
This is the line of code that I moved from viewDidLayoutSubviews to my loadPlayer method and it worked:
self.playerLayer.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
here is what I recently studied for AVPlayer
you can try it
XYAVMoviePlayer
enjoy your day :)
I have an instance of AVAudioPlayer that plays a local audio file from disk. The playback progress is reflected with a UIProgressView. I'm trying to replace that with a UISlider. To accomplish the task, the slider will:
move in response to timer event to indicate the current playback position
respond to manual touch to allow for video scrubbing
I'm not sure how to make the UISlider smart enough to know that when human touch began, it should stop responding to timer events, and when the touch is released, it should resume the audio player at a new position and start updating with timer events again.
Which "Send event" outlets in storyboard would I use to create the behavior
above?
Currently I have a generic "value changed" callback.
- (IBAction)sliderChanged:(id)sender {
if(self.backgroundMusicPlayer == nil)
{
NSError* error = nil;
NSData* data = [self.audioFile data];
self.backgroundMusicPlayer = [[AVAudioPlayer alloc]initWithData:data error:&error];
[self.backgroundMusicPlayer prepareToPlay];
}
self.backgroundMusicPlayer.currentTime = self.slider.value;
[self.backgroundMusicPlayer play];
}
What you have is right. To make your code respond to the user adjusting the slider, use the Value Changed event. If you want to be notified that the slider has been touched, use the Touch Down event, or add a gesture recognizer to the slider.
I would keep things simple and just check the isTracking property of UISlider which it inherits from UIControl.
I want to show a "paused" SKLabelNode when someone clicks on the screen and therefore pauses the sprite kit game.
So I have in touchesBegan ->
[self.pausedLabel setHidden:!self.pausedLabel.hidden];
[self.scene.view setPaused:!self.scene.view.paused];
The Game is paused correctly but the SKLabelNode is not shown (scene not rendered before paused?!)
If I add a NSTimer for pausing the scene the label is shown, but then the game continues for that timer-time.
Does anyone have a better solution for this?
Thanks in advance
I would use SKAction for this. You can use +runBlock: to add the code related to hiding the label, and then use the -runAction method with the completion handler to pause the scene. The runBlock: method may return immediately, but this way, the screen manages to update before the scene is paused.
SKAction *action = [SKAction runBlock:^{
[self.pausedLabel setHidden:!self.pausedLabel.hidden];
}];
[self.pausedLabel runAction:action completion:^{
[self.scene.view setPaused:!self.scene.paused];
}];
Just use a state ivar to determine if the scene should update its content or not.
When you click the button set this state to PAUSE and in your scene frame update loop test the state.
if (_state != PAUSE) {// Use enum for the state var
// Update scene objects
}
when your button is clicked:
Add pause label to scene
Set state to PAUSE
The benefits of this approach is that it allows you to decide exactly what will happen on pause (opposing to pausing the whole scene). You can animate a cool background while in pause or do anything you'd like as this is totally in your control
in my app i want to add feature . swipe on video take the video to next 2 sec from current playback time. but my player not doing this accurate and exact according to time which i pass to function it jump the current play back time at any where else. i already search a lot and found may be this is due to key-frame of my video i think i need to increase key-frame of video if yes
then (1) what is the best way to increase key-frame of video?
if this issue can solve without increasing key-frame then
(2) how can i do this ?
here is my code
-(void) handleOneFingerSwipeRight
{
if(labelTimer.isValid)
[labelTimer invalidate];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(signleTap) object:nil];
[videoPlayer pause];
double d = 0;
d = floor([videoPlayer currentPlaybackTime]);
d = d+2.0;
NSLog(#"current time %f",floor([videoPlayer currentPlaybackTime]));
NSLog(#"new time %f",d);
[videoPlayer setCurrentPlaybackTime:d];
[videoPlayer play];
[self setLable:[NSString stringWithFormat:#"Adaptive Forward %f",d]];
}