2017-04-10 5 views
1

Ich arbeite an einer Anwendung, die Video wiedergibt und es dem Benutzer ermöglicht, im Video vorwärts und rückwärts zu scrubben. Das Scrubbing muss reibungslos ablaufen, daher schreiben wir das Video immer mit SDAVAssetExportSession mit der Videokomprimierungseigenschaft AVVideoMaxKeyFrameIntervalKey:@1 so um, dass jedes Frame ein Keyframe ist und reibungsloses Rückwärts-Scrubbing ermöglicht. Dies funktioniert hervorragend und bietet eine reibungslose Wiedergabe. Die Anwendung verwendet Video aus einer Vielzahl von Quellen und kann auf Android- oder iOS-Geräten aufgezeichnet und sogar aus dem Internet heruntergeladen und der Anwendung hinzugefügt werden. Daher haben wir ganz unterschiedliche Kodierungen, von denen einige bereits zum Scrubben geeignet sind (jeder Frame ist ein Schlüsselbild). Gibt es eine Möglichkeit, das Keyframe-Intervall einer Videodatei zu erkennen, um unnötige Videoverarbeitung zu vermeiden? Ich habe viele Dokumente von AVFoundation durchgesehen und sehe keinen offensichtlichen Weg, diese Informationen zu erhalten. Danke für jede Hilfe zu diesem Thema.Erkennen des aktuellen Keyframe-Intervalls in AVAsset

Antwort

1

Wenn Sie die Datei schnell analysieren können, ohne die Bilder zu decodieren, indem Sie AVAssetReaderTrackOutput mit Null outputSettings erstellen. Die Frame-Sample-Puffer, auf die Sie stoßen, haben ein Anhang-Array, das ein Wörterbuch mit nützlichen Informationen enthält, einschließlich, ob der Frame von anderen Frames abhängt oder ob andere Frames davon abhängen. Ich würde das ehemalige als einen Keyframe interpretieren, obwohl es mir eine geringe Anzahl (4% Keyframes in einer Datei?) Gibt. Wie auch immer, der Code:

let asset = AVAsset(url: inputUrl) 
let reader = try! AVAssetReader(asset: asset) 

let videoTrack = asset.tracks(withMediaType: AVMediaTypeVideo)[0] 
let trackReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: nil) 

reader.add(trackReaderOutput) 
reader.startReading() 

var numFrames = 0 
var keyFrames = 0 

while true { 
    if let sampleBuffer = trackReaderOutput.copyNextSampleBuffer() { 
     // NB: not every sample buffer corresponds to a frame! 
     if CMSampleBufferGetNumSamples(sampleBuffer) > 0 { 
      numFrames += 1 
      if let attachmentArray = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, false) as? NSArray { 
       let attachment = attachmentArray[0] as! NSDictionary 
       // print("attach on frame \(frame): \(attachment)") 
       if let depends = attachment[kCMSampleAttachmentKey_DependsOnOthers] as? NSNumber { 
        if !depends.boolValue { 
         keyFrames += 1 
        } 
       } 
      } 
     } 
    } else { 
     break 
    } 
} 

print("\(keyFrames) on \(numFrames)") 

N.B. Dies funktioniert nur für lokale Datei-Assets.

p.s. Sie sagen nicht, wie Sie schrubben oder spielen. Ein AVPlayerViewController und ein AVPlayer?

+0

Danke für die Antwort. Ich habe das heute implementiert (objektive c though). Ich verwende eine benutzerdefinierte UIView mit einem AVPlayer und AVPlayerLayer. Dies scheint ziemlich genau zu sein und gibt etwa 96% Keyframes in Videos zurück, die ich für die Verwendung aller Keyframes codiert habe. Viel weniger für Videos mit anderen Komprimierungseinstellungen. Bei dem Scrubber handelt es sich um ein Rad (scrollView), das die Scrollgeschwindigkeit in eine Scrub-Schleife umwandelt, die jeweils um ein Bild schreitet. Ich poste meine Methode als Antwort auf ein Q und ich werde verlinken, wenn ich es tue. Ich werde auch die Objective-c-Version davon veröffentlichen. – johnrechd

+0

Cool! Ich freue mich darauf, es zu sehen. Das ist seltsam an den fehlenden 4%. Ich frage mich, was die Erklärung sein könnte. –

1

Hier ist die Objective-C-Version der gleichen Antwort. Nach der Implementierung und Verwendung von Videos geben Videos mit allen Keyframes ca. 96% Keyframes aus diesem Code zurück. Ich bin nicht sicher warum, also benutze ich diese Nummer als einen bestimmenden Faktor, obwohl ich es genauer haben möchte. Ich schaue auch nur durch die ersten 600 Bilder oder das Ende des Videos (was immer zuerst kommt), da ich kein ganzes 20-minütiges Video durchlesen muss, um diese Entscheidung zu treffen.

+ (BOOL)videoNeedsProcessingForSlomo:(NSURL*)fileUrl { 
    BOOL needsProcessing = YES; 
    AVAsset* anAsset = [AVAsset assetWithURL:fileUrl]; 
    NSError *error; 
    AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:anAsset error:&error]; 
    if (error) { 
     DLog(@"Error:%@", error.localizedDescription); 
     return YES; 
    } 

    AVAssetTrack *videoTrack = [[anAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; 

    AVAssetReaderTrackOutput *trackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:nil]; 
    [assetReader addOutput:trackOutput]; 

    [assetReader startReading]; 

    float numFrames = 0; 
    float keyFrames = 0; 

    while (numFrames < 600) { // If the video is long - only parse through 20 seconds worth. 
     CMSampleBufferRef sampleBuffer = [trackOutput copyNextSampleBuffer]; 
     if (sampleBuffer) { 
      // NB: not every sample buffer corresponds to a frame! 
      if (CMSampleBufferGetNumSamples(sampleBuffer) > 0) { 
       numFrames += 1; 

       NSArray *attachmentArray = ((NSArray*)CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, false)); 
       if (attachmentArray) { 
        NSDictionary *attachment = attachmentArray[0]; 
        NSNumber *depends = attachment[(__bridge NSNumber*)kCMSampleAttachmentKey_DependsOnOthers]; 
        if (depends) { 
         if (depends.boolValue) { 
          keyFrames += 1; 
         } 
        } 
       } 
      } 
     } 
     else { 
      break; 
     } 
    } 

    needsProcessing = keyFrames/numFrames < 0.95f; // If more than 95% of the frames are keyframes - don't decompress. 

    return needsProcessing; 
} 
+0

Entschuldigung - Ich wusste nicht, dass Sie das Ziel c verwenden, die Frage wurde nicht getaggt –

+0

Keine Sorge, spielte nicht viel auf die Sprache für mich. Danke für Ihre Hilfe. Ich hatte etwas Ähnliches und hatte viele Dokumente gelesen, wusste aber nicht, dass der kCMSampleAttachmentKey_DependsOnOthers verwendet werden konnte, um dies zu bestimmen. – johnrechd