FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/codec2.c
Date: 2022-11-26 13:19:19
Exec Total Coverage
Lines: 3 99 3.0%
Branches: 1 54 1.9%

Line Branch Exec Source
1 /*
2 * codec2 muxer and demuxers
3 * Copyright (c) 2017 Tomas Härdin
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 "config_components.h"
23
24 #include "libavcodec/codec2utils.h"
25 #include "libavutil/channel_layout.h"
26 #include "libavutil/intreadwrite.h"
27 #include "libavutil/opt.h"
28 #include "avio_internal.h"
29 #include "avformat.h"
30 #include "internal.h"
31 #include "rawenc.h"
32 #include "pcm.h"
33
34 #define CODEC2_HEADER_SIZE 7
35 #define CODEC2_MAGIC 0xC0DEC2
36
37 //the lowest version we should ever run across is 0.8
38 //we may run across later versions as the format evolves
39 #define EXPECTED_CODEC2_MAJOR_VERSION 0
40 #define EXPECTED_CODEC2_MINOR_VERSION 8
41
42 typedef struct {
43 const AVClass *class;
44 int mode;
45 int frames_per_packet;
46 } Codec2Context;
47
48 6780 static int codec2_probe(const AVProbeData *p)
49 {
50 //must start wih C0 DE C2
51
1/2
✓ Branch 0 taken 6780 times.
✗ Branch 1 not taken.
6780 if (AV_RB24(p->buf) != CODEC2_MAGIC) {
52 6780 return 0;
53 }
54
55 //no .c2 files prior to 0.8
56 //be strict about major version while we're at it
57 if (p->buf[3] != EXPECTED_CODEC2_MAJOR_VERSION ||
58 p->buf[4] < EXPECTED_CODEC2_MINOR_VERSION) {
59 return 0;
60 }
61
62 //32 bits of identification -> low score
63 return AVPROBE_SCORE_EXTENSION + 1;
64 }
65
66 //Mimics codec2_samples_per_frame()
67 static int codec2_mode_frame_size(AVFormatContext *s, int mode)
68 {
69 int frame_size_table[CODEC2_MODE_MAX+1] = {
70 160, // 3200
71 160, // 2400
72 320, // 1600
73 320, // 1400
74 320, // 1300
75 320, // 1200
76 320, // 700
77 320, // 700B
78 320, // 700C
79 };
80
81 if (mode < 0 || mode > CODEC2_MODE_MAX) {
82 av_log(s, AV_LOG_ERROR, "unknown codec2 mode %i, can't find frame_size\n", mode);
83 return 0;
84 } else {
85 return frame_size_table[mode];
86 }
87 }
88
89 //Mimics (codec2_bits_per_frame()+7)/8
90 static int codec2_mode_block_align(AVFormatContext *s, int mode)
91 {
92 int block_align_table[CODEC2_MODE_MAX+1] = {
93 8, // 3200
94 6, // 2400
95 8, // 1600
96 7, // 1400
97 7, // 1300
98 6, // 1200
99 4, // 700
100 4, // 700B
101 4, // 700C
102 };
103
104 if (mode < 0 || mode > CODEC2_MODE_MAX) {
105 av_log(s, AV_LOG_ERROR, "unknown codec2 mode %i, can't find block_align\n", mode);
106 return 0;
107 } else {
108 return block_align_table[mode];
109 }
110 }
111
112 //Computes bitrate from mode, with frames rounded up to the nearest octet.
113 //So 700 bit/s (28 bits/frame) becomes 800 bits/s (32 bits/frame).
114 static int codec2_mode_bit_rate(AVFormatContext *s, int mode)
115 {
116 int frame_size = codec2_mode_frame_size(s, mode);
117 int block_align = codec2_mode_block_align(s, mode);
118
119 if (frame_size <= 0 || block_align <= 0) {
120 return 0;
121 }
122
123 return 8 * 8000 * block_align / frame_size;
124 }
125
126 static int codec2_read_header_common(AVFormatContext *s, AVStream *st)
127 {
128 int mode = codec2_mode_from_extradata(st->codecpar->extradata);
129
130 st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
131 st->codecpar->codec_id = AV_CODEC_ID_CODEC2;
132 st->codecpar->sample_rate = 8000;
133 st->codecpar->format = AV_SAMPLE_FMT_S16;
134 st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
135 st->codecpar->bit_rate = codec2_mode_bit_rate(s, mode);
136 st->codecpar->frame_size = codec2_mode_frame_size(s, mode);
137 st->codecpar->block_align = codec2_mode_block_align(s, mode);
138
139 if (st->codecpar->bit_rate <= 0 ||
140 st->codecpar->frame_size <= 0 ||
141 st->codecpar->block_align <= 0) {
142 return AVERROR_INVALIDDATA;
143 }
144
145 avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
146
147 return 0;
148 }
149
150 static int codec2_read_header(AVFormatContext *s)
151 {
152 AVStream *st = avformat_new_stream(s, NULL);
153 int ret, version;
154
155 if (!st) {
156 return AVERROR(ENOMEM);
157 }
158
159 if (avio_rb24(s->pb) != CODEC2_MAGIC) {
160 av_log(s, AV_LOG_ERROR, "not a .c2 file\n");
161 return AVERROR_INVALIDDATA;
162 }
163
164 ret = ff_alloc_extradata(st->codecpar, CODEC2_EXTRADATA_SIZE);
165 if (ret) {
166 return ret;
167 }
168
169 ret = ffio_read_size(s->pb, st->codecpar->extradata, CODEC2_EXTRADATA_SIZE);
170 if (ret < 0) {
171 return ret;
172 }
173
174 version = AV_RB16(st->codecpar->extradata);
175 if ((version >> 8) != EXPECTED_CODEC2_MAJOR_VERSION) {
176 avpriv_report_missing_feature(s, "Major version %i", version >> 8);
177 return AVERROR_PATCHWELCOME;
178 }
179
180 ffformatcontext(s)->data_offset = CODEC2_HEADER_SIZE;
181
182 return codec2_read_header_common(s, st);
183 }
184
185 static int codec2_read_packet(AVFormatContext *s, AVPacket *pkt)
186 {
187 Codec2Context *c2 = s->priv_data;
188 AVStream *st = s->streams[0];
189 int ret, size, n, block_align, frame_size;
190
191 block_align = st->codecpar->block_align;
192 frame_size = st->codecpar->frame_size;
193
194 if (block_align <= 0 || frame_size <= 0 || c2->frames_per_packet <= 0) {
195 return AVERROR(EINVAL);
196 }
197
198 //try to read desired number of frames, compute n from to actual number of bytes read
199 size = c2->frames_per_packet * block_align;
200 ret = av_get_packet(s->pb, pkt, size);
201 if (ret < 0) {
202 return ret;
203 }
204
205 //only set duration - compute_pkt_fields() and ff_pcm_read_seek() takes care of everything else
206 //tested by spamming the seek functionality in ffplay
207 n = ret / block_align;
208 pkt->duration = n * frame_size;
209
210 return ret;
211 }
212
213 static int codec2_write_header(AVFormatContext *s)
214 {
215 AVStream *st;
216
217 if (s->nb_streams != 1 || s->streams[0]->codecpar->codec_id != AV_CODEC_ID_CODEC2) {
218 av_log(s, AV_LOG_ERROR, ".c2 files must have exactly one codec2 stream\n");
219 return AVERROR(EINVAL);
220 }
221
222 st = s->streams[0];
223
224 if (st->codecpar->extradata_size != CODEC2_EXTRADATA_SIZE) {
225 av_log(s, AV_LOG_ERROR, ".c2 files require exactly %i bytes of extradata (got %i)\n",
226 CODEC2_EXTRADATA_SIZE, st->codecpar->extradata_size);
227 return AVERROR(EINVAL);
228 }
229
230 avio_wb24(s->pb, CODEC2_MAGIC);
231 avio_write(s->pb, st->codecpar->extradata, CODEC2_EXTRADATA_SIZE);
232
233 return 0;
234 }
235
236 static int codec2raw_read_header(AVFormatContext *s)
237 {
238 Codec2Context *c2 = s->priv_data;
239 AVStream *st;
240 int ret;
241
242 if (c2->mode < 0) {
243 //FIXME: using a default value of -1 for mandatory options is an incredibly ugly hack
244 av_log(s, AV_LOG_ERROR, "-mode must be set in order to make sense of raw codec2 files\n");
245 return AVERROR(EINVAL);
246 }
247
248 st = avformat_new_stream(s, NULL);
249 if (!st) {
250 return AVERROR(ENOMEM);
251 }
252
253 ret = ff_alloc_extradata(st->codecpar, CODEC2_EXTRADATA_SIZE);
254 if (ret) {
255 return ret;
256 }
257
258 codec2_make_extradata(st->codecpar->extradata, c2->mode);
259
260 return codec2_read_header_common(s, st);
261 }
262
263 //transcoding report2074.c2 to wav went from 7.391s to 5.322s with -frames_per_packet 1000 compared to default, same sha1sum
264 #define FRAMES_PER_PACKET \
265 { "frames_per_packet", "Number of frames to read at a time. Higher = faster decoding, lower granularity", \
266 offsetof(Codec2Context, frames_per_packet), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM}
267
268 static const AVOption codec2_options[] = {
269 FRAMES_PER_PACKET,
270 { NULL },
271 };
272
273 static const AVOption codec2raw_options[] = {
274 CODEC2_AVOPTIONS("codec2 mode [mandatory]", Codec2Context, -1, -1, AV_OPT_FLAG_DECODING_PARAM),
275 FRAMES_PER_PACKET,
276 { NULL },
277 };
278
279 static const AVClass codec2_demux_class = {
280 .class_name = "codec2 demuxer",
281 .item_name = av_default_item_name,
282 .option = codec2_options,
283 .version = LIBAVUTIL_VERSION_INT,
284 .category = AV_CLASS_CATEGORY_DEMUXER,
285 };
286
287 static const AVClass codec2raw_demux_class = {
288 .class_name = "codec2raw demuxer",
289 .item_name = av_default_item_name,
290 .option = codec2raw_options,
291 .version = LIBAVUTIL_VERSION_INT,
292 .category = AV_CLASS_CATEGORY_DEMUXER,
293 };
294
295 #if CONFIG_CODEC2_DEMUXER
296 const AVInputFormat ff_codec2_demuxer = {
297 .name = "codec2",
298 .long_name = NULL_IF_CONFIG_SMALL("codec2 .c2 demuxer"),
299 .priv_data_size = sizeof(Codec2Context),
300 .extensions = "c2",
301 .read_probe = codec2_probe,
302 .read_header = codec2_read_header,
303 .read_packet = codec2_read_packet,
304 .read_seek = ff_pcm_read_seek,
305 .flags = AVFMT_GENERIC_INDEX,
306 .raw_codec_id = AV_CODEC_ID_CODEC2,
307 .priv_class = &codec2_demux_class,
308 };
309 #endif
310
311 #if CONFIG_CODEC2_MUXER
312 const AVOutputFormat ff_codec2_muxer = {
313 .name = "codec2",
314 .long_name = NULL_IF_CONFIG_SMALL("codec2 .c2 muxer"),
315 .priv_data_size = sizeof(Codec2Context),
316 .extensions = "c2",
317 .audio_codec = AV_CODEC_ID_CODEC2,
318 .video_codec = AV_CODEC_ID_NONE,
319 .write_header = codec2_write_header,
320 .write_packet = ff_raw_write_packet,
321 .flags = AVFMT_NOTIMESTAMPS,
322 };
323 #endif
324
325 #if CONFIG_CODEC2RAW_DEMUXER
326 const AVInputFormat ff_codec2raw_demuxer = {
327 .name = "codec2raw",
328 .long_name = NULL_IF_CONFIG_SMALL("raw codec2 demuxer"),
329 .priv_data_size = sizeof(Codec2Context),
330 .read_header = codec2raw_read_header,
331 .read_packet = codec2_read_packet,
332 .read_seek = ff_pcm_read_seek,
333 .flags = AVFMT_GENERIC_INDEX,
334 .raw_codec_id = AV_CODEC_ID_CODEC2,
335 .priv_class = &codec2raw_demux_class,
336 };
337 #endif
338