AVPlayer seek player at NSDate - ios

I have string which text is "00:01:30" in formate of hh:mm:ss.
I want this time as seek time for my video player. Means in sort i want to set 'seekToTime' from string value which is hh:mm:ss. I convert this in NSDate formate but not able to seeToTime using this. Please suggest.
Thank you.

self.audioPlayer.timeFormat(self.audioPlayer.getCurrentAudioTime()))
func getCurrentAudioTime(){
let dur = CMTimeGetSeconds(audioPlayer.currentTime())
let cmValue = NSTimeInterval(dur)
let newTime = CMTimeMakeWithSeconds(value, 1)
self.audioPlayer.seekToTime(newTime)
}
func timeFormat(value:Double) -> NSString
{
let someFloat = Float(value)
let floatvalue = lroundf(someFloat)
let floorvalue = Double(floatvalue)
let minutes = floor(floorvalue / 60)
let seconds = floorvalue - (minutes * 60)
let roundsec = Float(seconds)
let roundmin = Float(minutes)
let roundedSeconds = lroundf(roundsec)
let roundedMinutes = lroundf(roundmin)
var time : NSString!
time = NSString.init(format:"%d:%02d", roundedMinutes,roundedSeconds)
return time;
}

if you convert the string to CMTime format then assign the seek time, and the code is given below,
let fileURL: NSURL = NSURL(string: timeString)!
let asset = AVURLAsset(URL: fileURL, options: nil)
let audioDuration = asset.duration
let newTime:CMTime = CMTimeGetSeconds(audioDuration)
playerVal.seekToTime(newTime)
hope its helpful

You can convert the string to CMTime using the code below,
func seekTime(timeString:String) -> CMTime? {
let dateFormatter = NSDateFormatter.init()
dateFormatter.dateFormat = "HH:mm:ss"
if let startDate = dateFormatter.dateFromString("00:00:00") {
if let neededDate = dateFormatter.dateFromString(timeString) {
let interval = neededDate.timeIntervalSinceDate(startDate)
return CMTime.init(value: CMTimeValue(interval), timescale: 1)
}
}
return nil;
}

Related

Swift - Not enough bits to represent the passed value

Below code works fine on iOS devices and watchOS simulator.
static func getEventDateTime(startDateTime: Date?) -> String {
if let startDateTime = startDateTime {
let startTimeInMillis = Int(startDateTime.timeIntervalSince1970 * 1000)
let fiveMinutesInMillis = 300000
let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((startTimeInMillis-fiveMinutesInMillis)/1000))
return convertDateToString(eventStartDateTime)
}
return ""
}
However when I run it on Apple Watch Series 3, I get the following error: double value cannot be converted to int because the result would be greater than int.max on line let startTimeInMillis = Int(startDateTime.timeIntervalSince1970 * 1000).
So I changed
let startTimeInMillis = Int(startDateTime.timeIntervalSince1970 * 1000) to let startTimeInMillis = Int64(startDateTime.timeIntervalSince1970 * 1000)
and
let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((startTimeInMillis-fiveMinutesInMillis)/1000)) to let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((Int(startTimeInMillis)-fiveMinutesInMillis)/1000)).
Now I am getting following error: Not enough bits to represent the passed value on line let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((Int(startTimeInMillis)-fiveMinutesInMillis)/1000))
How do I change the function to make it work on Apple Watch Series 3 or watchOS 7?
Updated function code:
static func getEventDateTime(startDateTime: Date?) -> String {
if let startDateTime = startDateTime {
let startTimeInMillis = Int64(startDateTime.timeIntervalSince1970 * 1000)
let fiveMinutesInMillis = 300000
let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((Int(startTimeInMillis)-fiveMinutesInMillis)/1000))
return convertDateToString(eventStartDateTime)
}
return ""
}
Use the Calendar API to add/subtract time units (doesn't support milliseconds, but does support nanoseconds, which can be converted from): https://developer.apple.com/documentation/foundation/calendar.
func getEventDateTimeCal(startDateTime: Date?) -> String {
if let startDateTime = startDateTime,
let date = Calendar.current.date(byAdding: .minute, value: -5, to: startDateTime) {
return convertDateToString(date, startDateTime)
}
return ""
}
But also, if you don't need millisecond precision, subtract seconds from TimeInterval. Note that TimeInterval is typealias TimeInterval = Double and always represents seconds.
func getEventDateTime(startDateTime: Date?) -> String {
if let startDateTime = startDateTime {
let fiveMinutesInSeconds = 5.0 * 60
let eventStartDateTime = Date(timeIntervalSince1970: startDateTime.timeIntervalSince1970 - fiveMinutesInSeconds)
return convertDateToString(startDateTime, eventStartDateTime)
}
return ""
}

Convert String minutes seconds to Int

I've a string with minutes and seconds in format "minutes:seconds". For example, "5:36". I want to convert it to Int value. For example "5:36" string should be 336 Int value. How this can be done?
let timeString = "5:36"
let timeStringArray = timeString.split(separator: ":")
let minutesInt = Int(timeStringArray[0]) ?? 0
let secondsInt = Int(timeStringArray[1]) ?? 0
let resultInt = minutesInt * 60 + secondsInt
print(resultInt)
Here's a simple extension you can use which will validate the format of your input string too:
import Foundation
extension String {
func toSeconds() -> Int? {
let elements = components(separatedBy: ":")
guard elements.count == 2 else {
print("Provided string doesn't have two sides separated by a ':'")
return nil
}
guard let minutes = Int(elements[0]),
let seconds = Int(elements[1]) else {
print("Either the minute value or the seconds value cannot be converted to an Int")
return nil
}
return (minutes*60) + seconds
}
}
Usage:
let testString1 = "5:36"
let testString2 = "35:36"
print(testString1.toSeconds()) // prints: "Optional(336)"
print(testString2.toSeconds()) // prints: "Optional(2136)"
I tried out your example on the playground here's the code:
import Foundation
let time1String = "0:00"
let time2String = "5:36"
let timeformatter = DateFormatter()
timeformatter.dateFormat = "m:ss"
let time1 = timeformatter.date(from: time1String)
let time2 = timeformatter.date(from: time2String)
if let time1 = time1 {
print(time2?.timeIntervalSince(time1)) // prints: Optional(336.0)
}

Swift - AVPlayer progress via UISlider

I am trying to make video player where I need to show the progress via UISlider and UILabel(for updating time). Here is my code
let videoPlayer = AVPlayer()
var videoPlayerSlider: UISlider = UISlider()
var videoPlayerLabel: UILabel = UILabel()
func updateVideoPlayerSlider() {
guard let currentTime = videoPlayer.currentTime else {
return
}
let mins = currentTime / 60
let secs = currentTime.truncatingRemainder(dividingBy: 60)
let timeformatter = NumberFormatter()
timeformatter.minimumIntegerDigits = 2
timeformatter.minimumFractionDigits = 0
timeformatter.roundingMode = .down
guard let minsStr = timeformatter.string(from: NSNumber(value: mins)), let secsStr = timeformatter.string(from: NSNumber(value: secs)) else {
return
}
videoPlayerLabel.text = "\(minsStr).\(secsStr)"
videoPlayerSlider.value = Float(videoPlayer.currentTime())
}
It shows 2 error.
1.(at very 1st line of the function)Initializer for conditional binding must have optional type, not '() -> CMTime
2.(at last line of the function)Cannot invoke initializer for type 'Float' with an argument list of type '(CMTime)'
Any assistance would be appreciated.
let videoPlayer = AVPlayer()
var videoPlayerSlider: UISlider = UISlider()
var videoPlayerLabel: UILabel = UILabel()
func updateVideoPlayerSlider() {
// 1 . Guard got compile error because `videoPlayer.currentTime()` not returning an optional. So no just remove that.
let currentTimeInSeconds = CMTimeGetSeconds(videoPlayer.currentTime())
// 2 Alternatively, you could able to get current time from `currentItem` - videoPlayer.currentItem.duration
let mins = currentTimeInSeconds / 60
let secs = currentTimeInSeconds.truncatingRemainder(dividingBy: 60)
let timeformatter = NumberFormatter()
timeformatter.minimumIntegerDigits = 2
timeformatter.minimumFractionDigits = 0
timeformatter.roundingMode = .down
guard let minsStr = timeformatter.string(from: NSNumber(value: mins)), let secsStr = timeformatter.string(from: NSNumber(value: secs)) else {
return
}
videoPlayerLabel.text = "\(minsStr).\(secsStr)"
videoPlayerSlider.value = Float(currentTimeInSeconds) // I don't think this is correct to show current progress, however, this update will fix the compile error
// 3 My suggestion is probably to show current progress properly
if let currentItem = videoPlayer.currentItem {
let duration = currentItem.duration
if (CMTIME_IS_INVALID(duration)) {
// Do sth
return;
}
let currentTime = currentItem.currentTime()
videoPlayerSlider.value = Float(CMTimeGetSeconds(currentTime) / CMTimeGetSeconds(duration))
}
}
I hope this would help you

Convert Kelvin into Celsius in Swift

I'm trying to retrieve the temperature from the users current location.
I am using the API from OpenWeatherMap. The problem is, they provide the temperature in Kelvin as default, and I would like it in Celsius.
I understand that I just need to subtract 273.15 from the kelvin value....? But i'm struggling to figure out where to do that.
My code for setting my labels:
var jsonData: AnyObject?
func setLabels(weatherData: NSData) {
do {
self.jsonData = try NSJSONSerialization.JSONObjectWithData(weatherData, options: []) as! NSDictionary
} catch {
//handle error here
}
if let name = jsonData!["name"] as? String {
locationLabel.text = "using your current location, \(name)"
}
if let main = jsonData!["main"] as? NSDictionary {
if let temperature = main["temp"] as? Double {
self.tempLabel.text = String(format: "%.0f", temperature)
}
}
}
Can anyone help me get this right please, as I'm really not sure where to start, thanks.
Let me know if you need to see more of my code.
if let kelvinTemp = main["temp"] as? Double {
let celsiusTemp = kelvinTemp - 273.15
self.tempLabel.text = String(format: "%.0f", celsiusTemp)
}
or simply
self.tempLabel.text = String(format: "%.0f", temperature - 273.15)
For Swift 4.2:
Use a Measurement Formatter.
let mf = MeasurementFormatter()
This method converts one temperature type (Kelvin, Celsius, Fahrenheit) to another:
func convertTemp(temp: Double, from inputTempType: UnitTemperature, to outputTempType: UnitTemperature) -> String {
mf.numberFormatter.maximumFractionDigits = 0
mf.unitOptions = .providedUnit
let input = Measurement(value: temp, unit: inputTempType)
let output = input.converted(to: outputTempType)
return mf.string(from: output)
}
Usage:
let temperature = 291.0
let celsius = convertTemp(temp: temperature, from: .kelvin, to: .celsius) // 18°C
let fahrenheit = convertTemp(temp: temperature, from: .kelvin, to: .fahrenheit) // 64°F
To output the localized temperature format, remove the line mf.unitOptions = .providedUnit
From the code above, it seems to me the right place to do this would be right after you get the temperature
if let temperatureInKelvin = main["temp"] as? Double {
let temperatureInCelsius = temperatureInKelvin - 273.15
self.tempLabel.text = String(format: "%.0f", temperature)
}
In the future though, I would probably parse your JSON values in a separate class and store them in a model object which you can call later on.
A simpler example of the above handy function (updated for Swift 5.3) would be something like:
func convertTemperature(temp: Double, from inputTempType: UnitTemperature, to outputTempType: UnitTemperature) -> Double {
let input = Measurement(value: temp, unit: inputTempType)
let output = input.converted(to: outputTempType)
return output.value
}
Here:
self.tempLabel.text = String(format: "%.0f", temperature - 273.15)
or you can do it here (pseudo syntax as I don't know Swift that well):
if let temperature = (main["temp"] as? Double) - 273.15 {
self.tempLabel.text = String(format: "%.0f", temperature)
}

How do I get current playing time and total play time in AVPlayer?

Is it possible get playing time and total play time in AVPlayer? If yes, how can I do this?
You can access currently played item by using currentItem property:
AVPlayerItem *currentItem = yourAVPlayer.currentItem;
Then you can easily get the requested time values
CMTime duration = currentItem.duration; //total time
CMTime currentTime = currentItem.currentTime; //playing time
Swift 5:
if let currentItem = player.currentItem {
let duration = CMTimeGetSeconds(currentItem.duration)
let currentTime = CMTimeGetSeconds(currentItem.currentTime())
print("Duration: \(duration) s")
print("Current time: \(currentTime) s")
}
_audioPlayer = [self playerWithAudio:_audio];
_observer =
[_audioPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 2)
queue:dispatch_get_main_queue()
usingBlock:^(CMTime time)
{
_progress = CMTimeGetSeconds(time);
}];
Swift 3
let currentTime:Double = player.currentItem.currentTime().seconds
You can get the seconds of your current time by accessing the seconds property of the currentTime(). This will return a Double that represents the seconds in time. Then you can use this value to construct a readable time to present to your user.
First, include a method to return the time variables for H:mm:ss that you will display to the user:
func getHoursMinutesSecondsFrom(seconds: Double) -> (hours: Int, minutes: Int, seconds: Int) {
let secs = Int(seconds)
let hours = secs / 3600
let minutes = (secs % 3600) / 60
let seconds = (secs % 3600) % 60
return (hours, minutes, seconds)
}
Next, a method that will convert the values you retrieved above into a readable string:
func formatTimeFor(seconds: Double) -> String {
let result = getHoursMinutesSecondsFrom(seconds: seconds)
let hoursString = "\(result.hours)"
var minutesString = "\(result.minutes)"
if minutesString.characters.count == 1 {
minutesString = "0\(result.minutes)"
}
var secondsString = "\(result.seconds)"
if secondsString.characters.count == 1 {
secondsString = "0\(result.seconds)"
}
var time = "\(hoursString):"
if result.hours >= 1 {
time.append("\(minutesString):\(secondsString)")
}
else {
time = "\(minutesString):\(secondsString)"
}
return time
}
Now, update the UI with the previous calculations:
func updateTime() {
// Access current item
if let currentItem = player.currentItem {
// Get the current time in seconds
let playhead = currentItem.currentTime().seconds
let duration = currentItem.duration.seconds
// Format seconds for human readable string
playheadLabel.text = formatTimeFor(seconds: playhead)
durationLabel.text = formatTimeFor(seconds: duration)
}
}
With Swift 4.2, use this;
let currentPlayer = AVPlayer()
if let currentItem = currentPlayer.currentItem {
let duration = currentItem.asset.duration
}
let currentTime = currentPlayer.currentTime()
Swift 4
self.playerItem = AVPlayerItem(url: videoUrl!)
self.player = AVPlayer(playerItem: self.playerItem)
self.player?.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, 1), queue: DispatchQueue.main, using: { (time) in
if self.player!.currentItem?.status == .readyToPlay {
let currentTime = CMTimeGetSeconds(self.player!.currentTime())
let secs = Int(currentTime)
self.timeLabel.text = NSString(format: "%02d:%02d", secs/60, secs%60) as String//"\(secs/60):\(secs%60)"
})
}
AVPlayerItem *currentItem = player.currentItem;
NSTimeInterval currentTime = CMTimeGetSeconds(currentItem.currentTime);
NSLog(#" Capturing Time :%f ",currentTime);
Swift:
let currentItem = yourAVPlayer.currentItem
let duration = currentItem.asset.duration
var currentTime = currentItem.asset.currentTime
Swift 5:
Timer.scheduledTimer seems better than addPeriodicTimeObserver if you want to have a smooth progress bar
static public var currenTime = 0.0
static public var currenTimeString = "00:00"
Timer.scheduledTimer(withTimeInterval: 1/60, repeats: true) { timer in
if self.player!.currentItem?.status == .readyToPlay {
let timeElapsed = CMTimeGetSeconds(self.player!.currentTime())
let secs = Int(timeElapsed)
self.currenTime = timeElapsed
self.currenTimeString = NSString(format: "%02d:%02d", secs/60, secs%60) as String
print("AudioPlayer TIME UPDATE: \(self.currenTime) \(self.currenTimeString)")
}
}
Swift 4.2:
let currentItem = yourAVPlayer.currentItem
let duration = currentItem.asset.duration
let currentTime = currentItem.currentTime()
in swift 5+
You can query the player directly to find the current time of the actively playing AVPlayerItem.
The time is stored in a CMTime Struct for ease of conversion to various scales such as 10th of sec, 100th of a sec etc
In most cases we need to represent times in seconds so the following will show you what you want
let currentTimeInSecs = CMTimeGetSeconds(player.currentTime())

Resources