1
0
mirror of https://github.com/mpv-player/mpv synced 2024-12-27 09:32:40 +00:00
mpv/audio/decode/ad_lavc.c
wm4 139f6b5de7 ad_lavc, vd_lavc: fix a recent libavcodec deprecation warning
Both AVFrame.pts and AVFrame.pkt_pts have existed for a long time. Until
now, decoders always returned the pts via the pkt_pts field, while the
pts field was used for encoding and libavfilter only. Recently, pkt_pts
was deprecated, and pts was switched to always carry the pts.

This means we have to be careful not to accidentally use the wrong
field, depending on the libavcodec version. We have to explicitly check
the version numbers. Of course the version numbers are completely
idiotic, because idiotically the pkg-config and library names are the
same for FFmpeg and Libav, so we have to deal with this explicitly as
well.
2016-10-17 19:18:03 +02:00

320 lines
9.1 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 <stdbool.h>
#include <assert.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/common.h>
#include <libavutil/intreadwrite.h>
#include "mpv_talloc.h"
#include "config.h"
#include "common/av_common.h"
#include "common/codecs.h"
#include "common/msg.h"
#include "options/options.h"
#include "ad.h"
#include "audio/fmt-conversion.h"
struct priv {
AVCodecContext *avctx;
AVFrame *avframe;
struct mp_audio frame;
bool force_channel_map;
uint32_t skip_samples, trim_samples;
bool preroll_done;
double next_pts;
bool needs_reset;
AVRational codec_timebase;
};
static void uninit(struct dec_audio *da);
#define OPT_BASE_STRUCT struct ad_lavc_params
struct ad_lavc_params {
float ac3drc;
int downmix;
int threads;
char **avopts;
};
const struct m_sub_options ad_lavc_conf = {
.opts = (const m_option_t[]) {
OPT_FLOATRANGE("ac3drc", ac3drc, 0, 0, 6),
OPT_FLAG("downmix", downmix, 0),
OPT_INTRANGE("threads", threads, 0, 0, 16),
OPT_KEYVALUELIST("o", avopts, 0),
{0}
},
.size = sizeof(struct ad_lavc_params),
.defaults = &(const struct ad_lavc_params){
.ac3drc = 0,
.downmix = 1,
.threads = 1,
},
};
static int init(struct dec_audio *da, const char *decoder)
{
struct MPOpts *mpopts = da->opts;
struct ad_lavc_params *opts = mpopts->ad_lavc_params;
AVCodecContext *lavc_context;
AVCodec *lavc_codec;
struct mp_codec_params *c = da->codec;
struct priv *ctx = talloc_zero(NULL, struct priv);
da->priv = ctx;
ctx->codec_timebase = mp_get_codec_timebase(da->codec);
ctx->force_channel_map = c->force_channels;
lavc_codec = avcodec_find_decoder_by_name(decoder);
if (!lavc_codec) {
MP_ERR(da, "Cannot find codec '%s' in libavcodec...\n", decoder);
uninit(da);
return 0;
}
lavc_context = avcodec_alloc_context3(lavc_codec);
ctx->avctx = lavc_context;
ctx->avframe = av_frame_alloc();
lavc_context->refcounted_frames = 1;
lavc_context->codec_type = AVMEDIA_TYPE_AUDIO;
lavc_context->codec_id = lavc_codec->id;
#if LIBAVCODEC_VERSION_MICRO >= 100
lavc_context->pkt_timebase = ctx->codec_timebase;
#endif
if (opts->downmix && mpopts->audio_output_channels.num_chmaps == 1) {
lavc_context->request_channel_layout =
mp_chmap_to_lavc(&mpopts->audio_output_channels.chmaps[0]);
}
// Always try to set - option only exists for AC3 at the moment
av_opt_set_double(lavc_context, "drc_scale", opts->ac3drc,
AV_OPT_SEARCH_CHILDREN);
#if HAVE_AVFRAME_SKIP_SAMPLES
// Let decoder add AV_FRAME_DATA_SKIP_SAMPLES.
av_opt_set(lavc_context, "flags2", "+skip_manual", AV_OPT_SEARCH_CHILDREN);
#endif
mp_set_avopts(da->log, lavc_context, opts->avopts);
lavc_context->codec_tag = c->codec_tag;
lavc_context->sample_rate = c->samplerate;
lavc_context->bit_rate = c->bitrate;
lavc_context->block_align = c->block_align;
lavc_context->bits_per_coded_sample = c->bits_per_coded_sample;
lavc_context->channels = c->channels.num;
if (!mp_chmap_is_unknown(&c->channels))
lavc_context->channel_layout = mp_chmap_to_lavc(&c->channels);
// demux_mkv
mp_lavc_set_extradata(lavc_context, c->extradata, c->extradata_size);
mp_set_lav_codec_headers(lavc_context, c);
mp_set_avcodec_threads(da->log, lavc_context, opts->threads);
/* open it */
if (avcodec_open2(lavc_context, lavc_codec, NULL) < 0) {
MP_ERR(da, "Could not open codec.\n");
uninit(da);
return 0;
}
ctx->next_pts = MP_NOPTS_VALUE;
return 1;
}
static void uninit(struct dec_audio *da)
{
struct priv *ctx = da->priv;
if (!ctx)
return;
AVCodecContext *lavc_context = ctx->avctx;
if (lavc_context) {
if (avcodec_close(lavc_context) < 0)
MP_ERR(da, "Could not close codec.\n");
av_freep(&lavc_context->extradata);
av_freep(&lavc_context);
}
av_frame_free(&ctx->avframe);
}
static int control(struct dec_audio *da, int cmd, void *arg)
{
struct priv *ctx = da->priv;
switch (cmd) {
case ADCTRL_RESET:
avcodec_flush_buffers(ctx->avctx);
ctx->skip_samples = 0;
ctx->trim_samples = 0;
ctx->preroll_done = false;
ctx->next_pts = MP_NOPTS_VALUE;
ctx->needs_reset = false;
return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
struct mp_audio **out)
{
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
// If the decoder discards the timestamp for some reason, we use the
// interpolated PTS. Initialize it so that it works for the initial
// packet as well.
if (mpkt && priv->next_pts == MP_NOPTS_VALUE)
priv->next_pts = mpkt->pts;
int in_len = mpkt ? mpkt->len : 0;
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, &priv->codec_timebase);
int got_frame = 0;
av_frame_unref(priv->avframe);
if (priv->needs_reset)
control(da, ADCTRL_RESET, NULL);
#if HAVE_AVCODEC_NEW_CODEC_API
int ret = avcodec_send_packet(avctx, &pkt);
if (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
if (ret >= 0 && mpkt)
mpkt->len = 0;
ret = avcodec_receive_frame(avctx, priv->avframe);
if (ret >= 0)
got_frame = 1;
if (ret == AVERROR_EOF)
priv->needs_reset = true;
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
ret = 0;
}
#else
int ret = avcodec_decode_audio4(avctx, priv->avframe, &got_frame, &pkt);
if (mpkt) {
// At least "shorten" decodes sub-frames, instead of the whole packet.
// At least "mpc8" can return 0 and wants the packet again next time.
if (ret >= 0) {
ret = FFMIN(ret, mpkt->len); // sanity check against decoder overreads
mpkt->buffer += ret;
mpkt->len -= ret;
mpkt->pts = MP_NOPTS_VALUE; // don't reset PTS next time
}
// LATM may need many packets to find mux info
if (ret == AVERROR(EAGAIN)) {
mpkt->len = 0;
return 0;
}
}
#endif
if (ret < 0) {
MP_ERR(da, "Error decoding audio.\n");
return -1;
}
if (!got_frame)
return 0;
double out_pts = mp_pts_from_av(MP_AVFRAME_DEC_PTS(priv->avframe),
&priv->codec_timebase);
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
if (!mpframe)
return -1;
struct mp_chmap lavc_chmap = mpframe->channels;
if (lavc_chmap.num != avctx->channels)
mp_chmap_from_channels(&lavc_chmap, avctx->channels);
if (priv->force_channel_map) {
if (lavc_chmap.num == da->codec->channels.num)
lavc_chmap = da->codec->channels;
}
mp_audio_set_channels(mpframe, &lavc_chmap);
mpframe->pts = out_pts;
if (mpframe->pts == MP_NOPTS_VALUE)
mpframe->pts = priv->next_pts;
if (mpframe->pts != MP_NOPTS_VALUE)
priv->next_pts = mpframe->pts + mpframe->samples / (double)mpframe->rate;
#if HAVE_AVFRAME_SKIP_SAMPLES
AVFrameSideData *sd =
av_frame_get_side_data(priv->avframe, AV_FRAME_DATA_SKIP_SAMPLES);
if (sd && sd->size >= 10) {
char *d = sd->data;
priv->skip_samples += AV_RL32(d + 0);
priv->trim_samples += AV_RL32(d + 4);
}
#endif
if (!priv->preroll_done) {
// Skip only if this isn't already handled by AV_FRAME_DATA_SKIP_SAMPLES.
if (!priv->skip_samples)
priv->skip_samples = avctx->delay;
priv->preroll_done = true;
}
uint32_t skip = MPMIN(priv->skip_samples, mpframe->samples);
if (skip) {
mp_audio_skip_samples(mpframe, skip);
priv->skip_samples -= skip;
}
uint32_t trim = MPMIN(priv->trim_samples, mpframe->samples);
if (trim) {
mpframe->samples -= trim;
priv->trim_samples -= trim;
}
*out = mpframe;
av_frame_unref(priv->avframe);
MP_DBG(da, "Decoded %d -> %d samples\n", in_len, mpframe->samples);
return 0;
}
static void add_decoders(struct mp_decoder_list *list)
{
mp_add_lavc_decoders(list, AVMEDIA_TYPE_AUDIO);
}
const struct ad_functions ad_lavc = {
.name = "lavc",
.add_decoders = add_decoders,
.init = init,
.uninit = uninit,
.control = control,
.decode_packet = decode_packet,
};