/src/ffmpeg/libavformat/mca.c
Line | Count | Source |
1 | | /* |
2 | | * MCA demuxer |
3 | | * Copyright (c) 2020 Zixing Liu |
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/intreadwrite.h" |
23 | | #include "avformat.h" |
24 | | #include "avio_internal.h" |
25 | | #include "demux.h" |
26 | | #include "internal.h" |
27 | | |
28 | | typedef struct MCADemuxContext { |
29 | | uint32_t block_count; |
30 | | uint16_t block_size; |
31 | | uint32_t current_block; |
32 | | uint32_t data_start; |
33 | | uint32_t samples_per_block; |
34 | | } MCADemuxContext; |
35 | | |
36 | | static int probe(const AVProbeData *p) |
37 | 963k | { |
38 | 963k | if (AV_RL32(p->buf) == MKTAG('M', 'A', 'D', 'P') && |
39 | 1.17k | AV_RL16(p->buf + 4) <= 0x5) |
40 | 480 | return AVPROBE_SCORE_MAX / 3 * 2; |
41 | 962k | return 0; |
42 | 963k | } |
43 | | |
44 | | static int read_header(AVFormatContext *s) |
45 | 1.99k | { |
46 | 1.99k | AVStream *st; |
47 | 1.99k | MCADemuxContext *m = s->priv_data; |
48 | 1.99k | AVCodecParameters *par; |
49 | 1.99k | int64_t file_size = avio_size(s->pb); |
50 | 1.99k | uint16_t version = 0; |
51 | 1.99k | uint32_t header_size, data_size, data_offset, loop_start, loop_end, |
52 | 1.99k | nb_samples, nb_metadata, coef_offset = 0; |
53 | 1.99k | int ch, ret; |
54 | 1.99k | int64_t ret_size; |
55 | | |
56 | 1.99k | st = avformat_new_stream(s, NULL); |
57 | 1.99k | if (!st) |
58 | 0 | return AVERROR(ENOMEM); |
59 | 1.99k | par = st->codecpar; |
60 | 1.99k | par->codec_type = AVMEDIA_TYPE_AUDIO; |
61 | | |
62 | | // parse file headers |
63 | 1.99k | avio_skip(s->pb, 0x4); // skip the file magic |
64 | 1.99k | version = avio_rl16(s->pb); |
65 | 1.99k | avio_skip(s->pb, 0x2); // padding |
66 | 1.99k | par->ch_layout.nb_channels = avio_r8(s->pb); |
67 | 1.99k | avio_skip(s->pb, 0x1); // padding |
68 | 1.99k | m->block_size = avio_rl16(s->pb); |
69 | 1.99k | nb_samples = avio_rl32(s->pb); |
70 | 1.99k | par->sample_rate = avio_rl32(s->pb); |
71 | 1.99k | loop_start = avio_rl32(s->pb); |
72 | 1.99k | loop_end = avio_rl32(s->pb); |
73 | 1.99k | header_size = avio_rl32(s->pb); |
74 | 1.99k | data_size = avio_rl32(s->pb); |
75 | 1.99k | avio_skip(s->pb, 0x4); |
76 | 1.99k | nb_metadata = avio_rl16(s->pb); |
77 | 1.99k | avio_skip(s->pb, 0x2); // unknown u16 field |
78 | | |
79 | | // samples per frame = 14; frame size = 8 (2^3) |
80 | 1.99k | m->samples_per_block = (m->block_size * 14) >> 3; |
81 | | |
82 | 1.99k | if (m->samples_per_block < 1) |
83 | 115 | return AVERROR_INVALIDDATA; |
84 | | |
85 | 1.88k | m->block_count = nb_samples / m->samples_per_block; |
86 | 1.88k | st->duration = nb_samples; |
87 | | |
88 | | // sanity checks |
89 | 1.88k | if (!par->ch_layout.nb_channels || par->sample_rate <= 0 |
90 | 1.78k | || loop_start > loop_end || m->block_count < 1) |
91 | 165 | return AVERROR_INVALIDDATA; |
92 | 1.71k | if ((ret = av_dict_set_int(&s->metadata, "loop_start", |
93 | 1.71k | av_rescale(loop_start, AV_TIME_BASE, |
94 | 1.71k | par->sample_rate), 0)) < 0) |
95 | 0 | return ret; |
96 | 1.71k | if ((ret = av_dict_set_int(&s->metadata, "loop_end", |
97 | 1.71k | av_rescale(loop_end, AV_TIME_BASE, |
98 | 1.71k | par->sample_rate), 0)) < 0) |
99 | 0 | return ret; |
100 | 1.71k | if ((32 + 4 + m->block_size) > (INT_MAX / par->ch_layout.nb_channels) || |
101 | 1.71k | (32 + 4 + m->block_size) * par->ch_layout.nb_channels > INT_MAX - 8) |
102 | 0 | return AVERROR_INVALIDDATA; |
103 | 1.71k | avpriv_set_pts_info(st, 64, 1, par->sample_rate); |
104 | | |
105 | 1.71k | if (version <= 4) { |
106 | | // version <= 4 needs to use the file size to calculate the offsets |
107 | 243 | if (file_size < 0) { |
108 | 11 | return AVERROR_INVALIDDATA; |
109 | 11 | } |
110 | 232 | if (file_size - data_size > UINT32_MAX) |
111 | 44 | return AVERROR_INVALIDDATA; |
112 | 188 | m->data_start = file_size - data_size; |
113 | 188 | if (version <= 3) { |
114 | 112 | nb_metadata = 0; |
115 | | // header_size is not available or incorrect in older versions |
116 | 112 | header_size = m->data_start; |
117 | 112 | } |
118 | 1.47k | } else if (version == 5) { |
119 | | // read data_start location from the header |
120 | 1.33k | if (0x30 * par->ch_layout.nb_channels + 0x4 > header_size) |
121 | 42 | return AVERROR_INVALIDDATA; |
122 | 1.28k | data_offset = header_size - 0x30 * par->ch_layout.nb_channels - 0x4; |
123 | 1.28k | if ((ret_size = avio_seek(s->pb, data_offset, SEEK_SET)) < 0) |
124 | 105 | return ret_size; |
125 | 1.18k | m->data_start = avio_rl32(s->pb); |
126 | | // check if the metadata is reasonable |
127 | 1.18k | if (file_size > 0 && (int64_t)m->data_start + data_size > file_size) { |
128 | | // the header is broken beyond repair |
129 | 184 | if ((int64_t)header_size + data_size > file_size) { |
130 | 42 | av_log(s, AV_LOG_ERROR, |
131 | 42 | "MCA metadata corrupted, unable to determine the data offset.\n"); |
132 | 42 | return AVERROR_INVALIDDATA; |
133 | 42 | } |
134 | | // recover the data_start information from the data size |
135 | 142 | av_log(s, AV_LOG_WARNING, |
136 | 142 | "Incorrect header size found in metadata, " |
137 | 142 | "header size approximated from the data size\n"); |
138 | 142 | if (file_size - data_offset > UINT32_MAX) |
139 | 2 | return AVERROR_INVALIDDATA; |
140 | 140 | m->data_start = file_size - data_size; |
141 | 140 | } |
142 | 1.18k | } else { |
143 | 144 | avpriv_request_sample(s, "version %d", version); |
144 | 144 | return AVERROR_PATCHWELCOME; |
145 | 144 | } |
146 | | |
147 | | // coefficient alignment = 0x30; metadata size = 0x14 |
148 | 1.32k | if (0x30 * par->ch_layout.nb_channels + nb_metadata * 0x14 > header_size) |
149 | 27 | return AVERROR_INVALIDDATA; |
150 | 1.30k | coef_offset = |
151 | 1.30k | header_size - 0x30 * par->ch_layout.nb_channels + nb_metadata * 0x14; |
152 | | |
153 | 1.30k | st->start_time = 0; |
154 | 1.30k | par->codec_id = AV_CODEC_ID_ADPCM_THP_LE; |
155 | | |
156 | 1.30k | ret = ff_alloc_extradata(st->codecpar, 32 * par->ch_layout.nb_channels); |
157 | 1.30k | if (ret < 0) |
158 | 0 | return ret; |
159 | | |
160 | 1.30k | if ((ret_size = avio_seek(s->pb, coef_offset, SEEK_SET)) < 0) |
161 | 86 | return ret_size; |
162 | 41.7k | for (ch = 0; ch < par->ch_layout.nb_channels; ch++) { |
163 | 40.7k | if ((ret = ffio_read_size(s->pb, par->extradata + ch * 32, 32)) < 0) |
164 | 228 | return ret; |
165 | | // 0x30 (alignment) - 0x20 (actual size, 32) = 0x10 (padding) |
166 | 40.5k | avio_skip(s->pb, 0x10); |
167 | 40.5k | } |
168 | | |
169 | | // seek to the beginning of the adpcm data |
170 | | // there are some files where the adpcm audio data is not immediately after the header |
171 | 986 | if ((ret_size = avio_seek(s->pb, m->data_start, SEEK_SET)) < 0) |
172 | 65 | return ret_size; |
173 | | |
174 | 921 | return 0; |
175 | 986 | } |
176 | | |
177 | | static int read_packet(AVFormatContext *s, AVPacket *pkt) |
178 | 76.0k | { |
179 | 76.0k | AVCodecParameters *par = s->streams[0]->codecpar; |
180 | 76.0k | MCADemuxContext *m = s->priv_data; |
181 | 76.0k | uint16_t size = m->block_size; |
182 | 76.0k | uint32_t samples = m->samples_per_block; |
183 | 76.0k | int ret = 0; |
184 | | |
185 | 76.0k | if (avio_feof(s->pb)) |
186 | 1.17k | return AVERROR_EOF; |
187 | 74.8k | m->current_block++; |
188 | 74.8k | if (m->current_block > m->block_count) |
189 | 11 | return AVERROR_EOF; |
190 | | |
191 | 74.8k | if ((ret = av_get_packet(s->pb, pkt, size * par->ch_layout.nb_channels)) < 0) |
192 | 366 | return ret; |
193 | 74.4k | pkt->duration = samples; |
194 | 74.4k | pkt->stream_index = 0; |
195 | | |
196 | 74.4k | return 0; |
197 | 74.8k | } |
198 | | |
199 | | static int read_seek(AVFormatContext *s, int stream_index, |
200 | | int64_t timestamp, int flags) |
201 | 0 | { |
202 | 0 | AVStream *st = s->streams[stream_index]; |
203 | 0 | MCADemuxContext *m = s->priv_data; |
204 | 0 | int64_t ret = 0; |
205 | |
|
206 | 0 | if (timestamp < 0) |
207 | 0 | timestamp = 0; |
208 | 0 | timestamp /= m->samples_per_block; |
209 | 0 | if (timestamp >= m->block_count) |
210 | 0 | timestamp = m->block_count - 1; |
211 | 0 | ret = avio_seek(s->pb, m->data_start + timestamp * m->block_size * |
212 | 0 | st->codecpar->ch_layout.nb_channels, SEEK_SET); |
213 | 0 | if (ret < 0) |
214 | 0 | return ret; |
215 | | |
216 | 0 | m->current_block = timestamp; |
217 | 0 | avpriv_update_cur_dts(s, st, timestamp * m->samples_per_block); |
218 | 0 | return 0; |
219 | 0 | } |
220 | | |
221 | | const FFInputFormat ff_mca_demuxer = { |
222 | | .p.name = "mca", |
223 | | .p.long_name = NULL_IF_CONFIG_SMALL("MCA Audio Format"), |
224 | | .p.extensions = "mca", |
225 | | .priv_data_size = sizeof(MCADemuxContext), |
226 | | .read_probe = probe, |
227 | | .read_header = read_header, |
228 | | .read_packet = read_packet, |
229 | | .read_seek = read_seek, |
230 | | }; |