/// <summary> /// Opens the specified media Asynchronously /// </summary> /// <param name="uri">The URI.</param> /// <returns></returns> private async Task OpenAsync(Uri uri) { try { await Task.Run(() => { var mediaUrl = uri.IsFile ? uri.LocalPath : uri.ToString(); Container = new MediaContainer(mediaUrl); RaiseMediaOpeningEvent(); Container.Log(MediaLogMessageType.Debug, $"{nameof(OpenAsync)}: Entered"); Container.Initialize(); }); foreach (var t in Container.Components.MediaTypes) { Blocks[t] = new MediaBlockBuffer(MaxBlocks[t], t); Frames[t] = new MediaFrameQueue(); LastRenderTime[t] = TimeSpan.MinValue; Renderers[t] = CreateRenderer(t); } IsTaskCancellationPending = false; BlockRenderingCycle.Set(); FrameDecodingCycle.Set(); PacketReadingCycle.Set(); PacketReadingTask = new Thread(RunPacketReadingWorker) { IsBackground = true }; FrameDecodingTask = new Thread(RunFrameDecodingWorker) { IsBackground = true }; BlockRenderingTask = new Thread(RunBlockRenderingWorker) { IsBackground = true }; PacketReadingTask.Start(); FrameDecodingTask.Start(); BlockRenderingTask.Start(); RaiseMediaOpenedEvent(); if (LoadedBehavior == MediaState.Play) { Play(); } } catch (Exception ex) { RaiseMediaFailedEvent(ex); } finally { UpdateMediaProperties(); Container.Log(MediaLogMessageType.Debug, $"{nameof(OpenAsync)}: Completed"); } }
/// <summary> /// Runs the read task which keeps a packet buffer as full as possible. /// It reports on DownloadProgress by enqueueing an update to the property /// in order to avoid any kind of disruption to this thread caused by the UI thread. /// </summary> internal void RunPacketReadingWorker() { try { // Holds the packet count for each read cycle var packetsRead = new MediaTypeDictionary <int>(); // State variables for media types var t = MediaType.None; // Store Container in local variable to prevent NullReferenceException // when dispose occurs sametime with read cycle var mediaContainer = Container; var main = mediaContainer.Components.Main.MediaType; var auxs = mediaContainer.Components.MediaTypes.Where(c => c != main && (c == MediaType.Audio || c == MediaType.Video)).ToArray(); var all = auxs.Union(new[] { main }).ToArray(); // State variables for bytes read (give-up condition) var startBytesRead = 0UL; var currentBytesRead = 0UL; // Worker logic begins here while (IsTaskCancellationPending == false) { // Enter a read cycle SeekingDone?.WaitOne(); // Enter a packet reading cycle PacketReadingCycle?.Reset(); if (CanReadMorePackets && mediaContainer.Components.PacketBufferLength < DownloadCacheLength) { // Initialize Packets read to 0 for each component and state variables foreach (var k in mediaContainer.Components.MediaTypes) { packetsRead[k] = 0; } startBytesRead = mediaContainer.Components.TotalBytesRead; currentBytesRead = 0UL; // Start to perform the read loop while (CanReadMorePackets) { // Perform a packet read. t will hold the packet type. t = mediaContainer.Read(); // Discard packets that we don't need (i.e. MediaType == None) if (mediaContainer.Components.MediaTypes.Contains(t) == false) { continue; } // Update the packet count for the components packetsRead[t] += 1; // Ensure we have read at least some packets from main and auxiliary streams. if (packetsRead.Where(k => all.Contains(k.Key)).All(c => c.Value > 0)) { break; } // The give-up condition is that in spite of efforts to read at least one of each, // we could not find the required packet types. currentBytesRead = mediaContainer.Components.TotalBytesRead - startBytesRead; if (currentBytesRead > (ulong)DownloadCacheLength) { break; } } } // finish the reading cycle. PacketReadingCycle?.Set(); // Wait some if we have a full packet buffer or we are unable to read more packets (i.e. EOF). if (mediaContainer.Components.PacketBufferLength >= DownloadCacheLength || CanReadMorePackets == false || currentBytesRead <= 0) { Task.Delay(1).GetAwaiter().GetResult(); } } } catch (ThreadAbortException) { } finally { // Always exit notifying the reading cycle is done. PacketReadingCycle?.Set(); } }
/// <summary> /// Runs the read task which keeps a packet buffer as full as possible. /// It reports on DownloadProgress by enqueueing an update to the property /// in order to avoid any kind of disruption to this thread caused by the UI thread. /// </summary> internal void RunPacketReadingWorker() { // Holds the packet count for each read cycle var packetsRead = new MediaTypeDictionary <int>(); // State variables for media types var t = MediaType.None; // Store Container in local variable to prevent NullReferenceException // when dispose occurs sametime with read cycle var mediaContainer = Container; var main = mediaContainer.Components.Main.MediaType; var auxs = mediaContainer.Components.MediaTypes.FundamentalAuxsFor(main); var all = main.JoinMediaTypes(auxs); try { // Worker logic begins here while (IsTaskCancellationPending == false) { // Wait for seeking to be done. SeekingDone.WaitOne(); // Enter a packet reading cycle PacketReadingCycle.Reset(); // Initialize Packets read to 0 for each component and state variables foreach (var k in mediaContainer.Components.MediaTypes) { packetsRead[k] = 0; } // Start to perform the read loop // NOTE: Disrupting the packet reader causes errors in UPD streams. Disrupt as little as possible while (CanReadMorePackets && ShouldReadMorePackets && IsTaskCancellationPending == false) { // Perform a packet read. t will hold the packet type. t = mediaContainer.Read(); // Discard packets that we don't need (i.e. MediaType == None) if (mediaContainer.Components.MediaTypes.HasMediaType(t) == false) { continue; } // Update the packet count for the components packetsRead[t] += 1; // Ensure we have read at least some packets from main and auxiliary streams. if (packetsRead.FundamentalsGreaterThan(0)) { break; } } // finish the reading cycle. PacketReadingCycle.Set(); // Don't evaluate a pause condition if we are seeking if (SeekingDone.IsSet() == false) { continue; } // Wait some if we have a full packet buffer or we are unable to read more packets (i.e. EOF). if (ShouldReadMorePackets == false || CanReadMorePackets == false || packetsRead.GetSum() <= 0) { Task.Delay(1).GetAwaiter().GetResult(); } } } catch (ThreadAbortException) { /* swallow */ } catch { if (!IsDisposed) { throw; } } finally { // Always exit notifying the reading cycle is done. PacketReadingCycle.Set(); } }