Coverage Report

Created: 2025-08-28 07:26

/src/mpv/common/recorder.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * This file is part of mpv.
3
 *
4
 * mpv is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * mpv is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
#include <math.h>
19
20
#include <libavformat/avformat.h>
21
22
#include "common/av_common.h"
23
#include "common/common.h"
24
#include "common/global.h"
25
#include "common/msg.h"
26
#include "demux/demux.h"
27
#include "demux/packet.h"
28
#include "demux/packet_pool.h"
29
#include "demux/stheader.h"
30
31
#include "recorder.h"
32
33
// Maximum number of packets we buffer at most to attempt to resync streams.
34
// Essentially, this should be higher than the highest supported keyframe
35
// interval.
36
0
#define QUEUE_MAX_PACKETS 256
37
// Number of packets we should buffer at least to determine timestamps (due to
38
// codec delay and frame reordering, and potentially lack of DTS).
39
// Keyframe flags can trigger this earlier.
40
0
#define QUEUE_MIN_PACKETS 16
41
42
struct mp_recorder {
43
    struct mpv_global *global;
44
    struct mp_log *log;
45
    struct demux_packet_pool *packet_pool;
46
47
    struct mp_recorder_sink **streams;
48
    int num_streams;
49
50
    bool opened;            // mux context is valid
51
    bool muxing;            // we're currently recording (instead of preparing)
52
    bool muxing_from_start; // no discontinuity at start
53
    bool dts_warning;
54
55
    // The start timestamp of the currently recorded segment (the timestamp of
56
    // the first packet of the incoming packet stream).
57
    double base_ts;
58
    // The output packet timestamp corresponding to base_ts. It's the timestamp
59
    // of the first packet of the current segment written to the output.
60
    double rebase_ts;
61
62
    AVFormatContext *mux;
63
};
64
65
struct mp_recorder_sink {
66
    struct mp_recorder *owner;
67
    struct sh_stream *sh;
68
    AVStream *av_stream;
69
    AVPacket *avpkt;
70
    double max_out_pts;
71
    bool discont;
72
    bool proper_eof;
73
    struct demux_packet **packets;
74
    int num_packets;
75
};
76
77
static int add_stream(struct mp_recorder *priv, struct sh_stream *sh)
78
0
{
79
0
    enum AVMediaType av_type = mp_to_av_stream_type(sh->type);
80
0
    int ret = -1;
81
0
    AVCodecParameters *avp = NULL;
82
0
    if (av_type == AVMEDIA_TYPE_UNKNOWN)
83
0
        goto done;
84
85
0
    struct mp_recorder_sink *rst = talloc(priv, struct mp_recorder_sink);
86
0
    *rst = (struct mp_recorder_sink) {
87
0
        .owner = priv,
88
0
        .sh = sh,
89
0
        .av_stream = avformat_new_stream(priv->mux, NULL),
90
0
        .avpkt = av_packet_alloc(),
91
0
        .max_out_pts = MP_NOPTS_VALUE,
92
0
    };
93
94
0
    if (!rst->av_stream || !rst->avpkt)
95
0
        goto done;
96
97
0
    avp = mp_codec_params_to_av(sh->codec);
98
0
    if (!avp)
99
0
        goto done;
100
101
    // Check if we get the same codec_id for the output format;
102
    // otherwise clear it to have a chance at muxing
103
0
    if (av_codec_get_id(priv->mux->oformat->codec_tag,
104
0
                        avp->codec_tag) != avp->codec_id)
105
0
        avp->codec_tag = 0;
106
107
    // We don't know the delay, so make something up. If the format requires
108
    // DTS, the result will probably be broken. FFmpeg provides nothing better
109
    // yet (unless you demux with libavformat, which contains tons of hacks
110
    // that try to determine a PTS).
111
0
    if (!sh->codec->lav_codecpar)
112
0
        avp->video_delay = 16;
113
114
0
    if (avp->codec_id == AV_CODEC_ID_NONE)
115
0
        goto done;
116
117
0
    if (avcodec_parameters_copy(rst->av_stream->codecpar, avp) < 0)
118
0
        goto done;
119
120
0
    ret = 0;
121
0
    rst->av_stream->time_base = mp_get_codec_timebase(sh->codec);
122
123
0
    MP_TARRAY_APPEND(priv, priv->streams, priv->num_streams, rst);
124
125
0
done:
126
0
    if (avp)
127
0
        avcodec_parameters_free(&avp);
128
0
    return ret;
129
0
}
130
131
struct mp_recorder *mp_recorder_create(struct mpv_global *global,
132
                                       const char *target_file,
133
                                       struct sh_stream **streams,
134
                                       int num_streams,
135
                                       struct demux_attachment **attachments,
136
                                       int num_attachments)
137
0
{
138
0
    struct mp_recorder *priv = talloc_zero(NULL, struct mp_recorder);
139
140
0
    priv->global = global;
141
0
    priv->log = mp_log_new(priv, global->log, "recorder");
142
0
    priv->packet_pool = demux_packet_pool_get(global);
143
144
0
    if (!num_streams) {
145
0
        MP_ERR(priv, "No streams.\n");
146
0
        goto error;
147
0
    }
148
149
0
    priv->mux = avformat_alloc_context();
150
0
    if (!priv->mux)
151
0
        goto error;
152
153
0
    priv->mux->oformat = av_guess_format(NULL, target_file, NULL);
154
0
    if (!priv->mux->oformat) {
155
0
        MP_ERR(priv, "Output format not found.\n");
156
0
        goto error;
157
0
    }
158
159
0
    if (avio_open2(&priv->mux->pb, target_file, AVIO_FLAG_WRITE, NULL, NULL) < 0) {
160
0
        MP_ERR(priv, "Failed opening output file.\n");
161
0
        goto error;
162
0
    }
163
164
0
    for (int n = 0; n < num_streams; n++) {
165
0
        if (add_stream(priv, streams[n]) < 0) {
166
0
            MP_ERR(priv, "Can't mux one of the input streams.\n");
167
0
            goto error;
168
0
        }
169
0
    }
170
171
0
    if (!strcmp(priv->mux->oformat->name, "matroska")) {
172
        // Only attach attachments (fonts) to matroska - mp4, nut, mpegts don't
173
        // like them, and we find that out too late in the muxing process.
174
0
        AVStream *a_stream = NULL;
175
0
        for (int i = 0; i < num_attachments; ++i) {
176
0
            a_stream = avformat_new_stream(priv->mux, NULL);
177
0
            if (!a_stream) {
178
0
                MP_ERR(priv, "Can't mux one of the attachments.\n");
179
0
                goto error;
180
0
            }
181
0
            struct demux_attachment *attachment = attachments[i];
182
183
0
            a_stream->codecpar->codec_type = AVMEDIA_TYPE_ATTACHMENT;
184
185
0
            a_stream->codecpar->extradata  = av_mallocz(
186
0
                attachment->data_size + AV_INPUT_BUFFER_PADDING_SIZE
187
0
            );
188
0
            if (!a_stream->codecpar->extradata) {
189
0
                goto error;
190
0
            }
191
0
            memcpy(a_stream->codecpar->extradata,
192
0
                attachment->data, attachment->data_size);
193
0
            a_stream->codecpar->extradata_size = attachment->data_size;
194
195
0
            av_dict_set(&a_stream->metadata, "filename", attachment->name, 0);
196
0
            av_dict_set(&a_stream->metadata, "mimetype", attachment->type, 0);
197
0
        }
198
0
    }
199
200
    // Not sure how to write this in a "standard" way. It appears only mkv
201
    // and mp4 support this directly.
202
0
    char version[200];
203
0
    snprintf(version, sizeof(version), "%s experimental stream recording "
204
0
             "feature (can generate broken files - please report bugs)",
205
0
             mpv_version);
206
0
    av_dict_set(&priv->mux->metadata, "encoding_tool", version, 0);
207
208
0
    if (avformat_write_header(priv->mux, NULL) < 0) {
209
0
        MP_ERR(priv, "Writing header failed.\n");
210
0
        goto error;
211
0
    }
212
213
0
    priv->opened = true;
214
0
    priv->muxing_from_start = true;
215
216
0
    priv->base_ts = MP_NOPTS_VALUE;
217
0
    priv->rebase_ts = 0;
218
219
0
    MP_WARN(priv, "This is an experimental feature. Output files might be "
220
0
                  "broken or not play correctly with various players "
221
0
                  "(including mpv itself).\n");
222
223
0
    return priv;
224
225
0
error:
226
0
    mp_recorder_destroy(priv);
227
0
    return NULL;
228
0
}
229
230
static void flush_packets(struct mp_recorder *priv)
231
0
{
232
0
    for (int n = 0; n < priv->num_streams; n++) {
233
0
        struct mp_recorder_sink *rst = priv->streams[n];
234
0
        for (int i = 0; i < rst->num_packets; i++)
235
0
            talloc_free(rst->packets[i]);
236
0
        rst->num_packets = 0;
237
0
    }
238
0
}
239
240
static void mux_packet(struct mp_recorder_sink *rst,
241
                       struct demux_packet *pkt)
242
0
{
243
0
    struct mp_recorder *priv = rst->owner;
244
0
    struct demux_packet mpkt = *pkt;
245
246
0
    double diff = priv->rebase_ts - priv->base_ts;
247
0
    mpkt.pts = MP_ADD_PTS(mpkt.pts, diff);
248
0
    mpkt.dts = MP_ADD_PTS(mpkt.dts, diff);
249
250
0
    rst->max_out_pts = MP_PTS_MAX(rst->max_out_pts, pkt->pts);
251
252
0
    mp_set_av_packet(rst->avpkt, &mpkt, &rst->av_stream->time_base);
253
254
0
    rst->avpkt->stream_index = rst->av_stream->index;
255
256
0
    if (rst->avpkt->duration < 0 && rst->sh->type != STREAM_SUB)
257
0
        rst->avpkt->duration = 0;
258
259
0
    AVPacket *new_packet = av_packet_clone(rst->avpkt);
260
0
    if (!new_packet) {
261
0
        MP_ERR(priv, "Failed to allocate packet.\n");
262
0
        return;
263
0
    }
264
265
0
    if (av_interleaved_write_frame(priv->mux, new_packet) < 0)
266
0
        MP_ERR(priv, "Failed writing packet.\n");
267
268
0
    av_packet_free(&new_packet);
269
0
}
270
271
// Write all packets available in the stream queue
272
static void mux_packets(struct mp_recorder_sink *rst)
273
0
{
274
0
    struct mp_recorder *priv = rst->owner;
275
0
    if (!priv->muxing || !rst->num_packets)
276
0
        return;
277
278
0
    for (int n = 0; n < rst->num_packets; n++) {
279
0
        mux_packet(rst, rst->packets[n]);
280
0
        talloc_free(rst->packets[n]);
281
0
    }
282
283
0
    rst->num_packets = 0;
284
0
}
285
286
// If there was a discontinuity, check whether we can resume muxing (and from
287
// where).
288
static void check_restart(struct mp_recorder *priv)
289
0
{
290
0
    if (priv->muxing)
291
0
        return;
292
293
0
    double min_ts = MP_NOPTS_VALUE;
294
0
    double rebase_ts = 0;
295
0
    for (int n = 0; n < priv->num_streams; n++) {
296
0
        struct mp_recorder_sink *rst = priv->streams[n];
297
0
        int min_packets = rst->sh->type == STREAM_VIDEO ? QUEUE_MIN_PACKETS : 1;
298
299
0
        rebase_ts = MP_PTS_MAX(rebase_ts, rst->max_out_pts);
300
301
0
        if (rst->num_packets < min_packets) {
302
0
            if (!rst->proper_eof && rst->sh->type != STREAM_SUB)
303
0
                return;
304
0
            continue;
305
0
        }
306
307
0
        for (int i = 0; i < min_packets; i++)
308
0
            min_ts = MP_PTS_MIN(min_ts, rst->packets[i]->pts);
309
0
    }
310
311
    // Subtitle only stream (wait longer) or stream without any PTS (fuck it).
312
0
    if (min_ts == MP_NOPTS_VALUE)
313
0
        return;
314
315
0
    priv->rebase_ts = rebase_ts;
316
0
    priv->base_ts = min_ts;
317
318
0
    for (int n = 0; n < priv->num_streams; n++) {
319
0
        struct mp_recorder_sink *rst = priv->streams[n];
320
0
        rst->max_out_pts = min_ts;
321
0
    }
322
323
0
    priv->muxing = true;
324
325
0
    if (!priv->muxing_from_start)
326
0
        MP_WARN(priv, "Discontinuity at timestamp %f.\n", priv->rebase_ts);
327
0
}
328
329
void mp_recorder_destroy(struct mp_recorder *priv)
330
0
{
331
0
    if (priv->opened) {
332
0
        for (int n = 0; n < priv->num_streams; n++) {
333
0
            struct mp_recorder_sink *rst = priv->streams[n];
334
0
            mux_packets(rst);
335
0
            mp_free_av_packet(&rst->avpkt);
336
0
        }
337
338
0
        if (av_write_trailer(priv->mux) < 0)
339
0
            MP_ERR(priv, "Writing trailer failed.\n");
340
0
    }
341
342
0
    if (priv->mux) {
343
0
        if (avio_closep(&priv->mux->pb) < 0)
344
0
            MP_ERR(priv, "Closing file failed\n");
345
346
0
        avformat_free_context(priv->mux);
347
0
    }
348
349
0
    flush_packets(priv);
350
0
    talloc_free(priv);
351
0
}
352
353
// This is called on a seek, or when recording was started mid-stream.
354
void mp_recorder_mark_discontinuity(struct mp_recorder *priv)
355
0
{
356
357
0
    for (int n = 0; n < priv->num_streams; n++) {
358
0
        struct mp_recorder_sink *rst = priv->streams[n];
359
0
        mux_packets(rst);
360
0
        rst->discont = true;
361
0
        rst->proper_eof = false;
362
0
    }
363
364
0
    flush_packets(priv);
365
0
    priv->muxing = false;
366
0
    priv->muxing_from_start = false;
367
0
}
368
369
// Get a stream for writing. The pointer is valid until mp_recorder is
370
// destroyed. The stream ptr. is the same as one passed to
371
// mp_recorder_create() (returns NULL if it wasn't).
372
struct mp_recorder_sink *mp_recorder_get_sink(struct mp_recorder *r,
373
                                              struct sh_stream *stream)
374
0
{
375
0
    for (int n = 0; n < r->num_streams; n++) {
376
0
        struct mp_recorder_sink *rst = r->streams[n];
377
0
        if (rst->sh == stream)
378
0
            return rst;
379
0
    }
380
0
    return NULL;
381
0
}
382
383
// Pass a packet to the given stream. The function does not own the packet, but
384
// can create a new reference to it if it needs to retain it. Can be NULL to
385
// signal proper end of stream.
386
void mp_recorder_feed_packet(struct mp_recorder_sink *rst,
387
                             struct demux_packet *pkt)
388
0
{
389
0
    struct mp_recorder *priv = rst->owner;
390
391
0
    if (!pkt) {
392
0
        rst->proper_eof = true;
393
0
        check_restart(priv);
394
0
        mux_packets(rst);
395
0
        return;
396
0
    }
397
398
0
    if (pkt->dts == MP_NOPTS_VALUE && !priv->dts_warning) {
399
        // No, FFmpeg has no actually usable helpers to generate correct DTS.
400
        // No, FFmpeg doesn't tell us which formats need DTS at all.
401
        // No, we can not shut up the FFmpeg warning, which will follow.
402
0
        MP_WARN(priv, "Source stream misses DTS on at least some packets!\n"
403
0
                      "If the target file format requires DTS, the written "
404
0
                      "file will be invalid.\n");
405
0
        priv->dts_warning = true;
406
0
    }
407
408
0
    if (rst->discont && !pkt->keyframe)
409
0
        return;
410
0
    rst->discont = false;
411
412
0
    if (rst->num_packets >= QUEUE_MAX_PACKETS) {
413
0
        MP_ERR(priv, "Stream %d has too many queued packets; dropping.\n",
414
0
               rst->av_stream->index);
415
0
        return;
416
0
    }
417
418
0
    pkt = demux_copy_packet(rst->owner->packet_pool, pkt);
419
0
    if (!pkt)
420
0
        return;
421
0
    MP_TARRAY_APPEND(rst, rst->packets, rst->num_packets, pkt);
422
423
0
    check_restart(priv);
424
0
    mux_packets(rst);
425
0
}