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 OpenDivX/Divx4-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 odivx_enc_dec odivx_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 <malloc.h> |
43 |
|
44 |
#include "divx4.h" |
45 |
|
46 |
#define ARG_FRAMERATE 25 |
47 |
#define ARG_BITRATE 900 |
48 |
|
49 |
int QUALITY =5; |
50 |
int QUANTI = 0; // used for fixed-quantizer encoding |
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 |
59 |
|
60 |
void *enchandle = NULL; // enchandle is a void*, written by encore |
61 |
const long dechandle = 0x0815; // dechandle is a unique constant!!! |
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 status; |
118 |
ENC_PARAM enc_param; |
119 |
|
120 |
enc_param.x_dim = XDIM; |
121 |
enc_param.y_dim = YDIM; |
122 |
enc_param.framerate = ARG_FRAMERATE; // a float |
123 |
enc_param.bitrate = ARG_BITRATE*1000; |
124 |
|
125 |
enc_param.rc_period = 2000; |
126 |
enc_param.rc_reaction_period = 10; |
127 |
enc_param.rc_reaction_ratio = 20; |
128 |
|
129 |
enc_param.max_quantizer = 31; |
130 |
enc_param.min_quantizer = 1; |
131 |
|
132 |
enc_param.quality = QUALITY; |
133 |
|
134 |
enc_param.use_bidirect = 0; // use bidirectional coding |
135 |
enc_param.deinterlace = 0; // fast deinterlace |
136 |
enc_param.obmc = 0; // flag to enable overlapped block motion compensation mode |
137 |
|
138 |
enc_param.max_key_interval = (int)ARG_FRAMERATE*10; |
139 |
enc_param.handle = NULL; //will be filled by encore |
140 |
|
141 |
status = encore(enchandle, ENC_OPT_INIT, &enc_param, NULL); |
142 |
enchandle = enc_param.handle; |
143 |
|
144 |
// if (status) |
145 |
printf("Encore INIT return %d, handle=%lx\n", status, enchandle); |
146 |
|
147 |
return status; |
148 |
} |
149 |
|
150 |
int enc_stop() |
151 |
{ int status; |
152 |
|
153 |
status = encore(enchandle, ENC_OPT_RELEASE, NULL, NULL); |
154 |
// if (status) |
155 |
printf("Encore RELEASE return %d\n", status); |
156 |
|
157 |
return status; |
158 |
} |
159 |
|
160 |
int enc_main(unsigned char* image, unsigned char* bitstream, int *streamlength) |
161 |
{ int status; |
162 |
|
163 |
ENC_FRAME enc_frame; |
164 |
ENC_RESULT enc_result; |
165 |
|
166 |
enc_frame.image = (void *) image; |
167 |
enc_frame.bitstream = (void *) bitstream; |
168 |
enc_frame.length = 0; // filled by encore |
169 |
enc_frame.colorspace = ENC_CSP_YV12; // input is YUV |
170 |
enc_frame.mvs = NULL; // unsupported |
171 |
|
172 |
if (QUANTI==0) |
173 |
{ |
174 |
status = encore(enchandle, ENC_OPT_ENCODE, &enc_frame, &enc_result); |
175 |
} |
176 |
else |
177 |
{ enc_frame.quant = QUANTI; |
178 |
enc_frame.intra = -1; // let encoder decide if frame is INTER/INTRA |
179 |
status = encore(enchandle, ENC_OPT_ENCODE_VBR, &enc_frame, &enc_result); |
180 |
} |
181 |
|
182 |
/* ENC_OPT_ENCODE is used to encode using ratecontrol alg. and bitrate from enc_init, |
183 |
ENC_OPT_ENCODE_VBR is used to encode with fixed quantizer and INTER/INTRA mode |
184 |
*/ |
185 |
|
186 |
*streamlength = enc_frame.length; |
187 |
|
188 |
return status; |
189 |
} |
190 |
|
191 |
|
192 |
/*********************************************************************/ |
193 |
/* Routines for decoding: init encoder, frame step, release encoder */ |
194 |
/*********************************************************************/ |
195 |
|
196 |
int dec_init() |
197 |
{ |
198 |
int status; |
199 |
|
200 |
DEC_PARAM dec_param; |
201 |
DEC_SET dec_set; |
202 |
|
203 |
dec_param.x_dim = XDIM; |
204 |
dec_param.y_dim = YDIM; |
205 |
dec_param.output_format = DEC_RGB24; // output color format, , see <decore.h> |
206 |
|
207 |
dec_param.time_incr = 20; |
208 |
|
209 |
status = decore(dechandle, DEC_OPT_INIT, &dec_param, NULL); |
210 |
// if (status) |
211 |
printf("Decore INIT return %d\n", status); |
212 |
|
213 |
// We don't do any postprocessing here... |
214 |
|
215 |
/* dec_set.postproc_level = 0; |
216 |
status = decore(dechandle, DEC_OPT_SETPP, &dec_set, NULL); |
217 |
if (status) |
218 |
printf("Decore POSTPROC %d return %d\n", dec_set.postproc_level, status); |
219 |
*/ |
220 |
|
221 |
return status; |
222 |
} |
223 |
|
224 |
int dec_main(unsigned char *m4v_buffer, unsigned char *rgb_buffer, int m4v_size) |
225 |
{ |
226 |
int status; |
227 |
|
228 |
static DEC_FRAME dec_frame; |
229 |
static DEC_FRAME_INFO dec_frame_info; |
230 |
|
231 |
dec_frame.length = m4v_size; |
232 |
dec_frame.bitstream = m4v_buffer; |
233 |
dec_frame.bmp = rgb_buffer; |
234 |
dec_frame.render_flag = 1; // 0 means: skip this frame |
235 |
dec_frame.stride = XDIM; |
236 |
|
237 |
status = decore(dechandle, DEC_OPT_FRAME, &dec_frame, &dec_frame_info); |
238 |
if (status) |
239 |
printf("Decore Frame status %d!\n", status); |
240 |
|
241 |
return status; |
242 |
} |
243 |
|
244 |
int dec_stop() |
245 |
{ |
246 |
int status; |
247 |
status = decore(dechandle, DEC_OPT_RELEASE, NULL, NULL); |
248 |
// if (status) |
249 |
printf("Decore RELEASE return %d\n", status); |
250 |
return status; |
251 |
} |
252 |
|
253 |
|
254 |
/*********************************************************************/ |
255 |
/* Main program */ |
256 |
/*********************************************************************/ |
257 |
|
258 |
int main() |
259 |
{ |
260 |
unsigned char *divx_buffer = NULL; |
261 |
unsigned char *yuv_buffer = NULL; |
262 |
unsigned char *rgb_buffer = NULL; |
263 |
|
264 |
int status; |
265 |
int frame_size; |
266 |
int m4v_size; |
267 |
|
268 |
char filename[256]; |
269 |
|
270 |
FILE *filehandle; |
271 |
|
272 |
/* read YUV in pgm format from stdin */ |
273 |
if (read_pgmheader(stdin)) |
274 |
{ |
275 |
printf("Wrong input format, I want YUV encapsulated in PGM\n"); |
276 |
return 0; |
277 |
} |
278 |
|
279 |
/* now we know the sizes, so allocate memory */ |
280 |
yuv_buffer = (unsigned char *) malloc(XDIM*YDIM); |
281 |
if (!yuv_buffer) |
282 |
goto free_all_memory; // goto is one of the most underestimated instructions in C !!! |
283 |
divx_buffer = (unsigned char *) malloc(XDIM*YDIM*2); // this should really be enough! |
284 |
if (!divx_buffer) |
285 |
goto free_all_memory; // actually, every program should contain a goto |
286 |
|
287 |
YDIM = YDIM*2/3; // PGM is YUV 4:2:0 format, so height is *3/2 too much |
288 |
|
289 |
rgb_buffer = (unsigned char *) malloc(XDIM*YDIM*4); |
290 |
if (!rgb_buffer) |
291 |
goto free_all_memory; // the more, the better! |
292 |
|
293 |
/*********************************************************************/ |
294 |
/* DIVX PART Start */ |
295 |
/*********************************************************************/ |
296 |
|
297 |
status = enc_init(); |
298 |
// if (status) |
299 |
printf("Encore INIT return %d, handle=%lx\n", status, enchandle); |
300 |
|
301 |
status = dec_init(); |
302 |
// if (status) |
303 |
printf("Decore INIT return %d\n", status); |
304 |
|
305 |
|
306 |
/*********************************************************************/ |
307 |
/* Main loop */ |
308 |
/*********************************************************************/ |
309 |
|
310 |
do |
311 |
{ |
312 |
status = read_pgmdata(stdin, yuv_buffer); // read PGM data (YUV-format) |
313 |
if (status) |
314 |
{ |
315 |
fprintf(stderr, "PGM-Data-Error: %d\n", status); /* this should not happen */ |
316 |
continue; |
317 |
} |
318 |
|
319 |
/*********************************************************************/ |
320 |
/* encode and decode this frame */ |
321 |
/*********************************************************************/ |
322 |
|
323 |
status = enc_main(yuv_buffer, divx_buffer, &m4v_size); |
324 |
|
325 |
printf("Frame %5d: encore-ENCODE status %d, m4v-stream length=%7d bytes\n", |
326 |
filenr, status, m4v_size); |
327 |
|
328 |
if (save_m4v_flag) |
329 |
{ |
330 |
sprintf(filename, "%s/frame%05d.m4v", filepath, filenr); |
331 |
filehandle = fopen(filename, "wb"); |
332 |
fwrite(divx_buffer, m4v_size, 1, filehandle); |
333 |
fclose(filehandle); |
334 |
} |
335 |
|
336 |
status = dec_main(divx_buffer, rgb_buffer, m4v_size); |
337 |
if (status) |
338 |
printf("Frame %5d: decore status %d\n",status); |
339 |
|
340 |
if (save_dec_flag) |
341 |
{ |
342 |
sprintf(filename, "%s/dec%05d.ppm", filepath, filenr); |
343 |
filehandle=fopen(filename,"wb"); |
344 |
if (filehandle) |
345 |
{ |
346 |
fprintf(filehandle,"P6\n"); // rgb24 in PPM format |
347 |
fprintf(filehandle,"%d %d 255\n",XDIM,YDIM); |
348 |
fwrite(rgb_buffer,XDIM,YDIM*3,filehandle); |
349 |
fclose(filehandle); |
350 |
} |
351 |
} |
352 |
|
353 |
filenr++; |
354 |
status = read_pgmheader(stdin); // if it was the last PGM, stop after it |
355 |
if (status) |
356 |
{ |
357 |
fprintf(stderr, "PGM-Header-Error: %d\n", status); /* normally, just end of file */ |
358 |
} |
359 |
} |
360 |
while (!status); |
361 |
|
362 |
|
363 |
/*********************************************************************/ |
364 |
/* DIVX PART Stop */ |
365 |
/*********************************************************************/ |
366 |
|
367 |
|
368 |
/* Stop XviD */ |
369 |
|
370 |
dec_stop(); |
371 |
// if (status) |
372 |
printf("Encore RELEASE return %d\n", status); |
373 |
|
374 |
enc_stop(); |
375 |
// if (status) |
376 |
printf("Decore RELEASE return %d\n", status); |
377 |
|
378 |
free_all_memory: |
379 |
|
380 |
if (rgb_buffer) |
381 |
free(rgb_buffer); |
382 |
if (divx_buffer) |
383 |
free(divx_buffer); |
384 |
if (yuv_buffer) |
385 |
free(yuv_buffer); |
386 |
|
387 |
return 0; |
388 |
} |