I am using a library named Jukebox , to play audios files , I want to make it player faster or slower.
It inherits AVPlayerItem , but I can't find how.
Any one can help me?
I think you can use the AVPlayer rate property below
var rate: Float { get set }
For instance:-
if let playerItem = AVPlayerItem(URL: yourUrl), let player = AVPlayer(playerItem: playerItem) {
//This will update your player speed faster or slower accordingly
player.rate = Float(rateValue)
}
Related
I try to download file using Alamofire and I done that with progress. What I am trying to do now is building a type of local stream from video URL.
Alamofire store the downloaded file at destination.
let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .LocalDomainMask)
This destination is not valid to play in my case. eg.
if totalBytesRead >= 51831 {
let player = AVPlayer(URL: NSURL(fileURLWithPath: "\(destination)"))
let playerController = AVPlayerViewController()
playerController.player = player
self.presentViewController(playerController, animated: true) {
player.play()
}
}
Is there another way to do this ? OR can I store downloaded bytes to an array/buffer ?
Thanks
You don't need Alamofire in order to stream content which is in your case a video.
You can actually utilize existing Apple's classes such as:
AVPlayerItem
AVPlayerLayer
AVPlayer
Using those 3 classes you can create your own class like VideoPlayer that will use them for streaming.
Here's a code snip for the constructor to get you started:
playerItem = AVPlayerItem(URL: videoURL)
player = AVPlayer(playerItem: self.playerItem!)
playerLayer = AVPlayerLayer(player: self.player)
playerLayer?.frame = newFrame
playerLayer?.videoGravity = AVLayerVideoGravityResizeAspect
Now, you can use this class inside another View Controller:
self.containerView.layer.addSublayer(self.videoPlayer.playerLayer)
This will add the video player class you created and now you only need to play the video:
// Of type AVPlayer
player.play()
This is the easy part, if you want you will have to to implement your own buffer & rate and of course implement an observer to observe the rate and buffer:
// Observe the Rate of the player. when the video playing or paused
player.addObserver(theSelf, forKeyPath: "rate", options: .New, context: nil)
// Observe when the Video player Buffer is empty
playerItem.addObserver(theSelf, forKeyPath: "playbackBufferEmpty", options: .New, context: nil)
// Observe the loaded Buffer of the video
playerItem.addObserver(theSelf, forKeyPath: "loadedTimeRanges", options: NSKeyValueObservingOptions.New, context: nil)
Also, don't forget to remove them when you are done with them.
EDIT:
As asked, if you need to switch to a better quality during run time you will first need to observe the user's internet speed. After you solved this issue you will now need to stream the high quality video by for example adding another VideoPlayer layer on top of the current layer but only do it if your new high quality video buffer has at least 5 secs or so, so the user won't notice the change.
Good luck.
I'm making an iOS app in Swift that plays a video in a loop in a small layer in the top right corner of the screen which shows a video of specific coloured item. the user then taps the corresponding coloured item on the screen. when they do, the videoName variable is randomly changed to the next colour and the corresponding video is triggered. I have no problem raising, playing and looping the video with AVPlayer, AVPlayerItem as seen in the attached code.
Where I'm stuck is that whenever the next video is shown, previous ones stay open behind it. Also, After 16 videos have played, the player disappears altogether on my iPad. I've tried many suggestions presented in this and other sites but either swift finds a problem with them or it just doesn't work.
So question: within my code here, how do I tell it "hey the next video has started to play, remove the previous video and it's layer and free up the memory so i can play as many videos as needed"?
//set variables for video play
var playerItem:AVPlayerItem?
var player:AVPlayer?
//variables that contain video file path, name and extension
var videoPath = NSBundle.mainBundle().resourcePath!
var videoName = "blue"
let videoExtension = ".mp4"
//DISPLAY VIDEO
func showVideo(){
//Assign url path
let url = NSURL(fileURLWithPath: videoPath+"/Base.lproj/"+videoName+videoExtension)
playerItem = AVPlayerItem(URL: url)
player=AVPlayer(playerItem: playerItem!)
let playerLayer=AVPlayerLayer(player: player!)
//setplayser location in uiview and show video
playerLayer.frame=CGRectMake(700, 5, 350, 350)
self.view.layer.addSublayer(playerLayer)
player!.play()
// Add notification to know when the video ends, then replay it again. THIS IS A CONTINUAL LOOP
NSNotificationCenter.defaultCenter().addObserverForName(AVPlayerItemDidPlayToEndTimeNotification, object: player!.currentItem, queue: nil)
{ notification in
let t1 = CMTimeMake(5, 100);
self.player!.seekToTime(t1)
self.player!.play()
}
}
`
I adapted #Anupam Mishra's Swift code suggestion. It wasn't working at first but finally figured I had to take the playerLayer outside the function and close the playerLayer after I set the player to nil. Also. instead of using 'if(player!.rate>0 ...)' which would no doubt still work, I created a variable switch to indicate when to say "kill the player AND the layer" as seen below. It may not be pretty but it works! The following is for absolute newbies like myself - WHAT I LEARNED FROM THIS EXPERIENCE: it seems to me that an ios device only allows 16 layers to be added to a viewController (or superLayer). so each layer needs to be deleted before opening the next layer with its player unless you really want 16 layers running all at once. WHAT THIS CODE BELOW ACTUALLY DOES FOR YOU: this code creates a re-sizable layer over an existing viewController and plays a video from your bundle in an endless loop. When the next video is about to be called, the current video and the layer are totally removed, freeing up the memory, and then a new layer with the new video is played. The video layer size and location is totally customizable using the playerLayer.frame=CGRectMake(left, top, width, height) parameters. HOW TO MAKE IT ALL WORK: Assuming you've already added your videos to you bundle, create another function for your button tap. in that function, first call the 'stopPlayer()' function, change the 'videoName' variable to the new video name you desire, then call the 'showVideo()' function. (if you need to change the video extension, change 'videoExtension' from a let to a var.
`
//set variables for video play
var playerItem:AVPlayerItem?
var player:AVPlayer?
var playerLayer = AVPlayerLayer() //NEW playerLayer var location
//variables that contain video file path, name and extension
var videoPath = NSBundle.mainBundle().resourcePath!
var videoName = "blue"
let videoExtension = ".mp4"
var createLayerSwitch = true /*NEW switch to say whether on not to create the layer when referenced by the closePlayer and showVideo functions*/
//DISPLAY VIDEO
func showVideo(){
//Assign url path
let url = NSURL(fileURLWithPath: videoPath+"/Base.lproj/"+videoName+videoExtension)
playerItem = AVPlayerItem(URL: url)
player=AVPlayer(playerItem: playerItem!)
playerLayer=AVPlayerLayer(player: player!) //NEW: remove 'let' from playeLayer here.
//setplayser location in uiview and show video
playerLayer.frame=CGRectMake(700, 5, 350, 350)
self.view.layer.addSublayer(playerLayer)
player!.play()
createLayerSwitch = false //NEW switch to tell if a layer is already created or not. I set the switch to false so that when the next tapped item/button references the closePlayer() function, the condition is triggered to close the player AND the layer
// Add notification to know when the video ends, then replay it again without a pause between replays. THIS IS A CONTINUAL LOOP
NSNotificationCenter.defaultCenter().addObserverForName(AVPlayerItemDidPlayToEndTimeNotification, object: player!.currentItem, queue: nil)
{ notification in
let t1 = CMTimeMake(5, 100);
self.player!.seekToTime(t1)
self.player!.play()
}
}
//NEW function to kill the current player and layer before playing the next video
func closePlayer(){
if (createLayerSwitch == false) {
player!.pause()
player = nil
playerLayer.removefromsuperlayer()
}
}
`
Why don't just use replaceCurrentItemWithPlayerItem on your player ? You will keep the same player for all your videos. I think it's a better way to do.
Edit : replaceCurrentItemWithPlayerItem has to be call on the main thread
Before moving on next track first check, is player having any videos or music, to stop it do the following checks:
Swift Code-
if player!.rate > 0 && player!.error == nil
{
player!.pause()
player = nil
}
Objective-C Code-
if (player.rate > 0 && !player.error)
{
[player setRate:0.0];
}
I am trying to use an AVPlayer to play a video that has been recorded in my app. However, the player won't play the video. I know for a fact that this is a properly recorded mp4 file, because I can take it and play it on my Mac just fine. Here's the setup for the player:
let documents = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
let URL = NSURL(fileURLWithPath: "tempVideo", relativeToURL: documentsDirectory)
let asset = AVAsset(URL: URL)
let item = AVPlayerItem(asset: asset)
//videoPlayer is a property on the view controller being used
videoPlayer = AVPlayer()
//videoPlayerLayer is a property on the view controller being used
videoPlayerLayer = AVPlayerLayer(player: videoPlayer)
videoPlayerLayer.frame.size = view.frame.size
videoPlayerLayer.backgroundColor = UIColor.redColor().CGColor
view.layer.addSublayer(videoPlayerLayer!)
//wait 5 seconds
videoPlayer.play()
I know for sure that the videoPlayer is, in fact, ready to play, because I've checked its status property. I also know that videoPlayerLayer has properly been added to view.layer because its visible and takes up the whole screen. When I call videoPlayer.play(), the music playing on the device stops, but videoPlayerLayer doesn't show anything.
Any ideas? Thank you in advance for the help!
EDIT: I forgot to show that videoPlayerLayer is indeed connected to videoPlayer, I have updated my question to reflect this.
The correct answer was given by #Dershowitz123, but he or she left it in a comment so I can't mark it as correct. The solution was to change the URL to include the .mp4 extension. Thank you for your help.
I'm trying to change the audioTimePitchAlgorithm for playback at lower rates than 0.5 but don't seem to be having much luck. I've scoured SO for solutions but every implementation I try seems to still limit me to a rate of 0.5 or above...
var player: AVPlayer = AVQueuePlayer()
#IBAction func play(sender: AnyObject) {
let assetQueue = [aVItem1, aVItem2, aVItem3, aVItem4, aVItem5, aVItem6, aVItem7, aVItem8, aVItem9, aVItem0]
var itemQueue: [AVPlayerItem] = []
for index in 0...9{
let nextItem: AVPlayerItem = AVPlayerItem(asset: assetQueue[index])
nextItem.audioTimePitchAlgorithm = "AVAudioTimePitchAlgorithmSpectral"
itemQueue.append(nextItem)
}
player = AVQueuePlayer(items: itemQueue)
player.play()
player.rate = 0.25
}
I apologise in advance if this is actually a straight forward process and I am perhaps too new to this to grasp some of the underlying concepts. I have already also tried creating an AVMutableAudioMixInputParameters object and assigning it to an AVMutableAudioMix object and intern assigning this mix object to the AVPlayerItem, but this also yielded the same result (playback at a rate of 0.5), so I have only included my first, more simplified, code attempt. Any help would be greatly appreciated :)
In my application I've to stream videos from server. For that I've used below Code
-(void)playingSong:(NSURL*) url{
AVAsset *asset = [AVAsset assetWithURL:url];
duration = asset.duration;
playerItem = [AVPlayerItem playerItemWithAsset:asset];
player = [AVPlayer playerWithPlayerItem:playerItem];
[player play];
}
All are Global Variables
It's playing all videos when network is good, but unable to play videos with big size, when network is slow.
Means It's not playing for big size videos and it's playing small videos;
I'm using http Server not https;
for ex : 3min video it's playing but for 1hr video it's not.
Why so?
Seems like you have to download the whole video before you can begin playback. It can also be because of your server not AVPlayer.
when you serve videos on a site using plain HTTP – known as
progressive download – the position of the header becomes very
important. Either the header is placed in the beginning of the file or
it’s places in the end of the file. In case of the latter, you’ll have
to download the whole thing before you can begin playback – because
without the header, the player can’t start decoding.
Have look at this guide if your problem is because of videos source.
Have a look at this thread and change you implementation accordingly.
Download video in local and then play in avplayer.
DispatchQueue.global(qos: .background).async {
do {
let data = try Data(contentsOf: url)
DispatchQueue.main.async {
// store "data" in document folder
let fileUrl = URL(fileURLWithPath: <#localVideoURL#>)
let asset = AVAsset(url: fileUrl)
let item = AVPlayerItem(asset: asset)
let player = AVPlayer(playerItem: item)
let layer = AVPlayerLayer(player: player)
layer.bounds = self.view.bounds
self.view.layer.addSublayer(layer)
player.play()
}
} catch {
print(error.localizedDescription)
}
}