Swift play video in TableviewCell URL from firebase - ios

I have a tableview which can display images or videos.
When it comes to images I get no problems.
But when it comes to video I'm getting an error on the console which I don't understand.
The error is : "Task .<2> finished with error - code: -999"
From the research I've made it should be exit before execution.
But I really don't understand what it means.
Here's my code for the tableView Cell:
var post: Post? {
didSet {
updateView()
}
}
func updateView() {
captionLbl.text = post?.caption
usernameLbl.text = "Test"
if (post?.isVideo)! {
if let videoThumbUrlString = post?.videoThumbUrl {
let videoThumbUrl = URL(string: videoThumbUrlString)
postImgView.sd_setImage(with: videoThumbUrl)
}
}
if !(post?.isVideo)! {
if let photoUrlString = post?.photoUrl {
let photoUrl = URL(string: photoUrlString)
postImgView.sd_setImage(with: photoUrl)
}
}
}
func createPlayer() {
if (post?.isVideo)! {
if let videoUrlString = post?.videoUrl, let url = URL(string: videoUrlString) {
player = AVPlayer(url: url)
playerLayer = AVPlayerLayer(player: player)
playerLayer?.frame = self.postImgView.bounds
playerLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.postImgView.layer.addSublayer(playerLayer!)
player?.play()
activityIndicatorView.startAnimating()
playBtn.isHidden = true
}
}
}
#IBAction func playBtnPressed(_ sender: Any) {
createPlayer()
}
override func prepareForReuse() {
super.prepareForReuse()
playerLayer?.removeFromSuperlayer()
player?.pause()
activityIndicatorView.stopAnimating()
}
When i tap on playButton the video plays but i get the error I've mentioned before.
I'm not sure if is connected but also the activity indicator never stops running.
Hope somebody can help!
Thank you.
---------------------UPDATE
The full console log message is:
"
2017-10-30 14:10:00.996661+0100 PhotoApp[92272:543501] Task .<2> finished with error - code: -999
"

Well, error does not show something. But try next change:
if let videoUrlString = post?.videoUrl, let url = URL(string: videoUrlString) {
let asset = AVURLAsset(url: url) // < --
player = AVPlayer(playerItem: AVPlayerItem(asset: asset)) // < --
playerLayer = AVPlayerLayer(player: player)
playerLayer?.frame = self.postImgView.bounds
playerLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.postImgView.layer.addSublayer(playerLayer!)
player?.play()
activityIndicatorView.startAnimating()
playBtn.isHidden = true
}

Related

How to detect Video does not exist state from url in AVPlayer iOS

I am playing videos from urls in AVPlayer. It is working fine. But, Few videos keep loading but not player, I checked them in browser and they are showing Video does not exist.
So, How to detect that video does not exist state and how to show alert for end user understands.
override func viewDidLoad() {
super.viewDidLoad()
self.setupAVAudioSession()
}
func playVideo(){
if let str = videoUrl{
if let videoURL:URL = URL(string: str) {
player = AVPlayer(url: videoURL)
player?.rate = 1 //auto play
let playerFrame = CGRect(x: 0, y: 0, width: 200, height: 210)
let playerViewController = AVPlayerViewController()
playerViewController.delegate = self
playerViewController.player = player
playerViewController.view.frame = playerFrame
playerViewController.showsPlaybackControls = true
addChild(playerViewController)
videoPlayerView.addSubview(playerViewController.view)
}
}
}
func setupAVAudioSession(){
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
}
catch {
print("Setting category to AVAudioSessionCategoryPlayback failed.")
}
}
func playerViewController(_ playerViewController: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator){
}
func playerViewController(_ playerViewController: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator){
playerViewController.dismiss(animated: true)
}
Any suggestions?
You can check existence of your video url by sending HEAD request before start playing it e.g.:
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request) { data, response, error in
guard error == nil,
let r = response as? HTTPURLResponse,
r.statusCode == 200
else {
print("url is not valid")
return
}
print("url is valid")
}
task.resume()
I have not get any fix for this from mobile side, So, we have fixed this from backend side.

Playing Vimeo video using HCVimeoVideoExtractor, can't get video URL with specific quality

I'm using this lib https://github.com/superm0/HCVimeoVideoExtractor to play vimeo url. It works fine but changing the quality doesn't return videoURL for some videos. I'm talking about this line
if let videoURL = vid.videoURL[.Quality1080p]
I'm not able to understand why it doesn't return videoURL for a video when I pass Quality1080p?
How to fix it? Is there something wrong with my videos? Do I need to convert all of videos to same quality and then upload on Vimeo?
Following is my code:
import UIKit
import HCVimeoVideoExtractor
import AVKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func playClicked(_ sender: UIButton) {
guard let url = URL(string: "https://vimeo.com/1234567") else {
print("Error url nill")
return
}
HCVimeoVideoExtractor.fetchVideoURLFrom(url: url, completion: { ( video:HCVimeoVideo?, error:Error?) -> Void in
if let err = error {
print("Error = \(err.localizedDescription)")
return
}
guard let vid = video else {
print("Invalid video object")
return
}
print("Title = \(vid.title), url = \(vid.videoURL), thumbnail = \(vid.thumbnailURL)")
if let videoURL = vid.videoURL[.Quality1080p] {
let player = AVPlayer(url: videoURL)
DispatchQueue.main.async {
let playerController = AVPlayerViewController()
playerController.player = player
self.present(playerController, animated: true) {
player.play()
}
}
} else {
print("Error: videoURL not found")
}
})
}
}
This will helps you i think. Add this to your code
This is the count of your video last numbers
videoArr = self.arrFilterItems[indexPath.row].link
last9 = String(videoArr.suffix(9))
var last9 = String()
here link = https://vimeo.com/(123456789) == 9 or (123456) == 6

The URL given through the AVCaptureFileOutputRecordingDelegate function unable to be played

I'm trying to create a Snapchat-like app where the user can take a video by holding down a button. However, when the user is done taking a video, and the delegate calls fileOutput, the given url outputFileURL cannot be played using an AVPlayer. I know that the video was actually recorded though, because I can upload the file to Firebase and download it from there.
Here's my code for the fileOutput function:
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
if error == nil {
videoURL = outputFileURL
flipHiddenViews()
// playback video
player = AVPlayer(url: outputFileURL)
playerLayer = AVPlayerLayer(player: player)
playerLayer?.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer!)
player?.play()
} else {
print(error?.localizedDescription)
}
}
Here's how I initialize the button that the user holds down:
let photoButton:UIButton = {
let but = UIButton(type: .custom)
but.layer.cornerRadius = 40
but.layer.borderColor = UIColor.white.cgColor
but.layer.borderWidth = 4
but.clipsToBounds = true
but.addTarget(self, action: #selector(takeVideo), for: .touchDown)
but.addTarget(self, action: #selector(stopVideo), for: [.touchUpInside, .touchUpOutside])
but.translatesAutoresizingMaskIntoConstraints = false
return but
}()
Here's the takeVideo function:
#objc func takeVideo() {
let recordingDelegate:AVCaptureFileOutputRecordingDelegate? = self
if captureSession?.outputs != nil && videoFileOutput != nil {
captureSession?.removeOutput(videoFileOutput!)
}
videoFileOutput = AVCaptureMovieFileOutput()
self.captureSession?.addOutput(videoFileOutput!)
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let filePath = documentsURL.appendingPathComponent("temp")
// Do recording and save the output to the `filePath`
videoFileOutput?.startRecording(to: filePath, recordingDelegate: recordingDelegate!)
}
And finally, the stopVideo function:
#objc func stopVideo() {
videoFileOutput?.stopRecording()
}
What am I doing wrong?
Try this instead:
// playback video
player = AVPlayer(url: outputFileURL)
let playerController = AVPlayerViewController()
playerController.player = player
present(playerController, animated: true) {
player?.play()
}
Adding a .mov at the end of the "temp" name did it for me:
let filePath = documentsURL.appendingPathComponent("temp.mov")

Videos stops and plays from the beginning instead of pausing ,swift AVPlayer

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)

Seamless looping Video using AVPlayer- Swift

I am using AVPlayer to play local video in background using loop and video is playing fine but after finishing video it takes pause to play video in loop. I have tried many methods and also seen many post on stack overflow but i failed to find appropriate solution. I am using Swift3.
Code is here :
var videoplayer :AVPlayer = AVPlayer()
override func viewDidLoad() {
super.viewDidLoad()
let path = Bundle.main.path(forResource: "background4", ofType: "mp4")
videoplayer = AVPlayer(url: URL(fileURLWithPath: path!))
videoplayer.volume = 0
videoplayer.actionAtItemEnd = AVPlayerActionAtItemEnd.none;
let playerLayer = AVPlayerLayer(player: videoplayer)
playerLayer.frame = self.view.frame
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
if (videoplayer.rate != 0) {
print("playing videoplayer")
self.blurBgImage.isHidden = true
}
playerLayer.zPosition = -1
videoplayer.rate = 0
videoplayer.play()
self.blurBgImage.layer.addSublayer(playerLayer)
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: videoplayer.currentItem, queue: nil, using: { (_) in
DispatchQueue.main.async {
let t1 = CMTimeMake(5, 100)
self.videoplayer.seek(to: t1)
self.videoplayer.play()
}
})
}
I have also tried AVPlayerLooper.
Code is :
var playerLooper: NSObject?
var playerLayer:AVPlayerLayer!
var queuePlayer: AVQueuePlayer?
func playVideo(_ filmName: String){
if let path = Bundle.main.path(forResource: filmName, ofType: "mp4") let url = URL(fileURLWithPath: path)
if #available(tvOS 10.0, *) {
let playerItem = AVPlayerItem(url: url as URL)
self.videoplayer = AVQueuePlayer(items: [playerItem])
self.playerLayer = AVPlayerLayer(player: self.videoplayer)
self.playerLooper = AVPlayerLooper(player: self.videoplayer as! AVQueuePlayer, templateItem: playerItem)
self.blurBgImage.layer.addSublayer(playerLayer!)
self.playerLayer?.frame = self.view.frame
self.videoplayer.volume = 10
self.videoplayer.play()
} else {
videoplayer = AVPlayer(url: url)
videoplayer.play()
loopVideo(videoplayer)
}
}
}
What should i do for seamless looping? Thanks in Advance.
FYI there is sample code here: https://developer.apple.com/library/content/samplecode/avloopplayer/Introduction/Intro.html
#matt's deleted answer works fine for me (on device / simulator) for iOS 10+ devices:
Use AVPlayerLooper. That is exactly what it is for.
https://developer.apple.com/reference/avfoundation/avplayerlooper
Basically it implements AVQueuePlayer for you, constantly updating the
queue so that it never ends.
It seamlessly loops without any white/black blip.
E.g.
private var looper: AVPlayerLooper?
...
let queuePlayer = AVQueuePlayer(playerItem: item)
looper = AVPlayerLooper(player: queuePlayer, templateItem: item)
videoPlayerLayer.player = queuePlayer
If you end up doing this in a reusable cell (e.g. UICollectionView), then make sure you disable looping before cell re-use or you'll get some obscure crashes:
looper?.disableLooping()

Resources