Playing Video from Core Data uisng AVPlayer - swift

I have a video retrieved using NSData(ContentOf:) and stored in CoreData (I'm using External storage option in Core Data).
now, how can I play it using AVPlayer when I fetch it from CoreData. I couldn't find a clear answer and I'm a beginner so please help me.

It's not a good idea if the way you need to use that data requires a file URL. you should save the videos in the documents directory, and save the names of the videos in Core Data. And for play video retrieve video using video name and then play it.
let fm = FileManager.default
let docURL = try! fm.url(for:.documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let path = docURL.appendingPathComponent("myvideo.mp4")
let player = AVPlayer(url: URL(fileURLWithPath: path.absoluteString))
let vc = AVPlayerViewController()
vc.player = player
present(vc, animated: true) {
vc.player?.play()
}

Related

Playing songs downloaded into the cache directory

Downloading an mp3 and saving it into the Library/caches folder works, But accessing it later doesn't.
Checking if the song really exists, i opened up Finder and went to the simulator folders. and my folder looks like this:
My song is located at: Library/Caches/Digger/Flower.mp3
Trying to access this using AVPlayer:
let folderName = "Digger"
let fileName = "Flower.mp3"
let url = FileManager.default
.urls(for: .cachesDirectory, in: .userDomainMask)[0]
.appendingPathComponent(folderName).appendingPathComponent(fileName)
var urlString = url.absoluteString
print("MyPath: \(urlString)")
urlString.removeSubrange(urlString.range(of: "file://")!)
let player = AVPlayer(url: URL(string: urlString)!)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
player.play()
}
But it doesn't work.
Simulator screenshot upon runtime:
Your URL handling is incorrect.
Replace these lines:
var urlString = url.absoluteString
print("MyPath: \(urlString)")
urlString.removeSubrange(urlString.range(of: "file://")!)
let player = AVPlayer(url: URL(string: urlString)!)
with:
let player = AVPlayer(url: url)
url already is the URL that you need. No need to try to convert it in any way.
For reference, use url.path to convert a file URL into a path string. And if you need to create a URL from a path string, use URL(fileURLWithPath:), not URL(string:). Only use URL(string:) with a string that has a URL scheme such as file://, https://, etc.

How to save an audio file from firebase while its streaming

So I've been able to setup avplayer to stream the audio directly from firebase link, and I've also been able to set it up to download from firebase. What I'm trying to figure out is if its possible to basically save the file to a file URL while its streaming or when it's done streaming, because performing download task and also setting the players currentitem to stream basically takes twice the network resources.
Also im looking for a swift language solution preferably
In case anyone needs this I solved it by calling this function when the AVplayeritem finished buffering
func saveTrack(asset: AVURLAsset)
{
let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A)`
let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("YourApp/documents/Track01.m4a")
exporter?.outputURL = fileURL
exporter?.outputFileType = AVFileType.m4a
exporter?.exportAsynchronously(completionHandler: {
print(exporter?.status)
print(exporter?.estimatedOutputFileLength)
print(exporter?.maxDuration)
print("finished ssaving file")
print()
})
}

Load temp video and play

I need to play continuously a video stored inside the temp directory.
func setVideo(url vid: String!) {
let directory = NSTemporaryDirectory()
let tempURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("tempMovie\(Date().timeIntervalSince1970)").appendingPathExtension("mp4")
let tempFile = NSURL.fileURL(withPathComponents: [directory])
let file = vid.components(separatedBy: ".")
guard let path = Bundle.main.path(forResource: "\(tempURL)", ofType:file[2]) else {
debugPrint( "\(file.joined(separator: ".")) not found with path: \(file[0] + "." + file[1])")
return
}
let player = AVPlayer(url: URL(fileURLWithPath: path))
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
self.videoView.layer.addSublayer(playerLayer)
player.play()
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: nil, using: { (_) in
DispatchQueue.main.async {
self.player?.seek(to: kCMTimeZero)
self.player?.play()
}
})
}
The video location and file url is:
file:///private/var/mobile/Containers/Data/Application/E7D12401-F147-4905-83BE-72909F91E004/tmp/tempMovie1501672791.33525.mp4
The continuous loop part works from a previous project however I can't seem to get the temp stored video to load inside the player. I've tried to get the temp directory manually but it didn't work.
Moving the video from tmp to Documents directory worked for me.
This is how you do it:
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationURL = documentsDirectoryURL.appendingPathComponent("filename.fileExtension")
do {
try FileManager.default.moveItem(at: previousTempLocation, to: destinationURL)
} catch let error as NSError { print(error.localizedDescription)}
let player = AVPlayer(url: destinationURL)
Apple says this on /tmp directory
Use this directory to write temporary files that do not need to
persist between launches of your app. Your app should remove files
from this directory when they are no longer needed; however, the
system may purge this directory when your app is not running.
Apple also recommends this for user data
Put user data in Documents/. User data generally includes any files
you might want to expose to the user—anything you might want the user
to create, import, delete or edit. Video and audio apps may even include
files that the user has downloaded to watch or listen to later.

Are audio files in DocumentDirectory deleted when Core Data entity is deleted?

I'm recording audio with AVFoundation and saving the file in the DocumentDirectory. Then in Core Data the "sounds" entity stores the url as a string. Everything's working great. However I'm wondering: when sounds entities are deleted from Core Data how do to you delete the file from the DocumentDirectory ...or does that automagically happen?
My code to save to disk:
let directoryURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).first
let audioFileName = NSUUID().UUIDString + ".m4a"
let audioFileURL = directoryURL!.URLByAppendingPathComponent(audioFileName)
// URL for Core Data store
audioURL = audioFileName
// Setup audio session
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: AVAudioSessionCategoryOptions.DefaultToSpeaker)
} catch _ {
print("Error creating AVAudioSession")
}
let recordSettings = [AVFormatIDKey: NSNumber(unsignedInt: kAudioFormatMPEG4AAC), AVSampleRateKey: 44100.0, AVNumberOfChannelsKey: 2]
// Initiate and prepare recorder
audioRecorder = try?AVAudioRecorder(URL: audioFileURL!, settings: recordSettings)
audioRecorder?.delegate = self
audioRecorder?.meteringEnabled = true
audioRecorder?.prepareToRecord()
Thank you very much for any assistance! Sorry, I'm finding it a little difficult to track files in the document directory in the sim. (I'm in Swift 2.3)
Deleting a Core Data instance will affect-- at most-- things in the Core Data store. Core Data doesn't know that the string you saved points to a file, so it doesn't try to do anything special with the string.
You can remove the file using the removeItemAtURL:error: method on NSFileManager. A good place to do this would be in your managed object subclass-- override prepareForDeletion and delete the file there.

How to add external .vtt subtitle file to AVPlayerViewController in tvOS

Question:
How do I add an external WebVTT file to my AVPlayer in tvOS?
Description:
I've been watching this "What's New in HTTP Live Streaming" by Apple where they talk about different ways in implementing an external WebVTT file.
The whole subtitle domain is quite new to me, so I'm having hard time grasping the concept quite well. Within the video they talk about many different things which I do not quite understand such as ( Subtitle playlist ). However, getting passed all that my main concern is adding .vtt file to my AVPlayer.
In the video they talk about this AVMediaSelectionGroup() but I'm quite confused on how to use and how to implement this with my AVPlayerViewController :
class PlayerViewController: AVPlayerViewController {
override func viewDidLoad() {
self.setupVideoPlayerView()
}
private func setupVideoPlayerView()
{
let path = "https://link.to.my.video.mp4"
let subTitlePath = "https://link.to.my.webvtt.file.vtt"
let nsURL = URL(string: path)
let avPlayer = AVPlayer(url: nsURL!)
self.player = avPlayer
self.player!.seek(to: kCMTimeZero)
self.player!.play()
}
}
The AVMediaSelectionGroup doesn't seem to have any methods to add a subtitle? The closest thing I was able to find (that mentioned subtitles) are the following methods:
//Where self is the instance of AVPlayerViewController
self.allowedSubtitleOptionLanguages
self.requiresFullSubtitles
While it was almost mentioned no where in the apple documentation, I read in an article that subtitles need to be embedded in the HLS stream.
Subtitles are not intended to be added manually.. though I did find a StackOverflow post showing a hack. Unsure if it works or not.
Seeing that I use VIMEO Pro, the HLS stream that is provided has the WebVTT subtitles (that I uploaded to vimeo) embedded, thus solving my problem.
This works for me.......
let localVideoAsset = AVURLAsset(url: URL(string: url) ?? URL(string:"")!)
let videoPlusSubtitles = AVMutableComposition()
let videoTrack = videoPlusSubtitles.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
do{
guard localVideoAsset.tracks.count > 0 else{
// error msg
return
}
try? videoTrack?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: localVideoAsset.duration),
of: localVideoAsset.tracks(withMediaType: .video)[0],
at: CMTime.zero)
}
let subtitleURL = URL(fileURLWithPath: model.data?[self.selected].subtitlePath ?? "")
let subtitleAsset = AVURLAsset(url: subtitleURL)
let subtitleTrack = videoPlusSubtitles.addMutableTrack(withMediaType: .text, preferredTrackID: kCMPersistentTrackID_Invalid)
do{
guard subtitleAsset.tracks.count > 0 else{
//error msg
return
}
try? subtitleTrack?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: localVideoAsset.duration),
of: subtitleAsset.tracks(withMediaType: .text)[0],
at: CMTime.zero)
}
let playerViewController = AVPlayerViewController()
let player = AVPlayer(playerItem: AVPlayerItem(asset: videoPlusSubtitles))
playerViewController?.player = player
self.present(playerViewController ?? UIViewController(), animated: true) {
self.videoPlaying = true
self.playerViewController?.player?.play()
}
}
Okay, I think I solve this issue. I was looking for some solutions and I haven't found, so I implemented one to work.
I made available a SPM with the solution and it is quite simple to use. So I called it SimpleSubtitles.
It does support WebVTT, without Styles, and could be improved to support other formats
More info:
https://github.com/peantunes/SimpleSubtitles