AVAssetWriter视频数据编码
AVAssetWriter介绍
可以通过AVAssetWriter来对媒体样本重新做编码。
针对一个视频文件,只可以使用一个AVAssetWriter来写入,所以每一个文件都需要对应一个新的AVAssetWriter实例。
AVAssetWriter初始化
使用一个视频文件路径对AVAssetReader进行初始化,并指定文件类型。
NSError * error;
_mAssetWriter = [[AVAssetWriter alloc] initWithURL:videoUrl fileType:AVFileTypeAppleM4V error:&error];
AVAssetWriter设置Input
在写入之前,需要设置Input,与AVAssetReader的Output一样,也可以设置AVAssetWriterInput输入的类型为AVMediaTypeAudio或者AVMediaTypeVideo,以下设置以AVMediaTypeVideo为例。
在设置Input时可以指定output设置,这个设置里主要包含视频参数。
AVVideoCompressionPropertiesKey对应的属性值是编码相关的,比如一下参数:
- AVVideoAverageBitRateKey:视频尺寸*比率,10.1相当于AVCaptureSessionPresetHigh,数值越大,显示越精细(只支持H.264)。
- AVVideoMaxKeyFrameIntervalKey:关键帧最大间隔,若设置1每帧都是关键帧,数值越大压缩率越高(只支持H.264)。
- AVVideoProfileLevelKey:画质级别,与设备相关。
- P-Baseline Profile:基本画质。支持I/P 帧,只支持无交错(Progressive)和CAVLC;
- EP-Extended profile:进阶画质。支持I/P/B/SP/SI 帧,只支持无交错(Progressive)和CAVLC;
- MP-Main profile:主流画质。提供I/P/B 帧,支持无交错(Progressive)和交(Interlaced),也支持CAVLC 和CABAC 的支持;
- HP-High profile:高级画质。在main Profile 的基础上增加了8×8内部预测、自定义量化、 无损视频编码和更多的YUV 格式;
AVVideoCodecKey:视频的编码方式,这里设置为H.264.
AVVideoWidthKey, AVVideoHeightKey:视频的宽高。
更多的设置可以参考文档:Video Settings | Apple Developer Documentation
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;
}
}
AVAssetWriter视频数据编码的更多相关文章
- 海思HI35XX之----视频处理单元各通道间的关系
最近在折腾HI3518C的芯片,应用到IPCamera上,最终获取多路不同分辨率的视频流供不同需求的预览切换.此处简单记录一下视频前处理元VPSS(Video Process Sub-System)的 ...
- 用EasyClient开源项目采集Windows摄像头/麦克风的音视频进行RTSP直播
EasyClient是EasyDarwin开源流媒体团队开发的一款功能丰富的开源PC客户端项目,目前支持Windows.Android版本,后续将支持ios版本,其中Windows版本的EasyCli ...
- 音视频开发-FFmpeg
音视频开发是个非常复杂的,庞大的开发话题,初涉其中,先看一下结合 OEIP(开源项目) 新增例子. 可以打开flv,mp4类型文件,以及rtmp协议音视频数据,声音的播放使用SDL. 把采集的麦/声卡 ...
- FFMpeg写MP4文件例子分析
http://blog.csdn.net/eightdegree/article/details/7425811 这段时间看了FFMpeg提供的例子muxing.c,我略微修改了下源代码,使其生成一个 ...
- PS流的格式和解析总结
对于PS流,最近因为工作需要,所以MPEG2中的PS流格式和解包过程进行了学习. 首先我们需要知道PS包流格式是怎么样的: (来自http://blog.csdn.net/chen495810242/ ...
- PS 流格式解析(转)
对于PS流,最近因为工作需要,所以MPEG2中的PS流格式和解包过程进行了学习. 首先我们需要知道PS包流格式是怎么样的: 针对H264 做如下PS 封装:每个IDR NALU 前一般都会包含SPS. ...
- 新手学习FFmpeg - 调用API完成录屏并进行H.264编码
Screen Record H.264 目前在网络传输视频/音频流都一般会采用H.264进行编码,所以尝试调用FFMPEG API完成Mac录屏功能,同时编码为H.264格式. 在上一篇文章中,通过调 ...
- Android NDK 直播推流与引流
本篇介绍一下直播技术中推流与引流的简单实现. 1.流媒体服务器测试 首先利用快直播 app (其他支持 RTMP 推流与引流的 app 亦可)和 ffplay.exe 对流媒体服务器进行测试. 快直播 ...
- FFmpeg开发实战(六):使用 FFmpeg 将YUV数据编码为视频文件
本文中实现的一个小功能是把一个YUV原始视频数据(时间序列图像)经过h264编码为视频码流,然后在使用mp4封装格式封装. 编码&封装的流程图如下: 使用ffmpeg编码流程: 1.首先使用a ...
随机推荐
- PHP逐行解析文件,并写入数据库
$filePath为文件路径,上传文件则返回文件路径调用下面函数即可public function readText($filePath,&$errorCode,&$errorMess ...
- nginx 配置后页面访问是报500错
该问题是html文件权限问题. 用jenkins 并远程服务器上传到另一台服务器的html ,在配置好nginx 的location root 绝对位置后还是报错500 手工用root上传时访问正常 ...
- 将本地代码上传到云效git存放
前提已开通云效账号,然后进行云效首页的研发---代码 创建git组 创建git库 生成https://code.aliyun.com/test-demo1/v1的git库 准备将本地的test-dem ...
- lower_bound和upper_bound的实现
int lowerBound(int* nums, int numsSize, int target) { //注意left和right的初始值必须是left = 0, right = numsSzi ...
- Java7中Switch为什么只支持byte、short、char、int、String
Java 7中,switch的参数可以是String类型了,这对我们来说是一个很方便的改进.到目前为止switch支持这样几种数据类型:byte short int char String .但是,作 ...
- [DB] MySQL 索引分类
按数据结构 B树索引 数据位于叶子节点,到任何一个叶子节点的距离相同,一般不超过3-4层 B+树索引:每个叶子节点除了数据还存放前后叶子节点的指针,方便快速检索,是InnoDB采用的索引结构 Hash ...
- Gtkperf介绍
Gtkperf使用说明一.Gtkperf介绍GtkPerf是一种应用程序设计,测试基于GTK +的性能.问题的关键是建立共同的测试平台,运行预先基于GTK +工具(开放comboboxes ,切换按钮 ...
- 大数据 什么是 ETL
ETL 概念 ETL 这个术语来源于数据仓库,ETL 指的是将业务系统的数据经过抽取.清洗转换之后加载到数据仓库的过程.ETL 的目的是将企业中的分散.零乱.标准不统一的数据整合到一起,为企业的决策提 ...
- Java Stream 流(JDK 8 新特性)
什么是 Steam Java 8 中新增了 Stream(流)来简化集合类的使用,Stream 本质上是个接口,接口中定义了很多对 Stream 对象的操作. 我们知道,Java 中 List 和 S ...
- 逗号字符的使用、字符数组与字符串数组、sizeof与strlen
(1)连接两个表达式为一个表达式 for(ux=0,uxt=1;uxt<444;ux++,uxt++) 允许通过编译:他可以给FOR循环更多的初始化值: (2)一般定义的话要区别只有 字符数组 ...