H264
H264的官方测试源码,由德国hhi研究所负责开发。
特点:实现了264所有的特性,由于是官方的测试源码,所以学术研究的算法都是在JM基础上实现并和JM进行比较。但其程序结构冗长,
只考虑引入各种新特性以提高编码性能,忽视了编码复杂度,其编码复杂度极高,不宜实用。
X264
网上自由组织联合开发的兼容264标准码流的编码器,创始人是一个法国人。X264在网上的口碑极佳。
特点:注重实用。和JM相比,在不明显降低编码性能的前提下,努力降低编码的计算复杂度,故X264摈弃了264中一些对编码性能贡献
微笑但计算复杂度极高的新特性,如多参考帧、帧间预测中不必要的块模式、CABAC等。
编码格式输出:
总的来说H264的码流的打包方式有两种,一种为annex-b byte stream format的格式,这个是绝大部分编码器的默认输出格式,就是
每个帧的开头的3~4个字节是H264的start_code,0x00000001或者0x000001。
另一种是原始的NAL打包格式,就是开始的若干字节(1,2,4字节)是NAL的长度,而不是start_code,此时必须借助某个全局的数据
来获得编码器的profile,level,PPS,SPS等信息才可以解码。
h264encoder.h
/** * * Created on: Dec 10, 2010 * Author: Henry.wen */ #ifndef _H264ENCODER_H #define _H264ENCODER_H #include <stdint.h> #include <inttypes.h> extern "C" { #include "matroska.h" } void save_image(const char* filePath, const void* bufferBase, int width, int height); int encoder_init(const char* filePath, int width, int height); int encoder_frame(const void* frame); void encoder_close(); #endif
h264encoder.cpp
extern "C" { #include <matroska.h> } #include <skia/core/SkBitmap.h> #include <skia/images/SkImageEncoder.h> #include <android_runtime/AndroidRuntime.h> #include "h264encoder.h" #ifndef X264_MAX #define X264_MAX( a, b ) ( (a) < (b) ? (b) : (a)) #endif x264_param_t g_param; x264_picture_t g_pic_in; x264_picture_t g_pic_out; x264_t *g_encoder = NULL; int g_width = 0; int g_height = 0; FILE* g_file = 0; int64_t g_pts = 0; int g_flagInit = 0; using namespace android; static Mutex sg_mutexLock; #ifndef LOGI #define LOGI(...) ((void)__android_log_print("H264ENCODE", MOBIHEARTCAMERA_LOG_TAG, __VA_ARGS__)) #endif void save_image(const char* filePath, const void* bufferBase, int width, int height) { Mutex::Autolock lock(sg_mutexLock); SkBitmap b; b.setConfig(SkBitmap::kARGB_8888_Config, width, height); b.setPixels((void*)bufferBase); SkImageEncoder::EncodeFile(filePath, b, SkImageEncoder::kJPEG_Type, SkImageEncoder::kDefaultQuality); } int encoder_init(const char* filePath, int width, int height) { LOGI("encoder_init ============begin"); if(g_flagInit != 0) { LOGI("encoder_init have encoding!"); return 0; } Mutex::Autolock lock(sg_mutexLock); x264_param_default_preset(&g_param, "fast", "zerolatency"); g_param.i_width = width; g_param.i_height = height; g_param.i_fps_num = 25; g_param.i_fps_den = 1; g_width = width; g_height = height; g_param.i_keyint_max = 25; g_param.b_intra_refresh = 1; g_param.b_annexb = 1; x264_param_apply_profile(&g_param, x264_profile_names[0]); g_encoder = x264_encoder_open(&g_param); if(g_encoder == 0) { LOGI("encoder_init open encoder fail!"); return -1; } g_param.rc.i_lookahead = 0; if( 0 != x264_picture_alloc(&g_pic_in, X264_CSP_I420, width, height))//X264_CSP_I420 { LOGI("encoder_init alloc picture fail!"); x264_encoder_close(g_encoder); g_encoder = 0; return -1; } g_file = fopen(filePath, "w+b"); if(g_file == NULL) { x264_encoder_close(g_encoder); g_encoder = 0; return -1; } g_flagInit = 1; g_pts = 0; return 0; } int encoder_frame(const void* frame) { LOGI("encoder_frame ============begin"); if(g_flagInit > 0) { Mutex::Autolock lock(sg_mutexLock); g_pic_in.img.plane[0] = (uint8_t*)frame; uint8_t* tmpBuffer_uv = g_pic_in.img.plane[0] + g_width * g_height; uint8_t* tmpBuffer_u = g_pic_in.img.plane[1]; uint8_t* tmpBuffer_v = g_pic_in.img.plane[2]; int index = 0; for(int j = 0, nCount = (g_height >> 1) * g_width; j < nCount; j +=2) { *(tmpBuffer_u + index) = *(tmpBuffer_uv + index); *(tmpBuffer_v + index + 1) = *(tmpBuffer_uv + index + 1); ++index; } int nnal; x264_nal_t *nals; g_pic_in.i_pts = g_pts; g_pic_in.i_type = X264_TYPE_AUTO; int ret = x264_encoder_encode(g_encoder, &nals, &nnal, &g_pic_in, &g_pic_out); if( ret < 0) { LOGI("encoder_frame encode frame fail, ret = %d", ret); return 0; } ++g_pts; fwrite(nals[0].p_payload, ret, 1, g_file ); } LOGI("encoder_frame ============end"); return 0; } void encoder_close() { LOGI("encoder_close ============begin"); Mutex::Autolock lock(sg_mutexLock); if(g_encoder) x264_encoder_close(g_encoder); g_encoder = 0; if(g_file) fclose(g_file); g_file = 0; g_flagInit = 0; LOGI("encoder_close ============end"); }