Merge branch 'master' into patch-826994

This commit is contained in:
IgorA100
2026-04-30 16:49:38 +03:00
committed by GitHub
5 changed files with 95 additions and 72 deletions

View File

@@ -86,6 +86,12 @@ module.exports = defineConfig([{
"eol-last": "off",
"indent": "off",
},
}, {
"files": ["web/skins/classic/js/audioMotionAnalyzer.js"],
"languageOptions": {
sourceType: "module",
},
}, globalIgnores([
"**/*.min.js",
"web/api/lib",

View File

@@ -2464,7 +2464,9 @@ bool Monitor::Analyse() {
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
if (packet->codec_type == AVMEDIA_TYPE_VIDEO) {
packetqueue.clearPackets(packet);
if (packetqueue.should_try_clear(packet->keyframe)) {
packetqueue.clearPackets(packet);
}
// Only do these if it's a video packet.
shared_data->last_read_index = packet->image_index;
analysis_image_count++;

View File

@@ -259,7 +259,8 @@ bool PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
)
) {
Debug(3, "stream index %d ?= video_stream_id %d, keyframe %d, keep_keyframes %d, pending %d, counts %d > pre_event_count %d at begin %d",
add_packet->packet->stream_index, video_stream_id, add_packet->keyframe, keep_keyframes, clear_packets_pending_,
add_packet->packet->stream_index, video_stream_id, add_packet->keyframe, keep_keyframes,
clear_packets_pending_.load(std::memory_order_relaxed),
packet_counts[video_stream_id], pre_event_video_packet_count,
( *(pktQueue.begin()) != add_packet )
);
@@ -348,8 +349,12 @@ bool PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
while (*it != add_packet) {
zm_packet = *it;
if (zm_packet->queue_index >= min_iterator_queue_index) {
Debug(3, "Found iterator Counted %d video packets. Which would leave %d in packetqueue tail count is %d",
// Use > (not >=) so we still consider the iterator's own packet as a
// next_front candidate. Setting next_front to a packet that an iterator
// points at is safe: we delete strictly before next_front, so the
// iterator's packet stays in the queue.
if (zm_packet->queue_index > min_iterator_queue_index) {
Debug(3, "Past iterator. Counted %d video packets. Which would leave %d in packetqueue tail count is %d",
video_packets_to_delete, packet_counts[video_stream_id]-video_packets_to_delete, tail_count);
break;
}

View File

@@ -21,6 +21,7 @@
#include "zm_time.h"
#include <atomic>
#include <condition_variable>
#include <list>
#include <memory>
@@ -54,7 +55,7 @@ class PacketQueue {
bool has_out_of_order_packets_;
int max_keyframe_interval_;
int frames_since_last_keyframe_;
bool clear_packets_pending_;
std::atomic<bool> clear_packets_pending_;
uint64_t next_queue_index_;
Monitor *monitor_;
@@ -83,6 +84,17 @@ class PacketQueue {
int get_max_keyframe_interval() const { return max_keyframe_interval_; };
bool clearPackets(const std::shared_ptr<ZMPacket> &packet);
// Lock-free gate for callers: returns false when a clearPackets() call would
// certainly early-return. When keep_keyframes is on we only have work to do
// on a keyframe (start of a droppable GOP) or when a previous attempt was
// deferred via clear_packets_pending_. A stale read of pending is harmless;
// worst case we make one extra (cheap) early-returning call.
bool should_try_clear(bool is_keyframe) const {
if (!keep_keyframes) return true;
return is_keyframe || clear_packets_pending_.load(std::memory_order_relaxed);
}
int packet_count(int stream_id);
bool increment_it(packetqueue_iterator *it, bool wait);

View File

@@ -10,7 +10,7 @@ function checkAudioMotionEnabled() {
}
if (checkAudioMotionEnabled()) {
import('../assets/audioMotion-analyzer/src/audioMotion-analyzer.js').then(module => {
import('../assets/audioMotion-analyzer/src/audioMotion-analyzer.js').then((module) => {
if (module.AudioMotionAnalyzer) {
AudioMotionAnalyzer = module.AudioMotionAnalyzer;
} else {
@@ -35,12 +35,12 @@ export class _AudioMotionAnalyzer extends HTMLElement {
}
this.mid = stringToNumber(this.id);
this.gainNode = null; // This is required for controlling the signal level, as we're using a separate stream. This is because when using WebRTC, we can't get the audio stream from <video>.
[this.infoIsAudio, this.infoIsVideo] = [1,2].map(() => document.createElement('i'));
this.infoIsAudio.setAttribute('id',"ama_is-audio" + this.mid);
this.infoIsAudio.setAttribute('class','material-icons md-18');
[this.infoIsAudio, this.infoIsVideo] = [1, 2].map(() => document.createElement('i'));
this.infoIsAudio.setAttribute('id', "ama_is-audio" + this.mid);
this.infoIsAudio.setAttribute('class', 'material-icons md-18');
this.infoIsAudio.innerText = 'music_off';
this.infoIsVideo.setAttribute('id',"ama_is-video" + this.mid);
this.infoIsVideo.setAttribute('class','material-icons md-18');
this.infoIsVideo.setAttribute('id', "ama_is-video" + this.mid);
this.infoIsVideo.setAttribute('class', 'material-icons md-18');
this.infoIsVideo.innerText = 'videocam_off';
this.handlerEventListener = {};
this.currentPlayer = null; // The current player during initialization
@@ -49,12 +49,12 @@ export class _AudioMotionAnalyzer extends HTMLElement {
this.hide();
}
connectedCallback() {
//console.log('connectedCallback');
}
connectedCallback() {
//console.log('connectedCallback');
}
disconnectedCallback() {
//console.log('disconnectedCallback');
disconnectedCallback() {
//console.log('disconnectedCallback');
}
hide = function() {
@@ -133,7 +133,7 @@ export class _AudioMotionAnalyzer extends HTMLElement {
this.changeIconIsVideo('off');
manageEventListener.removeEventListener(this.handlerEventListener['volumechange']);
}
}; // END stop = function() {
}; // END stop = function() {
pause = function() {
if (this.audioMotion && this.audioMotion.isOn) {
@@ -155,71 +155,69 @@ export class _AudioMotionAnalyzer extends HTMLElement {
createMotionAnalyzer = function() {
const mid = this.mid;
const audioEl = this.getMediaStreamSource();
const volumeControls = document.getElementById(`volumeControls${mid}`);
const monitorStream = getMonitorStream(mid)
const monitorStream = getMonitorStream(mid);
if (!monitorStream) {
console.warn(`Audio visualization. Stream for monitor ID=${mid} not found.`);
return;
}
this.audioMotion = new AudioMotionAnalyzer(
document.getElementById(`audioVisualization${mid}`),
{
document.getElementById(`audioVisualization${mid}`),
{
//source: audioEl, // main audio source is the HTML <audio> element .webrtc - не работает пока.
//width: 100%,
canvas: document.querySelector(`#audioVisualization${mid} canvas`),
height: 80,
mode: 2, // This has little impact on performance. The lower the number, the larger the number of bars.
maxFPS: this.maxFPS,
loRes: this.loRes, //https://github.com/hvianna/audioMotion-analyzer?tab=readme-ov-file#lores-boolean
fftSize: 4096, // It has almost no impact on performance. The lower this number, the worse the frequency analysis; at 32, almost all the bars are identical... Optimally, 1024 or more
alphaBars: true,
noteLabels: false, // It's not really necessary.
showScaleX: false, // Removes frequency signatures.
showScaleY: false,
overlay: true, // Makes the background transparent.
bgAlpha: .5, // Background transparency only works with overlay: true.
ansiBands: true,
barSpace: .5,
//channelLayout: 'single',
channelLayout: 'dual-combined',
colorMode: 'gradient',
frequencyScale: 'log',
gradient: 'classic',
//ledBars: true,
//connectSpeakers: false, // Defaults to TRUE
lumiBars: false,
maxFreq: 5000,
minFreq: 125,
//maxDecibels: -15, // Def = -25
//minDecibels: -75, // Def = -85
mirror: 0,
radial: false,
//reflexFit: true,
//reflexRatio: .1,
//reflexAlpha: .25,
//reflexBright: 1,
//linearAmplitude: true,
linearAmplitude: false,
//linearBoost: 4, // 4 is the optimal mid-range value, approximately the same as with "linearAmplitude" disabled. Only works when linearAmplitude: true
showBgColor: true,
showPeaks: true,
trueLeds: true
}
canvas: document.querySelector(`#audioVisualization${mid} canvas`),
height: 80,
mode: 2, // This has little impact on performance. The lower the number, the larger the number of bars.
maxFPS: this.maxFPS,
loRes: this.loRes, //https://github.com/hvianna/audioMotion-analyzer?tab=readme-ov-file#lores-boolean
fftSize: 4096, // It has almost no impact on performance. The lower this number, the worse the frequency analysis; at 32, almost all the bars are identical... Optimally, 1024 or more
alphaBars: true,
noteLabels: false, // It's not really necessary.
showScaleX: false, // Removes frequency signatures.
showScaleY: false,
overlay: true, // Makes the background transparent.
bgAlpha: .5, // Background transparency only works with overlay: true.
ansiBands: true,
barSpace: .5,
//channelLayout: 'single',
channelLayout: 'dual-combined',
colorMode: 'gradient',
frequencyScale: 'log',
gradient: 'classic',
//ledBars: true,
//connectSpeakers: false, // Defaults to TRUE
lumiBars: false,
maxFreq: 5000,
minFreq: 125,
//maxDecibels: -15, // Def = -25
//minDecibels: -75, // Def = -85
mirror: 0,
radial: false,
//reflexFit: true,
//reflexRatio: .1,
//reflexAlpha: .25,
//reflexBright: 1,
//linearAmplitude: true,
linearAmplitude: false,
//linearBoost: 4, // 4 is the optimal mid-range value, approximately the same as with "linearAmplitude" disabled. Only works when linearAmplitude: true
showBgColor: true,
showPeaks: true,
trueLeds: true
}
);
monitorStream.audioMotion = this;
this.audioMotion.registerGradient( 'myGradient', {
bgColor: '#34495e', // background color (optional) - defaults to '#111'
dir: 'w', // add this property to create a horizontal gradient (optional)
colorStops: [ // list your gradient colors in this array (at least one color is required)
'hsl( 0, 100%, 50% )', // colors can be defined in any valid CSS format
{ color: 'yellow', pos: .6 }, // in an object, use `pos` to adjust the offset (0 to 1) of a colorStop
{ color: '#0f0', level: .5 } // use `level` to set the max bar amplitude (0 to 1) to use this color
dir: 'w', // add this property to create a horizontal gradient (optional)
colorStops: [ // list your gradient colors in this array (at least one color is required)
'hsl( 0, 100%, 50% )', // colors can be defined in any valid CSS format
{color: 'yellow', pos: .6}, // in an object, use `pos` to adjust the offset (0 to 1) of a colorStop
{color: '#0f0', level: .5} // use `level` to set the max bar amplitude (0 to 1) to use this color
]
});
this.audioMotion.setOptions({gradient:"myGradient"});
this.audioMotion.setOptions({gradient: "myGradient"});
if (monitorStream.audioTrack) {
this.connectToMediaStreamSource();
@@ -237,7 +235,7 @@ export class _AudioMotionAnalyzer extends HTMLElement {
}
}; // END destroy = function()
getInfoBlock = function() {
getInfoBlock = function() {
let info = document.querySelector('[id ^= "monitorStatus'+this.mid+'"] .stream-info-status-track'); // Watch&Montage page
if (!info) info = document.querySelector('[id ^= "wrapperEventVideo"] .stream-info-status-track'); // Event page
return info;
@@ -264,7 +262,7 @@ export class _AudioMotionAnalyzer extends HTMLElement {
}
};
connectToMediaStreamSource = async function () {
connectToMediaStreamSource = async function() {
if (!this.audioMotion) return;
const audioEl = this.getMediaStreamSource();
if (!audioEl) {
@@ -291,7 +289,7 @@ export class _AudioMotionAnalyzer extends HTMLElement {
await this.getTracksFromStream(monitorStream);
return;
}
const source = audioCtx.createMediaStreamSource(mediaStream);
const source = audioCtx.createMediaStreamSource(mediaStream);
source.connect(this.gainNode);
this.audioMotion.connectInput(this.gainNode);
//this.audioMotion.connectOutput(); // This will result in duplicate sound output.
@@ -334,9 +332,9 @@ export class _AudioMotionAnalyzer extends HTMLElement {
}
}
return ready;
}
};
listenerVolumechange = function(_this, event){ // Adjust the visualization level according to the stream's volume level
listenerVolumechange = function(_this, event) { // Adjust the visualization level according to the stream's volume level
if (event.target.muted === true) {
_this.gainNode.gain.value = 0;
} else {