HandBrake/libhb/ports.c

1760 lines
45 KiB
C

/* ports.c
Copyright (c) 2003-2025 HandBrake Team
This file is part of the HandBrake source code
Homepage: <http://handbrake.fr/>.
It may be used under the terms of the GNU General Public License v2.
For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
*/
#include "handbrake/project.h"
#ifdef SYS_MINGW
#define _WIN32_WINNT 0x0601
#endif
#ifdef SYS_LINUX
#define _GNU_SOURCE
#include <sched.h>
#endif
#include <pthread.h>
#if defined(SYS_DARWIN) || defined(SYS_FREEBSD) || defined(SYS_NETBSD) || defined(SYS_OPENBSD)
#include <sys/types.h>
#include <sys/sysctl.h>
#if HB_PROJECT_FEATURE_QSV && defined(SYS_FREEBSD)
#include <libdrm/drm.h>
#include <fcntl.h>
#endif
#endif
#ifdef SYS_MINGW
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <dlfcn.h>
#endif
#ifdef SYS_CYGWIN
#include <windows.h>
#endif
#ifdef SYS_MINGW
#include <pthread.h>
#include <windows.h>
#include <wchar.h>
#include <mbctype.h>
#include <locale.h>
#include <shlobj.h>
#endif
#ifdef SYS_SunOS
#include <sys/processor.h>
#endif
#include <time.h>
#include <sys/time.h>
#include <ctype.h>
#if defined( SYS_LINUX )
#include <linux/cdrom.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#if HB_PROJECT_FEATURE_QSV
#include <libdrm/drm.h>
#endif
#endif
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_NETBSD) || defined(SYS_OPENBSD)
#include <sys/utsname.h>
#endif
#ifdef __APPLE__
#include <CoreServices/CoreServices.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#endif
#include <stddef.h>
#include <unistd.h>
#include "handbrake/handbrake.h"
#include "libavutil/cpu.h"
/************************************************************************
* hb_get_date()
************************************************************************
* Returns the current date in milliseconds.
* On Win32, we implement a gettimeofday emulation here because
* libdvdread and libmp4v2 use it without checking.
************************************************************************/
/*
#ifdef SYS_CYGWIN
struct timezone
{
};
int gettimeofday( struct timeval * tv, struct timezone * tz )
{
int tick;
tick = GetTickCount();
tv->tv_sec = tick / 1000;
tv->tv_usec = ( tick % 1000 ) * 1000;
return 0;
}
#endif
*/
int hb_dvd_region(const char *device, int *region_mask)
{
#if defined( DVD_LU_SEND_RPC_STATE ) && defined( DVD_AUTH )
struct stat st;
dvd_authinfo ai;
int fd, ret;
fd = open( device, O_RDONLY );
if ( fd < 0 )
return -1;
if ( fstat( fd, &st ) < 0 )
{
close( fd );
return -1;
}
if ( !( S_ISBLK( st.st_mode ) || S_ISCHR( st.st_mode ) ) )
{
close( fd );
return -1;
}
ai.type = DVD_LU_SEND_RPC_STATE;
ret = ioctl(fd, DVD_AUTH, &ai);
close( fd );
if ( ret < 0 )
return ret;
*region_mask = ai.lrpcs.region_mask;
return 0;
#else
return -1;
#endif
}
uint64_t hb_get_date()
{
struct timeval tv;
gettimeofday( &tv, NULL );
return( (uint64_t) tv.tv_sec * 1000 + (uint64_t) tv.tv_usec / 1000 );
}
uint64_t hb_get_time_us()
{
#ifdef SYS_MINGW
static LARGE_INTEGER frequency;
LARGE_INTEGER cur_time;
if (frequency.QuadPart == 0)
{
QueryPerformanceFrequency(&frequency);
}
QueryPerformanceCounter(&cur_time);
return (uint64_t)(1000000 * cur_time.QuadPart / frequency.QuadPart);
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return ((uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec);
#endif
}
/************************************************************************
* hb_snooze()
************************************************************************
* Waits <delay> milliseconds.
************************************************************************/
void hb_snooze( int delay )
{
if( delay < 1 )
{
return;
}
#if defined( SYS_CYGWIN ) || defined( SYS_MINGW )
Sleep( delay );
#else
usleep( 1000 * delay );
#endif
}
/************************************************************************
* Get information about the operaring system
************************************************************************/
static void init_system_info();
struct
{
const char *name;
const char *version;
const char *build;
} hb_system_info;
static void init_system_info()
{
if (hb_system_info.name != NULL)
{
return;
}
#if defined(SYS_DARWIN)
char buf[256];
size_t buflen = sizeof(buf);
if (sysctlbyname("kern.osproductversion", &buf, &buflen, NULL, 0) == 0)
{
hb_system_info.version = strdup(buf);
}
buflen = sizeof(buf);
if (sysctlbyname("kern.osversion", &buf, &buflen, NULL, 0) == 0)
{
hb_system_info.build = strdup(buf);
}
hb_system_info.name = "macOS";
#elif defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_NETBSD) || defined(SYS_OPENBSD)
struct utsname uts;
if (uname(&uts) == 0)
{
hb_system_info.name = strdup(uts.sysname);
hb_system_info.version = strdup(uts.release);
hb_system_info.build = strdup(uts.version);
}
#elif defined(SYS_MINGW)
NTSYSAPI NTSTATUS RtlGetVersion(PRTL_OSVERSIONINFOW);
OSVERSIONINFOW vi = {0};
vi.dwOSVersionInfoSize = sizeof(vi);
char buf[32];
if (RtlGetVersion(&vi) == 0)
{
snprintf(buf, sizeof(buf), "%lu.%lu", vi.dwMajorVersion, vi.dwMinorVersion);
hb_system_info.version = strdup(buf);
snprintf(buf, sizeof(buf), "%lu", vi.dwBuildNumber);
hb_system_info.build = strdup(buf);
}
hb_system_info.name = "Windows";
#else
hb_system_info.name = NULL;
hb_system_info.version = NULL;
hb_system_info.build = NULL;
#endif
}
const char * hb_get_system_name()
{
init_system_info();
return hb_system_info.name;
}
const char * hb_get_system_version()
{
init_system_info();
return hb_system_info.version;
}
const char * hb_get_system_build()
{
init_system_info();
return hb_system_info.build;
}
/************************************************************************
* Get information about the CPU (number of cores, name, platform name)
************************************************************************/
static void init_cpu_info();
static int init_cpu_count();
struct
{
enum hb_cpu_platform platform;
const char *name;
union
{
char buf[48];
uint32_t buf4[12];
};
int count;
} hb_cpu_info;
int hb_get_cpu_count()
{
init_cpu_info();
return hb_cpu_info.count;
}
int hb_get_cpu_platform()
{
init_cpu_info();
return hb_cpu_info.platform;
}
const char* hb_get_cpu_name()
{
init_cpu_info();
return hb_cpu_info.name;
}
const char* hb_get_cpu_platform_name()
{
init_cpu_info();
switch (hb_cpu_info.platform)
{
case HB_CPU_PLATFORM_INTEL_BNL:
return "Intel microarchitecture Bonnell";
case HB_CPU_PLATFORM_INTEL_SNB:
return "Intel microarchitecture Sandy Bridge";
case HB_CPU_PLATFORM_INTEL_IVB:
return "Intel microarchitecture Ivy Bridge";
case HB_CPU_PLATFORM_INTEL_SLM:
return "Intel microarchitecture Silvermont";
case HB_CPU_PLATFORM_INTEL_HSW:
return "Intel microarchitecture Haswell";
case HB_CPU_PLATFORM_INTEL_BDW:
return "Intel microarchitecture Broadwell";
case HB_CPU_PLATFORM_INTEL_SKL:
return "Intel microarchitecture Skylake";
case HB_CPU_PLATFORM_INTEL_CHT:
return "Intel microarchitecture Airmont";
case HB_CPU_PLATFORM_INTEL_KBL:
return "Intel microarchitecture Kaby Lake";
case HB_CPU_PLATFORM_INTEL_CML:
return "Intel microarchitecture Comet Lake";
case HB_CPU_PLATFORM_INTEL_ICL:
return "Intel microarchitecture Ice Lake";
case HB_CPU_PLATFORM_INTEL_TGL:
return "Intel microarchitecture Tiger Lake";
case HB_CPU_PLATFORM_INTEL_ADL:
return "Intel microarchitecture Alder Lake performance hybrid architecture";
default:
return NULL;
}
}
#if ARCH_X86_64
# define REG_b "rbx"
# define REG_S "rsi"
#elif ARCH_X86_32
# define REG_b "ebx"
# define REG_S "esi"
#endif // ARCH_X86_32
#if ARCH_X86_64 || ARCH_X86_32
#define cpuid(index, eax, ebx, ecx, edx) \
__asm__ volatile ( \
"mov %%"REG_b", %%"REG_S" \n\t" \
"cpuid \n\t" \
"xchg %%"REG_b", %%"REG_S \
: "=a" (*eax), "=S" (*ebx), "=c" (*ecx), "=d" (*edx) \
: "0" (index))
#endif // ARCH_X86_64 || ARCH_X86_32
static void init_cpu_info()
{
if (hb_cpu_info.count != 0)
return;
hb_cpu_info.name = "Unknown";
hb_cpu_info.count = init_cpu_count();
hb_cpu_info.platform = HB_CPU_PLATFORM_UNSPECIFIED;
#if ARCH_X86_64 || ARCH_X86_32
if (av_get_cpu_flags() & AV_CPU_FLAG_SSE)
{
int eax, ebx, ecx, edx, family, model;
cpuid(1, &eax, &ebx, &ecx, &edx);
family = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
model = ((eax >> 4) & 0xf) + ((eax >> 12) & 0xf0);
// Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 4/April 2022
// Table 2-1. CPUID Signature Values of DisplayFamily_DisplayModel
switch (family)
{
case 0x06:
{
switch (model)
{
case 0x1C:
case 0x26:
case 0x27:
case 0x35:
case 0x36:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_BNL;
break;
case 0x2A:
case 0x2D:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_SNB;
break;
case 0x3A:
case 0x3E:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_IVB;
break;
case 0x37:
case 0x4A:
case 0x4D:
case 0x5A:
case 0x5D:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_SLM;
break;
case 0x3C:
case 0x3F:
case 0x45:
case 0x46:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_HSW;
break;
case 0x3D:
case 0x4F:
case 0x56:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_BDW;
break;
case 0x4C:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_CHT;
break;
case 0x4E:
case 0x5E:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_SKL;
break;
case 0x8E:
case 0x9E:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_KBL;
break;
case 0xA5:
case 0xA6:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_CML;
break;
case 0x7D:
case 0x7E:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_ICL;
break;
case 0x8C:
case 0x8D:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_TGL;
break;
case 0x97:
case 0x9A:
hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_ADL;
break;
default:
break;
}
} break;
default:
break;
}
// Intel 64 and IA-32 Architectures Software Developer's Manual, Vol. 2A
// Figure 3-8: Determination of Support for the Processor Brand String
// Table 3-17: Information Returned by CPUID Instruction
cpuid(0x80000000, &eax, &ebx, &ecx, &edx);
if ((eax & 0x80000004) < 0x80000004)
{
cpuid(0x80000002,
&hb_cpu_info.buf4[ 0],
&hb_cpu_info.buf4[ 1],
&hb_cpu_info.buf4[ 2],
&hb_cpu_info.buf4[ 3]);
cpuid(0x80000003,
&hb_cpu_info.buf4[ 4],
&hb_cpu_info.buf4[ 5],
&hb_cpu_info.buf4[ 6],
&hb_cpu_info.buf4[ 7]);
cpuid(0x80000004,
&hb_cpu_info.buf4[ 8],
&hb_cpu_info.buf4[ 9],
&hb_cpu_info.buf4[10],
&hb_cpu_info.buf4[11]);
hb_cpu_info.name = hb_cpu_info.buf;
}
}
#elif defined(SYS_DARWIN)
size_t buflen = sizeof(hb_cpu_info.buf);
if (sysctlbyname("machdep.cpu.brand_string", &hb_cpu_info.buf, &buflen, NULL, 0) == 0)
{
hb_cpu_info.name = hb_cpu_info.buf;
}
#endif
// ensure string is null-terminated and trim trailing whitespace
int ii = sizeof(hb_cpu_info.buf) - 1;
do {
hb_cpu_info.buf[ii] = '\0';
ii -= 1;
}
while (ii > 0 && isspace(hb_cpu_info.buf[ii]));
while (isspace(*hb_cpu_info.name))
{
// skip leading whitespace to prettify
hb_cpu_info.name++;
}
}
/*
* Whenever possible, returns the number of CPUs on the current computer.
* Returns 1 otherwise.
*/
static int init_cpu_count()
{
int cpu_count = 1;
#if defined(SYS_CYGWIN) || defined(SYS_MINGW)
cpu_count = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
#elif defined(SYS_LINUX)
unsigned int bit;
cpu_set_t p_aff;
memset( &p_aff, 0, sizeof(p_aff) );
sched_getaffinity( 0, sizeof(p_aff), &p_aff );
for( cpu_count = 0, bit = 0; bit < sizeof(p_aff); bit++ )
cpu_count += (((uint8_t *)&p_aff)[bit / 8] >> (bit % 8)) & 1;
#elif defined(SYS_DARWIN) || defined(SYS_FREEBSD) || defined(SYS_NETBSD) || defined(SYS_OPENBSD)
size_t length = sizeof( cpu_count );
#if defined SYS_OPENBSD || defined(SYS_NETBSD)
#ifdef HW_NCPUONLINE
int mib[2] = { CTL_HW, HW_NCPUONLINE };
#else
int mib[2] = { CTL_HW, HW_NCPU };
#endif
if( sysctl(mib, 2, &cpu_count, &length, NULL, 0) )
#else
if( sysctlbyname("hw.ncpu", &cpu_count, &length, NULL, 0) )
#endif
{
cpu_count = 1;
}
#elif defined( SYS_SunOS )
{
processorid_t cpumax;
int i,j=0;
cpumax = sysconf(_SC_CPUID_MAX);
for(i = 0; i <= cpumax; i++ )
{
if(p_online(i, P_STATUS) != -1)
{
j++;
}
}
cpu_count=j;
}
#endif
cpu_count = MAX( 1, cpu_count );
cpu_count = MIN( cpu_count, 384 );
return cpu_count;
}
int hb_platform_init()
{
int result = 0;
#if defined(SYS_MINGW) && defined(PTW32_VERSION)
result = !pthread_win32_process_attach_np();
if (result)
{
hb_error("pthread_win32_process_attach_np() failed!");
return -1;
}
#endif
#if defined(_WIN32) || defined(__MINGW32__)
/*
* win32 _IOLBF (line-buffering) is the same as _IOFBF (full-buffering).
* force it to unbuffered otherwise informative output is not easily parsed.
*/
result = setvbuf(stdout, NULL, _IONBF, 0);
if (result)
{
hb_error("setvbuf(stdout, NULL, _IONBF, 0) failed!");
return -1;
}
result = setvbuf(stderr, NULL, _IONBF, 0);
if (result)
{
hb_error("setvbuf(stderr, NULL, _IONBF, 0) failed!");
return -1;
}
#endif
init_cpu_info();
return result;
}
/************************************************************************
* Get app data config directory
***********************************************************************/
void hb_get_user_config_directory( char path[512] )
{
/* Create the base */
#if defined( SYS_CYGWIN ) || defined( SYS_MINGW )
#ifndef CSIDL_FLAG_DONT_UNEXPAND
/*
* XXX: some old MinGW toolchains don't have SHGetKnownFolderPath.
*
* SHGetFolderPath is deprecated, but this should be no problem in practice.
*
* Note: explicitly call the Unicode/WCHAR function SHGetFolderPathW.
*/
WCHAR wide_path[MAX_PATH];
if (SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wide_path) == S_OK &&
WideCharToMultiByte(CP_UTF8, 0, wide_path, -1, path, 512, NULL, NULL) != 0)
{
path[511] = 0;
return;
}
#else
WCHAR *wide_path;
if (SHGetKnownFolderPath(&FOLDERID_RoamingAppData, 0, NULL, &wide_path) == S_OK &&
WideCharToMultiByte(CP_UTF8, 0, wide_path, -1, path, 512, NULL, NULL) != 0)
{
CoTaskMemFree(wide_path);
path[511] = 0;
return;
}
else if (wide_path != NULL)
{
CoTaskMemFree(wide_path);
}
#endif // !defined CSIDL_FLAG_DONT_UNEXPAND
#elif defined( SYS_LINUX )
char *p;
if ((p = getenv("XDG_CONFIG_HOME")) != NULL)
{
strncpy(path, p, 511);
path[511] = 0;
return;
}
else if ((p = getenv("HOME")) != NULL)
{
strncpy(path, p, 511);
path[511] = 0;
int len = strlen(path);
strncpy(path + len, "/.config", 511 - len - 1);
path[511] = 0;
return;
}
#elif defined( __APPLE__ )
if (macOS_get_user_config_directory(path) == 0)
{
return;
}
#endif
hb_error("Failed to lookup user config directory!");
path[0] = 0;
}
/************************************************************************
* Get a user config filename for HB
***********************************************************************/
void hb_get_user_config_filename( char name[1024], char *fmt, ... )
{
va_list args;
hb_get_user_config_directory( name );
#if defined( SYS_CYGWIN ) || defined( SYS_MINGW )
strcat( name, "\\" );
#else
strcat( name, "/" );
#endif
va_start( args, fmt );
vsnprintf( &name[strlen(name)], 1024 - strlen(name), fmt, args );
va_end( args );
}
/************************************************************************
* Creates a uniquely-named temporary directory for this process. On
* POSIX systems, mkdtemp(3) is used to ensure the directory name is
* unique even if multiple HandBrake instances have the same PID.
***********************************************************************/
static pthread_once_t tmp_control = PTHREAD_ONCE_INIT;
static char *tmp_dirname = NULL;
static const char *tmp_override = NULL;
static void
hb_init_temporary_directory (void)
{
char *path = NULL, *base = NULL, *p;
#if defined( SYS_CYGWIN ) || defined( SYS_MINGW )
if (tmp_override != NULL && tmp_override[0] != '\0')
{
base = strdup(tmp_override);
}
else
{
DWORD i_size = 0;
WCHAR wide_base[MAX_PATH + 1];
i_size = GetTempPathW(MAX_PATH, wide_base);
if (i_size > 0 && i_size <= MAX_PATH)
{
int base_size = WideCharToMultiByte(CP_UTF8, 0, wide_base, -1,
NULL, 0, 0, NULL);
if (base_size)
{
base = malloc(base_size);
WideCharToMultiByte(CP_UTF8, 0, wide_base, -1,
base, base_size, 0, NULL);
}
}
if (base == NULL)
{
base = malloc(MAX_PATH + 1);
if (getcwd(base, MAX_PATH) == NULL)
strcpy(base, "c:"); /* Bad fallback but ... */
}
}
/* c:/path/ works like a charm under cygwin(win32?) so use it */
while ((p = strchr(base, '\\')))
*p = '/';
/* I prefer to remove eventual last '/' (for cygwin) */
if (base[strlen(base)-1] == '/')
base[strlen(base)-1] = '\0';
path = hb_strdup_printf("%s/HandBrake-%d", base, (int)getpid());
hb_mkdir(path);
free(base);
#else
if (tmp_override != NULL && tmp_override[0] != '\0')
base = strdup(tmp_override);
else if ((p = getenv("TMPDIR")) != NULL ||
(p = getenv("TEMP")) != NULL)
base = strdup(p);
else
base = strdup("/tmp");
if (base[strlen(base)-1] == '/')
base[strlen(base)-1] = '\0';
/* Create a new randomly-named directory from the template */
path = hb_strdup_printf("%s/handbrake-XXXXXX", base);
if (!mkdtemp(path))
{
/* We still use the path even if directory creation fails */
hb_error("Failed to create a temporary directory at %s\n", path);
}
free(base);
#endif
tmp_dirname = path;
}
/************************************************************************
* Sets the location of the temporary directory. This function must be
* called before the first use of hb_get_temporary_directory().
***********************************************************************/
void
hb_set_temporary_directory (const char *tmp_dir)
{
tmp_override = tmp_dir;
pthread_once(&tmp_control, hb_init_temporary_directory);
tmp_override = NULL;
}
const char *
hb_get_temporary_directory (void)
{
/* Ensure the directory name is only created once */
pthread_once(&tmp_control, hb_init_temporary_directory);
return tmp_dirname;
}
/************************************************************************
* Get a temporary filename for HB
***********************************************************************/
char * hb_get_temporary_filename( char *fmt, ... )
{
va_list args;
char * name, * path;
va_start( args, fmt );
name = hb_strdup_vaprintf(fmt, args);
va_end( args );
path = hb_strdup_printf("%s/%s", hb_get_temporary_directory(), name);
free(name);
return path;
}
/************************************************************************
* hb_stat
************************************************************************
* Wrapper to the real stat, needed to handle utf8 filenames on
* windows.
***********************************************************************/
int hb_stat(const char *path, hb_stat_t *sb)
{
#ifdef SYS_MINGW
wchar_t path_utf16[MAX_PATH];
if (!MultiByteToWideChar(CP_UTF8, 0, path, -1, path_utf16, MAX_PATH))
return -1;
return _wstat64( path_utf16, sb );
#else
return stat(path, sb);
#endif
}
/************************************************************************
* hb_fopen
************************************************************************
* Wrapper to the real fopen, needed to handle utf8 filenames on
* windows.
***********************************************************************/
FILE * hb_fopen(const char *path, const char *mode)
{
#ifdef SYS_MINGW
FILE *f;
wchar_t path_utf16[MAX_PATH];
wchar_t mode_utf16[16];
if (!MultiByteToWideChar(CP_UTF8, 0, path, -1, path_utf16, MAX_PATH))
return NULL;
if (!MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_utf16, 16))
return NULL;
errno_t ret = _wfopen_s(&f, path_utf16, mode_utf16);
if (ret)
return NULL;
return f;
#else
return fopen(path, mode);
#endif
}
HB_DIR* hb_opendir(const char *path)
{
#ifdef SYS_MINGW
HB_DIR *dir;
wchar_t path_utf16[MAX_PATH];
if (!MultiByteToWideChar(CP_UTF8, 0, path, -1, path_utf16, MAX_PATH))
return NULL;
dir = malloc(sizeof(HB_DIR));
if (dir == NULL)
return NULL;
dir->wdir = _wopendir(path_utf16);
if (dir->wdir == NULL)
{
free(dir);
return NULL;
}
return dir;
#else
return opendir(path);
#endif
}
int hb_closedir(HB_DIR *dir)
{
#ifdef SYS_MINGW
int ret;
ret = _wclosedir(dir->wdir);
free(dir);
return ret;
#else
return closedir(dir);
#endif
}
struct dirent * hb_readdir(HB_DIR *dir)
{
#ifdef SYS_MINGW
struct _wdirent *entry;
entry = _wreaddir(dir->wdir);
if (entry == NULL)
return NULL;
int len = WideCharToMultiByte(CP_UTF8, 0, entry->d_name, -1,
dir->entry.d_name, sizeof(dir->entry.d_name),
NULL, NULL );
dir->entry.d_ino = entry->d_ino;
dir->entry.d_reclen = entry->d_reclen;
dir->entry.d_namlen = len - 1;
return &dir->entry;
#else
return readdir(dir);
#endif
}
void hb_rewinddir(HB_DIR *dir)
{
#ifdef SYS_MINGW
_wrewinddir(dir->wdir);
#else
return rewinddir(dir);
#endif
}
char * hb_strr_dir_sep(const char *path)
{
#ifdef SYS_MINGW
char *sep = strrchr(path, '/');
if (sep == NULL)
sep = strrchr(path, '\\');
return sep;
#else
return strrchr(path, '/');
#endif
}
/************************************************************************
* hb_mkdir
************************************************************************
* Wrapper to the real mkdir, needed only because it doesn't take a
* second argument on Win32. Grrr.
***********************************************************************/
int hb_mkdir(const char * path)
{
#ifdef SYS_MINGW
wchar_t path_utf16[MAX_PATH];
if (!MultiByteToWideChar(CP_UTF8, 0, path, -1, path_utf16, MAX_PATH))
return -1;
return _wmkdir(path_utf16);
#else
return mkdir(path, 0755);
#endif
}
/************************************************************************
* Portable thread implementation
***********************************************************************/
struct hb_thread_s
{
char * name;
int priority;
thread_func_t * function;
void * arg;
hb_lock_t * lock;
int exited;
pthread_t thread;
};
/* Get a unique identifier to thread and represent as 64-bit unsigned.
* If unsupported, the value 0 is be returned.
* Caller should use result only for display/log purposes.
*/
static uint64_t hb_thread_to_integer( const hb_thread_t* t )
{
#if defined( SYS_CYGWIN )
return (uint64_t)t->thread;
#elif defined( _WIN32 ) || defined( __MINGW32__ )
# if defined(PTW32_VERSION)
return (uint64_t)(ptrdiff_t)t->thread.p;
# else
return (uint64_t)t->thread;
# endif
#elif defined( SYS_DARWIN )
return (unsigned long)t->thread;
#else
return (uint64_t)t->thread;
#endif
}
/************************************************************************
* hb_thread_func()
************************************************************************
* We use it as the root routine for any thread, for two reasons:
* + To set the thread priority on macOS (pthread_setschedparam() could
* be called from hb_thread_init(), but it's nicer to do it as we
* are sure it is done before the real routine starts)
* + Get informed when the thread exits, so we know whether
* hb_thread_close() will block or not.
***********************************************************************/
static void attribute_align_thread hb_thread_func( void * _t )
{
hb_thread_t * t = (hb_thread_t *) _t;
#if (defined( SYS_DARWIN ) && !defined(__aarch64__)) || defined( SYS_FREEBSD ) || \
defined( SYS_NETBSD ) || defined( SYS_OPENBSD )
/* Set the thread priority
Do not change priority on Darwin arm systems */
struct sched_param param;
memset( &param, 0, sizeof( struct sched_param ) );
param.sched_priority = t->priority;
pthread_setschedparam( pthread_self(), SCHED_OTHER, &param );
#endif
#if defined( SYS_DARWIN )
pthread_setname_np( t->name );
#endif
/* Start the actual routine */
t->function( t->arg );
/* Inform that the thread can be joined now */
hb_deep_log( 2, "thread %"PRIx64" exited (\"%s\")", hb_thread_to_integer( t ), t->name );
hb_lock( t->lock );
t->exited = 1;
hb_unlock( t->lock );
}
/************************************************************************
* hb_thread_init()
************************************************************************
* name: user-friendly name
* function: the thread routine
* arg: argument of the routine
* priority: HB_LOW_PRIORITY or HB_NORMAL_PRIORITY
***********************************************************************/
hb_thread_t * hb_thread_init( const char * name, void (* function)(void *),
void * arg, int priority )
{
hb_thread_t * t = calloc( sizeof( hb_thread_t ), 1 );
t->name = strdup( name );
t->function = function;
t->arg = arg;
t->priority = priority;
t->lock = hb_lock_init();
/* Create and start the thread */
pthread_create( &t->thread, NULL,
(void * (*)( void * )) hb_thread_func, t );
hb_deep_log( 2, "thread %"PRIx64" started (\"%s\")", hb_thread_to_integer( t ), t->name );
return t;
}
/************************************************************************
* hb_thread_close()
************************************************************************
* Joins the thread and frees memory.
***********************************************************************/
void hb_thread_close( hb_thread_t ** _t )
{
hb_thread_t * t = *_t;
/* Join the thread */
pthread_join( t->thread, NULL );
hb_deep_log( 2, "thread %"PRIx64" joined (\"%s\")", hb_thread_to_integer( t ), t->name );
hb_lock_close( &t->lock );
free( t->name );
free( t );
*_t = NULL;
}
/************************************************************************
* hb_thread_has_exited()
************************************************************************
* Returns 1 if the thread can be joined right away, 0 otherwise.
***********************************************************************/
int hb_thread_has_exited( hb_thread_t * t )
{
int exited;
hb_lock( t->lock );
exited = t->exited;
hb_unlock( t->lock );
return exited;
}
/************************************************************************
* Portable mutex implementation
***********************************************************************/
struct hb_lock_s
{
pthread_mutex_t mutex;
};
/************************************************************************
* hb_lock_init()
* hb_lock_close()
* hb_lock()
* hb_unlock()
************************************************************************
* Basic wrappers to OS-specific semaphore or mutex functions.
***********************************************************************/
hb_lock_t * hb_lock_init()
{
hb_lock_t * l = calloc( sizeof( hb_lock_t ), 1 );
pthread_mutexattr_t mta;
pthread_mutexattr_init(&mta);
#if defined( SYS_CYGWIN ) || defined( SYS_FREEBSD ) || defined( SYS_NETBSD ) || \
defined( SYS_OPENBSD )
pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_NORMAL);
#endif
pthread_mutex_init( &l->mutex, &mta );
return l;
}
void hb_lock_close( hb_lock_t ** _l )
{
hb_lock_t * l = *_l;
if (l == NULL)
{
return;
}
pthread_mutex_destroy( &l->mutex );
free( l );
*_l = NULL;
}
void hb_lock( hb_lock_t * l )
{
pthread_mutex_lock( &l->mutex );
}
void hb_unlock( hb_lock_t * l )
{
pthread_mutex_unlock( &l->mutex );
}
/************************************************************************
* Portable condition variable implementation
***********************************************************************/
struct hb_cond_s
{
pthread_cond_t cond;
};
/************************************************************************
* hb_cond_init()
* hb_cond_close()
* hb_cond_wait()
* hb_cond_signal()
************************************************************************
* Win9x is not supported by this implementation (SignalObjectAndWait()
* only available on Windows 2000/XP).
***********************************************************************/
hb_cond_t * hb_cond_init()
{
hb_cond_t * c = calloc( sizeof( hb_cond_t ), 1 );
if( c == NULL )
return NULL;
pthread_cond_init( &c->cond, NULL );
return c;
}
void hb_cond_close( hb_cond_t ** _c )
{
hb_cond_t * c = *_c;
if (c == NULL)
{
return;
}
pthread_cond_destroy( &c->cond );
free( c );
*_c = NULL;
}
void hb_cond_wait( hb_cond_t * c, hb_lock_t * lock )
{
pthread_cond_wait( &c->cond, &lock->mutex );
}
void hb_clock_gettime( struct timespec *tp )
{
struct timeval tv;
gettimeofday( &tv, NULL );
tp->tv_sec = tv.tv_sec;
tp->tv_nsec = tv.tv_usec * 1000;
}
void hb_yield(void)
{
sched_yield();
}
void hb_cond_timedwait( hb_cond_t * c, hb_lock_t * lock, int msec )
{
struct timespec ts;
hb_clock_gettime(&ts);
ts.tv_nsec += (msec % 1000) * 1000000;
ts.tv_sec += msec / 1000 + (ts.tv_nsec / 1000000000);
ts.tv_nsec %= 1000000000;
pthread_cond_timedwait( &c->cond, &lock->mutex, &ts );
}
void hb_cond_signal( hb_cond_t * c )
{
pthread_cond_signal( &c->cond );
}
void hb_cond_broadcast( hb_cond_t * c )
{
pthread_cond_broadcast( &c->cond );
}
/************************************************************************
* Network
***********************************************************************/
struct hb_net_s
{
int socket;
};
hb_net_t * hb_net_open( char * address, int port )
{
hb_net_t * n = calloc( sizeof( hb_net_t ), 1 );
struct sockaddr_in sock;
struct hostent * host;
#ifdef SYS_MINGW
WSADATA wsaData;
int iResult, winsock_init = 0;
// Initialize Winsock
if (!winsock_init)
{
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
hb_log("WSAStartup failed: %d", iResult);
free(n);
return NULL;
}
winsock_init = 1;
}
#endif
/* TODO: find out why this doesn't work on Win32 */
if( !( host = gethostbyname( address ) ) )
{
hb_log( "gethostbyname failed (%s)", address );
free( n );
return NULL;
}
memset( &sock, 0, sizeof( struct sockaddr_in ) );
sock.sin_family = host->h_addrtype;
sock.sin_port = htons( port );
memcpy( &sock.sin_addr, host->h_addr, host->h_length );
if( ( n->socket = socket( host->h_addrtype, SOCK_STREAM, 0 ) ) < 0 )
{
hb_log( "socket failed" );
free( n );
return NULL;
}
if( connect( n->socket, (struct sockaddr *) &sock,
sizeof( struct sockaddr_in ) ) < 0 )
{
hb_log( "connect failed" );
free( n );
return NULL;
}
return n;
}
int hb_net_send( hb_net_t * n, char * buffer )
{
return send( n->socket, buffer, strlen( buffer ), 0 );
}
int hb_net_recv( hb_net_t * n, char * buffer, int size )
{
return recv( n->socket, buffer, size - 1, 0 );
}
void hb_net_close( hb_net_t ** _n )
{
hb_net_t * n = (hb_net_t *) *_n;
close( n->socket );
free( n );
*_n = NULL;
}
/************************************************************************
* OS Backup Include / Exclude
***********************************************************************/
void hb_system_backup_set_excluded(const char *path, int exclude)
{
#ifdef __APPLE__
if (path != NULL)
{
CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
(const UInt8 *)path,
strlen(path),
false);
if (url != NULL)
{
CSBackupSetItemExcluded(url, exclude, false);
CFRelease(url);
}
}
#endif
}
/************************************************************************
* OS Sleep Allow / Prevent
***********************************************************************/
void* hb_system_sleep_opaque_init()
{
void *opaque = NULL;
#ifdef __APPLE__
opaque = calloc(sizeof(IOPMAssertionID), 1);
if (opaque == NULL)
{
hb_error("hb_system_sleep: failed to allocate opaque");
return NULL;
}
IOPMAssertionID *assertionID = (IOPMAssertionID*)opaque;
*assertionID = -1;
#endif
return opaque;
}
void hb_system_sleep_opaque_close(void **opaque)
{
if (*opaque != NULL)
{
hb_system_sleep_private_enable(*opaque);
}
#ifdef __APPLE__
if (*opaque != NULL)
{
IOPMAssertionID *assertionID = (IOPMAssertionID*)*opaque;
free(assertionID);
}
#endif
*opaque = NULL;
}
void hb_system_sleep_private_enable(void *opaque)
{
#ifdef __APPLE__
if (opaque == NULL)
{
hb_error("hb_system_sleep: opaque is NULL");
return;
}
IOPMAssertionID *assertionID = (IOPMAssertionID*)opaque;
if (*assertionID == -1)
{
// nothing to do
return;
}
IOReturn success = IOPMAssertionRelease(*assertionID);
if (success == kIOReturnSuccess)
{
hb_deep_log(3,
"hb_system_sleep: assertion %d released, sleep allowed",
*assertionID);
*assertionID = -1;
}
else
{
hb_log("hb_system_sleep: failed to allow system sleep");
}
#endif
}
void hb_system_sleep_private_disable(void *opaque)
{
#ifdef __APPLE__
if (opaque == NULL)
{
hb_error("hb_system_sleep: opaque is NULL");
return;
}
IOPMAssertionID *assertionID = (IOPMAssertionID*)opaque;
if (*assertionID != -1)
{
// nothing to do
return;
}
// 128 chars limit for IOPMAssertionCreateWithName
CFStringRef reasonForActivity =
CFSTR("HandBrake is currently scanning and/or encoding");
IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertPreventUserIdleSystemSleep,
kIOPMAssertionLevelOn,
reasonForActivity,
assertionID);
if (success == kIOReturnSuccess)
{
hb_deep_log(3,
"hb_system_sleep: assertion %d created, sleep prevented",
*assertionID);
}
else
{
hb_log("hb_system_sleep: failed to prevent system sleep");
}
#endif
}
void * hb_dlopen(const char *name)
{
#ifdef SYS_MINGW
HMODULE h = LoadLibraryExA(name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
#else
void *h = dlopen(name, RTLD_LAZY | RTLD_LOCAL);
#endif
return h;
}
void * hb_dlsym(void *h, const char *name)
{
#ifdef SYS_MINGW
FARPROC p = GetProcAddress(h, name);
#else
void *p = dlsym(h, name);
#endif
return p;
}
int hb_dlclose(void *h)
{
#ifdef SYS_MINGW
return FreeLibrary(h);
#else
return dlclose(h);
#endif
}
size_t hb_getline(char ** lineptr, size_t * n, FILE * fp)
{
#ifdef SYS_MINGW
char * bufptr = NULL;
char * p = bufptr;
size_t size;
int c;
if (lineptr == NULL)
{
return -1;
}
if (fp == NULL)
{
return -1;
}
if (n == NULL)
{
return -1;
}
bufptr = *lineptr;
size = *n;
c = fgetc(fp);
if (c == EOF)
{
return -1;
}
if (bufptr == NULL)
{
bufptr = malloc(128);
if (bufptr == NULL)
{
return -1;
}
size = 128;
}
p = bufptr;
while (c != EOF)
{
if ((p - bufptr) >= (size - 1))
{
char * tmp;
size = size + 128;
tmp = realloc(bufptr, size);
if (tmp == NULL)
{
free(bufptr);
return -1;
}
p = tmp + (p - bufptr);
bufptr = tmp;
}
*p++ = c;
if (c == '\n')
{
break;
}
c = fgetc(fp);
}
*p++ = '\0';
*lineptr = bufptr;
*n = size;
return p - bufptr - 1;
#else
return getline(lineptr, n, fp);
#endif
}
char * hb_strndup(const char * src, size_t len)
{
#ifdef SYS_MINGW
char * result, * end;
if (src == NULL)
{
return NULL;
}
end = memchr(src, 0, len);
if (end != NULL)
{
len = end - src;
}
result = malloc(len + 1);
if (result == NULL)
{
return NULL;
}
memcpy(result, src, len);
result[len] = 0;
return result;
#else
return strndup(src, len);
#endif
}
#if HB_PROJECT_FEATURE_QSV
#if defined(SYS_LINUX) || defined(SYS_FREEBSD)
#define MAX_NODES 16
#define DRI_RENDER_NODE_START 128
#define DRI_RENDER_NODE_LAST (DRI_RENDER_NODE_START + MAX_NODES - 1)
#define DRI_CARD_NODE_START 0
#define DRI_CARD_NODE_LAST (DRI_CARD_NODE_START + MAX_NODES - 1)
const char* DRI_PATH = "/dev/dri/";
const char* DRI_NODE_RENDER = "renderD";
const char* DRI_NODE_CARD = "card";
static int try_adapter(const char * name, const char * dir,
const char * prefix, int node_start, int node_last)
{
int node;
int len = strlen(name);
char * driverName = malloc(len + 1);
drm_version_t version = {};
version.name_len = len + 1;
version.name = driverName;
for (node = node_start; node <= node_last; node++)
{
char * adapter = hb_strdup_printf("%s%s%d", dir, prefix, node);
int fd = open(adapter, O_RDWR);
free(adapter);
if (fd < 0)
{
continue;
}
if (!ioctl(fd, DRM_IOCTL_VERSION, &version) &&
version.name_len == len && !strncmp(driverName, name, len))
{
free(driverName);
return fd;
}
close(fd);
}
free(driverName);
return -1;
}
static int open_adapter(const char * name, const uint dri_render_node)
{
int fd;
// If dri_render_node is unknown enumerate across the predifined range of renders
if (dri_render_node == 0)
{
fd = try_adapter(name, DRI_PATH, DRI_NODE_RENDER,
DRI_RENDER_NODE_START, DRI_RENDER_NODE_LAST);
}
else
{
fd = try_adapter(name, DRI_PATH, DRI_NODE_RENDER,
dri_render_node, dri_render_node);
}
if (fd < 0)
{
fd = try_adapter(name, DRI_PATH, DRI_NODE_CARD,
DRI_CARD_NODE_START, DRI_CARD_NODE_LAST);
}
return fd;
}
static int try_va_interface(hb_display_t * hbDisplay,
const char * interface_name)
{
if (interface_name != NULL)
{
vaSetDriverName(hbDisplay->vaDisplay, (char *) interface_name);
}
hbDisplay->vaDisplay = vaGetDisplayDRM(hbDisplay->vaFd);
if (hbDisplay->vaDisplay == NULL)
{
return -1;
}
int major = 0, minor = 0;
VAStatus vaRes = vaInitialize(hbDisplay->vaDisplay, &major, &minor);
if (vaRes != VA_STATUS_SUCCESS)
{
vaTerminate(hbDisplay->vaDisplay);
return -1;
}
hbDisplay->handle = hbDisplay->vaDisplay;
hbDisplay->mfxType = MFX_HANDLE_VA_DISPLAY;
return 0;
}
hb_display_t * hb_display_init(const char * driver_name,
const uint32_t dri_render_node,
const char * const * interface_names)
{
hb_display_t * hbDisplay = calloc(sizeof(hb_display_t), 1);
char * env;
int ii;
hbDisplay->vaDisplay = NULL;
hbDisplay->vaFd = open_adapter(driver_name, dri_render_node);
if (hbDisplay->vaFd < 0)
{
hb_deep_log( 3, "hb_va_display_init: no display found" );
free(hbDisplay);
return NULL;
}
if ((env = getenv("LIBVA_DRIVER_NAME")) != NULL)
{
// Use only environment if it's set
hb_log("hb_display_init: using VA driver '%s'", env);
if (try_va_interface(hbDisplay, NULL) == 0)
{
return hbDisplay;
}
}
else
{
// Try default
hb_log("hb_display_init: attempting VA default driver");
if (try_va_interface(hbDisplay, NULL) == 0)
{
return hbDisplay;
}
// Try list of VA driver names
for (ii = 0; interface_names[ii] != NULL; ii++)
{
hb_log("hb_display_init: attempting VA driver '%s'",
interface_names[ii]);
if (try_va_interface(hbDisplay, interface_names[ii]) == 0)
{
return hbDisplay;
}
}
}
// No working VA driver found
close(hbDisplay->vaFd);
free(hbDisplay);
return NULL;
}
void hb_display_close(hb_display_t ** _d)
{
hb_display_t * hbDisplay = *_d;
if (hbDisplay == NULL)
{
return;
}
if (hbDisplay->vaDisplay)
{
vaTerminate(hbDisplay->vaDisplay);
}
if (hbDisplay->vaFd >= 0)
{
close(hbDisplay->vaFd);
}
free(hbDisplay);
*_d = NULL;
}
#else // !SYS_LINUX && !SYS_FREEBSD
hb_display_t * hb_display_init(const char * driver_name,
const uint32_t dri_render_node,
const char * const * interface_names)
{
return NULL;
}
void hb_display_close(hb_display_t ** _d)
{
(void)_d;
}
#endif // SYS_LINUX || SYS_FREEBSD
#else // !HB_PROJECT_FEATURE_QSV
hb_display_t * hb_display_init(const char * driver_name,
const uint32_t dri_render_node,
const char * const * interface_names)
{
return NULL;
}
void hb_display_close(hb_display_t ** _d)
{
(void)_d;
}
#endif // HB_PROJECT_FEATURE_QSV