Hi, I replaced MPMoviePlayerController with AVPlayerViewController since MPMoviePlayerController is deprecated.
I'm nearly there, but have one question. My movie start as a view within a view. When playing fullscreen I want it to jump back to NO fullscreen when finished playing. But I don't know how. Here is my code:
- (void)viewDidLoad {
// grab a local URL to our video
NSURL *videoURL = [[NSBundle mainBundle]URLForResource:#"movie" withExtension:#"m4v"];
// create an AVPlayer
AVPlayer *player = [AVPlayer playerWithURL:videoURL];
// create a player view controller
self.controller = [[AVPlayerViewController alloc]init];
controller.player = player;
[player play];
// show the view controller
[self addChildViewController:controller];
[self.view addSubview:controller.view];
controller.view.frame = CGRectMake(0,25, 750, 422);
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:player];
}
With MPMoviePlayer it used to work with this code:
- (void) playerPlaybackDidFinish:(NSNotification*)notification{
// movie finished playing
[moviePlayerController setFullscreen:NO];
}
With what code do I need to replace it??
-(void)itemDidFinishPlaying:(NSNotification *) notification {
// Will be called when AVPlayer finishes playing playerItem
???????????}
Thanks, Meg
#iOS 10 and higher and Swift 4.2 this code are working.
Write this code in your player init methods
if #available(iOS 11.0, *) {
self.playerVC?.exitsFullScreenWhenPlaybackEnds = true
}
NotificationCenter.default.addObserver(self, selector: #selector(self.playerItemDidReachEnd(notification:)), name: .AVPlayerItemDidPlayToEndTime, object:self.playerVC?.player!.currentItem)
This is youy notification delegate
func playerItemDidReachEnd(note:NSNotification){
print("finished")
dismissViewControllerAnimated(true, completion: nil)
}
Related
I have a simple app with an AVPlayer playing M3U8 content. Within the app, I do the following to allow background audio:
NSError *audioError;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&audioError];
if (audioError == nil)
{
[[AVAudioSession sharedInstance] setActive:YES error:nil];
}
When I hit the lock button on my phone, the audio pauses. If I hit the lock button again (to look at the lock screen) I see media controls and I can un-pause the content to hear the audio.
My question is, how do I prevent this auto-pause so it keeps playing when the lock button is first pressed? I tried something like the following:
- (id) init
{
// ...
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(handleEnteredBackground) name: UIApplicationDidEnterBackgroundNotification object: nil];
// ...
}
- (void) handleEnteredBackground
{
// [player play];
[player performSelector:#selector(play) withObject:nil afterDelay:1];
}
But this didn't seem to work.
After some searching I figured out my issue:
https://developer.apple.com/documentation/avfoundation/media_assets_playback_and_editing/creating_a_basic_video_player_ios_and_tvos/playing_audio_from_a_video_asset_in_the_background
If you’re playing audio-only assets, such as MP3 or M4A files, your setup is complete and your app can play background audio. If you need to play the audio portion of a video asset, an additional step is required. If the player’s current item is displaying video on the device, playback of the AVPlayer instance is automatically paused when the app is sent to the background. If you want to continue playing audio, you disconnect the AVPlayer instance from the presentation when entering the background and reconnect it when returning to the foreground.
Their (Swift) example code looks like so:
func applicationDidEnterBackground(_ application: UIApplication) {
// Disconnect the AVPlayer from the presentation when entering background
// If presenting video with AVPlayerViewController
playerViewController.player = nil
// If presenting video with AVPlayerLayer
playerLayer.player = nil
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Reconnect the AVPlayer to the presentation when returning to foreground
// If presenting video with AVPlayerViewController
playerViewController.player = player
// If presenting video with AVPlayerLayer
playerLayer.player = player
}
An Objective-C equivalent would be:
- (void) applicationDidEnterBackground:(UIApplication*)application
{
playerViewController.player = nil;
}
- (void) applicationWillEnterForeground:(UIApplication*)application
{
playerViewController.player = player;
}
Or in my case the player was embedded into a View and was not controlled directly by the app delegate or the view controller, so I used the following:
- (id) init
{
// ...
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(handleEnteredBackground) name: UIApplicationDidEnterBackgroundNotification object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(handleEnteredForeground) name: UIApplicationDidBecomeActiveNotification object: nil];
// ...
}
- (void) handleEnteredBackground
{
controller.player = nil;
}
- (void) handleEnteredForeground
{
controller.player = player;
}
I would like to play an intro video before my game starts on OSX. What is the easiest way to setup an AVPlayer and play the video? I would like the video to play completely before the game can proceed any further.
I have gone through the AVPlayer documentation on the mac developer library but ran into the following problem on my OSX app: AVPlayer does not show video
Please help!
So after the splash screen, Your first view controller can have this:
-(void) playMovieAtURL: (NSURL*) theURL {
MPMoviePlayerController* theMovie =
[[MPMoviePlayerController alloc] initWithContentURL: theURL];
theMovie.scalingMode = MPMovieScalingModeAspectFill;
theMovie.movieControlMode = MPMovieControlModeHidden;
// Register for the playback finished notification
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: #selector(myMovieFinishedCallback:)
name: MPMoviePlayerPlaybackDidFinishNotification
object: theMovie];
// Movie playback is asynchronous, so this method returns immediately.
[theMovie play];
}
// When the movie is done, release the controller.
-(void) myMovieFinishedCallback: (NSNotification*) aNotification
{
MPMoviePlayerController* theMovie = [aNotification object];
[[NSNotificationCenter defaultCenter]
removeObserver: self
name: MPMoviePlayerPlaybackDidFinishNotification
object: theMovie];
// Release the movie instance created in playMovieAtURL:
[theMovie release];
}
More info: https://developer.apple.com/library/ios/documentation/audiovideo/conceptual/multimediapg/UsingVideo/UsingVideo.html
In myMovieFinishedCallback, you can then show the menu of the game.
I hope I helped :)
It will showing black screen with loading activity indicator When i click the video in full screen and touch up the forward or Backward button.
I have using the following code for movie player
NSURL *fURL = [NSURL URLWithString:[self.winnersCircleDictionary objectForKey:#"videoname"]];
self.players=nil;
self.players=[[MPMoviePlayerController alloc]initWithContentURL:fURL];
[self.players setContentURL:fURL];
self.players.scalingMode = MPMovieScalingModeAspectFit;
self.players.repeatMode=MPMovieRepeatModeOne;
[[self.players view] setFrame:_webviewWinnerCircle.frame];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerPlaybackStateChanged:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:self.players];
object:nil];
self.players.shouldAutoplay=NO;
self.players.currentPlaybackTime=0.0;
[scrollViewWinnerCircle addSubview:self.players.view];
Forward and Backward button working fine in iOS 6 and this issue only have in iOS 7.
Thank You.
I just tried your code and got the issue , And solved like this.
Prepared the MoviePlayer like [self.players prepareToPlay]; for playing the selected video after adding the self.player.view as subview to the scrollViewWinnerCircle.
The code is as follows.
self.players = [[MPMoviePlayerController alloc]initWithContentURL:fURL];
[self.players setContentURL:fURL];
self.players.scalingMode = MPMovieScalingModeAspectFit;
self.players.repeatMode = MPMovieRepeatModeOne;
[[self.players view] setFrame:_containerView.frame];
self.players.shouldAutoplay = NO;
self.players.currentPlaybackTime = 0.0;
[_containerView addSubview:self.players.view];
[self.players prepareToPlay]; // Prepares a movie player for playback.
[self.players pause]; // Pause playback of the loaded movie
It was the problem with the source, also didn't specified the source type.
Do like the following, its working for me with the url i used here.
NSURL *fURL = [NSURL URLWithString:#"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"];
self.players = nil;
self.players = [[MPMoviePlayerController alloc]initWithContentURL:fURL];
[self.players setContentURL:fURL];
// Specifying media source type
self.players.movieSourceType = MPMovieSourceTypeStreaming;
It take some time to load initially.
you have to use this code instead of MPMovieplayerController and import these library in you class:
AVKit
AVFoundation
NSURL *videoURL = [NSURL URLWithString:#"https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"];
AVPlayer *player = [AVPlayer playerWithURL:videoURL];
AVPlayerViewController *playerViewController = [AVPlayerViewController new];
playerViewController.player = player;
[player play];
[self presentViewController:playerViewController animated:YES completion:nil];
after using above code you will be able to forward and back forward video.
What is the appropriate point to send the play message to a MPMoviePlayerController instance instantiated in a splitView detail View Controller?
My app is receiving the above console message (with a !) but not crashing...
The app is utilizing MPMoviePlayerController to play a movie from an asset URL
and the responsilble method is called as follows:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self startPlayingVideo:self];
}
It plays the video just fine but the console message is looming...
If I move the method call to viewWillAppear:animate:, the console message does not show up. The issue is now I can only hear audio and do not see the video.
For completeness, here is the called method...
- (void) startPlayingVideo:(id)sender
NSURL *movieURL = [NSURL URLWithString:self.movieURLString];
if (self.moviePlayer != nil){
[self stopPlayingVideo:nil];
}
self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:movieURL ];
if (self.moviePlayer != nil){
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(videoHasFinishedPlaying:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:self.moviePlayer];
self.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
[self.moviePlayer prepareToPlay];
[self.moviePlayer play];
[self.view addSubview:self.moviePlayer.view];
[self.moviePlayer setFullscreen:YES animated:YES];
} else {
NSLog(#"Failed to instantiate the movie player.");
}
}
My original issue stemmed from having the MoviePlayerController as an entirely different viewController (embedded in a detail navigation controller). I redesigned the parent view to include a moviePlayer child view. This solved the issue.
I've looked around but I can't find a delegate protocol for the AVPlayer class. What gives?
I'm using its subclass, AVQueuePlayer, to play an array of AVPlayerItems, each loaded from a URL. Is there any way I can call a method when a song finishes playing? Notably at the end of the queue?
And if that's not possible, is there any way I could call a method when the song STARTS playing, after buffering? I'm trying to get a loading icon in there but it turns the icon off before the music actually begins, even though it's after the [audioPlayer play] action.
Yes, the AVPlayer class does not have a delegate protocol like the AVAudioPlayer. You need to subscribe to notifications on an AVPlayerItem. You can create an AVPlayerItem using the same URL that you would otherwise pass to -initWithURL: on AVPlayer.
-(void)startPlaybackForItemWithURL:(NSURL*)url {
// First create an AVPlayerItem
AVPlayerItem* playerItem = [AVPlayerItem playerItemWithURL:url];
// Subscribe to the AVPlayerItem's DidPlayToEndTime notification.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];
// Pass the AVPlayerItem to a new player
AVPlayer* player = [[[AVPlayer alloc] initWithPlayerItem:playerItem] autorelease];
// Begin playback
[player play]
}
-(void)itemDidFinishPlaying:(NSNotification *) notification {
// Will be called when AVPlayer finishes playing playerItem
}
Yes. Add a KVO observer to the player's status or rate:
- (IBAction)go {
self.player = .....
self.player.actionAtItemEnd = AVPlayerActionStop;
[self.player addObserver:self forKeyPath:#"rate" options:0 context:0];
}
- (void)stopped {
...
[self.player removeObserver:self]; //assumes we are the only observer
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == 0) {
if(player.rate==0.0) //stopped
[self stopped];
}
else
[super observeVal...];
}
So basically, that's it.
Disclaimer: I wrote that in here so I didn't check if the code was good. ALSO I never used AVPlayer before but it should be about right.
Swift 3 - I add an observer to AVPlayerItem every time I add a video to the player:
func playVideo(url: URL) {
let playerItem = AVPlayerItem(asset: AVURLAsset(url: someVideoUrl))
NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidPlayToEndTime), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: playerItem)
self.player.replaceCurrentItem(with: playerItem)
self.player.play()
}
func playerItemDidPlayToEndTime() {
// load next video or something
}
There is a lot of information in the Apple docs AVFoundation Programming Guide (look for the monitoring playback section). It appears to be mainly through KVO so you may wish to brush up on that if you are not too familiar (there is a guide for that to Key Value Observing ProgrammingGuide.
I'm using this one, and it works:
_player = [[AVPlayer alloc]initWithURL:[NSURL URLWithString:_playingAudio.url]];
CMTime endTime = CMTimeMakeWithSeconds(_playingAudio.duration, 1);
timeObserver = [_player addBoundaryTimeObserverForTimes:[NSArray arrayWithObject:[NSValue valueWithCMTime:endTime]] queue:NULL usingBlock:^(void) {
[_player removeTimeObserver:timeObserver];
timeObserver = nil;
//TODO play next sound
}];
[self play];
where _playingAudio is my custom class with some properties and timeObserver is id ivar.