I'm working on a test application that will run an mp4 file from internet.
code is :
-(IBAction)playRemoteVideo
{
NSString *mp4File = #"http://archive.org/download/Pbtestfilemp4videotestmp4/video_test_512kb.mp4";
MPMoviePlayerViewController *playerController = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL URLWithString:mp4File]];
[self presentMoviePlayerViewControllerAnimated:playerController];
playerController.moviePlayer.movieSourceType=MPMovieSourceTypeStreaming;
[playerController.moviePlayer play];
[playerController release];
playerController=nil;
}
When I run the application and played the video the player tries to load the video for a while but after I got this exception on console
2012-04-18 22:45:11.309 VideoPlayer[891:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An AVPlayerItem can occupy only one position in a player's queue at a time.'
*** First throw call stack:
(0x1df1052 0x1333d0a 0x27cfb31 0x27cbb2a 0x27e45cc 0x103b73 0xd4e6a 0x2ff2445 0x2ff44f0 0x1d28833 0x1d27db4 0x1d27ccb 0x16d8879 0x16d893e 0x24ea9b 0x1d12 0x1c85)
terminate called throwing an exception(gdb)
If I execute the same code with an m3u8 file , for instance;
http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8
I can get the video running but same does not work for mp4 file.
Do you have any idea why I got this exception and what's wrong with my code?
I run the application on Iphone simulator and I have XCode 4.2
Best Regards
Tugrul
Set the movie player's control style like so.
[self.mPlayer.moviePlayer setControlStyle:MPMovieControlStyleDefault];
Also, local files have the MPMovieSourceTypeFile, not MPMovieSourceTypeStreaming property set.
One more scenario where this can happen is, as the log quite helpfully tells you, when "An AVPlayerItem can occupy only one position in a player's queue at a time." Basically, when you're trying to get two videos to start playing at the same time, or even INTERACT with two videos/MPMoviePlayerController objects at the same time.
In my app I use two MPMoviePlayerController's and keep swapping them around to create the illusion for the user of infinitely moving and swapping between different videos.
This was working fine so far, but I recently added notifications for some events that should result in the videos being paused and resumed. However I didn't realize both my player objects were listening to the notifications at the same time, and hence tried to trigger either "Pause" or "Play" at the same time. This caused the framework to think I was trying to play multiple videos at the same time, and threw this exception.
All I had to do now was make sure that only one player object was listening to notifications at any given point. Just a small tweak in my application logic.
So if you're getting a weird error, it need not be the framework that has a problem, a malicious operating system bent on making your life hell or an act of god. It could just be pure, good old-fashioned bad code. :)
I know this error. Try this code here.
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL: url];
self.movieController = player;
self.movieController.fullscreen = YES;
self.movieController.controlStyle = MPMovieControlStyleDefault;
// Register to receive a notification when the movie has finished playing.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:self.movieController];
// Register to receive a notification when the movie has finished playing.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerDidExitFullscreenNotification
object:self.movieController];
[self.movieController prepareToPlay];
[self.movieController.view setFrame: self.view.bounds]; // player's frame must match parent's
[self.view addSubview: self.movieController.view];
// ...
[self.movieController play];
Related
I'm having trouble understanding this class and getting it to work properly, here is the piece of code where I use it:
MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:_videoURL];
UIImage *videoThumbnail = [moviePlayer thumbnailImageAtTime:0 timeOption:MPMovieTimeOptionNearestKeyFrame];
[lastImageView setImage:videoThumbnail];
[moviePlayer setControlStyle:MPMovieControlStyleNone];
[moviePlayer setShouldAutoplay:YES];
[moviePlayer prepareToPlay];
[moviePlayer.view setFrame:lastImageView.frame];
moviePlayer.view.transform = CGAffineTransformMakeRotation((90*M_PI)/180);
[self.view addSubview:moviePlayer.view];
[moviePlayer play];
The only reason why the videoThumbnail line is still there is because i didn't get the video to play until I was just trying it out to see if it would get the image from there and then it suddenly began to work... sort of.
Now it plays for 2-3 secs and then terminates without sending MPMoviePlayerPlaybackDidFinishNotification or MPMoviePlayerPlaybackStateDidChangeNotification
I googled around a bit and couldn't find any useful tips, could someone tell me what's wrong or what i am forgetting
If you're not assigning the newly created MPMoviePlayerController instance to anything other than a variable with local scope (moviePlayer), then the movie player will be deallocated before the movie gets playing. (I imagine the thumbnailImageAtTime call keeps it around for a bit longer.)
Try assigning the movie player instance to a retained (strong) instance variable or property. Of course it should be released when finished, as multiple movie player instances don't play well together.
Also, note that, as of iOS 5, calling prepareToPlay is required. The following is from chapter 28 of Matt Neuberg's Programming iOS 5, Second Edition:
Before you can display a movie in your interface with an MPMoviePlayerController, you must call prepareToPlay, which is supplied through the MPMediaPlayer protocol (adopted by MPMoviePlayerController). This requirement is new in iOS 5, and is a major difference from previous versions of the system; your old code can break if it didn’t make this call.
I want to play videos in iphone app and i am using MPMoviePlayerController. It used to work fine but suddenly when i tried to integrate the viewcontroller class which plays the video, it stopped playing. I tried to import the MediaPlayer Framework and did a lot of R&D stuff but still its not working. I am using this code..
NSURL *fileURL = [NSURL URLWithString:xmlParser.videoUrlLink];
self.moviePlayerController = [[MPMoviePlayerController alloc] initWithContentURL:fileURL];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:self.moviePlayerController];
//[moviePlayerController setControlStyle:MPMovieControlStyleEmbedded];
[self.moviePlayerController.view setFrame:CGRectMake(5,65,307,200)];
[self.view addSubview:self.moviePlayerController.view];
[self.moviePlayerController play];
I am getting a warning and the method moviePlayBackDidFinish: is getting called repeatedly even before the video is played. The warning is
An instance 0x168da0 of class AVPlayerItem was deallocated while key value observers
were still registered with it. Observation info was leaked, and may even become
mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop
here in the debugger. Here's the current observation info: ( Context: 0x0, Property:
0x10b570> Context: 0x0, Property: 0x117ab0>
Can someone please help me on how to get out of this....It would be of great help....
I have an app that streams video from the net and plays it using an MPMoviePlayerController object for playback on the device or via AirPlay.
The app supports background operation and has the 'audio' option listed within the required UIBackgroundModes key in its plist file.
When playing over AirPlay, the app can successfully be pushed to the background and the video continues to play properly. So far, so good.
According to the Apple documentation:
Including the audio key tells the system frameworks that they should
continue playing and make the necessary callbacks to the app at
appropriate intervals. If the app does not include this key, any audio
being played by the app stops when the app moves to the background.
However, these callbacks are not being made.
The app uses two types of callback: those associated with notifications MPMoviePlayerController and AVPlayer send during playback together with timer based callbacks that monitor the playback position and performance stats for monitoring purposes.
Looking at Apple's notes, I would certainly expect to receive the first type of callback so that the app can respond to MPMoviePlayerPlaybackStateDidChangeNotification, MPMoviePlayerPlaybackDidFinishNotification and MPMoviePlayerLoadStateDidChangeNotification, but this doesn't happen.
Does anyone know if it is possible to receive these during background AirPlay playback and, if so, how was this achieved?
**Please note: the app works correctly when running in the foreground and receives the notifications fine. It is only when pushed to the background and playing over AirPlay that the notifications are not received.
Likewise, the video plays over AirPlay in the background properly. It is only the notifications which are not received**
I had this issue and have fixed it though it was a few months back. I could send you my entire class for movie playback if this doesn't work. Note it is using the navigation controller model.
NOTE: This is tested on iPad 2 not on the iPhone.
I show my VC like this:
- (IBAction)playMovie:(id)sender {
MovieVC* movController = [[MovieVC alloc] initWithID:2];
movController.view.backgroundColor = [UIColor blackColor];
AppDelegate *appDel = [[UIApplication sharedApplication] delegate];
[appDel.navigationController pushViewController:movController animated:NO];
[movController release];
}
Then in my MovieVC view controller class i set up the video playback like this:
- (void)initMoviePlayer {
mMoviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:[self getMovieURL]];
mMoviePlayer.allowsAirPlay = YES;
mMoviePlayer.view.frame = [self.view bounds];
mMoviePlayer.view.backgroundColor = [UIColor clearColor];
mMoviePlayer.shouldAutoplay = YES;
mMoviePlayer.fullscreen = YES;
mMoviePlayer.scalingMode = MPMovieScalingModeAspectFit;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePreloadDidFinish:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:mMoviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
}
This fixed it for me, if it doesn't fix comment and ill edit with the entire class file.
In one of my projects I did what you did, and it worked for me.
Two differences in my project :
I am not streaming via airPlay (device playback only),
I am just playing audio files.
My step-by-step :
Add the audio option to UIBackgroundModes in the plist file,
Register to the NotificationCenter for MPMoviePlayerPlaybackDidFinishNotification and MPMoviePlayerPlaybackStateDidChangeNotification with the following code :
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(playbackStateChanged:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:moviePlayer];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(playbackEnded:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayer];
It works like a charm.
For completeness, I ought to add that at present I believe there is no solution to this problem.
I've discussed it directly with Apple via tech support and there was no practical work-around available.
This functionality was needed to allow the app to record stats about the playback of the stream at regular intervals. While this is fine when the video is played on the device's screen and over AirPlay while the app is in the foreground, it isn't possible to do with the app in the background.
The solution I've gone with instead is to disable the idle timer during all types of playback and to re-enable afterwards using:
[UIApplication sharedApplication].idleTimerDisabled = YES;
and
[UIApplication sharedApplication].idleTimerDisabled = NO;
While this isn't a solution to the original question, it's a workaround for avoiding the issue in the first place.
I ran into a problem... perhaps someone bumped into something similar: I have an application that uses a MPMoviePlayerController, and used to work perfectly well.
Trying to compile and run it with new Xcode 4.2 using iPhone 5 Simulator, MPMoviePlayerController is not sending notifications when I load a movie. Looks like it fails to properly detect the video file.
The code looks more or less like that (simplified code):
// First I'm initializing the player with a URL from a file
MPMoviePlayerController *player;
player = [[MPMoviePlayerController alloc] init];
player.shouldAutoplay = NO;
NSURL *url = [NSURL fileURLWithPath:path];
player.contentURL = url;
// Then I'm adding an observer in order to wait for the player to find movie duration
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(durationAvailable:)
After the above, I'm waiting in a loop for the notification to get called, by waiting for about 10 seconds, during which I'm calling the run loop.
Using the same code, same Xcode 4.2 version, but with iPhone 4.3 Simulator, after about a second the notification gets called, and I can read the player duration. However, when running the exact same code on iPhone 5 Simulator, the notification function never gets called, and if I try to read the movie duration afterwards it contains 0.
Any idea?
Thanks,
Ariel
OK, I managed to find a workaround to the problem.
Apparently, adding this line after adding the observer does the trick:
[player pause];
It looks like if you don't "activate" the player somehow, the notifications will not be fired. In my case, I just wanted to get a notification for the duration of the movie without starting a playback, and it worked well in versions prior to iOS5, but it looks like they changed the behavior. Anyway, the above line solves the problem.
Ariel
try this MPMoviePlayerPlaybackStateDidChangeNotification :
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(stop)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:nil];
I'm having trouble getting MPMoviePlayerController to work on the device. It runs fine in simulator, playing a five-second video and then sending the appropriate callback (myMovieFinishedCallback:) to the view controller. When it runs on the device, the movie never shows up, even though I can trace through [player play] with no problems. myMovieFinishedCallback: is never called, and there are no errors. All the right resources are being copied. Any idea what's going on?
Here's the code I use to create and use the player,
Update: I've switched over to using MPMoviePlayerViewController. On the device the movie still does not play, I just get the spinning progress wheel indefinitely. The controls also flash briefly even though I've set the player to MPMovieControlStyleNone - anybody know how I can fix this? The movie is five seconds and about 1.5 MB if that makes any difference.
Update: Other movie files work, but I can't figure out how to make mine work. I've tried it as a .mov and a .mp4 and the settings seem to be right. Any idea what would cause the MPMoviePlayer to show a progress wheel forever on the device only?
- (void) playMovie
{
NSString *url = [[NSBundle mainBundle]
pathForResource:#"myMovie"
ofType:#"mp4"];
MPMoviePlayerViewController *playerViewController =
[[MPMoviePlayerViewController alloc]
initWithContentURL:[NSURL fileURLWithPath:url]];
playerViewController.moviePlayer.controlStyle = MPMovieControlStyleNone;
playerViewController.moviePlayer.scalingMode = MPMovieScalingModeFill;
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(myMovieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:[playerViewController moviePlayer]];
playerViewController.view.frame = movieView.frame;
[movieView addSubview:playerViewController.view];
//---play movie---
MPMoviePlayerController *player = [playerViewController moviePlayer];
[player play];
}
Your code works fine for me, so there are two possibilities that come to mind (there may be others):
You movieView has not been properly initialized or has not had its frame set so that it's visible.
The video format of the video you're displaying isn't supported on the device.
I would try using a different video. Maybe one you can confirm you've played on the device before. Here's the little demo project I threw together: http://www.cimgf.com/files/PlayMovie.zip