public static async ValueTask HttpSendFirstChunked(this JT1078HttpContext context, ReadOnlyMemory <byte> buffer) { context.Context.Response.AddHeader("Access-Control-Allow-Headers", "*"); context.Context.Response.AppendHeader("Access-Control-Allow-Origin", "*"); context.Context.Response.StatusCode = (int)HttpStatusCode.OK; context.Context.Response.SendChunked = true; await context.Context.Response.OutputStream.WriteAsync(buffer); }
/// <summary> /// 发送音视频数据 /// </summary> /// <param name="httpContext"></param> /// <param name="data"></param> /// <param name="firstSend"></param> public async void SendAVData(JT1078HttpContext httpContext, byte[] data, bool firstSend) { if (httpContext.IsWebSocket) { if (firstSend) { httpContext.FirstSend = firstSend; Sessions.TryUpdate(httpContext.SessionId, httpContext, httpContext); } try { await httpContext.WebSocketSendBinaryAsync(data); } catch (Exception ex) { if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogInformation($"[ws close]:{httpContext.SessionId}-{httpContext.Sim}-{httpContext.ChannelNo}-{httpContext.StartTime:yyyyMMddhhmmss}"); } remove(httpContext.SessionId); } } else { if (firstSend) { httpContext.FirstSend = firstSend; Sessions.TryUpdate(httpContext.SessionId, httpContext, httpContext); try { await httpContext.HttpSendFirstChunked(data); } catch (Exception ex) { if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogInformation($"[http close]:{httpContext.SessionId}-{httpContext.Sim}-{httpContext.ChannelNo}-{httpContext.StartTime:yyyyMMddhhmmss}"); } remove(httpContext.SessionId); } } else { try { await httpContext.HttpSendChunked(data); } catch (Exception ex) { if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogInformation($"[http close]:{httpContext.SessionId}-{httpContext.Sim}-{httpContext.ChannelNo}-{httpContext.StartTime:yyyyMMddhhmmss}"); } remove(httpContext.SessionId); } } } }
public void AddOrUpdateHlsSession(JT1078HttpContext httpContext) { //如果不存在就添加,如果存在则删除后添加(保持key和value中的sessionid一致) var session = Sessions.FirstOrDefault(m => m.Value.Sim == httpContext.Sim && m.Value.ChannelNo == httpContext.ChannelNo && m.Value.RTPVideoType == RTPVideoType.Http_Hls); if (!string.IsNullOrEmpty(session.Key)) { Sessions.TryRemove(session.Key, out var _); } Sessions.TryAdd(httpContext.SessionId, httpContext); }
public static async ValueTask HttpClose(this JT1078HttpContext context) { byte[] b = Encoding.UTF8.GetBytes("close"); context.Context.Response.StatusCode = (int)HttpStatusCode.OK; context.Context.Response.KeepAlive = false; context.Context.Response.ContentLength64 = b.Length; var output = context.Context.Response.OutputStream; await output.WriteAsync(b, 0, b.Length); context.Context.Response.OutputStream.Close(); context.Context.Response.Close(); }
public void AddOrUpdate(JT1078HttpContext httpContext) { var session = Sessions.FirstOrDefault(m => m.Value.Sim == httpContext.Sim && m.Value.ChannelNo == httpContext.ChannelNo); if (string.IsNullOrEmpty(session.Key)) { Sessions.TryAdd(httpContext.SessionId, httpContext); } else { Sessions.TryUpdate(session.Key, httpContext, session.Value); } }
public bool TryAdd(JT1078HttpContext httpContext) { return(Sessions.TryAdd(httpContext.SessionId, httpContext)); }
private async ValueTask ProcessRequestAsync(HttpListenerContext context, IPrincipal principal) { if (context.Request.RawUrl.StartsWith("/favicon.ico")) { context.Http404(); return; } if (context.Request.RawUrl.Contains(".m3u8") || context.Request.RawUrl.Contains(".ts")) { hLSRequestManager.HandleHlsRequest(context, principal); return; } if (Logger.IsEnabled(LogLevel.Trace)) { Logger.LogTrace($"[http RequestTraceIdentifier]:{context.Request.RequestTraceIdentifier.ToString()}-{context.Request.RemoteEndPoint.ToString()}"); } if (Logger.IsEnabled(LogLevel.Trace)) { Logger.LogTrace($"[http RequestTraceIdentifier]:{context.Request.RequestTraceIdentifier.ToString()}-{context.Request.RemoteEndPoint.ToString()}"); } string sim = context.Request.QueryString.Get("sim"); string channel = context.Request.QueryString.Get("channel"); if (string.IsNullOrEmpty(sim) || string.IsNullOrEmpty(channel)) { await context.Http400(); return; } int.TryParse(channel, out int channelNo); if (context.Request.IsWebSocketRequest) { HttpListenerWebSocketContext wsContext = await context.AcceptWebSocketAsync(null, keepAliveInterval : TimeSpan.FromSeconds(5)); var jT1078HttpContext = new JT1078HttpContext(context, wsContext, principal); jT1078HttpContext.Sim = sim; jT1078HttpContext.ChannelNo = channelNo; jT1078HttpContext.RTPVideoType = RTPVideoType.Ws_Flv; SessionManager.TryAdd(jT1078HttpContext); //这个发送出去,flv.js就报错了 //await jT1078HttpContext.WebSocketSendHelloAsync(); await Task.Factory.StartNew(async(state) => { //https://www.bejson.com/httputil/websocket/ //ws://localhost:15555?token=22&sim=1221&channel=1 var websocketContext = state as JT1078HttpContext; while (websocketContext.WebSocketContext.WebSocket.State == WebSocketState.Open || websocketContext.WebSocketContext.WebSocket.State == WebSocketState.Connecting) { var buffer = ArrayPool <byte> .Shared.Rent(256); try { //客户端主动断开需要有个线程去接收通知,不然会客户端会卡死直到超时 WebSocketReceiveResult receiveResult = await websocketContext.WebSocketContext.WebSocket.ReceiveAsync(buffer, CancellationToken.None); if (receiveResult.EndOfMessage) { if (receiveResult.Count > 0) { var data = buffer.AsSpan().Slice(0, receiveResult.Count).ToArray(); if (Logger.IsEnabled(LogLevel.Trace)) { Logger.LogTrace($"[ws receive]:{Encoding.UTF8.GetString(data)}"); } await websocketContext.WebSocketSendTextAsync(data); } } } finally { ArrayPool <byte> .Shared.Return(buffer); } } if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogInformation($"[ws close]:{websocketContext.SessionId}-{websocketContext.Sim}-{websocketContext.ChannelNo}-{websocketContext.StartTime:yyyyMMddhhmmss}"); } SessionManager.TryRemove(websocketContext.SessionId); }, jT1078HttpContext); } else { var jT1078HttpContext = new JT1078HttpContext(context, principal); jT1078HttpContext.Sim = sim; jT1078HttpContext.RTPVideoType = RTPVideoType.Http_Flv; jT1078HttpContext.ChannelNo = channelNo; SessionManager.TryAdd(jT1078HttpContext); } }
private async ValueTask ProcessRequestAsync(HttpListenerContext context, IPrincipal principal) { if (context.Request.RawUrl.StartsWith("/favicon.ico")) { context.Http404(); return; } var uri = new Uri(context.Request.RawUrl); string url = uri.AbsolutePath; var queryParams = uri.Query.Substring(1, uri.Query.Length - 1).Split('&'); if (queryParams.Length < 2) { context.Http404(); return; } if (url.EndsWith(".m3u8") || url.EndsWith(".ts")) { string key = $"{queryParams[0].Split('=')[1]}_{queryParams[1].Split('=')[1]}";//默认queryParams第一个参数是终端号,第二个参数是通道号 memoryCache.GetOrCreate(key, (cacheEntry) => { cacheEntry.SetSlidingExpiration(TimeSpan.FromSeconds(20)); cacheEntry.RegisterPostEvictionCallback((key, value, reason, state) => { //当清空httpssion时,同时清除tcpsseion }); return(DateTime.Now); }); string filename = Path.GetFileName(url); string filepath = Path.Combine(Configuration.HlsRootDirectory, key, filename); if (!File.Exists(filepath)) { context.Http404(); return; } try { using (FileStream sr = new FileStream(filepath, FileMode.Open)) { context.Response.ContentLength64 = sr.Length; await sr.CopyToAsync(context.Response.OutputStream); } string ext = Path.GetExtension(filename); if (ext == ".m3u8") { context.Response.ContentType = m3u8Mime; } else if (ext == ".ts") { context.Response.ContentType = tsMime; } context.Response.StatusCode = (int)HttpStatusCode.OK; } catch (Exception ex) { Logger.LogError(ex, $"{context.Request.RawUrl}"); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } finally { context.Response.OutputStream.Close(); context.Response.Close(); } return; } if (Logger.IsEnabled(LogLevel.Trace)) { Logger.LogTrace($"[http RequestTraceIdentifier]:{context.Request.RequestTraceIdentifier.ToString()}-{context.Request.RemoteEndPoint.ToString()}"); } string sim = context.Request.QueryString.Get("sim"); string channel = context.Request.QueryString.Get("channel"); if (string.IsNullOrEmpty(sim) || string.IsNullOrEmpty(channel)) { await context.Http400(); return; } int.TryParse(channel, out int channelNo); if (context.Request.IsWebSocketRequest) { HttpListenerWebSocketContext wsContext = await context.AcceptWebSocketAsync(null, keepAliveInterval : TimeSpan.FromSeconds(5)); var jT1078HttpContext = new JT1078HttpContext(context, wsContext, principal); jT1078HttpContext.Sim = sim; jT1078HttpContext.ChannelNo = channelNo; SessionManager.TryAdd(jT1078HttpContext); await jT1078HttpContext.WebSocketSendHelloAsync(); await Task.Factory.StartNew(async(state) => { //https://www.bejson.com/httputil/websocket/ //ws://localhost:15555?token=22&sim=1221&channel=1 var websocketContext = state as JT1078HttpContext; while (websocketContext.WebSocketContext.WebSocket.State == WebSocketState.Open || websocketContext.WebSocketContext.WebSocket.State == WebSocketState.Connecting) { var buffer = ArrayPool <byte> .Shared.Rent(256); try { //客户端主动断开需要有个线程去接收通知,不然会客户端会卡死直到超时 WebSocketReceiveResult receiveResult = await websocketContext.WebSocketContext.WebSocket.ReceiveAsync(buffer, CancellationToken.None); if (receiveResult.EndOfMessage) { if (receiveResult.Count > 0) { var data = buffer.AsSpan().Slice(0, receiveResult.Count).ToArray(); if (Logger.IsEnabled(LogLevel.Trace)) { Logger.LogTrace($"[ws receive]:{Encoding.UTF8.GetString(data)}"); } await websocketContext.WebSocketSendTextAsync(data); } } } finally { ArrayPool <byte> .Shared.Return(buffer); } } if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogInformation($"[ws close]:{websocketContext.SessionId}-{websocketContext.Sim}-{websocketContext.ChannelNo}-{websocketContext.StartTime:yyyyMMddhhmmss}"); } SessionManager.TryRemove(websocketContext.SessionId); }, jT1078HttpContext); } else { var jT1078HttpContext = new JT1078HttpContext(context, principal); jT1078HttpContext.Sim = sim; jT1078HttpContext.ChannelNo = channelNo; SessionManager.TryAdd(jT1078HttpContext); } }
/// <summary> /// 处理hls实时视频请求 /// </summary> /// <param name="context"></param> /// <param name="principal"></param> /// <param name="jT1078AVInfo"></param> public async void HandleHlsRequest(HttpListenerContext context, IPrincipal principal, JT1078AVInfo jT1078AVInfo) { string filename = Path.GetFileName(context.Request.Url.AbsolutePath.ToString()); string filepath = Path.Combine(Configuration.HlsRootDirectory, jT1078AVInfo.ToString(), filename); if (hLSPathStorage.ExsitPath(filepath)) { try { using (FileStream sr = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { if (filename.Contains("m3u8")) { await context.HttpM3U8Async(sr); } else if (filename.Contains("ts")) { await context.HttpTsAsync(sr); } else { context.Http404(); } } } catch (Exception ex) { Logger.LogError(ex, ex.Message); context.Http404(); } } else { if (!File.Exists(filepath)) { if (filename.ToLower().Contains("m3u8")) { var directory = Path.Combine(Configuration.HlsRootDirectory, jT1078AVInfo.ToString()); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } if (!hLSPathStorage.ExistFileSystemWatcher(directory)) { var fileSystemWatcher = new FileSystemWatcher(); fileSystemWatcher.Path = directory; fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite; //NotifyFilters.CreateTime fileSystemWatcher.Filter = "*.m3u8"; // Only watch text files. fileSystemWatcher.Changed += async(sender, arg) => { if (context.Response.ContentLength64 != 0) { return; } //wwwroot\1234_2\live.m3u8 //var key = arg.FullPath.Replace(arg.Name, "").Substring(arg.FullPath.Replace(arg.Name, "").IndexOf("\\")).Replace("\\", ""); var key = arg.FullPath.Substring(arg.FullPath.IndexOf("\\") + 1, (arg.FullPath.LastIndexOf("\\") - arg.FullPath.IndexOf("\\")) - 1); var sim = key.Split("_")[0]; var channel = int.Parse(key.Split("_")[1]); try { using (FileStream sr = new FileStream(arg.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { hLSPathStorage.AddPath(arg.FullPath, key); await context.HttpM3U8Async(sr); } } catch (Exception ex) { Logger.LogError(ex, $"{context.Request.Url}"); context.Http404(); } finally { hLSPathStorage.DeleteFileSystemWatcher(directory); } }; fileSystemWatcher.EnableRaisingEvents = true; // Begin watching. hLSPathStorage.AddFileSystemWatcher(directory, fileSystemWatcher); } } else { context.Http404(); return; } } else { hLSPathStorage.AddPath(filepath, jT1078AVInfo.ToString()); using (FileStream sr = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { if (filename.Contains("m3u8")) { await context.HttpM3U8Async(sr); } else if (filename.Contains("ts")) { await context.HttpTsAsync(sr); } else { context.Http404(); } } } var jT1078HttpContext = new JT1078HttpContext(context, principal); jT1078HttpContext.Sim = jT1078AVInfo.Sim; jT1078HttpContext.ChannelNo = jT1078AVInfo.ChannelNo; jT1078HttpContext.RTPVideoType = RTPVideoType.Http_Hls; HttpSessionManager.AddOrUpdateHlsSession(jT1078HttpContext); } }
/// <summary> /// 处理hls实时视频请求 /// </summary> /// <param name="context"></param> /// <param name="principal"></param> public async void HandleHlsRequest(HttpListenerContext context, IPrincipal principal) { if (context.Request.QueryString.Count < 2) { context.Http404(); return; } string sim = context.Request.QueryString.Get("sim"); //终端sim卡号 string channelNo = context.Request.QueryString.Get("channelNo"); //通道号 string key = $"{sim}_{channelNo}"; string filename = Path.GetFileName(context.Request.Url.AbsolutePath.ToString()); string filepath = Path.Combine(Configuration.HlsRootDirectory, key, filename); if (!File.Exists(filepath)) { if (filename.ToLower().Contains("m3u8")) { fileSystemWatcher = new FileSystemWatcher(); fileSystemWatcher.Path = Path.Combine(Configuration.HlsRootDirectory, key); fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite; //NotifyFilters.CreateTime fileSystemWatcher.Filter = "*.m3u8"; // Only watch text files. fileSystemWatcher.Changed += (sender, arg) => { if (context.Response.ContentLength64 != 0) { return; } //wwwroot\1234_2\live.m3u8 //var key = arg.FullPath.Replace(arg.Name, "").Substring(arg.FullPath.Replace(arg.Name, "").IndexOf("\\")).Replace("\\", ""); var key = arg.FullPath.Substring(arg.FullPath.IndexOf("\\") + 1, arg.FullPath.LastIndexOf("\\")); var sim = key.Split("_")[0]; var channel = int.Parse(key.Split("_")[1]); try { using (FileStream sr = new FileStream(arg.FullPath, FileMode.Open)) { context.Response.ContentType = m3u8Mime; context.Response.StatusCode = (int)HttpStatusCode.OK; context.Response.ContentLength64 = sr.Length; sr.CopyTo(context.Response.OutputStream); } } catch (Exception ex) { Logger.LogError(ex, $"{context.Request.Url}"); } finally { context.Response.OutputStream.Close(); context.Response.Close(); } }; fileSystemWatcher.EnableRaisingEvents = true; // Begin watching. } else { context.Http404(); return; } } else { try { using (FileStream sr = new FileStream(filepath, FileMode.Open)) { if (filename.ToLower().Contains("m3u8")) { context.Response.ContentType = m3u8Mime; } else { context.Response.ContentType = tsMime; } context.Response.StatusCode = (int)HttpStatusCode.OK; context.Response.ContentLength64 = sr.Length; await sr.CopyToAsync(context.Response.OutputStream); } } catch (Exception ex) { Logger.LogError(ex, $"{context.Request.Url}"); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } finally { context.Response.OutputStream.Close(); context.Response.Close(); } } var jT1078HttpContext = new JT1078HttpContext(context, principal); jT1078HttpContext.Sim = sim; jT1078HttpContext.ChannelNo = int.Parse(channelNo); jT1078HttpContext.RTPVideoType = RTPVideoType.Http_Hls; HttpSessionManager.AddOrUpdate(jT1078HttpContext); }
private async ValueTask ProcessRequestAsync(HttpListenerContext context, IPrincipal principal) { if (context.Request.RawUrl.StartsWith("/favicon.ico")) { Http404(context); } if (Logger.IsEnabled(LogLevel.Trace)) { Logger.LogTrace($"[http RequestTraceIdentifier]:{context.Request.RequestTraceIdentifier.ToString()}-{context.Request.RemoteEndPoint.ToString()}"); } string sim = context.Request.QueryString.Get("sim"); string channel = context.Request.QueryString.Get("channel"); if (string.IsNullOrEmpty(sim) || string.IsNullOrEmpty(channel)) { await Http400(context); return; } int.TryParse(channel, out int channelNo); if (context.Request.IsWebSocketRequest) { HttpListenerWebSocketContext wsContext = await context.AcceptWebSocketAsync(null); var jT1078HttpContext = new JT1078HttpContext(context, wsContext, principal); jT1078HttpContext.Sim = sim; jT1078HttpContext.ChannelNo = channelNo; //todo: add session manager await wsContext.WebSocket.SendAsync(Encoding.UTF8.GetBytes("hello,jt1078"), WebSocketMessageType.Text, true, CancellationToken.None); await Task.Factory.StartNew(async(state) => { //https://www.bejson.com/httputil/websocket/ //ws://127.0.0.1:15555?token=22&sim=1221&channel=1 var websocketContext = state as JT1078HttpContext; while (websocketContext.WebSocketContext.WebSocket.State == WebSocketState.Open || websocketContext.WebSocketContext.WebSocket.State == WebSocketState.Connecting) { var buffer = ArrayPool <byte> .Shared.Rent(256); try { WebSocketReceiveResult receiveResult = await websocketContext.WebSocketContext.WebSocket.ReceiveAsync(buffer, CancellationToken.None); if (receiveResult.EndOfMessage) { if (receiveResult.Count > 0) { var data = buffer.AsSpan().Slice(0, receiveResult.Count).ToArray(); if (Logger.IsEnabled(LogLevel.Trace)) { Logger.LogTrace($"[ws receive]:{Encoding.UTF8.GetString(data)}"); } await websocketContext.WebSocketContext.WebSocket.SendAsync(data, WebSocketMessageType.Text, true, CancellationToken.None); } } } finally { ArrayPool <byte> .Shared.Return(buffer); } } if (Logger.IsEnabled(LogLevel.Trace)) { Logger.LogTrace($"[ws close]:{websocketContext}"); } //todo:session close notice await websocketContext.WebSocketContext.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "normal", CancellationToken.None); }, jT1078HttpContext); } else { var jT1078HttpContext = new JT1078HttpContext(context, principal); jT1078HttpContext.Sim = sim; jT1078HttpContext.ChannelNo = channelNo; //todo:add session manager //todo:set http chunk //todo:session close notice byte[] b = Encoding.UTF8.GetBytes("ack"); context.Response.StatusCode = 200; context.Response.KeepAlive = true; context.Response.ContentLength64 = b.Length; await context.Response.OutputStream.WriteAsync(b, 0, b.Length); context.Response.Close(); } }
public static async ValueTask WebSocketSendHelloAsync(this JT1078HttpContext context) { await context.WebSocketContext.WebSocket.SendAsync(Hello, WebSocketMessageType.Text, true, CancellationToken.None); }
public static async ValueTask WebSocketSendBinaryAsync(this JT1078HttpContext context, ReadOnlyMemory <byte> buffer) { await context.WebSocketContext.WebSocket.SendAsync(buffer, WebSocketMessageType.Binary, true, CancellationToken.None); }
public static async ValueTask WebSocketClose(this JT1078HttpContext context, string content) { await context.WebSocketContext.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, content, CancellationToken.None); }
public static async ValueTask HttpSendChunked(this JT1078HttpContext context, ReadOnlyMemory <byte> buffer) { context.Context.Response.StatusCode = (int)HttpStatusCode.OK; await context.Context.Response.OutputStream.WriteAsync(buffer); }