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
}

_______________________________________________
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss

Reply via email to