h.264编码库x264实例

x264是一个开源的H.264编码库。本文介绍其在x86 linux的编译方法,并给出实例。

一、编译

x264编译需要使用到yasm,到http://www.tortall.net/projects/yasm/releases/下载,最新版本为1.3.1。编译命令如下:

1
# ./configure;make;make install

x264官方下载:http://www.videolan.org/developers/x264.html。下载压缩包名为:last_x264.tar.bz2。解压得到的文件名为x264-snapshot-xxxx。编译命令如下:

1
2
3
$ ./configure  --prefix=/home/latelee/bin/x264 --enable-static
$ make
$ make install

二、实例

下面给出使用的示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/**
x264库编码要使用以下库
-lpthread -ldl
要求头文件:stdint.h
ffprobe信息:
Input #0, h264, from 'test.h264':
Duration: N/A, bitrate: N/A
Stream #0:0: Video: h264 (High), yuv420p, 176x144, 25 fps, 25 tbr, 1200k tbn
, 50 tbc

*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h> // for uint8_t in x264.h

#include "x264.h"

int x264_encode(const char* infile, int width, int height, int type, const char* outfile)
{
FILE *fp_src = NULL;
FILE *fp_dst = NULL;
unsigned int luma_size = 0;
unsigned int chroma_size = 0;
int i_nal = 0;
int i_frame = 0;
unsigned int i_frame_size = 0;

int csp = type;//X264_CSP_I420;
x264_nal_t* nal = NULL;
x264_t* handle = NULL;
x264_picture_t* pic_in = NULL;
x264_picture_t* pic_out = NULL;
x264_param_t param;

fp_src = fopen(infile, "rb");
fp_dst = fopen(outfile, "wb");
if(fp_src==NULL || fp_dst==NULL)
{
perror("Error open yuv files:");
return -1;
}

x264_param_default(¶m);

param.i_width = width;
param.i_height = height;
/*
//Param
param.i_log_level = X264_LOG_DEBUG;
param.i_threads = X264_SYNC_LOOKAHEAD_AUTO;
param.i_frame_total = 0;
param.i_keyint_max = 10;
param.i_bframe = 5;
param.b_open_gop = 0;
param.i_bframe_pyramid = 0;
param.rc.i_qp_constant=0;
param.rc.i_qp_max=0;
param.rc.i_qp_min=0;
param.i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
param.i_fps_den = 1;
param.i_fps_num = 25;
param.i_timebase_den = param.i_fps_num;
param.i_timebase_num = param.i_fps_den;
*/
param.i_csp = csp;
param.b_repeat_headers = 1;
param.b_annexb = 1;

// profile: high x264_profile_names定义见x264.h
x264_param_apply_profile(¶m, x264_profile_names[2]);

pic_in = (x264_picture_t*)malloc(sizeof(x264_picture_t));
if (pic_in == NULL)
{
goto out;
}
pic_out = (x264_picture_t*)malloc(sizeof(x264_picture_t));
if (pic_out == NULL)
{
goto out;
}
x264_picture_init(pic_out);
x264_picture_alloc(pic_in, csp, param.i_width, param.i_height);

handle = x264_encoder_open(¶m);

// 计算有多少帧
luma_size = param.i_width * param.i_height;
fseek(fp_src, 0, SEEK_END);
switch (csp)
{
case X264_CSP_I444:
i_frame = ftell(fp_src) / (luma_size*3);
chroma_size = luma_size;
break;
case X264_CSP_I420:
i_frame = ftell(fp_src) / (luma_size*3/2);
chroma_size = luma_size / 4;
break;
default:
printf("Colorspace Not Support.\n");
goto out;
}
fseek(fp_src, 0, SEEK_SET);

printf("framecnt: %d, y: %d u: %d\n", i_frame, luma_size, chroma_size);

// 编码
for (int i = 0; i < i_frame; i++)
{
switch(csp)
{
case X264_CSP_I444:
case X264_CSP_I420:
i_frame_size = fread(pic_in->img.plane[0], 1, luma_size, fp_src);
if (i_frame_size != luma_size)
{
printf("read luma failed %d.\n", i_frame_size);
break;
}
i_frame_size = fread(pic_in->img.plane[1], 1, chroma_size, fp_src);
if (i_frame_size != chroma_size)
{
printf("read chroma1 failed %d.\n", i_frame_size);
break;
}
i_frame_size = fread(pic_in->img.plane[2], 1, chroma_size, fp_src);
if (i_frame_size != chroma_size)
{
printf("read chrome2 failed %d.\n", i_frame_size);
break;
}
break;
default:
printf("Colorspace Not Support.\n");
return -1;
}
pic_in->i_pts = i;

i_frame_size = x264_encoder_encode(handle, &nal, &i_nal, pic_in, pic_out);
printf("encode frame: %5d framesize: %d nal: %d\n", i+1, i_frame_size, i_nal);
if (i_frame_size < 0)
{
printf("Error encode frame: %d.\n", i+1);
goto out;
}
#if 01
else if (i_frame_size)
{
if(!fwrite(nal->p_payload, 1, i_frame_size, fp_dst))
goto out;
}
#endif
// 另一种做法
#if 0
// i_nal有可能为0,有可能多于1
for (int j = 0; j < i_nal; j++)
{
fwrite(nal[j].p_payload, 1, nal[j].i_payload, fp_dst);
}
#endif
}

//flush encoder
while(x264_encoder_delayed_frames(handle))
//while(1)
{
static int cnt = 1;
i_frame_size = x264_encoder_encode(handle, &nal, &i_nal, NULL, pic_out);
printf("flush frame: %d framesize: %d nalsize: %d\n", cnt++, i_frame_size, i_nal);
if(i_frame_size == 0)
{
break;
//goto out;
}
#if 01
else if(i_frame_size)
{
if(!fwrite(nal->p_payload, 1, i_frame_size, fp_dst))
goto out;
}
#endif
// 另一种做法
#if 0
// i_nal有可能为0,有可能多于1
for (int j = 0; j < i_nal; j++)
{
fwrite(nal[j].p_payload, 1, nal[j].i_payload, fp_dst);
}
#endif
}

out:
x264_picture_clean(pic_in);
if (handle)
{
x264_encoder_close(handle);
handle = NULL;
}

if (pic_in)
free(pic_in);
if (pic_out)
free(pic_out);

fclose(fp_src);
fclose(fp_dst);

return 0;
}

使用方法:

1
x264_encode("../src/suzie_qcif_176x144_yuv420p.yuv", 176, 144, 1, "test.h264");

Makefile如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#
# (C) Copyleft 2011~2015
# Late Lee from http://www.latelee.org
#
# A simple Makefile for *ONE* project(c or/and cpp file) in *ONE* directory
#
# note:
# you can put head file(s) in 'include' directory, so it looks
# a little neat.
#
# usage: $ make
# $ make debug=y
#
# log
# 2013-05-14 sth about debug...
###############################################################################

# !!!=== cross compile...
CROSS_COMPILE =

CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
AR = $(CROSS_COMPILE)ar

ARFLAGS = cr
RM = -rm -rf
MAKE = make

CFLAGS := -Wall

#****************************************************************************
# debug can be set to y to include debugging info, or n otherwise
debug := n

#****************************************************************************

ifeq ($(debug), y)
CFLAGS += -ggdb -rdynamic
else
CFLAGS += -O2 -s
endif

# !!!===
DEFS =

CFLAGS += $(DEFS)

LDFLAGS := $(LIBS)

# !!!===
INCDIRS := -I./include

# !!!===
CFLAGS += $(INCDIRS)

# !!!===
LDFLAGS += ./lib/libx264.a ./lib/libx265.a -lpthread -ldl

# !!!===
# source file(s), including c file(s) cpp file(s)
# you can also use $(wildcard *.c), etc.
SRC_C := $(wildcard *.c)
SRC_CPP := $(wildcard *.cpp)

# object file(s)
OBJ_C := $(patsubst %.c,%.o,$(SRC_C))
OBJ_CPP := $(patsubst %.cpp,%.o,$(SRC_CPP))

# !!!===
# executable file
target = a.out

###############################################################################

all: $(target)

$(target): $(OBJ_C) $(OBJ_CPP)
@echo "Generating executable file..." $(notdir $(target))
@$(CXX) $(CFLAGS) $^ -o $(target) $(LDFLAGS)

# make all .c or .cpp
%.o: %.c
@echo "Compiling: " $(addsuffix .c, $(basename $(notdir $@)))
@$(CC) $(CFLAGS) -c $< -o $@

%.o: %.cpp
@echo "Compiling: " $(addsuffix .cpp, $(basename $(notdir $@)))
@$(CXX) $(CFLAGS) -c $< -o $@

clean:
@echo "Cleaning..."
@$(RM) $(target)
@$(RM) *.o *.back *~

.PHONY: all clean

李迟 2015.9.14 中午