Discussion:
[Live-devel] doGetNextFrame interval
James Heliker
2014-10-10 03:45:09 UTC
Permalink
Hi Ross / All -

In my AudioBufferSource (based on AudioInputDevice) the doGetNextFrame
happens at an extremely fast interval - which is sometimes causing my PCM
audio running at 44.1 kHz to get fragmented.

For example, I am tuning my server and client software to work with 64
(pcm)frame chunks, however now and then a 20 frame chunk gets out the door
as my ASIO device isn't done writing a chunk to my circular buffer.

Is there any way to turn the doGetNextFrame interval down / slow it down?
Would that have unintended side effects that I'm not considering?

Any advice would be very much appreciated. I've got my latency where I need
it to be, but it appears these fragmented chunks are causing the remaining
audible glitches that I'm battling every few seconds.

Thanks for your help!!

- James
Ross Finlayson
2014-10-10 04:25:14 UTC
Permalink
> In my AudioBufferSource (based on AudioInputDevice) the doGetNextFrame happens at an extremely fast interval - which is sometimes causing my PCM audio running at 44.1 kHz to get fragmented.

The frequency at which "doGetNextFrame()" gets called depends entirely on the value that you set for "fDurationInMicroseconds". If you set this appropriately
i.e., fDurationInMicroseconds = (numSamplesDelivered*1000000)/samplesPerSecond
then "doGetNextFrame()" will get called at the appropriate frequency.

Alternatively, if you leave "fDurationInMicroseconds" at its default value of zero, then "doGetNextFrame()" will get called again immediately after you complete delivery of the previous frame, but that's OK, provided that you don't actually complete the delivery (i.e., call "FramedSource::afterGetting()") until you have accumulated however many samples you want to deliver each time. (Of course, you shouldn't 'block' or 'spin' waiting for this to happen.)


Ross Finlayson
Live Networks, Inc.
http://www.live555.com/
James Heliker
2014-10-10 21:26:20 UTC
Permalink
Hi Ross -

Thanks so much for getting back to me!

I see what you mean, and fDurationInMicroseconds is being calculated
correctly. For my fragmentation issue, If I don't call afterGetting() then
doGetNextFrame() is not ever re-invoked, so the server essentially stops on
the first partial read.

Is there a method or variable I should set to indicate to Live555 that I
want to continue accumulating data?

- James

On Thu, Oct 9, 2014 at 9:25 PM, Ross Finlayson <***@live555.com>
wrote:

> In my AudioBufferSource (based on AudioInputDevice) the doGetNextFrame
> happens at an extremely fast interval - which is sometimes causing my PCM
> audio running at 44.1 kHz to get fragmented.
>
>
> The frequency at which "doGetNextFrame()" gets called depends entirely on
> the value that you set for "fDurationInMicroseconds". If you set this
> appropriately
> i.e., fDurationInMicroseconds =
> (numSamplesDelivered*1000000)/samplesPerSecond
> then "doGetNextFrame()" will get called at the appropriate frequency.
>
> Alternatively, if you leave "fDurationInMicroseconds" at its default value
> of zero, then "doGetNextFrame()" will get called again immediately after
> you complete delivery of the previous frame, but that's OK, provided that
> you don't actually complete the delivery (i.e., call
> "FramedSource::afterGetting()") until you have accumulated however many
> samples you want to deliver each time. (Of course, you shouldn't 'block'
> or 'spin' waiting for this to happen.)
>
>
> Ross Finlayson
> Live Networks, Inc.
> http://www.live555.com/
>
>
> _______________________________________________
> live-devel mailing list
> live-***@lists.live555.com
> http://lists.live555.com/mailman/listinfo/live-devel
>
>
Ross Finlayson
2014-10-11 15:14:17 UTC
Permalink
> I see what you mean, and fDurationInMicroseconds is being calculated correctly. For my fragmentation issue, If I don't call afterGetting() then doGetNextFrame() is not ever re-invoked, so the server essentially stops on the first partial read.
>
> Is there a method or variable I should set to indicate to Live555 that I want to continue accumulating data?

Your "doGetNextFrame()" implementation should call "FramedSource::afterGetting()" *only* when you have a complete frame that you want to deliver to the downstream object. If you don't yet have a complete frame, then simply return, without calling "FramedSource::afterGetting()". If you do that, then "doGetNextFrame()" won't get called again in the meantime.

But, you're probably thinking, if "doGetNextFrame()" simply returns without completing delivery of a frame, and doesn't get called again, then how can I figure out when I later get enough data to deliver? Basically, that has to be an 'event' that gets handled within the event loop. One way to do this is by 'polling' - i.e., by scheduling a periodic delayed task (using "TaskScheduler::scheduleDelayedTask()") that checks whether you have enough data (and then calls "FramedSource::afterGetting()" when you do have enough data). Another way is to have a separate thread that's gathering data, and then have this thread use an 'event trigger' (by calling "TaskScheduler::triggerEvent()") to signal when data is ready to be delivered. For an example of this, see the 'DeviceSource' code in "liveMedia/DeviceSource.cpp". (Note that if you do this, then "TaskScheduler::triggerEvent()" is the *only* LIVE555 function that the separate 'data gathering' thread is allowed to call; see <http://www.live555.com/liveMedia/faq.html#threads>).


Ross Finlayson
Live Networks, Inc.
http://www.live555.com/
Krishna Patel
2014-10-11 22:10:29 UTC
Permalink
Hi,
Live555 client drops connection after 90 seconds of receiving RTSP stream from Wowza Streaming Server 4.0.6 over HTTP tunneling protocol. Socket operation fail with WSAECONNABORTED error (An established connection was aborted by the software in your host machine.) Receiving the stream over TCP works just fine. This behavior can be seen with openRTSP v2014.10.07:

openRTSP -T 80 rtsp://q1604.dnsdojo.com:1935/live/sys1.stream

Thanks,
Krishna.
Ross Finlayson
2014-10-11 22:41:36 UTC
Permalink
You'll need to contact 'Wowza' about this. The problem appears to be with their server.

Ross Finlayson
Live Networks, Inc.
http://www.live555.com/
Deanna Earley
2014-10-13 08:09:09 UTC
Permalink
Look at a packet capture from Wireshark or similar.
The server explicitly stopped the stream and closed the connection after 90 seconds.

--
Deanna Earley | Lead developer | icatchercctv

w: www.icode.co.uk/icatcher<http://www.icode.co.uk/icatcher> | t: 01329 835335 | f: 01329 835338
Registered Office : 71 The Hundred, Romsey, SO51 8BZ. Company Number : 03428325

From: live-devel [mailto:live-devel-***@ns.live555.com] On Behalf Of Krishna Patel
Sent: 11 October 2014 23:10
To: LIVE555 Streaming Media - development & use
Subject: [Live-devel] Wowza Streaming Engine compatibility problem

Hi,
Live555 client drops connection after 90 seconds of receiving RTSP stream from Wowza Streaming Server 4.0.6 over HTTP tunneling protocol. Socket operation fail with WSAECONNABORTED error (An established connection was aborted by the software in your host machine.) Receiving the stream over TCP works just fine. This behavior can be seen with openRTSP v2014.10.07:

openRTSP -T 80 rtsp://q1604.dnsdojo.com:1935/live/sys1.stream

Thanks,
Krishna.
James Heliker
2014-10-12 02:10:51 UTC
Permalink
Hi Ross -

Thanks again for your help here. I understand what you're saying with using
an event trigger for when additional data is available, but I'm having no
luck implementing as my C++ is fairly bad. Coming from C# and other
higher-level languages is proving to be a bit of a learning curve for me!

I have a very simple circular buffer in my code as RtAudio code (for ASIO
input device) calls back my code, and the Live555 code also calls back my
code, both at their own regular intervals. Below, I've posted my
AudioBufferSource::doGetNextFrame(), in hopes you might be able to provide
a more verbose suggestion... I understand if you don't have the time.

Thanks again!

- James

void AudioBufferSource::doGetNextFrame() {

signed availableFrames = fBufferManager->_writeCnt -
fBufferManager->_readCnt;
while (availableFrames < 64)
{
availableFrames = fBufferManager->_writeCnt -
fBufferManager->_readCnt;
}

fFrameSize = 0;

if (fLimitNumBytesToStream && fNumBytesToStream < fMaxSize) {
fMaxSize = fNumBytesToStream;
}
if (fPreferredFrameSize < fMaxSize) {
fMaxSize = fPreferredFrameSize;
}

unsigned bytesPerSample = (fNumChannels*fBitsPerSample) / 8;
if (bytesPerSample == 0) bytesPerSample = 1; // because we can't read
less than a byte at a time

unsigned numberOfPCMFramesToGet = fMaxSize / bytesPerSample;
unsigned pcmFramesGotten = fBufferManager->getFrames((int16_t*)fTo,
numberOfPCMFramesToGet);

unsigned numBytesRead = pcmFramesGotten * bytesPerSample;
fFrameSize += numBytesRead;
fTo += numBytesRead;
fMaxSize -= numBytesRead;
fNumBytesToStream -= numBytesRead;

// Set the 'presentation time' and 'duration' of this RTP frame
if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
// This is the first frame, so use the current time:
gettimeofday(&fPresentationTime, NULL);
}
else {
// Increment by the play time of the previous RTP frame
unsigned uSeconds = fPresentationTime.tv_usec + fLastPlayTime;
fPresentationTime.tv_sec += uSeconds / 1000000;
fPresentationTime.tv_usec = uSeconds % 1000000;
}

// Remember the play time of this RTP frame
fDurationInMicroseconds = fLastPlayTime =
(unsigned)((fPlayTimePerSample*fFrameSize) / bytesPerSample);

// Inform the reader that he has data:
// To avoid possible infinite recursion, we need to return to the event
loop to do this:
nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
(TaskFunc*)FramedSource::afterGetting, this);
}




On Sat, Oct 11, 2014 at 8:14 AM, Ross Finlayson <***@live555.com>
wrote:

> I see what you mean, and fDurationInMicroseconds is being calculated
> correctly. For my fragmentation issue, If I don't call afterGetting() then
> doGetNextFrame() is not ever re-invoked, so the server essentially stops on
> the first partial read.
>
> Is there a method or variable I should set to indicate to Live555 that I
> want to continue accumulating data?
>
>
> Your "doGetNextFrame()" implementation should call
> "FramedSource::afterGetting()" *only* when you have a complete frame that
> you want to deliver to the downstream object. If you don't yet have a
> complete frame, then simply return, without
> calling "FramedSource::afterGetting()". If you do that, then
> "doGetNextFrame()" won't get called again in the meantime.
>
> But, you're probably thinking, if "doGetNextFrame()" simply returns
> without completing delivery of a frame, and doesn't get called again, then
> how can I figure out when I later get enough data to deliver? Basically,
> that has to be an 'event' that gets handled within the event loop. One way
> to do this is by 'polling' - i.e., by scheduling a periodic delayed task
> (using "TaskScheduler::scheduleDelayedTask()") that checks whether you have
> enough data (and then calls "FramedSource::afterGetting()" when you do have
> enough data). Another way is to have a separate thread that's gathering
> data, and then have this thread use an 'event trigger' (by calling
> "TaskScheduler::triggerEvent()") to signal when data is ready to be
> delivered. For an example of this, see the 'DeviceSource' code in
> "liveMedia/DeviceSource.cpp". (Note that if you do this, then
> "TaskScheduler::triggerEvent()" is the *only* LIVE555 function that the
> separate 'data gathering' thread is allowed to call; see <
> http://www.live555.com/liveMedia/faq.html#threads>).
>
>
> Ross Finlayson
> Live Networks, Inc.
> http://www.live555.com/
>
>
> _______________________________________________
> live-devel mailing list
> live-***@lists.live555.com
> http://lists.live555.com/mailman/listinfo/live-devel
>
>
Ross Finlayson
2014-10-12 04:54:44 UTC
Permalink
> Below, I've posted my AudioBufferSource::doGetNextFrame(), in hopes you might be able to provide a more verbose suggestion... I understand if you don't have the time.

Yes, unfortunately I'm limited in how much time I can spend providing assistance for free (especially for individual projects like this). But hopefully I've helped point you in the right direction.


Ross Finlayson
Live Networks, Inc.
http://www.live555.com/
Loading...