您的位置:首页 > 移动开发 > IOS开发

iOS FFmpeg实时YUV420P编码H264

2015-12-17 18:06 706 查看
使用FFmpeg进行H264编码需要在FFmpeg编译中引入libx264

初始化设置编码环境

- (BOOL)setEncode
{
avcodec_register_all();
_H264Codec = avcodec_find_encoder(CODEC_ID_H264);
if (_H264Codec == nil) {
NSLog(@"编解码器不支持");
return NO;
}
_codecContext = avcodec_alloc_context3(_H264Codec);
if (_codecContext == nil) {
NSLog(@"初始化编解码环境失败");
return NO;
}
_codecContext -> width = 640;
_codecContext -> height = 480;
_codecContext->pix_fmt = PIX_FMT_YUV420P;

if(avcodec_open2(_codecContext, _H264Codec, NULL) < 0) {

NSLog(@"打开编码器失败");
return NO;
}
return YES;
}如果avcodec_find_encoder(CODEC_ID_H264)返回NULL且确定

在编译时已连接如libx264,检查编译时config中是否设置了--enable-encoder=libx264

编码

- (void)encodeWithData:(const void *)data andConfig:(YUVConfig)config andReslutBlock:(encodeBlock)reslut {
dispatch_sync(_encode_queue, ^{

if (reslut != nil) {
_encodeReslutBlock = [reslut copy];

AVFrame *codecFrame;
_pts ++;
codecFrame = av_frame_alloc();
codecFrame->width = config.width;
codecFrame->height = config.height;
codecFrame->format = _codecContext->pix_fmt;
codecFrame->pts = _pts;

AVPacket packet;
av_init_packet(&packet);
packet.size = 0;
packet.data = NULL;

avpicture_fill((AVPicture*)codecFrame, data, _codecContext->pix_fmt, config.width, config.height);

// 解码
int ret = 0;
int got_picture = 0;

ret = avcodec_encode_video2(_codecContext, &packet, codecFrame, &got_picture);
if (got_picture) {
NSData *buffer = [NSData dataWithBytes:packet.data length:packet.size];
dispatch_async(_main_queue, ^{
_encodeReslutBlock(buffer);
});
}

// 释放
av_frame_free(&codecFrame);
av_free_packet(&packet);
}
});
}

codecFrame->pts =
_pts;此处不设置或者设置的值有问题会导致[libx264 @ 0x13789b000] non-strictly-monotonic PTS但是不影响解码,编解码过程需加锁或在同步线程中进行

编解码结束后释放- (void)dealloc {

_H264Codec = nil;
avcodec_close(_codecContext);
avcodec_free_context(&_codecContext);
_codecContext = nil;

}

完整H264编码代码:

#import <Foundation/Foundation.h>
typedef void(^encodeBlock)(NSData* H264buf);
typedef struct {
int width;
int height;
int64_t pts;// 视频帧出现时间
int64_t duration;
} YUVConfig;
@interface H264EncodeManager : NSObject
/**
* H264编码
*
* @param data 未编码数据
* @param width 视频宽
* @param height 视频高
* @param reslut H264数据
*/
- (void)encodeWithData:(const void*)data andConfig:(YUVConfig)config andReslutBlock:(encodeBlock)reslut;
@end


#import "H264EncodeManager.h"
#import <libavcodec/avcodec.h>
#import <libavformat/avformat.h>
#import <libavutil/opt.h>
#import <libavutil/imgutils.h>
#import <libswscale/swscale.h>
@interface H264EncodeManager()
{
dispatch_queue_t _encode_queue;
dispatch_queue_t _main_queue;
}
@property (nonatomic, assign) int64_t pts;
/**
*  编解码器环境
*/
@property (nonatomic, assign) AVCodecContext *codecContext;

/**
*  编解码器
*/
@property (nonatomic, assign) AVCodec *H264Codec;

/**
*  编码返回结果
*/
@property (nonatomic, strong) encodeBlock encodeReslutBlock;

@end
@implementation H264EncodeManager
- (instancetype)init {
if (self = [super init]) {

_encode_queue = dispatch_queue_create("encodeQueue", nil);
_main_queue = dispatch_get_main_queue();
if (![self setEncode]) {
return nil;
}
}
return self;
}

- (void)dealloc {

_H264Codec = nil;
avcodec_close(_codecContext);
avcodec_free_context(&_codecContext);
_codecContext = nil;

}

#pragma mark - Private
- (BOOL)setEncode
{
// 初始化codec
avcodec_register_all();
_H264Codec = avcodec_find_encoder(CODEC_ID_H264);
if (_H264Codec == nil) {
NSLog(@"编解码器不支持");
return  NO;
}

//  初始化上下文
_codecContext = avcodec_alloc_context3(_H264Codec);
if (_codecContext == nil) {
NSLog(@"初始化编解码环境失败");
return NO;
}
_codecContext -> width = 640;
_codecContext -> height = 480;
_codecContext->pix_fmt = PIX_FMT_YUV420P;

//  打开编码器
if(avcodec_open2(_codecContext, _H264Codec, NULL) < 0) {

NSLog(@"打开编码器失败");
return NO;
}
return YES;
}

#pragma mark - 编码
- (void)encodeWithData:(const void *)data andConfig:(YUVConfig)config andReslutBlock:(encodeBlock)reslut {
dispatch_sync(_encode_queue, ^{

if (reslut != nil) {
_encodeReslutBlock = [reslut copy];

AVFrame *codecFrame;
_pts ++;
codecFrame = av_frame_alloc();
codecFrame->width = config.width;
codecFrame->height = config.height;
codecFrame->format = _codecContext->pix_fmt;
codecFrame->pts = _pts;

AVPacket packet;
av_init_packet(&packet);
packet.size = 0;
packet.data = NULL;

avpicture_fill((AVPicture*)codecFrame, data, _codecContext->pix_fmt, config.width, config.height);

//  解码
int ret = 0;
int got_picture = 0;

ret = avcodec_encode_video2(_codecContext, &packet, codecFrame, &got_picture);
if (got_picture) {
NSData *buffer = [NSData dataWithBytes:packet.data length:packet.size];
dispatch_async(_main_queue, ^{
_encodeReslutBlock(buffer);
});
}

//  释放
av_frame_free(&codecFrame);
av_free_packet(&packet);
}
});
}
@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  iOS FFmpeg H264 编码 yuv