AVPlayer And Local Files - iphone

I am building a MP3 player for iOS that plays audio files hosted on the web. I want to offer the ability to play the files offline so I have the files downloading using ASIHTTP but I am cannot seem to find info on initiallzing AVPlayer with a mp3 in the app documents directory. Has anyone done this before? Is it even possible?
*I posted an answer below that shows how to use the iOS AvPlayer for both local and http files. Hope this helps!

I decided to answer my own question because I felt like there is very little documentation on how to use the Apple provided AVPlayer for both local and stream (over http) files. To help understand the solution, I put together a sample project on GitHub in Objective-C and Swift The code below is Objective-C but you can download my Swift example to see that. It is very similar!
What I found is that the two ways of setting up the files are almost identical except for how you instantiate your NSURL for the Asset > PlayerItem > AVPlayer chain.
Here is an outline of the core methods
.h file (partial code)
-(IBAction) BtnGoClick:(id)sender;
-(IBAction) BtnGoLocalClick:(id)sender;
-(IBAction) BtnPlay:(id)sender;
-(IBAction) BtnPause:(id)sender;
-(void) setupAVPlayerForURL: (NSURL*) url;
.m file (partial code)
-(IBAction) BtnGoClick:(id)sender {
NSURL *url = [[NSURL alloc] initWithString:#""];
[self setupAVPlayerForURL:url];
}
-(IBAction) BtnGoLocalClick:(id)sender {
// - - - Pull media from documents folder
//NSString* saveFileName = #"MyAudio.mp3";
//NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
//NSString *documentsDirectory = [paths objectAtIndex:0];
//NSString *path = [documentsDirectory stringByAppendingPathComponent:saveFileName];
// - - -
// - - - Pull media from resources folder
NSString *path = [[NSBundle mainBundle] pathForResource:#"MyAudio" ofType:#"mp3"];
// - - -
NSURL *url = [[NSURL alloc] initFileURLWithPath: path];
[self setupAVPlayerForURL:url];
}
-(void) setupAVPlayerForURL: (NSURL*) url {
AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
AVPlayerItem *anItem = [AVPlayerItem playerItemWithAsset:asset];
player = [AVPlayer playerWithPlayerItem:anItem];
[player addObserver:self forKeyPath:#"status" options:0 context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (object == player && [keyPath isEqualToString:#"status"]) {
if (player.status == AVPlayerStatusFailed) {
NSLog(#"AVPlayer Failed");
} else if (player.status == AVPlayerStatusReadyToPlay) {
NSLog(#"AVPlayer Ready to Play");
} else if (player.status == AVPlayerItemStatusUnknown) {
NSLog(#"AVPlayer Unknown");
}
}
}
-(IBAction) BtnPlay:(id)sender {
[player play];
}
-(IBAction) BtnPause:(id)sender {
[player pause];
}
Check out the Objective-C source code for a working example of this.
Hope this helps!
-Update 12/7/2015 I now have a Swift example of the source code you can view here.

I got AVPlayer to work with local URL by prepending file:// to my local url
NSURL * localURL = [NSURL URLWithString:[#"file://" stringByAppendingString:YOUR_LOCAL_URL]];
AVPlayer * player = [[AVPlayer alloc] initWithURL:localURL];

Yes,thats possible to download and save the .mp3(or any kind of file)into NSDocument directory and then you can retrive from that and play by using AVAudioPlayer.
NSString *downloadURL=**your url to download .mp3 file**
NSURL *url = [NSURLURLWithString:downloadURL];
NSURLConnectionalloc *downloadFileConnection = [[[NSURLConnectionalloc] initWithRequest: [NSURLRequestrequestWithURL:url] delegate:self] autorelease];//initialize NSURLConnection
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *fileDocPath = [NSStringstringWithFormat:#"%#/",docDir];//document directory path
[fileDocPathretain];
NSFileManager *filemanager=[ NSFileManager defaultManager ];
NSError *error;
if([filemanager fileExistsAtPath:fileDocPath])
{
//just check existence of files in document directory
}
NSURLConnection is used to download the content.NSURLConnection Delegate methods are used to support downloading.
(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSFileManager *filemanager=[NSFileManagerdefaultManager];
if(![filemanager fileExistsAtPath:filePath])
{
[[NSFileManagerdefaultManager] createFileAtPath:fileDocPath contents:nil attributes:nil];
}
NSFileHandle *handle = [NSFileHandlefileHandleForWritingAtPath:filePath];
[handle seekToEndOfFile];
[handle writeData:data];
[handle closeFile];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
UIAlertView *alertView=[[UIAlertViewalloc]initWithTitle:#”"message:
[NSStringstringWithFormat:#"Connection failed!\n Error - %# ", [error localizedDescription]] delegate:nilcancelButtonTitle:#”Ok”otherButtonTitles:nil];
[alertView show];
[alertView release];
[downloadFileConnectioncancel];//cancel downloding
}
Retrieve the downloaded Audio and Play:
NSString *docDir1 = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *myfilepath = [docDir1 stringByAppendingPathComponent:YourAudioNameinNSDOCDir];
NSLog(#”url:%#”,myfilepath);
NSURL *AudioURL = [[[NSURLalloc]initFileURLWithPath:myfilepath]autorelease];
Just write your code to play Audio by using AudioURL
I Like to know if u have any clarification in this regard.
Thank you

Try this
NSString*thePath=[[NSBundle mainBundle] pathForResource:#"yourVideo" ofType:#"MOV"];
NSURL*theurl=[NSURL fileURLWithPath:thePath];

Swift local playback version, assuming I have a file "shelter.mp3" in my bundle:
#IBAction func button(_ sender: Any?) {
guard let url = Bundle.main.url(forResource: "shelter", withExtension: "mp3") else {
return
}
let player = AVPlayer(url: url)
player.play()
playerView?.player = player;
}
See here for details about playerView or playing a remote url.

its very difficult to play a song using a Avplayer why you are not use a MPMoviePlayerController player . i have play song from document directory .i am posting a code pls refer this .its working fine .and also u directly from url live .
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *publicDocumentsDir = [paths objectAtIndex:0];
NSString *dataPath = [publicDocumentsDir stringByAppendingPathComponent:#"Ringtone"];
NSString *fullPath = [dataPath stringByAppendingPathComponent:[obj.DownloadArray objectAtIndex:obj.tagvalue]];
[[UIApplication sharedApplication] setStatusBarHidden:NO animated:NO];
NSURL *url = [NSURL fileURLWithPath:fullPath];
videoPlayer =[[MPMoviePlayerController alloc] initWithContentURL: url];
[[videoPlayer view] setFrame: [self.view bounds]];
[vvideo addSubview: [videoPlayer view]];
videoPlayer.view.frame=CGRectMake(0, 0,260, 100);
videoPlayer.view.backgroundColor=[UIColor clearColor];
videoPlayer.controlStyle = MPMovieControlStyleFullscreen;
videoPlayer.shouldAutoplay = YES;
[videoPlayer play];
videoPlayer.repeatMode=YES;
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:#selector(moviePlayerEvent:) name:MPMoviePlayerLoadStateDidChangeNotification object:videoPlayer];
/* NSNotificationCenter *notificationCenter1 = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:#selector(moviePlayerEvent1:) name:MPMoviePlaybackStateStopped object:videoPlayer];
*/
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackStateChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:videoPlayer];
}
-(void)playbackStateChange:(NSNotification*)notification{
if([[UIApplication sharedApplication]respondsToSelector:#selector(setStatusBarHidden: withAnimation:)])
{
[[UIApplication sharedApplication] setStatusBarHidden:NO
withAnimation:UIStatusBarAnimationNone];
}
else
{
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
}
}
-(void)moviePlayerEvent:(NSNotification*)aNotification{
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:NO];
}
-(void)moviePlayerEvent1:(NSNotification*)aNotification{
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:NO];
}

Related

AVPlayer is Not Playing From NSCachesDirectory?

Hi I am Trying to Play Video Files From NSCachesDirectory. The Player is not loading the Files from NSCachesDirectory in videoView layer.
Is it Possible to play Video from cacheDirectory Memory?
Please suggest me...
I have tried this..
NSArray *myPathList = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesDir = [myPathList objectAtIndex:0];
NSString *songPath = [[NSString alloc] initWithString: [cachesDir stringByAppendingPathComponent:[NSString stringWithFormat:#"Song.%#", downloadURL]]];
NSURL *pathurl = [[NSURL alloc] initFileURLWithPath:songPath];
NSLog(#"--pathurl---%#",pathurl);
avPlayer =[AVPlayer playerWithURL:pathurl];
self.avPlayerLayer =[AVPlayerLayer playerLayerWithPlayer:avPlayer];
if ( !([avPlayer status] == AVPlayerStatusReadyToPlay) && (dataReceivedSoFar.length >10000))
{
avPlayerLayer.frame =CGRectMake(0, 0, 320, 450);
[[self.videoView layer] addSublayer:avPlayerLayer];
[avPlayer play];
NSLog(#"Video IS Playing");
[alert dismissWithClickedButtonIndex:0 animated:YES];
}
KVO approach.
Add observer:
self.player = [AVPlayer playerWithURL:url];
[self.player addObserver:self forKeyPath:#"status" options:0 context:&PlayerStatusContext];
Observing value
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if (context == &PlayerStatusContext) {
AVPlayer *thePlayer = (AVPlayer *)object;
if ([thePlayer status] == AVPlayerStatusFailed) {
NSError *error = [self.player error];
// Respond to error: for example, display an alert sheet.
return;
}
// Deal with other status change if appropriate.
if ([thePlayer status] == AVPlayerStatusReadyToPlay) {
[self play];
}
}
// Deal with other change notifications if appropriate.
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
return;
}
Playing:
- (IBAction)play:sender {
[self.player play];
}
EDITING:
As awolCZ said AVPlayer can not play partially downloaded files. But you could use something like http://test.com/test.mp3 for URL.
I'm not sure that [avPlayer status] == AVPlayerStatusReadyToPlay returns YES in this case. It takes some time for AVPlayer to load the item. You should can play immediately even if it's better way to track AVPlayer.status using KVO.
Lukas
EDIT: Try it this way (quick win):
NSArray *myPathList = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesDir = [myPathList objectAtIndex:0];
NSString *songPath = [[NSString alloc] initWithString: [cachesDir stringByAppendingPathComponent:[NSString stringWithFormat:#"Song.%#", downloadURL]]];
NSURL *pathurl = [[NSURL alloc] initFileURLWithPath:songPath];
NSLog(#"--pathurl---%#",pathurl);
avPlayer =[AVPlayer playerWithURL:pathurl];
self.avPlayerLayer =[AVPlayerLayer playerLayerWithPlayer:avPlayer];
avPlayerLayer.frame =CGRectMake(0, 0, 320, 450);
[[self.videoView layer] addSublayer:avPlayerLayer];
[avPlayer play];

how to read files from directory

i got a project that someone else write.
the project saves video file in this path,file://localhost/var/mobile/Applications/CAC8F2CB-1C7D-4805-BF1A-42B63B258E95/Documents/output.mp4
i want to play the file with MPMoviePlayerController, how can i access the file?
Firslty load the path and file URL:
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *localFilePath = [photoDirectoryPath stringByAppendingPathComponent:#"output.mp4"];
Then load up MPMoviePlayerController with the URL
- (void)initMoviePlayer
{
didExit = NO;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePreloadDidFinish:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:mMoviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
NSLog(#"initMoviePlayer: %# ",[self getMovieURL]);
mMoviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:[self getMovieURL]];
mMoviePlayer.shouldAutoplay = YES;
mMoviePlayer.view.hidden = YES;
[mMoviePlayer setControlStyle:MPMovieControlStyleNone];
mMoviePlayer.view.frame = [self.view bounds];
mMoviePlayer.scalingMode = MPMovieScalingModeNone;
[[self view] addSubview:[mMoviePlayer view]];
[mMoviePlayer play];
}
- (void) moviePreloadDidFinish:(NSNotification*)notification {
// start playing the movie
mMoviePlayer.view.hidden = NO;
animateTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(playMovie:)
userInfo:nil
repeats:NO];
}
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
}
Try this code mate,
NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0];
NSString *videoFilePath = [documentDirectory stringByAppendingPathComponent:#"output.mp4"];
NSURL *videoURL = [NSURL URLWithString:videoFilePath];
MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:videoURL];
Try this :-
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:#"output.mp4"];
Here in fullPath you will have path you mentioned above.
For more detail on how to play video, refer this tutorial
Hope this helps you..
If you store the above URL somewhere
NSString *fullURL = #"/var/mobile/Applications/CAC8F2CB-1C7D-4805-BF1A-42B63B258E95/Documents/output.mp4"
NSURL *movieURL = [NSURL fileURLWithPath: isDirectory:YES]; //If YES not works use NO
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
Hope that helps

XCODE IOS memory leak for AVAudioPlayer

I use ARC.
Here is the method for starting the audio in my code:
.h file:
#property (retain,nonatomic)AVAudioPlayer *myAudio;
and .m files
-(void)myPlaySound:(NSString *)mySoundFile NumberOfLoops:(int)loopsCount ofType:(NSString *)fileType
{
if([myAudio isPlaying])
{
[myAudio stop];
}
NSURL* musicFile = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:mySoundFile
ofType:fileType]];
NSError *error;
myAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:musicFile error:&error];
self.theAudio = myAudio;
myAudio.volume = audioVolume;
myAudio.numberOfLoops = loopsCount;
myAudio.currentTime = 0.0;
[myAudio prepareToPlay];
if([myAudio play] == NO)
{
NSLog(#"error");
}
}
Based on the button press, I play different audio files of different length and bit rate and types.
Its working, but I get memory leak issue.

No audio played using MPMoviePlayerController

I have been using MPMoviePlayerController to play mp4 file. Video is playing fine but there is no audio at all.
I have downloaded first in one function and played using another. Please check the code below. Am I doing anything wrong here...
- (void) download_file : (NSString *) msg_url
{
if (msg_url != nil) {
// Download and play
NSURL *url = [NSURL URLWithString:msg_url];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if ( urlData )
{
char file_path[512] = {0};
get_app_data_path(OFI_VC_MAILBOX_PATH, file_path); // Give document path.
NSString *filePath = [NSString stringWithFormat:#"%s/%#", file_path,#"mailbox.mp4"];
[urlData writeToFile:filePath atomically:YES];
[self performSelectorOnMainThread:#selector(play_mp4_file:) withObject:filePath waitUntilDone:NO];
}
}
}
- (void) play_mp4_file : (NSString *) filepath
{
if (is_same_view == true) {
[self.view setUserInteractionEnabled:YES];
if (filepath != nil) {
NSURL *file_url = [NSURL fileURLWithPath:filepath];
moviePlayer = [[MPMoviePlayerController alloc]
initWithContentURL:file_url];
moviePlayer.controlStyle = MPMovieControlStyleDefault;
moviePlayer.shouldAutoplay = YES;
moviePlayer.useApplicationAudioSession = YES;
[self.view addSubview:moviePlayer.view];
[moviePlayer setFullscreen:YES animated:YES];
}
}
}
Thanks for your helps
I have fixed the issue. Problem is with .mp4 extension, I have used .mov. Its playing audio now.

Bug in AVAudioPlayer class?

I'll keep it short and sweet - I'm building an application just for practice before I buy myself the iPhone Developer Program.
I'm experimenting with the AVFoundation.framework and I keep running into a bug which will only let me play the first sound I initialize in my code. Do you think any of you could help me out? Thanks in advance!! :)
view controller
-(IBAction)play {
if (segments.selectedSegmentIndex == 0) {
NSLog(#"Segment = 0");
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:#"meow" ofType:#"wav"];
if (path != nil) {
NSURL *url = [NSURL fileURLWithPath:path];
AVAudioPlayer *player = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:NULL];
[player prepareToPlay];
[player play];
}
}
else if (segments.selectedSegmentIndex == 1) {
NSLog(#"Segment = 1");
NSBundle *bundle = [NSBundle mainBundle];
NSString *path2 = [bundle pathForResource:#"meowloud" ofType:#"wav"];
if (path2 != nil) {
NSURL *url2 = [NSURL fileURLWithPath:path2];
AVAudioPlayer *player2 = [[AVAudioPlayer alloc]initWithContentsOfURL:url2 error:NULL];
[player2 prepareToPlay];
[player2 play];
// [player2 release];
}
}
text1.textColor = [UIColor clearColor];
text2.textColor = [UIColor clearColor];
text3.textColor = [UIColor clearColor];
text4.textColor = [UIColor clearColor];
}
When this code gets executed, only the meow.wav is executed, no matter which segment is selected.
Your code has a couple of memory leaks. You allocate instances of AVAudioPlayer in your play method, but you never release those instances. See Cocoa's memory management qguidelines for details. Since the AVAudioPlayer instances need to remain in memory to play, the easiest solution is to add a member variable to your viewController class and set it up as a retain property.
For the sake of Don't Repeat Yourself (DRY), I would also suggest adding a method which encapsulates all of the code needed to play a single wav file.
Here is how I would write this view controller:
MyViewController.h
#interface MyViewController : UIViewController
{
AVAudioPlayer *player;
}
#property(nonatomic, retain) AVAudioPlayer *player;
- (void)playWavFile:(NSString *)fileName;
- (IBAction)play;
#end
MyViewController.m
#synthesize player;
- (void)dealloc {
[player release];
}
- (void)playWavFile:(NSString *)fileName {
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:fileName ofType:#"wav"];
if (path != nil) {
AVAudioPlayer *newPlayer;
NSURL *url = [NSURL fileURLWithPath:path];
newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url
error:NULL];
[self setPlayer:newPlayer];
[newPlayer release];
[newPlayer prepareToPlay];
[newPlayer play];
}
}
- (IBAction)play {
if (segments.selectedSegmentIndex == 0) {
[self playWavFile:#"meow"];
}
else if (segments.selectedSegmentIndex == 1) {
[self playWavFile:#"meowloud"];
}
}