JavaCV:将任意视频转码为h264编码的mp4格式视频

视频工具类

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.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;
			}
		}
	}
}

测试类

/**
 * 测试类
 * 
 * @author alderaan
 * @version 创建时间:2022年5月10日 下午3:47:49
 *
 */
public class App
{
	public static void main(String[] args)
	{
		try
		{
			// videoConvert函数,根据outputfile的格式后缀设置转码后的视频格式,推荐使用mp4格式后缀
			VideoUtil.videoConvert("/home/alderaan/test.MOV", "/result.mp4");
		} catch (java.lang.Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("执行完毕!");
	}
}

POM

		<!-- Linux x86_64 使用 -->
		<dependency>
			<groupId>org.bytedeco</groupId>
			<artifactId>javacpp</artifactId>
			<version>1.5.7</version>
		</dependency>
		<dependency>
			<groupId>org.bytedeco</groupId>
			<artifactId>opencv</artifactId>
			<version>4.5.5-1.5.7</version>
			<classifier>linux-x86_64-gpu</classifier>
		</dependency>
		<dependency>
			<groupId>org.bytedeco</groupId>
			<artifactId>openblas</artifactId>
			<version>0.3.19-1.5.7</version>
			<classifier>linux-x86_64</classifier>
		</dependency>
		<dependency>
			<groupId>org.bytedeco</groupId>
			<artifactId>ffmpeg</artifactId>
			<version>5.0-1.5.7</version>
			<classifier>linux-x86_64-gpl</classifier>
		</dependency>
		<!-- Windows x86_64 使用 -->
		<dependency>
			<groupId>org.bytedeco</groupId>
			<artifactId>opencv</artifactId>
			<version>4.5.5-1.5.7</version>
			<classifier>windows-x86_64</classifier>
		</dependency>
		<dependency>
			<groupId>org.bytedeco</groupId>
			<artifactId>openblas</artifactId>
			<version>0.3.19-1.5.7</version>
			<classifier>windows-x86_64</classifier>
		</dependency>
		<dependency>
			<groupId>org.bytedeco</groupId>
			<artifactId>ffmpeg</artifactId>
			<version>5.0-1.5.7</version>
			<classifier>windows-x86_64</classifier>
		</dependency>
  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
要将一个cv::Mat转码为H265并存入另一个cv::Mat,你可以使用FFmpeg库进行编码和解码。以下是一个示例代码: ```cpp extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/opt.h> } cv::Mat cvMatToH265(cv::Mat& frame) { cv::Mat h265Frame; AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H265); if (!codec) { // 处理无法找到编码器的情况 return h265Frame; } AVCodecContext* codecContext = avcodec_alloc_context3(codec); if (!codecContext) { // 处理无法分配编码器上下文的情况 return h265Frame; } // 设置编码器参数 codecContext->width = frame.cols; codecContext->height = frame.rows; codecContext->pix_fmt = AV_PIX_FMT_YUV420P; codecContext->time_base = {1, 30}; // 打开编码器 if (avcodec_open2(codecContext, codec, nullptr) < 0) { // 处理无法打开编码器的情况 avcodec_free_context(&codecContext); return h265Frame; } // 创建AVFrame用于保存转换后的图像数据 AVFrame* avFrame = av_frame_alloc(); avFrame->format = codecContext->pix_fmt; avFrame->width = frame.cols; avFrame->height = frame.rows; // 分配图像数据缓冲区 int bufferSize = av_image_get_buffer_size(codecContext->pix_fmt, frame.cols, frame.rows, 1); uint8_t* buffer = (uint8_t*)av_malloc(bufferSize); av_image_fill_arrays(avFrame->data, avFrame->linesize, buffer, codecContext->pix_fmt, frame.cols, frame.rows, 1); // 将cv::Mat转换为AVFrame并编码为H265 cv::Mat yuvFrame; cv::cvtColor(frame, yuvFrame, cv::COLOR_BGR2YUV_I420); avFrame->data[0] = yuvFrame.data; avFrame->data[1] = yuvFrame.data + frame.cols * frame.rows; avFrame->data[2] = yuvFrame.data + frame.cols * frame.rows * 5 / 4; AVPacket packet; av_init_packet(&packet); packet.data = nullptr; packet.size = 0; int ret = avcodec_send_frame(codecContext, avFrame); if (ret < 0) { // 处理发送帧数据失败的情况 av_frame_free(&avFrame); avcodec_free_context(&codecContext); return h265Frame; } while (ret >= 0) { ret = avcodec_receive_packet(codecContext, &packet); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { // 处理接收编码数据失败的情况 av_packet_unref(&packet); av_frame_free(&avFrame); avcodec_free_context(&codecContext); return h265Frame; } h265Frame = cv::Mat(packet.size, 1, CV_8UC1, packet.data); av_packet_unref(&packet); } // 清理资源 av_frame_free(&avFrame); avcodec_free_context(&codecContext); return h265Frame; } ``` 你可以调用`cvMatToH265`函数,并将输入的cv::Mat作为参数传递给它,它会返回一个包含H265数据的cv::Mat。请注意,函数返回的cv::Mat仅包含编码后的H265数据,你需要根据需要将其解码和显示。 ```cpp cv::Mat inputFrame = cv::imread("input.jpg"); cv::Mat h265Data = cvMatToH265(inputFrame); // 进行解码和显示 // ... ``` 请确保你已正确设置了FFmpeg库以及相关头文件和库文件的路径。此外,注意在使用OpenCV之前,要确保正确包含相关的OpenCV头文件并连接正确的库文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值