In my application I have used AVPlayer to play videos, If we play a video and go bock to previous page video is playing in backgraound well but if we play the same video again we have to show continuation of the video and it should not begin from start , Here I can able to set slider position according to the video duration but the screen not showing video content sound is coming.
For first time to play any video i called below method
func setup() {
UserDefaults.standard.set(videoString as NSString, forKey: CurrentURL)
UserDefaults.standard.synchronize()
let targetTime:CMTime = CMTimeMake(0, 1)
mediaPlayer.seek(to: targetTime)
mediaPlayer.pause()
print("Video URL \(videoString)")
let url = NSURL(string: videoString)
let playerItem = AVPlayerItem(url: url! as URL)
mediaPlayer=AVPlayer(playerItem: playerItem)
let playerLayer=AVPlayerLayer(player: mediaPlayer)
playerLayer.frame = self.view.bounds
playerLayer.backgroundColor = UIColor.black.cgColor
//playerLayer.videoGravity = AVLayerVideoGravityResize
playerLayer.videoGravity = AVLayerVideoGravityResizeAspect
self.view.layer.addSublayer(playerLayer)
AppDelegate.shared().showLoading()
videoView.isHidden = false
mediaPlayer.play()
let duration : CMTime = mediaPlayer.currentItem!.asset.duration
let seconds : Float64 = CMTimeGetSeconds(duration)
videoSlider.maximumValue = Float(seconds)
videoPlayButton.setImage(UIImage(named: "Pause"), for: .normal)
videoPlaying = true
//Adding Default Periodic Observer On Player
mediaPlayer.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, 1), queue: DispatchQueue.main) { (CMTime) -> Void in
if mediaPlayer.currentItem?.status == .readyToPlay {
AppDelegate.shared().removeLoading()
self.videoSlider.value = Float(CMTimeGetSeconds(mediaPlayer.currentTime()))
let currentTime : Int = Int(CMTimeGetSeconds(mediaPlayer.currentTime()))
let minutes = currentTime/60
let seconds = currentTime - minutes * 60
self.durationLabel.text = NSString(format: "%02d:%02d", minutes,seconds) as String
}
}
}
If user plays same video second time i'm checking current url which is stored in userdefaults if it is playing same video i did like following
let runningSrtring = UserDefaults.standard.object(forKey: CurrentURL) as! NSString
if runningSrtring.isEqual(to: videoString) {
//self.view.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
videoPlayButton.setImage(UIImage(named: "Pause"), for: .normal)
let duration : CMTime = mediaPlayer.currentItem!.asset.duration
let seconds : Float64 = CMTimeGetSeconds(duration)
self.videoSlider.value = Float(CMTimeGetSeconds((mediaPlayer.currentTime())))
videoSlider.minimumValue = 0
videoSlider.maximumValue = Float(seconds)
let playerLayer = AVPlayerLayer(player: mediaPlayer)
playerLayer.frame = self.view.frame
playerLayer.backgroundColor = UIColor.black.cgColor
//playerLayer.videoGravity = AVLayerVideoGravityResize
playerLayer.videoGravity = AVLayerVideoGravityResizeAspect
self.view.layer.addSublayer(playerLayer)
mediaPlayer.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, 1), queue: DispatchQueue.main) { (CMTime) -> Void in
if mediaPlayer.currentItem?.status == .readyToPlay {
self.videoSlider.value = Float(CMTimeGetSeconds((mediaPlayer.currentTime())))
let currentTime : Int = Int(CMTimeGetSeconds(mediaPlayer.currentTime()))
let minutes = currentTime/60
let seconds = currentTime - minutes * 60
self.durationLabel.text = NSString(format: "%02d:%02d", minutes,seconds) as String
}
}
}
But the lauer is not showing video content if i play same video again , here if the user play same video which is already running it should continue from current duration instead of restarting
Related
I have a code that applies cifilters to a video and play it once applied but for some reason, there is a noticeable lag as compared to playing it without filter.
func addComposition(editItem: EditItem, addCrop: Bool = true) {
pause()
let renderSize = editItem.getVideoRenderSize(track: track, editItem: editItem, addCrop: addCrop)
let videoComposition = AVMutableVideoComposition(asset: asset) { [weak self] (request) in
guard let self = self else { return }
let input = request.sourceImage.clampedToExtent()
let frameCounter = Float(CMTimeGetSeconds(request.compositionTime)) * self.fps
if !editItem.defersPreview {
let outputImage = editItem.executeVideoFilterPipeline(on: input, excludedTypes: addCrop ? [] : [.crop], frameIndex: frameCounter, request: request, renderSize: renderSize)
if let output = outputImage {
request.finish(with: output, context: self.context)
}
}
}
videoComposition.renderSize = renderSize
videoComposition.sourceTrackIDForFrameTiming = kCMPersistentTrackID_Invalid
let frameRateTooHighForPhone = CMTime(value: 1, timescale: 1000)
videoComposition.frameDuration = frameRateTooHighForPhone
player.currentItem?.videoComposition = videoComposition
composition = videoComposition
play()
}
I already tried updating the frameduration as I was hoping it will do the trick.
I am wondering what the best solution to looping audio for a defined duration on iOS is.
I am currently playing around with
AVAudioPlayer (where I can define a repeat count but can't define an end-time)
AVPlayer (where I can define a forwardPlaybackEndTime bot not a loop count)
AVPlayerLooper (that I don't yet fully understand)
So what I need is to define a duration for which a certain sound-file is repeated. F.e. I have a 8 second mp3 and want to play it for f.e one minute.
What would also be suuuuper great, is if I could cross-fade when it starts over again.
You were on the right track with AVPlayerLooper.
This is how you setup AVPlayerLooper
var playerLooper: AVPlayerLooper!
var player: AVQueuePlayer!
func play(_ url: URL) {
let asset = AVAsset(url: url)
let playerItem = AVPlayerItem(asset: asset)
player = AVQueuePlayer(playerItem: playerItem)
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
player.play()
}
To stop the loop after a set amount of time you can use addBoundaryTimeObserver(forTimes:queue:using:)
For example:
let assetDuration = CMTimeGetSeconds(asset.duration)
let maxDuration = 60.0 // Define max duration
let maxLoops = floor(maxDuration / assetDuration)
let lastLoopDuration = maxDuration - (assetDuration * maxLoops)
let boundaryTime = CMTimeMakeWithSeconds(lastLoopDuration, preferredTimescale: 1)
let boundaryTimeValue = NSValue(time: boundaryTime)
player.addBoundaryTimeObserver(forTimes: [boundaryTimeValue], queue: DispatchQueue.main) { [weak self] in
if self?.playerLooper.loopCount == Int(maxLoops) {
self?.player.pause()
}
}
For fading in/out you have to set the audioMix property to your AVPlayerItem instance before using it.
let introRange = CMTimeRangeMake(start: CMTimeMakeWithSeconds(0, preferredTimescale: 1), duration: CMTimeMakeWithSeconds(1, preferredTimescale: 1))
let endingSecond = CMTimeRangeMake(start: CMTimeMakeWithSeconds(assetDuration - 1, preferredTimescale: 1), duration: CMTimeMakeWithSeconds(1, preferredTimescale: 1))
let inputParams = AVMutableAudioMixInputParameters(track: asset.tracks.first! as AVAssetTrack)
inputParams.setVolumeRamp(fromStartVolume: 0, toEndVolume: 1, timeRange: introRange)
inputParams.setVolumeRamp(fromStartVolume: 1, toEndVolume: 0, timeRange: endingSecond)
let audioMix = AVMutableAudioMix()
audioMix.inputParameters = [inputParams]
playerItem.audioMix = audioMix
Complete function:
func play(_ url: URL) {
let asset = AVAsset(url: url)
let playerItem = AVPlayerItem(asset: asset)
let assetDuration = CMTimeGetSeconds(asset.duration)
let introRange = CMTimeRangeMake(start: CMTimeMakeWithSeconds(0, preferredTimescale: 1), duration: CMTimeMakeWithSeconds(1, preferredTimescale: 1))
let endingSecond = CMTimeRangeMake(start: CMTimeMakeWithSeconds(assetDuration - 1, preferredTimescale: 1), duration: CMTimeMakeWithSeconds(1, preferredTimescale: 1))
let inputParams = AVMutableAudioMixInputParameters(track: asset.tracks.first! as AVAssetTrack)
inputParams.setVolumeRamp(fromStartVolume: 0, toEndVolume: 1, timeRange: introRange)
inputParams.setVolumeRamp(fromStartVolume: 1, toEndVolume: 0, timeRange: endingSecond)
let audioMix = AVMutableAudioMix()
audioMix.inputParameters = [inputParams]
playerItem.audioMix = audioMix
player = AVQueuePlayer(playerItem: playerItem)
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
player.play()
let maxDuration = 60.0 // Define max duration
let maxLoops = floor(maxDuration / assetDuration)
let lastLoopDuration = maxDuration - (assetDuration * maxLoops)
let boundaryTime = CMTimeMakeWithSeconds(lastLoopDuration, preferredTimescale: 1)
let boundaryTimeValue = NSValue(time: boundaryTime)
player.addBoundaryTimeObserver(forTimes: [boundaryTimeValue], queue: DispatchQueue.main) { [weak self] in
if self?.playerLooper.loopCount == Int(maxLoops) {
self?.player.pause()
}
}
}
I am trying to play videos on avplayer uing the following code...but get nothing as result as it shows duration nan.
func setUpPlayer(fileURL:URL){
let playerItem:AVPlayerItem = AVPlayerItem(url: fileURL)
player = AVPlayer(playerItem: playerItem)
let playerLayer=AVPlayerLayer(player: player!)
playerLayer.frame=CGRect(x:self.videoContainer.frame.origin.x, y:self.videoContainer.frame.origin.y+20, width:self.videoContainer.frame.size.width, height:self.videoContainer.frame.size.height-40)
player?.addObserver(
self, forKeyPath:"currentItem", options:.initial, context:nil)
self.view.layer.addSublayer(playerLayer)
rangeSlider.setVideoURL(videoURL:fileURL)
rangeSlider.delegate = self
self.endTime = CMTimeGetSeconds((player?.currentItem?.duration)!)
let timeInterval: CMTime = CMTimeMakeWithSeconds(0.01, 100)
// let asset:AVURLAsset = AVURLAsset.init(url:videoURL)
// let videoDuration:CMTime = asset.duration;
//
//
// let timeInterval: CMTime = CMTimeMakeWithSeconds(videoDuration,100)
//CMTimeGetSeconds(videoDuration)
timeObserver = player?.addPeriodicTimeObserver(forInterval: timeInterval,
queue: DispatchQueue.main) { (elapsedTime: CMTime) -> Void in
self.observeTime(elapsedTime: elapsedTime)
} as AnyObject!
}
I am doing this first time.Kindly give some solution to resolve this problem.Thanks in advance!
you missed to write "player.play()" in your code
let timeRange = self.avPlayer.currentItem.loadedTimeRanges[0].CMTimeRangeValue
let duration = CMTimeGetSeconds(timeRange.duration)
try this it will definiyely helpful
I have a uiView where I am playing a video with custom controls.
When I pause the video , it goes to the beginning of the video and pauses instead of pausing at that particular frame
Following is my Code
func playButtonTapped(cell: ViewTableCell) {
guard let indexPath = self.tableView.indexPath(for: cell) else {
return
}
let url = Bundle.main.path(forResource:ArtistFeeds.sharedInstance.videoFeeds[indexPath.row].videoUrl, ofType:"mp4")
let path = NSURL.fileURL(withPath: url!)
currentCell = tableView.cellForRow(at: indexPath) as! ViewTableCell
currentCell.videoPlayerItem = AVPlayerItem.init(url: path)
let playImage = UIImage(named: "image_video_play") as UIImage?
let pauseImage = UIImage(named: "image_video_pause") as UIImage?
if currentCell.avPlayer?.rate == 1.0 {
currentCell.stopPlayback()
currentCell.playButton.isHidden = false
currentCell.playButton.setImage(playImage, for: .normal)
} else {
currentCell.startPlayback()
currentCell.playButton.isHidden = true
currentCell.playButton.setImage(pauseImage, for: .normal)
}
}
I am setting up the videoPlayer in the ViewTableCell class as follows
var avPlayer: AVPlayer?
var avPlayerLayer: AVPlayerLayer?
var videoPlayerItem: AVPlayerItem? = nil {
didSet {
avPlayer?.replaceCurrentItem(with: self.videoPlayerItem)
}
}
#IBAction func playButtonAction(_ sender: UIButton) {
self.delegate?.playButtonTapped(cell: self)
}
func setupMoviePlayer(){
self.avPlayer = AVPlayer.init(playerItem: self.videoPlayerItem)
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer?.videoGravity = AVLayerVideoGravityResizeAspect
avPlayer?.volume = 3
avPlayer?.actionAtItemEnd = .none
avPlayerLayer?.frame = videoView.bounds
self.backgroundColor = .clear
videoView.layer.insertSublayer(avPlayerLayer!, at: 0)
let interval = CMTime(value: 1, timescale: 2)
avPlayer?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using :{ (progressTime) in
let seconds = CMTimeGetSeconds(progressTime)
if let duration = self.avPlayer?.currentItem?.duration{
let durationSeconds = CMTimeGetSeconds(duration)
self.videoSlider.value = Float(seconds/durationSeconds)
}
})
}
func stopPlayback(){
self.avPlayer?.pause()
}
func startPlayback(){
self.avPlayer?.play()
}
The problem is on every on click of the play button "currentCell.videoPlayerItem = AVPlayerItem.init(url: path)" is being called which is replacing the videoPlayerItem.
I want to pause the video at that particular frame and not go back to the beginning of the video.
How can I overcome this problem. Any help will be appreciated.
You replace the videoPlayerItem every times you press the button. That's why it resets:
avPlayer?.replaceCurrentItem(with: self.videoPlayerItem)
I can play the whole entire song, but I want to only play 30 seconds of it. How would I do that?
if let url: String = "hiddingthelink"{
let urlplayer = NSURL(string: url)
let playeritem = AVPlayerItem(URL:(urlplayer)!)
player = AVPlayer(playerItem: playeritem)
self.player.volume = 1.0
self.player.rate = 1.0
self.player.play()
}