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

Raman Shuishniou rommer at ibuffed.com
Sat Feb 17 10:08:59 UTC 2018


16.02.2018 23:50, Georg Chini пишет:
> 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.)

Switching from pipe to silence can be easily done by generation a little 
less silence in first iteration. It's not working when switching from 
silence to pipe.

Actually we don't know if the writer can buffer data and/or trying to do 
some congession control. When the pipe have some data - source must read 
and post it immediately.

While switching from silence to pipe we can wait until we run out of 
silence, but pipe has data to post right here and right now. For 
example, if the latency is 20ms and we wait for 10ms before starting the 
first read, the writer will just write more data to pipe while we 
waiting (+10ms). We will read and post that extra data on the next 
iteration.

So we need to drop extra data from pipe or buffer the head of data each 
iteration. Both solutions are bad. I think we'll just give the 
source-output(s) a chance to deal with the extra silence we generated.


> 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