java通过ffmpeg进行视频流的拉取和存储

package util;

import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.avformat.AVStream;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;

import java.util.logging.Logger;


public class VideoUtil {

    @Value("3")
    private Integer retryCount;//重试次数,默认值
    @Value("100")
    private Integer sleepTime;//休眠时间,默认值

    private static final Logger logger = Logger.getLogger(VideoUtil.class.getName());
    /**
     * 视频转码函数(仅转码)
     *
     * @param inputfile  原始视频文件完整路径
     * @param outputfile 目标视频文件完整保存路径(必须完整文件名,即包含格式后缀,推荐格式后缀为.mp4)
     * @throws Exception 异常
     */
    public static void videoConvert(String inputfile, String outputfile, Integer retryCount, Integer sleepTime) throws Exception {
        if (outputfile.lastIndexOf('.') < 0) {
            throw new Exception("Error! Output file format undetected!");
        }
        String format = outputfile.substring(outputfile.lastIndexOf('.'));

        FFmpegLogCallback.set();
        Frame frame;
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputfile);
        FFmpegFrameRecorder recorder = null;

        try {
            long start = System.currentTimeMillis();
            logger.info("视频存储开始:"+ DateUtil.getCurrentDateTime());
            logger.info("开始初始化帧抓取器");
            // 初始化帧抓取器,例如数据结构(时间戳、编码器上下文、帧对象等),
            // 如果入参等于true,还会调用avformat_find_stream_info方法获取流的信息,放入AVFormatContext类型的成员变量oc中
            grabber.start(true);

            logger.info("帧抓取器初始化完成");

            // grabber.start方法中,初始化的解码器信息存在放在grabber的成员变量oc中
            AVFormatContext avformatcontext = grabber.getFormatContext();

            // 文件内有几个媒体流(一般是视频流+音频流)
            int streamNum = avformatcontext.nb_streams();

            // 没有媒体流就不用继续了
            if (streamNum < 1)
            {
                logger.info("文件内不存在媒体流");
                throw new Exception("Error! There is no media stream in the file!");
            }

            // 取得视频的帧率
            double framerate = grabber.getVideoFrameRate();

            System.out.printf("视频帧率[%f],视频时长[%d]秒,媒体流数量[%d]\r\n", framerate, avformatcontext.duration() / 1000000,
                    avformatcontext.nb_streams());

            // 遍历每一个流,检查其类型
            for (int i = 0; i < streamNum; i++) {
                AVStream avstream = avformatcontext.streams(i);
                AVCodecParameters avcodecparameters = avstream.codecpar();
                System.out.printf("流的索引[%d],编码器类型[%d],编码器ID[%d]\r\n", i, avcodecparameters.codec_type(),
                        avcodecparameters.codec_id());
            }

            // 视频宽度
            int frameWidth = grabber.getImageWidth();
            // 视频高度
            int frameHeight = grabber.getImageHeight();
            // 音频通道数量
            int audiochannels = grabber.getAudioChannels();

            System.out.printf("视频宽度[%d],视频高度[%d],音频通道数[%d]\r\n", frameWidth, frameHeight, audiochannels);

            recorder = new FFmpegFrameRecorder(outputfile, frameWidth, frameHeight, audiochannels);
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);

                recorder.setFormat(format);
            // 使用原始视频的码率,若需要则自行修改码率
            recorder.setVideoBitrate(grabber.getVideoBitrate());

            // 一秒内的帧数,帧率
            recorder.setFrameRate(framerate);

            // 两个关键帧之间的帧数
            recorder.setGopSize((int)framerate);

            // 设置音频通道数,与视频源的通道数相等
            recorder.setAudioChannels(grabber.getAudioChannels());

            recorder.start();

            int videoframenum = 0;
            int audioframenum = 0;
            int dataframenum = 0;

            logger.info("开始录制\r\n");

            // 循环条件
            int count = 0;

            // 持续从视频源取帧
            while (true) {
                frame = grabber.grab();
                if (frame != null) {
                    // 有图像,就把视频帧加一
                    if (null != frame.image) {
                        videoframenum++;
                        // 取出的每一帧,都保存到视频
                        recorder.record(frame);
                    }
                    // 有声音,就把音频帧加一
                    if (null != frame.samples) {
                        audioframenum++;
                        // 取出的每一帧,都保存到视频
                        recorder.record(frame);
                    }
                    // 有数据,就把数据帧加一
                    if (null != frame.data) {
                        dataframenum++;
                    }
                    count = 0;
                }else {
                    if (retryCount == null || retryCount == 0) {
                        break;
                    }
                    if (count > retryCount) {
                        break;
                    }
                    if (sleepTime != null && sleepTime > 0) {
                        // 休眠一段时间后重试
                        Thread.sleep(sleepTime);
                    }
                    count ++;
                }
            }
            long end = System.currentTimeMillis();
            long second = (end - start) / 1000;
            logger.info("视频存储结束:"+ DateUtil.getCurrentDateTime());
            logger.info("视频存储总耗时(秒):"+ second);
            System.out.printf("转码完成,视频帧[%d],音频帧[%d],数据帧[%d]\r\n", videoframenum, audioframenum, dataframenum);
            logger.info("转码完成,视频帧"+videoframenum+",音频帧" + audioframenum + ",数据帧" + dataframenum);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (recorder != null) {
                try {
                    recorder.close();
                } catch (Exception e) {
                  e.printStackTrace();
                }
            }
            try {
                grabber.close();
            } catch (FrameGrabber.Exception e) {
                e.printStackTrace();
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值