时间:2023-10-09 09:54:01 | 来源:网站运营
时间:2023-10-09 09:54:01 来源:网站运营
直播视频网站设计到实现(例子-java版本):首先这里直播使用到的流程为:docker run -p 11935:1935 -p 1985:1985 -p 8080:8080 ossrs/srs:3
第二步maven中添加依赖 <!-- javacv相关依赖,一个就够了 --> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5.8</version> </dependency>
package com.netsdk.test;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.ffmpeg.global.avutil;import org.bytedeco.javacv.*;import java.util.logging.Logger;public class Test1 { /** * 本地MP4文件的完整路径 */ private static final String MP4_FILE_PATH = "E://data//testh264.mp4"; /** * SRS的推流地址这里要改 */ private static final String SRS_PUSH_ADDRESS = "rtmp://xxxxxx:11935/live/457578"; /** * 读取指定的mp4文件,推送到SRS服务器 * @param sourceFilePath 视频文件的绝对路径 * @param PUSH_ADDRESS 推流地址 * @throws Exception */ private static void grabAndPush(String sourceFilePath, String PUSH_ADDRESS) throws Exception { // ffmepg日志级别 avutil.av_log_set_level(avutil.AV_LOG_INFO); FFmpegLogCallback.set(); // 实例化帧抓取器对象,将文件路径传入 FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(MP4_FILE_PATH); long startTime = System.currentTimeMillis(); System.out.println("开始初始化帧抓取器"); // 初始化帧抓取器,例如数据结构(时间戳、编码器上下文、帧对象等), // 如果入参等于true,还会调用avformat_find_stream_info方法获取流的信息,放入AVFormatContext类型的成员变量oc中 grabber.start(true); System.out.println("帧抓取器初始化完成,耗时[{}]毫秒:"); System.out.println(System.currentTimeMillis()-startTime); // grabber.start方法中,初始化的解码器信息存在放在grabber的成员变量oc中 AVFormatContext avFormatContext = grabber.getFormatContext(); // 文件内有几个媒体流(一般是视频流+音频流) int streamNum = avFormatContext.nb_streams(); // 没有媒体流就不用继续了 if (streamNum<1) { System.out.println("文件内不存在媒体流"); return; } // 取得视频的帧率 int frameRate = (int)grabber.getVideoFrameRate(); System.out.println("视频帧率[{}],视频时长[{}]秒,媒体流数量[{}]"); System.out.println(frameRate); System.out.println(avFormatContext.duration()/1000000); System.out.println(avFormatContext.nb_streams()); // 遍历每一个流,检查其类型 for (int i=0; i< streamNum; i++) { AVStream avStream = avFormatContext.streams(i); AVCodecParameters avCodecParameters = avStream.codecpar(); System.out.println("流的索引[{}],编码器类型[{}],编码器ID[{}]"); System.out.println(i); System.out.println(avCodecParameters.codec_type()); System.out.println(avCodecParameters.codec_id()); } // 视频宽度 int frameWidth = grabber.getImageWidth(); // 视频高度 int frameHeight = grabber.getImageHeight(); // 音频通道数量 int audioChannels = grabber.getAudioChannels(); System.out.println("视频宽度[{}],视频高度[{}],音频通道数[{}]"); System.out.println(frameWidth); System.out.println(frameHeight); System.out.println(audioChannels); // 实例化FFmpegFrameRecorder,将SRS的推送地址传入 FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(SRS_PUSH_ADDRESS, frameWidth, frameHeight, audioChannels); // 设置编码格式 recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 设置封装格式 recorder.setFormat("flv"); // 一秒内的帧数 recorder.setFrameRate(frameRate); // 两个关键帧之间的帧数 recorder.setGopSize(frameRate); // 设置音频通道数,与视频源的通道数相等 recorder.setAudioChannels(grabber.getAudioChannels()); startTime = System.currentTimeMillis(); System.out.println("开始初始化帧抓取器"); // 初始化帧录制器,例如数据结构(音频流、视频流指针,编码器), // 调用av_guess_format方法,确定视频输出时的封装方式, // 媒体上下文对象的内存分配, // 编码器的各项参数设置 recorder.start(); System.out.println("帧录制初始化完成,耗时[{}]毫秒"); System.out.println(System.currentTimeMillis()-startTime); Frame frame; startTime = System.currentTimeMillis(); System.out.println("开始推流"); long videoTS = 0; int videoFrameNum = 0; int audioFrameNum = 0; int dataFrameNum = 0; // 假设一秒钟15帧,那么两帧间隔就是(1000/15)毫秒 int interVal = 1000/frameRate; // 发送完一帧后sleep的时间,不能完全等于(1000/frameRate),不然会卡顿, // 要更小一些,这里取八分之一 interVal/=8; // 持续从视频源取帧 while (null!=(frame=grabber.grab())) { videoTS = 1000 * (System.currentTimeMillis() - startTime); // 时间戳 recorder.setTimestamp(videoTS); // 有图像,就把视频帧加一 if (null!=frame.image) { videoFrameNum++; } // 有声音,就把音频帧加一 if (null!=frame.samples) { audioFrameNum++; } // 有数据,就把数据帧加一 if (null!=frame.data) { dataFrameNum++; } // 取出的每一帧,都推送到SRS recorder.record(frame); // 停顿一下再推送 Thread.sleep(interVal); } System.out.println("推送完成,视频帧[{}],音频帧[{}],数据帧[{}],耗时[{}]秒"); System.out.println(videoFrameNum); System.out.println(audioFrameNum); System.out.println(dataFrameNum); System.out.println((System.currentTimeMillis()-startTime)/1000); // 关闭帧录制器 recorder.close(); // 关闭帧抓取器 grabber.close(); } public static void main(String[] args) throws Exception { grabAndPush(MP4_FILE_PATH, SRS_PUSH_ADDRESS); }}
// 创建管道输出流 pipedOutputStream = new PipedOutputStream(); // 创建管道输入流 pipedInputStream = new PipedInputStream(); try { // 将管道输入流与输出流连接 此过程也可通过重载的构造函数来实现 pipedOutputStream.connect(pipedInputStream); } catch (IOException e) { e.printStackTrace(); }
2.第二步将我们取得的流写到管道中(比如大华的sdk、海康的、或者其他从硬件设备中获取的,如果是本机自带的摄像头可以直接用JavaCV提供的录制功能,而不需要使用管道进行推送)关键词:实现,例子,版本,设计,视频,直播