[svn] / branches / dev-api-3 / vfw / src / 2pass.c Repository:
ViewVC logotype

Annotation of /branches/dev-api-3/vfw/src/2pass.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 105 - (view) (download)
Original Path: trunk/vfw/src/2pass.c

1 : h 102 /**************************************************************************
2 :     *
3 :     * XVID 2PASS CODE
4 :     * codec
5 :     *
6 :     * This program is free software; you can redistribute it and/or modify
7 :     * it under the terms of the GNU General Public License as published by
8 :     * the Free Software Foundation; either version 2 of the License, or
9 :     * (at your option) any later version.
10 :     *
11 :     * This program is distributed in the hope that it will be useful,
12 :     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 :     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 :     * GNU General Public License for more details.
15 :     *
16 :     * You should have received a copy of the GNU General Public License
17 :     * along with this program; if not, write to the Free Software
18 :     * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 :     *
20 :     *************************************************************************/
21 :    
22 :     /**************************************************************************
23 :     *
24 :     * History:
25 :     *
26 :     * 31.03.2002 inital version;
27 :     *
28 :     *************************************************************************/
29 :    
30 :     #include <windows.h>
31 :     #include <math.h>
32 :    
33 :     #include "2pass.h"
34 :    
35 :    
36 :     int codec_2pass_init(CODEC* codec)
37 :     {
38 :     TWOPASS *twopass = &codec->twopass;
39 :     DWORD version = -20;
40 :     DWORD read, wrote;
41 :    
42 :     int frames = 0, credits_frames = 0, i_frames = 0;
43 :     __int64 total_ext = 0, total = 0, i_total = 0, i_boost_total = 0, start = 0, end = 0, start_curved = 0, end_curved = 0;
44 :     __int64 desired = (__int64)codec->config.desired_size * 1024;
45 :    
46 :     double total1 = 0.0;
47 :     double total2 = 0.0;
48 :    
49 :     if (codec->config.hinted_me)
50 :     {
51 :     codec->twopass.hintstream = malloc(100000);
52 :    
53 :     if (codec->twopass.hintstream == NULL)
54 :     {
55 :     DEBUGERR("couldn't allocate memory for mv hints");
56 :     return ICERR_ERROR;
57 :     }
58 :     }
59 :    
60 :     switch (codec->config.mode)
61 :     {
62 :     case DLG_MODE_2PASS_1 :
63 :     twopass->stats1 = CreateFile(codec->config.stats1, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
64 :     if (twopass->stats1 == INVALID_HANDLE_VALUE)
65 :     {
66 :     DEBUGERR("2pass init error - couldn't create stats1");
67 :     return ICERR_ERROR;
68 :     }
69 :     if (WriteFile(twopass->stats1, &version, sizeof(DWORD), &wrote, 0) == 0 || wrote != sizeof(DWORD))
70 :     {
71 :     CloseHandle(twopass->stats1);
72 :     twopass->stats1 = INVALID_HANDLE_VALUE;
73 :     DEBUGERR("2pass init error - couldn't write to stats1");
74 :     return ICERR_ERROR;
75 :     }
76 :     break;
77 :    
78 :     case DLG_MODE_2PASS_2_INT :
79 :     case DLG_MODE_2PASS_2_EXT :
80 :     twopass->stats1 = CreateFile(codec->config.stats1, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
81 :     if (twopass->stats1 == INVALID_HANDLE_VALUE)
82 :     {
83 :     DEBUGERR("2pass init error - couldn't open stats1");
84 :     return ICERR_ERROR;
85 :     }
86 :     if (ReadFile(twopass->stats1, &version, sizeof(DWORD), &read, 0) == 0 || read != sizeof(DWORD))
87 :     {
88 :     CloseHandle(twopass->stats1);
89 :     twopass->stats1 = INVALID_HANDLE_VALUE;
90 :     DEBUGERR("2pass init error - couldn't read from stats1");
91 :     return ICERR_ERROR;
92 :     }
93 :     if (version != -20)
94 :     {
95 :     CloseHandle(twopass->stats1);
96 :     twopass->stats1 = INVALID_HANDLE_VALUE;
97 :     DEBUGERR("2pass init error - wrong .stats version");
98 :     return ICERR_ERROR;
99 :     }
100 :    
101 :     if (codec->config.mode == DLG_MODE_2PASS_2_EXT)
102 :     {
103 :     if (twopass->stats2 != INVALID_HANDLE_VALUE)
104 :     {
105 :     CloseHandle(twopass->stats2);
106 :     }
107 :    
108 :     twopass->stats2 = CreateFile(codec->config.stats2, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
109 :    
110 :     if (twopass->stats2 == INVALID_HANDLE_VALUE)
111 :     {
112 :     CloseHandle(twopass->stats1);
113 :     twopass->stats1 = INVALID_HANDLE_VALUE;
114 :     DEBUGERR("2pass init error - couldn't open stats2");
115 :     return ICERR_ERROR;
116 :     }
117 :     if (ReadFile(twopass->stats2, &version, sizeof(DWORD), &read, 0) == 0 || read != sizeof(DWORD))
118 :     {
119 :     CloseHandle(twopass->stats1);
120 :     twopass->stats1 = INVALID_HANDLE_VALUE;
121 :     CloseHandle(twopass->stats2);
122 :     twopass->stats2 = INVALID_HANDLE_VALUE;
123 :     DEBUGERR("2pass init error - couldn't read from stats2");
124 :     return ICERR_ERROR;
125 :     }
126 :     if (version != -20)
127 :     {
128 :     CloseHandle(twopass->stats1);
129 :     twopass->stats1 = INVALID_HANDLE_VALUE;
130 :     CloseHandle(twopass->stats2);
131 :     twopass->stats2 = INVALID_HANDLE_VALUE;
132 :     DEBUGERR("2pass init error - wrong .stats version");
133 :     return ICERR_ERROR;
134 :     }
135 :    
136 :     while (1)
137 :     {
138 :     if (!ReadFile(twopass->stats1, &twopass->nns1, sizeof(NNSTATS), &read, NULL) || read != sizeof(NNSTATS) ||
139 :     !ReadFile(twopass->stats2, &twopass->nns2, sizeof(NNSTATS), &read, NULL) || read != sizeof(NNSTATS))
140 :     {
141 :     DWORD err = GetLastError();
142 :    
143 :     if (err == ERROR_HANDLE_EOF || err == ERROR_SUCCESS)
144 :     {
145 :     break;
146 :     }
147 :     else
148 :     {
149 :     CloseHandle(twopass->stats1);
150 :     CloseHandle(twopass->stats2);
151 :     twopass->stats1 = INVALID_HANDLE_VALUE;
152 :     twopass->stats2 = INVALID_HANDLE_VALUE;
153 :     DEBUGERR("2pass init error - incomplete stats1/stats2 record?");
154 :     return ICERR_ERROR;
155 :     }
156 :     }
157 :    
158 :     if (!codec_is_in_credits(&codec->config, frames))
159 :     {
160 :     if (twopass->nns1.quant & NNSTATS_KEYFRAME)
161 :     {
162 :     i_boost_total = twopass->nns2.bytes * codec->config.keyframe_boost / 100;
163 :     i_total += twopass->nns2.bytes;
164 :     ++i_frames;
165 :     }
166 :    
167 :     total += twopass->nns1.bytes;
168 :     total_ext += twopass->nns2.bytes;
169 :     }
170 :     else
171 :     ++credits_frames;
172 :    
173 :     ++frames;
174 :     }
175 :    
176 :     twopass->movie_curve = ((double)(total_ext + i_boost_total) / total_ext);
177 :     twopass->average_frame = ((double)(total_ext - i_total) / (frames - credits_frames - i_frames) / twopass->movie_curve);
178 :    
179 :     SetFilePointer(twopass->stats1, sizeof(DWORD), 0, FILE_BEGIN);
180 :     SetFilePointer(twopass->stats2, sizeof(DWORD), 0, FILE_BEGIN);
181 :    
182 :     // perform prepass to compensate for over/undersizing
183 :     frames = 0;
184 :    
185 :     if (codec->config.use_alt_curve)
186 :     {
187 :     twopass->alt_curve_low = twopass->average_frame - twopass->average_frame * (double)codec->config.alt_curve_low_dist / 100.0;
188 :     twopass->alt_curve_low_diff = twopass->average_frame - twopass->alt_curve_low;
189 :     twopass->alt_curve_high = twopass->average_frame + twopass->average_frame * (double)codec->config.alt_curve_high_dist / 100.0;
190 :     twopass->alt_curve_high_diff = twopass->alt_curve_high - twopass->average_frame;
191 :     if (codec->config.alt_curve_use_auto)
192 :     {
193 :     if (total > total_ext)
194 :     {
195 :     codec->config.alt_curve_min_rel_qual = (int)(100.0 - (100.0 - 100.0 / ((double)total / (double)total_ext)) * (double)codec->config.alt_curve_auto_str / 100.0);
196 :     if (codec->config.alt_curve_min_rel_qual < 20)
197 :     codec->config.alt_curve_min_rel_qual = 20;
198 :     }
199 :     else
200 :     codec->config.alt_curve_min_rel_qual = 100;
201 :     }
202 :     twopass->alt_curve_mid_qual = (1.0 + (double)codec->config.alt_curve_min_rel_qual / 100.0) / 2.0;
203 :     twopass->alt_curve_qual_dev = 1.0 - twopass->alt_curve_mid_qual;
204 :     if (codec->config.alt_curve_low_dist > 100)
205 :     {
206 :     switch(codec->config.alt_curve_type)
207 :     {
208 :     case 2: // Sine Curve (high aggressiveness)
209 :     twopass->alt_curve_qual_dev *= 2.0 / (1.0 +
210 :     sin(DEG2RAD * (twopass->average_frame * 90.0 / twopass->alt_curve_low_diff)));
211 :     twopass->alt_curve_mid_qual = 1.0 - twopass->alt_curve_qual_dev *
212 :     sin(DEG2RAD * (twopass->average_frame * 90.0 / twopass->alt_curve_low_diff));
213 :     break;
214 :     case 1: // Linear (medium aggressiveness)
215 :     twopass->alt_curve_qual_dev *= 2.0 / (1.0 +
216 :     twopass->average_frame / twopass->alt_curve_low_diff);
217 :     twopass->alt_curve_mid_qual = 1.0 - twopass->alt_curve_qual_dev *
218 :     twopass->average_frame / twopass->alt_curve_low_diff;
219 :     break;
220 :     case 0: // Cosine Curve (low aggressiveness)
221 :     twopass->alt_curve_qual_dev *= 2.0 / (1.0 +
222 :     (1.0 - cos(DEG2RAD * (twopass->average_frame * 90.0 / twopass->alt_curve_low_diff))));
223 :     twopass->alt_curve_mid_qual = 1.0 - twopass->alt_curve_qual_dev *
224 :     (1.0 - cos(DEG2RAD * (twopass->average_frame * 90.0 / twopass->alt_curve_low_diff)));
225 :     }
226 :     }
227 :     }
228 :    
229 :     while (1)
230 :     {
231 :     if (!ReadFile(twopass->stats1, &twopass->nns1, sizeof(NNSTATS), &read, NULL) || read != sizeof(NNSTATS) ||
232 :     !ReadFile(twopass->stats2, &twopass->nns2, sizeof(NNSTATS), &read, NULL) || read != sizeof(NNSTATS))
233 :     {
234 :     DWORD err = GetLastError();
235 :    
236 :     if (err == ERROR_HANDLE_EOF || err == ERROR_SUCCESS)
237 :     {
238 :     break;
239 :     }
240 :     else
241 :     {
242 :     CloseHandle(twopass->stats1);
243 :     CloseHandle(twopass->stats2);
244 :     twopass->stats1 = INVALID_HANDLE_VALUE;
245 :     twopass->stats2 = INVALID_HANDLE_VALUE;
246 :     DEBUGERR("2pass init error - incomplete stats1/stats2 record?");
247 :     return ICERR_ERROR;
248 :     }
249 :     }
250 :    
251 :     if (!codec_is_in_credits(&codec->config, frames) &&
252 :     !(twopass->nns1.quant & NNSTATS_KEYFRAME))
253 :     {
254 :     double dbytes = twopass->nns2.bytes / twopass->movie_curve;
255 :     total1 += dbytes;
256 :    
257 :     if (codec->config.use_alt_curve)
258 :     {
259 :     if (dbytes > twopass->average_frame)
260 :     {
261 :     if (dbytes >= twopass->alt_curve_high)
262 :     total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev);
263 :     else
264 :     {
265 :     switch(codec->config.alt_curve_type)
266 :     {
267 :     case 2:
268 :     total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
269 :     sin(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_high_diff)));
270 :     break;
271 :     case 1:
272 :     total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
273 :     (dbytes - twopass->average_frame) / twopass->alt_curve_high_diff);
274 :     break;
275 :     case 0:
276 :     total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
277 :     (1.0 - cos(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_high_diff))));
278 :     }
279 :     }
280 :     }
281 :     else
282 :     {
283 :     if (dbytes <= twopass->alt_curve_low)
284 :     total2 += dbytes;
285 :     else
286 :     {
287 :     switch(codec->config.alt_curve_type)
288 :     {
289 :     case 2:
290 :     total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
291 :     sin(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_low_diff)));
292 :     break;
293 :     case 1:
294 :     total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
295 :     (dbytes - twopass->average_frame) / twopass->alt_curve_low_diff);
296 :     break;
297 :     case 0:
298 :     total2 += dbytes * (twopass->alt_curve_mid_qual + twopass->alt_curve_qual_dev *
299 :     (1.0 - cos(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_low_diff))));
300 :     }
301 :     }
302 :     }
303 :     }
304 :     else
305 :     {
306 :     if (dbytes > twopass->average_frame)
307 :     {
308 :     total2 += ((double)dbytes + (twopass->average_frame - dbytes) *
309 :     codec->config.curve_compression_high / 100.0);
310 :     }
311 :     else
312 :     {
313 :     total2 += ((double)dbytes + (twopass->average_frame - dbytes) *
314 :     codec->config.curve_compression_low / 100.0);
315 :     }
316 :     }
317 :     }
318 :    
319 :     ++frames;
320 :     }
321 :    
322 :     twopass->curve_comp_scale = total1 / total2;
323 :    
324 :     if (!codec->config.use_alt_curve)
325 :     {
326 :     int asymmetric_average_frame;
327 :     char s[100];
328 :    
329 :     asymmetric_average_frame = (int)(twopass->average_frame * twopass->curve_comp_scale);
330 :     wsprintf(s, "middle frame size for asymmetric curve compression: %i", asymmetric_average_frame);
331 :     DEBUG2P(s);
332 :     }
333 :    
334 :     SetFilePointer(twopass->stats1, sizeof(DWORD), 0, FILE_BEGIN);
335 :     SetFilePointer(twopass->stats2, sizeof(DWORD), 0, FILE_BEGIN);
336 :     }
337 :     else // DLG_MODE_2PASS_2_INT
338 :     {
339 :     while (1)
340 :     {
341 :     if (!ReadFile(twopass->stats1, &twopass->nns1, sizeof(NNSTATS), &read, NULL) || read != sizeof(NNSTATS))
342 :     {
343 :     DWORD err = GetLastError();
344 :    
345 :     if (err == ERROR_HANDLE_EOF || err == ERROR_SUCCESS)
346 :     {
347 :     break;
348 :     }
349 :     else
350 :     {
351 :     CloseHandle(twopass->stats1);
352 :     twopass->stats1 = INVALID_HANDLE_VALUE;
353 :     DEBUGERR("2pass init error - incomplete stats2 record?");
354 :     return ICERR_ERROR;
355 :     }
356 :     }
357 :    
358 :     if (codec_is_in_credits(&codec->config, frames) == CREDITS_START)
359 :     {
360 :     start += twopass->nns1.bytes;
361 :     ++credits_frames;
362 :     }
363 :     else if (codec_is_in_credits(&codec->config, frames) == CREDITS_END)
364 :     {
365 :     end += twopass->nns1.bytes;
366 :     ++credits_frames;
367 :     }
368 :     else if (twopass->nns1.quant & NNSTATS_KEYFRAME)
369 :     {
370 :     i_total += twopass->nns1.bytes + twopass->nns1.bytes * codec->config.keyframe_boost / 100;
371 :     total += twopass->nns1.bytes * codec->config.keyframe_boost / 100;
372 :     ++i_frames;
373 :     }
374 :    
375 :     total += twopass->nns1.bytes;
376 :    
377 :     ++frames;
378 :     }
379 :    
380 :     // compensate for avi frame overhead
381 :     desired -= frames * 24;
382 :    
383 :     switch (codec->config.credits_mode)
384 :     {
385 :     case CREDITS_MODE_RATE :
386 :    
387 :     // credits curve = (total / desired_size) * (100 / credits_rate)
388 :     twopass->credits_start_curve = twopass->credits_end_curve =
389 :     ((double)total / desired) * ((double)100 / codec->config.credits_rate);
390 :    
391 :     start_curved = (__int64)(start / twopass->credits_start_curve);
392 :     end_curved = (__int64)(end / twopass->credits_end_curve);
393 :    
394 :     // movie curve = (total - credits) / (desired_size - curved credits)
395 :     twopass->movie_curve = (double)
396 :     (total - start - end) /
397 :     (desired - start_curved - end_curved);
398 :    
399 :     break;
400 :    
401 :     case CREDITS_MODE_QUANT :
402 :    
403 :     // movie curve = (total - credits) / (desired_size - credits)
404 :     twopass->movie_curve = (double)
405 :     (total - start - end) / (desired - start - end);
406 :    
407 :     // aid the average asymmetric frame calculation below
408 :     start_curved = start;
409 :     end_curved = end;
410 :    
411 :     break;
412 :    
413 :     case CREDITS_MODE_SIZE :
414 :    
415 :     // start curve = (start / start desired size)
416 :     twopass->credits_start_curve = (double)
417 :     (start / 1024) / codec->config.credits_start_size;
418 :    
419 :     // end curve = (end / end desired size)
420 :     twopass->credits_end_curve = (double)
421 :     (end / 1024) / codec->config.credits_end_size;
422 :    
423 :     start_curved = (__int64)(start / twopass->credits_start_curve);
424 :     end_curved = (__int64)(end / twopass->credits_end_curve);
425 :    
426 :     // movie curve = (total - credits) / (desired_size - curved credits)
427 :     twopass->movie_curve = (double)
428 :     (total - start - end) /
429 :     (desired - start_curved - end_curved);
430 :    
431 :     break;
432 :     }
433 :    
434 :     // average frame size = (desired - curved credits - curved keyframes) /
435 :     // (frames - credits frames - keyframes)
436 :     twopass->average_frame = (double)
437 :     (desired - start_curved - end_curved - (i_total / twopass->movie_curve)) /
438 :     (frames - credits_frames - i_frames);
439 :    
440 :     SetFilePointer(twopass->stats1, sizeof(DWORD), 0, FILE_BEGIN);
441 :    
442 :     // perform prepass to compensate for over/undersizing
443 :     frames = 0;
444 :    
445 :     if (codec->config.use_alt_curve)
446 :     {
447 :     twopass->alt_curve_low = twopass->average_frame - twopass->average_frame * (double)codec->config.alt_curve_low_dist / 100.0;
448 :     twopass->alt_curve_low_diff = twopass->average_frame - twopass->alt_curve_low;
449 :     twopass->alt_curve_high = twopass->average_frame + twopass->average_frame * (double)codec->config.alt_curve_high_dist / 100.0;
450 :     twopass->alt_curve_high_diff = twopass->alt_curve_high - twopass->average_frame;
451 :     if (codec->config.alt_curve_use_auto)
452 :     {
453 :     if (twopass->movie_curve > 1.0)
454 :     {
455 :     codec->config.alt_curve_min_rel_qual = (int)(100.0 - (100.0 - 100.0 / twopass->movie_curve) * (double)codec->config.alt_curve_auto_str / 100.0);
456 :     if (codec->config.alt_curve_min_rel_qual < 20)
457 :     codec->config.alt_curve_min_rel_qual = 20;
458 :     }
459 :     else
460 :     codec->config.alt_curve_min_rel_qual = 100;
461 :     }
462 :     twopass->alt_curve_mid_qual = (1.0 + (double)codec->config.alt_curve_min_rel_qual / 100.0) / 2.0;
463 :     twopass->alt_curve_qual_dev = 1.0 - twopass->alt_curve_mid_qual;
464 :     if (codec->config.alt_curve_low_dist > 100)
465 :     {
466 :     switch(codec->config.alt_curve_type)
467 :     {
468 :     case 2: // Sine Curve (high aggressiveness)
469 :     twopass->alt_curve_qual_dev *= 2.0 / (1.0 +
470 :     sin(DEG2RAD * (twopass->average_frame * 90.0 / twopass->alt_curve_low_diff)));
471 :     twopass->alt_curve_mid_qual = 1.0 - twopass->alt_curve_qual_dev *
472 :     sin(DEG2RAD * (twopass->average_frame * 90.0 / twopass->alt_curve_low_diff));
473 :     break;
474 :     case 1: // Linear (medium aggressiveness)
475 :     twopass->alt_curve_qual_dev *= 2.0 / (1.0 +
476 :     twopass->average_frame / twopass->alt_curve_low_diff);
477 :     twopass->alt_curve_mid_qual = 1.0 - twopass->alt_curve_qual_dev *
478 :     twopass->average_frame / twopass->alt_curve_low_diff;
479 :     break;
480 :     case 0: // Cosine Curve (low aggressiveness)
481 :     twopass->alt_curve_qual_dev *= 2.0 / (1.0 +
482 :     (1.0 - cos(DEG2RAD * (twopass->average_frame * 90.0 / twopass->alt_curve_low_diff))));
483 :     twopass->alt_curve_mid_qual = 1.0 - twopass->alt_curve_qual_dev *
484 :     (1.0 - cos(DEG2RAD * (twopass->average_frame * 90.0 / twopass->alt_curve_low_diff)));
485 :     }
486 :     }
487 :     }
488 :    
489 :     while (1)
490 :     {
491 :     if (!ReadFile(twopass->stats1, &twopass->nns1, sizeof(NNSTATS), &read, NULL) || read != sizeof(NNSTATS))
492 :     {
493 :     DWORD err = GetLastError();
494 :    
495 :     if (err == ERROR_HANDLE_EOF || err == ERROR_SUCCESS)
496 :     {
497 :     break;
498 :     }
499 :     else
500 :     {
501 :     CloseHandle(twopass->stats1);
502 :     twopass->stats1 = INVALID_HANDLE_VALUE;
503 :     DEBUGERR("2pass init error - incomplete stats2 record?");
504 :     return ICERR_ERROR;
505 :     }
506 :     }
507 :    
508 :     if (!codec_is_in_credits(&codec->config, frames) &&
509 :     !(twopass->nns1.quant & NNSTATS_KEYFRAME))
510 :     {
511 :     double dbytes = twopass->nns1.bytes / twopass->movie_curve;
512 :     total1 += dbytes;
513 :    
514 :     if (codec->config.use_alt_curve)
515 :     {
516 :     if (dbytes > twopass->average_frame)
517 :     {
518 :     if (dbytes >= twopass->alt_curve_high)
519 :     total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev);
520 :     else
521 :     {
522 :     switch(codec->config.alt_curve_type)
523 :     {
524 :     case 2:
525 :     total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
526 :     sin(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_high_diff)));
527 :     break;
528 :     case 1:
529 :     total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
530 :     (dbytes - twopass->average_frame) / twopass->alt_curve_high_diff);
531 :     break;
532 :     case 0:
533 :     total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
534 :     (1.0 - cos(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_high_diff))));
535 :     }
536 :     }
537 :     }
538 :     else
539 :     {
540 :     if (dbytes <= twopass->alt_curve_low)
541 :     total2 += dbytes;
542 :     else
543 :     {
544 :     switch(codec->config.alt_curve_type)
545 :     {
546 :     case 2:
547 :     total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
548 :     sin(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_low_diff)));
549 :     break;
550 :     case 1:
551 :     total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
552 :     (dbytes - twopass->average_frame) / twopass->alt_curve_low_diff);
553 :     break;
554 :     case 0:
555 :     total2 += dbytes * (twopass->alt_curve_mid_qual + twopass->alt_curve_qual_dev *
556 :     (1.0 - cos(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_low_diff))));
557 :     }
558 :     }
559 :     }
560 :     }
561 :     else
562 :     {
563 :     if (dbytes > twopass->average_frame)
564 :     {
565 :     total2 += ((double)dbytes + (twopass->average_frame - dbytes) *
566 :     codec->config.curve_compression_high / 100.0);
567 :     }
568 :     else
569 :     {
570 :     total2 += ((double)dbytes + (twopass->average_frame - dbytes) *
571 :     codec->config.curve_compression_low / 100.0);
572 :     }
573 :     }
574 :     }
575 :    
576 :     ++frames;
577 :     }
578 :    
579 :     twopass->curve_comp_scale = total1 / total2;
580 :    
581 :     if (!codec->config.use_alt_curve)
582 :     {
583 :     int asymmetric_average_frame;
584 :     char s[100];
585 :    
586 :     asymmetric_average_frame = (int)(twopass->average_frame * twopass->curve_comp_scale);
587 :     wsprintf(s, "middle frame size for asymmetric curve compression: %i", asymmetric_average_frame);
588 :     DEBUG2P(s);
589 :     }
590 :    
591 :     SetFilePointer(twopass->stats1, sizeof(DWORD), 0, FILE_BEGIN);
592 :     }
593 :    
594 :     if (codec->config.use_alt_curve)
595 :     {
596 :     if (codec->config.alt_curve_use_auto_bonus_bias)
597 :     codec->config.alt_curve_bonus_bias = codec->config.alt_curve_min_rel_qual;
598 :    
599 :     twopass->curve_bias_bonus = (total1 - total2) * (double)codec->config.alt_curve_bonus_bias / 100.0 / (double)(frames - credits_frames - i_frames);
600 :     twopass->curve_comp_scale = ((total1 - total2) * (1.0 - (double)codec->config.alt_curve_bonus_bias / 100.0) + total2) / total2;
601 :    
602 :    
603 :     // special info for alt curve: bias bonus and quantizer thresholds,
604 :     {
605 :     double curve_temp, dbytes;
606 :     char s[100];
607 :     int i, newquant, percent;
608 :     int oldquant = 1;
609 :    
610 :     wsprintf(s, "avg scaled framesize:%i", (int)(twopass->average_frame));
611 :     DEBUG2P(s);
612 :    
613 :     wsprintf(s, "bias bonus:%i bytes", (int)(twopass->curve_bias_bonus));
614 :     DEBUG2P(s);
615 :    
616 :     for (i=1; i <= (int)(twopass->alt_curve_high*2)+1; i++)
617 :     {
618 :     dbytes = i;
619 :     if (dbytes > twopass->average_frame)
620 :     {
621 :     if (dbytes >= twopass->alt_curve_high)
622 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev);
623 :     else
624 :     {
625 :     switch(codec->config.alt_curve_type)
626 :     {
627 :     case 2:
628 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
629 :     sin(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_high_diff)));
630 :     break;
631 :     case 1:
632 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
633 :     (dbytes - twopass->average_frame) / twopass->alt_curve_high_diff);
634 :     break;
635 :     case 0:
636 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
637 :     (1.0 - cos(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_high_diff))));
638 :     }
639 :     }
640 :     }
641 :     else
642 :     {
643 :     if (dbytes <= twopass->alt_curve_low)
644 :     curve_temp = dbytes;
645 :     else
646 :     {
647 :     switch(codec->config.alt_curve_type)
648 :     {
649 :     case 2:
650 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
651 :     sin(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_low_diff)));
652 :     break;
653 :     case 1:
654 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
655 :     (dbytes - twopass->average_frame) / twopass->alt_curve_low_diff);
656 :     break;
657 :     case 0:
658 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual + twopass->alt_curve_qual_dev *
659 :     (1.0 - cos(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_low_diff))));
660 :     }
661 :     }
662 :     }
663 :    
664 :     if (twopass->movie_curve > 1.0)
665 :     dbytes *= twopass->movie_curve;
666 :    
667 :     newquant = (int)(dbytes * 2.0 / (curve_temp * twopass->curve_comp_scale + twopass->curve_bias_bonus));
668 :     if (newquant > 1)
669 :     {
670 :     if (newquant != oldquant)
671 :     {
672 :     oldquant = newquant;
673 :     percent = (int)((i - twopass->average_frame) * 100.0 / twopass->average_frame);
674 :     wsprintf(s, "quant:%i threshold at %i : %i percent", newquant, i, percent);
675 :     DEBUG2P(s);
676 :     }
677 :     }
678 :     }
679 :     }
680 :     }
681 :    
682 :     twopass->overflow = 0;
683 :    
684 :     break;
685 :     }
686 :    
687 :     return ICERR_OK;
688 :     }
689 :    
690 :    
691 :     int codec_2pass_get_quant(CODEC* codec, XVID_ENC_FRAME* frame)
692 :     {
693 :     static double quant_error[32];
694 :     static double curve_comp_error;
695 :     static int last_quant;
696 :    
697 :     TWOPASS * twopass = &codec->twopass;
698 :    
699 :     DWORD read;
700 :     int bytes1, bytes2;
701 :     int overflow;
702 :     int credits_pos;
703 :    
704 :     if (codec->framenum == 0)
705 :     {
706 :     int i;
707 :    
708 :     for (i=0 ; i<32 ; ++i)
709 :     {
710 :     quant_error[i] = 0.0;
711 : h 105 twopass->quant_count[i] = 0;
712 : h 102 }
713 :    
714 :     curve_comp_error = 0.0;
715 :     last_quant = 0;
716 :     }
717 :    
718 :     if (ReadFile(twopass->stats1, &twopass->nns1, sizeof(NNSTATS), &read, 0) == 0 || read != sizeof(NNSTATS))
719 :     {
720 :     DEBUGERR("2ndpass quant: couldn't read from stats1");
721 :     return ICERR_ERROR;
722 :     }
723 :     if (codec->config.mode == DLG_MODE_2PASS_2_EXT)
724 :     {
725 :     if (ReadFile(twopass->stats2, &twopass->nns2, sizeof(NNSTATS), &read, 0) == 0 || read != sizeof(NNSTATS))
726 :     {
727 :     DEBUGERR("2ndpass quant: couldn't read from stats2");
728 :     return ICERR_ERROR;
729 :     }
730 :     }
731 :    
732 :     bytes1 = twopass->nns1.bytes;
733 :     overflow = twopass->overflow / 8;
734 :    
735 :     // override codec i-frame choice (reenable in credits)
736 :     frame->intra = (twopass->nns1.quant & NNSTATS_KEYFRAME);
737 :    
738 :     if (frame->intra)
739 :     {
740 :     overflow = 0;
741 :     }
742 :    
743 :     credits_pos = codec_is_in_credits(&codec->config, codec->framenum);
744 :    
745 :     if (credits_pos)
746 :     {
747 :     if (codec->config.mode == DLG_MODE_2PASS_2_INT)
748 :     {
749 :     switch (codec->config.credits_mode)
750 :     {
751 :     case CREDITS_MODE_RATE :
752 :     case CREDITS_MODE_SIZE :
753 :     if (credits_pos == CREDITS_START)
754 :     {
755 :     bytes2 = (int)(bytes1 / twopass->credits_start_curve);
756 :     }
757 :     else // CREDITS_END
758 :     {
759 :     bytes2 = (int)(bytes1 / twopass->credits_end_curve);
760 :     }
761 :    
762 :     frame->intra = -1;
763 :     break;
764 :    
765 :     case CREDITS_MODE_QUANT :
766 :     if (codec->config.credits_quant_i != codec->config.credits_quant_p)
767 :     {
768 :     frame->quant = frame->intra ?
769 :     codec->config.credits_quant_i :
770 :     codec->config.credits_quant_p;
771 :     }
772 :     else
773 :     {
774 :     frame->quant = codec->config.credits_quant_p;
775 :     frame->intra = -1;
776 :     }
777 :    
778 :     twopass->bytes1 = bytes1;
779 :     twopass->bytes2 = bytes1;
780 :     twopass->desired_bytes2 = bytes1;
781 :     return ICERR_OK;
782 :     }
783 :     }
784 :     else // DLG_MODE_2PASS_2_EXT
785 :     {
786 :     bytes2 = twopass->nns2.bytes;
787 :     }
788 :     }
789 :     else // Foxer: apply curve compression outside credits
790 :     {
791 :     double dbytes, curve_temp;
792 :    
793 :     bytes2 = (codec->config.mode == DLG_MODE_2PASS_2_INT) ? bytes1 : twopass->nns2.bytes;
794 :    
795 :     if (frame->intra)
796 :     {
797 :     dbytes = ((int)(bytes2 + bytes2 * codec->config.keyframe_boost / 100)) /
798 :     twopass->movie_curve;
799 :     }
800 :     else
801 :     {
802 :     dbytes = bytes2 / twopass->movie_curve;
803 :     }
804 :    
805 :     // spread the compression error across payback_delay frames
806 :     if (codec->config.bitrate_payback_method == 0)
807 :     {
808 :     bytes2 = (int)(curve_comp_error / codec->config.bitrate_payback_delay);
809 :     }
810 :     else
811 :     {
812 :     bytes2 = (int)(curve_comp_error * dbytes /
813 :     twopass->average_frame / codec->config.bitrate_payback_delay);
814 :    
815 :     if (labs(bytes2) > fabs(curve_comp_error))
816 :     {
817 :     bytes2 = (int)curve_comp_error;
818 :     }
819 :     }
820 :    
821 :     curve_comp_error -= bytes2;
822 :    
823 :     if (codec->config.use_alt_curve)
824 :     {
825 :     if (!frame->intra)
826 :     {
827 :     if (dbytes > twopass->average_frame)
828 :     {
829 :     if (dbytes >= twopass->alt_curve_high)
830 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev);
831 :     else
832 :     {
833 :     switch(codec->config.alt_curve_type)
834 :     {
835 :     case 2:
836 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
837 :     sin(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_high_diff)));
838 :     break;
839 :     case 1:
840 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
841 :     (dbytes - twopass->average_frame) / twopass->alt_curve_high_diff);
842 :     break;
843 :     case 0:
844 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
845 :     (1.0 - cos(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_high_diff))));
846 :     }
847 :     }
848 :     }
849 :     else
850 :     {
851 :     if (dbytes <= twopass->alt_curve_low)
852 :     curve_temp = dbytes;
853 :     else
854 :     {
855 :     switch(codec->config.alt_curve_type)
856 :     {
857 :     case 2:
858 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
859 :     sin(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_low_diff)));
860 :     break;
861 :     case 1:
862 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev *
863 :     (dbytes - twopass->average_frame) / twopass->alt_curve_low_diff);
864 :     break;
865 :     case 0:
866 :     curve_temp = dbytes * (twopass->alt_curve_mid_qual + twopass->alt_curve_qual_dev *
867 :     (1.0 - cos(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_low_diff))));
868 :     }
869 :     }
870 :     }
871 :     curve_temp = curve_temp * twopass->curve_comp_scale + twopass->curve_bias_bonus;
872 :    
873 :     bytes2 += ((int)curve_temp);
874 :     curve_comp_error += curve_temp - ((int)curve_temp);
875 :     }
876 :     else
877 :     {
878 :     curve_comp_error += dbytes - ((int)dbytes);
879 :     bytes2 += ((int)dbytes);
880 :     }
881 :     }
882 :     else if ((codec->config.curve_compression_high + codec->config.curve_compression_low) &&
883 :     !frame->intra)
884 :     {
885 :     if (dbytes > twopass->average_frame)
886 :     {
887 :     curve_temp = twopass->curve_comp_scale *
888 :     ((double)dbytes + (twopass->average_frame - dbytes) *
889 :     codec->config.curve_compression_high / 100.0);
890 :     }
891 :     else
892 :     {
893 :     curve_temp = twopass->curve_comp_scale *
894 :     ((double)dbytes + (twopass->average_frame - dbytes) *
895 :     codec->config.curve_compression_low / 100.0);
896 :     }
897 :    
898 :     bytes2 += ((int)curve_temp);
899 :     curve_comp_error += curve_temp - ((int)curve_temp);
900 :     }
901 :     else
902 :     {
903 :     curve_comp_error += dbytes - ((int)dbytes);
904 :     bytes2 += ((int)dbytes);
905 :     }
906 :    
907 :     // cap bytes2 to first pass size, lowers number of quant=1 frames
908 :     if (bytes2 > bytes1)
909 :     {
910 :     curve_comp_error += bytes2 - bytes1;
911 :     bytes2 = bytes1;
912 :     }
913 :     else if (bytes2 < 1)
914 :     {
915 :     curve_comp_error += --bytes2;
916 :     bytes2 = 1;
917 :     }
918 :     }
919 :    
920 :     twopass->desired_bytes2 = bytes2;
921 :    
922 :     // Foxer: scale overflow in relation to average size, so smaller frames don't get
923 :     // too much/little bitrate
924 :     overflow = (int)((double)overflow * bytes2 / twopass->average_frame);
925 :    
926 :     // Foxer: reign in overflow with huge frames
927 :     if (labs(overflow) > labs(twopass->overflow))
928 :     {
929 :     overflow = twopass->overflow;
930 :     }
931 :    
932 :     // Foxer: make sure overflow doesn't run away
933 :     if (overflow > bytes2 * 6 / 10)
934 :     {
935 :     bytes2 += (overflow <= bytes2) ? bytes2 * 6 / 10 : overflow * 6 / 10;
936 :     }
937 :     else if (overflow < bytes2 * -6 / 10)
938 :     {
939 :     bytes2 += bytes2 * -6 / 10;
940 :     }
941 :     else
942 :     {
943 :     bytes2 += overflow;
944 :     }
945 :    
946 :     if (bytes2 < 1)
947 :     {
948 :     bytes2 = 1;
949 :     }
950 :    
951 :     twopass->bytes1 = bytes1;
952 :     twopass->bytes2 = bytes2;
953 :    
954 :     // very 'simple' quant<->filesize relationship
955 :     frame->quant = ((twopass->nns1.quant & ~NNSTATS_KEYFRAME) * bytes1) / bytes2;
956 :    
957 :     if (frame->quant < 1)
958 :     {
959 :     frame->quant = 1;
960 :     }
961 :     else if (frame->quant > 31)
962 :     {
963 :     frame->quant = 31;
964 :     }
965 :     else if (!frame->intra)
966 :     {
967 :     // Foxer: aid desired quantizer precision by accumulating decision error
968 :     quant_error[frame->quant] += ((double)((twopass->nns1.quant & ~NNSTATS_KEYFRAME) *
969 :     bytes1) / bytes2) - frame->quant;
970 :    
971 :     if (quant_error[frame->quant] >= 1.0)
972 :     {
973 :     quant_error[frame->quant] -= 1.0;
974 :     ++frame->quant;
975 :     }
976 :     }
977 :    
978 :     // we're done with credits
979 :     if (codec_is_in_credits(&codec->config, codec->framenum))
980 :     {
981 :     return ICERR_OK;
982 :     }
983 :    
984 :     if (frame->intra)
985 :     {
986 :     if (frame->quant < codec->config.min_iquant)
987 :     {
988 :     frame->quant = codec->config.min_iquant;
989 :     DEBUG2P("I-frame quantizer raised");
990 :     }
991 :     if (frame->quant > codec->config.max_iquant)
992 :     {
993 :     frame->quant = codec->config.max_iquant;
994 :     DEBUG2P("I-frame quantizer lowered");
995 :     }
996 :     }
997 :     else
998 :     {
999 :     if (frame->quant > codec->config.max_pquant)
1000 :     {
1001 :     frame->quant = codec->config.max_pquant;
1002 :     }
1003 :     if (frame->quant < codec->config.min_pquant)
1004 :     {
1005 :     frame->quant = codec->config.min_pquant;
1006 :     }
1007 :    
1008 :     // subsequent frame quants can only be +- 2
1009 :     if (last_quant)
1010 :     {
1011 :     if (frame->quant > last_quant + 2)
1012 :     {
1013 :     frame->quant = last_quant + 2;
1014 :     DEBUG2P("P-frame quantizer prevented from rising too steeply");
1015 :     }
1016 :     if (frame->quant < last_quant - 2)
1017 :     {
1018 :     frame->quant = last_quant - 2;
1019 :     DEBUG2P("P-frame quantizer prevented from falling too steeply");
1020 :     }
1021 :     }
1022 :     }
1023 :    
1024 :     last_quant = frame->quant;
1025 :    
1026 :     if (codec->config.quant_type == QUANT_MODE_MOD)
1027 :     {
1028 :     frame->general |= (frame->quant < 4) ? XVID_MPEGQUANT : XVID_H263QUANT;
1029 :     frame->general &= (frame->quant < 4) ? ~XVID_H263QUANT : ~XVID_MPEGQUANT;
1030 :     }
1031 :    
1032 :     return ICERR_OK;
1033 :     }
1034 :    
1035 :    
1036 :     int codec_2pass_update(CODEC* codec, XVID_ENC_FRAME* frame, XVID_ENC_STATS* stats)
1037 :     {
1038 :     static __int64 total_size;
1039 :    
1040 :     NNSTATS nns1;
1041 :     DWORD wrote;
1042 : h 105 int credits_pos;
1043 : h 102 char* quant_type;
1044 :    
1045 :     if (codec->framenum == 0)
1046 :     {
1047 :     total_size = 0;
1048 :     }
1049 :    
1050 :     quant_type = (frame->general & XVID_H263QUANT) ? "H.263" :
1051 :     ((frame->general & XVID_MPEGQUANT) && (frame->general & XVID_CUSTOM_QMATRIX)) ?
1052 :     "Cust" : "MPEG";
1053 :    
1054 :     switch (codec->config.mode)
1055 :     {
1056 :     case DLG_MODE_2PASS_1 :
1057 :     nns1.bytes = frame->length; // total bytes
1058 :     nns1.dd_v = stats->hlength; // header bytes
1059 :    
1060 :     nns1.dd_u = nns1.dd_y = 0;
1061 :     nns1.dk_v = nns1.dk_u = nns1.dk_y = 0;
1062 :     nns1.md_u = nns1.md_y = 0;
1063 :     nns1.mk_u = nns1.mk_y = 0;
1064 :    
1065 :     nns1.quant = stats->quant;
1066 :     if (frame->intra)
1067 :     {
1068 :     nns1.quant |= NNSTATS_KEYFRAME;
1069 :     }
1070 :     nns1.kblk = stats->kblks;
1071 :     nns1.mblk = stats->mblks;
1072 :     nns1.ublk = stats->ublks;
1073 :     nns1.lum_noise[0] = nns1.lum_noise[1] = 1;
1074 :    
1075 :     total_size += frame->length;
1076 :    
1077 :     DEBUG1ST(frame->length, (int)total_size/1024, frame->intra, frame->quant, quant_type, stats->kblks, stats->mblks)
1078 :    
1079 :     if (WriteFile(codec->twopass.stats1, &nns1, sizeof(NNSTATS), &wrote, 0) == 0 || wrote != sizeof(NNSTATS))
1080 :     {
1081 :     DEBUGERR("stats1: WriteFile error");
1082 :     return ICERR_ERROR;
1083 :     }
1084 :     break;
1085 :    
1086 :     case DLG_MODE_2PASS_2_INT :
1087 :     case DLG_MODE_2PASS_2_EXT :
1088 :     codec->twopass.overflow += codec->twopass.desired_bytes2 - frame->length;
1089 : h 105
1090 :     credits_pos = codec_is_in_credits(&codec->config, codec->framenum);
1091 :     if (!credits_pos)
1092 :     codec->twopass.quant_count[frame->quant]++;
1093 :    
1094 :     DEBUG2ND(frame->quant, quant_type, frame->intra, codec->twopass.bytes1, codec->twopass.desired_bytes2, frame->length, codec->twopass.overflow, credits_pos)
1095 : h 102 break;
1096 :    
1097 :     default:
1098 :     break;
1099 :     }
1100 :    
1101 :     return ICERR_OK;
1102 :     }
1103 :    
1104 : h 105 void codec_2pass_finish(CODEC* codec)
1105 :     {
1106 :     int i;
1107 :     char s[100];
1108 :     if (codec->config.mode == DLG_MODE_2PASS_2_EXT || codec->config.mode == DLG_MODE_2PASS_2_INT)
1109 :     {
1110 :     // output the quantizer distribution for this encode.
1111 : h 102
1112 : h 105 OutputDebugString("Quantizer distribution for 2nd pass:");
1113 :     for (i=1; i<=31; i++)
1114 :     {
1115 :     if (codec->twopass.quant_count[i])
1116 :     {
1117 :     wsprintf(s, "Q:%i:%i", i, codec->twopass.quant_count[i]);
1118 :     OutputDebugString(s);
1119 :     }
1120 :     }
1121 :     return;
1122 :     }
1123 :     }

No admin address has been configured
ViewVC Help
Powered by ViewVC 1.0.4