web-dev-qa-db-ja.com

AVFoundation-AVAssetを反転してビデオファイルを出力します

私はこの質問が数回尋ねられるのを見ました、しかしそれらのどれも実用的な答えを持っていないようです。

要件は、ソースビデオと同じ圧縮、フォーマット、およびフレームレートを維持しながら、ビデオファイルを逆にして出力することです(逆に再生するだけではありません)。

理想的には、ソリューションはこれをすべてメモリまたはバッファで実行し、フレームを画像ファイルに生成して(例:AVAssetImageGeneratorを使用)、それを再コンパイルすることを回避できます(リソースを大量に消費し、信頼性の低いタイミング結果、フレームの変更) /オリジナルからの画質など)。

-

私の貢献:これはまだ機能していませんが、これまでに試した中で最高のものです:

  • AVAssetReaderを使用して、サンプルフレームをCMSampleBufferRef[]の配列に読み込みます。
  • 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];
    }
    
24
Andy Hin

過去数日間これに取り組み、それを機能させることができました。

ここのソースコード: http://www.andyhin.com/post/5/reverse-video-avfoundation

AVAssetReaderを使用してサンプル/フレームを読み取り、画像/ピクセルバッファーを抽出して、ミラーフレームの表示時間を追加します。

16
Andy Hin