Airplay Button is not showing in Player Controls with AVPlayer - ios

I created a Video Player using AVPlayer and AVPlayerViewController. I have set "allowsExternalPlayback" property to true and also "usesExternalPlaybackWhileExternalScreenIsActive" property to true. But still I am not getting Airplay Icon in Player Controls.
player = AVPlayer(URL: url!)
player!.allowsExternalPlayback = true
player?.usesExternalPlaybackWhileExternalScreenIsActive = true
I am running my app on ios 9.2.

You need to add an MPVolumeView in order to get this. You can read about this here: https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/AirPlayGuide/EnrichYourAppforAirPlay/EnrichYourAppforAirPlay.html

func appleTv()
{
let rect = CGRect(x: -100, y: 0, width: 0, height: 0)
let airplayVolume = MPVolumeView(frame: rect)
airplayVolume.showsVolumeSlider = false
self.view.addSubview(airplayVolume)
for view: UIView in airplayVolume.subviews {
if let button = view as? UIButton {
button.sendActions(for: .touchUpInside)
break
}
}
airplayVolume.removeFromSuperview()
}

Related

How do I use screen mirroring in iOS apps

I just need to use screen mirroring on enabled device when I click the button on my app screen. There is an airplay button in my app, clicking on which will check for available mirroring devices such as tv or laptop but I don't think it is for screen mirroring. I want to call screen mirroring in my app (I want to use mirroring not casting) on that screen.
I don't know Objective-c well, so please use swift if you use example code. Thanks.
Please check below, this might help you to understand how currently apps are achieving this.
Screen Mirroring iPhone to smart TV
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let routePickerView = AVRoutePickerView(frame: CGRect(x: 0.0, y: 30.0, width: 30.0, height: 30.0))
routePickerView.backgroundColor = UIColor.lightGray
self.view.addSubview(routePickerView)
let avAsset = AVAsset(url: URL(string: "https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4")!)
let avPlayerItem = AVPlayerItem(asset: avAsset)
let avPlayer = AVPlayer(playerItem: avPlayerItem)
let avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer.frame = CGRect(x: 0.0, y: 40.0, width: self.view.frame.size.width, height: self.view.frame.size.height - 40.0)
self.view.layer.addSublayer(avPlayerLayer)
avPlayer.seek(to: CMTime.zero)
avPlayer.play()
}
This code shows a player and adds the ability to mirror ONLY the player into the Mac screen.
You can check below as well.
how-to-do-screen-mirroring-using-airplay-from-the-application-not-from-control

AVPlayer Aspect Ratio

Ok so in my iOS app there is a screen with an AVPlayerViewController that takes care of playing videos. When this video is played it fills the entire screen however on some videos they video seems stretched as if it is being forced to fit. Is there any way to make the video fit the entire screen without losing the aspect ratio.
The code below is what I use to lay out the AVPlayerController. I figured some error here is the cause of this flaw in aspect ratio.
/
// Setup the view for stories
private func setupViews() {
view.backgroundColor = .clear
// Setup the blur view for loading
blurView = UIVisualEffectView(frame: view.frame)
blurView.effect = UIBlurEffect(style: .regular)
self.view.addSubview(blurView)
// Indicator for loading
let indicatorFrame = CGRect(x: view.center.x - 20, y: view.center.y - 20, width: 40, height: 40)
indicator = UIActivityIndicatorView(frame: indicatorFrame)
indicator.hidesWhenStopped = true
indicator.activityIndicatorViewStyle = .whiteLarge
blurView.contentView.addSubview(indicator)
// Setup the Progress bar
// spb = SegmentedProgressBar(numberOfSegments: allStories.count)
//// spb.frame = CGRect(x: 0, y: 1, width: self.view.frame.width, height: 9)
// self.view.addSubview(spb)
// spb.snp.makeConstraints { (make) in
// make.left.right.equalTo(view.safeAreaLayoutGuide)
// make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(1)
// make.height.equalTo(9)
// }
//
// spb.delegate = self
// Player Controller
playerController = AVPlayerViewController()
playerController.showsPlaybackControls = false
self.addChildViewController(playerController)
// Image view for the story
imageView = UIImageView(frame: self.view.frame)
infoView = UIView(frame: self.view.frame)
infoView.backgroundColor = UIColor.clear
// The image view for the user of the current story
infoImageView = UIImageView()
self.view.addSubview(imageView)
self.view.addSubview(playerController.view)
// self.view.bringSubview(toFront: spb)
self.view.addSubview(infoView)
self.infoView.addSubview(infoImageView)
infoImageView.snp.makeConstraints { (make) in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(2)
make.left.equalTo(view.safeAreaLayoutGuide.snp.left).offset(10)
make.height.width.equalTo(30)
}
infoImageView.layer.cornerRadius = 15
infoImageView.layer.masksToBounds = true
// The name for the user of the current story
infoNameLabel = UILabel()
infoNameLabel.backgroundColor = UIColor.black.withAlphaComponent(0.1)
infoNameLabel.textColor = UIColor.white
infoNameLabel.font = UIFont.boldSystemFont(ofSize: 18)
infoNameLabel.adjustsFontSizeToFitWidth = true
self.infoView.addSubview(infoNameLabel)
infoNameLabel.snp.makeConstraints { (make) in
make.left.equalTo(infoImageView.snp.right).offset(5)
make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(2)
make.height.equalTo(30)
make.width.equalTo(80)
}
// Time label for long ago the story was posted
infoTimeLabel = UILabel()
infoTimeLabel.backgroundColor = UIColor.black.withAlphaComponent(0.1)
infoTimeLabel.textColor = UIColor.white
self.infoView.addSubview(infoTimeLabel)
infoTimeLabel.snp.makeConstraints { (make) in
make.left.equalTo(infoNameLabel.snp.right).offset(5)
make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(2)
make.height.equalTo(30)
make.width.equalTo(50)
}
infoView.isUserInteractionEnabled = true
infoView.addGestureRecognizer(tapInfoView)
infoView.addGestureRecognizer(swipeInfoView)
infoTimeLabel.layer.cornerRadius = 10
infoTimeLabel.layer.masksToBounds = true
infoTimeLabel.textAlignment = .center
infoNameLabel.layer.cornerRadius = 10
infoNameLabel.layer.masksToBounds = true
infoNameLabel.textAlignment = .center
playerController.videoGravity = AVLayerVideoGravity.resizeAspectFill.rawValue
playerController.view.frame = view.frame
// spb.durations = durations
}
Any help is greatly appreciated
to keep the video aspect ratio in the defined view frame use
AVLayerVideoGravity.resizeAspect.
For the current case replace
playerController.videoGravity = AVLayerVideoGravity.resizeAspectFill.rawValue
to
playerController.videoGravity = AVLayerVideoGravity.resizeAspect
I am not sure about it but use this :
playerController.view.frame = view.bounds
Instead of this:
playerController.view.frame = view.frame
The AVLayerVideoGravity.resizeAspectFill.rawValue should do what is says and I never had a problem with it. However, I use the bounds instead of the frame and I remember getting problem when using the frame.
You also have AVLayerVideoGravity.resizeAspect.rawValue that will fit the video instead of filling it.
You can also try setting the frame before the video gravity.

AVPlayerViewController controls not working

I have an app which plays video clips on demand. This has worked well in previous versions of Xcode but I've upgraded to 8.3.3 and am having problems with the AVPlayerViewController.
The video plays and displays correctly. The control bar appears at the bottom of the view but does not respond to taps and, once faded out, does not reappear on tapping the video - unless, that is, I tap near the top left of the view.
My guess is that the actual controls are hidden in some kind of overlay which has the wrong size i.e. it does not properly overlay the whole of the video view. Is there some way to force the AVPlayerViewController to relayout?
I've tried adding:
_playerController!.view.setNeedsLayout()
_playerController!.view.layoutIfNeeded()
But this has no effect.
Here's my code:
override func viewDidLoad() {
super.viewDidLoad()
_player = AVPlayer()
_playerController = AVPlayerViewController()
_playerController!.showsPlaybackControls = true
_playerController!.allowsPictureInPicturePlayback = false
_playerController!.videoGravity = AVLayerVideoGravityResizeAspectFill
_playerController!.player = _player
self.addChildViewController(_playerController!)
videoView.addSubview(_playerController!.view)
...
override func viewDidLayoutSubviews() {
let frame = UIScreen.main.bounds
_vidWidth = frame.width - 256.0
_vidHeight = _vidWidth / 854.0 * 480.0
videoView.frame = CGRect(x: 0, y: 10.0, width: _vidWidth, height: _vidHeight)
videoView.backgroundColor = UIColor.black
_playerController?.view.frame = CGRect(x: 0, y: 0, width: _vidWidth, height: _vidHeight)
...
func playVideo(_ clip: Clip) {
var videoUrl: URL? = nil
if clip.offlineLocation != nil && clip.state == 2 {
_videoPath = clip.offlineLocation!
videoUrl = URL(fileURLWithPath: _videoPath!)
}
else {
_videoPath = "https://xxxx.cloudfront.net/" + clip.location
videoUrl = URL(string: _videoPath!)
}
NSLog(_videoPath!)
let asset = AVURLAsset(url:videoUrl!, options:nil)
let playerItem = AVPlayerItem(asset: asset)
_player!.replaceCurrentItem(with: playerItem)
_player!.play()
}

How to make player resume from the newest frame?

I use ijkplayer to play the live video in my ios app.
Here is the code for player initialization
let options = IJKFFOptions.byDefault()
let streamUrl:String = "http://xxxxxxxxx.com/test/7777.flv"
let player = IJKFFMoviePlayerController(contentURLString: streamUrl, with: options)
let autoresize = UIViewAutoresizing.flexibleWidth.rawValue | UIViewAutoresizing.flexibleHeight.rawValue
player?.view.autoresizingMask = UIViewAutoresizing(rawValue: autoresize)
player?.view.frame = CGRect(x: 0, y: 0, width: 1, height: 1)
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.compressExpand(_:)))
player?.view.addGestureRecognizer(gesture)
player?.view.backgroundColor = UIColor.black
player?.scalingMode = .aspectFit
player?.shouldAutoplay = false
self.player = player
self.player.shouldShowHudView = true
self.player.setPlayerOptionIntValue(1000, forKey: "max_cached_duration")
The issue is I use player.pause() to resume the player for some time. Then I use player.play() to resume playing. Live video then playing from the frame where paused, what I want is to resume the video from the latest or newest frame.
You can release the player, and initiate it again. This should play the demo from the latest frame. This may be not the best solution.

how to show video in a small frame in ios

I want to show a video of a topic in top half of the view and its matter in textview in the bottom half of the view. For that video control i want to have the features like play,pause,stop,ff etc. Also i want to play it from local resource as my web services hasn't been setup yet. pls suggest a good solution
I have tried UIWebView and added constraints to webview and textview but for some reason the web view is not showing the video correctly. below is my code
let purl = NSURL(fileURLWithPath: "/Users/Rohit/Desktop/videos/demo/demo/video1.mp4") webView.loadHTMLString("<iframe width = \" \(webView.frame.width) \" height = \"\(webView.frame.height)\" src = \"\(purl)\"></iframe>", baseURL: nil)
webView.backgroundColor = UIColor.green
webView.mediaPlaybackRequiresUserAction = true
webView.scrollView.isScrollEnabled = true
webView.isUserInteractionEnabled = true
Import AVFoundation and AVKit
Then play the video using an URL object (in Swift 3 NSURL is renamed to URL)
let player = AVPlayer(URL: URI)
let controller = AVPlayerViewController()
controller.player = player
self.addChildViewController(controller)
let screenSize = UIScreen.main.bounds.size
let videoFrame = CGRect(x: 0, y: 10, width: screenSize.width, height: (screenSize.height - 10) * 0.5)
controller.view.frame = videoFrame
self.view.addSubview(controller.view)
player.play()
You can use AVPlayerLayer and give it bounds.
private func inits() {
//let rootLayer: CALayer = self.layer
// rootLayer.masksToBounds = true
avPlayerLayer = AVPlayerLayer(player: player)
avPlayerLayer.bounds = self.bounds
// avPlayerLayer.backgroundColor = UIColor.yellowColor().CGColor
self.layer.insertSublayer(avPlayerLayer, atIndex: 0)
}

Resources