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
}

_______________________________________________
pulseaudio-discuss mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss

Reply via email to