1
0
mirror of https://github.com/mpv-player/mpv synced 2025-04-01 00:07:33 +00:00

core: do initial A-V sync by modifying audio stream

Add code to enforce matching pts with video when (re)starting the
audio stream, by either cutting away the first samples or inserting
silence at the beginning. New option -noinitial-audio-sync can be used
to disable this and return to old behavior.
This commit is contained in:
Uoti Urpala 2010-11-13 19:27:01 +02:00
parent 642ce15ef7
commit a4ce95de81
8 changed files with 130 additions and 19 deletions

View File

@ -1036,6 +1036,17 @@ in a playlist or intend to read from stdin later on via the loadfile or
loadlist slave commands.
.
.TP
.B \-noinitial-audio-sync
When starting a video file or after events such as seeking MPlayer will by
default modify the audio stream to make it start from the same timestamp as
video, by either inserting silence at the start or cutting away the first
samples.
This option disables that functionality and makes the player behave like
older MPlayer versions did: video and audio are both started immediately
even if their start timestamps differ, and then video timing is gradually
adjusted if necessary to reach correct synchronization later.
.
.TP
.B \-nojoystick
Turns off joystick support.
.

View File

@ -309,6 +309,7 @@ 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_MAKE_FLAGS("initial-audio-sync", initial_audio_sync, 0),
{"noautosync", &autosync, CONF_TYPE_FLAG, 0, 0, -1, NULL},
{"autosync", &autosync, CONF_TYPE_INT, CONF_RANGE, 0, 10000, NULL},

View File

@ -28,6 +28,7 @@ void set_default_mplayer_options(struct MPOpts *opts)
.chapterrange = {-1, -1},
.edition_id = -1,
.user_correct_pts = -1,
.initial_audio_sync = 1,
.key_fifo_size = 7,
.doubleclick_time = 300,
.audio_id = -1,

View File

@ -369,6 +369,16 @@ int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate,
return 1;
}
static void set_min_out_buffer_size(struct sh_audio *sh, int len)
{
if (sh->a_out_buffer_size < len) {
mp_msg(MSGT_DECAUDIO, MSGL_V, "Increasing filtered audio buffer size "
"from %d to %d\n", sh->a_out_buffer_size, len);
sh->a_out_buffer = realloc(sh->a_out_buffer, len);
sh->a_out_buffer_size = len;
}
}
static int filter_n_bytes(sh_audio_t *sh, int len)
{
assert(len-1 + sh->audio_out_minsize <= sh->a_buffer_size);
@ -408,13 +418,7 @@ static int filter_n_bytes(sh_audio_t *sh, int len)
af_data_t *filter_output = af_play(sh->afilter, &filter_input);
if (!filter_output)
return -1;
if (sh->a_out_buffer_size < sh->a_out_buffer_len + filter_output->len) {
int newlen = sh->a_out_buffer_len + filter_output->len;
mp_msg(MSGT_DECAUDIO, MSGL_V, "Increasing filtered audio buffer size "
"from %d to %d\n", sh->a_out_buffer_size, newlen);
sh->a_out_buffer = realloc(sh->a_out_buffer, newlen);
sh->a_out_buffer_size = newlen;
}
set_min_out_buffer_size(sh, sh->a_out_buffer_len + filter_output->len);
memcpy(sh->a_out_buffer + sh->a_out_buffer_len, filter_output->audio,
filter_output->len);
sh->a_out_buffer_len += filter_output->len;
@ -479,6 +483,15 @@ int decode_audio(sh_audio_t *sh_audio, int minlen)
return 0;
}
void decode_audio_prepend_bytes(struct sh_audio *sh, int count, int byte)
{
set_min_out_buffer_size(sh, sh->a_out_buffer_len + count);
memmove(sh->a_out_buffer + count, sh->a_out_buffer, sh->a_out_buffer_len);
memset(sh->a_out_buffer, byte, count);
sh->a_out_buffer_len += count;
}
void resync_audio_stream(sh_audio_t *sh_audio)
{
sh_audio->a_in_buffer_len = 0; // clear audio input buffer

View File

@ -25,6 +25,7 @@
void afm_help(void);
int init_best_audio_codec(sh_audio_t *sh_audio, char** audio_codec_list, char** audio_fm_list);
int decode_audio(sh_audio_t *sh_audio, int minlen);
void decode_audio_prepend_bytes(struct sh_audio *sh, int count, int byte);
void resync_audio_stream(sh_audio_t *sh_audio);
void skip_audio_frame(sh_audio_t *sh_audio);
void uninit_audio(sh_audio_t *sh_audio);

View File

@ -125,6 +125,10 @@ typedef struct MPContext {
/* We're starting playback from scratch or after a seek. Show first
* video frame immediately and reinitialize sync. */
bool restart_playback;
/* After playback restart (above) or audio stream change, adjust audio
* stream by cutting samples or adding silence at the beginning to make
* audio playback position match video position. */
bool syncing_audio;
// 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.

103
mplayer.c
View File

@ -22,6 +22,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include "config.h"
#include "talloc.h"
@ -1790,6 +1792,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
}
mpctx->mixer.audio_out = mpctx->audio_out;
mpctx->mixer.volstep = volstep;
mpctx->syncing_audio = true;
return;
init_error:
@ -2112,7 +2115,7 @@ static void adjust_sync(struct MPContext *mpctx, double frame_time)
{
current_module = "av_sync";
if (!mpctx->sh_audio)
if (!mpctx->sh_audio || mpctx->syncing_audio)
return;
double a_pts = written_audio_pts(mpctx) - mpctx->delay;
@ -2133,6 +2136,67 @@ static void adjust_sync(struct MPContext *mpctx, double frame_time)
mpctx->total_avsync_change += change;
}
#define ASYNC_PLAY_DONE -3
static int audio_start_sync(struct MPContext *mpctx, int playsize)
{
struct MPOpts *opts = &mpctx->opts;
sh_audio_t * const sh_audio = mpctx->sh_audio;
int res;
// Timing info may not be set without
res = decode_audio(sh_audio, 1);
if (res < 0)
return res;
double ptsdiff = written_audio_pts(mpctx) - mpctx->sh_video->pts -
mpctx->delay - audio_delay;
int bytes = ptsdiff * ao_data.bps / mpctx->opts.playback_speed;
bytes -= bytes % (ao_data.channels * af_fmt2bits(ao_data.format) / 8);
if (fabs(ptsdiff) > 300) // pts reset or just broken?
bytes = 0;
if (bytes <= 0) {
mpctx->syncing_audio = false;
while (1) {
int a = FFMIN(-bytes, FFMAX(playsize, 20000));
int res = decode_audio(sh_audio, a);
bytes += sh_audio->a_out_buffer_len;
if (bytes >= 0) {
memmove(sh_audio->a_out_buffer,
sh_audio->a_out_buffer +
sh_audio->a_out_buffer_len - bytes,
bytes);
sh_audio->a_out_buffer_len = bytes;
if (res < 0)
return res;
return decode_audio(sh_audio, playsize);
}
sh_audio->a_out_buffer_len = 0;
if (res < 0)
return res;
}
} else {
int fillbyte = 0;
if ((ao_data.format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_US)
fillbyte = 0x80;
if (bytes >= playsize) {
/* This case could fall back to the one below with
* bytes = playsize, but then silence would keep accumulating
* in a_out_buffer if the AO accepts less data than it asks for
* in playsize. */
char *p = malloc(playsize);
memset(p, fillbyte, playsize);
playsize = mpctx->audio_out->play(p, playsize, 0);
free(p);
mpctx->delay += opts->playback_speed*playsize/(double)ao_data.bps;
return ASYNC_PLAY_DONE;
}
mpctx->syncing_audio = false;
decode_audio_prepend_bytes(sh_audio, bytes, fillbyte);
return decode_audio(sh_audio, playsize);
}
}
static int fill_audio_out_buffers(struct MPContext *mpctx)
{
struct MPOpts *opts = &mpctx->opts;
@ -2167,10 +2231,20 @@ static int fill_audio_out_buffers(struct MPContext *mpctx)
// Fill buffer if needed:
current_module="decode_audio";
t = GetTimer();
int res = decode_audio(sh_audio, playsize);
if (!opts->initial_audio_sync || (ao_data.format & AF_FORMAT_SPECIAL_MASK))
mpctx->syncing_audio = false;
int res;
if (mpctx->syncing_audio && mpctx->sh_video)
res = audio_start_sync(mpctx, playsize);
else
res = decode_audio(sh_audio, playsize);
if (res < 0) { // EOF, error or format change
if (res == -2)
format_change = true;
else if (res == ASYNC_PLAY_DONE)
return 1;
else if (mpctx->d_audio->eof) {
audio_eof = 1;
int unitsize = ao_data.channels * af_fmt2bits(ao_data.format) / 8;
@ -4181,7 +4255,8 @@ if(!mpctx->sh_audio && mpctx->d_audio->sh) {
/*========================== PLAY AUDIO ============================*/
if (mpctx->sh_audio && !mpctx->paused)
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)
@ -4234,14 +4309,9 @@ if(!mpctx->sh_video) {
}
if (frame_time < 0)
mpctx->stop_play = AT_END_OF_FILE;
else {
if (mpctx->restart_playback) {
// Show this frame immediately, rest normally
mpctx->restart_playback = false;
} else {
mpctx->time_frame += frame_time / opts->playback_speed;
adjust_sync(mpctx, frame_time);
}
else if (!mpctx->restart_playback) {
mpctx->time_frame += frame_time / opts->playback_speed;
adjust_sync(mpctx, frame_time);
}
}
if (mpctx->timeline) {
@ -4287,7 +4357,8 @@ if(!mpctx->sh_video) {
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) {
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;
@ -4309,6 +4380,14 @@ if(!mpctx->sh_video) {
// 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

View File

@ -42,6 +42,7 @@ typedef struct MPOpts {
int correct_pts;
int user_correct_pts;
int user_pts_assoc_mode;
int initial_audio_sync;
int key_fifo_size;
int doubleclick_time;
int audio_id;