Merge pull request #1146 from dfpc-coe/bufferError

Handle Buffer Error
This commit is contained in:
Nick
2025-12-16 10:33:40 -07:00
committed by GitHub
4 changed files with 67 additions and 51 deletions

View File

@@ -389,16 +389,11 @@ async function createPlayer(): Promise<void> {
try {
const url = new URL(videoProtocols.value!.hls!.url);
// Configure HLS.js with settings optimized for MediaMTX live streaming
player.value = new Hls({
enableWorker: true,
lowLatencyMode: false, // More forgiving for stream restarts
debug: false,
backBufferLength: 90, // Keep more buffer for smoother playback
maxBufferLength: 30, // Larger buffer for resilience
maxMaxBufferLength: 600,
liveSyncDurationCount: 3, // More tolerant of discontinuities
liveMaxLatencyDurationCount: 10,
lowLatencyMode: true,
debug: localStorage.getItem('debug') === 'true',
backBufferLength: 90,
xhrSetup: (xhr: XMLHttpRequest) => {
// Add authentication if stream requires it
if (url.username && url.password) {
@@ -407,10 +402,8 @@ async function createPlayer(): Promise<void> {
}
});
// Attach HLS player to video element
player.value.attachMedia(videoTag.value!);
// Load HLS source when media is attached
player.value.on(Hls.Events.MEDIA_ATTACHED, async () => {
if (player.value) player.value.loadSource(url.toString());
});
@@ -431,7 +424,7 @@ async function createPlayer(): Promise<void> {
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
if (!data.fatal) {
handleStreamRestart(); // Handle muxer restart scenario
player.value!.recoverMediaError();
break;
} else {
console.log("Fatal network error:", data);
@@ -440,7 +433,7 @@ async function createPlayer(): Promise<void> {
}
case Hls.ErrorTypes.MEDIA_ERROR:
if (!data.fatal) {
handleStreamRestart(); // Handle muxer restart scenario
player.value!.recoverMediaError();
break;
} else {
console.log("Fatal media error:", data);
@@ -472,24 +465,6 @@ async function createPlayer(): Promise<void> {
}
}
/**
* Handle MediaMTX muxer restarts gracefully
* This occurs when MediaMTX creates new segment naming due to source hiccups
*/
function handleStreamRestart(): void {
console.log('Handling HLS stream restart (muxer restart detected)');
if (player.value && videoProtocols.value?.hls) {
try {
player.value.stopLoad();
player.value.startLoad();
} catch (err) {
console.error('Error handling stream restart:', err);
// Fall back to full retry if restart handling fails
handleStreamError(err instanceof Error ? err : new Error(String(err)));
}
}
}
/**
* Handle stream errors with exponential backoff retry logic
* Implements 3-attempt retry system with increasing delays: 1s, 2s, 4s

View File

@@ -1,31 +1,56 @@
<template>
<div
v-if='url_links.length || responder_links.length'
class='col-12 py-2'
class='col-12'
>
<div
v-if='url_links.length'
class='col-12 mb-3'
class='col-12'
>
<div class='col-12 mb-2'>
<div
class='d-flex align-items-center cursor-pointer user-select-none py-2 px-2 rounded transition-all mx-2'
:class='{ "bg-accent": expandedLinks, "hover": !expandedLinks }'
@click='expandedLinks = !expandedLinks'
>
<IconLink
:size='18'
stroke='1'
color='#6b7990'
class='ms-2 me-1'
/>
<label class='subheader user-select-none'>Links</label>
<label class='subheader cursor-pointer m-0'>Links</label>
<div class='ms-auto d-flex align-items-center'>
<span class='badge bg-blue-lt me-2'>{{ url_links.length }}</span>
<IconChevronDown
class='transition-transform'
:class='{ "rotate-180": !expandedLinks }'
:size='18'
/>
</div>
</div>
<div class='list-group list-group-flush bg-accent rounded mx-2'>
<a
v-for='(link, link_it) of url_links'
:key='link_it'
:href='link.url'
target='_blank'
class='list-group-item list-group-item-action d-flex align-items-center bg-transparent border-0'
>
<span class='text-truncate'>{{ link.remarks || link.url }}</span>
</a>
<div
class='grid-transition'
:class='{ expanded: expandedLinks }'
>
<div class='overflow-hidden mb-2'>
<div class='list-group list-group-flush bg-accent rounded mx-2 mt-2'>
<a
v-for='(link, link_it) of url_links'
:key='link_it'
:href='link.url'
target='_blank'
class='list-group-item list-group-item-action d-flex align-items-center bg-transparent border-0'
>
<IconExternalLink
:size='18'
stroke='1'
class='me-2'
/>
<span class='text-truncate'>{{ link.remarks || link.url }}</span>
</a>
</div>
</div>
</div>
</div>
@@ -35,8 +60,8 @@
>
<div
class='d-flex align-items-center cursor-pointer user-select-none py-2 px-2 rounded transition-all mx-2'
:class='{ "bg-accent": expanded, "hover": !expanded }'
@click='expanded = !expanded'
:class='{ "bg-accent": expandedResponders, "hover": !expandedResponders }'
@click='expandedResponders = !expandedResponders'
>
<IconUsers
:size='18'
@@ -49,7 +74,7 @@
<span class='badge bg-blue-lt me-2'>{{ responder_links.length }}</span>
<IconChevronDown
class='transition-transform'
:class='{ "rotate-180": !expanded }'
:class='{ "rotate-180": !expandedResponders }'
:size='18'
/>
</div>
@@ -57,7 +82,7 @@
<div
class='grid-transition'
:class='{ expanded: expanded }'
:class='{ expanded: expandedResponders }'
>
<div class='overflow-hidden'>
<div class='row row-cards mx-2 pt-2'>
@@ -102,10 +127,11 @@
<script setup lang='ts'>
import { computed, ref } from 'vue';
import { IconUsers, IconLink, IconChevronDown } from '@tabler/icons-vue';
import { IconUsers, IconLink, IconChevronDown, IconExternalLink } from '@tabler/icons-vue';
import timediff from '../../../timediff';
const expanded = ref(false);
const expandedResponders = ref(false);
const expandedLinks = ref(false);
const props = defineProps<{
links: Array<{
@@ -150,4 +176,8 @@ const responder_links = computed(() => {
.transition-transform {
transition: transform 0.3s ease-out;
}
.list-group-item-action:hover {
background-color: rgba(255, 255, 255, 0.05) !important;
}
</style>

View File

@@ -13,6 +13,12 @@
/>
<label class='subheader cursor-pointer m-0'>Times</label>
<div class='ms-auto d-flex align-items-center'>
<span
v-if='props.cot.properties.start'
class='cursor-pointer me-2 text-muted small'
@click.stop='mode = mode === "relative" ? "absolute" : "relative"'
v-text='`Start: ${startProp}`'
/>
<IconChevronDown
class='transition-transform'
:class='{ "rotate-180": !expanded }'

View File

@@ -892,7 +892,12 @@ export const useMapStore = defineStore('cloudtak', {
this.radial.mode = opts.mode;
if (feat.properties && feat.properties.center) {
this.radial.lngLat = mapgl.LngLat.convert(feat.properties.center as LngLatLike);
if (typeof feat.properties.center === 'string') {
const parts = JSON.parse(feat.properties.center);
this.radial.lngLat = new mapgl.LngLat(parts[0], parts[1]);
} else {
this.radial.lngLat = mapgl.LngLat.convert(feat.properties.center as LngLatLike);
}
} else {
this.radial.lngLat = opts.lngLat;
}