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 并进行了一些小的调整以使其正常工作

高级别:

  1. 您需要添加第二个 AVPlayer,它将负责在擦除期间播放
  2. 当您开始擦洗时,从第一个播放器当前所在的位置开始在第二个 AVPlayer 中播放视频
  3. 一旦你开始玩第二个播放器,它应该被锁定不被洗涤器更新并且它应该播放 1/4 到 1/2 秒的短时间(用时间进行实验持续时间)
  4. 这段时间过去后,暂停第二个播放器并打开它以再次更新 - 这就是产生锯齿状噪音的原因

这是一个小例子,它给了我一些接近你所追求的东西:

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
            }
        }
    }
}