Coverage Report

Created: 2026-07-25 07:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/pcm-dvdenc.c
Line
Count
Source
1
/*
2
 * LPCM codecs for PCM formats found in Video DVD streams
3
 * Copyright (c) 2018 Paul B Mahol
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
22
#include "libavutil/avassert.h"
23
#include "libavutil/channel_layout.h"
24
#include "avcodec.h"
25
#include "bytestream.h"
26
#include "codec_internal.h"
27
#include "encode.h"
28
29
typedef struct PCMDVDContext {
30
    uint8_t header[3];       // Header added to every frame
31
    int block_size;          // Size of a block of samples in bytes
32
    int samples_per_block;   // Number of samples per channel per block
33
    int groups_per_block;    // Number of 20/24-bit sample groups per block
34
} PCMDVDContext;
35
36
static av_cold int pcm_dvd_encode_init(AVCodecContext *avctx)
37
0
{
38
0
    PCMDVDContext *s = avctx->priv_data;
39
0
    int quant, freq, frame_size;
40
41
0
    switch (avctx->sample_rate) {
42
0
    case 48000:
43
0
        freq = 0;
44
0
        break;
45
0
    case 96000:
46
0
        freq = 1;
47
0
        break;
48
0
    default:
49
0
        av_unreachable("Already checked via CODEC_SAMPLERATES");
50
0
    }
51
52
0
    switch (avctx->sample_fmt) {
53
0
    case AV_SAMPLE_FMT_S16:
54
0
        avctx->bits_per_coded_sample = 16;
55
0
        quant = 0;
56
0
        break;
57
0
    case AV_SAMPLE_FMT_S32:
58
0
        avctx->bits_per_coded_sample = 24;
59
0
        quant = 2;
60
0
        break;
61
0
    default:
62
0
        av_unreachable("Already checked via CODEC_SAMPLEFMTS");
63
0
    }
64
65
0
    avctx->bits_per_coded_sample = 16 + quant * 4;
66
0
    avctx->block_align           = avctx->ch_layout.nb_channels * avctx->bits_per_coded_sample / 8;
67
0
    avctx->bit_rate              = avctx->block_align * 8LL * avctx->sample_rate;
68
0
    if (avctx->bit_rate > 9800000) {
69
0
        av_log(avctx, AV_LOG_ERROR, "Too big bitrate: reduce sample rate, bitdepth or channels.\n");
70
0
        return AVERROR(EINVAL);
71
0
    }
72
73
0
    if (avctx->sample_fmt == AV_SAMPLE_FMT_S16) {
74
0
        s->samples_per_block = 1;
75
0
        s->block_size        = avctx->ch_layout.nb_channels * 2;
76
0
        frame_size           = 2008 / s->block_size;
77
0
    } else {
78
0
        switch (avctx->ch_layout.nb_channels) {
79
0
        case 1:
80
0
        case 2:
81
0
        case 4:
82
            /* one group has all the samples needed */
83
0
            s->block_size        = 4 * avctx->bits_per_coded_sample / 8;
84
0
            s->samples_per_block = 4 / avctx->ch_layout.nb_channels;
85
0
            s->groups_per_block  = 1;
86
0
            break;
87
0
        case 8:
88
            /* two groups have all the samples needed */
89
0
            s->block_size        = 8 * avctx->bits_per_coded_sample / 8;
90
0
            s->samples_per_block = 1;
91
0
            s->groups_per_block  = 2;
92
0
            break;
93
0
        default:
94
            /* need avctx->ch_layout.nb_channels groups */
95
0
            s->block_size        = 4 * avctx->ch_layout.nb_channels *
96
0
                                   avctx->bits_per_coded_sample / 8;
97
0
            s->samples_per_block = 4;
98
0
            s->groups_per_block  = avctx->ch_layout.nb_channels;
99
0
            break;
100
0
        }
101
102
0
        frame_size = FFALIGN(2008 / s->block_size, s->samples_per_block);
103
0
    }
104
105
0
    s->header[0] = 0x0c;
106
0
    s->header[1] = (quant << 6) | (freq << 4) | (avctx->ch_layout.nb_channels - 1);
107
0
    s->header[2] = 0x80;
108
109
0
    avctx->frame_size = frame_size;
110
111
0
    return 0;
112
0
}
113
114
static int pcm_dvd_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
115
                                const AVFrame *frame, int *got_packet_ptr)
116
0
{
117
0
    PCMDVDContext *s = avctx->priv_data;
118
0
    int samples = frame->nb_samples * avctx->ch_layout.nb_channels;
119
0
    int64_t pkt_size = (int64_t)(frame->nb_samples / s->samples_per_block) * s->block_size + 3;
120
0
    int blocks = (pkt_size - 3) / s->block_size;
121
0
    const int16_t *src16;
122
0
    const int32_t *src32;
123
0
    PutByteContext pb;
124
0
    int ret;
125
126
0
    if ((ret = ff_get_encode_buffer(avctx, avpkt, pkt_size, 0)) < 0)
127
0
        return ret;
128
129
0
    memcpy(avpkt->data, s->header, 3);
130
131
0
    src16 = (const int16_t *)frame->data[0];
132
0
    src32 = (const int32_t *)frame->data[0];
133
134
0
    bytestream2_init_writer(&pb, avpkt->data + 3, avpkt->size - 3);
135
136
0
    switch (avctx->sample_fmt) {
137
0
    case AV_SAMPLE_FMT_S16:
138
0
        do {
139
0
            bytestream2_put_be16(&pb, *src16++);
140
0
        } while (--samples);
141
0
        break;
142
0
    case AV_SAMPLE_FMT_S32:
143
0
        if (avctx->ch_layout.nb_channels == 1) {
144
0
            do {
145
0
                for (int i = 2; i; i--) {
146
0
                    bytestream2_put_be16(&pb, src32[0] >> 16);
147
0
                    bytestream2_put_be16(&pb, src32[1] >> 16);
148
0
                    bytestream2_put_byte(&pb, (uint8_t)((*src32++) >> 8));
149
0
                    bytestream2_put_byte(&pb, (uint8_t)((*src32++) >> 8));
150
0
                }
151
0
            } while (--blocks);
152
0
        } else {
153
0
            do {
154
0
                for (int i = s->groups_per_block; i; i--) {
155
0
                    bytestream2_put_be16(&pb, src32[0] >> 16);
156
0
                    bytestream2_put_be16(&pb, src32[1] >> 16);
157
0
                    bytestream2_put_be16(&pb, src32[2] >> 16);
158
0
                    bytestream2_put_be16(&pb, src32[3] >> 16);
159
0
                    bytestream2_put_byte(&pb, (uint8_t)((*src32++) >> 8));
160
0
                    bytestream2_put_byte(&pb, (uint8_t)((*src32++) >> 8));
161
0
                    bytestream2_put_byte(&pb, (uint8_t)((*src32++) >> 8));
162
0
                    bytestream2_put_byte(&pb, (uint8_t)((*src32++) >> 8));
163
0
                }
164
0
            } while (--blocks);
165
0
        }
166
0
        break;
167
0
    }
168
169
0
    *got_packet_ptr = 1;
170
171
0
    return 0;
172
0
}
173
174
const FFCodec ff_pcm_dvd_encoder = {
175
    .p.name         = "pcm_dvd",
176
    CODEC_LONG_NAME("PCM signed 16|20|24-bit big-endian for DVD media"),
177
    .p.type         = AVMEDIA_TYPE_AUDIO,
178
    .p.id           = AV_CODEC_ID_PCM_DVD,
179
    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SMALL_LAST_FRAME |
180
                      AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
181
    .priv_data_size = sizeof(PCMDVDContext),
182
    .init           = pcm_dvd_encode_init,
183
    FF_CODEC_ENCODE_CB(pcm_dvd_encode_frame),
184
    CODEC_SAMPLERATES(48000, 96000),
185
    CODEC_CH_LAYOUTS(AV_CHANNEL_LAYOUT_MONO,    AV_CHANNEL_LAYOUT_STEREO,
186
                     AV_CHANNEL_LAYOUT_5POINT1, AV_CHANNEL_LAYOUT_7POINT1),
187
    CODEC_SAMPLEFMTS(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32),
188
};