public static void poll(ReadOnlySpan <pollfd> waitHandles, out eDecoderBits decoderBits, out eAudioBits audio, out bool seekRequest, out bool shutdownRequest) { LibC.poll(waitHandles); decoderBits = (eDecoderBits)waitHandles[0].revents; audio = (eAudioBits)waitHandles[1].revents; ePollEvents seek = waitHandles[2].revents; ePollEvents shutdown = waitHandles[3].revents; if (decoderBits.HasFlag(eDecoderBits.Error)) { throw new ApplicationException("poll(3) returned error status for the decoder device, probably the decoder failed"); } if (audio.HasFlag(eAudioBits.Error)) { throw new ApplicationException("poll(3) returned error status for the audio queue"); } if (seek.HasFlag(ePollEvents.POLLERR)) { throw new ApplicationException("poll(3) returned error status for the seek event handle"); } if (shutdown.HasFlag(ePollEvents.POLLERR)) { throw new ApplicationException("poll(3) returned error status for the shutdown event handle"); } seekRequest = seek.HasFlag(ePollEvents.POLLIN); shutdownRequest = shutdown.HasFlag(ePollEvents.POLLIN); }
void runThread() { int playerHandlesCount = render.pollHandlesCount; Span <pollfd> waitHandles = stackalloc pollfd[playerHandlesCount + 3]; render.setupPollHandles(waitHandles.Slice(0, playerHandlesCount)); setupInternalHandles(waitHandles, playerHandlesCount); while (true) { LibC.poll(waitHandles); if (waitHandles[playerHandlesCount + 1].revents.HasFlag(ePollEvents.POLLIN)) { // Asked to shut down return; } if (waitHandles[playerHandlesCount + 2].revents.HasFlag(ePollEvents.POLLIN)) { // Got a seek event if (!handleSeek()) { return; } Logger.logDebug("Audio thread seek complete"); continue; } if (waitHandles[playerHandlesCount].revents.HasFlag(ePollEvents.POLLIN)) { // Got a buffer with encoded frame. // Dequeue from Linux kernel so the poll() no longer triggers the data available bit for that particular frame, enqueue into the thread-local managed-only queue. // var d = queues.dequeueEncoded(); // encodedFrames.Enqueue( d ); pendingQueue.enqueue(); } render.handlePollResult(this, waitHandles); } }
bool handleSeek() { // Get seek destination timestamp TimeSpan seekDest; lock (this) { if (!seekDestination.HasValue) { throw new ApplicationException("Seek event was set, but no destination timestamp"); } seekEventHandle.reset(); seekDest = seekDestination.Value; } // Stop playing, drop data in the ALSA queue render.beginSeek(); pendingQueue.discardAndFlush(); // Tell decoder thread it can now send new samples m_clock?.drainComplete(); // Wait for video decoder to catch up if (!m_clock.waitForVideoFrame()) { return(false); } // Run the poll waiting for that special frame Span <pollfd> waitHandles = stackalloc pollfd[3]; setupInternalHandles(waitHandles, 0); bool foundTarget = false; while (true) { LibC.poll(waitHandles); if (waitHandles[1].revents.HasFlag(ePollEvents.POLLIN)) { // Asked to shut down return(false); } if (waitHandles[2].revents.HasFlag(ePollEvents.POLLIN)) { // Got another seek event.. Logger.logWarning("Multiple seeks are not supported, probably won’t work"); lock (this) { if (!seekDestination.HasValue) { throw new ApplicationException("Seek event was set, but no destination timestamp"); } seekEventHandle.reset(); seekDest = seekDestination.Value; seekDestination = null; } } if (waitHandles[0].revents.HasFlag(ePollEvents.POLLIN)) { // Got a buffer with encoded frame. var d = pendingQueue.queues.dequeueEncoded(); if (foundTarget) { pendingQueue.enqueue(d); if (fillBuffer()) { return(true); } } else { if (d.timestamp != seekDest) { Logger.logVerbose("Audio thread got sample {0}, needs {1}", d.timestamp, seekDest); // Not the one we're looking for pendingQueue.queues.enqueueEmpty(d.index); continue; } // Received seek destination frame. Logger.logVerbose("Audio thread got the destination audio sample after seek, preparing to resume the playback"); pendingQueue.enqueue(d); render.prepareEndSeek(); foundTarget = true; if (fillBuffer()) { return(true); } } } } }