
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.youkuaiyun.com/u010605082/article/details/50896685
伪流技术是一种能在常见HTTP服务器如APACHE、tomcat、IIS或lighttpd上安装提供的一种协议。它使用服务端脚本来提供客户端到服务器的视频交互。播放器在URL上携带start time的参数发送HTTP请求到服务端,服务端的脚本处理视频流并且给予回复,保证提供的视频流起始位置与START TIME参数所对应。这个start time的参数通常命名为“start”,这个技术同样被风靡全球的youtube所使用,它使用的是lighttpd WEB服务器。
对于播放器来说,使用伪流或其他流式的解决方案最大的好处是:能跳转到尚未下载到的视频部分。这种情况在很符合大文件播放需求,比如2个小时的视频,用户想立刻跳转到它的后面部分开始播放,(这样不需要下载中间用户不关心的部分了)。好处如下:
- 能够随机跳转到视频的任意时间
- 从视频的中间开始播放
- 提供客户方流媒体服务器和服务端脚本集成的可能
- 支持FLV和H.264的视频
直到现在,仍然不存在一项旨在网页上显示视频的标准。
今天,大多数视频是通过插件(比如 Flash)来显示的。然而,并非所有浏览器都拥有同样的插件。
HTML5 规定了一种通过 video 元素来包含视频的标准方法。
Ogg = 带有 Theora 视频编码和 Vorbis 音频编码的 Ogg 文件
MPEG4 = 带有 H.264 视频编码和 AAC 音频编码的 MPEG 4 文件
WebM = 带有 VP8 视频编码和 Vorbis 音频编码的 WebM 文件
package com.roden.video;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardOpenOption.READ;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public final class PseudostreamingServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final int BUFFER_LENGTH = 1024 * 16;
private static final long EXPIRE_TIME = 1000 * 60 * 60 * 24;
private static final Pattern RANGE_PATTERN = Pattern.compile("bytes=(?<start>\d*)-(?<end>\d*)");
private String videoPath;
@Override
public void init() throws ServletException {
videoPath = getInitParameter("videoPath");
}
@Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
private void processRequest(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
String videoFilename = URLDecoder.decode(request.getParameter("video"), "UTF-8");
Path video = Paths.get(videoPath, videoFilename);
int length = (int) Files.size(video);
int start = 0;
int end = length - 1;
String range = request.getHeader("Range");
range=range==null?"":range;
Matcher matcher = RANGE_PATTERN.matcher(range);
if (matcher.matches()) {
String startGroup = matcher.group("start");
start = startGroup.isEmpty() ? start : Integer.valueOf(startGroup);
start = start < 0 ? 0 : start;
String endGroup = matcher.group("end");
end = endGroup.isEmpty() ? end : Integer.valueOf(endGroup);
end = end > length - 1 ? length - 1 : end;
}
int contentLength = end - start + 1;
response.reset();
response.setBufferSize(BUFFER_LENGTH);
response.setHeader("Content-Disposition", String.format("inline;filename="%s"", videoFilename));
response.setHeader("Accept-Ranges", "bytes");
response.setDateHeader("Last-Modified", Files.getLastModifiedTime(video).toMillis());
response.setDateHeader("Expires", System.currentTimeMillis() + EXPIRE_TIME);
response.setContentType(Files.probeContentType(video));
response.setHeader("Content-Range", String.format("bytes %s-%s/%s", start, end, length));
response.setHeader("Content-Length", String.format("%s", contentLength));
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
int bytesRead;
int bytesLeft = contentLength;
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_LENGTH);
try (SeekableByteChannel input = Files.newByteChannel(video, READ);
OutputStream output = response.getOutputStream()) {
input.position(start);
while ((bytesRead = input.read(buffer)) != -1 && bytesLeft > 0) {
buffer.clear();
output.write(buffer.array(), 0, bytesLeft < bytesRead ? bytesLeft : bytesRead);
bytesLeft -= bytesRead;
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
由于使用了Nio请使用jdk7以上版本
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<servlet>
<servlet-name>stream</servlet-name>
<servlet-class>com.roden.video.PseudostreamingServlet</servlet-class>
<init-param>
<param-name>videoPath</param-name>
<param-value>F:/BaiduYunDownload/</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>stream</servlet-name>
<url-pattern>/stream</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
注意此处配置了视频文件的保存目录,需要根据实际情况进行修改
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Video Pseudostreaming</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="//code.jquery.com/jquery-1.12.0.min.js"></script>
<script>
$(document).ready(function(){
var video = $('#myvideo');
$("#play").click(function(){ video[0].play(); });
$("#pause").click(function(){ video[0].pause(); });
$("#go10").click(function(){ video[0].currentTime+=10; });
$("#back10").click(function(){ video[0].currentTime-=10; });
$("#rate1").click(function(){ video[0].playbackRate+=2; });
$("#rate0").click(function(){ video[0].playbackRate-=2; });
$("#volume1").click(function(){ video[0].volume+=0.1; });
$("#volume0").click(function(){ video[0].volume-=0.1; });
$("#muted1").click(function(){ video[0].muted=true; });
$("#muted0").click(function(){ video[0].muted=false; });
$("#full").click(function(){
video[0].webkitEnterFullscreen(); // webkit类型的浏览器
video[0].mozRequestFullScreen(); // FireFox浏览器
});
});
</script>
</head>
<body>
<video id="myvideo" width="80%" height="80%" controls="controls">
<source src="stream?video=美丽的风景.mp4" />
<!--<source src="stream?video=美丽的风景.mp4" type="video/mp4" />
<source src="stream?video=美丽的风景.mp4" type="video/webM" />
<source src="stream?video=美丽的风景.mp4" type="video/ogg" />-->
你的浏览器不支持html5
</video>
<hr>
<button id="play">播放</button>
<button id="pause">暂停</button>
<button id="go10">快进10秒</button>
<button id="back10">快退10秒</button>
<button id="rate1">播放速度+</button>
<button id="rate0">播放速度-</button>
<button id="volume1">声音+</button>
<button id="volume0">声音-</button>
<button id="muted1">静音</button>
<button id="muted0">解除静音</button>
<button id="full">全屏</button>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
注意根据目录下的文件修改请求文件名 示例中为美丽的风景
由于IE对html5的兼容性较差,ie9以上才支持mp4格式,其它版本可以使用flash进行播放
2、3年经验的切图仔,如何把开发思维提前 5 年?
每个前端开发都想面试大厂、带团队,可是大厂技术思维领先你至少5年,想要进击,就得把眼光放到前方,工程化正是你需要的!快狗打车Leader王超力作,58沈剑首次站台,学完直接落地
java实现摄像头调用,处理视频流
packagecom.fx;importjava.awt.BorderLayout;importjava.awt.Toolkit;importjavafx.application.Platform;i…
博文
来自: 随风飘扬的尘土
如何捕捉视频流并保存到文件(Capture video to File)
1将视频流保存到AVI文件AVIMuxfilter接收从capturepin过来的视频流,然后将其打包成AVI流。音频流也可以连接到AVIMuxFilter上,这样muxfilter就将视频流和视频流…
博文
来自: Simon、的专栏
实时监控、直播流、流媒体、视频网站开发方案设计简要
欢迎大家积极开心的加入讨论群群号:371249677(点击这里进群)一、本地推送端1、本地:采用javaCV(安卓和java平台推荐javaCV)、ffmpeg、openCV或者jmf可以很方便的获取…
博文
来自: 博客内容将迁移至博客园、掘金和SegmentFault!-eguid
JAVA-IO之读取媒体文件(图片、音频、视频)
在JAVA中使用字节流处理媒体文件。关键词:FileInputStreamFileOutputStreamBufferedInputStreamBufferedOutputStream下面代码演示复制…
博文
来自: 薛定谔的猫窝
java输出视频流
@RequestMapping(value="/imageView1",method={RequestMethod.GET})publicvoidimageView1(Http…
博文
来自: Fisher_yu01的博客
Java web 几种实现在网页页面里播放视频的 插件及方法
第一种使用jQuery库的video.js (1)首先我们要下载video.js 网址:http://www.jq22.com/jquery-info404(2)然后在要播放的html/jsp页面引…
博文
来自: www_worf的博客
java制作视频播放器
前言国庆前几天,无意中看到关于VLCJ包的文章,可以导入java工程制作自己的java视频播放器,感觉有意思,就自己捣鼓几天,做出了个功能较为齐全的跨平台的视频播放器,高级功能之后有时间再弄,我把项目…
博文
来自: gannyee
软件工业的JIT宣言
JIT起源于二十世纪初期,兴盛于日本。在工业化大生产的机械时代,它在各行各业中被采用并改进,更是广泛应用于制造业。无疑,在这一阶段,JIT管理对于生产行业,特别是制造业的贡献,功不可抹;它使机械…
博文
来自: shtonyhu的专栏
java处理视频文件,读取视频的时长
最近需要用java处理视频文件,读取视频的时长,在网上查,没找到合适的方法,用JMF吧,支持的格式太少,用JNI去读取其他语言写的API,但比较麻烦。无意中找到了jave这个开源的项目,看了看介绍,是…
博文
来自: sivyer123的专栏
Java 处理视频 、音频文件(读取视频时长等) – JAVE
简介 TheJAVE(JavaAudioVideoEncoder)libraryisJavawrapperontheffmpegproject.Developerscantaketakeadva…
博文
来自: jerome_s的博客
Java中使用Opencv从视频文件中获取帧
实现功能:使用Java获取mp4、mov、avi等视频文件中的图像帧,每秒获取一帧图像,并保存环境要求:需要安装Opencv,安装FFmpeg,下载javacv包操作系统:本次实验使用的Ubuntu系…
博文
来自: Fang的博客
用java实现视频截帧的方法
在软件系统中有时在wed端、移动端显示视频时,如果同时显示多个视频,有可能会将多个视频的数据全部装入内存,在一定程度上回影响系统性能。比较好的一中解决方案是显示视频的一帧图片,点击图片之后即可播放视频…
博文
来自: weibin_6388的专栏
【Java】Red5服务器搭建(实现在线直播,流媒体视频播放)
引言流媒体文件是目前非常流行的网络媒体格式之一,这种文件允许用户一边下载一边播放,从而大大减少了用户等待播放的时间。另外通过网络播放流媒体文件时,文件本身不会在本地磁盘中存储,这样就节省了大量的磁盘空…
博文
来自: 小帅丶的专栏
java返回H5视频流能够在IOS中播放
在IOS中,H5页面中,video标签下,视频源必须满足range的要求,如果视频比如快进或者后退,实际上是再次发送了视频源的请求只不过range的范围不同。不说了,上代码:publicvoidret…
博文
来自: LeoHan163的博客
JAVA 解码rtsp视频流
packagecom.aast.test;importjava.io.*;importjava.text.SimpleDateFormat;importjava.util.Date;importorg…
博文
来自: douzi949389的博客
html5 video标签播放视频流解决方案
项目要求从文件服务器读取音视频文件,以流的方式传给前台,并能够播放视频。做了一个demo,用html5的video,audio标签实现。后台实现代码:@GetMapping(value="/…
博文
来自: weixin_37781081的博客
java处理苹果浏览器safari无法播放视频流(Accept-Ranges)
最近在做一个在线播放视频流的程序,安卓和电脑上都可以正常的播放。可以唯独ios上无法正常播放。开始在网上找了很多资料觉得不是很到位,所以在这里记录一下。刚开始时代码返回的视频流是在一个请求里全部返回的…
博文
来自: u010120886的博客
屏蔽百家号 -(baijiahao)
快过年了,回家了,发个非技术博客吧。最近被百家号恶心到不行,搜了下屏蔽方法,在家懒得翻墙用谷歌,又懒得装插件设置屏蔽,找到了一个简单有效的方法,直接在搜索内容后边加-(baijiahao),效果还不错…
博文
来自: 慢慢积累
ARM上Linux的TCP通信实例 – ZYNQ7020学习
这是在接触一段时间的Linux网络通信后回过来给自己重新熟悉一些基本函数功能,所以,这里不做任何代码注释,自己慢慢去查看每一个函数的原型、参数含义、返回值以及调用方式,这样才能真正学到东…
博文
来自: 逸璞丷昊的博客
linux下的C语言开发(定时器)
定时器是我们需要经常处理的一种资源。那linux下面的定时器又是怎么一回事呢?其实,在linux里面有一种进程中信息传递的方法,那就是信号。这里的定时器就相当于系统每隔一段时间给进程发一个定时信号,我…
博文
来自: weixin_34347651的博客
安装 win7+ubuntu16.0 双系统
电脑配置:1,双硬盘:SSD120G+HDD500G;2,电脑没有EFI模式,只能用GRUB模式;3,电脑boot只能识别主硬盘位的SSD,光驱位HHD开机时不识别;(导致按教程安装不成功,折腾了三天…
博文
来自: 独钓寒江雪
Win7+Ubuntu16.04双系统安装
1、安装前准备1.1下载Ubuntu16.04镜像(32位或64位)1.2使用ultraISO制作U盘启动盘1.3安装Ubuntu系统1.4用EasyBCD创建启动系统启动引导1.5启动系统2、划分系…
博文
来自: Asia-Lee的博客
Ajax与Struts2的action之间的数据交互
本文将主要说明Ajax与Action数据交互的实现过程,前端使用JQuery中Ajax的相关方法,get或者post,将数据以Json格式传回到业务调度Action中,Action中处理后,再讲数据以…
博文
来自: 正在加载中
tensorflow-android 官方demo源码分析
1引言目前深度学习模型已经应用到了各个领域,将TensorFlow训练模型部署到终端上也逐步变为了现实。特别是mobileNet等体积小,占用内存少的模型出现后,将深度学习应用到终端上逐渐变得火热起来…
博文
来自: 谢杨易的博客
无需备份!!!动态磁盘转换为基本磁盘!!!绝对可用!!!
刚刚换了固态硬盘,替换下来的机械硬盘,买了移动硬盘盒,组装成了移动硬盘,但是,因为这个硬盘之前在我的电脑里设置过,插上后是动态的,磁盘管理识别无效。百度一番,说是要备份后,在磁盘管理中转换为基本磁盘,…
博文
来自: 不努力的人不配谈理想
css3实现旋转相册效果
&lt;!doctypehtml&gt;&lt;htmllang="en"&gt;&lt;head&gt;&lt;metac…
博文
来自: scarsun的博客
spring boot spring security 自定义 filter FilterSecurityInterceptor 访问资源 静态资源 和 不需要权限访问都失效了
问题:spring boot security 中,filters="none" 对应哪个?自定义AbstractSecurityInterceptor后 静态资源也进入拦截器,登…
博文
来自: laokaizzz的专栏
VS2017必备的插件扩展
“工具善其事,必先利其器!装好这些插件让vs更上一层楼”ReSharper : 首先的是Resharper,这个基本是目前是我开发过程中必备的工具集,唯一的缺点就是吃内存,所以你的内存要是低于8G,…
博文
来自: ethan1121的博客
软考中级网络工程师学习笔记(知识点汇总)单点详细版
线路交换1、线路交换进行通信:是指在两个站之间有一个实际的物理连接,这种连接是结点之间线路的连接序列。2、线路通信三种状态:线路建立、数据传送、线路拆除3、线路交换缺点:典型的用户/主机数据连接状态,…
博文
来自: 华农老林的博客
使用inno setup做打包的时候,如何检测系统是否已安装了.net 4.0 以上的版本?
.net framework 能否打包到安装程序里面? 如何没有安装,系统帮自动安装. 有没相关的例子代码?
论坛
springboot后台接收集合(对象集合)
最近在工作中遇到一个问题,发现直接用集合去接收前台传过来的集合,会报如下图的错然后百度了很多种方法,百度的过程中看到的有比如在接收的参数前面加入@RequestBody但是我试过之后好像没什么用也有说…
博文
来自: weixin_43009990的博客
vmware的命令行参数
vmrun命令行工具:对于自动化测试有如下实用命令行(-T代表目标类型,后更参数ws代表Workstation):lPowerCommands:开启、停止、重启、挂起虚拟机vmrun-Twsstart…
博文
来自: 我是guyue,guyue就是我O(∩_∩)O
Unexpected token o in JSON at position 1 报错原因
欢迎访问我的个人博客http://xiaolongwu.cn/写在前面的话这个问题在之前做项目时碰到过一次,当时按照网上的做法,去掉JSON.parse()这一层转换后就没有这个报错了,数据也能正常使…
博文
来自: 积少成多
iOSTAT 命令的详解 (可以用作Solaris 查看内存、CPU和磁盘读写速度)
iostat-报告I/O统计信息用法概要/usr/bin/iostat[-cCdDeEiImMnpPrstxXYz][-ln][-Tu|d][disk]…[interval[count]]描述io…
博文
来自: Booooom