Hello Ross,

We have been running for some time a RTSPServer that acquires video from a live
source (with our own DeviceSource) from which we create 2 or 3 replicas: one for
multicast streaming and another to feed a FileSink for file storage. The third
replica is created on demand for unicast streaming.

Recently we detected a problem on some servers when the file partition on which
the files are being stored becomes full. When that happens, all pipelines
(FileSink, multicast and unicast) stop running.

The FileSink halt was expected and we are currently implementing a strategy to
deal with disk full issues. But, at least, the multicast streaming should
continue and we were somewhat surprised with these symptoms, so we tried to
understand what was going on.

This is what we concluded: when the partition becomes full, an error must be
detected in FileSink::afterGettingFrame(...) when fflush(fOutFid) is called.
After that, no next frame is requested from our upstream source, even though
there are several replicas in the replicator.

We think this is because in MediaSink::onSourceClosure(...) fSource is set to
NULL and, when MediaSink::stopPlaying() is called, fSource->stopGettingFrames()
is NOT CALLED since it was previously set to NULL.

And, since fSource->stopGettingFrames() is not being called, neither is
StreamReplica::doStopGettingFrames(). Now, we believe that our file sink replica
was the master replica in StreamReplicator and, since it's not being
deactivated, no other replica requests any more frames.

The following patch to MediaSink solved our current problem:

--- liveMedia/MediaSink.cpp (revision 16076)
+++ liveMedia/MediaSink.cpp (working copy)
@@ -92,7 +92,7 @@
 
 void MediaSink::onSourceClosure(void* clientData) {
   MediaSink* sink = (MediaSink*)clientData;
- sink->fSource = NULL; // indicates that we can be played again
+ // sink->fSource = NULL; // indicates that we can be played again
   if (sink->fAfterFunc != NULL) {
     (*(sink->fAfterFunc))(sink->fAfterClientData);
   }

We're just not sure that this is a correct solution or if we should override
MediaSink::stopPlaying() to set the MediaSink::fSource before 'if (fSource !=
NULL) fSource->stopGettingFrames()' is called, or something else entirely.

I've attached a small modification to testProgs/testReplicator.cpp that creates
2 replicas from a ByteStreamFileSource that reads the video
"bipbop-gear1-all.ts" (available from the LIVE555 site).

We run this app with the following script:

#--------
#!/bin/sh

./testReplicator &

DISK_AVAIL=`df -h . | sed '1d' | awk '{print $4}' | cut -d'%' -f1`

echo "type <Return> to continue and allocate ${DISK_AVAIL} on current partition"
echo "ATTENTION: This will allocate all free space"

read cont

fallocate -l ${DISK_AVAIL} dummy.1
#--------

It allows us to check that, after fallocate completely fills up the available
space, both the file and the udp sink replicas stop working.

Any help will be appreciated.

Thank You,
Bruno Abreu

-- 
Living Data - Sistemas de Informação e Apoio à Decisão, Lda.

LxFactory - Rua Rodrigues de Faria, 103,
edifício I - 4º piso                  Phone:  +351 213622163
1300-501 LISBOA                       Fax:    +351 213622165
Portugal                              URL: www.livingdata.pt

/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2.1 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// Copyright (c) 1996-2013, Live Networks, Inc.  All rights reserved
// A demo application that receives a UDP multicast stream, replicates it (using the "FrameReplicator" class),
// and retransmits one replica stream to another (multicast or unicast) address & port,
// and writes the other replica stream to a file.
//
// main program

#include <liveMedia.hh>
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"

UsageEnvironment* env;

// To receive a "source-specific multicast" (SSM) stream, uncomment this:
//#define USE_SSM 1

void startReplicaUDPSink(StreamReplicator* replicator, char const* outputAddressStr, portNumBits outputPortNum); // forward
void startReplicaFileSink(StreamReplicator* replicator, char const* outputFileName); // forward
void fileSinkAfterPlaying(void* clientData);

int main(int argc, char** argv) {
  // Begin by setting up our usage environment:
  TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  env = BasicUsageEnvironment::createNew(*scheduler);

#define TRANSPORT_PACKET_SIZE 188
#define TRANSPORT_PACKETS_PER_NETWORK_PACKET 7
  // Create the video source:
  unsigned const inputDataChunkSize
    = TRANSPORT_PACKETS_PER_NETWORK_PACKET*TRANSPORT_PACKET_SIZE;
  ByteStreamFileSource* fileSource
    = ByteStreamFileSource::createNew(*env, "bipbop-gear1-all.ts", inputDataChunkSize);
  if (fileSource == NULL) return NULL;
  MPEG2TransportStreamFramer* source = MPEG2TransportStreamFramer::createNew(*env, fileSource);

  // And feed this into a 'stream replicator':
  StreamReplicator* replicator = StreamReplicator::createNew(*env, source);

  // Then create a network (UDP) 'sink' object to receive a replica of the input stream, and start it.
  // If you wish, you can duplicate this line - with different network addresses and ports - to create multiple output UDP streams:
  startReplicaUDPSink(replicator, "239.255.43.43", 4444);

  // Then create a file 'sink' object to receive a replica of the input stream, and start it.
  // If you wish, you can duplicate this line - with a different file name - to create multiple output files:
  startReplicaFileSink(replicator, "test.out");

  // Finally, enter the 'event loop' (which is where most of the 'real work' in a LIVE555-based application gets done):
  env->taskScheduler().doEventLoop(); // does not return

  return 0; // only to prevent compiler warning
}

void startReplicaUDPSink(StreamReplicator* replicator, char const* outputAddressStr, portNumBits outputPortNum) {
  // Begin by creating an input stream from our replicator:
  FramedSource* source = replicator->createStreamReplica();

  // Create a 'groupsock' for the destination address and port:
  struct in_addr outputAddress;
  outputAddress.s_addr = our_inet_addr(outputAddressStr);

  Port const outputPort(outputPortNum);
  unsigned char const outputTTL = 255;

  Groupsock* outputGroupsock = new Groupsock(*env, outputAddress, outputPort, outputTTL);

  // Then create a liveMedia 'sink' object, encapsulating this groupsock:
  unsigned const maxPacketSize = 65536; // allow for large UDP packets
  MediaSink* sink = BasicUDPSink::createNew(*env, outputGroupsock, maxPacketSize);

  // Now, start playing, feeding the sink object from the source:
  sink->startPlaying(*source, NULL, NULL);
}

void startReplicaFileSink(StreamReplicator* replicator, char const* outputFileName) {
  // Begin by creating an input stream from our replicator:
  FramedSource* source = replicator->createStreamReplica();

  // Then create a 'file sink' object to receive thie replica stream:
  MediaSink* sink = FileSink::createNew(*env, outputFileName);

  // Now, start playing, feeding the sink object from the source:
  sink->startPlaying(*source, fileSinkAfterPlaying, sink);
}

void fileSinkAfterPlaying(void* clientData) {
    *env << "FileSink stopped! Please check upstream source read task schedule\n";
}
_______________________________________________
live-devel mailing list
live-devel@lists.live555.com
http://lists.live555.com/mailman/listinfo/live-devel

Reply via email to