I am trying to play local videos in my app using AVPlayer and AVPlayerViewController. The problem is that when I try to play the video at some parts the video just starts freezing and playing 1 or 2 frames per second, while the audio sounds perfectly fine. It is not a heavy video (4.5 MB, 35 seconds long), it was exported at 25 fps(using adobe premiere).
I tried a couple of things to fix it, some of them fixed the freezes, but are not a good solution:
I exported the video at 15 frames per second, this made it run fine, but it was choppy since it was 15 fps.
I tried exporting the video at 150% speed, it ran smoothly.
Setting the rate to 0.75 made the video run smoothly.
Setting the rate higher made it freeze way more often.
For some reason when I started the video half way through at 15 seconds it didn't freeze.
This is what the code looks like:
let videoURL: URL = Bundle.main.url(forResource: currentComic?.getVideo(), withExtension: "mp4")!
player = AVPlayer(url: videoURL)
videoPlayerController.player = player
videoPlayerController.showsPlaybackControls = true
videoPlayerController.videoGravity = AVLayerVideoGravity.resizeAspectFill
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
self.present(videoPlayerController, animated: true, completion: nil)
var item = videoPlayerController.player!.currentItem
var previousPageCMTimeTag = CMTimeMakeWithSeconds(0, preferredTimescale: 1)
videoPlayerController.player!.seek(to: previousPageCMTimeTag)
videoPlayerController.player!.play()
player.rate = 1
videoPlayerController is a variable of AVPlayerViewController, and player is a variable of AVPlayer. I really hope someone can help me with this; it has been driving me crazy for the past few days.
I recently ran into the same issue and spent about a week diagnosing it.
Setting showsPlaybackControls while the AVPlayerViewController is onscreen will cause the video to freeze while the player continues playing audio-only.
The documentation for this property warns that setting this property while the player is visible "creates or destroys user interface elements", but we never thought it would destroy video playback entirely!
It's hard to tell if your code is doing this without seeing how else videoPlayerController is used, but make sure your AVPlayerViewController is not onscreen when mutating this property.
Related
Here is my code to start the video:
let videoURL = NSURL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let playerAV = AVPlayer(url: videoURL! as URL)
let playerLayerAV = AVPlayerLayer(player: playerAV)
playerLayerAV.frame = ...
self.view.layer.addSublayer(playerLayerAV)
playerAV.play()
I can see the video preview, I can also see it's being loaded, I can even move the slider and see that preview is changing to the time I'm choosing. But if I hit play - nothing happens. I tried to press "play" button manually, I tried to call .play programmatically, the state of the "Play" button changes to "||" but then switches back to ">" right away.
I'm also tracking debug info related to the player state and I notice the following. Right when I created a player the status is below:
Status: ReadyToPlay
TimeControl: WaitingToPlayAtSpecifiedRate
ReasonToWait: AVPlayerWaitingToMinimizeStallsReason
Time: {0/1 = 0.000}
Rate: 1
Loaded Time Ranges: [Location=0,Length=0]
And it almost instantly changes its TimeControl: Paused and Rate: 0:
Status: ReadyToPlay
TimeControl: Paused
ReasonToWait:
Time: {0/1 = 0.000}
Rate: 0
Loaded Time Ranges: [Location=0,Length=0]
In both cases, it shows that playerItem.LoadedTimeRanges has one element but length and locations are 0 when the UI shows that the video has been completely loaded and ready to play.
Turned out that there is no issue with the video, views or controllers setup. The video is loaded and ready to play and paused right away because of a mode of the share audioSession. It was previously set to .record to process another app use-case.
The solution is to restore an original audioSession mode which includes playback and the video will play without any issues.
I have a UIView with a video as its background. When I present another subview, and then pop back to the one with the video, this happens:
Video [11 seconds] (kept it a video since a GIF wouldn't have the same framerate, and the problem would be less clear.)
As you can see, the video speeds up, and then slows down when it reaches a certain point. What I'd like to accomplish is for the video to always keep playing at it's normal speed, and thus eliminate the speedup happening.
This is the code used for the showing of the video:
self.player = AVPlayer(url: URL(fileURLWithPath: path))
let playerLayer = AVPlayerLayer(player: self.player)
self.layer.addSublayer(playerLayer)
playerLayer.frame = self.bounds
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
Does AVPlayer perhaps have an option to disable this, or is there a way around it?
Thanks.
I would assume this is happening because the video keeps playing while the other view controller is presented on the screen.
What I would try to solve this is to pause the video in viewWillDisappear and then play it again in viewWillAppear.
This question has the same problem, but the solutions didn't work.
The AVPlayer sometimes plays a blank video: there is sound but no video.
While blank videos were played, we printed the frame and status of the player. The frame was non-zero and was correctly set. The status was also readyToPlay. The play function is also invoked on the main thread.
In other words, the frame for the player layer is valid, and the player is also ready to play. Yet no video appears, even though sound does.
The issue seems to be a timing one. If we wait 5-10 seconds before playing the video, the video works fine each time.
This issue appears on iOS 10, not iOS 8 or 9.
This thread on the Apple forums suggests it might be a bug related to
AVVideoCompositionCoreAnimationTool, which we also use.
Any solutions?
This happens more often on iPhone 7 devices than iPhone 5s devices.
Code:
fileprivate func playVideo(_ videoURL: String, prompt: String) {
// Show <playerView>
playerView.isHidden = false
// Use new video in player
playerItem = AVPlayerItem(url: URL(fileURLWithPath: videoURL))
print("STATUS: \(player.status == AVPlayerStatus.readyToPlay). FRAME: \(playerLayer.frame). MAIN THREAD: \(Thread.isMainThread)")
player.replaceCurrentItem(with: playerItem)
// Start playing video
player.seek(to: kCMTimeZero)
player.actionAtItemEnd = .none
player.play()
}
I think you need to play with a AVPlayerViewController or AVPlayerLayer.
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
//set player layer frame and attach it to our view
playerLayer.frame = self.containerView.bounds;
[self.containerView.layer addSublayer:playerLayer];
//play the video
[player play];
From Apple doc:
AVPlayer and AVPlayerItem are nonvisual objects meaning that on their
own are unable to present an asset’s video on screen. You have two
primary approaches you can use to present your video content on
screen:
AVKit: The best way to present your video content is by using the
AVKit framework’s AVPlayerViewController class in iOS and tvOS or the
AVPlayerView class in macOS. These classes present the video content,
along with playback controls and other media features giving you a
full-featured playback experience.
AVPlayerLayer: If you are building a custom interface for your player,
you use a Core Animation CALayer subclass provided by AVFoundation
called AVPlayerLayer. The player layer can be set as a view’s backing
layer or can be added directly to the layer hierarchy. Unlike
AVPlayerView and AVPlayerViewController, a player layer doesn’t
present any playback controls, but simply presents the visual content
on screen. It is up to you to build the playback transport controls to
play, pause, and seek through the media.
In case anyone encounters this issue, the problem seems related to an iOS bug. The problem doesn't occur on iOS 8/9 devices, only on new iOS 10 devices. Starting with iOS 10.2, the problem vanishes.
Unfortunately, still no programmatic solution for 10.0.x or 10.1.x.
I am creating an application with five buttons. When I click on each button each audio will play. It is working. Now my problem is when I click first button audio play with 1 second delay(App stuck for 1 second) and play. Next time clicks a button audio play without any delay. What could be the issue here?
I am using the following code to play an audio
var currentAudio = try? AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("sample_audio", ofType: "mp3")!));
currentAudio!.stop()
currentAudio!.currentTime = 0
currentAudio!.play();
Please someone help me to finds this issue.
You could use AVAudioPlayer's .prepareToPlay() method to preload the player's buffers, it will increase AVAudioPlayer's performance (faster start).
The idea is to prepare the player some time before actually playing it:
currentAudio?.prepareToPlay()
then later, in your play function, it will start immediately:
currentAudio?.play()
I have a recorded video clip that I want to play in reverse.
Playing forward is fine but as soon as I seek to the end of the video file and set the rate of playback to -1.0 the video clip seeks to the end of the clip (I see this in the timeline bar above the video) but does not play in reverse.
After I present the player view controller I check if it is ready to use:
print("readyForDisplay = \(playerViewController.readyForDisplay)")
This tells me that all is ready to prepare to play.
I check if reverse play is possible:
let reversePlay = playerViewController.player!.currentItem?.canPlayReverse
print("reversePlay = \(reversePlay)")
This returns TRUE
I then seek to the end of the clip and set the play back rate to -1.0 for reverse play.
playerViewController.player!.seekToTime(playerViewController.player!.currentItem!.asset.duration)
playerViewController.player!.rate = -1.0
I believe having got this far it is ready to play because if I add the following:
let status : AVPlayerItemStatus? = playerViewController.player!.currentItem?.status
if status == AVPlayerItemStatus.ReadyToPlay {
print("Ready to play")
}
It shows me that the clip is ready to play, so I am assuming that seeking to the end of clip (which is only 2 seconds long) has completed.
I then play the clip:
playerViewController.player!.play()
It plays fine if I don't seek to the end and attempt to change the rate to set it to reverse play.
What am I missing here?
I decided to add some logging after launching the video and found that the rate was -1 before launching and 1 immediately after so it seems that the rate is reset to 1.
Could this be a bug?
Anyway setting the rate immediately after the request to play the video has the desire effect.