视频流
视频流,一种流媒体传输方式,市面上常用的流媒体传输协议分别有RTSP、FLV、M3U8、HLS和HDS以及WebRTC。像更早一点的通过falsh插件播放的协议就是RTMP。
RTSP(Real-Time Streaming Protocol)是一种实时流媒体传输协议,适用于点对点传输,可以支持高质量的视频流传输,但是需要较高的带宽支持。
FLV(Flash Video)是一种常见的流媒体格式。它可以通过HTTP传输,在不需要特殊服务器配置的情况下即可播放。FLV支持流式传输和P2P传输,是一种较为稳定和可靠的流媒体协议。
M3U8是苹果公司推出的一种流媒体协议,在移动设备上广泛使用。M3U8协议基于HTTP协议传输,可以实现自适应码率和实时转换等功能,可以在不同的网络环境下提供更好的用户体验。
总的来说就是:RTSP适用于高带宽网络,FLV是能够在不需要特殊服务器配置的情况下即可播放,M3U8协议支持自适应码率和实时转换等功能。不同的协议可以根据具体的需求进行选择。
代码
这边笔者是用的是通过Java调取命令行的形式,首先需要下载ffmpeg,ffmpeg提供了各种平台的下载,下载完成之后通过执行ffmpeg命令来判断是否下载成功(注:下载完成之后还需要把ffmpeg加入到系统加环境变量中)。下面是我的代码部分:
public String start(HkVideoDto hkVideoDto) {
String name = hkVideoDto.getName().replace("#", "");
String pinYinHeadChar = StringUtils.getPinYinHeadChar(name);
String rtsp = getRtspStream(hkVideoDto);
log.info("开始获取rtsp流地址:{}", rtsp);
// 视频存储路径
String resultUrl = "D:\\syps\\video\\" + pinYinHeadChar + ".m3u8";
log.info("当前视频存储路径:{}", resultUrl);
// 清空旧视频
File videoFolder = new File("D:\\syps\\video");
if (videoFolder.isDirectory()) {
File[] files = videoFolder.listFiles();
// 匹配以pinYinHeadChar字符串为前缀,.ts为后缀的文件
Pattern pattern = Pattern.compile("^" + pinYinHeadChar + ".*\\.ts$");
for (File file : files) {
if (file.isFile()) {
String fileName = file.getName();
Matcher matcher = pattern.matcher(fileName);
if (matcher.matches()) {
System.out.println("删除文件:" + fileName);
file.delete();
}
}
}
}
File file = new File("D:\\syps\\video\\daemonTmp");
log.info("当前daemonTmp文件存储路径:{}", file.getAbsolutePath());
if (!file.exists()) {
file.mkdirs();
log.info("创建daemonTmp文件夹成功!");
}
File tmpFile = new File("D:\\syps\\video\\temp.tmp");
log.info("当前temp缓存文件存储路径:{}", tmpFile.getAbsolutePath());
List<String> commandList = Arrays.asList("ffmpeg", "-i", rtsp, "-vcodec", "libx264",
"-map", "0:v", "-c:a", "copy",
"-f", "hls", "-threads", "0", "-preset", "ultrafast", "-movflags", "faststart",
"-hls_flags", "split_by_time+delete_segments", "-hls_time", "6", "-hls_list_size", "10", resultUrl);
// 将命令参数列表转换为单个字符串
String commandString = String.join(" ", commandList);
// 将命令行字符串记录到日志
log.info("执行的ffmpeg命令: {}", commandString);
// 使用命令参数列表创建ProcessBuilder
ProcessBuilder pb = new ProcessBuilder()
.command(commandList)
.redirectErrorStream(true)
.redirectOutput(tmpFile);
try {
log.info("命令行的命令为:", pb.command());
Process process = pb.start();
log.info("开始执行ffmpeg命令");
// 读取命令输出结果
try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
System.out.println("执行完成");
} catch (Exception e) {
log.error("执行ffmpeg命令出错:{}", e.getMessage());
e.printStackTrace();
}
return pinYinHeadChar;
}
我这里是通过Java提供的 ProcessBuilder类来调用命令提示符来完成的。在写命令之前可以先进入在服务器中查看命令是否可以被执行。然后在通过这个代码来完成转流。
ProcessBuilder
类在Java中可以跨平台使用,它会自动处理操作系统之间的差异。
但是,请确保在Linux服务器上安装了ffmpeg
工具,因为这是你要执行的命令。如果ffmpeg
不在默认的PATH
环境变量中,你可能需要提供ffmpeg
的完整路径。例如,你可以将ffmpeg
替换为/usr/local/bin/ffmpeg
(或者在你的系统中ffmpeg
的实际路径)。
在Linux上安装ffmpeg
的方法可能因发行版而异。在CentOS上,你可以使用以下命令安装ffmpeg
:
sudo yum install epel-release -y
sudo yum install ffmpeg ffmpeg-devel -y
安装完成后,你可以使用ffmpeg
命令。你的Java代码应该可以在Linux服务器上正常运行,只要确保所有相关的工具和库都已经安装。
大致流程可以理解成
首先拉取rtsp流,然后转为hls的分段数据片,最后通过m3u8的格式来播放这些数据片成一个完整的实时流视频。最后返回的是一个视频的播放地址。播放地址需要配合前端同时也需要在nginx中配置视频的播放地址。
问题:大部分视频播放404导致无法播放视频
1:首先想到可能是代码层面的问题,但是经过排查发现代码层面没有问题
2:对代码进行增加更多的日志来获取更多的信息,发现都顺利执行完成了
3:由于是通过代码调用命令行的形式来完成视频的转流以及播放,故通过添加命令行的执行文件观察ß执行流程
4:并且尝试通过vlc直接播放rtsp流发现可以正常播放,然后通过直接在windows窗口调用命令行直接代码中的指令,发现可以正常生成ts文件
5:当你使用 Executor 框架并异步执行 FFmpeg 命令时,命令的执行可能还没有完成,对应的文件可能还没有完整生成,而你的请求却已经过来了,因此会返回 404 错误。因此,在异步执行 FFmpeg 命令时,你需要等待命令执行完成并确认文件完整生成后,才能返回生成的文件路径。
这种方式能够找到文件而不报 404 错误,是因为你没有使用 Executor 框架来异步执行 FFmpeg 命令,而是直接在当前线程中执行了。因此,当命令执行完成后,文件已经被完整地生成了。而且没有使用 Executor 时,你也不需要等待异步执行完成,直接返回生成的文件路径即可。
6:出现播放同一个时间段的原因可能是视频流的切片时间长度(hls_time参数)设置过短,而直接卡住播放不动可能是由于转流过程出错,导致hls切片文件不完整或者损坏。
建议检查以下几个方面:
hls_time参数配置是否合理。hls_time参数是指hls切片的时间长度,如果设置过短,会导致切片频繁,播放过程中容易卡顿。可以将hls_time参数调整为较大的值,比如10s或者15s。
检查转流命令是否正确。在执行转流命令时,需要注意各个参数的含义和格式是否正确,以及输入和输出文件的路径是否正确。
检查转流过程中是否有错误发生。可以查看命令的输出信息,检查是否有异常信息提示,比如网络连接错误、数据传输错误等。同时,还可以检查生成的切片文件是否完整和正常,可使用播放器进行验证。
综上所述,可以通过调整hls_time参数、检查命令和检查转流过程中的错误信息来解决播放同一个时间段和卡顿的问题。如果问题仍然存在,可以详细查看日志信息,定位具体问题。
7:ProcessBuilder pb = new ProcessBuilder() .command("ffmpeg", "-i", rtsp, "-vcodec", "libx264", "-map", "0", "-f", "hls", "-threads", "6", "-preset", "ultrafast", "-hls_flags", "6", "-hls_list_size", "6", "-hls_time", "5", resultUrl)命令参数如下
ffmpeg:命令名称,表示要运行的程序。 -i:输入文件,这里代表输入的视频流地址。 rtsp:视频流地址,指定要转换的源视频流地址。 -vcodec:指定视频的编码器。 libx264:使用x264编码器。 -map:指定要复制的流。 0:表示从第一个输入(输入文件)中复制所有流。 -f:输出的封装格式。 hls:HTTP Live Streaming,Apple的流媒体传输协议。 -threads:使用的线程数,加快编码速度。 6:指定使用6个线程。 -preset:预设编码速度和质量。 ultrafast:尽可能快的编码速度,但会影响视频质量。 -hls_flags:设置hls标志,控制hls行为。 ``:设置为6,表示只保留最新的6个切片文件。 -hls_list_size:设置hls列表文件长度。 6:表示列表文件长度为6。 -hls_time:设置hls分片时长。 5:表示每个分片的时长为5秒。 resultUrl:转换的结果输出路径,存储输出文件的路径。有关更多的参数可以自行去了解,然后根据自己系统的需要来选择最合适的参数来播放。