当前位置: 首页 > article >正文

利用javacv实现视频转h264

网上找到的一个实用的视频转换工具类,可将视频转为h264编码(方便在浏览器下播放视频)。

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.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.FFmpegLogCallback;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;

/**
 * 视频工具类
 * 
 * @author alderaan
 * @version 创建时间:2022年5月10日 下午3:43:18
 *
 */
public class VideoUtil
{
    /**
     * 视频转码函数(仅转码)
     * 
     * @param inputfile  原始视频文件完整路径
     * @param outputfile 目标视频文件完整保存路径(必须完整文件名,即包含格式后缀,推荐格式后缀为.mp4)
     * @throws Exception 异常
     */
    public static void videoConvert(String inputfile, String outputfile) 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
        {
            System.out.println("开始初始化帧抓取器");

            // 初始化帧抓取器,例如数据结构(时间戳、编码器上下文、帧对象等),
            // 如果入参等于true,还会调用avformat_find_stream_info方法获取流的信息,放入AVFormatContext类型的成员变量oc中
            grabber.start(true);

            System.out.println("帧抓取器初始化完成");

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

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

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

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

            System.out.println("videocodec:" + grabber.getVideoMetadata());
            
            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;

            // 持续从视频源取帧
            while (null != (frame = grabber.grab()))
            {
                // 有图像,就把视频帧加一
                if (null != frame.image)
                {
                    videoframenum++;
                    // 取出的每一帧,都保存到视频
                    recorder.record(frame);
                }

                // 有声音,就把音频帧加一
                if (null != frame.samples)
                {
                    audioframenum++;
                    // 取出的每一帧,都保存到视频
                    recorder.record(frame);
                }

                // 有数据,就把数据帧加一
                if (null != frame.data)
                {
                    dataframenum++;
                }
            }

            System.out.printf("转码完成,视频帧[%d],音频帧[%d],数据帧[%d]\r\n", videoframenum, audioframenum, dataframenum);

        } catch (Exception e)
        {
            // e.printStackTrace();
            throw e;
        } finally
        {
            if (recorder != null)
            {
                try
                {
                    recorder.close();
                } catch (Exception e)
                {
                    // System.out.println("recorder.close异常" + e);
                    throw e;
                }
            }

            try
            {
                grabber.close();
            } catch (FrameGrabber.Exception e)
            {
                // System.out.println("frameGrabber.close异常" + e);
                throw e;
            }
        }
    }
}


http://www.kler.cn/a/291003.html

相关文章:

  • 《Java核心技术I》Swing的滑动条
  • Spring Boot 2.1.7 数据源自动加载过程详解
  • 数据结构大作业——家谱管理系统(超详细!完整代码!)
  • 安卓环境配置及打开新项目教程,2024年12月20日最新版
  • Linux设置篇
  • 3.使用SD卡挂载petalinux根文件系统
  • 万物皆对象 - 一文详解JS面向对象编程的核心方法
  • C# WPF上位机与PLC如何是实现通信同步
  • 华为云Flexus云服务器X实例与AI大模型融合实践:打造高效智能应用
  • 二、基本语法配置请求
  • 果蔬识别系统性能优化之路
  • 集成电路学习:什么是MCU微控制器
  • 软件测试中错误推断法(错误猜测法或错误推测法)
  • 排序
  • MATLAB基础应用精讲-【数模应用】极差分析(附MATLAB、python和R语言代码实现)
  • 深度学习(八)-图像色彩操作
  • 基于FCM模糊聚类算法的图像分割matlab仿真
  • 【小设计】基于宏实现的C++ 可复用setter 和getter设计
  • 嵌入式面经 嵌入式软件开发 嵌入式硬件开发 经纬恒润嵌入式面试汇总总结
  • RK3588平台开发系列讲解(显示篇)图像的宽高和跨距
  • scss中的mix函数
  • 基于深度学习的人机交互中的认知模型
  • Google 的 9 年职业生涯回顾
  • ubuntu通过smba访问华为设备
  • 线性表之栈
  • ThreadLocal 在线程池中的内存泄漏问题