gpt4 book ai didi

ios - 结合视频文件与 AVAssetWriter

转载 作者:行者123 更新时间:2023-11-29 12:46:45 25 4
gpt4 key购买 nike

我正在尝试将多个视频文件合并到一个具有特定编解码器设置的文件中。我曾经为此使用 AVAssetExportSession,但现在我需要比 AVAssetExportSession 提供更多的编解码器控制。

下面我发布了处理视频文件组合的 createFinalVideo: 函数。

我采用的方法是尝试使用 AVAssetWriter 写入同一个文件,同时在应该附加下一个视频的位置简单地启动 session 。我知道这不会起作用,因为 AVAssetWriter 显然不允许这种行为。

以前,我在 for 循环之外定义了 AVAssetWriter,并且我试图为每个视频文件添加一个新输入(for 循环的每次传递)。但是,AVAssetWriter 似乎不允许在 [AVAssetWriter startWriting] 被调用后添加新输入。

我的问题是如何以正确的方式做我想做的事情?

/**
* Final video creation. Merges audio-only and video-only files.
**/
-(void)createFinalVideo:(id)args
{
ENSURE_SINGLE_ARG(args, NSDictionary);

// presentation id
NSString * presID = [args objectForKey:@"presID"];

// array of video paths
NSArray * videoPathsArray = [args objectForKey:@"videoPaths"];

videoSuccessCallback = [args objectForKey:@"videoSuccess"];
videoCancelCallback = [args objectForKey:@"videoCancel"];
videoErrorCallback = [args objectForKey:@"videoError"];

NSError * error = nil;

NSFileManager * fileMgr = [NSFileManager defaultManager];
NSString * bundleDirectory = [[NSBundle mainBundle] bundlePath];
NSString * documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];

/*********************************************************************/
/* BEGIN: merge all videos into a final MP4 */
/*********************************************************************/

// create the final video output file as MP4 file
NSString * finalOutputFilePath = [NSString stringWithFormat:@"%@/%@/final_video.mp4", documentsDirectory, presID];
NSURL * finalOutputFileUrl = [NSURL fileURLWithPath:finalOutputFilePath];

// delete file if it exists
if ([fileMgr fileExistsAtPath:finalOutputFilePath]) {
[fileMgr removeItemAtPath:finalOutputFilePath error:nil];
}

float renderWidth = 640, renderHeight = 480;

NSDictionary *videoCleanApertureSettings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:renderWidth], AVVideoCleanApertureWidthKey,
[NSNumber numberWithInt:renderHeight], AVVideoCleanApertureHeightKey,
[NSNumber numberWithInt:10], AVVideoCleanApertureHorizontalOffsetKey,
[NSNumber numberWithInt:10], AVVideoCleanApertureVerticalOffsetKey,
nil];

NSDictionary *codecSettings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:1960000], AVVideoAverageBitRateKey,
[NSNumber numberWithInt:24],AVVideoMaxKeyFrameIntervalKey,
videoCleanApertureSettings, AVVideoCleanApertureKey,
AVVideoProfileLevelH264Baseline30, AVVideoProfileLevelKey,
nil];

NSDictionary *videoCompressionSettings = [NSDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecH264, AVVideoCodecKey,
codecSettings,AVVideoCompressionPropertiesKey,
[NSNumber numberWithInt:renderWidth], AVVideoWidthKey,
[NSNumber numberWithInt:renderHeight], AVVideoHeightKey,
AVVideoScalingModeResizeAspect, AVVideoScalingModeKey,
nil];

NSError *aerror = nil;

// next start time for adding to the compositions
CMTime nextStartTime = kCMTimeZero;

// loop through the video paths and add videos to the composition
for (NSString * path in videoPathsArray) {
// wait for each video to finish writing before continuing
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

// create video writer
AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:finalOutputFileUrl fileType:AVFileTypeQuickTimeMovie error:nil];
NSParameterAssert(videoWriter);

NSLog(@"at the top of the for loop");
NSLog(@"%@", path);

AVAssetWriterInput* videoWriterInput = [AVAssetWriterInput
assetWriterInputWithMediaType:AVMediaTypeVideo
outputSettings:videoCompressionSettings];
NSParameterAssert(videoWriterInput);
NSParameterAssert([videoWriter canAddInput:videoWriterInput]);
videoWriterInput.expectsMediaDataInRealTime = YES;
[videoWriter addInput:videoWriterInput];

AVAssetWriterInput* audioWriterInput = [AVAssetWriterInput
assetWriterInputWithMediaType:AVMediaTypeAudio
outputSettings:nil];
NSParameterAssert(audioWriterInput);
NSParameterAssert([videoWriter canAddInput:audioWriterInput]);
audioWriterInput.expectsMediaDataInRealTime = NO;
[videoWriter addInput:audioWriterInput];

[videoWriter startWriting];

// video setup
AVAsset *avAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:path] options:nil];
AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:avAsset error:&aerror];
AVAssetTrack *videoTrack = [[avAsset tracksWithMediaType:AVMediaTypeVideo]objectAtIndex:0];

CMTime videoDuration = avAsset.duration;
// Wait until the duration is actually available
int durationAttempts = 5;
while(CMTimeGetSeconds(videoDuration) == 0 && durationAttempts > 0) {
durationAttempts--;
[NSThread sleepForTimeInterval:0.3];
videoDuration = avAsset.duration;
}
NSLog(@"[INFO] MODULE-VIDUTILS video duration in secs: %f", CMTimeGetSeconds(videoDuration));

//videoWriterInput.transform = videoTrack.preferredTransform;
NSDictionary *videoOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey];
AVAssetReaderTrackOutput *asset_reader_output = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack outputSettings:videoOptions];
[reader addOutput:asset_reader_output];

//audio setup
AVAssetReader *audioReader = [AVAssetReader assetReaderWithAsset:avAsset error:nil];
AVAssetTrack* audioTrack = [[avAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
AVAssetReaderOutput *readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:nil];
[audioReader addOutput:readerOutput];

NSLog(@"startSessionAtSourceTime: %f", CMTimeGetSeconds(nextStartTime));
[videoWriter startSessionAtSourceTime:nextStartTime];
// set next start time
nextStartTime = CMTimeAdd(nextStartTime, videoDuration);
[reader startReading];

dispatch_queue_t _processingQueue = dispatch_queue_create("AVAssetWriterQueue", DISPATCH_QUEUE_SERIAL);
[videoWriterInput requestMediaDataWhenReadyOnQueue:_processingQueue usingBlock:^{
while ([videoWriterInput isReadyForMoreMediaData]) {
CMSampleBufferRef sampleBuffer;
if ([reader status] == AVAssetReaderStatusReading &&
(sampleBuffer = [asset_reader_output copyNextSampleBuffer])) {

BOOL result = [videoWriterInput appendSampleBuffer:sampleBuffer];
CFRelease(sampleBuffer);

if (!result) {
[reader cancelReading];
NSLog(@"NO RESULT");
NSLog (@"[INFO] MODULE-VIDUTILS createFinalVideo AVAssetWriterInputStatusFailed: %@", videoWriter.error);
if (videoErrorCallback != nil) {
[self _fireEventToListener:@"videoError" withObject:nil listener:videoErrorCallback thisObject:nil];
}
return;
break;
}
} else {
[videoWriterInput markAsFinished];

switch ([reader status]) {
case AVAssetReaderStatusReading:
// the reader has more for other tracks, even if this one is done
break;

case AVAssetReaderStatusCompleted:
[audioReader startReading];
[videoWriter startSessionAtSourceTime:nextStartTime];
NSLog(@"Request");
NSLog(@"Asset Writer ready :%d", audioWriterInput.readyForMoreMediaData);
while (audioWriterInput.readyForMoreMediaData) {
CMSampleBufferRef nextBuffer;
if ([audioReader status] == AVAssetReaderStatusReading && (nextBuffer = [readerOutput copyNextSampleBuffer])) {
NSLog(@"Ready");
if (nextBuffer) {
NSLog(@"NextBuffer");
[audioWriterInput appendSampleBuffer:nextBuffer];
}
} else {
[audioWriterInput markAsFinished];

//dictionary to hold duration
if ([audioReader status] == AVAssetReaderStatusCompleted) {
NSLog (@"[INFO] MODULE-VIDUTILS createFinalVideo AVAssetReaderStatusCompleted");
[videoWriter finishWritingWithCompletionHandler:^{
switch([videoWriter status]) {
case AVAssetWriterStatusCompleted:
NSLog (@"[INFO] MODULE-VIDUTILS createFinalVideo AVAssetWriterStatusCompleted");
dispatch_semaphore_signal(semaphore);
break;

case AVAssetWriterStatusCancelled:
NSLog (@"[INFO] MODULE-VIDUTILS createFinalVideo AVAssetWriterStatusCancelled");
if (videoSuccessCallback != nil) {
[self _fireEventToListener:@"videoCancel" withObject:nil listener:videoCancelCallback thisObject:nil];
}
return;
break;

case AVAssetWriterStatusFailed:
NSLog (@"[INFO] MODULE-VIDUTILS createFinalVideo AVAssetWriterStatusFailed");
if (videoSuccessCallback != nil) {
[self _fireEventToListener:@"videoError" withObject:nil listener:videoErrorCallback thisObject:nil];
}
return;
break;
}
}];
break;
}
}
}
break;

case AVAssetReaderStatusFailed:
NSLog (@"[INFO] MODULE-VIDUTILS createFinalVideo AVAssetReaderStatusFailed, @%", reader.error);
if (videoSuccessCallback != nil) {
[self _fireEventToListener:@"videoError" withObject:nil listener:videoErrorCallback thisObject:nil];
}
[videoWriter cancelWriting];
return;
break;
}
break;
}
}
}];
// wait for the writing to finish
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"Write Ended");
}
NSLog(@"got here -- should have waited for all videos to complete first");

// call success if we got here
if (videoSuccessCallback != nil) {
[self _fireEventToListener:@"videoSuccess" withObject:nil listener:videoSuccessCallback thisObject:nil];
}
}

最佳答案

我找到了一个名为 SDAVAssetExportSessionAVAssetExportSession 的替代品允许您指定设置而不是使用预设。

关于ios - 结合视频文件与 AVAssetWriter,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23503039/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com