Coverage Report

Created: 2025-12-10 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/audio/out/ao_lavc.c
Line
Count
Source
1
/*
2
 * audio encoding using libavformat
3
 *
4
 * Copyright (C) 2011-2012 Rudolf Polzer <divVerent@xonotic.org>
5
 * NOTE: this file is partially based on ao_pcm.c by Atmosfear
6
 *
7
 * This file is part of mpv.
8
 *
9
 * mpv is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU Lesser General Public
11
 * License as published by the Free Software Foundation; either
12
 * version 2.1 of the License, or (at your option) any later version.
13
 *
14
 * mpv is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public
20
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <assert.h>
26
#include <limits.h>
27
28
#include <libavutil/common.h>
29
#include <libavutil/samplefmt.h>
30
31
#include "config.h"
32
#include "options/options.h"
33
#include "common/common.h"
34
#include "audio/aframe.h"
35
#include "audio/chmap_avchannel.h"
36
#include "audio/format.h"
37
#include "audio/fmt-conversion.h"
38
#include "filters/filter_internal.h"
39
#include "filters/f_utils.h"
40
#include "misc/lavc_compat.h"
41
#include "mpv_talloc.h"
42
#include "ao.h"
43
#include "internal.h"
44
#include "common/msg.h"
45
46
#include "common/encode_lavc.h"
47
48
struct priv {
49
    struct encoder_context *enc;
50
51
    int pcmhack;
52
    int aframesize;
53
    int framecount;
54
    int64_t lastpts;
55
    int sample_size;
56
    double expected_next_pts;
57
    struct mp_filter *filter_root;
58
    struct mp_filter *fix_frame_size;
59
60
    AVRational worst_time_base;
61
62
    bool shutdown;
63
};
64
65
static bool write_frame(struct ao *ao, struct mp_frame frame);
66
67
static bool supports_format(const AVCodec *codec, int format)
68
0
{
69
0
    const enum AVSampleFormat *sampleformat;
70
0
    int ret = mp_avcodec_get_supported_config(NULL, codec,
71
0
                                              AV_CODEC_CONFIG_SAMPLE_FORMAT,
72
0
                                              (const void **)&sampleformat);
73
0
    if (ret >= 0 && !sampleformat)
74
0
        return true;
75
0
    for (; ret >= 0 && *sampleformat != AV_SAMPLE_FMT_NONE; sampleformat++)
76
0
    {
77
0
        if (af_from_avformat(*sampleformat) == format)
78
0
            return true;
79
0
    }
80
0
    return false;
81
0
}
82
83
static void select_format(struct ao *ao, const AVCodec *codec)
84
0
{
85
0
    int formats[AF_FORMAT_COUNT + 1];
86
0
    af_get_best_sample_formats(ao->format, formats);
87
88
0
    for (int n = 0; formats[n]; n++) {
89
0
        if (supports_format(codec, formats[n])) {
90
0
            ao->format = formats[n];
91
0
            break;
92
0
        }
93
0
    }
94
0
}
95
96
// open & setup audio device
97
static int init(struct ao *ao)
98
0
{
99
0
    struct priv *ac = ao->priv;
100
101
0
    ac->enc = encoder_context_alloc(ao->encode_lavc_ctx, STREAM_AUDIO, ao->log);
102
0
    if (!ac->enc)
103
0
        return -1;
104
0
    talloc_steal(ac, ac->enc);
105
106
0
    AVCodecContext *encoder = ac->enc->encoder;
107
0
    const AVCodec *codec = encoder->codec;
108
109
0
    const int *samplerates;
110
0
    int ret = mp_avcodec_get_supported_config(NULL, codec,
111
0
                                              AV_CODEC_CONFIG_SAMPLE_RATE,
112
0
                                              (const void **)&samplerates);
113
114
0
    int samplerate = 0;
115
0
    if (ret >= 0)
116
0
        samplerate = af_select_best_samplerate(ao->samplerate, samplerates);
117
0
    if (samplerate > 0)
118
0
        ao->samplerate = samplerate;
119
120
0
    encoder->time_base.num = 1;
121
0
    encoder->time_base.den = ao->samplerate;
122
123
0
    encoder->sample_rate = ao->samplerate;
124
125
0
    struct mp_chmap_sel sel = {0};
126
0
    mp_chmap_sel_add_any(&sel);
127
0
    if (!ao_chmap_sel_adjust2(ao, &sel, &ao->channels, false))
128
0
        goto fail;
129
0
    mp_chmap_reorder_to_lavc(&ao->channels);
130
0
    mp_chmap_to_av_layout(&encoder->ch_layout, &ao->channels);
131
132
0
    encoder->sample_fmt = AV_SAMPLE_FMT_NONE;
133
134
0
    select_format(ao, codec);
135
136
0
    ac->sample_size = af_fmt_to_bytes(ao->format);
137
0
    encoder->sample_fmt = af_to_avformat(ao->format);
138
0
    encoder->bits_per_raw_sample = ac->sample_size * 8;
139
140
0
    if (!encoder_init_codec_and_muxer(ac->enc))
141
0
        goto fail;
142
143
0
    ac->worst_time_base = encoder_get_mux_timebase_unlocked(ac->enc);
144
0
    ac->pcmhack = 0;
145
0
    if (encoder->frame_size <= 1)
146
0
        ac->pcmhack = av_get_bits_per_sample(encoder->codec_id) / 8;
147
148
0
    if (ac->pcmhack) {
149
0
        ac->aframesize = 16384; // "enough"
150
0
    } else {
151
0
        ac->aframesize = encoder->frame_size;
152
0
    }
153
154
    // enough frames for at least 0.25 seconds
155
0
    ac->framecount = ceil(ao->samplerate * 0.25 / ac->aframesize);
156
    // but at least one!
157
0
    ac->framecount = MPMAX(ac->framecount, 1);
158
159
0
    ac->lastpts = AV_NOPTS_VALUE;
160
161
0
    ao->untimed = true;
162
163
0
    ao->device_buffer = ac->aframesize * ac->framecount;
164
165
0
    ac->filter_root = mp_filter_create_root(ao->global);
166
0
    ac->fix_frame_size = mp_fixed_aframe_size_create(ac->filter_root,
167
0
                                                     ac->aframesize, true);
168
0
    MP_HANDLE_OOM(ac->fix_frame_size);
169
170
0
    return 0;
171
172
0
fail:
173
0
    mp_mutex_unlock(&ao->encode_lavc_ctx->lock);
174
0
    ac->shutdown = true;
175
0
    return -1;
176
0
}
177
178
// close audio device
179
static void uninit(struct ao *ao)
180
0
{
181
0
    struct priv *ac = ao->priv;
182
183
0
    if (!ac->shutdown) {
184
0
        if (!write_frame(ao, MP_EOF_FRAME))
185
0
            MP_WARN(ao, "could not flush last frame\n");
186
0
        encoder_encode(ac->enc, NULL);
187
0
    }
188
189
0
    talloc_free(ac->filter_root);
190
0
}
191
192
// must get exactly ac->aframesize amount of data
193
static void encode(struct ao *ao, struct mp_aframe *af)
194
0
{
195
0
    struct priv *ac = ao->priv;
196
0
    AVCodecContext *encoder = ac->enc->encoder;
197
0
    double outpts = mp_aframe_get_pts(af);
198
199
0
    AVFrame *frame = mp_aframe_to_avframe(af);
200
0
    MP_HANDLE_OOM(frame);
201
202
0
    frame->pts = rint(outpts * av_q2d(av_inv_q(encoder->time_base)));
203
204
0
    int64_t frame_pts = av_rescale_q(frame->pts, encoder->time_base,
205
0
                                     ac->worst_time_base);
206
0
    if (ac->lastpts != AV_NOPTS_VALUE && frame_pts <= ac->lastpts) {
207
        // whatever the fuck this code does?
208
0
        MP_WARN(ao, "audio frame pts went backwards (%d <- %d), autofixed\n",
209
0
                (int)frame->pts, (int)ac->lastpts);
210
0
        frame_pts = ac->lastpts + 1;
211
0
        ac->lastpts = frame_pts;
212
0
        frame->pts = av_rescale_q(frame_pts, ac->worst_time_base,
213
0
                                  encoder->time_base);
214
0
        frame_pts = av_rescale_q(frame->pts, encoder->time_base,
215
0
                                 ac->worst_time_base);
216
0
    }
217
0
    ac->lastpts = frame_pts;
218
219
0
    frame->quality = encoder->global_quality;
220
0
    encoder_encode(ac->enc, frame);
221
0
    av_frame_free(&frame);
222
0
}
223
224
static bool write_frame(struct ao *ao, struct mp_frame frame)
225
0
{
226
0
    struct priv *ac = ao->priv;
227
228
    // Can't push in frame if it doesn't want it output one.
229
0
    mp_pin_out_request_data(ac->fix_frame_size->pins[1]);
230
231
0
    if (!mp_pin_in_write(ac->fix_frame_size->pins[0], frame))
232
0
        return false; // shouldn't happen™
233
234
0
    while (1) {
235
0
        struct mp_frame fr = mp_pin_out_read(ac->fix_frame_size->pins[1]);
236
0
        if (!fr.type)
237
0
            break;
238
0
        if (fr.type != MP_FRAME_AUDIO)
239
0
            continue;
240
0
        struct mp_aframe *af = fr.data;
241
0
        encode(ao, af);
242
0
        mp_frame_unref(&fr);
243
0
    }
244
245
0
    return true;
246
0
}
247
248
static bool audio_write(struct ao *ao, void **data, int samples)
249
0
{
250
0
    struct priv *ac = ao->priv;
251
0
    struct encode_lavc_context *ectx = ao->encode_lavc_ctx;
252
253
    // See ao_driver.write_frames.
254
0
    struct mp_aframe *af = mp_aframe_new_ref(*(struct mp_aframe **)data);
255
256
0
    double nextpts;
257
0
    double pts = mp_aframe_get_pts(af);
258
0
    double outpts = pts;
259
260
    // for ectx PTS fields
261
0
    mp_mutex_lock(&ectx->lock);
262
263
0
    if (!ectx->options->rawts) {
264
        // Fix and apply the discontinuity pts offset.
265
0
        nextpts = pts;
266
0
        if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
267
0
            ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
268
0
        } else if (fabs(nextpts + ectx->discontinuity_pts_offset -
269
0
                        ectx->next_in_pts) > 30)
270
0
        {
271
0
            MP_WARN(ao, "detected an unexpected discontinuity (pts jumped by "
272
0
                    "%f seconds)\n",
273
0
                    nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts);
274
0
            ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
275
0
        }
276
277
0
        outpts = pts + ectx->discontinuity_pts_offset;
278
0
    }
279
280
    // Calculate expected pts of next audio frame (input side).
281
0
    ac->expected_next_pts = pts + mp_aframe_get_size(af) / (double) ao->samplerate;
282
283
    // Set next allowed input pts value (input side).
284
0
    if (!ectx->options->rawts) {
285
0
        nextpts = ac->expected_next_pts + ectx->discontinuity_pts_offset;
286
0
        if (nextpts > ectx->next_in_pts)
287
0
            ectx->next_in_pts = nextpts;
288
0
    }
289
290
0
    mp_mutex_unlock(&ectx->lock);
291
292
0
    mp_aframe_set_pts(af, outpts);
293
294
0
    return write_frame(ao, MAKE_FRAME(MP_FRAME_AUDIO, af));
295
0
}
296
297
static void get_state(struct ao *ao, struct mp_pcm_state *state)
298
0
{
299
0
    state->free_samples = 1;
300
0
    state->queued_samples = 0;
301
0
    state->delay = 0;
302
0
}
303
304
static bool set_pause(struct ao *ao, bool paused)
305
0
{
306
0
    return true; // signal support so common code doesn't write silence
307
0
}
308
309
static void start(struct ao *ao)
310
0
{
311
    // we use data immediately
312
0
}
313
314
static void reset(struct ao *ao)
315
0
{
316
0
}
317
318
const struct ao_driver audio_out_lavc = {
319
    .encode = true,
320
    .description = "audio encoding using libavcodec",
321
    .name      = "lavc",
322
    .write_frames = true,
323
    .priv_size = sizeof(struct priv),
324
    .init      = init,
325
    .uninit    = uninit,
326
    .get_state = get_state,
327
    .set_pause = set_pause,
328
    .write     = audio_write,
329
    .start     = start,
330
    .reset     = reset,
331
};
332
333
// vim: sw=4 ts=4 et tw=80