package com.yanzuoguang.util;

import com.yanzuoguang.util.helper.StringHelper;
import com.yanzuoguang.util.log.Log;
import com.yanzuoguang.util.vo.BaseVo;
import io.swagger.annotations.ApiModelProperty;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;

import static org.bytedeco.ffmpeg.global.avcodec.*;
import static org.bytedeco.ffmpeg.global.avutil.*;

/**
 * 压缩视频参数
 *
 * @author 颜佐光
 */
public class MediaParameter extends BaseVo {

    public static final int VIDEO_CODEC_MP4 = AV_CODEC_ID_H264;
    public static final String VIDEO_FORMAT_MP4 = "mp4";
    public static final int VIDEO_FIXED_MP4 = AV_PIX_FMT_YUV420P;
    public static final int SOUND_CODEC_MP3 = AV_CODEC_ID_VORBIS;
    public static final String ROTATE = "rotate";

    /**
     * 音频转码后的格式,为空时表示不改变格式
     */
    @ApiModelProperty(notes = "音频转码后的格式,为空时表示不改变格式")
    private int audioCodec = AV_CODEC_ID_NONE;
    /**
     * 音频帧速率FrameRate
     */
    @ApiModelProperty(notes = "音频转码后的格式,为空时表示不改变格式")
    private double audioFrameRate = AV_SAMPLE_FMT_NONE;
    /**
     * 音频转码的比特率,为0时表示采取原来的值
     */
    @ApiModelProperty(notes = "音频转码的比特率,为0时表示采取原来的值")
    private int audioBitRate;
    /**
     * 音频最小比率率,为0时表示最小无效
     */
    @ApiModelProperty(notes = "音频最小比率率,为0时表示最小无效")
    private int minAudioBitRate;
    /**
     * 音频比特率压缩比,算法为: 最终比特率= Math.max(minAudioBitRate,audioBitRate * audioBitRateZip)
     */
    @ApiModelProperty(notes = "音频比特率压缩比,算法为: 最终比特率= Math.max(minAudioBitRate,audioBitRate * audioBitRateZip)")
    private float audioBitRateZip = 0;
    /**
     * 视频转码后的格式
     */
    @ApiModelProperty(notes = "视频转码后的格式")
    private String videoFormat;
    /**
     * 视频转码后的央视
     */
    @ApiModelProperty(notes = "视频转码后的央视")
    private int videoCodeC = AV_CODEC_ID_NONE;

    /**
     * 视频像素格式
     */
    @ApiModelProperty(notes = "视频像素格式")
    private int videoPixelFormat = AV_PIX_FMT_NONE;
    /**
     * 视频宽度,为0时默认为原来的宽度
     */
    @ApiModelProperty(notes = "视频宽度,为0时默认为原来的宽度")
    private int videoWidth;
    /**
     * 视频高度,为0时默认为原来的高度
     */
    @ApiModelProperty(notes = "视频高度,为0时默认为原来的高度")
    private int videoHeight;
    /**
     * 最小视频宽度
     */
    @ApiModelProperty(notes = "最小视频宽度")
    private int minVideoWidth;
    /**
     * 最小视频高度
     */
    @ApiModelProperty(notes = "最小视频高度")
    private int minVideoHeight;
    /**
     * 视频帧速率FrameRate
     */
    @ApiModelProperty(notes = "视频帧速率FrameRate")
    private double videoFrameRate;
    /**
     * 视频原角度
     */
    @ApiModelProperty(notes = "视频原角度")
    private double videoRotate;
    /**
     * 视频变更角度,最终角度=视频原角度+视频变更角度
     */
    @ApiModelProperty(notes = "视频变更角度,最终角度=视频原角度+视频变更角度")
    private double videoRotateChange;
    /**
     * 视频宽度高度压缩比,算法为: 最终宽度=Math.max(minVideoWidth,videoWidth * videoSizeZip),
     * *                        最终高度:=Math.max(minVideoHeight,videoHeight * videoSizeZip)
     */
    @ApiModelProperty(notes = "视频宽度高度压缩比,算法为: 最终宽度=Math.max(minVideoWidth,videoWidth * videoSizeZip)," +
            "最终高度:=Math.max(minVideoHeight,videoHeight * videoSizeZip)")
    private float videoSizeZip = 0;
    /**
     * 视频转码的比特率,为0时表示采取原来的值
     */
    @ApiModelProperty(notes = "视频转码的比特率,为0时表示采取原来的值")
    private int videoBitRate;
    /**
     * 视频最小比率率,为0时表示最小无效
     */
    @ApiModelProperty(notes = "视频最小比率率,为0时表示最小无效")
    private int minVideoBitRate;
    /**
     * 视频比特率压缩比,算法为: 最终比特率= Math.max(minAudioBitRate,audioBitRate * audioBitRateZip)
     */
    @ApiModelProperty(notes = "视频比特率压缩比,算法为: 最终比特率= Math.max(minAudioBitRate,audioBitRate * audioBitRateZip)")
    private float videoBitRateZip = 0;

    /**
     * 默认构造函数
     */
    public MediaParameter() {
    }

    /**
     * 视频压缩
     *
     * @param videoSizeZip    宽,高,压缩比
     * @param videoBitRateZip 比特率压缩比,相当于视频质量
     */
    public MediaParameter(float videoSizeZip, float videoBitRateZip) {
        this.videoSizeZip = videoSizeZip;
        this.videoBitRateZip = videoBitRateZip;
    }

    /**
     * 视频压缩
     *
     * @param audioBitRateZip 音频质量压缩比
     * @param videoSizeZip    宽,高,压缩比
     * @param videoBitRateZip 比特率压缩比,相当于视频质量
     */
    public MediaParameter(float audioBitRateZip, float videoSizeZip, float videoBitRateZip) {
        this.audioBitRateZip = audioBitRateZip;
        this.videoSizeZip = videoSizeZip;
        this.videoBitRateZip = videoBitRateZip;
    }

    /**
     * 音频最终比特率
     *
     * @return
     */
    public int getAudioBitRateFinally() {
        return getMinValue(this.minAudioBitRate, this.audioBitRate, this.audioBitRateZip);
    }

    /**
     * 视频最终比特率
     *
     * @return
     */
    public int getVideoBitRateFinally() {
        return getMinValue(this.minVideoBitRate, this.videoBitRate, this.videoBitRateZip);
    }

    /**
     * 音频最终宽度
     *
     * @return
     */
    public int getVideoWidthFinally() {
        return getWidthHeightFinally(true);
    }

    /**
     * 音频最终高度
     *
     * @return
     */
    public int getVideoHeightFinally() {
        return getWidthHeightFinally(false);
    }

    private int getWidthHeightFinally(boolean width) {
        // 最大宽度和最大高度
        int maxWidth = Math.max(this.videoWidth, this.videoHeight);
        int maxHeight = Math.min(this.videoWidth, this.videoHeight);
        // 获取允许的最小宽度和高度
        int minWidth = Math.max(this.minVideoWidth, this.minVideoHeight);
        int minHeight = Math.min(this.minVideoWidth, this.minVideoHeight);
        // 计算最小宽度和高度
        int calcMinWidth = maxWidth * minHeight / maxHeight;
        int calcMinHeight = maxHeight * minWidth / maxWidth;
        // 设置最小的宽高
        if (minWidth == 0) {
            minWidth = calcMinWidth;
            calcMinHeight = minHeight;
        }
        if (minHeight == 0) {
            minHeight = calcMinHeight;
            calcMinWidth = minWidth;
        }

        // 判断宽高比列
        if (calcMinHeight > minHeight) {
            minHeight = calcMinHeight;
        } else {
            minWidth = calcMinWidth;
        }

        if (width) {
            int minValue = this.videoWidth > this.videoHeight ? minWidth : minHeight;
            return getMinValue(minValue, this.videoWidth, this.videoSizeZip);
        } else {
            int minValue = this.videoWidth < this.videoHeight ? minWidth : minHeight;
            return getMinValue(minValue, this.videoHeight, this.videoSizeZip);
        }
    }

    private int getMinValue(int min, int from, float zip) {
        if (from < min) {
            return from;
        } else {
            return Math.max(min, (int) (from * zip));
        }
    }

    /**
     * 音频最终高度
     *
     * @return
     */
    public double getVideoRotateFinally() {
        return this.videoRotate + this.videoRotateChange;
    }

    /**
     * 检查参数
     */
    public void check() {
        this.audioBitRateZip = StringHelper.getFirst(this.audioBitRateZip, 1f);
        this.videoSizeZip = StringHelper.getFirst(this.videoSizeZip, 1f);
        this.videoBitRateZip = StringHelper.getFirst(this.videoBitRateZip, 1f);
        if (audioBitRateZip > 1) {
            throw new RuntimeException("audioBitRateZip请在0~1之间");
        }
        if (videoSizeZip > 1) {
            throw new RuntimeException("videoSizeZip请在0~1之间");
        }
        if (videoBitRateZip > 1) {
            throw new RuntimeException("videoBitRateZip请在0~1之间");
        }
    }

    /**
     * 设置默认参数
     *
     * @param grabber 参数的视频
     */
    public void init(FFmpegFrameGrabber grabber) {
        this.check();
        // 读取音频属性
        this.audioBitRate = StringHelper.getFirst(this.audioBitRate, grabber.getAudioBitrate());
        this.audioCodec = StringHelper.getFirstRun(AV_CODEC_ID_NONE, AV_CODEC_ID_NONE, this.audioCodec, grabber.getAudioCodec());
        Log.info(MediaParameter.class, "getAudioCodecName:%s", grabber.getAudioCodecName());
        this.audioFrameRate = StringHelper.getFirstRun((double) AV_SAMPLE_FMT_NONE, (double) AV_SAMPLE_FMT_NONE, this.audioFrameRate, grabber.getAudioFrameRate());

        // 视频宽度高度
        this.videoWidth = StringHelper.getFirst(this.videoWidth, grabber.getImageWidth());
        this.videoHeight = StringHelper.getFirst(this.videoHeight, grabber.getImageHeight());
        // 读取视频格式
        this.videoFormat = StringHelper.getFirst(this.videoFormat, grabber.getFormat());
        this.videoCodeC = StringHelper.getFirstRun(AV_CODEC_ID_NONE, AV_CODEC_ID_NONE, this.videoCodeC, grabber.getVideoCodec());
        Log.info(MediaParameter.class, "getVideoCodecName:%s", grabber.getVideoCodecName());
        this.videoPixelFormat = StringHelper.getFirstRun(AV_PIX_FMT_NONE, AV_PIX_FMT_NONE, this.videoPixelFormat, grabber.getPixelFormat());
        // 读取视频属性
        this.videoRotate = StringHelper.toDouble(grabber.getVideoMetadata(ROTATE));
        this.videoFrameRate = StringHelper.getFirst(this.videoFrameRate, grabber.getVideoFrameRate());
        this.videoBitRate = StringHelper.getFirst(this.videoBitRate, grabber.getVideoBitrate());
    }

    /**
     * 设置转码参数
     *
     * @param recorder 需要转码的对象
     */
    public void init(FFmpegFrameGrabber grabber, FFmpegFrameRecorder recorder) {
        recorder.setTimestamp(grabber.getTimestamp());
        recorder.setFormat(grabber.getFormat());
        recorder.setOptions(grabber.getOptions());

        recorder.setMetadata(grabber.getMetadata());
        recorder.setAudioMetadata(grabber.getAudioMetadata());
        recorder.setVideoMetadata(grabber.getVideoMetadata());

        recorder.setAspectRatio(recorder.getAspectRatio());
        recorder.setAudioOptions(recorder.getAudioOptions());

        recorder.setVideoOptions(grabber.getVideoOptions());

        // 设置音频比特率
        recorder.setAudioBitrate(this.getAudioBitRateFinally());
        // recorder.setAudioQuality(this.audioBitRateZip);
        /*
        recorder.setAudioCodec(this.audioCodec);
        // 设置声道
        recorder.setAudioChannels(grabber.getAudioChannels());
        // 设置声音质量
        recorder.setAudioQuality(recorder.getAudioQuality());
        // 设置音频属性
        recorder.setSampleFormat(grabber.getSampleFormat());
        recorder.setSampleRate(grabber.getSampleRate());
        /* */

        // 视频格式
        recorder.setFormat(this.videoFormat);
        // 设置转码视频格式
        recorder.setVideoCodec(this.videoCodeC);
        // 设置像素格式
        recorder.setPixelFormat(this.videoPixelFormat);

        // 设置转码后的视频角度
        recorder.setVideoMetadata(ROTATE, String.valueOf(this.getVideoRotateFinally()));
        // 设置视频比特率
        recorder.setVideoBitrate(StringHelper.getFirst(this.getVideoBitRateFinally(), this.getAudioBitRateFinally()));
        // 设置帧速率
        // recorder.setFrameRate(this.videoFrameRate);
        // recorder.setVideoQuality(this.videoBitRateZip);
    }

    /**
     * 设置最小压缩比特率
     */
    public void initZipBitrate() {
        // 设置视频压缩率
        this.audioBitRateZip = StringHelper.getFirst(this.audioBitRateZip, 0.25f);
        // 设置视频压缩率
        this.videoBitRateZip = StringHelper.getFirst(this.videoBitRateZip, 0.1f);
        // 设置压缩大小
        this.videoSizeZip = StringHelper.getFirst(this.videoSizeZip, 0.1f);

        // 设置音频最小比特率
        this.minAudioBitRate = StringHelper.getFirst(this.minAudioBitRate, 48043);
    }

    /**
     * 设置宽高最小480 *360,最小标准
     */
    public void initZip480_360() {
        // 设置最小宽度,指的是宽度和高度中的大值,宽度>高度
        this.minVideoWidth = StringHelper.getFirst(this.minVideoWidth, 480);
        // 设置最小高度,指的是宽度和高度中的小值,宽度>高度
        this.minVideoHeight = StringHelper.getFirst(this.minVideoHeight, 360);
        // 设置视频最小比特率
        this.minVideoBitRate = StringHelper.getFirst(this.minVideoBitRate, 295581);
    }

    /**
     * 设置宽高最小800*600,老台式机标准
     */
    public void initZip800_600() {
        // 设置最小宽度,指的是宽度和高度中的大值,宽度>高度
        this.minVideoWidth = StringHelper.getFirst(this.minVideoWidth, 800);
        // 设置最小高度,指的是宽度和高度中的小值,宽度>高度
        this.minVideoHeight = StringHelper.getFirst(this.minVideoHeight, 600);
        // 设置视频最小比特率
        this.minVideoBitRate = StringHelper.getFirst(this.minVideoBitRate, 600000);
    }

    /**
     * 设置宽最小960,高度根据宽度自适应(途比达默认标准)
     */
    public void initZip960() {
        // 设置最小宽度,指的是宽度和高度中的大值,宽度>高度
        this.minVideoWidth = StringHelper.getFirst(this.minVideoWidth, 960);
        // 设置视频最小比特率
        this.minVideoBitRate = StringHelper.getFirst(this.minVideoBitRate, 2955811);
    }

    /**
     * 设置宽高最小1920*1080(微信录制视频标准)
     */
    public void initZip1920_1080() {
        // 设置最小宽度,指的是宽度和高度中的大值,宽度>高度
        this.minVideoWidth = StringHelper.getFirst(this.minVideoWidth, 1920);
        // 设置最小高度,指的是宽度和高度中的小值,宽度>高度
        this.minVideoHeight = StringHelper.getFirst(this.minVideoHeight, 1080);
        // 设置视频最小比特率
        this.minVideoBitRate = StringHelper.getFirst(this.minVideoBitRate, 8311136);
    }

    /**
     * 抓码为mp4
     */
    public void initMp4() {
        this.audioCodec = SOUND_CODEC_MP3;
        this.videoCodeC = VIDEO_CODEC_MP4;
        this.videoFormat = VIDEO_FORMAT_MP4;
        this.videoPixelFormat = VIDEO_FIXED_MP4;
    }

    /**
     * 压缩mp4
     */
    public void initZipMp4() {
        // 设置结果为mp4
        this.initMp4();
        // 设置最小宽高
        this.initZip960();
        // 设置最小压缩比特率
        this.initZipBitrate();
    }

    public int getAudioCodec() {
        return audioCodec;
    }

    public void setAudioCodec(int audioCodec) {
        this.audioCodec = audioCodec;
    }

    public double getAudioFrameRate() {
        return audioFrameRate;
    }

    public void setAudioFrameRate(double audioFrameRate) {
        this.audioFrameRate = audioFrameRate;
    }

    public int getAudioBitRate() {
        return audioBitRate;
    }

    public void setAudioBitRate(int audioBitRate) {
        this.audioBitRate = audioBitRate;
    }

    public int getMinAudioBitRate() {
        return minAudioBitRate;
    }

    public void setMinAudioBitRate(int minAudioBitRate) {
        this.minAudioBitRate = minAudioBitRate;
    }

    public float getAudioBitRateZip() {
        return audioBitRateZip;
    }

    public void setAudioBitRateZip(float audioBitRateZip) {
        this.audioBitRateZip = audioBitRateZip;
    }

    public String getVideoFormat() {
        return videoFormat;
    }

    public void setVideoFormat(String videoFormat) {
        this.videoFormat = videoFormat;
    }

    public int getVideoCodeC() {
        return videoCodeC;
    }

    public void setVideoCodeC(int videoCodeC) {
        this.videoCodeC = videoCodeC;
    }

    public int getVideoPixelFormat() {
        return videoPixelFormat;
    }

    public void setVideoPixelFormat(int videoPixelFormat) {
        this.videoPixelFormat = videoPixelFormat;
    }

    public int getVideoWidth() {
        return videoWidth;
    }

    public void setVideoWidth(int videoWidth) {
        this.videoWidth = videoWidth;
    }

    public int getVideoHeight() {
        return videoHeight;
    }

    public void setVideoHeight(int videoHeight) {
        this.videoHeight = videoHeight;
    }

    public int getMinVideoWidth() {
        return minVideoWidth;
    }

    public void setMinVideoWidth(int minVideoWidth) {
        this.minVideoWidth = minVideoWidth;
    }

    public int getMinVideoHeight() {
        return minVideoHeight;
    }

    public void setMinVideoHeight(int minVideoHeight) {
        this.minVideoHeight = minVideoHeight;
    }

    public double getVideoFrameRate() {
        return videoFrameRate;
    }

    public void setVideoFrameRate(double videoFrameRate) {
        this.videoFrameRate = videoFrameRate;
    }

    public double getVideoRotate() {
        return videoRotate;
    }

    public void setVideoRotate(double videoRotate) {
        this.videoRotate = videoRotate;
    }

    public double getVideoRotateChange() {
        return videoRotateChange;
    }

    public void setVideoRotateChange(double videoRotateChange) {
        this.videoRotateChange = videoRotateChange;
    }

    public float getVideoSizeZip() {
        return videoSizeZip;
    }

    public void setVideoSizeZip(float videoSizeZip) {
        this.videoSizeZip = videoSizeZip;
    }

    public int getVideoBitRate() {
        return videoBitRate;
    }

    public void setVideoBitRate(int videoBitRate) {
        this.videoBitRate = videoBitRate;
    }

    public int getMinVideoBitRate() {
        return minVideoBitRate;
    }

    public void setMinVideoBitRate(int minVideoBitRate) {
        this.minVideoBitRate = minVideoBitRate;
    }

    public float getVideoBitRateZip() {
        return videoBitRateZip;
    }

    public void setVideoBitRateZip(float videoBitRateZip) {
        this.videoBitRateZip = videoBitRateZip;
    }
}