Ho visto questa domanda alcune volte, ma nessuna sembra avere risposte funzionanti.AVFoundation: inversione di un file AVA e uscita del file video
L'esigenza è di invertire e generare un file video (non solo riprodurlo al contrario) mantenendo la stessa compressione, formato e frequenza fotogrammi del video sorgente. Idealmente, la soluzione sarebbe in grado di fare tutto questo in memoria o nel buffer ed evitare di generare i frame in file di immagini (ad es: usando AVAssetImageGenerator
) e poi ricompilarlo (risorse impegnative, risultati di timing inaffidabili, cambiamenti nel frame/qualità dell'immagine dall'originale, ecc.).
-
mio contributo: Questo non è ancora funzionante, ma il migliore che ho provato finora:
- Leggi nei fotogrammi di esempio in un array di
CMSampleBufferRef[]
usandoAVAssetReader
. - Scriverlo in ordine inverso utilizzando
AVAssetWriter
. - Problema: sembra che il tempo per ogni frame sia stato salvato nel
CMSampleBufferRef
quindi anche l'accodamento all'indietro non funzionerà. - Successivamente, ho provato a scambiare le informazioni di temporizzazione di ciascun fotogramma con fotogramma inverso/specchio.
- Problema: questo causa un errore sconosciuto con
AVAssetWriter
. Passo successivo: ho intenzione di guardare in
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]; }
Non penso che ciò sia possibile a causa del modo in cui funziona la compressione video ... dal mio punto di vista è possibile andare avanti da un fotogramma chiave, ma non indietro ... senza calcolare tutti i fotogrammi tra i fotogrammi chiave – Bastian
@Bastian puoi approfondire un po 'cosa intendi? Ho i dati di esempio non elaborati (CMSampleBufferRef) per ogni frame archiviato in una matrice. –
Solo una FYI a chiunque stia leggendo questo. L'ho capito e pubblicherò una risposta nei prossimi giorni. –