1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * raw FLAC muxer
3cabdff1aSopenharmony_ci * Copyright (c) 2006-2009 Justin Ruggles
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * This file is part of FFmpeg.
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
11cabdff1aSopenharmony_ci *
12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15cabdff1aSopenharmony_ci * Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci
22cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
23cabdff1aSopenharmony_ci#include "libavutil/channel_layout.h"
24cabdff1aSopenharmony_ci#include "libavutil/opt.h"
25cabdff1aSopenharmony_ci#include "libavutil/pixdesc.h"
26cabdff1aSopenharmony_ci#include "libavcodec/flac.h"
27cabdff1aSopenharmony_ci#include "libavcodec/packet_internal.h"
28cabdff1aSopenharmony_ci#include "avformat.h"
29cabdff1aSopenharmony_ci#include "avio_internal.h"
30cabdff1aSopenharmony_ci#include "flacenc.h"
31cabdff1aSopenharmony_ci#include "id3v2.h"
32cabdff1aSopenharmony_ci#include "internal.h"
33cabdff1aSopenharmony_ci#include "version.h"
34cabdff1aSopenharmony_ci#include "vorbiscomment.h"
35cabdff1aSopenharmony_ci
36cabdff1aSopenharmony_ci
37cabdff1aSopenharmony_citypedef struct FlacMuxerContext {
38cabdff1aSopenharmony_ci    const AVClass *class;
39cabdff1aSopenharmony_ci    int write_header;
40cabdff1aSopenharmony_ci
41cabdff1aSopenharmony_ci    int audio_stream_idx;
42cabdff1aSopenharmony_ci    int waiting_pics;
43cabdff1aSopenharmony_ci    /* audio packets are queued here until we get all the attached pictures */
44cabdff1aSopenharmony_ci    PacketList queue;
45cabdff1aSopenharmony_ci
46cabdff1aSopenharmony_ci    /* updated streaminfo sent by the encoder at the end */
47cabdff1aSopenharmony_ci    uint8_t streaminfo[FLAC_STREAMINFO_SIZE];
48cabdff1aSopenharmony_ci    int updated_streaminfo;
49cabdff1aSopenharmony_ci
50cabdff1aSopenharmony_ci    unsigned attached_types;
51cabdff1aSopenharmony_ci} FlacMuxerContext;
52cabdff1aSopenharmony_ci
53cabdff1aSopenharmony_cistatic int flac_write_block_padding(AVIOContext *pb, unsigned int n_padding_bytes,
54cabdff1aSopenharmony_ci                                    int last_block)
55cabdff1aSopenharmony_ci{
56cabdff1aSopenharmony_ci    avio_w8(pb, last_block ? 0x81 : 0x01);
57cabdff1aSopenharmony_ci    avio_wb24(pb, n_padding_bytes);
58cabdff1aSopenharmony_ci    ffio_fill(pb, 0, n_padding_bytes);
59cabdff1aSopenharmony_ci    return 0;
60cabdff1aSopenharmony_ci}
61cabdff1aSopenharmony_ci
62cabdff1aSopenharmony_cistatic int flac_write_block_comment(AVIOContext *pb, AVDictionary **m,
63cabdff1aSopenharmony_ci                                    int last_block, int bitexact)
64cabdff1aSopenharmony_ci{
65cabdff1aSopenharmony_ci    const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT;
66cabdff1aSopenharmony_ci    int64_t len;
67cabdff1aSopenharmony_ci
68cabdff1aSopenharmony_ci    ff_metadata_conv(m, ff_vorbiscomment_metadata_conv, NULL);
69cabdff1aSopenharmony_ci
70cabdff1aSopenharmony_ci    len = ff_vorbiscomment_length(*m, vendor, NULL, 0);
71cabdff1aSopenharmony_ci    if (len >= ((1<<24) - 4))
72cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
73cabdff1aSopenharmony_ci
74cabdff1aSopenharmony_ci    avio_w8(pb, last_block ? 0x84 : 0x04);
75cabdff1aSopenharmony_ci    avio_wb24(pb, len);
76cabdff1aSopenharmony_ci    ff_vorbiscomment_write(pb, *m, vendor, NULL, 0);
77cabdff1aSopenharmony_ci
78cabdff1aSopenharmony_ci    return 0;
79cabdff1aSopenharmony_ci}
80cabdff1aSopenharmony_ci
81cabdff1aSopenharmony_cistatic int flac_write_picture(struct AVFormatContext *s, AVPacket *pkt)
82cabdff1aSopenharmony_ci{
83cabdff1aSopenharmony_ci    FlacMuxerContext *c = s->priv_data;
84cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
85cabdff1aSopenharmony_ci    const AVPixFmtDescriptor *pixdesc;
86cabdff1aSopenharmony_ci    const CodecMime *mime = ff_id3v2_mime_tags;
87cabdff1aSopenharmony_ci    AVDictionaryEntry *e;
88cabdff1aSopenharmony_ci    const char *mimetype = NULL, *desc = "";
89cabdff1aSopenharmony_ci    const AVStream *st = s->streams[pkt->stream_index];
90cabdff1aSopenharmony_ci    int i, mimelen, desclen, type = 0, blocklen;
91cabdff1aSopenharmony_ci
92cabdff1aSopenharmony_ci    if (!pkt->data)
93cabdff1aSopenharmony_ci        return 0;
94cabdff1aSopenharmony_ci
95cabdff1aSopenharmony_ci    while (mime->id != AV_CODEC_ID_NONE) {
96cabdff1aSopenharmony_ci        if (mime->id == st->codecpar->codec_id) {
97cabdff1aSopenharmony_ci            mimetype = mime->str;
98cabdff1aSopenharmony_ci            break;
99cabdff1aSopenharmony_ci        }
100cabdff1aSopenharmony_ci        mime++;
101cabdff1aSopenharmony_ci    }
102cabdff1aSopenharmony_ci    if (!mimetype) {
103cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "No mimetype is known for stream %d, cannot "
104cabdff1aSopenharmony_ci               "write an attached picture.\n", st->index);
105cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
106cabdff1aSopenharmony_ci    }
107cabdff1aSopenharmony_ci    mimelen = strlen(mimetype);
108cabdff1aSopenharmony_ci
109cabdff1aSopenharmony_ci    /* get the picture type */
110cabdff1aSopenharmony_ci    e = av_dict_get(st->metadata, "comment", NULL, 0);
111cabdff1aSopenharmony_ci    for (i = 0; e && i < FF_ARRAY_ELEMS(ff_id3v2_picture_types); i++) {
112cabdff1aSopenharmony_ci        if (!av_strcasecmp(e->value, ff_id3v2_picture_types[i])) {
113cabdff1aSopenharmony_ci            type = i;
114cabdff1aSopenharmony_ci            break;
115cabdff1aSopenharmony_ci        }
116cabdff1aSopenharmony_ci    }
117cabdff1aSopenharmony_ci
118cabdff1aSopenharmony_ci    if ((c->attached_types & (1 << type)) & 0x6) {
119cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Duplicate attachment for type '%s'\n", ff_id3v2_picture_types[type]);
120cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
121cabdff1aSopenharmony_ci    }
122cabdff1aSopenharmony_ci
123cabdff1aSopenharmony_ci    if (type == 1 && (st->codecpar->codec_id != AV_CODEC_ID_PNG ||
124cabdff1aSopenharmony_ci                      st->codecpar->width != 32 ||
125cabdff1aSopenharmony_ci                      st->codecpar->height != 32)) {
126cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "File icon attachment must be a 32x32 PNG");
127cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
128cabdff1aSopenharmony_ci    }
129cabdff1aSopenharmony_ci
130cabdff1aSopenharmony_ci    c->attached_types |= (1 << type);
131cabdff1aSopenharmony_ci
132cabdff1aSopenharmony_ci    /* get the description */
133cabdff1aSopenharmony_ci    if ((e = av_dict_get(st->metadata, "title", NULL, 0)))
134cabdff1aSopenharmony_ci        desc = e->value;
135cabdff1aSopenharmony_ci    desclen = strlen(desc);
136cabdff1aSopenharmony_ci
137cabdff1aSopenharmony_ci    blocklen = 4 + 4 + mimelen + 4 + desclen + 4 + 4 + 4 + 4 + 4 + pkt->size;
138cabdff1aSopenharmony_ci    if (blocklen >= 1<<24) {
139cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Picture block too big %d >= %d\n", blocklen, 1<<24);
140cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
141cabdff1aSopenharmony_ci    }
142cabdff1aSopenharmony_ci
143cabdff1aSopenharmony_ci    avio_w8(pb, 0x06);
144cabdff1aSopenharmony_ci    avio_wb24(pb, blocklen);
145cabdff1aSopenharmony_ci
146cabdff1aSopenharmony_ci    avio_wb32(pb, type);
147cabdff1aSopenharmony_ci
148cabdff1aSopenharmony_ci    avio_wb32(pb, mimelen);
149cabdff1aSopenharmony_ci    avio_write(pb, mimetype, mimelen);
150cabdff1aSopenharmony_ci
151cabdff1aSopenharmony_ci    avio_wb32(pb, desclen);
152cabdff1aSopenharmony_ci    avio_write(pb, desc, desclen);
153cabdff1aSopenharmony_ci
154cabdff1aSopenharmony_ci    avio_wb32(pb, st->codecpar->width);
155cabdff1aSopenharmony_ci    avio_wb32(pb, st->codecpar->height);
156cabdff1aSopenharmony_ci    if ((pixdesc = av_pix_fmt_desc_get(st->codecpar->format)))
157cabdff1aSopenharmony_ci        avio_wb32(pb, av_get_bits_per_pixel(pixdesc));
158cabdff1aSopenharmony_ci    else
159cabdff1aSopenharmony_ci        avio_wb32(pb, 0);
160cabdff1aSopenharmony_ci    avio_wb32(pb, 0);
161cabdff1aSopenharmony_ci
162cabdff1aSopenharmony_ci    avio_wb32(pb, pkt->size);
163cabdff1aSopenharmony_ci    avio_write(pb, pkt->data, pkt->size);
164cabdff1aSopenharmony_ci    return 0;
165cabdff1aSopenharmony_ci}
166cabdff1aSopenharmony_ci
167cabdff1aSopenharmony_cistatic int flac_finish_header(struct AVFormatContext *s)
168cabdff1aSopenharmony_ci{
169cabdff1aSopenharmony_ci    int i, ret, padding = s->metadata_header_padding;
170cabdff1aSopenharmony_ci    if (padding < 0)
171cabdff1aSopenharmony_ci        padding = 8192;
172cabdff1aSopenharmony_ci    /* The FLAC specification states that 24 bits are used to represent the
173cabdff1aSopenharmony_ci     * size of a metadata block so we must clip this value to 2^24-1. */
174cabdff1aSopenharmony_ci    padding = av_clip_uintp2(padding, 24);
175cabdff1aSopenharmony_ci
176cabdff1aSopenharmony_ci    for (i = 0; i < s->nb_streams; i++) {
177cabdff1aSopenharmony_ci        AVStream *st = s->streams[i];
178cabdff1aSopenharmony_ci        AVPacket *pkt = st->priv_data;
179cabdff1aSopenharmony_ci        if (!pkt)
180cabdff1aSopenharmony_ci            continue;
181cabdff1aSopenharmony_ci        ret = flac_write_picture(s, pkt);
182cabdff1aSopenharmony_ci        av_packet_unref(pkt);
183cabdff1aSopenharmony_ci        if (ret < 0 && (s->error_recognition & AV_EF_EXPLODE))
184cabdff1aSopenharmony_ci            return ret;
185cabdff1aSopenharmony_ci    }
186cabdff1aSopenharmony_ci
187cabdff1aSopenharmony_ci    ret = flac_write_block_comment(s->pb, &s->metadata, !padding,
188cabdff1aSopenharmony_ci                                   s->flags & AVFMT_FLAG_BITEXACT);
189cabdff1aSopenharmony_ci    if (ret)
190cabdff1aSopenharmony_ci        return ret;
191cabdff1aSopenharmony_ci
192cabdff1aSopenharmony_ci    /* The command line flac encoder defaults to placing a seekpoint
193cabdff1aSopenharmony_ci     * every 10s.  So one might add padding to allow that later
194cabdff1aSopenharmony_ci     * but there seems to be no simple way to get the duration here.
195cabdff1aSopenharmony_ci     * So just add the amount requested by the user. */
196cabdff1aSopenharmony_ci    if (padding)
197cabdff1aSopenharmony_ci        flac_write_block_padding(s->pb, padding, 1);
198cabdff1aSopenharmony_ci
199cabdff1aSopenharmony_ci    return 0;
200cabdff1aSopenharmony_ci}
201cabdff1aSopenharmony_ci
202cabdff1aSopenharmony_cistatic int flac_init(struct AVFormatContext *s)
203cabdff1aSopenharmony_ci{
204cabdff1aSopenharmony_ci    AVCodecParameters *par;
205cabdff1aSopenharmony_ci    FlacMuxerContext *c = s->priv_data;
206cabdff1aSopenharmony_ci    int i;
207cabdff1aSopenharmony_ci
208cabdff1aSopenharmony_ci    c->audio_stream_idx = -1;
209cabdff1aSopenharmony_ci    for (i = 0; i < s->nb_streams; i++) {
210cabdff1aSopenharmony_ci        AVStream *st = s->streams[i];
211cabdff1aSopenharmony_ci        if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
212cabdff1aSopenharmony_ci            if (c->audio_stream_idx >= 0 || st->codecpar->codec_id != AV_CODEC_ID_FLAC) {
213cabdff1aSopenharmony_ci                av_log(s, AV_LOG_ERROR, "Invalid audio stream. Exactly one FLAC "
214cabdff1aSopenharmony_ci                       "audio stream is required.\n");
215cabdff1aSopenharmony_ci                return AVERROR(EINVAL);
216cabdff1aSopenharmony_ci            }
217cabdff1aSopenharmony_ci            par = s->streams[i]->codecpar;
218cabdff1aSopenharmony_ci            c->audio_stream_idx = i;
219cabdff1aSopenharmony_ci        } else if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
220cabdff1aSopenharmony_ci            if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
221cabdff1aSopenharmony_ci                av_log(s, AV_LOG_WARNING, "Video stream #%d is not an attached picture. Ignoring\n", i);
222cabdff1aSopenharmony_ci                continue;
223cabdff1aSopenharmony_ci            } else if (st->codecpar->codec_id == AV_CODEC_ID_GIF) {
224cabdff1aSopenharmony_ci                av_log(s, AV_LOG_ERROR, "GIF image support is not implemented.\n");
225cabdff1aSopenharmony_ci                return AVERROR_PATCHWELCOME;
226cabdff1aSopenharmony_ci            } else if (!c->write_header) {
227cabdff1aSopenharmony_ci                av_log(s, AV_LOG_ERROR, "Can't write attached pictures without a header.\n");
228cabdff1aSopenharmony_ci                return AVERROR(EINVAL);
229cabdff1aSopenharmony_ci            }
230cabdff1aSopenharmony_ci            c->waiting_pics++;
231cabdff1aSopenharmony_ci        } else {
232cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Only audio streams and pictures are allowed in FLAC.\n");
233cabdff1aSopenharmony_ci            return AVERROR(EINVAL);
234cabdff1aSopenharmony_ci        }
235cabdff1aSopenharmony_ci    }
236cabdff1aSopenharmony_ci    if (c->audio_stream_idx < 0) {
237cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "No audio stream present.\n");
238cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
239cabdff1aSopenharmony_ci    }
240cabdff1aSopenharmony_ci
241cabdff1aSopenharmony_ci    /* add the channel layout tag */
242cabdff1aSopenharmony_ci    if (par->ch_layout.order == AV_CHANNEL_ORDER_NATIVE &&
243cabdff1aSopenharmony_ci        !(par->ch_layout.u.mask & ~0x3ffffULL) &&
244cabdff1aSopenharmony_ci        !ff_flac_is_native_layout(par->ch_layout.u.mask)) {
245cabdff1aSopenharmony_ci        AVDictionaryEntry *chmask = av_dict_get(s->metadata, "WAVEFORMATEXTENSIBLE_CHANNEL_MASK",
246cabdff1aSopenharmony_ci                                                NULL, 0);
247cabdff1aSopenharmony_ci
248cabdff1aSopenharmony_ci        if (chmask) {
249cabdff1aSopenharmony_ci            av_log(s, AV_LOG_WARNING, "A WAVEFORMATEXTENSIBLE_CHANNEL_MASK is "
250cabdff1aSopenharmony_ci                   "already present, this muxer will not overwrite it.\n");
251cabdff1aSopenharmony_ci        } else {
252cabdff1aSopenharmony_ci            uint8_t buf[32];
253cabdff1aSopenharmony_ci            snprintf(buf, sizeof(buf), "0x%"PRIx64, par->ch_layout.u.mask);
254cabdff1aSopenharmony_ci            av_dict_set(&s->metadata, "WAVEFORMATEXTENSIBLE_CHANNEL_MASK", buf, 0);
255cabdff1aSopenharmony_ci        }
256cabdff1aSopenharmony_ci    }
257cabdff1aSopenharmony_ci
258cabdff1aSopenharmony_ci    return 0;
259cabdff1aSopenharmony_ci}
260cabdff1aSopenharmony_ci
261cabdff1aSopenharmony_cistatic int flac_write_header(struct AVFormatContext *s)
262cabdff1aSopenharmony_ci{
263cabdff1aSopenharmony_ci    FlacMuxerContext *c = s->priv_data;
264cabdff1aSopenharmony_ci    AVCodecParameters *par = s->streams[c->audio_stream_idx]->codecpar;
265cabdff1aSopenharmony_ci    int ret;
266cabdff1aSopenharmony_ci
267cabdff1aSopenharmony_ci    if (!c->write_header)
268cabdff1aSopenharmony_ci        return 0;
269cabdff1aSopenharmony_ci
270cabdff1aSopenharmony_ci    ret = ff_flac_write_header(s->pb, par->extradata,
271cabdff1aSopenharmony_ci                               par->extradata_size, 0);
272cabdff1aSopenharmony_ci    if (ret < 0)
273cabdff1aSopenharmony_ci        return ret;
274cabdff1aSopenharmony_ci
275cabdff1aSopenharmony_ci    if (!c->waiting_pics)
276cabdff1aSopenharmony_ci        ret = flac_finish_header(s);
277cabdff1aSopenharmony_ci
278cabdff1aSopenharmony_ci    return ret;
279cabdff1aSopenharmony_ci}
280cabdff1aSopenharmony_ci
281cabdff1aSopenharmony_cistatic int flac_write_audio_packet(struct AVFormatContext *s, AVPacket *pkt)
282cabdff1aSopenharmony_ci{
283cabdff1aSopenharmony_ci    FlacMuxerContext *c = s->priv_data;
284cabdff1aSopenharmony_ci    uint8_t *streaminfo;
285cabdff1aSopenharmony_ci    size_t streaminfo_size;
286cabdff1aSopenharmony_ci
287cabdff1aSopenharmony_ci    /* check for updated streaminfo */
288cabdff1aSopenharmony_ci    streaminfo = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
289cabdff1aSopenharmony_ci                                         &streaminfo_size);
290cabdff1aSopenharmony_ci    if (streaminfo && streaminfo_size == FLAC_STREAMINFO_SIZE) {
291cabdff1aSopenharmony_ci        memcpy(c->streaminfo, streaminfo, FLAC_STREAMINFO_SIZE);
292cabdff1aSopenharmony_ci        c->updated_streaminfo = 1;
293cabdff1aSopenharmony_ci    }
294cabdff1aSopenharmony_ci
295cabdff1aSopenharmony_ci    if (pkt->size)
296cabdff1aSopenharmony_ci        avio_write(s->pb, pkt->data, pkt->size);
297cabdff1aSopenharmony_ci    return 0;
298cabdff1aSopenharmony_ci}
299cabdff1aSopenharmony_ci
300cabdff1aSopenharmony_cistatic int flac_queue_flush(AVFormatContext *s)
301cabdff1aSopenharmony_ci{
302cabdff1aSopenharmony_ci    FlacMuxerContext *c = s->priv_data;
303cabdff1aSopenharmony_ci    AVPacket *const pkt = ffformatcontext(s)->pkt;
304cabdff1aSopenharmony_ci    int ret, write = 1;
305cabdff1aSopenharmony_ci
306cabdff1aSopenharmony_ci    ret = flac_finish_header(s);
307cabdff1aSopenharmony_ci    if (ret < 0)
308cabdff1aSopenharmony_ci        write = 0;
309cabdff1aSopenharmony_ci
310cabdff1aSopenharmony_ci    while (c->queue.head) {
311cabdff1aSopenharmony_ci        avpriv_packet_list_get(&c->queue, pkt);
312cabdff1aSopenharmony_ci        if (write && (ret = flac_write_audio_packet(s, pkt)) < 0)
313cabdff1aSopenharmony_ci            write = 0;
314cabdff1aSopenharmony_ci        av_packet_unref(pkt);
315cabdff1aSopenharmony_ci    }
316cabdff1aSopenharmony_ci    return ret;
317cabdff1aSopenharmony_ci}
318cabdff1aSopenharmony_ci
319cabdff1aSopenharmony_cistatic int flac_write_trailer(struct AVFormatContext *s)
320cabdff1aSopenharmony_ci{
321cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
322cabdff1aSopenharmony_ci    int64_t file_size;
323cabdff1aSopenharmony_ci    FlacMuxerContext *c = s->priv_data;
324cabdff1aSopenharmony_ci
325cabdff1aSopenharmony_ci    if (c->waiting_pics) {
326cabdff1aSopenharmony_ci        av_log(s, AV_LOG_WARNING, "No packets were sent for some of the "
327cabdff1aSopenharmony_ci               "attached pictures.\n");
328cabdff1aSopenharmony_ci        flac_queue_flush(s);
329cabdff1aSopenharmony_ci    }
330cabdff1aSopenharmony_ci
331cabdff1aSopenharmony_ci    if (!c->write_header || !c->updated_streaminfo)
332cabdff1aSopenharmony_ci        return 0;
333cabdff1aSopenharmony_ci
334cabdff1aSopenharmony_ci    if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
335cabdff1aSopenharmony_ci        /* rewrite the STREAMINFO header block data */
336cabdff1aSopenharmony_ci        file_size = avio_tell(pb);
337cabdff1aSopenharmony_ci        avio_seek(pb, 8, SEEK_SET);
338cabdff1aSopenharmony_ci        avio_write(pb, c->streaminfo, FLAC_STREAMINFO_SIZE);
339cabdff1aSopenharmony_ci        avio_seek(pb, file_size, SEEK_SET);
340cabdff1aSopenharmony_ci    } else {
341cabdff1aSopenharmony_ci        av_log(s, AV_LOG_WARNING, "unable to rewrite FLAC header.\n");
342cabdff1aSopenharmony_ci    }
343cabdff1aSopenharmony_ci
344cabdff1aSopenharmony_ci    return 0;
345cabdff1aSopenharmony_ci}
346cabdff1aSopenharmony_ci
347cabdff1aSopenharmony_cistatic void flac_deinit(struct AVFormatContext *s)
348cabdff1aSopenharmony_ci{
349cabdff1aSopenharmony_ci    FlacMuxerContext *c = s->priv_data;
350cabdff1aSopenharmony_ci
351cabdff1aSopenharmony_ci    avpriv_packet_list_free(&c->queue);
352cabdff1aSopenharmony_ci    for (unsigned i = 0; i < s->nb_streams; i++)
353cabdff1aSopenharmony_ci        av_packet_free((AVPacket **)&s->streams[i]->priv_data);
354cabdff1aSopenharmony_ci}
355cabdff1aSopenharmony_ci
356cabdff1aSopenharmony_cistatic int flac_write_packet(struct AVFormatContext *s, AVPacket *pkt)
357cabdff1aSopenharmony_ci{
358cabdff1aSopenharmony_ci    FlacMuxerContext *c = s->priv_data;
359cabdff1aSopenharmony_ci    int ret;
360cabdff1aSopenharmony_ci
361cabdff1aSopenharmony_ci    if (pkt->stream_index == c->audio_stream_idx) {
362cabdff1aSopenharmony_ci        if (c->waiting_pics) {
363cabdff1aSopenharmony_ci            /* buffer audio packets until we get all the pictures */
364cabdff1aSopenharmony_ci            ret = avpriv_packet_list_put(&c->queue, pkt, NULL, 0);
365cabdff1aSopenharmony_ci            if (ret < 0) {
366cabdff1aSopenharmony_ci                av_log(s, AV_LOG_ERROR, "Out of memory in packet queue; skipping attached pictures\n");
367cabdff1aSopenharmony_ci                c->waiting_pics = 0;
368cabdff1aSopenharmony_ci                ret = flac_queue_flush(s);
369cabdff1aSopenharmony_ci                if (ret < 0)
370cabdff1aSopenharmony_ci                    return ret;
371cabdff1aSopenharmony_ci                return flac_write_audio_packet(s, pkt);
372cabdff1aSopenharmony_ci            }
373cabdff1aSopenharmony_ci        } else
374cabdff1aSopenharmony_ci            return flac_write_audio_packet(s, pkt);
375cabdff1aSopenharmony_ci    } else {
376cabdff1aSopenharmony_ci        AVStream *st = s->streams[pkt->stream_index];
377cabdff1aSopenharmony_ci
378cabdff1aSopenharmony_ci        if (!c->waiting_pics ||
379cabdff1aSopenharmony_ci            !(st->disposition & AV_DISPOSITION_ATTACHED_PIC))
380cabdff1aSopenharmony_ci            return 0;
381cabdff1aSopenharmony_ci
382cabdff1aSopenharmony_ci        /* warn only once for each stream */
383cabdff1aSopenharmony_ci        if (st->nb_frames == 1) {
384cabdff1aSopenharmony_ci            av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d,"
385cabdff1aSopenharmony_ci                   " ignoring.\n", pkt->stream_index);
386cabdff1aSopenharmony_ci        }
387cabdff1aSopenharmony_ci        if (st->nb_frames >= 1)
388cabdff1aSopenharmony_ci            return 0;
389cabdff1aSopenharmony_ci
390cabdff1aSopenharmony_ci        st->priv_data = av_packet_clone(pkt);
391cabdff1aSopenharmony_ci        if (!st->priv_data)
392cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Out of memory queueing an attached picture; skipping\n");
393cabdff1aSopenharmony_ci        c->waiting_pics--;
394cabdff1aSopenharmony_ci
395cabdff1aSopenharmony_ci        /* flush the buffered audio packets */
396cabdff1aSopenharmony_ci        if (!c->waiting_pics &&
397cabdff1aSopenharmony_ci            (ret = flac_queue_flush(s)) < 0)
398cabdff1aSopenharmony_ci            return ret;
399cabdff1aSopenharmony_ci    }
400cabdff1aSopenharmony_ci
401cabdff1aSopenharmony_ci    return 0;
402cabdff1aSopenharmony_ci}
403cabdff1aSopenharmony_ci
404cabdff1aSopenharmony_cistatic const AVOption flacenc_options[] = {
405cabdff1aSopenharmony_ci    { "write_header", "Write the file header", offsetof(FlacMuxerContext, write_header), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
406cabdff1aSopenharmony_ci    { NULL },
407cabdff1aSopenharmony_ci};
408cabdff1aSopenharmony_ci
409cabdff1aSopenharmony_cistatic const AVClass flac_muxer_class = {
410cabdff1aSopenharmony_ci    .class_name = "flac muxer",
411cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
412cabdff1aSopenharmony_ci    .option     = flacenc_options,
413cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
414cabdff1aSopenharmony_ci};
415cabdff1aSopenharmony_ci
416cabdff1aSopenharmony_ciconst AVOutputFormat ff_flac_muxer = {
417cabdff1aSopenharmony_ci    .name              = "flac",
418cabdff1aSopenharmony_ci    .long_name         = NULL_IF_CONFIG_SMALL("raw FLAC"),
419cabdff1aSopenharmony_ci    .priv_data_size    = sizeof(FlacMuxerContext),
420cabdff1aSopenharmony_ci    .mime_type         = "audio/x-flac",
421cabdff1aSopenharmony_ci    .extensions        = "flac",
422cabdff1aSopenharmony_ci    .audio_codec       = AV_CODEC_ID_FLAC,
423cabdff1aSopenharmony_ci    .video_codec       = AV_CODEC_ID_PNG,
424cabdff1aSopenharmony_ci    .init              = flac_init,
425cabdff1aSopenharmony_ci    .write_header      = flac_write_header,
426cabdff1aSopenharmony_ci    .write_packet      = flac_write_packet,
427cabdff1aSopenharmony_ci    .write_trailer     = flac_write_trailer,
428cabdff1aSopenharmony_ci    .deinit            = flac_deinit,
429cabdff1aSopenharmony_ci    .flags             = AVFMT_NOTIMESTAMPS,
430cabdff1aSopenharmony_ci    .priv_class        = &flac_muxer_class,
431cabdff1aSopenharmony_ci};
432