2015-05-11 12 views
23

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[] usando AVAssetReader.
  • 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]; 
    } 
    
+2

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

+0

@Bastian puoi approfondire un po 'cosa intendi? Ho i dati di esempio non elaborati (CMSampleBufferRef) per ogni frame archiviato in una matrice. –

+0

Solo una FYI a chiunque stia leggendo questo. L'ho capito e pubblicherò una risposta nei prossimi giorni. –

risposta

13

lavorato a questo negli ultimi giorni ed è stato in grado di farlo funzionare.

Codice sorgente qui: http://www.andyhin.com/post/5/reverse-video-avfoundation

Utilizza AVAssetReader per leggere i campioni/frame, estrae il buffer delle immagini/pixel, e poi aggiunge con il tempo di presentazione della cornice dello specchio.

+1

plusone per il risultato esilarante su github ... – Shai

+0

questo codice ha problemi. Se si passa un video orizzontale o verticale, l'orientamento dell'output si incasina. Non disturbarti nemmeno con un normale video ad alta risoluzione di iPhone, questo controllo esegue semplicemente la memoria all'infinito e si blocca. –

+1

@ SamB il codice è una prova di concetto. In realtà supporta solo un singolo tipo di file e un orientamento (dai un'occhiata al codice sorgente). Se si desidera supportare altre impostazioni e formati, è necessario modificarlo. Inoltre, se si elaborano file di grandi dimensioni, probabilmente non si desidera archiviarli in memoria, basta utilizzare la soluzione generica di Apple e scriverli su disco. –

Problemi correlati