Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Core Audio Format muxer | ||
3 | * Copyright (c) 2011 Carl Eugen Hoyos | ||
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 "avformat.h" | ||
23 | #include "caf.h" | ||
24 | #include "isom.h" | ||
25 | #include "avio_internal.h" | ||
26 | #include "mux.h" | ||
27 | #include "libavutil/intfloat.h" | ||
28 | #include "libavutil/dict.h" | ||
29 | #include "libavutil/mem.h" | ||
30 | |||
31 | #define FRAME_SIZE_OFFSET 40 | ||
32 | |||
33 | typedef struct { | ||
34 | int64_t data; | ||
35 | int size_buffer_size; | ||
36 | int size_entries_used; | ||
37 | int packets; | ||
38 | } CAFContext; | ||
39 | |||
40 | 7 | static uint32_t codec_flags(enum AVCodecID codec_id) { | |
41 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
7 | switch (codec_id) { |
42 | ✗ | case AV_CODEC_ID_PCM_F32BE: | |
43 | case AV_CODEC_ID_PCM_F64BE: | ||
44 | ✗ | return 1; //< kCAFLinearPCMFormatFlagIsFloat | |
45 | 1 | case AV_CODEC_ID_PCM_S16LE: | |
46 | case AV_CODEC_ID_PCM_S24LE: | ||
47 | case AV_CODEC_ID_PCM_S32LE: | ||
48 | 1 | return 2; //< kCAFLinearPCMFormatFlagIsLittleEndian | |
49 | ✗ | case AV_CODEC_ID_PCM_F32LE: | |
50 | case AV_CODEC_ID_PCM_F64LE: | ||
51 | ✗ | return 3; //< kCAFLinearPCMFormatFlagIsFloat | kCAFLinearPCMFormatFlagIsLittleEndian | |
52 | 6 | default: | |
53 | 6 | return 0; | |
54 | } | ||
55 | } | ||
56 | |||
57 | 9 | static uint32_t samples_per_packet(const AVCodecParameters *par) { | |
58 | 9 | enum AVCodecID codec_id = par->codec_id; | |
59 | 9 | int channels = par->ch_layout.nb_channels, block_align = par->block_align; | |
60 | 9 | int frame_size = par->frame_size, sample_rate = par->sample_rate; | |
61 | |||
62 |
5/14✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
9 | switch (codec_id) { |
63 | 3 | case AV_CODEC_ID_PCM_S8: | |
64 | case AV_CODEC_ID_PCM_S16LE: | ||
65 | case AV_CODEC_ID_PCM_S16BE: | ||
66 | case AV_CODEC_ID_PCM_S24LE: | ||
67 | case AV_CODEC_ID_PCM_S24BE: | ||
68 | case AV_CODEC_ID_PCM_S32LE: | ||
69 | case AV_CODEC_ID_PCM_S32BE: | ||
70 | case AV_CODEC_ID_PCM_F32LE: | ||
71 | case AV_CODEC_ID_PCM_F32BE: | ||
72 | case AV_CODEC_ID_PCM_F64LE: | ||
73 | case AV_CODEC_ID_PCM_F64BE: | ||
74 | case AV_CODEC_ID_PCM_ALAW: | ||
75 | case AV_CODEC_ID_PCM_MULAW: | ||
76 | 3 | return 1; | |
77 | 1 | case AV_CODEC_ID_MACE3: | |
78 | case AV_CODEC_ID_MACE6: | ||
79 | 1 | return 6; | |
80 | ✗ | case AV_CODEC_ID_ADPCM_IMA_QT: | |
81 | ✗ | return 64; | |
82 | 2 | case AV_CODEC_ID_AMR_NB: | |
83 | case AV_CODEC_ID_GSM: | ||
84 | case AV_CODEC_ID_ILBC: | ||
85 | case AV_CODEC_ID_QCELP: | ||
86 | 2 | return 160; | |
87 | ✗ | case AV_CODEC_ID_GSM_MS: | |
88 | ✗ | return 320; | |
89 | ✗ | case AV_CODEC_ID_MP1: | |
90 | ✗ | return 384; | |
91 | ✗ | case AV_CODEC_ID_OPUS: | |
92 | ✗ | return frame_size * 48000 / sample_rate; | |
93 | ✗ | case AV_CODEC_ID_MP2: | |
94 | case AV_CODEC_ID_MP3: | ||
95 | ✗ | return 1152; | |
96 | ✗ | case AV_CODEC_ID_AC3: | |
97 | ✗ | return 1536; | |
98 | 1 | case AV_CODEC_ID_QDM2: | |
99 | case AV_CODEC_ID_QDMC: | ||
100 | 1 | return 2048 * channels; | |
101 | 2 | case AV_CODEC_ID_ALAC: | |
102 | 2 | return 4096; | |
103 | ✗ | case AV_CODEC_ID_ADPCM_IMA_WAV: | |
104 | ✗ | return (block_align - 4 * channels) * 8 / (4 * channels) + 1; | |
105 | ✗ | case AV_CODEC_ID_ADPCM_MS: | |
106 | ✗ | return (block_align - 7 * channels) * 2 / channels + 2; | |
107 | ✗ | default: | |
108 | ✗ | return 0; | |
109 | } | ||
110 | } | ||
111 | |||
112 | 7 | static int caf_write_header(AVFormatContext *s) | |
113 | { | ||
114 | 7 | AVIOContext *pb = s->pb; | |
115 | 7 | AVCodecParameters *par = s->streams[0]->codecpar; | |
116 | 7 | CAFContext *caf = s->priv_data; | |
117 | 7 | const AVDictionaryEntry *t = NULL; | |
118 | 7 | unsigned int codec_tag = ff_codec_get_tag(ff_codec_caf_tags, par->codec_id); | |
119 | 7 | int64_t chunk_size = 0; | |
120 | 7 | int frame_size = par->frame_size, sample_rate = par->sample_rate; | |
121 | |||
122 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | switch (par->codec_id) { |
123 | ✗ | case AV_CODEC_ID_AAC: | |
124 | ✗ | av_log(s, AV_LOG_ERROR, "muxing codec currently unsupported\n"); | |
125 | ✗ | return AVERROR_PATCHWELCOME; | |
126 | } | ||
127 | |||
128 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
7 | if (par->codec_id == AV_CODEC_ID_OPUS && par->ch_layout.nb_channels > 2) { |
129 | ✗ | av_log(s, AV_LOG_ERROR, "Only mono and stereo are supported for Opus\n"); | |
130 | ✗ | return AVERROR_INVALIDDATA; | |
131 | } | ||
132 | |||
133 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (!codec_tag) { |
134 | ✗ | av_log(s, AV_LOG_ERROR, "unsupported codec\n"); | |
135 | ✗ | return AVERROR_INVALIDDATA; | |
136 | } | ||
137 | |||
138 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
7 | if (!par->block_align && !(pb->seekable & AVIO_SEEKABLE_NORMAL)) { |
139 | ✗ | av_log(s, AV_LOG_ERROR, "Muxing variable packet size not supported on non seekable output\n"); | |
140 | ✗ | return AVERROR_INVALIDDATA; | |
141 | } | ||
142 | |||
143 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
7 | if (par->codec_id != AV_CODEC_ID_MP3 || frame_size != 576) |
144 | 7 | frame_size = samples_per_packet(par); | |
145 | |||
146 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (par->codec_id == AV_CODEC_ID_OPUS) |
147 | ✗ | sample_rate = 48000; | |
148 | |||
149 | 7 | ffio_wfourcc(pb, "caff"); //< mFileType | |
150 | 7 | avio_wb16(pb, 1); //< mFileVersion | |
151 | 7 | avio_wb16(pb, 0); //< mFileFlags | |
152 | |||
153 | 7 | ffio_wfourcc(pb, "desc"); //< Audio Description chunk | |
154 | 7 | avio_wb64(pb, 32); //< mChunkSize | |
155 | 7 | avio_wb64(pb, av_double2int(sample_rate)); //< mSampleRate | |
156 | 7 | avio_wl32(pb, codec_tag); //< mFormatID | |
157 | 7 | avio_wb32(pb, codec_flags(par->codec_id)); //< mFormatFlags | |
158 | 7 | avio_wb32(pb, par->block_align); //< mBytesPerPacket | |
159 | 7 | avio_wb32(pb, frame_size); //< mFramesPerPacket | |
160 | 7 | avio_wb32(pb, par->ch_layout.nb_channels); //< mChannelsPerFrame | |
161 | 7 | avio_wb32(pb, av_get_bits_per_sample(par->codec_id)); //< mBitsPerChannel | |
162 | |||
163 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (par->ch_layout.order == AV_CHANNEL_ORDER_NATIVE) { |
164 | 7 | ffio_wfourcc(pb, "chan"); | |
165 | 7 | avio_wb64(pb, 12); | |
166 | 7 | ff_mov_write_chan(pb, par->ch_layout.u.mask); | |
167 | } | ||
168 | |||
169 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
|
7 | if (par->codec_id == AV_CODEC_ID_ALAC) { |
170 | 1 | ffio_wfourcc(pb, "kuki"); | |
171 | 1 | avio_wb64(pb, 12 + par->extradata_size); | |
172 | 1 | avio_write(pb, "\0\0\0\14frmaalac", 12); | |
173 | 1 | avio_write(pb, par->extradata, par->extradata_size); | |
174 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
|
6 | } else if (par->codec_id == AV_CODEC_ID_AMR_NB) { |
175 | 1 | ffio_wfourcc(pb, "kuki"); | |
176 | 1 | avio_wb64(pb, 29); | |
177 | 1 | avio_write(pb, "\0\0\0\14frmasamr", 12); | |
178 | 1 | avio_wb32(pb, 0x11); /* size */ | |
179 | 1 | avio_write(pb, "samrFFMP", 8); | |
180 | 1 | avio_w8(pb, 0); /* decoder version */ | |
181 | |||
182 | 1 | avio_wb16(pb, 0x81FF); /* Mode set (all modes for AMR_NB) */ | |
183 | 1 | avio_w8(pb, 0x00); /* Mode change period (no restriction) */ | |
184 | 1 | avio_w8(pb, 0x01); /* Frames per sample */ | |
185 |
3/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
5 | } else if (par->codec_id == AV_CODEC_ID_QDM2 || par->codec_id == AV_CODEC_ID_QDMC) { |
186 | 1 | ffio_wfourcc(pb, "kuki"); | |
187 | 1 | avio_wb64(pb, par->extradata_size); | |
188 | 1 | avio_write(pb, par->extradata, par->extradata_size); | |
189 | } | ||
190 | |||
191 | 7 | ff_standardize_creation_time(s); | |
192 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 5 times.
|
7 | if (av_dict_count(s->metadata)) { |
193 | 2 | ffio_wfourcc(pb, "info"); //< Information chunk | |
194 |
2/2✓ Branch 1 taken 14 times.
✓ Branch 2 taken 2 times.
|
16 | while ((t = av_dict_iterate(s->metadata, t))) { |
195 | 14 | chunk_size += strlen(t->key) + strlen(t->value) + 2; | |
196 | } | ||
197 | 2 | avio_wb64(pb, chunk_size + 4); | |
198 | 2 | avio_wb32(pb, av_dict_count(s->metadata)); | |
199 | 2 | t = NULL; | |
200 |
2/2✓ Branch 1 taken 14 times.
✓ Branch 2 taken 2 times.
|
16 | while ((t = av_dict_iterate(s->metadata, t))) { |
201 | 14 | avio_put_str(pb, t->key); | |
202 | 14 | avio_put_str(pb, t->value); | |
203 | } | ||
204 | } | ||
205 | |||
206 | 7 | ffio_wfourcc(pb, "data"); //< Audio Data chunk | |
207 | 7 | caf->data = avio_tell(pb); | |
208 | 7 | avio_wb64(pb, -1); //< mChunkSize | |
209 | 7 | avio_wb32(pb, 0); //< mEditCount | |
210 | |||
211 | 7 | return 0; | |
212 | } | ||
213 | |||
214 | 866 | static int caf_write_packet(AVFormatContext *s, AVPacket *pkt) | |
215 | { | ||
216 | 866 | CAFContext *caf = s->priv_data; | |
217 | 866 | AVStream *const st = s->streams[0]; | |
218 | |||
219 |
2/2✓ Branch 0 taken 412 times.
✓ Branch 1 taken 454 times.
|
866 | if (!st->codecpar->block_align) { |
220 | uint8_t *pkt_sizes; | ||
221 | 412 | int i, alloc_size = caf->size_entries_used + 5U; | |
222 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 412 times.
|
412 | if (alloc_size < 0) |
223 | ✗ | return AVERROR(ERANGE); | |
224 | |||
225 | 412 | pkt_sizes = av_fast_realloc(st->priv_data, | |
226 | 412 | &caf->size_buffer_size, | |
227 | alloc_size); | ||
228 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 412 times.
|
412 | if (!pkt_sizes) |
229 | ✗ | return AVERROR(ENOMEM); | |
230 | 412 | st->priv_data = pkt_sizes; | |
231 |
2/2✓ Branch 0 taken 1648 times.
✓ Branch 1 taken 412 times.
|
2060 | for (i = 4; i > 0; i--) { |
232 | 1648 | unsigned top = pkt->size >> i * 7; | |
233 |
2/2✓ Branch 0 taken 127 times.
✓ Branch 1 taken 1521 times.
|
1648 | if (top) |
234 | 127 | pkt_sizes[caf->size_entries_used++] = 128 | top; | |
235 | } | ||
236 | 412 | pkt_sizes[caf->size_entries_used++] = pkt->size & 127; | |
237 | 412 | caf->packets++; | |
238 | } | ||
239 | 866 | avio_write(s->pb, pkt->data, pkt->size); | |
240 | 866 | return 0; | |
241 | } | ||
242 | |||
243 | 7 | static int caf_write_trailer(AVFormatContext *s) | |
244 | { | ||
245 | 7 | CAFContext *caf = s->priv_data; | |
246 | 7 | AVIOContext *pb = s->pb; | |
247 | 7 | AVStream *st = s->streams[0]; | |
248 | 7 | AVCodecParameters *par = st->codecpar; | |
249 | |||
250 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (pb->seekable & AVIO_SEEKABLE_NORMAL) { |
251 | 7 | int64_t file_size = avio_tell(pb); | |
252 | |||
253 | 7 | avio_seek(pb, caf->data, SEEK_SET); | |
254 | 7 | avio_wb64(pb, file_size - caf->data - 8); | |
255 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
|
7 | if (!par->block_align) { |
256 | 2 | int packet_size = samples_per_packet(par); | |
257 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!packet_size) { |
258 | ✗ | packet_size = st->duration / (caf->packets - 1); | |
259 | ✗ | avio_seek(pb, FRAME_SIZE_OFFSET, SEEK_SET); | |
260 | ✗ | avio_wb32(pb, packet_size); | |
261 | } | ||
262 | 2 | avio_seek(pb, file_size, SEEK_SET); | |
263 | 2 | ffio_wfourcc(pb, "pakt"); | |
264 | 2 | avio_wb64(pb, caf->size_entries_used + 24U); | |
265 | 2 | avio_wb64(pb, caf->packets); ///< mNumberPackets | |
266 | 2 | avio_wb64(pb, caf->packets * packet_size); ///< mNumberValidFrames | |
267 | 2 | avio_wb32(pb, 0); ///< mPrimingFrames | |
268 | 2 | avio_wb32(pb, 0); ///< mRemainderFrames | |
269 | 2 | avio_write(pb, st->priv_data, caf->size_entries_used); | |
270 | } | ||
271 | } | ||
272 | 7 | return 0; | |
273 | } | ||
274 | |||
275 | const FFOutputFormat ff_caf_muxer = { | ||
276 | .p.name = "caf", | ||
277 | .p.long_name = NULL_IF_CONFIG_SMALL("Apple CAF (Core Audio Format)"), | ||
278 | .p.mime_type = "audio/x-caf", | ||
279 | .p.extensions = "caf", | ||
280 | .priv_data_size = sizeof(CAFContext), | ||
281 | .p.audio_codec = AV_CODEC_ID_PCM_S16BE, | ||
282 | .p.video_codec = AV_CODEC_ID_NONE, | ||
283 | .p.subtitle_codec = AV_CODEC_ID_NONE, | ||
284 | .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH, | ||
285 | .write_header = caf_write_header, | ||
286 | .write_packet = caf_write_packet, | ||
287 | .write_trailer = caf_write_trailer, | ||
288 | .p.codec_tag = ff_caf_codec_tags_list, | ||
289 | }; | ||
290 |