Microphone access requested without microphone usage - swift - swift

I'm having an issue with an animation (mp4) on my app. Sometimes(not all the time) when the app is launched, it requests microphone access but I am not requesting for one anywhere in the app. I'm only using AVPlayer to play the mp4 content. The code below is the only one related to the player. Any idea why I'm being requested for mic access? Thanks
import UIKit
import Foundation
import MediaPlayer
import AVKit
class AnimationLaunchscreen: UIViewController {
var player: AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
let timer = Timer.scheduledTimer(timeInterval: 6.0, target: self, selector: #selector(timeToMoveOn), userInfo: nil, repeats: false)
self.loadVideo()
}
#objc func timeToMoveOn() {
self.performSegue(withIdentifier: "goToTableView", sender: self)
}
func loadVideo() {
let path = Bundle.main.path(forResource: "stopwatchAnimation", ofType:"mp4")
let filePathURL = NSURL.fileURL(withPath: path!)
let player = AVPlayer(url: filePathURL)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.frame
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer.zPosition = -1
self.view.layer.addSublayer(playerLayer)
player.seek(to: CMTime.zero)
player.play()
}
override func viewWillAppear(_ animated: Bool) {
makeStatusBarBlack()
}
}

Adding the answer here so it is easier to find than reading the comments. This really appears to be a bug on simulator only (https://forums.developer.apple.com/thread/110423). Running on device works just fine.

Related

How can I tap to close AVPlayer in Swift 5?

I'm creating an extremely simple game, and I've hidden the video controls.
#IBAction func playVideo1(_ sender: Any) {
// play video connected to button 1
guard let firstVideo = Bundle.main.path(forResource: "Video1", ofType:"mp4") else {
debugPrint("Video not found")
return
}
// create an AVPlayer, passing it mp4
let player = AVPlayer(url: URL(fileURLWithPath: firstVideo))
// Create a new AVPlayerViewController and pass it a reference to the player.
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
// Modally present the player and call the player's play() method when complete.
present(controller, animated: true) {
player.play()
}
} // end playVideo1
One of the two options would be OK.
Option 1: Tap to close the video.
Option 2: Have the AVPlayer close automatically at the end of the video.
I appreciate any help.
Thanks!
You can add a tap gesture recognizer to AVPlayerViewController's view (for closing on tap), or you could subscribe to AVPlayerItemDidPlayToEndTime notification (for closing when video ends playing). Something like this:
#IBAction func playVideo1(_ sender: Any) {
// play video connected to button 1
guard let firstVideo = Bundle.main.path(forResource: "Video1", ofType:"mp4") else {
debugPrint("Video not found")
return
}
// create an AVPlayer, passing it mp4
let player = AVPlayer(url: URL(fileURLWithPath: firstVideo))
// Create a new AVPlayerViewController and pass it a reference to the player.
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
//for closing when video ends
NotificationCenter.default.addObserver(self, selector: #selector(closePlayer), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: controller.player?.currentItem)
//for closing on tap
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(closePlayer))
controller.view.addGestureRecognizer(tapGestureRecognizer)
// Modally present the player and call the player's play() method when complete.
present(controller, animated: true) {
player.play()
}
} // end playVideo1
#objc func closePlayer() {
dismiss(animated: true)
//if you go notification route, don't forget to remove observer
NotificationCenter.default.removeObserver(self)
}
For closing the videoPlayer when on user tap you can use the following-
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(closePlayerOnTouch))
controller.view.addGestureRecognizer(tapGestureRecognizer)
and then add this after the button -
#objc func closePlayerOnTouch() {
dismiss(animated: true)
NotificationCenter.default.removeObserver(self)
}
#Predrag's answer is really awesome though.
import UIKit
import AVFoundation
class ViewController: UIViewController {
#IBOutlet weak var btnPlay: UIButton!
var player:AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnPress(sender: AnyObject) {
if (btnPlay.titleLabel?.text == "Play") {
initPlayer()
btnPlay.setTitle("Stop", forState: UIControlState.Normal)
} else {
stopPlayer()
btnPlay.setTitle("Play", forState: UIControlState.Normal)
}
}
func initPlayer() {
if let play = player {
print("playing")
play.play()
} else {
print("player allocated")
player = AVPlayer(URL: NSURL(string: "http://streaming.radio.rtl.fr/rtl-1-48-192")!)
print("playing")
player!.play()
}
}
func stopPlayer() {
if let play = player {
print("stopped")
play.pause()
player = nil
print("player deallocated")
} else {
print("player was already deallocated")
}
}
}

How can I show same video as a background in all View Controllers without any interruption during the pages transition?

There are 2 pages(ViewController) in Main.storyboard: HomeViewController and DetailViewController
I gave a storyboard ID for the DetailViewController:DetailPage
I have added a button into first page to take user to the second page.
I also have added a back button into second page to take user to the first page back.
I need to show a video in the background in all pages.
So I have added an UIView component in all pages to show a video.
I gave 0,0,0,0 constraint values for these 2 UIView components in each pages.
First let me share source codes and then I would like to ask my questions.
HomeViewController.swift file
import UIKit
import AVFoundation
class HomeViewController: UIViewController {
private var player: AVPlayer!
#IBOutlet weak var outlet4TheVideoUiView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
configureVideoIssue()
}
override func viewWillAppear(_ animated: Bool) {
configureVideoIssue()
}
func configureVideoIssue()
{
// BACKGROUND VIDEO SCOPE STARTS
let path = URL(fileURLWithPath: Bundle.main.path(forResource: "clouds", ofType: "mp4")!)
let player = AVPlayer(url: path)
self.player = player
let newLayer = AVPlayerLayer(player: player)
newLayer.frame = self.view.bounds
outlet4TheVideoUiView.frame = self.view.bounds
outlet4TheVideoUiView.layer.addSublayer(newLayer)
newLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
player.play()
// video bitince tekrar oynatmak için
player.actionAtItemEnd = AVPlayer.ActionAtItemEnd.none
NotificationCenter.default.addObserver(self, selector: #selector(self.videoDidPlayToEnd(notification:)),
name: NSNotification.Name(rawValue: "AVPlayerItemDidPlayToEndTimeNotification"), object: player.currentItem)
NotificationCenter.default.addObserver(self, selector: #selector(enteredBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(enteredForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
// BACKGROUND VIDEO SCOPE ENDS
}
#objc func enteredBackground() {
player.pause()
}
#objc func enteredForeground() {
player.play()
}
#objc func videoDidPlayToEnd(notification: Notification)
{
let player: AVPlayerItem = notification.object as! AVPlayerItem
player.seek(to: .zero, completionHandler: nil)
}
#IBAction func linkBtnClick(_ sender: UIButton) {
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DetailPage") as! DetailViewController
controller.modalPresentationStyle = .fullScreen
//present(controller, animated: true, completion: nil)
show(controller, sender: nil)
}
}
DetailViewController.swift file
import UIKit
import AVFoundation
class DetailViewController: UIViewController {
private var player: AVPlayer!
#IBOutlet weak var outlet4TheVideoUiView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
configureVideoIssue()
// Do any additional setup after loading the view.
}
func configureVideoIssue()
{
// BACKGROUND VIDEO SCOPE STARTS
let path = URL(fileURLWithPath: Bundle.main.path(forResource: "clouds", ofType: "mp4")!)
let player = AVPlayer(url: path)
self.player = player
let newLayer = AVPlayerLayer(player: player)
newLayer.frame = self.view.bounds
outlet4TheVideoUiView.frame = self.view.bounds
outlet4TheVideoUiView.layer.addSublayer(newLayer)
newLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
player.play()
// video bitince tekrar oynatmak için
player.actionAtItemEnd = AVPlayer.ActionAtItemEnd.none
NotificationCenter.default.addObserver(self, selector: #selector(self.videoDidPlayToEnd(notification:)),
name: NSNotification.Name(rawValue: "AVPlayerItemDidPlayToEndTimeNotification"), object: player.currentItem)
NotificationCenter.default.addObserver(self, selector: #selector(enteredBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(enteredForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
// BACKGROUND VIDEO SCOPE ENDS
}
#objc func enteredBackground() {
player.pause()
}
#objc func enteredForeground() {
player.play()
}
#objc func videoDidPlayToEnd(notification: Notification)
{
let player: AVPlayerItem = notification.object as! AVPlayerItem
player.seek(to: .zero, completionHandler: nil)
}
#IBAction func btnBackClick(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
First Question: I am sure that it shouldn't be like this because same codes are written in both ViewControllers. So how can I avoid this? Any suggestions? I should use same video ui view object as a background for all view controllers. Does it make sense? If it does not make sense. What should I do? For example, I use asp.net user control component in Visual Studio for this scenario. I create one video component(user control) and I can use this component for all pages as a background.
Second Question: (if we can't find a solution to the first question)Let's assume that app user sees Homepage right now. And let's assume that user clicks button to see secondpage(detail page)after 10th second. Button click action takes user to the second page but video starts from the first second. Video should continue from 10th second or 11th second. How can I do this?
Third Question: Second page comes from the bottom after i click button in first page. Can second page comes with fading first page out animation and appearing second page animation?
Important details: Video should stop after app is minimized and should continue after app is maximized by app user. And other important detail is: buttons are not inside of the video view. User can see buttons above the video with this way. Let me show layers for the components in order with an image:
One way that you can do this is set your view on the rootWindow and then set all your views' background color to clear.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let window=self.window!
backgroundVideoView.bounds = window.bounds
window.addSubview(backgroundVideoView)
playVideo()
return true
}
Here, backgroundVideoView refers to the view which will be playing your video.
Although, I must warn you, this will consume a lot of memory but will get you the desired behavior.

AvFoundation framework, AvPlayer pause method causes a crash after app minimized

I am showing mp4 video file in the background with UIView component and AVFoundation framework.
But it gives an error after app is minimized by app user. Because player.pause() method causes a crash as you see. Here is the error: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
import UIKit
import AVFoundation
import AVKit
class ViewController: UIViewController {
private var player: AVPlayer!
#IBOutlet weak var videoUiViewOutlet: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.setupView()
}
private func setupView()
{
let path = URL(fileURLWithPath: Bundle.main.path(forResource: "clouds", ofType: "mp4")!)
let player = AVPlayer(url: path)
let newLayer = AVPlayerLayer(player: player)
newLayer.frame = self.videoUiViewOutlet.frame
self.videoUiViewOutlet.layer.addSublayer(newLayer)
newLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
player.play()
player.actionAtItemEnd = AVPlayer.ActionAtItemEnd.none
NotificationCenter.default.addObserver(self, selector: #selector(self.videoDidPlayToEnd(notification:)),
name: NSNotification.Name(rawValue: "AVPlayerItemDidPlayToEndTimeNotification"), object: player.currentItem)
NotificationCenter.default.addObserver(self, selector: #selector(enteredBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(enteredForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
#objc func videoDidPlayToEnd(notification: Notification)
{
let player: AVPlayerItem = notification.object as! AVPlayerItem
player.seek(to: .zero, completionHandler: nil)
}
#objc func enteredBackground() {
print("scope: enteredBackground")
player.pause()
}
#objc func enteredForeground() {
print("scope: enteredForeground")
player.play()
}
}
How can I solve this problem? Video should pause after app is minimized. And video should continue after app is maximized.
The issue is because you haven't assigned the value to player property of the class ViewController, i.e.
private var player: AVPlayer!
And since it is a forced unwrap optional, it'll have nil as its default value. And using it in enteredBackground() method will result in runtime exception.
Solution:
In setupView() method, replace
let player = AVPlayer(url: path)
with
self.player = AVPlayer(url: path)

How To Make Play/Pause Button on Apple TV Remote Play/Pause AVAudioPlayer

I am making a music app for tvOS which has a AVAudioPlayer. I was wondering how to make the Play/Pause Button on Apple TV remote Play/Pause the AVAudioPlayer? Here's my current code:
import UIKit
import AVFoundation
class MusicViewController: UIViewController, AVAudioPlayerDelegate {
#IBOutlet weak var progressView: UIProgressView!
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
do {
audioPlayer = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "Roots", ofType: "mp3")!))
audioPlayer.prepareToPlay()
var audioSession = AVAudioSession.sharedInstance()
Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(updateAudioProgressView), userInfo: nil, repeats: true)
progressView.setProgress(Float(audioPlayer.currentTime/audioPlayer.duration), animated: false)
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayback)
}
}
catch {
print(error)
}
audioPlayer.delegate = self
}
// Set the music to automaticly play and stop
override func viewDidAppear(_ animated: Bool) {
audioPlayer.play()
}
override func viewDidDisappear(_ animated: Bool) {
audioPlayer.stop()
}
func updateAudioProgressView()
{
if audioPlayer.isPlaying
{
// Update progress
progressView.setProgress(Float(audioPlayer.currentTime/audioPlayer.duration), animated: true)
}
}
}
I've been looking arround trying to figure this out. I haven't worked with tvOS before, so this is new for me. Thanks so much for the help!
These functions have been working for us. They add a gesture recognizer that listens for the play/pause button on the remote. You can add this to your app delegate.
func initializePlayButtonRecognition() {
addPlayButtonRecognizer(#selector(AppDelegate.handlePlayButton(_:)))
}
func addPlayButtonRecognizer(_ selector: Selector) {
let playButtonRecognizer = UITapGestureRecognizer(target: self, action:selector)
playButtonRecognizer.allowedPressTypes = [NSNumber(value: UIPressType.playPause.rawValue as Int)]
self.window?.addGestureRecognizer(playButtonRecognizer)
}
func handlePlayButton(_ sender: AnyObject) {
if audioPlayer.isPlaying {
audioPlayer.pause() {
} else {
audioPlayer.play()
}
}

Local Video File loop tvOS

I'm attempting to play a simple local clip in Xcode7 using Swift for tvOS. All I want is for the video to load fullscreen in the view and loop. Every tutorial I see has the old MPMoviePlayerController or loads from a URL. What is the best way to do this on tvOS?
Update2: This got the video to loop but there is a pause in between. looking into it now to see how to do it seamlessly.
import UIKit
import AVKit
import AVFoundation
class ViewController: UIViewController {
var videoPlayer: AVPlayer!
var playerLayer: AVPlayerLayer?
override func viewDidLoad() {
super.viewDidLoad()
let path = NSBundle.mainBundle().pathForResource("video", ofType:"mp4")
let url = NSURL(fileURLWithPath: path!)
let playerItem = AVPlayerItem(URL: url)
self.videoPlayer = AVPlayer(playerItem: playerItem)
self.playerLayer = AVPlayerLayer(player: self.videoPlayer)
self.playerLayer!.frame = self.view.frame
self.videoPlayer!.play()
self.view.layer.addSublayer(self.playerLayer!)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("playerDidReachEnd:"), name: AVPlayerItemDidPlayToEndTimeNotification, object:nil)
}
func playerDidReachEnd(notification: NSNotification) {
self.videoPlayer.seekToTime(kCMTimeZero)
self.videoPlayer.play()
}
}
Let's say your file is named video.m4v and is stored locally on the device (don't forget to add it to Copy Bundle Resources in Build Phases). Create a stored property so you can access the AVPlayer-instance:
var videoPlayer: AVPlayer?
Setup the video player and add the AVPlayerLayer to your view hierarchy.
if let path = NSBundle.mainBundle().pathForResource("video", ofType:"m4v") {
let url = NSURL(fileURLWithPath: path)
let playerItem = AVPlayerItem(URL: url)
self.videoPlayer = AVPlayer(playerItem: playerItem)
self.playerLayer = AVPlayerLayer(player: self.videoPlayer)
self.playerLayer!.frame = self.view.frame
self.view.layer.addSublayer(self.playerLayer!)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("playerDidReachEnd:"), name: AVPlayerItemDidPlayToEndTimeNotification, object:nil)
}
You have now subscribed a notification that i sent when this video ends. Implement handler and tell the player to replay video.
func playerDidReachEnd(notification: NSNotification) {
self.videoPlayer.seekToTime(kCMTimeZero)
self.videoPlayer.play()
}