可以通过AVAssetWriter来对媒体样本重新做编码。
针对一个视频文件,只可以使用一个AVAssetWriter来写入,所以每一个文件都需要对应一个新的AVAssetWriter实例。
使用一个视频文件路径对AVAssetReader进行初始化,并指定文件类型。
NSError * error;
_mAssetWriter = [[AVAssetWriter alloc] initWithURL:videoUrl fileType:AVFileTypeAppleM4V error:&error];
在写入之前,需要设置Input,与AVAssetReader的Output一样,也可以设置AVAssetWriterInput输入的类型为AVMediaTypeAudio或者AVMediaTypeVideo,以下设置以AVMediaTypeVideo为例。
在设置Input时可以指定output设置,这个设置里主要包含视频参数。
AVVideoCompressionPropertiesKey对应的属性值是编码相关的,比如一下参数:
NSDictionary *codec_settings = @{AVVideoAverageBitRateKey: @(_bitRate)};
NSDictionary *video_settings = @{AVVideoCodecKey: AVVideoCodecH264,
AVVideoCompressionPropertiesKey: codec_settings,
AVVideoWidthKey: @(1920),
AVVideoHeightKey: @(1080)};
_mAssetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:video_settings];
针对AVAssetWriterInput还可以设置相应的AVAssetWriterInputPixelBufferAdaptor来接收CVPixelBuffer。
AVAssetWriterInputPixelBufferAdaptor提供了一个CVPixelBufferPoolRef,您可以使用它来分配用于写入输出文件的像素缓冲区。文档中写到使用提供的像素缓冲池进行缓冲区分配通常比附加使用单独池分配的像素缓冲区更有效。
初始化的时候可以设置相关的参数,比如CVPixelBuffer的颜色格式,CPU和GPU的内存共享方式等。
CVPixelBuffer可以由AVAssetWriterInputPixelBufferAdaptor提供的缓冲池创建。
CVOpenGLESTextureCacheRef创建一块专门用于存放纹理的缓冲区,这样每次传递纹理像素数据给GPU时,直接使用这个缓冲区中的内存,避免了重复创建,提高了效率。
NSMutableDictionary * attributes = [NSMutableDictionary dictionary];
attributes[(NSString *) kCVPixelBufferPixelFormatTypeKey] = @(kCVPixelFormatType_32BGRA);
NSDictionary *IOSurface_properties = @{@"IOSurfaceOpenGLESFBOCompatibility": @YES, @"IOSurfaceOpenGLESTextureCompatibility": @YES};
attributes[(NSString *) kCVPixelBufferIOSurfacePropertiesKey] = IOSurface_properties;
_mAssetWriterPixelBufferInput = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:_mAssetWriterInput
sourcePixelBufferAttributes:attributes];
CVPixelBufferRef renderTarget;
CVOpenGLESTextureCacheRef videoTextureCache;
CVReturn err;
if (videoTextureCache == NULL) {
err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, [EAGLContext currentContext], NULL, & videoTextureCache);
if (err) {
//错误处理
}
}
err = CVPixelBufferPoolCreatePixelBuffer (NULL, [_mAssetWriterPixelBufferInput pixelBufferPool], &renderTarget);
if (err) {
//错误处理
}
//对CVPixelBuffer添加附加信息,做颜色格式的转化
CVBufferSetAttachment(renderTarget,
kCVImageBufferColorPrimariesKey,
kCVImageBufferColorPrimaries_ITU_R_709_2,
kCVAttachmentMode_ShouldPropagate);
CVBufferSetAttachment(renderTarget,
kCVImageBufferYCbCrMatrixKey,
kCVImageBufferYCbCrMatrix_ITU_R_601_4,
kCVAttachmentMode_ShouldPropagate);
CVBufferSetAttachment(renderTarget,
kCVImageBufferTransferFunctionKey,
kCVImageBufferTransferFunction_ITU_R_709_2,
kCVAttachmentMode_ShouldPropagate);
从CVPixelBuffer创建OpenGL的texture,会将renderTarget中的像素数据传输给OpenGL,可以在该texture上的绘制再编码进文件中。
CVOpenGLESTextureRef renderTexture;
err = CVOpenGLESTextureCacheCreateTextureFromImage (kCFAllocatorDefault,
videoTextureCache,
renderTarget,
NULL,
GL_TEXTURE_2D,
GL_RGBA,
[1920],
[1080],
GL_BGRA,
GL_UNSIGNED_BYTE,
0,
& renderTexture);
在写入之前设置好Input,之后调用startWriting方法。
if ([_mAssetWriter canAddInput:_mAssetWriterInput]){
[_mAssetWriter addInput:_mAssetWriterInput];
}
[_mAssetWriter startWriting];
[_mAssetWriter startSessionAtSourceTime:kCMTimeZero];
以AVAssetReader读取的sampleBuffer作为输入源来做数据写入,需要处理的异常情况也比较多,注意writer的状态处理。
代码示例
//判断input是否准备好接受新的数据
while (_mAssetWriterInput.isReadyForMoreMediaData)
{
CMSampleBufferRef sampleBuffer = [output copyNextSampleBuffer];
if (sampleBuffer)
{
BOOL error = NO;
if (_reader.status != AVAssetReaderStatusReading || _writer.status != AVAssetWriterStatusWriting)
{
error = YES;
}
if (_videoOutput == output)
{
// update the video progress
_lastSamplePresentationTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
if (![_mAssetWriterPixelBufferInput appendPixelBuffer:pixelBuffer withPresentationTime:_lastSamplePresentationTime])
{
error = YES;
}
dispatch_async(dispatch_get_main_queue(), ^{
_progress(CMTimeGetSeconds(_lastSamplePresentationTime) / _duration * 0.8);
});
}
if (error){
return NO;
}
}
else
{
//数据写入完成,标记Input结束
[_mAssetWriterInput markAsFinished];
return NO;
}
}
原文:https://www.cnblogs.com/zoule/p/14913203.html