1
/*****************************************************************************
2
* codec.c: vfw x264 encoder
3
*****************************************************************************
4
* Copyright (C) 2003 Laurent Aimar
5
* $Id: codec.c,v 1.1 2004/06/03 19:27:09 fenrir Exp $
8
* Laurent Aimar <fenrir@via.ecp.fr>
10
* This program is free software; you can redistribute it and/or modify
11
* it under the terms of the GNU General Public License as published by
12
* the Free Software Foundation; either version 2 of the License, or
13
* (at your option) any later version.
15
* This program is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
* GNU General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with this program; if not, write to the Free Software
22
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23
*****************************************************************************/
27
#include <stdio.h> /* debug only */
30
#define X264_MAX(a,b) ( (a)>(b) ? (a) : (b) )
31
#define X264_MIN(a,b) ( (a)<(b) ? (a) : (b) )
34
* return a valid x264 CSP or X264_CSP_NULL if unsuported */
35
static int get_csp( BITMAPINFOHEADER *hdr )
37
switch( hdr->biCompression )
52
int i_vflip = hdr->biHeight < 0 ? 0 : X264_CSP_VFLIP;
54
if( hdr->biBitCount == 24 )
55
return X264_CSP_BGR | i_vflip;
56
if( hdr->biBitCount == 32 )
57
return X264_CSP_BGRA | i_vflip;
67
/* Test that we can do the compression */
68
LRESULT compress_query( CODEC *codec, BITMAPINFO *lpbiInput, BITMAPINFO *lpbiOutput )
70
BITMAPINFOHEADER *inhdr = &lpbiInput->bmiHeader;
71
BITMAPINFOHEADER *outhdr = &lpbiOutput->bmiHeader;
72
CONFIG *config = &codec->config;
74
if( get_csp( inhdr ) == X264_CSP_NONE )
75
return ICERR_BADFORMAT;
77
if( lpbiOutput == NULL )
80
if( inhdr->biWidth != outhdr->biWidth ||
81
inhdr->biHeight != outhdr->biHeight )
82
return ICERR_BADFORMAT;
84
/* We need x16 width/height */
85
if( inhdr->biWidth % 16 != 0 || inhdr->biHeight % 16 != 0 )
86
return ICERR_BADFORMAT;
89
if( inhdr->biCompression != mmioFOURCC( config->fcc[0], config->fcc[1],
90
config->fcc[2], config->fcc[3] ) )
91
return ICERR_BADFORMAT;
97
LRESULT compress_get_format( CODEC *codec, BITMAPINFO *lpbiInput, BITMAPINFO *lpbiOutput )
99
BITMAPINFOHEADER *inhdr = &lpbiInput->bmiHeader;
100
BITMAPINFOHEADER *outhdr = &lpbiOutput->bmiHeader;
101
CONFIG *config = &codec->config;
103
if( get_csp( inhdr ) == X264_CSP_NONE )
104
return ICERR_BADFORMAT;
106
if( lpbiOutput == NULL )
107
return sizeof(BITMAPINFOHEADER);
109
memcpy( outhdr, inhdr, sizeof( BITMAPINFOHEADER ) );
110
outhdr->biSize = sizeof( BITMAPINFOHEADER );
111
outhdr->biSizeImage = compress_get_size( codec, lpbiInput, lpbiOutput );
112
outhdr->biXPelsPerMeter = 0;
113
outhdr->biYPelsPerMeter = 0;
114
outhdr->biClrUsed = 0;
115
outhdr->biClrImportant = 0;
116
outhdr->biCompression = mmioFOURCC( config->fcc[0], config->fcc[1],
117
config->fcc[2], config->fcc[3] );
123
LRESULT compress_get_size( CODEC *codec, BITMAPINFO *lpbiInput, BITMAPINFO *lpbiOutput )
125
return 2 * lpbiOutput->bmiHeader.biWidth * lpbiOutput->bmiHeader.biHeight * 3;
129
LRESULT compress_frames_info(CODEC * codec, ICCOMPRESSFRAMES * icf )
131
codec->fincr = icf->dwScale;
132
codec->fbase = icf->dwRate;
133
codec->config.i_frame_total = icf->lFrameCount;
137
static void x264_log_vfw( void *p_private, int i_level, const char *psz_fmt, va_list arg )
139
char error_msg[1024];
141
HWND *hCons = p_private;
143
vsprintf( error_msg, psz_fmt, arg );
145
/* strip final linefeeds (required) */
146
idx=strlen( error_msg ) - 1;
147
while( idx >= 0 && error_msg[idx] == '\n' )
148
error_msg[idx--] = 0;
151
*hCons = CreateDialog( g_hInst, MAKEINTRESOURCE( IDD_ERRCONSOLE ), NULL,
152
callback_err_console );
153
//ShowWindow( *hCons, SW_SHOW );
155
idx = SendDlgItemMessage( *hCons, IDC_CONSOLE, LB_ADDSTRING, 0, ( LPARAM )error_msg );
157
/* make sure that the last item added is visible (autoscroll) */
159
SendDlgItemMessage( *hCons, IDC_CONSOLE, LB_SETTOPINDEX, ( WPARAM )idx, 0 );
163
static void statsfilename_renumber( char *dest, char *src, int i_pass )
165
char *last_dot = strrchr( src, '.' );
166
char *last_slash = X264_MAX( strrchr( src, '/' ), strrchr( src, '\\' ) );
169
sprintf( pass_str, "-%i", i_pass );
171
if( last_slash < last_dot ) {
172
dest[ last_dot - src ] = 0;
173
strcat( dest, pass_str );
174
strcat( dest, last_dot );
178
strcat( dest, pass_str );
183
LRESULT compress_begin(CODEC * codec, BITMAPINFO * lpbiInput, BITMAPINFO * lpbiOutput )
185
CONFIG *config = &codec->config;
189
/* Destroy previous handle */
190
if( codec->h != NULL )
192
x264_encoder_close( codec->h );
196
/* Get default param */
197
x264_param_default( ¶m );
199
param.rc.psz_stat_out = malloc (MAX_PATH);
200
param.rc.psz_stat_in = malloc (MAX_PATH);
201
param.i_threads = config->i_threads;
203
param.i_log_level = config->i_log_level - 1;
204
param.pf_log = x264_log_vfw;
205
param.p_log_private = malloc( sizeof( HWND ) );
206
*( ( HWND * )param.p_log_private ) = NULL; /* error console window handle */
207
codec->hCons = ( HWND * )param.p_log_private;
209
param.analyse.b_psnr = 0;
211
/* Set params: TODO to complete */
212
param.i_width = lpbiInput->bmiHeader.biWidth;
213
param.i_height= lpbiInput->bmiHeader.biHeight;
215
param.i_fps_num = codec->fbase;
216
param.i_fps_den = codec->fincr;
217
param.i_frame_total = config->i_frame_total;
219
param.i_frame_reference = config->i_refmax;
220
param.i_keyint_min = config->i_keyint_min;
221
param.i_keyint_max = config->i_keyint_max;
222
param.i_scenecut_threshold = config->i_scenecut_threshold;
223
param.rc.i_qp_min = config->i_qp_min;
224
param.rc.i_qp_max = config->i_qp_max;
225
param.rc.i_qp_step = config->i_qp_step;
226
param.b_deblocking_filter = config->b_filter;
227
param.b_cabac = config->b_cabac;
228
param.analyse.b_chroma_me = config->b_chroma_me;
229
param.rc.f_ip_factor = 1 + (float)config->i_key_boost / 100;
230
param.rc.f_pb_factor = 1 + (float)config->i_b_red / 100;
231
param.rc.f_qcompress = (float)config->i_curve_comp / 100;
232
param.vui.i_sar_width = config->i_sar_width;
233
param.vui.i_sar_height = config->i_sar_height;
235
param.i_bframe = config->i_bframe;
236
if( config->i_bframe > 1 && config->b_b_wpred)
237
param.analyse.b_weighted_bipred = 1;
238
if( config->i_bframe > 1 && config->b_b_refs)
239
param.b_bframe_pyramid = 1;
240
param.b_bframe_adaptive = config->b_bframe_adaptive;
241
param.i_bframe_bias = config->i_bframe_bias;
242
param.analyse.i_subpel_refine = config->i_subpel_refine + 1; /* 0..4 -> 1..5 */
243
param.analyse.i_me_method = config->i_me_method;
244
param.analyse.i_me_range = config->i_me_range;
246
/* bframe prediction - gui goes alphabetically, so 1=SPATIAL, 2=TEMPORAL */
247
switch(config->i_direct_mv_pred) {
248
case 0: param.analyse.i_direct_mv_pred = X264_DIRECT_PRED_SPATIAL; break;
249
case 1: param.analyse.i_direct_mv_pred = X264_DIRECT_PRED_TEMPORAL; break;
251
param.i_deblocking_filter_alphac0 = config->i_inloop_a;
252
param.i_deblocking_filter_beta = config->i_inloop_b;
253
param.analyse.inter = 0;
254
if( config->b_bsub16x16 )
255
param.analyse.inter |= X264_ANALYSE_BSUB16x16;
256
if( config->b_psub16x16 )
258
param.analyse.inter |= X264_ANALYSE_PSUB16x16;
259
if( config->b_psub8x8 )
260
param.analyse.inter |= X264_ANALYSE_PSUB8x8;
263
param.analyse.inter |= X264_ANALYSE_I4x4;
265
param.analyse.inter |= X264_ANALYSE_I8x8;
266
param.analyse.b_transform_8x8 = config->b_dct8x8;
267
if( config->b_mixedref )
268
param.analyse.b_mixed_references = 1;
271
switch( config->i_encoding_type )
273
case 0: /* 1 PASS ABR */
275
param.rc.i_bitrate = config->bitrate;
277
case 1: /* 1 PASS CQ */
278
param.rc.i_qp_constant = config->i_qp;
283
for( pass_number = 1; pass_number < 99; pass_number++ )
286
statsfilename_renumber( param.rc.psz_stat_out, config->stats, pass_number );
287
if( ( f = fopen( param.rc.psz_stat_out, "r" ) ) != NULL )
290
if( config->i_pass == 1 )
291
unlink( param.rc.psz_stat_out );
296
if( config->i_pass > pass_number )
298
/* missing 1st pass statsfile */
299
free( param.rc.psz_stat_out );
300
free( param.rc.psz_stat_in );
304
param.rc.i_bitrate = config->i_2passbitrate;
307
if( config->i_pass == 1 )
309
statsfilename_renumber( param.rc.psz_stat_out, config->stats, 1 );
310
param.rc.b_stat_write = 1;
311
param.rc.f_rate_tolerance = 4;
312
if( config->b_fast1pass )
314
/* adjust or turn off some flags to gain speed, if needed */
315
param.analyse.i_subpel_refine = X264_MAX( X264_MIN( 3, param.analyse.i_subpel_refine - 1 ), 1 );
316
param.i_frame_reference = ( param.i_frame_reference + 1 ) >> 1;
317
param.analyse.inter &= ( ~X264_ANALYSE_PSUB8x8 );
318
param.analyse.inter &= ( ~X264_ANALYSE_BSUB16x16 );
323
statsfilename_renumber( param.rc.psz_stat_in, config->stats, pass_number - 1 );
324
param.rc.b_stat_read = 1;
325
if( config->b_updatestats )
326
param.rc.b_stat_write = 1;
327
param.rc.f_rate_tolerance = 1;
334
/* Open the encoder */
335
codec->h = x264_encoder_open( ¶m );
337
free( param.rc.psz_stat_out );
338
free( param.rc.psz_stat_in );
340
if( codec->h == NULL )
347
LRESULT compress_end(CODEC * codec)
349
if( codec->h != NULL )
351
x264_encoder_close( codec->h );
355
free( codec->hCons );
361
LRESULT compress( CODEC *codec, ICCOMPRESS *icc )
363
BITMAPINFOHEADER *inhdr = icc->lpbiInput;
364
BITMAPINFOHEADER *outhdr = icc->lpbiOutput;
374
/* Init the picture */
375
memset( &pic, 0, sizeof( x264_picture_t ) );
376
pic.img.i_csp = get_csp( inhdr );
378
/* For now biWidth can be divided by 16 so no problem */
379
switch( pic.img.i_csp & X264_CSP_MASK )
384
pic.img.i_stride[0] = inhdr->biWidth;
385
pic.img.i_stride[1] =
386
pic.img.i_stride[2] = inhdr->biWidth / 2;
388
pic.img.plane[0] = (uint8_t*)icc->lpInput;
389
pic.img.plane[1] = pic.img.plane[0] + inhdr->biWidth * inhdr->biHeight;
390
pic.img.plane[2] = pic.img.plane[1] + inhdr->biWidth * inhdr->biHeight / 4;
395
pic.img.i_stride[0] = 2 * inhdr->biWidth;
396
pic.img.plane[0] = (uint8_t*)icc->lpInput;
401
pic.img.i_stride[0] = 3 * inhdr->biWidth;
402
pic.img.plane[0] = (uint8_t*)icc->lpInput;
407
pic.img.i_stride[0] = 4 * inhdr->biWidth;
408
pic.img.plane[0] = (uint8_t*)icc->lpInput;
412
return ICERR_BADFORMAT;
416
x264_encoder_encode( codec->h, &nal, &i_nal, &pic, &pic );
418
/* create bitstream, unless we're dropping it in 1st pass */
421
if( codec->config.i_encoding_type != 2 || codec->config.i_pass > 1 ) {
422
for( i = 0; i < i_nal; i++ ) {
423
int i_size = outhdr->biSizeImage - i_out;
424
x264_nal_encode( (uint8_t*)icc->lpOutput + i_out, &i_size, 1, &nal[i] );
430
outhdr->biSizeImage = i_out;
432
/* Set key frame only for IDR, as they are real synch point, I frame
433
aren't always synch point (ex: with multi refs, ref marking) */
434
if( pic.i_type == X264_TYPE_IDR )
435
*icc->lpdwFlags = AVIIF_KEYFRAME;