AVPlayer Content Modes - swift

I have a AVPlayer, and am using it as a background in my UIView as an aesthetic. To initialize and instantiate it, I do
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
guard let path = Bundle.main.path(forResource: "Background", ofType:"mp4") else {
debugPrint("Background.mp4 not found")
return
}
let player = AVPlayer(url: URL(fileURLWithPath: path))
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
player.actionAtItemEnd = .none
player.volume = 0.0
//chage content mode ** TODO **
self.view.layer.addSublayer(playerLayer)
player.play()
}
When I run this, the video plays just as expected, however the AVPlayer sets the content mode to fitToWidth as a default. I searched through all the methods of player and playerLayer for some type of method on changing content type, but to no avail.
What I want is instead of fitToWidth I would like fitToHeight.
Any thoughts? Thanks for looking!

I can think of some really easy way to do this. When your video content is fetched, use videoRect method in AVPlayerLayer to extract the frame of the video image. Then resize your view according to the size of the image such that height goes to the extent that you want and width is proportion of the ratio of video height vs the height of view.

Related

Fit video inside of a view

I'm trying to fit a video inside the view I've created on storyboard.
Here is how I added the view to my storyboard.
Here is how it shown in simulator. I want to fit the video inside of the view I've created.
func createVideoView() {
if let url = URL(string: self.videoUrl!) {
let player = AVPlayer(url: url)
let avController = AVPlayerViewController()
avController.player = player
// your desired frame
avController.view.frame = self.videoView.frame
self.view.addSubview(avController.view)
self.addChild(avController)
}
}
Also here is how I create the video. Notice that videoView variable is the view I want to show the video in.
AVPlayerController has attribute videoGravity to set fill video and resizeAspectFill is the one you need to set fill video on the screen. Notice that when you fill maybe it will not show correctly for all video.
func createVideoView() {
if let url = URL(string: "https://jplayer.org/video/m4v/Big_Buck_Bunny_Trailer.m4v") {
let player = AVPlayer(url: url)
let avController = AVPlayerViewController()
avController.player = player
// your desired frame
avController.view.frame = self.videoView.frame
avController.videoGravity = .resizeAspectFill // here
self.view.addSubview(avController.view)
self.addChild(avController)
}
}
Beside, you should make the AVPlayer as the subview of your videoView so that you can handle easier than addSubView to your viewController.
func createVideoView() {
if let url = URL(string: "https://jplayer.org/video/m4v/Big_Buck_Bunny_Trailer.m4v") {
let player = AVPlayer(url: url)
let avController = AVPlayerViewController()
avController.player = player
// your desired frame
avController.view.frame = self.videoView.bounds // change to bounds
avController.videoGravity = .resizeAspectFill // here
self.videoView.addSubview(avController.view) // add subview to videoView
self.addChild(avController)
}
}

Playback buttons do not appear in an AVPlayer after adding a layer - iOS 16

I am using an AVPlayer to present a video. The app has only one .mp4 but for a different use case, the same video needs to get flipped.
The buttons are there and completely functional, you can press the play and the 15 seconds forward/backward buttons but they don't appear on the screen (4th video in the attached image)
The issue seems to be that the flip layer I am adding overlays the new layout buttons.
The potential fix I was thinking of is to flip the video before adding it to the player.
Do you know if there is a straightforward solution for this?
Maybe there is an easy way to keep the iOS 15 playback button layout?
The code the app is using to flip the video is as follows:
#IBAction func pressButton(_ sender: Any) {
guard let path = Bundle.main.path(forResource: "sample-5s", ofType:"mp4") else {
return
}
let avPlayer = AVPlayer(url: URL(fileURLWithPath: path))
let avPlayerController = AVPlayerViewController()
present(avPlayerController, animated: true, completion: {
let flippedLayer = AVPlayerLayer(player: avPlayer)
let transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
flippedLayer.frame = (avPlayerController as UIViewController).view.frame
flippedLayer.setAffineTransform(transform)
(avPlayerController as UIViewController).view.layer.addSublayer(flippedLayer)
avPlayerController.player = avPlayer
avPlayer.play()
})
}
Found a solution. It's all about adding AVPlayerViewController inside the parent view controller. Assume that you have a AVPlayerViewController inside a viewController. So first in viewDidLoad make sure to add it as a child.
var playerController = AVPlayerViewController()
lazy var playerBaseView: UIView = {
let view = UIView()
view.backgroundColor = .clear
return view
}()
override func viewDidLoad() {
// Make your player parent view constraints or do it on Storyboard
playerBaseView.snp.makeConstraints { (make) in
make.edges.equalTo(view.safeAreaLayoutGuide.snp.edges)
}
addChild(playerController) // Add as a child
playerBaseView.addSubview(playerController.view)
// Make your playerController view constraints or do it on Storyboard
playerController.view.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}
For Swift UI
CustomVideoPlayer()
.compositingGroup()
struct CustomVideoPlayer : UIViewControllerRepresentable {
var player: AVPlayer
func makeUIViewController(context: UIViewControllerRepresentableContext<CustomVideoPlayer>) -> AVPlayerViewController {
let controller = AVPlayerViewController()
controller.player = player
return controller
}
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<CustomVideoPlayer>) {
}
Fixed problem for me in IOS 16 Swift UI

Video doesn't play full screen in a given view in swift. Need help fixing it

I have a view that plays video in app.
The constraints given are correct but some how in devices like iPhone 11, iPhone 11 pro the view leaves some space at trailing end.
Adding screenshots for reference.
*Need help fixing the video display to full view.
Constraints:
Align Trailing - Safe area
Align Top - Safe area
Align Leading - Safe area
Height Equals - 150
Code:
import UIKit
import AVFoundation
class VideoViewController: UIViewController {
#IBOutlet weak var videoView: UIView!
var player: AVQueuePlayer!
var playerItem: AVPlayerItem!
var playerLooper: AVPlayerLooper!
override func viewDidLoad() {
super.viewDidLoad()
videoView()
}
func videoViewForLogin() {
//video
guard let url = Bundle.main.url(forResource: "splashpanoslice", withExtension: "mp4") else {return}
let playerPath = AVPlayer(url: url)
player = AVQueuePlayer()
let playerLayer = AVPlayerLayer(player: player)
playerItem = AVPlayerItem(url: url)
let duration = Int64( ( (Float64(CMTimeGetSeconds(AVAsset(url: url).duration)) * 10.0) - 1) / 10.0 )
playerLayer.frame = videoView.bounds
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem, timeRange: CMTimeRange(start: CMTime.zero, end: CMTimeMake(value: duration, timescale: 1)) )
playerLayer.videoGravity = .resizeAspectFill
videoView.layer.insertSublayer(playerLayer, at: 1)
player.play()
}
}
The function code is taken from a reference - How do i make a looping video in swift
Since you're using frames, I think you should try moving layout code later than viewDidLoad(). Perhaps, viewDidLayoutSubviews(), when the view size is determined and also invalidated when displaying parameters are changed (e.g. the screen orientation has changed).

How to add a video in the UI in Swift

I want to be able to add a video (called "Logo-Animation4.mp4") into the UI in Swift, just like you can a UIImage in a UIImageView.
Right now, I've tried just putting an AVPlayer on the view, but it comes up with the default iOS AVPlayer, which contains the regular fullscreen, controls, and volume stuff that I don't want. I just want the video to play in the UI without any way to have the user interact with it.
I've thought about animating the video using a regular UIImageView's animation feature, but my video isn't that short, and it would be hard to get every single frame, and input it into the code.
How should I go about doing this?
To achieve what you are asking, you'll need to use AVPlayerLayer
Add a UIView outlet
#IBOutlet weak var videoView: UIView!
Import AVFoundation
Create player variables
var player : AVPlayer!
var avPlayerLayer : AVPlayerLayer!
Add the following code to your viewDidLoad
guard let path = Bundle.main.path(forResource: "Logo-Animation4", ofType:"mp4") else {
debugPrint("Logo-Animation4.mp4 not found")
return
}
player = AVPlayer(url: URL(fileURLWithPath: path))
avPlayerLayer = AVPlayerLayer(player: player)
avPlayerLayer.videoGravity = AVLayerVideoGravity.resize
videoView.layer.addSublayer(avPlayerLayer)
player.play()
Add this method
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
avPlayerLayer.frame = videoView.layer.bounds
}

Disable Audio (and interruption) with MPMoviePlayerController using Swift

At the moment, this is how I'm playing a video on the subview of my UIViewController:
override func viewDidAppear(animated: Bool) {
let filePath = NSBundle.mainBundle().pathForResource("musicvideo", ofType: "mp4")
self.moviePlayerController.contentURL = NSURL.fileURLWithPath(filePath)
self.moviePlayerController.play()
self.moviePlayerController.repeatMode = .One
self.moviePlayerController.view.frame = self.view.bounds
self.moviePlayerController.scalingMode = .AspectFill
self.moviePlayerController.controlStyle = .None
self.moviePlayerController.allowsAirPlay = false
self.view.addSubview(self.moviePlayerController.view)
}
I've read on ways to disable the audio by doing the following below (none of which work, at all). Keep in mind I'm trying to disable it to the point of not interrupting the current music playing via the Music app, Spotify, etc.
// Playing media items with the applicationMusicPlayer will restore the user's Music state after the application quits.
// The current volume of playing music, in the range of 0.0 to 1.0.
// This property is deprecated -- use MPVolumeView for volume control instead.
1) MPMusicPlayerController.applicationMusicPlayer().volume = 0
2) MPVolumeView doesn't even have a setting for setting the actual volume? It's a control.
3) self.moviePlayerController.useApplicationAudioSession = false
So I found this answer.
This is my Swift code that I ended up going with. I then used an AVPlayerLayer to add to the view as a sublayer, which works perfectly.
Thanks to the OP who managed to get a hold of an Apple technician and provided the original Objective-C code.
The only problems I'm facing now is that it:
1) Interrupts current music playback, whether it's from Music, Spotify, etc.
2) Video stops playing if I close the app and open it up again.
override func viewDidAppear(animated: Bool) {
let filePath = NSBundle.mainBundle().pathForResource("musicvideo", ofType: "mp4")
var asset: AVURLAsset?
asset = AVURLAsset.URLAssetWithURL(NSURL.fileURLWithPath(filePath), options: nil)
var audioTracks = NSArray()
audioTracks = asset!.tracksWithMediaType(AVMediaTypeAudio)
// Mute all the audio tracks
let allAudioParams = NSMutableArray()
for track: AnyObject in audioTracks {
// AVAssetTrack
let audioInputParams = AVMutableAudioMixInputParameters()
audioInputParams.setVolume(0.0, atTime: kCMTimeZero)
audioInputParams.trackID = track.trackID
allAudioParams.addObject(audioInputParams)
}
let audioZeroMix = AVMutableAudioMix()
audioZeroMix.inputParameters = allAudioParams
// Create a player item
let playerItem = AVPlayerItem(asset: asset)
playerItem.audioMix = audioZeroMix
// Create a new Player, and set the player to use the player item
// with the muted audio mix
let player = AVPlayer.playerWithPlayerItem(playerItem) as AVPlayer
player.play()
let layer = AVPlayerLayer(player: player)
player.actionAtItemEnd = .None
layer.frame = self.view.bounds
layer.videoGravity = AVLayerVideoGravityResizeAspectFill
self.view.layer.addSublayer(layer)
}