4

我正在使用下面的代码获取一系列 UIImage 并将它们转换为 mov。出于某种原因,我不断在底部或右侧获得绿色边框,具体取决于它是风景照片还是肖像照片。图像为 215x320。

如何去除绿色边框?有没有更好的方法从 UIImages 创建 .mov?

- (void)createMov
{
  UIImage *first = [self.videoFrames objectAtIndex:0];

  CGSize frameSize = first.size;

  NSError *error = nil;
  AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:
                                [NSURL fileURLWithPath:fileName] fileType:AVFileTypeQuickTimeMovie
                                                        error:&error];

  if (error) {
    NSLog(@"error creating AssetWriter: %@",[error description]);
  }

  int numPixels = first.size.width * first.size.height;
  int bitsPerSecond = numPixels * 11.04;


  NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                 AVVideoCodecH264, AVVideoCodecKey,
                                 [NSNumber numberWithInt:frameSize.width], AVVideoWidthKey,
                                 [NSNumber numberWithInt:frameSize.height], AVVideoHeightKey,
                                 [NSDictionary dictionaryWithObjectsAndKeys:
                                  [NSNumber numberWithInteger:bitsPerSecond], AVVideoAverageBitRateKey,
                                  [NSNumber numberWithInteger:30], AVVideoMaxKeyFrameIntervalKey,
                                  nil], AVVideoCompressionPropertiesKey,
                                 nil];

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

  NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init];
  [attributes setObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32ARGB] forKey:   (NSString*)kCVPixelBufferPixelFormatTypeKey];
  [attributes setObject:[NSNumber numberWithUnsignedInt:frameSize.width] forKey:(NSString*)kCVPixelBufferWidthKey];
  [attributes setObject:[NSNumber numberWithUnsignedInt:frameSize.height] forKey:(NSString*)kCVPixelBufferHeightKey];

  AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor
                                              assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput
                                               sourcePixelBufferAttributes:attributes];

  [videoWriter addInput:writerInput];

  // fixes all errors
  writerInput.expectsMediaDataInRealTime = YES;

  //Start a session:
  BOOL start = [videoWriter startWriting];
  NSLog(@"Session started? %d", start);
  [videoWriter startSessionAtSourceTime:kCMTimeZero];


  // Writing.
  CVPixelBufferRef buffer = NULL;
  buffer = [self pixelBufferFromCGImage:[first CGImage]];
  BOOL result = [adaptor appendPixelBuffer:buffer withPresentationTime:kCMTimeZero];

  if (result == NO) //failes on 3GS, but works on iphone 4
    NSLog(@"failed to append buffer");

  if(buffer)
    CVBufferRelease(buffer);

  [NSThread sleepForTimeInterval:0.05];

  int fps = [Utils frameRate];

  int i = 0;
  for (UIImage *imgFrame in self.videoFrames) {
    i = [self addFrame:adaptor videoWriter:videoWriter buffer:buffer imgFrame:imgFrame i:i fps:fps];
  }

  //Finish the session:
  [writerInput markAsFinished];
  [videoWriter finishWriting];
  CVPixelBufferPoolRelease(adaptor.pixelBufferPool);
  [videoWriter release];
  [writerInput release];
}


- (int)addFrame:(AVAssetWriterInputPixelBufferAdaptor *)adaptor videoWriter:(AVAssetWriter *)videoWriter buffer:(CVPixelBufferRef)buffer imgFrame:(UIImage *)imgFrame i:(int)i fps:    (float)fps
{
  if (adaptor.assetWriterInput.readyForMoreMediaData) {

    i++;
    NSLog(@"inside for loop %d",i);
    CMTime frameTime = CMTimeMake(1, fps);
    CMTime lastTime=CMTimeMake(i, fps);
    CMTime presentTime=CMTimeAdd(lastTime, frameTime);

    buffer = [self pixelBufferFromCGImage:[imgFrame CGImage]];
    BOOL result = [adaptor appendPixelBuffer:buffer withPresentationTime:presentTime];

    if (result == NO) { //fails on 3GS, but works on iphone 4
      NSLog(@"failed to append buffer");
      NSLog(@"The error is %@", [videoWriter error]);
    }

    if (buffer) {
      CVBufferRelease(buffer);
    }

    [NSThread sleepForTimeInterval:0.05];
  } else {
    NSLog(@"error");
    i--;
  }
  [NSThread sleepForTimeInterval:0.02];

  return i;
}


- (CVPixelBufferRef)pixelBufferFromCGImage:(CGImageRef)image
{
  NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                           [NSNumber numberWithBool:YES],     kCVPixelBufferCGImageCompatibilityKey,
                           [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                           nil];
  CVPixelBufferRef pxbuffer = NULL;

  CVPixelBufferCreate(kCFAllocatorDefault, CGImageGetWidth(image),
                      CGImageGetHeight(image), kCVPixelFormatType_32ARGB, (CFDictionaryRef) options,
                  &pxbuffer);

  CVPixelBufferLockBaseAddress(pxbuffer, 0);
  void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
  size_t rowBytes = CVPixelBufferGetBytesPerRow(pxbuffer);


  CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
  CGContextRef context = CGBitmapContextCreate(pxdata, CGImageGetWidth(image),
                                           CGImageGetHeight(image), 8, rowBytes, rgbColorSpace,
                                           kCGImageAlphaNoneSkipFirst);

  CGContextConcatCTM(context, CGAffineTransformMakeRotation(0));


  CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image),
                                     CGImageGetHeight(image)), image);
  CGColorSpaceRelease(rgbColorSpace);
  CGContextRelease(context);

  CVPixelBufferUnlockBaseAddress(pxbuffer, 0);

  return pxbuffer;
}
4

1 回答 1

2

似乎我遇到了类似的问题,在阅读了以下两篇文章后,我的脑海中浮现出一个想法。

http://forum.videohelp.com/threads/314973-green-line-at-bottom-of-video-window

http://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Features

NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                       AVVideoCodecH264, AVVideoCodecKey,
                                       [NSNumber numberWithUnsignedLong:sourceWidth], AVVideoWidthKey,
                                       [NSNumber numberWithUnsignedLong:sourceHeight], AVVideoHeightKey,
                                       nil];
AVAssetWriterInput *writerInput =
        [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];

在开始通过 AVAssertWriter 写入数据之前,请重置sourceWidthsourceHeight使用此方法:

size_t fixedLineValue(size_t sourceValue){
    size_t divide = sourceValue%4;
    size_t finalValue = sourceValue;
    if (divide) {
        finalValue=(sourceValue/4+1)*4;
    }
    return finalValue;
}   

我无法弄清楚为什么要这样做,但在那之后,丑陋的绿色边框被删除了。如果有更好的答案,请发表评论,让我们知道。谢谢。

于 2014-02-24T10:10:02.427 回答