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
初始化设置编码环境
- (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
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- Managed Media Aggregation using Rtsp and Rtp
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- ASP编码必备的8条原则
- XML指南——XML编码
- C#中字符串编码处理
- ExtJS中文乱码之GBK格式编码解决方案及代码
- 程序员趣味读物 谈谈Unicode编码
- 文本文件编码方式区别
- C语言安全编码之数值中的sizeof操作符
- C#实现获取文本文件的编码的一个类(区分GB2312和UTF8)
- VC中BASE64编码和解码使用详解
- 计算机中的字符串编码、乱码、BOM等问题详解
- Base64编码解码原理及C#编程实例
- 利用Ffmpeg获得flv视频缩略图和视频时间的代码