summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUoti Urpala <uau@glyph.nonexistent.invalid>2010-11-13 19:27:01 +0200
committerUoti Urpala <uau@glyph.nonexistent.invalid>2010-11-13 19:46:02 +0200
commita4ce95de811698503075a5f0b513e595f11f97a4 (patch)
tree20785a9ff28439b08fb22993e25954731937bc25
parent642ce15ef770dd5dbea7c7ee16cbf45f6e86feae (diff)
downloadmpv-a4ce95de811698503075a5f0b513e595f11f97a4.tar.bz2
mpv-a4ce95de811698503075a5f0b513e595f11f97a4.tar.xz
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.
-rw-r--r--DOCS/man/en/mplayer.111
-rw-r--r--cfg-mplayer.h1
-rw-r--r--defaultopts.c1
-rw-r--r--libmpcodecs/dec_audio.c27
-rw-r--r--libmpcodecs/dec_audio.h1
-rw-r--r--mp_core.h4
-rw-r--r--mplayer.c103
-rw-r--r--options.h1
8 files changed, 130 insertions, 19 deletions
diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1
index 708fd10199..4f26e0e173 100644
--- a/DOCS/man/en/mplayer.1
+++ b/DOCS/man/en/mplayer.1
@@ -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.
.
diff --git a/cfg-mplayer.h b/cfg-mplayer.h
index 95477edb33..21733912d5 100644
--- a/cfg-mplayer.h
+++ b/cfg-mplayer.h
@@ -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},
diff --git a/defaultopts.c b/defaultopts.c
index 1314e21f28..5b4e8f762f 100644
--- a/defaultopts.c
+++ b/defaultopts.c
@@ -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,
diff --git a/libmpcodecs/dec_audio.c b/libmpcodecs/dec_audio.c
index 59abe398fb..7bbbdeb058 100644
--- a/libmpcodecs/dec_audio.c
+++ b/libmpcodecs/dec_audio.c
@@ -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
diff --git a/libmpcodecs/dec_audio.h b/libmpcodecs/dec_audio.h
index 3ec2cef8bf..c2b92c9818 100644
--- a/libmpcodecs/dec_audio.h
+++ b/libmpcodecs/dec_audio.h
@@ -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);
diff --git a/mp_core.h b/mp_core.h
index ed90f4377b..206cbdacd3 100644
--- a/mp_core.h
+++ b/mp_core.h
@@ -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.
diff --git a/mplayer.c b/mplayer.c
index 83c6328b5d..77350cad73 100644
--- a/mplayer.c
+++ b/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
diff --git a/options.h b/options.h
index a12038708c..86d524f57f 100644
--- a/options.h
+++ b/options.h
@@ -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;