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 :)
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)
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.
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]];
}
I am using AVPlayer, and its working good.
I put slider and it is progression according to player duration.
But i want if user drags slider then play time changes accordingly. i.e. Song forwards or backwards according to slider position.
I have tried
-(IBAction)sliderChange:(id)sender{
[player pause];
CMTime t = CMTimeMake(slider.value,1);
[player seekToTime:t];
[player play];
}
But it again takes slider to starting point. Any help would be much appreciated.
EDIT (WORKING WITH BELOW LINK)
AVPlayer Video SeekToTime
-(IBAction) valueChangeSliderTimer:(id)sender{
[avplayer pause];
isPlaying = FALSE;
[btnPauseAndPlay setTitle:#"Play" forState:UIControlStateNormal];
float timeInSecond = sliderTimer.value;
timeInSecond *= 1000;
CMTime cmTime = CMTimeMake(timeInSecond, 1000);
[avplayer seekToTime:cmTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
}
Edited Code .
And try this link as well :
Try this link
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;
}