【WebLabelSoft】项目打卡之视频帧标注实时恢复(帧截取+标注恢复+同步播放+帧跳转)

Web演示

在这里插入图片描述

1.同步播放

在播放事件触发后,依据定时器实时获取帧及对应数据,并在画布恢复背景帧及标注信息

1.1添加播放控件

$("#PlayPause").click(function () {
    if ($("#myVideo")[0].paused){
        $("#myVideo")[0].play();
    } else{
        $("#myVideo")[0].pause();
    }
});

1.2视频帧截取

通过不断视频帧截取,并粘贴至画布,以模拟视频播放的效果。
踩的坑:
1)画布是完全覆盖在视频上,起初为了看见视频画面,设置画布区域透明度opacity: 0.5 直接导致的是画布上的标注信息(线条、文字)变得模糊。
2)截取视频帧时采取定时器去描述时间间隔,去截取视频帧,drawImage至画布。然而setTimeout和setInterval精度不高且会出现丢帧。

实现步骤:
①定时器:requestAnimationFrame

function render() {
    var now = Date.now();
    if($("#myVideo")[0].currentTime>=$(".duration").text()|| globalFrame>=globalMaxFrame) {
        $(".current-time").text($(".duration").text());
    }else{
        $(".current-time").text($("#myVideo")[0].currentTime.toFixed(2));
    }

    if ($("#myVideo")[0].played) {
        console.log(globalFrame, ":", Math.round($("#myVideo")[0].currentTime / globalFrameTime));
        if(globalFrame!==Math.round($("#myVideo")[0].currentTime / globalFrameTime)) {
            var currentRatio = $("#myVideo")[0].currentTime / $("#myVideo")[0].duration;
            $(".progress-bar").css("width", currentRatio*100 + '%'); //只有当帧改变再更新进度条
            console.log("实际时间",now,"AnimationFrame时间戳",$("#myVideo")[0].currentTime);
            getCurrentFrame();  //该自定义函数主要加载当前时间戳对应帧(并调用标注信息加载及绘制操作函数)

            timer = window.requestAnimationFrame(render);
        }else {
            console.log("帧没变化");
            timer = window.requestAnimationFrame(render);
        }
    }else {
        window.cancelAnimationFrame(timer);
    }
}

var timer;
$("#myVideo").bind({
    'play': function () {
        if($("#myVideo")[0].currentTime>globalFrameTime) {
        }else {
            $(".progress-bar").css("width",0+'%'); //进度条归0
        }
        // timer = setInterval(getCurrentFrame, 1000 * globalFrameTime);
        timer = requestAnimationFrame(render);
        console.log("video play");
    },
    'pause': function () {
        // clearInterval(timer);
        window.cancelAnimationFrame(timer);
        console.log("video pause");
    },
    'ended': function () {
        $(".progress-bar").css("width", 100 + '%'); //进度条满血
        $(" .play-btn span").removeClass("glyphicon-pause").addClass("glyphicon-play");
        // clearInterval(timer);
        window.cancelAnimationFrame(timer);
        console.log("video ended");
    }
});

②绘制视频帧
思路:为了让帧作为画布背景,以致标签信息在背景帧的上面,每次在清除完画布后,立即绘制对应的背景帧。

function clearCanvas() { //清除画布
    canvas.ctx.clearRect(0, 0, canvas.width, canvas.height);
    if($("#videoName").val()) {
        var v = $("#myVideo")[0];
        canvas.ctx.drawImage(v,0,0,canvas.width,canvas.height);
    }else {
        var image = $("#myimg")[0];
        canvas.ctx.drawImage(image,0,0,canvas.width,canvas.height);
    }
}

1.2.标注恢复

①②③④⑤⑥⑦⑧√×✔✘☞☜
页面显示,后端所发送含标注信息的json格式数据
实现步骤:
①获取当前帧:自定义getCurrentFrame();

function getCurrentFrame() {
    var currentTime = $("#myVideo")[0].currentTime; // 检测当前的播放时间
    globalFrame = Math.round(currentTime / globalFrameTime); //globalFrameTime为帧间隔
    console.log("当前帧", globalFrame,"当前时间戳",currentTime,"/",$("#myVideo")[0].duration);
    currentFrameData();
}

②当前帧数据获取

function currentFrameData() { //总共26帧 0~25
    // var currentTime = $("#myVideo")[0].currentTime; // 检测当前的播放时间
    // globalFrame = Math.round(currentTime / globalFrameTime);
    if (globalFrame>=globalMaxFrame) {
        globalFrame = globalMaxFrame;
        console.log("帧结束", globalFrame);
    }

    if (globalVideoJsonData.hasOwnProperty(globalFrame.toString())) {
    	//data的索引后是个json字符串,需要转为json对象 js才能操作
        var jsonObj = JSON.parse(globalVideoJsonData[globalFrame.toString()]); //当下帧对应数据 
        console.log(globalFrame, " data:", jsonObj);
        restoreData(jsonObj); //恢复数据至对应变量
    } else {
        console.log(globalFrame,"该帧数据不存在");
        clearCanvas();
        canvas.labels = [];
        canvas.polygons = [];
    }
}

③当前帧显示:帧背景+标注信息

function restoreData(data) {
    clearCanvas();  //清空画布,绘制当下帧背景
    canvas.labels = [];
    canvas.polygons = [];
    // alert("data:" + data.labels); //可以 .属性 方式访问,说明闲杂是json对象  //JS操作的是JSON对象
    $.each(data.polygons,function (index,value) {
        var polygon = {};
        for(var i=0;i<Object.keys(value).length/2;i++) {
            polygon["x"+i] = value["x"+i]
            polygon["y"+i] = value["y"+i]
        }
        canvas.labels.push(data.labels[index]);
        canvas.polygons.push(polygon);
     });
    console.log("labels",canvas.labels);
    drawPolygons();  //画四边形框
    drawAllLabel();  //画标签
}

☞帧间隔 globalFrameTime
web端视频标签可以获取视频的总时长与当前时间(注意这个当前时间是不精确的,因为会存在加载卡顿),所以本地数据库是以帧数去获取保存对象数据的。故为二者对应,我们需要知道当前时间的对应帧(即除以帧间隔)
措施:我们在上传视频、OCR识别完成返回的视频标注数据中嵌入当前视频总帧数,然后通过总时间除以总帧数即可

globalVideoJsonData = data; //data为ajax异步通信返回的视频标注数据,内含视频的总帧数
globalMaxFrame = globalVideoJsonData["totalFramesNum"] - 1; // 0 ~
globalFrameTime = $("#myVideo")[0].duration / (globalMaxFrame+1); //从0开始。注帧间隔中显示的是前一帧

2.帧跳转

对于视频,我们通常需要去进行信息回查,故只从前到后的依次播放不能满足需求

2.1.前后帧切换

踩的坑:
1)帧跳转(即人为该变时间戳$("#myVideo")[0].currentTime = newValue,赋予新值后,视频不会立即跳转,加载目标时间戳的帧需要时间。这也是起初导致帧切换后,标注信息切换过去了,而帧背景绘制的仍是原始帧的图片。由于目标帧还未加载完,便要画背景帧,绘制完背景帧后视频才切换到目标位置(此时视频画面和画布真背景是不一样的
solution:在绘制背景帧前加个延时,以便其目标帧加载完

①添加前后帧切换控件

$("#Prev").click(function () {
    getPrevFrame();
});

$("#Next").click(function () {
    getNextFrame();
});

function getNextFrame() {
    var currentTime = $("#myVideo")[0].currentTime; // 检测当前的播放时间
    if(currentTime+globalFrameTime<=$("#myVideo")[0].duration) {
        globalFrame = Math.round(currentTime / globalFrameTime);
        globalFrame += 1;
        $("#myVideo")[0].currentTime = (globalFrame+0.25)*globalFrameTime;
        $(".current-time").text($("#myVideo")[0].currentTime.toFixed(2));
        var currentRatio = $("#myVideo")[0].currentTime / $("#myVideo")[0].duration;
        $(".progress-bar").css("width", currentRatio*100 + '%');

        console.log("后一帧", globalFrame,"时间戳",$("#myVideo")[0].currentTime);
        // currentFrameData(); //放在滑动条改变响应函数中,以致目标帧加载完
    }else {
        $("#myVideo")[0].currentTime = $("#myVideo")[0].duration;
        $(".current-time").text($(".duration").text());
    }
}
function getPrevFrame() {
    var currentTime = $("#myVideo")[0].currentTime; // 检测当前的播放时间
    if(currentTime>0) {
        globalFrame = Math.round(currentTime / globalFrameTime);
        globalFrame -= 1;
        if(globalFrame<=0) {
            globalFrame = 0;
            $(".progress-bar").css("width",0+'%');
            $("#myVideo")[0].currentTime = 0;
        }else{
            $("#myVideo")[0].currentTime = (globalFrame+0.25)*globalFrameTime;
        }
        $(".current-time").text($("#myVideo")[0].currentTime.toFixed(2));
        var currentRatio = $("#myVideo")[0].currentTime / $("#myVideo")[0].duration;
        $(".progress-bar").css("width", currentRatio*100 + '%');

        console.log("前一帧哈", globalFrame,"时间戳",$("#myVideo")[0].currentTime);
        // currentFrameData();  //放在滑动条改变响应函数中,以致目标帧加载完
    }
}

②目标帧加载缓冲
给视频添加监听滑动条改变的事件(即时间戳人为赋值跳转)

$("#myVideo").bind({
	   ...
     'seeking':function() {  //帧跳转
        console.log('开始移动进度条');
        setTimeout(function () {  //给个定时器好让图片加载完
            console.log("视频帧缓冲");
            currentFrameData();  //当前帧数据
        }, 200); //200ms
    }
});

2.2 进度条跳转

开始跳转

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星空•物语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值