打造一个简单的视频同步网站
任务需求
主要实现的任务就是大家通过加入同一个房间,然后能够观看到同一部视频,并且可以在一方设置了一些状态,比如暂停等, 其他同房间的用户可以捕获到并更新自己的状态。
在这里对于观看同一部视频而言,视频的来源来自一个m3u8视频源,这样直接用videojs就可以播,并且因为m3u8这样的视频可以在网上找并且也可以自己做,所以灵活性大,比如大家想看一部aa的电影,我们如果能找到这部电影的m3u8地址,那么就不用下载直接用即可,如果网上没有,那么我们可以下载下来然后转码成m3u8来看,所以更灵活。
对于大家同步而言,这里的考虑是对于每个房间,用房间号做key,然后视频的播放状态封装成一个类,做value,大家的获取和更新都可以基于这一份播放状态来更新,保持一致。而这个key-value保存到webcontext里面即可,大家都可以看得到。
下面就通过前端和后端两部分来进行开发。
前端部分
界面搭建
主要两部分,第一个就是输入房间信息,这个用一个模态框,还有就是视频播放,这里用videojs控制:
<div class="modal fade" tabindex="-1" role="dialog" id="join_room">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">加入房间</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">用户名</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="userName" name="userName" placeholder="">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">房间号</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="roomId" name="roomId" placeholder="">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">房间密码</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="roomPasswd" name="roomPasswd" placeholder="">
<span class="help-block"></span>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id = "join" >保存</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="container" >
<div class="row">
<div class="col-md-4" id="room_id"></div>
<div class="col-md-4" id="movie_url_area"></div>
<div class="col-md-4" id="update_url_btn_area"></div>
</div>
<div class="row">
<div class="col-md-12" id="video_area" style="display:none;">
<video id="my_video" class="video-js" controls preload="auto" width="960" height="400" data-setup="{}" >
<p class="vjs-no-js">Not support</p>
</video>
</div>
</div>
</div>
进入房间
var vid = document.getElementById("my_video");
var userName;
var roomId;
var movieSyc;
$(function(){
reset_form("#join_room form");
$('#join_room').modal({
backdrop: "static"
});
});
当输入网址后,第一件事情是需要输入用户名,房间名和密码(也就是上面的弹出一个模态框join_room),然后到后台查询,有三种情况:
1、房间不存在,则后台创建一个空房,并返回对应视频同步状态
2、房间存在且密码正确,则返回该房间的视频同步状态
3、房间存在且密码错误,返回错误码
$("#join").click(function(){
roomId = $("#roomId").val();
var roomPasswd = $("#roomPasswd").val();
userName = $("#userName").val();
$.ajax({url:"${BASE_PATH}/get",
type:"GET",
data:{"roomId":roomId,"roomPasswd":roomPasswd,"userName":userName},
success:function(result){
if(result.code==100){
$("#join_room").modal('hide');
$("#room_id").text("房间号:"+roomId);
$("#movie_url_area").append($("<input type='text' class='form-control' id='movie_url' placeholder='地址m3u8'></input>"));
$("#update_url_btn_area").append($("<button type='button' class='btn btn-primary' id = 'update_url_btn' >更新</button>"));
$("#video_area").attr("style","");
videojs("my_video").ready(function() {});
if(result.extend.movieSyc.movieUrl!=null){
update_url(result.extend.movieSyc.movieUrl);
}
get_update();
report_time();
}else{
$("#roomPasswd").addClass("has-error");
$(".help-block").text(result.msg);
}
}});
});
function update_url(url){
$("#movie_url").val(url);
videojs("my_video").src(url);
}
如果成功进入房间里,则需要构建这个房间,主要包括一个输入框,输入电影地址,一个按钮用来提交,一个videojs用来播放视频。result.code==100
里面就是做这些内容的,并且如果进入的房间里面本来就有人在播放电影,这里需要进行一个把当前的电影地址更新一下。
除此之外里面还有另外的两个函数get_update();
和report_time();
,get_update是一个死循环函数,主要是持续向后台发请求,如果后台发现电影播放状态更新了,就响应get_update,如果状态没更新就阻塞,这样达到保持自己最新。而report_time主要是将当前播放时间汇报给后台,这样可以让大家都知道彼此的进度,保持大家一致。稍后会简单分析这两个函数。
get_update
function get_update(){
//持续更新,进入房间后使用
$.ajax({url:"${BASE_PATH}/getupdate",
type:"GET",
data:{"roomId":roomId,"userName":userName},
success:function(result){
movieSyc = result.extend.movieSyc;
console.log(result);
update_status(movieSyc);
get_update(); // 持续更新
}});
}
从上面可以看出,get_update里面是发送了一个ajax请求,如果请求成功了还会继续调用这个函数,如果后台阻塞,则这里也会等待。获取完成后,会对状态做一个简单的更新update_status(movieSyc);
function update_status(movieSyc) {
if(movieSyc.movieUrl!=null){
if(movieSyc.movieUrl!= $("#movie_url").val()){
update_url(movieSyc.movieUrl); //因为更新url会重新加载视频,如果未更改则不修改
}
if(movieSyc.time!=null){
set_time(vid,movieSyc.time);
}
if(movieSyc.play==true){
videojs("my_video").autoplay();
videojs("my_video").play();
}else {
videojs("my_video").pause();
}
}
}
可以看出其主要就是根据后台信息,来更新当前的状态。
report_time
这个主要是按照一定时钟,来报告自己的状态:
function report_time(){
setInterval(function(){
if(movieSyc.play){
var currentTime = Math.floor(videojs("my_video").currentTime());
click_update(roomId,null,null,currentTime);
}
},10000);
}
如果视频是播放的状态,则按照每10s来报告一次自己的进度条状态,这里涉及到一个函数click_update(roomId,null,null,currentTime);
,这个函数就是向后台发送请求,来更新后台的房间状态:
function click_update(roomId,movie_url,play,time){
$.ajax({url:"${BASE_PATH}/update",
type:"GET",
data:{"roomId":roomId,"movieUrl":movie_url,"play":play,"time":time}
});
}
这里不仅仅可以更新时间,还有播放状态,url等。
其他事件的更新
除了按照10s一次来更新自己的时间状态来避免进度不一致,如果发生了一些事件,比如播放暂停等,我们也需要报告状态,而这个事件可以通过事件函数捕获:
videojs("my_video").on("play",function()
{
click_update(roomId,null,true,Math.floor(videojs("my_video").currentTime()));
});
videojs("my_video").on("pause", function(){
click_update(roomId,null,false,Math.floor(videojs("my_video").currentTime()));
});
上面两个就可以对播放暂停进行上报状态,并且对拖动进度条也可以上报,因为拖动进度条会触发播放和暂停事件,而播放和暂停里面也上报了当前时间。
更新url的上报:
$(document).on("click","#update_url_btn", function () {
var movie_url = $("#movie_url").val();
click_update(roomId,movie_url,true,0);
update_url(movie_url)
});
直接通过点击更新这个按钮来上报,播放时间置为0。
其他函数
上面还用到了一个set_time,其实就是设置时间,这里主要是想着如果时间差距不大,可以不重置,所以封装了一下:
function set_time(vid,time){
if(Math.abs(videojs("my_video").currentTime()-time)<5){
return;
}else{
videojs("my_video").currentTime(time);
}
}