2015-05-11 2 views
23

Я видел этот вопрос несколько раз, но ни у кого из них нет рабочих ответов.AVFoundation - Обратный AVAsset и выходной видеофайл

Требование состоит в том, чтобы отменить и вывести видеофайл (а не просто воспроизвести его в обратном порядке), сохраняя в качестве исходного видео одинаковое сжатие, формат и частоту кадров.

В идеале решение могло бы сделать это все в памяти или буфере и не генерировать кадры в файлы изображений (например: AVAssetImageGenerator), а затем перекомпилировать его (ресурсоемкие, ненадежные результаты синхронизации, изменения в кадрах/качество изображения от оригинала и т. д.).

-

Моего вклад: Это все еще не работает, но лучший, что я пытался до сих пор:

  • чтения в выборке кадров в массиве CMSampleBufferRef[] с помощью AVAssetReader.
  • Запишите его в обратном порядке, используя AVAssetWriter.
  • Проблема: Кажется, что синхронизация для каждого кадра сохраняется в CMSampleBufferRef, поэтому даже добавление их назад не будет работать.
  • Далее я попытался изменить временную информацию каждого кадра с помощью обратного/зеркального фрейма.
  • Проблема: Это вызывает неизвестную ошибку с AVAssetWriter.
  • Следующий шаг: Я буду смотреть в 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

Я не думаю, что это возможно из-за того, как работает сжатие видео ... из моего понимания вы можете перейти только с ключевого кадра, но не назад .. без вычисления всех кадров между ключевыми кадрами – Bastian

+0

@Bastian можете ли вы немного рассказать о том, что вы имеете в виду? У меня есть исходные данные образца (CMSampleBufferRef) для каждого кадра, хранящегося в массиве. –

+0

Просто FYI для всех, кто это читает. Я понял это и буду отвечать за ответ в ближайшие дни. –

ответ

13

работал над этим в течение последних нескольких дней, и был в состоянии заставить его работать.

Исходный код здесь: http://www.andyhin.com/post/5/reverse-video-avfoundation

Использует AVAssetReader зачитать образцы/кадры, извлекает буфер изображения/пиксел, а затем добавляет его во время презентации на раме зеркала.

+1

плюс для веселого результата на github ... – Shai

+0

этот код имеет проблемы. Если вы передадите пейзаж или портретное видео, тогда его ориентация на выходе все испортится. Даже не утруждайте себя обычным видео высокого разрешения iphone, этот элемент управления просто запускает память до бесконечности и сбой. –

+1

@SamB код является доказательством концепции. Он фактически поддерживает только один тип файла и ориентацию (посмотрите исходный код). Если вы хотите поддерживать другие настройки и форматы, вам нужно будет его изменить. Кроме того, если вы обрабатываете большие файлы, вы, вероятно, не хотите хранить их в памяти - просто используйте универсальное яблочное решение и запишите его на диск. –

Смежные вопросы