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 <NSObject>
- (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<ScreenCaptureViewDelegate> 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, "Memory not allocated!");
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, "Context not created!");
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 < 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
// 'setNeedsDisplay' 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:@"%@/%@", , @"output.mp4"];
NSURL* outputURL = [ initFileURLWithPath:outputPath];
NSFileManager* fileManager = ;
if () {
NSError* error;
if ( == NO) {
NSLog(@"Could not delete old recording file at path:%@", outputPath);
}
}
return outputURL;
}
-(BOOL) setUpWriter {
NSError* error = nil;
videoWriter = [ initWithURL: fileType:AVFileTypeQuickTimeMovie error:&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(@"Waiting...");
;
status = videoWriter.status;
}
@synchronized(self) {
[videoWriter finishWritingWithCompletionHandler:^{
;
BOOL success = YES;
id delegateObj = self.delegate;
NSString *outputPath = [ initWithFormat:@"%@/%@", , @"output.mp4"];
NSURL *outputURL = [ initFileURLWithPath:outputPath];
NSLog(@"Completed recording, file is stored at:%@", 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(@"Not ready for video data");
}
else {
@synchronized (self) {
UIImage *newFrame = self.currentScreen;
CVPixelBufferRef pixelBuffer = NULL;
CGImageRef cgImage = CGImageCreateCopy();
CFDataRef image = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
int status = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, avAdaptor.pixelBufferPool, &pixelBuffer);
if(status != 0){
//could not get a buffer from the pool
NSLog(@"Error creating pixel buffer:status=%d", 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(@"Warning:Unable to write buffer to video");
}
//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'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]