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对应的属性值是编码相关的,比如一下参数:

  1. AVVideoAverageBitRateKey:视频尺寸*比率,10.1相当于AVCaptureSessionPresetHigh,数值越大,显示越精细(只支持H.264)。
  2. AVVideoMaxKeyFrameIntervalKey:关键帧最大间隔,若设置1每帧都是关键帧,数值越大压缩率越高(只支持H.264)。
  3. 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视频数据编码的更多相关文章

  1. 海思HI35XX之----视频处理单元各通道间的关系

    最近在折腾HI3518C的芯片,应用到IPCamera上,最终获取多路不同分辨率的视频流供不同需求的预览切换.此处简单记录一下视频前处理元VPSS(Video Process Sub-System)的 ...

  2. 用EasyClient开源项目采集Windows摄像头/麦克风的音视频进行RTSP直播

    EasyClient是EasyDarwin开源流媒体团队开发的一款功能丰富的开源PC客户端项目,目前支持Windows.Android版本,后续将支持ios版本,其中Windows版本的EasyCli ...

  3. 音视频开发-FFmpeg

    音视频开发是个非常复杂的,庞大的开发话题,初涉其中,先看一下结合 OEIP(开源项目) 新增例子. 可以打开flv,mp4类型文件,以及rtmp协议音视频数据,声音的播放使用SDL. 把采集的麦/声卡 ...

  4. FFMpeg写MP4文件例子分析

    http://blog.csdn.net/eightdegree/article/details/7425811 这段时间看了FFMpeg提供的例子muxing.c,我略微修改了下源代码,使其生成一个 ...

  5. PS流的格式和解析总结

    对于PS流,最近因为工作需要,所以MPEG2中的PS流格式和解包过程进行了学习. 首先我们需要知道PS包流格式是怎么样的: (来自http://blog.csdn.net/chen495810242/ ...

  6. PS 流格式解析(转)

    对于PS流,最近因为工作需要,所以MPEG2中的PS流格式和解包过程进行了学习. 首先我们需要知道PS包流格式是怎么样的: 针对H264 做如下PS 封装:每个IDR NALU 前一般都会包含SPS. ...

  7. 新手学习FFmpeg - 调用API完成录屏并进行H.264编码

    Screen Record H.264 目前在网络传输视频/音频流都一般会采用H.264进行编码,所以尝试调用FFMPEG API完成Mac录屏功能,同时编码为H.264格式. 在上一篇文章中,通过调 ...

  8. Android NDK 直播推流与引流

    本篇介绍一下直播技术中推流与引流的简单实现. 1.流媒体服务器测试 首先利用快直播 app (其他支持 RTMP 推流与引流的 app 亦可)和 ffplay.exe 对流媒体服务器进行测试. 快直播 ...

  9. FFmpeg开发实战(六):使用 FFmpeg 将YUV数据编码为视频文件

    本文中实现的一个小功能是把一个YUV原始视频数据(时间序列图像)经过h264编码为视频码流,然后在使用mp4封装格式封装. 编码&封装的流程图如下: 使用ffmpeg编码流程: 1.首先使用a ...

随机推荐

  1. Junit单元测试&反射&注解

    内容索引 1. Junit单元测试 2. 反射 3. 注解 Junit单元测试: * 测试分类: 1. 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值. 2. 白盒测试:需要写代码的.关 ...

  2. Window内核学习之保护模式基础

    段寄存器 段寄存器有6个分别是 cs,ss,ds,es,fs,gs.这些段寄存器包含16位的可见部分和80位的隐藏部分,共90位. 16位的可见部分就是我们知道的cs等段寄存器的值,我们可以在od中查 ...

  3. Ubuntu切换到root账户

    使用命令sudo su或sudo -i然后输入密码就可以切换到root帐号,exit可以退出.

  4. sscanf的应用

    1.提取字符串 2.提取指定长度的字符串 3.提取指定字符为止的字符串 4.取仅包含指定字符集的字符串 5.取到指定字符集为止的字符串 #include <stdio.h> int mai ...

  5. 【Git】git clone报错 git fatal: Unable to find remote helper for 'https'

    [参考资料] https://stackoverflow.com/questions/8329485/unable-to-find-remote-helper-for-https-during-git ...

  6. 基于LNMP架构搭建wordpress个人博客

    搭建过程 注意防火墙和selinux的影响可以先关闭. 一.安装nginx # 1.更改nginx源安装nginx [root@web01 ~]# vi /etc/yum.repos.d/nginx. ...

  7. Otter远程调试

    Otter远程调试 环境配置: 机器 172.16.0.2 172.16.0.3 172.16.0.4 ZK FOLLOWER FOLLOWER LEADER MySQL Manager MySQL ...

  8. docker,docker-compose,harbor安装

    安装docker-ce 下载docker-ce.repo: wget -O /etc/yum.repos.d/docker-ce.repo https://download.docker.com/li ...

  9. selenium多表单切换以及多窗口切换、警告窗处理

    selenium表单切换 在做UI自动化,有时候要定位的元素属性在页面上明明是唯一的.却怎么也不执行对元素的操作动作,这时候多半是iframe表单在作怪. 切入表单:iddriver.switch_t ...

  10. STM32关于多线程运行的疑问

    我有一个疑问,如果 STM32在操作系统环境下 能够一个线程串口中断接收数据 另一个线程 继续进行其他的操作