There is no error message when playing the sound. I put a print statement for testing, and the url is getting the right path. The play button clicks, and I hear a faint click sometimes, but that's it. No other sound comes out. I've tried different mp3 files. I get an error message if I delete the mp3 file. I've checked the volume.
The play button is in a second view controller. The main view controller has a table view. When user taps on the cell, it takes him to this view controller.
// DetailVC.swift
import AVFoundation
import UIKit
class DetailVC: UIViewController {
var duaPlayer = "" //is this correct???
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func playbutton(sender: AnyObject) {
do {
let url = NSBundle.mainBundle().URLForResource("track1", withExtension: "mp3")
var duaPlayer = try AVAudioPlayer(contentsOfURL: url!)
duaPlayer.prepareToPlay()
print (url)
duaPlayer.volume = 0.5
duaPlayer.play()
}
catch
{ fatalError("err")}
}
i think that AVAudioPlayer do not support streaming. For live streaming use AVPlayer
//try this, it should work
var player = AVPlayer()
func configureView() {
let url = "http://yoururl.com"
let playerItem = AVPlayerItem( URL:NSURL( string:url ) )
player = AVPlayer(playerItem:playerItem)
player.rate = 1.0;
player.play()
}
in the other hand , with AVAudioPlayer you can download the mp3 file first than play it -->
let url = "http://YourURL/music/music.mp3"
let fileURL = NSURL(string:url)
let soundData = NSData.dataWithContentsOfURL(fileURL, options: nil, error: nil)
var error: NSError?
self.player = AVAudioPlayer(data: soundData, error: &error)
if player == nil {
if let e = error {
println(e.localizedDescription)
}
}
player.prepareToPlay()
player.volume = 1.0
player.delegate = self
player.play()
PS: Please keep in mind since iOS 9: App Transport Security (ATS) must be configured to accept non ssl connection.
add this code to your info.plist->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Related
I have a tableview which sets a UIImage to hold either an image url from AWS or a thumbnail generated from a video URL also from AWS. The video url refuses to display in my tableview and it throws this error in the debugger.
2017-12-29 12:20:37.053337-0800 VideoFit[3541:1366125] CredStore - performQuery - Error copying matching creds. Error=-25300, query={
class = inet;
"m_Limit" = "m_LimitAll";
"r_Attributes" = 1;
sync = syna;
}
When I click the cell to display either the image or video url, it segues to the video player correctly and then I get the error again and the triangular start button has a line through it signifying that there is no video to be played.
But when I print the url it has successfully passed so that is not the issue, the issue is AVPlayer can't handle my AWS video url for some reason. It is an https link so that means it must be secure but I already set my arbitrary loads to true
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Here is some code for displaying my videos in the videoPlayer VC and also the thumbnail generator function, perhaps there is some issue lying with these?
import UIKit
import AVKit
import MediaPlayer
class VideoPlayerVC: AVPlayerViewController {
var urlToPlay: String?
override func viewDidLoad() {
super.viewDidLoad()
print("Here is the url ---> \(String(describing: urlToPlay))")
playVideo()
}
private func playVideo() {
guard let urlFromString = urlToPlay else { print("No url to play") ;return }
let url = URL(string: urlFromString)
print("Here is the url to play ---> \(String(describing: url))")
let asset: AVURLAsset = AVURLAsset(url: url!)
let item: AVPlayerItem = AVPlayerItem(asset: asset)
let player: AVPlayer = AVPlayer(playerItem: item)
self.player = player
self.showsPlaybackControls = true
self.player?.play()
}
}
This is how I make the thumbnail, when my cellforRow atIndexPath method runs it throws this error for every video object in the tableview.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "sortedExerciseCell") as? SortedExerciseCell! {
// Do if check for videoURI and imageURI
if selectedExerciseArray[indexPath.row].imageURI != nil {
if let imageURI = URL(string: selectedExerciseArray[indexPath.row].imageURI!) {
print("It's a photo!")
// Using DispatchQueue.global(qos: .background).async loads cells in background
DispatchQueue.global(qos: .background).async {
let data = try? Data(contentsOf: imageURI)
DispatchQueue.main.async {
cell.exerciseImg.image = UIImage(data: data!)
}
}
}
} else {
if let videoURI = URL(string: selectedExerciseArray[indexPath.row].videoURI!) {
print("It's a video!")
print(videoURI)
DispatchQueue.global(qos: .background).async {
DispatchQueue.main.async {
cell.exerciseImg.image = self.thumbnailForVideoAtURL(url: videoURI)
// for every video object the above error is thrown!!!
}
}
}
}
cell.exerciseName.text = selectedExerciseArray[indexPath.row].name
return cell
} else {
return UITableViewCell()
}
}
// Used to just display the video thumbnail in cell, when clicked on will display video as needed
private func thumbnailForVideoAtURL(url: URL) -> UIImage? {
let asset = AVAsset(url: url)
let assetImageGenerator = AVAssetImageGenerator(asset: asset)
do {
print("Doing the video thumbnail from backend")
let imageRef = try assetImageGenerator.copyCGImage(at: CMTimeMake(1, 60) , actualTime: nil)
return UIImage(cgImage: imageRef)
} catch {
print("This is failing for some reason")
print("error")
return nil
}
}
I've looked at similar questions on stack overflow about this but none seem to give a complete answer on how to solve this problem, most chalking it up to an iOS 11 bug that can't be fixed or transport security (Already have arbitrary loads on so this can't be the issue..) anyone else have any approaches that might work?
Important note - My backend developer can only view the video url's from a webpage, in other words he must make a basic website to display the video after downloading it from the web. I'm not sure if this is standard procedure for handling video url's from AWS but I decided to try loading the url with "loadHTMLString" in a custom UIViewController conforming to WKUIDelegate, I see the video but the same situation happens where the triangular start button is crossed out signifying no video can be played. I'm not really sure what else I can try at this moment, any help is appreciated.
Here is one of the links I've pulled from my backend.
https://videofitapptestbucket.s3.us-west-2.amazonaws.com/100001427750
It seems that your problem is in file extension. DMS is not recognized by OS, it throws when creating image with assetgenerator.
Error Domain=AVFoundationErrorDomain Code=-11828 "Cannot Open" UserInfo={NSLocalizedFailureReason=This media format is not supported., NSLocalizedDescription=Cannot Open, NSUnderlyingError=0x6040000534d0 {Error Domain=NSOSStatusErrorDomain Code=-12847 "(null)"}}
Rename your files on server. The mp4 extension seems to work just fine with your file.
Also, subclassing AVPlayerViewController is generally not that great idea as Apple says that it will result in undefined behavior. (read: random mess). I would suggest to use class that have AVPlayer inside.
Try:
if let path = urlToPlay {
let url = URL(string: path)!
let videoPlayer = AVPlayer(url: url)
self.player = videoPlayer
DispatchQueue.main.async {
self.player?.play()
}
}
Call it in viewDidAppear() method
I have trying to play audio from URL. I tried both AVPlayer and AVAudioPlayer but none of them worked.
Here is the URL which I am trying to play.
Here is the code I am using.
// AVPlayer Code
self.avPlayerItem = AVPlayerItem.init(url: urlOfAudio)
self.avPlayer = AVPlayer.init(playerItem: avPlayerItem)
self.avPlayer.volume = 1.0
self.avPlayer.play()
// AVAudioPlayer
if let url = URL.init(string: escapeCharactorString){
do {
self.playerReference = try AVAudioPlayer.init(contentsOf: url)
guard let player = playerReference else {
return
}
player.volume = 1.0
player.prepareToPlay()
player.numberOfLoops = -1
player.play()
} catch let error {
}
}
None of them worked for me.
Please let me know where I am going wrong in it.
There is nothing wrong with your code, it is perfect but the issue is something related to server settings that audio file stored. Here is your working code which successfully playing an other mp3 file,
var avPlayer:AVPlayer?
var avPlayerItem:AVPlayerItem?
override func viewDidLoad() {
super.viewDidLoad()
// let urlstring = "http://www.noiseaddicts.com/samples_1w72b820/2514.mp3"
let urlstring = "https://s3.amazonaws.com/kargopolov/kukushka.mp3"
let url = NSURL(string: urlstring)
print("playing \(String(describing: url))")
avPlayerItem = AVPlayerItem.init(url: url! as URL)
avPlayer = AVPlayer.init(playerItem: avPlayerItem)
avPlayer?.volume = 1.0
avPlayer?.play()
}
So please try the mp3 file with different host it will solve your problem
The cause is Application Transport Security. You need to add:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoadsForMedia</key>
<true/>
</dict>
to your application's .plist file to load media from HTTP schemes. HTTPS schemes need no such exception.
The audio files for our iOS app sound much worse when played through the app than when played outside the app. The sounds seem harsher and more "ecohy".
Here's our code for playing audio. Are we somehow altering the playback or natural sound of the audio?
Two of the audio files we're using can be found here:
private func createAudioPlayer(filename: String) -> AVAudioPlayer {
// Define file URL
let path = Bundle.main.path(forResource: filename, ofType: nil)
let url = URL(fileURLWithPath: path!)
// Create player
let audioPlayer: AVAudioPlayer!
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
} catch {
audioPlayer = nil
printError("Error creating audio player for \(url): \(error)")
logEvent("Audio Error", userData: nil)
}
// Print status
print("Created audio player for \(filename)")
// Return player
return audioPlayer
}
func play(file: AudioFileEnum) {
if let player = audioPlayers[file] {
if player.isPlaying {
player.pause()
}
player.currentTime = 0
player.play()
} else {
printError("Error finding audio player for \(file)")
}
}
Have you tried setting the volume on the audioPlayer object to something smaller, like 0.05f, and adjusting from there?
I'm creating my first app. I have an app with music playing in the background with the following code:
var backgroundMusicPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
//background Music
func playBackgroundMusic(filename: String) {
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
guard let newURL = url else {
print("Could not find file: \(filename)")
return
}
do {
backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: newURL)
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
} catch let error as NSError {
print(error.description)
}
}
playBackgroundMusic("Starship.wav")
}
So what should I do in order to stop/mute the background music when I switch to another ViewController? Should I do this my FirstViewController or SecondViewController?
Obviously, I don't want the sound to be off in the SecondViewController as I have other stuff that will be playing there.
To mute sound I simply mute the volume.
backgroundMusicPlayer.volume = 0
and set it to normal if I want sound
backgroundMusicPlayer.volume = 1
If you just want to pause music you can call
backgroundMusicPlayer.pause()
To resume you call
backgroundMusicPlayer.resume()
If you want to stop music and reset it to the beginning you say this
backgroundMusicPlayer.stop()
backgroundMusicPlayer.currentTime = 0
backgroundMusicPlayer.prepareToPlay()
Did you also consider putting your music into a singleton class so its easier to play music in your different viewControllers.
Not sure this is what you are looking for as your question is a bit vague.
In my game I added a button to the GameScene and played music:
let url = NSBundle.mainBundle().URLForResource("Happy Background Music", withExtension: "mp3")
let bgMusic = AVAudioPlayer(contentsOfURL: url, error: nil)
#IBAction func SoundOnOff(sender: UIButton) {
bgMusic.numberOfLoops = -1
bgMusic.play()
}
and the music played, but I want to add an if so when the button is pressed the music will stop. What if should I write?
Change your code with this,
let url = NSBundle.mainBundle().URLForResource("Happy Background Music", withExtension: "mp3")
do{
let bgMusic = try AVAudioPlayer(contentsOfURL: url!)
}catch{
print(error)
}
#IBAction func SoundOnOff(sender: UIButton) {
if bgMusic.playing {
bgMusic.stop()
} else {
self.bgMusic.play()
}
}
Edit: Description
playing- Property
-A Boolean value that indicates whether the audio player is playing (true) or not (false). (read-only)
AVAudioPlayer has a built-in property playing, which you can use to decide whether to play or pause the music:
#IBAction func SoundOnOff(sender: UIButton) {
if bgMusic.playing {
bgMusic.pause()
} else {
bgMusic.numberOfLoops = -1
bgMusic.play()
}
}
Just seen that this is only available in iOS9 and later. So if you're targeting iOS9 then this is possible.
If you're using SpriteKit to write your game I would recommend using the SKAudioNode class to play your BGMusic.
let url = NSBundle.mainBundle().URLForResource("Happy Background Music", withExtension: "mp3")
let audioNode = SKAudioNode(URL:url)
audioNode.positional = false
audioNode.autoplayLooped = true
self.addChild(audioNode)
To pause the music you create an SKAction ...
let pauseAction = SKAction.pause()
audioNode.runAction(pauseAction)
You can resume it also but just trying to find the code for that...