core: Rewrite some of the A/V sync related code

Notable functionality changes:
* Timing change between any two frames is now accurately limited to
  1/10 of their nominal distance. Previous code did not always use the
  correct duration.
* The status line now keeps showing the same A-V sync value from one
  video frame change to the next. Previously it kept recalculating
  the value using a new audio position but the same video position
  when the status line was updated between video frames. This
  incorrectly showed the video losing sync with audio.
* The status line now displays actual measured A-V difference in
  autosync mode too. The previous code displayed values that
  completely ignored real timing in autosync mode, showing 0 A-V
  difference even when video was significantly behind audio due to
  inadequate decoding speed. The new behavior can make the shown A-V
  values appear more unstable if the audio out has unreliable delay
  measurements (the most likely reason to use autosync), but this is a
  display change rather than a timing quality change.
* Autosync mode now tries to adjust timing by the amount of time
  vo_flip() calls take, so the calls start earlier and finish at the
  time the frame should be shown. Previously non-autosync mode
  adjusted for this but autosync did not.
* The warning about the system being too slow to decode the video in
  realtime is now displayed in autosync mode too.
This commit is contained in:
Uoti Urpala 2008-12-08 20:04:08 +02:00
parent 5204fda121
commit 321c93ee99
2 changed files with 88 additions and 81 deletions

View File

@ -1,6 +1,8 @@
#ifndef MPLAYER_MP_CORE_H
#define MPLAYER_MP_CORE_H
#include <stdbool.h>
#include "options.h"
#include "mixer.h"
#include "subreader.h"
@ -76,6 +78,9 @@ typedef struct MPContext {
// struct.
int num_buffered_frames;
// Show a video frame as quickly as possible without trying to adjust
// for AV sync. Used when starting a file or after seeking.
bool update_video_immediately;
// AV sync: the next frame should be shown when the audio out has this
// much (in seconds) buffered data left. Increased when more data is
// written to the ao, decreased when moving to the next frame.
@ -84,6 +89,17 @@ typedef struct MPContext {
double delay;
// AV sync: time until next frame should be shown
float time_frame;
// How long the last vo flip() call took. Used to adjust timing with
// the goal of making flip() calls finish (rather than start) at the
// specified time.
float last_vo_flip_duration;
// How much video timing has been changed to make it match the audio
// timeline. Used for status line information only.
double total_avsync_change;
// A-V sync difference when last frame was displayed. Kept to display
// the same value if the status line is updated at a time where no new
// video frame is shown.
double last_av_difference;
// Timestamp from the last time some timing functions read the
// current time, in (occasionally wrapping) microseconds. Used
@ -118,6 +134,10 @@ typedef struct MPContext {
// step this many frames, then pause
int step_frames;
// Set after showing warning about decoding being too slow for realtime
// playback rate. Used to avoid showing it multiple times.
bool drop_message_shown;
#ifdef CONFIG_DVDNAV
struct mp_image *nav_smpi; ///< last decoded dvdnav video image
unsigned char *nav_buffer; ///< last read dvdnav video frame

149
mplayer.c
View File

@ -4,6 +4,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "config.h"
#include "talloc.h"
@ -259,8 +260,6 @@ static char *stream_dump_name="stream.dump";
// A-V sync:
static float default_max_pts_correction=-1;//0.01f;
static float max_pts_correction=0;//default_max_pts_correction;
static float c_total=0;
float audio_delay=0;
static int ignore_start=0;
@ -1160,16 +1159,25 @@ static void sadd_hhmmssf(char *buf, unsigned *pos, int len, float time) {
saddf(buf, pos, len, "%02d.%1d", ss, f1);
}
/**
* \brief print the status line
* \param a_pos audio position
* \param a_v A-V desynchronization
* \param corr amount out A-V synchronization
*/
static void print_status(struct MPContext *mpctx, float a_pos, float a_v, float corr)
static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame)
{
struct MPOpts *opts = &mpctx->opts;
sh_video_t * const sh_video = mpctx->sh_video;
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;
if (mpctx->last_av_difference > 0.5 && drop_frame_cnt > 50
&& !mpctx->drop_message_shown) {
mp_msg(MSGT_AVSYNC,MSGL_WARN,MSGTR_SystemTooSlow);
mpctx->drop_message_shown = true;
}
}
if (quiet)
return;
int width;
char *line;
unsigned pos = 0;
@ -1204,7 +1212,8 @@ static void print_status(struct MPContext *mpctx, float a_pos, float a_v, float
// A-V sync
if (mpctx->sh_audio && sh_video)
saddf(line, &pos, width, "A-V:%7.3f ct:%7.3f ", a_v, corr);
saddf(line, &pos, width, "A-V:%7.3f ct:%7.3f ",
mpctx->last_av_difference, mpctx->total_avsync_change);
// Video stats
if (sh_video)
@ -1696,7 +1705,8 @@ static int check_framedrop(struct MPContext *mpctx, double frame_time) {
++total_frame_cnt;
// we should avoid dropping too many frames in sequence unless we
// are too late. and we allow 100ms A-V delay here:
if (d < -dropped_frames*frame_time-0.100 && !mpctx->paused) {
if (d < -dropped_frames*frame_time-0.100 && !mpctx->paused
&& !mpctx->update_video_immediately) {
++drop_frame_cnt;
++dropped_frames;
return frame_dropping;
@ -1921,71 +1931,39 @@ static void mp_dvdnav_save_smpi(struct MPContext *mpctx, int in_size,
}
#endif /* CONFIG_DVDNAV */
static void adjust_sync_and_print_status(struct MPContext *mpctx,
int between_frames,
float timing_error)
/* Modify video timing to match the audio timeline. There are two main
* reasons this is needed. First, video and audio can start from different
* positions at beginning of file or after a seek (MPlayer starts both
* immediately even if they have different pts). Second, the file can have
* audio timestamps that are inconsistent with the duration of the audio
* packets, for example two consecutive timestamp values differing by
* one second but only a packet with enough samples for half a second
* of playback between them.
*/
static void adjust_sync(struct MPContext *mpctx, double frame_time)
{
struct MPOpts *opts = &mpctx->opts;
current_module="av_sync";
current_module = "av_sync";
if(mpctx->sh_audio){
double a_pts, v_pts;
if (!mpctx->sh_audio)
return;
if (autosync)
/*
* If autosync is enabled, the value for delay must be calculated
* a bit differently. It is set only to the difference between
* the audio and video timers. Any attempt to include the real
* or corrected delay causes the pts_correction code below to
* try to correct for the changes in delay which autosync is
* trying to measure. This keeps the two from competing, but still
* allows the code to correct for PTS drift *only*. (Using a delay
* value here, even a "corrected" one, would be incompatible with
* autosync mode.)
*/
a_pts = written_audio_pts(mpctx) - mpctx->delay;
else
a_pts = playing_audio_pts(mpctx);
double a_pts = written_audio_pts(mpctx) - mpctx->delay;
double v_pts = mpctx->sh_video->pts;
double av_delay = a_pts - v_pts;
// Try to sync vo_flip() so it will *finish* at given time
av_delay += mpctx->last_vo_flip_duration;
av_delay -= audio_delay; // This much pts difference is desired
v_pts = mpctx->sh_video->pts;
{
static int drop_message=0;
double AV_delay = a_pts - audio_delay - v_pts;
double x;
if (AV_delay>0.5 && drop_frame_cnt>50 && drop_message==0){
++drop_message;
mp_msg(MSGT_AVSYNC,MSGL_WARN,MSGTR_SystemTooSlow);
}
if (autosync)
x = AV_delay*0.1f;
else
/* Do not correct target time for the next frame if this frame
* was late not because of wrong target time but because the
* target time could not be met */
x = (AV_delay + timing_error * opts->playback_speed) * 0.1f;
if (x < -max_pts_correction)
x = -max_pts_correction;
else if (x> max_pts_correction)
x = max_pts_correction;
if (default_max_pts_correction >= 0)
max_pts_correction = default_max_pts_correction;
else
max_pts_correction = mpctx->sh_video->frametime*0.10; // +-10% of time
if (!between_frames) {
mpctx->delay+=x;
c_total+=x;
}
if(!quiet)
print_status(mpctx, a_pts - audio_delay, AV_delay, c_total);
}
} else {
// No audio:
if (!quiet)
print_status(mpctx, 0, 0, 0);
}
double change = av_delay * 0.1;
double max_change = default_max_pts_correction >= 0 ?
default_max_pts_correction : frame_time * 0.1;
if (change < -max_change)
change = -max_change;
else if (change > max_change)
change = max_change;
mpctx->delay += change;
mpctx->total_avsync_change += change;
}
static int fill_audio_out_buffers(struct MPContext *mpctx)
@ -2522,6 +2500,7 @@ static int seek(MPContext *mpctx, double amount, int style)
mpctx->num_buffered_frames = 0;
mpctx->delay = 0;
mpctx->time_frame = 0;
mpctx->update_video_immediately = true;
// Not all demuxers set d_video->pts during seek, so this value
// (which is used by at least vobsub and edl code below) may
// be completely wrong (probably 0).
@ -2544,8 +2523,7 @@ static int seek(MPContext *mpctx, double amount, int style)
edl_seek_reset(mpctx);
c_total = 0;
max_pts_correction = 0.1;
mpctx->total_avsync_change = 0;
audio_time_usage = 0; video_time_usage = 0; vout_time_usage = 0;
drop_frame_cnt = 0;
@ -3733,6 +3711,9 @@ if (mpctx->stream->type == STREAMTYPE_DVDNAV) {
get_relative_time(mpctx); // reset current delta
mpctx->time_frame = 0;
mpctx->drop_message_shown = 0;
mpctx->update_video_immediately = true;
mpctx->total_avsync_change = 0;
while(!mpctx->stop_play){
float aq_sleep_time=0;
@ -3763,11 +3744,10 @@ if(!mpctx->sh_video) {
double a_pos=0;
// sh_audio can be NULL due to video stream switching
// TODO: handle this better
if(!quiet || end_at.type == END_AT_TIME && mpctx->sh_audio)
if (mpctx->sh_audio)
a_pos = playing_audio_pts(mpctx);
if(!quiet)
print_status(mpctx, a_pos, 0, 0);
print_status(mpctx, a_pos, false);
if(end_at.type == END_AT_TIME && end_at.pos < a_pos)
mpctx->stop_play = PT_NEXT_ENTRY;
@ -3795,7 +3775,13 @@ if(!mpctx->sh_video) {
else {
// might return with !eof && !blit_frame if !correct_pts
mpctx->num_buffered_frames += blit_frame;
mpctx->time_frame += frame_time / opts->playback_speed; // for nosound
if (mpctx->update_video_immediately) {
// Show this frame immediately, rest normally
mpctx->update_video_immediately = false;
} else {
mpctx->time_frame += frame_time / opts->playback_speed;
adjust_sync(mpctx, frame_time);
}
}
}
@ -3837,11 +3823,12 @@ if(!mpctx->sh_video) {
vo_flip_page(mpctx->video_out);
mpctx->num_buffered_frames--;
vout_time_usage += (GetTimer() - t2) * 0.000001;
mpctx->last_vo_flip_duration = (GetTimer() - t2) * 0.000001;
vout_time_usage += mpctx->last_vo_flip_duration;
print_status(mpctx, MP_NOPTS_VALUE, true);
}
//====================== A-V TIMESTAMP CORRECTION: =========================
adjust_sync_and_print_status(mpctx, frame_time_remaining, mpctx->time_frame);
else
print_status(mpctx, MP_NOPTS_VALUE, false);
//============================ Auto QUALITY ============================