Coverage Report

Created: 2026-01-26 07:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/demux/demux_raw.c
Line
Count
Source
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 <stdlib.h>
19
#include <stdio.h>
20
#include <string.h>
21
22
#include <libavcodec/avcodec.h>
23
#include <libavutil/common.h>
24
25
#include "common/av_common.h"
26
27
#include "options/m_config.h"
28
#include "options/m_option.h"
29
30
#include "stream/stream.h"
31
#include "demux.h"
32
#include "stheader.h"
33
#include "codec_tags.h"
34
35
#include "video/fmt-conversion.h"
36
#include "video/img_format.h"
37
38
#include "osdep/endian.h"
39
40
struct demux_rawaudio_opts {
41
    struct m_channels channels;
42
    int samplerate;
43
    int aformat;
44
};
45
46
// Ad-hoc schema to systematically encode the format as int
47
#define PCM(sign, is_float, bits, is_be) \
48
    ((sign) | ((is_float) << 1) | ((is_be) << 2) | ((bits) << 3))
49
#define NE (BYTE_ORDER == BIG_ENDIAN)
50
51
#define OPT_BASE_STRUCT struct demux_rawaudio_opts
52
const struct m_sub_options demux_rawaudio_conf = {
53
    .opts = (const m_option_t[]) {
54
        {"channels", OPT_CHANNELS(channels), .flags = M_OPT_CHANNELS_LIMITED},
55
        {"rate", OPT_INT(samplerate), M_RANGE(1000, 8 * 48000)},
56
        {"format", OPT_CHOICE(aformat,
57
            {"u8",      PCM(0, 0,  8, 0)},
58
            {"s8",      PCM(1, 0,  8, 0)},
59
            {"u16le",   PCM(0, 0, 16, 0)},  {"u16be",    PCM(0, 0, 16, 1)},
60
            {"s16le",   PCM(1, 0, 16, 0)},  {"s16be",    PCM(1, 0, 16, 1)},
61
            {"u24le",   PCM(0, 0, 24, 0)},  {"u24be",    PCM(0, 0, 24, 1)},
62
            {"s24le",   PCM(1, 0, 24, 0)},  {"s24be",    PCM(1, 0, 24, 1)},
63
            {"u32le",   PCM(0, 0, 32, 0)},  {"u32be",    PCM(0, 0, 32, 1)},
64
            {"s32le",   PCM(1, 0, 32, 0)},  {"s32be",    PCM(1, 0, 32, 1)},
65
            {"floatle", PCM(0, 1, 32, 0)},  {"floatbe",  PCM(0, 1, 32, 1)},
66
            {"doublele",PCM(0, 1, 64, 0)},  {"doublebe", PCM(0, 1, 64, 1)},
67
            {"u16",     PCM(0, 0, 16, NE)},
68
            {"s16",     PCM(1, 0, 16, NE)},
69
            {"u24",     PCM(0, 0, 24, NE)},
70
            {"s24",     PCM(1, 0, 24, NE)},
71
            {"u32",     PCM(0, 0, 32, NE)},
72
            {"s32",     PCM(1, 0, 32, NE)},
73
            {"float",   PCM(0, 1, 32, NE)},
74
            {"double",  PCM(0, 1, 64, NE)})},
75
        {0}
76
    },
77
    .size = sizeof(struct demux_rawaudio_opts),
78
    .defaults = &(const struct demux_rawaudio_opts){
79
        // Note that currently, stream_cdda expects exactly these parameters!
80
        .channels = {
81
            .set = 1,
82
            .chmaps = (struct mp_chmap[]){ MP_CHMAP_INIT_STEREO, },
83
            .num_chmaps = 1,
84
        },
85
        .samplerate = 44100,
86
        .aformat = PCM(1, 0, 16, 0), // s16le
87
    },
88
    .change_flags = UPDATE_DEMUXER,
89
};
90
91
#undef PCM
92
#undef NE
93
94
struct demux_rawvideo_opts {
95
    int vformat;
96
    int mp_format;
97
    char *codec;
98
    int width;
99
    int height;
100
    float fps;
101
    int imgsize;
102
};
103
104
#undef OPT_BASE_STRUCT
105
#define OPT_BASE_STRUCT struct demux_rawvideo_opts
106
const struct m_sub_options demux_rawvideo_conf = {
107
    .opts = (const m_option_t[]) {
108
        {"w", OPT_INT(width), M_RANGE(1, 8192)},
109
        {"h", OPT_INT(height), M_RANGE(1, 8192)},
110
        {"format", OPT_FOURCC(vformat)},
111
        {"mp-format", OPT_IMAGEFORMAT(mp_format)},
112
        {"codec", OPT_STRING(codec)},
113
        {"fps", OPT_FLOAT(fps), M_RANGE(0.001, 1000)},
114
        {"size", OPT_INT(imgsize), M_RANGE(1, 8192 * 8192 * 4)},
115
        {0}
116
    },
117
    .size = sizeof(struct demux_rawvideo_opts),
118
    .defaults = &(const struct demux_rawvideo_opts){
119
        .vformat = MKTAG('I', '4', '2', '0'),
120
        .width = 1280,
121
        .height = 720,
122
        .fps = 25,
123
    },
124
};
125
126
struct priv {
127
    struct sh_stream *sh;
128
    int frame_size;
129
    int read_frames;
130
    double frame_rate;
131
};
132
133
static int generic_open(struct demuxer *demuxer)
134
0
{
135
0
    struct stream *s = demuxer->stream;
136
0
    struct priv *p = demuxer->priv;
137
138
0
    int64_t end = stream_get_size(s);
139
0
    if (end >= 0)
140
0
        demuxer->duration = (end / p->frame_size) / p->frame_rate;
141
142
0
    return 0;
143
0
}
144
145
static int demux_rawaudio_open(demuxer_t *demuxer, enum demux_check check)
146
108k
{
147
108k
    struct demux_rawaudio_opts *opts =
148
108k
        mp_get_config_group(demuxer, demuxer->global, &demux_rawaudio_conf);
149
150
108k
    if (check != DEMUX_CHECK_REQUEST && check != DEMUX_CHECK_FORCE)
151
108k
        return -1;
152
153
0
    if (opts->channels.num_chmaps != 1) {
154
0
        MP_ERR(demuxer, "Invalid channels option given.\n");
155
0
        return -1;
156
0
    }
157
158
0
    struct sh_stream *sh = demux_alloc_sh_stream(STREAM_AUDIO);
159
0
    struct mp_codec_params *c = sh->codec;
160
0
    c->channels = opts->channels.chmaps[0];
161
0
    c->force_channels = true;
162
0
    c->samplerate = opts->samplerate;
163
164
0
    c->native_tb_num = 1;
165
0
    c->native_tb_den = c->samplerate;
166
167
0
    int f = opts->aformat;
168
    // See PCM():               sign   float  bits    endian
169
0
    mp_set_pcm_codec(sh->codec, f & 1, f & 2, f >> 3, f & 4);
170
0
    int samplesize = ((f >> 3) + 7) / 8;
171
172
0
    demux_add_sh_stream(demuxer, sh);
173
174
0
    struct priv *p = talloc_ptrtype(demuxer, p);
175
0
    demuxer->priv = p;
176
0
    *p = (struct priv) {
177
0
        .sh = sh,
178
0
        .frame_size = samplesize * c->channels.num,
179
0
        .frame_rate = c->samplerate,
180
0
        .read_frames = c->samplerate / 8,
181
0
    };
182
183
0
    return generic_open(demuxer);
184
0
}
185
186
static int demux_rawvideo_open(demuxer_t *demuxer, enum demux_check check)
187
108k
{
188
108k
    struct demux_rawvideo_opts *opts =
189
108k
        mp_get_config_group(demuxer, demuxer->global, &demux_rawvideo_conf);
190
191
108k
    if (check != DEMUX_CHECK_REQUEST && check != DEMUX_CHECK_FORCE)
192
108k
        return -1;
193
194
0
    int width = opts->width;
195
0
    int height = opts->height;
196
197
0
    if (!width || !height) {
198
0
        MP_ERR(demuxer, "rawvideo: width or height not specified!\n");
199
0
        return -1;
200
0
    }
201
202
0
    const char *decoder = "rawvideo";
203
0
    int imgfmt = opts->vformat;
204
0
    int imgsize = opts->imgsize;
205
0
    int mp_imgfmt = 0;
206
0
    if (opts->mp_format && !IMGFMT_IS_HWACCEL(opts->mp_format)) {
207
0
        mp_imgfmt = opts->mp_format;
208
0
        if (!imgsize) {
209
0
            struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(opts->mp_format);
210
0
            for (int p = 0; p < desc.num_planes; p++) {
211
0
                imgsize += ((width >> desc.xs[p]) * (height >> desc.ys[p]) *
212
0
                            desc.bpp[p] + 7) / 8;
213
0
            }
214
0
        }
215
0
    } else if (opts->codec && opts->codec[0])
216
0
        decoder = talloc_strdup(demuxer, opts->codec);
217
218
0
    if (!imgsize) {
219
0
        int bpp = 0;
220
0
        switch (imgfmt) {
221
0
        case MKTAG('Y', 'V', '1', '2'):
222
0
        case MKTAG('I', '4', '2', '0'):
223
0
        case MKTAG('I', 'Y', 'U', 'V'):
224
0
            bpp = 12;
225
0
            break;
226
0
        case MKTAG('U', 'Y', 'V', 'Y'):
227
0
        case MKTAG('Y', 'U', 'Y', '2'):
228
0
            bpp = 16;
229
0
            break;
230
0
        }
231
0
        if (!bpp) {
232
0
            MP_ERR(demuxer, "rawvideo: img size not specified and unknown format!\n");
233
0
            return -1;
234
0
        }
235
0
        imgsize = width * height * bpp / 8;
236
0
    }
237
238
0
    struct sh_stream *sh = demux_alloc_sh_stream(STREAM_VIDEO);
239
0
    struct mp_codec_params *c = sh->codec;
240
0
    c->codec = decoder;
241
0
    c->codec_tag = imgfmt;
242
0
    c->fps = opts->fps;
243
0
    c->reliable_fps = true;
244
0
    c->disp_w = width;
245
0
    c->disp_h = height;
246
0
    if (mp_imgfmt) {
247
0
        c->lav_codecpar = avcodec_parameters_alloc();
248
0
        MP_HANDLE_OOM(c->lav_codecpar);
249
0
        c->lav_codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
250
0
        c->lav_codecpar->codec_id = mp_codec_to_av_codec_id(decoder);
251
0
        c->lav_codecpar->format = imgfmt2pixfmt(mp_imgfmt);
252
0
        c->lav_codecpar->width = width;
253
0
        c->lav_codecpar->height = height;
254
0
    }
255
0
    demux_add_sh_stream(demuxer, sh);
256
257
0
    struct priv *p = talloc_ptrtype(demuxer, p);
258
0
    demuxer->priv = p;
259
0
    *p = (struct priv) {
260
0
        .sh = sh,
261
0
        .frame_size = imgsize,
262
0
        .frame_rate = c->fps,
263
0
        .read_frames = 1,
264
0
    };
265
266
0
    return generic_open(demuxer);
267
0
}
268
269
static bool raw_read_packet(struct demuxer *demuxer, struct demux_packet **pkt)
270
0
{
271
0
    struct priv *p = demuxer->priv;
272
273
0
    if (demuxer->stream->eof)
274
0
        return false;
275
276
0
    struct demux_packet *dp = new_demux_packet(demuxer->packet_pool,
277
0
                                               p->frame_size * p->read_frames);
278
0
    if (!dp) {
279
0
        MP_ERR(demuxer, "Can't read packet.\n");
280
0
        return true;
281
0
    }
282
283
0
    dp->keyframe = true;
284
0
    dp->pos = stream_tell(demuxer->stream);
285
0
    dp->pts = (dp->pos  / p->frame_size) / p->frame_rate;
286
287
0
    int len = stream_read(demuxer->stream, dp->buffer, dp->len);
288
0
    demux_packet_shorten(dp, len);
289
290
0
    dp->stream = p->sh->index;
291
0
    *pkt = dp;
292
293
0
    return true;
294
0
}
295
296
static void raw_seek(demuxer_t *demuxer, double seek_pts, int flags)
297
0
{
298
0
    struct priv *p = demuxer->priv;
299
0
    stream_t *s = demuxer->stream;
300
0
    int64_t end = stream_get_size(s);
301
0
    int64_t frame_nr = seek_pts * p->frame_rate;
302
0
    frame_nr = frame_nr - (frame_nr % p->read_frames);
303
0
    int64_t pos = frame_nr * p->frame_size;
304
0
    if (flags & SEEK_FACTOR)
305
0
        pos = end * seek_pts;
306
0
    if (pos < 0)
307
0
        pos = 0;
308
0
    if (end > 0 && pos > end)
309
0
        pos = end;
310
0
    stream_seek(s, (pos / p->frame_size) * p->frame_size);
311
0
}
312
313
const demuxer_desc_t demuxer_desc_rawaudio = {
314
    .name = "rawaudio",
315
    .desc = "Uncompressed audio",
316
    .open = demux_rawaudio_open,
317
    .read_packet = raw_read_packet,
318
    .seek = raw_seek,
319
};
320
321
const demuxer_desc_t demuxer_desc_rawvideo = {
322
    .name = "rawvideo",
323
    .desc = "Uncompressed video",
324
    .open = demux_rawvideo_open,
325
    .read_packet = raw_read_packet,
326
    .seek = raw_seek,
327
};