I took some code from here which seemed to do the job with basically relying a mjpg to an another controller. Frame-by-frame MJPEG streaming with C#/ASP.NET MVC
This at the time seemed to work, but on returning to the code now it doesn't.
The Image appears and that part works however it seems to basically prevent any other requests to the API (e.g left, right) for around 1-3 mins. This is in Chrome and Edge.
In the developers toolbox the request keeps just going up in time/size. So it's like Chrome is awaiting something or I've not sent something.
Ideas?
public class CameraController : ApiController
{
private MJPEGStream mjpegStream = new MJPEGStream();
private bool frameAvailable = false;
private Bitmap frame = null;
private string BOUNDARY = "frame";
private WebClient Client { get; set; }
private string URL { get; set; }
private string CameraNumber { get; set; }
/// <summary>
/// Initializer for the MJPEGstream
/// </summary>
CameraController()
{
Client = new WebClient { Credentials = new NetworkCredential(ConfigurationManager.AppSettings["WebCamUser"], ConfigurationManager.AppSettings["WebCamPassword"]) };
URL = ConfigurationManager.AppSettings["WebCamIP"].ToString();
CameraNumber = ConfigurationManager.AppSettings["WebCameraNumber"].ToString();
mjpegStream.Source = ConfigurationManager.AppSettings["WebCamIP"].ToString()+@"/mjpg/video.mjpg";
mjpegStream.ForceBasicAuthentication = true;
mjpegStream.Login = ConfigurationManager.AppSettings["WebCamUser"];
mjpegStream.Password = ConfigurationManager.AppSettings["WebCamPassword"];
mjpegStream.NewFrame += new NewFrameEventHandler(showFrameEvent);
}
[HttpGet]
public string Image()
{
var imageName = DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".jpg";
//ok
//just have something here that deletes anything older than 2 mins
string[] files = Directory.GetFiles(System.Web.Hosting.HostingEnvironment.MapPath("~/Content/Temp/"));
foreach (string file in files)
{
FileInfo fi = new FileInfo(file);
if (fi.CreationTime < DateTime.Now.AddMinutes(-2))
{
fi.Delete();
}
}
var p = Path.Combine(System.Web.Hosting.HostingEnvironment.MapPath("~/Content/Temp/"), imageName);
Client.DownloadFile(URL + "/axis-cgi/jpg/image.cgi", p);
return imageName;
}
[HttpGet]
public bool Left()
{
var response = Client.DownloadString(URL + "/axis-cgi/com/ptz.cgi?camera=" + CameraNumber + "&move=left");
return true;
}
[HttpGet]
public bool Right()
{
var response = Client.DownloadString(URL + "/axis-cgi/com/ptz.cgi?camera=" + CameraNumber + "&move=right");
return true;
}
[HttpGet]
public bool Up()
{
var response = Client.DownloadString(URL + "/axis-cgi/com/ptz.cgi?camera=" + CameraNumber + "&move=up");
return true;
}
[HttpGet]
public bool Down()
{
var response = Client.DownloadString(URL + "/axis-cgi/com/ptz.cgi?camera=" + CameraNumber + "&move=down");
return true;
}
[HttpGet]
public bool ZoomIn()
{
var response = Client.DownloadString(URL + "/axis-cgi/com/ptz.cgi?camera=" + CameraNumber + "&rzoom=2500");
return true;
}
[HttpGet]
public bool ZoomOut()
{
var response = Client.DownloadString(URL + "/axis-cgi/com/ptz.cgi?camera=" + CameraNumber + "&rzoom=-2500");
return true;
}
[HttpGet]
public string GetImageVideoFallback()
{
var image = Client.DownloadData(URL + "/axis-cgi/jpg/image.cgi");
var base64 = Convert.ToBase64String(image);
var imgSrc = String.Format("data:image/jpg;base64,{0}", base64);
return imgSrc;
}
[HttpGet]
public HttpResponseMessage GetVideoContent()
{
mjpegStream.Start();
var response = Request.CreateResponse();
Func<Stream, HttpContent, TransportContext, Task> func = StartStream;
response.Content = new PushStreamContent(func);
response.Content.Headers.Remove("Content-Type");
response.Content.Headers.TryAddWithoutValidation("Content-Type", "multipart/x-mixed-replace;boundary=" + BOUNDARY);
return response;
}
/// <summary>
/// Craete an appropriate header.
/// </summary>
/// <param name="length"></param>
/// <returns></returns>
private byte[] CreateHeader(int length)
{
string header =
"--" + BOUNDARY + "\r\n" +
"Content-Type:image/jpeg\r\n" +
"Content-Length:" + length + "\r\n\r\n";
return Encoding.UTF8.GetBytes(header);
}
public byte[] CreateFooter()
{
return Encoding.UTF8.GetBytes("\r\n");
}
/// <summary>
/// Write the given frame to the stream
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="frame">Bitmap format frame</param>
private async Task WriteFrameAsync(Stream stream, Bitmap frame)
{
// prepare image data
byte[] imageData = null;
// this is to make sure memory stream is disposed after using
using (MemoryStream ms = new MemoryStream())
{
frame.Save(ms, ImageFormat.Jpeg);
imageData = ms.ToArray();
}
// prepare header
byte[] header = CreateHeader(imageData.Length);
// prepare footer
byte[] footer = CreateFooter();
// Start writing data
await stream.WriteAsync(header, 0, header.Length);
await stream.WriteAsync(imageData, 0, imageData.Length);
//await stream.WriteAsync(footer, 0, footer.Length);
await Task.Delay(1000 / 30);
await stream.FlushAsync();
}
/// <summary>
/// While the MJPEGStream is running and clients are connected,
/// continue sending frames.
/// </summary>
/// <param name="stream">Stream to write to.</param>
/// <param name="httpContent">The content information</param>
/// <param name="transportContext"></param>
private async Task StartStream(Stream stream, HttpContent httpContent, TransportContext transportContext)
{
while (mjpegStream.IsRunning && HttpContext.Current.Response.IsClientConnected)
{
if (frameAvailable)
{
try
{
frameAvailable = false;
await WriteFrameAsync(stream, frame);
await Task.Delay(1000 / 30);
}
catch (Exception) { }
}
else
{
Thread.Sleep(100);
}
}
stopStream();
stream.Flush();
stream.Close();
stream.Dispose();
}
/// <summary>
/// This event is thrown when a new frame is detected by the MJPEGStream
/// </summary>
/// <param name="sender">Object that is sending the event</param>
/// <param name="eventArgs">Data from the event, including the frame</param>
private void showFrameEvent(object sender, NewFrameEventArgs eventArgs)
{
frame = new Bitmap(eventArgs.Frame);
frameAvailable = true;
}
/// <summary>
/// Stop the stream.
/// </summary>
private void stopStream()
{
//System.Diagnostics.Debug.WriteLine("Stop stream");
mjpegStream.Stop();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
stopStream();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
base.Dispose(disposing);
}
}
Ok, so link back to the question/answer that set me on the right path. How to stream a mjpeg video on a website
So the issue I had was that I needed session variables (security reasons) in my API and so used this in my global.asax.
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
private bool IsWebApiRequest()
{
return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(WebApiConfig.UrlPrefixRelative);
}
Updating the session/code to
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.ReadOnly);
Solved the problem and everything started working again.