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