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]; }
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
@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. –
Nur ein Vorgeschmack auf jeden, der dies liest. Ich habe es herausgefunden und werde in den nächsten Tagen eine Antwort schreiben. –