Свойство актива AVAssetTrack иногда ноль - PullRequest
1 голос
/ 16 апреля 2019

Я пытаюсь получить свойство asset от объекта AVAssetTrack, но иногда оно nil. Кажется, проблема возникает только после того, как я использую Dispatch.main.async.

Согласно документации необходимо использовать loadValuesAsynchronously(forKeys:, completion:), чтобы избежать блокировки основного потока, и вернуться к основному потоку после завершения загрузки.

let asset = AVURLAsset(url: videoInAppBundleURL)
let track = asset.tracks(withMediaType: .video).first!
assert(track.asset != nil) // passes
track.loadValuesAsynchronously(forKeys: [#keyPath(AVAssetTrack.asset)]) {
    assert(track.asset != nil) // passes
    DispatchQueue.main.async {
        assert(track.asset != nil) // FAILS
        // [...]
    }
}

То, что я узнал, это:

  • Не имеет значения, работаю ли я на устройстве или Тренажер.
  • Кажется, это не проблема с video / videoURL. Видео является частью основного пакета, я пробовал как .mp4 и .mov файлы, и я убедился, что видео работает, отображая его через AVPlayerViewController.

Вот рабочий демонстрационный проект .

Мне также интересно: почему свойство AVAssetTrack asset является необязательным? (все !! остальные свойства не являются обязательными)

Примечание: этот вопрос был отредактирован после прочтения полезных комментариев Мэтта и дальнейшего изучения.

1 Ответ

1 голос
/ 18 апреля 2019

Я воспроизвел проблему с некоторыми изменениями вашего примера с github, например:

    let asset = AVURLAsset(url: videoInAppBundleURL)
    let tracksKey = #keyPath(AVAsset.tracks)
    asset.loadValuesAsynchronously(forKeys: [tracksKey]) {
        let track = asset.tracks(withMediaType: .video).first!
        DispatchQueue.main.async {
            assert(track.asset != nil) // fails
        }
    }

Хорошо, но теперь внимательно наблюдайте, как я выполняю удивительный трюк:

    let asset = AVURLAsset(url: videoInAppBundleURL)
    let tracksKey = #keyPath(AVAsset.tracks)
    asset.loadValuesAsynchronously(forKeys: [tracksKey]) {
        let track = asset.tracks(withMediaType: .video).first!
        DispatchQueue.main.async {
            print(asset) // <-- amazing trick
            assert(track.asset != nil) // passes!
        }
    }

Вау! Все, что я сделал, это добавил утверждение print - и теперь внезапно то же самое утверждение прошло. Это на самом деле параллельно вашему первоначальному утверждению (которое вы позже отредактировали), что «Иногда проблемы исчезают при пошаговом выполнении кода с помощью отладчика».

Итак, теперь, когда мои подозрения полностью пробудились, я сделал что-то невероятно умное (даже если я сам так говорю). Я удалил print(asset), но я переключил конфигурацию схемы с Debug на Release. Престо, утверждение все еще проходит.

Итак, вы обнаружили причуду компилятора - смею ли я назвать это ошибкой?

Но подождите, это еще не все. Вы достаточно разумно спросили, почему asset является опциональным. Потому что это weak:

weak open var asset: AVAsset? { get }

Итак, ваш ответ. Трек имеет только слабую ссылку на свой актив. Если мы передадим дорожку в асинхронную очередь и не будем приводить сам актив вместе с нами, то слабая ссылка отпустится, и актив будет потерян - в сборке отладки.

Надеюсь, это поможет. Вы, вероятно, ожидаете, что я сделаю какое-то грандиозное заключение о том, является ли это ошибкой, но я не собираюсь этого делать, извините. Я предложил два обходных пути (используйте сборку Release или намеренно перенесите ссылку на ресурс в асинхронную очередь), и это все, что я могу сделать.

...