AiCPlayer
Interface of aic vm - for rendering aspect, sensors, video records
main.c
Go to the documentation of this file.
1 
39 #include <SDL2/SDL.h> // for SDL_Init, SDL_INIT_VIDEO, SDL_Quit
40 #include <SDL2/SDL_error.h> // for SDL_GetError
41 #include <SDL2/SDL_events.h> // for SDL_Event, SDL_UserEvent, SDL_MouseMo..
42 #include <SDL2/SDL_keyboard.h> // for SDL_Keysym
43 #include <SDL2/SDL_mouse.h> // for SDL_GetMouseState, SDL_BUTTON_LEFT
44 #include <SDL2/SDL_render.h> // for SDL_CreateRenderer, SDL_RenderClear
45 #include <SDL2/SDL_surface.h> // for SDL_CreateRGBSurface, SDL_FreeSurface
46 #include <SDL2/SDL_syswm.h> // for SDL_SysWMinfo, SDL_GetWindowWMInfo
47 #include <SDL2/SDL_version.h> // for SDL_VERSION
48 #include <SDL2/SDL_video.h> // for SDL_Window, SDL_SetWindowSize, SDL_WI..
49 #include <X11/Xlib.h> // for XInitThreads, XOpenDisplay, Display
50 #include <pthread.h> // for pthread_t
51 #include <signal.h> // for signal, SIGPIPE, SIGSEGV, SIG_IGN
52 #include <stdint.h> // for int32_t
53 #include <stdio.h> // for NULL, snprintf
54 #include <stdlib.h> // for atexit, exit, free, malloc
55 #include <string.h> // for strncmp
56 #include <unistd.h> // for close
57 
58 #include "buffer_sizes.h"
59 #include "config_env.h"
60 #include "dump_trace.h"
61 #include "grabber.h"
62 #include "host_gl.h"
63 #include "logger.h"
64 #include "render_api.h"
65 #include "sdl_events.h"
66 #include "sensors.h"
67 #include "socket.h"
68 
69 #define LOG_TAG "main"
70 
71 #define USER_EVENT_ROTATION 1
72 
73 #define USER_EVENT_NEWCLIENT 2
74 
76 #define INPUT_PORT 22469
77 
78 static SDL_Surface* s_window_surface = NULL;
79 static SDL_Window* s_window = NULL;
80 static char* s_vmip = NULL;
81 static int input_in_progress = 0;
82 
88 
93 int g_width;
94 
100 float g_rotation = 0.0;
101 
103 void* g_window_id = NULL;
104 
106 static SDL_Window* open_window(int width, int height)
107 {
108  char title[BUF_SIZE];
109  SDL_Window* window;
110  SDL_Renderer* renderer;
111 
112  if (SDL_Init(SDL_INIT_VIDEO))
113  LOGE("SDL_Init failed: %s\n", SDL_GetError());
114 
115  atexit(SDL_Quit);
116  snprintf(title, sizeof(title), "AiC Player (%i * %i)", width, height);
117  window =
118  SDL_CreateWindow(title, 0, 0, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI);
119  if (!window)
120  LOGE("Can't create window");
121 
122  grabber_set_display(XOpenDisplay(NULL));
123  renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
124 
125  if (renderer == NULL)
126  LOGE("Can't create renderer");
127 
128  s_window_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0, 0, 0, 0);
129  SDL_SetRenderDrawColor(renderer, 3, 169, 244, 255);
130  SDL_RenderClear(renderer);
131  SDL_RenderPresent(renderer);
132 
133  return window;
134 }
135 
137 static void* get_window_id(SDL_Window* window)
138 {
139  SDL_SysWMinfo wminfo;
140  void* winhandle;
141 
142  SDL_VERSION(&wminfo.version);
143  SDL_GetWindowWMInfo(window, &wminfo);
144  winhandle = (void*) wminfo.info.x11.window;
145  return winhandle;
146 }
147 
149 static void callback_rotation(float angle)
150 {
151  float* angle_alloc = (float*) malloc(sizeof(float));
152  if (!angle_alloc)
153  LOGE("callback_rotation(): out of memory");
154  SDL_Event rotation_event;
155  rotation_event.type = SDL_USEREVENT;
156  rotation_event.user.code = USER_EVENT_ROTATION;
157  LOGI("Rotation callback from the VM: %f ", angle);
158  *angle_alloc = angle;
159  rotation_event.user.data2 = angle_alloc;
160  SDL_PushEvent(&rotation_event);
161 }
162 
164 static void recreate_subwindow(int width, int height, float rotation)
165 {
166  setOpenGLDisplayRotation(rotation);
168  SDL_FreeSurface(s_window_surface);
169  s_window_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0, 0, 0, 0);
170  if (!s_window_surface)
171  LOGE("Unable to recreate Window surface");
172 
173  g_window_id = get_window_id(s_window);
174  createOpenGLSubwindow(g_window_id, 0, 0, width, height, rotation);
175  SDL_SetWindowSize(s_window, width, height);
176 }
177 
179 static void do_rotation(float rotation)
180 {
181  LOGI("Rotating the screen to: %f°", rotation);
182  switch ((int) rotation)
183  {
184  case 0:
185  case 180:
186  recreate_subwindow(g_width, g_height, rotation);
187  break;
188  case 90:
189  case 270:
190  recreate_subwindow(g_height, g_width, rotation);
191  break;
192  default:
193  LOGE("Unknown rotation value: %f°", rotation);
194  break;
195  }
196 
198  g_rotation = rotation;
199 }
200 
201 static void rotation_received(float rotation)
202 {
203  if (rotation == 0.0 || rotation == 90.0 || rotation == 180.0 || rotation == 270.0)
204  do_rotation(rotation);
205  else
206  LOGW("Unexpected rotation value: %f°", rotation);
207 }
208 
210 static void* connect_input(void* arg)
211 {
212  (void) arg;
213  input_in_progress = 1;
214 
215  socket_t* input_socket = (socket_t*) malloc(sizeof(socket_t));
216  if (!input_socket)
217  LOGE("connect_input thread: out of memory");
218  do
219  {
220  *input_socket = open_socket(s_vmip, INPUT_PORT);
221  if (*input_socket == SOCKET_ERROR)
222  {
223  LOGW("Could not connect to input daemon (:%d): %s", INPUT_PORT, strerror(errno));
224  sleep(1);
225  }
226  } while (*input_socket == SOCKET_ERROR);
227  LOGI("input client connected with socket %d", *input_socket);
228 
229  SDL_Event conn_event;
230  conn_event.type = SDL_USEREVENT;
231  conn_event.user.code = USER_EVENT_NEWCLIENT;
232  conn_event.user.data1 = input_socket;
233  SDL_PushEvent(&conn_event);
234  input_in_progress = 0;
235 
236  return 0;
237 }
238 
240 static void main_loop(int width, int height)
241 {
242  SDL_Event event;
243  socket_t input_socket = 0;
244  pthread_t input_thread;
245 
246 reconnect:
247  if (input_socket > 0)
248  {
249  close(input_socket);
250  input_socket = 0;
251  if (!input_in_progress)
252  {
253  if (pthread_create(&input_thread, NULL, connect_input, NULL) != 0)
254  LOGE("Unable to start input thread, exiting...");
255  }
256  }
257 
258  while (SDL_WaitEvent(&event))
259  {
260  switch (event.type)
261  {
262  case SDL_USEREVENT:
263  switch (event.user.code)
264  {
265  case USER_EVENT_ROTATION:
266  rotation_received(*(float*) event.user.data2);
267  free(event.user.data2);
268  break;
270  if (input_socket)
271  close(input_socket);
272  char buffer[BUF_SIZE];
273  input_socket = *(socket_t*) event.user.data1;
274  free(event.user.data1);
275  LOGI("Got new uinput client, socket=%d", input_socket);
276  snprintf(buffer, BUF_SIZE, "CONFIG:%d:%d\n", width, height);
277  send(input_socket, buffer, strlen(buffer), 0);
278  break;
279  }
280  break;
281  case SDL_MOUSEMOTION:
282  if (sdl_mouse_motion(&event, input_socket) == SOCKET_ERROR)
283  goto reconnect;
284  break;
285  case SDL_MOUSEBUTTONDOWN:
286  case SDL_MOUSEBUTTONUP:
287  if (sdl_mouse_button(&event, input_socket) == SOCKET_ERROR)
288  goto reconnect;
289  break;
290  case SDL_MOUSEWHEEL:
291  if (sdl_mouse_wheel(&event, input_socket) == SOCKET_ERROR)
292  goto reconnect;
293  break;
294  case SDL_KEYDOWN:
295  case SDL_KEYUP:
296  if (sdl_key(&event, input_socket) == SOCKET_ERROR)
297  goto reconnect;
298  break;
299  case SDL_QUIT:
300  exit(0);
301  break;
302  }
303  }
304 }
305 
306 int main()
307 {
308  init_logger();
309 
310  // env parameters
311  char* amqp_host;
312  char* path_results;
313  char* vm_id;
314  int dpi;
315  int enable_record;
316  int height;
317  int width;
318 
319  static char port_gl[] = "22468";
320 
321  pthread_t gl_thread;
322  pthread_t input_thread;
323  pthread_t amqp_grabber_thread;
324  pthread_t socket_grabber_thread;
325 
326  av_register_all();
327 
328  // print stack trace upon segmentation fault
329 
330  signal(SIGSEGV, dump_trace);
331  signal(SIGPIPE, SIG_IGN);
332 
333  // All config values are mandatory, no defaults.
334 
335  amqp_host = configvar_string("AIC_PLAYER_AMQP_HOST");
336  vm_id = configvar_string("AIC_PLAYER_VM_ID");
337  s_vmip = configvar_string("AIC_PLAYER_VM_HOST");
338  width = configvar_int("AIC_PLAYER_WIDTH");
339  height = configvar_int("AIC_PLAYER_HEIGHT");
340  enable_record = configvar_bool("AIC_PLAYER_ENABLE_RECORD");
341  path_results = configvar_string("AIC_PLAYER_PATH_RECORD");
342  dpi = configvar_int("AIC_PLAYER_DPI");
343  g_width = width;
344  g_height = height;
345 
346  grabber_set_path_results(path_results);
347 
348  XInitThreads();
349 
350  LOGI("Creating window surface: %dx%d", width, height);
351  s_window = open_window(width, height);
352  g_window_id = get_window_id(s_window);
353 
354  if (!initLibrary())
355  LOGE("Unable to initialize Library");
356 
358  LOGE("invalid stream mode for setStreamMode()");
359 
360  if (!initOpenGLRenderer(width, height, port_gl, 6))
361  LOGE("initOpenGLRenderer failed");
362 
363  if (pthread_create(&gl_thread, NULL, (void*) &manage_socket_gl, s_vmip))
364  LOGE("Error creating thread");
365 
366  if (!createOpenGLSubwindow(g_window_id, 0, 0, width, height, 0))
367  LOGE("Unable to setup SubWindow");
368 
369  AiC_CallbackRotation(callback_rotation);
370  AiC_setDPI(dpi);
371 
372  sensor_params param_listener;
373  if (enable_record)
374  {
375  char sensor_name[] = "recording";
376 
377  const int32_t str_length = BUF_SIZE;
378  g_strlcpy(param_listener.sensor, sensor_name, str_length);
379  param_listener.gvmip = s_vmip;
380  g_strlcpy(param_listener.exchange, sensor_name, str_length);
381 
382  snprintf(param_listener.queue, str_length, "android-events.%s.%s", vm_id, sensor_name);
383 
384  if (strncmp(amqp_host, "0", 1))
385  pthread_create(&amqp_grabber_thread, 0, &grab_handler_amqp, &param_listener);
386  else
387  LOGI("Working without AMQP");
388 
389  pthread_create(&socket_grabber_thread, 0, &grab_handler_sock, &param_listener);
390  }
391 
392  if (pthread_create(&input_thread, NULL, connect_input, NULL) != 0)
393  LOGE("Unable to start input thread");
394 
395  main_loop(width, height);
396 
397  return 0;
398 }
Utilities to get config values from the environment.
int sdl_key(SDL_Event *event, socket_t input_socket)
Produce a key press/release event to the virtual input.
Definition: sdl_events.c:64
char sensor[BUF_SIZE]
Sensor name.
Definition: sensors.h:36
int configvar_int(char *varname)
Get the value of a integer config variable from the env.
Definition: config_env.c:36
int main()
Definition: main.c:306
Parameter for sensor threads.
Definition: sensors.h:31
void grabber_set_path_results(char *results)
Set the static path to the screenshots/movies dir.
Definition: grabber.c:73
int configvar_bool(char *varname)
Get the value of a boolean config variable from the env.
Definition: config_env.c:45
#define LOGE(...)
Log at ERROR level (makes the application abort)
Definition: logger.h:31
#define BUF_SIZE
Small, fixed-size buffers.
Definition: buffer_sizes.h:9
void dump_trace()
Dump a backtrace if there is a segfault.
Definition: dump_trace.c:9
const char * gvmip
VM IP.
Definition: sensors.h:42
int socket_t
Alias to differenciate between regular ints and socket fds.
Definition: socket.h:13
int sdl_mouse_wheel(SDL_Event *event, socket_t input_socket)
Produce a mouse wheel event to the virtual input.
Definition: sdl_events.c:126
void init_logger()
Definition: logger.c:76
int initOpenGLRenderer(int width, int height, char *addr, size_t addrLen)
int manage_socket_gl(void *arg)
Manage the remote OpenGL to the VM.
Definition: host_gl.c:128
int createOpenGLSubwindow(void *window, int x, int y, int width, int height, float zRot)
void grabber_set_display(Display *display)
Set the static X display pointer.
Definition: grabber.c:68
void * grab_handler_amqp(void *args)
Definition: grabber.c:804
Define dump_trace()
socket_t open_socket(const char *ip, short port)
Connect to a host:port couple.
Definition: socket.c:29
#define USER_EVENT_ROTATION
Definition: main.c:71
int g_height
Global variable for the window height.
Definition: main.c:87
OpenGL proxy management.
char queue[BUF_SIZE]
Queue name.
Definition: sensors.h:40
void * g_window_id
Global variable for the X window id.
Definition: main.c:103
void repaintOpenGLDisplay(void)
void AiC_setDPI(int dpi)
Defines ports and structures for sensor threads.
char * configvar_string(char *varname)
Get the value of a config variable from the env.
Definition: config_env.c:29
char exchange[BUF_SIZE]
Exchange name.
Definition: sensors.h:38
#define STREAM_MODE_TCP
Definition: render_api.h:8
#define INPUT_PORT
Definition: main.c:76
Logging macros.
int destroyOpenGLSubwindow(void)
float g_rotation
Definition: main.c:100
float AiC_CallbackRotation(void(*fn)(float))
int g_width
Global variable for the window width.
Definition: main.c:93
#define LOGW(...)
Log at WARNING level.
Definition: logger.h:27
int sdl_mouse_motion(SDL_Event *event, socket_t input_socket)
Produce a mouse motion event to the virtual input.
Definition: sdl_events.c:112
int setStreamMode(int mode)
grabber records videos or snapshots from ampq messages
void setOpenGLDisplayRotation(float zRot)
Produce virtual input events from SDL events.
void * grab_handler_sock()
Start/Stop from socket connection via protobuf message.
#define SOCKET_ERROR
Alias for the recv() return value in case of error.
Definition: socket.h:10
Define common buffer sizes.
Define socket utilities to simplify networking.
#define USER_EVENT_NEWCLIENT
Definition: main.c:73
int sdl_mouse_button(SDL_Event *event, socket_t input_socket)
Produce a mouse click event to the virtual input.
Definition: sdl_events.c:141
#define LOGI(...)
Log at INFO level.
Definition: logger.h:23
int initLibrary(void)
Header for functions defined in the AOSP opengl libs.