PageRenderTime 63ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/applications/osmo4_ios/main.c

https://github.com/svettom/gpac
C | 1060 lines | 931 code | 84 blank | 45 comment | 317 complexity | 6e40473a6e28ccf59ae8d76dae21bc4d MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. /*
  2. * GPAC - Multimedia Framework C SDK
  3. *
  4. * Authors: Jean Le Feuvre, Romain Bouqueau
  5. * Copyright (c) Telecom ParisTech 2000-2012
  6. * All rights reserved
  7. *
  8. * This file is part of GPAC / command-line client
  9. *
  10. * GPAC is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU Lesser General Public License as published by
  12. * the Free Software Foundation; either version 2, or (at your option)
  13. * any later version.
  14. *
  15. * GPAC is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public
  21. * License along with this library; see the file COPYING. If not, write to
  22. * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  23. *
  24. */
  25. /*includes both terminal and od browser*/
  26. #include "../../include/gpac/terminal.h"
  27. #include "../../include/gpac/term_info.h"
  28. #include "../../include/gpac/constants.h"
  29. #include "../../include/gpac/options.h"
  30. #include "../../include/gpac/modules/service.h"
  31. #ifndef WIN32
  32. #include <pwd.h>
  33. #include <unistd.h>
  34. #else
  35. #include <windows.h> /*for GetModuleFileName*/
  36. #include <direct.h> /*for _mkdir*/
  37. #include <shlobj.h> /*for getting user-dir*/
  38. #ifndef SHGFP_TYPE_CURRENT
  39. #define SHGFP_TYPE_CURRENT 0 /*needed for MinGW*/
  40. #endif
  41. #ifdef _MSC_VER
  42. /*get rid of console*/
  43. #if 0
  44. #pragma comment(linker,"/SUBSYSTEM:WINDOWS")
  45. #pragma comment(linker,"/ENTRY:main")
  46. #else
  47. #pragma comment(linker,"/SUBSYSTEM:CONSOLE")
  48. #endif
  49. #endif // _MSC_VER
  50. #endif //WIN32
  51. static Bool restart = 0;
  52. #if defined(__DARWIN__) || defined(__APPLE__)
  53. static Bool not_threaded = 1;
  54. #else
  55. static Bool not_threaded = 0;
  56. #endif
  57. static Bool no_audio = 0;
  58. static Bool no_regulation = 0;
  59. static Bool bench_mode = 0;
  60. Bool is_connected = 0;
  61. Bool startup_file = 0;
  62. GF_User user;
  63. GF_Terminal *term;
  64. u64 Duration;
  65. GF_Err last_error = GF_OK;
  66. static Fixed bench_speed = FLT2FIX(20);
  67. static Bool request_next_playlist_item = 0;
  68. static GF_Config *cfg_file;
  69. static Bool display_rti = 0;
  70. static Bool Run;
  71. static Bool CanSeek = 0;
  72. static char the_url[GF_MAX_PATH];
  73. static char pl_path[GF_MAX_PATH];
  74. static Bool no_mime_check = 1;
  75. static Bool be_quiet = 0;
  76. static u32 log_time_start = 0;
  77. static u32 forced_width=0;
  78. static u32 forced_height=0;
  79. /*windowless options*/
  80. u32 align_mode = 0;
  81. u32 init_w = 0;
  82. u32 init_h = 0;
  83. u32 last_x, last_y;
  84. Bool right_down = 0;
  85. void dump_frame(GF_Terminal *term, char *rad_path, u32 dump_type, u32 frameNum);
  86. Bool dump_file(char *the_url, u32 dump_mode, Double fps, u32 width, u32 height, Float scale, u32 *times, u32 nb_times);
  87. void PrintUsage()
  88. {
  89. fprintf(stdout, "Usage Osmo4iOS [options] [filename]\n"
  90. "\t-c fileName: user-defined configuration file\n"
  91. "\t-rti fileName: logs run-time info (FPS, CPU, Mem usage) to file\n"
  92. "\t-rtix fileName: same as -rti but driven by GPAC logs\n"
  93. "\t-quiet: removes script message, buffering and downloading status\n"
  94. "\t-opt option: Overrides an option in the configuration file. String format is section:key=value\n"
  95. "\t-log-file file: sets output log file.\n"
  96. "\t-log-level lev: sets log level. Possible values are:\n"
  97. "\t \"error\" : logs only error messages\n"
  98. "\t \"warning\" : logs error+warning messages\n"
  99. "\t \"info\" : logs error+warning+info messages\n"
  100. "\t \"debug\" : logs all messages\n"
  101. "\n"
  102. "\t-log-tools lt: sets tool(s) to log. List of \':\'-separated values:\n"
  103. "\t \"core\" : libgpac core\n"
  104. "\t \"coding\" : bitstream formats (audio, video, scene)\n"
  105. "\t \"container\" : container formats (ISO File, MPEG-2 TS, AVI, ...)\n"
  106. "\t \"network\" : network data exept RTP trafic\n"
  107. "\t \"rtp\" : rtp trafic\n"
  108. "\t \"author\" : authoring tools (hint, import, export)\n"
  109. "\t \"sync\" : terminal sync layer\n"
  110. "\t \"codec\" : terminal codec messages\n"
  111. "\t \"parser\" : scene parsers (svg, xmt, bt) and other\n"
  112. "\t \"media\" : terminal media object management\n"
  113. "\t \"scene\" : scene graph and scene manager\n"
  114. "\t \"script\" : scripting engine messages\n"
  115. "\t \"interact\" : interaction engine (events, scripts, etc)\n"
  116. "\t \"compose\" : composition engine (2D, 3D, etc)\n"
  117. "\t \"service\" : network service management\n"
  118. "\t \"mmio\" : Audio/Video HW I/O management\n"
  119. "\t \"none\" : no tool logged\n"
  120. "\t \"all\" : all tools logged\n"
  121. "\n"
  122. "\t-size WxH: specifies visual size (default: scene size)\n"
  123. "\t-scale s: scales the visual size (default: 1)\n"
  124. #if defined(__DARWIN__) || defined(__APPLE__)
  125. "\t-thread: enables thread usage for terminal and compositor \n"
  126. #else
  127. "\t-no-thread: disables thread usage (except for audio)\n"
  128. #endif
  129. "\t-no-audio: disables audio \n"
  130. "\t-no-wnd: uses windowless mode (Win32 only)\n"
  131. "\t-align vh: specifies v and h alignment for windowless mode\n"
  132. " possible v values: t(op), m(iddle), b(ottom)\n"
  133. " possible h values: l(eft), m(iddle), r(ight)\n"
  134. " default alignment is top-left\n"
  135. " default alignment is top-left\n"
  136. "\t-pause: pauses at first frame\n"
  137. "\n"
  138. "Dumper Options:\n"
  139. "\t-bmp [times]: dumps given frames to bmp\n"
  140. "\t-raw [times]: dumps given frames to bmp\n"
  141. "\t-avi [times]: dumps given file to raw avi\n"
  142. "\t-rgbds: dumps the RGBDS pixel format texture\n"
  143. " with -avi [times]: dumps an rgbds-format .avi\n"
  144. "\t-rgbd: dumps the RGBD pixel format texture\n"
  145. " with -avi [times]: dumps an rgbd-format .avi\n"
  146. "\t-depth: dumps depthmap (z-buffer) frames\n"
  147. " with -avi [times]: dumps depthmap in grayscale .avi\n"
  148. " with -bmp: dumps depthmap in grayscale .bmp\n"
  149. "\t-fps FPS: specifies frame rate for AVI dumping (default: 25.0)\n"
  150. "\t-2d: uses 2D compositor\n"
  151. "\t-3d: uses 3D compositor\n"
  152. "\t-fill: uses fill aspect ratio for dumping (default: none)\n"
  153. "\t-show: show window while dumping (default: no)\n"
  154. "MP4Client - GPAC command line player and dumper - version %s\n"
  155. "GPAC Written by Jean Le Feuvre (c) 2001-2005 - ENST (c) 2005-200X\n",
  156. GPAC_FULL_VERSION
  157. );
  158. }
  159. static void PrintTime(u64 time)
  160. {
  161. u32 ms, h, m, s;
  162. h = (u32) (time / 1000 / 3600);
  163. m = (u32) (time / 1000 / 60 - h*60);
  164. s = (u32) (time / 1000 - h*3600 - m*60);
  165. ms = (u32) (time - (h*3600 + m*60 + s) * 1000);
  166. fprintf(stdout, "%02d:%02d:%02d.%02d", h, m, s, ms);
  167. }
  168. static u32 rti_update_time_ms = 200;
  169. static FILE *rti_logs = NULL;
  170. static u64 memory_at_gpac_startup = 0;
  171. static void UpdateRTInfo(const char *legend)
  172. {
  173. GF_SystemRTInfo rti;
  174. /*refresh every second*/
  175. if (!display_rti && !rti_logs) return;
  176. if (!gf_sys_get_rti(rti_update_time_ms, &rti, 0) && !legend)
  177. return;
  178. if (display_rti) {
  179. if (!rti.process_memory) rti.process_memory = (u32) (memory_at_gpac_startup-rti.physical_memory_avail);
  180. if (!rti.gpac_memory) rti.gpac_memory = (u32) (memory_at_gpac_startup-rti.physical_memory_avail);
  181. if (display_rti==2) {
  182. fprintf(stdout, "FPS %02.2f - CPU %02d (%02d) - Mem %d kB\r",
  183. gf_term_get_framerate(term, 0), rti.total_cpu_usage, rti.process_cpu_usage, (u32) (rti.gpac_memory / 1024) );
  184. } else {
  185. char szMsg[1024];
  186. GF_Event evt;
  187. sprintf(szMsg, "FPS %02.2f - CPU %02d (%02d) - Mem %d kB",
  188. gf_term_get_framerate(term, 0), rti.total_cpu_usage, rti.process_cpu_usage, (u32) (rti.gpac_memory / 1024) );
  189. evt.type = GF_EVENT_SET_CAPTION;
  190. evt.caption.caption = szMsg;
  191. gf_term_user_event(term, &evt);
  192. }
  193. }
  194. if (rti_logs) {
  195. fprintf(rti_logs, "% 8d\t% 8d\t% 8d\t% 4d\t% 8d\t%s",
  196. gf_sys_clock(),
  197. gf_term_get_time_in_ms(term),
  198. rti.total_cpu_usage,
  199. (u32) gf_term_get_framerate(term, 0),
  200. (u32) (rti.gpac_memory / 1024),
  201. legend ? legend : ""
  202. );
  203. if (!legend) fprintf(rti_logs, "\n");
  204. }
  205. }
  206. static void ResetCaption()
  207. {
  208. GF_Event event;
  209. if (display_rti) return;
  210. event.type = GF_EVENT_SET_CAPTION;
  211. if (is_connected) {
  212. char szName[1024];
  213. NetInfoCommand com;
  214. event.caption.caption = NULL;
  215. /*get any service info*/
  216. if (!startup_file && gf_term_get_service_info(term, gf_term_get_root_object(term), &com) == GF_OK) {
  217. strcpy(szName, "");
  218. if (com.track_info) {
  219. char szBuf[10];
  220. sprintf(szBuf, "%02d ", (u32) (com.track_info>>16) );
  221. strcat(szName, szBuf);
  222. }
  223. if (com.artist) { strcat(szName, com.artist); strcat(szName, " "); }
  224. if (com.name) { strcat(szName, com.name); strcat(szName, " "); }
  225. if (com.album) { strcat(szName, "("); strcat(szName, com.album); strcat(szName, ")"); }
  226. if (strlen(szName)) event.caption.caption = szName;
  227. }
  228. if (!event.caption.caption) {
  229. char *str = strrchr(the_url, '\\');
  230. if (!str) str = strrchr(the_url, '/');
  231. event.caption.caption = str ? str+1 : the_url;
  232. }
  233. } else {
  234. event.caption.caption = "GPAC MP4Client " GPAC_FULL_VERSION;
  235. }
  236. gf_term_user_event(term, &event);
  237. }
  238. #ifdef WIN32
  239. u32 get_sys_col(int idx)
  240. {
  241. u32 res;
  242. DWORD val = GetSysColor(idx);
  243. res = (val)&0xFF; res<<=8;
  244. res |= (val>>8)&0xFF; res<<=8;
  245. res |= (val>>16)&0xFF;
  246. return res;
  247. }
  248. #endif
  249. void switch_bench()
  250. {
  251. if (is_connected) {
  252. bench_mode = !bench_mode;
  253. display_rti = !display_rti;
  254. ResetCaption();
  255. gf_term_set_speed(term, bench_mode ? bench_speed : FIX_ONE);
  256. }
  257. }
  258. Bool GPAC_EventProc(void *ptr, GF_Event *evt)
  259. {
  260. if (!term) return 0;
  261. switch (evt->type) {
  262. case GF_EVENT_DURATION:
  263. Duration = 1000;
  264. Duration = (u64) (((s64) Duration) * evt->duration.duration);
  265. CanSeek = evt->duration.can_seek;
  266. break;
  267. case GF_EVENT_MESSAGE:
  268. {
  269. const char *servName;
  270. if (!evt->message.service || !strcmp(evt->message.service, the_url)) {
  271. servName = "main service";
  272. } else if (!strnicmp(evt->message.service, "data:", 5)) {
  273. servName = "";
  274. } else {
  275. servName = evt->message.service;
  276. }
  277. if (!evt->message.message) return 0;
  278. if (evt->message.error) {
  279. if (!is_connected) last_error = evt->message.error;
  280. fprintf(stderr, "%s (%s): %s\n", evt->message.message, servName, gf_error_to_string(evt->message.error));
  281. } else if (!be_quiet)
  282. fprintf(stderr, "(%s) %s\r", servName, evt->message.message);
  283. }
  284. break;
  285. case GF_EVENT_PROGRESS:
  286. {
  287. char *szTitle = "";
  288. if (evt->progress.progress_type==0) szTitle = "Buffer ";
  289. else if (evt->progress.progress_type==1) szTitle = "Download ";
  290. else if (evt->progress.progress_type==2) szTitle = "Import ";
  291. gf_set_progress(szTitle, evt->progress.done, evt->progress.total);
  292. }
  293. break;
  294. case GF_EVENT_DBLCLICK:
  295. gf_term_set_option(term, GF_OPT_FULLSCREEN, !gf_term_get_option(term, GF_OPT_FULLSCREEN));
  296. return 0;
  297. case GF_EVENT_MOUSEDOWN:
  298. if (evt->mouse.button==GF_MOUSE_RIGHT) {
  299. right_down = 1;
  300. last_x = evt->mouse.x;
  301. last_y = evt->mouse.y;
  302. }
  303. return 0;
  304. case GF_EVENT_MOUSEUP:
  305. if (evt->mouse.button==GF_MOUSE_RIGHT) {
  306. right_down = 0;
  307. last_x = evt->mouse.x;
  308. last_y = evt->mouse.y;
  309. }
  310. return 0;
  311. case GF_EVENT_MOUSEMOVE:
  312. if (right_down && (user.init_flags & GF_TERM_WINDOWLESS) ) {
  313. GF_Event move;
  314. move.move.x = evt->mouse.x - last_x;
  315. move.move.y = last_y-evt->mouse.y;
  316. move.type = GF_EVENT_MOVE;
  317. move.move.relative = 1;
  318. gf_term_user_event(term, &move);
  319. }
  320. return 0;
  321. case GF_EVENT_KEYUP:
  322. switch (evt->key.key_code) {
  323. case GF_KEY_SPACE:
  324. if (evt->key.flags & GF_KEY_MOD_CTRL) switch_bench();
  325. break;
  326. }
  327. break;
  328. case GF_EVENT_KEYDOWN:
  329. gf_term_process_shortcut(term, evt);
  330. switch (evt->key.key_code) {
  331. case GF_KEY_SPACE:
  332. if (evt->key.flags & GF_KEY_MOD_CTRL) {
  333. /*ignore key repeat*/
  334. if (!bench_mode) switch_bench();
  335. }
  336. break;
  337. case GF_KEY_PAGEDOWN:
  338. case GF_KEY_MEDIANEXTTRACK:
  339. request_next_playlist_item = 1;
  340. break;
  341. case GF_KEY_MEDIAPREVIOUSTRACK:
  342. break;
  343. case GF_KEY_ESCAPE:
  344. gf_term_set_option(term, GF_OPT_FULLSCREEN, !gf_term_get_option(term, GF_OPT_FULLSCREEN));
  345. break;
  346. case GF_KEY_F:
  347. if (evt->key.flags & GF_KEY_MOD_CTRL) fprintf(stderr, "Rendering rate: %f FPS\n", gf_term_get_framerate(term, 0));
  348. break;
  349. case GF_KEY_T:
  350. if (evt->key.flags & GF_KEY_MOD_CTRL) fprintf(stderr, "Scene Time: %f \n", gf_term_get_time_in_ms(term)/1000.0);
  351. break;
  352. case GF_KEY_D:
  353. if (evt->key.flags & GF_KEY_MOD_CTRL) gf_term_set_option(term, GF_OPT_DRAW_MODE, (gf_term_get_option(term, GF_OPT_DRAW_MODE)==GF_DRAW_MODE_DEFER) ? GF_DRAW_MODE_IMMEDIATE : GF_DRAW_MODE_DEFER );
  354. break;
  355. case GF_KEY_4:
  356. if (evt->key.flags & GF_KEY_MOD_CTRL)
  357. gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_4_3);
  358. break;
  359. case GF_KEY_5:
  360. if (evt->key.flags & GF_KEY_MOD_CTRL)
  361. gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_16_9);
  362. break;
  363. case GF_KEY_6:
  364. if (evt->key.flags & GF_KEY_MOD_CTRL)
  365. gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN);
  366. break;
  367. case GF_KEY_7:
  368. if (evt->key.flags & GF_KEY_MOD_CTRL)
  369. gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_KEEP);
  370. break;
  371. case GF_KEY_P:
  372. if (evt->key.flags & GF_KEY_MOD_CTRL && is_connected) {
  373. Bool is_pause = gf_term_get_option(term, GF_OPT_PLAY_STATE);
  374. fprintf(stderr, "[Status: %s]\n", is_pause ? "Playing" : "Paused");
  375. gf_term_set_option(term, GF_OPT_PLAY_STATE, (gf_term_get_option(term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) ? GF_STATE_PLAYING : GF_STATE_PAUSED);
  376. }
  377. break;
  378. case GF_KEY_S:
  379. if ((evt->key.flags & GF_KEY_MOD_CTRL) && is_connected) {
  380. gf_term_set_option(term, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE);
  381. fprintf(stderr, "Step time: ");
  382. PrintTime(gf_term_get_time_in_ms(term));
  383. fprintf(stderr, "\n");
  384. }
  385. break;
  386. case GF_KEY_H:
  387. if ((evt->key.flags & GF_KEY_MOD_CTRL) && is_connected)
  388. gf_term_switch_quality(term, 1);
  389. break;
  390. case GF_KEY_L:
  391. if ((evt->key.flags & GF_KEY_MOD_CTRL) && is_connected)
  392. gf_term_switch_quality(term, 0);
  393. break;
  394. }
  395. break;
  396. case GF_EVENT_CONNECT:
  397. if (evt->connect.is_connected) {
  398. is_connected = 1;
  399. fprintf(stderr, "Service Connected\n");
  400. } else if (is_connected) {
  401. fprintf(stderr, "Service %s\n", is_connected ? "Disconnected" : "Connection Failed");
  402. is_connected = 0;
  403. Duration = 0;
  404. }
  405. if (init_w && init_h) {
  406. gf_term_set_size(term, init_w, init_h);
  407. }
  408. ResetCaption();
  409. break;
  410. case GF_EVENT_EOS:
  411. restart = 1;
  412. break;
  413. case GF_EVENT_SIZE:
  414. if (user.init_flags & GF_TERM_WINDOWLESS) {
  415. GF_Event move;
  416. move.type = GF_EVENT_MOVE;
  417. move.move.align_x = align_mode & 0xFF;
  418. move.move.align_y = (align_mode>>8) & 0xFF;
  419. move.move.relative = 2;
  420. gf_term_user_event(term, &move);
  421. }
  422. break;
  423. case GF_EVENT_SCENE_SIZE:
  424. if (forced_width && forced_height) {
  425. GF_Event size;
  426. size.type = GF_EVENT_SIZE;
  427. size.size.width = forced_width;
  428. size.size.height = forced_height;
  429. gf_term_user_event(term, &size);
  430. }
  431. break;
  432. case GF_EVENT_METADATA:
  433. ResetCaption();
  434. break;
  435. case GF_EVENT_QUIT:
  436. Run = 0;
  437. break;
  438. case GF_EVENT_DISCONNECT:
  439. gf_term_disconnect(term);
  440. break;
  441. case GF_EVENT_MIGRATE:
  442. {
  443. }
  444. break;
  445. case GF_EVENT_NAVIGATE_INFO:
  446. if (evt->navigate.to_url) fprintf(stderr, "Go to URL: \"%s\"\r", evt->navigate.to_url);
  447. break;
  448. case GF_EVENT_NAVIGATE:
  449. if (gf_term_is_supported_url(term, evt->navigate.to_url, 1, no_mime_check)) {
  450. strcpy(the_url, evt->navigate.to_url);
  451. fprintf(stderr, "Navigating to URL %s\n", the_url);
  452. gf_term_navigate_to(term, evt->navigate.to_url);
  453. return 1;
  454. } else {
  455. fprintf(stderr, "Navigation destination not supported\nGo to URL: %s\n", evt->navigate.to_url);
  456. }
  457. break;
  458. case GF_EVENT_SET_CAPTION:
  459. gf_term_user_event(term, evt);
  460. break;
  461. case GF_EVENT_AUTHORIZATION:
  462. if (!strlen(evt->auth.user)) {
  463. fprintf(stderr, "Authorization required for site %s\n", evt->auth.site_url);
  464. fprintf(stderr, "login: ");
  465. scanf("%s", evt->auth.user);
  466. } else {
  467. fprintf(stderr, "Authorization required for %s@%s\n", evt->auth.user, evt->auth.site_url);
  468. }
  469. fprintf(stderr, "password: ");
  470. gf_prompt_set_echo_off(1);
  471. scanf("%s", evt->auth.password);
  472. gf_prompt_set_echo_off(0);
  473. return 1;
  474. case GF_EVENT_SYS_COLORS:
  475. #ifdef WIN32
  476. evt->sys_cols.sys_colors[0] = get_sys_col(COLOR_ACTIVEBORDER);
  477. evt->sys_cols.sys_colors[1] = get_sys_col(COLOR_ACTIVECAPTION);
  478. evt->sys_cols.sys_colors[2] = get_sys_col(COLOR_APPWORKSPACE);
  479. evt->sys_cols.sys_colors[3] = get_sys_col(COLOR_BACKGROUND);
  480. evt->sys_cols.sys_colors[4] = get_sys_col(COLOR_BTNFACE);
  481. evt->sys_cols.sys_colors[5] = get_sys_col(COLOR_BTNHIGHLIGHT);
  482. evt->sys_cols.sys_colors[6] = get_sys_col(COLOR_BTNSHADOW);
  483. evt->sys_cols.sys_colors[7] = get_sys_col(COLOR_BTNTEXT);
  484. evt->sys_cols.sys_colors[8] = get_sys_col(COLOR_CAPTIONTEXT);
  485. evt->sys_cols.sys_colors[9] = get_sys_col(COLOR_GRAYTEXT);
  486. evt->sys_cols.sys_colors[10] = get_sys_col(COLOR_HIGHLIGHT);
  487. evt->sys_cols.sys_colors[11] = get_sys_col(COLOR_HIGHLIGHTTEXT);
  488. evt->sys_cols.sys_colors[12] = get_sys_col(COLOR_INACTIVEBORDER);
  489. evt->sys_cols.sys_colors[13] = get_sys_col(COLOR_INACTIVECAPTION);
  490. evt->sys_cols.sys_colors[14] = get_sys_col(COLOR_INACTIVECAPTIONTEXT);
  491. evt->sys_cols.sys_colors[15] = get_sys_col(COLOR_INFOBK);
  492. evt->sys_cols.sys_colors[16] = get_sys_col(COLOR_INFOTEXT);
  493. evt->sys_cols.sys_colors[17] = get_sys_col(COLOR_MENU);
  494. evt->sys_cols.sys_colors[18] = get_sys_col(COLOR_MENUTEXT);
  495. evt->sys_cols.sys_colors[19] = get_sys_col(COLOR_SCROLLBAR);
  496. evt->sys_cols.sys_colors[20] = get_sys_col(COLOR_3DDKSHADOW);
  497. evt->sys_cols.sys_colors[21] = get_sys_col(COLOR_3DFACE);
  498. evt->sys_cols.sys_colors[22] = get_sys_col(COLOR_3DHIGHLIGHT);
  499. evt->sys_cols.sys_colors[23] = get_sys_col(COLOR_3DLIGHT);
  500. evt->sys_cols.sys_colors[24] = get_sys_col(COLOR_3DSHADOW);
  501. evt->sys_cols.sys_colors[25] = get_sys_col(COLOR_WINDOW);
  502. evt->sys_cols.sys_colors[26] = get_sys_col(COLOR_WINDOWFRAME);
  503. evt->sys_cols.sys_colors[27] = get_sys_col(COLOR_WINDOWTEXT);
  504. return 1;
  505. #else
  506. memset(evt->sys_cols.sys_colors, 0, sizeof(u32)*28);
  507. return 1;
  508. #endif
  509. break;
  510. }
  511. return 0;
  512. }
  513. void list_modules(GF_ModuleManager *modules)
  514. {
  515. u32 i;
  516. fprintf(stderr, "\rAvailable modules:\n");
  517. for (i=0; i<gf_modules_get_count(modules); i++) {
  518. char *str = (char *) gf_modules_get_file_name(modules, i);
  519. if (str) fprintf(stderr, "\t%s\n", str);
  520. }
  521. fprintf(stderr, "\n");
  522. }
  523. void set_navigation()
  524. {
  525. GF_Err e;
  526. char navstr[20], nav;
  527. u32 type = gf_term_get_option(term, GF_OPT_NAVIGATION_TYPE);
  528. e = GF_OK;
  529. if (!type) {
  530. fprintf(stdout, "Content/compositor doesn't allow user-selectable navigation\n");
  531. } else if (type==1) {
  532. fprintf(stdout, "Select Navigation (\'N\'one, \'E\'xamine, \'S\'lide): ");
  533. scanf("%s", navstr);
  534. nav = navstr[0];
  535. if (nav=='N') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_NONE);
  536. else if (nav=='E') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_EXAMINE);
  537. else if (nav=='S') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_SLIDE);
  538. else fprintf(stdout, "Unknown selector \'%c\' - only \'N\',\'E\',\'S\' allowed\n", nav);
  539. } else if (type==2) {
  540. fprintf(stdout, "Select Navigation (\'N\'one, \'W\'alk, \'F\'ly, \'E\'xamine, \'P\'an, \'S\'lide, \'G\'ame, \'V\'R, \'O\'rbit): ");
  541. scanf("%s", navstr);
  542. nav = navstr[0];
  543. if (nav=='N') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_NONE);
  544. else if (nav=='W') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_WALK);
  545. else if (nav=='F') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_FLY);
  546. else if (nav=='E') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_EXAMINE);
  547. else if (nav=='P') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_PAN);
  548. else if (nav=='S') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_SLIDE);
  549. else if (nav=='G') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_GAME);
  550. else if (nav=='O') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_ORBIT);
  551. else if (nav=='V') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_VR);
  552. else fprintf(stdout, "Unknown selector %c - only \'N\',\'W\',\'F\',\'E\',\'P\',\'S\',\'G\', \'V\', \'O\' allowed\n", nav);
  553. }
  554. if (e) fprintf(stdout, "Error setting mode: %s\n", gf_error_to_string(e));
  555. }
  556. static Bool get_time_list(char *arg, u32 *times, u32 *nb_times)
  557. {
  558. char *str;
  559. Float var;
  560. Double sec;
  561. u32 h, m, s, ms, f, fps;
  562. if (!arg || (arg[0]=='-') || !isdigit(arg[0])) return 0;
  563. /*SMPTE time code*/
  564. if (strchr(arg, ':') && strchr(arg, ';') && strchr(arg, '/')) {
  565. if (sscanf(arg, "%02d:%02d:%02d;%02d/%02d", &h, &m, &s, &f, &fps)==5) {
  566. sec = 0;
  567. if (fps) sec = ((Double)f) / fps;
  568. sec += 3600*h + 60*m + s;
  569. times[*nb_times] = (u32) (1000*sec);
  570. (*nb_times) ++;
  571. return 1;
  572. }
  573. }
  574. while (arg) {
  575. str = strchr(arg, '-');
  576. if (str) str[0] = 0;
  577. /*HH:MM:SS:MS time code*/
  578. if (strchr(arg, ':') && (sscanf(arg, "%02d:%02d:%02d:%02d", &h, &m, &s, &ms)==4)) {
  579. sec = ms;
  580. sec /= 1000;
  581. sec += 3600*h + 60*m + s;
  582. times[*nb_times] = (u32) (1000*sec);
  583. (*nb_times) ++;
  584. } else if (sscanf(arg, "%f", &var)==1) {
  585. sec = atof(arg);
  586. times[*nb_times] = (u32) (1000*sec);
  587. (*nb_times) ++;
  588. }
  589. if (!str) break;
  590. str[0] = '-';
  591. arg = str+1;
  592. }
  593. return 1;
  594. }
  595. static void on_gpac_log(void *cbk, u32 ll, u32 lm, const char *fmt, va_list list)
  596. {
  597. FILE *logs = cbk;
  598. if (rti_logs && (lm & GF_LOG_RTI)) {
  599. char szMsg[2048];
  600. vsprintf(szMsg, fmt, list);
  601. UpdateRTInfo(szMsg + 6 /*"[RTI] "*/);
  602. } else {
  603. if (log_time_start) fprintf(logs, "[At %d]", gf_sys_clock() - log_time_start);
  604. vfprintf(logs, fmt, list);
  605. fflush(logs);
  606. }
  607. }
  608. static void init_rti_logs(char *rti_file, char *url, Bool use_rtix)
  609. {
  610. if (rti_logs) fclose(rti_logs);
  611. rti_logs = gf_f64_open(rti_file, "wt");
  612. if (rti_logs) {
  613. fprintf(rti_logs, "!! GPAC RunTime Info ");
  614. if (url) fprintf(rti_logs, "for file %s", url);
  615. fprintf(rti_logs, " !!\n");
  616. fprintf(rti_logs, "SysTime(ms)\tSceneTime(ms)\tCPU\tFPS\tMemory(kB)\tObservation\n");
  617. /*turn on RTI loging*/
  618. if (use_rtix) {
  619. gf_log_set_callback(NULL, on_gpac_log);
  620. gf_log_set_tool_level(GF_LOG_ALL, GF_LOG_ERROR);
  621. gf_log_set_tool_level(GF_LOG_RTI, GF_LOG_DEBUG);
  622. GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI, ("[RTI] System state when enabling log\n"));
  623. } else if (log_time_start) {
  624. log_time_start = gf_sys_clock();
  625. }
  626. }
  627. }
  628. #ifdef GPAC_IPHONE
  629. int SDL_main (int argc, char *argv[])
  630. #else
  631. int main (int argc, char *argv[])
  632. #endif
  633. {
  634. char c;
  635. const char *str;
  636. u32 i, times[100], nb_times, dump_mode;
  637. u32 simulation_time = 0;
  638. Bool auto_exit = 0;
  639. Bool start_fs = 1;
  640. Bool use_rtix = 0;
  641. Bool rgbds_dump = 0;
  642. Bool rgbd_dump = 0;
  643. Bool depth_dump = 0;
  644. Bool pause_at_first = 0;
  645. Bool enable_mem_tracker = 0;
  646. Double fps = 25.0;
  647. Bool ret, fill_ar, visible;
  648. char *url_arg, *the_cfg, *rti_file;
  649. GF_SystemRTInfo rti;
  650. FILE *playlist = NULL;
  651. FILE *logfile = NULL;
  652. Float scale = 1;
  653. /*by default use current dir*/
  654. strcpy(the_url, ".");
  655. memset(&user, 0, sizeof(GF_User));
  656. dump_mode = 0;
  657. fill_ar = visible = 0;
  658. url_arg = the_cfg = rti_file = NULL;
  659. nb_times = 0;
  660. times[0] = 0;
  661. /*first locate config file if specified*/
  662. for (i=1; i<(u32) argc; i++) {
  663. char *arg = argv[i];
  664. if (!strcmp(arg, "-c") || !strcmp(arg, "-cfg")) {
  665. the_cfg = argv[i+1];
  666. i++;
  667. }
  668. else if (!strcmp(arg, "-mem-track")) enable_mem_tracker = 1;
  669. }
  670. gf_sys_init(enable_mem_tracker);
  671. cfg_file = gf_cfg_init(the_cfg, NULL);
  672. if (!cfg_file) {
  673. fprintf(stdout, "Error: Configuration File \"GPAC.cfg\" not found\n");
  674. if (logfile) fclose(logfile);
  675. return 1;
  676. }
  677. gf_log_set_tools_levels( gf_cfg_get_key(cfg_file, "General", "Logs") );
  678. for (i=1; i<(u32) argc; i++) {
  679. char *arg = argv[i];
  680. // if (isalnum(arg[0]) || (arg[0]=='/') || (arg[0]=='.') || (arg[0]=='\\') ) {
  681. if (arg[0] != '-') {
  682. url_arg = arg;
  683. } else if (!strcmp(arg, "-c") || !strcmp(arg, "-cfg")) {
  684. the_cfg = argv[i+1];
  685. i++;
  686. } else if (!strcmp(arg, "-rti")) {
  687. rti_file = argv[i+1];
  688. i++;
  689. } else if (!strcmp(arg, "-rtix")) {
  690. rti_file = argv[i+1];
  691. i++;
  692. use_rtix = 1;
  693. } else if (!strcmp(arg, "-fill")) {
  694. fill_ar = 1;
  695. } else if (!strcmp(arg, "-show")) {
  696. visible = 1;
  697. } else if (!strcmp(arg, "-avi")) {
  698. if (rgbds_dump) dump_mode = 5;
  699. else if (depth_dump) dump_mode = 8;
  700. else if (rgbd_dump) dump_mode = 10;
  701. else dump_mode=1;
  702. if ((url_arg || (i+2<(u32)argc)) && get_time_list(argv[i+1], times, &nb_times)) i++;
  703. } else if (!strcmp(arg, "-rgbds")) { /*get dump in rgbds pixel format*/
  704. rgbds_dump = 1;
  705. dump_mode=6; /* rgbds texture directly*/
  706. if (dump_mode==1) dump_mode = 5; /* .avi rgbds dump*/
  707. } else if (!strcmp(arg, "-rgbd")) { /*get dump in rgbd pixel format*/
  708. rgbd_dump = 1;
  709. dump_mode=9; /* rgbd texture directly*/
  710. if (dump_mode==1) dump_mode = 10; /* .avi rgbds dump*/
  711. } else if (!strcmp(arg, "-depth")) {
  712. depth_dump = 1;
  713. if (dump_mode==2) dump_mode=7; /* grayscale .bmp depth dump*/
  714. else if (dump_mode==1) dump_mode=8; /* .avi depth dump*/
  715. else dump_mode=4; /*depth dump*/
  716. } else if (!strcmp(arg, "-bmp")) {
  717. if(depth_dump) dump_mode=7; /*grayscale depth .bmp dump*/
  718. else dump_mode=2;
  719. if ((url_arg || (i+2<(u32)argc)) && get_time_list(argv[i+1], times, &nb_times)) i++;
  720. } else if (!strcmp(arg, "-raw")) {
  721. dump_mode = 3;
  722. if ((url_arg || (i+2<(u32)argc)) && get_time_list(argv[i+1], times, &nb_times)) i++;
  723. } else if (!stricmp(arg, "-size")) {
  724. if (sscanf(argv[i+1], "%dx%d", &forced_width, &forced_height) != 2) {
  725. forced_width = forced_height = 0;
  726. }
  727. i++;
  728. } else if (!stricmp(arg, "-scale")) {
  729. sscanf(argv[i+1], "%f", &scale);
  730. i++;
  731. } else if (!stricmp(arg, "-fps")) {
  732. fps = atof(argv[i+1]);
  733. i++;
  734. } else if (!strcmp(arg, "-quiet")) {
  735. be_quiet = 1;
  736. } else if (!strcmp(arg, "-log-file") || !strcmp(arg, "-lf")) {
  737. logfile = gf_f64_open(argv[i+1], "wt");
  738. gf_log_set_callback(logfile, on_gpac_log);
  739. i++;
  740. } else if (!strcmp(arg, "-logs")) {
  741. gf_log_set_tools_levels( argv[i+1] );
  742. i++;
  743. } else if (!strcmp(arg, "-log-clock") || !strcmp(arg, "-lc")) {
  744. log_time_start = 1;
  745. } else if (!strcmp(arg, "-align")) {
  746. if (argv[i+1][0]=='m') align_mode = 1;
  747. else if (argv[i+1][0]=='b') align_mode = 2;
  748. align_mode <<= 8;
  749. if (argv[i+1][1]=='m') align_mode |= 1;
  750. else if (argv[i+1][1]=='r') align_mode |= 2;
  751. i++;
  752. }
  753. else if (!strcmp(arg, "-no-wnd")) user.init_flags |= GF_TERM_WINDOWLESS;
  754. #if defined(__DARWIN__) || defined(__APPLE__)
  755. else if (!strcmp(arg, "-thread")) not_threaded = 0;
  756. #else
  757. else if (!strcmp(arg, "-no-thread")) not_threaded = 1;
  758. #endif
  759. else if (!strcmp(arg, "-no-audio")) no_audio = 1;
  760. else if (!strcmp(arg, "-no-regulation")) no_regulation = 1;
  761. else if (!strcmp(arg, "-fs")) start_fs = 1;
  762. else if (!strcmp(arg, "-pause")) pause_at_first = 1;
  763. else if (!strcmp(arg, "-exit")) auto_exit = 1;
  764. else if (!strcmp(arg, "-mem-track")) enable_mem_tracker = 1;
  765. else if (!strcmp(arg, "-opt")) {
  766. char *sep, *sep2, szSec[1024], szKey[1024], szVal[1024];
  767. sep = strchr(argv[i+1], ':');
  768. if (sep) {
  769. sep[0] = 0;
  770. strcpy(szSec, argv[i+1]);
  771. sep[0] = ':'; sep ++;
  772. sep2 = strchr(sep, '=');
  773. if (sep2) {
  774. sep2[0] = 0;
  775. strcpy(szKey, sep);
  776. strcpy(szVal, sep2+1);
  777. sep2[0] = '=';
  778. if (!stricmp(szVal, "null")) szVal[0]=0;
  779. gf_cfg_set_key(cfg_file, szSec, szKey, szVal[0] ? szVal : NULL);
  780. }
  781. }
  782. i++;
  783. }
  784. else if (!strncmp(arg, "-run-for=", 9)) simulation_time = atoi(arg+9);
  785. else {
  786. PrintUsage();
  787. return 1;
  788. }
  789. }
  790. if (!logfile) {
  791. const char *opt = gf_cfg_get_key(cfg_file, "General", "LogFile");
  792. if (opt) {
  793. logfile = gf_f64_open(opt, "wt");
  794. if (logfile)
  795. gf_log_set_callback(logfile, on_gpac_log);
  796. }
  797. }
  798. if (dump_mode && !url_arg) {
  799. fprintf(stdout, "Missing argument for dump\n");
  800. PrintUsage();
  801. if (logfile) fclose(logfile);
  802. return 1;
  803. }
  804. if (dump_mode) rti_file = NULL;
  805. gf_sys_get_rti(500, &rti, GF_RTI_SYSTEM_MEMORY_ONLY);
  806. memory_at_gpac_startup = rti.physical_memory_avail;
  807. if (rti_file) init_rti_logs(rti_file, url_arg, use_rtix);
  808. /*setup dumping options*/
  809. if (dump_mode) {
  810. user.init_flags |= GF_TERM_NO_AUDIO | GF_TERM_NO_COMPOSITOR_THREAD | GF_TERM_NO_REGULATION /*| GF_TERM_INIT_HIDE*/;
  811. if (visible || dump_mode==8) user.init_flags |= GF_TERM_INIT_HIDE;
  812. } else {
  813. init_w = forced_width;
  814. init_h = forced_height;
  815. }
  816. fprintf(stderr, "Loading modules\n");
  817. str = gf_cfg_get_key(cfg_file, "General", "ModulesDirectory");
  818. user.modules = gf_modules_new((const char *) str, cfg_file);
  819. if (user.modules) i = gf_modules_get_count(user.modules);
  820. if (!i || !user.modules) {
  821. fprintf(stdout, "Error: no modules found in %s - exiting\n", str);
  822. if (user.modules) gf_modules_del(user.modules);
  823. gf_cfg_del(cfg_file);
  824. gf_sys_close();
  825. if (logfile) fclose(logfile);
  826. return 1;
  827. }
  828. fprintf(stderr, "Modules Loaded (%d found in %s)\n", i, str);
  829. user.config = cfg_file;
  830. user.EventProc = GPAC_EventProc;
  831. /*dummy in this case (global vars) but MUST be non-NULL*/
  832. user.opaque = user.modules;
  833. if (not_threaded) user.init_flags |= GF_TERM_NO_COMPOSITOR_THREAD;
  834. if (no_audio) user.init_flags |= GF_TERM_NO_AUDIO;
  835. if (no_regulation) user.init_flags |= GF_TERM_NO_REGULATION;
  836. fprintf(stderr, "Loading GPAC Terminal\n");
  837. term = gf_term_new(&user);
  838. if (!term) {
  839. fprintf(stderr, "\nInit error - check you have at least one video out and one rasterizer...\nFound modules:\n");
  840. list_modules(user.modules);
  841. gf_modules_del(user.modules);
  842. gf_cfg_del(cfg_file);
  843. gf_sys_close();
  844. if (logfile) fclose(logfile);
  845. return 1;
  846. }
  847. fprintf(stderr, "Terminal Loaded\n");
  848. if (dump_mode) {
  849. // gf_term_set_option(term, GF_OPT_VISIBLE, 0);
  850. if (fill_ar) gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN);
  851. } else {
  852. /*check video output*/
  853. str = gf_cfg_get_key(cfg_file, "Video", "DriverName");
  854. if (!strcmp(str, "Raw Video Output")) fprintf(stdout, "WARNING: using raw output video (memory only) - no display used\n");
  855. /*check audio output*/
  856. str = gf_cfg_get_key(cfg_file, "Audio", "DriverName");
  857. if (!str || !strcmp(str, "No Audio Output Available")) fprintf(stdout, "WARNING: no audio output availble - make sure no other program is locking the sound card\n");
  858. str = gf_cfg_get_key(cfg_file, "General", "NoMIMETypeFetch");
  859. no_mime_check = (str && !stricmp(str, "yes")) ? 1 : 0;
  860. }
  861. str = gf_cfg_get_key(cfg_file, "HTTPProxy", "Enabled");
  862. if (str && !strcmp(str, "yes")) {
  863. str = gf_cfg_get_key(cfg_file, "HTTPProxy", "Name");
  864. if (str) fprintf(stdout, "HTTP Proxy %s enabled\n", str);
  865. }
  866. if (rti_file) {
  867. str = gf_cfg_get_key(cfg_file, "General", "RTIRefreshPeriod");
  868. if (str) {
  869. rti_update_time_ms = atoi(str);
  870. } else {
  871. gf_cfg_set_key(cfg_file, "General", "RTIRefreshPeriod", "200");
  872. }
  873. UpdateRTInfo("At GPAC load time\n");
  874. }
  875. Run = 1;
  876. ret = 1;
  877. if (dump_mode) {
  878. if (!nb_times) {
  879. times[0] = 0;
  880. nb_times++;
  881. }
  882. ret = dump_file(url_arg, dump_mode, fps, forced_width, forced_height, scale, times, nb_times);
  883. Run = 0;
  884. } else
  885. /*connect if requested*/
  886. if (url_arg) {
  887. char *ext;
  888. strcpy(the_url, url_arg);
  889. ext = strrchr(the_url, '.');
  890. if (ext && (!stricmp(ext, ".m3u") || !stricmp(ext, ".pls"))) {
  891. fprintf(stdout, "Opening Playlist %s\n", the_url);
  892. playlist = gf_f64_open(the_url, "rt");
  893. if (playlist) {
  894. strcpy(pl_path, the_url);
  895. fscanf(playlist, "%s", the_url);
  896. fprintf(stdout, "Opening URL %s\n", the_url);
  897. gf_term_connect_with_path(term, the_url, pl_path);
  898. } else {
  899. fprintf(stdout, "Hit 'h' for help\n\n");
  900. }
  901. } else {
  902. fprintf(stdout, "Opening URL %s\n", the_url);
  903. if (pause_at_first) fprintf(stdout, "[Status: Paused]\n");
  904. gf_term_connect_from_time(term, the_url, 0, pause_at_first);
  905. }
  906. } else {
  907. fprintf(stdout, "Hit 'h' for help\n\n");
  908. str = gf_cfg_get_key(cfg_file, "General", "StartupFile");
  909. if (str) {
  910. strcpy(the_url, "MP4Client "GPAC_FULL_VERSION);
  911. gf_term_connect(term, str);
  912. startup_file = 1;
  913. }
  914. }
  915. /*force fullscreen*/
  916. if (0 && start_fs)
  917. gf_term_set_option(term, GF_OPT_FULLSCREEN, 1);
  918. while (Run) {
  919. if (restart) {
  920. restart = 0;
  921. gf_term_play_from_time(term, 0, 0);
  922. }
  923. if (request_next_playlist_item) {
  924. c = '\n';
  925. request_next_playlist_item = 0;
  926. if (playlist) {
  927. gf_term_disconnect(term);
  928. if (fscanf(playlist, "%s", the_url) == EOF) {
  929. fprintf(stdout, "No more items - exiting\n");
  930. Run = 0;
  931. } else {
  932. fprintf(stdout, "Opening URL %s\n", the_url);
  933. gf_term_connect_with_path(term, the_url, pl_path);
  934. }
  935. }
  936. }
  937. if (!use_rtix || display_rti) UpdateRTInfo(NULL);
  938. if (not_threaded) {
  939. //printf("gf_term_process_step from run loop\n");
  940. gf_term_process_step(term);
  941. if (auto_exit && gf_term_get_option(term, GF_OPT_IS_OVER)) {
  942. Run = 0;
  943. }
  944. } else {
  945. gf_sleep(rti_update_time_ms);
  946. }
  947. /*sim time*/
  948. if (simulation_time && (gf_term_get_time_in_ms(term)>simulation_time)) {
  949. Run = 0;
  950. }
  951. continue;
  952. }
  953. gf_term_disconnect(term);
  954. if (rti_file) UpdateRTInfo("Disconnected\n");
  955. fprintf(stdout, "Deleting terminal... ");
  956. if (playlist) fclose(playlist);
  957. gf_term_del(term);
  958. fprintf(stdout, "OK\n");
  959. fprintf(stdout, "GPAC cleanup ...\n");
  960. gf_modules_del(user.modules);
  961. gf_cfg_del(cfg_file);
  962. #ifdef GPAC_MEMORY_TRACKING
  963. if (enable_mem_tracker) {
  964. gf_memory_print();
  965. fprintf(stdout, "print any key\n");
  966. while (!gf_prompt_has_input()) {
  967. gf_sleep(100);
  968. }
  969. }
  970. #endif
  971. gf_sys_close();
  972. if (rti_logs) fclose(rti_logs);
  973. if (logfile) fclose(logfile);
  974. fprintf(stdout, "Bye\n");
  975. return 0;
  976. }