【问题标题】:ASSETWriterInput for making Video from UIImages on Iphone IssuesAVASSETWriterInput 用于从 Iphone 问题上的图像制作视频
【发布时间】:2015-11-03 23:20:14
【问题描述】:

我尝试以下两种方法将UIImages 像素缓冲区附加到ASSETWriterInput。一切看起来都不错,只是视频文件中没有数据。怎么了?

1 适配器类

AVAssetWriterInputPixelBufferAdaptor * avAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:NULL];

[avAdaptor appendPixelBufferixelBuffer withPresentationTime:CMTimeMake(1, 10)];

2 制作

// Create sample buffer.
CMSampleBufferRef sampleBuffer = NULL;
result = CMSampleBufferCreateForImageBuffer(kCFAllocatorDef ault, pixelBuffer, true, NULL, NULL, videoInfo, &timing, &sampleBuffer);

// Ship out the frame.
NSParameterAssert(CMSampleBufferDataIsReady(sample Buffer));
NSParameterAssert([writerInput isReadyForMoreMediaData]);
BOOL success = [writerInput appendSampleBuffer:sampleBuffer];

【问题讨论】:

    标签: iphone image video avfoundation


    【解决方案1】:

    我发现由于某种原因我需要多次附加缓冲区。这个示例中我制作的测试应用程序中的时间可能不合适,但既然它可以工作,它应该会给你一个好主意。

    + (void)writeImageAsMovie:(UIImage*)image toPath:(NSString*)path size:(CGSize)size duration:(int)duration 
    {
        NSError *error = nil;
        AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:
                                      [NSURL fileURLWithPath:path] fileType:AVFileTypeQuickTimeMovie
                                                                  error:&error];
        NSParameterAssert(videoWriter);
    
        NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                       AVVideoCodecH264, AVVideoCodecKey,
                                       [NSNumber numberWithInt:size.width], AVVideoWidthKey,
                                       [NSNumber numberWithInt:size.height], AVVideoHeightKey,
                                       nil];
        AVAssetWriterInput* writerInput = [[AVAssetWriterInput
                                            assetWriterInputWithMediaType:AVMediaTypeVideo
                                            outputSettings:videoSettings] retain];
    
        AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor
                                                         assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput
                                                         sourcePixelBufferAttributes:nil];
        NSParameterAssert(writerInput);
        NSParameterAssert([videoWriter canAddInput:writerInput]);
        [videoWriter addInput:writerInput];
    
        //Start a session:
        [videoWriter startWriting];
        [videoWriter startSessionAtSourceTime:kCMTimeZero];
    
        //Write samples:
        CVPixelBufferRef buffer = [Utils pixelBufferFromCGImage:image.CGImage size:size];
        [adaptor appendPixelBuffer:buffer withPresentationTime:kCMTimeZero];
        [adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(duration-1, 2)];
    
        //Finish the session:
        [writerInput markAsFinished];
        [videoWriter endSessionAtSourceTime:CMTimeMake(duration, 2)];
        [videoWriter finishWriting];
    }
    

    此方法不是必需的,但在这里用作像素缓冲区源的示例:

    + (CVPixelBufferRef) pixelBufferFromCGImage:(CGImageRef)image size:(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, (CFDictionaryRef) options, 
                              &pxbuffer);
        status=status;//Added to make the stupid compiler not show a stupid warning.
        NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
    
        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, 
                                                     kCGImageAlphaNoneSkipFirst);
        NSParameterAssert(context);
    
        //CGContextTranslateCTM(context, 0, CGImageGetHeight(image));
        //CGContextScaleCTM(context, 1.0, -1.0);//Flip vertically to account for different origin
    
        CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), 
                                             CGImageGetHeight(image)), image);
        CGColorSpaceRelease(rgbColorSpace);
        CGContextRelease(context);
    
        CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
    
        return pxbuffer;
    }
    

    【讨论】:

    • 效果很好。双重命令是错误吗?无论如何,还有任何想法如何向该视频添加音频。我正在使用 AvAudioPlayer。
    • 您可以制作一个 AVMutableComposition 并将您的音频和视频插入到合成中。如果要淡入淡出等,可以使用 AVMutableAudioMix。可以将合成和 audioMix 添加到播放器的 AVPlayerItem 中。
    • 我还发现您必须将缓冲区附加两次——至少,我还没有弄清楚如何在没有这些杂物的情况下使其工作。有人知道为什么这是必要的吗?
    • 如果不实现上述方法,它会工作吗?以及在duration参数中传递什么?
    • @iApple: 在 int..like int duration=10 中获取持续时间; :)
    【解决方案2】:

    这段代码有点问题。结果,它给了我一个扭曲的图像。

    变化:

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

    收件人:

    CGContextRef context = CGBitmapContextCreate(pxdata,
                                                 size.width,
                                                 size.height,
                                                 8,
                                                 CVPixelBufferGetBytesPerRow(pxbuffer),
                                                 rgbColorSpace,
                                                 kCGImageAlphaNoneSkipFirst);
    

    帮助。

    【讨论】:

    • 谢谢谢谢谢谢!我已经为此敲了几个小时。我的代码在 3 倍规模的设备上运行良好,但在其他设备上运行良好。这就是解决方案。
    【解决方案3】:

    等一下,虽然@Peter DeWeese 给出的答案是要遵循的方向,但代码有两个大问题:首先,您需要等待系统准备好添加新媒体,其次,您有一个很大的内存泄漏,因为您需要在将缓冲区附加到视频编写器后释放它。

    这在您的情况下是正确的,但在一般情况下更是如此,您希望在多个帧中循环,如下所示:

    NSInteger i = 0;
    
    for (; i<n; i++) {
    
         image = allImages[i];
    
         CVPixelBufferRef buffer = [self pixelBufferFromCGImage:image.CGImage cropFrame:frame];
    
        // wait for more media data is ready
        while (adaptor.assetWriterInput.readyForMoreMediaData == FALSE) {
            NSDate *maxDate = [NSDate dateWithTimeIntervalSinceNow:0.1];
            [[NSRunLoop currentRunLoop] runUntilDate:maxDate];
        }
        NSLog(@"Panorama: appending frame %ld out of %ld", (long)i, (long)n);
    
        // Append data
        [adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(i, freq)];//kCMTimeZero];
    
    
        // release the buffer
        CVPixelBufferRelease(buffer);
    
    }
    

    【讨论】:

      猜你喜欢
      • 2019-08-10
      • 2015-06-15
      • 1970-01-01
      • 1970-01-01
      • 2011-03-23
      • 2011-05-18
      • 1970-01-01
      • 2018-08-13
      • 1970-01-01
      相关资源
      最近更新 更多