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];
}
Ответы
Ответ 1
Работал над этим в течение последних нескольких дней и смог заставить его работать.
Исходный код здесь: http://www.andyhin.com/post/5/reverse-video-avfoundation
Использует AVAssetReader
для считывания сэмплов/кадров, извлекает буфер изображения/пикселя и затем добавляет его со временем представления зеркального кадра.