Coverage Report

Created: 2026-08-14 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/video/out/vo_lavc.c
Line
Count
Source
1
/*
2
 * video encoding using libavformat
3
 *
4
 * Copyright (C) 2010 Nicolas George <george@nsup.org>
5
 * Copyright (C) 2011-2012 Rudolf Polzer <divVerent@xonotic.org>
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
26
#include <libplacebo/utils/libav.h>
27
28
#include "common/common.h"
29
#include "options/options.h"
30
#include "misc/lavc_compat.h"
31
#include "video/fmt-conversion.h"
32
#include "video/mp_image.h"
33
#include "mpv_talloc.h"
34
#include "vo.h"
35
36
#include "common/encode_lavc.h"
37
38
#include "sub/osd.h"
39
40
struct priv {
41
    struct encoder_context *enc;
42
43
    bool shutdown;
44
};
45
46
static int preinit(struct vo *vo)
47
0
{
48
0
    struct priv *vc = vo->priv;
49
0
    vc->enc = encoder_context_alloc(vo->encode_lavc_ctx, STREAM_VIDEO, vo->log);
50
0
    if (!vc->enc)
51
0
        return -1;
52
0
    talloc_steal(vc, vc->enc);
53
0
    return 0;
54
0
}
55
56
static void uninit(struct vo *vo)
57
0
{
58
0
    struct priv *vc = vo->priv;
59
0
    struct encoder_context *enc = vc->enc;
60
61
0
    if (!vc->shutdown)
62
0
        encoder_encode(enc, NULL); // finish encoding
63
0
}
64
65
static int reconfig2(struct vo *vo, struct mp_image *img)
66
0
{
67
0
    struct priv *vc = vo->priv;
68
0
    AVCodecContext *encoder = vc->enc->encoder;
69
70
0
    struct mp_image_params *params = &img->params;
71
0
    enum AVPixelFormat pix_fmt = imgfmt2pixfmt(params->imgfmt);
72
0
    AVRational aspect = {params->p_w, params->p_h};
73
0
    int width = params->w;
74
0
    int height = params->h;
75
76
0
    if (vc->shutdown)
77
0
        return -1;
78
79
0
    if (avcodec_is_open(encoder)) {
80
0
        if (width == encoder->width && height == encoder->height &&
81
0
            pix_fmt == encoder->pix_fmt)
82
0
        {
83
            // consider these changes not critical
84
0
            MP_ERR(vo, "Ignoring mid-stream parameter changes!\n");
85
0
            return 0;
86
0
        }
87
88
        /* FIXME Is it possible with raw video? */
89
0
        MP_ERR(vo, "resolution changes not supported.\n");
90
0
        goto error;
91
0
    }
92
93
    // When we get here, this must be the first call to reconfigure(). Thus, we
94
    // can rely on no existing data in vc having been allocated yet.
95
    // Reason:
96
    // - Second calls after reconfigure() already failed once fail (due to the
97
    //   vc->shutdown check above).
98
    // - Second calls after reconfigure() already succeeded once return early
99
    //   (due to the avcodec_is_open() check above).
100
101
0
    if (pix_fmt == AV_PIX_FMT_NONE) {
102
0
        MP_FATAL(vo, "Format %s not supported by lavc.\n",
103
0
                 mp_imgfmt_to_name(params->imgfmt));
104
0
        goto error;
105
0
    }
106
107
0
    encoder->sample_aspect_ratio = aspect;
108
0
    encoder->width = width;
109
0
    encoder->height = height;
110
0
    encoder->pix_fmt = pix_fmt;
111
0
    encoder->colorspace = pl_system_to_av(params->repr.sys);
112
0
    encoder->color_range = pl_levels_to_av(params->repr.levels);
113
114
0
    AVRational tb;
115
116
    // we want to handle:
117
    //      1/25
118
    //   1001/24000
119
    //   1001/30000
120
    // for this we would need 120000fps...
121
    // however, mpeg-4 only allows 16bit values
122
    // so let's take 1001/30000 out
123
0
    tb.num = 24000;
124
0
    tb.den = 1;
125
126
0
    const AVRational *rates;
127
0
    int ret = mp_avcodec_get_supported_config(encoder, NULL,
128
0
                                              AV_CODEC_CONFIG_FRAME_RATE,
129
0
                                              (const void **)&rates);
130
0
    if (ret >= 0 && rates && rates[0].den)
131
0
        tb = rates[av_find_nearest_q_idx(tb, rates)];
132
133
0
    encoder->time_base = av_inv_q(tb);
134
135
    // Used for rate control, level selection, etc.
136
    // Usually it's not too catastrophic if this isn't exactly correct,
137
    // as long as it's not off by orders of magnitude.
138
    // If we don't set anything, encoders will use the time base,
139
    // and 24000 is so high that the output can end up extremely screwy (see #11215),
140
    // so we default to 240 if we don't have a real value.
141
0
    if (img->nominal_fps > 0)
142
0
        encoder->framerate = av_d2q(img->nominal_fps, img->nominal_fps * 1001 + 2); // Hopefully give exact results for NTSC rates
143
0
    else
144
0
        encoder->framerate = (AVRational){ 240, 1 };
145
146
0
    if (!encoder_init_codec_and_muxer(vc->enc))
147
0
        goto error;
148
149
0
    return 0;
150
151
0
error:
152
0
    vc->shutdown = true;
153
0
    return -1;
154
0
}
155
156
static int query_format(struct vo *vo, int format)
157
0
{
158
0
    struct priv *vc = vo->priv;
159
160
0
    enum AVPixelFormat pix_fmt = imgfmt2pixfmt(format);
161
0
    const enum AVPixelFormat *p;
162
0
    int ret = mp_avcodec_get_supported_config(vc->enc->encoder, NULL,
163
0
                                              AV_CODEC_CONFIG_PIX_FORMAT,
164
0
                                              (const void **)&p);
165
166
0
    if (ret >= 0 && !p)
167
0
        return 1;
168
169
0
    while (ret >= 0 && p && *p != AV_PIX_FMT_NONE) {
170
0
        if (*p == pix_fmt)
171
0
            return 1;
172
0
        p++;
173
0
    }
174
175
0
    return 0;
176
0
}
177
178
static bool draw_frame(struct vo *vo, struct vo_frame *voframe)
179
0
{
180
0
    struct priv *vc = vo->priv;
181
0
    struct encoder_context *enc = vc->enc;
182
0
    struct encode_lavc_context *ectx = enc->encode_lavc_ctx;
183
0
    AVCodecContext *avc = enc->encoder;
184
185
0
    if (voframe->redraw || voframe->repeat || voframe->num_frames < 1)
186
0
        goto done;
187
188
0
    struct mp_image *mpi = voframe->frames[0];
189
190
0
    struct mp_osd_res dim = osd_res_from_image_params(vo->params);
191
0
    osd_draw_on_image(vo->osd, dim, mpi->pts, OSD_DRAW_SUB_ONLY, mpi);
192
193
0
    if (vc->shutdown)
194
0
        goto done;
195
196
    // Lock for shared timestamp fields.
197
0
    mp_mutex_lock(&ectx->lock);
198
199
0
    double pts = mpi->pts;
200
0
    double outpts = pts;
201
0
    if (!enc->options->rawts) {
202
        // fix the discontinuity pts offset
203
0
        if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
204
0
            ectx->discontinuity_pts_offset = ectx->next_in_pts - pts;
205
0
        } else if (fabs(pts + ectx->discontinuity_pts_offset -
206
0
                        ectx->next_in_pts) > 30)
207
0
        {
208
0
            MP_WARN(vo, "detected an unexpected discontinuity (pts jumped by "
209
0
                    "%f seconds)\n",
210
0
                    pts + ectx->discontinuity_pts_offset - ectx->next_in_pts);
211
0
            ectx->discontinuity_pts_offset = ectx->next_in_pts - pts;
212
0
        }
213
214
0
        outpts = pts + ectx->discontinuity_pts_offset;
215
0
    }
216
217
0
    if (!enc->options->rawts) {
218
        // calculate expected pts of next video frame
219
0
        double timeunit = av_q2d(avc->time_base);
220
0
        double expected_next_pts = pts + timeunit;
221
        // set next allowed output pts value
222
0
        double nextpts = expected_next_pts + ectx->discontinuity_pts_offset;
223
0
        if (nextpts > ectx->next_in_pts)
224
0
            ectx->next_in_pts = nextpts;
225
0
    }
226
227
0
    mp_mutex_unlock(&ectx->lock);
228
229
0
    AVFrame *frame = mp_image_to_av_frame(mpi);
230
0
    MP_HANDLE_OOM(frame);
231
232
0
    frame->pts = rint(outpts * av_q2d(av_inv_q(avc->time_base)));
233
0
    frame->pict_type = 0; // keep this at unknown/undefined
234
0
    frame->quality = avc->global_quality;
235
0
    encoder_encode(enc, frame);
236
0
    av_frame_free(&frame);
237
238
0
done:
239
0
    return VO_TRUE;
240
0
}
241
242
static void flip_page(struct vo *vo)
243
0
{
244
0
}
245
246
static int control(struct vo *vo, uint32_t request, void *data)
247
0
{
248
0
    return VO_NOTIMPL;
249
0
}
250
251
const struct vo_driver video_out_lavc = {
252
    .encode = true,
253
    .description = "video encoding using libavcodec",
254
    .name = "lavc",
255
    .caps = VO_CAP_UNTIMED,
256
    .priv_size = sizeof(struct priv),
257
    .preinit = preinit,
258
    .query_format = query_format,
259
    .reconfig2 = reconfig2,
260
    .control = control,
261
    .uninit = uninit,
262
    .draw_frame = draw_frame,
263
    .flip_page = flip_page,
264
};
265
266
// vim: sw=4 ts=4 et tw=80