Suchen Sie nach dem tatsächlichen ersten Bild eines Videos, dessen Zeitstempel nicht bei Null beginnen (AVPlayer / AVAssIOS

Programmierung für iOS
Anonymous
 Suchen Sie nach dem tatsächlichen ersten Bild eines Videos, dessen Zeitstempel nicht bei Null beginnen (AVPlayer / AVAss

Post by Anonymous »

Ich arbeite an einer iOS-App, die Videos mit AVPlayer lädt, und bin auf ein häufiges Problem gestoßen: Einige Videos haben Zeitstempel, die nicht bei Null beginnen. Wenn ich also Null versuche, erhalte ich je nach Player-Hintergrund einen schwarzen oder transparenten Rahmen.
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
Jetzt hat das erste Bild einen Zeitstempel von etwa 0,066 Sekunden.
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)
}
Was ich versucht habe:
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)
Egal was ich tue, ich kann anscheinend nicht den wahren ersten Zeitstempel (z. B. 0,066 Sekunden) erreichen oder anstreben.
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?
Jeder Einblick, wie AVFoundation diese Zeiten intern normalisiert oder ausgleicht, wäre ebenfalls willkommen.

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post