Save worktree changes
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
function renderRtmpRegion(region, regionContent) {
|
||||
var url = String(regionContent.value || '').trim();
|
||||
var disableAudio = regionContent.disable_audio === undefined ? true : Boolean(regionContent.disable_audio);
|
||||
if (!url) {
|
||||
return '<div class="template-region rtmp" style="' + region.baseStyle + '"><div class="template-region-placeholder">RTMP stream</div></div>';
|
||||
}
|
||||
|
||||
return '<div class="template-region rtmp" style="' + region.baseStyle + '"><video class="template-region-rtmp-video" data-rtmp-source="' + escapeHtml(url) + '" data-rtmp-disable-audio="' + (disableAudio ? '1' : '0') + '" autoplay playsinline preload="auto" tabindex="-1" disablepictureinpicture></video><div class="template-region-placeholder template-region-rtmp-placeholder">Loading RTMP stream...</div></div>';
|
||||
}
|
||||
|
||||
function getRtmpSessionUrl(sourceUrl, disableAudio) {
|
||||
return '/api/rtmp/session?source=' + encodeURIComponent(sourceUrl) + '&disableAudio=' + (disableAudio ? '1' : '0');
|
||||
}
|
||||
|
||||
var rtmpPreloadSignature = '';
|
||||
|
||||
function getRtmpPreloadSlides(sourceSlides, targetIndex) {
|
||||
var availableSlides = Array.isArray(sourceSlides) ? sourceSlides : [];
|
||||
if (!availableSlides.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var normalizedIndex = Number(targetIndex || 0);
|
||||
if (!Number.isFinite(normalizedIndex) || normalizedIndex < 0 || normalizedIndex >= availableSlides.length) {
|
||||
normalizedIndex = 0;
|
||||
}
|
||||
|
||||
var preloadSlides = [];
|
||||
var currentSlide = availableSlides[normalizedIndex];
|
||||
var nextSlide = availableSlides[normalizedIndex + 1];
|
||||
|
||||
if (currentSlide) {
|
||||
preloadSlides.push(currentSlide);
|
||||
}
|
||||
if (nextSlide && nextSlide !== currentSlide) {
|
||||
preloadSlides.push(nextSlide);
|
||||
}
|
||||
|
||||
return preloadSlides;
|
||||
}
|
||||
|
||||
function getRtmpPreloadEntries(sourceSlides) {
|
||||
var entries = [];
|
||||
var seen = Object.create(null);
|
||||
|
||||
(Array.isArray(sourceSlides) ? sourceSlides : []).forEach(function (slide) {
|
||||
var content = slide && slide.content ? slide.content : {};
|
||||
var regions = slide && slide.template && Array.isArray(slide.template.regions) ? slide.template.regions : [];
|
||||
regions.forEach(function (region) {
|
||||
if (region.region_type !== 'rtmp') {
|
||||
return;
|
||||
}
|
||||
var regionContent = content[region.region_key] || {};
|
||||
var url = String(regionContent.value || '').trim();
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
var disableAudio = regionContent.disable_audio === undefined ? true : Boolean(regionContent.disable_audio);
|
||||
var key = url + '\n' + (disableAudio ? '1' : '0');
|
||||
if (seen[key]) {
|
||||
return;
|
||||
}
|
||||
seen[key] = true;
|
||||
entries.push({
|
||||
url: url,
|
||||
disableAudio: disableAudio
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
function syncRtmpPreloads(sourceSlides, targetIndex) {
|
||||
var entries = getRtmpPreloadEntries(getRtmpPreloadSlides(sourceSlides, targetIndex));
|
||||
var signature = entries.map(function (entry) {
|
||||
return entry.url + '\n' + (entry.disableAudio ? '1' : '0');
|
||||
}).join('\n');
|
||||
|
||||
if (signature === rtmpPreloadSignature) {
|
||||
return;
|
||||
}
|
||||
|
||||
rtmpPreloadSignature = signature;
|
||||
if (!entries.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
entries.forEach(function (entry) {
|
||||
fetch(getRtmpSessionUrl(entry.url, entry.disableAudio), {
|
||||
credentials: 'same-origin'
|
||||
}).catch(function () {
|
||||
return null;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function syncRtmpRegions(root) {
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
var videos = root.querySelectorAll('video[data-rtmp-source]');
|
||||
Array.prototype.forEach.call(videos, function (video) {
|
||||
if (!video || video.dataset.rtmpInitialized === '1') {
|
||||
return;
|
||||
}
|
||||
|
||||
var sourceUrl = String(video.dataset.rtmpSource || '').trim();
|
||||
var disableAudio = String(video.dataset.rtmpDisableAudio || '1') !== '0';
|
||||
var region = video.parentNode;
|
||||
var placeholder = region ? region.querySelector('.template-region-rtmp-placeholder') : null;
|
||||
|
||||
if (!sourceUrl) {
|
||||
if (placeholder) {
|
||||
placeholder.textContent = 'RTMP stream';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
video.dataset.rtmpInitialized = '1';
|
||||
video.muted = disableAudio;
|
||||
video.controls = false;
|
||||
video.playsInline = true;
|
||||
video.autoplay = true;
|
||||
|
||||
var markReady = function () {
|
||||
if (placeholder) {
|
||||
placeholder.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
var tryPlay = function () {
|
||||
if (video && typeof video.play === 'function') {
|
||||
video.play().catch(function () {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
video.addEventListener('canplay', markReady, { once: true });
|
||||
video.addEventListener('playing', markReady, { once: true });
|
||||
|
||||
fetch(getRtmpSessionUrl(sourceUrl, disableAudio), {
|
||||
credentials: 'same-origin'
|
||||
}).then(function (response) {
|
||||
if (!response.ok) {
|
||||
throw new Error('Unable to initialize RTMP stream.');
|
||||
}
|
||||
return response.json();
|
||||
}).then(function (payload) {
|
||||
var playlistUrl = payload && payload.playlistUrl ? String(payload.playlistUrl).trim() : '';
|
||||
if (!playlistUrl) {
|
||||
throw new Error('RTMP playlist URL was not returned.');
|
||||
}
|
||||
|
||||
if (window.Hls && window.Hls.isSupported && window.Hls.isSupported()) {
|
||||
var hls = new window.Hls({
|
||||
enableWorker: true,
|
||||
lowLatencyMode: true,
|
||||
liveSyncDurationCount: 4,
|
||||
liveMaxLatencyDurationCount: 8,
|
||||
maxBufferLength: 20,
|
||||
maxLiveSyncPlaybackRate: 1,
|
||||
backBufferLength: 30
|
||||
});
|
||||
video.__rtmpHls = hls;
|
||||
hls.attachMedia(video);
|
||||
hls.on(window.Hls.Events.MEDIA_ATTACHED, function () {
|
||||
hls.loadSource(playlistUrl);
|
||||
});
|
||||
hls.on(window.Hls.Events.MANIFEST_PARSED, function () {
|
||||
tryPlay();
|
||||
});
|
||||
hls.on(window.Hls.Events.ERROR, function (_event, data) {
|
||||
if (data && data.fatal) {
|
||||
try {
|
||||
hls.destroy();
|
||||
} catch (_error) {
|
||||
// ignore cleanup errors
|
||||
}
|
||||
video.__rtmpHls = null;
|
||||
if (placeholder) {
|
||||
placeholder.style.display = 'flex';
|
||||
placeholder.textContent = 'RTMP playback failed.';
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (video.canPlayType && video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
video.src = playlistUrl;
|
||||
tryPlay();
|
||||
return;
|
||||
}
|
||||
|
||||
if (placeholder) {
|
||||
placeholder.style.display = 'flex';
|
||||
placeholder.textContent = 'RTMP playback is not supported in this browser.';
|
||||
}
|
||||
}).catch(function (_error) {
|
||||
if (placeholder) {
|
||||
placeholder.style.display = 'flex';
|
||||
placeholder.textContent = 'Unable to load RTMP stream.';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function destroyRtmpRegions(root) {
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
var videos = root.querySelectorAll('video[data-rtmp-source]');
|
||||
Array.prototype.forEach.call(videos, function (video) {
|
||||
if (video.__rtmpHls) {
|
||||
try {
|
||||
video.__rtmpHls.destroy();
|
||||
} catch (_error) {
|
||||
// ignore cleanup errors
|
||||
}
|
||||
video.__rtmpHls = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user