Swift 5 Loop MP4 In UIView With Full Screen Controls - swift

I've spent a few days trying different combinations of code I've come across on StackOverflow to no avail.
Here's the code I'm currently using to display MP4 videos (looped, no sound) within a UIView component.
import Foundation
import AVFoundation
import AVKit
class VideoPlayerLooped {
public var videoPlayer:AVQueuePlayer?
public var videoPlayerLayer:AVPlayerLayer?
var playerLooper: NSObject?
var queuePlayer: AVQueuePlayer?
func playVideo(fileName:String, inView:UIView)
{
if let path = Bundle.main.path(forResource: fileName, ofType: "mp4")
{
let url = URL(fileURLWithPath: path)
let playerItem = AVPlayerItem(url: url as URL)
_ = try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, mode: .default, options: .mixWithOthers)
videoPlayer = AVQueuePlayer(items: [playerItem])
videoPlayer?.isMuted = true
videoPlayer?.preventsDisplaySleepDuringVideoPlayback = false
playerLooper = AVPlayerLooper(player: videoPlayer!, templateItem: playerItem)
videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
videoPlayerLayer!.frame = inView.bounds
inView.layer.addSublayer(videoPlayerLayer!)
videoPlayer?.play()
}
}
func remove()
{
videoPlayerLayer?.removeFromSuperlayer()
}
}
Although this solution works to play and loop MP4s, it doesn't provide the user with an option to click the video for a full screen version (landscape rotation) with controls.
Any recommendations or light that can be shed will be most helpful!

You could use an AVPlayerViewController. Apple has a tutorial on the subject in the Xcode help system. It's also available online. Check this link.

Related

Why is my local video file path within the document directory not working after some time? [Swift]

I have a custom video recording view controller, in which I save using a URL:
Sample URL: file:///var/mobile/Containers/Data/Application/802497BB-8413-4245-B33A-708EF8DD9CAF/Documents/FA6AF78A-2692-4CC8-8BE2-7F18140C3C98.mp4
The URL is generated using these functions:
// Gets the directory that the video is stored in
func getPathDirectory() -> URL {
// Searches a FileManager for paths and returns the first one
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentDirectory = paths[0]
return documentDirectory
}
func generateURL() -> URL? {
let path = getPathDirectory().appendingPathComponent(NSUUID().uuidString + ".mp4")
return path
}
I then replay the video once in a separate playback controller, and it works no problem. I have included it right below:
import AVFoundation
class VideoPlaybackController: UIViewController {
let avPlayer = AVPlayer()
var avPlayerLayer: AVPlayerLayer!
var videoURL: URL!
#IBOutlet weak var videoView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// print("VideoPlaybackController: \(videoURL!)")
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer.frame = view.bounds
avPlayerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
videoView.layer.insertSublayer(avPlayerLayer, at: 0)
view.layoutIfNeeded()
let playerItem = AVPlayerItem(url: self.videoURL as URL)
avPlayer.replaceCurrentItem(with: playerItem)
avPlayer.play()
}
}
While the video works, after some time, the video cannot be replayed, even though I saved it within the document directory. I downloaded the xcappdata and looked within the contents of the app, and saw that the video file is still there, so I am confused as to why the video is not available after some time.
Is there something that I am missing? Thank you for your time.

Detect Whether Or Not Video Clip Is Copy Protected

I have a desktop (macOS) application that reads video clips (.mov, .m4v, .mp4...). The application reads a video clip as an avAsset object as follows.
import Cocoa
import AVKit
class VideoViewController: NSViewController {
var videoPlayer: AVPlayer!
var videoURL: URL?
var videoDur: Double!
var videoFrameRate: Float!
var videoData: Data!
var naturalSize: CGSize!
var hasVideo = false
#IBOutlet weak var moviePlayer: AVPlayerView!
#IBOutlet weak var layerView: LayerView!
func readVideoClip(url: URL) {
let avAsset = AVURLAsset(url: url)
let videoTracks = avAsset.tracks(withMediaType: .video)
if videoTracks.count > 0 {
let videoTrack = videoTracks[0]
let playerItem = AVPlayerItem(asset: avAsset)
let avFrameRate = videoTrack.nominalFrameRate
videoPlayer = AVPlayer(playerItem: playerItem)
moviePlayer.player = videoPlayer
moviePlayer.isHidden = false
naturalSize = videoTrack.naturalSize
/* variables */
hasVideo = true
videoURL = url
videoDur = totalDuration
videoFrameRate = avFrameRate
}
}
}
My question is whether or not there is a way of telling whether or not the selected video clip is copy-protected. The application is already available at Mac App Store. I've submitted a software update for it. And they've rejected it by saying the following.
We found the app allows the user to select copy protected files such
as .m4v and does not inform the user that the file cannot be
converted/saved/revised.
It seems that AVPlayerView has canBeginTrimming property. And the following returns false with a non-protected video clip.
if !moviePlayer.canBeginTrimming {
// it returns false
}
I guess an extreme measure will be to not accept the M4V format when the user selects a video clip. Thanks.

Unexpectedly found nil while unwrapping an Optional value, while trying to play video

I am using AVFoundation kit to play a local video
I tried this code:
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVPlayer?
#IBOutlet weak var videoViewContainer: UIView!
override func viewDidLoad() {
super.viewDidLoad()
initializeVideoPlayerWithVideo()
}
func initializeVideoPlayerWithVideo() {
// get the path string for the video from assets
let videoString:String? = Bundle.main.path(forResource: "Air Bike", ofType: "mov")
guard let unwrappedVideoPath = videoString else {return}
// convert the path string to a url
let videoUrl = URL(fileURLWithPath: unwrappedVideoPath)
// initialize the video player with the url
self.player = AVPlayer(url: videoUrl)
// create a video layer for the player
let layer: AVPlayerLayer = AVPlayerLayer(player: player)
// make the layer the same size as the container view
layer.frame = videoViewContainer.bounds
// make the video fill the layer as much as possible while keeping its aspect size
layer.videoGravity = AVLayerVideoGravity.resizeAspectFill
// add the layer to the container view
videoViewContainer.layer.addSublayer(layer)
}
#IBAction func playVideoButtonTapped(_ sender: UIButton) {
// play the video if the player is initialized
player?.play()
}
}
I've trying a few different approaches and I'm still getting the same error message can someone How can I resolve this issue?
func initializeVideoPlayerWithVideo() {
guard let path = Bundle.main.path(forResource: "jagdeep", ofType:".MOV") else {
debugPrint("video.m4v not found")
return
}
self.player = AVPlayer(url: URL(fileURLWithPath: path))
// create a video layer for the player
let layer: AVPlayerLayer = AVPlayerLayer(player: player)
// make the layer the same size as the container view
layer.frame = videoViewContainer.bounds
// make the video fill the layer as much as possible while keeping its aspect size
layer.videoGravity = AVLayerVideoGravity.resizeAspectFill
// add the layer to the container view
videoViewContainer.layer.addSublayer(layer)
player?.play()
}
HERE IS MY VIDEO NAME jagdeep and type .MOV AND its working on my side. It's problem in your code where you using type mov. I just drag a video name jagdeep.MOV to my project and use Your code. its ok
**use**
layer.frame = videoViewContainer.layer.bounds
instead of
// make the layer the same size as the container view
layer.frame = videoViewContainer.bounds
Thank you for your response seems like the file cannot be found here is a screenshot from my project what could be the reason the video is located in my main folder! Screenshot

How to loop background music without lag/delay when song repeats (seamless)

I am trying to make a game with background music looping in it. I made the song file in Adobe Audition (which is similar to audacity) and when I play it in a loop in Adobe Audition it loops how I want it.
When I play it in Xcode however, it has a lag in between the loops. I am using AVFoundations for the sound playing.
I have searched everywhere but I can't find the solution for the problem.
Is there any way you can loop audio files without there being any lags in between ? (I believe its called "seamless looping" )
here is the code:
class GameScene: SKScene {
...// Other Code
var ButtonAudio = URL(fileURLWithPath: Bundle.main.path(forResource: "Gamescene(new)", ofType: "mp3")!)
var ButtonAudioPlayer = AVAudioPlayer()
... //Other Code
}
And When I Call it:
override func didMove(to view: SKView) {
...//Code
ButtonAudioPlayer = try! AVAudioPlayer(contentsOf: ButtonAudio, fileTypeHint: nil)
ButtonAudioPlayer.numberOfLoops = -1
ButtonAudioPlayer.prepareToPlay()
ButtonAudioPlayer.play()
...//More Code
}
Can someone help me with this issue ?
Thank you in advance!
You can use AVPlayerLooper and AVQueuePlayer to do this.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var queuePlayer = AVQueuePlayer()
var playerLooper: AVPlayerLooper?
override func viewDidLoad() {
super.viewDidLoad()
guard let url = Bundle.main.url(forResource: "Gamescene(new)", withExtension: "mp3") else { return }
let playerItem = AVPlayerItem(asset: AVAsset(url: url))
playerLooper = AVPlayerLooper(player: queuePlayer, templateItem: playerItem)
queuePlayer.play()
}
}
The solution proposed by #dave234 only works in iOS > 10. Since I needed to make seamless playback in iOS > 9, I did something different:
Instead of AVPlayer, I created AVQueuePlayer and immediately added two identical melodies to the queue.
Next, I made a listener to the penultimate melody.
When the listener was triggered, I added another similar record to the queue after the last one.
In fact, in order to avoid delay, I always play the penultimate record.
My code:
var player: AVQueuePlayer?
override func viewDidLoad() {
super.viewDidLoad()
if let path = Bundle.main.path(forResource: "music_file", ofType: "mp3") {
player = createPlayer(url: URL(fileURLWithPath: path))
}
}
func createPlayer(url: URL) -> AVQueuePlayer {
let player = AVQueuePlayer(items: [AVPlayerItem(url: url), AVPlayerItem(url: url)])
loopPlayer(playerItem: player.items()[player.items().count - 2])
return player
}
func loopPlayer(playerItem: AVPlayerItem) {
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: playerItem, queue: .main) { _ in
if let player = self.player, let url = (playerItem.asset as? AVURLAsset)?.url {
player.insert(AVPlayerItem(url: url), after: player.items()[player.items().count - 1])
self.loopPlayer(playerItem: player.items()[player.items().count - 2])
}
}
}

TVOS getting ReferenceError: Can't find variable when calling SWIFT function

I am trying to playback youtube videos with tvml on my appleTV. It is based on:
https://gist.github.com/nickv2002/b7bb28cdccc000bdb910
The first time I start it, it is working, but after I play around (leaving the app), I get:
ReferenceError: Can't find variable: playYTblock
After rebooting/exit(0)ing the app the ATV it is working again... it seems, the context between the app<>tvjs is lost - anyone with ideas?
Here is my code:
in AppDelegate.swift
let playerVC = YTPlayerViewController()
in the application function:
playerVC.createPlayYT( appController! )
in presenter.js
if (youtubeUrl && (event.type === "play")) {
playYTblock(youtubeUrl);
}
in the template.xml.js
<listItemLockup youtubeUrl="H4O6oEaIDrs">
btw has anyone an idea why the event.type === select is fired right after loading the template (without clicking on my side)
Can't answer about that exact problem but,
Here is what i'm using is to play youtube video on tvos :
pod "AKYoutubeParser"
A parser to retrieve the actual youtube video urls.
And the video playback is handled by apple core stuff (AVFoundation, AVKit)
you can delete all the hud part if you don't use it.
import AKYoutubeParser
import AVFoundation
import AVKit
func displayTrailer(trailer: String) {
AKYoutubeParser.h264Streams(trailer) { [weak self] streams, error in
if let s = streams, v = AKYoutubeParser.getBestQuality(s) {
let playerVC = AVPlayerViewController()
let playerItem = AVPlayerItem(asset: AVAsset(URL: v.url))
let videoPlayer = AVPlayer(playerItem: playerItem)
playerVC.player = videoPlayer
self?.presentViewController(playerVC, animated: true) {
playerVC.player?.play()
}
}
}
}