AVPlayer sometimes doesn't load a video - ios

I have a one button and AVPlayer. After button click I record a video using DBAttachmentPickerController and I want to load it to the AVPlayer.
In function below I try to load it:
func refresh(attachmentInfo: AttachmentInfo) {
self.videoLayer.player = nil
if let url = attachmentInfo.url {
self.player = AVPlayer(url: url) // line A
self.videoLayer.player = self.player // line B
}
}
In 6/10 cases it works fine, but sometimes the video doesn't load to AVPlayer.
When I set breakpoints in line A and B it works always.
Any ideas?

I also hit this exact issue as yours. In my case the AVPlayer and AVPlayerLayer were inside tableview cells which made the problem worse. However, I was using an API which returned the actual video URL, and an Image URL which consisted of the video's first frame's image. In the tableview cell logic, I first attempt to load the video into AVPlayer and then check if the AVPlayer has any .video assets, if it didn't then I simply load the Image from the other URL into the UIView (using a UIImageView of course) with a "Play" button image in the center of the Image. When the user taps the play button the selector function should try to load the AVPlayer again with the video URL. If the video loads and AVPlayer has assets then play() else simply return (and possibly alert the user that the video can't be loaded).
If you do not have the image of the first frame of the video, you can simply use a placeholder image and a button.
The way to check if the player has a video or not-
if player.currentItem?.asset.tracks(withMediaType: .video).count == 0{
print("Oops! no video here")
//load an image, and add a button here
}else{
//do whatever needs to be done if video is available
}
Unfortunately, there isn't much else that can be done here unless you have some solid networking code where in you can download the video in a utility thread and store it in cache, but then again you have to be cognizant about the user's data usage, spawning too many download threads etc.
For image caching in the UITableView cell, I used SDWebImage library from Github so that I can be (somewhat) assured that my image (for the video) atleast would be cached in case of bad/slow network, or network interruptions.

Related

Swift access to a file that is stored in another folder

I am creating a app, where you can record videos or images. You can record videos or images on your own or you can pick it from the UIImagePicker. The videos/images are listed in a collection view.
When you tab on a row in the collection view it shows the image or the video.
One thing I don't understand is, if I record a video to safe it, it shows up in the collection view, but when I choose the video that I recorded before, I only see a grey background and not the actually video.
If I choose a video from the gallery it shows up and play.
This is how I display the video
player = AVPlayer(url: arrayOfVideoUrls[i]!)
print(arrayOfVideoUrls[i]!)
playerLayer = AVPlayerLayer(player: player)
playerLayer?.frame = view.frame
view.layer.addSublayer(playerLayer!)
player?.play()
My guess is, that I can't open the video, because it is saved in another folder.
This is the folder of my self recorded Video:
file:///private/var/mobile/Containers/Data/Application/E0EBF7F9-713C-451B-B764-D2056ECC6E48/tmp/7834C1AF-1417-4389-B455-92DA52DFF93F.mov
This is the folder of the video from the gallery:
file:///private/var/mobile/Containers/Data/PluginKitPlugin/1A977F99-6C5C-40F7-933D-E325D8E967E4/tmp/trim.31B4A909-E8FA-4CBE-B64F-30646A6366B3.MOV
You need to give the path whenever you save your recorded video and after saving you need to check that path whether its containing file or not.
And Please make sure, you give different name to your recording whenever you record a new video.And to play that video append the name of video with the path.

Implementing media player using AVPLayer to play audio from remote url in popup view in modal view controller in Swift 4

Sorry I made title too long as I'm trying to accomplish a lot of things in an attempt to play audio file from an url using AVPlayer in a modal view controller. I've used Apple's foundation example - AVFoundationSimplePlayer, tweaked it and added few things. Player works fine but I've few challenges to solve as described below:
Here is the complete source code Source Code of my project
1) Refer to screenshot below, I want the background of modal vc to be transparent so that I can see the view which launched this modal view.
2) There is delay after loading the modal vc and before the popup view appears with controls highlighted. I'm not sure if this is normal to have delay? If it's normal and we can't do anything about it then I want to add activity control during this loading period. I'm not sure when to stop this activity control.
3) There are errors shown before player loads the control. Not sure what they are?
2018-09-05 13:32:56.519014+0100
AVFoundationSimplePlayer-Swift[44042:2403222] Task
.<2> finished with error - code:
-999 2018-09-05 13:32:57.607560+0100 AVFoundationSimplePlayer-Swift[44042:2403221] Task
.<3> finished with error - code:
-999
I just checked your attached sample code. You are using play function before assigning any AVPlayerItem to your player. You're loading your asset asynchronously and before downloading the asset player can not play. So what you should do
asset = AVURLAsset(url: movieURL, options: nil)
/// Just after making an asset and remove the code written in asset's setter
let item = AVPlayerItem(asset: asset!)
player = AVPlayer(playerItem: item)
/// This extra line is to playing the live url. It will play what it downloads in chunks.
if #available(iOS 10.0, *) {
player.automaticallyWaitsToMinimizeStalling = false
} else {
// Fallback on earlier versions
}
/// Now your player is ready to play
player.play()
EDIT 1
If you're still struggling with above piece of code so I have made changes in PlayerViewController.m file to fix your issues. Here is the gist link of that file. Search /// TheTiger Change to find what change I made. Now I'm able to play audio file with proper time and slider value.
EDIT 2
I thought you will be able to see the difference but no problem I will explain in more detail.
#3. Regarding your error message this is just a Xcode debug message and you can disable it. See this answer for more detail.
#2. There is a delay in presenting the modal because you have all the code in your viewWillAppear: method just move that code in videDidAppear: and let the view appear first before doing anything. It will remove the delay.
#1. Background of modal vc to be transparent. So it is possible to make it transparent See this answer.
This Sample Code just works fine.

Play video in UITableViewCell or UICollectionViewCell iOS

There is so much stuff in Stackoverflow about playing videos in cell, that every person may feel confused, especially that most of question are not solved and it looks like that every developer gives up.
So I wanted to create new question where there will be up-to-date answers.
Objective:
Play video in UITableViewCell or UICollectionViewCell when user taps cell (yes, this is simplified version, this is not about autplay).
Solution:
I think everyone agrees that we should use AVPlayer instead of deprecated MPMoviePlayerController.
Cell should have layer where AVPlayer can be assigned, so in my code while creating cell I added this code:
VideoCell.swift
let layer = AVPlayerLayer()
layer.backgroundColor = UIColor.clear.cgColor
layer.videoGravity = AVLayerVideoGravityResizeAspectFill
Now when cell is tapped I'm using this code:
ViewController.swift
cell.playerLayer.player = player
player.replaceCurrentItem(with: AVPlayerItem(asset: asset))
player.play()
Where player is global instance of AVPlayer and asset is AVAsset initialized with video URL.
It would work fine, but AVPlayer needs time to download info and buffer some part of video, and what user gets is delay. So we can think about one solution, which is downloading video first and playing it locally when users taps in cell.
Question:
Is it really good approach to downloading every video in data source and then playing it locally? When should we remove it from device? Facebook and Instagram looks like they download only part of the video first, is there any who can share own experience?
Objective-C code will be appreciated too.

How to cache AVPlayer video?

I have AVPlayers embedded into UITableViewCells and need a way to cache the video that's loaded so that when I refresh the table view, it doesn't have to load the videos again. The code I'm using at the moment to load videos is as follows:
let videoString = "http://www.someurl.com"
let videoURL = NSURL(string: videoString)
videoPlayer = AVPlayer(URL: videoURL!)
videoPlayer!.actionAtItemEnd = AVPlayerActionAtItemEnd.None
videoView.playerLayer.player = videoPlayer
I cache images by storing the returned image from a server in a dictionary with it's ID as the key. I'm looking for a similar way for videos, any help would be appreciated.
Ideally their is no in-built support to cache the vidoes,however depending upon the size of the video you may try to download it in background, once the video start playing.
However do note that this process will increase the cache size of the app, and may consume lot of space depending upon the size of the video, so you may cache few videos that the user has watched few minutes back, or you may restrict it to the 5-10 latest vidoes.

How to stop downloading a streaming HLS video after navigating away from AVPlayer view?

I am trying to play a HLS stream using AVPlayer. The player plays the stream fine, however, after navigating away from the player view, it does not seem to stop downloading data for the HLS stream. I see a network data spinner on the status bar for a few minutes after navigating away from the AVPlayer view.
I have tried cancelPendingSeeks, cancelLoading, and tried removing the AVPlayerLayer from its superlayer using removeFromSuperLayer, however, none of these seems to solve the issue — there is still a spinner on the status bar.
This spinner is seen on actual device, but there is no spinner on the simulator. I am certain the video is being downloaded; I can see the data usage in network monitoring apps. How can I fix this?
Call pause on your AVPlayer object, then replace the item with a nil item, and set your video player to nil.
[anAVPlayerObject pause];
[anAVPlayerObject replaceCurrentItemWithPlayerItem:nil];
anAVPlayerObject = nil;

Resources