/*
 * audio filter for runtime AC-3 encoding with libavcodec.
 *
 * Copyright (C) 2007 Ulion <ulion A gmail P com>
 *
 * This file is part of mpv.
 *
 * mpv is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * mpv is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>

#include <libavcodec/avcodec.h>
#include <libavutil/intreadwrite.h>
#include <libavutil/common.h>
#include <libavutil/bswap.h>
#include <libavutil/mem.h>

#include "config.h"

#include "audio/aframe.h"
#include "audio/chmap_avchannel.h"
#include "audio/chmap_sel.h"
#include "audio/fmt-conversion.h"
#include "audio/format.h"
#include "common/av_common.h"
#include "common/common.h"
#include "filters/f_autoconvert.h"
#include "filters/f_utils.h"
#include "filters/filter_internal.h"
#include "filters/user_filters.h"
#include "options/m_option.h"
#include "misc/lavc_compat.h"


#define AC3_MAX_CHANNELS 6
#define AC3_MAX_CODED_FRAME_SIZE 3840
#define AC3_FRAME_SIZE (6  * 256)
static const uint16_t ac3_bitrate_tab[19] = {
    32, 40, 48, 56, 64, 80, 96, 112, 128,
    160, 192, 224, 256, 320, 384, 448, 512, 576, 640
};

struct f_opts {
    bool add_iec61937_header;
    int bit_rate;
    int min_channel_num;
    char *encoder;
    char **avopts;
};

struct priv {
    struct f_opts *opts;

    struct mp_pin *in_pin;
    struct mp_aframe *cur_format;
    struct mp_aframe *in_frame;
    struct mp_aframe_pool *out_pool;

    const struct AVCodec  *lavc_acodec;
    struct AVCodecContext *lavc_actx;
    AVPacket              *lavc_pkt;
    int bit_rate;
    int out_samples;    // upper bound on encoded output per AC3 frame
};

static bool reinit(struct mp_filter *f)
{
    struct priv *s = f->priv;

    mp_aframe_reset(s->cur_format);

    static const int default_bit_rate[AC3_MAX_CHANNELS+1] = \
        {0, 96000, 192000, 256000, 384000, 448000, 448000};

    if (s->opts->add_iec61937_header) {
        s->out_samples = AC3_FRAME_SIZE;
    } else {
        s->out_samples = AC3_MAX_CODED_FRAME_SIZE /
                         mp_aframe_get_sstride(s->in_frame);
    }

    int format = mp_aframe_get_format(s->in_frame);
    int rate = mp_aframe_get_rate(s->in_frame);
    struct mp_chmap chmap = {0};
    mp_aframe_get_chmap(s->in_frame, &chmap);

    int bit_rate = s->bit_rate;
    if (!bit_rate && chmap.num < AC3_MAX_CHANNELS + 1)
        bit_rate = default_bit_rate[chmap.num];

    avcodec_free_context(&s->lavc_actx);
    s->lavc_actx = avcodec_alloc_context3(s->lavc_acodec);
    if (!s->lavc_actx) {
        MP_ERR(f, "Audio LAVC, couldn't reallocate context!\n");
        return false;
    }

    if (mp_set_avopts(f->log, s->lavc_actx, s->opts->avopts) < 0)
        return false;

    // Put sample parameters
    s->lavc_actx->sample_fmt = af_to_avformat(format);

    mp_chmap_to_av_layout(&s->lavc_actx->ch_layout, &chmap);
    s->lavc_actx->sample_rate = rate;
    s->lavc_actx->bit_rate = bit_rate;

    if (avcodec_open2(s->lavc_actx, s->lavc_acodec, NULL) < 0) {
        MP_ERR(f, "Couldn't open codec %s, br=%d.\n", "ac3", bit_rate);
        return false;
    }

    if (s->lavc_actx->frame_size < 1) {
        MP_ERR(f, "encoder didn't specify input frame size\n");
        return false;
    }

    mp_aframe_config_copy(s->cur_format, s->in_frame);
    return true;
}

static void af_lavcac3enc_reset(struct mp_filter *f)
{
    struct priv *s = f->priv;

    TA_FREEP(&s->in_frame);
}

static void af_lavcac3enc_destroy(struct mp_filter *f)
{
    struct priv *s = f->priv;

    af_lavcac3enc_reset(f);
    av_packet_free(&s->lavc_pkt);
    avcodec_free_context(&s->lavc_actx);
}

static void swap_16(uint16_t *ptr, size_t size)
{
    for (size_t n = 0; n < size; n++)
        ptr[n] = av_bswap16(ptr[n]);
}

static void af_lavcac3enc_process(struct mp_filter *f)
{
    struct priv *s = f->priv;

    if (!mp_pin_in_needs_data(f->ppins[1]))
        return;

    bool err = true;
    struct mp_aframe *out = NULL;
    AVPacket *pkt = s->lavc_pkt;

    // Send input as long as it wants.
    while (1) {
        if (avcodec_is_open(s->lavc_actx)) {
            int lavc_ret = avcodec_receive_packet(s->lavc_actx, pkt);
            if (lavc_ret >= 0)
                break;
            if (lavc_ret < 0 && lavc_ret != AVERROR(EAGAIN)) {
                MP_FATAL(f, "Encode failed (receive).\n");
                goto error;
            }
        }
        AVFrame *frame = NULL;
        struct mp_frame input = mp_pin_out_read(s->in_pin);
        // The following code assumes no sample data buffering in the encoder.
        switch (input.type) {
        case MP_FRAME_NONE:
            goto done; // no data yet
        case MP_FRAME_EOF:
            mp_pin_in_write(f->ppins[1], input);
            goto done;
        case MP_FRAME_AUDIO:
            TA_FREEP(&s->in_frame);
            s->in_frame = input.data;
            if (mp_aframe_get_channels(s->in_frame) < s->opts->min_channel_num) {
                // Just pass it through.
                s->in_frame = NULL;
                mp_pin_in_write(f->ppins[1], input);
                goto done;
            }
            if (!mp_aframe_config_equals(s->in_frame, s->cur_format)) {
                if (!reinit(f))
                    goto error;
            }
            frame = mp_frame_to_av(input, NULL);
            if (!frame)
                goto error;
            break;
        default: goto error; // unexpected packet type
        }
        int lavc_ret = avcodec_send_frame(s->lavc_actx, frame);
        av_frame_free(&frame);
        if (lavc_ret < 0 && lavc_ret != AVERROR(EAGAIN)) {
            MP_FATAL(f, "Encode failed (send).\n");
            goto error;
        }
    }

    if (!s->in_frame)
        goto error;

    out = mp_aframe_create();
    mp_aframe_set_format(out, AF_FORMAT_S_AC3);
    mp_aframe_set_chmap(out, &(struct mp_chmap)MP_CHMAP_INIT_STEREO);
    mp_aframe_set_rate(out, 48000);

    if (mp_aframe_pool_allocate(s->out_pool, out, s->out_samples) < 0)
        goto error;

    int sstride = mp_aframe_get_sstride(out);

    mp_aframe_copy_attributes(out, s->in_frame);

    int frame_size = pkt->size;
    int header_len = 0;
    char hdr[8];

    if (s->opts->add_iec61937_header && pkt->size > 5) {
        int bsmod = pkt->data[5] & 0x7;
        int len = frame_size;

        frame_size = AC3_FRAME_SIZE * 2 * 2;
        header_len = 8;

        AV_WL16(hdr,     0xF872);   // iec 61937 syncword 1
        AV_WL16(hdr + 2, 0x4E1F);   // iec 61937 syncword 2
        hdr[5] = bsmod;             // bsmod
        hdr[4] = 0x01;              // data-type ac3
        AV_WL16(hdr + 6, len << 3); // number of bits in payload
    }

    if (frame_size > s->out_samples * sstride)
        abort();

    uint8_t **planes = mp_aframe_get_data_rw(out);
    if (!planes)
        goto error;
    char *buf = planes[0];
    memcpy(buf, hdr, header_len);
    memcpy(buf + header_len, pkt->data, pkt->size);
    memset(buf + header_len + pkt->size, 0,
           frame_size - (header_len + pkt->size));
    swap_16((uint16_t *)(buf + header_len), pkt->size / 2);
    mp_aframe_set_size(out, frame_size / sstride);
    mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out));
    out = NULL;

done:
    err = false;
    // fall through
error:
    av_packet_unref(pkt);
    talloc_free(out);
    if (err)
        mp_filter_internal_mark_failed(f);
}

static const struct mp_filter_info af_lavcac3enc_filter = {
    .name = "lavcac3enc",
    .priv_size = sizeof(struct priv),
    .process = af_lavcac3enc_process,
    .reset = af_lavcac3enc_reset,
    .destroy = af_lavcac3enc_destroy,
};

static void add_chmaps_to_autoconv(struct mp_filter *f,
                                   struct mp_autoconvert *conv,
                                   const AVCodecContext *avctx)
{
    const AVChannelLayout *lch;
    int ret = mp_avcodec_get_supported_config(avctx, NULL,
                                              AV_CODEC_CONFIG_CHANNEL_LAYOUT,
                                              (const void **)&lch);

    for (int n = 0; ret >= 0 && lch && lch[n].nb_channels; n++) {
        struct mp_chmap chmap = {0};

        if (!mp_chmap_from_av_layout(&chmap, &lch[n])) {
            char layout[128] = {0};
            MP_VERBOSE(f, "Skipping unsupported channel layout: %s\n",
                       av_channel_layout_describe(&lch[n],
                                                  layout, 128) < 0 ?
                       "undefined" : layout);
            continue;
        }

        if (mp_chmap_is_valid(&chmap))
            mp_autoconvert_add_chmap(conv, &chmap);
    }
}

static struct mp_filter *af_lavcac3enc_create(struct mp_filter *parent,
                                              void *options)
{
    struct mp_filter *f = mp_filter_create(parent, &af_lavcac3enc_filter);
    if (!f) {
        talloc_free(options);
        return NULL;
    }

    mp_filter_add_pin(f, MP_PIN_IN, "in");
    mp_filter_add_pin(f, MP_PIN_OUT, "out");

    struct priv *s = f->priv;
    s->opts = talloc_steal(s, options);
    s->cur_format = talloc_steal(s, mp_aframe_create());
    s->out_pool = mp_aframe_pool_create(s);

    s->lavc_acodec = avcodec_find_encoder_by_name(s->opts->encoder);
    if (!s->lavc_acodec) {
        MP_ERR(f, "Couldn't find encoder %s.\n", s->opts->encoder);
        goto error;
    }

    s->lavc_actx = avcodec_alloc_context3(s->lavc_acodec);
    if (!s->lavc_actx) {
        MP_ERR(f, "Audio LAVC, couldn't allocate context!\n");
        goto error;
    }

    s->lavc_pkt = av_packet_alloc();
    if (!s->lavc_pkt)
        goto error;

    if (mp_set_avopts(f->log, s->lavc_actx, s->opts->avopts) < 0)
        goto error;

    const AVChannelLayout *ch_layouts;
    int ret_ch = mp_avcodec_get_supported_config(s->lavc_actx, s->lavc_acodec,
                                                 AV_CODEC_CONFIG_CHANNEL_LAYOUT,
                                                 (const void **)&ch_layouts);

    const enum AVSampleFormat *sample_fmts;
    int ret_fmt = mp_avcodec_get_supported_config(s->lavc_actx, s->lavc_acodec,
                                                  AV_CODEC_CONFIG_SAMPLE_FORMAT,
                                                  (const void **)&sample_fmts);

    // For this one, we require the decoder to export lists of all supported
    // parameters. (Not all decoders do that, but the ones we're interested
    // in do.)
    if (ret_ch < 0 || !ch_layouts || ret_fmt < 0 || !sample_fmts) {
        MP_ERR(f, "Audio encoder doesn't list supported parameters.\n");
        goto error;
    }

    if (s->opts->bit_rate) {
        int i;
        for (i = 0; i < 19; i++) {
            if (ac3_bitrate_tab[i] == s->opts->bit_rate) {
                s->bit_rate = ac3_bitrate_tab[i] * 1000;
                break;
            }
        }
        if (i >= 19) {
            MP_WARN(f, "unable set unsupported bitrate %d, using default "
                    "bitrate (check manpage to see supported bitrates).\n",
                    s->opts->bit_rate);
        }
    }

    struct mp_autoconvert *conv = mp_autoconvert_create(f);
    if (!conv)
        abort();

    for (int i = 0; sample_fmts[i] != AV_SAMPLE_FMT_NONE; i++) {
        int mpfmt = af_from_avformat(sample_fmts[i]);
        if (mpfmt)
            mp_autoconvert_add_afmt(conv, mpfmt);
    }

    add_chmaps_to_autoconv(f, conv, s->lavc_actx);

    // At least currently, the AC3 encoder doesn't export sample rates.
    mp_autoconvert_add_srate(conv, 48000);

    mp_pin_connect(conv->f->pins[0], f->ppins[0]);

    struct mp_filter *fs = mp_fixed_aframe_size_create(f, AC3_FRAME_SIZE, true);
    if (!fs)
        abort();

    mp_pin_connect(fs->pins[0], conv->f->pins[1]);
    s->in_pin = fs->pins[1];

    return f;

error:
    av_packet_free(&s->lavc_pkt);
    avcodec_free_context(&s->lavc_actx);
    talloc_free(f);
    return NULL;
}

#define OPT_BASE_STRUCT struct f_opts

const struct mp_user_filter_entry af_lavcac3enc = {
    .desc = {
        .description = "runtime encode to ac3 using libavcodec",
        .name = "lavcac3enc",
        .priv_size = sizeof(OPT_BASE_STRUCT),
        .priv_defaults = &(const OPT_BASE_STRUCT) {
            .add_iec61937_header = true,
            .bit_rate = 640,
            .min_channel_num = 3,
            .encoder = "ac3",
        },
        .options = (const struct m_option[]) {
            {"tospdif", OPT_BOOL(add_iec61937_header)},
            {"bitrate", OPT_CHOICE(bit_rate,
                {"auto", 0}, {"default", 0}), M_RANGE(32, 640)},
            {"minch", OPT_INT(min_channel_num), M_RANGE(2, 6)},
            {"encoder", OPT_STRING(encoder)},
            {"o", OPT_KEYVALUELIST(avopts)},
            {0}
        },
    },
    .create = af_lavcac3enc_create,
};
