2014-06-19 5 views
1

Я хотел бы экспортировать один UIImage в качестве фильма и сохранять на диск. Я нашел несколько примеров создания фильма из массива UIImage. Самое близкое, что я получил, это использовать этот код: https://github.com/HarrisonJackson/HJImagesToVideo. Прямо передать массив с одним изображением, однако я не уверен, как изменить этот код, чтобы установить желаемую продолжительность фильма. Я попытался изменить CMTime на CMTimeMakeWithSeconds(5, 300), однако экспортированное видео иногда появляется пустым.Экспорт одного UIImage как фильма

+ (void)writeImageAsMovie:(NSArray *)array 
        toPath:(NSString*)path 
        size:(CGSize)size 
         fps:(int)fps 
     animateTransitions:(BOOL)shouldAnimateTransitions 
     withCallbackBlock:(SuccessBlock)callbackBlock 
{ 
    NSLog(@"%@", path); 
    NSError *error = nil; 
    AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path] 
                  fileType:AVFileTypeMPEG4 
                   error:&error]; 
    if (error) { 
     if (callbackBlock) { 
      callbackBlock(NO); 
     } 
     return; 
    } 
    NSParameterAssert(videoWriter); 

    NSDictionary *videoSettings = @{AVVideoCodecKey: AVVideoCodecH264, 
            AVVideoWidthKey: [NSNumber numberWithInt:size.width], 
            AVVideoHeightKey: [NSNumber numberWithInt:size.height]}; 

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

    AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput 
                                sourcePixelBufferAttributes:nil]; 
    NSParameterAssert(writerInput); 
    NSParameterAssert([videoWriter canAddInput:writerInput]); 
    [videoWriter addInput:writerInput]; 

    //Start a session: 
    [videoWriter startWriting]; 
    [videoWriter startSessionAtSourceTime:kCMTimeZero]; 

    CVPixelBufferRef buffer; 
    CVPixelBufferPoolCreatePixelBuffer(NULL, adaptor.pixelBufferPool, &buffer); 

    CMTime presentTime = CMTimeMake(0, fps); 

    int i = 0; 
    while (1) 
    { 

     if(writerInput.readyForMoreMediaData){ 

      presentTime = CMTimeMake(i, fps); 

      if (i >= [array count]) { 
       buffer = NULL; 
      } else { 
       buffer = [HJImagesToVideo pixelBufferFromCGImage:[array[i] CGImage] size:CGSizeMake(480, 320)]; 
      } 

      if (buffer) { 
       //append buffer 

       BOOL appendSuccess = [HJImagesToVideo appendToAdapter:adaptor 
                  pixelBuffer:buffer 
                   atTime:presentTime 
                  withInput:writerInput]; 
       NSAssert(appendSuccess, @"Failed to append"); 

       if (shouldAnimateTransitions && i + 1 < array.count) { 

        //Create time each fade frame is displayed 
        CMTime fadeTime = CMTimeMake(1, fps*TransitionFrameCount); 

        //Add a delay, causing the base image to have more show time before fade begins. 
        for (int b = 0; b < FramesToWaitBeforeTransition; b++) { 
         presentTime = CMTimeAdd(presentTime, fadeTime); 
        } 

        //Adjust fadeFrameCount so that the number and curve of the fade frames and their alpha stay consistant 
        NSInteger framesToFadeCount = TransitionFrameCount - FramesToWaitBeforeTransition; 

        //Apply fade frames 
        for (double j = 1; j < framesToFadeCount; j++) { 

         buffer = [HJImagesToVideo crossFadeImage:[array[i] CGImage] 
                 toImage:[array[i + 1] CGImage] 
                  atSize:CGSizeMake(480, 320) 
                 withAlpha:j/framesToFadeCount]; 

         BOOL appendSuccess = [HJImagesToVideo appendToAdapter:adaptor 
                    pixelBuffer:buffer 
                     atTime:presentTime 
                    withInput:writerInput]; 
         presentTime = CMTimeAdd(presentTime, fadeTime); 

         NSAssert(appendSuccess, @"Failed to append"); 
        } 
       } 

       i++; 
      } else { 

       //Finish the session: 
       [writerInput markAsFinished]; 

       [videoWriter finishWritingWithCompletionHandler:^{ 
        NSLog(@"Successfully closed video writer"); 
        if (videoWriter.status == AVAssetWriterStatusCompleted) { 
         if (callbackBlock) { 
          callbackBlock(YES); 
         } 
        } else { 
         if (callbackBlock) { 
          callbackBlock(NO); 
         } 
        } 
       }]; 

       CVPixelBufferPoolRelease(adaptor.pixelBufferPool); 

       NSLog (@"Done"); 
       break; 
      } 
     } 
    } 
} 

+ (CVPixelBufferRef)pixelBufferFromCGImage:(CGImageRef)image 
             size:(CGSize)imageSize 
{ 
    NSDictionary *options = @{(id)kCVPixelBufferCGImageCompatibilityKey: @YES, 
           (id)kCVPixelBufferCGBitmapContextCompatibilityKey: @YES}; 
    CVPixelBufferRef pxbuffer = NULL; 
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, imageSize.width, 
              imageSize.height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options, 
              &pxbuffer); 
    NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); 

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

    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGContextRef context = CGBitmapContextCreate(pxdata, imageSize.width, 
               imageSize.height, 8, 4*imageSize.width, rgbColorSpace, 
               kCGImageAlphaNoneSkipFirst); 
    NSParameterAssert(context); 

    CGContextDrawImage(context, CGRectMake(0 + (imageSize.width-CGImageGetWidth(image))/2, 
              (imageSize.height-CGImageGetHeight(image))/2, 
              CGImageGetWidth(image), 
              CGImageGetHeight(image)), image); 
    CGColorSpaceRelease(rgbColorSpace); 
    CGContextRelease(context); 

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0); 

    return pxbuffer; 
} 

+ (CVPixelBufferRef)crossFadeImage:(CGImageRef)baseImage 
          toImage:(CGImageRef)fadeInImage 
          atSize:(CGSize)imageSize 
         withAlpha:(CGFloat)alpha 
{ 
    NSDictionary *options = @{(id)kCVPixelBufferCGImageCompatibilityKey: @YES, 
           (id)kCVPixelBufferCGBitmapContextCompatibilityKey: @YES}; 
    CVPixelBufferRef pxbuffer = NULL; 
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, imageSize.width, 
              imageSize.height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options, 
              &pxbuffer); 
    NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); 

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

    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGContextRef context = CGBitmapContextCreate(pxdata, imageSize.width, 
               imageSize.height, 8, 4*imageSize.width, rgbColorSpace, 
               kCGImageAlphaNoneSkipFirst); 
    NSParameterAssert(context); 

    CGRect drawRect = CGRectMake(0 + (imageSize.width-CGImageGetWidth(baseImage))/2, 
           (imageSize.height-CGImageGetHeight(baseImage))/2, 
           CGImageGetWidth(baseImage), 
           CGImageGetHeight(baseImage)); 

    CGContextDrawImage(context, drawRect, baseImage); 

    CGContextBeginTransparencyLayer(context, nil); 
    CGContextSetAlpha(context, alpha); 
    CGContextDrawImage(context, drawRect, fadeInImage); 
    CGContextEndTransparencyLayer(context); 

    CGColorSpaceRelease(rgbColorSpace); 
    CGContextRelease(context); 

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0); 

    return pxbuffer; 
} 

+ (BOOL)appendToAdapter:(AVAssetWriterInputPixelBufferAdaptor*)adaptor 
      pixelBuffer:(CVPixelBufferRef)buffer 
       atTime:(CMTime)presentTime 
       withInput:(AVAssetWriterInput*)writerInput 
{ 
    while (!writerInput.readyForMoreMediaData) { 
     usleep(1); 
    } 

    return [adaptor appendPixelBuffer:buffer withPresentationTime:presentTime]; 
} 

ответ

0

CMTimeMake (0, fps); -> Не следует использовать 0. CMTimeMake (x, y); определяет длину каждого кадра. x/y секунд. Так, например, если вы установили CMTimeMake (1, 10), это означает, что каждое изображение будет отображаться в течение 1/10 секунд. Я думаю, что ваше видео стало пустым, потому что вы установили 0 для x, то есть 0/y секунд, который будет 0, поэтому все кадры будут отображаться в течение 0 секунд ... Если вы хотите отправить точную длину видео, вы может сделать это так:

желаемая длина видео поделена на ваш счет изображения) ... Преобразуйте это в формат x/y, а затем получите то, что вы должны ввести в CMTime.

Если вы хотите, чтобы сделать 10-секундное видео, с 100 кадров он должен выглядеть следующим образом: 10/100 = 0,1 = 1/10 сек/кадр -> CMTimeMake (1,10)

Если вы хотите сделать 30-секундное видео с 1000 кадрами, оно должно выглядеть примерно так: 30/1000 = 0,03 = 3/100 сек/кадр -> CMTimeMake (3,100)

Надеюсь, это помогло :).

Смежные вопросы