HLS спира на фон или при заключен екран: как да го възстановим

Диагностицирайте HLS, който спира, изостава или не се възстановява след смяна на раздел, приложение, заключване, замразяване или отхвърляне на страницата.

HLS плейърът може да работи нормално, докато зрителят не смени раздела, не премине към друго приложение, не заключи телефона или не остави устройството неактивно. При връщане картината може да е спряла, звукът да липсва, плейърът да изостава значително от живото предаване или следващата заявка за плейлист да е неуспешна. Симптомите си приличат, но могат да възникнат в различни слоеве: кода на приложението, управлението на жизнения цикъл на браузъра, медийните правила на операционната система, изтекъл прозорец на живото предаване, оторизацията или медийния конвейер.

Не обещавайте, че уеб страница ще продължи да възпроизвежда видео, докато е скрита, на всяко устройство. Разглеждайте непрекъснатото фоново възпроизвеждане и надеждното възстановяване на преден план като отделни продуктови изисквания. Тествайте само потоци, които притежавате или имате право да проверявате, и премахвайте подписани URL адреси, бисквитки, токени, идентификатори на устройства, IP адреси и частни имена на хостове от споделените записи.

Метод на проверка — 24 септември 2026 г.: изпълнихме детерминиран тест на политика за възстановяване с Node.js 24.12.0. Синтетичният прозорец на живо се премести от медийно време 40–70 към 58–88. Запазената позиция 42 беше изтекла, затова тестът избра зададена позиция за синхронизация с живото предаване 76; позиция 62 все още можеше да се търси и беше запазена. VOD позиция 120 в диапазон 0–300 беше запазена, HTTP 403 доведе до повторна оторизация вместо повтарящи се медийни опити, а wasDiscarded: true — до повторно инициализиране на живото предаване. Това проверява единствено изчисленията за диапазони и решения в теста. То не тества промяна в жизнения цикъл на браузър, HLS плейър, декодиране, мрежа, заключен екран на телефон, фонови правила на операционна система или физическо устройство. Скриптът е в scripts/test-hls-background-recovery.cjs.

Назовете точно прехода, който тествате

„Спира във фонов режим“ не е възпроизводим доклад. Преди да променяте кода, запишете точния преход и очакваното поведение.

ПреходКакво може да наблюдава страницатаКакво не доказва това
Друг раздел на браузъра става активенvisibilitychange и document.visibilityState === "hidden"Че страницата е замразена, отхвърлена или има право да възпроизвежда звук
Браузърът или приложението минава във фонов режимПромяната във видимостта може да е последното надеждно събитиеЧе по-късно събитие от типа unload непременно ще се изпълни
Екранът се заключваДокументът може да стане скритЧе всеки браузър и ОС запазват еднаква медийна или мрежова политика
Страницата се замразяваЗадачите, които подлежат на замразяване, включително много таймери и обратни извиквания, спиратЧе процесът за изобразяване е унищожен
Страницата се отхвърляСтраницата се премахва за пестене на ресурси и по-късно се презареждаЧе JavaScript е получил събитие при самото отхвърляне
Компонентът се демонтира или маршрутът се сменяПриложението може да унищожи плейъра или да изчисти източникаЧе браузърът е наложил фонова политика

MDN описва visibilitychange при смяна на раздел, минимизиране на прозорец и преминаване към друго приложение на мобилно устройство. Указанията на Chrome за жизнения цикъл разграничават скрито, замразено, прекратено и отхвърлено състояние и предупреждават, че преходът към скрито често е последният надеждно наблюдаем преход на мобилно устройство. Това са полезни сигнали, а не междуплатформена гаранция, че медията ще продължи.

Съберете доказателства, преди страницата да изчезне

Добавете временен диагностичен регистратор в контролирана версия. Записвайте монотонно и календарно време, видимост, медийно състояние, текуща позиция, всички диапазони за търсене и буфериране и последното събитие на плейъра или мрежата. Не записвайте пълни подписани URL адреси.

function ranges(value) {
  return Array.from(
    { length: value.length },
    (_, index) => ({ start: value.start(index), end: value.end(index) })
  );
}

function snapshot(video, event) {
  console.info('media-lifecycle', {
    event,
    monotonicMs: performance.now(),
    visibility: document.visibilityState,
    paused: video.paused,
    ended: video.ended,
    currentTime: video.currentTime,
    readyState: video.readyState,
    networkState: video.networkState,
    seekable: ranges(video.seekable),
    buffered: ranges(video.buffered),
  });
}

document.addEventListener('visibilitychange', () => snapshot(video, 'visibilitychange'));
for (const name of ['pause', 'play', 'playing', 'waiting', 'stalled', 'suspend', 'emptied', 'error']) {
  video.addEventListener(name, () => snapshot(video, name));
}

Ако плейърът предоставя събития за манифест, фрагмент, буфер, ниво и фатална грешка, записвайте ги на същата монотонна времева линия. Събирайте pagehide, pageshow, freeze и resume, когато се поддържат, но никога не разчитайте на unload като единствено място за запис на състояние или телеметрия.

Първо изключете спиране, предизвикано от приложението

Много фонови повреди са причинени от самото приложение. Потърсете всяко pause(), премахване на източник, destroy() или detachMedia() на плейъра, изчистване при смяна на маршрут, нулиране на състояние и обработчик на visibilitychange. Компонент на рамката може да се демонтира, когато изгледът се скрие. Обработчик за пестене на енергия може умишлено да спре зареждането, но да няма съответен път за възстановяване. Два слушателя на жизнения цикъл също могат да се надпреварват: единият възстановява, а другият връща старото състояние „пауза“.

Добавяйте причина към всеки преднамерен преход, вместо да записвате само „паузирано“. Стекът на извикванията в диагностична версия може да посочи кой е извикал обвивката около pause() или унищожаването. Сравнете минимална страница с плейър и цялото приложение със същия разрешен поток. Ако минималната страница се възстановява, а приложението — не, проверете управлението от приложението, преди да обвинявате HLS или CDN.

Първо доказателство след връщанеВероятна границаСледваща контролирана проверка
Екземплярът на плейъра липсва или източникът е празенЖизнен цикъл на компонента или унищожаванеПроследете демонтиране, destroy, задаване на източник и възстановяване на състояние
Медията е паузирана без неуспешна заявкаПреднамерена пауза, платформена политика или изгубено намерениеЗапишете реда на събитията и тествайте изрично действие за продължаване
play() е отхвърленПолитика за възпроизвеждане или недостъпно медийно състояниеЗапишете името и съобщението на отхвърления Promise; не скривайте грешката
Плейлистът отново се зарежда, но съдържанието е староCDN кеш или спрян цикъл за обновяванеСравнете времеви печати, медийна последователност, Age и повтарящо се съдържание
Първата заявка връща 401/403Изтекла оторизация или подписан подчинен URLОбновете оторизацията по одобрения път
Стар сегмент връща 404/410Запазената позиция е напуснала периода на съхранениеСравнете новия прозорец на плейлиста и диапазоните за търсене
Байтовете пристигат, но декодирането не продължаваМедиен конвейер, времеви печати, кодек или прекъсванеЗапазете грешките на плейъра и проверете първата медия след връщането

Прочетете времевата линия отново след връщане

Не приемайте, че позицията, запазена преди преминаването във фонов режим, още съществува. Плъзгащият се плейлист на живо може да напредне, докато зареждането е ограничено или спряно. Прочетете новия медиен плейлист, след което запишете video.seekable, позицията за синхронизация с живото предаване, избраното представяне, прекъсванията и запазения currentTime.

За живо предаване изберете между три състояния:

  1. Запазената позиция още е достъпна: запазете я, ако продуктът поддържа умишлено DVR гледане.
  2. Запазената позиция е изтекла: присъединете се към проверена позиция за синхронизация в текущия диапазон.
  3. Все още няма надежден диапазон: презаредете времевата линия и изчакайте; не търсете измислена стойност.

За VOD възстановете запазената позиция само ако попада в текущ диапазон за търсене. При живо и VOD ограничете всяка цел до реален диапазон. MDN отбелязва, че по-старо съдържание на живо може да стане недостъпно и че заявеният currentTime може да бъде коригиран до поддържана позиция.

function contains(ranges, time) {
  return ranges.some(range => time >= range.start && time <= range.end);
}

function chooseLiveTarget({ savedTime, ranges, liveSyncPosition }) {
  if (!ranges.length) return null;
  if (contains(ranges, savedTime)) return savedTime;

  const latest = ranges.at(-1);
  return Math.min(latest.end, Math.max(latest.start, liveSyncPosition));
}

Когато е налична, използвайте документираната позиция за синхронизация на плейъра, а не математическия край. Разделът с технически статии съдържа свързани ръководства за латентност и връщане към живо. Ако търсенето прескача напред или назад, използвайте раздела за HLS диагностика, докато съответният превод стане наличен.

Третирайте намерението за възпроизвеждане като състояние, а не като догадка

Преди страницата да се скрие, запазете дали възпроизвеждането е поискано от потребителя, текущата позиция, идентичността на съдържанието, типа поток, избраните писти, силата на звука и обезличена версия на оторизацията. Не приравнявайте paused === false с разрешение за безкрайно автоматично рестартиране: потребителят може да е паузирал чрез системните медийни контроли, докато страницата е скрита.

При връщане:

  1. Създайте плейъра отново само ако е бил унищожен или страницата е била отхвърлена.
  2. Обновете оторизацията, когато доказателствата показват изтичане; не повтаряйте безкрайно 401/403.
  3. Заредете текущия плейлист и изчакайте реален диапазон за търсене.
  4. Изберете VOD, DVR или цел за синхронизация според обещанието на продукта.
  5. Извикайте play() само ако запазеното намерение още изисква възпроизвеждане.
  6. Обработете върнатия Promise и покажете ясен бутон за продължаване, ако той бъде отхвърлен.
  7. Считайте възстановяването за завършено едва след playing и напредващ currentTime, не веднага след извикването.

Това прави възстановяването идемпотентно. Повтарящи се visibilitychange събития или нови изобразявания от рамката не бива да създават множество HLS екземпляри, дублирани слушатели, конкуриращи се обновявания на плейлиста или цикъл от play/pause извиквания.

Отделете грешката в оторизацията от медийната грешка

Краткотрайните подписи често изтичат, докато устройството е неактивно. Основният плейлист може още да е кеширан, но следващият медиен плейлист, ключ, инициализационна секция или сегмент да използва изтекъл подчинен URL. Запишете класа на ресурса и обезличения статус на първата заявка след връщането.

Търсене или изчистване на медийния буфер не поправя 401 или 403. Обновете сесията или вземете нов оторизиран URL чрез поддържания от услугата процес. Никога не добавяйте идентификационни данни от един произход към друг, не публикувайте подписани URL адреси и не отслабвайте контрола на достъпа, за да изглежда възстановяването успешно.

Ако плейлистът е актуален, но обявен обект връща 404, използвайте раздела с ръководства за HLS за диагностика на остарял плейлист и липсващ сегмент. Ако заявките след връщане просто са бавни, разделът за отстраняване на проблеми съдържа метода за разделяне на пропускателна способност, наличност на сегменти, декодиране и състояние на плейъра.

Проектирайте интерфейс за честно показване на възстановяването

Интерфейсът трябва да различава „Пауза“, „Повторно свързване“, „Връщане към живо“, „Изоставане от живото“ и „Докоснете за продължаване“. Не показвайте „На живо“, докато страницата още използва стар плейлист или преди позицията да започне да напредва. Запазете умишлената DVR позиция, ако още се поддържа; предложете отделно действие за връщане към живо, вместо тихо да я презаписвате.

Ако фоновият звук е продуктово изискване, документирайте поддържания браузър, режим на инсталиране, операционна система, тип съдържание, изискване за потребителски жест, контроли на заключен екран и поведение при прекъсване. Успешен тест в един настолен раздел не е доказателство за iOS Safari, инсталирано PWA, Android Chrome, WebView или нативно приложение.

Изпълнете матрица от преходи на физически устройства

Автоматизацията може да провери кода за решения и изобразяването на страницата, но платформената политика изисква физически устройства. Използвайте същия разрешен поток и задръжте всеки преход достатъчно дълго, за да се премести прозорецът на живо или да изтече подписът.

ИзмерениеМинимални случаи за записване
Път на устройствотоПоддържан телефон, таблет, настолен компютър, инсталирано PWA, WebView или нативна обвивка
ПреходСмяна на раздел, смяна на приложение, заключване, отключване, входящо прекъсване, освобождаване на процеса от браузъра
ПродължителностКратко връщане, след повече от един целеви интервал, след DVR прозореца, след валидността на URL
МедияVOD, плъзгащо се живо, EVENT/DVR, видео със звук и само звук, ако се поддържа
МрежаНепроменена мрежа, офлайн в скрито състояние, Wi-Fi към мобилна мрежа, повторна автентикация в captive portal
Очакван резултатПродължаване, безопасна пауза, възстановяване на позицията, синхронизация с живо или потребителско действие

За всеки случай записвайте версията на устройството и ОС, браузъра или WebView, плейъра, времената на прехода, действително получените събития, запазеното намерение, последователността на плейлиста преди и след, диапазоните за търсене, първата заявка след връщането, резултата от Promise на play() и времето до потвърдено playing. Обозначавайте всяка физически непроверена платформа като прегледана само по документация или непроверена.

Опишете дефекта около едно конкретно връщане

Практичен доклад може да гласи: „При медийно време 42 зрителят заключи тестовия телефон, докато диапазонът на живо беше 40–70. След връщането текущият диапазон беше 58–88, затова 42 беше изтекло. Обновеният плейлист се зареди успешно, плейърът показа позиция за синхронизация 76 и възпроизвеждането достигна playing на 76 след една разрешена от потребителя заявка.“ Така се посочва изтичането на времевата линия, вместо само „мобилното възпроизвеждане спря“.

Включете очакваното фоново поведение, запазеното намерение, точния преход, събитията, които действително са настъпили, времевата линия преди и след, обезличени мрежови доказателства, промени в управлението на плейъра, подробности за отхвърлен Promise и физическото устройство. Не твърдете, че ОС е прекратила, замразила или отхвърлила страницата без съответно доказателство.

Основни източници

Започнете от прехода, който потребителят действително е извършил, и проследете първото променено състояние или заявка. Запазете намерението преди страницата да изчезне, изградете отново текущата времева линия след връщането и възстановете до поддържана позиция, вместо да възпроизвеждате остаряло състояние. Така възстановяването на преден план остава проверимо, дори когато продължителното фоново възпроизвеждане не е платформена гаранция.