ffplay 的事件處理依賴於SDL。在 main 函數中:ide
... flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER; ... if (SDL_Init (flags)) { av_log(NULL, AV_LOG_FATAL, "Could not initialize SDL - %s\n", SDL_GetError()); av_log(NULL, AV_LOG_FATAL, "(Did you set the DISPLAY variable?)\n"); exit(1); }
以上代碼完成SDL的初始化工做,其中包含SDL_EVENT的初始化。函數
事件處理都是在 event_loop 中進行:oop
/* handle an event sent by the GUI */ static void event_loop(VideoState *cur_stream) { SDL_Event event; double incr, pos, frac; for (;;) { double x; refresh_loop_wait_event(cur_stream, &event); switch (event.type) { case SDL_KEYDOWN: if (exit_on_keydown) { do_exit(cur_stream); break; } switch (event.key.keysym.sym) { case SDLK_ESCAPE: case SDLK_q: do_exit(cur_stream); break; case SDLK_f: toggle_full_screen(cur_stream); cur_stream->force_refresh = 1; break; ... break; case SDL_VIDEOEXPOSE: cur_stream->force_refresh = 1; break; case SDL_MOUSEBUTTONDOWN: ... case SDL_MOUSEMOTION: ... break; case SDL_VIDEORESIZE: ... break; case SDL_QUIT: case FF_QUIT_EVENT: do_exit(cur_stream); break; case FF_ALLOC_EVENT: alloc_picture(event.user.data1); break; default: break; } } }
event_loop 中有循環不停的獲取事件,具體的獲取事件及刷新視頻的操做在函數 refresh_loop_wait_event 中完成。.net
static void refresh_loop_wait_event(VideoState *is, SDL_Event *event) { double remaining_time = 0.0; SDL_PumpEvents();//更新事件隊列 while (!SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) { /* 根據時間差值判斷是否隱藏焦點 */ if (!cursor_hidden && av_gettime_relative() - cursor_last_shown > CURSOR_HIDE_DELAY) { SDL_ShowCursor(0); cursor_hidden = 1; } if (remaining_time > 0.0) av_usleep((int64_t)(remaining_time * 1000000.0)); remaining_time = REFRESH_RATE; if (is->show_mode != SHOW_MODE_NONE && (!is->paused || is->force_refresh)) video_refresh(is, &remaining_time); SDL_PumpEvents(); } }
其中,SDL_PumpEvents 函數主動收集來自輸入設備的事件,填入事件循環中,從而更新事件隊列。code
SDL_PeepEvents 會檢查事件隊列,若是隊列中有事件,回取出事件進行處理;若是沒有則會按照REFRESH_RATE的延遲去刷新視頻。視頻
具體的SDL事件機制能夠參考 https://my.oschina.net/u/735973/blog/832117 。blog