ios - 将图像转换为视频的 ios FPS 错误

标签 ios iphone video avfoundation avassetwriter

大家好,这里需要快速帮助;我试图使用在线教程将图像转换为视频。该过程在 iPhone/i 设备和 QuickTime 上完成并正确播放,但是在任何其他播放器(例如 VLC Realplayer、网络播放器等)上播放时,它无法播放。查看生成的最终文件后,我可以看到视频的 fps 范围在 0 到 0.16 之间(理想情况下应该在 24-30fps 之间)。任何人都可以建议一种修复 fps 的方法。我使用的代码如下:

+ (void)createVideoWithImages:(NSArray <NSString *> *)images withDuration:(long int)duration completion:(void (^) (BOOL success, NSString *output, NSError *error))completion
{
    __block BOOL success                = NO;
    __block NSString *videoOutputPath   = [ViewController newfNameWithPath];
    NSError *error                      = nil;

    double width                    = 0;
    double height                   = 0;
    CGSize imageSize                = CGSizeZero;
    NSUInteger framesPerSecond      = 30;
    NSMutableArray *imageArray      = [[NSMutableArray alloc] initWithCapacity:images.count];

    for (NSUInteger i = 0; i < [images count]; i++)
    {
        NSString *imagePath = [images objectAtIndex:i];
        [imageArray addObject:[UIImage imageWithContentsOfFile:imagePath]];

        UIImage *newImage = [ViewController createImage:((UIImage *)[imageArray objectAtIndex:i])];

        UIImage *img = [ViewController imageOrientation:newImage];
        [imageArray replaceObjectAtIndex:i withObject:img];

        width   = (width > ((UIImage *)[imageArray lastObject]).size.width) ? width : ((UIImage *)[imageArray lastObject]).size.width;
        height  = (height > ((UIImage *)[imageArray lastObject]).size.height) ? height : ((UIImage *)[imageArray lastObject]).size.height;
    }

    imageSize  = CGSizeMake(1280, 720);

    AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[ViewController getURL:videoOutputPath] fileType:AVFileTypeQuickTimeMovie error:&error];
    NSParameterAssert(videoWriter);

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

    NSDictionary *aspectRatioSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                         [NSNumber numberWithInt:3], AVVideoPixelAspectRatioHorizontalSpacingKey,
                                         [NSNumber numberWithInt:3],AVVideoPixelAspectRatioVerticalSpacingKey,
                                         nil];

    NSDictionary *codecSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                   [NSNumber numberWithInt:960000], AVVideoAverageBitRateKey,
                                   [NSNumber numberWithInt:1],AVVideoMaxKeyFrameIntervalKey,
                                   videoCleanApertureSettings, AVVideoCleanApertureKey,
                                   aspectRatioSettings, AVVideoPixelAspectRatioKey,
                                   //AVVideoProfileLevelH264Main30, AVVideoProfileLevelKey,
                                   nil];

    NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                   AVVideoCodecH264, AVVideoCodecKey,
                                   codecSettings,AVVideoCompressionPropertiesKey,
                                   [NSNumber numberWithInt: 1280], AVVideoWidthKey,
                                   [NSNumber numberWithInt: 720], AVVideoHeightKey,
                                   nil];


    AVAssetWriterInput* videoWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
    AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:videoWriterInput sourcePixelBufferAttributes:nil];

    NSParameterAssert(videoWriterInput);
    NSParameterAssert([videoWriter canAddInput:videoWriterInput]);
    videoWriterInput.expectsMediaDataInRealTime = YES;
    [videoWriter addInput:videoWriterInput];


    videoWriterInput.transform = CGAffineTransformMakeRotation(M_PI_2 * 2);

    if (![videoWriter startWriting])
    {
        completion(success, nil, videoWriter.error);
    }

    [videoWriter startSessionAtSourceTime:kCMTimeZero];

    CVPixelBufferRef buffer         = NULL;
    int frameCount                  = 0;
    double numberOfSecondsPerFrame  = duration / [imageArray count];
    double frameDuration            = framesPerSecond * numberOfSecondsPerFrame;

    for(UIImage *image in imageArray)
    {
        buffer                      = [ViewController pixelBufferFromCGImage:[image CGImage] withSize:imageSize];
        BOOL completeWitnNoError    = NO;
        int counter                 = 0;

        while (!completeWitnNoError && counter < 30)
        {

            NSLog(@"writing image: %ld at frame: %d",(long)frameCount, counter);
            if (adaptor.assetWriterInput.readyForMoreMediaData)
            {

                CMTime frameTime        = CMTimeMake(frameCount*frameDuration, (int32_t)framesPerSecond);
                CMTimeShow(frameTime);
                completeWitnNoError     = [adaptor appendPixelBuffer:buffer withPresentationTime:frameTime];

                if(!completeWitnNoError)
                {
                    if(buffer)
                        CVPixelBufferRelease(buffer);

                    completion(!completion, nil, videoWriter.error);
                }
            }
            else
            {
                if(buffer)
                    CVPixelBufferRelease(buffer);

                NSLog(@"adaptor not ready %d, %d\n", frameCount, counter);
                [NSThread sleepForTimeInterval:0.1];
            }

            counter++;
        }

        if(buffer)
            CVPixelBufferRelease(buffer);

        if (!completeWitnNoError)
        {
            NSDictionary *userInfo = @{
                                       NSLocalizedDescriptionKey:               NSLocalizedString(@"Error\n", nil),
                                       NSLocalizedFailureReasonErrorKey:        NSLocalizedString(@"Images to video writing failed ", nil),
                                       NSLocalizedRecoverySuggestionErrorKey:   NSLocalizedString(@"Images to video writing failed ", nil)
                                       };

            NSError *error = [NSError errorWithDomain:@"Images to video error" code: -1005 userInfo:userInfo];
            completion(!completion, nil, error);
        }

        frameCount++;
    }

    [videoWriterInput markAsFinished];
    [videoWriter finishWritingWithCompletionHandler:^{

        switch ([videoWriter status])
        {
            case AVAssetReaderStatusReading:
                break;

            case AVAssetReaderStatusCompleted:
            {
                NSLog(@"IMAGES TO VIDEO WRITE SUCCESS");
                success = !success;
                completion(success, videoOutputPath, videoWriter.error);
                break;
            }
            case AVAssetReaderStatusCancelled:
            {
                NSLog(@"IMAGES TO VIDEO WRITE FAILURE");
                completion(success, nil, videoWriter.error);
                [videoWriter cancelWriting];
                break;
            }

            case AVAssetReaderStatusFailed:
            {
                NSLog(@"IMAGES TO VIDEO WRITE FAILURE");
                completion(success, nil, videoWriter.error);
                [videoWriter cancelWriting];
                break;
            }

            case AVAssetReaderStatusUnknown:
            {
                NSLog(@"IMAGES TO VIDEO WRITE FAILURE");
                completion(success, nil, videoWriter.error);
                [videoWriter cancelWriting];
                break;
            }
        }
    }];

    return;
}

和缓冲区:

+ (CVPixelBufferRef) pixelBufferFromCGImage:(CGImageRef)image withSize:(CGSize)size
{

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                             nil];

    CVPixelBufferRef pxbuffer = NULL;
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault,
                                          size.width,
                                          size.height,
                                          kCVPixelFormatType_32ARGB,
                                          (__bridge CFDictionaryRef) options,
                                          &pxbuffer);

    NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);

    if (status != kCVReturnSuccess)
    {
        NSString *description  = [NSString stringWithFormat:@"Failed to create pixel buffer."];
        NSDictionary *userInfo = @{
                                   NSLocalizedDescriptionKey:               NSLocalizedString(description, nil),
                                   NSLocalizedFailureReasonErrorKey:        NSLocalizedString(@"Could create buffer", nil),
                                   NSLocalizedRecoverySuggestionErrorKey:   NSLocalizedString(@"Try again", nil)
                                   };

        NSError *error = [NSError errorWithDomain:@"CVPixelBufferRef" code: -1005 userInfo:userInfo];
        NSLog(@"ALERT: %@", [error localizedDescription]);
    }

    CVPixelBufferLockBaseAddress(pxbuffer, 0);
    void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
    NSParameterAssert(pxdata != NULL);

    CGColorSpaceRef rgbColorSpace   = CGColorSpaceCreateDeviceRGB();
    CGContextRef context            = CGBitmapContextCreate(pxdata, size.width,
                                                            size.height, 8, 4*size.width, rgbColorSpace,
                                                            kCGImageAlphaPremultipliedFirst);


    CGContextConcatCTM(context, CGAffineTransformMakeRotation(0));
    CGContextDrawImage(context, CGRectMake((size.width/(CGImageGetWidth(image) / 2)), (size.height/(CGImageGetWidth(image) / 2)), 1280, 720), image);

    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);
    CVPixelBufferUnlockBaseAddress(pxbuffer, 0);

    return pxbuffer;
}

有什么建议吗?

最佳答案

尝试使用我以前使用过的 CEMovieMaker 并且工作起来很有魅力,因为我认为你的问题出在 videoCodec

https://github.com/cameronehrlich/CEMovieMaker

    @interface ViewController ()

@property (nonatomic, strong) CEMovieMaker *movieMaker;

@end

- (IBAction)process:(id)sender
{
    NSMutableArray *frames = [[NSMutableArray alloc] init];

    UIImage *icon1 = [UIImage imageNamed:@"icon1"];
    UIImage *icon2 = [UIImage imageNamed:@"icon2"];
    UIImage *icon3 = [UIImage imageNamed:@"icon3"];

    NSDictionary *settings = [CEMovieMaker videoSettingsWithCodec:AVVideoCodecH264 withHeight:icon1.size.width andWidth:icon1.size.height];
    self.movieMaker = [[CEMovieMaker alloc] initWithSettings:settings];
    for (NSInteger i = 0; i < 10; i++) {
        [frames addObject:icon1];
        [frames addObject:icon2];
        [frames addObject:icon3];
    }

    [self.movieMaker createMovieFromImages:[frames copy] withCompletion:^(BOOL success, NSURL *fileURL){
        if (success) {
            [self viewMovieAtUrl:fileURL];
        }
    }];
}

- (void)viewMovieAtUrl:(NSURL *)fileURL
{
    MPMoviePlayerViewController *playerController = [[MPMoviePlayerViewController alloc] initWithContentURL:fileURL];
    [playerController.view setFrame:self.view.bounds];
    [self presentMoviePlayerViewControllerAnimated:playerController];
    [playerController.moviePlayer prepareToPlay];
    [playerController.moviePlayer play];
    [self.view addSubview:playerController.view];
}

关于ios - 将图像转换为视频的 ios FPS 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37532222/

相关文章:

ios - 存储大字符串数组时编译器错误的解决方法

ios - UITableviewController 与可编辑 UIWebView 的 contentInset 混淆

video - FFmpeg 编码视频并添加字幕不适用于 map

android - 如何在 Android 中使用 GPUImage 在视频上添加滤镜?

objective-c - App Store - Method Swizzling 合法性

ios - 在 Xcode Beta 6 之后快速使用 NSPredicate 崩溃

ios - 具有动态内容的分页滚动

iphone - Birdhouse 应用程序的自定义 URL

iphone - 在 UIScrollView 中显示 UIView 的子区域

python - 在 VideoCapture 之后读取图像时 OpenCV Python 挂起