How to receive NSNotifications from UIWebView embedded YouTube video playback - ios

I didn't received any notifications for MPMoviePlayerController. What am I doing wrong?
I use following logic.
I'm begining to play youtube video in UIWebView. UIWebView calls a standard MPMoviePlayerController. I don't control MPMoviePlayerController because I didn't instantiate MPMoviePlayerController.
I run youtube's clip with autoplay (1 second delay):
[self performSelector:#selector(touchInView:) withObject:b afterDelay:1];
My code is:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(loadStateDidChange:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playbackDidFinish:) name:MPMoviePlayerDidExitFullscreenNotification object:nil];
[self embedYouTube];
}
- (void)loadStateDidChange:(NSNotification*)notification
{
NSLog(#"________loadStateDidChange");
}
- (void)playbackDidFinish:(NSNotification*)notification
{
NSLog(#"________DidExitFullscreenNotification");
}
- (void)embedYouTube
{
CGRect frame = CGRectMake(25, 89, 161, 121);
NSString *urlString = [NSString stringWithString:#"http://www.youtube.com/watch?v=sh29Pm1Rrc0"];
NSString *embedHTML = #"<html><head>\
<body style=\"margin:0\">\
<embed id=\"yt\" src=\"%#\" type=\"application/x-shockwave-flash\" \
width=\"%0.0f\" height=\"%0.0f\"></embed>\
</body></html>";
NSString *html = [NSString stringWithFormat:embedHTML, urlString, frame.size.width, frame.size.height];
UIWebView *videoView = [[UIWebView alloc] initWithFrame:frame];
videoView.delegate = self;
for (id subview in videoView.subviews)
if ([[subview class] isSubclassOfClass: [UIScrollView class]])
((UIScrollView *)subview).bounces = NO;
[videoView loadHTMLString:html baseURL:nil];
[self.view addSubview:videoView];
[videoView release];
}
- (void)webViewDidFinishLoad:(UIWebView *)_webView
{
UIButton *b = [self findButtonInView:_webView];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(touchInView:) object:b];
[self performSelector:#selector(touchInView:) withObject:b afterDelay:1];
}
- (UIButton *)findButtonInView:(UIView *)view
{
UIButton *button = nil;
if ([view isMemberOfClass:[UIButton class]]) {
return (UIButton *)view;
}
if (view.subviews && [view.subviews count] > 0)
{
for (UIView *subview in view.subviews)
{
button = [self findButtonInView:subview];
if (button) return button;
}
}
return button;
}
- (void)touchInView:(UIButton*)b
{
[b sendActionsForControlEvents:UIControlEventTouchUpInside];
}
UPDATE: I'm creating application that plays youtube's video. You can run playlist and you will see first video. When first video has ended, second video begins play automatically and so on.
I need to support ios 4.1 and above.
UPDATE2: #H2CO3 I'm trying to use your url-scheme, but it don't works. Delegate method didn't called on exit event. I added my html url to log.
It is:
<html><head> <body style="margin:0">
<script>function endMovie()
{document.location.href="somefakeurlscheme://video-ended";}
</script> <embed id="yt" src="http://www.youtube.com/watch?v=sh29Pm1Rrc0"
onended="endMovie()" type="application/x-shockwave-flash"
width="161" height="121"></embed>
</body></html>
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if ([[[request URL] absoluteString] hasPrefix:#"somefakeurlscheme://video-ended"])
{
[self someMethodSupposedToDetectVideoEndedEvent];
return NO; // prevent really loading the URL
}
return YES; // else load the URL as desired
}
UPDATE3
#Till, I cann't caught UIMoviePlayerControllerDidExitFullscreenNotification, but I found MPAVControllerItemPlaybackDidEndNotification. MPAVControllerItemPlaybackDidEndNotification appears when playback video is ended.
But I don't understand how do I catch onDone notifications?

There are no documented notifications sent by the UIWebView embedded movie player.
In fact, the closed implementation used within the UIWebView does differ from the public MPMoviePlayerController in many aspects (e.g. DRM).
The most important classes used for playing video content within that UIWebView are called MPAVController and UIMoviePlayerController. The latter one makes the player appear like the MPMoviePlayerController fullscreen interface.
In case you dare to risk a rejection by Apple, there are actually ways to still achieve what you are looking for.
NOTE
This is not documented and is subject to break on each and every new iOS release. It does however work on iOS4.3, 5.0 and 5.01, 5.1 and 6.0 and it may work on other versions as well.
I am not able to test this solution on iOS 4.1 and 4.2, so that is up to you to do. I highly suspect that it will work.
Fullscreen State
If, for example you are intending to react upon the user tapping the DONE button, you may be able to do it this way:
UPDATE The old version of this answer recommended to use UIMoviePlayerControllerDidExitFullscreenNotification whereas this new version (updated for iOS6) recommends using UIMoviePlayerControllerWillExitFullscreenNotification.
C-Language Level:
void PlayerWillExitFullscreen (CFNotificationCenterRef center,
void *observer,
CFStringRef name,
const void *object,
CFDictionaryRef userInfo)
{
//do something...
}
CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
NULL,
PlayerWillExitFullscreen,
CFSTR("UIMoviePlayerControllerWillExitFullscreenNotification"),
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately);
Objective-C Level:
- (void)playerWillExitFullscreen:(NSNotification *)notification
{
//do something...
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerWillExitFullscreen:)
name:#"UIMoviePlayerControllerWillExitFullscreenNotification"
object:nil];
I did draft both, C-Level and Objective-C-Level options because the best way to actually find out about all of this is to use C-Level (CoreFoundation) functions as shown at the end of my answer. If the sender of a notification does not use Objective-C (NSNotifications), you may actually not be able to trap them using the NSNotification-mechanics.
Playback State
For examining the playback state, look out for "MPAVControllerPlaybackStateChangedNotification" (as drafted above) and examine the userInfo which may look like this:
{
MPAVControllerNewStateParameter = 1;
MPAVControllerOldStateParameter = 2;
}
Further Reverse Engineering
For reverse engineering and exploring all the notifications sent, use the following snippet.
void MyCallBack (CFNotificationCenterRef center,
void *observer,
CFStringRef name,
const void *object,
CFDictionaryRef userInfo)
{
NSLog(#"name: %#", name);
NSLog(#"userinfo: %#", userInfo);
}
CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
NULL,
MyCallBack,
NULL,
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately);

In iOS 4.3+ you can use the UIMoviePlayerControllerDidEnterFullscreenNotification and UIMoviePlayerControllerDidExitFullscreenNotification notifications:
-(void)viewDidLoad
{
...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(youTubeStarted:) name:#"UIMoviePlayerControllerDidEnterFullscreenNotification" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(youTubeFinished:) name:#"UIMoviePlayerControllerDidExitFullscreenNotification" object:nil];
}
-(void)youTubeStarted:(NSNotification *)notification{
// your code here
}
-(void)youTubeFinished:(NSNotification *)notification{
// your code here
}

As far as I know, the implementation details of UIWebView (and all system classes made by Apple) are not to be relied upon when making a Cocoa Touch application. Maybe it's the case that an UIWebView's video player is not a standard MPMoviePlayerController class and it might have a totally different delegation/notification system, which is not supposed to be accessible by the user.
I suggest you to use the HTML5 element and detect the "onended" event of this tag:
<html>
<body>
<script>
function endMovie() {
// detect the event here
document.location.href="somefakeurlscheme://video-ended";
}
</script>
<video src="http://youtube.com/watch?v=aiugvdk755f" onended="endMovie()"></video>
</body>
</html>
In fact, from the endMovie JavaScript function, you can redirect to a bogus URL which you can catch in your -webView:shouldStartLoadWithRequest: (UIWebViewDelegate) method thus get notified that the video has ended:
- (BOOL) webView:(UIWebView *)wv shouldStartLoadWithRequest:(NSURLRequest *)req {
if ([[[req URL] absoluteString] hasPrefix:#"somefakeurlscheme://video-ended"]) {
[self someMethodSupposedToDetectVideoEndedEvent];
return NO; // prevent really loading the URL
}
return YES; // else load the URL as desired
}
Hope this helps.

Based on the #H2CO3 answer but with the iframe API. It was the only way I could make it work.
This doesn't use any private API which makes it more future proof.
Here's the code to embed your Youtube video. Check the API for more ways to customise this.
<html>
<body>
<!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
<div id="player"></div>
<script>
// 2. This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// 3. This function creates an <iframe> (and YouTube player)
//    after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '480',
width: '640',
videoId: 'aiugvdk755f',
events: {
'onStateChange': onPlayerStateChange
}
});
}
// 5. The API calls this function when the player's state changes.
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED) {
endedMovie();
}
}
function endedMovie() {
// detect the event here
document.location.href="somefakeurlscheme://video-ended";
}
</script>
</body>
</html>
And this is how you get notified that the video ended (UIWebViewDelegate method).
- (BOOL) webView:(UIWebView *)wv shouldStartLoadWithRequest:(NSURLRequest *)req {
if ([[[req URL] absoluteString] hasPrefix:#"somefakeurlscheme://video-ended"]) {
[self someMethodSupposedToDetectVideoEndedEvent];
return NO; // prevent really loading the URL
}
return YES; // else load the URL as desired
}

in ViewDidLoad add the following code
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(VideoExitFullScreen:) name:#"UIMoviePlayerControllerDidExitFullscreenNotification" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(VideoEnterFullScreen:) name:#"UIMoviePlayerControllerDidEnterFullscreenNotification" object:nil];
The following methods are for showing the message/functions for respective process of entering/exiting to/from full screen
- (void)VideoExitFullScreen:(id)sender{
// Your respective content/function for Exit from full screen
}
- (void)VideoEnterFullScreen:(id)sender{
// Your respective content/function for Enter to full screen
}

This works for me in iOS 6.1, it hides/removes other windows when the AVPlayerItemDidPlayToEndTimeNotification is received:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playerItemEnded:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
...
- (void)playerItemEnded:(NSNotification *)notification
{
for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
if (window != self.window) {
window.hidden = YES;
}
}
}

[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(youTubeStarted:) name:UIWindowDidBecomeVisibleNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(youTubeFinished:) name:UIWindowDidBecomeHiddenNotification object:nil];
-(void)youTubeStarted:(NSNotification *)notification
{
// Entered Fullscreen code goes here..
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.fullScreenVideoIsPlaying = YES;
NSLog(#"%f %f",webViewForWebSite.frame.origin.x,webViewForWebSite.frame.origin.y);
}
-(void)youTubeFinished:(NSNotification *)notification{
// Left fullscreen code goes here...
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.fullScreenVideoIsPlaying = NO;
//CODE BELOW FORCES APP BACK TO PORTRAIT ORIENTATION ONCE YOU LEAVE VIDEO.
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO];
//present/dismiss viewcontroller in order to activate rotating.
UIViewController *mVC = [[UIViewController alloc] init];
[self presentViewController:mVC animated:NO completion:Nil];
// [self presentModalViewController:mVC animated:NO];
[self dismissViewControllerAnimated:NO completion:Nil];
// [self dismissModalViewControllerAnimated:NO];
}

For iOS8 (Also I have an embedded video that is not a youtube video) the only solution I could get to work was to catch either one of viewWill/DidLayoutSubviews, and as an added bonus you don't need to change the HTML or use any private APIs :
So basically:
#property (nonatomic) BOOL showingVideoFromWebView;
...
...
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeOther) {
//Was "other" in my case... Might be UIWebViewNavigationTypeLinkClicked
self.showingVideoFromWebView = YES;
}
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
// Do whatever...
// Note: This will get called both when video is entering fullscreen AND exiting!
self.showingVideoFromWebView = NO;
}
In my case my web view is inside a UITableViewCell so I had to find a way to communicate between the cell and the view controller, and to also avoid using a BOOL flag I did this:
- (BOOL)webView:(UIWebView *)webView shouldStartLoad.....
... if (opening video check....) {
[[NSNotificationCenter defaultCenter] addObserverForName:#"webViewEmbedVidChangedState" object:nil queue:nil usingBlock:^(NSNotification *note) {
// Do whatever need to be done when the video is either
// entering fullscreen or exiting fullscreen....
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"webViewEmbedVidChangedState" object:nil];
}];
}
- (void)viewWillLayoutSubviews.....
[[NSNotificationCenter defaultCenter] postNotificationName:#"webViewEmbedVidChangedState" object:nil];

Actually for the reverse engineering purposes you can also use Cocoa API
like
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleNotification:)
name:nil
object:nil];
In this case you will receive all notifications

Related

MPMoviePlayerController - detect pressing Next/Prev buttons

I'm using MPMoviePlayerController and I need to detect pressing Next/Prev buttons. I tried several things, none of which seem to works.
Here is what I tried:
remote control events
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
-(void) viewWillDisappear:(BOOL)animated
{
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
-(BOOL)canBecomeFirstResponder
{
return YES;
}
-(void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent
{
// stuff
}
The problem is remoteControlReceivedWithEvent method is never called. I've read that this will not work in iOS version higher than 6 - I'm working on iOS 7
notifications
I tried using MPMoviePlayerPlaybackStateDidChangeNotification and check against MPMoviePlaybackStateSeekingForward or MPMoviePlaybackStateSeekingBackward - unfortunatelly, these playback state are set when dragging the playback bar, not when pressing Next/Prev buttons.
Any ideas?
Sorry I don´t understand your problem very well, but if you want use the controls out your App in the Control Center, you can use:
// You need cath the singleton
MPRemoteCommandCenter *myRemote = [MPRemoteCommandCenter sharedCommandCenter];
//And add the selector you can fire depends on the button, a couple of examples:
[myRemote.playCommand addTarget:self action:#selector(myPlayMethods)];
[myRemote.nextTrackCommand addTarget:self action:#selector(myNextTrackMethods)];
try registering for event in :
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Turn on remote control event delivery
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
// Set itself as the first responder
[self becomeFirstResponder];
}
Also Don't set kAudioSessionProperty_OverrideCategoryMixWithOthers property
Have you tried MPMoviePlayerNowPlayingMovieDidChangeNotification?
If this does not work then i would suggest moving to a lower level API i.e. AVPlayer. It provides fine grained control over all the actions while video playing and otherwise.
You need to register to handle a notification for moviePlayerLoadStateChanged. When you press the next/prev buttons moviePlayerLoadStateChanged will be called and the loadState will be MPMovieLoadStateUnknown
-(void)registerMyStuff {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerLoadStateChanged:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:self.mpc];
}
- (void)moviePlayerLoadStateChanged:(NSNotification *)notification
{
MPMoviePlayerController *moviePlayer = notification.object;
MPMovieLoadState loadState = moviePlayer.loadState;
if(loadState == MPMovieLoadStateUnknown)
{
// this is where the next/prev buttons notify
// there is no video in this state so load one
// just reload the default movie
NSLog(#"MPMovieLoadStateUnknown");
self.mpc.contentURL = self.fileURL;
[self.mpc prepareToPlay];
return;
}
else if(loadState & MPMovieLoadStatePlayable)
{
NSLog(#"MPMovieLoadStatePlayable");
}
else if(loadState & MPMovieLoadStatePlaythroughOK)
{
NSLog(#"MPMovieLoadStatePlaythroughOK");
} else if(loadState & MPMovieLoadStateStalled)
{
NSLog(#"MPMovieLoadStateStalled");
}
}
You change MPMoviePlayerController overlayView just like change UIImagePickerController overlayView to implement the function you need.
MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc]
initWithContentURL:someUrl];
moviePlayer.movieControlMode = MPMovieControlModeHidden;
[moviePlayer play];
NSArray *windows = [[UIApplication sharedApplication] windows];
if ([windows count] > 1) {
UIWindow *moviePlayerWindow = [[UIApplication sharedApplication] keyWindow];
[moviePlayerWindow addSubview:yourCustomOverlayView];
}

iOS 7 MPMoviePlayerController seek forward button brings the video to the End and displays Black screen

I am facing an issue with MPMoviePlayerController in iOS 7. I enter the fullscreen and then click (just a single tap) on seek forward button (>>|) , and the video playback ends and gives a black screen with a text "Loading" on the header.
I registered notification for "MPMoviePlayerPlaybackStateDidChangeNotification".
**[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerPlaybackStateDidChange:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:self.player];**
It does not get fired on a single click of seek forward button.
Also on registration of "MPMoviePlayerPlaybackDidFinishNotification"
**[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerPlaybackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];**
I get "MPMovieFinishReasonPlaybackEnded" event fired on that single click of seek forward button.
Any one knows the reason why? Is this a bug in apple?
I need to either stop this behavior of showing a black screen on single click , or just disable single click of seek forward button so that nothing happens.
Any one knows how to achieve this?
I fixed this by removing the MPMoviePlayer object completely, setting it to nil, removing it from it's superview and re-adding it using the original video Url. Code below:
- (void)addPlayerForUrl:(NSURL *)url {
self.player = [[MPMoviePlayerController alloc] initWithContentURL:url];
self.player.view.frame = self.videoView.bounds;
self.player.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.player.controlStyle = MPMovieControlStyleDefault;
[self.videoView insertSubview:self.player.view atIndex:0];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerLoadStateDidChangedNotification:)
name:MPMoviePlayerReadyForDisplayDidChangeNotification
object:self.player];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerPlaybackStateDidChangeNotification:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:self.player];
}
#pragma mark - Notifications
- (void)moviePlayerLoadStateDidChangedNotification:(NSNotification *)notification {
self.isVideoPreloaded = YES;
self.videoPlayButton.hidden = YES;
self.photoImageView.hidden = YES;
self.videoLoadingImageView.hidden = YES;
}
- (void)moviePlayerPlaybackStateDidChangeNotification:(NSNotification *)notification {
NSURL *url = self.player.contentURL;
switch (self.player.playbackState) {
case MPMoviePlaybackStateSeekingBackward:
case MPMoviePlaybackStateSeekingForward:
break;
case MPMoviePlaybackStatePlaying:
self.videoPlayButton.hidden = YES;
if (!self.isVideoPreloaded) {
self.videoLoadingImageView.hidden = NO;
[self.videoLoadingImageView startAnimating];
} else {
self.videoLoadingImageView.hidden = YES;
}
break;
case MPMoviePlaybackStatePaused:
case MPMoviePlaybackStateStopped:
self.videoPlayButton.hidden = NO;
self.videoLoadingImageView.hidden = YES;
[self.player endSeeking];
[self.player.view removeFromSuperview];
[self.player setFullscreen:NO];
self.player = nil;
[self addPlayerForUrl:url];
break;
default:
break;
}
}
Notice how I keep the NSURL, right before the switch statement in the moviePlayerPlaybackStateDidChangeNotification. That way, I can re-initialize and re-add the MPMoviePlayer object.
Btw, my mpmovieplayer is on a tableviewCell if you're wondering. Hope this helps and let me know if you have questions. Good luck!
MPMoviePlayerLoadStateDidChangeNotification will be called when you single tap on the fast-forward or rewind button. You should check the loadState and just give it the path to your video and prepareToPlay again.
- (void)moviePlayerLoadStateChanged:(NSNotification *)notification {
MPMoviePlayerController *moviePlayer = notification.object;
MPMovieLoadState loadState = moviePlayer.loadState;
if(loadState == MPMovieLoadStateUnknown) {
moviePlayer.contentURL = [NSURL fileURLWithPath:videoPath]
[moviePlayer prepareToPlay];
}
.....
}
The reason you're getting MPMovieFinishReasonPlaybackEnded is because playback reached the end of the video (sorry if this is obvious). So it seem's your seek forward actions are seeking all the way to the end of the video. You can check the playback state with MPMoviePlaybackStateSeekingForward.
A quick solution could be to create your own forward button that seek's ahead by a specified time (ie. 5 seconds). But perhaps this isn't the functionality you're looking for.

UITextfield doesn't show keyboard after dismiss from presentMoviePlayerViewControllerAnimated

I am working with an app which required to open "MPMoviePlayerViewController" when clicked on a video file.
There is a tabbarController into our app which has four Navigation controllers for four tabs.
My app only support portrait orientation, but video should support both landscape & portrait orientations. So, I make Subclass of "MPMoviePlayerViewController".
Code of that class..
#interface MyMovieViewController : MPMoviePlayerViewController
#end
#implementation MyMovieViewController
-(void)viewDidLoad{
[self setWantsFullScreenLayout:NO];
}
-(void)viewWillDisappear:(BOOL)animated{
[self resignFirstResponder];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return YES;
}
-(BOOL)shouldAutorotate{
return YES;
}
#end
In First tab, I show Gallery. There is a code to open video file.
UIGraphicsBeginImageContext(CGSizeMake(1,1));
MPMoviePlayerViewController * vc = [[MyMovieViewController alloc] initWithContentURL:[[[elcAsset asset] valueForProperty:ALAssetPropertyURLs] valueForKey:[[[[elcAsset asset] valueForProperty:ALAssetPropertyURLs] allKeys] objectAtIndex:0]]];
UIGraphicsEndImageContext();
// Remove the movie player view controller from the "playback did finish" notification observers
[[NSNotificationCenter defaultCenter] removeObserver:vc
name:MPMoviePlayerPlaybackDidFinishNotification
object:vc.moviePlayer];
// Register this class as an observer instead
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: #selector(doneButtonClick:)
name: MPMoviePlayerPlaybackDidFinishNotification
object: vc.moviePlayer];
[self presentMoviePlayerViewControllerAnimated:vc];
[vc.moviePlayer prepareToPlay];
[vc.moviePlayer play];
Video is working file. It also support both orientations. but when I switch to another tab, then no textfield show keyboard when clicked on it.
Please help. Thanks is advance.
I have solved the issue.
Actually, MPMoviePlayerViewController support both orientations and my app support only portrait. When i dismiss MPMoviePlayerViewController,then parentview of it think that it is Landscape mode.
So, it display landscape keyboard, which's CGPoint is lower than screen. Thats why I am not able to see keyboard.
I have make write following code into my CustomNavigationController.
- (BOOL)shouldAutorotate
{
return [self.visibleViewController shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations
{
return [self.visibleViewController supportedInterfaceOrientations];
}
Now, it is working fine.
YOu need to dismiss Your MoviePlayer..
Use this notification that calls when your video is over
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myMovieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:self.theMoviePlayer];
and write method of it.
-(void)myMovieFinishedCallback:(NSNotification*)aNotification
{
[self dismissMoviePlayerViewControllerAnimated];
MPMoviePlayerController* theMovie = [aNotification object];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:theMovie];
}
You can try this delegate method,
(BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar{
[myTextField becomeFirstResponder];
return YES;
}
If this does not work, try to find out what is called after dismissing the view and then insert following code... This will definitely work.
[myTextField becomeFirstResponder];

MPMoviePlayerViewController stops after a few seconds

I have an app in which I am streaming a live TV channel in one of tabs. I am using MPMoviePlayerViewController. I did declare my MPMoviePlayerViewController in my header file and synthesize it in my implementation file.
Here's my viewDidAppear:
- (void)viewDidAppear:(BOOL)animated
{
NSURL *movieURL = [[NSURL alloc]initWithString:#"http://mysuperURL"];
moviePlayerController = [[MPMoviePlayerViewController alloc] initWithContentURL:movieURL];
[self checkIt];
}
And my checkIt function
- (void) checkIt {
if ([[moviePlayerController moviePlayer] loadState] == MPMovieLoadStateUnknown) { // before you wreck yourself
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(checkIt) userInfo:nil repeats:NO];
} else {
[moviePlayerController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentModalViewController:moviePlayerController animated:YES];
}
}
However the video freezes after two seconds and the app stops responding.
You should use the MPMoviePlayerNotifications instead of manually polling the current state.
For example - somewhere in you initializing code:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(MPMoviePlayerLoadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
Now implement a notification handler:
- (void)MPMoviePlayerLoadStateDidChange:(NSNotification *)notification
{
NSLog(#"loadstate change: %Xh", movieController_.loadState);
}
And somewhere within your deinitializing code:
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
Also note that MPMoviePlayerController.loadState is a bitmap -> you need to mask out the value you want to check for.
For Example:
if ((movieController_.loadState & MPMovieLoadStatePlayable) == MPMovieLoadStatePlayable)
{
NSLog(#"yay, it became playable");
}
Asfar as my knowledge concern usage of timer it is freezing and it takes time streaming also.

iPad MPMoviePlayerController - Disable Fullscreen

Is there a way to disable the fullscreen button of the MPMoviePlayerController ?
Just did it:
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieEventFullscreenHandler:)
name:MPMoviePlayerWillEnterFullscreenNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieEventFullscreenHandler:)
name:MPMoviePlayerDidEnterFullscreenNotification
object:nil];
self.moviePlayer.controlStyle = MPMovieControlStyleEmbedded;
}
- (void)movieEventFullscreenHandler:(NSNotification*)notification {
[self.moviePlayer setFullscreen:NO animated:NO];
[self.moviePlayer setControlStyle:MPMovieControlStyleEmbedded];
}
Depending on your needs, you can also simply disable all user interactions on the player view.
player.view.userInteractionEnabled = NO;
You can set controlStyle to Fullscreen. these controls are somewhat different, but it doesn't feature a Fullscreen button!
[_moviePlayerController setControlStyle:MPMovieControlStyleFullscreen];
You could hide the playback controls and add your own custom ones, this will prevent the default buttons being rendered at all
I.e with
[player setMovieControlMode:MPMovieControlModeNone];
Unfortunately none of above worked for me properly, so picking the above I implemented the following (and worked fine):
Hide the full screen button.
Add this code in the method where you initialise the movie player.
....
//Because we have to wait until controllers are shown
[self performSelector:#selector(hideFullscreenButton) withObject:self afterDelay:0.5];
...
Add the methods:
-(void) hideFullscreenButton{
//Hide full screen mode button
[self hideFullscreenSubview:movieClip.view.subviews];
}
-(void) hideFullscreenSubview:(NSArray*)arr{
for(UIView *v in arr){
if([v.subviews count]>0)
[self hideFullscreenSubview:v.subviews];
else
NSLog(#"%#",v);
if(v.frame.origin.x==975 ){
v.hidden=TRUE;
}
}
}
The problem relies that there is no tag to identify which view you have to hide. In my case I figure it out by the view coordinates.
Overwrite tap gestures for do not allowing fullscreen zoom.
movieClip.controlStyle = MPMovieControlStyleEmbedded;
//Disable tap for not allowing that video control set on a full screen mode.
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget: self action:#selector(doSingleTap)];
singleTap.numberOfTapsRequired = 1;
[movieClip.view addGestureRecognizer:singleTap];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget: self action:#selector(doDoubleTap)];
doubleTap.numberOfTapsRequired = 2;
[movieClip.view addGestureRecognizer:doubleTap];
[singleTap requireGestureRecognizerToFail:doubleTap];
And add the selector methods:
-(void) doSingleTap{
//DO NOTHING!!!
}
-(void) doDoubleTap{
//DO NOTHING!!!
}
There's a cheat:
MPMoviePlayerController *mpc = (...some instance...)
UIView *fsbutton = [[mpc view] viewWithTag:512];
[fsbutton setHidden:YES];
The main catch is, you have to do it in viewDidAppear: or similar, because the MoviePlayer view sets itself up somewhere inside didMoveToWindow or didMoveToSuperview, which happen after viewWillAppear:. So you get a brief flash of the fullscreen button. Other obvious catches include: brittle vs. Apple changing that 512 tag value (although it works in 3.2 - 4.2); and of course Apple would rather you not do this.
The endorsed solution is to set the control style to MPMovieControlStyleNone and roll your own transport controls, which is more work.
No, there is no way. Hopefully with the next update.
in order to disable switch to full screen mode, either form button or pinch gesture, you can use this:
moviePlayer.controlStyle = MPMovieControlStyleNone;
moviePlayer.view.userInteractionEnabled =NO;
Wired does this. For the videos that start in fullscreen, they have the standard MPMoviePlayerController controls, but are missing the fullscreen buttons. And they're using the standard built-in ones, since they suddenly got an AirPlay button with 4.2.
Simple block to remove pinch zoom here
Hope it help
it work with me on iOS6
for (UIView *view in moviePlayer.view.subviews) {
for(UIPinchGestureRecognizer *pinch in view.gestureRecognizers){
if([pinch isKindOfClass:[UIPinchGestureRecognizer class]])
[view removeGestureRecognizer:pinch];
}
}
This worked on iOS 7, iPhone 5s.
Add Notification:
MPMoviePlayerDidEnterFullscreenNotification : #"moviePlayFullscreenNote:"
- (void)moviePlayFullscreenNote:(NSNotification*)notification
{
if (notification.object == self.videoPlayer)
{
[self.videoPlayer setFullscreen:NO animated:YES];
self.videoPlayer.controlStyle = MPMovieControlStyleEmbedded;
}
}
Notice that I only listen for "DID" and not the "WILL" notification as well as running it animated. I think this works as it gives the system time to react. When I used the "WILL" and "DID" as noted in answers above it led to a black screen with no controls. There is a slight glitch that is visible when the transition occurs, but I need the play/scrub buttons from embedded.
Fullscreen button along with pause button can be removed.
[self.videoPlayer setControlStyle:MPMovieControlStyleNone];
If the only thing you want to do is disable pinch to go full screen (i.e. keep interaction enabled and whatever control style you want), you can use this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSSet *set = [event allTouches];
NSArray *arr = [set allObjects];
for (int i = 0; i < arr.count; i++) {
UITouch *touch = (UITouch *) [arr objectAtIndex:i];
NSArray *recognisers = touch.gestureRecognizers;
for (UIGestureRecognizer *recogniser in recognisers) {
if (recogniser.enabled && [recogniser isMemberOfClass:[UIPinchGestureRecognizer class]]) {
recogniser.enabled = NO;
}
}
}
}
This is the Swift version of the first solution of Javier Calatrava Llavería:
func hideFullScreenButton() {
self.hideFullScreenSubview((self.moviePlayerController?.view.subviews)!)
}
func hideFullScreenSubview(subviews: [UIView]) {
for view: UIView in subviews {
if view.subviews.count > 0 {
self.hideFullScreenSubview(view.subviews)
}
if view.frame.origin.x == 631 {
view.hidden = true
}
}
}
And when the user taps on Play:
self.performSelector(#selector(VideoViewController.hideFullScreenButton), withObject: self, afterDelay: 0.5)
(VideoViewController is the view controller in which I have the MPMoviePlayerController)
I know, it's a little outdated, but anyway. I did some research in that direction, and looks like a found an answer. I do not know, why it's working, but it is.
-(void) playMovieAtURL: (NSURL*) theURL {
MPMoviePlayerController* theMovie =
[[MPMoviePlayerController alloc] initWithContentURL: theURL];
//That line is for ARC. Without it, it may not work.
self.moviePlayer = theMovie;
theMovie.scalingMode = MPMovieScalingModeAspectFill;
theMovie.controlStyle = MPMovieControlStyleFullscreen;
theMovie.repeatMode = MPMovieRepeatModeOne;
//Here you'd better use your custom ViewController subclass, if you want autorotating and all that stuff.
UIViewController * vc = [UIViewController new];
[vc.view addSubview:theMovie.view];
theMovie.fullscreen = YES;
theMovie.view.frame = vc.view.bounds;
vc.view = theMovie.view;
[self presentModalViewController:vc animated:YES];
theMovie.fullscreen = YES;
[theMovie prepareToPlay];
[theMovie play];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myMovieFinishedCallback:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
}
// When the movie is done, release the controller.
-(void) myMovieFinishedCallback: (NSNotification*) aNotification
{
[self dismissModalViewControllerAnimated:YES];
MPMoviePlayerController* theMovie = [aNotification object];
[[NSNotificationCenter defaultCenter]
removeObserver: self
name: MPMoviePlayerPlaybackDidFinishNotification
object: theMovie];
[self.moviePlayer.view removeFromSuperview];
self.moviePlayer = nil;
// Release the movie instance created in playMovieAtURL:
}
Put a UIView or UIButton with transparent background on top of the view that shows the video, so that the user won't be able to tap on the view that contains the video.

Resources