Skip to content

Commit

Permalink
Version 258:
Browse files Browse the repository at this point in the history
* UI3 now detects when an H.264 stream request is redirected or receives an HTTP 403 Forbidden response, and treats it as a session loss instead of handling it as a protocol error.
  • Loading branch information
bp2008 committed Nov 22, 2023
1 parent d388f1e commit dcb7859
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 36 deletions.
2 changes: 1 addition & 1 deletion ui3.htm
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
};
</script>
<script type="text/javascript">
var ui_version = "257";
var ui_version = "258";
var bi_version = "%%VERSION%%";
var appPath_raw = "%%VIRTDIR%%";
var local_bi_session = "%%SESSION%%";
Expand Down
120 changes: 85 additions & 35 deletions ui3/ui3.js
Original file line number Diff line number Diff line change
Expand Up @@ -6220,37 +6220,42 @@ function PtzButtons()
return;
ExecJSON({ cmd: "ptz", camera: cameraId }, function (response)
{
if (videoPlayer.Loading().image.id == cameraId)
if (response.result === "fail")
toaster.ErrorResponse(response);
else
{
/*
brightness:-1
contrast:0
irmode:0
powermode:-1
presetnum:15
presets:[""]
talksamplerate:8000
*/
currentPtzData = response.data;
if (currentPtzData)
{
currentPtzData.presetMap = {};
if (currentPtzData.presets)
if (videoPlayer.Loading().image.id == cameraId)
{
/*
brightness:-1
contrast:0
irmode:0
powermode:-1
presetnum:15
presets:[""]
talksamplerate:8000
*/
currentPtzData = response.data;
if (currentPtzData)
{
for (var i = 0; i < currentPtzData.presets.length; i++)
currentPtzData.presetMap = {};
if (currentPtzData.presets)
{
var objType = typeof currentPtzData.presets[i];
if (objType === "string")
currentPtzData.presetMap[i + 1] = MakePresetObj(i + 1, currentPtzData.presets[i]);
else if (objType === "object")
currentPtzData.presetMap[parseInt(currentPtzData.presets[i].num)] = currentPtzData.presets[i];
for (var i = 0; i < currentPtzData.presets.length; i++)
{
var objType = typeof currentPtzData.presets[i];
if (objType === "string")
currentPtzData.presetMap[i + 1] = MakePresetObj(i + 1, currentPtzData.presets[i]);
else if (objType === "object")
currentPtzData.presetMap[parseInt(currentPtzData.presets[i].num)] = currentPtzData.presets[i];
}
}
currentPtzData.cameraId = cameraId;
self.SetIRButtonState();
self.SetBrightnessButtonState();
self.SetContrastButtonState();
}
}
currentPtzData.cameraId = cameraId;
self.SetIRButtonState();
self.SetBrightnessButtonState();
self.SetContrastButtonState();
}
}, function ()
{
Expand Down Expand Up @@ -14067,6 +14072,7 @@ function SessionManager()
self.HandleSuccessfulLogin(response, true);
sessionExpiredToast.remove();
sessionExpiredToast = null;
videoPlayer.ReopenStreamAtCurrentSeekPosition();
}
else
{
Expand Down Expand Up @@ -17559,7 +17565,7 @@ function FetchH264VideoModule()
if (message !== "INPUT REQUIRED")
toaster.Error(message, 15000);
}
var StreamEnded = function (message, wasJpeg, wasAppTriggered, videoFinishedStreaming, responseError)
var StreamEnded = function (message, wasJpeg, wasAppTriggered, videoFinishedStreaming, responseError, isSessionLoss)
{
if (currentServer.isLoggingOut)
return;
Expand Down Expand Up @@ -17591,9 +17597,17 @@ function FetchH264VideoModule()
else if (!serverTimeLimiter.isNearStreamLimit())
{
programmaticSoundPlayer.NotifyDisconnected();
reconnectingToast.showText("The video stream was lost. Attempting to reconnect...");
clearTimeout(failureRecoveryTimeout);
failureRecoveryTimeout = setTimeout(ReopenStreamAtCurrentSeekPosition, 2000);
if (isSessionLoss)
{
reconnectingToast.showText("The video player is waiting for session recovery...");
sessionManager.ReestablishLostSession();
}
else
{
reconnectingToast.showText("The video stream was lost. Attempting to reconnect...");
failureRecoveryTimeout = setTimeout(ReopenStreamAtCurrentSeekPosition, 2000);
}
}
}
}
Expand Down Expand Up @@ -26158,7 +26172,7 @@ function ClipExportStreamer(path, startTimeMs, durationMs, useTranscodeMethod, i
aviEncoder = new AVIEncoder("H264", bitmapInfoHeader, waveFormatEx ? "ulaw" : null, waveFormatEx);
progressUpdate(1, "Export progress: 0%");
}
var StreamEnded = function (message, wasJpeg, wasAppTriggered, videoFinishedStreaming, responseError)
var StreamEnded = function (message, wasJpeg, wasAppTriggered, videoFinishedStreaming, responseError, isSessionLoss)
{
if (!exportCompleteCb)
return;
Expand Down Expand Up @@ -28933,6 +28947,33 @@ function Toaster()
{
return showToastInternal('error', message, showTime, closeButton, onClick, extendedTimeOut);
}
this.ErrorResponse = function (responseObject)
{
var msg;
if (responseObject)
{
var json = JSON.stringify(responseObject);
console.log("toaster.ErrorResponse", json);
if (responseObject.data && responseObject.data.reason)
msg = responseObject.data.reason;
else if (responseObject.result === "fail")
msg = "JSON API response failed without a reason being given. " + json;
else
msg = "JSON API response failed for an unknown reason. " + json;
}
else
{
console.log("toaster.ErrorResponse(null)");
msg = "JSON API response object was null.";
}
try
{
var err = new Error();
msg += " \n" + err.stack;
}
catch (ex) { }
return showToastInternal('error', msg, 10000);
}
}
function showSuccessToast(message, showTime, closeButton)
{
Expand Down Expand Up @@ -29892,7 +29933,7 @@ var safeFetch = new (function ()
streamEndedCbForActiveFetch = queuedRequest.streamEnded;
streamer = new FetchVideoH264Streamer(queuedRequest.url, queuedRequest.headerCallback, queuedRequest.frameCallback, queuedRequest.statusBlockCallback, queuedRequest.streamInfoCallback, StreamEndedWrapper, queuedRequest.options);
}
var StreamEndedWrapper = function (message, wasJpeg, wasAppTriggered, videoFinishedStreaming, responseError)
var StreamEndedWrapper = function (message, wasJpeg, wasAppTriggered, videoFinishedStreaming, responseError, isSessionLoss)
{
if (stopTimeout != null)
{
Expand All @@ -29901,7 +29942,7 @@ var safeFetch = new (function ()
}
streamer = null;
if (streamEndedCbForActiveFetch)
streamEndedCbForActiveFetch(message, wasJpeg, wasAppTriggered, videoFinishedStreaming, responseError);
streamEndedCbForActiveFetch(message, wasJpeg, wasAppTriggered, videoFinishedStreaming, responseError, isSessionLoss);
OpenStreamNow();
}
var StopTimedOut = function ()
Expand Down Expand Up @@ -30002,15 +30043,24 @@ function FetchVideoH264Streamer(url, headerCallback, frameCallback, statusBlockC
{
if (res.status === 0)
{
responseError = res.status + " " + res.statusText;
HandleGroupableConnectionError();
CallStreamEnded("Server unreachable");
}
else if (res.status === 403 || res.status === 302) // this request may redirect to the login page
else if (res.status === 403 || res.redirected || (res.status && res.status.toString().startsWith("3")))
{
CallStreamEnded("Your session has been lost.");
// This will be problematic if the server redirects the video request for any other reason than session loss.
if (res.redirected)
responseError = "0 fetch redirected";
else
responseError = res.status + " " + res.statusText;
var contentType = res.headers.get("Content-Type");
var isSessionLoss = typeof contentType === "string" && !contentType.startsWith("video");
CallStreamEnded("Your session has been lost.", undefined, undefined, isSessionLoss);
}
else if (res.status === 503)
{
responseError = res.status + " " + res.statusText;
HandleGroupableConnectionError();
CallStreamEnded("Server responded saying service is unavailable");
}
Expand Down Expand Up @@ -30086,13 +30136,13 @@ function FetchVideoH264Streamer(url, headerCallback, frameCallback, statusBlockC
}
});
}
function CallStreamEnded(message, naturalEndOfStream, wasJpeg)
function CallStreamEnded(message, naturalEndOfStream, wasJpeg, isSessionLoss)
{
if (typeof streamEnded === "function")
{
try
{
streamEnded(message, wasJpeg, stopCalledByApp, naturalEndOfStream, responseError);
streamEnded(message, wasJpeg, stopCalledByApp, naturalEndOfStream, responseError, isSessionLoss);
}
catch (e)
{
Expand Down

0 comments on commit dcb7859

Please sign in to comment.