Hello i am new to objective - c
I'm having a problem with the UIWebView and MPMoviePlayerController: My UIWebView has a movie inside the html (it's a local html file), I'm using html5 and a video tag for the video.
I want a notification when video starts or stops in UIWebView....
I have tried using MPMoviePlayerPlaybackDidFinishNotification, but it doesnt fire ...
I have also tried to make the my main UIViewController's view a view of my own, and intercept -didAddSubview: and -willRemoveSubview:. but with no sucess...
Does any body know how to get notification from uiwebview??
You can observe #"MPAVControllerPlaybackStateChangedNotification" (use nil for the object). This notification isn't documented so I don't know if the App Store will approve your app.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackStateDidChange:)
name:#"MPAVControllerPlaybackStateChangedNotification"
object:nil];
The notification has the key MPAVControllerNewStateParameter in its userInfo. The value seems to be 0 before playback starts, 1 when it is paused, 2 when it is playing, and 3 (momentarily) when you are dragging the playback slider.
- (void)playbackStateDidChange:(NSNotification *)note
{
NSLog(#"note.name=%# state=%d", note.name, [[note.userInfo objectForKey:#"MPAVControllerNewStateParameter"] intValue]);
}
I searched alot about this..Here is the solution that I have found for getting the playback end notification call. Tested code on iOS6.0 and above. All thanks to #Morten.
In viewDidLoad add observer
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackDidEnd:)
name:#"MPAVControllerItemPlaybackDidEndNotification"//#"MPAVControllerPlaybackStateChangedNotification"
object:nil];
Then simply add following javascript code webViewDidFinishLoad delegate as below
- (void)webViewDidFinishLoad:(UIWebView *)webView {
//http://stackoverflow.com/a/12504918/860488
[videoView stringByEvaluatingJavaScriptFromString:#"\
var intervalId = setInterval(function() { \
var vph5 = document.getElementById(\"video-player\");\
if (vph5) {\
vph5.playVideo();\
clearInterval(intervalId);\
} \
}, 100);"];
}
- (void)playbackDidEnd:(NSNotification *)note
{
//do your stuff here
[videoView removeFromSuperview];
videoView.delegate = nil;
videoView = nil;
}
You will get playbackDid End call in the above selected and can do whatever is your requirement.
Happy Coding !!
Related
I need to play a video in my UIViewcontroller using MPMoviePlayerController. So before playing the video i need to show an activity Indicator View before the video is buffered. Once the video starts playing i need to remove the Activity Indicator. I am not able to find out on how to get notified as soon as the video starts playing. Any suggestion on this would be of great help. Thanks.
Your probably looking for something like this:
- (void)viewDidAppear:(BOOL)animated {
NSLog(#"VIEW DID LOAD");
// Register to receive a notification that the movie is now in memory and ready to play
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieLoadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
}
-(void)movieLoadStateDidChange:(id)sender{
NSLog(#"STATE CHANGED");
if(MPMovieLoadStatePlaythroughOK ) {
NSLog(#"State is Playable OK");
NSLog(#"Enough data has been buffered for playback to continue uninterrupted..");
aiv.hidden = YES;
[aiv stopAnimating];
}
}
I also found that from this link which may help you out too: http://www.sdkboy.com/?p=48
if(MPMovieLoadStatePlaythroughOK ) { - this check is incorect. It's always TRUE.
Take a look:
Can a UIActivityIndicator be displayed over a MPMoviePlayerController in full screen mode?
I'm trying to create an iPad app where i need to playback a quicktime movie which contains some chapter markers. When each marker is reached i need a small overlay to be shown on top of the video.
Is there a way to trigger an event/function each time a marker is reached? And if so, how?
I'm not interested in having to develop an entire movieplayer with codec handling from scratch, since this is out of my comfortzone - So I'm hoping this is possible using MPMoviePlayer or something similar.
Any help is greatly appreciated! :)
Register to receive the following notification:
#define MPAVControllerTimeDidJumpNotification #"MPAVControllerTimeDidJumpNotification"
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleTimeChanged:) name:MPAVControllerTimeDidJumpNotification object:nil];
-(void)handleTimeChanged:(NSNotification *)notification
{
static int i = 0;
NSDictionary * userInfo = notification.userInfo;
int lastPositionInSeconds = [[userInfo valueForKey:#"MPAVControllerTimeParameter"] intValue];
if(lastPositionInSeconds > markers[i])
{
i++;
[self showOverlay: i];
}
}
Also register to receive MPMoviePlayerPlaybackDidFinishNotification notification to stop listening for the MPAVControllerTimeDidJumpNotification notification.
I am trying to get an embedded video playback properly implemented within a tab-bar navigation. In my specific case, the video shall be displayed in a non-fullscreen manner on a UIView that is hosted by a UIViewController, managed by a UITabBarController.
For simplifying the example, lets say I have two tabs within my tabbar. First one shows some random stuff, second one shows the viewcontroller that hosts the embedded video.
Once the user selects the second tab, the video is loading and playing properly.
For initializing the player, I am using the following code from within my UIView derived class, triggered by the initializer (initWithFrame):
- (void)initPlayback
{
self.movieViewController = [[MPMoviePlayerViewController alloc] init];
movieViewController_.wantsFullScreenLayout = NO;
movieViewController_.moviePlayer.controlStyle = MPMovieControlStyleEmbedded;
[self addSubview:self.movieViewController.view];
}
For starting the playback, I am using the following code, triggered by the viewWillAppear method of my UIViewController derived class:
- (void)playVideo
{
[movieViewController_.moviePlayer setContentURL:fileURL_];
}
If then, the user selects the first tab (while the video is still playing), I am making sure that the video is stopped as it would continue playing if that was not done:
- (void)stopVideo
{
[movieViewController_.moviePlayer stop];
}
Once the user selects the second tab again, the view stays blank, nothing is loaded or played even though the playVideo-method is invoked.
What am I missing, why is the video playback failing when reselecting the second tab?
==========new attempt=============
This time I stopped relying on shouldAutoplay (as suggested) but that did not make a difference.
Adapted and added code for this;
- (void)MPMoviePlayerLoadStateDidChange:(NSNotification *)notification
{
if (movieViewController_.moviePlayer.loadState == MPMovieLoadStatePlayable &&
movieViewController_.moviePlayer.playbackState != MPMoviePlaybackStatePlaying)
{
[movieViewController_.moviePlayer play];
}
}
- (void)deregisterFromNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
}
- (void)registerForNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(MPMoviePlayerLoadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
}
- (void)initPlayback
{
NSLog(#"playback init...");
self.movieViewController = [[MPMoviePlayerViewController alloc] init];
movieViewController_.wantsFullScreenLayout = NO;
movieViewController_.moviePlayer.shouldAutoplay = NO;
movieViewController_.moviePlayer.controlStyle = MPMovieControlStyleEmbedded;
movieViewController_.moviePlayer.currentPlaybackTime = 0.0f;
[self addSubview:movieViewController_.view];
}
- (void)playVideo
{
NSLog(#"playback starting...");
[self registerForNotifications];
[movieViewController_.moviePlayer setContentURL:fileURL_];
}
- (void)stopVideo
{
NSLog(#"playback stopping...");
[movieViewController_.moviePlayer stop];
[self deregisterFromNotifications];
}
In playVideo I think it should be
[movieViewController_.moviePlayer setContentURL:fileURL_];
[movieViewController_.moviePlayer play];
I assume that it works the first time because autoplay defaults to YES
MPMoviePlayerViewController is a subclass of UIViewController. If the second tab is dedicated to displaying video why not just use an instance of it as the root view controller for the second tab?
Adding [movieViewController_.moviePlayer prepareToPlay] to my playVideo method does the trick (when working with remote streams).
I've got a UITableView which lists movie files from on disk. For each cell row, there is a worker instance allocated for each visible row, used to generate a thumbnail for the movie file and get its duration to display in the row.
For each instance of MPMoviePlayerController in the worker class Im listening for a MPMovieDurationAvailableNotification event from the movie player. For some reason this event only seems to be dispatched (or at least Im only able to catch it) from one of the worker instances. Here is the init and listener code. There are a few comments inline.
- (id) initWithRequestAsset:(RequestAsset *)asset {
if (self = [super init]) {
self.requestAsset = asset;
self.moviePlayer = [MPMoviePlayerController alloc];
[self setupMoviePlayerListeners];
[self.moviePlayer initWithContentURL:self.requestAsset.urlPath];
self.moviePlayer.shouldAutoplay = NO;
// I've also tried to retain the moviePlayer, to no avail
[self.moviePlayer release];
}
return self;
}
- (void) setupMoviePlayerListeners {
//
// If the object: is set to nil then Im able to catch three notifications, but they are all from last instance of the MPMoviePlayerController
//
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(onMovieDurationAvailable:)
name:MPMovieDurationAvailableNotification
object:self.moviePlayer];
}
- (void) onMovieDurationAvailable:(NSNotification *)notification {
NSLog(#"duration received notification");
self.requestAsset.duration = [[notification object] duration];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMovieDurationAvailableNotification object:self.moviePlayer];
}
What am I doing wrong? I figured if I were to set the object: parameter to the instance of the MPMoviePlayerController it would allow me to get only the event for that instance. However, it appears that Im only getting the last notification dispatched.
You can only have 1 active MPMoviePlayerController instance. You can create multiple but only 1 will work at a time.
See (about 2 screens down):
http://developer.apple.com/library/ios/#documentation/MediaPlayer/Reference/MPMoviePlayerController_Class/Reference/Reference.html
"Note: Although you can create multiple MPMoviePlayerController objects and present their views in your interface, only one movie player at a time can play its movie."
I'm trying to play two videos using the MPMoviePlayerController class and allow the user to switch between the two videos by swiping their finger across the screen. Everything works great for the first movie. The overlay view correctly detects the swipe and starts the next movie playing.
Unfortunately things don't work so well with the second movie. I'm not sure what's happening, but the overlay view does not actually seem to be on top of the movie player and is certainly not responding to touch events. In fact, double tapping the screen while the second movie is playing zooms the movie in and out, so touches seem to be going to the MPMoviePlayerController.
I've tried a number of different approaches here, but none of them work. Here is the current version of the code:
- (void)allocateMoviePlayerForCurrentVideo
{
// Create a movie player for the first video in the playlist
NSURL *url = [videoURLs objectAtIndex:nowPlayingVideoIndex];
moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:url];
moviePlayer.scalingMode = MPMovieScalingModeAspectFill;
moviePlayer.movieControlMode = MPMovieControlModeHidden;
moviePlayer.backgroundColor = [UIColor blackColor];
// Register for the movie preload complete notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePreloadComplete:)
name:MPMoviePlayerContentPreloadDidFinishNotification
object:nil];
// Register for the playback did finish notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieDidFinishPlaying:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
}
The code which handles the MPMoviePlayerContentPreloadDidFinishNotification starts the movie playing and adds the overlay view:
- (void)moviePreloadComplete:(NSNotification *)notification
{
// Remove this object from observing the preload complete notification
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerContentPreloadDidFinishNotification
object:nil];
// Start playing the current movie
[moviePlayer play];
// Add the overlay view to the movie player
UIWindow *moviePlayerWindow= [[[UIApplication sharedApplication] windows] objectAtIndex:1];
overlayView = [[GGDMovieOverlayView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
overlayView.backgroundColor = [UIColor clearColor];
[moviePlayerWindow addSubview:overlayView];
[moviePlayerWindow bringSubviewToFront:overlayView];
// Register for swipe notifications from the overlay view
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleMoviePlayerSwipeNotification:)
name:GGDMoviePlayerSwipeNotification
object:nil];
}
And finally, here's the routine which handles the swipe notification:
- (void)handleMoviePlayerSwipeNotification:(NSNotification *)notification
{
// Stop the current movie player and release it
[moviePlayer stop];
[moviePlayer release];
moviePlayer = nil;
// Remove the overlay view from its superview
[overlayView removeFromSuperview];
// Advance to the next movie, treating the array of video URLs as a circular array
if ( ++nowPlayingVideoIndex == [videoURLs count] ) {
nowPlayingVideoIndex = 0;
}
[self allocateMoviePlayerForCurrentVideo];
}
I fear there's something obvious I'm missing, but this has been driving me crazy for a while now. I very much appreciate any help!
Thanks,
Adam
I just went through this myself and I can explain the issue and a slightly better workaround.
The problem with running the next movie in sequence is that the notification that you get when the movie has finished playing occurs before the current movie player window has been removed from the view. This is an issue because (as per the Apple example) the only way to get the movie player window is to grab the top window on the stack (or the key window). But if you try to use this technique immediately you'll get the old movie player window that is fading out, not the new one that is going to start. (As an aside - frankly, the Apple example is an illustration that there really isn't a proper way to get the movie window at all.. their example is terrible and is probably a race condition to begin with unless somehow the play method blocks until the window is actually on the stack).
Anyway, rather than an arbitrary delay, what I did is to tag the player window at the time that it is first created e.g.
UIWindow *moviePlayerWindow = [[UIApplication sharedApplication] keyWindow];
moviePlayerWindow.tag = MY_MOVIE_WINDOW_TAG; // this is just an int
and then in the moviePlaybackDidFinish method I call a waitForMovieWindowToExit method that looks for that tagged window. If it's still there then I use a timer to check again in 0.1 seconds. When it finally disappears I play the next sequence.
So, it's still a mess and still using a timer, but at least you are guaranteed to play the next movie within 0.1 second (or whatever) of the next possible time and it won't break if the system is a little slow, etc.
If anyone wants more code I can post it.
Fixed by using a NSTimer to add the overlay view after a delay to make sure the movie has started playing.
Thanks,
Adam