public override async Task Open(CancellationToken openCancellationToken) { LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested(); var mediaSource = OriginalMediaSource; var url = mediaSource.Path; Directory.CreateDirectory(Path.GetDirectoryName(TempFilePath) ?? throw new InvalidOperationException("Path can't be a root directory.")); var typeName = GetType().Name; Logger.LogInformation("Opening {StreamType} Live stream from {Url}", typeName, url); // Response stream is disposed manually. var response = await _httpClientFactory.CreateClient(NamedClient.Default) .GetAsync(url, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None) .ConfigureAwait(false); var contentType = response.Content.Headers.ContentType?.ToString() ?? string.Empty; if (contentType.Contains("matroska", StringComparison.OrdinalIgnoreCase) || contentType.Contains("mp4", StringComparison.OrdinalIgnoreCase) || contentType.Contains("dash", StringComparison.OrdinalIgnoreCase) || contentType.Contains("mpegURL", StringComparison.OrdinalIgnoreCase) || contentType.Contains("text/", StringComparison.OrdinalIgnoreCase)) { // Close the stream without any sharing features response.Dispose(); return; } SetTempFilePath("ts"); var taskCompletionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); _ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token); // OpenedMediaSource.Protocol = MediaProtocol.File; // OpenedMediaSource.Path = tempFile; // OpenedMediaSource.ReadAtNativeFramerate = true; MediaSource.Path = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; MediaSource.Protocol = MediaProtocol.Http; // OpenedMediaSource.Path = TempFilePath; // OpenedMediaSource.Protocol = MediaProtocol.File; // OpenedMediaSource.Path = _tempFilePath; // OpenedMediaSource.Protocol = MediaProtocol.File; // OpenedMediaSource.SupportsDirectPlay = false; // OpenedMediaSource.SupportsDirectStream = true; // OpenedMediaSource.SupportsTranscoding = true; var res = await taskCompletionSource.Task.ConfigureAwait(false); if (!res) { Logger.LogWarning("Zero bytes copied from stream {StreamType} to {FilePath} but no exception raised", GetType().Name, TempFilePath); throw new EndOfStreamException(string.Format(CultureInfo.InvariantCulture, "Zero bytes copied from stream {0}", GetType().Name)); } }
private void RegisterServerEndpoints() { var udn = CreateUuid(_appHost.SystemId); var descriptorUri = "/dlna/" + udn + "/description.xml"; var bindAddresses = NetworkManager.CreateCollection( _networkManager.GetInternalBindAddresses() .Where(i => i.AddressFamily == AddressFamily.InterNetwork || (i.AddressFamily == AddressFamily.InterNetworkV6 && i.Address.ScopeId != 0))); if (bindAddresses.Count == 0) { // No interfaces returned, so use loopback. bindAddresses = _networkManager.GetLoopbacks(); } foreach (IPNetAddress address in bindAddresses) { if (address.AddressFamily == AddressFamily.InterNetworkV6) { // Not supporting IPv6 right now continue; } // Limit to LAN addresses only if (!_networkManager.IsInLocalNetwork(address)) { continue; } var fullService = "urn:schemas-upnp-org:device:MediaServer:1"; _logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, address); var uri = new UriBuilder(_appHost.GetApiUrlForLocalAccess(false) + descriptorUri); var device = new SsdpRootDevice { CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info. Location = uri.Uri, // Must point to the URL that serves your devices UPnP description document. Address = address.Address, PrefixLength = address.PrefixLength, FriendlyName = "Jellyfin", Manufacturer = "Jellyfin", ModelName = "Jellyfin Server", Uuid = udn // This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc. }; SetProperies(device, fullService); _publisher.AddDevice(device); var embeddedDevices = new[] { "urn:schemas-upnp-org:service:ContentDirectory:1", "urn:schemas-upnp-org:service:ConnectionManager:1", // "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1" }; foreach (var subDevice in embeddedDevices) { var embeddedDevice = new SsdpEmbeddedDevice { FriendlyName = device.FriendlyName, Manufacturer = device.Manufacturer, ModelName = device.ModelName, Uuid = udn // This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc. }; SetProperies(embeddedDevice, subDevice); device.AddDevice(embeddedDevice); } } }
private async Task <IEnumerable <MediaSourceInfo> > GetMediaSourcesInternal(BaseItem item, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken) { IEnumerable <MediaSourceInfo> sources; var forceRequireOpening = false; try { if (activeRecordingInfo != null) { sources = await EmbyTV.EmbyTV.Current.GetRecordingStreamMediaSources(activeRecordingInfo, cancellationToken) .ConfigureAwait(false); } else { sources = await _liveTvManager.GetChannelMediaSources(item, cancellationToken) .ConfigureAwait(false); } } catch (NotImplementedException) { sources = _mediaSourceManager.GetStaticMediaSources(item, false); forceRequireOpening = true; } var list = sources.ToList(); foreach (var source in list) { source.Type = MediaSourceType.Default; source.BufferMs ??= 1500; if (source.RequiresOpening || forceRequireOpening) { source.RequiresOpening = true; } if (source.RequiresOpening) { var openKeys = new List <string> { item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), source.Id ?? string.Empty }; source.OpenToken = string.Join(StreamIdDelimiter, openKeys); } // Dummy this up so that direct play checks can still run if (string.IsNullOrEmpty(source.Path) && source.Protocol == MediaProtocol.Http) { source.Path = _appHost.GetApiUrlForLocalAccess(); } } _logger.LogDebug("MediaSources: {@MediaSources}", list); return(list); }