$(document).ready(function(e){ var powerVideoWindow = $('#powerVideo') var powerVideoMonitorsListElement = $('#powerVideoMonitorsList') var powerVideoMonitorViewsElement = $('#powerVideoMonitorViews') var powerVideoTimelineStripsContainer = $('#powerVideoTimelineStrips') var powerVideoDateRangeElement = $('#powerVideoDateRange') var powerVideoVideoLimitElement = $('#powerVideoVideoLimit') var powerVideoEventLimitElement = $('#powerVideoEventLimit') var powerVideoLoadedVideos = {} var powerVideoLoadedEvents = {} var powerVideoLoadedChartData = {} var loadedTableGroupIds = {} var eventsLabeledByTime = {} var monitorSlotPlaySpeeds = {} var currentlyPlayingVideos = {} var extenders = { onVideoPlayerTimeUpdateExtensions: [], onVideoPlayerTimeUpdate: function(extender){ extenders.onVideoPlayerTimeUpdateExtensions.push(extender) }, onVideoPlayerCreateExtensions: [], onVideoPlayerCreate: function(extender){ extenders.onVideoPlayerCreateExtensions.push(extender) }, } var activeTimeline = null // fix utc/localtime translation (use timelapseJpeg as guide, it works as expected) > powerVideoDateRangeElement.daterangepicker({ startDate:$.ccio.timeObject().subtract(moment.duration("24:00:00")), endDate:$.ccio.timeObject().add(moment.duration("24:00:00")), timePicker: true, timePicker24Hour: true, timePickerSeconds: true, timePickerIncrement: 30, locale: { format: 'DD/MM/YYYY h:mm A' } },function(start, end, label){ // $.pwrvid.drawTimeline() powerVideoDateRangeElement.focus() getSelectedMonitors().each(function(n,activeElement){ var monitorId = $(activeElement).attr('data-monitor') requestTableData(monitorId) }) }); // fix utc/localtime translation (use timelapseJpeg as guide, it works as expected) /> var loadVideosToTimeLineMemory = function(monitorId,videos,events){ powerVideoLoadedVideos[monitorId] = videos powerVideoLoadedEvents[monitorId] = events } var drawMonitorsList = function(){ var html = '' $.each($.ccio.mon,function(n,monitor){ html += `
${monitor.name}
` }) powerVideoMonitorsListElement.html(html) } var requestTableData = function(monitorId,user){ if(!user)user = $user var dateData = powerVideoDateRangeElement.data('daterangepicker') $.ccio.cx({ f: 'monitor', ff: 'get', fff: 'videos&events', videoLimit: parseInt(powerVideoVideoLimitElement.val()) || 0, eventLimit: parseInt(powerVideoEventLimitElement.val()) || 500, startDate: dateData.startDate.utc().format('YYYY-MM-DDTHH:mm:ss'), endDate: dateData.endDate.utc().format('YYYY-MM-DDTHH:mm:ss'), ke: user.ke, mid: monitorId }) } var unloadTableData = function(monitorId,user){ if(!user)user = $user delete(powerVideoLoadedVideos[monitorId]) delete(powerVideoLoadedEvents[monitorId]) delete(loadedTableGroupIds[monitorId]) delete(loadedTableGroupIds[monitorId + '_events']) powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid="${monitorId}"]`).remove() drawLoadedTableData() } var checkEventsAgainstVideo = function(video,events){ var videoStartTime = new Date(video.time) var videoEndTime = new Date(video.end) var eventsToCheck = events video.detections = {} var newSetOfEventsWithoutChecked = {} $.each(eventsToCheck,function(n,event){ var eventTime = new Date(event.time) var seekPosition = (eventTime - videoStartTime) / 1000 if (videoStartTime <= eventTime && eventTime <= videoEndTime) { if(!video.details.confidence)video.details.confidence = 0 video.detections[seekPosition] = event eventsLabeledByTime[video.mid][video.time][seekPosition] = event }else{ newSetOfEventsWithoutChecked[n] = video } }) eventsToCheck = newSetOfEventsWithoutChecked } var prepareVideosAndEventsForTable = function(monitorId,videos,events){ var chartData = [] eventsLabeledByTime[monitorId] = {} $.each(videos,function(n,video){ eventsLabeledByTime[monitorId][video.time] = {} if(videos[n - 1])video.videoAfter = videos[n - 1] if(videos[n + 1])video.videoBefore = videos[n + 1] checkEventsAgainstVideo(video,events) chartData.push({ group: loadedTableGroupIds[monitorId], content: `
${video.time}
`, start: video.time, end: video.end, videoInfo: video }) }) $.each(events,function(n,event){ var eventReason = event.details && event.details.reason ? event.details.reason.toUpperCase() : "UNKNOWN" var eventSlotTag = eventReason if(eventReason === 'OBJECT' && event.details.matrices && event.details.matrices[0]){ eventSlotTag = [] event.details.matrices.forEach(function(matrix){ eventSlotTag.push(matrix.tag) }) eventSlotTag = eventSlotTag.join(', ') } chartData.push({ group: loadedTableGroupIds[monitorId + '_events'], content: `
${eventSlotTag}
`, start: event.time, eventInfo: event }) }) return chartData } var getMiniEventsChartConfig = function(video){ var monitorId = video.mid var labels = [] var chartData = [] var events = video.detections $.each(events,function(n,v){ if(!v.details.confidence){v.details.confidence=0} var time = $.ccio.timeObject(v.time).format('MM/DD/YYYY HH:mm:ss') labels.push(time) chartData.push(v.details.confidence) }) var timeFormat = 'MM/DD/YYYY HH:mm:ss'; Chart.defaults.global.defaultFontColor = '#fff'; var config = { type: 'bar', data: { labels: labels, datasets: [{ type: 'line', label: 'Motion Confidence', backgroundColor: window.chartColors.blue, borderColor: window.chartColors.red, data: chartData, }] }, options: { maintainAspectRatio: false, title: { fontColor: "white", text:"Events in this video" }, scales: { xAxes: [{ type: "time", display: true, time: { format: timeFormat, } }], }, } }; return config } var drawMiniEventsChart = function(video,chartConfig){ var videoContainer = powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid=${video.mid}]`) var canvas = videoContainer.find('canvas') var ctx = canvas[0].getContext("2d") var miniChart = new Chart(ctx, chartConfig) canvas.click(function(f) { var target = miniChart.getElementsAtEvent(f)[0]; if(!target){return false} var event = video.detections[target._index] var video1 = videoContainer.find('video')[0] video1.currentTime = $.ccio.timeObject(event.time).diff($.ccio.timeObject(video.time),'seconds') video1.play() }) } var getAllChartDataForLoadedVideos = function(){ var chartData = [] Object.keys(powerVideoLoadedVideos).forEach(function(monitorId,n){ var videos = powerVideoLoadedVideos[monitorId] var events = powerVideoLoadedEvents[monitorId] var parsedVideos = prepareVideosAndEventsForTable(monitorId,videos,events) powerVideoLoadedChartData[monitorId] = parsedVideos chartData = chartData.concat(parsedVideos) }) return chartData } var visuallySelectItemInRow = function(video){ powerVideoTimelineStripsContainer.find(`[timeline-video-file="${video.mid}${video.time}"]`).parents('.vis-item').addClass('vis-selected') } var visuallyDeselectItemInRow = function(video){ powerVideoTimelineStripsContainer.find(`[timeline-video-file="${video.mid}${video.time}"]`).parents('.vis-item').removeClass('vis-selected') } var drawTableTimeout = null var drawLoadedTableData = function(){ // destroy old try{ if(activeTimeline && activeTimeline.destroy){ activeTimeline.destroy() } }catch(err){ } // powerVideoTimelineStripsContainer.html(`
${lang['Please Wait...']}
`) clearTimeout(drawTableTimeout) drawTableTimeout = setTimeout(function(){ var container = powerVideoTimelineStripsContainer[0] var groupsDataSet = new vis.DataSet() var groups = [] var groupId = 1 Object.keys(powerVideoLoadedVideos).forEach(function(monitorId,n){ groups.push({ id: groupId, content: monitorId }) groupId += 1 groups.push({ id: groupId, content: lang.Events }) groupId += 1 loadedTableGroupIds[monitorId] = groupId - 2 loadedTableGroupIds[monitorId + '_events'] = groupId - 1 }) groupsDataSet.add(groups) var chartData = getAllChartDataForLoadedVideos() if(chartData.length > 0){ var items = new vis.DataSet(chartData) var options = { selectable: false, stack: false, showCurrentTime: false, } // Create a Timeline var timeline = new vis.Timeline(container, items, groupsDataSet, options) powerVideoTimelineStripsContainer.find('.loading').remove() var timeChanging = false timeline.on('rangechange', function(properties){ timeChanging = true }) timeline.on('rangechanged', function(properties){ setTimeout(function(){ timeChanging = false },300) }) timeline.on('click', function(properties){ if(!timeChanging){ var selectedTime = properties.time var videosAtSameTime = findAllVideosAtTime(selectedTime) powerVideoTimelineStripsContainer.find('.vis-item').removeClass('vis-selected') $.each(videosAtSameTime,function(monitorId,videos){ var selectedVideo = videos[0] if(selectedVideo){ loadVideoIntoMonitorSlot(selectedVideo,selectedTime) visuallySelectItemInRow(selectedVideo) } }) } }) activeTimeline = timeline }else{ powerVideoTimelineStripsContainer.html(`
${lang['No Data']}
`) } },1000) } var drawMatrices = function(event,options){ var streamObjectsContainer = options.streamObjectsContainer var height = options.height var width = options.width var monitorId = options.mid var widthRatio = width / event.details.imgWidth var heightRatio = height / event.details.imgHeight streamObjectsContainer.find('.stream-detected-object[name="'+event.details.name+'"]').remove() var html = '' $.each(event.details.matrices,function(n,matrix){ html += `
` if(matrix.tag)html += `${matrix.tag}` html += '
' }) streamObjectsContainer.append(html) } var attachEventsToVideoActiveElement = function(video){ var monitorId = video.mid var videoPlayerContainer = powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid=${monitorId}]`) var videoElement = videoPlayerContainer.find(`video.videoNow`) var streamObjectsContainer = videoPlayerContainer.find(`.videoPlayer-stream-objects`) var detectionInfoContainerMotion = videoPlayerContainer.find(`.videoPlayer-detection-info-motion`) var detectionInfoContainerObject = videoPlayerContainer.find(`.videoPlayer-detection-info-object`) var detectionInfoContainerRaw = videoPlayerContainer.find(`.videoPlayer-detection-info-raw`) var motionMeterProgressBar = videoPlayerContainer.find(`.videoPlayer-motion-meter .progress-bar`) var motionMeterProgressBarTextBox = videoPlayerContainer.find(`.videoPlayer-motion-meter .progress-bar span`) var videoCurrentTimeProgressBar = powerVideoTimelineStripsContainer.find(`[timeline-video-file="${video.mid}${video.time}"] .progress-bar`)[0] var preloadedNext = false var reinitializeStreamObjectsContainer = function(){ height = videoElement.height() width = videoElement.width() } reinitializeStreamObjectsContainer() $(videoElement) .resize(reinitializeStreamObjectsContainer) // .off('loadeddata').on('loadeddata', function() { // reinitializeStreamObjectsContainer() // var allLoaded = true // getAllActiveVideosInSlots().each(function(n,videoElement){ // if(!videoElement.readyState === 4)allLoaded = false // }) // setTimeout(function(){ // if(allLoaded){ // playAllSlots() // } // },1500) // }) // .off("pause").on("pause",function(){ // console.log(monitorId,'pause') // }) // .off("play").on("play",function(){ // console.log(monitorId,'play') // }) .off("timeupdate").on("timeupdate",function(){ var event = eventsLabeledByTime[monitorId][video.time][parseInt(this.currentTime)] if(event){ if(event.details.matrices){ drawMatrices(event,{ streamObjectsContainer: streamObjectsContainer, monitorId: monitorId, height: height, width: width, }) detectionInfoContainerObject.html($.ccio.init('jsontoblock',event.details.matrices)) } if(event.details.confidence){ motionMeterProgressBar.css('width',event.details.confidence+'%') motionMeterProgressBarTextBox.text(event.details.confidence) var html = `
${lang['Region']} : ${event.details.name}
${lang['Confidence']} : ${event.details.confidence}
${lang['Plugin']} : ${event.details.plug}
` detectionInfoContainerMotion.html(html) // detectionInfoContainerRaw.html($.ccio.init('jsontoblock',{`${lang['Plug']}`:event.details.plug})) } } var currentTime = this.currentTime; var watchPoint = Math.floor((currentTime/this.duration) * 100) if(!preloadedNext && watchPoint >= 75){ preloadedNext = true var videoAfter = videoPlayerContainer.find(`video.videoAfter`)[0] videoAfter.setAttribute('preload',true) } if(videoCurrentTimeProgressBar)videoCurrentTimeProgressBar.style.width = `${watchPoint}%` extenders.onVideoPlayerTimeUpdateExtensions.forEach(function(extender){ extender(videoElement,watchPoint) }) }) var onEnded = function() { visuallyDeselectItemInRow(video) if(video.videoAfter){ visuallySelectItemInRow(video.videoAfter) loadVideoIntoMonitorSlot(video.videoAfter) } } videoElement[0].onended = onEnded videoElement[0].onerror = onEnded } var dettachEventsToVideoActiveElement = function(monitorId){ var videoElement = powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid=${monitorId}] video.videoNow`) $(videoElement) // .off('loadeddata') .off("pause") .off("play") .off("timeupdate") } var findAllVideosAtTime = function(selectedTime){ var time = new Date(selectedTime) var parsedVideos = {} $.each(powerVideoLoadedVideos,function(monitorId,videos){ var videosFilteredByTime = videos.filter(function(video){ return ( (new Date(video.time)) <= time && time < (new Date(video.end)) ) }); parsedVideos[monitorId] = videosFilteredByTime }) return parsedVideos } var resetVisualDetectionDataForMonitorSlot = function(monitorId){ var videoPlayerContainer = powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid=${monitorId}]`) var streamObjectsContainer = videoPlayerContainer.find(`.videoPlayer-stream-objects`) var detectionInfoContainerObject = videoPlayerContainer.find(`.videoPlayer-detection-info-object`) var detectionInfoContainerMotion = videoPlayerContainer.find(`.videoPlayer-detection-info-motion`) var motionMeterProgressBar = videoPlayerContainer.find(`.videoPlayer-motion-meter .progress-bar`) var motionMeterProgressBarTextBox = videoPlayerContainer.find(`.videoPlayer-motion-meter .progress-bar span`) detectionInfoContainerObject.empty() detectionInfoContainerMotion.empty() streamObjectsContainer.empty() motionMeterProgressBar.css('width','0') motionMeterProgressBarTextBox.text('0') } var loadVideoIntoMonitorSlot = function(video,selectedTime){ if(!video)return resetVisualDetectionDataForMonitorSlot(video.mid) currentlyPlayingVideos[video.mid] = video var timeToStartAt = selectedTime - new Date(video.time) var numberOfMonitors = Object.keys(powerVideoLoadedVideos).length // if(numberOfMonitors > 3)numberOfMonitors = 3 //start new row after 3 if(numberOfMonitors == 1)numberOfMonitors = 2 //make single monitor not look like a doofus if(timeToStartAt < 0)timeToStartAt = 0 var widthOfBlock = 100 / numberOfMonitors var videoContainer = powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid=${video.mid}] .videoPlayer-buffers`) if(videoContainer.length === 0){ if(!monitorSlotPlaySpeeds)monitorSlotPlaySpeeds[video.mid] = {} powerVideoMonitorViewsElement.append(`
`) videoContainer = powerVideoMonitorViewsElement.find(`.videoPlayer[data-mid=${video.mid}] .videoPlayer-buffers`) }else{ powerVideoMonitorViewsElement.find('.videoPlayer').css('width',`${widthOfBlock}%`) } var videoCurrentNow = videoContainer.find('.videoNow') var videoCurrentAfter = videoContainer.find('.videoAfter') // var videoCurrentBefore = videoContainer.find('.videoBefore') dettachEventsToVideoActiveElement(video.mid) videoContainer.find('video').each(function(n,v){ v.pause() }) var videoIsSame = (video.href == videoCurrentNow.attr('video')) var videoIsAfter = (video.href == videoCurrentAfter.attr('video')) // var videoIsBefore = (video.href == videoCurrentBefore.attr('video')) var drawVideoHTML = function(position){ var videoData var exisitingElement = videoContainer.find('.' + position) if(position){ videoData = video[position] }else{ position = 'videoNow' videoData = video } if(videoData){ videoContainer.append('') } } if( videoIsSame || videoIsAfter // || videoIsBefore ){ switch(true){ case videoIsSame: var videoNow = videoContainer.find('video.videoNow')[0] if(!videoNow.paused)videoNow.pause() videoNow.currentTime = timeToStartAt / 1000 if(videoNow.paused)videoNow.play() return break; case videoIsAfter: // videoCurrentBefore.remove() videoCurrentNow.remove() videoCurrentAfter.removeClass('videoAfter').addClass('videoNow') // videoCurrentNow.removeClass('videoNow').addClass('videoBefore') drawVideoHTML('videoAfter') break; // case videoIsBefore: // videoCurrentAfter.remove() // videoCurrentBefore.removeClass('videoBefore').addClass('videoNow') // videoCurrentNow.removeClass('videoNow').addClass('videoAfter') // drawVideoHTML('videoBefore') // break; } }else{ videoContainer.empty() drawVideoHTML()//videoNow // drawVideoHTML('videoBefore') drawVideoHTML('videoAfter') } var videoNow = videoContainer.find('video.videoNow')[0] attachEventsToVideoActiveElement(video) // videoNow.setAttribute('preload',true) videoNow.muted = true videoNow.playbackRate = monitorSlotPlaySpeeds[video.mid] || 1 videoNow.currentTime = timeToStartAt / 1000 videoNow.play() extenders.onVideoPlayerCreateExtensions.forEach(function(extender){ extender(videoElement,watchPoint) }) drawMiniEventsChart(video,getMiniEventsChartConfig(video)) } var getSelectedMonitors = function(){ return powerVideoMonitorsListElement.find('.active') } var getAllActiveVideosInSlots = function(){ return powerVideoMonitorViewsElement.find('video.videoNow') } var pauseAllSlots = function(){ getAllActiveVideosInSlots().each(function(n,video){ if(!video.paused)video.pause() }) } var toggleZoomAllSlots = function(){ powerVideoMonitorViewsElement.find(`.videoPlayer`).each(function(n,videoContainer){ var streamWindow = $(videoContainer) var monitorId = streamWindow.attr('data-mid') var enabled = streamWindow.attr('zoomEnabled') if(enabled === '1'){ streamWindow .attr('zoomEnabled','0') .off('mouseover') .off('mouseout') .off('mousemove') .off('touchmove') .find('.zoomGlass').remove() }else{ const magnifyStream = function(e){ var videoElement = streamWindow.find('video.videoNow') console.log(videoElement[0].currentTime) $.ccio.magnifyStream({ p: streamWindow, videoUrl: streamWindow.find('video.videoNow').find('source').attr('src'), setTime: videoElement[0].currentTime, monitor: $.ccio.mon[$user.ke + monitorId + $user.auth_token], targetForZoom: 'video.videoNow', magnifyOffsetElement: '.videoPlayer-buffers', zoomAmount: 1, auto: false, animate: false, pageX: e.pageX, pageY: e.pageY },$user) } streamWindow .attr('zoomEnabled','1') .on('mouseover', function(){ streamWindow.find(".zoomGlass").show() }) .on('mouseout', function(){ streamWindow.find(".zoomGlass").hide() }) .on('mousemove', magnifyStream) .on('touchmove', magnifyStream) } }) } var playAllSlots = function(){ getAllActiveVideosInSlots().each(function(n,video){ if(video.paused)video.play() }) } var setPlaySpeedOnAllSlots = function(playSpeed){ Object.keys(powerVideoLoadedVideos).forEach(function(monitorId){ monitorSlotPlaySpeeds[monitorId] = playSpeed }) getAllActiveVideosInSlots().each(function(n,video){ video.playbackRate = playSpeed }) } var nextVideoAllSlots = function(){ Object.keys(currentlyPlayingVideos).forEach(function(monitorId){ var video = currentlyPlayingVideos[monitorId] visuallyDeselectItemInRow(video) visuallySelectItemInRow(video.videoAfter) loadVideoIntoMonitorSlot(video.videoAfter,0) }) } var previousVideoAllSlots = function(){ Object.keys(currentlyPlayingVideos).forEach(function(monitorId){ var video = currentlyPlayingVideos[monitorId] visuallyDeselectItemInRow(video) visuallySelectItemInRow(video.videoBefore) loadVideoIntoMonitorSlot(video.videoBefore,0) }) } $user.ws.on('f',function (d){ switch(d.f){ case'videos&events': var videos = d.videos.videos var events = d.events loadVideosToTimeLineMemory(d.id,videos,events) drawLoadedTableData() break; } }) $('body') .on('dblclick','.videoPlayer',function(){ var el = $(this) $.ccio.init('fullscreen',this) }) .on('click','[data-monitor]',function(){ var el = $(this) var monitorId = el.attr('data-monitor') el.toggleClass('active') if(el.hasClass('active')){ requestTableData(monitorId) }else{ unloadTableData(monitorId) } }) .on('click','[powerVideo-control]',function(){ var el = $(this) var controlType = el.attr('powerVideo-control') switch(controlType){ case'toggleZoom': toggleZoomAllSlots() break; case'playAll': playAllSlots() break; case'pauseAll': pauseAllSlots() break; case'playSpeedAll': var playSpeed = el.attr('data-speed') setPlaySpeedOnAllSlots(playSpeed) break; case'previousVideoAll': playAllSlots() previousVideoAllSlots() break; case'nextVideoAll': playAllSlots() nextVideoAllSlots() break; } }) powerVideoWindow.on('shown.bs.modal',function(){ drawMonitorsList() }) $.powerVideoViewer = { window: powerVideoWindow, drawMonitorsList: drawMonitorsList, activeTimeline: activeTimeline, monitorListElement: powerVideoMonitorsListElement, monitorViewsElement: powerVideoMonitorViewsElement, timelineStripsElement: powerVideoTimelineStripsContainer, dateRangeElement: powerVideoDateRangeElement, loadedVideos: powerVideoLoadedVideos, loadedEvents: powerVideoLoadedEvents, loadedChartData: powerVideoLoadedChartData, loadedTableGroupIds: loadedTableGroupIds, extenders: extenders } })