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:format,Description];
    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];
}

Ответы на вопрос(1)

Ваш ответ на вопрос