AVPlayer cannot play video - swift

I am trying to play a video inside a subclass of JSQMessagesViewController, but it doesn't start playing onClick event.
I am downloading the video from firebase, and then I write the data on the device by appending a unique reference -LK7WSGtGAQ2anpDxkCQ. Please see below print out statement for the complete path.
//response to collection view tap events
//called every time we type on a message
override func collectionView(_ collectionView: JSQMessagesCollectionView!, didTapMessageBubbleAt indexPath: IndexPath!) {
let message = messages[indexPath.item]
print("message type is \(message.type) and message media uid is \(message.mediaMessage?.mediaUID)")
if message.type == MessageType.image {
let jsqMessage = jsqMessages[indexPath.item]
let mediaItem = jsqMessage.media as! JSQPhotoMediaItem
let photos = IDMPhoto.photos(withImages: [mediaItem.image])
let browser = IDMPhotoBrowser(photos: photos)
self.present(browser!, animated: true, completion: nil)
}
if message.type == MessageType.video {
let jsqMessage = jsqMessages[indexPath.item]
if jsqMessage.isMediaMessage {
if let mediaItem = jsqMessage.media as? JSQVideoMediaItem {
print("mediaItem.fileURL is \(mediaItem.fileURL)")
let player = AVPlayer(url: mediaItem.fileURL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
}
}
}
}
}//end of extension
mediaItem.fileURL is
Optional(file:///Users/bogdanbarbulescu/Library/Developer/CoreSimulator/Devices/7FB42206-997D-4AC2-B0BD-CEE2E22DAFBE/data/Containers/Data/Application/8F17DF31-16B1-4B27-ACB2-015AA55D8979/Documents/-LK7WSGtGAQ2anpDxkCQ)
Update
The video can by played now after appending .MOV to the url above, but there is no sound when it's played. How to fix this?

The problem was that when I was saving the data on the disk, I did not append .MOV extension to the end of the path, thus AVPlayer, could not play the video.
Before:
mediaItem.fileURL is file:///Users/bogdanbarbulescu/Library/Developer/CoreSimulator/Devices/7FB42206-997D-4AC2-B0BD-CEE2E22DAFBE/data/Containers/Data/Application/8F17DF31-16B1-4B27-ACB2-015AA55D8979/Documents/-LK7WSGtGAQ2anpDxkCQ
After: file:///Users/bogdanbarbulescu/Library/Developer/CoreSimulator/Devices/7FB42206-997D-4AC2-B0BD-CEE2E22DAFBE/data/Containers/Data/Application/8F17DF31-16B1-4B27-ACB2-015AA55D8979/Documents/-LK7WSGtGAQ2anpDxkCQ.MOV

Related

Do you need to ask for permission to use the microphone when running an app in simulator?

I Created a simple audio player/recorder player using AVAudioPlayer and AVAudioRecorder. The audio player works as expected but the audio recorder doesn't seem to record or playback the recording even though it builds successfully without any errors. Im wondering if it needs permission to the mic? I'm using version 11.X The code is below any help is much appreciated.
import UIKit
import AVFoundation //must import AVFoundation
class ViewController: UIViewController {
// create property called audioPlayer equal to player
var audioPlayer : AVAudioPlayer?
var audioRecorder : AVAudioRecorder?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//create the audio session
let session = AVAudioSession.sharedInstance()
//set category where we want it to happen (PlayAndRecord) try? = if it doesnt work then set it nil
try? session.setCategory(AVAudioSession.Category.playAndRecord)
//play any audio through device speaker
try? session.overrideOutputAudioPort(.speaker)
// set the session to active, if it doesnt work se it to nil
try? session.setActive(true)
//setup the recording and settings to where audio will be saved (Document folder on device) and set the name and file type (iloveaudio.mp3)
if let basePath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
let paths = [basePath, "iloveaudioios.mp3"]
if let audioURL = NSURL.fileURL(withPathComponents: paths) {
//create a settings dictionary> orignally it's set to empty using ([:])
var settings : [String : Any] = [:]
//set the file type to mp3
settings[AVFormatIDKey] = Int(kAudioFileMP3Type)
//set the sample rate
settings[AVSampleRateKey] = 44100.0
//set number of channels
settings[AVNumberOfChannelsKey] = 2
// specify the path and settings using var and try to run the recorder if it faile set it to nil
audioRecorder = try? AVAudioRecorder(url: audioURL, settings: settings)
//prepare the recorder of ot fails set it to nil
audioRecorder?.prepareToRecord()
}
}
playerSetup(audioURL: nil)
}
//which file to play if something is recorded
func playerSetup(audioURL:URL?) {
if audioURL == nil {
if let audioPath = Bundle.main.path(forResource: "testaudio", ofType: "mp3") {
let tempAudioURL = URL(fileURLWithPath: audioPath)
audioPlayer = try? AVAudioPlayer(contentsOf: tempAudioURL)
}
} else {
audioPlayer = try? AVAudioPlayer(contentsOf: audioURL!)
}
//check if our audio fille exist? and set it to the proerty audioPath
audioPlayer?.prepareToPlay()
//create audio player, make sure it not nil and if its not the prepare to play, then play
}
#IBAction func PlayPressed(_ sender: Any) {
audioPlayer?.play()
}
#IBAction func Pausedpressed(_ sender: Any) {
audioPlayer?.pause()
}
#IBAction func Stop(_ sender: Any) {
audioPlayer?.stop()
audioPlayer?.currentTime = 0
}
#IBAction func Record(_ sender: Any) {
if let recorder = audioRecorder {
if !recorder.isRecording {
recorder.record()
}
}
}
#IBAction func StopRecord(_ sender: Any) {
if let recorder = audioRecorder {
if recorder.isRecording {
recorder.stop()
playerSetup(audioURL: recorder.url)
}
}
}
}
Yes , You need to add Permission in info.plist
Go to info.plist of your project
Add Privacy - Microphone Usage Description and set it's Value YES(true)
After Setting , when you'll start recording in your app for the first time it will ask for permission , Allow that and you're all set !

How to stop AVAudioPlayer from colliding with other sound from another viewcontroller?

I have two view controllers for this app. The first one includes a button that can be clicked for a video to play. The second one has a list of songs which can be played. My problem is when I play a song from the 2nd controller, the background music collides with the sound of the video. I want the music from the 2nd controller to stop and not to collide with the sound of the video from the 1st controller.
I tried to use this code in the 1st controller but it didn't work:
audioPlayer.stop()
It gives an error saying that it is "EXC_BAD_ACCESS Swift"
Could anyone please help me? Thx :)
As per my understanding you need to manage A single player throughout your app. In my one project I managed to use it in following way. Please check if that helps you
struct Manager
{
//Required Objects - AVFoundation
///AVAudio Session
static var recordingSession: AVAudioSession!
///AVAudio Recorder
static var recorder: AVAudioRecorder?
///AVAudio Player
static var player: AVAudioPlayer?
///Bool to check an audio is being played or not
static var recordingalreadyPlayedStatus = Bool()
}
Select File From MainSource and Play it
//MARK: Button Handler
/**
Function is used to play selected Row music Audio File
- parameter sender : UIButton Reference
*/
#objc func playButtonHandler(sender: UIButton)
{
let buttonPosition : CGPoint = sender.convert(CGPoint.zero, to: self.recordingListTableView)
let indexPath : IndexPath = self.recordingListTableView.indexPathForRow(at: buttonPosition)!
///Check is Recorder is Recording Sound ?
if (Manager.recorder != nil) {
return
}
///Check Audio Already Playing ?
/// Bool Used to check is anything getting Played
if (Manager.recordingalreadyPlayedStatus) {
///If yes
///Stop From Playing
if self.currentIndexPath == indexPath {
///Time to stop playing music
var cell = self.recordingListTableView.dequeueReusableCell(withIdentifier: "Cell") as! recordingListCell
cell = self.recordingListTableView.cellForRow(at: self.currentIndexPath) as! recordingListCell
cell.playButton.setImage(#imageLiteral(resourceName: "start"), for: .normal)
Manager.player?.stop()
Manager.recordingalreadyPlayedStatus = false
self.currentIndexPath = indexPath
print("Stopped Playing Current Sound")
}
else {
///Time to stop playing music
var cell = self.recordingListTableView.dequeueReusableCell(withIdentifier: "Cell") as! recordingListCell
cell = self.recordingListTableView.cellForRow(at: self.currentIndexPath) as! recordingListCell
cell.playButton.setImage(#imageLiteral(resourceName: "start"), for: .normal)
Manager.player?.stop()
Manager.recordingalreadyPlayedStatus = false
///Play music at new index
self.playMusic(indexPath: indexPath)
}
}
else {
/// NoThing was Played lets play Directly
self.playMusic(indexPath: indexPath)
}
print("Starting to Play Sound")
}
Play Audio
//MARK: Play Music
/**
This Function get the URL of Audio and Plays it in View
*/
func playMusic(indexPath: IndexPath)
{
//get cell Index Status Using CGPoint
var cell = self.recordingListTableView.dequeueReusableCell(withIdentifier: "Cell") as! recordingListCell
cell = self.recordingListTableView.cellForRow(at: indexPath) as! recordingListCell
//Set Current value so we can change status if any other audio being played
self.currentIndexPath = indexPath
coreClassObject.initWithData(data: fileNameArray, index: indexPath.row)
//Play music now
let isMusicPlayed = RecordingManager.shared.playRecording(fileName: coreClassObject.audioFileName)
//Change Button status
if (isMusicPlayed)
{
//yes being played
//time to change button Text
cell.playButton.setImage(#imageLiteral(resourceName: "stop"), for: .normal)
}
else
{
//music is not played
cell.playButton.setImage(#imageLiteral(resourceName: "start"), for: .normal)
}
}
Function used to check is recording in progress if no, play Audio
//MARK: Play Recording at selected Index
/**
This function is used to check is any file being currently Played ?
if no, Play File else return bool that another file is being already played
- parameter fileName : File Name refer to Audio File name which is to be played
- returns : Bool That file is Successfully played or not
*/
func playRecording(fileName:String) -> Bool {
/// Just a bool used here to check is recording being played
/// Used to Update timer in TableView Cell
var recordingPlayed = Bool()
/// Whenever we need to play an audio file we will allocate player
/// Optional will let you compare that player is nil ?
if Manager.recorder == nil {
/// Player is found nil
/// Thus no Audio is being Played here
/// Yes, We can Play Sound
recordingPlayed = true
Manager.recordingalreadyPlayedStatus = true
//Set player with audio File
do {
try Manager.player = AVAudioPlayer.init(contentsOf: returnPathAtSelectedIndex(fileName: fileName))
//Set required delegates and Values
Manager.player?.delegate = self
Manager.player?.volume = 1.0
Manager.player?.prepareToPlay()
Manager.player?.play()
}
catch {
print("Error while playing music: \(error.localizedDescription)")
}
}
else {
/// Player is already playing a file
/// Can not play audio for now
recordingPlayed = false
Manager.recorder = nil
Manager.recordingalreadyPlayedStatus = false
}
return recordingPlayed
}

avplayer with slow-internet connection not working after first frame

my problem is that when having very bad connection,the activtiy indicator start animating till first frame in video is shown,then disappear thinking is the video is playing,but the video stops playing stuck on loaded first frame,until the whole video is loaded then its resume playing,how to show activity indicator while video is stuck on frame and buffering ,then play until next loaded frame ?
notes:
it's working when internet connetion is off ,video is played until the loaded frame and activity indicator is shown,then when turn on video us resumed to play and activity indicator is hidden
it's working when normal internet connection is present
removing and showing indicator using override observevalue for key path
"currentItem.loadedTimeRanges"/"currentItem.playbackBufferEmpty"
i made a uiview class with avplayer in it
import UIKit
import AVKit
import AVFoundation
class videoplaying: UIView {
override static var layerClass: AnyClass {
return AVPlayerLayer.self;
}
var playerlayer: AVPlayerLayer{
return layer as! AVPlayerLayer;
}
var player: AVPlayer?{
get{
return playerlayer.player
}
set {
playerlayer.player = newValue
}
}
var playetitem: AVPlayerItem?
}
i assigned a uivew in uicollectioncell to this class(using storyboard)
avplayer starts playing and adding observes when pressing play in uicollectioncell
#IBAction func play(_ sender: Any) {
activityindicator.isHidden = false
activityindicator.startAnimating()
self.butttoonheight.isHidden = true
self.postimage.isHidden = true
let url2 = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let avplayer = AVPlayer(url: url2! )
let playeritem = AVPlayerItem(url: url2!)
//videoss is class of type uiview
videoss.playetitem = playeritem
videoss.playerlayer.player = avplayer
videoss.player?.play()
videoss.player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
videoss.player?.addObserver(self, forKeyPath: "rate", options: .new
, context: nil)
videoss.player?.addObserver(self, forKeyPath: "currentItem.playbackBufferEmpty", options: .new, context: nil)
playying.isHidden = false
}
//observing when video is playing
//playpause button to play or pause video while bad network is present video is stuck on first frame and playorpause is not changing while pressed
#IBAction func playorpause(_ sender: Any) {
if videoss.player?.timeControlStatus == AVPlayerTimeControlStatus.paused{
videoss.player?.play()
playying.setImage(UIImage(named: "pas50"), for: .normal)
}
if videoss.player?.timeControlStatus == AVPlayerTimeControlStatus.playing{
videoss.player?.pause()
playying.setImage(UIImage(named: "p24"), for: .normal)
}
}
override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "rate"{
print(videoss.player?.rate)
if videoss.player?.rate == 0.0 {
print("dawdaopwdaopwdipo")
}
}
if keyPath == "currentItem.loadedTimeRanges"{
print("its is working")
activityindicator.stopAnimating()
activityindicator.isHidden = true
}
if keyPath == "currentItem.playbackBufferEmpty"
{
activityindicator.startAnimating()
activityindicator.isHidden = false
print("pkawdawdawd")
}
}
I solved this problem by adding a timer that runs ever 0.3 seconds,evrry 0.3seconds it checks the current time of the video if current time equals previous time then the video is not playing activity indicator is shown,if not video if playing activity indicators was hidden,also you need to check if the users pause the video,an advantage is you also get the current time also in the same function.
What i tried also was seeing rate of the video ,but over slow internet the rate was always giving that is playing,but it wasn’t,which didn’t help

AVQueuePlayer scrubber thumbnail always first video

I have implemented player AVPlayerViewController on tvOS using AVQueuePlayer. The playback of videos works as expected but on all videos following the first the scrubber / time slider shows thumbnails for the first video.
The player is being setup using the following code:
class TFLPlayerController: AVPlayerViewController, AVPlayerViewControllerDelegate {
var queuePlayer = AVQueuePlayer()
...
func addVideoToQueue(video: Video, after: AVPlayerItem?){
let mainVideo = AVPlayerItem(URL: NSURL(string: videoURL)!)
self.queuePlayer.insertItem(mainVideo, afterItem: nil)
}
func readyToPlay() {
self.addVideoToQueue(videoRecord, after: nil)
self.player = self.queuePlayer
if let currentItem = queuePlayer.currentItem {
// Set time to get make a call to the API for the next video
let callUpNextAPITime = CMTimeMakeWithSeconds(0.5, currentItem.asset.duration.timescale)
let callUpNextAPITimeValue = NSValue(CMTime: callUpNextAPITime)
let getUpNextObserver = queuePlayer.addBoundaryTimeObserverForTimes([callUpNextAPITimeValue], queue: dispatch_get_main_queue(), usingBlock: { () -> Void in
self.getUpNext()
})
}
self.player?.play()
}
func getUpNext(){
// Get the next video to play
let playlistId = presentingPlaylist?.identifier ?? nil
if let currentItem = self.queuePlayer.currentItem {
// Get the current items id
let currentVideoId = itemVideo[currentItem]?.identifier
if let currentVideoId = currentVideoId {
// Call API to get the next video to be played from the server
dataLayer.getNextVideo(currentVideoId, playlistIdentifier: playlistId) {
video, error in
...
if let nextVideo = video {
let currentVideo: AVPlayerItem = self.queuePlayer.currentItem!
self.addVideoToQueue(nextVideo, after: currentVideo)
}
}
}
}
}
I have also instantiated the AVQueuePlayer using the code below as I thought there might be an issue with the initial queue setup but the issue remain:
AVQueuePlayer(playerItem: mainVideo)

AVPlayer selectively playing MP3 audio file in documents directory

I'm writing a podcast app and I want the user to play the local file they've downloaded if it exists (rather than stream it). The streaming works fine, but if the local file is found, it's not played by AVPlayer (this is actually not true for one out of about 5 files for reasons beyond me -- tapping on one podcast will play locally (I'm in airplane mode to verify).
I believe this is the Swift equivalent of this question, but I haven't been able to implement that solution in Swift.
Here's the code:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var theURL = posts.objectAtIndex(indexPath.row).valueForKey("enclosure") as? String
var theMp3Filename = theURL!.lastPathComponent
for i in downloadedList {
if i as! String == theMp3Filename {
// If we've d/l'ed the mp3, insert the local path
theURL = documentsDirectory().stringByAppendingPathComponent(theMp3Filename)
var path: NSURL = NSURL.fileURLWithPath(theURL!)!
player = AVPlayer(URL: path)
player.play()
} else {
let podcastEpisode = AVPlayerItem(URL: NSURL(string: theURL!))
player = AVPlayer(playerItem: podcastEpisode)
player.play()
}
}
}
downloadedList is just an array I generate with the downloaded filenames. I am getting into the local condition, and the path looks right, but AVPlayer usually isn't working.
Any guidance would be appreciated.
Add this right after you set your path variable for playing downloaded audio:
let asset = AVURLAsset(URL: path)
let playerItem = AVPlayerItem(asset: asset)
player = AVPlayer(playerItem: playerItem)
player.play()