From: Henrik Gramner Date: Sun, 11 Aug 2013 17:50:42 +0000 (+0200) Subject: Windows Unicode support X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fa3cac516cb71b8ece09cedbfd0ce631ca8a2a4c;p=libx264 Windows Unicode support Windows, unlike most other operating systems, uses UTF-16 for Unicode strings while x264 is designed for UTF-8. This patch does the following in order to handle things like Unicode filenames: * Keep strings internally as UTF-8. * Retrieve the CLI command line as UTF-16 and convert it to UTF-8. * Always use Unicode versions of Windows API functions and convert strings to UTF-16 when calling them. * Attempt to use legacy 8.3 short filenames for external libraries without Unicode support. --- diff --git a/common/common.c b/common/common.c index ba9f5d1c..c736c953 100644 --- a/common/common.c +++ b/common/common.c @@ -1104,7 +1104,7 @@ static void x264_log_default( void *p_unused, int i_level, const char *psz_fmt, break; } fprintf( stderr, "x264 [%s]: ", psz_prefix ); - vfprintf( stderr, psz_fmt, arg ); + x264_vfprintf( stderr, psz_fmt, arg ); } /**************************************************************************** @@ -1269,7 +1269,7 @@ char *x264_slurp_file( const char *filename ) int b_error = 0; size_t i_size; char *buf; - FILE *fh = fopen( filename, "rb" ); + FILE *fh = x264_fopen( filename, "rb" ); if( !fh ) return NULL; b_error |= fseek( fh, 0, SEEK_END ) < 0; diff --git a/common/opencl.c b/common/opencl.c index 974aff36..5d0dce50 100644 --- a/common/opencl.c +++ b/common/opencl.c @@ -28,7 +28,7 @@ #ifdef _WIN32 #include -#define ocl_open LoadLibrary( "OpenCL" ) +#define ocl_open LoadLibraryW( L"OpenCL" ) #define ocl_close FreeLibrary #define ocl_address GetProcAddress #else @@ -122,7 +122,7 @@ static int x264_detect_switchable_graphics( void ); static cl_program x264_opencl_cache_load( x264_t *h, const char *dev_name, const char *dev_vendor, const char *driver_version ) { /* try to load cached program binary */ - FILE *fp = fopen( h->param.psz_clbin_file, "rb" ); + FILE *fp = x264_fopen( h->param.psz_clbin_file, "rb" ); if( !fp ) return NULL; @@ -169,7 +169,7 @@ fail: * is also saved in the cache file so we do not reuse stale binaries */ static void x264_opencl_cache_save( x264_t *h, cl_program program, const char *dev_name, const char *dev_vendor, const char *driver_version ) { - FILE *fp = fopen( h->param.psz_clbin_file, "wb" ); + FILE *fp = x264_fopen( h->param.psz_clbin_file, "wb" ); if( !fp ) { x264_log( h, X264_LOG_INFO, "OpenCL: unable to open clbin file for write\n" ); @@ -304,7 +304,7 @@ static cl_program x264_opencl_compile( x264_t *h ) goto fail; } - FILE *log_file = fopen( "x264_kernel_build_log.txt", "w" ); + FILE *log_file = x264_fopen( "x264_kernel_build_log.txt", "w" ); if( !log_file ) { x264_log( h, X264_LOG_WARNING, "OpenCL: Compilation failed, unable to create file x264_kernel_build_log.txt\n" ); @@ -672,9 +672,9 @@ static int x264_detect_switchable_graphics( void ) int ret = 0; #ifdef _WIN32 - hDLL = LoadLibrary( "atiadlxx.dll" ); + hDLL = LoadLibraryW( L"atiadlxx.dll" ); if( !hDLL ) - hDLL = LoadLibrary( "atiadlxy.dll" ); + hDLL = LoadLibraryW( L"atiadlxy.dll" ); #else hDLL = dlopen( "libatiadlxx.so", RTLD_LAZY|RTLD_GLOBAL ); #endif diff --git a/common/osdep.c b/common/osdep.c index a1084d0f..6952dd0a 100644 --- a/common/osdep.c +++ b/common/osdep.c @@ -5,6 +5,7 @@ * * Authors: Steven Walters * Laurent Aimar + * Henrik Gramner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +27,11 @@ #include "common.h" +#ifdef _WIN32 +#include +#include +#endif + #if SYS_WINDOWS #include #include @@ -35,8 +41,6 @@ #include #if PTW32_STATIC_LIB -#define WIN32_LEAN_AND_MEAN -#include /* this is a global in pthread-win32 to indicate if it has been initialized or not */ extern int ptw32_processInitialized; #endif @@ -134,3 +138,64 @@ void x264_intel_cpu_indicator_init( void ) {} #endif #endif + +#ifdef _WIN32 +/* Functions for dealing with Unicode on Windows. */ +FILE *x264_fopen( const char *filename, const char *mode ) +{ + wchar_t filename_utf16[MAX_PATH]; + wchar_t mode_utf16[16]; + if( utf8_to_utf16( filename, filename_utf16 ) && utf8_to_utf16( mode, mode_utf16 ) ) + return _wfopen( filename_utf16, mode_utf16 ); + return NULL; +} + +int x264_rename( const char *oldname, const char *newname ) +{ + wchar_t oldname_utf16[MAX_PATH]; + wchar_t newname_utf16[MAX_PATH]; + if( utf8_to_utf16( oldname, oldname_utf16 ) && utf8_to_utf16( newname, newname_utf16 ) ) + { + /* POSIX says that rename() removes the destination, but Win32 doesn't. */ + _wunlink( newname_utf16 ); + return _wrename( oldname_utf16, newname_utf16 ); + } + return -1; +} + +int x264_stat( const char *path, x264_struct_stat *buf ) +{ + wchar_t path_utf16[MAX_PATH]; + if( utf8_to_utf16( path, path_utf16 ) ) + return _wstati64( path_utf16, buf ); + return -1; +} + +int x264_vfprintf( FILE *stream, const char *format, va_list arg ) +{ + HANDLE console = NULL; + DWORD mode; + + if( stream == stdout ) + console = GetStdHandle( STD_OUTPUT_HANDLE ); + else if( stream == stderr ) + console = GetStdHandle( STD_ERROR_HANDLE ); + + /* Only attempt to convert to UTF-16 when writing to a non-redirected console screen buffer. */ + if( GetConsoleMode( console, &mode ) ) + { + char buf[4096]; + wchar_t buf_utf16[4096]; + + int length = vsnprintf( buf, sizeof(buf), format, arg ); + if( length > 0 && length < sizeof(buf) ) + { + /* WriteConsoleW is the most reliable way to output Unicode to a console. */ + int length_utf16 = MultiByteToWideChar( CP_UTF8, 0, buf, length, buf_utf16, sizeof(buf_utf16)/sizeof(wchar_t) ); + WriteConsoleW( console, buf_utf16, length_utf16, NULL, NULL ); + return length; + } + } + return vfprintf( stream, format, arg ); +} +#endif diff --git a/common/osdep.h b/common/osdep.h index cdc0f61b..dba1eb07 100644 --- a/common/osdep.h +++ b/common/osdep.h @@ -5,6 +5,7 @@ * * Authors: Loren Merritt * Laurent Aimar + * Henrik Gramner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,6 +33,7 @@ #include #include #include +#include #include "config.h" @@ -40,11 +42,6 @@ #define log2(x) (log(x)/0.693147180559945) #endif -#ifdef _WIN32 -#include // _setmode() -#include // _O_BINARY -#endif - #ifdef __ICL #define inline __inline #define strcasecmp _stricmp @@ -67,11 +64,27 @@ #if !defined(isfinite) && (SYS_OPENBSD || SYS_SunOS) #define isfinite finite #endif + #ifdef _WIN32 -#define rename(src,dst) (unlink(dst), rename(src,dst)) // POSIX says that rename() removes the destination, but win32 doesn't. #ifndef strtok_r #define strtok_r(str,delim,save) strtok(str,delim) #endif + +#define utf8_to_utf16( utf8, utf16 )\ + MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, utf8, -1, utf16, sizeof(utf16)/sizeof(wchar_t) ) +FILE *x264_fopen( const char *filename, const char *mode ); +int x264_rename( const char *oldname, const char *newname ); +#define x264_struct_stat struct _stati64 +#define x264_fstat _fstati64 +int x264_stat( const char *path, x264_struct_stat *buf ); +int x264_vfprintf( FILE *stream, const char *format, va_list arg ); +#else +#define x264_fopen fopen +#define x264_rename rename +#define x264_struct_stat struct stat +#define x264_fstat fstat +#define x264_stat stat +#define x264_vfprintf vfprintf #endif #ifdef __ICL @@ -366,16 +379,16 @@ static ALWAYS_INLINE void x264_prefetch( void *p ) static inline uint8_t x264_is_regular_file( FILE *filehandle ) { - struct stat file_stat; - if( fstat( fileno( filehandle ), &file_stat ) ) + x264_struct_stat file_stat; + if( x264_fstat( fileno( filehandle ), &file_stat ) ) return -1; return S_ISREG( file_stat.st_mode ); } static inline uint8_t x264_is_regular_file_path( const char *filename ) { - struct stat file_stat; - if( stat( filename, &file_stat ) ) + x264_struct_stat file_stat; + if( x264_stat( filename, &file_stat ) ) return -1; return S_ISREG( file_stat.st_mode ); } diff --git a/common/win32thread.c b/common/win32thread.c index 4be914f1..b446b76c 100644 --- a/common/win32thread.c +++ b/common/win32thread.c @@ -261,7 +261,7 @@ int x264_pthread_cond_wait( x264_pthread_cond_t *cond, x264_pthread_mutex_t *mut int x264_win32_threading_init( void ) { /* find function pointers to API functions, if they exist */ - HANDLE kernel_dll = GetModuleHandle( TEXT( "kernel32.dll" ) ); + HANDLE kernel_dll = GetModuleHandleW( L"kernel32.dll" ); thread_control.cond_init = (void*)GetProcAddress( kernel_dll, "InitializeConditionVariable" ); if( thread_control.cond_init ) { @@ -288,7 +288,7 @@ int x264_pthread_num_processors_np( void ) * On platforms that support processor grouping, use GetThreadGroupAffinity to get the current thread's affinity instead. */ #if ARCH_X86_64 /* find function pointers to API functions specific to x86_64 platforms, if they exist */ - HANDLE kernel_dll = GetModuleHandle( TEXT( "kernel32.dll" ) ); + HANDLE kernel_dll = GetModuleHandleW( L"kernel32.dll" ); BOOL (*get_thread_affinity)( HANDLE thread, x264_group_affinity_t *group_affinity ) = (void*)GetProcAddress( kernel_dll, "GetThreadGroupAffinity" ); if( get_thread_affinity ) { diff --git a/common/win32thread.h b/common/win32thread.h index 844b18ab..48494d51 100644 --- a/common/win32thread.h +++ b/common/win32thread.h @@ -26,7 +26,6 @@ #ifndef X264_WIN32THREAD_H #define X264_WIN32THREAD_H -#define WIN32_LEAN_AND_MEAN #include /* the following macro is used within x264 */ #undef ERROR diff --git a/configure b/configure index 6f3ac025..07796b1f 100755 --- a/configure +++ b/configure @@ -510,6 +510,7 @@ case $host_os in else SYS="WINDOWS" DEVNULL="NUL" + LDFLAGSCLI="$LDFLAGSCLI -lshell32" RC="${RC-${cross_prefix}windres}" fi ;; @@ -517,6 +518,7 @@ case $host_os in SYS="WINDOWS" EXE=".exe" DEVNULL="NUL" + LDFLAGSCLI="$LDFLAGSCLI -lshell32" [ $compiler = ICL ] && RC="${RC-rc}" || RC="${RC-${cross_prefix}windres}" ;; sunos*|solaris*) @@ -945,7 +947,7 @@ fi if [ "$avs" = "auto" ] ; then avs="no" # cygwin can use avisynth if it can use LoadLibrary - if [ $SYS = WINDOWS ] || ([ $SYS = CYGWIN ] && cc_check windows.h "" "LoadLibrary(0);") ; then + if [ $SYS = WINDOWS ] || ([ $SYS = CYGWIN ] && cc_check windows.h "" "LoadLibraryW(0);") ; then avs="avisynth" define HAVE_AVS define USE_AVXSYNTH 0 @@ -1043,7 +1045,7 @@ if [ "$opencl" = "yes" ]; then fi log_ok # cygwin can use opencl if it can use LoadLibrary - if [ $SYS = WINDOWS ] || ([ $SYS = CYGWIN ] && cc_check windows.h "" "LoadLibrary(0);") ; then + if [ $SYS = WINDOWS ] || ([ $SYS = CYGWIN ] && cc_check windows.h "" "LoadLibraryW(0);") ; then opencl="yes" define HAVE_OPENCL elif [ "$SYS" = "LINUX" -o "$SYS" = "MACOSX" ] ; then diff --git a/encoder/encoder.c b/encoder/encoder.c index 3b3bc638..52392269 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -82,7 +82,7 @@ static int x264_threadpool_wait_all( x264_t *h ) static void x264_frame_dump( x264_t *h ) { - FILE *f = fopen( h->param.psz_dump_yuv, "r+b" ); + FILE *f = x264_fopen( h->param.psz_dump_yuv, "r+b" ); if( !f ) return; @@ -1623,7 +1623,7 @@ x264_t *x264_encoder_open( x264_param_t *param ) if( h->param.psz_dump_yuv ) { /* create or truncate the reconstructed video file */ - FILE *f = fopen( h->param.psz_dump_yuv, "w" ); + FILE *f = x264_fopen( h->param.psz_dump_yuv, "w" ); if( !f ) { x264_log( h, X264_LOG_ERROR, "dump_yuv: can't write to %s\n", h->param.psz_dump_yuv ); diff --git a/encoder/ratecontrol.c b/encoder/ratecontrol.c index 00121847..030d2ab8 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -872,7 +872,7 @@ int x264_ratecontrol_new( x264_t *h ) char *mbtree_stats_in = x264_strcat_filename( h->param.rc.psz_stat_in, ".mbtree" ); if( !mbtree_stats_in ) return -1; - rc->p_mbtree_stat_file_in = fopen( mbtree_stats_in, "rb" ); + rc->p_mbtree_stat_file_in = x264_fopen( mbtree_stats_in, "rb" ); x264_free( mbtree_stats_in ); if( !rc->p_mbtree_stat_file_in ) { @@ -1140,7 +1140,7 @@ parse_error: if( !rc->psz_stat_file_tmpname ) return -1; - rc->p_stat_file_out = fopen( rc->psz_stat_file_tmpname, "wb" ); + rc->p_stat_file_out = x264_fopen( rc->psz_stat_file_tmpname, "wb" ); if( rc->p_stat_file_out == NULL ) { x264_log( h, X264_LOG_ERROR, "ratecontrol_init: can't open stats file\n" ); @@ -1158,7 +1158,7 @@ parse_error: if( !rc->psz_mbtree_stat_file_tmpname || !rc->psz_mbtree_stat_file_name ) return -1; - rc->p_mbtree_stat_file_out = fopen( rc->psz_mbtree_stat_file_tmpname, "wb" ); + rc->p_mbtree_stat_file_out = x264_fopen( rc->psz_mbtree_stat_file_tmpname, "wb" ); if( rc->p_mbtree_stat_file_out == NULL ) { x264_log( h, X264_LOG_ERROR, "ratecontrol_init: can't open mbtree stats file\n" ); @@ -1338,7 +1338,7 @@ void x264_ratecontrol_delete( x264_t *h ) b_regular_file = x264_is_regular_file( rc->p_stat_file_out ); fclose( rc->p_stat_file_out ); if( h->i_frame >= rc->num_entries && b_regular_file ) - if( rename( rc->psz_stat_file_tmpname, h->param.rc.psz_stat_out ) != 0 ) + if( x264_rename( rc->psz_stat_file_tmpname, h->param.rc.psz_stat_out ) != 0 ) { x264_log( h, X264_LOG_ERROR, "failed to rename \"%s\" to \"%s\"\n", rc->psz_stat_file_tmpname, h->param.rc.psz_stat_out ); @@ -1350,7 +1350,7 @@ void x264_ratecontrol_delete( x264_t *h ) b_regular_file = x264_is_regular_file( rc->p_mbtree_stat_file_out ); fclose( rc->p_mbtree_stat_file_out ); if( h->i_frame >= rc->num_entries && b_regular_file ) - if( rename( rc->psz_mbtree_stat_file_tmpname, rc->psz_mbtree_stat_file_name ) != 0 ) + if( x264_rename( rc->psz_mbtree_stat_file_tmpname, rc->psz_mbtree_stat_file_name ) != 0 ) { x264_log( h, X264_LOG_ERROR, "failed to rename \"%s\" to \"%s\"\n", rc->psz_mbtree_stat_file_tmpname, rc->psz_mbtree_stat_file_name ); diff --git a/input/avs.c b/input/avs.c index d237a446..6e8801c2 100644 --- a/input/avs.c +++ b/input/avs.c @@ -35,7 +35,7 @@ #define avs_address dlsym #else #include -#define avs_open LoadLibrary( "avisynth" ) +#define avs_open LoadLibraryW( L"avisynth" ) #define avs_close FreeLibrary #define avs_address GetProcAddress #endif @@ -172,7 +172,7 @@ static float get_avs_version( avs_hnd_t *h ) static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt ) { - FILE *fh = fopen( psz_filename, "r" ); + FILE *fh = x264_fopen( psz_filename, "r" ); if( !fh ) return -1; FAIL_IF_ERROR( !x264_is_regular_file( fh ), "AVS input is incompatible with non-regular file `%s'\n", psz_filename ); @@ -192,7 +192,16 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c if( avs_version <= 0 ) return -1; x264_cli_log( "avs", X264_LOG_DEBUG, "using avisynth version %.2f\n", avs_version ); + +#ifdef _WIN32 + /* Avisynth doesn't support Unicode filenames. */ + char ansi_filename[MAX_PATH]; + FAIL_IF_ERROR( !x264_ansi_filename( psz_filename, ansi_filename, MAX_PATH, 0 ), "invalid ansi filename\n" ); + AVS_Value arg = avs_new_value_string( ansi_filename ); +#else AVS_Value arg = avs_new_value_string( psz_filename ); +#endif + AVS_Value res; char *filename_ext = get_filename_extension( psz_filename ); diff --git a/input/ffms.c b/input/ffms.c index 1a067780..054728b2 100644 --- a/input/ffms.c +++ b/input/ffms.c @@ -5,6 +5,7 @@ * * Authors: Mike Gurlitz * Steven Walters + * Henrik Gramner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,8 +35,6 @@ #ifdef _WIN32 #include -#else -#define SetConsoleTitle(t) #endif typedef struct @@ -60,7 +59,7 @@ static int FFMS_CC update_progress( int64_t current, int64_t total, void *privat char buf[200]; sprintf( buf, "ffms [info]: indexing input file [%.1f%%]", 100.0 * current / total ); fprintf( stderr, "%s \r", buf+5 ); - SetConsoleTitle( buf ); + x264_cli_set_console_title( buf ); fflush( stderr ); return 0; } @@ -82,7 +81,21 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c ffms_hnd_t *h = calloc( 1, sizeof(ffms_hnd_t) ); if( !h ) return -1; + +#ifdef __MINGW32__ + /* FFMS supports UTF-8 filenames, but it uses std::fstream internally which is broken with Unicode in MinGW. */ FFMS_Init( 0, 0 ); + char src_filename[MAX_PATH]; + char idx_filename[MAX_PATH]; + FAIL_IF_ERROR( !x264_ansi_filename( psz_filename, src_filename, MAX_PATH, 0 ), "invalid ansi filename\n" ); + if( opt->index_file ) + FAIL_IF_ERROR( !x264_ansi_filename( opt->index_file, idx_filename, MAX_PATH, 1 ), "invalid ansi filename\n" ); +#else + FFMS_Init( 0, 1 ); + char *src_filename = psz_filename; + char *idx_filename = opt->index_file; +#endif + FFMS_ErrorInfo e; e.BufferSize = 0; int seekmode = opt->seek ? FFMS_SEEK_NORMAL : FFMS_SEEK_LINEAR_NO_RW; @@ -90,29 +103,29 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c FFMS_Index *idx = NULL; if( opt->index_file ) { - struct stat index_s, input_s; - if( !stat( opt->index_file, &index_s ) && !stat( psz_filename, &input_s ) && + x264_struct_stat index_s, input_s; + if( !x264_stat( opt->index_file, &index_s ) && !x264_stat( psz_filename, &input_s ) && input_s.st_mtime < index_s.st_mtime ) - idx = FFMS_ReadIndex( opt->index_file, &e ); + idx = FFMS_ReadIndex( idx_filename, &e ); } if( !idx ) { if( opt->progress ) { - idx = FFMS_MakeIndex( psz_filename, 0, 0, NULL, NULL, 0, update_progress, &h->time, &e ); + idx = FFMS_MakeIndex( src_filename, 0, 0, NULL, NULL, 0, update_progress, &h->time, &e ); fprintf( stderr, " \r" ); } else - idx = FFMS_MakeIndex( psz_filename, 0, 0, NULL, NULL, 0, NULL, NULL, &e ); + idx = FFMS_MakeIndex( src_filename, 0, 0, NULL, NULL, 0, NULL, NULL, &e ); FAIL_IF_ERROR( !idx, "could not create index\n" ) - if( opt->index_file && FFMS_WriteIndex( opt->index_file, idx, &e ) ) + if( opt->index_file && FFMS_WriteIndex( idx_filename, idx, &e ) ) x264_cli_log( "ffms", X264_LOG_WARNING, "could not write index file\n" ); } int trackno = FFMS_GetFirstTrackOfType( idx, FFMS_TYPE_VIDEO, &e ); FAIL_IF_ERROR( trackno < 0, "could not find video track\n" ) - h->video_source = FFMS_CreateVideoSource( psz_filename, trackno, idx, 1, seekmode, &e ); + h->video_source = FFMS_CreateVideoSource( src_filename, trackno, idx, 1, seekmode, &e ); FAIL_IF_ERROR( !h->video_source, "could not create video source\n" ) h->track = FFMS_GetTrackFromVideo( h->video_source ); diff --git a/input/raw.c b/input/raw.c index e853dd0a..32134358 100644 --- a/input/raw.c +++ b/input/raw.c @@ -70,7 +70,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c if( !strcmp( psz_filename, "-" ) ) h->fh = stdin; else - h->fh = fopen( psz_filename, "rb" ); + h->fh = x264_fopen( psz_filename, "rb" ); if( h->fh == NULL ) return -1; diff --git a/input/timecode.c b/input/timecode.c index a476e5d4..7653f837 100644 --- a/input/timecode.c +++ b/input/timecode.c @@ -368,7 +368,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c timecode_input.picture_alloc = h->input.picture_alloc; timecode_input.picture_clean = h->input.picture_clean; - tcfile_in = fopen( psz_filename, "rb" ); + tcfile_in = x264_fopen( psz_filename, "rb" ); FAIL_IF_ERROR( !tcfile_in, "can't open `%s'\n", psz_filename ) else if( !x264_is_regular_file( tcfile_in ) ) { diff --git a/input/y4m.c b/input/y4m.c index 014450cb..da8f9803 100644 --- a/input/y4m.c +++ b/input/y4m.c @@ -81,7 +81,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c if( !strcmp( psz_filename, "-" ) ) h->fh = stdin; else - h->fh = fopen(psz_filename, "rb"); + h->fh = x264_fopen(psz_filename, "rb"); if( h->fh == NULL ) return -1; diff --git a/output/flv_bytestream.c b/output/flv_bytestream.c index 44c19238..38a4ca9e 100644 --- a/output/flv_bytestream.c +++ b/output/flv_bytestream.c @@ -96,7 +96,7 @@ flv_buffer *flv_create_writer( const char *filename ) if( !strcmp( filename, "-" ) ) c->fp = stdout; else - c->fp = fopen( filename, "wb" ); + c->fp = x264_fopen( filename, "wb" ); if( !c->fp ) { free( c ); diff --git a/output/matroska_ebml.c b/output/matroska_ebml.c index 2f41a2d1..12c43d55 100644 --- a/output/matroska_ebml.c +++ b/output/matroska_ebml.c @@ -307,7 +307,7 @@ mk_writer *mk_create_writer( const char *filename ) if( !strcmp( filename, "-" ) ) w->fp = stdout; else - w->fp = fopen( filename, "wb" ); + w->fp = x264_fopen( filename, "wb" ); if( !w->fp ) { mk_destroy_contexts( w ); diff --git a/output/mp4.c b/output/mp4.c index ee54e66f..c6192a89 100644 --- a/output/mp4.c +++ b/output/mp4.c @@ -173,7 +173,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt mp4_hnd_t *p_mp4; *p_handle = NULL; - FILE *fh = fopen( psz_filename, "w" ); + FILE *fh = x264_fopen( psz_filename, "w" ); if( !fh ) return -1; FAIL_IF_ERR( !x264_is_regular_file( fh ), "mp4", "MP4 output is incompatible with non-regular file `%s'\n", psz_filename ) @@ -183,7 +183,15 @@ static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt return -1; memset( p_mp4, 0, sizeof(mp4_hnd_t) ); + +#ifdef _WIN32 + /* GPAC doesn't support Unicode filenames. */ + char ansi_filename[MAX_PATH]; + FAIL_IF_ERR( !x264_ansi_filename( psz_filename, ansi_filename, MAX_PATH, 1 ), "mp4", "invalid ansi filename\n" ) + p_mp4->p_file = gf_isom_open( ansi_filename, GF_ISOM_OPEN_WRITE, NULL ); +#else p_mp4->p_file = gf_isom_open( psz_filename, GF_ISOM_OPEN_WRITE, NULL ); +#endif p_mp4->b_dts_compress = opt->use_dts_compress; diff --git a/output/raw.c b/output/raw.c index d8ed3a98..4bfb7895 100644 --- a/output/raw.c +++ b/output/raw.c @@ -30,7 +30,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt { if( !strcmp( psz_filename, "-" ) ) *p_handle = stdout; - else if( !(*p_handle = fopen( psz_filename, "w+b" )) ) + else if( !(*p_handle = x264_fopen( psz_filename, "w+b" )) ) return -1; return 0; diff --git a/x264.c b/x264.c index e2296048..3415cd65 100644 --- a/x264.c +++ b/x264.c @@ -8,6 +8,7 @@ * Steven Walters * Fiona Glaser * Kieran Kunhya + * Henrik Gramner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +28,15 @@ * For more information, contact us at licensing@x264.com. *****************************************************************************/ +#ifdef _WIN32 +/* The following two defines must be located before the inclusion of any system header files. */ +#define WINVER 0x0500 +#define _WIN32_WINNT 0x0500 +#include +#include /* _setmode() */ +#include /* _O_BINARY */ +#endif + #include #define _GNU_SOURCE #include @@ -38,13 +48,6 @@ #define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "x264", __VA_ARGS__ ) -#ifdef _WIN32 -#include -#else -#define GetConsoleTitle(t,n) -#define SetConsoleTitle(t) -#endif - #if HAVE_LAVF #undef DECLARE_ALIGNED #include @@ -61,18 +64,97 @@ #include #endif +#ifdef _WIN32 +#define CONSOLE_TITLE_SIZE 200 +static wchar_t org_console_title[CONSOLE_TITLE_SIZE] = L""; + +void x264_cli_set_console_title( const char *title ) +{ + wchar_t title_utf16[CONSOLE_TITLE_SIZE]; + if( utf8_to_utf16( title, title_utf16 ) ) + SetConsoleTitleW( title_utf16 ); +} + +static int utf16_to_ansi( const wchar_t *utf16, char *ansi, int size ) +{ + int invalid; + return WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, utf16, -1, ansi, size, NULL, &invalid ) && !invalid; +} + +/* Some external libraries doesn't support Unicode in filenames, + * as a workaround we can try to get an ANSI filename instead. */ +int x264_ansi_filename( const char *filename, char *ansi_filename, int size, int create_file ) +{ + wchar_t filename_utf16[MAX_PATH]; + if( utf8_to_utf16( filename, filename_utf16 ) ) + { + if( create_file ) + { + /* Create the file using the Unicode filename if it doesn't already exist. */ + FILE *fh = _wfopen( filename_utf16, L"ab" ); + if( fh ) + fclose( fh ); + } + + /* Check if the filename already is valid ANSI. */ + if( utf16_to_ansi( filename_utf16, ansi_filename, size ) ) + return 1; + + /* Check for a legacy 8.3 short filename. */ + int short_length = GetShortPathNameW( filename_utf16, filename_utf16, MAX_PATH ); + if( short_length > 0 && short_length < MAX_PATH ) + if( utf16_to_ansi( filename_utf16, ansi_filename, size ) ) + return 1; + } + return 0; +} + +/* Retrieve command line arguments as UTF-8. */ +static int get_argv_utf8( int *argc_ptr, char ***argv_ptr ) +{ + int ret = 0; + wchar_t **argv_utf16 = CommandLineToArgvW( GetCommandLineW(), argc_ptr ); + if( argv_utf16 ) + { + int argc = *argc_ptr; + int offset = (argc+1) * sizeof(char*); + int size = offset; + + for( int i = 0; i < argc; i++ ) + size += WideCharToMultiByte( CP_UTF8, 0, argv_utf16[i], -1, NULL, 0, NULL, NULL ); + + char **argv = *argv_ptr = malloc( size ); + if( argv ) + { + for( int i = 0; i < argc; i++ ) + { + argv[i] = (char*)argv + offset; + offset += WideCharToMultiByte( CP_UTF8, 0, argv_utf16[i], -1, argv[i], size-offset, NULL, NULL ); + } + argv[argc] = NULL; + ret = 1; + } + LocalFree( argv_utf16 ); + } + return ret; +} +#endif + /* Ctrl-C handler */ static volatile int b_ctrl_c = 0; static int b_exit_on_ctrl_c = 0; static void sigint_handler( int a ) { if( b_exit_on_ctrl_c ) - exit(0); + { +#ifdef _WIN32 + SetConsoleTitleW( org_console_title ); +#endif + exit( 0 ); + } b_ctrl_c = 1; } -static char UNUSED originalCTitle[200] = ""; - typedef struct { int b_progress; int i_seek; @@ -211,7 +293,7 @@ void x264_cli_log( const char *name, int i_level, const char *fmt, ... ) fprintf( stderr, "%s [%s]: ", name, s_level ); va_list arg; va_start( arg, fmt ); - vfprintf( stderr, fmt, arg ); + x264_vfprintf( stderr, fmt, arg ); va_end( arg ); } @@ -221,7 +303,7 @@ void x264_cli_printf( int i_level, const char *fmt, ... ) return; va_list arg; va_start( arg, fmt ); - vfprintf( stderr, fmt, arg ); + x264_vfprintf( stderr, fmt, arg ); va_end( arg ); } @@ -275,18 +357,22 @@ int main( int argc, char **argv ) FAIL_IF_ERROR( x264_threading_init(), "unable to initialize threading\n" ) #ifdef _WIN32 - _setmode(_fileno(stdin), _O_BINARY); - _setmode(_fileno(stdout), _O_BINARY); -#endif + FAIL_IF_ERROR( !get_argv_utf8( &argc, &argv ), "unable to convert command line to UTF-8\n" ) - GetConsoleTitle( originalCTitle, sizeof(originalCTitle) ); + GetConsoleTitleW( org_console_title, CONSOLE_TITLE_SIZE ); + _setmode( _fileno( stdin ), _O_BINARY ); + _setmode( _fileno( stdout ), _O_BINARY ); + _setmode( _fileno( stderr ), _O_BINARY ); +#endif /* Parse command line */ if( parse( argc, argv, ¶m, &opt ) < 0 ) ret = -1; +#ifdef _WIN32 /* Restore title; it can be changed by input modules */ - SetConsoleTitle( originalCTitle ); + SetConsoleTitleW( org_console_title ); +#endif /* Control-C handler */ signal( SIGINT, sigint_handler ); @@ -306,7 +392,10 @@ int main( int argc, char **argv ) if( opt.qpfile ) fclose( opt.qpfile ); - SetConsoleTitle( originalCTitle ); +#ifdef _WIN32 + SetConsoleTitleW( org_console_title ); + free( argv ); +#endif return ret; } @@ -1096,7 +1185,7 @@ static int select_input( const char *demuxer, char *used_demuxer, char *filename b_regular = b_regular && x264_is_regular_file_path( filename ); if( b_regular ) { - FILE *f = fopen( filename, "r" ); + FILE *f = x264_fopen( filename, "r" ); if( f ) { b_regular = x264_is_regular_file( f ); @@ -1340,7 +1429,7 @@ static int parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ) input_opt.index_file = optarg; break; case OPT_QPFILE: - opt->qpfile = fopen( optarg, "rb" ); + opt->qpfile = x264_fopen( optarg, "rb" ); FAIL_IF_ERROR( !opt->qpfile, "can't open qpfile `%s'\n", optarg ) if( !x264_is_regular_file( opt->qpfile ) ) { @@ -1399,7 +1488,7 @@ static int parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ) tcfile_name = optarg; break; case OPT_TCFILE_OUT: - opt->tcfile_out = fopen( optarg, "wb" ); + opt->tcfile_out = x264_fopen( optarg, "wb" ); FAIL_IF_ERROR( !opt->tcfile_out, "can't open `%s'\n", optarg ) break; case OPT_TIMEBASE: @@ -1723,11 +1812,9 @@ static int64_t print_status( int64_t i_start, int64_t i_previous, int i_frame, i eta/3600, (eta/60)%60, eta%60 ); } else - { sprintf( buf, "x264 %d frames: %.2f fps, %.2f kb/s", i_frame, fps, bitrate ); - } fprintf( stderr, "%s \r", buf+5 ); - SetConsoleTitle( buf ); + x264_cli_set_console_title( buf ); fflush( stderr ); // needed in windows return i_time; } diff --git a/x264.h b/x264.h index f6611385..c3e2dba8 100644 --- a/x264.h +++ b/x264.h @@ -41,7 +41,7 @@ #include "x264_config.h" -#define X264_BUILD 137 +#define X264_BUILD 138 /* Application developers planning to link against a shared library version of * libx264 from a Microsoft Visual Studio or similar development environment @@ -334,7 +334,7 @@ typedef struct x264_param_t int b_constrained_intra; int i_cqm_preset; - char *psz_cqm_file; /* JM format */ + char *psz_cqm_file; /* filename (in UTF-8) of CQM file, JM format */ uint8_t cqm_4iy[16]; /* used only if i_cqm_preset == X264_CQM_CUSTOM */ uint8_t cqm_4py[16]; uint8_t cqm_4ic[16]; @@ -350,7 +350,7 @@ typedef struct x264_param_t int i_log_level; int b_visualize; int b_full_recon; /* fully reconstruct frames, even when not necessary for encoding. Implied by psz_dump_yuv */ - char *psz_dump_yuv; /* filename for reconstructed frames */ + char *psz_dump_yuv; /* filename (in UTF-8) for reconstructed frames */ /* Encoder analyser parameters */ struct @@ -416,9 +416,9 @@ typedef struct x264_param_t /* 2pass */ int b_stat_write; /* Enable stat writing in psz_stat_out */ - char *psz_stat_out; + char *psz_stat_out; /* output filename (in UTF-8) of the 2pass stats file */ int b_stat_read; /* Read stat from psz_stat_in and use it */ - char *psz_stat_in; + char *psz_stat_in; /* input filename (in UTF-8) of the 2pass stats file */ /* 2pass params (same as ffmpeg ones) */ float f_qcompress; /* 0.0 => cbr, 1.0 => constant qp */ @@ -486,7 +486,7 @@ typedef struct x264_param_t int b_opencl; /* use OpenCL when available */ int i_opencl_device; /* specify count of GPU devices to skip, for CLI users */ void *opencl_device_id; /* pass explicit cl_device_id as void*, for API users */ - char *psz_clbin_file; /* compiled OpenCL kernel cache file */ + char *psz_clbin_file; /* filename (in UTF-8) of the compiled OpenCL kernel cache file */ /* Slicing parameters */ int i_slice_max_size; /* Max size per slice in bytes; includes estimated NAL overhead. */ diff --git a/x264cli.h b/x264cli.h index 74376025..1794992a 100644 --- a/x264cli.h +++ b/x264cli.h @@ -63,6 +63,13 @@ static inline char *get_filename_extension( char *filename ) void x264_cli_log( const char *name, int i_level, const char *fmt, ... ); void x264_cli_printf( int i_level, const char *fmt, ... ); +#ifdef _WIN32 +void x264_cli_set_console_title( const char *title ); +int x264_ansi_filename( const char *filename, char *ansi_filename, int size, int create_file ); +#else +#define x264_cli_set_console_title( title ) +#endif + #define RETURN_IF_ERR( cond, name, ret, ... )\ if( cond )\ {\ diff --git a/x264res.rc b/x264res.rc index 73c82ef9..d25c8108 100644 --- a/x264res.rc +++ b/x264res.rc @@ -3,7 +3,7 @@ ***************************************************************************** * Copyright (C) 2012-2013 x264 project * - * Authors: Henrik Gramner + * Authors: Henrik Gramner * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,7 +50,7 @@ FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN - BLOCK "040904E4" + BLOCK "040904B0" BEGIN VALUE "CompanyName", "x264 project" #ifdef DLL @@ -73,6 +73,6 @@ BEGIN BLOCK "VarFileInfo" BEGIN - VALUE "Translation", 0x0409, 0x04E4 + VALUE "Translation", 0x0409, 0x04B0 /* U.S. English (Unicode) */ END END