Например, вы можете воспроизвести это, взяв любой обычный файл MP4 и сместив его временные метки с помощью FFmpeg:
Код: Выделить всё
ffmpeg -itsoffset 0.066 -i input.mp4 -c copy output_shifted.mp4
На самом деле это очень часто случается с видео, записанными камерой или повторно мультиплексированными — они часто не начинаются с нуля из-за смещений кодера или мультиплексора.
Проблема:
Когда я загружаю такой файл в AVPlayer, я не могу искать до реального первого кадра.
Все, что я пытаюсь сделать, сообщает о 0 как о времени начала, хотя FFprobe четко показывает первый PTS около 0,06.
Вот код, который я использую для загрузки и отображения первого кадра перед воспроизведением:
Код: Выделить всё
import SwiftUI
import AVFoundation
import Observation
import AVKit
@MainActor
@Observable
final class VideoPlayerViewModel {
let player = AVPlayer()
private var readyObservation: NSKeyValueObservation?
func prepareAndPlay(url: URL) async throws {
print("preparing")
player.pause()
player.automaticallyWaitsToMinimizeStalling = true
let item = AVPlayerItem(url: url)
player.replaceCurrentItem(with: item)
await readyToPlay(item)
print("ready to play first frame")
// Trying to show the very first frame
await player.seek(to: .zero)
// await player.seek(to: CMTime(value: 3, timescale: 30)) // works if I seek later
try await Task.sleep(for: .seconds(20)) // for demo purposes
print("playing")
player.play()
}
private func readyToPlay(_ item: AVPlayerItem) async {
if item.status == .readyToPlay { return }
await withCheckedContinuation { (cont: CheckedContinuation) in
readyObservation = item.observe(\.status, options: [.initial, .new]) { _, _ in
Task { @MainActor in
switch item.status {
case .readyToPlay, .failed:
self.readyObservation?.invalidate()
cont.resume()
default:
break
}
}
}
}
}
}
struct CustomVideoPlayer: View {
let url: URL
@State private var viewModel = VideoPlayerViewModel()
var body: some View {
VideoPlayer(player: viewModel.player)
.task {
do {
try await viewModel.prepareAndPlay(url: url)
} catch {}
}
}
}
#Preview {
CustomVideoPlayer(
// You can download this video and use the following ffmpeg command to shift the timestamps:
// "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
// ffmpeg -itsoffset 0.066 -i BigBuckBunny.mp4 -c copy video.mp4
// Then copy and paste the video in your project
url: Bundle.main.url(forResource: "video", withExtension: "mp4")!
)
.frame(height: 220)
.background(Color.black)
}
Все следующие API сообщают 0 в качестве времени начала, даже для смещенных видео:
Код: Выделить всё
item.seekableTimeRanges.first [*]AVAssetImageGenerator.requestedTimeToleranceBefore/After [*]CMSampleBufferGetPresentationTimeStamp [*]player.seek(to: .zero)
Вопрос:
Есть ли в AVFoundation надежный способ:
- Получить истинную начальную PTS (метку времени презентации) видеодорожки и
- Искать фактический первый кадр, даже если временные метки файла не начинаются с нуля?
Подробнее здесь: https://stackoverflow.com/questions/797 ... rt-at-zero
Мобильная версия