AVPlayer - 在 seeking/scrubbing 时播放声音
AVPlayer -Play sound while seeking/scrubbing
我的 avplayer 上有一个 uislider,有没有办法在拖拽时播放视频的声音?
var player: AVPlayer?
func createPlayer() {
player = AVPlayer()
// init player with playerItem, asset, eventually add to playerLayer ...
player?.volume = 1
}
@objc func sliderValueChanged(_ slider: UISlider) {
let sliderValue = slider.value
let seekTime = CMTimeMakeWithSeconds(Double(sliderValue), preferredTimescale: 1000)
player?.seek(to: seekTime, toleranceBefore: .zero, toleranceAfter: .zero)
}
由于 UISlider 的值在您与之交互时不断更新,AVPlayer 没有机会播放任何音频,因为每次更新滑块的值时都会执行 seek
函数。
我使用了评论中的 SO example 并进行了一些小的调整以使其正常工作
高级别:
- 您需要添加第二个 AVPlayer,它将负责在擦除期间播放
- 当您开始擦洗时,从第一个播放器当前所在的位置开始在第二个 AVPlayer 中播放视频
- 一旦你开始玩第二个播放器,它应该被锁定不被洗涤器更新并且它应该播放 1/4 到 1/2 秒的短时间(用时间进行实验持续时间)
- 这段时间过去后,暂停第二个播放器并打开它以再次更新 - 这就是产生锯齿状噪音的原因
这是一个小例子,它给了我一些接近你所追求的东西:
import UIKit
import AVKit
class AVPlayerScrubVC: UIViewController {
let playerViewController = AVPlayerViewController()
var player: AVPlayer?
var player2: AVPlayer?
var isScrubbingLocked = false
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
title = "Player Scrub"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
configurePlayer()
configureScrubber()
}
private func configurePlayer()
{
// Add your own video URL
let videoURL
= Bundle.main.url(forResource: "sample_1",
withExtension: "mp4")
if let videoURL = videoURL {
player = AVPlayer(url: videoURL)
player2 = AVPlayer(url: videoURL)
player2?.volume = 5
playerViewController.player = player
playerViewController.showsPlaybackControls = false
// Add the AVPlayerController as a child of the current
// view controller
self.addChild(playerViewController)
// Configure the player view
let playerView = playerViewController.view
playerView?.backgroundColor = .clear
playerView?.frame = self.view.bounds
// Add the AVPlayerViewController's view as a subview
// of the current view
self.view.addSubview(playerView!)
playerViewController.didMove(toParent: self)
// Start playing the content
playerViewController.player?.play()
}
}
// Configure the UISlider
private func configureScrubber() {
let slider = UISlider()
slider.translatesAutoresizingMaskIntoConstraints = false
slider.minimumValue = 0
let videoTime
= Float(CMTimeGetSeconds((player?.currentItem?.asset.duration)!))
slider.maximumValue = videoTime
slider.addTarget(self, action: #selector(sliderValueChanged(_:)),
for: .valueChanged)
view.addSubview(slider)
view.addConstraints([
slider.leadingAnchor
.constraint(equalTo: view.leadingAnchor,
constant: 16),
slider.bottomAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: -16),
slider.trailingAnchor
.constraint(equalTo: view.trailingAnchor,
constant: -16),
slider.heightAnchor
.constraint(equalToConstant: 70)
])
}
@objc
func sliderValueChanged(_ slider: UISlider) {
let sliderValue = slider.value
let seekTime = CMTimeMakeWithSeconds(Double(sliderValue),
preferredTimescale: 1000)
player?.seek(to: seekTime,
toleranceBefore: .zero,
toleranceAfter: .zero)
if !isScrubbingLocked {
audioScrub(to: seekTime)
}
}
func audioScrub(to currentTime: CMTime) {
DispatchQueue.main.async {
// Lock the scrubbing
self.isScrubbingLocked = true
// play the simultaneous player
self.player2?.play()
// make sure that it plays for at least 0.5 of a second
// before it stops to get that scrubbing effect
// Play around with the delay to get the results best for you
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// now that 1/2 of a second has passed
// (you can make it longer or shorter as you please)
// - pause the simultaneous player
self.player2?.pause()
// now move the simultaneous player to the same point as the
// original video player:
self.player2?.seek(to: currentTime)
// setting this variable back to true allows the process to be
// repeated as long as video is being scrubbed:
self.isScrubbingLocked = false
}
}
}
}
我的 avplayer 上有一个 uislider,有没有办法在拖拽时播放视频的声音?
var player: AVPlayer?
func createPlayer() {
player = AVPlayer()
// init player with playerItem, asset, eventually add to playerLayer ...
player?.volume = 1
}
@objc func sliderValueChanged(_ slider: UISlider) {
let sliderValue = slider.value
let seekTime = CMTimeMakeWithSeconds(Double(sliderValue), preferredTimescale: 1000)
player?.seek(to: seekTime, toleranceBefore: .zero, toleranceAfter: .zero)
}
由于 UISlider 的值在您与之交互时不断更新,AVPlayer 没有机会播放任何音频,因为每次更新滑块的值时都会执行 seek
函数。
我使用了评论中的 SO example 并进行了一些小的调整以使其正常工作
高级别:
- 您需要添加第二个 AVPlayer,它将负责在擦除期间播放
- 当您开始擦洗时,从第一个播放器当前所在的位置开始在第二个 AVPlayer 中播放视频
- 一旦你开始玩第二个播放器,它应该被锁定不被洗涤器更新并且它应该播放 1/4 到 1/2 秒的短时间(用时间进行实验持续时间)
- 这段时间过去后,暂停第二个播放器并打开它以再次更新 - 这就是产生锯齿状噪音的原因
这是一个小例子,它给了我一些接近你所追求的东西:
import UIKit
import AVKit
class AVPlayerScrubVC: UIViewController {
let playerViewController = AVPlayerViewController()
var player: AVPlayer?
var player2: AVPlayer?
var isScrubbingLocked = false
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
title = "Player Scrub"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
configurePlayer()
configureScrubber()
}
private func configurePlayer()
{
// Add your own video URL
let videoURL
= Bundle.main.url(forResource: "sample_1",
withExtension: "mp4")
if let videoURL = videoURL {
player = AVPlayer(url: videoURL)
player2 = AVPlayer(url: videoURL)
player2?.volume = 5
playerViewController.player = player
playerViewController.showsPlaybackControls = false
// Add the AVPlayerController as a child of the current
// view controller
self.addChild(playerViewController)
// Configure the player view
let playerView = playerViewController.view
playerView?.backgroundColor = .clear
playerView?.frame = self.view.bounds
// Add the AVPlayerViewController's view as a subview
// of the current view
self.view.addSubview(playerView!)
playerViewController.didMove(toParent: self)
// Start playing the content
playerViewController.player?.play()
}
}
// Configure the UISlider
private func configureScrubber() {
let slider = UISlider()
slider.translatesAutoresizingMaskIntoConstraints = false
slider.minimumValue = 0
let videoTime
= Float(CMTimeGetSeconds((player?.currentItem?.asset.duration)!))
slider.maximumValue = videoTime
slider.addTarget(self, action: #selector(sliderValueChanged(_:)),
for: .valueChanged)
view.addSubview(slider)
view.addConstraints([
slider.leadingAnchor
.constraint(equalTo: view.leadingAnchor,
constant: 16),
slider.bottomAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: -16),
slider.trailingAnchor
.constraint(equalTo: view.trailingAnchor,
constant: -16),
slider.heightAnchor
.constraint(equalToConstant: 70)
])
}
@objc
func sliderValueChanged(_ slider: UISlider) {
let sliderValue = slider.value
let seekTime = CMTimeMakeWithSeconds(Double(sliderValue),
preferredTimescale: 1000)
player?.seek(to: seekTime,
toleranceBefore: .zero,
toleranceAfter: .zero)
if !isScrubbingLocked {
audioScrub(to: seekTime)
}
}
func audioScrub(to currentTime: CMTime) {
DispatchQueue.main.async {
// Lock the scrubbing
self.isScrubbingLocked = true
// play the simultaneous player
self.player2?.play()
// make sure that it plays for at least 0.5 of a second
// before it stops to get that scrubbing effect
// Play around with the delay to get the results best for you
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// now that 1/2 of a second has passed
// (you can make it longer or shorter as you please)
// - pause the simultaneous player
self.player2?.pause()
// now move the simultaneous player to the same point as the
// original video player:
self.player2?.seek(to: currentTime)
// setting this variable back to true allows the process to be
// repeated as long as video is being scrubbed:
self.isScrubbingLocked = false
}
}
}
}