2015-05-11 13 views
23

Bu sorunun birkaç kez sorulmuş olduğunu gördüm, ancak bunların hiçbiri herhangi bir yanıt almamış görünüyor.AVFoundation - Bir AVAsset ve çıkış video dosyasını tersine çevir

Gereksinim, video dosyasıyla aynı sıkıştırma, biçim ve kare hızını koruyarak bir video dosyasını (sadece ters olarak oynatmak değil) tersine çevirip çıkarmaktır.

İdeal olarak, çözüm bunu bellekte veya arabellekte yapabilir ve kareleri görüntü dosyalarına (örneğin: AVAssetImageGenerator kullanarak) üretmekten ve sonra yeniden derlemeyi (kaynak yoğun, güvenilir olmayan zamanlama sonuçları, çerçeve içinde değişiklikler) engelleyebilir. Orijinal görüntü kalitesi, vb.).

-

My katkısı: Bu hala çalışmıyorsa, ama en iyi şimdiye kadar denedim :

  • Oku örnek çerçevelerinde CMSampleBufferRef[] dizisi AVAssetReader kullanarak içine.
  • AVAssetWriter'u kullanarak ters sırayla geri yazın.
  • Sorun: Her çerçevenin zamanlaması CMSampleBufferRef'a kaydedilmiş gibi görünüyor, bu yüzden onları geriye eklemeler bile işe yaramayacaktır.
  • Sonraki, her çerçevenin zamanlama bilgilerini ters/ayna çerçevesiyle değiştirmeyi denedim.
  • Sorun: Bu, AVAssetWriter ile bilinmeyen bir hataya neden oluyor.
  • Sonraki Adım: Ben son birkaç gün içinde bu konuda çalıştı 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

Video sıkıştırmanın çalışma şeklinden dolayı bunun mümkün olduğunu sanmıyorum ... anlayışımdan sadece bir anahtar kareden ileri gidebilirsin, ama geriye doğru değil .. anahtar kareleri arasındaki tüm kareleri hesaplamadan – Bastian

+0

@Bastian Ne demek istediğin hakkında biraz bilgi verebilir misin? Dizide saklanan her çerçeve için ham örnek verisi (CMSampleBufferRef) var. –

+0

Bunu okuyan herkese sadece bir FYI. Bunu anladım ve önümüzdeki birkaç gün içinde bir cevap yazacağım. Github ... –

cevap

13

bakmak olacak ve çalışma elde edebildi ediyorum. Burada

kaynak kodu: http://www.andyhin.com/post/5/reverse-video-avfoundation

numuneler/çerçeveleri okuma AVAssetReader kullanır resim/piksel tampon özleri ve ayna çerçevesinin sunum süresi ile ekler.

+1

artı Bu kod sorunları var. Bir manzara veya portre videosu iletirseniz, çıkış yönelimi tamamen dağılır. Düzenli bir iphone yüksek çözünürlüklü video ile bile uğraşmayın, bu kontrol sadece belleği sonsuza ve çökmelere karşı çalıştırır. – Shai

+0

üzerinde komik sonuç için –

+1

@SamB kod bir kavramın kanıtıdır. Aslında sadece tek bir dosya türünü ve yönlendirmeyi destekler (kaynak koduna bakın). Diğer ayarları ve formatları desteklemek istiyorsanız, bunu değiştirmeniz gerekecektir. Ayrıca, büyük dosyaları işliyorsanız, muhtemelen bellekte saklamak istemezsiniz - sadece genel elma çözümünü kullanın ve diske yazın. –

İlgili konular