[pulseaudio-discuss] [PATCH v6 1/2] pipe-source: generate silence when no writers connected

Georg Chini georg at chini.tk
Fri Feb 16 20:50:21 UTC 2018


On 16.02.2018 17:37, Raman Shishniou wrote:
>
> On 02/16/2018 12:00 PM, Georg Chini wrote:
>> On 14.02.2018 23:16, Raman Shyshniou wrote:
>>> Currently the pipe-source does not produce any data if no
>>> writer is connected. This patch enable silence generator
>>> when last writer closed pipe. It will stop automatically
>>> when any data appears.
>>> ---
>> After my fixes to module-null-source, I think your logic is not yet
>> completely correct. I would propose to do it like that:
>>
>> In source_process_msg():
>>
>>            case PA_SOURCE_MESSAGE_GET_LATENCY:
>>                 current_latency = now - time stamp
>>                 if (u->corkfd < 0)
>>                     current_latency += data in pipe
>>
>>            case PA_SOURCE_MESSAGE_SET_STATE:
>>                 if (SUSPENDED or INIT -> IDLE || SUSPENDED or INIT -> RUNNING) {
>>                     get time stamp
>>                     u->starting = true
>>                 }
>>
>> In thread_func():
>>
>> close u->corkfd
>> u->corkfd = -1
>> u->starting = true
>>
>> timer_elapsed = false
>> revents = 0
>> get time stamp
>>
>> for (;;) {
>>      if (source is open) {
>>
>>          /* We have to wait at least one configured source latency before starting
>>           * to read data */
>>          if (revents & POLLIN && !u->starting) {
>>              read data from pipe
>>              if (u->corkfd >=0) {
>>                  close corkfd
>>                  u->corkfd = -1
>>             }
>>
>>          } else if (timer_elapsed && u->corkfd > 0)
>>              generate silence
>>
>>         if (data was read/generated) {
>>              post data
>>              time stamp += data written
>>         }
>>
>>         set timer absolute time stamp + configured (or fixed) source latency
>>     } else
>>         set timer disabled
>>
>>     run rtpoll
>>     get timer_elapsed
>>
>>      if (u->starting && timer_elapsed)
>>          u->starting = false
>>
>>     if (revents & POLLHUP) {
>>         open pipe for writing
>>         u->corkfd = write file descriptor
>>         revents = revents & ~POLLHUP
>>     }
>>
>>     error check
>> }
>>
>> You can also add a source_update_requested_latency_cb() like
>> in module-null-source and pass  PA_SOURCE_DYNAMIC_LATENCY
>> to pa_source_new() to make the latency configurable.
>>
>> I hope I did not forget anything ...
> The incoming data can has a sample rate that differs from the system clock.
> For example, due to imperfect hardware oscillator. It's a bad idea to expect
> a data at the system clock rate. When the source is receiving a real data from
> pipe the timer should be disabled and u->timestamp and u->starting doesn't make sense.

You don't rely on the timer when data is coming from the pipe. The
rtpollrun will return as soon as data is available in the pipe and then
the timer expiration time will be moved forward.
You are right, if the clocks do not match, the timer will expire sooner
or later, before data is available in the pipe. This does not matter
however because there is no data available and corkfd is < 0, it
will just be an iteration without any action.
You can anyway not expect that the thread is only woken up when
the timer expires or something happens on the pipe side. The
thread is also woken up when messages are sent to it.

I agree that the timer can be disabled and that the time stamp
gets meaningless the way it is set now when reading from the
pipe. But we must continue without interruption when we switch
from pipe to silence, so we should set the time stamp to
something meaningful before switching to silence. (Switching
between pipe and silence properly would not have worked with
my code above.)
Also I still believe that waiting one pipe fill time before reading
the first data from the pipe will increase stability because then
there is a larger buffer. When switching between pipe and silence,
we have to wait until pipe data or silence runs out, otherwise we will
supply too many samples. I have made another attempt on some
pseudo-code, hope it is better this time:

for (;;) {
     if (source is open) {

         /* We wait one configured source latency before starting
          * to read data to improve stability */
         if (revents & POLLIN && !u->starting) {

             if (u->corkfd >=0) {
                 close corkfd
                 u->corkfd = -1
                 /* Let's wait until we run out of silence before
                  * sending pipe data */
                 u->starting = true
            } else {
                 read data from pipe
                 /* This is the time when we will run out of data */
                 time stamp = now + data to write
           }

        } else if (timer_elapsed && u->corkfd >= 0) {
             generate silence (amount now - timestamp)
             time stamp += data to write
        }

        if (data was read/generated)
            post data

        if (starting || u->corkfd >= 0)
            set timer absolute time stamp + fixed source latency
        else
           set timer disabled

    } else
        set timer disabled

    run rtpoll
    get timer_elapsed

     if (u->starting && timer_elapsed)
         u->starting = false

    if (revents & POLLHUP && u->corkfd < 0) {
        open pipe for writing
        u->corkfd = write file descriptor
        revents = revents & ~(POLLHUP|POLLIN)
        timer_elapsed = true

        /* Time left to play for remaining pipe data */
        delta = 0
        if (time stamp > now)
            delta = timestamp - now

        /* If there is some data left in the pipe, we could post
         * it here and add the amount to delta */

        time stamp = now + delta - fixed source latency
    }

    error check
}



More information about the pulseaudio-discuss mailing list