]> git.sesse.net Git - x264/blob - vfw/codec.c
642cc669dea63521af659dfea341f7a6ce887b98
[x264] / vfw / codec.c
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 $
6  *
7  * Authors: Justin Clay
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 #include "x264vfw.h"
26
27 #include <stdio.h> /* debug only */
28 #include <io.h>
29
30 #define X264_MAX(a,b) ( (a)>(b) ? (a) : (b) )
31 #define X264_MIN(a,b) ( (a)<(b) ? (a) : (b) )
32
33 /* get_csp:
34  *  return a valid x264 CSP or X264_CSP_NULL if unsuported */
35 static int get_csp( BITMAPINFOHEADER *hdr )
36 {
37     int i_vlip = hdr->biHeight < 0 ? 0 : X264_CSP_VFLIP;
38
39     switch( hdr->biCompression )
40     {
41         case FOURCC_I420:
42         case FOURCC_IYUV:
43             return X264_CSP_I420;
44
45         case FOURCC_YV12:
46             return X264_CSP_YV12;
47
48         case FOURCC_YUYV:
49         case FOURCC_YUY2:
50             return X264_CSP_YUYV;
51
52         case BI_RGB:
53             if( hdr->biBitCount == 24 )
54                 return X264_CSP_BGR | i_vlip;
55             if( hdr->biBitCount == 32 )
56                 return X264_CSP_BGRA | i_vlip;
57             else
58                 return X264_CSP_NONE;
59
60         default:
61             return X264_CSP_NONE;
62     }
63 }
64
65 /* Test that we can do the compression */
66 LRESULT compress_query( CODEC *codec, BITMAPINFO *lpbiInput, BITMAPINFO *lpbiOutput )
67 {
68     BITMAPINFOHEADER *inhdr = &lpbiInput->bmiHeader;
69     BITMAPINFOHEADER *outhdr = &lpbiOutput->bmiHeader;
70     CONFIG           *config = &codec->config;
71
72     if( get_csp( inhdr ) == X264_CSP_NONE )
73         return ICERR_BADFORMAT;
74
75     if( lpbiOutput == NULL )
76         return ICERR_OK;
77
78     if( inhdr->biWidth != outhdr->biWidth ||
79         inhdr->biHeight != outhdr->biHeight )
80         return ICERR_BADFORMAT;
81
82     /* We need x16 width/height */
83     if( inhdr->biWidth % 16 != 0 || inhdr->biHeight % 16 != 0 )
84         return ICERR_BADFORMAT;
85
86
87     if( inhdr->biCompression != mmioFOURCC( config->fcc[0], config->fcc[1],
88                                             config->fcc[2], config->fcc[3] ) )
89         return ICERR_BADFORMAT;
90
91     return ICERR_OK;
92 }
93
94 /* */
95 LRESULT compress_get_format( CODEC *codec, BITMAPINFO *lpbiInput, BITMAPINFO *lpbiOutput )
96 {
97     BITMAPINFOHEADER *inhdr = &lpbiInput->bmiHeader;
98     BITMAPINFOHEADER *outhdr = &lpbiOutput->bmiHeader;
99     CONFIG           *config = &codec->config;
100
101     if( get_csp( inhdr ) == X264_CSP_NONE )
102         return ICERR_BADFORMAT;
103
104     if( lpbiOutput == NULL )
105         return sizeof(BITMAPINFOHEADER);
106
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] );
116
117     return ICERR_OK;
118 }
119
120 /* */
121 LRESULT compress_get_size( CODEC *codec, BITMAPINFO *lpbiInput, BITMAPINFO *lpbiOutput )
122 {
123     return 2 * lpbiOutput->bmiHeader.biWidth * lpbiOutput->bmiHeader.biHeight * 3;
124 }
125
126 /* */
127 LRESULT compress_frames_info(CODEC * codec, ICCOMPRESSFRAMES * icf )
128 {
129     codec->fincr = icf->dwScale;
130     codec->fbase = icf->dwRate;
131     return ICERR_OK;
132 }
133
134 static void x264_log_vfw( void *p_private, int i_level, const char *psz_fmt, va_list arg )
135
136     char error_msg[1024];
137     int idx;
138     HWND *hCons = p_private;
139
140     vsprintf( error_msg, psz_fmt, arg );
141     
142     /* strip final linefeeds (required) */
143     idx=strlen( error_msg ) - 1;
144     while( idx >= 0 && error_msg[idx] == '\n' )
145         error_msg[idx--] = 0;
146
147     if(!( *hCons ) ) {
148         *hCons = CreateDialog( g_hInst, MAKEINTRESOURCE( IDD_ERRCONSOLE ), NULL, 
149                  callback_err_console );
150         //ShowWindow( *hCons, SW_SHOW );
151     }
152     idx = SendDlgItemMessage( *hCons, IDC_CONSOLE, LB_ADDSTRING, 0, ( LPARAM )error_msg );
153     
154     /* make sure that the last item added is visible (autoscroll) */
155     if( idx >= 0 ) 
156         SendDlgItemMessage( *hCons, IDC_CONSOLE, LB_SETTOPINDEX, ( WPARAM )idx, 0 );
157
158 }
159
160 static void statsfilename_renumber( char *dest, char *src, int i_pass )
161 {
162     char *last_dot = strrchr( src, '.' );
163     char *last_slash = X264_MAX( strrchr( src, '/' ), strrchr( src, '\\' ) );
164     char pass_str[5];
165
166     sprintf( pass_str, "-%i", i_pass );
167     strcpy( dest, src );
168     if( last_slash < last_dot ) {
169         dest[ last_dot - src ] = 0;
170         strcat( dest, pass_str );
171         strcat( dest, last_dot );
172     }
173     else
174     {
175         strcat( dest, pass_str );
176     }
177 }
178
179 /* */
180 LRESULT compress_begin(CODEC * codec, BITMAPINFO * lpbiInput, BITMAPINFO * lpbiOutput )
181 {
182     CONFIG *config = &codec->config;
183     x264_param_t param;
184     int pass_number;
185
186     /* Destroy previous handle */
187     if( codec->h != NULL )
188     {
189         x264_encoder_close( codec->h );
190         codec->h = NULL;
191     }
192
193     /* Get default param */
194     x264_param_default( &param );
195
196     param.rc.psz_stat_out = malloc (MAX_PATH);
197     param.rc.psz_stat_in = malloc (MAX_PATH);
198   
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;
204
205     param.analyse.b_psnr = 0;
206     param.analyse.inter = 0;
207     param.analyse.intra = X264_ANALYSE_I4x4;
208
209     /* Set params: TODO to complete */
210     param.i_width = lpbiInput->bmiHeader.biWidth;
211     param.i_height= lpbiInput->bmiHeader.biHeight;
212
213     param.i_fps_num = codec->fbase;
214     param.i_fps_den = codec->fincr;
215
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;
224
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 */
229
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;
234     }
235     param.i_deblocking_filter_alphac0 = config->i_inloop_a;
236     param.i_deblocking_filter_beta = config->i_inloop_b;
237
238     if( config->b_bsub16x16 )
239         param.analyse.inter |= X264_ANALYSE_BSUB16x16;
240     if( config->b_psub16x16 )
241     {
242         param.analyse.inter |= X264_ANALYSE_PSUB16x16;
243         if( config->b_psub8x8 )
244             param.analyse.inter |= X264_ANALYSE_PSUB8x8;
245     }
246     if( config->b_i4x4 )
247         param.analyse.inter |= X264_ANALYSE_I4x4;
248
249     switch( config->i_encoding_type )
250     {
251         case 0: /* 1 PASS CBR */
252             param.rc.b_cbr = 1;
253             param.rc.i_bitrate = config->bitrate;
254             break;
255         case 1: /* 1 PASS CQ */
256             param.rc.i_qp_constant = config->i_qp;
257             break;
258         default:
259         case 2: /* 2 PASS */
260         {
261             for( pass_number = 1; pass_number < 99; pass_number++ )
262             {
263                 FILE *f;
264                 statsfilename_renumber( param.rc.psz_stat_out, config->stats, pass_number );
265                 if( ( f = fopen( param.rc.psz_stat_out, "r" ) ) != NULL )
266                 {
267                     fclose( f );
268                     if( config->i_pass == 1 )
269                         unlink( param.rc.psz_stat_out );
270                 }
271                 else break;
272             }
273
274             if( config->i_pass > pass_number )
275             {
276                 /* missing 1st pass statsfile */
277                 free( param.rc.psz_stat_out );
278                 free( param.rc.psz_stat_in );
279                 return ICERR_ERROR;
280             }
281
282             if( config->i_pass == 1 )
283             {
284                 statsfilename_renumber( param.rc.psz_stat_out, config->stats, 1 );
285                 param.rc.b_stat_write = 1;
286                 if( config->b_fast1pass )
287                 {
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 );
293                 }
294             }
295             else
296             {
297                 statsfilename_renumber( param.rc.psz_stat_in, config->stats, pass_number - 1 );
298                 param.rc.i_bitrate = config->i_2passbitrate;
299                 param.rc.b_cbr = 1;
300                 param.rc.b_stat_read = 1;
301                 if( config->b_updatestats )
302                     param.rc.b_stat_write = 1;
303             }
304
305             break;
306         }
307     }
308
309     /* Open the encoder */
310     codec->h = x264_encoder_open( &param );
311
312     free( param.rc.psz_stat_out );
313     free( param.rc.psz_stat_in );
314
315     if( codec->h == NULL )
316         return ICERR_ERROR;
317
318     return ICERR_OK;
319 }
320
321 /* */
322 LRESULT compress_end(CODEC * codec)
323 {
324     if( codec->h != NULL )
325     {
326         x264_encoder_close( codec->h );
327         codec->h = NULL;
328     }
329
330     free( codec->hCons );
331     return ICERR_OK;
332 }
333
334 /* */
335 LRESULT compress( CODEC *codec, ICCOMPRESS *icc )
336 {
337     BITMAPINFOHEADER *inhdr = icc->lpbiInput;
338     BITMAPINFOHEADER *outhdr = icc->lpbiOutput;
339
340     x264_picture_t pic;
341
342     int        i_nal;
343     x264_nal_t *nal;
344     int        i_out;
345
346     int i;
347
348     /* Init the picture */
349     memset( &pic, 0, sizeof( x264_picture_t ) );
350     pic.img.i_csp = get_csp( inhdr );
351
352     /* For now biWidth can be divided by 16 so no problem */
353     switch( pic.img.i_csp & X264_CSP_MASK )
354     {
355         case X264_CSP_I420:
356         case X264_CSP_YV12:
357             pic.img.i_plane = 3;
358             pic.img.i_stride[0] = inhdr->biWidth;
359             pic.img.i_stride[1] =
360             pic.img.i_stride[2] = inhdr->biWidth / 2;
361
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;
365             break;
366
367         case X264_CSP_YUYV:
368             pic.img.i_plane = 1;
369             pic.img.i_stride[0] = 2 * inhdr->biWidth;
370             pic.img.plane[0]    = (uint8_t*)icc->lpInput;
371             break;
372
373         case X264_CSP_BGR:
374             pic.img.i_plane = 1;
375             pic.img.i_stride[0] = 3 * inhdr->biWidth;
376             pic.img.plane[0]    = (uint8_t*)icc->lpInput;
377             break;
378
379         case X264_CSP_BGRA:
380             pic.img.i_plane = 1;
381             pic.img.i_stride[0] = 4 * inhdr->biWidth;
382             pic.img.plane[0]    = (uint8_t*)icc->lpInput;
383             break;
384
385         default:
386             return ICERR_BADFORMAT;
387     }
388
389     /* encode it */
390     x264_encoder_encode( codec->h, &nal, &i_nal, &pic, &pic );
391
392     /* create bitstream, unless we're dropping it in 1st pass */
393     i_out = 0;
394
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] );
399
400             i_out += i_size;
401         }
402     }
403
404     outhdr->biSizeImage = i_out;
405
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;
410     else
411         *icc->lpdwFlags = 0;
412
413     return ICERR_OK;
414 }
415