1 |
/************************************************************************** |
2 |
* |
3 |
* XVID MPEG-4 VIDEO CODEC - Example for encoding and decoding |
4 |
* |
5 |
* This program is free software; you can redistribute it and/or modify |
6 |
* it under the terms of the GNU General Public License as published by |
7 |
* the Free Software Foundation; either version 2 of the License, or |
8 |
* (at your option) any later version. |
9 |
* |
10 |
* This program is distributed in the hope that it will be useful, |
11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 |
* GNU General Public License for more details. |
14 |
* |
15 |
* You should have received a copy of the GNU General Public License |
16 |
* along with this program; if not, write to the Free Software |
17 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
18 |
* |
19 |
*************************************************************************/ |
20 |
|
21 |
/************************************************************************ |
22 |
* |
23 |
* Test routine for XviD using the XviD-API |
24 |
* (C) Christoph Lampert, 2002/01/19 |
25 |
* |
26 |
* A sequence of YUV pics in PGM file format is encoded and decoded |
27 |
* |
28 |
* The program is plain C and needs no libraries except for libxvidcore, |
29 |
* so with UN*X you simply compile by |
30 |
* |
31 |
* gcc -o xvid_enc_dec xvid_enc_dec.c -lxvidcore |
32 |
* |
33 |
* Run without parameters, then PGM input input is read from stdin. |
34 |
* |
35 |
* PGM/xvid-output is saved, if corresponding flags are set |
36 |
* |
37 |
* input data can be generated e.g. out of MPEG2 with mpeg2dec -o pgmpipe |
38 |
* |
39 |
************************************************************************/ |
40 |
|
41 |
#include <stdio.h> |
42 |
#include <stdint.h> |
43 |
#include <malloc.h> |
44 |
|
45 |
#include "xvid.h" /* comes with XviD */ |
46 |
|
47 |
#define ARG_FRAMERATE 25 |
48 |
#define ARG_BITRATE 900 |
49 |
|
50 |
int QUALITY =5; |
51 |
|
52 |
int XDIM=0; |
53 |
int YDIM=0; // will be set when reading first image |
54 |
int filenr = 0; |
55 |
|
56 |
int save_m4v_flag = 0; // save MPEG4-bytestream? |
57 |
int save_dec_flag = 0; // save decompressed bytestream? |
58 |
char filepath[256] = "./"; // path where to save output |
59 |
|
60 |
void *enchandle = NULL; |
61 |
void *dechandle = NULL; |
62 |
|
63 |
/*********************************************************************/ |
64 |
/* Routines for file input/output, nothing specific to XviD */ |
65 |
/*********************************************************************/ |
66 |
|
67 |
int read_pgmheader(FILE* handle) |
68 |
{ int bytes,xsize,ysize,depth; |
69 |
char dummy[2]; |
70 |
|
71 |
bytes = fread(dummy,1,2,handle); |
72 |
|
73 |
if ( (bytes < 2) || (dummy[0] != 'P') || (dummy[1] != '5' )) |
74 |
return 1; |
75 |
fscanf(handle,"%d %d %d",&xsize,&ysize,&depth); |
76 |
if ( (xsize > 1440) || (ysize > 2880 ) || (depth != 255) ) |
77 |
{ |
78 |
fprintf(stderr,"%d %d %d\n",XDIM,YDIM,depth); |
79 |
return 2; |
80 |
} |
81 |
if ( (XDIM==0) || (YDIM==0) ) |
82 |
{ XDIM=xsize; |
83 |
YDIM=ysize; |
84 |
} |
85 |
|
86 |
return 0; |
87 |
} |
88 |
|
89 |
int read_pgmdata(FILE* handle, unsigned char *image) |
90 |
{ int i; |
91 |
char dummy; |
92 |
|
93 |
unsigned char* buff1_ptr2 = image + XDIM*YDIM; |
94 |
unsigned char* buff1_ptr3 = image + XDIM*YDIM + XDIM/2*YDIM/2; |
95 |
|
96 |
fread(image,XDIM,YDIM,stdin); |
97 |
|
98 |
for (i=0;i<YDIM/2;i++) |
99 |
{ |
100 |
fread(buff1_ptr2,XDIM/2,1,stdin); |
101 |
buff1_ptr2 += XDIM/2; |
102 |
fread(buff1_ptr3,XDIM/2,1,stdin); |
103 |
buff1_ptr3 += XDIM/2; |
104 |
} |
105 |
fread(&dummy,1,1,handle); // should be EOF |
106 |
return 0; |
107 |
} |
108 |
|
109 |
|
110 |
/*********************************************************************/ |
111 |
/* Routines for encoding: init encoder, frame step, release encoder */ |
112 |
/*********************************************************************/ |
113 |
|
114 |
#define FRAMERATE_INCR 1001 |
115 |
int enc_init() |
116 |
{ |
117 |
int xerr; |
118 |
|
119 |
XVID_INIT_PARAM xinit; |
120 |
XVID_ENC_PARAM xparam; |
121 |
|
122 |
xinit.cpu_flags = 0; |
123 |
xvid_init(NULL, 0, &xinit, NULL); |
124 |
|
125 |
xparam.width = XDIM; |
126 |
xparam.height = YDIM; |
127 |
if ((ARG_FRAMERATE - (int)ARG_FRAMERATE) == 0) |
128 |
{ |
129 |
xparam.fincr = 1; |
130 |
xparam.fbase = (int)ARG_FRAMERATE; |
131 |
} |
132 |
else |
133 |
{ |
134 |
xparam.fincr = FRAMERATE_INCR; |
135 |
xparam.fbase = (int)(FRAMERATE_INCR * ARG_FRAMERATE); |
136 |
} |
137 |
xparam.bitrate = ARG_BITRATE*1000; |
138 |
xparam.rc_period = 2000; |
139 |
xparam.rc_reaction_period = 10; |
140 |
xparam.rc_reaction_ratio = 20; |
141 |
xparam.min_quantizer = 1; |
142 |
xparam.max_quantizer = 31; |
143 |
xparam.max_key_interval = (int)ARG_FRAMERATE*10; |
144 |
xparam.motion_search = QUALITY; |
145 |
xparam.lum_masking = 0; // Luminance Masking is still under development |
146 |
xparam.quant_type = 0; // 0=h.263, 1=mpeg4 |
147 |
|
148 |
xerr = xvid_encore(NULL, XVID_ENC_CREATE, &xparam, NULL); |
149 |
enchandle=xparam.handle; |
150 |
|
151 |
return xerr; |
152 |
} |
153 |
|
154 |
int enc_stop() |
155 |
{ int xerr; |
156 |
|
157 |
xerr = xvid_encore(enchandle, XVID_ENC_DESTROY, NULL, NULL); |
158 |
return xerr; |
159 |
} |
160 |
|
161 |
int enc_main(unsigned char* image, unsigned char* bitstream, int *streamlength) |
162 |
{ int xerr; |
163 |
|
164 |
XVID_ENC_FRAME xframe; |
165 |
XVID_ENC_STATS xstats; |
166 |
|
167 |
xframe.bitstream = bitstream; |
168 |
xframe.length = -1; // this is written by the routine |
169 |
|
170 |
xframe.image = image; |
171 |
xframe.colorspace = XVID_CSP_YV12; // defined in <xvid.h> |
172 |
|
173 |
xframe.intra = -1; // let the codec decide between I-frame (1) and P-frame (0) |
174 |
xframe.quant = 0; |
175 |
// xframe.quant = QUANTI; // is quant != 0, use a fixed quant (and ignore bitrate) |
176 |
|
177 |
|
178 |
xerr = xvid_encore(enchandle, XVID_ENC_ENCODE, &xframe, &xstats); |
179 |
|
180 |
/* enc_result->is_key_frame = xframe.intra; |
181 |
enc_result->quantizer = xframe.quant; |
182 |
enc_result->total_bits = xframe.length * 8; |
183 |
enc_result->motion_bits = xstats.hlength * 8; |
184 |
enc_result->texture_bits = enc_result->total_bits - enc_result->motion_bits; |
185 |
*/ |
186 |
|
187 |
/* This is statictical data, e.g. for 2-pass. |
188 |
If you are not interested in any of this, you can use NULL instead of &xstats |
189 |
*/ |
190 |
|
191 |
*streamlength = xframe.length; |
192 |
|
193 |
return xerr; |
194 |
} |
195 |
|
196 |
|
197 |
/*********************************************************************/ |
198 |
/* Routines for decoding: init encoder, frame step, release encoder */ |
199 |
/*********************************************************************/ |
200 |
|
201 |
int dec_init() |
202 |
{ |
203 |
int xerr; |
204 |
|
205 |
XVID_INIT_PARAM xinit; |
206 |
XVID_DEC_PARAM xparam; |
207 |
|
208 |
xinit.cpu_flags = 0; |
209 |
xvid_init(NULL, 0, &xinit, NULL); |
210 |
xparam.width = XDIM; |
211 |
xparam.height = YDIM; |
212 |
|
213 |
xerr = xvid_decore(NULL, XVID_DEC_CREATE, &xparam, NULL); |
214 |
dechandle = xparam.handle; |
215 |
|
216 |
return xerr; |
217 |
} |
218 |
|
219 |
int dec_main(unsigned char *m4v_buffer, unsigned char *rgb_buffer, int m4v_size) |
220 |
{ |
221 |
int xerr; |
222 |
|
223 |
XVID_DEC_FRAME xframe; |
224 |
|
225 |
xframe.bitstream = m4v_buffer; |
226 |
xframe.length = m4v_size; |
227 |
xframe.image = rgb_buffer; |
228 |
xframe.stride = XDIM; |
229 |
xframe.colorspace = XVID_CSP_RGB24; // XVID_CSP_USER is fastest (no memcopy involved) |
230 |
|
231 |
/* |
232 |
xframe.colorspace = XVID_CSP_NULL; // use this if you want to skip a frame (no output) |
233 |
*/ |
234 |
|
235 |
xerr = xvid_decore(dechandle, XVID_DEC_DECODE, &xframe, NULL); |
236 |
|
237 |
return xerr; |
238 |
} |
239 |
|
240 |
int dec_stop() |
241 |
{ |
242 |
int xerr; |
243 |
xerr = xvid_decore(dechandle, XVID_DEC_DESTROY, NULL, NULL); |
244 |
|
245 |
return xerr; |
246 |
} |
247 |
|
248 |
|
249 |
/*********************************************************************/ |
250 |
/* Main program */ |
251 |
/*********************************************************************/ |
252 |
|
253 |
int main() |
254 |
{ |
255 |
unsigned char *divx_buffer = NULL; |
256 |
unsigned char *yuv_buffer = NULL; |
257 |
unsigned char *rgb_buffer = NULL; |
258 |
|
259 |
int status; |
260 |
int frame_size; |
261 |
int m4v_size; |
262 |
|
263 |
char filename[256]; |
264 |
|
265 |
FILE *filehandle; |
266 |
|
267 |
/* read YUV in pgm format from stdin */ |
268 |
if (read_pgmheader(stdin)) |
269 |
{ |
270 |
printf("Wrong input format, I want YUV encapsulated in PGM\n"); |
271 |
return 0; |
272 |
} |
273 |
|
274 |
/* now we know the sizes, so allocate memory */ |
275 |
|
276 |
yuv_buffer = (unsigned char *) malloc(XDIM*YDIM); |
277 |
if (!yuv_buffer) |
278 |
goto free_all_memory; // goto is one of the most underestimated instructions in C !!! |
279 |
|
280 |
divx_buffer = (unsigned char *) malloc(XDIM*YDIM*2); // this should really be enough! |
281 |
if (!divx_buffer) |
282 |
goto free_all_memory; // actually, every program should contain at least one goto |
283 |
|
284 |
YDIM = YDIM*2/3; // PGM is YUV 4:2:0 format, so real image height is *2/3 of PGM picture |
285 |
|
286 |
rgb_buffer = (unsigned char *) malloc(XDIM*YDIM*4); |
287 |
if (!rgb_buffer) |
288 |
goto free_all_memory; // but the more, the better! |
289 |
|
290 |
/*********************************************************************/ |
291 |
/* DIVX PART Start */ |
292 |
/*********************************************************************/ |
293 |
|
294 |
status = enc_init(); |
295 |
// if (status) |
296 |
printf("Encore INIT return %d, handle=%lx\n", status, enchandle); |
297 |
|
298 |
status = dec_init(); |
299 |
// if (status) |
300 |
printf("Decore INIT return %d\n", status); |
301 |
|
302 |
|
303 |
/*********************************************************************/ |
304 |
/* Main loop */ |
305 |
/*********************************************************************/ |
306 |
|
307 |
do |
308 |
{ |
309 |
status = read_pgmdata(stdin, yuv_buffer); // read PGM data (YUV-format) |
310 |
if (status) |
311 |
{ |
312 |
fprintf(stderr, "Couldn't read PGM body: %d\n", status); /* this should not happen */ |
313 |
continue; |
314 |
} |
315 |
|
316 |
/*********************************************************************/ |
317 |
/* encode and decode this frame */ |
318 |
/*********************************************************************/ |
319 |
|
320 |
status = enc_main(yuv_buffer, divx_buffer, &m4v_size); |
321 |
|
322 |
printf("Frame %5d: encore-ENCODE status %d, m4v-stream length=%7d bytes\n", |
323 |
filenr, status, m4v_size); |
324 |
|
325 |
if (save_m4v_flag) |
326 |
{ |
327 |
sprintf(filename, "%sframe%05d.m4v", filepath, filenr); |
328 |
filehandle = fopen(filename, "wb"); |
329 |
fwrite(divx_buffer, m4v_size, 1, filehandle); |
330 |
fclose(filehandle); |
331 |
} |
332 |
|
333 |
status = dec_main(divx_buffer, rgb_buffer, m4v_size); |
334 |
if (status) |
335 |
printf("Frame %5d: decore status %d\n",status); |
336 |
|
337 |
if (save_dec_flag) |
338 |
{ |
339 |
sprintf(filename, "%sdec%05d.ppm", filepath, filenr); |
340 |
filehandle=fopen(filename,"wb"); |
341 |
if (filehandle) |
342 |
{ |
343 |
fprintf(filehandle,"P6\n"); // rgb24 in PPM format |
344 |
fprintf(filehandle,"%d %d 255\n",XDIM,YDIM); |
345 |
fwrite(rgb_buffer,XDIM,YDIM*3,filehandle); |
346 |
fclose(filehandle); |
347 |
} |
348 |
} |
349 |
|
350 |
filenr++; |
351 |
status = read_pgmheader(stdin); // if it was the last PGM, stop after it |
352 |
if (status) |
353 |
{ |
354 |
fprintf(stderr, "Couldn't read next PGM header: %d\n", status); /* normally, just end of file */ |
355 |
} |
356 |
} |
357 |
while (!status); |
358 |
|
359 |
/*********************************************************************/ |
360 |
/* DIVX PART Stop */ |
361 |
/*********************************************************************/ |
362 |
|
363 |
dec_stop(); |
364 |
// if (status) |
365 |
printf("Encore RELEASE return %d\n", status); |
366 |
|
367 |
enc_stop(); |
368 |
// if (status) |
369 |
printf("Decore RELEASE return %d\n", status); |
370 |
|
371 |
free_all_memory: |
372 |
|
373 |
if (rgb_buffer) |
374 |
free(rgb_buffer); |
375 |
if (divx_buffer) |
376 |
free(divx_buffer); |
377 |
if (yuv_buffer) |
378 |
free(yuv_buffer); |
379 |
|
380 |
return 0; |
381 |
} |