今天给大家分享下视频的编码,由于ios在8.0才支持硬编码,所以这里加入了软编码,openH264和X264均可,这里介绍的是X264。
硬编码由于耗用CPU极低,编码效率高所以优先选择,软编码(X264)占用CPU高,一般在4s上720P 20帧就编不动了吧,当然也有优点,6.X、7.X系统都可用。
首先是编码器的设计图:
H264Encoder作为基类首先是创建编码器接口,当8.0以上系统 选择硬编码 其他软编码
+ (id)create
{
if (Version_iOS_8) {
H264VideoToolboxEncoder *encoder = [[H264VideoToolboxEncoder alloc] init];
return encoder;
} else
{
H264EncoderImpl *encoder = [[H264EncoderImpl alloc] init];
return encoder;
}
return nil;
}
编码接口,首先检查采集数据的分辨率帧率有无变化,如有变化则去销毁原有的,重新创建编码器,若无则直接编码
- (int)checkEncoder:(struct VideoCapability*)capability
{
if((*capability)!=(*_usingParam))
{
memcpy(_usingParam,capability,sizeof(struct VideoCapability));
[self finiEncoder];
if (_pTmpOut)
{
free(_pTmpOut);
_pTmpOut = 0;
}
[self initEncoder];
if (!_pTmpCfg)
_pTmpCfg = (uint8_t*)malloc(100);
if (!_pTmpOut)
_pTmpOut = (uint8_t*)malloc(_usingParam->width * _usingParam->height * 2 + 100);
}
return 0;
}
- (int)encode:(NativeVideoFrame*)avframe Capability:(struct VideoCapability*)capability
{
NSAutoLock* autolock = [[NSAutoLock alloc] initWithLock:_lock];
UNUSED(autolock);
if (!_running)
return 0;
[self checkEncoder:capability];
return [self realEncode:avframe TimeStamp:[Utils now_ms]];
}
H264Encoder的派生类有H264Encoderlmpl(软编)和H264VideoToolboxEncoder(硬编),他们都实现了基类的三个protected方法。
顺带介绍下OC中protected方法的写法
@interface H264EncoderImpl (Protected)
- (BOOL)initEncoder;
- (void)finiEncoder;
- (int)realEncode:(NativeVideoFrame *)avFrame TimeStamp:(long)ts;
@end
先来介绍下H264Encoderlmpl
//
// H264EncoderImpl.m
// AVSession
//
// Created by whw on 2016/11/22.
// Copyright © 2016年 meixin. All rights reserved.
//
#import "H264EncoderImpl.h"
#import "x264.h"
#import "AVDefine.h"
#import "libavformat/avformat.h"
#import "Utils.h"
#import "libyuv.h"
#include "VideoDefines.h"
#include "VideoFrame.h"
typedef struct
{
x264_param_t * param;
x264_t *handle;
x264_picture_t * picture;
x264_nal_t *nal;
} Encoder;
@interface H264EncoderImpl ()
{
Encoder* _encoder;
}
@end
@interface H264EncoderImpl (Protected)
- (BOOL)initEncoder;
- (void)finiEncoder;
- (int)realEncode:(NativeVideoFrame *)avFrame TimeStamp:(long)ts;
@end
@implementation H264EncoderImpl
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
- (int)realEncode:(NativeVideoFrame *)raw TimeStamp:(long)ts
{
raw->native2i420();
int framesize = raw->width()*raw->height();
_encoder->picture->img.i_stride[kYPlane] = raw->stride(kYPlane);
_encoder->picture->img.i_stride[kUPlane] = raw->stride(kUPlane);
_encoder->picture->img.i_stride[kVPlane] = raw->stride(kVPlane);
_encoder->picture->img.i_stride[kNumOfPlanes] = 0;
memcpy(_encoder->picture->img.plane[kYPlane],raw->buffer(kYPlane), framesize);
memcpy(_encoder->picture->img.plane[kUPlane],raw->buffer(kUPlane), framesize>>2);
memcpy(_encoder->picture->img.plane[kVPlane],raw->buffer(kVPlane), framesize>>2);
_encoder->picture->img.plane[kNumOfPlanes] = 0;
_encoder->picture->img.i_csp = X264_CSP_I420;
return [self CompressBuffer:_encoder TS:ts];
}
- (BOOL)initEncoder
{
Encoder *en = (Encoder *) malloc(sizeof(Encoder));
en->param = (x264_param_t *) malloc(sizeof(x264_param_t));
en->picture = (x264_picture_t *) malloc(sizeof(x264_picture_t));
x264_param_default_preset(en->param, "superfast" , "zerolatency");
en->param->b_sliced_threads = 0;
en->param->i_threads = 1;
en->param->rc.i_rc_method = X264_RC_ABR;
int realBitrate = [Utils calcBiteRate:_usingParam->width heght:_usingParam->height fps:_usingParam->fps];
realBitrate = realBitrate>>10;
en->param->rc.i_vbv_max_bitrate= 2 * realBitrate;
en->param->rc.i_bitrate = realBitrate;
en->param->rc.i_vbv_buffer_size = 2 * realBitrate;
en->param->i_fps_num = _usingParam->fps;
en->param->i_fps_den = 1;
en->param->i_keyint_min = _usingParam->fps * 2;
en->param->i_keyint_max = _usingParam->fps * 2;
en->param->i_timebase_num = 1;
en->param->i_timebase_den = 1000;
x264_param_apply_profile(en->param,"baseline");
en->param->i_csp = X264_CSP_I420;
en->param->i_log_level = X264_LOG_NONE;
en->param->i_width = _usingParam->width; //set frame width
en->param->i_height = _usingParam->height; //set frame height
if ((en->handle = x264_encoder_open(en->param)) == 0) {
//tyy
free(en->param);
free(en->picture);
free(en);
return NO;
}
/* Create a new pic */
x264_picture_alloc(en->picture, X264_CSP_I420, en->param->i_width, en->param->i_height);
_encoder = en;