summaryrefslogtreecommitdiffstats
path: root/audio/out
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-11-08 20:00:58 +0100
committerwm4 <wm4@nowhere>2013-11-08 20:00:58 +0100
commit8125252399d9b02a6f143d109a92341ff7eff5a9 (patch)
tree3b9fed7c1acadca63714c29d39fda681f0dda33a /audio/out
parent052a7d54ab4efbf9743767ea7aae95c98f5b5379 (diff)
downloadmpv-8125252399d9b02a6f143d109a92341ff7eff5a9.tar.bz2
mpv-8125252399d9b02a6f143d109a92341ff7eff5a9.tar.xz
audio: don't let ao_lavc access frontend internals, change gapless audio
ao_lavc.c accesses ao->buffer, which I consider internal. The access was done in ao_lavc.c/uninit(), which tried to get the left-over audio in order to write the last (possibly partial) audio frame. The play() function didn't accept partial frames, because the AOPLAY_FINAL_CHUNK flag was not correctly set, and handling it otherwise would require an internal FIFO. Fix this by making sure that with gapless audio (used with encoding), the AOPLAY_FINAL_CHUNK is set only once, instead when each file ends. Basically, move the hack in ao_lavc's uninit to uninit_player. One thing can not be entirely correctly handled: if gapless audio is active, we don't know really whether the AO is closed because the file ended playing (i.e. we want to send the buffered remainder of the audio to the AO), or whether the user is quitting the player. (The stop_play flag is overwritten, fixing that is perhaps not worth it.) Handle this by adding additional code to drain the AO and the buffers when playback is quit (see play_current_file() change). Test case: mpv avdevice://lavfi:sine=441 avdevice://lavfi:sine=441 -length 0.2267 -gapless-audio
Diffstat (limited to 'audio/out')
-rw-r--r--audio/out/ao.c6
-rw-r--r--audio/out/ao.h3
-rw-r--r--audio/out/ao_lavc.c59
3 files changed, 33 insertions, 35 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c
index 3f0865af22..ccbcc16e88 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -198,11 +198,7 @@ autoprobe:
void ao_uninit(struct ao *ao, bool cut_audio)
{
- assert(ao->buffer.len >= ao->buffer_playable_size);
- ao->buffer.len = ao->buffer_playable_size;
ao->driver->uninit(ao, cut_audio);
- if (!cut_audio && ao->buffer.len)
- mp_msg(MSGT_AO, MSGL_WARN, "Audio output truncated at end.\n");
talloc_free(ao);
}
@@ -234,8 +230,6 @@ int ao_get_space(struct ao *ao)
void ao_reset(struct ao *ao)
{
- ao->buffer.len = 0;
- ao->buffer_playable_size = 0;
if (ao->driver->reset)
ao->driver->reset(ao);
}
diff --git a/audio/out/ao.h b/audio/out/ao.h
index 3d64102ada..7e7b6968bb 100644
--- a/audio/out/ao.h
+++ b/audio/out/ao.h
@@ -73,7 +73,8 @@ struct ao {
int bps; // bytes per second
double pts; // some mplayer.c state (why is this here?)
struct bstr buffer;
- int buffer_playable_size;
+ int buffer_playable_size; // part of the part of the buffer the AO hasn't
+ // accepted yet with play()
bool probing; // if true, don't fail loudly on init
bool untimed;
bool no_persistent_volume; // the AO does the equivalent of af_volume
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c
index eb41710b82..4364b9054d 100644
--- a/audio/out/ao_lavc.c
+++ b/audio/out/ao_lavc.c
@@ -299,7 +299,6 @@ static int encode(struct ao *ao, double apts, void *data);
static int play(struct ao *ao, void *data, int len, int flags);
static void uninit(struct ao *ao, bool cut_audio)
{
- struct priv *ac = ao->priv;
struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
if (!encode_lavc_start(ectx)) {
@@ -307,33 +306,6 @@ static void uninit(struct ao *ao, bool cut_audio)
return;
}
- if (ac->buffer) {
- if (ao->buffer.len > 0) {
- // TRICK: append aframesize-1 samples to the end, then play() will
- // encode all it can
- size_t extralen =
- (ac->aframesize - 1) * ao->channels.num * ac->sample_size;
- void *paddingbuf = talloc_size(ao, ao->buffer.len + extralen);
- memcpy(paddingbuf, ao->buffer.start, ao->buffer.len);
- fill_with_padding((char *) paddingbuf + ao->buffer.len,
- extralen / ac->sample_size,
- ac->sample_size, ac->sample_padding);
- int written = play(ao, paddingbuf, ao->buffer.len + extralen, 0);
- if (written < ao->buffer.len) {
- MP_ERR(ao, "did not write enough data at the end\n");
- }
- talloc_free(paddingbuf);
- ao->buffer.len = 0;
- }
-
- double outpts = ac->expected_next_pts;
- if (!ectx->options->rawts && ectx->options->copyts)
- outpts += ectx->discontinuity_pts_offset;
- outpts += encode_lavc_getoffset(ectx, ac->stream);
-
- while (encode(ao, outpts, NULL) > 0) ;
- }
-
ao->priv = NULL;
}
@@ -484,6 +456,7 @@ static int play(struct ao *ao, void *data, int len, int flags)
double nextpts;
double pts = ao->pts;
double outpts;
+ int bytelen = len;
len /= ac->sample_size * ao->channels.num;
@@ -491,6 +464,36 @@ static int play(struct ao *ao, void *data, int len, int flags)
MP_WARN(ao, "not ready yet for encoding audio\n");
return 0;
}
+
+ if (flags & AOPLAY_FINAL_CHUNK) {
+ int written = 0;
+ if (len > 0) {
+ size_t extralen =
+ (ac->aframesize - 1) * ao->channels.num * ac->sample_size;
+ paddingbuf = talloc_size(NULL, bytelen + extralen);
+ memcpy(paddingbuf, data, bytelen);
+ fill_with_padding((char *) paddingbuf + bytelen,
+ extralen / ac->sample_size,
+ ac->sample_size, ac->sample_padding);
+ // No danger of recursion, because AOPLAY_FINAL_CHUNK not set
+ written = play(ao, paddingbuf, bytelen + extralen, 0);
+ if (written < bytelen) {
+ MP_ERR(ao, "did not write enough data at the end\n");
+ }
+ talloc_free(paddingbuf);
+ paddingbuf = NULL;
+ }
+
+ outpts = ac->expected_next_pts;
+ if (!ectx->options->rawts && ectx->options->copyts)
+ outpts += ectx->discontinuity_pts_offset;
+ outpts += encode_lavc_getoffset(ectx, ac->stream);
+
+ while (encode(ao, outpts, NULL) > 0) ;
+
+ return FFMIN(written, bytelen);
+ }
+
if (pts == MP_NOPTS_VALUE) {
MP_WARN(ao, "frame without pts, please report; synthesizing pts instead\n");
// synthesize pts from previous expected next pts