#ios #avfoundation #avplayer
Вопрос:
У нас есть фрагментированные данные о фильмах, которые поступают через WebSecureSocket (wss://). Мы записываем это в файл temp.mp4 и одновременно воспроизводим фрагменты, которые были записаны в файл. Итак, для этого мы использовали AVFragmentedAsset и AVFragmentedAssetMinder. Он работает так, как и ожидалось в симуляторе. Но в устройстве не обновляется продолжительность ресурса и не публикуется .Уведомление об обмене данными avassetdurationdid. Не уверен, в чем может быть проблема. Уже потратил 2 дня на выяснение этого, но безуспешно. Может кто-нибудь помочь мне с этим, пожалуйста,
Реализация..
public class WssPlayerSource: WebSocketDelegate {
static let ASSET_MINDER = AVFragmentedAssetMinder()
private let socketClient: WebSocketStreamingClient
private var tempMovieFile:FileHandle? = nil
private var startedPlay = false
private var fragmentedAsset: AVFragmentedAsset? = nil
let player = AVPlayer()
init(withUrl url: URL) {
socketClient = WebSocketStreamingClient(wssUrl: url)
socketClient.delegate = self
do {
self.tempMovieFile = try getTemporaryMovieFileWriter()
}catch {
print("Error opening file")
}
NotificationCenter.default.addObserver(self, selector: #selector(onVideoUpdate), name: .AVAssetDurationDidChange, object: nil)
socketClient.connect()
}
// Socket delegate
public func didReceive(event: WebSocketEvent, client: WebSocket) {
switch event {
case .binary(let data):
self.tempMovieFile?.write(data)
if !startedPlay {
startedPlay = true
DispatchQueue.global().async { [weak self] in
self?.didReceivedInitialData()
}
}
break;
default:
break;
}
}
func didReceivedInitialData() {
fragmentedAsset = AVFragmentedAsset(url: getTemporaryMovieFile()!)
fragmentedAsset?.loadValuesAsynchronously(forKeys: ["duration", "containsFragments", "canContainFragments"], completionHandler: {
WssPlayerSource.ASSET_MINDER.mindingInterval = 1
WssPlayerSource.ASSET_MINDER.addFragmentedAsset(self.fragmentedAsset!)
self.player.replaceCurrentItem(with: AVPlayerItem(asset: self.fragmentedAsset!))
self.player.play()
})
}
@objc
func onVideoUpdate() {
print("Video duration updated..")
// This is not called in device.. but in simulator it works as expected
}
func stop() {
socketClient.forceDisconnect()
NotificationCenter.default.removeObserver(self)
if let asset = self.fragmentedAsset {
WssPlayerSource.ASSET_MINDER.addFragmentedAsset(asset)
}
}
}
И
class ViewController: UIViewController {
@IBOutlet var playerView:PlayerView!
private static let SOCKET_URL = "wss://..."
private var playerSource:WssPlayerSource? = nil
private var playerLayer:AVPlayerLayer? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.playerSource = WssPlayerSource(withUrl: URL(string: ViewController.SOCKET_URL)!)
self.playerLayer = AVPlayerLayer(player: self.playerSource!.player)
self.playerLayer?.videoGravity = .resize
self.playerView.layer.addSublayer(self.playerLayer!)
}
}
Комментарии:
1. Покажите, как создается и поддерживается экземпляр PlayerController. Как мы можем быть уверены, что он не исчезнет до того, как появятся уведомления? Мне кажется, что это может быть слабая ссылка. (Плюс использование
sleep(2)
весьма подозрительно.)2. @мэтт, спасибо, что разбираешься в этом. Для удобства чтения я завернул код
PlayerController
для этого поста внутрь. Это создается и управляется в ViewController.sleep(2)
это не проблема, я просто добавил его, чтобы в файле было доступно больше данных до инициализации ресурса.3. Так это не твой настоящий код?
4. @мэтт, это был настоящий код. Только то, что я разместил требуемую часть. Теперь я обновил полный код. Пожалуйста, взгляните