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 int i_vlip = hdr->biHeight < 0 ? 0 : X264_CSP_VFLIP;
39 switch( hdr->biCompression )
53 if( hdr->biBitCount == 24 )
54 return X264_CSP_BGR | i_vlip;
55 if( hdr->biBitCount == 32 )
56 return X264_CSP_BGRA | i_vlip;
65 /* Test that we can do the compression */
66 LRESULT compress_query( CODEC *codec, BITMAPINFO *lpbiInput, BITMAPINFO *lpbiOutput )
68 BITMAPINFOHEADER *inhdr = &lpbiInput->bmiHeader;
69 BITMAPINFOHEADER *outhdr = &lpbiOutput->bmiHeader;
70 CONFIG *config = &codec->config;
72 if( get_csp( inhdr ) == X264_CSP_NONE )
73 return ICERR_BADFORMAT;
75 if( lpbiOutput == NULL )
78 if( inhdr->biWidth != outhdr->biWidth ||
79 inhdr->biHeight != outhdr->biHeight )
80 return ICERR_BADFORMAT;
82 /* We need x16 width/height */
83 if( inhdr->biWidth % 16 != 0 || inhdr->biHeight % 16 != 0 )
84 return ICERR_BADFORMAT;
87 if( inhdr->biCompression != mmioFOURCC( config->fcc[0], config->fcc[1],
88 config->fcc[2], config->fcc[3] ) )
89 return ICERR_BADFORMAT;
95 LRESULT compress_get_format( CODEC *codec, BITMAPINFO *lpbiInput, BITMAPINFO *lpbiOutput )
97 BITMAPINFOHEADER *inhdr = &lpbiInput->bmiHeader;
98 BITMAPINFOHEADER *outhdr = &lpbiOutput->bmiHeader;
99 CONFIG *config = &codec->config;
101 if( get_csp( inhdr ) == X264_CSP_NONE )
102 return ICERR_BADFORMAT;
104 if( lpbiOutput == NULL )
105 return sizeof(BITMAPINFOHEADER);
107 memcpy( outhdr, inhdr, sizeof( BITMAPINFOHEADER ) );
108 outhdr->biSize = sizeof( BITMAPINFOHEADER );
109 outhdr->biSizeImage = compress_get_size( codec, lpbiInput, lpbiOutput );
110 outhdr->biXPelsPerMeter = 0;
111 outhdr->biYPelsPerMeter = 0;
112 outhdr->biClrUsed = 0;
113 outhdr->biClrImportant = 0;
114 outhdr->biCompression = mmioFOURCC( config->fcc[0], config->fcc[1],
115 config->fcc[2], config->fcc[3] );
121 LRESULT compress_get_size( CODEC *codec, BITMAPINFO *lpbiInput, BITMAPINFO *lpbiOutput )
123 return 2 * lpbiOutput->bmiHeader.biWidth * lpbiOutput->bmiHeader.biHeight * 3;
127 LRESULT compress_frames_info(CODEC * codec, ICCOMPRESSFRAMES * icf )
129 codec->fincr = icf->dwScale;
130 codec->fbase = icf->dwRate;
134 static void x264_log_vfw( void *p_private, int i_level, const char *psz_fmt, va_list arg )
136 char error_msg[1024];
138 HWND *hCons = p_private;
140 vsprintf( error_msg, psz_fmt, arg );
142 /* strip final linefeeds (required) */
143 idx=strlen( error_msg ) - 1;
144 while( idx >= 0 && error_msg[idx] == '\n' )
145 error_msg[idx--] = 0;
148 *hCons = CreateDialog( g_hInst, MAKEINTRESOURCE( IDD_ERRCONSOLE ), NULL,
149 callback_err_console );
150 //ShowWindow( *hCons, SW_SHOW );
152 idx = SendDlgItemMessage( *hCons, IDC_CONSOLE, LB_ADDSTRING, 0, ( LPARAM )error_msg );
154 /* make sure that the last item added is visible (autoscroll) */
156 SendDlgItemMessage( *hCons, IDC_CONSOLE, LB_SETTOPINDEX, ( WPARAM )idx, 0 );
160 static void statsfilename_renumber( char *dest, char *src, int i_pass )
162 char *last_dot = strrchr( src, '.' );
163 char *last_slash = X264_MAX( strrchr( src, '/' ), strrchr( src, '\\' ) );
166 sprintf( pass_str, "-%i", i_pass );
168 if( last_slash < last_dot ) {
169 dest[ last_dot - src ] = 0;
170 strcat( dest, pass_str );
171 strcat( dest, last_dot );
175 strcat( dest, pass_str );
180 LRESULT compress_begin(CODEC * codec, BITMAPINFO * lpbiInput, BITMAPINFO * lpbiOutput )
182 CONFIG *config = &codec->config;
186 /* Destroy previous handle */
187 if( codec->h != NULL )
189 x264_encoder_close( codec->h );
193 /* Get default param */
194 x264_param_default( ¶m );
196 param.rc.psz_stat_out = malloc (MAX_PATH);
197 param.rc.psz_stat_in = malloc (MAX_PATH);
199 param.i_log_level = X264_LOG_ERROR;
200 param.pf_log = x264_log_vfw;
201 param.p_log_private = malloc( sizeof( HWND ) );
202 *( ( HWND * )param.p_log_private ) = NULL; /* error console window handle */
203 codec->hCons = ( HWND * )param.p_log_private;
205 param.analyse.b_psnr = 0;
206 param.analyse.inter = 0;
207 param.analyse.intra = X264_ANALYSE_I4x4;
209 /* Set params: TODO to complete */
210 param.i_width = lpbiInput->bmiHeader.biWidth;
211 param.i_height= lpbiInput->bmiHeader.biHeight;
213 param.i_fps_num = codec->fbase;
214 param.i_fps_den = codec->fincr;
216 param.i_frame_reference = config->i_refmax;
217 param.i_keyint_min = config->i_keyint_min;
218 param.i_keyint_max = config->i_keyint_max;
219 param.b_deblocking_filter = config->b_filter;
220 param.b_cabac = config->b_cabac;
221 param.rc.f_ip_factor = 1 + (float)config->i_key_boost / 100;
222 param.rc.f_pb_factor = 1 + (float)config->i_b_red / 100;
223 param.rc.f_qcompress = (float)config->i_curve_comp / 100;
225 param.i_bframe = config->i_bframe;
226 if( config->i_bframe > 1 )
227 param.analyse.b_weighted_bipred = 1;
228 param.analyse.i_subpel_refine = config->i_subpel_refine + 1; /* 0..4 -> 1..5 */
230 /* bframe prediction - gui goes alphabetically, so 1=SPATIAL, 2=TEMPORAL */
231 switch(config->i_direct_mv_pred) {
232 case 0: param.analyse.i_direct_mv_pred = X264_DIRECT_PRED_SPATIAL; break;
233 case 1: param.analyse.i_direct_mv_pred = X264_DIRECT_PRED_TEMPORAL; break;
235 param.i_deblocking_filter_alphac0 = config->i_inloop_a;
236 param.i_deblocking_filter_beta = config->i_inloop_b;
238 if( config->b_bsub16x16 )
239 param.analyse.inter |= X264_ANALYSE_BSUB16x16;
240 if( config->b_psub16x16 )
242 param.analyse.inter |= X264_ANALYSE_PSUB16x16;
243 if( config->b_psub8x8 )
244 param.analyse.inter |= X264_ANALYSE_PSUB8x8;
247 param.analyse.inter |= X264_ANALYSE_I4x4;
249 switch( config->i_encoding_type )
251 case 0: /* 1 PASS CBR */
253 param.rc.i_bitrate = config->bitrate;
255 case 1: /* 1 PASS CQ */
256 param.rc.i_qp_constant = config->i_qp;
261 for( pass_number = 1; pass_number < 99; pass_number++ )
264 statsfilename_renumber( param.rc.psz_stat_out, config->stats, pass_number );
265 if( ( f = fopen( param.rc.psz_stat_out, "r" ) ) != NULL )
268 if( config->i_pass == 1 )
269 unlink( param.rc.psz_stat_out );
274 if( config->i_pass > pass_number )
276 /* missing 1st pass statsfile */
277 free( param.rc.psz_stat_out );
278 free( param.rc.psz_stat_in );
282 if( config->i_pass == 1 )
284 statsfilename_renumber( param.rc.psz_stat_out, config->stats, 1 );
285 param.rc.b_stat_write = 1;
286 if( config->b_fast1pass )
288 /* adjust or turn off some flags to gain speed, if needed */
289 param.analyse.i_subpel_refine = X264_MAX( X264_MIN( 3, param.analyse.i_subpel_refine - 1 ), 1 );
290 param.i_frame_reference = ( param.i_frame_reference + 1 ) >> 1;
291 param.analyse.inter &= ( ~X264_ANALYSE_PSUB8x8 );
292 param.analyse.inter &= ( ~X264_ANALYSE_BSUB16x16 );
297 statsfilename_renumber( param.rc.psz_stat_in, config->stats, pass_number - 1 );
298 param.rc.i_bitrate = config->i_2passbitrate;
300 param.rc.b_stat_read = 1;
301 if( config->b_updatestats )
302 param.rc.b_stat_write = 1;
309 /* Open the encoder */
310 codec->h = x264_encoder_open( ¶m );
312 free( param.rc.psz_stat_out );
313 free( param.rc.psz_stat_in );
315 if( codec->h == NULL )
322 LRESULT compress_end(CODEC * codec)
324 if( codec->h != NULL )
326 x264_encoder_close( codec->h );
330 free( codec->hCons );
335 LRESULT compress( CODEC *codec, ICCOMPRESS *icc )
337 BITMAPINFOHEADER *inhdr = icc->lpbiInput;
338 BITMAPINFOHEADER *outhdr = icc->lpbiOutput;
348 /* Init the picture */
349 memset( &pic, 0, sizeof( x264_picture_t ) );
350 pic.img.i_csp = get_csp( inhdr );
352 /* For now biWidth can be divided by 16 so no problem */
353 switch( pic.img.i_csp & X264_CSP_MASK )
358 pic.img.i_stride[0] = inhdr->biWidth;
359 pic.img.i_stride[1] =
360 pic.img.i_stride[2] = inhdr->biWidth / 2;
362 pic.img.plane[0] = (uint8_t*)icc->lpInput;
363 pic.img.plane[1] = pic.img.plane[0] + inhdr->biWidth * inhdr->biHeight;
364 pic.img.plane[2] = pic.img.plane[1] + inhdr->biWidth * inhdr->biHeight / 4;
369 pic.img.i_stride[0] = 2 * inhdr->biWidth;
370 pic.img.plane[0] = (uint8_t*)icc->lpInput;
375 pic.img.i_stride[0] = 3 * inhdr->biWidth;
376 pic.img.plane[0] = (uint8_t*)icc->lpInput;
381 pic.img.i_stride[0] = 4 * inhdr->biWidth;
382 pic.img.plane[0] = (uint8_t*)icc->lpInput;
386 return ICERR_BADFORMAT;
390 x264_encoder_encode( codec->h, &nal, &i_nal, &pic, &pic );
392 /* create bitstream, unless we're dropping it in 1st pass */
395 if( codec->config.i_encoding_type != 2 || codec->config.i_pass > 1 ) {
396 for( i = 0; i < i_nal; i++ ) {
397 int i_size = outhdr->biSizeImage - i_out;
398 x264_nal_encode( (uint8_t*)icc->lpOutput + i_out, &i_size, 1, &nal[i] );
404 outhdr->biSizeImage = i_out;
406 /* Set key frame only for IDR, as they are real synch point, I frame
407 aren't always synch point (ex: with multi refs, ref marking) */
408 if( pic.i_type == X264_TYPE_IDR )
409 *icc->lpdwFlags = AVIIF_KEYFRAME;