15158846557 在线咨询 在线咨询
15158846557 在线咨询
所在位置: 首页 > 营销资讯 > 网站运营 > 直播视频网站设计到实现(例子-java版本)

直播视频网站设计到实现(例子-java版本)

时间:2023-10-09 09:54:01 | 来源:网站运营

时间:2023-10-09 09:54:01 来源:网站运营

直播视频网站设计到实现(例子-java版本):首先这里直播使用到的流程为:

1.获取直播流(本地文件、摄像头、在线获取的视频链接)

2.将流推送到推流服务器(不建议使用穿透局域方式,网上有很多打开摄像头获取流,通过网页显示的,这里要走公网,而且是多人查看的,最好用成熟的推流服务器,我这里用的是SRS,还有很多这样类似的服务器,比如EasyDarwin,因为SRS是企业级应用,可分布可扩展)

3.前端获取播放地址,进行播放(具体视频格式或者播放协议可以根据自己的需求来)

首先docker安装这个服务器到公网,这里用docker安装方便一点

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>


第三步我们先编写测试代码(准备一个mp4文件-播放时间尽量长一点):

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


使用这个播放器,把自己代码中设置推流到SRS中的地址拷贝进去




运行如下:

播放如下:

可以看到视频被在公网上正确播放出来了。

现在我们不在使用文件形式,而是使用实时的预览流:

1.第一步我们要建立管道流进行共享管理

// 创建管道输出流 pipedOutputStream = new PipedOutputStream(); // 创建管道输入流 pipedInputStream = new PipedInputStream(); try { // 将管道输入流与输出流连接 此过程也可通过重载的构造函数来实现 pipedOutputStream.connect(pipedInputStream); } catch (IOException e) { e.printStackTrace(); }2.第二步将我们取得的流写到管道中(比如大华的sdk、海康的、或者其他从硬件设备中获取的,如果是本机自带的摄像头可以直接用JavaCV提供的录制功能,而不需要使用管道进行推送)

这里由于其他原因我这里截图关键的写入指令:

同样可以使用上面的Test1的代码,但是有个地方需要修改一下:

这个地方这样设置一下,改成从管道中获取流,就可以实时播放了。





关键词:实现,例子,版本,设计,视频,直播

74
73
25
news

版权所有© 亿企邦 1997-2025 保留一切法律许可权利。

为了最佳展示效果,本站不支持IE9及以下版本的浏览器,建议您使用谷歌Chrome浏览器。 点击下载Chrome浏览器
关闭