mirror of
https://github.com/mpv-player/mpv
synced 2025-02-05 14:42:24 +00:00
671df54e4d
This is mainly a refactor. I'm hoping it will make some things easier in the future due to cleanly separating codec metadata and stream metadata. Also, declare that the "codec" field can not be NULL anymore. demux.c will set it to "" if it's NULL when added. This gets rid of a corner case everything had to handle, but which rarely happened.
293 lines
8.8 KiB
C
293 lines
8.8 KiB
C
/*
|
|
* This file is part of mpv.
|
|
*
|
|
* mpv is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* mpv is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
|
|
#include <libavutil/mem.h>
|
|
|
|
#include "demux/codec_tags.h"
|
|
|
|
#include "common/codecs.h"
|
|
#include "common/msg.h"
|
|
#include "misc/bstr.h"
|
|
|
|
#include "stream/stream.h"
|
|
#include "demux/demux.h"
|
|
|
|
#include "demux/stheader.h"
|
|
|
|
#include "dec_audio.h"
|
|
#include "ad.h"
|
|
#include "audio/format.h"
|
|
#include "audio/audio.h"
|
|
#include "audio/audio_buffer.h"
|
|
|
|
#include "audio/filter/af.h"
|
|
|
|
extern const struct ad_functions ad_lavc;
|
|
extern const struct ad_functions ad_spdif;
|
|
|
|
static const struct ad_functions * const ad_drivers[] = {
|
|
&ad_lavc,
|
|
&ad_spdif,
|
|
NULL
|
|
};
|
|
|
|
static void uninit_decoder(struct dec_audio *d_audio)
|
|
{
|
|
audio_reset_decoding(d_audio);
|
|
if (d_audio->ad_driver) {
|
|
MP_VERBOSE(d_audio, "Uninit audio decoder.\n");
|
|
d_audio->ad_driver->uninit(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)
|
|
{
|
|
if (!d_audio->ad_driver->init(d_audio, decoder)) {
|
|
MP_VERBOSE(d_audio, "Audio decoder init failed.\n");
|
|
d_audio->ad_driver = NULL;
|
|
uninit_decoder(d_audio);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
struct mp_decoder_list *audio_decoder_list(void)
|
|
{
|
|
struct mp_decoder_list *list = talloc_zero(NULL, struct mp_decoder_list);
|
|
for (int i = 0; ad_drivers[i] != NULL; i++)
|
|
ad_drivers[i]->add_decoders(list);
|
|
return list;
|
|
}
|
|
|
|
static struct mp_decoder_list *audio_select_decoders(struct dec_audio *d_audio)
|
|
{
|
|
struct MPOpts *opts = d_audio->opts;
|
|
const char *codec = d_audio->header->codec->codec;
|
|
|
|
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) {
|
|
struct mp_decoder_list *spdif =
|
|
mp_select_decoder_list(list, codec, "spdif", opts->audio_spdif);
|
|
mp_append_decoders(spdif, new);
|
|
talloc_free(new);
|
|
new = spdif;
|
|
}
|
|
talloc_free(list);
|
|
return new;
|
|
}
|
|
|
|
static const struct ad_functions *find_driver(const char *name)
|
|
{
|
|
for (int i = 0; ad_drivers[i] != NULL; i++) {
|
|
if (strcmp(ad_drivers[i]->name, name) == 0)
|
|
return ad_drivers[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int audio_init_best_codec(struct dec_audio *d_audio)
|
|
{
|
|
uninit_decoder(d_audio);
|
|
assert(!d_audio->ad_driver);
|
|
|
|
struct mp_decoder_entry *decoder = NULL;
|
|
struct mp_decoder_list *list = audio_select_decoders(d_audio);
|
|
|
|
mp_print_decoders(d_audio->log, MSGL_V, "Codec list:", list);
|
|
|
|
for (int n = 0; n < list->num_entries; n++) {
|
|
struct mp_decoder_entry *sel = &list->entries[n];
|
|
const struct ad_functions *driver = find_driver(sel->family);
|
|
if (!driver)
|
|
continue;
|
|
MP_VERBOSE(d_audio, "Opening audio decoder %s:%s\n",
|
|
sel->family, sel->decoder);
|
|
d_audio->ad_driver = driver;
|
|
if (init_audio_codec(d_audio, sel->decoder)) {
|
|
decoder = sel;
|
|
break;
|
|
}
|
|
MP_WARN(d_audio, "Audio decoder init failed for "
|
|
"%s:%s\n", sel->family, sel->decoder);
|
|
}
|
|
|
|
if (d_audio->ad_driver) {
|
|
d_audio->decoder_desc =
|
|
talloc_asprintf(d_audio, "%s [%s:%s]", decoder->desc, decoder->family,
|
|
decoder->decoder);
|
|
MP_VERBOSE(d_audio, "Selected audio codec: %s\n", d_audio->decoder_desc);
|
|
} else {
|
|
MP_ERR(d_audio, "Failed to initialize an audio decoder for codec '%s'.\n",
|
|
d_audio->header->codec->codec);
|
|
}
|
|
|
|
talloc_free(list);
|
|
return !!d_audio->ad_driver;
|
|
}
|
|
|
|
void audio_uninit(struct dec_audio *d_audio)
|
|
{
|
|
if (!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);
|
|
}
|
|
|
|
static int decode_new_frame(struct dec_audio *da)
|
|
{
|
|
while (!da->waiting) {
|
|
int ret = da->ad_driver->decode_packet(da, &da->waiting);
|
|
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;
|
|
if (d_audio->waiting) {
|
|
talloc_free(d_audio->waiting);
|
|
d_audio->waiting = NULL;
|
|
}
|
|
}
|