package com.yanzuoguang.util;

import com.yanzuoguang.util.log.Log;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.javacv.*;
import org.bytedeco.opencv.global.opencv_core;
import org.bytedeco.opencv.opencv_core.IplImage;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * 视频帮助类
 * <p>
 * 59s视频,抖音上拍摄59s,下载后是27.79mb。先从手机上拍摄59s,原视频是111mb,上传抖音后下载的视频是47.32mb
 *
 * @author 颜佐光
 */
public class MediaHelper extends ImageHelper {
    /**
     * 默认截取视频的中间帧为封面
     */
    public static final int FRAME_INDEX = 3;
    public static boolean Frame = true;

    /**
     * 从视频文件中获取第一张图片
     *
     * @param fromFile  视频文件
     * @param toFile    转码后保存的文件
     * @param parameter 转码后的参数,在转码完成后该参数值会被改变
     */
    public static void zipVideoMp4(String fromFile, String toFile, MediaParameter parameter) throws IOException {
        // 视频压缩参数初始化
        if (parameter == null) {
            parameter = new MediaParameter();
        }
        parameter.initZipMp4();
        convertVideo(fromFile, toFile, parameter);
    }

    /**
     * 转换成mp4
     *
     * @param fromFile
     * @param toFile
     * @param parameter
     */
    public static void convertVideoMp4(String fromFile, String toFile, MediaParameter parameter) throws IOException {
        // 视频压缩参数初始化
        if (parameter == null) {
            parameter = new MediaParameter();
        }
        parameter.initMp4();
        convertVideo(fromFile, toFile, parameter);
    }

    /**
     * 从视频文件中获取第一张图片
     *
     * @param fromFile  视频文件
     * @param toFile    转码后保存的文件
     * @param parameter 转码后的参数,在转码完成后该参数值会被改变
     */
    public static void convertVideo(String fromFile, String toFile, MediaParameter parameter) throws IOException {
        initFile(fromFile, toFile);
        // 视频压缩参数初始化
        if (parameter == null) {
            parameter = new MediaParameter();
        }
        parameter.check();

        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(fromFile);
        try {
            grabber.start();
            // 设置视频参数
            parameter.init(grabber);

            Log.info(MediaHelper.class, "开始转换...源文件:%s 目标文件:%s 旋转参数:%s",
                    fromFile, toFile, parameter.toString());

            // 定义转码工具
            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(
                    toFile,
                    parameter.getVideoWidthFinally(),
                    parameter.getVideoHeightFinally(),
                    grabber.getAudioChannels()
            );
            parameter.init(grabber, recorder);
            try {
                // 转码没有图像
                if (Frame) {
                    // 开始转换
                    // recorder.start(grabber.getFormatContext());
                    recorder.start();
                    Frame frame;

                    while ((frame = grabber.grab()) != null) {
                        // 从视频帧中获取图片
                        if (frame.image != null) {
                            recorder.record(frame);
                        }
                        // 音频帧写入输出流
                        if (frame.samples != null) {
                            recorder.record(frame);
                        }
                    }
                } else {
                    // 开始转换
                    recorder.start(grabber.getFormatContext());
                    AVPacket packet;
                    while ((packet = grabber.grabPacket()) != null) {
                        recorder.recordPacket(packet);
                    }
                }

            } finally {
                // 设置转码后的视频角度
                recorder.stop();
                recorder.release();
                recorder.close();
            }

            Log.info(MediaHelper.class, "转换完成...源文件:%s 目标文件:%s 旋转参数:%s ",
                    fromFile, toFile, parameter.toString());
        } finally {
            grabber.stop();
            grabber.close();
        }

    }

    private static void initFile(String fromFile, String toFile) {
        // 源文件判断
        File source = new File(fromFile);
        if (!source.exists()) {
            throw YzgError.getRuntimeException("039", fromFile);
        }
        File target = new File(toFile);
        if (!target.getParentFile().exists()) {
            target.getParentFile().mkdirs();
        }
        if (target.exists()) {
            target.delete();
        }
    }

    /**
     * 从视频文件中获取第一张图片
     *
     * @param fromFile 视频文件
     * @param toFile   第一帧图片
     * @throws IOException
     */
    public static void getVideoFirstImage(String fromFile, String toFile) {
        try {
            String suffix = toFile.substring(toFile.lastIndexOf(".") + 1);
            getGrabberFFmpegImage(fromFile, toFile, suffix, FRAME_INDEX);
        } catch (IOException ex) {
            throw YzgError.getRuntimeException(ex, "048", ex.getMessage());
        }
    }

    /**
     * 获取视频缩略图
     *
     * @param filePath 视频路径
     * @param toFile   存储路径
     * @param type     存储类型
     * @param index    获取第几帧
     * @throws IOException
     */
    public static void getGrabberFFmpegImage(String filePath, String toFile, String type, int index) throws IOException {
        // 初始化文件
        initFile(filePath, toFile);

        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filePath);
        MediaParameter parameter = new MediaParameter();
        try {
            // 打开视频
            grabber.start();
            parameter.init(grabber);

            double rotate = parameter.getVideoRotateFinally();
            // 整个视频的长度
            int ffLength = grabber.getLengthInFrames();
            // 取得能够取得的帧数
            index = Math.min(ffLength, index);

            Frame f;
            for (int i = 0; i < ffLength; i++) {
                f = grabber.grabImage();
                if (i >= index) {
                    doExecuteFrame(f, toFile, type, rotate);
                    break;
                }
            }
        } finally {
            grabber.close();
        }
    }

    /**
     * 获取视频缩略图
     *
     * @param filePath 视频路径
     * @throws IOException
     */
    public static MediaParameter getGrabberParameter(String filePath) throws IOException {
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filePath);
        MediaParameter parameter = new MediaParameter();
        try {
            // 打开视频
            grabber.start();
            parameter.init(grabber);
        } finally {
            grabber.close();
        }
        return parameter;
    }

    /**
     * 旋转图片
     *
     * @param src
     * @param angle
     * @return
     */
    public static IplImage rotate(IplImage src, double angle) {
        IplImage img = IplImage.create(
                src.height(), src.width(),
                src.depth(), src.nChannels()
        );
        opencv_core.cvTranspose(src, img);
        opencv_core.cvFlip(img, img, (int) angle);
        return img;
    }

    /**
     * 截取缩略图
     *
     * @param f              帧
     * @param targetFilePath 封面图片
     * @param type           封面图片类型
     * @param type           旋转角度
     */
    public static void doExecuteFrame(Frame f, String targetFilePath, String type, double rotate) throws IOException {
        if (null == f || null == f.image) {
            return;
        }
        Java2DFrameConverter converter = new Java2DFrameConverter();
        BufferedImage bi = converter.getBufferedImage(f);
        if (rotate > 0) {
            bi = ImageRotate.rotate(bi, (int) rotate);
        }
        File output = new File(targetFilePath);
        ImageIO.write(bi, type, output);
    }

    /**
     * 根据视频长度随机生成随机数集合
     *
     * @param baseNum:基础数字,此处为视频长度
     * @param length:随机数集合长度
     * @return:随机数集合
     */
    public static List<Integer> random(int baseNum, int length) {
        List<Integer> list = new ArrayList<Integer>(length);
        while (list.size() < length) {
            Integer next = (int) (Math.random() * baseNum);
            if (list.contains(next)) {
                continue;
            }
            list.add(next);
        }
        Collections.sort(list);
        return list;
    }

}