I'm trying to play a video downloaded to the device's document directory. For some reason, it won't happen. All I get is this symbol:
Here's my code:
let documentPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let videoFileUrl = URL(fileUrlWithPath: documentPath)
let videoFilePath = videoFileUrl.appendingPathComponent("somVideo.mp4").path
if FileManager.default.fileExists(atPath: videFilePath){
let player = AVPlayer(url: URL(string: videoFilePath)!)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
}
else {
print("File doesn't exist")
}
The file exists at the location - I have checked my documents directory for the simulator - and the else statement isn't fired. Nothing else is printed to the console.
Any help would be appreciated.
Here's the answer the OP edited into the question (and which was subsequently rolled back because answers should be answers). If OP posts a separate answer, I'll remove this one.
I managed to get it working with a little help from a colleague of mine. Here's the answer if anyone should be interested:
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
if let docDir : URL = urls.first {
let videoUrl = docDir.appendingPathComponent("\(objectId).mp4")
do {
let videoExists : Bool = try videoUrl.checkResourceIsReachable()
if videoExists {
let player = AVPlayer(url: videoUrl)
let playerController = AVPlayerViewController()
playerController.player = player
self.present(playerController, animated: true) {
playerController.player!.play()
}
}
else {
print("Video doesn't exist")
}
}
catch let error as NSError {
print(error)
}
}
Related
I try to save given video locally after then I need those saved videos for playing video in my app. I can't handle the saving video. Here is my saving try :
func saveVideoDocumentDirectory(url : URL){
let fileManager = FileManager.default
let paths = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(".MOV")
do{
let videoData = try Data(contentsOf: url)
fileManager.createFile(atPath: paths as String, contents: videoData, attributes: nil)
}catch{
//
}
}
here is the get file try
func getVideo(){
let fileManager = FileManager.default
let videoPAth = (self.getDirectoryPath() as NSString).appendingPathComponent(".MOV")
if fileManager.fileExists(atPath: videoPAth){
print(videoPAth)
play(url: URL(string: videoPAth)!)
}else{
print("No Video")
}
}
here is my play video func :
func play(url : URL)
{
let player = AVPlayer(url: url)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
present(playerViewController, animated: true)
{
playerViewController.player!.play()
}
}
Instead of Filemanager.createFile(), try using write instead.
let videoData = try Data(contentsOf: url)
try videoData.write(to: paths, options: .atomic)
Also, I recommend creating a folder first (from this answer).
extension URL {
static func createFolder(folderName: String) -> URL? {
let fileManager = FileManager.default
// Get document directory for device, this should succeed
if let documentDirectory = fileManager.urls(for: .documentDirectory,
in: .userDomainMask).first {
// Construct a URL with desired folder name
let folderURL = documentDirectory.appendingPathComponent(folderName)
// If folder URL does not exist, create it
if !fileManager.fileExists(atPath: folderURL.path) {
do {
// Attempt to create folder
try fileManager.createDirectory(atPath: folderURL.path,
withIntermediateDirectories: true,
attributes: nil)
} catch {
// Creation failed. Print error & return nil
print(error.localizedDescription)
return nil
}
}
// Folder either exists, or was created. Return URL
return folderURL
}
// Will only be called if document directory not found
return nil
}
}
Then, you can save like this:
guard let folderURL = URL.createFolder(folderName: "StoredVideos") else {
print("Can't create url")
return
}
let permanentFileURL = folderURL.appendingPathComponent(nameOfYourFile).appendingPathExtension("MOV")
let videoData = try Data(contentsOf: url)
try videoData.write(to: permanentFileURL, options: .atomic)
This will save you the hassle of NSSearchPathForDirectoriesInDomains.
My objective is to read a video media I downloaded and stored to my iOS device file system. Unfortunately, the video player stalls with the following code:
#IBAction func playVideo(_ sender: UIButton) {
if let video = detailItem {
do {
let url = try FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
.appendingPathComponent(video.uuid)
debugPrint("url: \(url)")
// Create an AVPlayer, passing it the HTTP Live Streaming URL.
let player = AVPlayer(url: url)
// Create a new AVPlayerViewController and pass it a reference to the player.
let controller = AVPlayerViewController()
controller.player = player
// Modally present the player and call the player's play() method when complete.
present(controller, animated: true) {
player.play()
}
} catch {
print("Error: \(error)")
}
}
}
I assumed it was a video format problem and the codec of my video wasn't supported.
But, when I bundle the exact same video within the app, and switch to this code, everything works fine:
#IBAction func playVideo(_ sender: UIButton) {
let url = Bundle.main.url(forResource: "myvideo", withExtension: "mp4")!
// if let video = detailItem {
do {
// let url = try FileManager.default.url(for: .documentDirectory,
// in: .userDomainMask,
// appropriateFor: nil,
// create: false).appendingPathComponent(video.uuid)
debugPrint("url: \(url)")
// Create an AVPlayer, passing it the HTTP Live Streaming URL.
let player = AVPlayer(url: url)
// Create a new AVPlayerViewController and pass it a reference to the player.
let controller = AVPlayerViewController()
controller.player = player
// Modally present the player and call the player's play() method when complete.
present(controller, animated: true) {
player.play()
}
} catch {
print("Error: \(error)")
}
// }
}
I have no idea what I am missing here and could get some help. 🙏
EDIT: The code in charge of the download is here:
let downloadTask = URLSession.shared.downloadTask(with: video.downloadURL, completionHandler: { (tempPathURL, urlResponse, error) in
guard let tempPathURL = tempPathURL else {
return
}
do {
let documentsDirectoryURL = try FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
try FileManager.default.moveItem(at: tempPathURL, to: documentsDirectoryURL.appendingPathComponent(video.uuid))
} catch {
print("Error: \(error)")
}
})
downloadTask.resume()
You are using the wrong URL for playing downloaded video. Please check your code carefully and notice that you are using the documents directory URL for the player, instead of the actual video.
you should use fileURLWithPath to run video from app files, like this:
func PlayVideo() {
let docPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let player = AVPlayer(url: URL(fileURLWithPath: docPath).appendingPathComponent("IMG_3039.mp4"))
let controller = AVPlayerViewController()
controller.player = player
self.present(controller, animated: true) {
player.play()
}
}
I am using AVPlayer to play a video with the localPath URL but it is not playing on AVPlayer.
And i am getting localPath with this code:
var selectedAssets = [TLPHAsset]()
for abcd in self.selectedAssets {
let asset = abcd
if asset.type == .video {
//------------- Video Path --------------------//
let fm = FileManager.default
let docsurl = try! fm.url(for:.documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let url = docsurl.appendingPathComponent(stringUrl!)
}
}
here is the path:-
file:///var/mobile/Containers/Data/Application/915BA33E-5DB9-42C4-B5CD-3898D81FBDC5/Documents/77666c29-75a3-4d89-aecf-15d0f47fbe83.mp4
let video = dbImageDataModel[indexPath.row].fileUrl
print(video)
playerView = AVPlayer(url: URL(fileURLWithPath: video))
playerViewController.player = playerView
self.present(playerViewController, animated: true, completion: {
self.playerViewController.player!.play()
})
You are using the wrong API.
absoluteString returns the URL string including the file:// scheme. To create a new URL you have to use URL(string rather than URL(fileURLWithPath.
To clarify
absoluteString returns file:///var/mobile/Containers/Data/Application.... To create an URL use URL(string:
path returns /var/mobile/Containers/Data/Application.... To create an URL use URL(fileURLWithPath:
I've been working on a small project that involves downloading a video file from a web server, copying said file to the documents directory and then playing it via AVPlayer.
Downloading the file to the documents directory hasn't been an issue. I'm able to download the file and save it without issue. However, when it comes to loading the file into AVPlayer, and in doing that I'm playing it in an instance of AVPlayerViewController, the vide controller pops up as it should, but video doesn't load.
I realize that when testing in the simulator the documents directory changes each time you rebuild the project. Which is why I check to see if the file is present before I play, and though I know the file is present, it still refuses to play.
Here is what my player code looks like:
let fileName = downloadURL.characters.split("/").map(String.init).last as String!
let fileNameHD = downloadURLHD.characters.split("/").map(String.init).last as String!
let downloadFilePath = getDocumentsDirectory() + "/" + "\(fileNameHD)"
let checkValidation = NSFileManager.defaultManager()
if checkValidation.fileExistsAtPath(downloadFilePath){
print("video found")
}
let videoFile = NSURL(string:downloadFilePath)
let player = AVPlayer(URL: videoFile!)
let playerController = AVPlayerViewController()
playerController.player = player
playerController.view.frame = self.view.frame
player.play()
self.presentViewController(playerController, animated: true) {
playerController.player!.play()
}
Every time when we rebuild the application Our Document Directory Path change.
So you can't play the video from the old document directory path. So instead of that you have to save the last path component of your URL. Like your document directory url look like this after downloaded the video on this path:-
let videoURL = "/var/mobile/Containers/Data/Application/1F6CDF42-3796-4153-B1E8-50D09D7F5894/Documents/2019_02_20_16_52_47-video.mp4"
var videoPath = ""
if let url = videoURL {
videoPath = url.lastPathComponent
}
print(videoPath)
// It will print the last path of your video url: - "2019_02_20_16_52_47-video.mp4"
Now save this path either in the Core Database or Sqlite or User Defaults where ever you want. Then if you want to play the video again. So you have to get this path from where you save it.
Note:- In Below function you have to pass the last path component of your video. How to call this function.
func playVideo() {
self.startVideoFrom(videoPath:"2019_02_20_16_52_47-video.mp4")
}
// Play Video from path
func startVideoFrom(videoPath: String) {
let outputMineURL = self.createNewPath(lastPath: videoPath)
let player = AVPlayer(url: outputMineURL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
}
/// Generate the new document directory path everytime when you rebuild the app and you have to append the last component of your URL.
func createNewPath(lastPath: String) -> URL {
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let destination = URL(fileURLWithPath: String(format: "%#/%#", documentsDirectory,lastPath))
return destination
}
For more reference, you can see this question:- https://stackoverflow.com/q/47864143/5665836
Try AVPlayer instead of AVPlayerViewController like,
let videoURL = NSURL(string: "your url string")
let player = AVPlayer(URL: videoURL!)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer)
player.play()
And import AVKit, import AVFoundation.
OR With viewController like this,
let player = AVPlayer(URL: url)
let playerController = AVPlayerViewController()
playerController.player = player
self.addChildViewController(playerController)
self.view.addSubview(playerController.view)
playerController.view.frame = self.view.frame
player.play()
func listVideos() -> [URL] {
let fileManager = FileManager.default
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let files = try? fileManager.contentsOfDirectory(
at: documentDirectory,
includingPropertiesForKeys: nil,
options: [.skipsSubdirectoryDescendants, .skipsHiddenFiles]
).filter {
[".mp4", ".mkv"].contains($0.pathExtension.lowercased())
}
return files ?? []
}
And Then Play Video Like this I am only playing first URL
let videosURLs = self.listVideos()
let player = AVPlayer(url: videosURLs[0])
let playerViewController = AVPlayerViewController()
playerViewController.player = player
present(playerViewController, animated: true) { () -> Void in
player.play()
}
Hello everybody
I've been stuck with this problem with a whole day and actually I don't find a way to play a video file, I will explain a little bit what I did:
I have the following Url: [https://rapidresponse-us-oem-storage11.mios.com/storage/storage/store/8157061/archive?Key=833101823][1]
If you could see in that url I'm getting a video file .mp4 and I need to play this video, the following is the code that I'm using, first I'm saving the file and after that i'm trying to play in the AVPlayerViewController:
let store = self.eventDetail?.EventDetailStore!
let url = "https://" + (self.eventDetail?.EventDetailServerStorage)! + "/storage/storage/store/" + store! + "/archive?Key=" + self.EventKey!
if let videoUrl = NSURL(string: url) {
// create your document folder url
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first! as NSURL
// your destination file url
let destinationUrl = documentsUrl.URLByAppendingPathComponent(videoUrl.lastPathComponent!)
print(destinationUrl)
// check if it exists before downloading it
if NSFileManager().fileExistsAtPath(destinationUrl.path!) {
print("The file already exists at path")
let player = AVPlayer(URL: destinationUrl)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.presentViewController(playerViewController, animated: true) {
playerViewController.player!.play()
}
} else {
// if the file doesn't exist
// just download the data from your url
if let myAudioDataFromUrl = NSData(contentsOfURL: videoUrl){
// after downloading your data you need to save it to your destination url
if myAudioDataFromUrl.writeToURL(destinationUrl, atomically: true) {
print(destinationUrl)
let player = AVPlayer(URL: destinationUrl)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.presentViewController(playerViewController, animated: true) {
playerViewController.player!.play()
}
} else {
let alert = UIAlertController(title: "Alert", message: "An error occurred", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
}
}
When I run the code i get the Video Controller but don't reproduce anything like no file exist, but the I debug the code and the file seems to be saved.
I will appreciate any help with this problem
Base on your suggestions I Updated the code, but it isn't work yet:
//video
var player: AVPlayer!
var destinationUrl: NSURL? = nil
dispatch_async(dispatch_get_global_queue(priority, 0)) {
let store = self.eventDetail?.EventDetailStore!
let url = "https://" + (self.eventDetail?.EventDetailServerStorage)! + "/storage/storage/store/" + store! + "/archive?Key=" + self.EventKey!
if let videoUrl = NSURL(string: url) {
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first! as NSURL
// your destination file url
destinationUrl = documentsUrl.URLByAppendingPathComponent(videoUrl.lastPathComponent!)
if NSFileManager().fileExistsAtPath(destinationUrl!.path!) == false {
if let myAudioDataFromUrl = NSData(contentsOfURL: videoUrl){
myAudioDataFromUrl.writeToURL(destinationUrl!, atomically: true)
}
}
}
dispatch_async(dispatch_get_main_queue()) {
if(destinationUrl != nil){
player = AVPlayer(URL: destinationUrl!)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.presentViewController(playerViewController, animated: true, completion: { () -> Void in playerViewController.player!.play() })
}
}
}
}
Your player is going out of scope and is being deallocated before your video has finished playing. Assign it to a class member to extend its lifetime.
You're missing the completion handler parameter:
self.presentViewController(playerViewController, animated: true) {
() in playerViewController.player!.play()
}