mirror of
https://github.com/mpv-player/mpv
synced 2024-12-26 00:42:57 +00:00
audio: refactor: work towards unentangling audio decoding and filtering
Similar to the video path. dec_audio.c now handles decoding only. It also looks very similar to dec_video.c, and actually contains some of the rewritten code from it. (A further goal might be unifying the decoders, I guess.) High potential for regressions.
This commit is contained in:
parent
27ecc417fe
commit
fef8b7984b
@ -196,21 +196,21 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
|
||||
// LATM may need many packets to find mux info
|
||||
if (ret == AVERROR(EAGAIN)) {
|
||||
mpkt->len = 0;
|
||||
return AD_OK;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
MP_ERR(da, "Error decoding audio.\n");
|
||||
return AD_ERR;
|
||||
return -1;
|
||||
}
|
||||
if (!got_frame)
|
||||
return mpkt ? AD_OK : AD_EOF;
|
||||
return 0;
|
||||
|
||||
double out_pts = mp_pts_from_av(priv->avframe->pkt_pts, NULL);
|
||||
|
||||
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
|
||||
if (!mpframe)
|
||||
return AD_ERR;
|
||||
return -1;
|
||||
|
||||
struct mp_chmap lavc_chmap = mpframe->channels;
|
||||
if (lavc_chmap.num != avctx->channels)
|
||||
|
@ -251,7 +251,7 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
|
||||
spdif_ctx->out_buffer_len = 0;
|
||||
|
||||
if (!mpkt)
|
||||
return AD_EOF;
|
||||
return 0;
|
||||
|
||||
double pts = mpkt->pts;
|
||||
|
||||
@ -261,17 +261,17 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
|
||||
pkt.pts = pkt.dts = 0;
|
||||
if (!spdif_ctx->lavf_ctx) {
|
||||
if (init_filter(da, &pkt) < 0)
|
||||
return AD_ERR;
|
||||
return -1;
|
||||
}
|
||||
int ret = av_write_frame(spdif_ctx->lavf_ctx, &pkt);
|
||||
avio_flush(spdif_ctx->lavf_ctx->pb);
|
||||
if (ret < 0)
|
||||
return AD_ERR;
|
||||
return -1;
|
||||
|
||||
int samples = spdif_ctx->out_buffer_len / spdif_ctx->fmt.sstride;
|
||||
*out = mp_audio_pool_get(spdif_ctx->pool, &spdif_ctx->fmt, samples);
|
||||
if (!*out)
|
||||
return AD_ERR;
|
||||
return -1;
|
||||
|
||||
memcpy((*out)->planes[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
|
||||
(*out)->pts = pts;
|
||||
|
@ -61,8 +61,6 @@ static void uninit_decoder(struct dec_audio *d_audio)
|
||||
d_audio->ad_driver = NULL;
|
||||
talloc_free(d_audio->priv);
|
||||
d_audio->priv = NULL;
|
||||
d_audio->afilter->initialized = -1;
|
||||
d_audio->decode_format = (struct mp_audio){0};
|
||||
}
|
||||
|
||||
static int init_audio_codec(struct dec_audio *d_audio, const char *decoder)
|
||||
@ -93,7 +91,7 @@ static struct mp_decoder_list *audio_select_decoders(struct dec_audio *d_audio)
|
||||
struct mp_decoder_list *list = audio_decoder_list();
|
||||
struct mp_decoder_list *new =
|
||||
mp_select_decoders(list, codec, opts->audio_decoders);
|
||||
if (d_audio->spdif_passthrough) {
|
||||
if (d_audio->try_spdif) {
|
||||
struct mp_decoder_list *spdif =
|
||||
mp_select_decoder_list(list, codec, "spdif", opts->audio_spdif);
|
||||
mp_append_decoders(spdif, new);
|
||||
@ -159,144 +157,101 @@ void audio_uninit(struct dec_audio *d_audio)
|
||||
return;
|
||||
MP_VERBOSE(d_audio, "Uninit audio filters...\n");
|
||||
uninit_decoder(d_audio);
|
||||
af_destroy(d_audio->afilter);
|
||||
talloc_free(d_audio->waiting);
|
||||
talloc_free(d_audio->current_frame);
|
||||
talloc_free(d_audio->packet);
|
||||
talloc_free(d_audio);
|
||||
}
|
||||
|
||||
static int decode_new_frame(struct dec_audio *da)
|
||||
{
|
||||
while (!da->waiting) {
|
||||
if (!da->packet) {
|
||||
if (demux_read_packet_async(da->header, &da->packet) == 0)
|
||||
return AD_WAIT;
|
||||
}
|
||||
|
||||
int ret = da->ad_driver->decode_packet(da, da->packet, &da->waiting);
|
||||
if (ret < 0 || (da->packet && da->packet->len == 0)) {
|
||||
talloc_free(da->packet);
|
||||
da->packet = NULL;
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (da->waiting) {
|
||||
if (da->waiting->pts != MP_NOPTS_VALUE) {
|
||||
if (da->pts != MP_NOPTS_VALUE) {
|
||||
da->pts += da->pts_offset / (double)da->waiting->rate;
|
||||
da->pts_offset = 0;
|
||||
}
|
||||
double newpts = da->waiting->pts;
|
||||
// Keep the interpolated timestamp if it doesn't deviate more
|
||||
// than 1 ms from the real one. (MKV rounded timestamps.)
|
||||
if (da->pts == MP_NOPTS_VALUE || da->pts_offset != 0 ||
|
||||
fabs(da->pts - newpts) > 0.001)
|
||||
{
|
||||
// Attempt to detect jumps in PTS. Even for the lowest
|
||||
// sample rates and with worst container rounded timestamp,
|
||||
// this should be a margin more than enough.
|
||||
if (da->pts != MP_NOPTS_VALUE && fabs(newpts - da->pts) > 0.1)
|
||||
{
|
||||
MP_WARN(da, "Invalid audio PTS: %f -> %f\n",
|
||||
da->pts, newpts);
|
||||
da->pts_reset = true;
|
||||
}
|
||||
da->pts = da->waiting->pts;
|
||||
da->pts_offset = 0;
|
||||
}
|
||||
}
|
||||
da->pts_offset += da->waiting->samples;
|
||||
da->decode_format = *da->waiting;
|
||||
mp_audio_set_null_data(&da->decode_format);
|
||||
}
|
||||
|
||||
if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
|
||||
da->pts = 0;
|
||||
}
|
||||
return mp_audio_config_valid(da->waiting) ? AD_OK : AD_ERR;
|
||||
}
|
||||
|
||||
/* Decode packets until we know the audio format. Then reinit the buffer.
|
||||
* Returns AD_OK on success, negative AD_* code otherwise.
|
||||
* Also returns AD_OK if already initialized (and does nothing).
|
||||
*/
|
||||
int initial_audio_decode(struct dec_audio *da)
|
||||
{
|
||||
return decode_new_frame(da);
|
||||
}
|
||||
|
||||
static bool copy_output(struct af_stream *afs, struct mp_audio_buffer *outbuf,
|
||||
int minsamples, bool eof)
|
||||
{
|
||||
while (mp_audio_buffer_samples(outbuf) < minsamples) {
|
||||
if (af_output_frame(afs, eof) < 0)
|
||||
return true; // error, stop doing stuff
|
||||
struct mp_audio *mpa = af_read_output_frame(afs);
|
||||
if (!mpa)
|
||||
return false; // out of data
|
||||
mp_audio_buffer_append(outbuf, mpa);
|
||||
talloc_free(mpa);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Try to get at least minsamples decoded+filtered samples in outbuf
|
||||
* (total length including possible existing data).
|
||||
* Return 0 on success, or negative AD_* error code.
|
||||
* In the former case outbuf has at least minsamples buffered on return.
|
||||
* In case of EOF/error it might or might not be. */
|
||||
int audio_decode(struct dec_audio *da, struct mp_audio_buffer *outbuf,
|
||||
int minsamples)
|
||||
{
|
||||
struct af_stream *afs = da->afilter;
|
||||
if (afs->initialized < 1)
|
||||
return AD_ERR;
|
||||
|
||||
MP_STATS(da, "start audio");
|
||||
|
||||
int res;
|
||||
while (1) {
|
||||
res = 0;
|
||||
|
||||
if (copy_output(afs, outbuf, minsamples, false))
|
||||
break;
|
||||
|
||||
res = decode_new_frame(da);
|
||||
if (res < 0) {
|
||||
// drain filters first (especially for true EOF case)
|
||||
copy_output(afs, outbuf, minsamples, true);
|
||||
break;
|
||||
}
|
||||
|
||||
// On format change, make sure to drain the filter chain.
|
||||
if (!mp_audio_config_equals(&afs->input, da->waiting)) {
|
||||
copy_output(afs, outbuf, minsamples, true);
|
||||
res = AD_NEW_FMT;
|
||||
break;
|
||||
}
|
||||
|
||||
struct mp_audio *mpa = da->waiting;
|
||||
da->waiting = NULL;
|
||||
if (af_filter_frame(afs, mpa) < 0)
|
||||
return AD_ERR;
|
||||
}
|
||||
|
||||
MP_STATS(da, "end audio");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void audio_reset_decoding(struct dec_audio *d_audio)
|
||||
{
|
||||
if (d_audio->ad_driver)
|
||||
d_audio->ad_driver->control(d_audio, ADCTRL_RESET, NULL);
|
||||
af_seek_reset(d_audio->afilter);
|
||||
d_audio->pts = MP_NOPTS_VALUE;
|
||||
d_audio->pts_offset = 0;
|
||||
d_audio->pts_reset = false;
|
||||
talloc_free(d_audio->waiting);
|
||||
d_audio->waiting = NULL;
|
||||
talloc_free(d_audio->current_frame);
|
||||
d_audio->current_frame = NULL;
|
||||
talloc_free(d_audio->packet);
|
||||
d_audio->packet = NULL;
|
||||
}
|
||||
|
||||
static void fix_audio_pts(struct dec_audio *da)
|
||||
{
|
||||
if (!da->current_frame)
|
||||
return;
|
||||
|
||||
if (da->current_frame->pts != MP_NOPTS_VALUE) {
|
||||
double newpts = da->current_frame->pts;
|
||||
// Keep the interpolated timestamp if it doesn't deviate more
|
||||
// than 1 ms from the real one. (MKV rounded timestamps.)
|
||||
if (da->pts == MP_NOPTS_VALUE || fabs(da->pts - newpts) > 0.001) {
|
||||
// Attempt to detect jumps in PTS. Even for the lowest
|
||||
// sample rates and with worst container rounded timestamp,
|
||||
// this should be a margin more than enough.
|
||||
if (da->pts != MP_NOPTS_VALUE && fabs(newpts - da->pts) > 0.1) {
|
||||
MP_WARN(da, "Invalid audio PTS: %f -> %f\n",
|
||||
da->pts, newpts);
|
||||
da->pts_reset = true;
|
||||
}
|
||||
da->pts = da->current_frame->pts;
|
||||
}
|
||||
}
|
||||
|
||||
if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
|
||||
da->pts = 0;
|
||||
|
||||
da->current_frame->pts = da->pts;
|
||||
|
||||
if (da->pts != MP_NOPTS_VALUE)
|
||||
da->pts += da->current_frame->samples / (double)da->current_frame->rate;
|
||||
}
|
||||
|
||||
void audio_work(struct dec_audio *da)
|
||||
{
|
||||
if (da->current_frame)
|
||||
return;
|
||||
|
||||
if (!da->packet && demux_read_packet_async(da->header, &da->packet) == 0) {
|
||||
da->current_state = AUDIO_WAIT;
|
||||
return;
|
||||
}
|
||||
|
||||
bool had_packet = !!da->packet;
|
||||
|
||||
int ret = da->ad_driver->decode_packet(da, da->packet, &da->current_frame);
|
||||
if (ret < 0 || (da->packet && da->packet->len == 0)) {
|
||||
talloc_free(da->packet);
|
||||
da->packet = NULL;
|
||||
}
|
||||
|
||||
if (da->current_frame && !mp_audio_config_valid(da->current_frame)) {
|
||||
talloc_free(da->current_frame);
|
||||
da->current_frame = NULL;
|
||||
}
|
||||
|
||||
da->current_state = AUDIO_OK;
|
||||
if (!da->current_frame) {
|
||||
da->current_state = AUDIO_EOF;
|
||||
if (had_packet)
|
||||
da->current_state = AUDIO_SKIP;
|
||||
}
|
||||
|
||||
fix_audio_pts(da);
|
||||
}
|
||||
|
||||
// Fetch an audio frame decoded with audio_work(). Returns one of:
|
||||
// AUDIO_OK: *out_frame is set to a new image
|
||||
// AUDIO_WAIT: waiting for demuxer; will receive a wakeup signal
|
||||
// AUDIO_EOF: end of file, no more frames to be expected
|
||||
// AUDIO_SKIP: dropped frame or something similar
|
||||
int audio_get_frame(struct dec_audio *da, struct mp_audio **out_frame)
|
||||
{
|
||||
*out_frame = NULL;
|
||||
if (da->current_frame) {
|
||||
*out_frame = da->current_frame;
|
||||
da->current_frame = NULL;
|
||||
return AUDIO_OK;
|
||||
}
|
||||
if (da->current_state == AUDIO_OK)
|
||||
return AUDIO_SKIP;
|
||||
return da->current_state;
|
||||
}
|
||||
|
@ -30,39 +30,38 @@ struct dec_audio {
|
||||
struct mp_log *log;
|
||||
struct MPOpts *opts;
|
||||
struct mpv_global *global;
|
||||
bool spdif_passthrough, spdif_failed;
|
||||
const struct ad_functions *ad_driver;
|
||||
struct sh_stream *header;
|
||||
struct af_stream *afilter;
|
||||
char *decoder_desc;
|
||||
struct mp_audio decode_format;
|
||||
struct mp_audio *waiting; // used on format-change
|
||||
// last known pts value in output from decoder
|
||||
double pts;
|
||||
// number of samples output by decoder after last known pts
|
||||
int pts_offset;
|
||||
|
||||
bool try_spdif;
|
||||
|
||||
// set every time a jump in timestamps is encountered
|
||||
bool pts_reset;
|
||||
|
||||
// For free use by the ad_driver
|
||||
void *priv;
|
||||
// Strictly internal to dec_audio.c
|
||||
struct demux_packet *packet;
|
||||
};
|
||||
|
||||
enum {
|
||||
AD_OK = 0,
|
||||
AD_ERR = -1,
|
||||
AD_EOF = -2,
|
||||
AD_NEW_FMT = -3,
|
||||
AD_WAIT = -4,
|
||||
// Strictly internal (dec_audio.c).
|
||||
|
||||
double pts; // endpts of previous frame
|
||||
struct demux_packet *packet;
|
||||
struct mp_audio *current_frame;
|
||||
int current_state;
|
||||
};
|
||||
|
||||
struct mp_decoder_list *audio_decoder_list(void);
|
||||
int audio_init_best_codec(struct dec_audio *d_audio);
|
||||
int audio_decode(struct dec_audio *d_audio, struct mp_audio_buffer *outbuf,
|
||||
int minsamples);
|
||||
int initial_audio_decode(struct dec_audio *d_audio);
|
||||
void audio_reset_decoding(struct dec_audio *d_audio);
|
||||
void audio_uninit(struct dec_audio *d_audio);
|
||||
|
||||
void audio_work(struct dec_audio *d_audio);
|
||||
|
||||
#define AUDIO_OK 1
|
||||
#define AUDIO_WAIT 0
|
||||
#define AUDIO_EOF -1
|
||||
#define AUDIO_SKIP -2
|
||||
int audio_get_frame(struct dec_audio *d_audio, struct mp_audio **out_frame);
|
||||
|
||||
void audio_reset_decoding(struct dec_audio *d_audio);
|
||||
|
||||
#endif /* MPLAYER_DEC_AUDIO_H */
|
||||
|
195
player/audio.c
195
player/audio.c
@ -43,6 +43,14 @@
|
||||
#include "core.h"
|
||||
#include "command.h"
|
||||
|
||||
enum {
|
||||
AD_OK = 0,
|
||||
AD_ERR = -1,
|
||||
AD_EOF = -2,
|
||||
AD_NEW_FMT = -3,
|
||||
AD_WAIT = -4,
|
||||
};
|
||||
|
||||
// Use pitch correction only for speed adjustments by the user, not minor sync
|
||||
// correction ones.
|
||||
static int get_speed_method(struct MPContext *mpctx)
|
||||
@ -55,7 +63,7 @@ static int get_speed_method(struct MPContext *mpctx)
|
||||
// return true; if filter recreation is needed, return false.
|
||||
static bool update_speed_filters(struct MPContext *mpctx)
|
||||
{
|
||||
struct af_stream *afs = mpctx->d_audio->afilter;
|
||||
struct af_stream *afs = mpctx->ao_chain->af;
|
||||
double speed = mpctx->audio_speed;
|
||||
|
||||
if (afs->initialized < 1)
|
||||
@ -81,7 +89,7 @@ static bool update_speed_filters(struct MPContext *mpctx)
|
||||
// Update speed, and insert/remove filters if necessary.
|
||||
static void recreate_speed_filters(struct MPContext *mpctx)
|
||||
{
|
||||
struct af_stream *afs = mpctx->d_audio->afilter;
|
||||
struct af_stream *afs = mpctx->ao_chain->af;
|
||||
|
||||
if (update_speed_filters(mpctx))
|
||||
return;
|
||||
@ -113,9 +121,9 @@ fail:
|
||||
|
||||
static int recreate_audio_filters(struct MPContext *mpctx)
|
||||
{
|
||||
assert(mpctx->d_audio);
|
||||
assert(mpctx->ao_chain);
|
||||
|
||||
struct af_stream *afs = mpctx->d_audio->afilter;
|
||||
struct af_stream *afs = mpctx->ao_chain->af;
|
||||
if (afs->initialized < 1 && af_init(afs) < 0)
|
||||
goto fail;
|
||||
|
||||
@ -134,11 +142,11 @@ fail:
|
||||
|
||||
int reinit_audio_filters(struct MPContext *mpctx)
|
||||
{
|
||||
struct dec_audio *d_audio = mpctx->d_audio;
|
||||
if (!d_audio)
|
||||
struct ao_chain *ao_c = mpctx->ao_chain;
|
||||
if (!ao_c)
|
||||
return 0;
|
||||
|
||||
af_uninit(mpctx->d_audio->afilter);
|
||||
af_uninit(ao_c->af);
|
||||
return recreate_audio_filters(mpctx) < 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
@ -148,7 +156,7 @@ void update_playback_speed(struct MPContext *mpctx)
|
||||
mpctx->audio_speed = mpctx->opts->playback_speed * mpctx->speed_factor_a;
|
||||
mpctx->video_speed = mpctx->opts->playback_speed * mpctx->speed_factor_v;
|
||||
|
||||
if (!mpctx->d_audio || mpctx->d_audio->afilter->initialized < 1)
|
||||
if (!mpctx->ao_chain || mpctx->ao_chain->af->initialized < 1)
|
||||
return;
|
||||
|
||||
if (!update_speed_filters(mpctx))
|
||||
@ -161,7 +169,11 @@ void reset_audio_state(struct MPContext *mpctx)
|
||||
audio_reset_decoding(mpctx->d_audio);
|
||||
if (mpctx->ao_buffer)
|
||||
mp_audio_buffer_clear(mpctx->ao_buffer);
|
||||
mpctx->audio_status = mpctx->d_audio ? STATUS_SYNCING : STATUS_EOF;
|
||||
if (mpctx->ao_chain) {
|
||||
mpctx->ao_chain->pts = MP_NOPTS_VALUE;
|
||||
af_seek_reset(mpctx->ao_chain->af);
|
||||
}
|
||||
mpctx->audio_status = mpctx->ao_chain ? STATUS_SYNCING : STATUS_EOF;
|
||||
mpctx->delay = 0;
|
||||
mpctx->audio_drop_throttle = 0;
|
||||
mpctx->audio_stat_start = 0;
|
||||
@ -185,12 +197,15 @@ void uninit_audio_out(struct MPContext *mpctx)
|
||||
|
||||
void uninit_audio_chain(struct MPContext *mpctx)
|
||||
{
|
||||
if (mpctx->d_audio) {
|
||||
if (mpctx->ao_chain) {
|
||||
mixer_uninit_audio(mpctx->mixer);
|
||||
audio_uninit(mpctx->d_audio);
|
||||
mpctx->d_audio = NULL;
|
||||
talloc_free(mpctx->ao_buffer);
|
||||
mpctx->ao_buffer = NULL;
|
||||
af_destroy(mpctx->ao_chain->af);
|
||||
talloc_free(mpctx->ao_chain);
|
||||
mpctx->ao_chain = NULL;
|
||||
mpctx->audio_status = STATUS_EOF;
|
||||
reselect_demux_streams(mpctx);
|
||||
|
||||
@ -210,15 +225,25 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
||||
|
||||
mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL);
|
||||
|
||||
struct ao_chain *ao_c = mpctx->ao_chain;
|
||||
|
||||
if (!mpctx->d_audio) {
|
||||
assert(!ao_c);
|
||||
mpctx->d_audio = talloc_zero(NULL, struct dec_audio);
|
||||
mpctx->d_audio->log = mp_log_new(mpctx->d_audio, mpctx->log, "!ad");
|
||||
mpctx->d_audio->global = mpctx->global;
|
||||
mpctx->d_audio->opts = opts;
|
||||
mpctx->d_audio->header = sh;
|
||||
mpctx->d_audio->afilter = af_new(mpctx->global);
|
||||
mpctx->d_audio->afilter->replaygain_data = sh->codec->replaygain_data;
|
||||
mpctx->d_audio->spdif_passthrough = true;
|
||||
ao_c = talloc_zero(NULL, struct ao_chain);
|
||||
mpctx->ao_chain = ao_c;
|
||||
ao_c->log = mpctx->d_audio->log;
|
||||
ao_c->af = af_new(mpctx->global);
|
||||
ao_c->af->replaygain_data = sh->codec->replaygain_data;
|
||||
ao_c->spdif_passthrough = true;
|
||||
ao_c->pts = MP_NOPTS_VALUE;
|
||||
ao_c->ao = mpctx->ao;
|
||||
ao_c->audio_src = mpctx->d_audio;
|
||||
mpctx->d_audio->try_spdif = ao_c->spdif_passthrough;
|
||||
mpctx->ao_buffer = mp_audio_buffer_create(NULL);
|
||||
if (!audio_init_best_codec(mpctx->d_audio))
|
||||
goto init_error;
|
||||
@ -232,7 +257,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
||||
}
|
||||
assert(mpctx->d_audio);
|
||||
|
||||
struct mp_audio in_format = mpctx->d_audio->decode_format;
|
||||
struct mp_audio in_format = ao_c->input_format;
|
||||
|
||||
if (!mp_audio_config_valid(&in_format)) {
|
||||
// We don't know the audio format yet - so configure it later as we're
|
||||
@ -248,7 +273,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
||||
uninit_audio_out(mpctx);
|
||||
}
|
||||
|
||||
struct af_stream *afs = mpctx->d_audio->afilter;
|
||||
struct af_stream *afs = ao_c->af;
|
||||
|
||||
afs->output = (struct mp_audio){0};
|
||||
if (mpctx->ao) {
|
||||
@ -272,7 +297,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
||||
|
||||
if (!mpctx->ao) {
|
||||
bool spdif_fallback = af_fmt_is_spdif(afs->output.format) &&
|
||||
mpctx->d_audio->spdif_passthrough;
|
||||
ao_c->spdif_passthrough;
|
||||
bool ao_null_fallback = opts->ao_null_fallback && !spdif_fallback;
|
||||
|
||||
mp_chmap_remove_useless_channels(&afs->output.channels,
|
||||
@ -282,6 +307,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
||||
mpctx->ao = ao_init_best(mpctx->global, ao_null_fallback, mpctx->input,
|
||||
mpctx->encode_lavc_ctx, afs->output.rate,
|
||||
afs->output.format, afs->output.channels);
|
||||
ao_c->ao = mpctx->ao;
|
||||
|
||||
struct mp_audio fmt = {0};
|
||||
if (mpctx->ao)
|
||||
@ -293,14 +319,16 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
||||
MP_ERR(mpctx, "Passthrough format unsupported.\n");
|
||||
ao_uninit(mpctx->ao);
|
||||
mpctx->ao = NULL;
|
||||
ao_c->ao = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mpctx->ao) {
|
||||
// If spdif was used, try to fallback to PCM.
|
||||
if (spdif_fallback) {
|
||||
mpctx->d_audio->spdif_passthrough = false;
|
||||
mpctx->d_audio->spdif_failed = true;
|
||||
ao_c->spdif_passthrough = false;
|
||||
ao_c->spdif_failed = true;
|
||||
mpctx->d_audio->try_spdif = false;
|
||||
if (!audio_init_best_codec(mpctx->d_audio))
|
||||
goto init_error;
|
||||
reset_audio_state(mpctx);
|
||||
@ -346,35 +374,22 @@ no_audio:
|
||||
// ao so far.
|
||||
double written_audio_pts(struct MPContext *mpctx)
|
||||
{
|
||||
struct dec_audio *d_audio = mpctx->d_audio;
|
||||
if (!d_audio)
|
||||
struct ao_chain *ao_c = mpctx->ao_chain;
|
||||
if (!ao_c)
|
||||
return MP_NOPTS_VALUE;
|
||||
|
||||
struct mp_audio in_format = d_audio->decode_format;
|
||||
struct mp_audio in_format = ao_c->input_format;
|
||||
|
||||
if (!mp_audio_config_valid(&in_format) || d_audio->afilter->initialized < 1)
|
||||
if (!mp_audio_config_valid(&in_format) || ao_c->af->initialized < 1)
|
||||
return MP_NOPTS_VALUE;
|
||||
|
||||
// first calculate the end pts of audio that has been output by decoder
|
||||
double a_pts = d_audio->pts;
|
||||
double a_pts = ao_c->pts;
|
||||
if (a_pts == MP_NOPTS_VALUE)
|
||||
return MP_NOPTS_VALUE;
|
||||
|
||||
// d_audio->pts is the timestamp of the first sample of the latest frame
|
||||
// the with a known pts that the decoder has returned. d_audio->pts_offset
|
||||
// is the amount of samples the decoder has returned after that timestamp
|
||||
// (includes the frame size).
|
||||
a_pts += d_audio->pts_offset / (double)in_format.rate;
|
||||
|
||||
// Now a_pts hopefully holds the pts for end of audio from decoder.
|
||||
// Subtract data in buffers between decoder and audio out.
|
||||
|
||||
// Decoded but not filtered
|
||||
if (d_audio->waiting)
|
||||
a_pts -= d_audio->waiting->samples / (double)in_format.rate;
|
||||
|
||||
// Data buffered in audio filters, measured in seconds of "missing" output
|
||||
double buffered_output = af_calc_delay(d_audio->afilter);
|
||||
double buffered_output = af_calc_delay(ao_c->af);
|
||||
|
||||
// Data that was ready for ao but was buffered because ao didn't fully
|
||||
// accept everything to internal buffers yet
|
||||
@ -498,20 +513,106 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool copy_output(struct af_stream *afs, struct mp_audio_buffer *outbuf,
|
||||
int minsamples, bool eof)
|
||||
{
|
||||
while (mp_audio_buffer_samples(outbuf) < minsamples) {
|
||||
if (af_output_frame(afs, eof) < 0)
|
||||
return true; // error, stop doing stuff
|
||||
struct mp_audio *mpa = af_read_output_frame(afs);
|
||||
if (!mpa)
|
||||
return false; // out of data
|
||||
mp_audio_buffer_append(outbuf, mpa);
|
||||
talloc_free(mpa);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int decode_new_frame(struct ao_chain *ao_c)
|
||||
{
|
||||
if (ao_c->input_frame)
|
||||
return AD_OK;
|
||||
|
||||
int res = AUDIO_SKIP;
|
||||
while (res == AUDIO_SKIP) {
|
||||
audio_work(ao_c->audio_src);
|
||||
res = audio_get_frame(ao_c->audio_src, &ao_c->input_frame);
|
||||
}
|
||||
|
||||
if (ao_c->input_frame)
|
||||
mp_audio_copy_config(&ao_c->input_format, ao_c->input_frame);
|
||||
|
||||
switch (res) {
|
||||
case AUDIO_OK: return AD_OK;
|
||||
case AUDIO_WAIT: return AD_WAIT;
|
||||
case AUDIO_EOF: return AD_EOF;
|
||||
default: abort();
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to get at least minsamples decoded+filtered samples in outbuf
|
||||
* (total length including possible existing data).
|
||||
* Return 0 on success, or negative AD_* error code.
|
||||
* In the former case outbuf has at least minsamples buffered on return.
|
||||
* In case of EOF/error it might or might not be. */
|
||||
static int filter_audio(struct ao_chain *ao_c, struct mp_audio_buffer *outbuf,
|
||||
int minsamples)
|
||||
{
|
||||
struct af_stream *afs = ao_c->af;
|
||||
if (afs->initialized < 1)
|
||||
return AD_ERR;
|
||||
|
||||
MP_STATS(ao_c, "start audio");
|
||||
|
||||
int res;
|
||||
while (1) {
|
||||
res = 0;
|
||||
|
||||
if (copy_output(afs, outbuf, minsamples, false))
|
||||
break;
|
||||
|
||||
res = decode_new_frame(ao_c);
|
||||
if (res < 0) {
|
||||
// drain filters first (especially for true EOF case)
|
||||
copy_output(afs, outbuf, minsamples, true);
|
||||
break;
|
||||
}
|
||||
|
||||
// On format change, make sure to drain the filter chain.
|
||||
if (!mp_audio_config_equals(&afs->input, ao_c->input_frame)) {
|
||||
copy_output(afs, outbuf, minsamples, true);
|
||||
res = AD_NEW_FMT;
|
||||
break;
|
||||
}
|
||||
|
||||
struct mp_audio *mpa = ao_c->input_frame;
|
||||
ao_c->input_frame = NULL;
|
||||
ao_c->pts = mpa->pts + mpa->samples / (double)mpa->rate;
|
||||
if (af_filter_frame(afs, mpa) < 0)
|
||||
return AD_ERR;
|
||||
}
|
||||
|
||||
MP_STATS(ao_c, "end audio");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
||||
{
|
||||
struct MPOpts *opts = mpctx->opts;
|
||||
struct dec_audio *d_audio = mpctx->d_audio;
|
||||
struct ao_chain *ao_c = mpctx->ao_chain;
|
||||
|
||||
dump_audio_stats(mpctx);
|
||||
|
||||
if (mpctx->ao && ao_query_and_reset_events(mpctx->ao, AO_EVENT_RELOAD)) {
|
||||
ao_reset(mpctx->ao);
|
||||
uninit_audio_out(mpctx);
|
||||
if (d_audio) {
|
||||
if (mpctx->d_audio->spdif_failed) {
|
||||
mpctx->d_audio->spdif_failed = false;
|
||||
mpctx->d_audio->spdif_passthrough = true;
|
||||
if (ao_c) {
|
||||
struct dec_audio *d_audio = ao_c->audio_src;
|
||||
if (d_audio && ao_c->spdif_failed) {
|
||||
ao_c->spdif_failed = false;
|
||||
mpctx->d_audio->try_spdif = true;
|
||||
if (!audio_init_best_codec(mpctx->d_audio)) {
|
||||
MP_ERR(mpctx, "Error reinitializing audio.\n");
|
||||
error_on_track(mpctx, mpctx->current_track[0][STREAM_AUDIO]);
|
||||
@ -522,13 +623,13 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
||||
}
|
||||
}
|
||||
|
||||
if (!d_audio)
|
||||
if (!ao_c)
|
||||
return;
|
||||
|
||||
if (d_audio->afilter->initialized < 1 || !mpctx->ao) {
|
||||
if (ao_c->af->initialized < 1 || !mpctx->ao) {
|
||||
// Probe the initial audio format. Returns AD_OK (and does nothing) if
|
||||
// the format is already known.
|
||||
int r = initial_audio_decode(mpctx->d_audio);
|
||||
int r = decode_new_frame(mpctx->ao_chain);
|
||||
if (r == AD_WAIT)
|
||||
return; // continue later when new data is available
|
||||
reinit_audio_chain(mpctx);
|
||||
@ -536,7 +637,7 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
||||
return; // try again next iteration
|
||||
}
|
||||
|
||||
if (mpctx->vo_chain && d_audio->pts_reset) {
|
||||
if (mpctx->vo_chain && ao_c->audio_src->pts_reset) {
|
||||
MP_VERBOSE(mpctx, "Reset playback due to audio timestamp reset.\n");
|
||||
reset_playback_state(mpctx);
|
||||
mpctx->sleeptime = 0;
|
||||
@ -586,7 +687,7 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
||||
int status = AD_OK;
|
||||
bool working = false;
|
||||
if (playsize > mp_audio_buffer_samples(mpctx->ao_buffer)) {
|
||||
status = audio_decode(d_audio, mpctx->ao_buffer, playsize);
|
||||
status = filter_audio(mpctx->ao_chain, mpctx->ao_buffer, playsize);
|
||||
if (status == AD_WAIT)
|
||||
return;
|
||||
if (status == AD_NEW_FMT) {
|
||||
|
@ -1242,9 +1242,9 @@ static int mp_property_filter_metadata(void *ctx, struct m_property *prop,
|
||||
struct vf_chain *vf = mpctx->vo_chain->vf;
|
||||
res = vf_control_by_label(vf, VFCTRL_GET_METADATA, &metadata, key);
|
||||
} else if (strcmp(type, "af") == 0) {
|
||||
if (!(mpctx->d_audio && mpctx->d_audio->afilter))
|
||||
if (!(mpctx->ao_chain && mpctx->ao_chain->af))
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
struct af_stream *af = mpctx->d_audio->afilter;
|
||||
struct af_stream *af = mpctx->ao_chain->af;
|
||||
res = af_control_by_label(af, AF_CONTROL_GET_METADATA, &metadata, key);
|
||||
}
|
||||
switch (res) {
|
||||
@ -1751,8 +1751,8 @@ static int mp_property_audio_params(void *ctx, struct m_property *prop,
|
||||
{
|
||||
MPContext *mpctx = ctx;
|
||||
struct mp_audio fmt = {0};
|
||||
if (mpctx->d_audio)
|
||||
fmt = mpctx->d_audio->decode_format;
|
||||
if (mpctx->ao_chain)
|
||||
fmt = mpctx->ao_chain->input_format;
|
||||
return property_audiofmt(fmt, action, arg);
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "options/options.h"
|
||||
#include "sub/osd.h"
|
||||
#include "demux/timeline.h"
|
||||
#include "audio/audio.h"
|
||||
#include "video/mp_image.h"
|
||||
#include "video/out/vo.h"
|
||||
|
||||
@ -171,6 +172,25 @@ struct vo_chain {
|
||||
struct dec_video *video_src;
|
||||
};
|
||||
|
||||
// Like vo_chain, for audio.
|
||||
struct ao_chain {
|
||||
struct mp_log *log;
|
||||
|
||||
double pts; // timestamp of first sample output by decoder
|
||||
bool spdif_passthrough, spdif_failed;
|
||||
|
||||
struct af_stream *af;
|
||||
struct ao *ao;
|
||||
|
||||
// 1-element input frame queue.
|
||||
struct mp_audio *input_frame;
|
||||
|
||||
// Last known input_mpi format (so vf can be reinitialized any time).
|
||||
struct mp_audio input_format;
|
||||
|
||||
struct dec_audio *audio_src;
|
||||
};
|
||||
|
||||
/* Note that playback can be paused, stopped, etc. at any time. While paused,
|
||||
* playback restart is still active, because you want seeking to work even
|
||||
* if paused.
|
||||
@ -278,6 +298,7 @@ typedef struct MPContext {
|
||||
struct ao *ao;
|
||||
struct mp_audio *ao_decoder_fmt; // for weak gapless audio check
|
||||
struct mp_audio_buffer *ao_buffer; // queued audio; passed to ao_play() later
|
||||
struct ao_chain *ao_chain;
|
||||
|
||||
struct vo_chain *vo_chain;
|
||||
|
||||
|
@ -889,8 +889,8 @@ static double find_best_speed(struct MPContext *mpctx, double vsync)
|
||||
|
||||
static bool using_spdif_passthrough(struct MPContext *mpctx)
|
||||
{
|
||||
if (mpctx->d_audio && mpctx->d_audio->afilter)
|
||||
return !af_fmt_is_pcm(mpctx->d_audio->afilter->input.format);
|
||||
if (mpctx->ao_chain)
|
||||
return !af_fmt_is_pcm(mpctx->ao_chain->input_format.format);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user