/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 | | }; |