| 1 | /* |
| 2 | * AIFF/AIFF-C muxer |
| 3 | * Copyright (c) 2006 Patrick Guimond |
| 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 "libavutil/intfloat.h" |
| 25 | #include "libavutil/opt.h" |
| 26 | #include "avformat.h" |
| 27 | #include "internal.h" |
| 28 | #include "aiff.h" |
| 29 | #include "avio_internal.h" |
| 30 | #include "isom.h" |
| 31 | #include "id3v2.h" |
| 32 | |
| 33 | typedef struct { |
| 34 | const AVClass *class; |
| 35 | int64_t form; |
| 36 | int64_t frames; |
| 37 | int64_t ssnd; |
| 38 | int audio_stream_idx; |
| 39 | AVPacketList *pict_list; |
| 40 | int write_id3v2; |
| 41 | int id3v2_version; |
| 42 | } AIFFOutputContext; |
| 43 | |
| 44 | static int put_id3v2_tags(AVFormatContext *s, AIFFOutputContext *aiff) |
| 45 | { |
| 46 | int ret; |
| 47 | uint64_t pos, end, size; |
| 48 | ID3v2EncContext id3v2 = { 0 }; |
| 49 | AVIOContext *pb = s->pb; |
| 50 | AVPacketList *pict_list = aiff->pict_list; |
| 51 | |
| 52 | if (!pb->seekable) |
| 53 | return 0; |
| 54 | |
| 55 | if (!s->metadata && !aiff->pict_list) |
| 56 | return 0; |
| 57 | |
| 58 | avio_wl32(pb, MKTAG('I', 'D', '3', ' ')); |
| 59 | avio_wb32(pb, 0); |
| 60 | pos = avio_tell(pb); |
| 61 | |
| 62 | ff_id3v2_start(&id3v2, pb, aiff->id3v2_version, ID3v2_DEFAULT_MAGIC); |
| 63 | ff_id3v2_write_metadata(s, &id3v2); |
| 64 | while (pict_list) { |
| 65 | if ((ret = ff_id3v2_write_apic(s, &id3v2, &pict_list->pkt)) < 0) |
| 66 | return ret; |
| 67 | pict_list = pict_list->next; |
| 68 | } |
| 69 | ff_id3v2_finish(&id3v2, pb, s->metadata_header_padding); |
| 70 | |
| 71 | end = avio_tell(pb); |
| 72 | size = end - pos; |
| 73 | |
| 74 | /* Update chunk size */ |
| 75 | avio_seek(pb, pos - 4, SEEK_SET); |
| 76 | avio_wb32(pb, size); |
| 77 | avio_seek(pb, end, SEEK_SET); |
| 78 | |
| 79 | if (size & 1) |
| 80 | avio_w8(pb, 0); |
| 81 | |
| 82 | return 0; |
| 83 | } |
| 84 | |
| 85 | static void put_meta(AVFormatContext *s, const char *key, uint32_t id) |
| 86 | { |
| 87 | AVDictionaryEntry *tag; |
| 88 | AVIOContext *pb = s->pb; |
| 89 | |
| 90 | if (tag = av_dict_get(s->metadata, key, NULL, 0)) { |
| 91 | int size = strlen(tag->value); |
| 92 | |
| 93 | avio_wl32(pb, id); |
| 94 | avio_wb32(pb, FFALIGN(size, 2)); |
| 95 | avio_write(pb, tag->value, size); |
| 96 | if (size & 1) |
| 97 | avio_w8(pb, 0); |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | static int aiff_write_header(AVFormatContext *s) |
| 102 | { |
| 103 | AIFFOutputContext *aiff = s->priv_data; |
| 104 | AVIOContext *pb = s->pb; |
| 105 | AVCodecContext *enc; |
| 106 | uint64_t sample_rate; |
| 107 | int i, aifc = 0; |
| 108 | |
| 109 | aiff->audio_stream_idx = -1; |
| 110 | for (i = 0; i < s->nb_streams; i++) { |
| 111 | AVStream *st = s->streams[i]; |
| 112 | if (aiff->audio_stream_idx < 0 && st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { |
| 113 | aiff->audio_stream_idx = i; |
| 114 | } else if (st->codec->codec_type != AVMEDIA_TYPE_VIDEO) { |
| 115 | av_log(s, AV_LOG_ERROR, "Only audio streams and pictures are allowed in AIFF.\n"); |
| 116 | return AVERROR(EINVAL); |
| 117 | } |
| 118 | } |
| 119 | if (aiff->audio_stream_idx < 0) { |
| 120 | av_log(s, AV_LOG_ERROR, "No audio stream present.\n"); |
| 121 | return AVERROR(EINVAL); |
| 122 | } |
| 123 | |
| 124 | enc = s->streams[aiff->audio_stream_idx]->codec; |
| 125 | |
| 126 | /* First verify if format is ok */ |
| 127 | if (!enc->codec_tag) |
| 128 | return -1; |
| 129 | if (enc->codec_tag != MKTAG('N','O','N','E')) |
| 130 | aifc = 1; |
| 131 | |
| 132 | /* FORM AIFF header */ |
| 133 | ffio_wfourcc(pb, "FORM"); |
| 134 | aiff->form = avio_tell(pb); |
| 135 | avio_wb32(pb, 0); /* file length */ |
| 136 | ffio_wfourcc(pb, aifc ? "AIFC" : "AIFF"); |
| 137 | |
| 138 | if (aifc) { // compressed audio |
| 139 | if (!enc->block_align) { |
| 140 | av_log(s, AV_LOG_ERROR, "block align not set\n"); |
| 141 | return -1; |
| 142 | } |
| 143 | /* Version chunk */ |
| 144 | ffio_wfourcc(pb, "FVER"); |
| 145 | avio_wb32(pb, 4); |
| 146 | avio_wb32(pb, 0xA2805140); |
| 147 | } |
| 148 | |
| 149 | if (enc->channels > 2 && enc->channel_layout) { |
| 150 | ffio_wfourcc(pb, "CHAN"); |
| 151 | avio_wb32(pb, 12); |
| 152 | ff_mov_write_chan(pb, enc->channel_layout); |
| 153 | } |
| 154 | |
| 155 | put_meta(s, "title", MKTAG('N', 'A', 'M', 'E')); |
| 156 | put_meta(s, "author", MKTAG('A', 'U', 'T', 'H')); |
| 157 | put_meta(s, "copyright", MKTAG('(', 'c', ')', ' ')); |
| 158 | put_meta(s, "comment", MKTAG('A', 'N', 'N', 'O')); |
| 159 | |
| 160 | /* Common chunk */ |
| 161 | ffio_wfourcc(pb, "COMM"); |
| 162 | avio_wb32(pb, aifc ? 24 : 18); /* size */ |
| 163 | avio_wb16(pb, enc->channels); /* Number of channels */ |
| 164 | |
| 165 | aiff->frames = avio_tell(pb); |
| 166 | avio_wb32(pb, 0); /* Number of frames */ |
| 167 | |
| 168 | if (!enc->bits_per_coded_sample) |
| 169 | enc->bits_per_coded_sample = av_get_bits_per_sample(enc->codec_id); |
| 170 | if (!enc->bits_per_coded_sample) { |
| 171 | av_log(s, AV_LOG_ERROR, "could not compute bits per sample\n"); |
| 172 | return -1; |
| 173 | } |
| 174 | if (!enc->block_align) |
| 175 | enc->block_align = (enc->bits_per_coded_sample * enc->channels) >> 3; |
| 176 | |
| 177 | avio_wb16(pb, enc->bits_per_coded_sample); /* Sample size */ |
| 178 | |
| 179 | sample_rate = av_double2int(enc->sample_rate); |
| 180 | avio_wb16(pb, (sample_rate >> 52) + (16383 - 1023)); |
| 181 | avio_wb64(pb, UINT64_C(1) << 63 | sample_rate << 11); |
| 182 | |
| 183 | if (aifc) { |
| 184 | avio_wl32(pb, enc->codec_tag); |
| 185 | avio_wb16(pb, 0); |
| 186 | } |
| 187 | |
| 188 | if (enc->codec_tag == MKTAG('Q','D','M','2') && enc->extradata_size) { |
| 189 | ffio_wfourcc(pb, "wave"); |
| 190 | avio_wb32(pb, enc->extradata_size); |
| 191 | avio_write(pb, enc->extradata, enc->extradata_size); |
| 192 | } |
| 193 | |
| 194 | /* Sound data chunk */ |
| 195 | ffio_wfourcc(pb, "SSND"); |
| 196 | aiff->ssnd = avio_tell(pb); /* Sound chunk size */ |
| 197 | avio_wb32(pb, 0); /* Sound samples data size */ |
| 198 | avio_wb32(pb, 0); /* Data offset */ |
| 199 | avio_wb32(pb, 0); /* Block-size (block align) */ |
| 200 | |
| 201 | avpriv_set_pts_info(s->streams[aiff->audio_stream_idx], 64, 1, |
| 202 | s->streams[aiff->audio_stream_idx]->codec->sample_rate); |
| 203 | |
| 204 | /* Data is starting here */ |
| 205 | avio_flush(pb); |
| 206 | |
| 207 | return 0; |
| 208 | } |
| 209 | |
| 210 | static int aiff_write_packet(AVFormatContext *s, AVPacket *pkt) |
| 211 | { |
| 212 | AIFFOutputContext *aiff = s->priv_data; |
| 213 | AVIOContext *pb = s->pb; |
| 214 | if (pkt->stream_index == aiff->audio_stream_idx) |
| 215 | avio_write(pb, pkt->data, pkt->size); |
| 216 | else { |
| 217 | int ret; |
| 218 | AVPacketList *pict_list, *last; |
| 219 | |
| 220 | if (s->streams[pkt->stream_index]->codec->codec_type != AVMEDIA_TYPE_VIDEO) |
| 221 | return 0; |
| 222 | |
| 223 | /* warn only once for each stream */ |
| 224 | if (s->streams[pkt->stream_index]->nb_frames == 1) { |
| 225 | av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d," |
| 226 | " ignoring.\n", pkt->stream_index); |
| 227 | } |
| 228 | if (s->streams[pkt->stream_index]->nb_frames >= 1) |
| 229 | return 0; |
| 230 | |
| 231 | pict_list = av_mallocz(sizeof(AVPacketList)); |
| 232 | if (!pict_list) |
| 233 | return AVERROR(ENOMEM); |
| 234 | |
| 235 | if ((ret = av_copy_packet(&pict_list->pkt, pkt)) < 0) { |
| 236 | av_freep(&pict_list); |
| 237 | return ret; |
| 238 | } |
| 239 | |
| 240 | if (!aiff->pict_list) |
| 241 | aiff->pict_list = pict_list; |
| 242 | else { |
| 243 | last = aiff->pict_list; |
| 244 | while (last->next) |
| 245 | last = last->next; |
| 246 | last->next = pict_list; |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | return 0; |
| 251 | } |
| 252 | |
| 253 | static int aiff_write_trailer(AVFormatContext *s) |
| 254 | { |
| 255 | int ret; |
| 256 | AVIOContext *pb = s->pb; |
| 257 | AIFFOutputContext *aiff = s->priv_data; |
| 258 | AVPacketList *pict_list = aiff->pict_list; |
| 259 | AVCodecContext *enc = s->streams[aiff->audio_stream_idx]->codec; |
| 260 | |
| 261 | /* Chunks sizes must be even */ |
| 262 | int64_t file_size, end_size; |
| 263 | end_size = file_size = avio_tell(pb); |
| 264 | if (file_size & 1) { |
| 265 | avio_w8(pb, 0); |
| 266 | end_size++; |
| 267 | } |
| 268 | |
| 269 | if (s->pb->seekable) { |
| 270 | /* Number of sample frames */ |
| 271 | avio_seek(pb, aiff->frames, SEEK_SET); |
| 272 | avio_wb32(pb, (file_size-aiff->ssnd-12)/enc->block_align); |
| 273 | |
| 274 | /* Sound Data chunk size */ |
| 275 | avio_seek(pb, aiff->ssnd, SEEK_SET); |
| 276 | avio_wb32(pb, file_size - aiff->ssnd - 4); |
| 277 | |
| 278 | /* return to the end */ |
| 279 | avio_seek(pb, end_size, SEEK_SET); |
| 280 | |
| 281 | /* Write ID3 tags */ |
| 282 | if (aiff->write_id3v2) |
| 283 | if ((ret = put_id3v2_tags(s, aiff)) < 0) |
| 284 | return ret; |
| 285 | |
| 286 | /* File length */ |
| 287 | file_size = avio_tell(pb); |
| 288 | avio_seek(pb, aiff->form, SEEK_SET); |
| 289 | avio_wb32(pb, file_size - aiff->form - 4); |
| 290 | |
| 291 | avio_flush(pb); |
| 292 | } |
| 293 | |
| 294 | while (pict_list) { |
| 295 | AVPacketList *next = pict_list->next; |
| 296 | av_free_packet(&pict_list->pkt); |
| 297 | av_freep(&pict_list); |
| 298 | pict_list = next; |
| 299 | } |
| 300 | |
| 301 | return 0; |
| 302 | } |
| 303 | |
| 304 | #define OFFSET(x) offsetof(AIFFOutputContext, x) |
| 305 | #define ENC AV_OPT_FLAG_ENCODING_PARAM |
| 306 | static const AVOption options[] = { |
| 307 | { "write_id3v2", "Enable ID3 tags writing.", |
| 308 | OFFSET(write_id3v2), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, ENC }, |
| 309 | { "id3v2_version", "Select ID3v2 version to write. Currently 3 and 4 are supported.", |
| 310 | OFFSET(id3v2_version), AV_OPT_TYPE_INT, {.i64 = 4}, 3, 4, ENC }, |
| 311 | { NULL }, |
| 312 | }; |
| 313 | |
| 314 | static const AVClass aiff_muxer_class = { |
| 315 | .class_name = "AIFF muxer", |
| 316 | .item_name = av_default_item_name, |
| 317 | .option = options, |
| 318 | .version = LIBAVUTIL_VERSION_INT, |
| 319 | }; |
| 320 | |
| 321 | AVOutputFormat ff_aiff_muxer = { |
| 322 | .name = "aiff", |
| 323 | .long_name = NULL_IF_CONFIG_SMALL("Audio IFF"), |
| 324 | .mime_type = "audio/aiff", |
| 325 | .extensions = "aif,aiff,afc,aifc", |
| 326 | .priv_data_size = sizeof(AIFFOutputContext), |
| 327 | .audio_codec = AV_CODEC_ID_PCM_S16BE, |
| 328 | .video_codec = AV_CODEC_ID_PNG, |
| 329 | .write_header = aiff_write_header, |
| 330 | .write_packet = aiff_write_packet, |
| 331 | .write_trailer = aiff_write_trailer, |
| 332 | .codec_tag = (const AVCodecTag* const []){ ff_codec_aiff_tags, 0 }, |
| 333 | .priv_class = &aiff_muxer_class, |
| 334 | }; |