Objective-C equivalent of simple Swift video player does not show video - swift

I'm creating a simple storyboard that plays an mp4 video. This works as expected in Swift, but when I try to do the exact same thing in Objective-C nothing happens.
Can anyone see if I'm doing anything wrong in the Objective-C code converted from Swift?
Notes:
Both projects are empty except for the view controller implementation
The video file anim2.mp4 is indeed included in both projects
For technical reasons, the video player must utilize Objective-C
Code:
// Swift implementation
import UIKit
import AVKit
import AVFoundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let path = Bundle.main.path(forResource: "anim2", ofType:"mp4");
let url = NSURL(fileURLWithPath: path!) as URL;
let player = AVPlayer(url: url);
let playerLayer = AVPlayerLayer(player: player);
playerLayer.frame = self.view.bounds;
self.view.layer.addSublayer(playerLayer);
player.play();
}
}
// Objective-C implementation
#import "ViewController.h"
#import <AVKit/AVKit.h>
#import <AVFoundation/AVFoundation.h>
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString* path = [[NSBundle mainBundle] pathForResource:#"anim2" ofType:#"mp4"];
NSURL* url = [NSURL fileURLWithPath:path isDirectory:false];
AVPlayer* player = [[AVPlayer alloc] initWithURL:url];
AVPlayerLayer* playerLayer = [[AVPlayerLayer alloc] initWithLayer:player];
playerLayer.frame = self.view.bounds;
[self.view.layer addSublayer:playerLayer];
[player play];
}
#end

This line is not the same:
AVPlayerLayer* playerLayer = [[AVPlayerLayer alloc] initWithLayer:player];
That tries to treat an AVPlayer as a CALayer, which is going to quietly fail. You don't get a warning here because initWithLayer: takes id as its type.
What you meant was:
AVPlayerLayer* playerLayer = [AVPlayerLayer playerLayerWithPlayer: player];

Related

How to use AVPlayer in multiple view and resume controls?

i create an app with xcode 4.6 on iOS 6.1 with a TabBar and 2 different tableView.
Each table view read a line form a file.plist and when tap on a row, you load a DetailView.
The DetailViewController is the same for the 2 TableView but:
If start app tap on a first button i see FirstView with my table and Audio List, if I choose one file on list I load a Detail view and listener my file very well, but if now tap on second view on TabBar i can start playing other audio file from the second view but i listen overlay to firs audio and AVplayer not override.
There my coding:
From TableView loading plist:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([#"detailList" isEqualToString:segue.identifier]) {
NSIndexPath *index = [self.tableView indexPathForCell:sender];
[[segue destinationViewController] setSaved:[loadData objectAtIndex:index.row]];
}
}
In a detali view:
if (mysound.rate == 1.0) {
UIImage *img = [UIImage imageNamed:#"Play_BT.png"];
[control setImage:img forState:UIControlStateNormal];
[mysound pause];
} else {
UIImage *img = [UIImage imageNamed:#"Pause_BT.png"];
[control setImage:img forState:UIControlStateNormal];
NSURL *url = [NSURL URLWithString:[saved objectForKey:#"audio"]];
mysound = [[AVPlayer alloc] initWithURL:url];
[mysound setAllowsExternalPlayback:YES];
[mysound setUsesExternalPlaybackWhileExternalScreenIsActive: YES];
[mysound play];
}
}
Here incoming the issues, this code working very goog, but if tapo on tab bar and load other audio from other plist I listen Audio overlay.
How can use AVPlayer in Override the NSURL *url = [NSURL URLWithString:[saved objectForKey:#"audio"]];when change from tabbar?
Thanks.
Solved, using this code:
In my AppDelegate .h adding
#interface AppDelegate : UIResponder <UIApplicationDelegate> {
AVPlayer *MyPlayer;
}
#property (strong, nonatomic) AVPlayer *MyPlayer;
in my AppDelegate .m inside didFinishLaunchingWithOptions adding
[MySound superclass];
Now can calling a method in all app with this code directly inside a .m file:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSURL *url = [NSURL URLWithString:[saved objectForKey:#"link"]];
appDelegate.MySound = [[AVPlayer alloc] initWithURL:url];
[appDelegate.MySound setAllowsExternalPlayback:YES];
[appDelegate.MySound setUsesExternalPlaybackWhileExternalScreenIsActive: YES];
[appDelegate.MySound play];
With this method now is possible controlling the AVPlayer evry where in the APP!!
I Hope this Help any guys ;)

When using storyboards, and changing a view, sound will not stop playing in new view

I am trying to do something similar to what was requested in this question:
Playing audio with controls in iOS
I am using Storyboards with XCode 4.2. On my initial view page, I just have buttons that call other views, and no sounds are played there. These other views contain a UINavigationController with a BarButtonItem to allow you to go back. Other view pages will have buttons that allow one to play a sound. Each sound has its own play button, and there are resume, pause and stop buttons.
All sounds play fine, but if I hit the back button to the main page, the sound keeps playing. The desired behavior is for the sound to stop when navigating back to the main page via the back button.
Before storyboards, I could easily code a back button to stop the audio, but storyboards appear not that simple. It appears that I have to implement the method prepareForSegue. Okay, I can set the Segue identifier can be set in the Attributes Inspector, and referring back to the earlier post, I will use showPlayer.
But I was thinking I could code the prepareForSegue in one of my sound views, as seen in this cool example:
http://www.scott-sherwood.com/?p=219
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([[segue identifier] isEqualToString:#"showPlayer"]){
SoundPageController *theAudio = (SoundPageController *)[segue ViewController];
theAudio.delegate = self;
}
}
In the main page ViewController.m, I tried adding:
#property (strong, nonatomic) AVAudioPlayer *theAudio;
And I tried adding something like:
- (void)viewDidLoad {
[super viewDidLoad];
// this is the important part: if we already have something playing, stop it!
if (audioPlayer != nil)
{
[audioPlayer stop];
}
// everything else should happen as normal.
audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:&error];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
I wasn't able to get this to work, so I reverted the changes back to original. I will put my simplified code below, and if you have suggestions how to do it right, please let me know!
ViewController.h:
#interface ViewController : UIViewController {
}
ViewController.m (pretty much default):
#import "ViewController.h"
#import "AppDelegate.h"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
SoundPage1.h:
#import
#import <AVFoundation/AVAudioPlayer.h>
#interface occupy : UIViewController <AVAudioPlayerDelegate> {
AVAudioPlayer* theAudio;
}
//-(IBAction)PressBack:(id)sender; now the back button is linked up through storyboard
-(IBAction)pushButton1;
-(IBAction)pushButton2;
-(IBAction)play;
-(IBAction)stop;
-(IBAction)pause;
#end
SoundPage1.m
#import "SoundPage1.h"
#import "AppDelegate.h"
#import "ViewController.h"
#implementation SoundPage1
-(IBAction)pushButton {
NSString *path = [[NSBundle mainBundle] pathForResource:#"sound1" ofType:#"mp3"];
if(theAudio)[theAudio release];
theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
theAudio.delegate = self;
theAudio.volume = 1.0;
[theAudio play];
}
-(IBAction)pushButton2 {
NSString *path = [[NSBundle mainBundle] pathForResource:#"sound2" ofType:#"mp3"];
if(theAudio)[theAudio release];
theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
theAudio.delegate = self;
theAudio.volume = 1.0;
[theAudio play];
}
// Old code from XCode 3.x when creating a back button that could stop the audio was easy
//-(IBAction)PressBack:(id)sender{
// [theAudio stop];
// ViewController* myappname = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
// [self.navigationController pushViewController:myappname animated:NO];
// }
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
Add in the code for play, stop and pause for the buttons.
I tried to format this, sorry in advance if it is hard to read.
Thanks for any suggestions!
Rob
You should stop the AVAudioPlayer in viewWillDisappear.
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if(theAudio && theAudio.isPlaying) {
[theAudio stop];
}
}
ViewDidUnload is only called by the program when the device is reaching the end of memory capacity, not when you change views. Only just found this out myself this week, while struggling with the same problem.
Hope that clears things up.

How can I update music volume on iPhone, through an UISlider?

I've tried to make an UISlider that controls background music volume, and whatever I set the value to should be the volume of the music. The trouble is, when I run the app in simulator, and move the slider, the volume stays that of the original volume, it doesn't update.
I am new to programming so I still need help with the basics.
Here is my viewcontroller.h:
#interface FifthViewController : UIViewController <AVAudioPlayerDelegate> {
IBOutlet UISlider *slider;
IBOutlet UIAlertView *alert;
AVAudioPlayer *theAudio;
}
-(IBAction)alertbutton;
-(IBAction)changeVolume;
#end;
and here is the viewcontroller.m:
- (void)viewDidLoad
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"background music" ofType:#"mp3"];
AVAudioPlayer* theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
theAudio.delegate = self;
[theAudio play];
theAudio.numberOfLoops = -1;
theAudio.volume=slider.value;
[super viewDidLoad];
}
I have connected "slider" to a horizontal slider and the sound plays and loops. And if I set the value of the slider higher or lower in Xcode, when I run the app, the volume adjusts accordingly. But if in the app I change the value of the slider, the volume remains the same . Can anyone look at my code and tell me what I'm doing wrong and how to fix it?
Assuming you have an instance of AVAudioPlayer setup in your controller.
Set the slider to run from 0.0 to 1.0 and connect it to an action.
-(void)volumeValueChange:(UISlider *)sender
{
(AVAudioPlayer *)myaudioplayer.volume = sender.value;
}
//.m implementation where audioPlayer is an an object of AVAudioPlayer
-(void)volumeValueChange:(UISlider *)sender
{
audioPlayer.volume = sender.value;
}

MPMoviePlayerController switching movies causes white flash

I have a small UIView that displays a repeated movie. When the user taps a button another movie is loaded and displayed in the same UIView.
The problem is that there is a half second "flash" between the removing of the first movie and the displaying of the second. Is there any to remove this?
- (void) setUpMovie:(NSString*)title {
NSString *url = [[NSBundle mainBundle] pathForResource:title ofType:#"mp4"];
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath:url]];
[[player view] setFrame:self.movieView.bounds];
[self.movieView addSubview:player.view];
if ([title isEqualToString:#"Bo_idle_02"]) {
[player setRepeatMode:MPMovieRepeatModeOne];
} else {
[player setRepeatMode:MPMovieRepeatModeNone];
}
[player setControlStyle:MPMovieControlStyleNone];
[player play];
}
- (void) startDanceAnimation { [self setUpMovie:#"Bo_dance_02"]; return; }
I managed to get my movies changing without the white flash using the AVFoundation as suggested previously. sudo code below, hope it helps someone :)
Apples reference docs can be found here and I used them to get most of my information:
http://developer.apple.com/library/ios/#documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/00_Introduction.html#//apple_ref/doc/uid/TP40010188-CH1-SW3
The first thing I did was to add the following class, called PlayerView (can't remember where I got it sorry) to my project. Its a subclass of UIView and its the view that the movie will be displayed in. Once you add it to your project open UI Builder, add a new view to an existing xib and change its class to PlayerView. Connect this using an IBOutlet. Again remember this is the view that will display the movie.
PlayerView.h
#import
#import
#import
#interface PlayerView : UIView {
AVPlayer *player;
}
#property (nonatomic,retain) AVPlayer *player;
#end
PlayerView.m
#import "PlayerView.h"
#implementation PlayerView
#synthesize player;
+ (Class)layerClass {
return [AVPlayerLayer class];
}
- (AVPlayer*)player {
return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer *)player {
[(AVPlayerLayer *)[self layer] setPlayer:player];
}
- (void) dealloc {
[super dealloc];
[player release];
}
#end
In the ViewContoller that displays the movies I have the following:
DisplayMovies.h
#import
#import
#class PlayerView;
#interface DisplayMovies : UIViewController {
IBOutlet AVPlayer *player;
AVPlayerItem *movieOneItem;
AVPlayerItem *movieTwoItem;
}
#property (nonatomic, retain) AVPlayer *player;
#property (retain) AVPlayerItem *movieOneItem;
#property (retain) AVPlayerItem *movieTwoItem;
DisplayMovies.m
#implementation DisplayMovies
#synthesize player, movieOneItem, movieTwoItem;
- (void)viewDidLoad {
// load the two movies
NSURL *movieOneItemURL = [[NSBundle mainBundle] URLForResource:#"movieOne" withExtension:#"mp4"];
AVURLAsset *movieOneItemAsset = [AVURLAsset URLAssetWithURL:movieOneItemURL options:nil];
self.movieOneItem = [AVPlayerItem playerItemWithAsset:movieOneItemAsset];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieOneItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.movieOneItem];
NSURL *movieTwoItemURL = [[NSBundle mainBundle] URLForResource:#"movieTwo" withExtension:#"mp4"];
AVURLAsset *movieTwoItemAsset = [AVURLAsset URLAssetWithURL:movieTwoItemURL options:nil];
self.movieTwoItem = [AVPlayerItem playerItemWithAsset:movieTwoItemAsset];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieTwoItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.movieTwoItem];
[self.player play];
}
- (void) movieOneItemDidReachEnd:(NSNotification*)notification {
// play movie two once movie one finishes
[self.player seekToTime:kCMTimeZero];
[self.player replaceCurrentItemWithPlayerItem:self.movieTwoItem];
[self.player play];
}
- (void) movieTwoItemDidReachEnd:(NSNotification*)notification {
// play movie one once movie two finishes
[self.player seekToTime:kCMTimeZero];
[self.player replaceCurrentItemWithPlayerItem:self.movieOneItem];
[self.player play];
}
The solution posted above didn't work for me. I was still getting short flashes between tracks. I was able to solve the problem by creating two views each with its own AVPlayer. I set them both playing and then switch between them by hiding the top view. This got rid of the flash, and I was able to animate a transition between the two tracks by hiding the top view by setting its alpha to 0.0 instead of using setHidden. Here is my code:
AVPlayerItem *theAsset = [self generatePlaylistAssetWithVideoOne]; //In my app this returns an AVPlayer Item
layerView1 = [[VideoLayerView alloc] initWithFrame:CGRectMake(-50, 0, 580, 320)]; //VideoLayerView is a subclass of UIView with the video layer stuff added in.
[layerView1 setPlayer:thePlayer];
[thePlayer play];
[self.view addSubview:layerView1];
AVPlayerItem *theAsset2 = [self generatePlaylistAssetWithVideoTwo];
[thePlayer2 setActionAtItemEnd:AVPlayerActionAtItemEndNone];
layerView2 = [[VideoLayerView alloc] initWithFrame:CGRectMake(-50, 0, 580, 320)];
[layerView2 setPlayer:thePlayer2];
[thePlayer2 play];
[self.view addSubview:layerView2];
Then to animate between the videos I used:
if (water) //Water is a BOOL set up earlier in the file
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
///DO STUFF
[layerView2 setAlpha:0];
[UIView commitAnimations];
water = NO;
}
else
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
///DO STUFF
[layerView2 setAlpha:1.0];
[UIView commitAnimations];
water = YES;
}
To change the videos, you just need to set the hidden view to start playing what you want and then do the switch after the flash.
I still have to come back to this project but I have received some more advice. I will be back working on this project in hopefully two weeks so I will let ye know which solution worked (fingers crossed) for me. In the mean time here's my options:
1.
You won't be able to use MPMoviePlayerController to switch the movie without a flash.
As an alternative, you can use AV Foundation. The AVPlayer -replaceCurrentItemWithPlayerItem: method will seamlessly transition from the old player item to the new one without flashes.
For more information about programming with AV Foundation, see the "AV Foundation Programming Guide" which you can find here:
http://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/AVFoundationPG/
2.
You can merge all your movies into a single (large) movie, then use the MPMediaPlayback currentPlaybackTime / endPlaybackTime properties to "select" and play the desired movie (within the large movie). If you use this technique you'll also need to make sure there's a key frame at the point in time where the new movie starts.
3.
Alternately, create a single MPMoviePlayerController object, use the -setContentURL: to set the new movie URL, and take advantage of the backgroundView property to make a more seamless transition. The backgroundView is automatically sized with the view property, but it will be hidden before the movie is preloaded. You can put a black view behind both movie views, and then make the backgroundView's backgroundColor = black, which should make it appear more seamless.
Hope this helps.
Find solution here http://joris.kluivers.nl/blog/2010/01/04/mpmovieplayercontroller-handle-with-care/ from iOS 6 you need to use [self.moviePlayer prepareToPlay]; and catch MPMoviePlayerReadyForDisplayDidChangeNotification to use [self.moviePlayer play];

Can't play video file

I'm probably missing something really obvious.
I've included <MediaPlayer/MediaPlayer.h> and have this code:
NSURL *videoURL = [NSURL fileURLWithPath:pathToFile];
MPMoviePlayerViewController *mediaPlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:videoURL];
[self presentMoviePlayerViewControllerAnimated:mediaPlayer];
mediaPlayer.view.backgroundColor = [UIColor blackColor];
[mediaPlayer release];
But the video won't play. I copied the code from another place where the video works perfectly.
pathToFile is correct becuase the variable is used in previous lines to move the video from the resources folder to the documents directory.
Any ideas why it might not be working?
Thanks
You can use following code except to presentMoviePlayerViewControllerAnimated
NSString *movieFile=[[NSBundle mainBundle] pathForResource:#"movie" ofType:#"m4v"];
moviePlayer= [[MPMoviePlayerController alloc] initWithContentURL:url];
moviePlayer.view.frame = self.view.frame;
[self.view addSubview:moviePlayer.view];
[moviePlayer setFullscreen:YES animated:YES];
this works fine for me.
I don't think presentMoviePlayerViewControllerAnimated retains its receiver, so it looks as though you're releasing your movie player too early. You could try making mediaPlayer a retained property:
#interface MyClass : SuperClass {
MVMoviePlayerViewController *mediaPlayer;
}
#property (nonatomic, retain) MVMoviePlayerViewController *mediaPlayer;
#end
#implementation MyClass
#synthesize mediaPlayer;
// rest of class implementation here...
#end
Then initialise as so:
self.mediaPlayer = [[[MPMoviePlayerViewController alloc]
initWithContentURL:videoURL] autorelease];
And release afterwards with:
self.mediaPlayer = nil;
(To write code that happens after the video has finished playing, check out the MPMoviePlayerPlaybackDidFinishNotification notification.)
Also bear in mind that presentMoviePlayerViewControllerAnimated first appeared in iOS 3.2, so this code won't work on earlier iOS versions. But I don't think that's the problem in this case.