ios - 自定义视频大小导致 AVAssetWriter 出现错误

标签 ios objective-c avfoundation avassetwriter avasset

我使用下面的代码渲染了一个带有红色矩形的简单视频。 _CanvasSize = CGSizeMake(320, 200); 一切正常。但是,如果我将大小更改为 _CanvasSize = CGSizeMake(321, 200);(100, 100),视频就会撕裂。 有谁知道为什么以及我应该选择哪个尺寸? (我使用 XCode 7.3.1 iOS 9 SDK)。

NSString *fileNameOut = @"temp.mp4";
NSString *directoryOut = @"tmp/";
NSString *outFile = [NSString stringWithFormat:@"%@%@",directoryOut,fileNameOut];
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@",outFile]];
NSURL *videoTempURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), fileNameOut]];

// WARNING: AVAssetWriter does not overwrite files for us, so remove the destination file if it already exists
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:[videoTempURL path]  error:NULL];


CGSize _CanvasSize;// = CGSizeMake(size.width, size.height);
NSError *error = nil;
NSInteger FPS = 30;
AVAssetWriter* VIDCtrl = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path] fileType:AVFileTypeMPEG4 error:&error];
if (!VIDCtrl || error)
{
    NSLog(@"Can NOT Create Video Writer");
    return;
}

_CanvasSize = CGSizeMake(321, 200);

NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                               AVVideoCodecH264, AVVideoCodecKey,
                               [NSNumber numberWithInt:_CanvasSize.width], AVVideoWidthKey,
                               [NSNumber numberWithInt:_CanvasSize.height], AVVideoHeightKey,
                               nil];

AVAssetWriterInput* writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
                                                                     outputSettings:videoSettings];

AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput
                                                                                                                 sourcePixelBufferAttributes:nil];
NSParameterAssert(writerInput);
NSParameterAssert([VIDCtrl canAddInput:writerInput]);
[VIDCtrl addInput:writerInput];
[VIDCtrl startWriting];
[VIDCtrl startSessionAtSourceTime:kCMTimeZero];
CVPixelBufferRef buffer = NULL;

double ftime = 600.0 / FPS;
double currenttime = 0;
double frametime = 1.0 / FPS;

int i = 0;
while (1)
{
    // Check if the writer is ready for more data, if not, just wait
    if(writerInput.readyForMoreMediaData){
        
        CMTime frameTime = CMTimeMake(ftime, 600);
        // CMTime = Value and Timescale.
        // Timescale = the number of tics per second you want
        // Value is the number of tics
        // For us - each frame we add will be 1/4th of a second
        // Apple recommend 600 tics per second for video because it is a
        // multiple of the standard video rates 24, 30, 60 fps etc.
        CMTime lastTime=CMTimeMake(i*ftime, 600);
        CMTime presentTime=CMTimeAdd(lastTime, frameTime);
        
        if (i == 0) {presentTime = CMTimeMake(0, 600);}
        // This ensures the first frame starts at 0.
        
        buffer = NULL;
        if (i < 30)
        {
            
            NSLog(@"%d %d",i, presentTime.value);
            CGSize sz = _CanvasSize;
            int height = sz.height, width = sz.width;
            
            NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                                     [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                                     nil];
            CVPixelBufferRef pxbuffer = NULL;
            if (!pxbuffer)
            {
                CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width,
                                                      height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options,
                                                      &pxbuffer);
                NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
            }
            
            CVPixelBufferLockBaseAddress(pxbuffer, 0);
            void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
            NSParameterAssert(pxdata != NULL);
            
            NSUInteger bytesPerPixel = 4;
            NSUInteger bytesPerRow = bytesPerPixel * sz.width;
            NSUInteger bitsPerComponent = 8;
            CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
            CGContextRef gc = CGBitmapContextCreate(pxdata, sz.width, sz.height,
                                                    bitsPerComponent, bytesPerRow, colorSpace,
                                                    kCGImageAlphaNoneSkipFirst);
            UIGraphicsPushContext(gc);
            CGContextTranslateCTM(gc, 0, sz.height);
            CGContextScaleCTM(gc, 1.0, -1.0);
            CGContextSetFillColorWithColor(gc, [UIColor whiteColor].CGColor);
            CGContextFillRect(gc, (CGRect){0,0,sz});

            CGContextSetStrokeColorWithColor(gc, [UIColor redColor].CGColor);
            CGContextStrokeRect(gc, CGRectMake(10, 10, 30, 30));
            
            CGColorSpaceRelease(colorSpace);
            CGContextRelease(gc);
            
            CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
            buffer = pxbuffer;
            i++;
        }
        
        currenttime+=frametime;
        
        if (buffer)
        {
            // Give the CGImage to the AVAssetWriter to add to your video
            [adaptor appendPixelBuffer:buffer withPresentationTime:presentTime];
            // CVBufferRelease(buffer);
            CFRelease(buffer);
        }
        else
        {
            //Finish the session:
            // This is important to be done exactly in this order
            [writerInput markAsFinished];
            // WARNING: finishWriting in the solution above is deprecated.
            // You now need to give a completion handler.
            [VIDCtrl finishWritingWithCompletionHandler:^{
                NSLog(@"Finished writing...checking completion status...");
                if (VIDCtrl.status != AVAssetWriterStatusFailed && VIDCtrl.status == AVAssetWriterStatusCompleted)
                {
                    NSLog(@"Video writing succeeded To %@",path);
                } else
                {
                    NSLog(@"Video writing failed: %@", VIDCtrl.error);
                }
                
            }]; // end videoWriter finishWriting Block
            
            CVPixelBufferPoolRelease(adaptor.pixelBufferPool);
            
            NSLog (@"Done");
            break;
        }
    }
}

这是 320 x 200 Canvas : enter image description here

这是 321 x 200 Canvas (甚至 100x100): enter image description here

最佳答案

好的,经过一天的测试。视频的宽度应能被 16 整除。(32、320、144、480、1280、1920 等....)

关于ios - 自定义视频大小导致 AVAssetWriter 出现错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38799826/

相关文章:

javascript - iOS 不更新 JavaScript 文件

objective-c - 避免复制 NSMutableArray 以使用多线程写入进行读取

swift - 声音导致游戏在 swift sprite 套件游戏中滞后?

ios - 在 GPUImage 中应用视频过滤器时播放音频

iphone - AVCapture追加SampleBuffer

ios - 保持 NSUserActivity 向后兼容 Xcode 9

iOS 9 - 在通过 SFSafariViewController 加载的网页底部隐藏工具栏

ios - ACAccountType 始终显示 0 个帐户

ios - 使用多个参数调用 NSString

ios - 场景套件 : how to update SCNGeometrySource