简介:这是一份基于Java开发的在线直播平台完整项目源码,适配期末大作业、毕业设计或课程设计场景,适合需要快速搭建直播类Web应用并理解其前后端协作的Java学习者。项目由高分通过的期末大作业提炼而成,下载后简单配置部署即可运行。压缩包共137个文件,其中55个Java文件承载后端业务逻辑,20个HTML页面、12个CSS样式表与7个JavaScript脚本共同构建前端展示与交互,17张PNG和17张JPG图片提供界面素材,另有properties、conf、xml等文件负责参数配置,整体仅7.46MB,结构清晰,便于按模块查阅和二次修改。包内还包含nginx与srs相关流媒体配置,并集成DPlayer、video.js等播放组件,可帮助读者掌握在线直播平台从推流、分发到页面播放的完整链路。已有194人学习/下载,适合作为JavaWeb综合实践、毕业设计原型或直播功能模块复现的参考。

1. 期末大作业名头之下,真正值得拆的是 HLS 这条链路

压缩包名字写着“期末大作业”,但把文件铺开看,它不是普通的增删改查演示:mvnw.cmd 说明后端是 Maven 工程,nginx.live.conf 和 srs.http.hls.conf 说明媒体部分用了 SRS 做 HLS 直播,DPlayer、Video.js 说明播放端走浏览器拉流,chatMain.css、loginStyle.css 说明聊天和用户面板已经切好。把这份 Java 在线直播平台源码跑起来,最大的收获不是交一份作业,而是把“推流 → 切片 → 分发 → 播放 → 聊天”这条完整链路拆清楚。适合正在准备毕业设计、期末大作业的 Java 方向学生,也适合后端工程师想给现有项目快速加直播模块时做参照。

2. 从 mvnw.cmd 到 Spring Boot 进程:后端先从压缩包变成可运行服务

2.1 mvnw.cmd 解决了构建环境不一致的问题

压缩包里出现 mvnw.cmd,说明这个 Java 项目使用 Maven Wrapper 管理构建。Wrapper 的价值在于锁定 Maven 版本:它读取 .mvn/wrapper/maven-wrapper.properties 里的 distributionUrl,在没有预装 Maven 的机器上也能拉取指定版本执行构建。对答辩和演示场景来说,这比要求现场装 Maven 可靠得多。

拿到压缩包后第一件事不是改代码,而是验证构建工具本身能跑通。Windows 下用 mvnw.cmd ,Linux 或 macOS 下用 ./mvnw ,两者逻辑一致。

cd online-live
mvnw.cmd -v
mvnw.cmd clean package -DskipTests -Dfile.encoding=UTF-8
java -jar target/online-live-0.0.1-SNAPSHOT.jar --server.port=8080

第一条命令如果打印出完整的 Maven 和 JDK 版本,说明 wrapper 正常,接着谈编译才有意义。第二条 clean package 把清理、编译、打包合并成一步; -DskipTests 跳过测试类,很多课程设计的测试类并不适配演示环境,跳过能少一个失败点; -Dfile.encoding=UTF-8 避免 Windows 控制台把源码里的中文注释显示成乱码。第三条 java -jar 启动 Spring Boot 的可执行 jar, --server.port=8080 用命令行参数覆盖配置文件端口,端口被占用时改成 --server.port=8081 即可。具体 jar 包名要看 pom.xml 里的 artifactId,可以用 ls target 确认。

提示: mvnw.cmd -v 报错时,先查 JAVA_HOME 是否指向 JDK 安装目录,再看配置里的 distributionUrl 是否可达,这两个是 wrapper 无法自行绕过的问题。

2.2 后端进程起来之后,先确认它给直播链路提供什么

Java 后端在这个项目里负责业务面,不负责媒体面。它要管三件事:用户登录、聊天消息、直播流地址查询。登录接口负责把前端页面和用户身份关联起来;直播流地址接口告诉播放器 m3u8 在哪;聊天消息通过 WebSocket 或轮询推到前端。

与直播相关的配置通常会集中在 application 配置里。下面是一个与这套源码场景匹配的典型写法:

server:
  port: 8080

live:
  push-url: rtmp://127.0.0.1:1935/live/live
  hls-url: http://127.0.0.1:8088/live/live.m3u8

chat:
  path: /ws/chat
  enabled: true

live.push-url 是给推流工具填的 RTMP 地址,对应 SRS 的 1935 端口,OBS 或 ffmpeg 推流时填这一条。 live.hls-url 是给浏览器播放器用的 HLS 地址,走 Nginx 的 8088 端口。 chat.path 是聊天消息通道,前后端约定同一个路径即可。注意推流端口和播放端口很可能不一致,这是直播项目的正常状态,推流走 RTMP,播放走 HTTP。

2.3 端口规划直接决定演示现场是否翻车

这个项目同时存在 Java 进程、SRS 进程和 Nginx 进程,三张端口网互相叠加,最常见的演示事故就是端口冲突导致某个进程静默失败。建议把端口关系在部署第一天就固定下来。

进程 默认端口 在项目里的角色 冲突时优先改谁
Spring Boot 后端 8080 登录、聊天、业务 API 改成 8081
SRS 主服务 1935 接收 RTMP 推流 保持 1935 不动
SRS HTTP API 1985 查询推流状态 与前端无关,可改
Nginx 播放入口 8088 对外提供 m3u8 和 ts 尽量保持 8088 不变

建议把后端端口改成 8081,SRS 的 1935 推流口和 1985 API 口保留,播放统一从 Nginx 的 8088 出口。这样做的好处是浏览器页面里只有一个媒体源地址,m3u8 和页面同源,跨域问题降到最低。别等到答辩前夜再调端口,三个进程同时跑起来以后再改配置,很容易漏改一处导致播放器黑屏。

3. srs.http.hls.conf 与 nginx.live.conf:推流、切片与播放出口

3.1 SRS 和 Nginx 在链路里的分工

srs.http.hls.conf 是 SRS 流媒体服务器的实例配置,不是前端文件。它的任务是接收推流端的 RTMP 信号,按设定参数把视频切片成 ts 分片,并生成 m3u8 播放列表。Nginx 的 nginx.live.conf 则把切片目录以静态文件方式暴露给浏览器,前端播放器拿到的其实是 Nginx 输出的 HTTP 地址。

为什么不在 Java 代码里直接做切片?切片涉及磁盘写入、缓存控制、时序管理,用 Java 实现成本高,稳定性也比不上成熟的流媒体进程。一个课程设计级别的直播系统,把媒体面交给 SRS,Java 只写业务接口,这是最合理的边界划分。

链路环节 使用的协议 对应配置
推流入场 RTMP SRS listen 1935
视频切片 HLS srs.http.hls.conf 的 hls 段
文件输出 HTTP 静态 nginx.live.conf 的 location
浏览器播放 HLS m3u8 + ts

3.2 srs.http.hls.conf 的 HLS 参数决定延迟和磁盘占用

SRS 配置语法接近 nginx 风格,核心是 vhost 下的 hls 段。这套源码对应的典型写法如下:

listen              1935;
max_connections     1000;
daemon              off;
srs_log_tank        console;

http_server {
    enabled         on;
    listen          8080;
    dir             ./objs/nginx/html;
}

vhost __defaultVhost__ {
    hls {
        enabled         on;
        hls_path        ./objs/nginx/html;
        hls_fragment    4;
        hls_window      30;
        hls_cleanup     on;
    }
}

listen 1935 是 RTMP 推流入口,ffmpeg 或 OBS 推流时连接这个端口。 http_server 段把 ./objs/nginx/html 目录暴露成静态文件服务,m3u8 和 ts 都落在这个目录。 hls_path 必须与 Nginx 的 alias 指向同一个目录,否则播放器拿到 m3u8 也读不到 ts 分片。 hls_fragment 4 表示每 4 秒生成一个 ts 分片,数值越小延迟越低,但分片文件数量会变多; hls_window 30 表示播放列表保留最近 30 秒内容; hls_cleanup on 自动清理过期分片,防止演示机器磁盘被写满。

注意:SRS 的 hls_path 目录必须真实存在,否则启动后分片写入失败,日志里有报错但进程看起来还在运行。

3.3 nginx.live.conf 用静态文件输出,避免浏览器跨域

nginx.live.conf 承担的是播放出口。它把 /live/ 路径直接映射到 SRS 的切片目录,不做业务判断,下面是常见的静态输出写法:

server {
    listen      8088;
    server_name localhost;

    location /live/ {
        types {
            application/vnd.apple.mpegurl m3u8;
            video/mp2t ts;
        }
        alias /usr/local/srs/objs/nginx/html/live/;
        add_header Cache-Control no-cache;
        add_header Access-Control-Allow-Origin *;
    }
}

location /live/ 把所有以 /live/ 开头的请求都收进来。 types 块让 m3u8 返回 application/vnd.apple.mpegurl ,ts 返回 video/mp2t ,这两个 MIME 类型缺失时,浏览器会把文件当 text/plain 直接拒播,控制台报错往往是媒体加载失败。 alias 把 URL 前缀映射到磁盘路径,注意 alias 与 root 的差异,alias 是整段替换,末尾路径要和 SRS 的 hls_path 结构一致。 Cache-Control no-cache 让播放器每次刷新都重新请求 m3u8,直播列表才能实时更新。 Access-Control-Allow-Origin * 是跨域报错的解药,页面和播放入口不在同源时必须有这一行。

4. DPlayer、Video.js 与 chatMain.css:播放页和聊天区怎么组织起来

4.1 样式引用顺序和页面骨架

前端页面的文件顺序直接决定播放器能否正常渲染。bootstrap.min.css 是基础样式,要放最前面;video-js.min.css 和 DPlayer.min.css 是播放器组件样式,排在 bootstrap 之后;chatMain.css、user-right.css、loginStyle.css 属于业务页面样式,放最后负责覆盖细节。如果一个页面同时出现 DPlayer 和 Video.js,只保留其中一套初始化逻辑,两套播放器同时初始化会争抢同一个 video 容器。

一个典型页面引用片段如下:

<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/video-js.min.css">
<link rel="stylesheet" href="css/DPlayer.min.css">
<link rel="stylesheet" href="css/chatMain.css">
<link rel="stylesheet" href="css/user-right.css">
<script src="js/DPlayer.min.js"></script>
<script src="js/video.min.js"></script>

CSS 放在 head 里,JS 放在 body 底部,这样播放器脚本执行时 DOM 已经解析完, document.getElementById 一定能拿到容器。如果脚本写在 head 里,需要包裹 DOMContentLoaded 事件,否则初始化时容器还不存在,播放器会静默失败。

4.2 DPlayer 播放 HLS 的实例参数

DPlayer 内置 HLS 解析能力,直接把 m3u8 地址交给 video 配置即可。下面是播放器初始化代码:

const dp = new DPlayer({
  container: document.getElementById('livePlayer'),
  autoplay: false,
  preload: 'auto',
  theme: '#ff5f33',
  video: {
    url: 'http://localhost:8088/live/live.m3u8',
    type: 'hls'
  }
});

container 指定播放器挂载到哪个 DOM 节点。 autoplay 设成 false,因为浏览器对带声音的自动播放有限制,用户没有交互直接 play 会被拦截。 preload 选 auto,让浏览器提前加载视频元数据。 video.url 就是上一章 Nginx 输出的播放地址, type 为 hls 时,播放器内部通过 MediaSource 模式拉取 m3u8 并读取 ts 分片。如果改成 flv 类型,需要额外引入 flv.js。

播放器初始化顺利不代表直播链路没隐患,建议加一个错误回调:

dp.on('error', function (err) {
  console.error('播放错误:', err);
});

回调本身只打印日志,真正的价值在于区分两类问题:地址 404 优先查 Nginx 的 alias 路径是否和 SRS 的 hls_path 一致;跨域报错查 Access-Control-Allow-Origin 头是否存在。浏览器控制台的错误信息比画面黑屏更早暴露问题位置。

4.3 chatMain.css 对应的聊天消息渲染

chatMain.css 解决的是聊天区布局,不是后端消息逻辑,通常固定聊天室的位置、高度和滚动行为。对应的结构一般长这样:

<div class="chat-main">
  <div class="chat-head">聊天室</div>
  <div class="chat-body" id="chatBody"></div>
  <div class="chat-footer">
    <input class="chat-input" id="chatInput" placeholder="说点什么">
    <button class="chat-send" id="chatSend">发送</button>
  </div>
</div>

chat-main 作为最外层容器撑满父级高度,chat-body 设置 flex: 1 和 overflow-y: auto ,新消息进入时聊天区域独立滚动。chat-footer 固定在聊天区底部,输入框占剩余宽度,发送按钮固定宽度,这是 chatMain.css 里最常见的 flex 布局手段。消息渲染不依赖框架,收到数据后向 chat-body 追加一个节点即可:

function appendChatMessage(msg) {
  const item = document.createElement('div');
  item.className = 'chat-item';
  item.innerHTML = '<span class="chat-nick">' + msg.nick + '</span> ' + msg.content;
  document.getElementById('chatBody').appendChild(item);
}

appendChatMessage 只做 DOM 追加,不整体重写 chatBody 的 innerHTML,避免直播间消息刷屏时页面频繁重排。nick 和 content 分别落在样式类上,与 user-right.css 里的用户信息展示互不干扰。

播放器与聊天模块的关键参数如下:

配置项 取值 作用
video.type hls 让 DPlayer 按 HLS 协议解析
autoplay false 规避浏览器自动播放限制
chat-body overflow-y auto 消息多时独立滚动
chat-footer flex 布局 输入框与发送按钮固定底部

5. 验证与演示排错:先看切片,再看浏览器控制台

5.1 用 curl 判断链路通到哪一段

启动后不急着开浏览器,先用 curl 确定性验证。播放地址走 Nginx 的 8088,就请求 m3u8:能看到分片列表,说明 SRS 在切、Nginx 也在正常输出文件。

curl -s http://127.0.0.1:8088/live/live.m3u8
curl -s http://127.0.0.1:1985/api/v1/streams

第一条命令期望看到以 #EXTM3U 开头、后续跟着 ts 文件名的内容。如果返回 404,去 SRS 的 ./objs/nginx/html/live 目录下看文件是否存在;文件存在但 404,问题就在 Nginx 的 alias 路径。第二条命令请求 SRS 的 HTTP API,返回结果里能看到 live 流,说明推流端和 SRS 的连接还活着。这两条命令分别覆盖“切片是否生成”和“推流是否在线”,哪一步失败,就把排查范围缩到对应进程。

5.2 三个演示现场最高频的翻车点

第一是端口冲突。SRS 内置 HTTP 服务和 Spring Boot 都默认监听 8080,后启动的进程会报 bind failed。建议按第二章的表格提前分配端口,在后端启动命令里显式写 --server.port=8081 。

第二是 MIME 类型缺失。播放器拿到 m3u8 却报 MEDIA_ERR_SRC_NOT_SUPPORTED,多半是 Nginx 没配置 m3u8 和 ts 的 types。把 application/vnd.apple.mpegurl m3u8 和 video/mp2t ts 加上后重启 Nginx 即可解决。

第三是 m3u8 缓存导致直播画面卡住。Nginx 默认对静态文件启用缓存,直播播放列表更新不及时,画面会停在十几秒前。更精细的做法是把 m3u8 和 ts 分开处理:

location ~* \.m3u8$ {
    add_header Cache-Control no-cache;
}
location ~* \.ts$ {
    add_header Cache-Control max-age=30;
}

m3u8 每次请求都回到服务器拿最新播放列表,ts 分片因为是已生成的静态文件,缓存 30 秒不会造成明显延迟,同时减少重复请求量。这个区分让直播进度更新及时,已切片文件的缓存能力也保留下来。

演示现场最容易被忽略的是推流源本身。答辩时如果用 ffmpeg 推一个循环视频,文件读完后画面会停在最后一帧。提前用循环参数推流,并把推流命令、后端启动、curl 检查顺序在终端里按序跑一遍,能省掉现场一半以上的调试时间。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

Logo

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。

更多推荐