在 iOS 中将多张 WebP 图像(通常是动图或序列帧)转换为视频,核心逻辑是利用 AVAssetWriter 将解码后的像素数据(CVPixelBuffer)按时间顺序写入到一个视频文件中。
由于 iOS 原生 UIImage 对 WebP 的支持在不同版本有所差异(iOS 14+ 支持较好),通常建议配合 libwebp 或 SDWebImageWebPCoder 来确保解码的兼容性。
1、转换流程架构
- 解码阶段:将 WebP 动图拆解为每一帧的
UIImage或CGImage。 - 缓冲区准备:创建一个
CVPixelBufferPool,将CGImage绘制到CVPixelBuffer中。 - 写入阶段:使用
AVAssetWriterInputPixelBufferAdaptor按指定帧率将 Buffer 压入视频流。
2、Objective-C 代码实现
以下是一个将 WebP 图像序列转换为 MP4 视频的核心类实现:
#import <AVFoundation/AVFoundation.h>
#import <VideoToolbox/VideoToolbox.h>
@implementation WebPToVideoConverter
- (void)convertImages:(NSArray<UIImage *> *)images toPath:(NSString *)outputPath {
// 1. 初始化视频输出配置
NSError *error = nil;
NSURL *videoURL = [NSURL fileURLWithPath:outputPath];
AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:videoURL
fileType:AVFileTypeMPEG4
error:&error];
// 设置 H.264 编码参数
NSDictionary *videoSettings = @{
AVVideoCodecKey: AVVideoCodecTypeH264,
AVVideoWidthKey: @(images.firstObject.size.width),
AVVideoHeightKey: @(images.firstObject.size.height),
};
AVAssetWriterInput *writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
outputSettings:videoSettings];
// 2. 创建适配器(用于输入 CVPixelBuffer)
AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor
assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput
sourcePixelBufferAttributes:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32ARGB)}];
[videoWriter addInput:writerInput];
[videoWriter startWriting];
[videoWriter startSessionAtSourceTime:kCMTimeZero];
// 3. 循环写入帧
NSInteger __block frameCount = 0;
CGFloat durationPerFrame = 0.1; // 假设每帧 100ms (10fps)
[writerInput requestMediaDataWhenReadyOnQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) usingBlock:^{
while ([writerInput isReadyForMoreMediaData] && frameCount < images.count) {
UIImage *image = images[frameCount];
CVPixelBufferRef buffer = [self pixelBufferFromCGImage:image.CGImage size:image.size];
if (buffer) {
CMTime presentationTime = CMTimeMake(frameCount, 10); // 10 为帧率
if (![adaptor appendPixelBuffer:buffer withPresentationTime:presentationTime]) {
NSLog(@"写入失败: %@", videoWriter.error);
}
CFRelease(buffer);
}
frameCount++;
}
if (frameCount >= images.count) {
[writerInput markAsFinished];
[videoWriter finishWritingWithCompletionHandler:^{
NSLog(@"视频转换完成: %@", outputPath);
}];
}
}];
}
// 辅助方法:CGImage 转 CVPixelBuffer
- (CVPixelBufferRef)pixelBufferFromCGImage:(CGImageRef)image size:(CGSize)size {
NSDictionary *options = @{(id)kCVPixelBufferCGImageCompatibilityKey: @YES,
(id)kCVPixelBufferCGBitmapContextCompatibilityKey: @YES};
CVPixelBufferRef pxbuffer = NULL;
CVPixelBufferCreate(kCFAllocatorDefault, size.width, size.height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef)options, &pxbuffer);
CVPixelBufferLockBaseAddress(pxbuffer, 0);
void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pxdata, size.width, size.height, 8, 4 * size.width, rgbColorSpace, kCGImageAlphaPremultipliedFirst);
CGContextDrawImage(context, CGRectMake(0, 0, size.width, size.height), image);
CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(context);
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
return pxbuffer;
}
@end
3、关键优化建议
3.1、WebP 解码
如果你的 WebP 是动图(Animated WebP),系统原生的 UIImage 可能无法直接获取序列。建议使用:
- SDWebImageWebPCoder:调用
[[SDImageWebPCoder sharedCoder] decodedImageWithData:webpData options:nil]。 - YYImage:它可以非常方便地遍历动图的每一帧及其持续时间(Duration)。
3.2、内存控制
大量 WebP 帧解码后会占用巨大内存。
- 不要一次性将所有
UIImage加载到NSArray中。 - 推荐方案:使用
CGImageSourceRef边解码边写入。
3.3、视频编码格式选择
- H.264 (AVVideoCodecTypeH264):兼容性最好。
- HEVC (AVVideoCodecTypeHEVC):如果 WebP 带有透明通道,且你希望输出带透明度的视频,建议使用 HEVC 并在配置中开启
Alpha支持。
3.4、帧率对齐
WebP 的每一帧持续时间可能是不固定的。在 CMTimeMake 时,应累加实际的 duration 而非使用固定的 frameCount,以保证视频丝滑不卡顿。
学习和提升音视频开发技术,推荐你加入我们的知识星球:【关键帧的音视频开发圈】

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。