[Live-devel] Proper use of StreamReplicator

Jan Ekholm jan.ekholm at d-pointer.com
Fri Oct 17 08:02:53 PDT 2014


On 17 okt 2014, at 16:48, Ross Finlayson <finlayson at live555.com> wrote:

>> Yes, I see JPEGVideoRTPSink being destroyed three times as createNewStreamSource() is called three times. 
>> First with a clientSessionID=0 and the other times with valid large ids. No idea why three times, perhaps VLC which
>> acts as the client does something interesting. Trying with testRTSPClient I only see two calls. Perhaps VLC retries
>> something as there is no data.
> 
> Yes, VLC first requests regular RTP/RTCP-over-UDP streaming, but then - if it receives no data within a certain period of time - tries again, this time requesting RTP/RTCP-over-TCP streaming.

That explains it, as there definitely was no data being sent.

>> So far there doesn't seem to be any error in what I'm trying to do, but the stream simply is not playing. Is there
>> really a startPlaying() being called for the replicated stream too?
> 
> Yes, there should be - it’s done in the “OnDemandServerMediaSubsession” code, when the server handles the RTSP “PLAY” command.
> 
> Unfortunately, I can’t figure out why it’s not working for you.  You’re going to have to work through the “StreamReplica” code, checking that "StreamReplica::doGetNextFrame()” gets called, as it should.

I found the culprit, did that mail not come through? I had some hassles with non responding email servers, so
perhaps it never was sent properly. Below I've copied the message.

---------------

Ok, I think I've found the issue. The culprit here is the StreamReplica class which is 
a FrameSource, but it is not a JPEG video source, i.e. it does not return True for:

virtual Boolean isJPEGVideoSource();

This is what is checked in:

Boolean JPEGVideoRTPSink::sourceIsCompatibleWithUs(MediaSource& source) {
   return source.isJPEGVideoSource();
}

which is checked from:

Boolean MediaSink::startPlaying(MediaSource& source,
				afterPlayingFunc* afterFunc,
				void* afterClientData) {
// Make sure we're not already being played:
if (fSource != NULL) {
 envir().setResultMsg("This sink is already being played");
 return False;
}

// Make sure our source is compatible:
if (!sourceIsCompatibleWithUs(source)) {
envir().setResultMsg("MediaSink::startPlaying(): source is not compatible!");
 return False;
}
...

To me it seems as if StreamReplica perhaps should forward all the methods below to the replicated source?

// Test for specific types of source:
virtual Boolean isFramedSource() const;
virtual Boolean isRTPSource() const;
virtual Boolean isMPEG1or2VideoStreamFramer() const;
virtual Boolean isMPEG4VideoStreamFramer() const;
virtual Boolean isH264VideoStreamFramer() const;
virtual Boolean isH265VideoStreamFramer() const;
virtual Boolean isDVVideoStreamFramer() const;
virtual Boolean isJPEGVideoSource() const;
virtual Boolean isAMRAudioSource() const;

But if I redefine that methods it all crashes later in:

unsigned JPEGVideoRTPSink::specialHeaderSize() const {
// Our source is known to be a JPEGVideoSource
JPEGVideoSource* source = (JPEGVideoSource*)fSource;
if (source == NULL) return 0; // sanity check
...

Here fSource is a StreamReplicator and not a JPEGVideoSource, so the cast will be bogus.
Would perhaps be better to use dynamic_cast<> here, then the sanity check would work. As the
StreamReplica class is internal to StreamReplicator specialHeaderSize() can not check for it when
casting and do special handling for that case.

So, for MJPEG there are a few issues.


-- 
Jan Ekholm
jan.ekholm at d-pointer.com




More information about the live-devel mailing list