AVPLAYER VIDEO BUFFER FULL??? (How To Reset/Empty The Buffer) - ios

So I'm using AVPlayerViewController and what i'm doing to load the video is basically this:
I have a large number of videos in my database, I make a call to the db to retrieve the next video after the current video has finished playing, the issue I think may have to do with the buffer being full? After looking around on stack & the internet I couldn't find an answer that helped me.
I'm trying to find out how can I reset the buffer after each video is played, or atleast know when the buffer is out of space so that I can display an image or something while the buffer loads, so then I can make a call to continue displaying videos. Below is my code for the AVPlayerViewController.
AVPlayerViewController *playerViewController = [[AVPlayerViewController
alloc] init];
playerViewController.player = [AVPlayer #"www.videoURL.com"];
self.avPlayerViewcontroller = playerViewController;
[playerViewController.view setFrame: userView.bounds];
[userView addSubview:playerViewController.view];
self.avPlayerViewcontroller.player.volume = 0.0;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(volumeChanged:)
name:#"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
[playerViewController.player play];
[userImageView setHidden:NO];
Edit:1) I'm about to play about 15-25 videos before the video play gives me this:
Edit:2) But if I close the app and then re-open it, the videos play on the app like nothings wrong. I'm not really sure whats going on.

For anyone that encounters this same scenario, The issue for me was inside of the the same block that I was making my API call I was initializing this:
AVPlayerViewController *playerViewController =
[[AVPlayerViewController alloc] init];
AVPlayerViewController was (STRONG) reference causing a retain cycle within the block, so each time I made an API call to grab a new video I was init a whole new AVPlayerViewController without releasing the previous. Along with that, I found that I had other objects within the API success response were being referenced strong as well. So I went back made those a WEAK reference and then I moved
AVPlayerViewController *playerViewController =
[[AVPlayerViewController alloc] init];
To the view did load method, so it's initialized ONCE instead of everytime a new video URL comes through.
It took me all day to figure out what was going one, because after looking at other users having problems with this, I was so focused on "BUFFERING" or running out of buffering was the culprit, but comes to find out it was just bad implementation of objects and misplacements.
Good luck to future programmers.

Related

Pre-load an AVPlayer streaming video

I'm trying to play a streaming video with a AVPlayer without any network delay. Fortunately, our app has a progress screen before the screen that plays the video. I'm hoping to use some of the time on this progress screen to pre-load the video so it plays without delay on the next screen.
The most promising approach that I've come up with is to use an AVQueuePlayer to play 2 videos, the first would be a video that I play off screen (so you don't see it) and is silent, so you don't hear it. From what I've read on SO AVQueuePlayer buffers the nth+1 video when the nth video is near completion.
Here's my code to do this:
NSString *blackVideoPath = [[NSBundle mainBundle] pathForResource:#"black10-counting" ofType:#"MOV"];
AVPlayerItem *blackVideoItem = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:blackVideoPath]];
AVPlayerItem *realVideoItem = [AVPlayerItem playerItemWithURL:_videoWithEffectsURL];
NSArray *theItems = [NSArray arrayWithObjects:blackVideoItem, realVideoItem, nil];
AVQueuePlayer *theQueuePlayer = [AVQueuePlayer queuePlayerWithItems:theItems];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[theItems firstObject]];
AVPlayerLayer* playerLayer = [AVPlayerLayer playerLayerWithPlayer:theQueuePlayer];
playerLayer.frame = self.view.bounds;
[self.view.layer addSublayer:playerLayer];
[theQueuePlayer play];
This does play my first video (for debugging I have a video with a counting soundtrack) but when reach the end and advance to the next screen (in playerItemDidReachEnd) my real video doesn't play immediately.
Just to make sure my URLs were correct I reversed blackVideoItem and realVideoItem in the list and I do hear the soundtrack to my "real" video.
I've searched SO a lot and it doesn't seem like there is a way to play a streaming video without a delay. I'd love to be wrong.
You may be able to do this without AVQueuePlayer.
As early as possible, create the AVPlayer with the remote asset.
Observe the AVPlayerItem (playback buffers, etc.) to make sure it has buffered enough data to play smoothly at the beginning. (See: Preloading video to play without delay)
Show your progress screen.
When the player item is ready, remove the progress screen
Depending on the network connection, you may not experience smooth playback the whole way through, but monitoring the player item should let you show some UI if your player is buffering.

Synchronized recording/playback ios

I'm creating an app where the user is supposed to video record himself while another video is playing on the screen next to the camera view. The result will be two video files, one source, and one recording. Using AVCaptureSession etc., I have successfully managed to record a video at the same time as another video is playing on the screen. The problem is that It's not completely in sync.
This is how I have set it up right now:
-(void)playAndRecordInSync //Session is already set etc.
{
player = [AVPlayer playerWithURL:url];
playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
[playerLayer setFrame:leftCameraView.bounds];
[playerLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[leftCameraView.layer addSublayer:playerLayer];
//Will stop recording camera when source video reaches end(notification):
player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[player currentItem]];
//Start playback:
[player play];
//Start recording:
[movieOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
[movieOutput stopRecording];
}
The result of this, is two videos, but the second (recorded) video is slightly longer than the source/original video. When playing them on top of each other (at the same time), then video nr. 2 has a delay of almost half a second. It feels like video2 has started recording way too soon, because of the "extra" time. It could probably also be that the playback started too late. Either way, I don't know how to make any consistency here based on this. I could probably play around a lot with sleep etc, but that would only work for my phone, as this has something to do with how fast the phone is.
Even if there is a delegate method like -(void)willStartPlaying for the AVPlayer, which I don't think there is, there would still be a problem with sync, as there's a minor wait till the return. Is there a way to fire two commands at once? Or is there another solution for this?
EDIT
I've been reading this (AVSynchronizedLayer), but I'm not sure if this is applicable to my situation, and I don't understand how to proceed.
I found out the delay was always caused by the playback starting too late, but both the recording and the playback were stopped at the same time, so I resolved this by finding v2.duration - v1.duration, and subtract the result from the beginning of v2, using AVAssetExportSession. When playing back both videos at once now, in different layers, there's still some delay from starting both, but by exporting the videos, I've confirmed that they're the same length and in sync now.

MPMoviePlayerController restarting instead of resuming

I am trying to have MPMoviePlayerController reach a programmed endPlaybackTime, then reassign the initial, current, and end times and "resume" play
So first play, say, from the start to 4 seconds, stop/pause, then resume and play from 4 to 8, etc...
but after I reassign current, initial, and endPlaybackTimes and run [mplayer play], the video restarts from the originally times (start to 4s) and plays to the original end time, even though debug messages confirm the new times after the second play
NSURL *fileURL = [NSURL fileURLWithPath:filepath];
_mplayer3 = [[MPMoviePlayerController alloc] initWithContentURL:fileURL];
_mplayer3.controlStyle = MPMovieControlStyleNone;
[_mplayer3.view setFrame: self.view.bounds];
[self.view insertSubview:_mplayer3.view belowSubview:_TopBrag];
_mplayer3.endPlaybackTime = 4.0;
[_mplayer3 setShouldAutoplay:NO];
[_mplayer3 prepareToPlay];
[_mplayer3 view].userInteractionEnabled=YES;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(parallaxDownDidFinish)
name:MPMoviePlayerPlaybackDidFinishNotification
object:_mplayer3];
[_mplayer3 play];
on the end notification, there's a gesture recognizer is created that appears to work correctly. in the gesture recognizer:
_mplayer3.initialPlaybackTime=_mplayer3.endPlaybackTime;
_mplayer3.currentPlaybackTime=_mplayer3.endPlaybackTime;
_mplayer3.endPlaybackTime+=4.0;
if (_mplayer3.endPlaybackTime > _mplayer3.duration)
_mplayer3.endPlaybackTime = _mplayer3.duration;
[_mplayer3 play]
and the video plays from 0 to 4 instead of 4 to 8, even though NSLogs after the play suggest the times are what's desired
appreciate any help
MPMoviePlayerController does not adhere to the initialPlaybackTime if not used on a fresh instance. You will need to release and realloc/assign the player to get this working. All you need to do is to reuse the initial code shown in your question once the player aught to continue.
Update:
Since you seem to be keen on keeping the player view active and as you are not using the standard user interface, I would suggest you to use AVPlayer instead. It is much more flexible and for your job it seems to be the right choice then.
I'm not sure whats the problem, because it should work, the problem i think is that you are in IOS 6, IOS 6 doesn't let you make such a small change, but if in your swipe you change the initial playtime to 9 or higher it should work....
try it and then let me know ;)

MPMoviePlayerController Audio show "Done" Button

I use the MPMoviePlayerController to Play an Audio Stream.
My code follows the example at:
http://iosdevelopertips.com/video/getting-mpmovieplayercontroller-to-cooperate-with-ios4-3-2-ipad-and-earlier-versions-of-iphone-sdk.html
Everything works fine, but while the stream is playing there is no "done"
button to close the Player.
I tested it first with a normal .mp3 file. With the file the only
possibility I found is to skip to the end,
so the player gets the MPMoviePlayerPlaybackDidFinishNotification
notification
(but this won't work on an endless stream, since there is no timeline to
skip to the end).
I tried various styles like [mp setControlStyle:MPMovieControlStyleFullscreen]; with no succes.
In the documentation at
MPMoviePlayerController Class stands:
This class plays any movie or audio
file supported in iOS. This includes
both streamed content and fixed-length
files
Is there a possibility to display that button while playing some audio
content, or has anyone another solution?
I tried to show you an screenshot but "new users can only post a maximum of two hyperlinks".
i found a solution by my self.
Using the MPMoviePlayerViewController class, instead of MPMoviePlayerController solved the problem:
NSString *path = #"http://yourstreamingurl.com/stream.m3u";
MPMoviePlayerViewController* mpviewController = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL URLWithString:path]];
mpviewController.moviePlayer.movieSourceType = MPMovieSourceTypeStreaming;
[self presentModalViewController:mpviewController animated:YES];
[[mpviewController moviePlayer] play];
For playing file locally, remove
mpviewController.moviePlayer.movieSourceType = MPMovieSourceTypeStreaming;
Other wise it will return
Terminating app due to uncaught exception NSInvalidArgumentException reason An AVPlayerItem cannot be associated with more than one instance of AVPlayer

MPMoviePlayerController: Removing ±1sec black screen, when changing contentURL?

I'm working on an iPad project where i have to play short video files one after another smoothly. For playing the videos i'm using MPMoviePlayerController. The problem i'm facing is that when i call
[self.moviePlayer setContentURL:videoURL]
it does start the next video, but there is ±1 sec delay of black screen before it starts to play the next video (the videos are read from the disk, not streamed). I need to avoid this black screen as well as the delay.
So maybe some of you also experienced this problem and have some solutions? Thanks.
Btw, for now, as to at least avoid the black screen, I capture the last frame of the ending video, show it in a UIImageView, and remove it after 1 sec delay. But i'm hoping to find a more elegant fix.
The effect you are talking about is actually a combination of two problems: a black blink when you change the video (which doesn't happen upon assigning the video for the first time) and the delay before the controller starts playing video.
I'm currently screwed with the second one and don't know how to solve yet. As for the first one, just try to use another instance of MPMoviePlayerController. I mean when a video finishes playing (you can subscribe to a corresponding notification) just remove the old player, create a new one and put video there. This way you will avoid blinking, but there will be a delay (not sure, because of loading the video or because of player creation) before the next video starts playing.
Hope this helps a bit.
Fond solution here
http://joris.kluivers.nl/blog/2010/01/04/mpmovieplayercontroller-handle-with-care/
you need to use [self.moviePlayer prepareToPlay]; and catch MPMoviePlayerReadyForDisplayDidChangeNotification to use [self.moviePlayer play];
Old post but Googlers will still come. :)
Creating a new MPMoviePlayerController then assigning it back to my previous player worked for me, no more black screen!
...
[self playVideoWithFilename:#"video1.mp4"];
}
- (void)playVideoWithFilename:(NSString *)fileName
{
MPMoviePlayerController *player = [MPMoviePlayerController new];
_myVidPlayer = player;
player = nil;
NSURL *vidPath = [[NSBundle mainBundle] URLForResource:fileName withExtension:nil];
[_myVidPlayer.view setBackgroundColor:[UIColor whiteColor]];
[_myVidPlayer.view setFrame:CGRectMake(0, 64, 320, 320)];
[_myVidPlayer setContentURL:vidPath];
[_myVidPlayer setControlStyle:MPMovieControlStyleNone];
[_myVidPlayer setRepeatMode:MPMovieRepeatModeOne];
[_myVidPlayer prepareToPlay];
[self.view addSubview: _myVidPlayer.view];
[_myVidPlayer play];
}
Note:
Available in iOS 2.0 and later
Deprecated in iOS 9.0
"Use AVPlayerViewController in AVKit."
I think that the problem is that the controller will fade out and back in between the movies.
You can control the background view color and contents, but I'm not sure that you can eliminate the fade in/out.

Resources