xref: /third_party/ffmpeg/libavformat/wavenc.c (revision cabdff1a)
1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * WAV muxer
3cabdff1aSopenharmony_ci * Copyright (c) 2001, 2002 Fabrice Bellard
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * Sony Wave64 muxer
6cabdff1aSopenharmony_ci * Copyright (c) 2012 Paul B Mahol
7cabdff1aSopenharmony_ci *
8cabdff1aSopenharmony_ci * WAV muxer RF64 support
9cabdff1aSopenharmony_ci * Copyright (c) 2013 Daniel Verkamp <daniel@drv.nu>
10cabdff1aSopenharmony_ci *
11cabdff1aSopenharmony_ci * EBU Tech 3285 - Supplement 3 - Peak Envelope Chunk encoder
12cabdff1aSopenharmony_ci * Copyright (c) 2014 Georg Lippitsch <georg.lippitsch@gmx.at>
13cabdff1aSopenharmony_ci *
14cabdff1aSopenharmony_ci * This file is part of FFmpeg.
15cabdff1aSopenharmony_ci *
16cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
17cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
18cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
19cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
20cabdff1aSopenharmony_ci *
21cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
22cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
23cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24cabdff1aSopenharmony_ci * Lesser General Public License for more details.
25cabdff1aSopenharmony_ci *
26cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
27cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
28cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29cabdff1aSopenharmony_ci */
30cabdff1aSopenharmony_ci
31cabdff1aSopenharmony_ci#include "config_components.h"
32cabdff1aSopenharmony_ci
33cabdff1aSopenharmony_ci#include <stdint.h>
34cabdff1aSopenharmony_ci#include <string.h>
35cabdff1aSopenharmony_ci
36cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
37cabdff1aSopenharmony_ci#include "libavutil/dict.h"
38cabdff1aSopenharmony_ci#include "libavutil/common.h"
39cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
40cabdff1aSopenharmony_ci#include "libavutil/mathematics.h"
41cabdff1aSopenharmony_ci#include "libavutil/opt.h"
42cabdff1aSopenharmony_ci#include "libavutil/time.h"
43cabdff1aSopenharmony_ci#include "libavutil/time_internal.h"
44cabdff1aSopenharmony_ci
45cabdff1aSopenharmony_ci#include "avformat.h"
46cabdff1aSopenharmony_ci#include "avio.h"
47cabdff1aSopenharmony_ci#include "avio_internal.h"
48cabdff1aSopenharmony_ci#include "internal.h"
49cabdff1aSopenharmony_ci#include "riff.h"
50cabdff1aSopenharmony_ci
51cabdff1aSopenharmony_ci#define RF64_AUTO   (-1)
52cabdff1aSopenharmony_ci#define RF64_NEVER  0
53cabdff1aSopenharmony_ci#define RF64_ALWAYS 1
54cabdff1aSopenharmony_ci
55cabdff1aSopenharmony_citypedef enum {
56cabdff1aSopenharmony_ci    PEAK_OFF = 0,
57cabdff1aSopenharmony_ci    PEAK_ON,
58cabdff1aSopenharmony_ci    PEAK_ONLY
59cabdff1aSopenharmony_ci} PeakType;
60cabdff1aSopenharmony_ci
61cabdff1aSopenharmony_citypedef enum {
62cabdff1aSopenharmony_ci    PEAK_FORMAT_UINT8 = 1,
63cabdff1aSopenharmony_ci    PEAK_FORMAT_UINT16
64cabdff1aSopenharmony_ci} PeakFormat;
65cabdff1aSopenharmony_ci
66cabdff1aSopenharmony_citypedef struct WAVMuxContext {
67cabdff1aSopenharmony_ci    const AVClass *class;
68cabdff1aSopenharmony_ci    int64_t data;
69cabdff1aSopenharmony_ci    int64_t fact_pos;
70cabdff1aSopenharmony_ci    int64_t ds64;
71cabdff1aSopenharmony_ci    int64_t minpts;
72cabdff1aSopenharmony_ci    int64_t maxpts;
73cabdff1aSopenharmony_ci    int16_t *peak_maxpos, *peak_maxneg;
74cabdff1aSopenharmony_ci    uint32_t peak_num_frames;
75cabdff1aSopenharmony_ci    unsigned peak_outbuf_size;
76cabdff1aSopenharmony_ci    uint32_t peak_outbuf_bytes;
77cabdff1aSopenharmony_ci    unsigned size_increment;
78cabdff1aSopenharmony_ci    uint8_t *peak_output;
79cabdff1aSopenharmony_ci    int last_duration;
80cabdff1aSopenharmony_ci    int write_bext;
81cabdff1aSopenharmony_ci    int write_peak;
82cabdff1aSopenharmony_ci    int rf64;
83cabdff1aSopenharmony_ci    int peak_block_size;
84cabdff1aSopenharmony_ci    int peak_format;
85cabdff1aSopenharmony_ci    int peak_block_pos;
86cabdff1aSopenharmony_ci    int peak_ppv;
87cabdff1aSopenharmony_ci    int peak_bps;
88cabdff1aSopenharmony_ci} WAVMuxContext;
89cabdff1aSopenharmony_ci
90cabdff1aSopenharmony_ci#if CONFIG_WAV_MUXER
91cabdff1aSopenharmony_cistatic inline void bwf_write_bext_string(AVFormatContext *s, const char *key, int maxlen)
92cabdff1aSopenharmony_ci{
93cabdff1aSopenharmony_ci    AVDictionaryEntry *tag;
94cabdff1aSopenharmony_ci    size_t len = 0;
95cabdff1aSopenharmony_ci
96cabdff1aSopenharmony_ci    if (tag = av_dict_get(s->metadata, key, NULL, 0)) {
97cabdff1aSopenharmony_ci        len = strlen(tag->value);
98cabdff1aSopenharmony_ci        len = FFMIN(len, maxlen);
99cabdff1aSopenharmony_ci        avio_write(s->pb, tag->value, len);
100cabdff1aSopenharmony_ci    }
101cabdff1aSopenharmony_ci
102cabdff1aSopenharmony_ci    ffio_fill(s->pb, 0, maxlen - len);
103cabdff1aSopenharmony_ci}
104cabdff1aSopenharmony_ci
105cabdff1aSopenharmony_cistatic void bwf_write_bext_chunk(AVFormatContext *s)
106cabdff1aSopenharmony_ci{
107cabdff1aSopenharmony_ci    AVDictionaryEntry *tmp_tag;
108cabdff1aSopenharmony_ci    uint64_t time_reference = 0;
109cabdff1aSopenharmony_ci    int64_t bext = ff_start_tag(s->pb, "bext");
110cabdff1aSopenharmony_ci
111cabdff1aSopenharmony_ci    bwf_write_bext_string(s, "description", 256);
112cabdff1aSopenharmony_ci    bwf_write_bext_string(s, "originator", 32);
113cabdff1aSopenharmony_ci    bwf_write_bext_string(s, "originator_reference", 32);
114cabdff1aSopenharmony_ci    bwf_write_bext_string(s, "origination_date", 10);
115cabdff1aSopenharmony_ci    bwf_write_bext_string(s, "origination_time", 8);
116cabdff1aSopenharmony_ci
117cabdff1aSopenharmony_ci    if (tmp_tag = av_dict_get(s->metadata, "time_reference", NULL, 0))
118cabdff1aSopenharmony_ci        time_reference = strtoll(tmp_tag->value, NULL, 10);
119cabdff1aSopenharmony_ci    avio_wl64(s->pb, time_reference);
120cabdff1aSopenharmony_ci    avio_wl16(s->pb, 1);  // set version to 1
121cabdff1aSopenharmony_ci
122cabdff1aSopenharmony_ci    if ((tmp_tag = av_dict_get(s->metadata, "umid", NULL, 0)) && strlen(tmp_tag->value) > 2) {
123cabdff1aSopenharmony_ci        unsigned char umidpart_str[17] = {0};
124cabdff1aSopenharmony_ci        int64_t i;
125cabdff1aSopenharmony_ci        uint64_t umidpart;
126cabdff1aSopenharmony_ci        size_t len = strlen(tmp_tag->value+2);
127cabdff1aSopenharmony_ci
128cabdff1aSopenharmony_ci        for (i = 0; i < len/16; i++) {
129cabdff1aSopenharmony_ci            memcpy(umidpart_str, tmp_tag->value + 2 + (i*16), 16);
130cabdff1aSopenharmony_ci            umidpart = strtoll(umidpart_str, NULL, 16);
131cabdff1aSopenharmony_ci            avio_wb64(s->pb, umidpart);
132cabdff1aSopenharmony_ci        }
133cabdff1aSopenharmony_ci        ffio_fill(s->pb, 0, 64 - i*8);
134cabdff1aSopenharmony_ci    } else
135cabdff1aSopenharmony_ci        ffio_fill(s->pb, 0, 64); // zero UMID
136cabdff1aSopenharmony_ci
137cabdff1aSopenharmony_ci    ffio_fill(s->pb, 0, 190); // Reserved
138cabdff1aSopenharmony_ci
139cabdff1aSopenharmony_ci    if (tmp_tag = av_dict_get(s->metadata, "coding_history", NULL, 0))
140cabdff1aSopenharmony_ci        avio_put_str(s->pb, tmp_tag->value);
141cabdff1aSopenharmony_ci
142cabdff1aSopenharmony_ci    ff_end_tag(s->pb, bext);
143cabdff1aSopenharmony_ci}
144cabdff1aSopenharmony_ci
145cabdff1aSopenharmony_cistatic av_cold void wav_deinit(AVFormatContext *s)
146cabdff1aSopenharmony_ci{
147cabdff1aSopenharmony_ci    WAVMuxContext *wav = s->priv_data;
148cabdff1aSopenharmony_ci
149cabdff1aSopenharmony_ci    av_freep(&wav->peak_maxpos);
150cabdff1aSopenharmony_ci    av_freep(&wav->peak_maxneg);
151cabdff1aSopenharmony_ci    av_freep(&wav->peak_output);
152cabdff1aSopenharmony_ci}
153cabdff1aSopenharmony_ci
154cabdff1aSopenharmony_cistatic av_cold int peak_init_writer(AVFormatContext *s)
155cabdff1aSopenharmony_ci{
156cabdff1aSopenharmony_ci    WAVMuxContext *wav = s->priv_data;
157cabdff1aSopenharmony_ci    AVCodecParameters *par = s->streams[0]->codecpar;
158cabdff1aSopenharmony_ci
159cabdff1aSopenharmony_ci    if (par->codec_id != AV_CODEC_ID_PCM_S8 &&
160cabdff1aSopenharmony_ci        par->codec_id != AV_CODEC_ID_PCM_S16LE &&
161cabdff1aSopenharmony_ci        par->codec_id != AV_CODEC_ID_PCM_U8 &&
162cabdff1aSopenharmony_ci        par->codec_id != AV_CODEC_ID_PCM_U16LE) {
163cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Codec %s not supported for Peak Chunk\n",
164cabdff1aSopenharmony_ci               avcodec_get_name(par->codec_id));
165cabdff1aSopenharmony_ci        return -1;
166cabdff1aSopenharmony_ci    }
167cabdff1aSopenharmony_ci
168cabdff1aSopenharmony_ci    wav->peak_bps = av_get_bits_per_sample(par->codec_id) / 8;
169cabdff1aSopenharmony_ci
170cabdff1aSopenharmony_ci    if (wav->peak_bps == 1 && wav->peak_format == PEAK_FORMAT_UINT16) {
171cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
172cabdff1aSopenharmony_ci               "Writing 16 bit peak for 8 bit audio does not make sense\n");
173cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
174cabdff1aSopenharmony_ci    }
175cabdff1aSopenharmony_ci    if (par->ch_layout.nb_channels > INT_MAX / (wav->peak_bps * wav->peak_ppv))
176cabdff1aSopenharmony_ci        return AVERROR(ERANGE);
177cabdff1aSopenharmony_ci    wav->size_increment = par->ch_layout.nb_channels * wav->peak_bps * wav->peak_ppv;
178cabdff1aSopenharmony_ci
179cabdff1aSopenharmony_ci    wav->peak_maxpos = av_calloc(par->ch_layout.nb_channels, sizeof(*wav->peak_maxpos));
180cabdff1aSopenharmony_ci    wav->peak_maxneg = av_calloc(par->ch_layout.nb_channels, sizeof(*wav->peak_maxneg));
181cabdff1aSopenharmony_ci    if (!wav->peak_maxpos || !wav->peak_maxneg)
182cabdff1aSopenharmony_ci        goto nomem;
183cabdff1aSopenharmony_ci
184cabdff1aSopenharmony_ci    return 0;
185cabdff1aSopenharmony_ci
186cabdff1aSopenharmony_cinomem:
187cabdff1aSopenharmony_ci    av_log(s, AV_LOG_ERROR, "Out of memory\n");
188cabdff1aSopenharmony_ci    return AVERROR(ENOMEM);
189cabdff1aSopenharmony_ci}
190cabdff1aSopenharmony_ci
191cabdff1aSopenharmony_cistatic int peak_write_frame(AVFormatContext *s)
192cabdff1aSopenharmony_ci{
193cabdff1aSopenharmony_ci    WAVMuxContext *wav = s->priv_data;
194cabdff1aSopenharmony_ci    AVCodecParameters *par = s->streams[0]->codecpar;
195cabdff1aSopenharmony_ci    unsigned new_size = wav->peak_outbuf_bytes + wav->size_increment;
196cabdff1aSopenharmony_ci    uint8_t *tmp;
197cabdff1aSopenharmony_ci    int c;
198cabdff1aSopenharmony_ci
199cabdff1aSopenharmony_ci    if (new_size > INT_MAX) {
200cabdff1aSopenharmony_ci        wav->write_peak = PEAK_OFF;
201cabdff1aSopenharmony_ci        return AVERROR(ERANGE);
202cabdff1aSopenharmony_ci    }
203cabdff1aSopenharmony_ci    tmp = av_fast_realloc(wav->peak_output, &wav->peak_outbuf_size, new_size);
204cabdff1aSopenharmony_ci    if (!tmp) {
205cabdff1aSopenharmony_ci        wav->write_peak = PEAK_OFF;
206cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
207cabdff1aSopenharmony_ci    }
208cabdff1aSopenharmony_ci    wav->peak_output = tmp;
209cabdff1aSopenharmony_ci
210cabdff1aSopenharmony_ci    for (c = 0; c < par->ch_layout.nb_channels; c++) {
211cabdff1aSopenharmony_ci        wav->peak_maxneg[c] = -wav->peak_maxneg[c];
212cabdff1aSopenharmony_ci
213cabdff1aSopenharmony_ci        if (wav->peak_bps == 2 && wav->peak_format == PEAK_FORMAT_UINT8) {
214cabdff1aSopenharmony_ci            wav->peak_maxpos[c] = wav->peak_maxpos[c] / 256;
215cabdff1aSopenharmony_ci            wav->peak_maxneg[c] = wav->peak_maxneg[c] / 256;
216cabdff1aSopenharmony_ci        }
217cabdff1aSopenharmony_ci
218cabdff1aSopenharmony_ci        if (wav->peak_ppv == 1)
219cabdff1aSopenharmony_ci            wav->peak_maxpos[c] =
220cabdff1aSopenharmony_ci                FFMAX(wav->peak_maxpos[c], wav->peak_maxneg[c]);
221cabdff1aSopenharmony_ci
222cabdff1aSopenharmony_ci        if (wav->peak_format == PEAK_FORMAT_UINT8) {
223cabdff1aSopenharmony_ci            wav->peak_output[wav->peak_outbuf_bytes++] =
224cabdff1aSopenharmony_ci                wav->peak_maxpos[c];
225cabdff1aSopenharmony_ci            if (wav->peak_ppv == 2) {
226cabdff1aSopenharmony_ci                wav->peak_output[wav->peak_outbuf_bytes++] =
227cabdff1aSopenharmony_ci                    wav->peak_maxneg[c];
228cabdff1aSopenharmony_ci            }
229cabdff1aSopenharmony_ci        } else {
230cabdff1aSopenharmony_ci            AV_WL16(wav->peak_output + wav->peak_outbuf_bytes,
231cabdff1aSopenharmony_ci                    wav->peak_maxpos[c]);
232cabdff1aSopenharmony_ci            wav->peak_outbuf_bytes += 2;
233cabdff1aSopenharmony_ci            if (wav->peak_ppv == 2) {
234cabdff1aSopenharmony_ci                AV_WL16(wav->peak_output + wav->peak_outbuf_bytes,
235cabdff1aSopenharmony_ci                        wav->peak_maxneg[c]);
236cabdff1aSopenharmony_ci                wav->peak_outbuf_bytes += 2;
237cabdff1aSopenharmony_ci            }
238cabdff1aSopenharmony_ci        }
239cabdff1aSopenharmony_ci        wav->peak_maxpos[c] = 0;
240cabdff1aSopenharmony_ci        wav->peak_maxneg[c] = 0;
241cabdff1aSopenharmony_ci    }
242cabdff1aSopenharmony_ci    wav->peak_num_frames++;
243cabdff1aSopenharmony_ci
244cabdff1aSopenharmony_ci    return 0;
245cabdff1aSopenharmony_ci}
246cabdff1aSopenharmony_ci
247cabdff1aSopenharmony_cistatic int peak_write_chunk(AVFormatContext *s)
248cabdff1aSopenharmony_ci{
249cabdff1aSopenharmony_ci    WAVMuxContext *wav = s->priv_data;
250cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
251cabdff1aSopenharmony_ci    AVCodecParameters *par = s->streams[0]->codecpar;
252cabdff1aSopenharmony_ci    int64_t peak = ff_start_tag(s->pb, "levl");
253cabdff1aSopenharmony_ci    int64_t now0;
254cabdff1aSopenharmony_ci    time_t now_secs;
255cabdff1aSopenharmony_ci    char timestamp[28];
256cabdff1aSopenharmony_ci
257cabdff1aSopenharmony_ci    /* Peak frame of incomplete block at end */
258cabdff1aSopenharmony_ci    if (wav->peak_block_pos) {
259cabdff1aSopenharmony_ci        int ret = peak_write_frame(s);
260cabdff1aSopenharmony_ci        if (ret < 0)
261cabdff1aSopenharmony_ci            return ret;
262cabdff1aSopenharmony_ci    }
263cabdff1aSopenharmony_ci
264cabdff1aSopenharmony_ci    memset(timestamp, 0, sizeof(timestamp));
265cabdff1aSopenharmony_ci    if (!(s->flags & AVFMT_FLAG_BITEXACT)) {
266cabdff1aSopenharmony_ci        struct tm tmpbuf;
267cabdff1aSopenharmony_ci        av_log(s, AV_LOG_INFO, "Writing local time and date to Peak Envelope Chunk\n");
268cabdff1aSopenharmony_ci        now0 = av_gettime();
269cabdff1aSopenharmony_ci        now_secs = now0 / 1000000;
270cabdff1aSopenharmony_ci        if (strftime(timestamp, sizeof(timestamp), "%Y:%m:%d:%H:%M:%S:", localtime_r(&now_secs, &tmpbuf))) {
271cabdff1aSopenharmony_ci            av_strlcatf(timestamp, sizeof(timestamp), "%03d", (int)((now0 / 1000) % 1000));
272cabdff1aSopenharmony_ci        } else {
273cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Failed to write timestamp\n");
274cabdff1aSopenharmony_ci            return -1;
275cabdff1aSopenharmony_ci        }
276cabdff1aSopenharmony_ci    }
277cabdff1aSopenharmony_ci
278cabdff1aSopenharmony_ci    avio_wl32(pb, 1);                           /* version */
279cabdff1aSopenharmony_ci    avio_wl32(pb, wav->peak_format);            /* 8 or 16 bit */
280cabdff1aSopenharmony_ci    avio_wl32(pb, wav->peak_ppv);               /* positive and negative */
281cabdff1aSopenharmony_ci    avio_wl32(pb, wav->peak_block_size);        /* frames per value */
282cabdff1aSopenharmony_ci    avio_wl32(pb, par->ch_layout.nb_channels);  /* number of channels */
283cabdff1aSopenharmony_ci    avio_wl32(pb, wav->peak_num_frames);        /* number of peak frames */
284cabdff1aSopenharmony_ci    avio_wl32(pb, -1);                          /* audio sample frame position (not implemented) */
285cabdff1aSopenharmony_ci    avio_wl32(pb, 128);                         /* equal to size of header */
286cabdff1aSopenharmony_ci    avio_write(pb, timestamp, 28);              /* ASCII time stamp */
287cabdff1aSopenharmony_ci    ffio_fill(pb, 0, 60);
288cabdff1aSopenharmony_ci
289cabdff1aSopenharmony_ci    avio_write(pb, wav->peak_output, wav->peak_outbuf_bytes);
290cabdff1aSopenharmony_ci
291cabdff1aSopenharmony_ci    ff_end_tag(pb, peak);
292cabdff1aSopenharmony_ci
293cabdff1aSopenharmony_ci    if (!wav->data)
294cabdff1aSopenharmony_ci        wav->data = peak;
295cabdff1aSopenharmony_ci
296cabdff1aSopenharmony_ci    return 0;
297cabdff1aSopenharmony_ci}
298cabdff1aSopenharmony_ci
299cabdff1aSopenharmony_cistatic int wav_write_header(AVFormatContext *s)
300cabdff1aSopenharmony_ci{
301cabdff1aSopenharmony_ci    WAVMuxContext *wav = s->priv_data;
302cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
303cabdff1aSopenharmony_ci    int64_t fmt;
304cabdff1aSopenharmony_ci
305cabdff1aSopenharmony_ci    if (s->nb_streams != 1) {
306cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "WAVE files have exactly one stream\n");
307cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
308cabdff1aSopenharmony_ci    }
309cabdff1aSopenharmony_ci
310cabdff1aSopenharmony_ci    if (wav->rf64 == RF64_ALWAYS) {
311cabdff1aSopenharmony_ci        ffio_wfourcc(pb, "RF64");
312cabdff1aSopenharmony_ci        avio_wl32(pb, -1); /* RF64 chunk size: use size in ds64 */
313cabdff1aSopenharmony_ci    } else {
314cabdff1aSopenharmony_ci        ffio_wfourcc(pb, "RIFF");
315cabdff1aSopenharmony_ci        avio_wl32(pb, -1); /* file length */
316cabdff1aSopenharmony_ci    }
317cabdff1aSopenharmony_ci
318cabdff1aSopenharmony_ci    ffio_wfourcc(pb, "WAVE");
319cabdff1aSopenharmony_ci
320cabdff1aSopenharmony_ci    if (wav->rf64 != RF64_NEVER) {
321cabdff1aSopenharmony_ci        /* write empty ds64 chunk or JUNK chunk to reserve space for ds64 */
322cabdff1aSopenharmony_ci        ffio_wfourcc(pb, wav->rf64 == RF64_ALWAYS ? "ds64" : "JUNK");
323cabdff1aSopenharmony_ci        avio_wl32(pb, 28); /* chunk size */
324cabdff1aSopenharmony_ci        wav->ds64 = avio_tell(pb);
325cabdff1aSopenharmony_ci        ffio_fill(pb, 0, 28);
326cabdff1aSopenharmony_ci    }
327cabdff1aSopenharmony_ci
328cabdff1aSopenharmony_ci    if (wav->write_peak != PEAK_ONLY) {
329cabdff1aSopenharmony_ci        /* format header */
330cabdff1aSopenharmony_ci        fmt = ff_start_tag(pb, "fmt ");
331cabdff1aSopenharmony_ci        if (ff_put_wav_header(s, pb, s->streams[0]->codecpar, 0) < 0) {
332cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Codec %s not supported in WAVE format\n",
333cabdff1aSopenharmony_ci                   avcodec_get_name(s->streams[0]->codecpar->codec_id));
334cabdff1aSopenharmony_ci            return AVERROR(ENOSYS);
335cabdff1aSopenharmony_ci        }
336cabdff1aSopenharmony_ci        ff_end_tag(pb, fmt);
337cabdff1aSopenharmony_ci    }
338cabdff1aSopenharmony_ci
339cabdff1aSopenharmony_ci    if (s->streams[0]->codecpar->codec_tag != 0x01 /* hence for all other than PCM */
340cabdff1aSopenharmony_ci        && (s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
341cabdff1aSopenharmony_ci        wav->fact_pos = ff_start_tag(pb, "fact");
342cabdff1aSopenharmony_ci        avio_wl32(pb, 0);
343cabdff1aSopenharmony_ci        ff_end_tag(pb, wav->fact_pos);
344cabdff1aSopenharmony_ci    }
345cabdff1aSopenharmony_ci
346cabdff1aSopenharmony_ci    if (wav->write_bext)
347cabdff1aSopenharmony_ci        bwf_write_bext_chunk(s);
348cabdff1aSopenharmony_ci
349cabdff1aSopenharmony_ci    if (wav->write_peak) {
350cabdff1aSopenharmony_ci        int ret;
351cabdff1aSopenharmony_ci        if ((ret = peak_init_writer(s)) < 0)
352cabdff1aSopenharmony_ci            return ret;
353cabdff1aSopenharmony_ci    }
354cabdff1aSopenharmony_ci
355cabdff1aSopenharmony_ci    avpriv_set_pts_info(s->streams[0], 64, 1, s->streams[0]->codecpar->sample_rate);
356cabdff1aSopenharmony_ci    wav->maxpts = wav->last_duration = 0;
357cabdff1aSopenharmony_ci    wav->minpts = INT64_MAX;
358cabdff1aSopenharmony_ci
359cabdff1aSopenharmony_ci    if (wav->write_peak != PEAK_ONLY) {
360cabdff1aSopenharmony_ci        /* info header */
361cabdff1aSopenharmony_ci        ff_riff_write_info(s);
362cabdff1aSopenharmony_ci
363cabdff1aSopenharmony_ci        /* data header */
364cabdff1aSopenharmony_ci        wav->data = ff_start_tag(pb, "data");
365cabdff1aSopenharmony_ci    }
366cabdff1aSopenharmony_ci
367cabdff1aSopenharmony_ci    return 0;
368cabdff1aSopenharmony_ci}
369cabdff1aSopenharmony_ci
370cabdff1aSopenharmony_cistatic int wav_write_packet(AVFormatContext *s, AVPacket *pkt)
371cabdff1aSopenharmony_ci{
372cabdff1aSopenharmony_ci    AVIOContext *pb  = s->pb;
373cabdff1aSopenharmony_ci    WAVMuxContext    *wav = s->priv_data;
374cabdff1aSopenharmony_ci
375cabdff1aSopenharmony_ci    if (wav->write_peak != PEAK_ONLY)
376cabdff1aSopenharmony_ci        avio_write(pb, pkt->data, pkt->size);
377cabdff1aSopenharmony_ci
378cabdff1aSopenharmony_ci    if (wav->write_peak) {
379cabdff1aSopenharmony_ci        int c = 0;
380cabdff1aSopenharmony_ci        int i;
381cabdff1aSopenharmony_ci        for (i = 0; i < pkt->size; i += wav->peak_bps) {
382cabdff1aSopenharmony_ci            if (wav->peak_bps == 1) {
383cabdff1aSopenharmony_ci                wav->peak_maxpos[c] = FFMAX(wav->peak_maxpos[c], *(int8_t*)(pkt->data + i));
384cabdff1aSopenharmony_ci                wav->peak_maxneg[c] = FFMIN(wav->peak_maxneg[c], *(int8_t*)(pkt->data + i));
385cabdff1aSopenharmony_ci            } else {
386cabdff1aSopenharmony_ci                wav->peak_maxpos[c] = FFMAX(wav->peak_maxpos[c], (int16_t)AV_RL16(pkt->data + i));
387cabdff1aSopenharmony_ci                wav->peak_maxneg[c] = FFMIN(wav->peak_maxneg[c], (int16_t)AV_RL16(pkt->data + i));
388cabdff1aSopenharmony_ci            }
389cabdff1aSopenharmony_ci            if (++c == s->streams[0]->codecpar->ch_layout.nb_channels) {
390cabdff1aSopenharmony_ci                c = 0;
391cabdff1aSopenharmony_ci                if (++wav->peak_block_pos == wav->peak_block_size) {
392cabdff1aSopenharmony_ci                    int ret = peak_write_frame(s);
393cabdff1aSopenharmony_ci                    if (ret < 0)
394cabdff1aSopenharmony_ci                        return ret;
395cabdff1aSopenharmony_ci                    wav->peak_block_pos = 0;
396cabdff1aSopenharmony_ci                }
397cabdff1aSopenharmony_ci            }
398cabdff1aSopenharmony_ci        }
399cabdff1aSopenharmony_ci    }
400cabdff1aSopenharmony_ci
401cabdff1aSopenharmony_ci    if(pkt->pts != AV_NOPTS_VALUE) {
402cabdff1aSopenharmony_ci        wav->minpts        = FFMIN(wav->minpts, pkt->pts);
403cabdff1aSopenharmony_ci        wav->maxpts        = FFMAX(wav->maxpts, pkt->pts);
404cabdff1aSopenharmony_ci        wav->last_duration = pkt->duration;
405cabdff1aSopenharmony_ci    } else
406cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "wav_write_packet: NOPTS\n");
407cabdff1aSopenharmony_ci    return 0;
408cabdff1aSopenharmony_ci}
409cabdff1aSopenharmony_ci
410cabdff1aSopenharmony_cistatic int wav_write_trailer(AVFormatContext *s)
411cabdff1aSopenharmony_ci{
412cabdff1aSopenharmony_ci    AVIOContext *pb  = s->pb;
413cabdff1aSopenharmony_ci    WAVMuxContext    *wav = s->priv_data;
414cabdff1aSopenharmony_ci    int64_t file_size, data_size;
415cabdff1aSopenharmony_ci    int64_t number_of_samples = 0;
416cabdff1aSopenharmony_ci    int rf64 = 0;
417cabdff1aSopenharmony_ci    int ret = 0;
418cabdff1aSopenharmony_ci
419cabdff1aSopenharmony_ci    if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) {
420cabdff1aSopenharmony_ci        if (wav->write_peak != PEAK_ONLY && avio_tell(pb) - wav->data < UINT32_MAX) {
421cabdff1aSopenharmony_ci            ff_end_tag(pb, wav->data);
422cabdff1aSopenharmony_ci        }
423cabdff1aSopenharmony_ci
424cabdff1aSopenharmony_ci        if (wav->write_peak && wav->peak_output) {
425cabdff1aSopenharmony_ci            ret = peak_write_chunk(s);
426cabdff1aSopenharmony_ci        }
427cabdff1aSopenharmony_ci
428cabdff1aSopenharmony_ci        /* update file size */
429cabdff1aSopenharmony_ci        file_size = avio_tell(pb);
430cabdff1aSopenharmony_ci        data_size = file_size - wav->data;
431cabdff1aSopenharmony_ci        if (wav->rf64 == RF64_ALWAYS || (wav->rf64 == RF64_AUTO && file_size - 8 > UINT32_MAX)) {
432cabdff1aSopenharmony_ci            rf64 = 1;
433cabdff1aSopenharmony_ci        } else if (file_size - 8 <= UINT32_MAX) {
434cabdff1aSopenharmony_ci            avio_seek(pb, 4, SEEK_SET);
435cabdff1aSopenharmony_ci            avio_wl32(pb, (uint32_t)(file_size - 8));
436cabdff1aSopenharmony_ci            avio_seek(pb, file_size, SEEK_SET);
437cabdff1aSopenharmony_ci        } else {
438cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR,
439cabdff1aSopenharmony_ci                   "Filesize %"PRId64" invalid for wav, output file will be broken\n",
440cabdff1aSopenharmony_ci                   file_size);
441cabdff1aSopenharmony_ci        }
442cabdff1aSopenharmony_ci        number_of_samples = av_rescale_q(wav->maxpts - wav->minpts + wav->last_duration,
443cabdff1aSopenharmony_ci                                       s->streams[0]->time_base,
444cabdff1aSopenharmony_ci                                       av_make_q(1, s->streams[0]->codecpar->sample_rate));
445cabdff1aSopenharmony_ci
446cabdff1aSopenharmony_ci        if(s->streams[0]->codecpar->codec_tag != 0x01) {
447cabdff1aSopenharmony_ci            /* Update num_samps in fact chunk */
448cabdff1aSopenharmony_ci            avio_seek(pb, wav->fact_pos, SEEK_SET);
449cabdff1aSopenharmony_ci            if (rf64 || (wav->rf64 == RF64_AUTO && number_of_samples > UINT32_MAX)) {
450cabdff1aSopenharmony_ci                rf64 = 1;
451cabdff1aSopenharmony_ci                avio_wl32(pb, -1);
452cabdff1aSopenharmony_ci            } else {
453cabdff1aSopenharmony_ci                avio_wl32(pb, number_of_samples);
454cabdff1aSopenharmony_ci                avio_seek(pb, file_size, SEEK_SET);
455cabdff1aSopenharmony_ci            }
456cabdff1aSopenharmony_ci        }
457cabdff1aSopenharmony_ci
458cabdff1aSopenharmony_ci        if (rf64) {
459cabdff1aSopenharmony_ci            /* overwrite RIFF with RF64 */
460cabdff1aSopenharmony_ci            avio_seek(pb, 0, SEEK_SET);
461cabdff1aSopenharmony_ci            ffio_wfourcc(pb, "RF64");
462cabdff1aSopenharmony_ci            avio_wl32(pb, -1);
463cabdff1aSopenharmony_ci
464cabdff1aSopenharmony_ci            /* write ds64 chunk (overwrite JUNK if rf64 == RF64_AUTO) */
465cabdff1aSopenharmony_ci            avio_seek(pb, wav->ds64 - 8, SEEK_SET);
466cabdff1aSopenharmony_ci            ffio_wfourcc(pb, "ds64");
467cabdff1aSopenharmony_ci            avio_wl32(pb, 28);                  /* ds64 chunk size */
468cabdff1aSopenharmony_ci            avio_wl64(pb, file_size - 8);       /* RF64 chunk size */
469cabdff1aSopenharmony_ci            avio_wl64(pb, data_size);           /* data chunk size */
470cabdff1aSopenharmony_ci            avio_wl64(pb, number_of_samples);   /* fact chunk number of samples */
471cabdff1aSopenharmony_ci            avio_wl32(pb, 0);                   /* number of table entries for non-'data' chunks */
472cabdff1aSopenharmony_ci
473cabdff1aSopenharmony_ci            /* write -1 in data chunk size */
474cabdff1aSopenharmony_ci            avio_seek(pb, wav->data - 4, SEEK_SET);
475cabdff1aSopenharmony_ci            avio_wl32(pb, -1);
476cabdff1aSopenharmony_ci
477cabdff1aSopenharmony_ci            avio_seek(pb, file_size, SEEK_SET);
478cabdff1aSopenharmony_ci        }
479cabdff1aSopenharmony_ci    }
480cabdff1aSopenharmony_ci
481cabdff1aSopenharmony_ci    return ret;
482cabdff1aSopenharmony_ci}
483cabdff1aSopenharmony_ci
484cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(WAVMuxContext, x)
485cabdff1aSopenharmony_ci#define ENC AV_OPT_FLAG_ENCODING_PARAM
486cabdff1aSopenharmony_cistatic const AVOption options[] = {
487cabdff1aSopenharmony_ci    { "write_bext", "Write BEXT chunk.", OFFSET(write_bext), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC },
488cabdff1aSopenharmony_ci    { "write_peak", "Write Peak Envelope chunk.",            OFFSET(write_peak), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, ENC, "peak" },
489cabdff1aSopenharmony_ci    { "off",        "Do not write peak chunk.",              0,                  AV_OPT_TYPE_CONST, { .i64 = PEAK_OFF  }, 0, 0, ENC, "peak" },
490cabdff1aSopenharmony_ci    { "on",         "Append peak chunk after wav data.",     0,                  AV_OPT_TYPE_CONST, { .i64 = PEAK_ON   }, 0, 0, ENC, "peak" },
491cabdff1aSopenharmony_ci    { "only",       "Write only peak chunk, omit wav data.", 0,                  AV_OPT_TYPE_CONST, { .i64 = PEAK_ONLY }, 0, 0, ENC, "peak" },
492cabdff1aSopenharmony_ci    { "rf64",       "Use RF64 header rather than RIFF for large files.",    OFFSET(rf64), AV_OPT_TYPE_INT,   { .i64 = RF64_NEVER  },-1, 1, ENC, "rf64" },
493cabdff1aSopenharmony_ci    { "auto",       "Write RF64 header if file grows large enough.",        0,            AV_OPT_TYPE_CONST, { .i64 = RF64_AUTO   }, 0, 0, ENC, "rf64" },
494cabdff1aSopenharmony_ci    { "always",     "Always write RF64 header regardless of file size.",    0,            AV_OPT_TYPE_CONST, { .i64 = RF64_ALWAYS }, 0, 0, ENC, "rf64" },
495cabdff1aSopenharmony_ci    { "never",      "Never write RF64 header regardless of file size.",     0,            AV_OPT_TYPE_CONST, { .i64 = RF64_NEVER  }, 0, 0, ENC, "rf64" },
496cabdff1aSopenharmony_ci    { "peak_block_size", "Number of audio samples used to generate each peak frame.",   OFFSET(peak_block_size), AV_OPT_TYPE_INT, { .i64 = 256 }, 0, 65536, ENC },
497cabdff1aSopenharmony_ci    { "peak_format",     "The format of the peak envelope data (1: uint8, 2: uint16).", OFFSET(peak_format), AV_OPT_TYPE_INT,     { .i64 = PEAK_FORMAT_UINT16 }, PEAK_FORMAT_UINT8, PEAK_FORMAT_UINT16, ENC },
498cabdff1aSopenharmony_ci    { "peak_ppv",        "Number of peak points per peak value (1 or 2).",              OFFSET(peak_ppv), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, 2, ENC },
499cabdff1aSopenharmony_ci    { NULL },
500cabdff1aSopenharmony_ci};
501cabdff1aSopenharmony_ci
502cabdff1aSopenharmony_cistatic const AVClass wav_muxer_class = {
503cabdff1aSopenharmony_ci    .class_name = "WAV muxer",
504cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
505cabdff1aSopenharmony_ci    .option     = options,
506cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
507cabdff1aSopenharmony_ci};
508cabdff1aSopenharmony_ci
509cabdff1aSopenharmony_ciconst AVOutputFormat ff_wav_muxer = {
510cabdff1aSopenharmony_ci    .name              = "wav",
511cabdff1aSopenharmony_ci    .long_name         = NULL_IF_CONFIG_SMALL("WAV / WAVE (Waveform Audio)"),
512cabdff1aSopenharmony_ci    .mime_type         = "audio/x-wav",
513cabdff1aSopenharmony_ci    .extensions        = "wav",
514cabdff1aSopenharmony_ci    .priv_data_size    = sizeof(WAVMuxContext),
515cabdff1aSopenharmony_ci    .audio_codec       = AV_CODEC_ID_PCM_S16LE,
516cabdff1aSopenharmony_ci    .video_codec       = AV_CODEC_ID_NONE,
517cabdff1aSopenharmony_ci    .write_header      = wav_write_header,
518cabdff1aSopenharmony_ci    .write_packet      = wav_write_packet,
519cabdff1aSopenharmony_ci    .write_trailer     = wav_write_trailer,
520cabdff1aSopenharmony_ci    .deinit            = wav_deinit,
521cabdff1aSopenharmony_ci    .flags             = AVFMT_TS_NONSTRICT,
522cabdff1aSopenharmony_ci    .codec_tag         = ff_wav_codec_tags_list,
523cabdff1aSopenharmony_ci    .priv_class        = &wav_muxer_class,
524cabdff1aSopenharmony_ci};
525cabdff1aSopenharmony_ci#endif /* CONFIG_WAV_MUXER */
526cabdff1aSopenharmony_ci
527cabdff1aSopenharmony_ci#if CONFIG_W64_MUXER
528cabdff1aSopenharmony_ci#include "w64.h"
529cabdff1aSopenharmony_ci
530cabdff1aSopenharmony_cistatic void start_guid(AVIOContext *pb, const uint8_t *guid, int64_t *pos)
531cabdff1aSopenharmony_ci{
532cabdff1aSopenharmony_ci    *pos = avio_tell(pb);
533cabdff1aSopenharmony_ci
534cabdff1aSopenharmony_ci    avio_write(pb, guid, 16);
535cabdff1aSopenharmony_ci    avio_wl64(pb, INT64_MAX);
536cabdff1aSopenharmony_ci}
537cabdff1aSopenharmony_ci
538cabdff1aSopenharmony_cistatic void end_guid(AVIOContext *pb, int64_t start)
539cabdff1aSopenharmony_ci{
540cabdff1aSopenharmony_ci    int64_t end, pos = avio_tell(pb);
541cabdff1aSopenharmony_ci
542cabdff1aSopenharmony_ci    end = FFALIGN(pos, 8);
543cabdff1aSopenharmony_ci    ffio_fill(pb, 0, end - pos);
544cabdff1aSopenharmony_ci    avio_seek(pb, start + 16, SEEK_SET);
545cabdff1aSopenharmony_ci    avio_wl64(pb, end - start);
546cabdff1aSopenharmony_ci    avio_seek(pb, end, SEEK_SET);
547cabdff1aSopenharmony_ci}
548cabdff1aSopenharmony_ci
549cabdff1aSopenharmony_cistatic int w64_write_header(AVFormatContext *s)
550cabdff1aSopenharmony_ci{
551cabdff1aSopenharmony_ci    WAVMuxContext *wav = s->priv_data;
552cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
553cabdff1aSopenharmony_ci    int64_t start;
554cabdff1aSopenharmony_ci    int ret;
555cabdff1aSopenharmony_ci
556cabdff1aSopenharmony_ci    avio_write(pb, ff_w64_guid_riff, sizeof(ff_w64_guid_riff));
557cabdff1aSopenharmony_ci    avio_wl64(pb, -1);
558cabdff1aSopenharmony_ci    avio_write(pb, ff_w64_guid_wave, sizeof(ff_w64_guid_wave));
559cabdff1aSopenharmony_ci    start_guid(pb, ff_w64_guid_fmt, &start);
560cabdff1aSopenharmony_ci    if ((ret = ff_put_wav_header(s, pb, s->streams[0]->codecpar, 0)) < 0) {
561cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Codec %s not supported\n",
562cabdff1aSopenharmony_ci               avcodec_get_name(s->streams[0]->codecpar->codec_id));
563cabdff1aSopenharmony_ci        return ret;
564cabdff1aSopenharmony_ci    }
565cabdff1aSopenharmony_ci    end_guid(pb, start);
566cabdff1aSopenharmony_ci
567cabdff1aSopenharmony_ci    if (s->streams[0]->codecpar->codec_tag != 0x01 /* hence for all other than PCM */
568cabdff1aSopenharmony_ci        && (s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
569cabdff1aSopenharmony_ci        start_guid(pb, ff_w64_guid_fact, &wav->fact_pos);
570cabdff1aSopenharmony_ci        avio_wl64(pb, 0);
571cabdff1aSopenharmony_ci        end_guid(pb, wav->fact_pos);
572cabdff1aSopenharmony_ci    }
573cabdff1aSopenharmony_ci
574cabdff1aSopenharmony_ci    start_guid(pb, ff_w64_guid_data, &wav->data);
575cabdff1aSopenharmony_ci
576cabdff1aSopenharmony_ci    return 0;
577cabdff1aSopenharmony_ci}
578cabdff1aSopenharmony_ci
579cabdff1aSopenharmony_cistatic int w64_write_trailer(AVFormatContext *s)
580cabdff1aSopenharmony_ci{
581cabdff1aSopenharmony_ci    AVIOContext    *pb = s->pb;
582cabdff1aSopenharmony_ci    WAVMuxContext *wav = s->priv_data;
583cabdff1aSopenharmony_ci    int64_t file_size;
584cabdff1aSopenharmony_ci
585cabdff1aSopenharmony_ci    if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
586cabdff1aSopenharmony_ci        end_guid(pb, wav->data);
587cabdff1aSopenharmony_ci
588cabdff1aSopenharmony_ci        file_size = avio_tell(pb);
589cabdff1aSopenharmony_ci        avio_seek(pb, 16, SEEK_SET);
590cabdff1aSopenharmony_ci        avio_wl64(pb, file_size);
591cabdff1aSopenharmony_ci
592cabdff1aSopenharmony_ci        if (s->streams[0]->codecpar->codec_tag != 0x01) {
593cabdff1aSopenharmony_ci            int64_t number_of_samples;
594cabdff1aSopenharmony_ci
595cabdff1aSopenharmony_ci            number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration,
596cabdff1aSopenharmony_ci                                           s->streams[0]->codecpar->sample_rate * (int64_t)s->streams[0]->time_base.num,
597cabdff1aSopenharmony_ci                                           s->streams[0]->time_base.den);
598cabdff1aSopenharmony_ci            avio_seek(pb, wav->fact_pos + 24, SEEK_SET);
599cabdff1aSopenharmony_ci            avio_wl64(pb, number_of_samples);
600cabdff1aSopenharmony_ci        }
601cabdff1aSopenharmony_ci
602cabdff1aSopenharmony_ci        avio_seek(pb, file_size, SEEK_SET);
603cabdff1aSopenharmony_ci    }
604cabdff1aSopenharmony_ci
605cabdff1aSopenharmony_ci    return 0;
606cabdff1aSopenharmony_ci}
607cabdff1aSopenharmony_ci
608cabdff1aSopenharmony_ciconst AVOutputFormat ff_w64_muxer = {
609cabdff1aSopenharmony_ci    .name              = "w64",
610cabdff1aSopenharmony_ci    .long_name         = NULL_IF_CONFIG_SMALL("Sony Wave64"),
611cabdff1aSopenharmony_ci    .extensions        = "w64",
612cabdff1aSopenharmony_ci    .priv_data_size    = sizeof(WAVMuxContext),
613cabdff1aSopenharmony_ci    .audio_codec       = AV_CODEC_ID_PCM_S16LE,
614cabdff1aSopenharmony_ci    .video_codec       = AV_CODEC_ID_NONE,
615cabdff1aSopenharmony_ci    .write_header      = w64_write_header,
616cabdff1aSopenharmony_ci    .write_packet      = wav_write_packet,
617cabdff1aSopenharmony_ci    .write_trailer     = w64_write_trailer,
618cabdff1aSopenharmony_ci    .flags             = AVFMT_TS_NONSTRICT,
619cabdff1aSopenharmony_ci    .codec_tag         = ff_wav_codec_tags_list,
620cabdff1aSopenharmony_ci};
621cabdff1aSopenharmony_ci#endif /* CONFIG_W64_MUXER */
622