Sie können dies beispielsweise reproduzieren, indem Sie eine beliebige normale MP4-Datei nehmen und deren Zeitstempel mit FFmpeg verschieben:
Code: Select all
ffmpeg -itsoffset 0.066 -i input.mp4 -c copy output_shifted.mp4
Das kommt tatsächlich sehr häufig bei mit der Kamera aufgenommenen oder neu gemischten Videos vor – sie beginnen aufgrund von Encoder- oder Muxer-Offsets oft nicht bei Null.
Das Problem:
Wenn ich eine solche Datei in AVPlayer lade, kann ich scheinbar nicht danach streben der echte erste Frame.
Alles, was ich versuche, meldet 0 als Startzeit, obwohl FFprobe den ersten PTS deutlich um 0,06 anzeigt.
Hier ist der Code, den ich verwende, um den ersten Frame vor der Wiedergabe zu laden und anzuzeigen:
Code: Select all
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)
}
Alle der folgenden APIs melden 0 als Startzeit, auch für versetzte Videos:
Code: Select all
item.seekableTimeRanges.first [*]AVAssetImageGenerator.requestedTimeToleranceBefore/After [*]CMSampleBufferGetPresentationTimeStamp [*]player.seek(to: .zero)
Frage:
Gibt es in AVFoundation eine zuverlässige Möglichkeit, Folgendes zu tun:
- Den wahren Start-PTS (Präsentations-Zeitstempel) einer Videospur abrufen und
- Suchen Sie nach dem tatsächlichen ersten Frame, auch wenn die Zeitstempel der Datei nicht bei Null beginnen?
Mobile version