diff --git a/.cirrus.yml b/.cirrus.yml index 9f374210e..c1495c372 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -2,8 +2,9 @@ task: name: freebsd-build freebsd_instance: matrix: - - image_family: freebsd-14-0 - - image_family: freebsd-13-3 + - image_family: freebsd-15-0 + - image_family: freebsd-14-2 + - image_family: freebsd-13-5 prepare_script: - pkg install -yq git cmake pkgconf jpeg-turbo mysql80-client ffmpeg libvncserver libjwt catch2 p5-DBI p5-DBD-mysql p5-Date-Manip p5-Test-LWP-UserAgent p5-Sys-Mmap v4l_compat diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 88bcbef7e..90fac1911 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -343,6 +343,8 @@ void Event::createNotes(std::string ¬es) { notes += ", "; notes += *setIter; } + if (mapIter != noteSetMap.begin()) + notes += ", "; } } // void Event::createNotes(std::string ¬es) diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 8b3c47238..9e2bf53c5 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -49,6 +49,7 @@ static CodecData dec_codecs[] = { #endif { AV_CODEC_ID_AV1, "av1", "libsvtav1", AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P, AV_HWDEVICE_TYPE_NONE, nullptr }, { AV_CODEC_ID_AV1, "av1", "libaom-av1", AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P, AV_HWDEVICE_TYPE_NONE, nullptr }, + { AV_CODEC_ID_AV1, "av1", "av1_vaapi", AV_PIX_FMT_YUV420P, AV_PIX_FMT_VAAPI, AV_HWDEVICE_TYPE_VAAPI, nullptr }, { AV_CODEC_ID_MJPEG, "mjpeg", "mjpeg", AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ422P, AV_HWDEVICE_TYPE_NONE, nullptr }, { AV_CODEC_ID_H264, "h264", "h264", AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P, AV_HWDEVICE_TYPE_NONE, nullptr }, { AV_CODEC_ID_H264, "h264", "h264_v4lm2m", AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P, AV_HWDEVICE_TYPE_NONE, nullptr }, @@ -80,6 +81,7 @@ static CodecData enc_codecs[] = { { AV_CODEC_ID_H264, "h264", "libx264", AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P, AV_HWDEVICE_TYPE_NONE, nullptr }, { AV_CODEC_ID_MJPEG, "mjpeg", "mjpeg", AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ420P, AV_HWDEVICE_TYPE_NONE, nullptr }, { AV_CODEC_ID_VP9, "vp9", "libvpx-vp9", AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P, AV_HWDEVICE_TYPE_NONE, nullptr }, + { AV_CODEC_ID_AV1, "av1", "av1_vaapi", AV_PIX_FMT_YUV420P, AV_PIX_FMT_VAAPI, AV_HWDEVICE_TYPE_VAAPI, nullptr }, { AV_CODEC_ID_AV1, "av1", "libsvtav1", AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P, AV_HWDEVICE_TYPE_NONE, nullptr }, { AV_CODEC_ID_AV1, "av1", "libaom-av1", AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P, AV_HWDEVICE_TYPE_NONE, nullptr }, }; diff --git a/web/CMakeLists.txt b/web/CMakeLists.txt index 9676f1cfa..126a67022 100644 --- a/web/CMakeLists.txt +++ b/web/CMakeLists.txt @@ -6,7 +6,7 @@ add_subdirectory(api) configure_file(includes/config.php.in "${CMAKE_CURRENT_BINARY_DIR}/includes/config.php" @ONLY) # Install the web files -install(DIRECTORY vendor api ajax css fonts graphics includes js lang skins views DESTINATION "${ZM_WEBDIR}" PATTERN "*.in" EXCLUDE PATTERN "*Make*" EXCLUDE PATTERN "*cmake*" EXCLUDE) +install(DIRECTORY vendor api ajax css fonts graphics includes js lang skins sounds views DESTINATION "${ZM_WEBDIR}" PATTERN "*.in" EXCLUDE PATTERN "*Make*" EXCLUDE PATTERN "*cmake*" EXCLUDE) install(FILES index.php robots.txt DESTINATION "${ZM_WEBDIR}") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/includes/config.php" DESTINATION "${ZM_WEBDIR}/includes") diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index dbdd93d28..aec047cb4 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -197,15 +197,15 @@ class Monitor extends ZM_Object { protected static $table = 'Monitors'; - protected static $RTSP2WebStream = null; - public static function getRTSP2WebStream() { - if (!isset($RTSP2WebStream)) { - $RTSP2WebStream = array( + protected static $RTSP2WebStreamOptions = null; + public static function getRTSP2WebStreamOptions() { + if (!isset($RTSP2WebStreamOptions)) { + $RTSP2WebStreamOptions = array( 'Primary' => translate('Primary'), 'Secondary' => translate('Secondary'), ); } - return $RTSP2WebStream; + return $RTSP2WebStreamOptions; } protected $defaults = array( diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 2fdbdfbc6..ffd7b6fb0 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -1253,8 +1253,28 @@ var doubleClickOnStream = function(event, touchEvent) { if (target) { if (document.fullscreenElement) { + if (getCookie('zmEventStats') && typeof eventStats !== "undefined") { + //Event page + eventStats.toggle(true); + wrapperEventVideo.removeClass('col-sm-12').addClass('col-sm-8'); + changeScale(); + } else if (getCookie('zmCycleShow') && typeof sidebarView !== "undefined") { + //Watch page + sidebarView.toggle(true); + monitorsSetScale(monitorId); + } closeFullscreen(); } else { + if (getCookie('zmEventStats') && typeof eventStats !== "undefined") { + //Event page + eventStats.toggle(false); + wrapperEventVideo.removeClass('col-sm-8').addClass('col-sm-12'); + changeScale(); + } else if (getCookie('zmCycleShow') && typeof sidebarView !== "undefined") { + //Watch page + sidebarView.toggle(false); + monitorsSetScale(monitorId); + } openFullscreen(target); } if (isMobile()) { @@ -1357,3 +1377,23 @@ $j(document).on('keyup.global keydown.global', function(e) { }); loadFontFaceObserver(); + +function canPlayCodec(filename) { + const re = /\.(\w+)\.(\w+)$/i; + const matches = re.exec(filename); + if (matches.length) { + const video = document.createElement('video'); + if (matches[1] == 'av1') matches[1] = 'avc1'; + const can = video.canPlayType('video/mp4; codecs="'+matches[1]+'"'); + if (can == "probably") { + console.log("can play "+matches[1]); + return true; + } else if (can == "maybe") { + console.log("can maybe play "+matches[1]); + return true; + } + console.log("cannot play "+matches[1]); + return false; + } + return false; +} diff --git a/web/skins/classic/js/skin.js.php b/web/skins/classic/js/skin.js.php index 838def609..43150af62 100644 --- a/web/skins/classic/js/skin.js.php +++ b/web/skins/classic/js/skin.js.php @@ -153,6 +153,7 @@ if ($user) { if (isset($c['Type']) and $c['Type'] == 'integer' and $c['Value'] != '') { echo 'const '. $name . ' = '.$value.';'.PHP_EOL; } else { + $value = html_entity_decode(validJsStr($value)); echo 'const '. $name . ' = \''.$value.'\';'.PHP_EOL; } } diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 2dddf12cf..1d0fd0c67 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -1346,6 +1346,7 @@ function initPage() { getAvailableTags(); getSelectedTags(); + // Load the event stats getStat(); zmPanZoom.init(); @@ -1359,7 +1360,10 @@ function initPage() { } //FIXME prevent blocking...not sure what is happening or best way to unblock - if (document.getElementById('videoobj')) { + const video_element = document.getElementById('videoobj'); + if (video_element) { + canPlayCodec(eventData.DefaultVideo); + vid = videojs('videoobj'); addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartDateTime); //$j('.vjs-progress-control').append('
');//add a place for videojs only on first load diff --git a/web/skins/classic/views/js/event.js.php b/web/skins/classic/views/js/event.js.php index 443cbb692..785b4b7a1 100644 --- a/web/skins/classic/views/js/event.js.php +++ b/web/skins/classic/views/js/event.js.php @@ -41,6 +41,7 @@ var eventData = { MaxScore: 'MaxScore() ?>', DiskSpace: 'DiskSpace(null)) ?>', Storage: 'Storage()->Name()).( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' ) ?>', + DefaultVideo: 'DefaultVideo()) ?>', Archived: Archived?'true':'false' ?>, Emailed: Emailed?'true':'false' ?>, Path: 'Path() ?>', diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index f36aa506f..7fdcc92b7 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -203,6 +203,7 @@ function selectLayout(new_layout_id) { changeMonitorStatusPosition(); //!!! After loading the saved layer, you must execute. monitorsSetScale(); */ + on_scroll(); setCookie('zmMontageLayout', layout_id); } // end function selectLayout(element) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 7e064321f..a9342d03d 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -866,6 +866,7 @@ $decoders = array( 'av1' => 'av1', 'av1_qsv' => 'av1_qsv', 'av1_cuvid' => 'av1_cuvid', + 'av1_vaapi' => 'av1_vaapi' #'av1_ni_quadra_dec' => 'av1_ni_quadra', ); echo htmlSelect('newMonitor[Decoder]', $decoders, $monitor->Decoder()); @@ -1189,7 +1190,8 @@ $videowriter_encoders = array( 'libsvtav1' => 'libsvtav1', 'libaom-av1' => 'libaom-av1', 'av1_qsv' => 'av1_qsv', - 'av1_ni_quadra_enc' => 'av1_ni_quadra' + 'av1_ni_quadra_enc' => 'av1_ni_quadra', + 'av1_vaapi' => 'av1_vaapi' ); echo htmlSelect('newMonitor[Encoder]', $videowriter_encoders, $monitor->Encoder()); ?> @@ -1271,7 +1273,7 @@ echo htmlSelect('newMonitor[OutputContainer]', $videowriter_containers, $monitor
  • - RTSP2WebStream()); ?> + RTSP2WebStream()); ?>
  • diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index a09e560ec..0b6b3dea1 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -309,7 +309,7 @@ echo htmlSelect('changeRate', $maxfps_options, $options['maxfps']); RTSP2WebStream(), array('data-on-change'=>'monitorChangeStreamChannel','id'=>'streamChannel')); + echo htmlSelect('streamChannel', ZM\Monitor::getRTSP2WebStreamOptions(), $monitor->RTSP2WebStream(), array('data-on-change'=>'monitorChangeStreamChannel','id'=>'streamChannel')); echo htmlSelect('streamQuality', $streamQuality, $streamQualitySelected, array('data-on-change'=>'changeStreamQuality','id'=>'streamQuality')); ?>