I have a youtube video that plays in a uiwebview in my app. The cell signal is not very good in most areas I am using the app.
How can I cache the youtube video for better performance when playing the video?
Try this
I start downloading the video file with a NSURLConnection
I then implement the received data delegate method like the following.
- (void)connection:(NSURLConnection *)aConnection didReceiveData:(NSData *)aData
{
bytesFetched += aData.length;
if( bytesFetched > kBytesRequiredBeforeStart && !hasCachedData ) // kBytesRequiredBeforeStart = 160000
{
[[NSNotificationCenter defaultCenter] postNotificationName:kVideoURLCacheHasDataNotification object:self];
hasCachedData = YES;
}
[self.fileHandle writeData:aData]; // this file handle is not closed until after the video has finished downloading
}
The fileHandle is created like this
- (NSFileHandle *)fileHandle
{
if( fileHandle == nil )
{
NSError * theError = nil;
cachedURL = [[NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingFormat:#"/%#", kTempFileName]] retain];
[[NSFileManager defaultManager] createFileAtPath:[self.cachedURL path] contents:nil attributes:nil];
fileHandle = [[NSFileHandle fileHandleForWritingToURL:self.cachedURL error:&theError] retain];
if( fileHandle == nil )
[[NSNotificationCenter defaultCenter] postNotificationName:kVideoURLCacheErrorOccuredNotification object:self];
}
return fileHandle;
}
*I also have a did finish delegate handling method like this*
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if( hasCachedData == NO )
{
[[NSNotificationCenter defaultCenter] postNotificationName:kVideoURLCacheHasDataNotification object:self];
hasCachedData = YES;
}
hasFinishedCaching = YES;
[[NSNotificationCenter defaultCenter] postNotificationName:kVideoURLCacheDidFinishLoadingNotification object:self];
}
I then have a method to observer the notification like the following
- (void)videoURLCacheHasDataNotification:(NSNotification *)aNotification
{
[self.videoController play];
}
where the videoController is an instance of MPMoviePlayerController created like below, cachedURL is the same one defined above.
- (MPMoviePlayerController *)videoController
{
if( videoController == nil )
{
NSURL * theURL = self.videoURLCache.cachedURL;
NSLog( #"Video URL = '%#'", theURL );
videoController = [[MPMoviePlayerController alloc] initWithContentURL:theURL];
videoController.shouldAutoplay = NO;
[videoController setFullscreen:NO animated:NO];
videoController.view.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleHeight;
videoController.repeatMode = MPMovieRepeatModeOne;
videoController.controlStyle = MPMovieControlStyleEmbedded;
videoController.view.frame = self.videoView.bounds;
[videoView addSubview:self.videoController.view];
}
NSParameterAssert(videoController != nil);
return videoController;
}
Related
I have this piece of code for playing audio, but once it is finished, I want to play the same audio again and again, I think I should use numberofloops=-1, but where I need to use this directly. Please help me.
#import "JetNapMusicPlayer.h"
#import <AVFoundation/AVFoundation.h>
#interface JetNapMusicPlayer()
#property(nonatomic,strong) AVQueuePlayer *avQueuePlayer;
#end
static JetNapMusicPlayer *sharedManager = nil;
#implementation JetNapMusicPlaye
#pragma mark Singleton Methods
+ (id)sharedManager {
#synchronized(self) {
if(sharedManager == nil)
sharedManager = [[super alloc] init];
}
return sharedManager;
}
- (id)init {
if (self = [super init]) {
// [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
MPRemoteCommandCenter *rcc = [MPRemoteCommandCenter sharedCommandCenter];
MPRemoteCommand *playCommand = rcc.playCommand;
[playCommand setEnabled:YES];
[playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent *event) {
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] play];
return MPRemoteCommandHandlerStatusSuccess;
}];
MPRemoteCommand *pauseCommand = rcc.pauseCommand;
[pauseCommand setEnabled:YES];
[pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent *event) {
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] pause];
return MPRemoteCommandHandlerStatusSuccess;
}];
}
return self;
}
- (void)dealloc {
[super dealloc];
}
-(AVPlayer *)avQueuePlayer
{
if (!_avQueuePlayer) {
[self initSession];
_avQueuePlayer = [[AVQueuePlayer alloc] init];
}
return _avQueuePlayer;
}
-(void)initSession
{
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(audioSessionInterrupted:)
name: AVAudioSessionInterruptionNotification
object: [AVAudioSession sharedInstance]];
//set audio category with options - for this demo we'll do playback only
NSError *categoryError = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:&categoryError];
if (categoryError) {
NSLog(#"Error setting category! %#", [categoryError description]);
}
//activation of audio session
NSError *activationError = nil;
BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError];
if (!success) {
if (activationError) {
NSLog(#"Could not activate audio session. %#", [activationError localizedDescription]);
} else {
NSLog(#"audio session could not be activated!");
}
}
}
#pragma mark - notifications
-(void)audioSessionInterrupted:(NSNotification*)interruptionNotification
{
NSLog(#"interruption received: %#", interruptionNotification);
}
#pragma mark - player actions
-(void) pause
{
[[self avQueuePlayer] pause];
}
-(void) play
{
[[self avQueuePlayer] play];
}
-(void) clear
{
[[self avQueuePlayer] removeAllItems];
}
#pragma mark - remote control events
#pragma mark - Kony FFI
+ (BOOL)playMusic:(NSString *)filename artistname:(NSString *)artistname songname:(NSString *)songname {
NSString *name = [filename stringByDeletingPathExtension];
NSString *ext = [filename pathExtension];
AVPlayerItem *avSongItem = [[AVPlayerItem alloc] initWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:[[NSString alloc] initWithFormat:name] ofType:ext]]];
if (avSongItem) {
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] clear];
[[[JetNapMusicPlayer sharedManager] avQueuePlayer] insertItem:avSongItem afterItem:nil];
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] play];
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = #{MPMediaItemPropertyTitle: songname, MPMediaItemPropertyArtist:artistname};
}
return YES;
}
+ (BOOL)stopMusic {
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] pause];
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] clear];
return YES;
}
#end
To loop a song use below code after alloc init of avSongItem.
avSongItem.actionAtItemEnd = AVPlayerActionAtItemEndNone;
More info : Looping a video with AVFoundation AVPlayer?
Also as mentioned in the link use notification.
avSongItem.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[avPlayer currentItem]];
this will prevent the player to pause at the end.
in the notification:
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
[p seekToTime:kCMTimeZero];
}
I am building an app with AVPlayer that will play songs from an api.
When a song ends, next song will be played. For this I am using the following code:
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackFinished:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[_audioPlayer currentItem]];
}
-(void)playbackFinished:(NSNotification *)notification {
// flagSkip = NO;
NSLog(#"## %# ", NSStringFromSelector(_cmd));
// if(flagSkip == NO)
[[DataSingleton sharedMySingleton] nextTrack];
// else
// flagSkip = NO;
}
On Swipe gesture, next song will be played.
For this, am removing the notification observer and adding it again as follows:
- (IBAction)skipButtonPressed:(id)sender
{
[[DataSingleton sharedMySingleton] nextTrack];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:[_audioPlayer currentItem]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackFinished:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[_audioPlayer currentItem]];
}else{
//
}
}
But when I swipe, sometimes the notification method gets called.
Where am I getting wrong? How do I solve this?
Edit to include [[DataSingleton sharedMySingleton] nextTrack]
-(void)nextTrack{
NSDictionary *prevTrackInfo;
if (currentIndex == -1){
// We're at the start of a refilled list, so previousTrack should be
// the only thing in the cache dir. Clean it up.
dispatch_queue_t removeFilesQueue;
NSLog(#"## %# removeFilesQueue", NSStringFromSelector(_cmd));
removeFilesQueue = dispatch_queue_create("com.zombieprocess.removeFilesQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(removeFilesQueue, ^{
// Code goes here
NSError *error = nil;
[fileMgr removeItemAtPath:[self getFeedBandsCacheDir] error:&error];
[fileMgr createDirectoryAtPath:[self getFeedBandsCacheDir] withIntermediateDirectories:YES attributes:nil error:nil];
});
//dispatch_release(removeFilesQueue);
} else{
if (trackInfo){
prevTrackInfo = trackInfo;
}else{
NSLog(#"nextTrack, attempting to store prevTrackInfo, no trackInfo for currentIndex: %d", currentIndex);
}
}
currentIndex += 1;
// We should not have this, but just in case
if (currentIndex >= self.feedEntries.count) {
// We are at the end. Get the feed again.
NSLog(#"## %# currentIndex >= self.feedEntries.count", NSStringFromSelector(_cmd));
if (self.feedEntries.count == 0) {
[self loadFeed];
return;
}
currentIndex = 0; // This will loop it back to the beginning
// [self loadFeed];
// return;
}
trackInfo = [self.feedEntries objectAtIndex:currentIndex];
[self dispatchPlayNotification];
if (prevTrackInfo && [self isTrackCached:prevTrackInfo] && prevTrackInfo != trackInfo && feedEntries.count > [self numberOfSongsToCache]){
NSLog(#"nextTrack, deleting cached copy: %#", [[prevTrackInfo objectForKey:#"file_url"] lastPathComponent]);
[self deleteCachedTrack:prevTrackInfo completionBlock:^{
[self fillDownloadQueue];
}];
} else {
[self fillDownloadQueue];
}
}
I guess you've already switched the next song. So you can't remove notification observer with [_audioPlayer currentItem], because it is already changed.
Update 1.
Try to replace 1st and 2nd lines:
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:[_audioPlayer currentItem]];
[[DataSingleton sharedMySingleton] nextTrack];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:[_audioPlayer currentItem]];
Instead of removing the notification listener, I would read the current state on the player inside the method (void)playbackFinished:(NSNotification *)notification.
For example:
- (IBAction)skipButtonPressed:(id)sender
{
didSkipSong = YES;
[[DataSingleton sharedMySingleton] nextTrack];
}
And in -(void)playbackFinished:(NSNotification *)notification
-(void)playbackFinished:(NSNotification *)notification {
if(didSkipSong){
didSkipSong = NO;
return;
}
[[DataSingleton sharedMySingleton] nextTrack];
}
Im trying to play a local video using AVFoundation. I don't know whats going on here that only if I play a video on the web and then play the local video it works. If I just play the local video it gives me the error:
Item cannot be played
he assets tracks were loaded, but could not be made playable.
And Here's my code:
- (void)setURL:(NSURL*)URL
{
if (mURL != URL)
{
[mURL release];
mURL = [URL copy];
NSLog(#"Im here %#",mURL);
}
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:mURL options:nil];
NSArray *requestedKeys = [NSArray arrayWithObjects:kTracksKey, kPlayableKey, nil];
/* Tells the asset to load the values of any of the specified keys that are not already loaded. */
[asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
^{
dispatch_async( dispatch_get_main_queue(),
^{
/* IMPORTANT: Must dispatch to main queue in order to operate on the AVPlayer and AVPlayerItem. */
[self prepareToPlayAsset:asset withKeys:requestedKeys];
});
}];
}
- (void)prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys
{
if (!asset.playable)
{
NSString *localizedDescription = NSLocalizedString(#"Item cannot be played", #"Item cannot be played description");
NSString *localizedFailureReason = NSLocalizedString(#"The assets tracks were loaded, but could not be made playable.", #"Item cannot be played failure reason");
NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
localizedDescription, NSLocalizedDescriptionKey,
localizedFailureReason, NSLocalizedFailureReasonErrorKey,
nil];
NSError *assetCannotBePlayedError = [NSError errorWithDomain:#"StitchedStreamPlayer" code:0 userInfo:errorDict];
[self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
return;
}
if (self.mPlayerItem)
{
[self.mPlayerItem removeObserver:self forKeyPath:kStatusKey];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.mPlayerItem];
}
self.mPlayerItem = [AVPlayerItem playerItemWithAsset:asset];
[self.mPlayerItem addObserver:self
forKeyPath:kStatusKey
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:AVPlayerDemoPlaybackViewControllerStatusObservationContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.mPlayerItem];
seekToZeroBeforePlay = NO;
if (!self.mPlayer)
{
[self setPlayer:[AVPlayer playerWithPlayerItem:self.mPlayerItem]];
[self.player addObserver:self
forKeyPath:kCurrentItemKey
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:AVPlayerDemoPlaybackViewControllerCurrentItemObservationContext];
[self.player addObserver:self
forKeyPath:kRateKey
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:AVPlayerDemoPlaybackViewControllerRateObservationContext];
}
if (self.player.currentItem != self.mPlayerItem)
{
[self.mPlayer replaceCurrentItemWithPlayerItem:self.mPlayerItem];
[self syncPlayPauseButtons];
}
[self.mScrubber setValue:0.0];
[self play:nil];
loading.hidden = YES;
}
NSLog gives the local URL that im using:
Im here file:///var/mobile/Containers/Data/Application/E806EC2A-2C5D-4B86-AD05-D9FD29E8FDDD/Documents/downloads/VVV%20-%20One%20Direction,%20Band%20Aid%2030,%20Mark%20Ronson,%20Bruno%20Mars,%20OneRepublic,%20DSCVR%20Ones%20To%20Watch%202015.m4v
Thanks in Advance!!
//You can use MPMoviePlayerController for Playing a Video.
Add MediaPlayer.framework (#import )
MPMoviePlayerController *movieController = [[MPMoviePlayerController alloc] init];
movieController.controlStyle = MPMovieControlStyleDefault;//MPMovieControlStyleNone;
[movieController setContentURL:#"Your local Url Path"];
[movieController.view setFrame:CGRectMake (0,0,214,186)];
[self.view addSubview:movieController.view];
[movieController.view bringSubviewToFront:self.view];
[movieController prepareToPlay];
[movieController play];
I am using the following code to play video files in MPMoviePlayerController
NSString* filePath = [[NSBundle mainBundle] pathForResource:#"one" ofType:#"mp4"];
NSURL* url = [NSURL fileURLWithPath:filePath];
_movie = [[MPMoviePlayerController alloc] initWithContentURL:url];
[_movie.view setFrame:self.view.bounds];
[self.view addSubview:_movie.view];
_movie.fullscreen=YES;
_movie.controlStyle=MPMovieControlStyleFullscreen;
[_movie prepareToPlay];
[_movie play];
and
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(close:)name:MPMoviePlayerPlaybackDidFinishNotification object:_movie];
and
- (void) close:(NSNotification *)notification {
int reason = [[[notification userInfo] valueForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
if(reason == MPMoviePlaybackStateStopped) {
NSLog(#"Stop");
}
else if (reason == MPMovieFinishReasonPlaybackEnded) {
NSLog(#"Playback Ended ");
}
else if (reason == MPMovieFinishReasonUserExited) {
NSLog(#"Exited");
[_movie.view removeFromSuperview];
}
else if (reason == MPMovieFinishReasonPlaybackError) {
//error
NSLog(#"Error");
}
}
I am able to get the Notification , and the Movieplayer is not removing from the superview.
What could be the problem ??
Try this follow instructions:
when I listen to MPMoviePlayerWillExitFullscreenNotification.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(doneButtonClick:)
name:MPMoviePlayerWillExitFullscreenNotification
object:_movie];
And selector method:
-(void)doneButtonClick:(NSNotification*)aNotification{
[_movie.view removeFromSuperview];
}
(or)
Better way to use mpmovieplayerviewcontroller in below tutorial
http://mobiledevelopertips.com/video/getting-mpmovieplayercontroller-to-cooperate-with-ios4-3-2-ipad-and-earlier-versions-of-iphone-sdk.html
I have been browsing through Google for various explanations but I STILL couldn't figure out when this code fires the screen is pitch black. Anyone able to spot a mistake?
UPDATE
- (IBAction)playVideo:(id)sender {
NSURL *videoUrl = [[DataStore singletonInstance] getVideoUrl:self withUuid:self.eventDetailVC.event.uuid];
if ([videoUrl checkResourceIsReachableAndReturnError:nil] == NO) {
NSLog(#"Video doesn't not exist.");
return;
}
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:videoUrl];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:player];
[previewView addSubview:player.view];
player.view.frame = previewView.bounds;
player.controlStyle = MPMovieControlStyleDefault;
[player play];
}
- (void)moviePlayBackDidFinish:(NSNotification*)notification {
NSLog(#"moviePlayBackDidFinish: called");
MPMoviePlayerController *player = [notification object];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification
object:player];
// Checking for errors
NSDictionary *notiUserInfo = [notification userInfo];
if (notiUserInfo != nil) {
NSError *errorInfo = [notiUserInfo objectForKey:#"error"];
if ([[errorInfo domain] isEqualToString:#"MediaPlayerErrorDomain"]) {
UIAlertView *notice = [[UIAlertView alloc] initWithTitle:#"Error"
message:[errorInfo localizedDescription]
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[notice show];
return;
}
}
// Remove from view
[player.view removeFromSuperview];
[player stop];
}
FYI moviePlayBackDidFinish is NOT called at all. I don't know why.
Create property for MPMoviePlayerController, because you retain view after adding it as subview, but not retain controller.
#property (strong, nonatomic) MPMoviePlayerController *player;
...
#synthesize player = _player;
...
- (IBAction)playVideo:(id)sender
{
NSURL *videoUrl = [[DataStore singletonInstance] getVideoUrl:self withUuid:self.eventDetailVC.event.uuid];
if ([videoUrl checkResourceIsReachableAndReturnError:nil] == NO)
{
NSLog(#"Video doesn't not exist.");
return;
}
self.player = [[MPMoviePlayerController alloc] initWithContentURL:videoUrl];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
[previewView addSubview:_player.view];
_player.view.frame = previewView.bounds;
_player.controlStyle = MPMovieControlStyleDefault;
[_player play];
}
- (void)moviePlayBackDidFinish:(NSNotification*)notification
{
NSLog(#"moviePlayBackDidFinish: called");
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
// Checking for errors
NSDictionary *notiUserInfo = [notification userInfo];
if (notiUserInfo != nil)
{
NSError *errorInfo = [notiUserInfo objectForKey:#"error"];
if ([[errorInfo domain] isEqualToString:#"MediaPlayerErrorDomain"])
{
UIAlertView *notice = [[UIAlertView alloc] initWithTitle:#"Error"
message:[errorInfo localizedDescription]
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[notice show];
return;
}
}
// Remove from view
[_player.view removeFromSuperview];
[_player stop];
self.player = nil;
}