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