Getting first frame of a captured video in iOS 5.0+ - ios

My app used to be able to get the first video frame using the code below - works for iOS 4.0+. But once my device is upgraded to iOS 5.0.1, the returned image is always NULL.
+(UIImage *)fFirstVideoFrame:(NSString *)path
{
MPMoviePlayerController *mp = [[MPMoviePlayerController alloc]
initWithContentURL:[NSURL URLWithString:path]];
UIImage *img = [mp thumbnailImageAtTime:0.0
timeOption:MPMovieTimeOptionNearestKeyFrame];
[mp stop];
[mp release];
return img;
}
Does anyone know how to get video frames in iOS 5.0+? Thanks.

Here is why (from MPMoviePlayerController documentation):
Behavior in iOS 4.3 and Earlier
In iOS 4.3 and earlier, a new movie player was automatically prepared to play. Starting in iOS 5.0, in order to facilitate finer-grained playback control, a new movie player is not automatically prepared to play. See the discussion for the initWithContentURL: instance method.
The discussion says, in part:
.. To prepare a new movie player for playback, call the prepareToPlay method ..
To be notified when a new movie player is ready to play, register for the MPMoviePlayerLoadStateDidChangeNotification notification. You can then check load state by accessing the loadState property.
Once the movie is ready to play, then you can request thumbnail images from it.

Related

How do I configure AVPlayer to play a video that was recorded at 120fps?

I have a simple app that records video at 120fps, saves it to the documents directory, and then uses an instance of AVPlayer to play it back.
If I set the playback rate to 1.0 (for the AVPlayer instance), the playback is a bit choppy (probably more along the lines of 60fps). However, if I interrupt the playback by pressing the "play" button (which then sets the playback rate to 1.0 again!), it plays back very smoothly.
For instance, this is what occurs when the user presses the play button (and yes, I observe the 'status' property of AVPlayer before allowing the user to play the video):
- (IBAction)playButtonPressed:(id)sender
{
[self.videoPlayer seekToTime:kCMTimeZero];
[self.videoPlayer setRate:1];
self.videoPlayer.volume = 1;
}
And this is my simple player-setup code:
- (void)setUpAVPlayer
{
self.videoURL = [[NSURL alloc] initFileURLWithPath:self.videoURL.path];
self.videoPlayer = [AVPlayer playerWithURL:self.videoURL];
[self.previewLayer setPlayer:self.videoPlayer];
// observe player status
[self.videoPlayer addObserver:self
forKeyPath:#"status"
options:0
context:nil];
}
There really is nothing unusual about what I'm doing. It plays back video fine. It's only the case where the video was recorded at 120fps that there is a playback issue.
(Also, this is running on my iPhone5s since it is the only device that supports 120fps.)

MPMoviePlayerController fail to play audio

I use MPMoviePlayerController to play a list of audio streams from url. I use the following code to init the player
self.player = [[[MPMoviePlayerController alloc] init] autorelease];
self.player.controlStyle = MPMovieControlStyleNone;
and later use the following code to set and reset contentUrl:
self.player.contentURL = url;
[self.player prepareToPlay];
but sometimes, not every time, it fails to play audio, post MPMoviePlayerPlaybackDidFinishNotification directly and gives following userInfo
{
MPMoviePlayerPlaybackDidFinishReasonUserInfoKey = 1;
error = "Error Domain=MediaPlayerErrorDomain Code=-11828 \"Cannot Open\" UserInfo=0xee7bf20 {NSLocalizedDescription=Cannot Open}";
}
anyone knows why?
I would suggest you use AVPlayer instead of MPMoviePlayerController.
I had seen such issues before when streaming audio with MPMoviePlayerController. AVPlayer is the more appropriate one for this task.

How to mute/unmute audio when playing video using MPMoviePlayerController?

I've created my own custom controls for use with the MPMoviePlayerController. So far everything works except the mute button control.
I've configured the AVAudioSession using the following code before I create my instance of the MPMoviePlayerController.
NSError *modeError = nil;
[self.audioSession setMode:AVAudioSessionModeMoviePlayback error:&modeError];
if (modeError != nil) {
NSLog(#"Error setting mode for AVAudioSession: %#", modeError);
}
NSError *categoryError = nil;
[self.audioSession setCategory:AVAudioSessionCategoryPlayback error:&categoryError];
if (categoryError != nil) {
NSLog(#"Error setting category for AVAudioSession: %#", categoryError);
}
Then in my mute button callback method I have the following code:
NSError *activeError = nil;
[self.audioSession setActive:NO error:&activeError];
if (activeError != nil) {
NSLog(#"Error setting inactive state for AVAudioSession: %#", activeError);
}
When clicking the Mute button I get the following unuseful error:
Error Domain=NSOSStatusErrorDomain Code=560030580 "The operation couldn’t be completed. (OSStatus error 560030580.)"
I am linking to the AVFoundation framework.
This is really starting to bug me as I can't for the life of me work out a way to reduce or mute the playback audio of my application.
I don't want to change the system global volume just the application level volume as defined by the AVAudioSession AVAudioSessionCategoryPlayback category.
It seems that you can set the volume of the AVAudioPlayer but not the MPMoviePlayerController. I've seen other posts here on SO that say just create an instance of AVAudioPlayer and set the volume but this just causes my app to crash and I expect it has something to do with the fact I'm not using the initWithContentsOfURL:error: or initWithData:error: and instead using `init'.
Any help would be appreciated.
After speaking to an Apple technician it turns out that it's not possible to control or mute the audio using MPMoviePlayerController.
Instead you have to create your own controller using AVFoundations AVPlayer class.
Once you're using that it's a matter of creating a custom audio mix and setting the volume level. It actually works very well.
Sample code:
AVURLAsset * asset = [AVURLAsset URLAssetWithURL:[self localMovieURL] options:nil];
NSArray *audioTracks = [asset tracksWithMediaType:AVMediaTypeAudio];
// Mute all the audio tracks
NSMutableArray * allAudioParams = [NSMutableArray array];
for (AVAssetTrack *track in audioTracks) {
AVMutableAudioMixInputParameters *audioInputParams =[AVMutableAudioMixInputParameters audioMixInputParameters];
[audioInputParams setVolume:0.0 atTime:kCMTimeZero ];
[audioInputParams setTrackID:[track trackID]];
[allAudioParams addObject:audioInputParams];
}
AVMutableAudioMix * audioZeroMix = [AVMutableAudioMix audioMix];
[audioZeroMix setInputParameters:allAudioParams];
// Create a player item
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
[playerItem setAudioMix:audioZeroMix]; // Mute the player item
// Create a new Player, and set the player to use the player item
// with the muted audio mix
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
self.mPlayer = player;
[mPlayer play];
I've written an MPMoviePlayerController replacement class that adds support for volume level. I will upload the to github shortly and add the link in this post.
I know this is an old post, but I managed to find a way to successfully control the volume of the MPMoviePlayerController control in iOS6 & iOS7, using an MPVolumeView. One gotcha is that it does NOT work in the simulator, only on the physical device. For just controlling the volume, adding a hidden MPVolumeView will work fine. However if you use a hidden one, the native OS volume display that appears when you change the volume using the physical device volume buttons will still appear centre screen. If you want to prevent this, make sure your MPVolumeView is not hidden. Instead, you can give it a very low alpha transparency and place it behind other views, so the user can't see it.
Here's the code i've used:
MPVolumeView *volumeView = [[MPVolumeView alloc]initWithFrame:CGRectZero];
[volumeView setShowsVolumeSlider:YES];
[volumeView setShowsRouteButton:NO];
// control must be VISIBLE if you want to prevent default OS volume display
// from appearing when you change the volume level
[volumeView setHidden:NO];
volumeView.alpha = 0.1f;
volumeView.userInteractionEnabled = NO;
// to hide from view just insert behind all other views
[self.view insertSubview:volumeView atIndex:0];
This allows you to control the volume by calling:
[[MPMusicPlayerController applicationMusicPlayer] setVolume:0.0];
But I was still getting the native OS volume display appearing the first time I would try to change the volume - on subsequent loads it did not show this display, so figuring it was something to do with the stage in the viewcontroller life cycle, I moved it from my viewDidLoad method to the viewDidAppear method - it worked - the volume muted and the native volume display did not appear, but I now was able to hear a split second of audio before the video started playing. So I hooked into the playback state did change delegate of the MPMoviePlayerController. In viewDidload I added:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(videoPlaybackStateDidChange:)
name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
And the delegate callback method:
-(void)videoPlaybackStateDidChange:(NSNotification *)aNotification
{
// Note, this doesn't work in simulator (even in iOS7), only on actual device!
if ([moviePlayerController playbackState] == MPMoviePlaybackStatePlaying)
{
[[MPMusicPlayerController applicationMusicPlayer] setVolume:0.0];
}
}
This muted the audio of the video before it started playing, but after the viewDidLoad in the life cycle, so no native OS volume muted display.
In my app, I retrieved and stored the current volume level before muting (using [MPMusicPlayerController applicationMusicPlayer].volume property), and then restored the volume to this level when the view controller was closed, meaning the user would be unaware that their device volume level was modified and reverted.
Also, if your MPMoviePlayerController is using a non-standard audio route in iOS7, calling [[MPMusicPlayerController applicationMusicPlayer] setVolume:0.0] may not work for you - in this case you can loop through the subviews of your MPVolumeView control until you find a view which subclasses UISlider. You can then call [sliderView setValue:0 animated:NO] which should work for you. This method isn't using any private Apple APIs so shouldn't get your app rejected - after all there are so many legitimate reasons why you would offer this functionality, and it was possible in iOS6 without having to go to these lengths! In fact, I was bamboozled to discover that Apple had removed the functionality to set the volume on MPMoviePlayerController in iOS7 in the first place.. enforced migration to AVPlayer?
Update: My iPad app has now been approved using this method and is live on the app store.

Movieplayercontroller using a m4v with two audio tracks

I need use in my App a m4v video with two audio tacks. But when I play the video, it starts with the two audio tracks at the same time. I need to load the audio track with the device's default language (English by default).
Here is my code:
_moviePlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
[_moviePlayer shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeLeft];
_moviePlayer.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
[self presentMoviePlayerViewControllerAnimated:_moviePlayer];
MPMoviePlayerController just as well as the wrapping MPMoviePlayerViewController do not support the selection of audio tracks.

Open movie app with precise video on iOS

I would like to open the iPad movie player from my app to play a video.
The video will already be on the device, and the app is destined to ad-hoc distribution on pre-configured device, thus we can say the video will always be here.
My problem is that I can't figure out how to open the video app! I've searched a lot of things, including UTI, or embedded video, but that doesn't meet my needs.
Is there any way to simply open the player with the specified file ?
Do you need to open the video app per se? Or do you just need to play local video file from your app? In the latter case, you can just use MPMoviePlayerController (or the wrapping MPMoviePlayerViewController for iOS > 3.2).
Something like this works for me.
NSURL * url = [[NSBundle mainBundle] URLForResource:#"yourvideo" withExtension:#"mov"];
player = [[MPMoviePlayerController alloc] initWithContentURL:url];
player.view.frame = self.view.bounds;
player.movieSourceType = MPMovieSourceTypeFile;
[self.view addSubview:player.view];
player.shouldAutoplay = NO;
player.fullscreen = YES;
[player prepareToPlay];

Resources