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