23

I want to play video from PHAsset, collected from iOS Photos. PHAsset video nsurl (https://stackoverflow.com/a/35099857/1084174) is valid to my own application lets say MyPlayer for few moments/blocks. When I am copying the image/video into MyPlayers own sandbox only then the nsurl becomes always valid. It seems to me,

I need to copy each video from temporary PHAsset nsurl to MyPlayers sandbox (appgroup/documents) and only then I can play the video with sandbox relative nsurl.

If this is the case how do all other player play long videos on the fly? If there is any other way to play video without copying to apps sandbox, please let me know the way.

Sazzad Hissain Khan
  • 37,929
  • 33
  • 189
  • 256

4 Answers4

27

After several days experiment finally I come upon a solution,

Import File

import AVKit

From PHAsset

static func playVideo (view:UIViewController, asset:PHAsset) {

        guard (asset.mediaType == PHAssetMediaType.Video)

            else {
                print("Not a valid video media type")
                return
        }

        PHCachingImageManager().requestAVAssetForVideo(asset, options: nil, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [NSObject : AnyObject]?) in

            let asset = asset as! AVURLAsset

            dispatch_async(dispatch_get_main_queue(), {

                let player = AVPlayer(URL: asset.URL)
                let playerViewController = AVPlayerViewController()
                playerViewController.player = player
                view.presentViewController(playerViewController, animated: true) {
                    playerViewController.player!.play()
                }
            })
        })
    }

From AppLocalUrl

static func playVideo (view:UIViewController, appLocalUrl:NSURL) {

        dispatch_async(dispatch_get_main_queue(), {

            let player = AVPlayer(URL: appLocalUrl)
            let playerViewController = AVPlayerViewController()
            playerViewController.player = player
            view.presentViewController(playerViewController, animated: true) {
                playerViewController.player!.play()
            }
        })
    }
Sazzad Hissain Khan
  • 37,929
  • 33
  • 189
  • 256
  • I have tested this piece of code and it does not work for slow-motion videos. So I have modified this solution. https://stackoverflow.com/a/60292456/397915 – iCoder Feb 19 '20 at 03:16
7

@Sazzad Hissain Khan solution in Swift 3:

From PHAsset:

func playVideo (view: UIViewController, videoAsset: PHAsset) {

    guard (videoAsset.mediaType == .video) else {
        print("Not a valid video media type")
        return
    }

    PHCachingImageManager().requestAVAsset(forVideo: videoAsset, options: nil) { (asset, audioMix, args) in
        let asset = asset as! AVURLAsset

        DispatchQueue.main.async {
            let player = AVPlayer(url: asset.url)
            let playerViewController = AVPlayerViewController()
            playerViewController.player = player
            view.present(playerViewController, animated: true) {
                playerViewController.player!.play()
            }
        }
    }
}

From AppLocalUrl:

func playVideo (view: UIViewController, appLocalUrl: URL) {

    DispatchQueue.main.async {

        let player = AVPlayer(url: appLocalUrl)
        let playerViewController = AVPlayerViewController()
        playerViewController.player = player
        view.present(playerViewController, animated: true) {
            playerViewController.player!.play()
        }
    }
}
YYamil
  • 1,100
  • 11
  • 11
  • Gotta look out for `AVComposition` assets. They're things like slow motion video. If you run into one of those, create an `AVPlayerItem` with it and pass the player item into the `AVPlayer`. – Mr Rogers Sep 05 '19 at 21:30
5

The below solution works for slow-motion videos as well.

func playVideo(asset: PHAsset) {
    guard asset.mediaType == .video else {
        // Handle if the asset is not a video.
        return
    }

    let options = PHVideoRequestOptions()
    options.isNetworkAccessAllowed = true

    PHCachingImageManager().requestPlayerItem(forVideo: asset, options: options) { (playerItem, info) in
        DispatchQueue.main.async {
            let playerViewController = AVPlayerViewController()
            playerViewController.player = AVPlayer(playerItem: playerItem)
            present(playerViewController, animated: true) {
                playerViewController.player?.play()
            }
        }
    }
}
iCoder
  • 1,645
  • 1
  • 15
  • 23
2

In Swift 5

func playVideo(view:UIViewController, asset:PHAsset) {

        guard (asset.mediaType == PHAssetMediaType.video)

            else {
                print("Not a valid video media type")
                return
        }

        PHCachingImageManager().requestAVAsset(forVideo: asset, options: nil) { (assets, audioMix, info) in
            let asset = assets as! AVURLAsset
            DispatchQueue.main.async {

                let player = AVPlayer(url: asset.url)
                let playerViewController = AVPlayerViewController()
                playerViewController.player = player
                view.present(playerViewController, animated: true) {
                    playerViewController.player!.play()
                }
            }
        }
    }
bhavik
  • 1,673
  • 2
  • 14
  • 20
  • your solution is not working on some devices with crash on this " let asset = assets as! AVURLAsset " with message: "Could not cast value of type 'AVComposition' (0x1ed8ebd88) to 'AVURLAsset' (0x1ed8eb680)" Can you guide me about this error? – Muhammad Danish Qureshi Apr 05 '21 at 12:36
  • I also tried with this but same error PHImageManager.default().requestAVAsset(forVideo: phAsset, options: nil, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [AnyHashable : Any]?) -> Void in – Muhammad Danish Qureshi Apr 05 '21 at 12:37