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:
parent
642ce15ef7
commit
a4ce95de81
@ -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.
|
||||
.
|
||||
|
@ -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},
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
103
mplayer.c
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user