Я использую библиотеку «OWVideoProcessor» для резки фрагментов видеозаписи в реальном времени. Видео отлично работает на любых устройствах Apple, но когда я играю в браузере (Dropbox), у него есть несколько секунд, добавленных спереди, и звук также отсутствует в секундах, добавленных спереди. Здесь вы можете увидеть примеры этого видео: https://www.dropbox.com/s/2vyhqlfgfh6gzlk/file32167%281%29.mp4?dl=0 Если вы загружаете видео на устройство Apple, видеоролик имеет 20 секунд. если вы играете в браузере, у него есть 29 секунд.Создание видеофайла AVFileTypeMPEG4 с AVAssetExportSession и AVMutableComposition
Это код для сшивания видео:
- (void)stitchVideoWithDestinationPath:(NSString *)destinationPath completion:(void(^)(NSError *error))completion {
[self.exportSession cancelExport];
NSLog(@"export started to path: %@", destinationPath);
AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init];
AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
CMTime startTime = kCMTimeZero;
int lastIndex = self.segmentStart + self.segmentCount - 1;
NSLog(@"Stitching segments in interval: [%d - %d]", self.segmentStart, lastIndex);
for (int i = self.segmentCount - 5; i < lastIndex; i++) {
CMTimeShow(startTime);
NSURL *url = [OWUtilities urlForRecordingSegmentCount:i basePath:self.basePath];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:@{AVURLAssetPreferPreciseDurationAndTimingKey: @(YES)}];
NSAssert(asset, @"Invalid asset at: %@", url);
BOOL hasAllTracks = [[asset tracks] count] >= 2;
if (hasAllTracks) {
CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
AVAssetTrack *track = nil;
track = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
[videoTrack insertTimeRange:timeRange ofTrack:track atTime:startTime error:nil];
track = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
[audioTrack insertTimeRange:timeRange ofTrack:track atTime:startTime error:nil];
startTime = CMTimeAdd(startTime, asset.duration);
}
}
NSTimeInterval segmentsDuration = CMTimeGetSeconds(startTime);
NSLog(@"Total segments duration: %.2f", segmentsDuration);
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetPassthrough];
if (![[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
NSArray *filePathsArray = [NSArray new];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
filePathsArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:documentsDirectory error:nil];
documentsDirectory = [documentsDirectory stringByAppendingString:@"/uploads/"];
documentsDirectory = [documentsDirectory stringByAppendingString:[destinationPath lastPathComponent]];
if([[NSFileManager defaultManager] fileExistsAtPath:documentsDirectory]) {
destinationPath = documentsDirectory;
}
}
exporter.outputURL = [NSURL fileURLWithPath:destinationPath];
exporter.outputFileType = AVFileTypeMPEG4;
BOOL trimRange = (segmentsDuration > self.outputSegmentDuration);
if (trimRange) {
CMTime duration = CMTimeMakeWithSeconds(self.outputSegmentDuration, startTime.timescale);
NSTimeInterval startInterval = segmentsDuration - self.outputSegmentDuration;
CMTime start = CMTimeMakeWithSeconds(startInterval, startTime.timescale);
exporter.timeRange = CMTimeRangeMake(start, duration);
NSLog(@"Exporting segment:");
CMTimeRangeShow(exporter.timeRange);
NSTimeInterval segmentsDuration2 = CMTimeGetSeconds(duration);
NSLog(@"Total segments duration: %.2f", segmentsDuration2);
}
@weakify(self, exporter);
[exporter exportAsynchronouslyWithCompletionHandler:^{
@strongify(self, exporter);
NSLog(@"error: %@", exporter.error);
if (completion && (exporter.status != AVAssetExportSessionStatusCancelled)) {
completion(exporter.error);
} else {
completion(nil);
}
if (self.exportSession == exporter) {
self.exportSession = nil;
}
}];
self.exportSession = exporter;
}