Using Haneke to cache then play MP4 files with AVPlayer - ios

I'm looking for some help solving what seems like a simple problem, but I just can't seem to figure it out.
I'm using Haneke from Cocoapods to cache MP4 files from URL (on my server) and then play the cached version using AVPlayer. I'm setting up an AVPreviewLayer inside a UICOllectionViewCell and running this code. The issue is that the player won't play the video initially, but it will play it again in the future (after i scroll back to that cell in the uicollectionview)
let getUrl = NSURL(string: location)
cache.fetch(URL: getUrl!).onSuccess { (data) in
// Hacky way of getting cache URL from Haneke
let basePath = DiskCache.basePath().stringByAppendingPathComponent("shared-data/original/")
let path = DiskCache(path: basePath, capacity: UINT64_MAX).pathForKey(getUrl!.absoluteString)
let fileURL = NSURL(fileURLWithPath: path)
self.player = AVPlayer(URL: fileURL)
let playerLayer = AVPlayerLayer(player: self.player)
playerLayer.frame = self.view.bounds
cell.videoView.layer.addSublayer(playerLayer)
print("Playing video")
self.player.play()
}

Related

Load Avplayer with large amount of data (decrypted output of a local file)

Am using below code for decrypting the VideoFile.mp4(CTR/Nopadding encrypted file) and playing it in Avplayer.
This code works for small files. But its crashing for big video files due to memory issues.
How to solve this issue. Please help
guard let content = Bundle.main.url(forResource: "videoFile", withExtension: "mp4") else{
return
}
let vidoeData = NSData(contentsOf: content)!
let vidoeDatadencrypted = vidoeData.decryptData(vidoeData as Data, withKey: aeskey.data(using: .utf8))
let tmpFileURL1 = URL(fileURLWithPath:NSTemporaryDirectory()).appendingPathComponent("video").appendingPathExtension("mp4")
let wasFileWritten1 = (try? vidoeDatadencrypted!.write(to: tmpFileURL1, options: [.atomic])) != nil
if wasFileWritten1{
let player = AVPlayer(url: tmpFileURL1)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer)
player.play()
}
update:
Able to achive this for smaller files using the above shared code.
how to load avplayer with NSStream?
you need to open your file using InputStream, pass the data chunk to CommonCrypto lib, write the output chunk to the OutputStream and repeat until file ends.
I haven't seen any guide to do so, but as a reference I've found a couple of examples for you: first, second, third.
Related questions for you to check out: first, second

Playing mpga format files with AVPlayer in ios swift

I tried to run a remote mpga format audio file with AVPlayer in ios and doesn't play. Below code works fine for remote mp3 files. Can i change the MIME type for the file loaded or are there any options to play the mpga file in ios.
let audioUrl = "https://example-files.online-convert.com/audio/mpga/example.mpga"
let url = URL(string: audioUrl)
let player: AVPlayer = AVPlayer(url: url!)
player.play()
console shows
nw_endpoint_flow_copy_multipath_subflow_counts Called on non-Multipath connection
let audioUrl = "https://example-files.online-convert.com/audio/mpga/example.mpga"
if let url = URL(string: audioUrl){
let playerItem = AVPlayerItem(url: url)
let player: AVPlayer(playerItem: playerItem)
player.play()
}

iOS - Swift - AVPlayer: Error playing with url containing khmer unicode

I've just built a custom video player with AVPlayer, and I've found that I can only play video with url containing english character(for example: http://app.ournsarath.com/uploads/videos/mp4s/09_03_2018_business.mp4). Whenever I pass the url containing khmer unicode(for example: http://app.ournsarath.com/uploads/sound/mp3s/01.06.2018Helpingothersចង់ជួយគេ.mp4), the player doesn't play at all.
I'm new to iOS development. Any advice or direction would be appreciated. Thanks!
I've found a solution by encoding the url string before passing it into AVPlayer.
func setupPlayer(link: String){
let encodedString = link.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
if let url = URL(string: encodedString!) {
player = AVPlayer(url: url)
let playerLayer = AVPlayerLayer(player: player)
self.layer.addSublayer(playerLayer)
playerLayer.frame = self.frame
player?.play()
}

How to play instagram video using AVPlayer

I am trying to play an instagram video from shared link , I have used following code but it doesn't stream , is there any additional step required ? Any API which will get the source video URL like graph API on Facebook ?
The URL is like this https://instagram.com/p/BOzamYMA-vb/
let videoURL = NSURL(string: "https://instagram.com/p/BOzamYMA-vb/")
let player = AVPlayer(url: videoURL! as URL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
This opens player but doesn't play video
Any help is appreciated.
You are giving a web page URL to AVPlayer, this can't work, you have to use the URL for the video itself.
Using the Fuzi HTML parsing library you can get the video URL from the web page.
The trick is to find the URL in the HTML.
For instagram, we can find it with this xpath: "//meta[#property='og:video']/#content".
Example:
import Fuzi
let link = "https://instagram.com/p/BOzamYMA-vb/"
if let pageURL = URL(string: link),
let data = try? Data(contentsOf: pageURL),
let doc = try? HTMLDocument(data: data)
{
let items = doc.xpath("//meta[#property='og:video']/#content")
if let item = items.first,
let url = URL(string: item.stringValue)
{
print(url)
}
}
This gets the URL from the page, "http://scontent-cdg2-1.cdninstagram.com/t50.2886-16/15872432_382094838793088_926533688240373760_n.mp4", and you can use it to download or stream the video like you would usually do.

AVPlayer & video from documents directory

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()
}

Resources