I have an Array which contains video URLS, I want to play those videos on UITableviewcell when it is fully visible.
I have tried this
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView
{
NSArray* cells = homeTabl.visibleCells;
for (HomeCell* cell in cells)
{
if (cell.frame.origin.y > offset.y &&
cell.frame.origin.y + cell.frame.size.height < offset.y + bounds.size.height)
{
NSIndexPath *path = [homeTabl indexPathForCell:cell] ;
index = path.row;
fullvisible = YES;
[homeTabl reloadData];
}
else
{
fullvisible = NO;
}
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (fullvisible)
{
NSURL *url = [NSURL URLWithString:[[responsearray objectAtIndex:indexPath.row]valueForKey:#"feed_video"]];
cell.videoItem = [AVPlayerItem playerItemWithURL:url];
cell.videoPlayer = [AVPlayer playerWithPlayerItem:cell.videoItem];
cell.avLayer = [AVPlayerLayer playerLayerWithPlayer:cell.videoPlayer];
cell.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[cell.videoItem addObserver:self forKeyPath:#"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
[cell.videoItem addObserver:self forKeyPath:#"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidBufferPlaying:) name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
cell.avLayer.frame = CGRectMake(5, 9, 310, 310);
[cell.contentView.layer addSublayer:cell.avLayer];
[cell.videoPlayer play];
[cell.contentView addSubview:cell.videoActivity];
}
else
{
cell.videoPlayer = nil;
[cell.avLayer removeFromSuperlayer];
cell.videoItem = nil;
[cell.videoPlayer pause];
}
}
This works fine - but it horrible slow down the scrolling of table view.
Please suggest me some other better method.
You can use
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
isScrolling = NO;
[homeTabl reloadData];
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)aScrollView
{
isScrolling = YES;
[homeTabl reloadData];
index = -1;
}
And in cellForRowatindexpath
if (fullvisible && index == indexPath.row) {
if (!isScrolling) {
NSLog(#"video index---%d",indexPath.row);
if (index == indexPath.row) {
NSLog(#"video index---%d",indexPath.row);
cell.videoActivity.hidden = NO;
// if (index == indexPath.row) {
NSURL *url = [NSURL URLWithString:[[responsearray objectAtIndex:indexPath.row]valueForKey:#"feed_video"]];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
cell.videoItem = [AVPlayerItem playerItemWithURL:url];
dispatch_sync(dispatch_get_main_queue(), ^{
cell.videoPlayer = [AVPlayer playerWithPlayerItem:cell.videoItem];
cell.avLayer = [AVPlayerLayer playerLayerWithPlayer:cell.videoPlayer];
cell.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[cell.videoItem addObserver:self forKeyPath:#"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
[cell.videoItem addObserver:self forKeyPath:#"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidBufferPlaying:) name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
cell.avLayer.frame = CGRectMake(5, 9, 310, 310);
[cell.contentView.layer addSublayer: cell.avLayer];
[ cell.videoPlayer play];
[cell.contentView addSubview:cell.videoActivity];
});
});
// }
// else{
// cell.videoActivity.hidden = YES;
// cell.videoPlayer = nil;
// [cell.avLayer removeFromSuperlayer];
// cell.videoItem = nil;
// [cell.videoPlayer pause];
// }
}
}}
else{
cell.videoActivity.hidden = YES;
cell.videoPlayer = nil;
[cell.avLayer removeFromSuperlayer];
cell.videoItem = nil;
[cell.videoPlayer pause];
}
You're loading your video items synonymously in the main thread; that means your video items are being downloaded and loaded in table view cells at the same time the table view cells are being created. That causes the issue and make the scrolling very laggy and slow. To fix the problem you need to load your videos asynchronously, in a separate thread.
Therefore, you must avoid loading images synchronously, but asynchronously instead, using a different thread other than the main thread, so that the scrolling and loading + displaying images can be done dependently, avoiding any kind of lag.
This is possibly a duplicate question, since it's already been asked and there are tutorials out there, but since I myself always had a problem with this (but for pictures) on my first iOS app and found it very confusing, I'm posting the answer here, hoping it's helpful.
Try moving all the lines related to the video load/display, especially playerItemWithURL in your - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath: to a separate block. Also do search and read other post about "asynchronous video loading in table view cells". There are lots of in-depth tutorials and Q/As about this.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
cell.videoItem = [AVPlayerItem playerItemWithURL:url];
dispatch_sync(dispatch_get_main_queue(), ^{
cell.videoPlayer = [AVPlayer playerWithPlayerItem:cell.videoItem];
cell.avLayer = [AVPlayerLayer playerLayerWithPlayer:cell.videoPlayer];
cell.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[cell.videoItem addObserver:self forKeyPath:#"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
[cell.videoItem addObserver:self forKeyPath:#"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidBufferPlaying:) name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
cell.avLayer.frame = CGRectMake(5, 9, 310, 310);
[cell.contentView.layer addSublayer: cell.avLayer];
[ cell.videoPlayer play];
[cell.contentView addSubview:cell.videoActivity];
});
});
Avoid reloading the table view every time you want to play a video. You can perform your video playing code on the visible cell without having to reload the entire tableview.
Also, I suspect you don't really want to play the video until after the table view has stopped scrolling. See How to detect when a UIScrollView has finished scrolling (detecting when a UIScrollView is finished scrolling is surprisingly not intuitive).
If you do really want to start playing the video before the scrollview stops scrolling, at least modify your code to check if the visible cell is already playing, then do nothing. Right now your tableView:cellForRowAtIndexPath: code that plays a video is executed many, many times whenever a cell is fully visible (which I suspect happens a lot!).
Related
I'm getting the following error, but the strange thing is that it only happens when I do a scroll on my tableview using the pagination, loading a second set of posts, and than I try to move out to new view controller,
if I load only the first page of posts, and move out to another view controller, there is no crash:
Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'An instance 0x170212330 of class AVPlayerItem was deallocated while key
value observers were still registered with it.
Current observation info: <NSKeyValueObservationInfo 0x174820360> (
<NSKeyValueObservance 0x174244290: Observer: 0x101c76c00, Key path:
playbackBufferEmpty, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0,
Property: 0x174243ea0>
<NSKeyValueObservance 0x1754583c0: Observer: 0x101c76c00, Key path:
playbackLikelyToKeepUp, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0,
Property: 0x174243f60>
I have a player on my cell, here is my code for view controller:
- (UITableViewCell *)tableView:(UITableView *)tableViewSelected cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = #"postCell";
PostCell* updateCell = [tableViewSelected dequeueReusableCellWithIdentifier:identifier];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"ADD VIDEO PLAYER and PLAY VIDEO");
NSString* videoString = [NSString stringWithFormat:#"%#%#%#",UrlBase,PostVideos,post.video];
NSString* expandedPath = [videoString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *videoURL = [NSURL URLWithString:expandedPath];
NSLog(#"URL : %#",videoURL);
updateCell.videoItem = [AVPlayerItem playerItemWithURL:videoURL];
[updateCell.videoItem addObserver:updateCell forKeyPath:#"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
[updateCell.videoItem addObserver:updateCell forKeyPath:#"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
updateCell.videoPlayer = [AVPlayer playerWithPlayerItem:updateCell.videoItem];
updateCell.avLayer = [AVPlayerLayer playerLayerWithPlayer:updateCell.videoPlayer];
updateCell.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:updateCell selector:#selector(itemDidBufferPlaying:) name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:updateCell selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:[updateCell.videoPlayer currentItem]];
updateCell.avLayer.frame = updateCell.picture.bounds;
[updateCell.videoView.layer addSublayer:updateCell.avLayer];
[updateCell.avLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
if(indexPath.row==0){
[updateCell.videoPlayer play];
}
});
});
return updateCell;
}
And the only difference I can see from adding a second page is:
When doing regular table view load I do:
[_tableView reloadData];
When loading a second page on the same table view I do:
[_tableView beginUpdates];
[_tableView insertRowsAtIndexPaths:[[GlobalSingleton sharedInstance] indexPathsToInsert] withRowAnimation:UITableViewRowAnimationTop];
[_tableView endUpdates];
Than on my Cell I do:
- (void)prepareForReuse{
[self removePlayer];
}
- (void) dealloc {
[self removePlayer];
}
-(void)removePlayer{
#try{
[self.videoItem removeObserver:self forKeyPath:#"playbackBufferEmpty"];
[self.videoItem removeObserver:self forKeyPath:#"playbackLikelyToKeepUp"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
NSLog(#"remove Observer!");
// [avLayer.player pause];
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
}
#finally {
NSLog(#"finally");
[self.avLayer removeFromSuperlayer];
self.playIV.hidden = YES;
self.videoActivity.hidden = YES;
self.videoView.hidden = YES;
self.videoItem = nil;
self.avLayer = nil;
self.videoPlayer = nil;
}
}
Is it possible that insertRowsAtIndexPaths is causing the cells to never be dealloc? I can't find how its possible to have the observers registered.
Somehow when using the insertRowsAtIndexPaths, the prepareForReuse is not been called, so I resolved it by unregistering the observers on the cellForRowAtIndexPath before adding a new videoItem to the Cell:
- (UITableViewCell *)tableView:(UITableView *)tableViewSelected cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = #"postCell";
PostCell* updateCell = [tableViewSelected dequeueReusableCellWithIdentifier:identifier];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
dispatch_sync(dispatch_get_main_queue(), ^{
NSString* videoString = [NSString stringWithFormat:#"%#%#%#",UrlBase,PostVideos,post.video];
NSString* expandedPath = [videoString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *videoURL = [NSURL URLWithString:expandedPath];
/** SOLUTION STARTS HERE **/
#try{
[self.videoItem removeObserver:self forKeyPath:#"playbackBufferEmpty"];
[self.videoItem removeObserver:self forKeyPath:#"playbackLikelyToKeepUp"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
}
#finally {
NSLog(#"finally");
}
/** SOLUTION ENDS HERE **/
updateCell.videoItem = [AVPlayerItem playerItemWithURL:videoURL];
[updateCell.videoItem addObserver:updateCell forKeyPath:#"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
[updateCell.videoItem addObserver:updateCell forKeyPath:#"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
updateCell.videoPlayer = [AVPlayer playerWithPlayerItem:updateCell.videoItem];
updateCell.avLayer = [AVPlayerLayer playerLayerWithPlayer:updateCell.videoPlayer];
updateCell.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:updateCell selector:#selector(itemDidBufferPlaying:) name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:updateCell selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:[updateCell.videoPlayer currentItem]];
updateCell.avLayer.frame = updateCell.picture.bounds;
[updateCell.videoView.layer addSublayer:updateCell.avLayer];
[updateCell.avLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
if(indexPath.row==0){
[updateCell.videoPlayer play];
}
});
});
return updateCell;
}
I am creating video with AVAssetExportSession and playing video when it finishes.
But Visual Part not showing instantly but only audio is playing instantly.
Visual part comes after some delay about of 20 - 30 seconds. Here is my code to play video
-(void)playUrl:(NSURL *)vUrl{
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:[_avPlayer currentItem]];
_avAsset=nil;
_avPlayerItem=nil;
_avPlayer =nil;
[_avPlayerLayer removeFromSuperlayer];
_avPlayerLayer=nil;
_avAsset=[AVAsset assetWithURL:vUrl];
_avPlayerItem =[[AVPlayerItem alloc]initWithAsset:_avAsset];
_avPlayer = [[AVPlayer alloc]init]; //WithPlayerItem:_avPlayerItem];
[_avPlayer replaceCurrentItemWithPlayerItem:_avPlayerItem];
_avPlayerLayer =[AVPlayerLayer playerLayerWithPlayer:_avPlayer];
[_avPlayerLayer setFrame:CGRectMake(0, 0, viewAVPlayer.frame.size.width, viewAVPlayer.frame.size.height)];
[viewAVPlayer.layer addSublayer:_avPlayerLayer];
[_avPlayer seekToTime:kCMTimeZero];
[_avPlayer play];
_avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(repeatPlayer:) name:AVPlayerItemDidPlayToEndTimeNotification object:[_avPlayer currentItem]];
}
please let me know if anybody know its answer. this code is working perfectly in iOS 9 but not iOS 10. Thanks in advance.
Try to set automaticallyWaitsToMinimizeStalling property of AVPlayer to NO in order to start playback immediately.
_avPlayer = [[AVPlayer alloc]init]; //WithPlayerItem:_avPlayerItem];
_avPlayer.automaticallyWaitsToMinimizeStalling = NO;
But if sufficient content is not available for playing then player might stall.
Apple documentation: https://developer.apple.com/reference/avfoundation/avplayer/1643482-automaticallywaitstominimizestal.
Hope this helps.
I do next:
First
I add watcher
- (void)attachWatcherBlock {
[self removeWatcherBlock];
if (self.videoPlayer) {
__weak typeof(self) wSelf = self;
self.timeObserver = [self.videoPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, NSEC_PER_SEC) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
if (wSelf.playerBlock && wSelf.videoPlayer) {
CGFloat playTime = CMTimeGetSeconds(wSelf.videoPlayer.currentTime);
CGFloat duration = CMTimeGetSeconds(wSelf.videoPlayer.currentItem.duration);
if (playTime > 0.0f) {
[wSelf replaceCoverToVideo];
}
wSelf.playerBlock(wSelf, playTime, duration);
}
}];
}
[self.videoPlayer play];
}
And then if in block playTime equals duration call replay
- (void)replay {
__weak typeof(self) wSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(wSelf) self = wSelf;
if (self.videoPlayer) {
[self.videoPlayer seekToTime:kCMTimeZero];
}
});
}
All this in my UIView subclass called NDVideoPlayerView
Im facing the same problem and my solution is take old code into main thread:
-(void)ExporterManager:(DoCoExporterManager *)manager DidSuccessComplementWithOutputUrl:(NSURL *)outputUrl{
//...
dispatch_async(dispatch_get_main_queue(), ^{
[_playView setContentUrl:outputUrl.path];
});
//...
}
Im using exportAsynchronouslyWithCompletionHandler to process my video.someone thinks that AVVideoCompositionCoreAnimationTool is the cause of the issuehttps://forums.developer.apple.com/thread/62521.
I'm not sure,but i do use it.
Just try it!
Hope this helps!
The AVVideoCompositionCoreAnimationTool when used in AVAssetExportSession interferes with AVPlayer in iOS 10.0 - 10.1.1. iOS 10.2 fixes this bug and your code should work normally.
Hi everyone I am working on an application in which I have a url of video and I have to play a video from that url. I have done this job from this code
- (IBAction)btnPlayVideo:(id)sender
{
NSString *fileName = #"Server Address/Vdieo.flv";
NSURL *streamURL = [NSURL URLWithString:fileName];
mPlayerVC = [[MPMoviePlayerViewController alloc] initWithContentURL:streamURL];
[self.view addSubview:mPlayerVC.view];
//play movie
MPMoviePlayerController *player = [mPlayerVC moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterFullscreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playbackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
player.view.frame = self.view.frame;
[player setFullscreen:YES animated:YES];
[self.view addSubview:player.view];
[player prepareToPlay];
[player play];
}
//============Other Methods====================
- (void)willEnterFullscreen:(NSNotification*)notification {
NSLog(#"willEnterFullscreen");
}
- (void)enteredFullscreen:(NSNotification*)notification {
NSLog(#"enteredFullscreen");
}
- (void)willExitFullscreen:(NSNotification*)notification {
NSLog(#"willExitFullscreen");
}
- (void)exitedFullscreen:(NSNotification*)notification {
NSLog(#"exitedFullscreen");
[mPlayerVC.view removeFromSuperview];
mPlayerVC = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)playbackFinished:(NSNotification*)notification {
NSNumber* reason = [[notification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
switch ([reason intValue]) {
case MPMovieFinishReasonPlaybackEnded:
NSLog(#"playbackFinished. Reason: Playback Ended");
break;
case MPMovieFinishReasonPlaybackError:
NSLog(#"playbackFinished. Reason: Playback Error");
break;
case MPMovieFinishReasonUserExited:
NSLog(#"playbackFinished. Reason: User Exited");
NSLog(#"exitedFullscreen");
[[NSNotificationCenter defaultCenter] removeObserver:self];
break;
default:
break;
}
[mPlayerVC.view removeFromSuperview];
mPlayerVC = nil;
}
My problem is that when this code run video player open and start loading and it takes too much time to run a video. Can anybody guide me how to run video in fast way from internet?
There's nothing in the code that suggests that lag is anything other than the time it takes to make the request and sufficiently buffer. The most common technique to improve UE is to start loading as early as possible, even before the user requests playback.
If this is possible, the code should be reorganized as follows:
// hang on to the movie player
#property(nonatomic,retain) MPMoviePlayerController *mp;
// call this as soon as its possible to know the user might want to see the video
- (void)primeVideo {
NSString *fileName = #"Server Address/Vdieo.flv";
NSURL *streamURL = [NSURL URLWithString:fileName];
MPMoviePlayerController *mp = [[MPMoviePlayerViewController alloc] initWithContentURL:streamURL];
// do this also in dealloc
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterFullscreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playbackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
mp.shouldAutoplay = NO;
[mp prepareToPlay];
self.mp = mp;
}
That does as much prep as possible without changing the UI. The rest of your code from the button action is left, tweaked a little...
- (IBAction)btnPlayVideo:(id)sender {
// if there's any way that the user can request playback before
// you've called primeVideo, check for that here. But hopefully you
// can call primeVideo before user even sees the play button
if (!self.mp) [self primeVideo];
self.mp.view.frame = self.view.frame;
[self.mp setFullscreen:YES animated:YES];
[self.view addSubview:self.mp.view];
MPMovieLoadState state = [self.mp loadState];
if (state & MPMovieLoadStatePlaythroughOK) [self.mp play];
else self.mp.shouldAutoplay = YES;
}
Im using AVQueuePlayer to loop through an array of AVPlayerItems.
The way I'm looping it, I listen to the AVPlayerItemDidPlayToEndTimeNotification and every time its called, I add the current object to the end of the queue.
heres the code:
viewWillAppear
{
...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[_queuePlayer currentItem]];
[_queuePlayer play];
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
AVPlayerItem *fakeNew = [[AVPlayerItem alloc] initWithAsset:p.asset];
if (_queuePlayer.items.count == 1)
{
[p seekToTime:kCMTimeZero];
[_queuePlayer play];
}
else
{
[_queuePlayer insertItem:fakeNew afterItem:[_queuePlayer.items lastObject]];
}
NSLog(#"array of items to play:%lu", (unsigned long)_queuePlayer.items.count);
}
The problem is, that the method is called only for the first video that plays, after that, the method stops getting called, so if for example i have 2 movies in the array, it would play them both+the first one again, any idea why is this happening?
More Info:
also tried to make a new player every time and set it to layer. failed to send the notification more than once just the same
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
[self.playList removeObjectAtIndex:0];
[self.playList addObject:p];
AVPlayer *newPlayer = [[AVPlayer alloc] initWithPlayerItem:[self.playList objectAtIndex:0]];
_player = newPlayer;
self.AVPlayerLayerView.layer.player = self.player;
[_player play];
}
After a lot of messing around, apparently for whatever reason, the view unregistered as observer every time, I just removed and added observer after every notification:
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
AVPlayerItem *fakeNewItem = [[AVPlayerItem alloc] initWithAsset:p.asset];
[self.playList removeObjectAtIndex:0];
[self.playList addObject:fakeNewItem];
AVPlayer *newPlayer = [[AVPlayer alloc] initWithPlayerItem:[self.playList objectAtIndex:0]];
_player = newPlayer;
self.AVPlayerLayerView.layer.player = self.player;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[_player currentItem]];
[_player play];
}
For a clean approach to resolve this issue. I approached with the next piece of code instead
The first is you have to add the code necessary to receive a feedback from the AVPlayer when the reproduction time changes.
- (void)addPeriodicTimeObserverForReproductionChanges {
#weakify(self);
[self.player
addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(kBQDefaultTimeIntervalReproductionChanges, NSEC_PER_SEC)
queue:self.eventsQueue
usingBlock:^(CMTime time) {
#strongify(self);
[self dispatchBlockOnMainQueue:^{
if ([self.delegate respondsToSelector:#selector(playerItemController:didChangeReproductionTime:)])
[self.delegate playerItemController:self
didChangeReproductionTime:time];
[self checkForItemPlayToEnd];
}];
}];
}
- (void)checkForItemPlayToEnd
{
CMTime durationScaled = CMTimeConvertScale(self.duration,self.player.currentTime.timescale, kCMTimeRoundingMethod_Default);
if (CMTIME_COMPARE_INLINE(durationScaled, ==, self.player.currentTime)) {
[self playerDidFinishReproducingItem];
}
}
I'm running the following code. The video plays fine but after it finishes it just goes to a black srcreen, my original view never comes back. When I tap on the black screen i just see the message "loading....." Can someone please explain what I'm doing wrong. Thanks
- (IBAction)video:(UIBarButtonItem *)sender
{
{
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"IMG_0973" ofType:#"MOV"]];
moviePlayer=[[MPMoviePlayerController alloc] initWithContentURL:url];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayBackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayBackDonePressed:) name:MPMoviePlayerDidExitFullscreenNotification object:moviePlayer];
moviePlayer.controlStyle=MPMovieControlStyleDefault;
//moviePlayer.shouldAutoplay=NO;
[moviePlayer play];
[self.view addSubview:moviePlayer.view];
[moviePlayer setFullscreen:YES animated:YES];
}
}
- (void) moviePlayBackDonePressed:(NSNotification*)notification
{
[moviePlayer stop];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerDidExitFullscreenNotification object:moviePlayer];
if ([moviePlayer respondsToSelector:#selector(setFullscreen:animated:)])
{
[moviePlayer.view removeFromSuperview];
}
moviePlayer=nil;
}
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
[moviePlayer stop];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:moviePlayer];
if ([moviePlayer respondsToSelector:#selector(setFullscreen:animated:)])
{
[moviePlayer.view removeFromSuperview];
}
}
Add this notification Method
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePreloadDidFinish:) name:MPMoviePlayerLoadStateDidChangeNotification
object:player];
This method is called after your movie is loaded and in this method you add your moviePlayer view.
-(void)moviePreloadDidFinish:(NSNotification*)notification
{
moviePlayer.controlStyle=MPMovieControlStyleDefault;
[self.view addSubview:moviePlayer.view];
[moviePlayer play];
[moviePlayer setFullscreen:YES animated:YES];
}
In your video IBAction, you need to add the subview before you tell it to play. Switch the lines [moviePlayer play] and [self.view addSubview:moviePlayer.view]. Let us know it it works! Actually you may need to place moviePlayer play even after the full screen line.
i think this will help you .....
-(void)playVideo
{
NSString *contentURL = [[NSBundle mainBundle] pathForResource:#"xyz" ofType:#"mp4"];
MPMoviePlayerViewController *moviePlayerViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL fileURLWithPath:contentURL]];
if (moviePlayerViewController)
{
[moviePlayerViewController.moviePlayer setMovieSourceType:MPMovieSourceTypeFile];
[moviePlayerViewController.moviePlayer setFullscreen:YES];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(MovieFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:moviePlayerViewController.moviePlayer];
[moviePlayerViewController.moviePlayer play];
[navi presentModalViewController:moviePlayerViewController animated:NO];
[moviePlayerViewController release];
moviePlayerViewController = nil;
}
}
-(void)MovieFinished:(NSNotification *)notification
{
MPMoviePlayerController *player = (MPMoviePlayerController *)notification.object;
[player stop];
[[NSNotificationCenter defaultCenter] removeObserver:self];
//do rest of the stuff
}