This question already has answers here:
FileManager cannot find audio file
(1 answer)
AVPlayer playerWithURL not working after app restart
(2 answers)
Closed 4 years ago.
I am trying to play a video (.mov) saved in document directory but have failed. The URL of the video was saved into core data. The video file was saved in document directory (I checked it by downloading the directory onto my mac) and I saved the video recorded through my iPhone and saved it as ".MOV". The video was there. The "videourl" in the code returned, "file:///var/mobile/Containers/Data/Application/E45E8260-EA7C-4D30-91AD-D65A208D2057/Documents/myvideo.MOV".
I want to play the video when a user hit the button. When the button was hit, AVplayer shows up but the video is played.
Please help me.
#IBAction func buttonTwoInVmDetailForWatchingAndEditing(_ sender: UIButton) {
print("***********************************")
print(vmIDInWatchingAndEditingViewController)
let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "TaskAnalysisURL")
let predicateWithVmIDInVideoPhotoView = NSPredicate(format: "vmID == %#", vmIDInWatchingAndEditingViewController as CVarArg)
fetchRequest.predicate = predicateWithVmIDInVideoPhotoView
do { //try context.fetch(Users.fetchRequest())
let taskAnalysisURLsSelected = try context.fetch(fetchRequest)
let taskAnalysisURLsSelectedTofetch = taskAnalysisURLsSelected as! [TaskAnalysisURL]
if taskAnalysisURLsSelectedTofetch.count == 0 {
print("================== if ================")
print(taskAnalysisURLsSelectedTofetch.count)
} else {
print("================== else ================")
print(taskAnalysisURLsSelectedTofetch.count)
for taskAnalysisURLSelectedTofetch in taskAnalysisURLsSelectedTofetch as [NSManagedObject] {
let stepTwoURLToPlayVideo = taskAnalysisURLSelectedTofetch.value(forKey: "stepTwoURL")
print("===========URL=============")
print(stepTwoURLToPlayVideo!)
let videoUrl = "file://\(String(describing: stepTwoURLToPlayVideo!))"
print(videoUrl)
let videourl = URL(fileURLWithPath: stepTwoURLToPlayVideo! as! String)
print("#########################")
print(videourl)
let player = AVPlayer(url: videourl) // video path coming from above function
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
let playerItem = AVPlayerItem(url: videourl)
let playerTwo = AVPlayer(playerItem: playerItem)
let playerLayer = AVPlayerLayer(player: playerTwo)
playerLayer.frame = self.view.frame
self.view.layer.addSublayer(playerLayer)
playerViewController.player!.play()
}
}
}
}
catch {
print(error.localizedDescription)
}
}
Related
I am trying to get the player to iterate through an array of strings that reference the URL of videos. I can only get it to play one of the videos in the array, if I hardcode it in the code, but I cannot do it to iterate.
I have gotten to play one video while it is hard coded in but not iterate.
var playerArray = [AVQueuePlayer]()
var player = AVQueuePlayer()
// var URLSArray = [URL]()
var ItemArray = [AVPlayerItem]()
var videos = ["video1", "video2","video3"]
var playerController = AVPlayerViewController()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let videoNum = (self.videos.count-1)
// var URLSArray = [URL]()
// for n in 0...videoNum{
// let pathString = (Bundle.main.path(forResource: videos[n], ofType: "mov"))
// let StrToURL = NSURL(string: pathString!)
// URLSArray.append(StrToURL! as URL)
// }
var ItemArray = [AVPlayerItem]()
// var playerController = AVPlayerViewController()
for n in 0...videoNum{
let pathString = (Bundle.main.path(forResource: videos[n], ofType: "mov"))
//print(pathString!)
let StrToURL = URL(string: pathString!)
// print(StrToURL!)
// ItemArray = [AVPlayerItem(url: StrToURL!)]
ItemArray.append(AVPlayerItem(url:StrToURL!))
print(n)
print(ItemArray)
// let player = AVPlayer(playerItem: ItemArray[0])
player = AVQueuePlayer(items: [ItemArray[n]])
player.play()
}
// playerController.player = player
// present(playerController, animated: true) {
// player.play()
}
I want it to finish playing one video and move on to the next video in the array, how ever I hard code the video from the array it works but if I am trying to iterate it acts as if the URL isn't working. This is what it says 2019-06-14 13:08:44.718684-0400 Video[5439:1288491] NSURLConnection finished with error - code -1002
Declare only the player
var player = AVQueuePlayer()
In viewDidAppear map the string array to the URLs in the application bundle and then the URLs to the player items. Then create the queued player and start playing.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let videos = ["video1", "video2", "video3"]
let videoURLs = videos.compactMap{ Bundle.main.url(forResource: $0, withExtension: "mov")}
let itemArray = videoURLs.map{ AVPlayerItem(url: $0) }
player = AVQueuePlayer(items: itemArray)
player.play()
// playerController.player = player
// present(playerController, animated: true) {
// player.play()
// }
}
I am developing a custom video player to stream HLS videos from server. I can successfully play HLS videos using AVPlayerItem and AVPlayer.
After that I want to add subtitle track and audio tracks for my video player. So I used AVMutableComposition to do so. So now the issue is when I am creating AVURLAsset for HLS Videos, I can't able to get video tracks from AVURLAsset. It is giving me always 0 tracks. I tried "loadValuesAsynchronously" of AVURLAsset and I tried adding KVO for "tracks" of AVPlayerItem. But None of these producing me any positive result.
I am using the following code.
func playVideo() {
let videoAsset = AVURLAsset(url: videoURL!)
let composition = AVMutableComposition()
// Video
let videoTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
do {
let tracks = videoAsset.tracks(withMediaType: .video)
guard let track = tracks.first else {
print("Can't get first video track")
return
}
try videoTrack?.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: track, at: kCMTimeZero)
} catch {
print(error)
return
}
guard let subtitlesUrl = Bundle.main.url(forResource: "en", withExtension: "vtt") else {
print("Can't load en.vtt from bundle")
return
}
//Subtitles
let subtitleAsset = AVURLAsset(url: subtitlesUrl)
let subtitleTrack = composition.addMutableTrack(withMediaType: .text, preferredTrackID: kCMPersistentTrackID_Invalid)
do {
let subTracks = subtitleAsset.tracks(withMediaType: AVMediaType.text)
guard let subTrack = subTracks.first else {
print("Can't get first subtitles track")
return
}
try subtitleTrack?.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: subTrack, at: kCMTimeZero)
} catch {
print(error)
return
}
// Prepare item and play it
let item = AVPlayerItem(asset: composition)
self.player = AVPlayer(playerItem: item)
self.playerLayer = AVPlayerLayer.init()
self.playerLayer.frame = self.bounds
self.playerLayer.contentsGravity = kCAGravityResizeAspect
self.playerLayer.player = player
self.layer.addSublayer(self.playerLayer)
self.player.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
self.player.play()
}
This procedure working well for .mp4 videos but not for HLS Videos(.m3u8). Anyone have some working solution for this?
or
How can we get tracks from HLS videos using AVURLAsset? If this is not possible then How can achieve similar result ?
Please let me know you feedback.
Many more thanks in advance.
For HLS video tracks(withMediaType: .video) will return an empty array.
Use this instead: player.currentItem.presentationSize.width and player.currentItem.presentationSize.height.
Pls let me know if it works.
I didn't have the exact same problem as you. But I got around a similar problem (querying for HDR) by instead of querying the tracks on the AVURLAsset, I queried the tracks on the AVPlayerItem.
Set up an observer on the item status:
player?.observe(\AVPlayer.currentItem?.status,
options: [.new, .initial], changeHandler: { [weak self] player, _ in
DispatchQueue.main.async {
self?.observedItemStatus(from: player)
}
})
Then query the AVMediaType of your choice (in your case text).
func observedItemStatus(from avPlayer: AVPlayer) {
guard let currentItem = avPlayer.currentItem else { return }
// ideally execute code based on currentItem.status...for the brevity of this example I won't.
let hasLegibleMedia = currentItem.tracks.first(where: {
$0.assetTrack?.mediaType == AVMediaType.text
})?.assetTrack.hasMediaCharacteristic(.legible)
}
Alternatively if you need more than just a Bool, you could do a loop to access the assetTrack you really want.
I have used this process to download file from the link. Now i want the file path to access this video and play with APPlayer:
#IBAction func btnplayClicked(sender: AnyObject) {
let videoImageUrl = "https://devimages-cdn.apple.com/samplecode/avfoundationMedia/AVFoundationQueuePlayer_HLS2/master.m3u8"
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
let url = NSURL(string: videoImageUrl);
let urlData = NSData(contentsOfURL: url!);
if(urlData != nil)
{
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0];
let filePath="\(documentsPath)/video.mp4";
dispatch_async(dispatch_get_main_queue(), {
urlData?.writeToFile(filePath, atomically: true);
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(NSURL(fileURLWithPath: filePath))
}) { completed, error in
if completed {
print("Video is saved!")
if let path = NSBundle.mainBundle().pathForResource("video", ofType: ".mp4")
{
let apath = NSURL(fileURLWithPath: path)
let video = AVPlayer(URL: apath)
let videoPlayer = AVPlayerViewController()
videoPlayer.player = video
self.presentViewController(videoPlayer, animated: true, completion: {
video.play()
})
}
}
}
})
}
})
}
Video is downloaded and i can see it on gallery.But AVPlayer does not play the video..What am i doing wrong here..?
AS iOS greek Greek told you are accessing wrong path,you can do it with this approach:
if completed {
dispatch_async(dispatch_get_main_queue(), {
// Call UI related operations
let apath = NSURL(fileURLWithPath: filePath)
let video = AVPlayer(URL: apath)
let videoPlayer = AVPlayerViewController()
videoPlayer.player = video
self.presentViewController(videoPlayer, animated: true, completion: {
video.play()
})
})
}
Did you try to load the AVPlayer directly from the cached path? doing so:
if completed {
let video = AVPlayer(URL: NSURL(fileURLWithPath: filePath))
let videoPlayer = AVPlayerViewController()
videoPlayer.player = video
self.presentViewController(videoPlayer, animated: true, completion: {
video.play()
})
}
//Firstly my English very bad, I'm sorry...
I'm implementing AVPlayer my app. Player is working fine but if I play another videos many times Player is not working and only just this screen looks.
Here's my code
let player:AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
let videoFile = object?.objectForKey("video") as? PFFile
videoFile?.getFilePathInBackgroundWithBlock({ (filePath, error) in
if error == nil {
let videoURL = NSURL(fileURLWithPath: filePath!)
self.player = AVPlayer(URL: videoURL)
let playerController = AVPlayerViewController()
playerController.player = self.player
playerController.showsPlaybackControls = false
playerController.videoGravity = AVLayerVideoGravityResizeAspectFill
playerController.view.frame = self.videoView.bounds
self.videoView.addSubview(playerController.view)
self.addChildViewController(playerController)
self.player!.play()
} else {
print(error)
}
})
}
I don't understand the way you fetch the video object but you can try the following code. It works fine.
var player:AVPlayer! // this shouldn't be let.
override func viewDidLoad() {
super.viewDidLoad()
let query = PFQuery(className:"TestClass") //your classname in parse. Change it with your clas name
query.getObjectInBackgroundWithId("SALe4gX2nk") { // get the video file with id. Change it with your id.
(object: PFObject?, error: NSError?) -> Void in
let videoFile = object?.objectForKey("video") as? PFFile
let url = NSURL(string: (videoFile?.url)!)
self.player = AVPlayer(URL: url!)
let playerController = AVPlayerViewController()
playerController.player = self.player
playerController.showsPlaybackControls = false
playerController.videoGravity = AVLayerVideoGravityResizeAspectFill
playerController.view.frame = self.view.bounds
self.view.addSubview(playerController.view)
self.addChildViewController(playerController)
self.player!.play()
}
}
I'm trying to play video from my parse server I'm getting empty video player And when i print the url i get the correct video url
let thevideo:PFFile! = (self.selectedmsg["file"] as! PFFile)
let url:NSURL = NSURL(string: thevideo.url!)!
let player = AVPlayer(URL: url)
let playercontroller = AVPlayerViewController()
playercontroller.player = player
self.presentViewController(playercontroller, animated: true) {
player.play()
}
Any help? to play videos from my parse.