| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2026 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / common tools sub-project | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include <gpac/tools.h> | |
| #include <gpac/network.h> | |
| #include <gpac/config_file.h> | |
| #if defined(_WIN32_WCE) | |
| #include <winbase.h> | |
| #include <winsock.h> | |
| #include <tlhelp32.h> | |
| //#include <direct.h> | |
| #if !defined(__GNUC__) | |
| #pragma comment(lib, "toolhelp") | |
| #endif | |
| #elif defined(WIN32) | |
| #include <time.h> | |
| #include <sys/timeb.h> | |
| #include <io.h> | |
| #include <windows.h> | |
| #include <tlhelp32.h> | |
| #include <direct.h> | |
| #if !defined(__GNUC__) | |
| #pragma comment(lib, "winmm") | |
| #endif | |
| #else | |
| #include <time.h> | |
| #include <sys/stat.h> | |
| #include <sys/time.h> | |
| #include <dirent.h> | |
| #include <unistd.h> | |
| #include <sys/times.h> | |
| #include <sys/resource.h> | |
| #ifndef __BEOS__ | |
| #include <errno.h> | |
| #endif | |
| #define SLEEP_ABS_SELECT 1 | |
| static u32 sys_start_time = 0; | |
| static u64 sys_start_time_hr = 0; | |
| #endif | |
| #ifndef _WIN32_WCE | |
| #include <locale.h> | |
| #endif | |
| #include <gpac/revision.h> | |
| #define GPAC_FULL_VERSION GPAC_VERSION "-rev" GPAC_GIT_REVISION | |
| #define GPAC_COPYRIGHT "(c) 2000-2024 Telecom Paris distributed under LGPL v2.1+ - https://gpac.io" | |
| GF_EXPORT | |
| const char *gf_gpac_version() | |
| { | |
| return GPAC_FULL_VERSION; | |
| } | |
| #ifdef GPAC_MP4BOX_MINI | |
| #define MINI_BUILD_DISCLAIMER "\n\tMINI build (encoders, decoders, audio and video output disabled)" | |
| #endif | |
| GF_EXPORT | |
| const char *gf_gpac_copyright() | |
| { | |
| return GPAC_COPYRIGHT | |
| #ifdef GPAC_MP4BOX_MINI | |
| MINI_BUILD_DISCLAIMER | |
| #endif | |
| ; | |
| } | |
| GF_EXPORT | |
| const char *gf_gpac_copyright_cite() | |
| { | |
| return GPAC_COPYRIGHT | |
| #ifdef GPAC_MP4BOX_MINI | |
| MINI_BUILD_DISCLAIMER | |
| #endif | |
| "\n\n" \ | |
| "Please cite our work in your research:\n" | |
| "\tGPAC Filters: https://doi.org/10.1145/3339825.3394929\n" | |
| "\tGPAC: https://doi.org/10.1145/1291233.1291452\n" | |
| ; | |
| } | |
| GF_EXPORT | |
| u32 gf_gpac_abi_major() | |
| { | |
| return GPAC_VERSION_MAJOR; | |
| } | |
| GF_EXPORT | |
| u32 gf_gpac_abi_minor() | |
| { | |
| return GPAC_VERSION_MINOR; | |
| } | |
| GF_EXPORT | |
| u32 gf_gpac_abi_micro() | |
| { | |
| return GPAC_VERSION_MICRO; | |
| } | |
| #if !defined(WIN32) && !defined(GPAC_CONFIG_EMSCRIPTEN) | |
| GF_EXPORT | |
| u32 gf_sys_clock() | |
| { | |
| struct timeval now; | |
| gettimeofday(&now, NULL); | |
| return (u32) ( ( (u64)(now.tv_sec)*1000 + (now.tv_usec) / 1000) - sys_start_time ); | |
| } | |
| GF_EXPORT | |
| u64 gf_sys_clock_high_res() | |
| { | |
| struct timeval now; | |
| gettimeofday(&now, NULL); | |
| return (u64)(now.tv_sec)*1000000 + (now.tv_usec) - sys_start_time_hr; | |
| } | |
| #endif | |
| #ifdef GPAC_CONFIG_EMSCRIPTEN | |
| #include <emscripten/emscripten.h> | |
| GF_EXPORT | |
| u32 gf_sys_clock() | |
| { | |
| return (u32)(emscripten_get_now() - sys_start_time); | |
| } | |
| GF_EXPORT | |
| u64 gf_sys_clock_high_res() | |
| { | |
| return (long long)(emscripten_get_now() * 1000.0) - sys_start_time_hr; | |
| } | |
| #endif | |
| GF_Err gf_sys_enable_rmtws(Bool start); | |
| GF_Err gf_sys_enable_userws(Bool start); | |
| static Bool gpac_disable_rti = GF_FALSE; | |
| GF_EXPORT | |
| void gf_sleep(u32 ms) | |
| { | |
| #ifdef WIN32 | |
| Sleep(ms); | |
| #else | |
| s32 sel_err; | |
| struct timeval tv; | |
| #ifndef SLEEP_ABS_SELECT | |
| u32 prev, now, elapsed; | |
| #endif | |
| #ifdef SLEEP_ABS_SELECT | |
| tv.tv_sec = ms/1000; | |
| tv.tv_usec = (ms%1000)*1000; | |
| #else | |
| prev = gf_sys_clock(); | |
| #endif | |
| do { | |
| errno = 0; | |
| #ifndef SLEEP_ABS_SELECT | |
| now = gf_sys_clock(); | |
| elapsed = (now - prev); | |
| if ( elapsed >= ms ) { | |
| break; | |
| } | |
| prev = now; | |
| ms -= elapsed; | |
| tv.tv_sec = ms/1000; | |
| tv.tv_usec = (ms%1000)*1000; | |
| #endif | |
| sel_err = select(0, NULL, NULL, NULL, &tv); | |
| } while ( sel_err && (errno == EINTR) ); | |
| #endif | |
| } | |
| #ifndef gettimeofday | |
| #ifdef _WIN32_WCE | |
| #include <time.h> | |
| //#include <wce_time.h> | |
| /* | |
| * Author of first version (timeval.h): by Wu Yongwei | |
| * Author of Windows CE version: Mateusz Loskot (mateusz@loskot.net) | |
| * | |
| * All code here is considered in the public domain though we do wish our names | |
| * could be retained if anyone uses them. | |
| */ | |
| /* | |
| * Constants used internally by time functions. | |
| */ | |
| #ifndef _TM_DEFINED | |
| struct tm | |
| { | |
| int tm_sec; /* seconds after the minute - [0,59] */ | |
| int tm_min; /* minutes after the hour - [0,59] */ | |
| int tm_hour; /* hours since midnight - [0,23] */ | |
| int tm_mday; /* day of the month - [1,31] */ | |
| int tm_mon; /* months since January - [0,11] */ | |
| int tm_year; /* years since 1900 */ | |
| int tm_wday; /* days since Sunday - [0,6] */ | |
| int tm_yday; /* days since January 1 - [0,365] */ | |
| int tm_isdst; /* daylight savings time flag */ | |
| }; | |
| #define _TM_DEFINED | |
| #endif /* _TM_DEFINED */ | |
| #ifndef _TIMEZONE_DEFINED | |
| struct timezone | |
| { | |
| int tz_minuteswest; /* minutes W of Greenwich */ | |
| int tz_dsttime; /* type of dst correction */ | |
| }; | |
| #define _TIMEZONE_DEFINED | |
| #endif /* _TIMEZONE_DEFINED */ | |
| #if defined(_MSC_VER) || defined(__BORLANDC__) | |
| #define EPOCHFILETIME (116444736000000000i64) | |
| #else | |
| #define EPOCHFILETIME (116444736000000000LL) | |
| #endif | |
| int gettimeofday(struct timeval *tp, struct timezone *tzp) | |
| { | |
| SYSTEMTIME st; | |
| FILETIME ft; | |
| LARGE_INTEGER li; | |
| TIME_ZONE_INFORMATION tzi; | |
| __int64 t; | |
| if (NULL != tp) | |
| { | |
| GetSystemTime(&st); | |
| SystemTimeToFileTime(&st, &ft); | |
| li.LowPart = ft.dwLowDateTime; | |
| li.HighPart = ft.dwHighDateTime; | |
| t = li.QuadPart; /* In 100-nanosecond intervals */ | |
| t -= EPOCHFILETIME; /* Offset to the Epoch time */ | |
| t /= 10; /* In microseconds */ | |
| tp->tv_sec = (long)(t / 1000000); | |
| tp->tv_usec = (long)(t % 1000000); | |
| } | |
| if (NULL != tzp) | |
| { | |
| GetTimeZoneInformation(&tzi); | |
| tzp->tz_minuteswest = tzi.Bias; | |
| if (tzi.StandardDate.wMonth != 0) | |
| { | |
| tzp->tz_minuteswest += tzi.StandardBias * 60; | |
| } | |
| if (tzi.DaylightDate.wMonth != 0) | |
| { | |
| tzp->tz_dsttime = 1; | |
| } | |
| else | |
| { | |
| tzp->tz_dsttime = 0; | |
| } | |
| } | |
| return 0; | |
| } | |
| #if _GPAC_UNUSED | |
| /* | |
| time between jan 1, 1601 and jan 1, 1970 in units of 100 nanoseconds | |
| FILETIME in Win32 is from jan 1, 1601 | |
| */ | |
| s32 __gettimeofday(struct timeval *tp, void *tz) | |
| { | |
| FILETIME ft; | |
| SYSTEMTIME st; | |
| s32 val; | |
| GetSystemTime(&st); | |
| SystemTimeToFileTime(&st, &ft); | |
| val = (s32) ((*(LONGLONG *) &ft - TIMESPEC_TO_FILETIME_OFFSET) / 10000000); | |
| tp->tv_sec = (u32) val; | |
| val = (s32 ) ((*(LONGLONG *) &ft - TIMESPEC_TO_FILETIME_OFFSET - ((LONGLONG) val * (LONGLONG) 10000000)) * 100); | |
| tp->tv_usec = val; | |
| return 0; | |
| } | |
| #endif | |
| #elif defined(WIN32) | |
| #include <WinSock2.h> | |
| static s32 gettimeofday(struct timeval *tp, void *tz) | |
| { | |
| struct _timeb timebuffer; | |
| _ftime( &timebuffer ); | |
| tp->tv_sec = (long) (timebuffer.time); | |
| tp->tv_usec = timebuffer.millitm * 1000; | |
| return 0; | |
| } | |
| #endif | |
| #endif | |
| #ifdef _WIN32_WCE | |
| void CE_Assert(u32 valid, char *file, u32 line) | |
| { | |
| if (!valid) { | |
| char szBuf[2048]; | |
| u16 wcBuf[2048]; | |
| sprintf(szBuf, "File %s : line %d", file, line); | |
| CE_CharToWide(szBuf, wcBuf); | |
| MessageBox(NULL, wcBuf, _T("GPAC Assertion Failure"), MB_OK); | |
| exit(EXIT_FAILURE); | |
| } | |
| } | |
| void CE_WideToChar(unsigned short *w_str, char *str) | |
| { | |
| WideCharToMultiByte(CP_ACP, 0, w_str, -1, str, GF_MAX_PATH, NULL, NULL); | |
| } | |
| void CE_CharToWide(char *str, unsigned short *w_str) | |
| { | |
| MultiByteToWideChar(CP_ACP, 0, str, -1, w_str, GF_MAX_PATH); | |
| } | |
| #endif | |
| GF_EXPORT | |
| void gf_rand_init(Bool Reset) | |
| { | |
| if (Reset) { | |
| srand(1); | |
| } else { | |
| #if defined(_WIN32_WCE) | |
| srand( (u32) GetTickCount() ); | |
| #else | |
| srand( (u32) time(NULL) ); | |
| #endif | |
| } | |
| } | |
| GF_EXPORT | |
| u32 gf_rand() | |
| { | |
| return rand(); | |
| } | |
| #ifndef _WIN32_WCE | |
| #include <sys/stat.h> | |
| #endif | |
| GF_EXPORT | |
| void gf_utc_time_since_1970(u32 *sec, u32 *msec) | |
| { | |
| #if defined (WIN32) && !defined(_WIN32_WCE) | |
| struct _timeb tb; | |
| _ftime( &tb ); | |
| *sec = (u32) tb.time; | |
| *msec = tb.millitm; | |
| #else | |
| struct timeval tv; | |
| gettimeofday(&tv, NULL); | |
| *sec = (u32) tv.tv_sec; | |
| *msec = tv.tv_usec/1000; | |
| #endif | |
| } | |
| GF_EXPORT | |
| void gf_get_user_name(char buf[1024]) | |
| { | |
| gf_strlcpy(buf, "gpac-user", 1024); | |
| #if 0 | |
| s32 len; | |
| char *t; | |
| gf_strcpy(buf, ""); | |
| len = 1024; | |
| GetUserName(buf, &len); | |
| if (!len) { | |
| t = getenv("USER"); | |
| if (t) gf_strcpy(buf, t); | |
| } | |
| #endif | |
| #if 0 | |
| struct passwd *pw; | |
| pw = getpwuid(getuid()); | |
| gf_strcpy(buf, ""); | |
| if (pw && pw->pw_name) { | |
| gf_strcpy(name, pw->pw_name); | |
| } | |
| #endif | |
| } | |
| #ifndef WIN32 | |
| GF_EXPORT | |
| char * my_str_upr(char *str) | |
| { | |
| u32 i; | |
| for (i=0; i<strlen(str); i++) { | |
| str[i] = toupper(str[i]); | |
| } | |
| return str; | |
| } | |
| GF_EXPORT | |
| char * my_str_lwr(char *str) | |
| { | |
| u32 i; | |
| for (i=0; i<strlen(str); i++) { | |
| str[i] = tolower(str[i]); | |
| } | |
| return str; | |
| } | |
| #endif | |
| /*seems OK under mingw also*/ | |
| #ifdef WIN32 | |
| #ifdef _WIN32_WCE | |
| Bool gf_prompt_has_input() | |
| { | |
| return 0; | |
| } | |
| char gf_prompt_get_char() { | |
| return 0; | |
| } | |
| GF_EXPORT | |
| void gf_prompt_set_echo_off(Bool echo_off) { | |
| return; | |
| } | |
| GF_Err gf_prompt_get_size(u32 *width, u32 *height) | |
| { | |
| return GF_NOT_SUPPORTED; | |
| } | |
| #else | |
| #include <conio.h> | |
| #include <windows.h> | |
| Bool gf_prompt_has_input() | |
| { | |
| return kbhit(); | |
| } | |
| char gf_prompt_get_char() | |
| { | |
| #ifdef GPAC_ENABLE_COVERAGE | |
| if (gf_sys_is_cov_mode()) return 0; | |
| #endif | |
| return getchar(); | |
| } | |
| GF_EXPORT | |
| void gf_prompt_set_echo_off(Bool echo_off) | |
| { | |
| #ifdef GPAC_ENABLE_COVERAGE | |
| if (gf_sys_is_cov_mode()) return; | |
| #endif | |
| DWORD flags; | |
| HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); | |
| BOOL ret = GetConsoleMode(hStdin, &flags); | |
| if (!ret) { | |
| DWORD err = GetLastError(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CONSOLE, ("[Console] GetConsoleMode() return with the following error code: %d\n", err)); | |
| return; | |
| } | |
| if (echo_off) flags &= ~ENABLE_ECHO_INPUT; | |
| else flags |= ENABLE_ECHO_INPUT; | |
| SetConsoleMode(hStdin, flags); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_prompt_get_size(u32 *width, u32 *height) | |
| { | |
| CONSOLE_SCREEN_BUFFER_INFO info; | |
| HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); | |
| BOOL ret = GetConsoleScreenBufferInfo(hStdin, &info); | |
| if (!ret) { | |
| DWORD err = GetLastError(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CONSOLE, ("[Console] GetConsoleScreenBufferInfo() return with the following error code: %d\n", err)); | |
| return GF_IO_ERR; | |
| } | |
| if (width) *width = info.dwSize.X; | |
| if (height) *height = info.dwSize.Y; | |
| return GF_OK; | |
| } | |
| #endif | |
| #else | |
| /*linux kbhit/getchar- borrowed on debian mailing lists, (author Mike Brownlow)*/ | |
| #include <termios.h> | |
| #include <sys/ioctl.h> | |
| static struct termios t_orig, t_new; | |
| static s32 ch_peek = -1; | |
| static void init_keyboard() | |
| { | |
| tcgetattr(0, &t_orig); | |
| t_new = t_orig; | |
| t_new.c_lflag &= ~ICANON; | |
| t_new.c_lflag &= ~ECHO; | |
| t_new.c_lflag &= ~ISIG; | |
| t_new.c_cc[VMIN] = 1; | |
| t_new.c_cc[VTIME] = 0; | |
| tcsetattr(0, TCSANOW, &t_new); | |
| } | |
| static void close_keyboard(Bool new_line) | |
| { | |
| tcsetattr(0,TCSANOW, &t_orig); | |
| if (new_line) fprintf(stderr, "\n"); | |
| } | |
| GF_EXPORT | |
| void gf_prompt_set_echo_off(Bool echo_off) | |
| { | |
| #ifdef GPAC_ENABLE_COVERAGE | |
| if (gf_sys_is_cov_mode()) return; | |
| #endif | |
| init_keyboard(); | |
| if (echo_off) t_orig.c_lflag &= ~ECHO; | |
| else t_orig.c_lflag |= ECHO; | |
| close_keyboard(GF_FALSE); | |
| } | |
| GF_EXPORT | |
| Bool gf_prompt_has_input() | |
| { | |
| u8 ch; | |
| s32 nread; | |
| pid_t fg = tcgetpgrp(STDIN_FILENO); | |
| //we are not foreground nor piped (used for IDEs), can't read stdin | |
| if ((fg!=-1) && (fg != getpgrp())) { | |
| return GF_FALSE; | |
| } | |
| init_keyboard(); | |
| if (ch_peek != -1) return GF_TRUE; | |
| t_new.c_cc[VMIN]=0; | |
| tcsetattr(0, TCSANOW, &t_new); | |
| nread = (s32) read(0, &ch, 1); | |
| t_new.c_cc[VMIN]=1; | |
| tcsetattr(0, TCSANOW, &t_new); | |
| if(nread == 1) { | |
| ch_peek = ch; | |
| return GF_TRUE; | |
| } | |
| close_keyboard(GF_FALSE); | |
| return GF_FALSE; | |
| } | |
| GF_EXPORT | |
| char gf_prompt_get_char() | |
| { | |
| char ch; | |
| #ifdef GPAC_ENABLE_COVERAGE | |
| if (gf_sys_is_cov_mode()) return 0; | |
| #endif | |
| if (ch_peek != -1) { | |
| ch = ch_peek; | |
| ch_peek = -1; | |
| close_keyboard(GF_TRUE); | |
| return ch; | |
| } | |
| if (0==read(0,&ch,1)) | |
| ch = 0; | |
| close_keyboard(GF_TRUE); | |
| return ch; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_prompt_get_size(u32 *width, u32 *height) | |
| { | |
| #if defined(TIOCGWINSZ) | |
| struct winsize ws; | |
| if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != 0) return GF_IO_ERR; | |
| if (width) *width = ws.ws_col; | |
| if (height) *height = ws.ws_row; | |
| return GF_OK; | |
| #elif defined(WIOCGETD) | |
| struct uwdata w; | |
| if (ioctl(2, WIOCGETD, &w) != 0) return GF_IO_ERR; | |
| if (width && (w.uw_width > 0)) | |
| *width = w.uw_width / w.uw_hs; | |
| if (height && (w.uw_height > 0)) | |
| *height = w.uw_height / w.uw_vs; | |
| return GF_OK; | |
| #else | |
| return GF_NOT_SUPPORTED; | |
| #endif | |
| } | |
| #endif | |
| static u32 sys_init = 0; | |
| static u32 last_update_time = 0; | |
| static u64 last_process_k_u_time = 0; | |
| GF_SystemRTInfo the_rti; | |
| #if defined(_WIN32_WCE) | |
| static LARGE_INTEGER frequency , init_counter; | |
| static u64 last_total_k_u_time = 0; | |
| static u32 mem_usage_at_startup = 0; | |
| #ifndef GetCurrentPermissions | |
| DWORD GetCurrentPermissions(); | |
| #endif | |
| #ifndef SetProcPermissions | |
| void SetProcPermissions(DWORD ); | |
| #endif | |
| #elif defined(WIN32) | |
| static LARGE_INTEGER frequency , init_counter; | |
| static u64 last_proc_idle_time = 0; | |
| static u64 last_proc_k_u_time = 0; | |
| static HINSTANCE psapi_hinst = NULL; | |
| typedef BOOL(WINAPI* NTGetSystemTimes)(VOID *,VOID *,VOID *); | |
| NTGetSystemTimes MyGetSystemTimes = NULL; | |
| typedef BOOL(WINAPI* NTGetProcessMemoryInfo)(HANDLE,VOID *,DWORD); | |
| NTGetProcessMemoryInfo MyGetProcessMemoryInfo = NULL; | |
| typedef int(WINAPI* NTQuerySystemInfo)(ULONG,PVOID,ULONG,PULONG); | |
| NTQuerySystemInfo MyQuerySystemInfo = NULL; | |
| #ifndef PROCESS_MEMORY_COUNTERS | |
| typedef struct _PROCESS_MEMORY_COUNTERS | |
| { | |
| DWORD cb; | |
| DWORD PageFaultCount; | |
| SIZE_T PeakWorkingSetSize; | |
| SIZE_T WorkingSetSize; | |
| SIZE_T QuotaPeakPagedPoolUsage; | |
| SIZE_T QuotaPagedPoolUsage; | |
| SIZE_T QuotaPeakNonPagedPoolUsage; | |
| SIZE_T QuotaNonPagedPoolUsage; | |
| SIZE_T PagefileUsage; | |
| SIZE_T PeakPagefileUsage; | |
| } PROCESS_MEMORY_COUNTERS; | |
| #endif | |
| #ifndef SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION | |
| typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION | |
| { | |
| LARGE_INTEGER IdleTime; | |
| LARGE_INTEGER KernelTime; | |
| LARGE_INTEGER UserTime; | |
| LARGE_INTEGER Reserved1[2]; | |
| ULONG Reserved2; | |
| } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; | |
| #endif | |
| #else | |
| static u64 last_cpu_u_k_time = 0; | |
| static u64 last_cpu_idle_time = 0; | |
| static u64 mem_at_startup = 0; | |
| #endif | |
| #ifdef WIN32 | |
| static u32 (*OS_GetSysClock)(); | |
| u32 gf_sys_clock() | |
| { | |
| return OS_GetSysClock(); | |
| } | |
| static u64 (*OS_GetSysClockHR)(); | |
| u64 gf_sys_clock_high_res() | |
| { | |
| return OS_GetSysClockHR(); | |
| } | |
| #endif | |
| #ifdef WIN32 | |
| #include <timeapi.h> | |
| static u32 OS_GetSysClockHIGHRES() | |
| { | |
| LARGE_INTEGER now; | |
| QueryPerformanceCounter(&now); | |
| now.QuadPart -= init_counter.QuadPart; | |
| return (u32) ((now.QuadPart * 1000) / frequency.QuadPart); | |
| } | |
| static u64 OS_GetSysClockHIGHRES_FULL() | |
| { | |
| LARGE_INTEGER now; | |
| QueryPerformanceCounter(&now); | |
| now.QuadPart -= init_counter.QuadPart; | |
| return (u64) ((now.QuadPart * 1000000) / frequency.QuadPart); | |
| } | |
| static u32 OS_GetSysClockNORMAL() | |
| { | |
| #ifdef _WIN32_WCE | |
| return GetTickCount(); | |
| #else | |
| return timeGetTime(); | |
| #endif | |
| } | |
| static u64 OS_GetSysClockNORMAL_FULL() | |
| { | |
| u64 res = OS_GetSysClockNORMAL(); | |
| return res*1000; | |
| } | |
| #endif /* WIN32 */ | |
| #if defined(__sh__) | |
| /* Avoid exception for denormalized floating point values */ | |
| static int | |
| sh4_get_fpscr() | |
| { | |
| int ret; | |
| asm volatile ("sts fpscr,%0" : "=r" (ret)); | |
| return ret; | |
| } | |
| static void | |
| sh4_put_fpscr(int nv) | |
| { | |
| asm volatile ("lds %0,fpscr" : : "r" (nv)); | |
| } | |
| #define SH4_FPSCR_FR 0x00200000 | |
| #define SH4_FPSCR_SZ 0x00100000 | |
| #define SH4_FPSCR_PR 0x00080000 | |
| #define SH4_FPSCR_DN 0x00040000 | |
| #define SH4_FPSCR_RN 0x00000003 | |
| #define SH4_FPSCR_RN_N 0 | |
| #define SH4_FPSCR_RN_Z 1 | |
| extern int __fpscr_values[2]; | |
| void | |
| sh4_change_fpscr(int off, int on) | |
| { | |
| int b = sh4_get_fpscr(); | |
| off = ~off; | |
| off |= 0x00180000; | |
| on &= ~ 0x00180000; | |
| b &= off; | |
| b |= on; | |
| sh4_put_fpscr(b); | |
| __fpscr_values[0] &= off; | |
| __fpscr_values[0] |= on; | |
| __fpscr_values[1] &= off; | |
| __fpscr_values[1] |= on; | |
| } | |
| #endif | |
| GF_EXPORT | |
| struct tm *gf_gmtime(const time_t *time) | |
| { | |
| #ifdef _WIN32_WCE | |
| FILETIME filet; | |
| LPSYSTEMTIME syst; | |
| *(LONGLONG *) &filet = (sec/* - GF_NTP_SEC_1900_TO_1970*/) * 10000000 + TIMESPEC_TO_FILETIME_OFFSET; | |
| FileTimeToSystemTime(&filet, &syst); | |
| if (syst.wSecond>60) | |
| syst.wSecond=60; | |
| #endif | |
| struct tm *tm = gmtime(time); | |
| //see issue #859, no clue how this happened... | |
| if (tm->tm_sec>60) | |
| tm->tm_sec = 60; | |
| return tm; | |
| } | |
| #ifdef GPAC_MEMORY_TRACKING | |
| void gf_mem_enable_tracker(u32 mem_track_type); | |
| #endif | |
| static u64 memory_at_gpac_startup = 0; | |
| static u32 gpac_argc = 0; | |
| const char **gpac_argv = NULL; | |
| Bool *gpac_argv_state = NULL; | |
| static Bool gpac_test_mode = GF_FALSE; | |
| static Bool gpac_old_arch = GF_FALSE; | |
| static Bool gpac_discard_config = GF_FALSE; | |
| //in error.c | |
| #ifndef GPAC_DISABLE_LOG | |
| extern FILE *gpac_log_file; | |
| extern Bool gpac_log_time_start; | |
| extern Bool gpac_log_utc_time; | |
| void gf_logs_init(); | |
| void gf_logs_close(); | |
| #endif | |
| GF_EXPORT | |
| Bool gf_log_use_file() | |
| { | |
| #ifndef GPAC_DISABLE_LOG | |
| return gpac_log_file ? GF_TRUE : GF_FALSE; | |
| #else | |
| return GF_FALSE; | |
| #endif | |
| } | |
| static void progress_quiet(const void *cbck, const char *title, u64 done, u64 total) { } | |
| void gpac_disable_progress() | |
| { | |
| gf_set_progress_callback(NULL, progress_quiet); | |
| } | |
| GF_EXPORT | |
| Bool gf_sys_is_test_mode() | |
| { | |
| return gpac_test_mode; | |
| } | |
| GF_EXPORT | |
| Bool gf_sys_old_arch_compat() | |
| { | |
| return gpac_old_arch; | |
| } | |
| #ifdef GPAC_ENABLE_COVERAGE | |
| GF_EXPORT | |
| Bool gf_sys_is_cov_mode() | |
| { | |
| return gpac_test_mode; | |
| } | |
| #endif | |
| const char *gpac_log_file_name=NULL; | |
| #ifndef GPAC_DISABLE_LOG | |
| extern Bool gpac_log_dual; | |
| #ifdef GPAC_CONFIG_EMSCRIPTEN | |
| extern Bool gpac_log_console; | |
| #endif | |
| #endif | |
| GF_EXPORT | |
| void gf_log_reset_file() | |
| { | |
| #ifndef GPAC_DISABLE_LOG | |
| if (gpac_log_file_name) { | |
| if (gpac_log_file) gf_fclose(gpac_log_file); | |
| #ifdef GPAC_CONFIG_EMSCRIPTEN | |
| gpac_log_console = GF_FALSE; | |
| if (gpac_log_file_name && !strcmp(gpac_log_file_name, "console")) { | |
| gpac_log_file = NULL; | |
| gpac_log_console = GF_TRUE; | |
| return; | |
| } | |
| #endif | |
| gpac_log_file = gf_fopen(gpac_log_file_name, "wt"); | |
| } | |
| #endif | |
| } | |
| static Bool gpac_has_global_filter_args=GF_FALSE; | |
| static Bool gpac_has_global_filter_meta_args=GF_FALSE; | |
| #include <gpac/thread.h> | |
| GF_Mutex *logs_mx = NULL; | |
| Bool gf_sys_has_filter_global_args() | |
| { | |
| return gpac_has_global_filter_args; | |
| } | |
| Bool gf_sys_has_filter_global_meta_args() | |
| { | |
| return gpac_has_global_filter_meta_args; | |
| } | |
| static u32 gpac_quiet = 0; | |
| char gf_prog_lf = '\r'; | |
| #ifndef GPAC_DISABLE_NETWORK | |
| extern Bool gpac_use_poll; | |
| #endif | |
| GF_EXPORT | |
| GF_Err gf_sys_set_args(s32 argc, const char **argv) | |
| { | |
| s32 i; | |
| if (!gpac_argc) { | |
| Bool gf_opts_load_option(const char *arg_name, const char *val, Bool *consumed_next, GF_Err *e); | |
| #ifdef GPAC_ENABLE_RESTRICT | |
| void gf_cfg_load_restrict(); | |
| #endif | |
| #ifndef GPAC_DISABLE_NETWORK | |
| gpac_use_poll = GF_TRUE; | |
| #endif | |
| for (i=1; i<argc; i++) { | |
| Bool consumed; | |
| GF_Err e; | |
| Bool use_sep = GF_FALSE; | |
| Bool bool_value = GF_TRUE; | |
| char szArg[1024]; | |
| char *arg_val; | |
| const char *arg = argv[i]; | |
| if (!arg) continue; | |
| arg_val = strchr(arg, '='); | |
| if (arg_val) { | |
| u32 len = (u32) (arg_val-arg); | |
| if (len>1023) len = 1023; | |
| memcpy(szArg, arg, len); | |
| szArg[len]=0; | |
| arg_val++; | |
| arg = szArg; | |
| use_sep = GF_TRUE; | |
| } else if (i+1<argc) { | |
| arg_val = (char *) argv[i+1]; | |
| } | |
| if ((arg[0] != '-') || ! arg[1]) { | |
| continue; | |
| } | |
| if (arg_val && (!strcmp(arg_val, "no") || !strcmp(arg_val, "false") || !strcmp(arg_val, "0") ) ) | |
| bool_value = GF_FALSE; | |
| if (arg[1]=='-') { | |
| gpac_has_global_filter_args = GF_TRUE; | |
| } else if (arg[1]=='+') { | |
| gpac_has_global_filter_meta_args = GF_TRUE; | |
| } else if (!strcmp(arg, "-log-file") || !strcmp(arg, "-lf")) { | |
| #ifndef GPAC_DISABLE_LOG | |
| gpac_log_file_name = arg_val; | |
| #endif | |
| if (!use_sep) i += 1; | |
| } else if (!strcmp(arg, "-log-dual") || !strcmp(arg, "-ld")) { | |
| #ifndef GPAC_DISABLE_LOG | |
| gpac_log_dual = GF_TRUE; | |
| #endif | |
| } else if (!strcmp(arg, "-logs") ) { | |
| e = gf_log_set_tools_levels(arg_val, GF_FALSE); | |
| if (e) return e; | |
| if (!use_sep) i += 1; | |
| } else if (!strcmp(arg, "-log-clock") || !strcmp(arg, "-lc")) { | |
| #ifndef GPAC_DISABLE_LOG | |
| gpac_log_time_start = GF_TRUE; | |
| #endif | |
| } else if (!strcmp(arg, "-log-utc") || !strcmp(arg, "-lu")) { | |
| #ifndef GPAC_DISABLE_LOG | |
| gpac_log_utc_time = GF_TRUE; | |
| #endif | |
| } else if (!strcmp(arg, "-quiet")) { | |
| gpac_quiet = 2; | |
| } else if (!strcmp(arg, "-noprog")) { | |
| if (!gpac_quiet) gpac_quiet = 1; | |
| } else if (!strcmp(arg, "-proglf")) { | |
| gf_prog_lf = '\n'; | |
| } else if (!stricmp(arg, "-for-test")) { | |
| gpac_test_mode = bool_value; | |
| } else if (!stricmp(arg, "-old-arch")) { | |
| if (bool_value) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_APP, ("[core] -old-arch will be deprecated in next release - please update your scripts\n")); | |
| } | |
| gpac_old_arch = bool_value; | |
| } else if (!stricmp(arg, "-no-save")) { | |
| gpac_discard_config = bool_value; | |
| } else if (!stricmp(arg, "-no-poll")) { | |
| #ifndef GPAC_DISABLE_NETWORK | |
| gpac_use_poll = bool_value ? GF_FALSE : GF_TRUE; | |
| #endif | |
| } | |
| #if !defined(GPAC_DISABLE_NETCAP) | |
| else if (!stricmp(arg, "-netcap")) { | |
| if (!arg_val) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_APP, ("[core] Missing value for argument -netcap, ignoring\n")); | |
| } else { | |
| GF_Err gf_netcap_setup(char *rules); | |
| e = gf_netcap_setup(arg_val); | |
| if (e) return e; | |
| } | |
| } | |
| #endif | |
| else if (!stricmp(arg, "-ntp-shift")) { | |
| s32 shift = arg_val ? atoi(arg_val) : 0; | |
| gf_net_set_ntp_shift(shift); | |
| if (!use_sep) i += 1; | |
| } else if (gf_opts_load_option(arg, arg_val, &consumed, &e)) { | |
| if (e) return e; | |
| if (consumed && !use_sep) | |
| i += 1; | |
| } | |
| } | |
| #ifdef GPAC_CONFIG_DARWIN | |
| //if running in xcode disable color logs (not supported by output console) | |
| if (getenv("__XCODE_BUILT_PRODUCTS_DIR_PATHS") != NULL) { | |
| gf_log_set_tools_levels("ncl", GF_FALSE); | |
| } | |
| #endif | |
| #ifndef GPAC_DISABLE_LOG | |
| gf_log_reset_file(); | |
| #endif | |
| if (gf_opts_get_bool("core", "rmt")) | |
| gf_sys_enable_rmtws(GF_TRUE); | |
| if (gpac_quiet) { | |
| if (gpac_quiet==2) gf_log_set_tool_level(GF_LOG_ALL, GF_LOG_QUIET); | |
| gf_set_progress_callback(NULL, progress_quiet); | |
| } | |
| #ifdef GPAC_ENABLE_RESTRICT | |
| //now that we have parsed all options, load restrict | |
| gf_cfg_load_restrict(); | |
| #endif | |
| } | |
| //for OSX we allow overwrite of argc/argv due to different behavior between console-mode apps and GUI | |
| #if !defined(__DARWIN__) && !defined(__APPLE__) | |
| if (!gpac_argc && (argc>=0) ) | |
| #endif | |
| { | |
| gpac_argc = (u32) argc; | |
| gpac_argv = argv; | |
| gpac_argv_state = gf_realloc(gpac_argv_state, sizeof(Bool) * argc); | |
| for (i=0; i<argc; i++) { | |
| gpac_argv_state[i] = GF_FALSE; | |
| if (!strncmp(argv[i], "-p", 2)) gpac_argv_state[i] = GF_TRUE; | |
| else if (!strcmp(argv[i], "-mem-track")) gpac_argv_state[i] = GF_TRUE; | |
| else if (!strcmp(argv[i], "-mem-track-stack")) gpac_argv_state[i] = GF_TRUE; | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| void gf_sys_mark_arg_used(s32 arg_idx, Bool used) | |
| { | |
| if (arg_idx < (s32) gpac_argc) | |
| gpac_argv_state[arg_idx] = used; | |
| } | |
| GF_EXPORT | |
| Bool gf_sys_is_arg_used(s32 arg_idx) | |
| { | |
| if (arg_idx < (s32) gpac_argc) | |
| return gpac_argv_state[arg_idx]; | |
| return GF_FALSE; | |
| } | |
| GF_EXPORT | |
| u32 gf_sys_is_quiet() | |
| { | |
| return gpac_quiet; | |
| } | |
| GF_EXPORT | |
| u32 gf_sys_get_argc() | |
| { | |
| return gpac_argc; | |
| } | |
| GF_EXPORT | |
| const char **gf_sys_get_argv() | |
| { | |
| return gpac_argv; | |
| } | |
| GF_EXPORT | |
| const char *gf_sys_get_arg(u32 arg) | |
| { | |
| if (!gpac_argc || !gpac_argv) return NULL; | |
| if (arg>=gpac_argc) return NULL; | |
| return gpac_argv[arg]; | |
| } | |
| GF_EXPORT | |
| const char *gf_sys_find_global_arg(const char *arg) | |
| { | |
| u32 i; | |
| if (!gpac_argc || !gpac_argv) return NULL; | |
| for (i=0; i<gpac_argc; i++) { | |
| const char *sep; | |
| u32 len; | |
| const char *an_arg = gpac_argv[i]; | |
| if (an_arg[0]!='-') continue; | |
| if ((an_arg[1]!='-') && (an_arg[1]!='+')) continue; | |
| an_arg += 2; | |
| sep = strchr(an_arg, '@'); | |
| if (sep) an_arg = sep+1; | |
| sep = strchr(an_arg, '='); | |
| if (sep) len = (u32) (sep - an_arg); | |
| else len = (u32) strlen(an_arg); | |
| if (len != (u32) strlen(arg)) continue; | |
| if (strncmp(an_arg, arg, len)) continue; | |
| if (!sep) return ""; | |
| return sep; | |
| } | |
| return NULL; | |
| } | |
| #if !defined(GPAC_DISABLE_RMTWS) && !defined(GPAC_DISABLE_NETWORK) | |
| RMT_WS* rmtws_handle=NULL; | |
| RMT_WS* userws_handle=NULL; | |
| #endif | |
| GF_EXPORT | |
| void* gf_sys_get_rmtws() { | |
| #if !defined(GPAC_DISABLE_RMTWS) && !defined(GPAC_DISABLE_NETWORK) | |
| return (void*)rmtws_handle; | |
| #else | |
| return NULL; | |
| #endif | |
| } | |
| GF_EXPORT | |
| void* gf_sys_get_userws() { | |
| #if !defined(GPAC_DISABLE_RMTWS) && !defined(GPAC_DISABLE_NETWORK) | |
| return (void*)userws_handle; | |
| #else | |
| return NULL; | |
| #endif | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sys_enable_rmtws(Bool start) { | |
| #if !defined(GPAC_DISABLE_RMTWS) && !defined(GPAC_DISABLE_NETWORK) | |
| if (start && !rmtws_handle) { | |
| rmtws_handle = rmt_ws_new(); | |
| if (!rmtws_handle) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] unable to initialize RMT websocket server\n")); | |
| return GF_OUT_OF_MEM; | |
| } | |
| RMT_Settings *rmcfg = gf_rmt_get_settings(rmtws_handle); | |
| rmcfg->port = gf_opts_get_int("core", "rmt-port"); | |
| rmcfg->limit_connections_to_localhost = gf_opts_get_bool("core", "rmt-localhost"); | |
| rmcfg->msSleepBetweenServerUpdates = gf_opts_get_int("core", "rmt-sleep"); | |
| rmcfg->cert = gf_opts_get_key("core", "rmt-cert"); | |
| rmcfg->pkey = gf_opts_get_key("core", "rmt-pkey"); | |
| rmt_ws_run(rmtws_handle); | |
| } else if (!start && rmtws_handle) { | |
| rmt_ws_del(rmtws_handle); | |
| rmtws_handle=NULL; | |
| } | |
| return GF_OK; | |
| #else | |
| return GF_NOT_SUPPORTED; | |
| #endif | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sys_enable_userws(Bool start) { | |
| #if !defined(GPAC_DISABLE_RMTWS) && !defined(GPAC_DISABLE_NETWORK) | |
| if (start && !userws_handle) { | |
| userws_handle = rmt_ws_new(); | |
| if (!userws_handle) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] unable to initialize RMT websocket server\n")); | |
| return GF_OUT_OF_MEM; | |
| } | |
| RMT_Settings *rmcfg = gf_rmt_get_settings(userws_handle); | |
| rmcfg->port = gf_opts_get_int("core", "userws-port"); | |
| rmcfg->limit_connections_to_localhost = gf_opts_get_bool("core", "userws-localhost"); | |
| rmcfg->msSleepBetweenServerUpdates = gf_opts_get_int("core", "userws-sleep"); | |
| rmcfg->cert = gf_opts_get_key("core", "userws-cert"); | |
| rmcfg->pkey = gf_opts_get_key("core", "userws-pkey"); | |
| rmt_ws_run(userws_handle); | |
| } else if (!start && userws_handle) { | |
| rmt_ws_del(userws_handle); | |
| userws_handle=NULL; | |
| } | |
| return GF_OK; | |
| #else | |
| return GF_NOT_SUPPORTED; | |
| #endif | |
| } | |
| #include <gpac/list.h> | |
| GF_List *all_blobs = NULL; | |
| GF_EXPORT | |
| GF_Err gf_blob_get_ex(GF_Blob *blob, u8 **out_data, u32 *out_size, u32 *out_flags) | |
| { | |
| if (!blob) | |
| return GF_BAD_PARAM; | |
| if (gf_list_find(all_blobs, blob)<0) | |
| return GF_URL_REMOVED; | |
| gf_mx_p(blob->mx); | |
| if (out_data) *out_data = blob->data; | |
| if (out_size) *out_size = blob->size; | |
| if (out_flags) *out_flags = blob->flags; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_blob_get(const char *blob_url, u8 **out_data, u32 *out_size, u32 *out_flags) | |
| { | |
| GF_Blob *blob = NULL; | |
| if (sscanf(blob_url, "gmem://%p", &blob) != 1) return GF_BAD_PARAM; | |
| return gf_blob_get_ex(blob, out_data, out_size, out_flags); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_blob_release_ex(GF_Blob *blob) | |
| { | |
| if (!blob) | |
| return GF_BAD_PARAM; | |
| if (gf_list_find(all_blobs, blob)<0) | |
| return GF_URL_REMOVED; | |
| gf_mx_v(blob->mx); | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_blob_release(const char *blob_url) | |
| { | |
| GF_Blob *blob = NULL; | |
| if (sscanf(blob_url, "gmem://%p", &blob) != 1) return GF_BAD_PARAM; | |
| return gf_blob_release_ex(blob); | |
| } | |
| GF_EXPORT | |
| char *gf_blob_register(GF_Blob *blob) | |
| { | |
| char szPath[200]; | |
| if (!blob) return NULL; | |
| sprintf(szPath, "gmem://%p", blob); | |
| if (!all_blobs) { | |
| all_blobs = gf_list_new(); | |
| } else { | |
| gf_list_del_item(all_blobs, blob); | |
| } | |
| gf_list_add(all_blobs, blob); | |
| return gf_strdup(szPath); | |
| } | |
| GF_EXPORT | |
| void gf_blob_unregister(GF_Blob *blob) | |
| { | |
| if (!blob) return; | |
| if (all_blobs) gf_list_del_item(all_blobs, blob); | |
| } | |
| //unused | |
| #if 0 | |
| GF_Blob *gf_blob_from_url(const char *blob_url) | |
| { | |
| GF_Blob *blob = NULL; | |
| if (sscanf(blob_url, "gmem://%p", &blob) != 1) return NULL; | |
| if (!blob) | |
| return NULL; | |
| if (gf_list_find(all_blobs, blob)<0) | |
| return NULL; | |
| return blob; | |
| } | |
| #endif | |
| GF_EXPORT | |
| GF_BlobRangeStatus gf_blob_query_range(GF_Blob *blob, Bool check_when_complete, u64 start_offset, u32 size) | |
| { | |
| if (!blob) return GF_BLOB_RANGE_CORRUPTED; | |
| if (blob->range_valid) return blob->range_valid(blob, check_when_complete, start_offset, &size); | |
| if (blob->flags & GF_BLOB_IN_TRANSFER) return GF_BLOB_RANGE_IN_TRANSFER; | |
| return GF_BLOB_RANGE_VALID; | |
| } | |
| void gf_init_global_config(const char *profile); | |
| void gf_uninit_global_config(Bool discard_config); | |
| static GF_Config *gpac_lang_file = NULL; | |
| static const char *gpac_lang_code = NULL; | |
| GF_Config *gf_sys_get_lang_file() | |
| { | |
| const char *opt = gf_opts_get_key("core", "lang"); | |
| char szSharedPath[GF_MAX_PATH]; | |
| if (!opt) return NULL; | |
| if (gpac_lang_code && strcmp(gpac_lang_code, opt)) { | |
| gf_cfg_del(gpac_lang_file); | |
| gpac_lang_file = NULL; | |
| } | |
| gpac_lang_code = opt; | |
| if (gpac_lang_file) return gpac_lang_file; | |
| if (! gf_opts_default_shared_directory(szSharedPath)) return NULL; | |
| gf_strcat(szSharedPath, "/lang/"); | |
| gf_strcat(szSharedPath, opt); | |
| gf_strcat(szSharedPath, ".txt"); | |
| if (!gf_file_exists(szSharedPath)) return NULL; | |
| gpac_lang_file = gf_cfg_new(NULL, szSharedPath); | |
| return gpac_lang_file; | |
| } | |
| GF_EXPORT | |
| const char *gf_sys_localized(const char *sec_name, const char *key_name, const char *def_val) | |
| { | |
| GF_Config *lcfg = gf_sys_get_lang_file(); | |
| if (!lcfg) return def_val; | |
| const char *opt = gf_cfg_get_key(lcfg, sec_name, key_name); | |
| if (opt) return opt; | |
| return def_val; | |
| } | |
| static void gf_sys_refresh_cache() | |
| { | |
| u32 i, count; | |
| count = gf_opts_get_section_count(); | |
| for (i=0; i<count; i++) { | |
| const char *opt; | |
| u32 sec, frac, exp; | |
| Bool force_delete; | |
| const char *file; | |
| const char *name = gf_opts_get_section_name(i); | |
| if (strncmp(name, "@cache=", 7)) continue; | |
| file = gf_opts_get_key(name, "cacheFile"); | |
| opt = gf_opts_get_key(name, "expireAfterNTP"); | |
| if (!opt) { | |
| if (file) gf_file_delete((char*) file); | |
| gf_opts_del_section(name); | |
| i--; | |
| count--; | |
| continue; | |
| } | |
| force_delete = 0; | |
| if (file && gf_file_exists(file)) { | |
| force_delete = 1; | |
| } | |
| sscanf(opt, "%u", &exp); | |
| gf_net_get_ntp(&sec, &frac); | |
| if (exp && (exp<sec)) force_delete=1; | |
| if (force_delete) { | |
| if (file) gf_file_delete(file); | |
| gf_opts_del_section(name); | |
| i--; | |
| count--; | |
| continue; | |
| } | |
| } | |
| } | |
| GF_EXPORT | |
| GF_Err gf_sys_init(GF_MemTrackerType mem_tracker_type, const char *profile) | |
| { | |
| if (!sys_init) { | |
| #if defined (WIN32) | |
| #if defined(_WIN32_WCE) | |
| MEMORYSTATUS ms; | |
| #else | |
| SYSTEM_INFO sysinfo; | |
| #endif | |
| #endif | |
| #ifdef GPAC_MEMORY_TRACKING | |
| gf_mem_enable_tracker(mem_tracker_type); | |
| #endif | |
| #ifndef GPAC_DISABLE_LOG | |
| gf_logs_init(); | |
| /*by default log subsystem is initialized to error on all tools, and info on console to debug scripts*/ | |
| gf_log_set_tool_level(GF_LOG_ALL, GF_LOG_WARNING); | |
| gf_log_set_tool_level(GF_LOG_APP, GF_LOG_INFO); | |
| gf_log_set_tool_level(GF_LOG_CONSOLE, GF_LOG_INFO); | |
| #endif | |
| #if defined(__sh__) | |
| /* Round all denormalized floatting point number to 0.0 */ | |
| sh4_change_fpscr(0,SH4_FPSCR_DN) ; | |
| #endif | |
| #if defined(WIN32) | |
| frequency.QuadPart = 0; | |
| /*clock setup*/ | |
| if (QueryPerformanceFrequency(&frequency)) { | |
| QueryPerformanceCounter(&init_counter); | |
| OS_GetSysClock = OS_GetSysClockHIGHRES; | |
| OS_GetSysClockHR = OS_GetSysClockHIGHRES_FULL; | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] using WIN32 performance timer\n")); | |
| } else { | |
| OS_GetSysClock = OS_GetSysClockNORMAL; | |
| OS_GetSysClockHR = OS_GetSysClockNORMAL_FULL; | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] using WIN32 regular timer\n")); | |
| } | |
| #ifndef _WIN32_WCE | |
| timeBeginPeriod(1); | |
| #endif | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] checking for run-time info tools")); | |
| #if defined(_WIN32_WCE) | |
| last_total_k_u_time = last_process_k_u_time = 0; | |
| last_update_time = 0; | |
| memset(&the_rti, 0, sizeof(GF_SystemRTInfo)); | |
| the_rti.pid = GetCurrentProcessId(); | |
| the_rti.nb_cores = 1; | |
| GlobalMemoryStatus(&ms); | |
| mem_usage_at_startup = ms.dwAvailPhys; | |
| #else | |
| /*cpu usage tools are buried in win32 dlls...*/ | |
| MyGetSystemTimes = (NTGetSystemTimes) GetProcAddress(GetModuleHandle("kernel32.dll"), "GetSystemTimes"); | |
| if (!MyGetSystemTimes) { | |
| MyQuerySystemInfo = (NTQuerySystemInfo) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation"); | |
| if (MyQuerySystemInfo) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CORE, (" - CPU: QuerySystemInformation")); | |
| } | |
| } else { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CORE, (" - CPU: GetSystemsTimes")); | |
| } | |
| psapi_hinst = LoadLibrary("psapi.dll"); | |
| MyGetProcessMemoryInfo = (NTGetProcessMemoryInfo) GetProcAddress(psapi_hinst, "GetProcessMemoryInfo"); | |
| if (MyGetProcessMemoryInfo) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CORE, (" - memory: GetProcessMemoryInfo")); | |
| } | |
| last_process_k_u_time = last_proc_idle_time = last_proc_k_u_time = 0; | |
| last_update_time = 0; | |
| memset(&the_rti, 0, sizeof(GF_SystemRTInfo)); | |
| the_rti.pid = GetCurrentProcessId(); | |
| GetSystemInfo( &sysinfo ); | |
| the_rti.nb_cores = sysinfo.dwNumberOfProcessors; | |
| #endif | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("\n")); | |
| #else | |
| /*linux threads and OSX...*/ | |
| last_process_k_u_time = 0; | |
| last_cpu_u_k_time = last_cpu_idle_time = 0; | |
| last_update_time = 0; | |
| memset(&the_rti, 0, sizeof(GF_SystemRTInfo)); | |
| the_rti.pid = getpid(); | |
| the_rti.nb_cores = (u32) sysconf( _SC_NPROCESSORS_ONLN ); | |
| sys_start_time = 0; | |
| sys_start_time_hr = 0; | |
| sys_start_time = gf_sys_clock(); | |
| sys_start_time_hr = gf_sys_clock_high_res(); | |
| #endif | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] process id %d\n", the_rti.pid)); | |
| #ifndef _WIN32_WCE | |
| setlocale( LC_NUMERIC, "C" ); | |
| #endif | |
| #ifndef GPAC_CONFIG_EMSCRIPTEN | |
| //avoid log mutex on emscriptem - let the FS IO deal with it | |
| logs_mx = gf_mx_new("Logs"); | |
| #endif | |
| gf_rand_init(GF_FALSE); | |
| gf_init_global_config(profile); | |
| gf_sys_refresh_cache(); | |
| } | |
| sys_init += 1; | |
| /*init RTI stats*/ | |
| if (!memory_at_gpac_startup) { | |
| GF_SystemRTInfo rti; | |
| if (profile && !strcmp(profile, "n")) { | |
| gpac_disable_rti=GF_TRUE; | |
| memory_at_gpac_startup = 0; | |
| } else if (gf_sys_get_rti(500, &rti, GF_RTI_SYSTEM_MEMORY_ONLY)) { | |
| memory_at_gpac_startup = rti.physical_memory_avail; | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] System init OK - process id %d - %d MB physical RAM - %d cores\n", rti.pid, (u32) (rti.physical_memory/1024/1024), rti.nb_cores)); | |
| } else { | |
| memory_at_gpac_startup = 0; | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| #if !defined(GPAC_DISABLE_NETCAP) && !defined(GPAC_DISABLE_NETWORK) | |
| void gf_net_close_capture(); | |
| #endif | |
| extern GF_List *gfio_delete_handlers; | |
| extern GF_List *allocated_gfios; | |
| GF_EXPORT | |
| void gf_sys_close() | |
| { | |
| if (sys_init > 0) { | |
| void gf_sys_cleanup_help(); | |
| sys_init --; | |
| if (sys_init) return; | |
| /*prevent any call*/ | |
| last_update_time = 0xFFFFFFFF; | |
| #if defined(WIN32) && !defined(_WIN32_WCE) | |
| timeEndPeriod(1); | |
| MyGetSystemTimes = NULL; | |
| MyGetProcessMemoryInfo = NULL; | |
| MyQuerySystemInfo = NULL; | |
| if (psapi_hinst) FreeLibrary(psapi_hinst); | |
| psapi_hinst = NULL; | |
| #endif | |
| gf_sys_enable_rmtws(GF_FALSE); | |
| gf_sys_enable_userws(GF_FALSE); | |
| #ifdef GPAC_HAS_QJS | |
| void gf_js_delete_runtime(); | |
| gf_js_delete_runtime(); | |
| #endif | |
| gf_uninit_global_config(gpac_discard_config); | |
| #ifndef GPAC_DISABLE_LOG | |
| gf_logs_close(); | |
| #endif | |
| if (gpac_lang_file) gf_cfg_del(gpac_lang_file); | |
| gpac_lang_file = NULL; | |
| gf_sys_cleanup_help(); | |
| #ifndef GPAC_DISABLE_THREADS | |
| GF_Mutex *old_log_mx = logs_mx; | |
| logs_mx = NULL; | |
| gf_mx_del(old_log_mx); | |
| #endif | |
| gf_log_reset_extras(); | |
| if (gpac_argv_state) { | |
| gf_free(gpac_argv_state); | |
| gpac_argv_state = NULL; | |
| } | |
| gpac_argc = 0; | |
| gpac_argv = NULL; | |
| memory_at_gpac_startup = 0; | |
| gpac_test_mode = gpac_old_arch = gpac_discard_config = GF_FALSE; | |
| gf_list_del(all_blobs); | |
| all_blobs = NULL; | |
| gf_list_del(gfio_delete_handlers); | |
| gf_list_del(allocated_gfios); | |
| #if !defined(GPAC_DISABLE_NETCAP) && !defined(GPAC_DISABLE_NETWORK) | |
| gf_net_close_capture(); | |
| #endif | |
| #ifdef GPAC_CONFIG_EMSCRIPTEN | |
| fprintf(stderr, "\n\n"); | |
| fflush(stderr); | |
| #endif | |
| } | |
| } | |
| #ifdef GPAC_MEMORY_TRACKING | |
| extern size_t gpac_allocated_memory; | |
| extern size_t gpac_nb_alloc_blocs; | |
| #endif | |
| /*CPU and Memory Usage*/ | |
| #ifdef WIN32 | |
| Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) | |
| { | |
| #if defined(_WIN32_WCE) | |
| THREADENTRY32 tentry; | |
| u64 total_cpu_time, process_cpu_time; | |
| DWORD orig_perm; | |
| #endif | |
| MEMORYSTATUS ms; | |
| u64 creation, exit, kernel, user, process_k_u_time, proc_idle_time, proc_k_u_time; | |
| u32 entry_time; | |
| HANDLE hSnapShot; | |
| gf_assert(sys_init); | |
| if (!rti) return GF_FALSE; | |
| proc_idle_time = proc_k_u_time = process_k_u_time = 0; | |
| entry_time = gf_sys_clock(); | |
| if (last_update_time && (entry_time - last_update_time < refresh_time_ms)) { | |
| memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); | |
| return GF_FALSE; | |
| } | |
| if (flags & GF_RTI_SYSTEM_MEMORY_ONLY) { | |
| memset(rti, 0, sizeof(GF_SystemRTInfo)); | |
| rti->sampling_instant = last_update_time; | |
| GlobalMemoryStatus(&ms); | |
| rti->physical_memory = ms.dwTotalPhys; | |
| rti->physical_memory_avail = ms.dwAvailPhys; | |
| #ifdef GPAC_MEMORY_TRACKING | |
| rti->gpac_memory = (u64) gpac_allocated_memory; | |
| #endif | |
| return GF_TRUE; | |
| } | |
| #if defined (_WIN32_WCE) | |
| total_cpu_time = process_cpu_time = 0; | |
| /*get a snapshot of all running threads*/ | |
| orig_perm = GetCurrentPermissions(); | |
| SetProcPermissions(0xFFFFFFFF); | |
| hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); | |
| if (hSnapShot) { | |
| tentry.dwSize = sizeof(THREADENTRY32); | |
| the_rti.thread_count = 0; | |
| /*note we always act as if GF_RTI_ALL_PROCESSES_TIMES flag is set, since there is no other way | |
| to enumerate threads from a process, and GetProcessTimes doesn't exist on CE*/ | |
| if (Thread32First(hSnapShot, &tentry)) { | |
| do { | |
| /*get thread times*/ | |
| if (GetThreadTimes( (HANDLE) tentry.th32ThreadID, (FILETIME *) &creation, (FILETIME *) &exit, (FILETIME *) &kernel, (FILETIME *) &user)) { | |
| total_cpu_time += user + kernel; | |
| if (tentry.th32OwnerProcessID==the_rti.pid) { | |
| process_cpu_time += user + kernel; | |
| the_rti.thread_count ++; | |
| } | |
| } | |
| } while (Thread32Next(hSnapShot, &tentry)); | |
| } | |
| CloseToolhelp32Snapshot(hSnapShot); | |
| } | |
| if (flags & GF_RTI_PROCESS_MEMORY) { | |
| HEAPLIST32 hlentry; | |
| HEAPENTRY32 hentry; | |
| the_rti.process_memory = 0; | |
| hlentry.dwSize = sizeof(HEAPLIST32); | |
| hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, the_rti.pid); | |
| if (hSnapShot && Heap32ListFirst(hSnapShot, &hlentry)) { | |
| do { | |
| hentry.dwSize = sizeof(hentry); | |
| if (Heap32First(hSnapShot, &hentry, hlentry.th32ProcessID, hlentry.th32HeapID)) { | |
| do { | |
| the_rti.process_memory += hentry.dwBlockSize; | |
| } while (Heap32Next(hSnapShot, &hentry)); | |
| } | |
| } while (Heap32ListNext(hSnapShot, &hlentry)); | |
| } | |
| CloseToolhelp32Snapshot(hSnapShot); | |
| } | |
| SetProcPermissions(orig_perm); | |
| total_cpu_time /= 10; | |
| process_cpu_time /= 10; | |
| #else | |
| /*XP-SP1 and Win2003 servers only have GetSystemTimes support. This will give a better estimation | |
| of CPU usage since we can take into account the idle time*/ | |
| if (MyGetSystemTimes) { | |
| u64 u_time; | |
| MyGetSystemTimes(&proc_idle_time, &proc_k_u_time, &u_time); | |
| proc_k_u_time += u_time; | |
| proc_idle_time /= 10; | |
| proc_k_u_time /= 10; | |
| } | |
| /*same rq for NtQuerySystemInformation*/ | |
| else if (MyQuerySystemInfo) { | |
| DWORD ret; | |
| SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info; | |
| MyQuerySystemInfo(0x8 /*SystemProcessorPerformanceInformation*/, &info, sizeof(info), &ret); | |
| if (ret && (ret<=sizeof(info))) { | |
| proc_idle_time = info.IdleTime.QuadPart / 10; | |
| proc_k_u_time = (info.KernelTime.QuadPart + info.UserTime.QuadPart) / 10; | |
| } | |
| } | |
| /*no special API available, ONLY FETCH TIMES if requested (may eat up some time)*/ | |
| else if (flags & GF_RTI_ALL_PROCESSES_TIMES) { | |
| PROCESSENTRY32 pentry; | |
| /*get a snapshot of all running threads*/ | |
| hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | |
| if (!hSnapShot) return GF_FALSE; | |
| pentry.dwSize = sizeof(PROCESSENTRY32); | |
| if (Process32First(hSnapShot, &pentry)) { | |
| do { | |
| HANDLE procH = NULL; | |
| if (pentry.th32ProcessID) procH = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pentry.th32ProcessID); | |
| if (procH && GetProcessTimes(procH, (FILETIME *) &creation, (FILETIME *) &exit, (FILETIME *) &kernel, (FILETIME *) &user) ) { | |
| user += kernel; | |
| proc_k_u_time += user; | |
| if (pentry.th32ProcessID==the_rti.pid) { | |
| process_k_u_time = user; | |
| //nb_threads = pentry.cntThreads; | |
| } | |
| } | |
| if (procH) CloseHandle(procH); | |
| } while (Process32Next(hSnapShot, &pentry)); | |
| } | |
| CloseHandle(hSnapShot); | |
| proc_k_u_time /= 10; | |
| } | |
| if (!process_k_u_time) { | |
| HANDLE procH = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, the_rti.pid); | |
| if (procH && GetProcessTimes(procH, (FILETIME *) &creation, (FILETIME *) &exit, (FILETIME *) &kernel, (FILETIME *) &user) ) { | |
| process_k_u_time = user + kernel; | |
| } | |
| if (procH) CloseHandle(procH); | |
| if (!process_k_u_time) return GF_FALSE; | |
| } | |
| process_k_u_time /= 10; | |
| /*this won't cost a lot*/ | |
| if (MyGetProcessMemoryInfo) { | |
| PROCESS_MEMORY_COUNTERS pmc; | |
| HANDLE procH = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, the_rti.pid); | |
| MyGetProcessMemoryInfo(procH, &pmc, sizeof (pmc)); | |
| the_rti.process_memory = pmc.WorkingSetSize; | |
| if (procH) CloseHandle(procH); | |
| } | |
| /*THIS IS VERY HEAVY (eats up mem and time) - only perform if requested*/ | |
| else if (flags & GF_RTI_PROCESS_MEMORY) { | |
| HEAPLIST32 hlentry; | |
| HEAPENTRY32 hentry; | |
| the_rti.process_memory = 0; | |
| hlentry.dwSize = sizeof(HEAPLIST32); | |
| hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, the_rti.pid); | |
| if (hSnapShot && Heap32ListFirst(hSnapShot, &hlentry)) { | |
| do { | |
| hentry.dwSize = sizeof(hentry); | |
| if (Heap32First(&hentry, hlentry.th32ProcessID, hlentry.th32HeapID)) { | |
| do { | |
| the_rti.process_memory += hentry.dwBlockSize; | |
| } while (Heap32Next(&hentry)); | |
| } | |
| } while (Heap32ListNext(hSnapShot, &hlentry)); | |
| } | |
| CloseHandle(hSnapShot); | |
| } | |
| #endif | |
| the_rti.sampling_instant = last_update_time; | |
| if (last_update_time) { | |
| the_rti.sampling_period_duration = entry_time - last_update_time; | |
| the_rti.process_cpu_time_diff = (u32) ((process_k_u_time - last_process_k_u_time)/1000); | |
| #if defined(_WIN32_WCE) | |
| the_rti.total_cpu_time_diff = (u32) ((total_cpu_time - last_total_k_u_time)/1000); | |
| /*we're not that accurate....*/ | |
| if (the_rti.total_cpu_time_diff > the_rti.sampling_period_duration) | |
| the_rti.sampling_period_duration = the_rti.total_cpu_time_diff; | |
| /*rough values*/ | |
| the_rti.cpu_idle_time = the_rti.sampling_period_duration - the_rti.total_cpu_time_diff; | |
| if (!the_rti.sampling_period_duration) the_rti.sampling_period_duration=1; | |
| the_rti.total_cpu_usage = (u32) (100 * the_rti.total_cpu_time_diff / the_rti.sampling_period_duration); | |
| if (the_rti.total_cpu_time_diff + the_rti.cpu_idle_time==0) the_rti.total_cpu_time_diff ++; | |
| the_rti.process_cpu_usage = (u32) (100*the_rti.process_cpu_time_diff / (the_rti.total_cpu_time_diff + the_rti.cpu_idle_time) ); | |
| #else | |
| /*oops, we have no choice but to assume 100% cpu usage during this period*/ | |
| if (!proc_k_u_time) { | |
| the_rti.total_cpu_time_diff = the_rti.sampling_period_duration; | |
| proc_k_u_time = last_proc_k_u_time + the_rti.sampling_period_duration; | |
| the_rti.cpu_idle_time = 0; | |
| the_rti.total_cpu_usage = 100; | |
| if (the_rti.sampling_period_duration) | |
| the_rti.process_cpu_usage = (u32) (100*the_rti.process_cpu_time_diff / the_rti.sampling_period_duration); | |
| } else { | |
| u64 samp_sys_time, idle; | |
| the_rti.total_cpu_time_diff = (u32) ((proc_k_u_time - last_proc_k_u_time)/1000); | |
| /*we're not that accurate....*/ | |
| if (the_rti.total_cpu_time_diff > the_rti.sampling_period_duration) { | |
| the_rti.sampling_period_duration = the_rti.total_cpu_time_diff; | |
| } | |
| if (!proc_idle_time) | |
| proc_idle_time = last_proc_idle_time + (the_rti.sampling_period_duration - the_rti.total_cpu_time_diff); | |
| samp_sys_time = proc_k_u_time - last_proc_k_u_time; | |
| idle = proc_idle_time - last_proc_idle_time; | |
| the_rti.cpu_idle_time = (u32) (idle/1000); | |
| if (samp_sys_time) { | |
| the_rti.total_cpu_usage = (u32) ( (samp_sys_time - idle) / (samp_sys_time / 100) ); | |
| the_rti.process_cpu_usage = (u32) (100*the_rti.process_cpu_time_diff / (samp_sys_time/1000)); | |
| } | |
| } | |
| #endif | |
| } | |
| last_update_time = entry_time; | |
| last_process_k_u_time = process_k_u_time; | |
| GlobalMemoryStatus(&ms); | |
| the_rti.physical_memory = ms.dwTotalPhys; | |
| #ifdef GPAC_MEMORY_TRACKING | |
| the_rti.gpac_memory = (u64) gpac_allocated_memory; | |
| #endif | |
| the_rti.physical_memory_avail = ms.dwAvailPhys; | |
| #if defined(_WIN32_WCE) | |
| last_total_k_u_time = total_cpu_time; | |
| if (!the_rti.process_memory && (mem_usage_at_startup >= ms.dwAvailPhys)) | |
| the_rti.process_memory = mem_usage_at_startup - ms.dwAvailPhys; | |
| #else | |
| last_proc_idle_time = proc_idle_time; | |
| last_proc_k_u_time = proc_k_u_time; | |
| #endif | |
| memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); | |
| return GF_TRUE; | |
| } | |
| #elif defined(GPAC_CONFIG_DARWIN) && !defined(GPAC_CONFIG_IOS) | |
| #include <sys/types.h> | |
| #include <sys/sysctl.h> | |
| #include <sys/vmmeter.h> | |
| #include <mach/mach_init.h> | |
| #include <mach/mach_host.h> | |
| #include <mach/mach_port.h> | |
| #include <mach/mach_traps.h> | |
| #include <mach/task_info.h> | |
| #include <mach/thread_info.h> | |
| #include <mach/thread_act.h> | |
| #include <mach/vm_region.h> | |
| #include <mach/vm_map.h> | |
| #include <mach/task.h> | |
| #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 | |
| #include <mach/shared_region.h> | |
| #else | |
| #include <mach/shared_memory_server.h> | |
| #endif | |
| #include <mach/mach_error.h> | |
| static u64 total_physical_memory = 0; | |
| Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) | |
| { | |
| size_t length; | |
| u32 entry_time, i, percent; | |
| int mib[6]; | |
| u64 result; | |
| int pagesize; | |
| u64 process_u_k_time; | |
| double utime, stime; | |
| vm_statistics_data_t vmstat; | |
| task_t task; | |
| kern_return_t error; | |
| thread_array_t thread_table; | |
| unsigned table_size; | |
| thread_basic_info_t thi; | |
| thread_basic_info_data_t thi_data; | |
| struct task_basic_info ti; | |
| mach_msg_type_number_t count = HOST_VM_INFO_COUNT, size = sizeof(ti); | |
| entry_time = gf_sys_clock(); | |
| memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); | |
| if (last_update_time && (entry_time - last_update_time < refresh_time_ms)) { | |
| return 0; | |
| } | |
| mib[0] = CTL_HW; | |
| mib[1] = HW_PAGESIZE; | |
| length = sizeof(pagesize); | |
| if (sysctl(mib, 2, &pagesize, &length, NULL, 0) < 0) { | |
| return 0; | |
| } | |
| if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, &count) != KERN_SUCCESS) { | |
| return 0; | |
| } | |
| the_rti.physical_memory = (vmstat.wire_count + vmstat.active_count + vmstat.inactive_count + vmstat.free_count)* pagesize; | |
| the_rti.physical_memory_avail = vmstat.free_count * pagesize; | |
| if (!total_physical_memory) { | |
| mib[0] = CTL_HW; | |
| mib[1] = HW_MEMSIZE; | |
| length = sizeof(u64); | |
| if (sysctl(mib, 2, &result, &length, NULL, 0) >= 0) { | |
| total_physical_memory = result; | |
| } | |
| } | |
| the_rti.physical_memory = total_physical_memory; | |
| error = task_for_pid(mach_task_self(), the_rti.pid, &task); | |
| if (error) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] Cannot get process task for PID %d: error %d\n", the_rti.pid, error)); | |
| return 0; | |
| } | |
| error = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&ti, &size); | |
| if (error) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] Cannot get process task info (PID %d): error %d\n", the_rti.pid, error)); | |
| return 0; | |
| } | |
| percent = 0; | |
| the_rti.process_memory = ti.resident_size; | |
| Bool gather_th_times = GF_TRUE; | |
| utime = ti.user_time.seconds + ti.user_time.microseconds * 1e-6; | |
| stime = ti.system_time.seconds + ti.system_time.microseconds * 1e-6; | |
| size = TASK_INFO_MAX; | |
| error = task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t)&ti, &size); | |
| if (error == KERN_SUCCESS) { | |
| task_thread_times_info_t ttimes = (task_thread_times_info_t)&ti; | |
| utime = ttimes->user_time.seconds + ttimes->user_time.microseconds * 1e-6; | |
| stime = ttimes->system_time.seconds + ttimes->system_time.microseconds * 1e-6; | |
| gather_th_times = GF_FALSE; | |
| } | |
| error = task_threads(task, &thread_table, &table_size); | |
| if (error != KERN_SUCCESS) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] Cannot get threads task for PID %d: error %d\n", the_rti.pid, error)); | |
| return 0; | |
| } | |
| thi = &thi_data; | |
| for (i = 0; i != table_size; ++i) { | |
| count = THREAD_BASIC_INFO_COUNT; | |
| error = thread_info(thread_table[i], THREAD_BASIC_INFO, (thread_info_t)thi, &count); | |
| if (error != KERN_SUCCESS) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[RTI] Unexpected thread_info error for process %d: %s\n", the_rti.pid, mach_error_string(error) )); | |
| break; | |
| } | |
| if ((thi->flags & TH_FLAGS_IDLE) == 0) { | |
| if (gather_th_times) { | |
| utime += thi->user_time.seconds + thi->user_time.microseconds * 1e-6; | |
| stime += thi->system_time.seconds + thi->system_time.microseconds * 1e-6; | |
| } | |
| percent += (u32) (100 * (double)thi->cpu_usage / TH_USAGE_SCALE); | |
| } | |
| } | |
| vm_deallocate(mach_task_self(), (vm_offset_t)thread_table, table_size * sizeof(thread_array_t)); | |
| mach_port_deallocate(mach_task_self(), task); | |
| process_u_k_time = utime + stime; | |
| the_rti.sampling_instant = last_update_time; | |
| if (last_update_time) { | |
| the_rti.sampling_period_duration = (entry_time - last_update_time); | |
| the_rti.process_cpu_time_diff = (process_u_k_time - last_process_k_u_time) * 10; | |
| the_rti.total_cpu_time_diff = the_rti.sampling_period_duration; | |
| /*TODO*/ | |
| the_rti.cpu_idle_time = 0; | |
| the_rti.total_cpu_usage = 0; | |
| if (!the_rti.process_cpu_time_diff) the_rti.process_cpu_time_diff = the_rti.total_cpu_time_diff; | |
| the_rti.process_cpu_usage = percent; | |
| } else { | |
| mem_at_startup = the_rti.physical_memory_avail; | |
| } | |
| if (!the_rti.process_memory && (mem_at_startup >= the_rti.physical_memory_avail)) | |
| the_rti.process_memory = mem_at_startup - the_rti.physical_memory_avail; | |
| #ifdef GPAC_MEMORY_TRACKING | |
| the_rti.gpac_memory = gpac_allocated_memory; | |
| #endif | |
| last_process_k_u_time = process_u_k_time; | |
| last_cpu_idle_time = 0; | |
| last_update_time = entry_time; | |
| memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); | |
| return 1; | |
| } | |
| //ios | |
| #elif defined(GPAC_CONFIG_IOS) | |
| #import <mach/mach.h> | |
| #import <assert.h> | |
| Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) | |
| { | |
| u64 u_k_time = 0; | |
| kern_return_t kr; | |
| task_info_data_t tinfo; | |
| mach_msg_type_number_t task_info_count; | |
| task_basic_info_t basic_info; | |
| thread_array_t thread_list; | |
| mach_msg_type_number_t thread_count; | |
| thread_info_data_t thinfo; | |
| mach_msg_type_number_t thread_info_count; | |
| thread_basic_info_t basic_info_th; | |
| u32 j, tot_cpu = 0, nb_threads = 0; | |
| u32 entry_time = gf_sys_clock(); | |
| if (last_update_time && (entry_time - last_update_time < refresh_time_ms)) { | |
| memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); | |
| return GF_FALSE; | |
| } | |
| task_info_count = TASK_INFO_MAX; | |
| kr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)tinfo, &task_info_count); | |
| if (kr != KERN_SUCCESS) { | |
| return GF_FALSE; | |
| } | |
| basic_info = (task_basic_info_t)tinfo; | |
| the_rti.gpac_memory = the_rti.process_memory = basic_info->resident_size; | |
| // get threads in the task | |
| kr = task_threads(mach_task_self(), &thread_list, &thread_count); | |
| if (kr != KERN_SUCCESS) { | |
| return GF_FALSE; | |
| } | |
| if (thread_count > 0) | |
| nb_threads = (u32) thread_count; | |
| for (j = 0; j < nb_threads; j++) { | |
| thread_info_count = THREAD_INFO_MAX; | |
| kr = thread_info(thread_list[j], THREAD_BASIC_INFO, | |
| (thread_info_t)thinfo, &thread_info_count); | |
| if (kr != KERN_SUCCESS) { | |
| vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t)); | |
| return GF_FALSE; | |
| } | |
| basic_info_th = (thread_basic_info_t)thinfo; | |
| if (!(basic_info_th->flags & TH_FLAGS_IDLE)) { | |
| u64 tot_sec = basic_info_th->user_time.seconds + basic_info_th->system_time.seconds; | |
| tot_sec *= 1000000; | |
| tot_sec += basic_info_th->user_time.microseconds + basic_info_th->system_time.microseconds; | |
| u_k_time += tot_sec; | |
| tot_cpu += basic_info_th->cpu_usage; | |
| } | |
| } // for each thread | |
| the_rti.process_cpu_usage = (tot_cpu * 100) / TH_USAGE_SCALE; | |
| the_rti.total_cpu_usage = the_rti.process_cpu_usage; | |
| kr = vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t)); | |
| gf_assert(kr == KERN_SUCCESS); | |
| if (last_update_time) { | |
| the_rti.sampling_period_duration = (entry_time - last_update_time); | |
| the_rti.process_cpu_time_diff = 0; | |
| /*move to ms*/ | |
| the_rti.total_cpu_time_diff = (u32) (u_k_time - last_cpu_u_k_time) / 1000; | |
| /*we're not that accurate....*/ | |
| if (the_rti.total_cpu_time_diff > the_rti.sampling_period_duration) | |
| the_rti.sampling_period_duration = the_rti.total_cpu_time_diff; | |
| } else { | |
| mem_at_startup = the_rti.physical_memory_avail; | |
| } | |
| #ifdef GPAC_MEMORY_TRACKING | |
| the_rti.gpac_memory = gpac_allocated_memory; | |
| #endif | |
| last_cpu_u_k_time = u_k_time; | |
| last_update_time = entry_time; | |
| memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); | |
| last_update_time = entry_time; | |
| return GF_TRUE; | |
| } | |
| #else | |
| #ifdef GPAC_CONFIG_EMSCRIPTEN | |
| typedef struct { | |
| int arena; /* non-mmapped space allocated from system */ | |
| int ordblks; /* number of free chunks */ | |
| int smblks; /* always 0 */ | |
| int hblks; /* always 0 */ | |
| int hblkhd; /* space in mmapped regions */ | |
| int usmblks; /* maximum total allocated space */ | |
| int fsmblks; /* always 0 */ | |
| int uordblks; /* total allocated space */ | |
| int fordblks; /* total free space */ | |
| int keepcost; /* releasable (via malloc_trim) space */ | |
| } s_mallinfo; | |
| extern s_mallinfo mallinfo(); | |
| #endif | |
| Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) | |
| { | |
| u32 entry_time; | |
| u64 process_u_k_time; | |
| u32 u_k_time, idle_time; | |
| #ifndef GPAC_CONFIG_EMSCRIPTEN | |
| char szProc[100]; | |
| char line[2048]; | |
| #endif | |
| gf_assert(sys_init); | |
| entry_time = gf_sys_clock(); | |
| if (last_update_time && (entry_time - last_update_time < refresh_time_ms)) { | |
| memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); | |
| return GF_FALSE; | |
| } | |
| u_k_time = idle_time = 0; | |
| #ifndef GPAC_CONFIG_EMSCRIPTEN | |
| FILE *f = gf_fopen("/proc/stat", "r"); | |
| if (f) { | |
| char line[2048]; | |
| u32 k_time, nice_time, u_time; | |
| if (gf_fgets(line, 128, f) != NULL) { | |
| if (sscanf(line, "cpu %u %u %u %u\n", &u_time, &k_time, &nice_time, &idle_time) == 4) { | |
| u_k_time = u_time + k_time + nice_time; | |
| } | |
| } | |
| gf_fclose(f); | |
| } | |
| #endif | |
| process_u_k_time = 0; | |
| the_rti.process_memory = 0; | |
| /*FIXME? under LinuxThreads this will only fetch stats for the calling thread, we would have to enumerate /proc to get | |
| the complete CPU usage of all threads of the process...*/ | |
| #if 0 | |
| sprintf(szProc, "/proc/%d/stat", the_rti.pid); | |
| f = gf_fopen(szProc, "r"); | |
| if (f) { | |
| gf_fflush(f); | |
| if (gf_fgets(line, 2048, f) != NULL) { | |
| char state; | |
| char *start; | |
| long cutime, cstime, priority, nice, itrealvalue, rss; | |
| int exit_signal, processor; | |
| unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime,starttime, vsize, rlim, startcode, endcode, startstack, kstkesp, kstkeip, signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap, rem; | |
| int ppid, pgrp ,session, tty_nr, tty_pgrp, res; | |
| start = strchr(line, ')'); | |
| if (start) start += 2; | |
| else { | |
| start = strchr(line, ' '); | |
| start++; | |
| } | |
| res = sscanf(start,"%c %d %d %d %d %d %lu %lu %lu %lu \ | |
| %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu \ | |
| %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu \ | |
| %lu %lu %lu %lu %lu %d %d", | |
| &state, &ppid, &pgrp, &session, &tty_nr, &tty_pgrp, &flags, &minflt, &cminflt, &majflt, | |
| &cmajflt, &utime, &stime, &cutime, &cstime, &priority, &nice, &itrealvalue, &rem, &starttime, | |
| &vsize, &rss, &rlim, &startcode, &endcode, &startstack, &kstkesp, &kstkeip, &signal, &blocked, | |
| &sigignore, &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor); | |
| if (res) process_u_k_time = (u64) (cutime + cstime); | |
| else { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] PROC %s parse error\n", szProc)); | |
| } | |
| } else { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] error reading pid/stat\n\n", szProc)); | |
| } | |
| gf_fclose(f); | |
| } else { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open %s\n", szProc)); | |
| } | |
| #endif | |
| #ifndef GPAC_CONFIG_EMSCRIPTEN | |
| struct rusage r_usage; | |
| getrusage(RUSAGE_SELF,&r_usage); | |
| the_rti.process_memory = r_usage.ru_maxrss*1024; | |
| if (!the_rti.process_memory) { | |
| sprintf(szProc, "/proc/%d/status", the_rti.pid); | |
| f = gf_fopen(szProc, "r"); | |
| if (f) { | |
| while (gf_fgets(line, 1024, f) != NULL) { | |
| if (!strnicmp(line, "VmRSS:", 7)) { | |
| sscanf(line, "VmRSS: "LLD" kB", &the_rti.process_memory); | |
| the_rti.process_memory *= 1024; | |
| } | |
| } | |
| gf_fclose(f); | |
| } | |
| } | |
| #endif | |
| #ifdef GPAC_CONFIG_EMSCRIPTEN | |
| the_rti.physical_memory = EM_ASM_INT(return HEAP8.length); | |
| s_mallinfo mi = mallinfo(); | |
| the_rti.physical_memory_avail = the_rti.physical_memory - (unsigned int)sbrk(0) + mi.fordblks; | |
| #elif defined(GPAC_CONFIG_FREEBSD) | |
| /* FreeBSD doesn't have /proc/meminfo, would need sysctl to get memory info */ | |
| the_rti.physical_memory = the_rti.physical_memory_avail = 0; | |
| #else | |
| the_rti.physical_memory = the_rti.physical_memory_avail = 0; | |
| f = gf_fopen("/proc/meminfo", "r"); | |
| if (f) { | |
| char line[2048]; | |
| while (gf_fgets(line, 1024, f) != NULL) { | |
| if (!strnicmp(line, "MemTotal:", 9)) { | |
| sscanf(line, "MemTotal: " LLU " kB", &the_rti.physical_memory); | |
| the_rti.physical_memory *= 1024; | |
| } else if (!strnicmp(line, "MemFree:", 8)) { | |
| sscanf(line, "MemFree: " LLU " kB", &the_rti.physical_memory_avail); | |
| the_rti.physical_memory_avail *= 1024; | |
| break; | |
| } | |
| } | |
| gf_fclose(f); | |
| } else { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open /proc/meminfo\n")); | |
| } | |
| #endif | |
| the_rti.sampling_instant = last_update_time; | |
| if (last_update_time) { | |
| the_rti.sampling_period_duration = (entry_time - last_update_time); | |
| the_rti.process_cpu_time_diff = (u32) (process_u_k_time - last_process_k_u_time) * 10; | |
| #ifndef GPAC_CONFIG_EMSCRIPTEN | |
| /*oops, we have no choice but to assume 100% cpu usage during this period*/ | |
| if (!u_k_time) { | |
| the_rti.total_cpu_time_diff = the_rti.sampling_period_duration; | |
| u_k_time = (u32) (last_cpu_u_k_time + the_rti.sampling_period_duration); | |
| the_rti.cpu_idle_time = 0; | |
| the_rti.total_cpu_usage = 100; | |
| if (!the_rti.process_cpu_time_diff) the_rti.process_cpu_time_diff = the_rti.total_cpu_time_diff; | |
| if (the_rti.sampling_period_duration) | |
| the_rti.process_cpu_usage = (u32) ( 100 * the_rti.process_cpu_time_diff / the_rti.sampling_period_duration); | |
| else | |
| the_rti.process_cpu_usage = 100; | |
| } else { | |
| u64 samp_sys_time; | |
| /*move to ms (/proc/stat gives times in 100 ms unit*/ | |
| the_rti.total_cpu_time_diff = (u32) (u_k_time - last_cpu_u_k_time)*10; | |
| /*we're not that accurate....*/ | |
| if (the_rti.total_cpu_time_diff > the_rti.sampling_period_duration) | |
| the_rti.sampling_period_duration = the_rti.total_cpu_time_diff; | |
| if (!idle_time) idle_time = (the_rti.sampling_period_duration - the_rti.total_cpu_time_diff)/10; | |
| samp_sys_time = u_k_time - last_cpu_u_k_time; | |
| the_rti.cpu_idle_time = (u32) (idle_time - last_cpu_idle_time); | |
| if (the_rti.cpu_idle_time + samp_sys_time > 0) | |
| the_rti.total_cpu_usage = (u32) ( 100 * samp_sys_time / (the_rti.cpu_idle_time + samp_sys_time ) ); | |
| else | |
| the_rti.total_cpu_usage = 0; | |
| /*move to ms (/proc/stat gives times in 100 ms unit*/ | |
| the_rti.cpu_idle_time *= 10; | |
| if (!the_rti.process_cpu_time_diff) the_rti.process_cpu_time_diff = the_rti.total_cpu_time_diff; | |
| if (the_rti.cpu_idle_time + 10*samp_sys_time > 0) | |
| the_rti.process_cpu_usage = (u32) ( 100 * the_rti.process_cpu_time_diff / (the_rti.cpu_idle_time + 10*samp_sys_time ) ); | |
| else | |
| the_rti.process_cpu_usage = 0; | |
| } | |
| #endif // GPAC_CONFIG_EMSCRIPTEN | |
| } else { | |
| mem_at_startup = the_rti.physical_memory_avail; | |
| } | |
| if (!the_rti.process_memory && (mem_at_startup >= the_rti.physical_memory_avail)) | |
| the_rti.process_memory = mem_at_startup - the_rti.physical_memory_avail; | |
| #ifdef GPAC_MEMORY_TRACKING | |
| the_rti.gpac_memory = gpac_allocated_memory; | |
| #endif | |
| last_process_k_u_time = process_u_k_time; | |
| last_cpu_idle_time = idle_time; | |
| last_cpu_u_k_time = u_k_time; | |
| last_update_time = entry_time; | |
| memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); | |
| return GF_TRUE; | |
| } | |
| #endif | |
| GF_EXPORT | |
| Bool gf_sys_get_rti(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) | |
| { | |
| if (gpac_disable_rti) return GF_FALSE; | |
| Bool res = gf_sys_get_rti_os(refresh_time_ms, rti, flags); | |
| if (res && memory_at_gpac_startup) { | |
| if (!rti->process_memory && (memory_at_gpac_startup > rti->physical_memory_avail)) | |
| rti->process_memory = memory_at_gpac_startup - rti->physical_memory_avail; | |
| if (!rti->gpac_memory) | |
| rti->gpac_memory = rti->process_memory; | |
| } | |
| return res; | |
| } | |
| static char szCacheDir[GF_MAX_PATH]; | |
| const char * gf_get_default_cache_directory_ex(Bool do_create) | |
| { | |
| const char *cache_dir; | |
| char root_tmp[GF_MAX_PATH]; | |
| size_t len; | |
| cache_dir = gf_opts_get_key("core", "cache"); | |
| if (cache_dir) return cache_dir; | |
| #ifdef _WIN32_WCE | |
| gf_strcpy(szCacheDir, "\\windows\\temp" ); | |
| #elif defined(WIN32) | |
| GetTempPath(GF_MAX_PATH, szCacheDir); | |
| #elif defined(GPAC_CONFIG_ANDROID) | |
| gf_strcpy(szCacheDir, "/data/data/io.gpac.gpac/cache"); | |
| #else | |
| gf_strcpy(szCacheDir, "/tmp"); | |
| #endif | |
| gf_strcpy(root_tmp, szCacheDir); | |
| len = strlen(szCacheDir); | |
| if (szCacheDir[len-1] != GF_PATH_SEPARATOR) { | |
| szCacheDir[len] = GF_PATH_SEPARATOR; | |
| szCacheDir[len+1] = 0; | |
| } | |
| gf_strcat(szCacheDir, "gpac_cache"); | |
| if (do_create && !gf_dir_exists(szCacheDir) && gf_mkdir(szCacheDir)!=GF_OK ) { | |
| gf_strcpy(szCacheDir, root_tmp); | |
| return szCacheDir; | |
| } | |
| return szCacheDir; | |
| } | |
| GF_EXPORT | |
| const char * gf_get_default_cache_directory() | |
| { | |
| return gf_get_default_cache_directory_ex(GF_TRUE); | |
| } | |
| GF_EXPORT | |
| Bool gf_sys_get_battery_state(Bool *onBattery, u32 *onCharge, u32*level, u32 *batteryLifeTime, u32 *batteryFullLifeTime) | |
| { | |
| #if defined(_WIN32_WCE) | |
| SYSTEM_POWER_STATUS_EX sps; | |
| GetSystemPowerStatusEx(&sps, 0); | |
| if (onBattery) *onBattery = sps.ACLineStatus ? 0 : 1; | |
| if (onCharge) *onCharge = (sps.BatteryFlag & BATTERY_FLAG_CHARGING) ? 1 : 0; | |
| if (level) *level = sps.BatteryLifePercent; | |
| if (batteryLifeTime) *batteryLifeTime = sps.BatteryLifeTime; | |
| if (batteryFullLifeTime) *batteryFullLifeTime = sps.BatteryFullLifeTime; | |
| #elif defined(WIN32) | |
| SYSTEM_POWER_STATUS sps; | |
| GetSystemPowerStatus(&sps); | |
| if (onBattery) *onBattery = sps.ACLineStatus ? GF_FALSE : GF_TRUE; | |
| if (onCharge) *onCharge = (sps.BatteryFlag & BATTERY_FLAG_CHARGING) ? 1 : 0; | |
| if (level) *level = sps.BatteryLifePercent; | |
| if (batteryLifeTime) *batteryLifeTime = sps.BatteryLifeTime; | |
| if (batteryFullLifeTime) *batteryFullLifeTime = sps.BatteryFullLifeTime; | |
| #endif | |
| return GF_TRUE; | |
| } | |
| #if 0 //global lock currently not used | |
| struct GF_GlobalLock { | |
| const char * resourceName; | |
| }; | |
| #ifndef WIN32 | |
| #define CPF_CLOEXEC 1 | |
| #include <sys/stat.h> | |
| #include <fcntl.h> | |
| #include <unistd.h> | |
| struct _GF_GlobalLock_opaque { | |
| char * resourceName; | |
| char * pidFile; | |
| int fd; | |
| }; | |
| GF_GlobalLock * gf_create_PID_file( const char * resourceName ) | |
| { | |
| const char * prefix = "/gpac_lock_"; | |
| const char * dir = gf_get_default_cache_directory(); | |
| char * pidfile; | |
| int flags; | |
| int status; | |
| pidfile = gf_malloc(strlen(dir)+strlen(prefix)+strlen(resourceName)+1); | |
| gf_strcpy(pidfile, dir); | |
| gf_strcat(pidfile, prefix); | |
| /* Use only valid names for file */ | |
| { | |
| const char *res; | |
| char * pid = &(pidfile[strlen(pidfile)]); | |
| for (res = resourceName; *res ; res++) { | |
| if (*res >= 'A' && *res <= 'z') | |
| *pid = * res; | |
| else | |
| *pid = '_'; | |
| pid++; | |
| } | |
| *pid = '\0'; | |
| } | |
| int fd = open(pidfile, O_RDWR | O_CREAT , S_IRUSR | S_IWUSR); | |
| if (fd == -1) | |
| goto exit; | |
| /* Get the flags */ | |
| flags = fcntl(fd, F_GETFD); | |
| if (flags == -1) { | |
| goto exit; | |
| } | |
| /* Set FD_CLOEXEC, so exclusive lock will be removed on exit, so even if GPAC crashes, | |
| * lock will be allowed for next instance */ | |
| flags |= FD_CLOEXEC; | |
| /* Now, update the flags */ | |
| if (fcntl(fd, F_SETFD, flags) == -1) { | |
| goto exit; | |
| } | |
| /* Now, we try to lock the file */ | |
| { | |
| struct flock fl; | |
| fl.l_type = F_WRLCK; | |
| fl.l_whence = SEEK_SET; | |
| fl.l_start = fl.l_len = 0; | |
| status = fcntl(fd, F_SETLK, &fl); | |
| } | |
| if (status == -1) { | |
| goto exit; | |
| } | |
| if (ftruncate(fd, 0) == -1) { | |
| goto exit; | |
| } | |
| /* Write the PID */ | |
| { | |
| int sz = 100; | |
| char * buf = gf_malloc( sz ); | |
| sz = snprintf(buf, sz, "%ld\n", (long) getpid()); | |
| if (write(fd, buf, sz) != sz) { | |
| gf_free(buf); | |
| goto exit; | |
| } | |
| } | |
| sync(); | |
| { | |
| GF_GlobalLock * lock = gf_malloc( sizeof(GF_GlobalLock)); | |
| lock->resourceName = gf_strdup(resourceName); | |
| lock->pidFile = pidfile; | |
| lock->fd = fd; | |
| return lock; | |
| } | |
| exit: | |
| if (fd >= 0) | |
| close(fd); | |
| return NULL; | |
| } | |
| #else /* WIN32 */ | |
| struct _GF_GlobalLock_opaque { | |
| char * resourceName; | |
| HANDLE hMutex; /*a named mutex is a system-mode object on windows*/ | |
| }; | |
| #endif | |
| GF_EXPORT | |
| GF_GlobalLock * gf_global_resource_lock(const char * resourceName) { | |
| #ifdef WIN32 | |
| #ifdef _WIN32_WCE | |
| unsigned short sWResourceName[MAX_PATH]; | |
| #endif | |
| DWORD lastErr; | |
| GF_GlobalLock *lock = gf_malloc(sizeof(GF_GlobalLock)); | |
| lock->resourceName = gf_strdup(resourceName); | |
| /*first ensure mutex is created*/ | |
| #ifdef _WIN32_WCE | |
| CE_CharToWide((char *)resourceName, sWResourceName); | |
| lock->hMutex = CreateMutex(NULL, TRUE, sWResourceName); | |
| #else | |
| lock->hMutex = CreateMutex(NULL, TRUE, resourceName); | |
| #endif | |
| lastErr = GetLastError(); | |
| if (lastErr && lastErr == ERROR_ALREADY_EXISTS) | |
| return NULL; | |
| if (!lock->hMutex) | |
| { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't create mutex for global lock: %d\n", lastErr)); | |
| return NULL; | |
| } | |
| /*then lock it*/ | |
| switch (WaitForSingleObject(lock->hMutex, INFINITE)) { | |
| case WAIT_ABANDONED: | |
| case WAIT_TIMEOUT: | |
| gf_assert(0); /*serious error: someone has modified the object elsewhere*/ | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't get the global lock\n")); | |
| gf_global_resource_unlock(lock); | |
| return NULL; | |
| } | |
| return lock; | |
| #else /* WIN32 */ | |
| return gf_create_PID_file(resourceName); | |
| #endif /* WIN32 */ | |
| } | |
| /*! | |
| * Unlock a previouly locked resource | |
| \param lock The resource to unlock | |
| \param GF_OK if evertything went fine | |
| */ | |
| GF_EXPORT | |
| GF_Err gf_global_resource_unlock(GF_GlobalLock * lock) { | |
| if (!lock) | |
| return GF_BAD_PARAM; | |
| #ifndef WIN32 | |
| gf_assert( lock->pidFile); | |
| close(lock->fd); | |
| if (unlink(lock->pidFile)) | |
| perror("Failed to unlink lock file"); | |
| gf_free(lock->pidFile); | |
| lock->pidFile = NULL; | |
| lock->fd = -1; | |
| #else /* WIN32 */ | |
| { | |
| /*MSDN: "The mutex object is destroyed when its last handle has been closed."*/ | |
| BOOL ret = ReleaseMutex(lock->hMutex); | |
| if (!ret) { | |
| DWORD err = GetLastError(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't release mutex for global lock: %d\n", err)); | |
| } | |
| ret = CloseHandle(lock->hMutex); | |
| if (!ret) { | |
| DWORD err = GetLastError(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't destroy mutex for global lock: %d\n", err)); | |
| } | |
| } | |
| #endif | |
| if (lock->resourceName) | |
| gf_free(lock->resourceName); | |
| lock->resourceName = NULL; | |
| gf_free(lock); | |
| return GF_OK; | |
| } | |
| #endif //global lock uni-used | |
| static u32 ntp_shift = GF_NTP_SEC_1900_TO_1970; | |
| GF_EXPORT | |
| void gf_net_set_ntp_shift(s32 shift) | |
| { | |
| ntp_shift = GF_NTP_SEC_1900_TO_1970 + shift; | |
| } | |
| /* | |
| NTP tools | |
| */ | |
| GF_EXPORT | |
| void gf_net_get_ntp(u32 *sec, u32 *frac) | |
| { | |
| u64 frac_part; | |
| struct timeval now; | |
| gettimeofday(&now, NULL); | |
| if (sec) { | |
| *sec = (u32) (now.tv_sec) + ntp_shift; | |
| } | |
| if (frac) { | |
| frac_part = now.tv_usec * 0xFFFFFFFFULL; | |
| frac_part /= 1000000; | |
| *frac = (u32) ( frac_part ); | |
| } | |
| } | |
| GF_EXPORT | |
| u64 gf_net_get_ntp_ts() | |
| { | |
| u64 res; | |
| u32 sec, frac; | |
| gf_net_get_ntp(&sec, &frac); | |
| res = sec; | |
| res<<= 32; | |
| res |= frac; | |
| return res; | |
| } | |
| GF_EXPORT | |
| s32 gf_net_ntp_diff_ms(u64 ntp_a, u64 ntp_b) | |
| { | |
| u32 ntp_a_s, ntp_a_f, ntp_b_s, ntp_b_f; | |
| s64 ntp_a_ms, ntp_b_ms; | |
| ntp_a_s = (ntp_a >> 32); | |
| ntp_a_f = (u32) (ntp_a & 0xFFFFFFFFULL); | |
| ntp_b_s = (ntp_b >> 32); | |
| ntp_b_f = (u32) (ntp_b & 0xFFFFFFFFULL); | |
| ntp_a_ms = ntp_a_s; | |
| ntp_a_ms *= 1000; | |
| ntp_a_ms += ((u64) ntp_a_f)*1000 / 0xFFFFFFFFULL; | |
| ntp_b_ms = ntp_b_s; | |
| ntp_b_ms *= 1000; | |
| ntp_b_ms += ((u64) ntp_b_f)*1000 / 0xFFFFFFFFULL; | |
| return (s32) (ntp_a_ms - ntp_b_ms); | |
| } | |
| GF_EXPORT | |
| s32 gf_net_get_ntp_diff_ms(u64 ntp) | |
| { | |
| u32 remote_s, remote_f, local_s, local_f; | |
| s64 local, remote; | |
| remote_s = (ntp >> 32); | |
| remote_f = (u32) (ntp & 0xFFFFFFFFULL); | |
| gf_net_get_ntp(&local_s, &local_f); | |
| local = local_s; | |
| local *= 1000; | |
| local += ((u64) local_f)*1000 / 0xFFFFFFFFULL; | |
| remote = remote_s; | |
| remote *= 1000; | |
| remote += ((u64) remote_f)*1000 / 0xFFFFFFFFULL; | |
| return (s32) (local - remote); | |
| } | |
| /*! | |
| Adds or remove a given amount of microseconds to an NTP timestamp | |
| \param ntp NTP timestamp | |
| \param usec microseconds to add/remove | |
| \return adjusted NTP timestamp | |
| */ | |
| GF_EXPORT | |
| u64 gf_net_ntp_add_usec(u64 ntp, s32 usec) | |
| { | |
| u64 sec, frac; | |
| s64 usec_ntp; | |
| sec = (ntp >> 32); | |
| frac = (ntp & 0xFFFFFFFFULL); | |
| usec_ntp = (s64) ( frac*1000000 / 0xFFFFFFFFULL ); | |
| usec_ntp += usec; | |
| while (usec_ntp > 1000000) { | |
| usec_ntp -= 1000000; | |
| sec += 1; | |
| } | |
| while (usec_ntp < 0) { | |
| usec_ntp += 1000000; | |
| sec -= 1; | |
| } | |
| ntp = ( usec_ntp * 0xFFFFFFFFULL / 1000000 ) & 0xFFFFFFFFULL; | |
| ntp |= (sec<<32); | |
| return ntp; | |
| } | |
| GF_EXPORT | |
| u64 gf_net_get_ntp_ms() | |
| { | |
| u32 sec, frac; | |
| u64 time_ms; | |
| Double msec; | |
| gf_net_get_ntp(&sec, &frac); | |
| time_ms = sec; | |
| time_ms *= 1000; | |
| msec = frac*1000.0; | |
| msec /= 0xFFFFFFFF; | |
| time_ms += (u32)msec; | |
| return time_ms; | |
| } | |
| GF_EXPORT | |
| s32 gf_net_get_timezone() | |
| { | |
| #if defined(_WIN32_WCE) | |
| return 0; | |
| #else | |
| //this has been commented due to some reports of broken implementation on some systems ... | |
| // s32 val = timezone; | |
| // return val; | |
| /*FIXME - avoid errors at midnight when estimating timezone this does not work !!*/ | |
| s32 t_timezone; | |
| struct tm t_gmt, t_local; | |
| time_t t_time; | |
| t_time = time(NULL); | |
| t_gmt = *gf_gmtime(&t_time); | |
| t_local = *localtime(&t_time); | |
| t_timezone = (t_gmt.tm_hour - t_local.tm_hour) * 3600 + (t_gmt.tm_min - t_local.tm_min) * 60; | |
| return t_timezone; | |
| #endif | |
| } | |
| GF_EXPORT | |
| Bool gf_net_time_is_dst() | |
| { | |
| #if defined(_WIN32_WCE) | |
| return GF_FALSE; | |
| #else | |
| struct tm t_local; | |
| time_t t_time; | |
| t_time = time(NULL); | |
| t_local = *localtime(&t_time); | |
| return t_local.tm_isdst ? GF_TRUE : GF_FALSE; | |
| #endif | |
| } | |
| //no mkgmtime on mingw..., use our own | |
| #if (defined(WIN32) && defined(__GNUC__)) | |
| static Bool leap_year(u32 year) { | |
| year += 1900; | |
| return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0) ? GF_TRUE : GF_FALSE; | |
| } | |
| static time_t gf_mktime_utc(struct tm *tm) | |
| { | |
| static const u32 days_per_month[2][12] = { | |
| {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, | |
| {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} | |
| }; | |
| time_t time=0; | |
| int i; | |
| for (i=70; i<tm->tm_year; i++) { | |
| time += leap_year(i) ? 366 : 365; | |
| } | |
| for (i=0; i<tm->tm_mon; ++i) { | |
| time += days_per_month[leap_year(tm->tm_year)][i]; | |
| } | |
| time += tm->tm_mday - 1; | |
| time *= 24; | |
| time += tm->tm_hour; | |
| time *= 60; | |
| time += tm->tm_min; | |
| time *= 60; | |
| time += tm->tm_sec; | |
| return time; | |
| } | |
| #elif defined(WIN32) | |
| static time_t gf_mktime_utc(struct tm *tm) | |
| { | |
| return _mkgmtime(tm); | |
| } | |
| #elif defined(GPAC_CONFIG_ANDROID) | |
| #if defined(__LP64__) | |
| static time_t gf_mktime_utc(struct tm *tm) | |
| { | |
| return timegm(tm); | |
| } | |
| #else | |
| #include <time64.h> | |
| static time_t gf_mktime_utc(struct tm *tm) | |
| { | |
| static const time_t kTimeMax = ~(1L << (sizeof(time_t) * CHAR_BIT - 1)); | |
| static const time_t kTimeMin = (1L << (sizeof(time_t) * CHAR_BIT - 1)); | |
| time64_t result = timegm64(tm); | |
| if (result < kTimeMin || result > kTimeMax) | |
| return -1; | |
| return result; | |
| } | |
| #endif | |
| #else | |
| static time_t gf_mktime_utc(struct tm *tm) | |
| { | |
| return timegm(tm); | |
| } | |
| #endif | |
| GF_EXPORT | |
| u64 gf_net_parse_date(const char *val) | |
| { | |
| u64 current_time; | |
| char szDay[50], szMonth[50]; | |
| u32 year, month, day, h, m, s, ms; | |
| s32 oh, om; | |
| Float secs; | |
| Bool neg_time_zone = GF_FALSE; | |
| #ifdef _WIN32_WCE | |
| SYSTEMTIME syst; | |
| FILETIME filet; | |
| #else | |
| struct tm t; | |
| memset(&t, 0, sizeof(struct tm)); | |
| #endif | |
| szDay[0] = szMonth[0] = 0; | |
| year = month = day = h = m = s = 0; | |
| oh = om = 0; | |
| secs = 0; | |
| Bool has_sep = strchr(val, ':') ? GF_TRUE : GF_FALSE; | |
| if (sscanf(val, "%d-%d-%dT%d:%d:%g-%d:%d", &year, &month, &day, &h, &m, &secs, &oh, &om) == 8) { | |
| neg_time_zone = GF_TRUE; | |
| } | |
| else if (sscanf(val, "%d-%d-%dT%d:%d:%g+%d:%d", &year, &month, &day, &h, &m, &secs, &oh, &om) == 8) { | |
| } | |
| else if (sscanf(val, "%d-%d-%dT%d:%d:%gZ", &year, &month, &day, &h, &m, &secs) == 6) { | |
| } | |
| else if (sscanf(val, "%d/%d/%dT%d:%d:%gZ", &year, &month, &day, &h, &m, &secs) == 6) { | |
| } | |
| else if (sscanf(val, "%3s, %d %3s %d %d:%d:%d", szDay, &day, szMonth, &year, &h, &m, &s)==7) { | |
| secs = (Float) s; | |
| } | |
| else if (sscanf(val, "%9s, %d-%3s-%d %02d:%02d:%02d GMT", szDay, &day, szMonth, &year, &h, &m, &s)==7) { | |
| secs = (Float) s; | |
| } | |
| else if (sscanf(val, "%3s %3s %d %02d:%02d:%02d %d", szDay, szMonth, &day, &year, &h, &m, &s)==7) { | |
| secs = (Float) s; | |
| } | |
| else if (!has_sep && (sscanf(val, LLU, ¤t_time) == 1) && current_time > 1000000000000ULL && current_time < GF_INT_MAX * 1000ULL) { | |
| return current_time; // guessed duration since UTC0 in milliseconds | |
| } | |
| else if (!has_sep && (sscanf(val, LLU, ¤t_time) == 1) && current_time < GF_INT_MAX) { | |
| return current_time * 1000; // guessed raw duration since UTC0 in seconds | |
| } else { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot parse date string %s\n", val)); | |
| return 0; | |
| } | |
| if (month) { | |
| month -= 1; | |
| } else { | |
| if (!strcmp(szMonth, "Jan")) month = 0; | |
| else if (!strcmp(szMonth, "Feb")) month = 1; | |
| else if (!strcmp(szMonth, "Mar")) month = 2; | |
| else if (!strcmp(szMonth, "Apr")) month = 3; | |
| else if (!strcmp(szMonth, "May")) month = 4; | |
| else if (!strcmp(szMonth, "Jun")) month = 5; | |
| else if (!strcmp(szMonth, "Jul")) month = 6; | |
| else if (!strcmp(szMonth, "Aug")) month = 7; | |
| else if (!strcmp(szMonth, "Sep")) month = 8; | |
| else if (!strcmp(szMonth, "Oct")) month = 9; | |
| else if (!strcmp(szMonth, "Nov")) month = 10; | |
| else if (!strcmp(szMonth, "Dec")) month = 11; | |
| } | |
| #ifdef _WIN32_WCE | |
| memset(&syst, 0, sizeof(SYSTEMTIME)); | |
| syst.wYear = year; | |
| syst.wMonth = month + 1; | |
| syst.wDay = day; | |
| syst.wHour = h; | |
| syst.wMinute = m; | |
| syst.wSecond = (u32) secs; | |
| SystemTimeToFileTime(&syst, &filet); | |
| current_time = (u64) ((*(LONGLONG *) &filet - TIMESPEC_TO_FILETIME_OFFSET) / 10000000); | |
| #else | |
| t.tm_year = year>1000 ? year-1900 : year; | |
| t.tm_mday = day; | |
| t.tm_hour = h; | |
| t.tm_min = m; | |
| t.tm_sec = (u32) secs; | |
| t.tm_mon = month; | |
| if (strlen(szDay) ) { | |
| if (!strcmp(szDay, "Mon") || !strcmp(szDay, "Monday")) t.tm_wday = 0; | |
| else if (!strcmp(szDay, "Tue") || !strcmp(szDay, "Tuesday")) t.tm_wday = 1; | |
| else if (!strcmp(szDay, "Wed") || !strcmp(szDay, "Wednesday")) t.tm_wday = 2; | |
| else if (!strcmp(szDay, "Thu") || !strcmp(szDay, "Thursday")) t.tm_wday = 3; | |
| else if (!strcmp(szDay, "Fri") || !strcmp(szDay, "Friday")) t.tm_wday = 4; | |
| else if (!strcmp(szDay, "Sat") || !strcmp(szDay, "Saturday")) t.tm_wday = 5; | |
| else if (!strcmp(szDay, "Sun") || !strcmp(szDay, "Sunday")) t.tm_wday = 6; | |
| } | |
| current_time = gf_mktime_utc(&t); | |
| if ((s64) current_time == -1) { | |
| //use 1 ms | |
| return 1; | |
| } | |
| if (current_time == 0 && secs == 0.0) { | |
| //use 1 ms | |
| return 1; | |
| } | |
| #endif | |
| if (om || oh) { | |
| s32 diff = (60*oh + om)*60; | |
| if (!neg_time_zone) diff = -diff; | |
| current_time = current_time + diff; | |
| } | |
| current_time *= 1000; | |
| ms = (u32) ( (secs - (u32) secs) * 1000); | |
| return current_time + ms; | |
| } | |
| GF_EXPORT | |
| u64 gf_net_get_utc_ts(u32 year, u32 month, u32 day, u32 hour, u32 min, u32 sec) | |
| { | |
| u64 current_time; | |
| #ifdef _WIN32_WCE | |
| SYSTEMTIME syst; | |
| FILETIME filet; | |
| #else | |
| struct tm t; | |
| memset(&t, 0, sizeof(struct tm)); | |
| #endif | |
| #ifdef _WIN32_WCE | |
| memset(&syst, 0, sizeof(SYSTEMTIME)); | |
| syst.wYear = year; | |
| syst.wMonth = month + 1; | |
| syst.wDay = day; | |
| syst.wHour = hour; | |
| syst.wMinute = min; | |
| syst.wSecond = (u32) sec; | |
| SystemTimeToFileTime(&syst, &filet); | |
| current_time = (u64) ((*(LONGLONG *) &filet - TIMESPEC_TO_FILETIME_OFFSET) / 10000000); | |
| #else | |
| t.tm_year = year>1000 ? year-1900 : year; | |
| t.tm_mday = day; | |
| t.tm_hour = hour; | |
| t.tm_min = min; | |
| t.tm_sec = (u32) sec; | |
| t.tm_mon = month; | |
| current_time = gf_mktime_utc(&t); | |
| if ((s64) current_time == -1) { | |
| //use 1 ms | |
| return 1; | |
| } | |
| if (current_time == 0) { | |
| //use 1 ms | |
| return 1; | |
| } | |
| #endif | |
| current_time *= 1000; | |
| return current_time; | |
| } | |
| GF_EXPORT | |
| u64 gf_net_ntp_to_utc(u64 ntp) | |
| { | |
| u64 current_time; | |
| Double msec; | |
| u32 sec = ntp>>32; | |
| u32 frac = ntp & 0xFFFFFFFFUL; | |
| current_time = sec - GF_NTP_SEC_1900_TO_1970; | |
| current_time *= 1000; | |
| msec = frac*1000.0; | |
| msec /= 0xFFFFFFFF; | |
| current_time += (u64) msec; | |
| return current_time; | |
| } | |
| GF_EXPORT | |
| u64 gf_net_get_utc() | |
| { | |
| u64 current_time; | |
| Double msec; | |
| u32 sec, frac; | |
| gf_net_get_ntp(&sec, &frac); | |
| current_time = sec - GF_NTP_SEC_1900_TO_1970; | |
| current_time *= 1000; | |
| msec = frac*1000.0; | |
| msec /= 0xFFFFFFFF; | |
| current_time += (u64) msec; | |
| return current_time; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_bin128_parse(const char *string, bin128 value) | |
| { | |
| u32 len; | |
| u32 i=0; | |
| if (!strnicmp(string, "0x", 2)) string += 2; | |
| len = (u32) strlen(string); | |
| if (len >= 32) { | |
| u32 j; | |
| for (j=0; j<len; j+=2) { | |
| u32 v; | |
| char szV[5]; | |
| while (string[j] && !isalnum(string[j])) | |
| j++; | |
| if (!string[j]) | |
| break; | |
| sprintf(szV, "%c%c", string[j], string[j+1]); | |
| sscanf(szV, "%x", &v); | |
| if (i > 15) { | |
| // force error check below | |
| i++; | |
| break; | |
| } | |
| value[i] = v; | |
| i++; | |
| } | |
| } | |
| if (i != 16) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[CORE] 128bit blob is not 16-bytes long: %s\n", string)); | |
| return GF_BAD_PARAM; | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_file_load_data_filep(FILE *file, u8 **out_data, u32 *out_size) | |
| { | |
| u64 fsize; | |
| *out_data = NULL; | |
| *out_size = 0; | |
| fsize = gf_fsize(file); | |
| if (fsize>0xFFFFFFFFUL) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ( "[Core] file %s is too big to load in memory (" LLU " bytes)\n", fsize) ); | |
| return GF_OUT_OF_MEM; | |
| } | |
| *out_size = (u32) fsize; | |
| if (fsize == 0) { | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[Core] file is empty\n")); | |
| return GF_OK; | |
| } | |
| /* First, read the dump in a buffer */ | |
| *out_data = gf_malloc((size_t)(fsize+1) * sizeof(char)); | |
| if (! *out_data) { | |
| return GF_OUT_OF_MEM; | |
| } | |
| fsize = gf_fread(*out_data, (size_t)fsize, file); | |
| if ((u32) fsize != *out_size) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] file read failed\n")); | |
| gf_free(*out_data); | |
| *out_data = NULL; | |
| *out_size = 0; | |
| return GF_IO_ERR; | |
| } | |
| (*out_data)[fsize] = 0; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_file_load_data(const char *file_name, u8 **out_data, u32 *out_size) | |
| { | |
| GF_Err e; | |
| FILE *file = gf_fopen(file_name, "rb"); | |
| if (!file) { | |
| if (!gf_file_exists(file_name)) | |
| return GF_URL_ERROR; | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot open file %s\n", file_name)); | |
| return GF_IO_ERR; | |
| } | |
| e = gf_file_load_data_filep(file, out_data, out_size); | |
| gf_fclose(file); | |
| return e; | |
| } | |
| #ifndef WIN32 | |
| #include <unistd.h> | |
| #include <fcntl.h> | |
| GF_EXPORT | |
| u32 gf_sys_get_process_id() | |
| { | |
| return getpid (); | |
| } | |
| GF_EXPORT | |
| Bool gf_sys_check_process_id(u32 pid) | |
| { | |
| return (getpgid(pid) == -1) ? GF_FALSE : GF_TRUE; | |
| } | |
| #else | |
| #include <windows.h> | |
| GF_EXPORT | |
| u32 gf_sys_get_process_id() | |
| { | |
| return GetCurrentProcessId(); | |
| } | |
| GF_EXPORT | |
| Bool gf_sys_check_process_id(u32 pid) | |
| { | |
| HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); | |
| Bool ret = GF_FALSE; | |
| if (processHandle) { | |
| CloseHandle(processHandle); | |
| ret = GF_TRUE; | |
| } | |
| return ret; | |
| } | |
| #endif | |
| GF_EXPORT | |
| GF_LockStatus gf_sys_create_lockfile(const char *lockname) | |
| { | |
| char szPID[20]; | |
| u32 retry = 10; | |
| sprintf(szPID, "%u", gf_sys_get_process_id()); | |
| u32 len = (u32)strlen(szPID); | |
| while (retry) { | |
| retry--; | |
| #ifdef GPAC_HAS_FD | |
| s32 fd = open(lockname, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, S_IRUSR|S_IWUSR); | |
| if (fd != -1) { | |
| s32 wlen = write(fd, szPID, len); | |
| close(fd); | |
| if (wlen == (s32)len) return GF_LOCKFILE_NEW; | |
| continue; | |
| } | |
| #else | |
| #if defined __STDC_VERSION__ | |
| FILE *f = fopen(lockname, "wx"); | |
| #else | |
| FILE *f = gf_file_exists(lockname) ? NULL : fopen(lockname, "w"); | |
| #endif | |
| if (f) { | |
| s32 wlen = (s32) fwrite(szPID, 1, len, f); | |
| fclose(f); | |
| if (wlen == (s32)len) return GF_LOCKFILE_NEW; | |
| continue; | |
| } | |
| #endif | |
| //existing, check pid | |
| u8 *data=NULL; | |
| u32 size=0, pid; | |
| GF_Err e = gf_file_load_data(lockname, &data, &size); | |
| if (!data || !size || e!=GF_OK) continue; | |
| sscanf(data, "%u", &pid); | |
| gf_free(data); | |
| //already locked by ourselves | |
| if (pid == gf_sys_get_process_id()) return GF_LOCKFILE_REUSE; | |
| //pid is alive, lock fail | |
| if (gf_sys_check_process_id(pid)) return GF_LOCKFILE_FAILED; | |
| //file exists but pid dead, grab the lock | |
| gf_file_delete(lockname); | |
| } | |
| return GF_LOCKFILE_FAILED; | |
| } | |
| #ifndef WIN32 | |
| #include <termios.h> | |
| GF_EXPORT | |
| int gf_getch() | |
| { | |
| struct termios old; | |
| struct termios new; | |
| int rc; | |
| #ifdef GPAC_ENABLE_COVERAGE | |
| if (gf_sys_is_cov_mode()) return 0; | |
| #endif | |
| if (tcgetattr(0, &old) == -1) { | |
| return -1; | |
| } | |
| new = old; | |
| new.c_lflag &= ~(ICANON | ECHO | ISIG); | |
| new.c_cc[VMIN] = 1; | |
| new.c_cc[VTIME] = 0; | |
| if (tcsetattr(0, TCSANOW, &new) == -1) { | |
| return -1; | |
| } | |
| rc = getchar(); | |
| (void) tcsetattr(0, TCSANOW, &old); | |
| return rc; | |
| } | |
| #else | |
| GF_EXPORT | |
| int gf_getch() | |
| { | |
| #ifdef GPAC_ENABLE_COVERAGE | |
| if (gf_sys_is_cov_mode()) return 0; | |
| #endif | |
| return getchar(); | |
| } | |
| #endif | |
| GF_EXPORT | |
| Bool gf_read_line_input(char * line, int maxSize, Bool showContent) | |
| { | |
| char read; | |
| int i = 0; | |
| #ifdef GPAC_ENABLE_COVERAGE | |
| if (gf_sys_is_cov_mode()) return GF_FALSE; | |
| #endif | |
| if (fflush( stderr )) | |
| perror("Failed to flush buffer %s"); | |
| do { | |
| line[i] = '\0'; | |
| if (i >= maxSize - 1) | |
| return GF_FALSE; | |
| read = gf_getch(); | |
| if (read == 8 || read == 127) { | |
| if (i > 0) { | |
| fprintf(stderr, "\b \b"); | |
| i--; | |
| } | |
| } else if (read >= 32) { | |
| fputc(showContent ? read : '*', stderr); | |
| line[i++] = read; | |
| } | |
| fflush(stderr); | |
| } while (read != '\n'); | |
| if (!read) | |
| return GF_FALSE; | |
| return GF_TRUE; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_creds_check_password(const char *user, char *password) | |
| { | |
| u32 i, len, v; | |
| u8 passbuf[150]; | |
| u8 hash[GF_SHA256_DIGEST_SIZE], ohash[GF_SHA256_DIGEST_SIZE]; | |
| const char *cred_file = gf_opts_get_key("core", "users"); | |
| if (!cred_file) return GF_NOT_FOUND; | |
| GF_Config *creds = gf_cfg_new(NULL, cred_file); | |
| if (!creds) return GF_NOT_FOUND; | |
| len = (u32) strlen(password); | |
| const char *pass_hex = gf_cfg_get_key(creds, user, "password"); | |
| const char *salt_hex = gf_cfg_get_key(creds, user, "salt"); | |
| if (!salt_hex || !pass_hex || (strlen(salt_hex) != 64) || (strlen(pass_hex) != 64) || (len>=100)) { | |
| gf_cfg_del(creds); | |
| return GF_NOT_FOUND; | |
| } | |
| u8 salt[GF_SHA256_DIGEST_SIZE]; | |
| for (i=0; i<GF_SHA256_DIGEST_SIZE; i++) { | |
| sscanf(salt_hex+2*i, "%02x", &v); | |
| salt[i] = v; | |
| } | |
| for (i=0; i<GF_SHA256_DIGEST_SIZE; i++) { | |
| sscanf(pass_hex+2*i, "%02x", &v); | |
| ohash[i] = v; | |
| } | |
| memcpy(passbuf, password, len); | |
| passbuf[len]='@'; | |
| memcpy(passbuf+len+1, salt, GF_SHA256_DIGEST_SIZE); | |
| len += GF_SHA256_DIGEST_SIZE+1; | |
| gf_sha256_csum(passbuf, len, hash); | |
| len=1; | |
| for (i=0; i<GF_SHA256_DIGEST_SIZE; i++) { | |
| if (hash[i] != ohash[i]) len=0; | |
| } | |
| if (len) { | |
| sprintf((char *)passbuf, LLU, gf_net_get_utc()); | |
| gf_cfg_set_key(creds, user, "last_auth", (char *)passbuf); | |
| gf_cfg_save(creds); | |
| } | |
| gf_cfg_del(creds); | |
| if (!len) return GF_AUTHENTICATION_FAILURE; | |
| return GF_OK; | |
| } | |
| #include <gpac/token.h> | |
| GF_EXPORT | |
| Bool gf_creds_check_membership(const char *username, const char *users, const char *groups) | |
| { | |
| if (!username) return GF_FALSE; | |
| //users specified and username is listed | |
| if (users && gf_token_find_word(users, username, ",")) return GF_TRUE; | |
| if (!groups) | |
| return users ? GF_TRUE : GF_FALSE; | |
| //check all defined groups | |
| const char *cred_file = gf_opts_get_key("core", "users"); | |
| if (!cred_file) return GF_FALSE; | |
| GF_Config *creds = gf_cfg_new(NULL, cred_file); | |
| if (!creds) return GF_FALSE; | |
| u32 i, nb_groups = gf_cfg_get_key_count(creds, "groups"); | |
| for (i=0; i<nb_groups; i++) { | |
| const char *g = gf_cfg_get_key_name(creds, "groups", i); | |
| if (!gf_token_find_word(groups, g, ",")) continue; | |
| //group is present in groups being checked, check user | |
| const char *members = gf_cfg_get_key(creds, "groups", g); | |
| if (gf_token_find_word(members, username, ",")) { | |
| gf_cfg_del(creds); | |
| return GF_TRUE; | |
| } | |
| } | |
| gf_cfg_del(creds); | |
| return GF_FALSE; | |
| } |