From 188fbb6622cdc0429f7f966e239354cc054cd077 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Mon, 13 Dec 2010 21:12:49 +0200 Subject: core: move central play loop to a separate function --- mplayer.c | 576 +++++++++++++++++++++++++++++++------------------------------- 1 file changed, 287 insertions(+), 289 deletions(-) diff --git a/mplayer.c b/mplayer.c index 4707936447..16726c7b0a 100644 --- a/mplayer.c +++ b/mplayer.c @@ -3027,6 +3027,291 @@ int seek_chapter(struct MPContext *mpctx, int chapter, double *seek_pts, return chapter; } + +static void run_playloop(struct MPContext *mpctx) +{ + struct MPOpts *opts = &mpctx->opts; + float aq_sleep_time = 0; + + if (opts->chapterrange[1] > 0) { + int cur_chapter = get_current_chapter(mpctx); + if (cur_chapter!=-1 && cur_chapter + 1 > opts->chapterrange[1]) + mpctx->stop_play = PT_NEXT_ENTRY; + } + + if (!mpctx->sh_audio && mpctx->d_audio->sh) { + mpctx->sh_audio = mpctx->d_audio->sh; + mpctx->sh_audio->ds = mpctx->d_audio; + reinit_audio_chain(mpctx); + } + +/*========================== PLAY AUDIO ============================*/ + + if (mpctx->sh_audio && !mpctx->paused + && (!mpctx->restart_playback || !mpctx->sh_video)) + if (!fill_audio_out_buffers(mpctx)) + // at eof, all audio at least written to ao + if (!mpctx->sh_video) + mpctx->stop_play = AT_END_OF_FILE; + + + if (!mpctx->sh_video) { + // handle audio-only case: + double a_pos = 0; + // sh_audio can be NULL due to video stream switching + // TODO: handle this better + if (mpctx->sh_audio) + a_pos = playing_audio_pts(mpctx); + + print_status(mpctx, a_pos, false); + + if (end_at.type == END_AT_TIME && end_at.pos < a_pos) + mpctx->stop_play = PT_NEXT_ENTRY; + update_subtitles(mpctx, &mpctx->opts, NULL, a_pos, mpctx->video_offset, + mpctx->d_sub, 0); + update_osd_msg(mpctx); + } else { + +/*========================== PLAY VIDEO ============================*/ + + vo_pts = mpctx->sh_video->timer * 90000.0; + vo_fps = mpctx->sh_video->fps; + + bool blit_frame = mpctx->video_out->frame_loaded; + if (!blit_frame) { + double frame_time = update_video(mpctx); + blit_frame = mpctx->video_out->frame_loaded; + mp_dbg(MSGT_AVSYNC, MSGL_DBG2, "*** ftime=%5.3f ***\n", frame_time); + if (mpctx->sh_video->vf_initialized < 0) { + mp_tmsg(MSGT_CPLAYER, MSGL_FATAL, + "\nFATAL: Could not initialize video filters (-vf) " + "or video output (-vo).\n"); + mpctx->stop_play = PT_NEXT_ENTRY; + return; + } + if (blit_frame) { + struct sh_video *sh_video = mpctx->sh_video; + update_subtitles(mpctx, &mpctx->opts, sh_video, sh_video->pts, + mpctx->video_offset, mpctx->d_sub, 0); + update_teletext(sh_video, mpctx->demuxer, 0); + update_osd_msg(mpctx); + struct vf_instance *vf = mpctx->sh_video->vfilter; + vf->control(vf, VFCTRL_DRAW_EOSD, NULL); + vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd); + vo_osd_changed(0); + } + if (frame_time < 0) + mpctx->stop_play = AT_END_OF_FILE; + else if (!mpctx->restart_playback) { + mpctx->time_frame += frame_time / opts->playback_speed; + adjust_sync(mpctx, frame_time); + } + } + if (mpctx->timeline) { + struct timeline_part *next = + mpctx->timeline + mpctx->timeline_part + 1; + if (mpctx->sh_video->pts >= next->start + || mpctx->stop_play == AT_END_OF_FILE + && mpctx->timeline_part + 1 < mpctx->num_timeline_parts) { + seek(mpctx, next->start, SEEK_ABSOLUTE); + return; + } + } + +// ========================================================================== + + current_module = "vo_check_events"; + vo_check_events(mpctx->video_out); + +#ifdef CONFIG_X11 + if (stop_xscreensaver) { + current_module = "stop_xscreensaver"; + xscreensaver_heartbeat(mpctx->x11_state); + } +#endif + if (heartbeat_cmd) { + static unsigned last_heartbeat; + unsigned now = GetTimerMS(); + if (now - last_heartbeat > 30000) { + last_heartbeat = now; + system(heartbeat_cmd); + } + } + + bool frame_time_remaining = sleep_until_update(mpctx, + &mpctx->time_frame, + &aq_sleep_time); + +//====================== FLIP PAGE (VIDEO BLT): ========================= + + current_module = "flip_page"; + if (!frame_time_remaining && blit_frame) { + unsigned int t2 = GetTimer(); + unsigned int pts_us = mpctx->last_time + mpctx->time_frame * 1e6; + int duration = -1; + double pts2 = mpctx->video_out->next_pts2; + if (pts2 != MP_NOPTS_VALUE && opts->correct_pts + && !mpctx->restart_playback) { + // expected A/V sync correction is ignored + double diff = (pts2 - mpctx->sh_video->pts); + diff /= opts->playback_speed; + if (mpctx->time_frame < 0) + diff += mpctx->time_frame; + if (diff < 0) + diff = 0; + if (diff > 10) + diff = 10; + duration = diff * 1e6; + } + vo_flip_page(mpctx->video_out, pts_us | 1, duration); + + mpctx->last_vo_flip_duration = (GetTimer() - t2) * 0.000001; + vout_time_usage += mpctx->last_vo_flip_duration; + if (mpctx->video_out->driver->flip_page_timed) { + // No need to adjust sync based on flip speed + mpctx->last_vo_flip_duration = 0; + // For print_status - VO call finishing early is OK for sync + mpctx->time_frame -= get_relative_time(mpctx); + } + if (mpctx->restart_playback) { + mpctx->syncing_audio = true; + if (mpctx->sh_audio && !mpctx->paused) + fill_audio_out_buffers(mpctx); + mpctx->restart_playback = false; + mpctx->time_frame = 0; + get_relative_time(mpctx); + } + print_status(mpctx, MP_NOPTS_VALUE, true); + } else + print_status(mpctx, MP_NOPTS_VALUE, false); + +//============================ Auto QUALITY ============================ + +/*Output quality adjustments:*/ + if (opts->auto_quality > 0) { + current_module = "autoq"; + if (output_quality < opts->auto_quality && aq_sleep_time > 0) + ++output_quality; + else + if (output_quality>1 && aq_sleep_time<0) + --output_quality; + else if (output_quality>0 && aq_sleep_time<-0.050f) // 50ms + output_quality = 0; + set_video_quality(mpctx->sh_video, output_quality); + } + + if (!frame_time_remaining && blit_frame) { + if (play_n_frames >= 0) { + --play_n_frames; + if (play_n_frames <= 0) + mpctx->stop_play = PT_NEXT_ENTRY; + } + if (mpctx->step_frames > 0) { + mpctx->step_frames--; + if (mpctx->step_frames == 0) + pause_player(mpctx); + } + } + +// FIXME: add size based support for -endpos + if (end_at.type == END_AT_TIME && + !frame_time_remaining && end_at.pos <= mpctx->sh_video->pts) + mpctx->stop_play = PT_NEXT_ENTRY; + + } // end if(mpctx->sh_video) + +#ifdef CONFIG_DVDNAV + if (mpctx->stream->type == STREAMTYPE_DVDNAV) { + nav_highlight_t hl; + mp_dvdnav_get_highlight(mpctx->stream, &hl); + if (!vo_spudec || !spudec_apply_palette_crop(vo_spudec, hl.palette, hl.sx, hl.sy, hl.ex, hl.ey)) { + osd_set_nav_box(hl.sx, hl.sy, hl.ex, hl.ey); + vo_osd_changed(OSDTYPE_DVDNAV); + } else { + osd_set_nav_box(0, 0, 0, 0); + vo_osd_changed(OSDTYPE_DVDNAV); + vo_osd_changed(OSDTYPE_SPU); + } + + if (mp_dvdnav_stream_has_changed(mpctx->stream)) { + double ar = -1.0; + if (mpctx->sh_video && + stream_control(mpctx->demuxer->stream, + STREAM_CTRL_GET_ASPECT_RATIO, &ar) + != STREAM_UNSUPPORTED) + mpctx->sh_video->stream_aspect = ar; + } + } +#endif + +//================= Keyboard events, SEEKing ==================== + + current_module = "key_events"; + + while (1) { + mp_cmd_t *cmd; + while ((cmd = mp_input_get_cmd(mpctx->input, 0, 0)) != NULL) { + run_command(mpctx, cmd); + mp_cmd_free(cmd); + if (mpctx->stop_play) + break; + if (mpctx->rel_seek_secs || mpctx->abs_seek_pos) { + cmd = mp_input_get_cmd(mpctx->input, 0, 1); + /* Allow seek commands to be combined, but execute the real seek + * before processing other commands */ + if (!cmd || cmd->id != MP_CMD_SEEK) + break; + } + } + if (!mpctx->paused || mpctx->stop_play || mpctx->rel_seek_secs + || mpctx->abs_seek_pos) + break; + if (mpctx->sh_video) { + update_osd_msg(mpctx); + int hack = vo_osd_changed(0); + vo_osd_changed(hack); + if (hack) { + if (redraw_osd(mpctx->sh_video, mpctx->osd) < 0) { + add_step_frame(mpctx); + break; + } else + vo_osd_changed(0); + } + } + pause_loop(mpctx); + } + +// handle -sstep + if (step_sec > 0 && !mpctx->paused) { + mpctx->osd_function = OSD_FFW; + mpctx->rel_seek_secs += step_sec; + } + + edl_update(mpctx); + + /* Looping. */ + if (mpctx->stop_play==AT_END_OF_FILE && opts->loop_times>=0) { + mp_msg(MSGT_CPLAYER, MSGL_V, "loop_times = %d\n", opts->loop_times); + + if (opts->loop_times>1) + opts->loop_times--; + else if (opts->loop_times==1) + opts->loop_times = -1; + play_n_frames = play_n_frames_mf; + mpctx->stop_play = 0; + mpctx->abs_seek_pos = SEEK_ABSOLUTE; + mpctx->rel_seek_secs = seek_to_sec; + } + + if (mpctx->rel_seek_secs || mpctx->abs_seek_pos) { + seek(mpctx, mpctx->rel_seek_secs, mpctx->abs_seek_pos); + + mpctx->rel_seek_secs = 0; + mpctx->abs_seek_pos = 0; + } +} + + static int find_ordered_chapter_sources(struct MPContext *mpctx, struct content_source *sources, int num_sources, @@ -4127,8 +4412,6 @@ if (verbose) opts->term_osd = 0; { -int frame_time_remaining=0; // flag -int blit_frame=0; // Make sure old OSD does not stay around, // e.g. with -fixed-vo and same-resolution files @@ -4240,293 +4523,8 @@ if (mpctx->stream->type == STREAMTYPE_DVDNAV) { vo_control(mpctx->video_out, mpctx->paused ? VOCTRL_PAUSE : VOCTRL_RESUME, NULL); -while(!mpctx->stop_play){ - float aq_sleep_time=0; - -if (opts->chapterrange[1] > 0) { - int cur_chapter = get_current_chapter(mpctx); - if(cur_chapter!=-1 && cur_chapter+1 > opts->chapterrange[1]) - goto goto_next_file; -} - -if(!mpctx->sh_audio && mpctx->d_audio->sh) { - mpctx->sh_audio = mpctx->d_audio->sh; - mpctx->sh_audio->ds = mpctx->d_audio; - reinit_audio_chain(mpctx); -} - -/*========================== PLAY AUDIO ============================*/ - -if (mpctx->sh_audio && !mpctx->paused - && (!mpctx->restart_playback || !mpctx->sh_video)) - if (!fill_audio_out_buffers(mpctx)) - // at eof, all audio at least written to ao - if (!mpctx->sh_video) - mpctx->stop_play = AT_END_OF_FILE; - - -if(!mpctx->sh_video) { - // handle audio-only case: - double a_pos=0; - // sh_audio can be NULL due to video stream switching - // TODO: handle this better - if (mpctx->sh_audio) - a_pos = playing_audio_pts(mpctx); - - print_status(mpctx, a_pos, false); - - if(end_at.type == END_AT_TIME && end_at.pos < a_pos) - mpctx->stop_play = PT_NEXT_ENTRY; - update_subtitles(mpctx, &mpctx->opts, NULL, a_pos, mpctx->video_offset, - mpctx->d_sub, 0); - update_osd_msg(mpctx); - -} else { - -/*========================== PLAY VIDEO ============================*/ - - vo_pts=mpctx->sh_video->timer*90000.0; - vo_fps=mpctx->sh_video->fps; - - blit_frame = mpctx->video_out->frame_loaded; - if (!blit_frame) { - double frame_time = update_video(mpctx); - blit_frame = mpctx->video_out->frame_loaded; - mp_dbg(MSGT_AVSYNC,MSGL_DBG2,"*** ftime=%5.3f ***\n",frame_time); - if (mpctx->sh_video->vf_initialized < 0) { - mp_tmsg(MSGT_CPLAYER,MSGL_FATAL, "\nFATAL: Could not initialize video filters (-vf) or video output (-vo).\n"); - mpctx->stop_play = PT_NEXT_ENTRY; - goto goto_next_file; - } - if (blit_frame) { - struct sh_video *sh_video = mpctx->sh_video; - update_subtitles(mpctx, &mpctx->opts, sh_video, sh_video->pts, - mpctx->video_offset, mpctx->d_sub, 0); - update_teletext(sh_video, mpctx->demuxer, 0); - update_osd_msg(mpctx); - struct vf_instance *vf = mpctx->sh_video->vfilter; - vf->control(vf, VFCTRL_DRAW_EOSD, NULL); - vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd); - vo_osd_changed(0); - } - if (frame_time < 0) - mpctx->stop_play = AT_END_OF_FILE; - else if (!mpctx->restart_playback) { - mpctx->time_frame += frame_time / opts->playback_speed; - adjust_sync(mpctx, frame_time); - } - } - if (mpctx->timeline) { - struct timeline_part *next = mpctx->timeline + mpctx->timeline_part + 1; - if (mpctx->sh_video->pts >= next->start - || mpctx->stop_play == AT_END_OF_FILE - && mpctx->timeline_part + 1 < mpctx->num_timeline_parts) { - seek(mpctx, next->start, SEEK_ABSOLUTE); - continue; - } - } - -// ========================================================================== - -// current_module="draw_osd"; -// if(vo_config_count) mpctx->video_out->draw_osd(); - - current_module="vo_check_events"; - vo_check_events(mpctx->video_out); - -#ifdef CONFIG_X11 - if (stop_xscreensaver) { - current_module = "stop_xscreensaver"; - xscreensaver_heartbeat(mpctx->x11_state); - } -#endif - if (heartbeat_cmd) { - static unsigned last_heartbeat; - unsigned now = GetTimerMS(); - if (now - last_heartbeat > 30000) { - last_heartbeat = now; - system(heartbeat_cmd); - } - } - - frame_time_remaining = sleep_until_update(mpctx, &mpctx->time_frame, &aq_sleep_time); - -//====================== FLIP PAGE (VIDEO BLT): ========================= - - current_module="flip_page"; - if (!frame_time_remaining && blit_frame) { - unsigned int t2=GetTimer(); - unsigned int pts_us = mpctx->last_time + mpctx->time_frame * 1e6; - int duration = -1; - double pts2 = mpctx->video_out->next_pts2; - if (pts2 != MP_NOPTS_VALUE && opts->correct_pts - && !mpctx->restart_playback) { - // expected A/V sync correction is ignored - double diff = (pts2 - mpctx->sh_video->pts); - diff /= opts->playback_speed; - if (mpctx->time_frame < 0) - diff += mpctx->time_frame; - if (diff < 0) - diff = 0; - if (diff > 10) - diff = 10; - duration = diff * 1e6; - } - vo_flip_page(mpctx->video_out, pts_us|1, duration); - - mpctx->last_vo_flip_duration = (GetTimer() - t2) * 0.000001; - vout_time_usage += mpctx->last_vo_flip_duration; - if (mpctx->video_out->driver->flip_page_timed) { - // No need to adjust sync based on flip speed - mpctx->last_vo_flip_duration = 0; - // For print_status - VO call finishing early is OK for sync - mpctx->time_frame -= get_relative_time(mpctx); - } - if (mpctx->restart_playback) { - mpctx->syncing_audio = true; - if (mpctx->sh_audio && !mpctx->paused) - fill_audio_out_buffers(mpctx); - mpctx->restart_playback = false; - mpctx->time_frame = 0; - get_relative_time(mpctx); - } - print_status(mpctx, MP_NOPTS_VALUE, true); - } - else - print_status(mpctx, MP_NOPTS_VALUE, false); - -//============================ Auto QUALITY ============================ - -/*Output quality adjustments:*/ -if (opts->auto_quality > 0) { - current_module="autoq"; -// float total=0.000001f * (GetTimer()-aq_total_time); -// if (output_quality < opts->auto_quality && aq_sleep_time > 0.05f * total) - if (output_quality < opts->auto_quality && aq_sleep_time > 0) - ++output_quality; - else -// if(output_quality>0 && aq_sleep_time<-0.05f*total) - if(output_quality>1 && aq_sleep_time<0) - --output_quality; - else - if(output_quality>0 && aq_sleep_time<-0.050f) // 50ms - output_quality=0; -// printf("total: %8.6f sleep: %8.6f q: %d\n",(0.000001f*aq_total_time),aq_sleep_time,output_quality); - set_video_quality(mpctx->sh_video,output_quality); -} - - if (!frame_time_remaining && blit_frame) { - if (play_n_frames >= 0) { - --play_n_frames; - if (play_n_frames <= 0) - mpctx->stop_play = PT_NEXT_ENTRY; - } - if (mpctx->step_frames > 0) { - mpctx->step_frames--; - if (mpctx->step_frames == 0) - pause_player(mpctx); - } - } - - -// FIXME: add size based support for -endpos - if (end_at.type == END_AT_TIME && - !frame_time_remaining && end_at.pos <= mpctx->sh_video->pts) - mpctx->stop_play = PT_NEXT_ENTRY; - -} // end if(mpctx->sh_video) - -#ifdef CONFIG_DVDNAV - if (mpctx->stream->type == STREAMTYPE_DVDNAV) { - nav_highlight_t hl; - mp_dvdnav_get_highlight (mpctx->stream, &hl); - if (!vo_spudec || !spudec_apply_palette_crop(vo_spudec, hl.palette, hl.sx, hl.sy, hl.ex, hl.ey)) { - osd_set_nav_box (hl.sx, hl.sy, hl.ex, hl.ey); - vo_osd_changed (OSDTYPE_DVDNAV); - } else { - osd_set_nav_box(0, 0, 0, 0); - vo_osd_changed(OSDTYPE_DVDNAV); - vo_osd_changed(OSDTYPE_SPU); - } - - if (mp_dvdnav_stream_has_changed(mpctx->stream)) { - double ar = -1.0; - if (mpctx->sh_video && - stream_control (mpctx->demuxer->stream, - STREAM_CTRL_GET_ASPECT_RATIO, &ar) - != STREAM_UNSUPPORTED) - mpctx->sh_video->stream_aspect = ar; - } - } -#endif - -//================= Keyboard events, SEEKing ==================== - - current_module="key_events"; - -{ - while (1) { - mp_cmd_t* cmd; - while ((cmd = mp_input_get_cmd(mpctx->input, 0, 0)) != NULL) { - run_command(mpctx, cmd); - mp_cmd_free(cmd); - if (mpctx->stop_play) - break; - if (mpctx->rel_seek_secs || mpctx->abs_seek_pos) { - cmd = mp_input_get_cmd(mpctx->input, 0, 1); - /* Allow seek commands to be combined, but execute the real seek - * before processing other commands */ - if (!cmd || cmd->id != MP_CMD_SEEK) - break; - } - } - if (!mpctx->paused || mpctx->stop_play || mpctx->rel_seek_secs - || mpctx->abs_seek_pos) - break; - if (mpctx->sh_video) { - update_osd_msg(mpctx); - int hack = vo_osd_changed(0); - vo_osd_changed(hack); - if (hack) { - if (redraw_osd(mpctx->sh_video, mpctx->osd) < 0) { - add_step_frame(mpctx); - break; - } - else - vo_osd_changed(0); - } - } - pause_loop(mpctx); - } -} - -// handle -sstep -if (step_sec > 0 && !mpctx->paused) { - mpctx->osd_function=OSD_FFW; - mpctx->rel_seek_secs+=step_sec; -} - - edl_update(mpctx); - - /* Looping. */ - if(mpctx->stop_play==AT_END_OF_FILE && opts->loop_times>=0) { - mp_msg(MSGT_CPLAYER,MSGL_V,"loop_times = %d\n", opts->loop_times); - - if(opts->loop_times>1) opts->loop_times--; else - if(opts->loop_times==1) opts->loop_times=-1; - play_n_frames=play_n_frames_mf; - mpctx->stop_play=0; - mpctx->abs_seek_pos=SEEK_ABSOLUTE; mpctx->rel_seek_secs=seek_to_sec; - } - -if(mpctx->rel_seek_secs || mpctx->abs_seek_pos){ - seek(mpctx, mpctx->rel_seek_secs, mpctx->abs_seek_pos); - - mpctx->rel_seek_secs=0; - mpctx->abs_seek_pos=0; -} - -} // while(!mpctx->stop_play) + while (!mpctx->stop_play) + run_playloop(mpctx); mp_msg(MSGT_GLOBAL,MSGL_V,"EOF code: %d \n",mpctx->stop_play); -- cgit v1.2.3 From 2ba074e613d79600023128974ea60a75251480b5 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Tue, 14 Dec 2010 20:20:30 +0200 Subject: core: timing: add special handling of long frame intervals Add separate handling for the case where the time to flip the next frame on the VO is more than 50 ms away. In that case don't update OSD contents yet, but wait for possible changes until 50 ms before the frame. Sleep until that time in mp_input_get_cmd(), so the sleep is done in select() and input events can be responded to immediately. Also raise the limit on audio out delay used to limit sleep. --- mplayer.c | 71 ++++++++++++++++++++++++++++++++------------------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/mplayer.c b/mplayer.c index 16726c7b0a..8274d936ff 100644 --- a/mplayer.c +++ b/mplayer.c @@ -2280,11 +2280,11 @@ static int fill_audio_out_buffers(struct MPContext *mpctx) return 1; } -static int sleep_until_update(struct MPContext *mpctx, float *time_frame, - float *aq_sleep_time) +static int sleep_until_near_frame(struct MPContext *mpctx, float *time_frame, + float *aq_sleep_time) { struct MPOpts *opts = &mpctx->opts; - int frame_time_remaining = 0; + double audio_limit = 2; current_module="calc_sleep_time"; *time_frame -= get_relative_time(mpctx); // reset timer @@ -2309,17 +2309,11 @@ static int sleep_until_update(struct MPContext *mpctx, float *time_frame, } *time_frame = delay - mpctx->delay / opts->playback_speed; - *time_frame -= mpctx->video_out->flip_queue_offset; // delay = amount of audio buffered in soundcard/driver - if (delay > 0.25) delay=0.25; else - if (delay < 0.10) delay=0.10; - - if (*time_frame > delay*0.6) { - // sleep time too big - may cause audio drops (buffer underrun) - frame_time_remaining = 1; - *time_frame = delay*0.5; - } + delay = FFMIN(delay, 0.5); + delay = FFMAX(delay, 0.1); + audio_limit = delay; } else { // If we're lagging more than 200 ms behind the right playback rate, // don't try to "catch up". @@ -2327,19 +2321,19 @@ static int sleep_until_update(struct MPContext *mpctx, float *time_frame, // without sleeping. if (*time_frame < -0.2 || opts->benchmark) *time_frame = 0; - *time_frame -= mpctx->video_out->flip_queue_offset; } - *aq_sleep_time += *time_frame; + double t = *time_frame - mpctx->video_out->flip_queue_offset; + if (t <= 0.05) + return 0; - //============================== SLEEP: =================================== - - // flag 256 means: libvo driver does its timing (dvb card) - if (*time_frame > 0.001 && !(mpctx->sh_video->output_flags&256)) - *time_frame = timing_sleep(mpctx, *time_frame); - *time_frame += mpctx->video_out->flip_queue_offset; - return frame_time_remaining; + t -= 0.05; + if (t > audio_limit * 0.6) + t = audio_limit * 0.5; + *aq_sleep_time += t; + mp_input_get_cmd(mpctx->input, t * 1000 + 1, 1); + return 1; } int reinit_video_chain(struct MPContext *mpctx) @@ -3089,17 +3083,6 @@ static void run_playloop(struct MPContext *mpctx) mpctx->stop_play = PT_NEXT_ENTRY; return; } - if (blit_frame) { - struct sh_video *sh_video = mpctx->sh_video; - update_subtitles(mpctx, &mpctx->opts, sh_video, sh_video->pts, - mpctx->video_offset, mpctx->d_sub, 0); - update_teletext(sh_video, mpctx->demuxer, 0); - update_osd_msg(mpctx); - struct vf_instance *vf = mpctx->sh_video->vfilter; - vf->control(vf, VFCTRL_DRAW_EOSD, NULL); - vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd); - vo_osd_changed(0); - } if (frame_time < 0) mpctx->stop_play = AT_END_OF_FILE; else if (!mpctx->restart_playback) { @@ -3138,14 +3121,32 @@ static void run_playloop(struct MPContext *mpctx) } } - bool frame_time_remaining = sleep_until_update(mpctx, - &mpctx->time_frame, - &aq_sleep_time); + bool frame_time_remaining = sleep_until_near_frame(mpctx, + &mpctx->time_frame, + &aq_sleep_time); //====================== FLIP PAGE (VIDEO BLT): ========================= current_module = "flip_page"; if (!frame_time_remaining && blit_frame) { + struct sh_video *sh_video = mpctx->sh_video; + update_subtitles(mpctx, &mpctx->opts, sh_video, sh_video->pts, + mpctx->video_offset, mpctx->d_sub, 0); + update_teletext(sh_video, mpctx->demuxer, 0); + update_osd_msg(mpctx); + struct vf_instance *vf = sh_video->vfilter; + vf->control(vf, VFCTRL_DRAW_EOSD, NULL); + vf->control(vf, VFCTRL_DRAW_OSD, mpctx->osd); + vo_osd_changed(0); + + mpctx->time_frame -= mpctx->video_out->flip_queue_offset; + aq_sleep_time += mpctx->time_frame; + // flag 256 means: libvo driver does its timing (dvb card) + if (mpctx->time_frame > 0.001 + && !(mpctx->sh_video->output_flags&256)) + mpctx->time_frame = timing_sleep(mpctx, mpctx->time_frame); + mpctx->time_frame += mpctx->video_out->flip_queue_offset; + unsigned int t2 = GetTimer(); unsigned int pts_us = mpctx->last_time + mpctx->time_frame * 1e6; int duration = -1; -- cgit v1.2.3 From 4f610adfc29a2aa2f1159ded016ca4f8960c52e9 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Tue, 14 Dec 2010 21:58:47 +0200 Subject: libvo: register X11 connection fd in input event system Register the X11 connection fd in the input system so that mp_input_get_cmd() can immediately wake up and handle keyboard or other X events. The callback calls vo_check_events() and tells the input system to handle any input possibly recorded during that. Before this was done for vo_xv only; this commit generalizes it to all VOs that call vo_x11_create_vo_window() - those are hopefully ones that will handle all X events in check_events(). The callback is only kept registered while the vo is properly configured. At other times calling check_events() would not clear pending input and so could lead to a busy loop. --- libvo/video_out.c | 24 +++++++++++++++++++++++- libvo/video_out.h | 2 ++ libvo/vo_xv.c | 16 ---------------- libvo/x11_common.c | 1 + 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/libvo/video_out.c b/libvo/video_out.c index 763527a4c1..3e2e108d4b 100644 --- a/libvo/video_out.c +++ b/libvo/video_out.c @@ -35,6 +35,8 @@ #include "geometry.h" #include "old_vo_wrapper.h" #include "input/input.h" +#include "mp_fifo.h" + #include "mp_msg.h" @@ -352,8 +354,12 @@ void vo_flip_page(struct vo *vo, unsigned int pts_us, int duration) void vo_check_events(struct vo *vo) { - if (!vo->config_ok) + if (!vo->config_ok) { + if (vo->registered_fd != -1) + mp_input_rm_key_fd(vo->input_ctx, vo->registered_fd); + vo->registered_fd = -1; return; + } vo->driver->check_events(vo); } @@ -365,6 +371,8 @@ void vo_seek_reset(struct vo *vo) void vo_destroy(struct vo *vo) { + if (vo->registered_fd != -1) + mp_input_rm_key_fd(vo->input_ctx, vo->registered_fd); vo->driver->uninit(vo); talloc_free(vo); } @@ -393,6 +401,8 @@ struct vo *init_best_video_out(struct MPOpts *opts, struct vo_x11_state *x11, .x11 = x11, .key_fifo = key_fifo, .input_ctx = input_ctx, + .event_fd = -1, + .registered_fd = -1, }; // first try the preferred drivers, with their optional subdevice param: if (vo_list && vo_list[0]) @@ -441,6 +451,13 @@ struct vo *init_best_video_out(struct MPOpts *opts, struct vo_x11_state *x11, return NULL; } +static int event_fd_callback(void *ctx, int fd) +{ + struct vo *vo = ctx; + vo_check_events(vo); + return mplayer_get_key(vo->key_fifo, 0); +} + int vo_config(struct vo *vo, uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t flags, char *title, uint32_t format) @@ -467,6 +484,11 @@ int vo_config(struct vo *vo, uint32_t width, uint32_t height, title, format); vo->config_ok = (ret == 0); vo->config_count += vo->config_ok; + if (vo->registered_fd == -1 && vo->event_fd != -1 && vo->config_ok) { + mp_input_add_key_fd(vo->input_ctx, vo->event_fd, 1, event_fd_callback, + NULL, vo); + vo->registered_fd = vo->event_fd; + } return ret; } diff --git a/libvo/video_out.h b/libvo/video_out.h index 39ea26a53a..36a161aaf5 100644 --- a/libvo/video_out.h +++ b/libvo/video_out.h @@ -251,6 +251,8 @@ struct vo { struct vo_x11_state *x11; struct mp_fifo *key_fifo; struct input_ctx *input_ctx; + int event_fd; // check_events() should be called when this has input + int registered_fd; // set to event_fd when registered in input system // requested position/resolution int dx; diff --git a/libvo/vo_xv.c b/libvo/vo_xv.c index 0dbc600847..63221782e2 100644 --- a/libvo/vo_xv.c +++ b/libvo/vo_xv.c @@ -61,9 +61,6 @@ Buffer allocation: #include "subopt-helper.h" -#include "input/input.h" -#include "mp_fifo.h" - #include "libavutil/common.h" static const vo_info_t info = { @@ -104,7 +101,6 @@ struct xvctx { struct vo_rect src_rect; struct vo_rect dst_rect; uint32_t max_width, max_height; // zero means: not set - int event_fd_registered; // for uninit called from preinit int mode_switched; int osd_objects_drawn; void (*draw_alpha_fnc)(void *ctx, int x0, int y0, int w, int h, @@ -645,19 +641,10 @@ static void uninit(struct vo *vo) if (ctx->mode_switched) vo_vm_close(vo); #endif - if (ctx->event_fd_registered) - mp_input_rm_key_fd(vo->input_ctx, ConnectionNumber(vo->x11->display)); // uninit() shouldn't get called unless initialization went past vo_init() vo_x11_uninit(vo); } -static int x11_fd_callback(void *ctx, int fd) -{ - struct vo *vo = ctx; - check_events(vo); - return mplayer_get_key(vo->key_fifo, 0); -} - static int preinit(struct vo *vo, const char *arg) { XvPortID xv_p; @@ -780,9 +767,6 @@ static int preinit(struct vo *vo, const char *arg) ctx->fo = XvListImageFormats(x11->display, x11->xv_port, (int *) &ctx->formats); - mp_input_add_key_fd(vo->input_ctx, ConnectionNumber(x11->display), 1, - x11_fd_callback, NULL, vo); - ctx->event_fd_registered = 1; return 0; error: diff --git a/libvo/x11_common.c b/libvo/x11_common.c index 655b1c69d0..b7fbb72307 100644 --- a/libvo/x11_common.c +++ b/libvo/x11_common.c @@ -1123,6 +1123,7 @@ final: x11->vo_gc = XCreateGC(mDisplay, x11->window, GCForeground, &xgcv); XSync(mDisplay, False); x11->vo_mouse_autohide = 1; + vo->event_fd = ConnectionNumber(x11->display); } void vo_x11_clearwindow_part(struct vo *vo, Window vo_window, -- cgit v1.2.3 From ff706ee86d4ca1e31de6c6c322a3bedb819ead05 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Tue, 14 Dec 2010 22:31:39 +0200 Subject: core: avoid using sh_video->pts as "current pts" Add a new field "video_pts" to mpctx. It records the time of the last frame flipped visible on VO. Change various code which used sh_video->pts to use either the new field or get_current_time(mpctx). --- command.c | 12 +++++------- mp_core.h | 3 +++ mplayer.c | 19 +++++++++++-------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/command.c b/command.c index 4ffe29a351..489cd2d047 100644 --- a/command.c +++ b/command.c @@ -2689,14 +2689,12 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0; if (abs == 2) { /* Absolute seek to a specific timestamp in seconds */ mpctx->abs_seek_pos = SEEK_ABSOLUTE; - if (sh_video) - mpctx->osd_function = - (v > sh_video->pts) ? OSD_FFW : OSD_REW; + mpctx->osd_function = v > get_current_time(mpctx) ? + OSD_FFW : OSD_REW; mpctx->rel_seek_secs = v; } else if (abs) { /* Absolute seek by percentage */ mpctx->abs_seek_pos = SEEK_ABSOLUTE | SEEK_FACTOR; - if (sh_video) - mpctx->osd_function = OSD_FFW; // Direction isn't set correctly + mpctx->osd_function = OSD_FFW; // Direction isn't set correctly mpctx->rel_seek_secs = v / 100.0; } else { mpctx->rel_seek_secs += v; @@ -2905,12 +2903,12 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) case MP_CMD_SUB_STEP: if (sh_video) { int movement = cmd->args[0].v.i; - step_sub(subdata, sh_video->pts, movement); + step_sub(subdata, mpctx->video_pts, movement); #ifdef CONFIG_ASS if (ass_track) sub_delay += ass_step_sub(ass_track, - (sh_video->pts + + (mpctx->video_pts + sub_delay) * 1000 + .5, movement) / 1000.; #endif set_osd_tmsg(OSD_MSG_SUB_DELAY, 1, osd_duration, diff --git a/mp_core.h b/mp_core.h index 206cbdacd3..1111ee49c4 100644 --- a/mp_core.h +++ b/mp_core.h @@ -148,6 +148,9 @@ typedef struct MPContext { // the same value if the status line is updated at a time where no new // video frame is shown. double last_av_difference; + /* timestamp of video frame currently visible on screen + * (or at least queued to be flipped by VO) */ + double video_pts; // Timestamp from the last time some timing functions read the // current time, in (occasionally wrapping) microseconds. Used diff --git a/mplayer.c b/mplayer.c index 8274d936ff..9ad00beaf9 100644 --- a/mplayer.c +++ b/mplayer.c @@ -1272,7 +1272,7 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame) if (mpctx->sh_audio && a_pos == MP_NOPTS_VALUE) a_pos = playing_audio_pts(mpctx); if (mpctx->sh_audio && sh_video && at_frame) { - mpctx->last_av_difference = a_pos - sh_video->pts - audio_delay; + mpctx->last_av_difference = a_pos - mpctx->video_pts - audio_delay; if (mpctx->time_frame > 0) mpctx->last_av_difference += mpctx->time_frame * opts->playback_speed; if (mpctx->last_av_difference > 0.5 && drop_frame_cnt > 50 @@ -1315,7 +1315,7 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame) // Video time if (sh_video) - saddf(line, &pos, width, "V:%6.1f ", sh_video->pts); + saddf(line, &pos, width, "V:%6.1f ", mpctx->video_pts); // A-V sync if (mpctx->sh_audio && sh_video) @@ -2131,7 +2131,7 @@ static int audio_start_sync(struct MPContext *mpctx, int playsize) int bytes; while (1) { double written_pts = written_audio_pts(mpctx); - double ptsdiff = written_pts - mpctx->sh_video->pts - mpctx->delay + double ptsdiff = written_pts - mpctx->video_pts - mpctx->delay - audio_delay; bytes = ptsdiff * ao_data.bps / mpctx->opts.playback_speed; bytes -= bytes % (ao_data.channels * af_fmt2bits(ao_data.format) / 8); @@ -2712,7 +2712,7 @@ static void edl_seek_reset(MPContext *mpctx) next_edl_record = edl_records; while (next_edl_record) { - if (next_edl_record->start_sec >= mpctx->sh_video->pts) + if (next_edl_record->start_sec >= get_current_time(mpctx)) break; if (next_edl_record->action == EDL_MUTE) @@ -2738,7 +2738,7 @@ static void edl_update(MPContext *mpctx) return; } - if (mpctx->sh_video->pts >= next_edl_record->start_sec) { + if (get_current_time(mpctx) >= next_edl_record->start_sec) { if (next_edl_record->action == EDL_SKIP) { mpctx->osd_function = OSD_FFW; mpctx->abs_seek_pos = 0; @@ -2782,6 +2782,7 @@ static void seek_reset(struct MPContext *mpctx) // (which is used by at least vobsub and edl code below) may // be completely wrong (probably 0). mpctx->sh_video->pts = mpctx->d_video->pts + mpctx->video_offset; + mpctx->video_pts = mpctx->sh_video->pts; update_subtitles(mpctx, &mpctx->opts, mpctx->sh_video, mpctx->sh_video->pts, mpctx->video_offset, mpctx->d_sub, 1); @@ -2872,7 +2873,7 @@ static int seek(MPContext *mpctx, double amount, int style) style |= SEEK_FORWARD; else style |= SEEK_BACKWARD; - amount += mpctx->sh_video->pts; + amount += get_current_time(mpctx); } /* At least the liba52 decoder wants to read from the input stream @@ -2939,7 +2940,7 @@ double get_current_time(struct MPContext *mpctx) return demuxer->stream_pts; struct sh_video *sh_video = demuxer->video->sh; if (sh_video) - return sh_video->pts; + return mpctx->video_pts; return playing_audio_pts(mpctx); } @@ -3130,6 +3131,7 @@ static void run_playloop(struct MPContext *mpctx) current_module = "flip_page"; if (!frame_time_remaining && blit_frame) { struct sh_video *sh_video = mpctx->sh_video; + mpctx->video_pts = sh_video->pts; update_subtitles(mpctx, &mpctx->opts, sh_video, sh_video->pts, mpctx->video_offset, mpctx->d_sub, 0); update_teletext(sh_video, mpctx->demuxer, 0); @@ -3154,7 +3156,7 @@ static void run_playloop(struct MPContext *mpctx) if (pts2 != MP_NOPTS_VALUE && opts->correct_pts && !mpctx->restart_playback) { // expected A/V sync correction is ignored - double diff = (pts2 - mpctx->sh_video->pts); + double diff = (pts2 - mpctx->video_pts); diff /= opts->playback_speed; if (mpctx->time_frame < 0) diff += mpctx->time_frame; @@ -4517,6 +4519,7 @@ if (mpctx->stream->type == STREAMTYPE_DVDNAV) { mpctx->time_frame = 0; mpctx->drop_message_shown = 0; mpctx->restart_playback = true; + mpctx->video_pts = 0; mpctx->total_avsync_change = 0; mpctx->last_chapter_seek = -1; // Make sure VO knows current pause state -- cgit v1.2.3 From de3be1d9cafaa35b540e33a5f042804bef8e97d8 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Tue, 14 Dec 2010 22:33:55 +0200 Subject: core: seek: use accurate seek mode with audio-only files Allow more accurate absolute-only seeks even if there is no video. --- mplayer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mplayer.c b/mplayer.c index 9ad00beaf9..08a9666a7f 100644 --- a/mplayer.c +++ b/mplayer.c @@ -2866,7 +2866,7 @@ static int seek(MPContext *mpctx, double amount, int style) amount *= mpctx->timeline[mpctx->num_timeline_parts].start; style &= ~SEEK_FACTOR; } - if ((mpctx->demuxer->accurate_seek || mpctx->timeline) && mpctx->sh_video + if ((mpctx->demuxer->accurate_seek || mpctx->timeline) && !(style & (SEEK_ABSOLUTE | SEEK_FACTOR))) { style |= SEEK_ABSOLUTE; if (amount > 0) -- cgit v1.2.3 From 00c2bafb0ac25a9c5ce845f49f6718b06ff03bbe Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Wed, 15 Dec 2010 01:02:14 +0200 Subject: core: remove looping in update_video(), modify command handling a bit Let higher-level code call update_video() again instead of looping inside it until there's a frame ready to show. Change the conditions for running user commands somewhat. Overall effect shouldn't be that big. Now other commands can be executed after a seek before a video frame is decoded; in this case the seek target time may be used as the "current position". --- mp_core.h | 3 +++ mplayer.c | 71 +++++++++++++++++++++++++++++++++++++++------------------------ 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/mp_core.h b/mp_core.h index 1111ee49c4..c02edd4155 100644 --- a/mp_core.h +++ b/mp_core.h @@ -152,6 +152,9 @@ typedef struct MPContext { * (or at least queued to be flipped by VO) */ double video_pts; + // used to prevent hanging in some error cases + unsigned int start_timestamp; + // Timestamp from the last time some timing functions read the // current time, in (occasionally wrapping) microseconds. Used // to turn a new time value to a delta from last time. diff --git a/mplayer.c b/mplayer.c index 08a9666a7f..25d0baac90 100644 --- a/mplayer.c +++ b/mplayer.c @@ -2457,13 +2457,13 @@ static double update_video_nocorrect_pts(struct MPContext *mpctx) struct sh_video *sh_video = mpctx->sh_video; double frame_time = 0; struct vo *video_out = mpctx->video_out; - while (!video_out->frame_loaded) { + while (1) { current_module = "filter_video"; // In nocorrect-pts mode there is no way to properly time these frames if (vo_get_buffered_frame(video_out, 0) >= 0) break; if (vf_output_queued_frame(sh_video->vfilter)) - continue; + break; unsigned char *packet = NULL; frame_time = sh_video->next_frame_time; if (mpctx->restart_playback) @@ -2507,10 +2507,9 @@ static double update_video_nocorrect_pts(struct MPContext *mpctx) #endif if (decoded_frame) { current_module = "filter video"; - if (filter_video(sh_video, decoded_frame, sh_video->pts)) - if (!video_out->config_ok) - break; + filter_video(sh_video, decoded_frame, sh_video->pts); } + break; } return frame_time; } @@ -2557,21 +2556,19 @@ static double update_video(struct MPContext *mpctx) double pts; - bool hit_eof = false; - while (!video_out->frame_loaded) { + while (1) { current_module = "filter_video"; - if (vo_get_buffered_frame(video_out, hit_eof) >= 0) + if (vo_get_buffered_frame(video_out, false) >= 0) break; // XXX Time used in this call is not counted in any performance - // timer now, OSD time is not updated correctly for filter-added frames + // timer now if (vf_output_queued_frame(sh_video->vfilter)) - continue; - if (hit_eof) - return -1; + break; unsigned char *packet = NULL; int in_size = ds_get_packet_pts(mpctx->d_video, &packet, &pts); if (pts != MP_NOPTS_VALUE) pts += mpctx->video_offset; + bool hit_eof = false; if (in_size < 0) { // try to extract last frames in case of decoder lag in_size = 0; @@ -2587,12 +2584,17 @@ static double update_video(struct MPContext *mpctx) if (decoded_frame) { determine_frame_pts(mpctx); current_module = "filter video"; - if (filter_video(sh_video, decoded_frame, sh_video->pts)) - if (!video_out->config_ok) - break; // We'd likely hang in this loop otherwise + filter_video(sh_video, decoded_frame, sh_video->pts); + } else if (hit_eof) { + if (vo_get_buffered_frame(video_out, true) < 0) + return -1; } + break; } + if (!video_out->frame_loaded) + return 0; + pts = video_out->next_pts; if (pts == MP_NOPTS_VALUE) { mp_msg(MSGT_CPLAYER, MSGL_ERR, "Video pts after filters MISSING\n"); @@ -2880,9 +2882,10 @@ static int seek(MPContext *mpctx, double amount, int style) * during initialization, so reinit must be done after the demux_seek() * call that clears possible stream EOF. */ bool need_reset = false; + double demuxer_amount = amount; if (mpctx->timeline) { - amount = timeline_set_from_time(mpctx, amount, &need_reset); - if (amount == -1) { + demuxer_amount = timeline_set_from_time(mpctx, amount, &need_reset); + if (demuxer_amount == -1) { mpctx->stop_play = AT_END_OF_FILE; // Clear audio from current position if (mpctx->sh_audio) { @@ -2893,13 +2896,22 @@ static int seek(MPContext *mpctx, double amount, int style) return -1; } } - int seekresult = demux_seek(mpctx->demuxer, amount, audio_delay, style); + int seekresult = demux_seek(mpctx->demuxer, demuxer_amount, audio_delay, + style); if (need_reset) reinit_decoders(mpctx); if (seekresult == 0) return -1; seek_reset(mpctx); + + /* Use the target time as "current position" for further relative + * seeks etc until a new video frame has been decoded */ + if ((style & (SEEK_ABSOLUTE | SEEK_FACTOR)) == SEEK_ABSOLUTE) + mpctx->video_pts = amount; + + mpctx->start_timestamp = GetTimerMS(); + return 0; } @@ -3051,6 +3063,7 @@ static void run_playloop(struct MPContext *mpctx) if (!mpctx->sh_video) { + mpctx->restart_playback = false; // handle audio-only case: double a_pos = 0; // sh_audio can be NULL due to video stream switching @@ -3253,21 +3266,25 @@ static void run_playloop(struct MPContext *mpctx) while (1) { mp_cmd_t *cmd; - while ((cmd = mp_input_get_cmd(mpctx->input, 0, 0)) != NULL) { + while ((cmd = mp_input_get_cmd(mpctx->input, 0, 1)) != NULL) { + /* Allow running consecutive seek commands to combine them, + * but execute the seek before running other commands. + * If the user seeks continuously (keeps arrow key down) + * try to finish showing a frame from one location before doing + * another seek (which could lead to unchanging display). */ + if ((mpctx->rel_seek_secs || mpctx->abs_seek_pos) + && cmd->id != MP_CMD_SEEK + || mpctx->restart_playback && cmd->id == MP_CMD_SEEK + && GetTimerMS() - mpctx->start_timestamp < 300) + break; + cmd = mp_input_get_cmd(mpctx->input, 0, 0); run_command(mpctx, cmd); mp_cmd_free(cmd); if (mpctx->stop_play) break; - if (mpctx->rel_seek_secs || mpctx->abs_seek_pos) { - cmd = mp_input_get_cmd(mpctx->input, 0, 1); - /* Allow seek commands to be combined, but execute the real seek - * before processing other commands */ - if (!cmd || cmd->id != MP_CMD_SEEK) - break; - } } if (!mpctx->paused || mpctx->stop_play || mpctx->rel_seek_secs - || mpctx->abs_seek_pos) + || mpctx->abs_seek_pos || mpctx->restart_playback) break; if (mpctx->sh_video) { update_osd_msg(mpctx); -- cgit v1.2.3 From 7e366113f75c696ae2b32f5faa5f80ec3fca96b8 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Sat, 18 Dec 2010 01:00:28 +0200 Subject: options: add "choice" option type, use for -pts-association-mode Add a "choice" option type. Options of this type take a string as input and set an int option variable to the value corresponding to the string. The string->int mapping is option-specific and is given in the option definition. Strings not found in the mapping are rejected as invalid option values. Change the option -pts-association-mode to use this new option type and accept values "auto, decoder, sort" instead of "0, 1, 2". The change in accepted values shouldn't cause problems as this option is not appropriate to use in normal user config files. --- DOCS/man/en/mplayer.1 | 9 +++++---- cfg-mplayer.h | 3 ++- m_option.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ m_option.h | 8 ++++++++ 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1 index 60ee3b9169..a6c103ce25 100644 --- a/DOCS/man/en/mplayer.1 +++ b/DOCS/man/en/mplayer.1 @@ -1076,16 +1076,17 @@ MPlayer will not load or search for video segments from other files, and will also ignore any chapter order specified for the main file. . .TP -.B \-pts\-association\-mode +.B \-pts\-association\-mode auto|decode|sort Select the method used to determine which container packet timestamp corresponds to a particular output frame from the video decoder. +Normally you shouldn't need to change this option. .PD 0 .RSs -.IPs 0 +.IPs auto Try to pick a working mode from the ones below automatically (default) -.IPs 1 +.IPs decoder Use decoder reordering functionality. -.IPs 2 +.IPs sort Maintain a buffer of unused pts values and use the lowest value for the frame. .RE .PD 1 diff --git a/cfg-mplayer.h b/cfg-mplayer.h index be52df881a..3545b77716 100644 --- a/cfg-mplayer.h +++ b/cfg-mplayer.h @@ -309,7 +309,8 @@ const m_option_t mplayer_opts[]={ // a-v sync stuff: OPT_MAKE_FLAGS("correct-pts", user_correct_pts, 0), - OPT_INTRANGE("pts-association-mode", user_pts_assoc_mode, 0, 0, 2), + OPT_CHOICE("pts-association-mode", user_pts_assoc_mode, 0, + ({"auto", 0}, {"decoder", 1}, {"sort", 2})), OPT_MAKE_FLAGS("initial-audio-sync", initial_audio_sync, 0), OPT_FLAG_CONSTANTS("noautosync", autosync, 0, 0, -1), OPT_INTRANGE("autosync", autosync, 0, 0, 10000), diff --git a/m_option.c b/m_option.c index daab955614..33c5e95a1b 100644 --- a/m_option.c +++ b/m_option.c @@ -267,6 +267,50 @@ const struct m_option_type m_option_type_intpair = { .set = copy_opt, }; +static int parse_choice(const struct m_option *opt, const char *name, + const char *param, void *dst, int src) +{ + if (param == NULL) + return M_OPT_MISSING_PARAM; + + struct m_opt_choice_alternatives *alt; + for (alt = opt->priv; alt->name; alt++) + if (!strcmp(param, alt->name)) + break; + if (!alt->name) { + mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Invalid value for option %s: %s\n", + name, param); + mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Valid values are:"); + for (alt = opt->priv; alt->name; alt++) + mp_msg(MSGT_CFGPARSER, MSGL_ERR, " %s", alt->name); + mp_msg(MSGT_CFGPARSER, MSGL_ERR, "\n"); + return M_OPT_INVALID; + } + if (dst) + *(int *)dst = alt->value; + + return 1; +} + +static char *print_choice(const m_option_t *opt, const void *val) +{ + int v = *(int *)val; + struct m_opt_choice_alternatives *alt; + for (alt = opt->priv; alt->name; alt++) + if (alt->value == v) + return strdup(alt->name); + abort(); +} + +const struct m_option_type m_option_type_choice = { + .name = "String", // same as arbitrary strings in option list for now + .size = sizeof(int), + .parse = parse_choice, + .print = print_choice, + .save = copy_opt, + .set = copy_opt, +}; + // Float #undef VAL diff --git a/m_option.h b/m_option.h index 26c457e24d..ed2b30de75 100644 --- a/m_option.h +++ b/m_option.h @@ -53,6 +53,7 @@ extern const m_option_type_t m_option_type_string_list; extern const m_option_type_t m_option_type_position; extern const m_option_type_t m_option_type_time; extern const m_option_type_t m_option_type_time_size; +extern const m_option_type_t m_option_type_choice; extern const m_option_type_t m_option_type_print; extern const m_option_type_t m_option_type_print_indirect; @@ -169,6 +170,11 @@ typedef struct { /// Ready made settings to parse a \ref m_span_t with a start-end syntax. extern const m_obj_params_t m_span_params_def; +struct m_opt_choice_alternatives { + char *name; + int value; +}; + // FIXME: backward compatibility #define CONF_TYPE_FLAG (&m_option_type_flag) @@ -543,5 +549,7 @@ int parse_timestring(const char *str, double *time, char endchar); #define OPT_STRING(optname, varname, flags) {optname, NULL, &m_option_type_string, flags, 0, 0, NULL, 1, offsetof(struct MPOpts, varname)} #define OPT_SETTINGSLIST(optname, varname, flags, objlist) {optname, NULL, &m_option_type_obj_settings_list, flags, 0, 0, objlist, 1, offsetof(struct MPOpts, varname)} #define OPT_AUDIOFORMAT(optname, varname, flags) {optname, NULL, &m_option_type_afmt, flags, 0, 0, NULL, 1, offsetof(struct MPOpts, varname)} +#define OPT_HELPER_REMOVEPAREN(...) __VA_ARGS__ +#define OPT_CHOICE(optname, varname, flags, choices) {optname, NULL, &m_option_type_choice, flags, 0, 0, &(const struct m_opt_choice_alternatives[]){OPT_HELPER_REMOVEPAREN choices, {NULL}}, 1, offsetof(struct MPOpts, varname)} #endif /* MPLAYER_M_OPTION_H */ -- cgit v1.2.3 From b9a3579ec980dfb01d0b952bf0b859ecdf62d7ea Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Sat, 18 Dec 2010 08:02:48 +0200 Subject: commands: add generic option -> property wrapper Add mp_property_generic_option(), a property function that can be used for generic option-based properties that do not require any action beyond manipulating the value of the option variable. Currently it directly implements GET and SET, plus STEP_UP for "choice" options only. Use it to add a property for -pts-association-mode (not particularly useful in normal use, but serves as a test). --- DOCS/tech/slave.txt | 1 + command.c | 37 +++++++++++++++++++++++++++++++++++++ m_config.c | 4 ++-- m_option.h | 6 ++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/DOCS/tech/slave.txt b/DOCS/tech/slave.txt index e923232689..be0ebf9937 100644 --- a/DOCS/tech/slave.txt +++ b/DOCS/tech/slave.txt @@ -513,6 +513,7 @@ name type min max get set step comment osdlevel int 0 3 X X X as -osdlevel speed float 0.01 100 X X X as -speed loop int -1 X X X as -loop +pts_association_mode string X X X as -pts-association-mode pause flag 0 1 X 1 if paused, use with pausing_keep_force filename string X file playing wo path path string X file playing diff --git a/command.c b/command.c index 489cd2d047..247af1b9d1 100644 --- a/command.c +++ b/command.c @@ -34,6 +34,7 @@ #include "libvo/sub.h" #include "m_option.h" #include "m_property.h" +#include "m_config.h" #include "metadata.h" #include "libmpcodecs/vf.h" #include "libmpcodecs/vd.h" @@ -226,6 +227,39 @@ static void log_sub(struct MPContext *mpctx) /// \ingroup Properties ///@{ +static int mp_property_generic_option(struct m_option *prop, int action, + void *arg, MPContext *mpctx) +{ + char *optname = prop->priv; + const struct m_option *opt = m_config_get_option(mpctx->mconfig, optname); + void *valptr = m_option_get_ptr(opt, &mpctx->opts); + + switch (action) { + case M_PROPERTY_GET_TYPE: + *(const struct m_option **)arg = opt; + return M_PROPERTY_OK; + case M_PROPERTY_GET: + m_option_copy(opt, arg, valptr); + return M_PROPERTY_OK; + case M_PROPERTY_SET: + m_option_copy(opt, valptr, arg); + return M_PROPERTY_OK; + case M_PROPERTY_STEP_UP: + if (opt->type == &m_option_type_choice) { + int v = *(int *) valptr; + int best = v; + struct m_opt_choice_alternatives *alt; + for (alt = opt->priv; alt->name; alt++) + if ((unsigned) alt->value - v - 1 < (unsigned) best - v - 1) + best = alt->value; + *(int *) valptr = best; + return M_PROPERTY_OK; + } + break; + } + return M_PROPERTY_NOT_IMPLEMENTED; +} + /// OSD level (RW) static int mp_property_osdlevel(m_option_t *prop, int action, void *arg, MPContext *mpctx) @@ -2189,6 +2223,8 @@ static const m_option_t mp_properties[] = { M_OPT_RANGE, 0, 1, NULL }, { "capturing", mp_property_capture, CONF_TYPE_FLAG, M_OPT_RANGE, 0, 1, NULL }, + { "pts_association_mode", mp_property_generic_option, &m_option_type_choice, + 0, 0, 0, "pts-association-mode" }, // Audio { "volume", mp_property_volume, CONF_TYPE_FLOAT, @@ -2362,6 +2398,7 @@ static struct property_osd_display { { "loop", 0, -1, _("Loop: %s") }, { "chapter", -1, -1, NULL }, { "capturing", 0, -1, _("Capturing: %s") }, + { "pts_association_mode", 0, -1, "PTS association mode: %s" }, // audio { "volume", OSD_VOLUME, -1, _("Volume") }, { "mute", 0, -1, _("Mute: %s") }, diff --git a/m_config.c b/m_config.c index 266ee10323..2752ced85f 100644 --- a/m_config.c +++ b/m_config.c @@ -150,7 +150,7 @@ static void m_option_save(const m_config_t *config, const m_option_t *opt, void *dst) { if (opt->type->save) { - const void *src = opt->new ? (char*)config->optstruct + opt->offset : opt->p; + const void *src = m_option_get_ptr(opt, config->optstruct); opt->type->save(opt, dst, src); } } @@ -159,7 +159,7 @@ static void m_option_set(const m_config_t *config, const m_option_t *opt, const void *src) { if (opt->type->set) { - void *dst = opt->new ? (char*)config->optstruct + opt->offset : opt->p; + void *dst = m_option_get_ptr(opt, config->optstruct); opt->type->set(opt, dst, src); } } diff --git a/m_option.h b/m_option.h index ed2b30de75..05d5751ceb 100644 --- a/m_option.h +++ b/m_option.h @@ -493,6 +493,12 @@ struct m_option { */ const m_option_t* m_option_list_find(const m_option_t* list,const char* name); +static inline void *m_option_get_ptr(const struct m_option *opt, + void *optstruct) +{ + return opt->new ? (char *) optstruct + opt->offset : opt->p; +} + /// Helper to parse options, see \ref m_option_type::parse. inline static int m_option_parse(const m_option_t* opt,const char *name, const char *param, void* dst, int src) { -- cgit v1.2.3 From 23f598e0ee2151923a44077f510cd484f239480b Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Sat, 18 Dec 2010 10:13:45 +0200 Subject: core: add struct for queued seek info To prepare for the addition of exact seek support, add a struct for queued seek state and a helper function to update its state. It would have been cumbersome to update additional state (showing whether the seek is forced to be exact or non-exact) manually at every point that handles seeks. --- command.c | 32 +++++++---------- mp_core.h | 13 +++++-- mplayer.c | 122 ++++++++++++++++++++++++++++++++++++++++++-------------------- 3 files changed, 106 insertions(+), 61 deletions(-) diff --git a/command.c b/command.c index 247af1b9d1..394bf566c4 100644 --- a/command.c +++ b/command.c @@ -454,8 +454,7 @@ static int mp_property_percent_pos(m_option_t *prop, int action, return m_property_int_ro(prop, action, arg, get_percent_pos(mpctx)); } - mpctx->abs_seek_pos = SEEK_ABSOLUTE | SEEK_FACTOR; - mpctx->rel_seek_secs = pos / 100.0; + queue_seek(mpctx, MPSEEK_FACTOR, pos / 100.0, 0); return M_PROPERTY_OK; } @@ -469,13 +468,12 @@ static int mp_property_time_pos(m_option_t *prop, int action, case M_PROPERTY_SET: if(!arg) return M_PROPERTY_ERROR; M_PROPERTY_CLAMP(prop, *(double*)arg); - mpctx->abs_seek_pos = SEEK_ABSOLUTE; - mpctx->rel_seek_secs = *(double*)arg; + queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double*)arg, 0); return M_PROPERTY_OK; case M_PROPERTY_STEP_UP: case M_PROPERTY_STEP_DOWN: - mpctx->rel_seek_secs += (arg ? *(double*)arg : 10.0) * - (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0); + queue_seek(mpctx, MPSEEK_RELATIVE, (arg ? *(double*)arg : 10.0) * + (action == M_PROPERTY_STEP_UP ? 1.0 : -1.0), 0); return M_PROPERTY_OK; } return m_property_time_ro(prop, action, arg, get_current_time(mpctx)); @@ -531,20 +529,16 @@ static int mp_property_chapter(m_option_t *prop, int action, void *arg, } double next_pts = 0; + queue_seek(mpctx, MPSEEK_NONE, 0, 0); chapter = seek_chapter(mpctx, chapter, &next_pts, &chapter_name); - mpctx->rel_seek_secs = 0; - mpctx->abs_seek_pos = 0; if (chapter >= 0) { - if (next_pts > -1.0) { - mpctx->abs_seek_pos = SEEK_ABSOLUTE; - mpctx->rel_seek_secs = next_pts; - } + if (next_pts > -1.0) + queue_seek(mpctx, MPSEEK_ABSOLUTE, next_pts, 0); if (chapter_name) set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration, "Chapter: (%d) %s", chapter + 1, chapter_name); - } - else if (step_all > 0) - mpctx->rel_seek_secs = 1000000000.; + } else if (step_all > 0) + queue_seek(mpctx, MPSEEK_RELATIVE, 1000000000, 0); else set_osd_tmsg(OSD_MSG_TEXT, 1, opts->osd_duration, "Chapter: (%d) %s", 0, mp_gtext("unknown")); @@ -2725,16 +2719,14 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) v = cmd->args[0].v.f; abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0; if (abs == 2) { /* Absolute seek to a specific timestamp in seconds */ - mpctx->abs_seek_pos = SEEK_ABSOLUTE; + queue_seek(mpctx, MPSEEK_ABSOLUTE, v, 0); mpctx->osd_function = v > get_current_time(mpctx) ? OSD_FFW : OSD_REW; - mpctx->rel_seek_secs = v; } else if (abs) { /* Absolute seek by percentage */ - mpctx->abs_seek_pos = SEEK_ABSOLUTE | SEEK_FACTOR; + queue_seek(mpctx, MPSEEK_FACTOR, v / 100.0, 0); mpctx->osd_function = OSD_FFW; // Direction isn't set correctly - mpctx->rel_seek_secs = v / 100.0; } else { - mpctx->rel_seek_secs += v; + queue_seek(mpctx, MPSEEK_RELATIVE, v, 0); mpctx->osd_function = (v > 0) ? OSD_FFW : OSD_REW; } } diff --git a/mp_core.h b/mp_core.h index c02edd4155..c64de2cdbe 100644 --- a/mp_core.h +++ b/mp_core.h @@ -161,8 +161,15 @@ typedef struct MPContext { unsigned int last_time; // Used to communicate the parameters of a seek between parts - double rel_seek_secs; - in