私はこの質問が数回尋ねられるのを見ました、しかしそれらのどれも実用的な答えを持っていないようです。
要件は、ソースビデオと同じ圧縮、フォーマット、およびフレームレートを維持しながら、ビデオファイルを逆にして出力することです(逆に再生するだけではありません)。
理想的には、ソリューションはこれをすべてメモリまたはバッファで実行し、フレームを画像ファイルに生成して(例: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];
}
過去数日間これに取り組み、それを機能させることができました。
ここのソースコード: http://www.andyhin.com/post/5/reverse-video-avfoundation
AVAssetReader
を使用してサンプル/フレームを読み取り、画像/ピクセルバッファーを抽出して、ミラーフレームの表示時間を追加します。