I'm trying to display a video with this code:
self.videoPlayer = AVPlayer.init(url: videoUrl as URL)
let playerLayer = AVPlayerLayer.init(player: self.videoPlayer)
self.videoPlayerView.layer.addSublayer(playerLayer)
playerLayer.frame = self.videoPlayerView.layer.bounds
self.videoPlayer.play()
but it does not appear inside of the UIView (i.e. self.videoPlayerView). It just shows a thin Rect at the middle of the screen as shown in the screenshot. How do I fix this?
screen shot
try this...
set the layer's frame to equal the view's frame
self.videoPlayer = AVPlayer.init(url: videoUrl as URL)
let playerLayer = AVPlayerLayer.init(player: self.videoPlayer)
self.videoPlayerView.layer.addSublayer(playerLayer)
playerLayer.frame = self.videoPlayerView.frame
self.videoPlayer.play()
Firstly the code is in Swift not Obective-C, I would change that tag! Secondly, how do you define the videoPlayerView frame? Thirdly, you change the frame of the playerLayer after it has already been added. Thus the frame will not change visually! Set the frame before you add it as a subLayer. Hope that helps.
Related
I have a custom UITableViewCell that displays either an image or a video. If there's a video, it initially downloads and displays the thumbnail from that video while the video is loading and preparing to play.
It all works, the problem is that I get the ratio of the thumbnail, store it in the database and when I download the thumbnail, it makes the height based on that ratio. It works great for images. But the issue is that the AVPlayerLayer frame doesn't cover the entire thumbnail and it looks like this:
Here's how I do it:
func updateView() {
if let videoUrlString = post?.videoUrl, let videoUrl = URL(string: videoUrlString) {
volumeView.isHidden = false
player = AVPlayer(url: videoUrl)
playerLayer = AVPlayerLayer(player: player)
playerLayer!.frame = postImageView.frame // thumbnail image
playerLayer!.videoGravity = AVLayerVideoGravity.resizeAspectFill
contentView.layer.addSublayer(playerLayer!)
self.volumeView.layer.zPosition = 1
layoutIfNeeded()
player?.play()
player?.isMuted = videoIsMuted
}
if let ratio = post?.ratio {
photoHeightConstraint.constant = UIScreen.main.bounds.width / ratio
layoutIfNeeded()
}
}
I'm not doing something right apparently or I'm missing something based on my final result. Any clue?
UPDATE:
I notice that if you scroll the cells slowly, the video is being displayed according to the size of the thumbnail. But if you scroll fast, it mismatches the frame. I assume it has something to do with reusability or it simply cannot catch up with the frame? Here's my code for prepareForReuse() method:
override func prepareForReuse() {
super.prepareForReuse()
profileImageView.image = UIImage(named: "placeholder")
postImageView.image = UIImage(named: "profile_placeholder")
volumeView.isHidden = true
if let p = player, let pLayer = playerLayer {
p.pause()
pLayer.removeFromSuperlayer()
}
}
The solution turned out to be rather simple. Basically each post contains either post or a video, but either way, it always contains an image. I also set the ratio of that image (the width devided by the height). So, all I had to do is set the player's width to the screen width devided by the ratio and it gives your the proper frame.
playerLayer.frame.size.height = UIScreen.main.bounds.width / postRatio
I have an app where I'd like a video to play in the background of a single view.
What I did is create an AVPlayer, add that to an AVPlayerLayer and add that as a sublayer to my view.
Then I set the frame and the videoGravity property:
self.player = AVPlayer(url: URL(fileURLWithPath: path))
let playerLayer = AVPlayerLayer(player: self.player)
self.layer.addSublayer(playerLayer)
playerLayer.frame = self.bounds
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
Now the video plays, except it doesn't fill the entire screen. It fills it's original size, but it doesn't stretch/scale.
Am I missing something? Why is my video not resizing?
Thanks.
Edit:
I added a screenshot and made the view purple for easier reference.
If you want to video to be stretch on the entire view, just delete the line
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
videoGravity default value is AVLayerVideoGravityResize
Update for Swift 5
playerLayer.videoGravity = .resizeAspectFill
Define the playerLayer.frame inside viewDidLayoutSubviews()
At the time you are defining the frames the final frames for the views were not yet calculated.
Quick question, does anybody know how i can get rid of the black bars at the top and bottom of my video? I just started using AVPlayer and i'm just removing codes here and there in attempt to remove the black layers. Appreciate those who can help me, Thanks!
UIViewController
import UIKit
import AVKit
import AVFoundation
private var looper: AVPlayerLooper?
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let path = Bundle.main.path(forResource: "Innie-Kiss", ofType:"mp4")
let player = AVQueuePlayer(url: URL(fileURLWithPath: path!))
looper = AVPlayerLooper(player: player, templateItem: AVPlayerItem(asset: AVAsset(url: URL(fileURLWithPath: path!))))
let controller = AVPlayerViewController()
controller.player = player
let screenSize = UIScreen.main.bounds.size
let videoFrame = CGRect(x: 0, y: 200, width: screenSize.width, height: (screenSize.height - 130) / 2)
controller.view.frame = videoFrame
self.view.addSubview(controller.view)
player.play()
}
}
You need to set the videoGravity of the AVLayer. The default value is .resizeAspect which will preserve the aspect ratio with black bars on top/bottom or left/right. What you want is .resize
The AVLayer you need to set the videoGravity on is the layer property of your AVPlayer.
see this for more info: https://developer.apple.com/documentation/avfoundation/avplayerlayer/1388915-videogravity
#import AVKit
var playerController = AVPlayerViewController()
playerController.videoGravity = .resizeAspect
/*playerController.videoGravity = .resizeAspectFill*/
playerController.view.backgroundColor = UIColor.white //set color as per super or custom view.
Note: If you set the videoGravity of the AVPlayerViewController. The default value is .resizeAspect then it is possible to visibility of black color(default) in background if you wants this color would similar to your super view's color then you must set superview's or custom view's color to your playercontroller's view background color.
*Example:Suppose super view's or custom view's background color is white then playerController view's background color must be set as playerController.view.backgroundColor = UIColor.white *
Note :playerController's backgroundColor should not be set as UIColor.white ,always. It must me set as super view' background color.
If you set playerController.videoGravity = .resizeAspectFill then it will fill the player content to super view or custom view, here also no black color would be shown. So choose either .resizeAspect or resizeAspectFill as per your requirements.
I am working with apple tv. I have a UIView inside my UIViewController. I am adding AVPlayerViewController as subview of my UIView. UIView's frame is CGRect(x: 50, y: 50, width: 1344, height: 756). I am setting AVPlayerViewController frame equals to my UIView's frame.
Issue is that video plays fine in its frame but the controls like pause, play, forward etc. are hidden and not working. But when I set frame as CGRect(x: 0, y: 0, width: 1920, height: 1080), controls are visible and working.
My code is as per below:
let url = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let item = AVPlayerItem(url: url!)
self.player = AVPlayer(playerItem: item)
self.avplayerController = AVPlayerViewController()
self.avplayerController.player = self.player
self.avplayerController.view.frame = videoPreviewLayer.frame
self.avplayerController.showsPlaybackControls = true
self.avplayerController.requiresLinearPlayback = true
self.addChildViewController(self.avplayerController)
self.view.addSubview(self.avplayerController.view)
self.avpController.player?.play()
Please help.
I managed to solve this using this code:
let player = AVPlayer(url: self.videoUrl!)
let avPlayerController = AVPlayerViewController()
avPlayerController.player = player;
avPlayerController.view.frame = self.videoPlayView.bounds;
self.addChildViewController(avPlayerController)
self.videoPlayView.addSubview(avPlayerController.view);
Now AVPlayerViewController shows most playback controls even as child to a uiview.
Courtesy:
https://www.techotopia.com/index.php/IOS_8_Video_Playback_using_AVPlayer_and_AVPlayerViewController
This is an expected behaviour:
AVPlayerViewController is designed such that when in full screen, the
full playback experience (scrubbing, info panel access, etc) are all
available. When in a space less than full screen, the assumption is
that it is just one of several interactive elements on screen, and
thus the view should not absorb all touch surface events as transport
control.
You can read more about this on this thread: https://forums.developer.apple.com/thread/19526
I am having a problem when the iPad is rotated the sub views frame needs to be changed to landscape as well.
The video is used as a landing screen on the home page and is played behind various elements on the page.
The problem is shown here
I currently have the code that detects if the screen is in landscape or horizontal which will be used to make the changes to the sub view if possible.
The player is set up using the code shown:
player = AVPlayer(URL: contentURL!)
player.actionAtItemEnd = .None
player.muted = true
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
playerLayer.zPosition = -1
playerLayer.frame = view.frame
view.layer.addSublayer(playerLayer)
player.play()
Any help would be appreciated, thank you.