Суть кода такова -- при запросе браузера перегоняю поток jpeg'ов , по сути mjpeg, для отображения видео.
Все работает, но есть вопрос-проблема -- как сделать так, чтобы страница при сеансе все время была в загрузке,
а не просто загрузилась и все? Ну т.е. крестик бы был (например, в хроме), чтобы при его нажатии остановить
поток видео на сервере? Потому как сейчас запрос находится на строке
await netStream.CopyToAsync(Response.Body);
и страница полностью загружена. А хотелось бы иметь возможность при нажатии на крестик в браузере поток остановить, ибо
по сути каждый запрос это утечка ресурсов -- клиент закрыл страницу, а я об этом ничего не знаю и продолжаю куда-то гнать
данные. (Кстати, я закрыл страницу , что при этом будет с Response.Body?)
Т.е. как сделать так, чтобы на время стриминга страница полностью не загружалась?
Заранее благодарю.
Кодом людям нужно помогать!
Re: Как сделать частично загруженную страницу при стриминге видео?
Ты хочешь чего-то очень странного. Никто не останавливает видео крестиком. Для остановки видео используется кнопка в интерфейсе плеера. Если твой http сервер поддерживает range запросы, то браузер, по крайней мере с обычными форматами видео, не будет загружать всё. Если не хочешь полагаться на браузер — напиши свой плеер, как в ютубе, который грузит чанки сам.
S>и страница полностью загружена. А хотелось бы иметь возможность при нажатии на крестик в браузере поток остановить, ибо S>по сути каждый запрос это утечка ресурсов -- клиент закрыл страницу, а я об этом ничего не знаю и продолжаю куда-то гнать S>данные. (Кстати, я закрыл страницу , что при этом будет с Response.Body?)
Когда клиент закроет страницу — у тебя сокет с той стороны закроется и твой сервер про это почти сразу же узнает. Как это .NET обрабатывает, я не знаю, но могу предположить, что он при попытке записи в закрытый сокет выкинет исключение. "В никуда" писать — это надо постараться.
Здравствуйте, vsb, Вы писали:
vsb>Ты хочешь чего-то очень странного. Никто не останавливает видео крестиком. Для остановки видео используется кнопка в интерфейсе плеера. Если твой http сервер поддерживает range запросы, то браузер, по крайней мере с обычными форматами видео, не будет загружать всё. Если не хочешь полагаться на браузер — напиши свой плеер, как в ютубе, который грузит чанки сам.
Неудачная аналогия, согласен. Нужно, чтобы во время показа видео страница была "в загрузке", а не "загрузилась".
vsb>Когда клиент закроет страницу — у тебя сокет с той стороны закроется и твой сервер про это почти сразу же узнает. Как это .NET обрабатывает, я не знаю, но могу предположить, что он при попытке записи в закрытый сокет выкинет исключение. "В никуда" писать — это надо постараться.
Ну вот со стороны web api хотелось бы знать соотв. события.
Кодом людям нужно помогать!
Re[3]: Как сделать частично загруженную страницу при стриминге
Здравствуйте, Sharov, Вы писали:
vsb>>Ты хочешь чего-то очень странного. Никто не останавливает видео крестиком. Для остановки видео используется кнопка в интерфейсе плеера. Если твой http сервер поддерживает range запросы, то браузер, по крайней мере с обычными форматами видео, не будет загружать всё. Если не хочешь полагаться на браузер — напиши свой плеер, как в ютубе, который грузит чанки сам.
S>Неудачная аналогия, согласен. Нужно, чтобы во время показа видео страница была "в загрузке", а не "загрузилась".
Прямого API для этого я не знаю. В целом страница "загружается", пока либо основной HTML загружается, либо один из стилей, скриптов, картинок загружается (последнее с оговорками про асинхронность и прочее).
Т.е. самый простой вариант это подгрузить "картинку", при запросе которой сервер будет просто ничего не отдавать, но и не закрывать соединение. Тогда в браузере будет показываться статус, что картинка грузится. При нажатии "стоп" браузер вроде разорвет соединение, т.е. сервер это может отследить.
При этом промежуточная прокси может разорвать соединение или может в браузере есть на этот счет какие-то таймауты, я точно не знаю, поэтому в браузере скрипт должен отслеживать, если соединение разорвано не по инициативе пользователя и запрашивать картинку заново.
В общем это всё очень сложная и странная машинерия и я не думаю, что так стоит делать. Но если сильно хочется, то как-то так.
Здравствуйте, rameel, Вы писали:
S>>Ну вот со стороны web api хотелось бы знать соотв. события. R>Добавь в метод параметр CancellationToken
Ну смотрите, у меня web api get запрос, который начинает гнать поток видео(потенциально бесконечно долго),
как мне узнать, что запрашивающая сторона либо закрыла вкладку, либо оборвала запрос. Если с запросом кажется, что
более-менее ясно, скорее всего будет какое-нибудь исключение при доступе к Response.Body, то как быть с вкладкой -- я
Как тут токен может помочь -- я тоже
Кодом людям нужно помогать!
Re[5]: Как сделать частично загруженную страницу при стримин
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, rameel, Вы писали:
S>Ну смотрите, у меня web api get запрос, который начинает гнать поток видео(потенциально бесконечно долго), S>как мне узнать, что запрашивающая сторона либо закрыла вкладку, либо оборвала запрос. Если с запросом кажется, что S>более-менее ясно, скорее всего будет какое-нибудь исключение при доступе к Response.Body, то как быть с вкладкой -- я S>Как тут токен может помочь -- я тоже
Когда запрос оборвется, token.IsCancellationRequested вернет true. Попытка записать что-то в Response.Body, когда запрос оборвали не приводит к исключению, по крайней мере в моем случае никаких исключений не наблюдал ни раньше ни сейчас (Сейчас вот проверил, никаких исключений)
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[6]: Как сделать частично загруженную страницу при стримин
Здравствуйте, rameel, Вы писали:
S>>Ну смотрите, у меня web api get запрос, который начинает гнать поток видео(потенциально бесконечно долго), S>>как мне узнать, что запрашивающая сторона либо закрыла вкладку, либо оборвала запрос. Если с запросом кажется, что S>>более-менее ясно, скорее всего будет какое-нибудь исключение при доступе к Response.Body, то как быть с вкладкой -- я S>>Как тут токен может помочь -- я тоже R>Когда запрос оборвется, token.IsCancellationRequested вернет true. Попытка записать что-то в Response.Body, когда запрос оборвали не приводит к исключению, по крайней мере в моем случае никаких исключений не наблюдал ни раньше ни сейчас (Сейчас вот проверил, никаких исключений)
Можно пример, а то не догоняю куда конкретно добавить токен?
УПД: Разобрался, что CopyToAsync принимает ct. Ну а кто и как его будет активировать?
Здравствуйте, rameel, Вы писали:
R>Добавляешь как параметр твоего метода-endpoint'а. Инфраструктура сама прокинет его в твой метод. Этот токен связан с текущим запросом. R>
[HttpGet(Name = "streamer")]
public async Task<IActionResult> Get2(CancellationToken cancellationToken)
{
using (var tcpClient = new TcpClient())
{
await tcpClient.ConnectAsync("localhost", 5555, cancellationToken);
using (var netStream = tcpClient.GetStream())
{
Response.ContentType = "multipart/x-mixed-replace; boundary=abcd";
var ns = netStream;
var tcpCl = tcpClient;
Response.OnCompleted(() =>
{
ns?.Dispose();
tcpCl?.Dispose();
return Task.CompletedTask;
});
await netStream.CopyToAsync(Response.Body,1024*1024, cancellationToken);
}
}
return Ok();
}
Т.е. теперь при закрытии страницы в браузере OnCompleted отрабатывает нормально. Интересно, а без ct и закрытия страницы, поток так и будет куда-то писать данные?
Т.е. я зыкрыл страницу, что стало с Response.Body? Я не пойми куда гоню поток и трачу ресурсы...
Да и еще (дурацкий) вопрос: а почему до return Ok() я не дохожу в случае отмены (например, закрытия страницы)? Ну типа запись в поток завершилась,
идем дальше.
Кодом людям нужно помогать!
Re[9]: Как сделать частично загруженную страницу при стримин
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, rameel, Вы писали:
S>Интересно, а без ct и закрытия страницы, поток так и будет куда-то писать данные? S>Т.е. я зыкрыл страницу, что стало с Response.Body? Я не пойми куда гоню поток и трачу ресурсы...
Потому и существует CancellationToken. А вот что стало с Response.Body, то это надо внутрь заглянуть, что там в кишках происходит
S>Да и еще (дурацкий) вопрос: а почему до return Ok() я не дохожу в случае отмены (например, закрытия страницы)? Ну типа запись в поток завершилась, S>идем дальше.
Скорее всего один из метотодов ReadAsync / WriteAsync выбросил исключение OperationCanceledException, через cancellationToken.ThrowIfCancellationRequested() или возможно исключение TaskCanceledException от Task.FromCanceled
Здравствуйте, rameel, Вы писали:
S>>Интересно, а без ct и закрытия страницы, поток так и будет куда-то писать данные? S>>Т.е. я зыкрыл страницу, что стало с Response.Body? Я не пойми куда гоню поток и трачу ресурсы... R>Потому и существует CancellationToken. А вот что стало с Response.Body, то это надо внутрь заглянуть, что там в кишках происходит
Еще дурацкий вопрос -- я историю развития не знаю, ct всегда существовал или ms его позже добавила?
Если так, то как раньше обходились?
S>>Да и еще (дурацкий) вопрос: а почему до return Ok() я не дохожу в случае отмены (например, закрытия страницы)? Ну типа запись в поток завершилась, S>>идем дальше. R>Скорее всего один из метотодов ReadAsync / WriteAsync выбросил исключение OperationCanceledException, через cancellationToken.ThrowIfCancellationRequested() или возможно исключение TaskCanceledException от Task.FromCanceled
Врубил все исключения, ничего не появилось, output window ничего такого про исключения не написал.
Очень странно. По идее в данном случае должно прерываться при любом исключении, даже если оно будет
перехвачено (хотя тут уже не уверен).
Кодом людям нужно помогать!
Re[11]: Как сделать частично загруженную страницу при стримин
Здравствуйте, Sharov, Вы писали:
S>Еще дурацкий вопрос -- я историю развития не знаю, ct всегда существовал или ms его позже добавила?
Точно не помню, скорее всего с появлением Task, а это вроде в 4 дотнете фреймворк который.
S>Если так, то как раньше обходились?
Хороший вопрос, деталей уже не помню, были события жизненного цикла по которым можно было понять что и как, типа BeginRequest, EndRequest и прочие у HttpApplication. Были всякие соглашения, что методы с определенным названием у модулей, global.asax.cs и прочее, автоматом подписывались на события приложения, например, Application_EndRequest. Не так просто как сейчас конечно.
S>Врубил все исключения, ничего не появилось, output window ничего такого про исключения не написал. S>Очень странно. По идее в данном случае должно прерываться при любом исключении, даже если оно будет S>перехвачено (хотя тут уже не уверен).
Поставь уровень логирования "Debug" в appsettings, посмотри что в консоль выводится. Или оберни метод в try-catch.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[12]: Как сделать частично загруженную страницу при стримин
Здравствуйте, rameel, Вы писали:
S>>Еще дурацкий вопрос -- я историю развития не знаю, ct всегда существовал или ms его позже добавила? R>Точно не помню, скорее всего с появлением Task, а это вроде в 4 дотнете фреймворк который.
Я про конкретно webapi, а то они могли это по традиции продолбать, а выходить из положения как-то надо было.
А про ct b task я в курсе -- начало 10-х. S>>Врубил все исключения, ничего не появилось, output window ничего такого про исключения не написал. S>>Очень странно. По идее в данном случае должно прерываться при любом исключении, даже если оно будет S>>перехвачено (хотя тут уже не уверен). R>Поставь уровень логирования "Debug" в appsettings, посмотри что в консоль выводится. Или оберни метод в try-catch.
Ничего про исключение нету -- , всюду поставил debug (asp.net тоже), остановка на всех clr исключениях
Лог
Microsoft.AspNetCore.Server.Kestrel.Connections: Debug: Connection id "0HMV10CQV0O98" accepted.
Microsoft.AspNetCore.Server.Kestrel.Connections: Debug: Connection id "0HMV10CQV0O98" started.
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/1.1 GET http://localhost:5030/Streamer — —
'StreamerTest.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.14\System.Threading.Tasks.dll'.
Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware: Debug: Wildcard detected, all requests with hosts will be allowed.
Microsoft.AspNetCore.Routing.Matching.DfaMatcher: Debug: 2 candidate(s) found for the request path '/Streamer'
Microsoft.AspNetCore.Routing.Matching.DfaMatcher: Debug: Endpoint 'StreamerTest.Controllers.StreamerController.Get123 (StreamerTest)' with route pattern 'Streamer' is valid for the request path '/Streamer'
Microsoft.AspNetCore.Routing.Matching.DfaMatcher: Debug: Endpoint 'StreamerTest.Controllers.StreamerController.CreateFooter (StreamerTest)' with route pattern 'Streamer' is valid for the request path '/Streamer'
Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware: Debug: Request matched endpoint 'StreamerTest.Controllers.StreamerController.Get123 (StreamerTest)'
'StreamerTest.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.14\System.Text.RegularExpressions.dll'.
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware: Debug: Static files was skipped as the request already matched an endpoint.
Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executing endpoint 'StreamerTest.Controllers.StreamerController.Get123 (StreamerTest)'
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Information: Route matched with {action = "Get123", controller = "Streamer"}. Executing controller action with signature System.Threading.Tasks.Task`1[Microsoft.AspNetCore.Mvc.IActionResult] Get123() on controller StreamerTest.Controllers.StreamerController (StreamerTest).
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Debug: Execution plan of authorization filters (in the following order): None
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Debug: Execution plan of resource filters (in the following order): None
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Debug: Execution plan of action filters (in the following order): Microsoft.AspNetCore.Mvc.ModelBinding.UnsupportedContentTypeFilter (Order: -3000), Microsoft.AspNetCore.Mvc.Infrastructure.ModelStateInvalidFilter (Order: -2000)
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Debug: Execution plan of exception filters (in the following order): None
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Debug: Execution plan of result filters (in the following order): Microsoft.AspNetCore.Mvc.Infrastructure.ClientErrorResultFilter (Order: -2000)
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Debug: Executing controller factory for controller StreamerTest.Controllers.StreamerController (StreamerTest)
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Debug: Executed controller factory for controller StreamerTest.Controllers.StreamerController (StreamerTest)
'StreamerTest.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.14\System.Net.NameResolution.dll'.
'StreamerTest.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.14\Microsoft.AspNetCore.WebUtilities.dll'.
Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets: Debug: Connection id "0HMV10CQV0O98" received FIN.
Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets: Debug: Connection id "0HMV10CQV0O98" sending FIN because: "The client closed the connection."
Microsoft.AspNetCore.Server.Kestrel.Connections: Debug: Connection id "0HMV10CQV0O98" disconnecting.
Кодом людям нужно помогать!
Re[13]: Как сделать частично загруженную страницу при стримин
Здравствуйте, rameel, Вы писали: S>>Ничего про исключение нету -- , всюду поставил debug (asp.net тоже), остановка на всех clr исключениях R>Странно, тогда оберни в try-catch и посмотри.
В общем, все работает как надо. Серверный код выше возвращает поток некоторых изображений, браузер в свою очередь генерирует код страницы (см. выше клиента).
Т.е. если я из браузера обращаюсь к соотв. апи. Я думал, что этот html кем-то сверстан и в нем делается запрос в соотв. api.
Далее, я скопировал этот html для локальных тестов и открывал как локальный файл в браузере, файл сразу грузился и гнал поток.
Что как бы не совпадало с ожидаемым поведением -- ведь это не страница видеоплейера, где страница загрузилась целиком и
можно отдельно управлять медиапотоком, а по сути медиапоток и есть, т.е. страница не должна загрузиться раньше, чем загрузится поток (т.е. страница
находится в ожидании (загрузки потока) ).
Короче, просто неправильно тестировал апи -- надо было через браузер, а не через автосгенеренный файл.