2015-05-11 5 views
23

Ich habe diese Frage ein paar Mal gefragt, aber keiner von ihnen scheint keine funktionierenden Antworten zu haben.AVFoundation - Reverse ein AVAsset und Video-Datei ausgeben

Die Anforderung besteht darin, eine Videodatei umzukehren und auszugeben (nicht nur rückwärts abzuspielen), wobei die gleiche Komprimierung, das gleiche Format und dieselbe Bildrate wie beim Quellvideo beibehalten werden.

Idealerweise wäre die Lösung in der Lage, dies alles im Speicher oder Puffer zu tun und die Bilder in Bilddateien zu erzeugen (zB: AVAssetImageGenerator) und dann neu zu kompilieren (ressourcenintensive, unzuverlässige Timing-Ergebnisse, Framewechsel/Bildqualität vom Original, etc.).

-

Mein Beitrag: Dies ist immer noch nicht funktioniert, aber das Beste, was ich habe bisher versucht:

  • liest in dem Probenrahmen in ein Array von CMSampleBufferRef[]AVAssetReader verwenden.
  • Mit AVAssetWriter in umgekehrter Reihenfolge zurückschreiben.
  • Problem: Scheint wie Timing für jeden Frame in der CMSampleBufferRef gespeichert wird, so dass sogar anfügen sie rückwärts funktioniert nicht.
  • Als nächstes habe ich versucht, die Timing-Informationen von jedem Frame mit Reverse/Spiegelrahmen zu tauschen.
  • Problem: Dies verursacht einen unbekannten Fehler mit AVAssetWriter.
  • Nächster Schritt: Ich werde schauen AVAssetWriterInputPixelBufferAdaptor

    - (AVAsset *)assetByReversingAsset:(AVAsset *)asset { 
        NSURL *tmpFileURL = [NSURL URLWithString:@"/tmp/test.mp4"];  
        NSError *error; 
    
        // initialize the AVAssetReader that will read the input asset track 
        AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:asset error:&error]; 
        AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] lastObject]; 
    
        AVAssetReaderTrackOutput* readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:nil]; 
        [reader addOutput:readerOutput]; 
        [reader startReading]; 
    
        // Read in the samples into an array 
        NSMutableArray *samples = [[NSMutableArray alloc] init]; 
    
        while(1) { 
         CMSampleBufferRef sample = [readerOutput copyNextSampleBuffer]; 
    
         if (sample == NULL) { 
          break; 
         } 
    
         [samples addObject:(__bridge id)sample]; 
         CFRelease(sample); 
        } 
    
        // initialize the the writer that will save to our temporary file. 
        CMFormatDescriptionRef formatDescription = CFBridgingRetain([videoTrack.formatDescriptions lastObject]); 
        AVAssetWriterInput *writerInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:nil sourceFormatHint:formatDescription]; 
        CFRelease(formatDescription); 
    
        AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:tmpFileURL 
                     fileType:AVFileTypeMPEG4 
                     error:&error]; 
        [writerInput setExpectsMediaDataInRealTime:NO]; 
        [writer addInput:writerInput]; 
        [writer startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp((__bridge CMSampleBufferRef)samples[0])]; 
        [writer startWriting]; 
    
    
        // Traverse the sample frames in reverse order 
        for(NSInteger i = samples.count-1; i >= 0; i--) { 
         CMSampleBufferRef sample = (__bridge CMSampleBufferRef)samples[i]; 
    
         // Since the timing information is built into the CMSampleBufferRef 
         // We will need to make a copy of it with new timing info. Will copy 
         // the timing data from the mirror frame at samples[samples.count - i -1] 
    
         CMItemCount numSampleTimingEntries; 
         CMSampleBufferGetSampleTimingInfoArray((__bridge CMSampleBufferRef)samples[samples.count - i -1], 0, nil, &numSampleTimingEntries); 
         CMSampleTimingInfo *timingInfo = malloc(sizeof(CMSampleTimingInfo) * numSampleTimingEntries); 
         CMSampleBufferGetSampleTimingInfoArray((__bridge CMSampleBufferRef)sample, numSampleTimingEntries, timingInfo, &numSampleTimingEntries); 
    
         CMSampleBufferRef sampleWithCorrectTiming; 
         CMSampleBufferCreateCopyWithNewTiming(
                   kCFAllocatorDefault, 
                   sample, 
                   numSampleTimingEntries, 
                   timingInfo, 
                   &sampleWithCorrectTiming); 
    
         if (writerInput.readyForMoreMediaData) { 
          [writerInput appendSampleBuffer:sampleWithCorrectTiming]; 
         } 
    
         CFRelease(sampleWithCorrectTiming); 
         free(timingInfo); 
        } 
    
        [writer finishWriting]; 
    
        return [AVAsset assetWithURL:tmpFileURL]; 
    } 
    
+2

Ich glaube nicht, dass dies möglich ist, weil die Videokompression funktioniert ... von meinem Verständnis aus können Sie nur von einem Keyframe vorwärts gehen, aber nicht rückwärts .. ohne alle Frames zwischen den Keyframes zu berechnen – Bastian

+0

@Bastian Kannst du ein wenig darüber nachdenken, was du meinst? Ich habe die rohen Beispieldaten (CMSampleBufferRef) für jeden Rahmen in einem Array gespeichert. –

+0

Nur ein Vorgeschmack auf jeden, der dies liest. Ich habe es herausgefunden und werde in den nächsten Tagen eine Antwort schreiben. –

Antwort

13

daran gearbeitet in den letzten Tagen und konnte es zum Laufen bringen.

Quellcode hier: http://www.andyhin.com/post/5/reverse-video-avfoundation

Verwendet AVAssetReader die Proben/Rahmen auszulesen, extrahiert das Bild/Pixelpuffer, und dann hängt es mit der Präsentationszeit des Spiegelrahmens.

+1

plusone für das urkomische Ergebnis auf github ... – Shai

+0

dieser Code hat Probleme. Wenn Sie ein Querformat- oder Porträtvideo weitergeben, wird die Ausrichtung der Ausgabe durcheinander gebracht. Machen Sie sich nicht einmal mit einem normalen iPhone-High-Res-Video auseinander, dieses Steuerelement lässt den Speicher einfach bis unendlich laufen und stürzt ab. –

+1

@SamB der Code ist ein Beweis für das Konzept. Es unterstützt nur einen einzigen Dateityp und Ausrichtung (siehe Quellcode). Wenn Sie andere Einstellungen und Formate unterstützen möchten, müssen Sie diese ändern. Wenn Sie große Dateien verarbeiten, möchten Sie sie wahrscheinlich nicht im Speicher ablegen - verwenden Sie einfach die generische Apple-Lösung und schreiben Sie sie auf die Festplatte. –