菜鸟教程小白 发表于 2022-12-13 10:29:40

ios - 从 iOS 图像制作视频


                                            <p><p>我找到了这个教程 <a href="http://codethink.no-ip.org/wordpress/archives/673#comment-118063" rel="noreferrer noopener nofollow">http://codethink.no-ip.org/wordpress/archives/673#comment-118063</a>从这个 SO 问题 <a href="https://stackoverflow.com/questions/11334977/screen-capture-video-in-ios-programmatically" rel="noreferrer noopener nofollow">Screen capture video in iOS programmatically</a>关于如何做这样的事情,它对于 iOS 来说有点过时了,所以我更新了它,并且非常接近它的工作,但是现在将 UIImages 放在一起还不能很好地工作。 </p>

<p>这是我在 viewDidLoad 中调用方法的方式</p>

<pre><code>;
                            ;
</code></pre>

<p>和 <code>captureView</code> 是一个连接到我的 View 的 IBOutlet。</p>

<p>然后我有类 ScreenCapture.h & .m</p>

<p>这里是.h</p>

<pre><code>@protocol ScreenCaptureViewDelegate &lt;NSObject&gt;
- (void) recordingFinished:(NSString*)outputPathOrNil;
@end

@interface ScreenCaptureView : UIView {
    //video writing
    AVAssetWriter *videoWriter;
    AVAssetWriterInput *videoWriterInput;
    AVAssetWriterInputPixelBufferAdaptor *avAdaptor;

    //recording state
    BOOL _recording;
    NSDate* startedAt;
    void* bitmapData;
}

//for recording video
- (bool) startRecording;
- (void) stopRecording;

//for accessing the current screen and adjusting the capture rate, etc.
@property(retain) UIImage* currentScreen;
@property(assign) float frameRate;
@property(nonatomic, assign) id&lt;ScreenCaptureViewDelegate&gt; delegate;

@end
</code></pre>

<p>这是我的 .m</p>

<pre><code>@interface ScreenCaptureView(Private)
- (void) writeVideoFrameAtTime:(CMTime)time;
@end

@implementation ScreenCaptureView

@synthesize currentScreen, frameRate, delegate;

- (void) initialize {
    // Initialization code
    self.clearsContextBeforeDrawing = YES;
    self.currentScreen = nil;
    self.frameRate = 10.0f;   //10 frames per seconds
    _recording = false;
    videoWriter = nil;
    videoWriterInput = nil;
    avAdaptor = nil;
    startedAt = nil;
    bitmapData = NULL;
}

- (id) initWithCoder:(NSCoder *)aDecoder {
    self = ;
    if (self) {
      ;
    }
    return self;
}

- (id) init {
    self = ;
    if (self) {
      ;
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame {
    self = ;
    if (self) {
      ;
    }
    return self;
}

- (CGContextRef) createBitmapContextOfSize:(CGSize) size {
    CGContextRef    context = NULL;
    CGColorSpaceRef colorSpace;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;

    bitmapBytesPerRow   = (size.width * 4);
    bitmapByteCount   = (bitmapBytesPerRow * size.height);
    colorSpace = CGColorSpaceCreateDeviceRGB();
    if (bitmapData != NULL) {
      free(bitmapData);
    }
    bitmapData = malloc( bitmapByteCount );
    if (bitmapData == NULL) {
      fprintf (stderr, &#34;Memory not allocated!&#34;);
      return NULL;
    }

    context = CGBitmapContextCreate (bitmapData,
                                     size.width,
                                     size.height,
                                     8,      // bits per component
                                     bitmapBytesPerRow,
                                     colorSpace,
                                     (CGBitmapInfo) kCGImageAlphaNoneSkipFirst);

    CGContextSetAllowsAntialiasing(context,NO);
    if (context== NULL) {
      free (bitmapData);
      fprintf (stderr, &#34;Context not created!&#34;);
      return NULL;
    }
    CGColorSpaceRelease( colorSpace );

    return context;
}

static int frameCount = 0;            //debugging
- (void) drawRect:(CGRect)rect {
    NSDate* start = ;
    CGContextRef context = ;

    //not sure why this is necessary...image renders upside-down and mirrored
    CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, self.frame.size.height);
    CGContextConcatCTM(context, flipVertical);

    ;

    CGImageRef cgImage = CGBitmapContextCreateImage(context);
    UIImage* background = ;
    CGImageRelease(cgImage);

    self.currentScreen = background;

    //debugging
    if (frameCount &lt; 40) {
          NSString* filename = ;
          NSString* pngPath = ;
          ;
          frameCount++;
    }

    //NOTE:to record a scrollview while it is scrolling you need to implement your UIScrollViewDelegate such that it calls
    //       &#39;setNeedsDisplay&#39; on the ScreenCaptureView.
    if (_recording) {
      float millisElapsed = [ timeIntervalSinceDate:startedAt] * 1000.0;
      ;
    }

    float processingSeconds = [ timeIntervalSinceDate:start];
    float delayRemaining = (1.0 / self.frameRate) - processingSeconds;

    CGContextRelease(context);

    //redraw at the specified framerate
    ;
}

- (void) cleanupWriter {
    avAdaptor = nil;

    videoWriterInput = nil;

    videoWriter = nil;

    startedAt = nil;

    if (bitmapData != NULL) {
      free(bitmapData);
      bitmapData = NULL;
    }
}

- (void)dealloc {
    ;
}

- (NSURL*) tempFileURL {
    NSString* outputPath = [ initWithFormat:@&#34;%@/%@&#34;, , @&#34;output.mp4&#34;];
    NSURL* outputURL = [ initFileURLWithPath:outputPath];
    NSFileManager* fileManager = ;
    if () {
      NSError* error;
      if ( == NO) {
            NSLog(@&#34;Could not delete old recording file at path:%@&#34;, outputPath);
      }
    }

    return outputURL;
}

-(BOOL) setUpWriter {
    NSError* error = nil;
    videoWriter = [ initWithURL: fileType:AVFileTypeQuickTimeMovie error:&amp;error];
    NSParameterAssert(videoWriter);

    //Configure video
    NSDictionary* videoCompressionProps = [NSDictionary dictionaryWithObjectsAndKeys:
                                           , AVVideoAverageBitRateKey,
                                           nil ];

    NSDictionary* videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                 AVVideoCodecH264, AVVideoCodecKey,
                                 , AVVideoWidthKey,
                                 , AVVideoHeightKey,
                                 videoCompressionProps, AVVideoCompressionPropertiesKey,
                                 nil];

    videoWriterInput = ;

    NSParameterAssert(videoWriterInput);
    videoWriterInput.expectsMediaDataInRealTime = YES;
    NSDictionary* bufferAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                    , kCVPixelBufferPixelFormatTypeKey, nil];

    avAdaptor = ;

    //add input
    ;
    ;
    ;

    return YES;
}

- (void) completeRecordingSession {

    ;

    // Wait for the video
    int status = videoWriter.status;
    while (status == AVAssetWriterStatusUnknown) {
      NSLog(@&#34;Waiting...&#34;);
      ;
      status = videoWriter.status;
    }

    @synchronized(self) {




       [videoWriter finishWritingWithCompletionHandler:^{

         ;
         BOOL success = YES;
         id delegateObj = self.delegate;
         NSString *outputPath = [ initWithFormat:@&#34;%@/%@&#34;, , @&#34;output.mp4&#34;];
         NSURL *outputURL = [ initFileURLWithPath:outputPath];

         NSLog(@&#34;Completed recording, file is stored at:%@&#34;, outputURL);
         if () {
               ;
         }


       }];


    }

}

- (bool) startRecording {
    bool result = NO;
    @synchronized(self) {
      if (! _recording) {
            result = ;
            startedAt = ;
            _recording = true;
      }
    }

    return result;
}

- (void) stopRecording {
    @synchronized(self) {
      if (_recording) {
            _recording = false;
            ;
      }
    }
}

-(void) writeVideoFrameAtTime:(CMTime)time {
    if (!) {
      NSLog(@&#34;Not ready for video data&#34;);
    }
    else {
      @synchronized (self) {
            UIImage *newFrame = self.currentScreen;
            CVPixelBufferRef pixelBuffer = NULL;
            CGImageRef cgImage = CGImageCreateCopy();
            CFDataRef image = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));

            int status = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, avAdaptor.pixelBufferPool, &amp;pixelBuffer);
            if(status != 0){
                //could not get a buffer from the pool
                NSLog(@&#34;Error creating pixel buffer:status=%d&#34;, status);
            }
            // set image data into pixel buffer
            CVPixelBufferLockBaseAddress( pixelBuffer, 0 );
            uint8_t *destPixels = CVPixelBufferGetBaseAddress(pixelBuffer);
            CFDataGetBytes(image, CFRangeMake(0, CFDataGetLength(image)), destPixels);//XXX:will work if the pixel buffer is contiguous and has the same bytesPerRow as the input data

            if(status == 0){
                BOOL success = ;
                if (!success)
                  NSLog(@&#34;Warning:Unable to write buffer to video&#34;);
            }

            //clean up
            CVPixelBufferUnlockBaseAddress( pixelBuffer, 0 );
            CVPixelBufferRelease( pixelBuffer );
            CFRelease(image);
            CGImageRelease(cgImage);
      }

    }

}
</code></pre>

<p>正如您在 <code>drawRect</code> 方法中看到的那样,我保存了所有图像,它们看起来很棒,但是当我尝试制作视频时,它只创建了一个看起来像this,当图像看起来像这样时。</p>

<p>这是输出,它是一个视频,但仅此而已。当图片看起来正常时(不倾斜,很奇怪)</p>

<p> <img src="/image/MvQP3.jpg" alt="enter image description here"/> </p>

<p>我的问题是制作视频时出了什么问题?</p>

<p>感谢您的帮助和您的时间,我知道这是一个很长的问题。</p></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>当我想从 CGImageRef(来自 UIImage)创建 CVPixelBufferRef(来自 UIImage)时,由于某些分辨率导致完全相同的视频效果,我发现了这篇文章。</p>

<p>就我而言,非常简短的回答是,我已将每行的字节数硬连接为宽度的 4 倍。以前一直在工作!现在我查询 CVPixelBuffer 本身来获取这个值和 <em>poof</em>,问题解决了! </p>

<p>产生问题的代码是这样的:</p>

<pre><code>CGContextRef context = CGBitmapContextCreate(pxdata, w, h, 8, 4*w, rgbColorSpace, bitMapInfo);
</code></pre>

<p>解决问题的代码如下:</p>

<pre><code>    CGContextRef context = CGBitmapContextCreate(
                                             pxdata, w, h,
                                             8, CVPixelBufferGetBytesPerRow(pxbuffer),
                                             rgbColorSpace,bitMapInfo);
</code></pre>

<p>在这两种情况下,都设置了 bitMapInfo:</p>

<pre><code>GBitmapInfo bitMapInfo =kCGImageAlphaPremultipliedFirst; // According to Apple&#39;s doc, this is safe:June 26, 2014
</code></pre></p>
                                   
                                                <p style="font-size: 20px;">关于ios - 从 iOS 图像制作视频,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/29552019/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/29552019/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - 从 iOS 图像制作视频