JavaCV将本地mp4文件推成flv格式的rtmp流

JavaCV将本地mp4文件推成flv格式的rtmp流

项目码云(Gitee)地址:https://gitee.com/banmajio/RTSPtoRTMP
项目github地址:https://github.com/banmajio/RTSPtoRTMP
个人博客:banmajio’s blog

javacv使用ffmpeg将rtsp转rtmp直播流播放的问题解决与优化系列文章:
FFmpeg转封装rtsp到rtmp(无需转码,低资源消耗)
JavaCV中FFmpegFrameGrabber调用start()方法时出现阻塞的解决办法
JavaCV使用FFmpeg进行rtsp转rtmp直播流画面延时的优化方法
JavaCV1.5.3版本FFmpegFrameGrabber初始化的时候加载时间长的解决方法
av_write_frame() error -22 while writing video packet解决方法
JvaCV报错avio_open2 error() error -138: Could not open 'null’的解决方法


注意事项

1.本篇博客仅作为一个demo使用,如果你的项目中需要该功能,请参考上述的一些优化方法和错误的解决方法
2.该demo使用到的nginx和一些优化注释请参考FFmpeg转封装rtsp到rtmp(无需转码,低资源消耗)
3.该demo使用的时JavaCV1.5.3版本,请尽量保持版本一致,1.5.3版本修复了内存溢出JVM崩溃的一些问题

推流思路

使用一个FileInputStream指向到本地的mp4绝对路径。将该inputstream用作FFmpegFrameGrabber构造方法的参数。通过FFmpegFrameGrabber采集器采集视频源,通过FFmpegFrameRecorder编码器转封装mp4为flv格式并推送到nginx形成rtmp的视频流。

代码实现

package com.junction;

import static org.bytedeco.ffmpeg.global.avcodec.av_packet_unref;

import java.io.FileInputStream;
import java.io.InputStream;

import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;

public class Test {
	public static void main(String[] args) {
		int err_index = 0;///推流过程中出现错误的次数
		try {
			InputStream in = new FileInputStream("D:\\test.mp4");
			FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(in, 0);
			grabber.setOption("stimeout", "2000000");
			grabber.setVideoOption("vcodec", "copy");
			grabber.setFormat("mpeg");
			grabber.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
			// h264编/解码器
			grabber.setVideoCodec(avcodec.AV_CODEC_ID_H264);
			grabber.start();

			FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("rtmp://localhost:1935/live/mp4test",
					grabber.getImageWidth(), grabber.getImageHeight(), 0);
			recorder.setInterleaved(true);
			// 设置比特率
			recorder.setVideoBitrate(2500000);
			// h264编/解码器
			recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
			// 封装flv格式
			recorder.setFormat("flv");
			recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
			// 视频帧率(保证视频质量的情况下最低25,低于25会出现闪屏)
			recorder.setFrameRate(grabber.getFrameRate());
			// 关键帧间隔,一般与帧率相同或者是视频帧率的两倍
			recorder.setGopSize((int) grabber.getFrameRate() * 2);
			AVFormatContext fc = null;
			fc = grabber.getFormatContext();
			recorder.start(fc);
			// 清空探测时留下的缓存
			grabber.flush();

			AVPacket pkt = null;
			long dts = 0;
			long pts = 0;

			System.out.println("开始推流");
			for (int no_frame_index = 0; no_frame_index < 5 || err_index < 5;) {
				pkt = grabber.grabPacket();
				if (pkt == null || pkt.size() <= 0 || pkt.data() == null) {
					// 空包记录次数跳过
					no_frame_index++;
					err_index++;
					continue;
				}
				// 获取到的pkt的dts,pts异常,将此包丢弃掉。
				if (pkt.dts() == avutil.AV_NOPTS_VALUE && pkt.pts() == avutil.AV_NOPTS_VALUE || pkt.pts() < dts) {
					err_index++;
					av_packet_unref(pkt);
					continue;
				}
				// 记录上一pkt的dts,pts
				dts = pkt.dts();
				pts = pkt.pts();
				// 推数据包
				err_index += (recorder.recordPacket(pkt) ? 0 : 1);
				// 将缓存空间的引用计数-1,并将Packet中的其他字段设为初始值。如果引用计数为0,自动的释放缓存空间。
				av_packet_unref(pkt);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("推流结束");
		}
	}
}

原创文章 23 获赞 11 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_40777510/article/details/105844878