Coverage Report

Created: 2026-01-26 07:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/audio/decode/ad_spdif.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2012 Naoya OYAMA
3
 *
4
 * This file is part of mpv.
5
 *
6
 * mpv is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * mpv is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
20
#include <string.h>
21
#include <assert.h>
22
23
#include <libavformat/avformat.h>
24
#include <libavcodec/avcodec.h>
25
#include <libavutil/opt.h>
26
27
#include "audio/aframe.h"
28
#include "audio/chmap_avchannel.h"
29
#include "audio/format.h"
30
#include "common/av_common.h"
31
#include "common/codecs.h"
32
#include "common/msg.h"
33
#include "demux/packet.h"
34
#include "demux/stheader.h"
35
#include "filters/f_decoder_wrapper.h"
36
#include "filters/filter_internal.h"
37
#include "options/options.h"
38
39
0
#define OUTBUF_SIZE 65536
40
41
struct spdifContext {
42
    struct mp_log   *log;
43
    struct mp_codec_params *codec;
44
    enum AVCodecID   codec_id;
45
    AVFormatContext *lavf_ctx;
46
    AVPacket        *avpkt;
47
    int              out_buffer_len;
48
    uint8_t          out_buffer[OUTBUF_SIZE];
49
    bool             need_close;
50
    bool             use_dts_hd;
51
    struct mp_aframe *fmt;
52
    int              sstride;
53
    struct mp_aframe_pool *pool;
54
55
    struct mp_decoder public;
56
};
57
58
#if LIBAVFORMAT_VERSION_MAJOR < 61
59
static int write_packet(void *p, uint8_t *buf, int buf_size)
60
#else
61
static int write_packet(void *p, const uint8_t *buf, int buf_size)
62
#endif
63
0
{
64
0
    struct spdifContext *ctx = p;
65
66
0
    int buffer_left = OUTBUF_SIZE - ctx->out_buffer_len;
67
0
    if (buf_size > buffer_left) {
68
0
        MP_ERR(ctx, "spdif packet too large.\n");
69
0
        buf_size = buffer_left;
70
0
    }
71
72
0
    memcpy(&ctx->out_buffer[ctx->out_buffer_len], buf, buf_size);
73
0
    ctx->out_buffer_len += buf_size;
74
0
    return buf_size;
75
0
}
76
77
// (called on both filter destruction _and_ if lavf fails to init)
78
static void ad_spdif_destroy(struct mp_filter *da)
79
0
{
80
0
    struct spdifContext *spdif_ctx = da->priv;
81
0
    AVFormatContext     *lavf_ctx  = spdif_ctx->lavf_ctx;
82
83
0
    if (lavf_ctx) {
84
0
        if (spdif_ctx->need_close)
85
0
            av_write_trailer(lavf_ctx);
86
0
        if (lavf_ctx->pb)
87
0
            av_freep(&lavf_ctx->pb->buffer);
88
0
        avio_context_free(&lavf_ctx->pb);
89
0
        avformat_free_context(lavf_ctx);
90
0
        spdif_ctx->lavf_ctx = NULL;
91
0
    }
92
0
    mp_free_av_packet(&spdif_ctx->avpkt);
93
0
}
94
95
static void determine_codec_params(struct mp_filter *da, AVPacket *pkt,
96
                                   int *out_profile, int *out_rate)
97
0
{
98
0
    struct spdifContext *spdif_ctx = da->priv;
99
0
    int profile = AV_PROFILE_UNKNOWN;
100
0
    AVCodecContext *ctx = NULL;
101
0
    AVFrame *frame = NULL;
102
103
0
    AVCodecParserContext *parser = av_parser_init(spdif_ctx->codec_id);
104
0
    if (parser) {
105
        // Don't make it wait for the next frame.
106
0
        parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
107
108
0
        ctx = avcodec_alloc_context3(NULL);
109
0
        if (!ctx) {
110
0
            av_parser_close(parser);
111
0
            goto done;
112
0
        }
113
114
0
        uint8_t *d = NULL;
115
0
        int s = 0;
116
0
        if (av_parser_parse2(parser, ctx, &d, &s, pkt->data, pkt->size, 0, 0, 0) > 0) {
117
0
            *out_profile = profile = ctx->profile;
118
0
            *out_rate = ctx->sample_rate;
119
0
            spdif_ctx->codec->codec_profile = avcodec_profile_name(spdif_ctx->codec_id, profile);
120
0
        }
121
122
0
        avcodec_free_context(&ctx);
123
0
        av_parser_close(parser);
124
0
    }
125
126
0
    if (profile != AV_PROFILE_UNKNOWN)
127
0
        return;
128
129
0
    const AVCodec *codec = avcodec_find_decoder(spdif_ctx->codec_id);
130
0
    if (!codec)
131
0
        goto done;
132
133
0
    frame = av_frame_alloc();
134
0
    if (!frame)
135
0
        goto done;
136
137
0
    ctx = avcodec_alloc_context3(codec);
138
0
    if (!ctx)
139
0
        goto done;
140
141
0
    if (avcodec_open2(ctx, codec, NULL) < 0)
142
0
        goto done;
143
144
0
    if (avcodec_send_packet(ctx, pkt) < 0)
145
0
        goto done;
146
0
    if (avcodec_receive_frame(ctx, frame) < 0)
147
0
        goto done;
148
149
0
    *out_profile = profile = ctx->profile;
150
0
    *out_rate = ctx->sample_rate;
151
152
0
    struct mp_codec_params *c = spdif_ctx->codec;
153
0
    c->codec_profile = av_get_profile_name(ctx->codec, ctx->profile);
154
0
    if (!c->codec_profile)
155
0
        c->codec_profile = avcodec_profile_name(ctx->codec_id, ctx->profile);
156
0
    c->codec = ctx->codec_descriptor->name;
157
0
    c->codec_desc = ctx->codec_descriptor->long_name;
158
0
    mp_chmap_from_av_layout(&c->channels, &ctx->ch_layout);
159
160
0
done:
161
0
    av_frame_free(&frame);
162
0
    avcodec_free_context(&ctx);
163
164
0
    if (profile == AV_PROFILE_UNKNOWN)
165
0
        MP_WARN(da, "Failed to parse codec profile.\n");
166
0
}
167
168
static int init_filter(struct mp_filter *da)
169
0
{
170
0
    struct spdifContext *spdif_ctx = da->priv;
171
172
0
    AVPacket *pkt = spdif_ctx->avpkt;
173
174
0
    int profile = AV_PROFILE_UNKNOWN;
175
0
    int c_rate = 0;
176
0
    determine_codec_params(da, pkt, &profile, &c_rate);
177
0
    MP_VERBOSE(da, "In: profile=%d samplerate=%d\n", profile, c_rate);
178
179
0
    AVFormatContext *lavf_ctx  = avformat_alloc_context();
180
0
    if (!lavf_ctx)
181
0
        goto fail;
182
183
0
    spdif_ctx->lavf_ctx = lavf_ctx;
184
185
0
    lavf_ctx->oformat = av_guess_format("spdif", NULL, NULL);
186
0
    if (!lavf_ctx->oformat)
187
0
        goto fail;
188
189
0
    void *buffer = av_mallocz(OUTBUF_SIZE);
190
0
    MP_HANDLE_OOM(buffer);
191
0
    lavf_ctx->pb = avio_alloc_context(buffer, OUTBUF_SIZE, 1, spdif_ctx, NULL,
192
0
                                      write_packet, NULL);
193
0
    if (!lavf_ctx->pb) {
194
0
        av_free(buffer);
195
0
        goto fail;
196
0
    }
197
198
    // Request minimal buffering
199
0
    lavf_ctx->pb->direct = 1;
200
201
0
    AVStream *stream = avformat_new_stream(lavf_ctx, 0);
202
0
    if (!stream)
203
0
        goto fail;
204
205
0
    stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
206
0
    stream->codecpar->codec_id   = spdif_ctx->codec_id;
207
208
0
    AVDictionary *format_opts = NULL;
209
210
0
    spdif_ctx->fmt = mp_aframe_create();
211
0
    talloc_steal(spdif_ctx, spdif_ctx->fmt);
212
213
0
    int num_channels = 0;
214
0
    int sample_format = 0;
215
0
    int samplerate = 0;
216
0
    switch (spdif_ctx->codec_id) {
217
0
    case AV_CODEC_ID_AAC:
218
0
        sample_format                   = AF_FORMAT_S_AAC;
219
0
        samplerate                      = 48000;
220
0
        num_channels                    = 2;
221
0
        break;
222
0
    case AV_CODEC_ID_AC3:
223
0
        sample_format                   = AF_FORMAT_S_AC3;
224
0
        samplerate                      = c_rate > 0 ? c_rate : 48000;
225
0
        num_channels                    = 2;
226
0
        break;
227
0
    case AV_CODEC_ID_DTS: {
228
0
        bool is_hd = profile == AV_PROFILE_DTS_HD_HRA ||
229
0
                     profile == AV_PROFILE_DTS_HD_MA  ||
230
0
                     profile == AV_PROFILE_DTS_HD_MA_X ||
231
0
                     profile == AV_PROFILE_DTS_HD_MA_X_IMAX ||
232
0
                     profile == AV_PROFILE_UNKNOWN;
233
234
        // Apparently, DTS-HD over SPDIF is specified to be 7.1 (8 channels)
235
        // for DTS-HD MA, and stereo (2 channels) for DTS-HD HRA. The bit
236
        // streaming rate as well as the signaled channel count are defined
237
        // based on this value.
238
0
        int dts_hd_spdif_channel_count = profile == AV_PROFILE_DTS_HD_HRA ?
239
0
                                         2 : 8;
240
0
        if (spdif_ctx->use_dts_hd && is_hd) {
241
0
            av_dict_set_int(&format_opts, "dtshd_rate",
242
0
                            dts_hd_spdif_channel_count * 96000, 0);
243
0
            sample_format               = AF_FORMAT_S_DTSHD;
244
0
            samplerate                  = 192000;
245
0
            num_channels                = dts_hd_spdif_channel_count;
246
0
        } else {
247
0
            sample_format               = AF_FORMAT_S_DTS;
248
0
            samplerate                  = c_rate > 44100 ? 48000 : 44100;
249
0
            num_channels                = 2;
250
0
        }
251
0
        break;
252
0
    }
253
0
    case AV_CODEC_ID_EAC3:
254
0
        sample_format                   = AF_FORMAT_S_EAC3;
255
0
        samplerate                      = 192000;
256
0
        num_channels                    = 2;
257
0
        break;
258
0
    case AV_CODEC_ID_MP3:
259
0
        sample_format                   = AF_FORMAT_S_MP3;
260
0
        samplerate                      = 48000;
261
0
        num_channels                    = 2;
262
0
        break;
263
0
    case AV_CODEC_ID_TRUEHD:
264
0
        sample_format                   = AF_FORMAT_S_TRUEHD;
265
0
        samplerate                      = 192000;
266
0
        num_channels                    = 8;
267
0
        break;
268
0
    default:
269
0
        abort();
270
0
    }
271
272
0
    stream->codecpar->sample_rate = samplerate;
273
274
0
    struct mp_chmap chmap;
275
0
    mp_chmap_from_channels(&chmap, num_channels);
276
0
    mp_aframe_set_chmap(spdif_ctx->fmt, &chmap);
277
0
    mp_aframe_set_format(spdif_ctx->fmt, sample_format);
278
0
    mp_aframe_set_rate(spdif_ctx->fmt, samplerate);
279
280
0
    spdif_ctx->sstride = mp_aframe_get_sstride(spdif_ctx->fmt);
281
282
0
    if (avformat_write_header(lavf_ctx, &format_opts) < 0) {
283
0
        MP_FATAL(da, "libavformat spdif initialization failed.\n");
284
0
        av_dict_free(&format_opts);
285
0
        goto fail;
286
0
    }
287
0
    av_dict_free(&format_opts);
288
289
0
    spdif_ctx->need_close = true;
290
291
0
    return 0;
292
293
0
fail:
294
0
    ad_spdif_destroy(da);
295
0
    mp_filter_internal_mark_failed(da);
296
0
    return -1;
297
0
}
298
299
static void ad_spdif_process(struct mp_filter *da)
300
0
{
301
0
    struct spdifContext *spdif_ctx = da->priv;
302
303
0
    if (!mp_pin_can_transfer_data(da->ppins[1], da->ppins[0]))
304
0
        return;
305
306
0
    struct mp_frame inframe = mp_pin_out_read(da->ppins[0]);
307
0
    if (inframe.type == MP_FRAME_EOF) {
308
0
        mp_pin_in_write(da->ppins[1], inframe);
309
0
        return;
310
0
    } else if (inframe.type != MP_FRAME_PACKET) {
311
0
        if (inframe.type) {
312
0
            MP_ERR(da, "unknown frame type\n");
313
0
            mp_filter_internal_mark_failed(da);
314
0
        }
315
0
        return;
316
0
    }
317
318
0
    struct demux_packet *mpkt = inframe.data;
319
0
    struct mp_aframe *out = NULL;
320
0
    double pts = mpkt->pts;
321
322
0
    if (!spdif_ctx->avpkt) {
323
0
        spdif_ctx->avpkt = av_packet_alloc();
324
0
        MP_HANDLE_OOM(spdif_ctx->avpkt);
325
0
    }
326
0
    mp_set_av_packet(spdif_ctx->avpkt, mpkt, NULL);
327
0
    spdif_ctx->avpkt->pts = spdif_ctx->avpkt->dts = 0;
328
0
    if (!spdif_ctx->lavf_ctx) {
329
0
        if (init_filter(da) < 0)
330
0
            goto done;
331
0
        mp_assert(spdif_ctx->avpkt);
332
0
        mp_assert(spdif_ctx->lavf_ctx);
333
0
    }
334
335
0
    spdif_ctx->out_buffer_len  = 0;
336
0
    int ret = av_write_frame(spdif_ctx->lavf_ctx, spdif_ctx->avpkt);
337
0
    avio_flush(spdif_ctx->lavf_ctx->pb);
338
0
    if (ret < 0) {
339
0
        MP_ERR(da, "spdif mux error: '%s'\n", mp_strerror(AVUNERROR(ret)));
340
0
        goto done;
341
0
    }
342
343
0
    out = mp_aframe_new_ref(spdif_ctx->fmt);
344
0
    int samples = spdif_ctx->out_buffer_len / spdif_ctx->sstride;
345
0
    if (mp_aframe_pool_allocate(spdif_ctx->pool, out, samples) < 0) {
346
0
        TA_FREEP(&out);
347
0
        goto done;
348
0
    }
349
350
0
    uint8_t **data = mp_aframe_get_data_rw(out);
351
0
    if (!data) {
352
0
        TA_FREEP(&out);
353
0
        goto done;
354
0
    }
355
356
0
    memcpy(data[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
357
0
    mp_aframe_set_pts(out, pts);
358
359
0
done:
360
0
    talloc_free(mpkt);
361
0
    if (out) {
362
0
        mp_pin_in_write(da->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out));
363
0
    } else {
364
0
        mp_filter_internal_mark_failed(da);
365
0
    }
366
0
}
367
368
static const int codecs[] = {
369
    AV_CODEC_ID_AAC,
370
    AV_CODEC_ID_AC3,
371
    AV_CODEC_ID_DTS,
372
    AV_CODEC_ID_EAC3,
373
    AV_CODEC_ID_MP3,
374
    AV_CODEC_ID_TRUEHD,
375
    AV_CODEC_ID_NONE
376
};
377
378
static bool find_codec(const char *name)
379
38.8k
{
380
240k
    for (int n = 0; codecs[n] != AV_CODEC_ID_NONE; n++) {
381
208k
        const char *format = mp_codec_from_av_codec_id(codecs[n]);
382
208k
        if (format && name && strcmp(format, name) == 0)
383
6.89k
            return true;
384
208k
    }
385
31.9k
    return false;
386
38.8k
}
387
388
// codec is the libavcodec name of the source audio codec.
389
// pref is a ","-separated list of names, some of them which do not match with
390
// libavcodec names (like dts-hd).
391
struct mp_decoder_list *select_spdif_codec(const char *codec, const char *pref)
392
38.8k
{
393
38.8k
    struct mp_decoder_list *list = talloc_zero(NULL, struct mp_decoder_list);
394
395
38.8k
    if (!find_codec(codec))
396
31.9k
        return list;
397
398
38.8k
    bool spdif_allowed = false, dts_hd_allowed = false;
399
6.89k
    bstr sel = bstr0(pref);
400
6.89k
    while (sel.len) {
401
0
        bstr decoder;
402
0
        bstr_split_tok(sel, ",", &decoder, &sel);
403
0
        if (decoder.len) {
404
0
            if (bstr_equals0(decoder, codec))
405
0
                spdif_allowed = true;
406
0
            if (bstr_equals0(decoder, "dts-hd") && strcmp(codec, "dts") == 0)
407
0
                spdif_allowed = dts_hd_allowed = true;
408
0
        }
409
0
    }
410
411
6.89k
    if (!spdif_allowed)
412
6.89k
        return list;
413
414
0
    const char *suffix_name = dts_hd_allowed ? "dts_hd" : codec;
415
0
    char name[80];
416
0
    snprintf(name, sizeof(name), "spdif_%s", suffix_name);
417
0
    mp_add_decoder(list, codec, name,
418
0
                   "libavformat/spdifenc audio pass-through decoder");
419
0
    return list;
420
6.89k
}
421
422
static const struct mp_filter_info ad_spdif_filter = {
423
    .name = "ad_spdif",
424
    .priv_size = sizeof(struct spdifContext),
425
    .process = ad_spdif_process,
426
    .destroy = ad_spdif_destroy,
427
};
428
429
static struct mp_decoder *create(struct mp_filter *parent,
430
                                 struct mp_codec_params *codec,
431
                                 const char *decoder)
432
0
{
433
0
    struct mp_filter *da = mp_filter_create(parent, &ad_spdif_filter);
434
0
    if (!da)
435
0
        return NULL;
436
437
0
    mp_filter_add_pin(da, MP_PIN_IN, "in");
438
0
    mp_filter_add_pin(da, MP_PIN_OUT, "out");
439
440
0
    da->log = mp_log_new(da, parent->log, NULL);
441
442
0
    struct spdifContext *spdif_ctx = da->priv;
443
0
    spdif_ctx->log = da->log;
444
0
    spdif_ctx->codec = codec;
445
0
    spdif_ctx->pool = mp_aframe_pool_create(spdif_ctx);
446
0
    spdif_ctx->public.f = da;
447
448
0
    if (strcmp(decoder, "spdif_dts_hd") == 0)
449
0
        spdif_ctx->use_dts_hd = true;
450
451
0
    spdif_ctx->codec_id = mp_codec_to_av_codec_id(codec->codec);
452
453
454
0
    if (spdif_ctx->codec_id == AV_CODEC_ID_NONE) {
455
0
        talloc_free(da);
456
0
        return NULL;
457
0
    }
458
459
0
    const AVCodecDescriptor *desc = avcodec_descriptor_get(spdif_ctx->codec_id);
460
0
    if (desc)
461
0
        codec->codec_desc = desc->long_name;
462
463
0
    return &spdif_ctx->public;
464
0
}
465
466
const struct mp_decoder_fns ad_spdif = {
467
    .create = create,
468
};