[asterisk-scf-commits] asterisk-scf/integration/test_channel.git branch "fileplayback-catchup" created.
Commits to the Asterisk SCF project code repositories
asterisk-scf-commits at lists.digium.com
Mon Jun 27 08:00:17 CDT 2011
branch "fileplayback-catchup" has been created
at 8cf24ecdd763f1ef6a6f2661d2f9606ba112e0cf (commit)
- Log -----------------------------------------------------------------
commit 8cf24ecdd763f1ef6a6f2661d2f9606ba112e0cf
Author: Brent Eagles <beagles at digium.com>
Date: Fri Jun 24 17:17:46 2011 -0230
Merging long lost file playback branch.
diff --git a/TestTools.py b/TestTools.py
new file mode 100644
index 0000000..d2da4f5
--- /dev/null
+++ b/TestTools.py
@@ -0,0 +1,112 @@
+#
+# Snippet module for running some simple tests with the test_channel library.
+#
+import Ice
+import AsteriskSCF
+import sys
+
+#
+# The file playback session endpoint implements a specialized SessionEndpoint
+# derived interface, AsteriskSCF::RemoteControl::V1::SessionFactory. This
+# interface allows you to register listeners to automatically be added to
+# session's it creates. This makes it possible for a session listener to react
+# immediately to events on that session.
+#
+class SessionListener(AsteriskSCF.SessionCommunications.V1.SessionListener):
+ def __init__(self, trackName):
+ self.mTrackname = trackName
+
+ def connected(self, sessionPrx, current):
+ try:
+ if sessionPrx == None:
+ print "Received connect with nil proxy"
+ return
+
+ mediaSession = sessionPrx.getMediaSession()
+ if mediaSession == None:
+ print "No media session"
+ return
+
+ sources = mediaSession.getSources()
+ if len(sources) < 1:
+ print "No sources"
+ return
+
+ remoteControl = AsteriskSCF.RemoteControl.V1.SourcePrx.checkedCast(sources[0].ice_facet("Control"))
+ if remoteControl == None:
+ print "No remote control interface"
+ return
+
+ #
+ # Media sink should already have been set by the bridge.
+ #
+ print "Starting the playback"
+ remoteControl.playTrack(self.mTrackname)
+ remoteControl.start()
+ except Ice.Exception,e:
+ print e
+
+
+def makeAutoPlaybackListener(filename):
+ return SessionListener(filename)
+
+global communicator
+global adapter
+global testEndpointLocatorPrx
+global endpointLocatorRegistryPrx
+global serviceLocatorPrx
+global sessionListener
+global sessionListenerPrx
+
+#
+# Set up our Ice communicator and an object adapter. This code can be safely
+# modified to use configuration for the object adapter configuraiton.
+#
+communicator = Ice.initialize(sys.argv)
+adapter = communicator.createObjectAdapterWithEndpoints("interactive", "default -p 54321")
+adapter.activate()
+
+#
+# Obtain some proxies to the services of interest. We'll use checked casts
+# for now, but these can easily be changed to unchecked casts if you want to
+# the script to run without really "doing anything" and the services aren't
+# running yet.
+#
+testEndpointLocatorPrx = AsteriskSCF.Core.Routing.V1.EndpointLocatorPrx.checkedCast(communicator.propertyToProxy("FilePlaybackLocator.Proxy"))
+if testEndpointLocatorPrx == None:
+ print "Unable to obtain file playback endpoint locator."
+ sys.exit(1)
+
+serviceLocatorPrx = AsteriskSCF.Core.Discovery.V1.ServiceLocatorPrx.checkedCast(communicator.propertyToProxy("LocatorService.Proxy"))
+if serviceLocatorPrx == None:
+ print "Unable to obtain service locator proxy."
+ sys.exit(1)
+
+#
+# The Locator registry is found by way of the service locator.
+#
+searchParams = AsteriskSCF.Core.Discovery.V1.ServiceLocatorParams()
+searchParams.category = AsteriskSCF.Core.Routing.V1.RoutingServiceLocatorRegistryDiscoveryCategory
+endpointRegistryPrx = AsteriskSCF.Core.Routing.V1.LocatorRegistryPrx.checkedCast(serviceLocatorPrx.locate(searchParams))
+if endpointRegistryPrx == None:
+ print "Unable to obtain locator registry proxy."
+ sys.exit(1)
+
+endpointRegistryPrx.addEndpointLocator("fileplayback", ["9.*"], testEndpointLocatorPrx)
+
+sessionListenerPrx = AsteriskSCF.SessionCommunications.V1.SessionListenerPrx.uncheckedCast(adapter.addWithUUID(makeAutoPlaybackListener("blip.au")))
+
+def registerDefaultExampleListener():
+ playbackEndpointPrx = AsteriskSCF.RemoteControl.V1.SessionFactory(testEndpointLocatorPrx.lookup("999")) # can really be anything.
+ playbackEndpointPrx.addDefaultSessionListener(sessionListenerPrx)
+
+#
+# Now we have an environment with everything setup to add a sample session
+# listener. It is recommended that you instantiate your own session listener
+# instance with an audio file of your liking and add it to the playback
+# endpoint. registerDefaultExampleListener() contains example code on how to do
+# this.
+#
+# If you run this code as an argument to "python -i", you can interactively
+# use these variables and methods to perform ad-hoc testing.
+#
diff --git a/slice/AsteriskSCF/TestChannel/RemoteControlIf.ice b/slice/AsteriskSCF/TestChannel/RemoteControlIf.ice
new file mode 100644
index 0000000..d1a30b5
--- /dev/null
+++ b/slice/AsteriskSCF/TestChannel/RemoteControlIf.ice
@@ -0,0 +1,84 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+#pragma once
+
+#include <Ice/BuiltinSequences.ice>
+#include <AsteriskSCF/Media/MediaIf.ice>
+#include <AsteriskSCF/SessionCommunications/SessionCommunicationsIf.ice>
+
+module AsteriskSCF
+{
+module TestChannel
+{
+module V1
+{
+
+exception FileNotFoundException
+{
+ string filename;
+};
+
+exception TrackNotFoundException
+{
+ string trackname;
+};
+
+interface SourceController
+{
+ Media::V1::FormatSeq filterFormats(Media::V1::FormatSeq possibleFormats);
+ bool allowFormatChange(Media::V1::Format newFormat);
+};
+
+interface SinkController
+{
+ Media::V1::FormatSeq filterFormats(Media::V1::FormatSeq possibleFormats);
+ bool allowWrite();
+};
+
+interface Source
+{
+ void start();
+ void pause();
+ void setController(SourceController* controller);
+ Ice::StringSeq listTracks();
+ void playTrack(string trackName)
+ throws FileNotFoundException, TrackNotFoundException;
+
+ Media::V1::StreamSink* getTrackInput(string trackName);
+
+ void destroy();
+};
+
+interface Sink
+{
+ void setController(SinkController* controller);
+ void startRecording();
+ void stopRecording();
+ void reset();
+ void copyToSink(Media::V1::StreamSink* destinationSink);
+
+ void destroy();
+};
+
+interface SessionFactory extends AsteriskSCF::SessionCommunications::V1::SessionEndpoint
+{
+ void addDefaultSessionListener(AsteriskSCF::SessionCommunications::V1::SessionListener* listener);
+ void removeDefaultSessionListener(AsteriskSCF::SessionCommunications::V1::SessionListener* listener);
+};
+
+}; /* End of module V1 */
+}; /* End of module RemoteControl */
+}; /* End of module AsteriskSCF */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5ffa22a..f190124 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,11 +1,16 @@
astscf_slice_include_collection(TEST_CHANNEL)
+include_directories(${logger_dir}/include)
+include_directories(${astscf-ice-util-cpp_dir}/include)
astscf_component_init(test_channel)
astscf_component_add_files(test_channel Service.cpp)
astscf_component_add_files(test_channel MediaEchoThread.cpp)
+astscf_component_add_files(test_channel MediaEchoThread.h)
astscf_component_add_files(test_channel MediaSession.cpp)
astscf_component_add_files(test_channel TestEndpoint.cpp)
astscf_component_add_files(test_channel TestEndpoint.h)
+astscf_component_add_files(test_channel FilePlaybackSessionEndpoint.h)
+astscf_component_add_files(test_channel FilePlaybackSessionEndpoint.cpp)
astscf_component_add_ice_libraries(test_channel IceStorm)
astscf_component_add_boost_libraries(test_channel thread date_time)
astscf_component_add_slice_collection_libraries(test_channel ASTSCF TEST_CHANNEL)
@@ -16,6 +21,7 @@ astscf_component_init(console_driver)
astscf_component_add_files(console_driver ConsoleDriver.cpp)
astscf_component_add_files(console_driver ConsoleDriver.h)
astscf_component_add_files(console_driver main.cpp)
-astscf_component_add_slice_collection_libraries(console_driver TEST_CHANNEL)
+astscf_component_add_slice_collection_libraries(console_driver ASTSCF TEST_CHANNEL)
astscf_component_build_standalone(console_driver)
astscf_component_install(console_driver)
+
diff --git a/src/FilePlaybackService.h b/src/FilePlaybackService.h
new file mode 100644
index 0000000..a42889c
--- /dev/null
+++ b/src/FilePlaybackService.h
@@ -0,0 +1,56 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+#pragma once
+
+#include <string>
+#include <Ice/Ice.h>
+
+namespace AsteriskSCF
+{
+namespace FilePlayback
+{
+inline std::string getId(const Ice::Current& current)
+{
+ return current.adapter->getCommunicator()->identityToString(current.id);
+}
+} // End of namespace FilePlayback
+} // End of namespace AsteriskSCF
+
+template <class T>
+class IdentityComparePred : public std::unary_function<T, bool>
+{
+public:
+ IdentityComparePred(const T& example) :
+ mExample(example)
+ {
+ }
+
+ bool operator()(const T& toTest)
+ {
+ if(toTest == 0)
+ {
+ return (mExample == 0);
+ }
+ if(mExample == 0)
+ {
+ return false;
+ }
+ return (mExample->ice_getIdentity() == toTest->ice_getIdentity());
+ }
+private:
+ T mExample;
+};
+
diff --git a/src/FilePlaybackSessionEndpoint.cpp b/src/FilePlaybackSessionEndpoint.cpp
new file mode 100644
index 0000000..a060ee8
--- /dev/null
+++ b/src/FilePlaybackSessionEndpoint.cpp
@@ -0,0 +1,1269 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+#include "FilePlaybackSessionEndpoint.h"
+#include <AsteriskSCF/System/Time/TimeIf.h>
+#include <AsteriskSCF/System/ExceptionsIf.h>
+#include <IceUtil/UUID.h>
+
+#include <boost/filesystem.hpp>
+#include "ListenerManager.h"
+#include "MediaEchoThread.h"
+#include <AsteriskSCF/TestChannel/RemoteControlIf.h>
+#include <IceUtil/Mutex.h>
+#include <IceUtil/Handle.h>
+#include <pjmedia.h>
+
+#include <fstream>
+#include <memory>
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// TODO:
+// * separate class implementations in this file into their own header/source files.
+// * rename the controller interface, its not really a controller but more like a filter/extension point
+// kind of thing.
+// * finish implementing the "record" functionality provided by the sink.
+// * incorporate matroska catalogue file
+// * it would be better to incorporate the matroska tools instead of the library directly as they've
+// implemented loads of higher level stuff. There is another C++ library to look at, but the name
+// escapes me at the moment.
+// * stereo to mono conversion
+// * real format handling
+//
+
+/**
+ * I usually eschew these using clauses, but the logger
+ * log level descriptors would get awfully cumbersome if I
+ * didn't do this.
+ */
+using namespace AsteriskSCF::System::Logging;
+
+namespace FBSPNS /* arbitrary ns name to deal with anonymous NS bugs */
+{
+
+static std::string getFilename(const Ice::CommunicatorPtr& communicator, const std::string& destination,
+ const AsteriskSCF::System::Logging::Logger& logger)
+{
+ boost::filesystem::path basePath = communicator->getProperties()->getProperty("MediaFiles");
+ basePath /= destination;
+ if (!boost::filesystem::exists(basePath))
+ {
+ logger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " non-existent file " << destination;
+ throw AsteriskSCF::TestChannel::V1::FileNotFoundException(basePath.string());
+ }
+ return basePath.string();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Default controller objects.
+//
+class DummySourceController : public AsteriskSCF::TestChannel::V1::SourceController
+{
+public:
+ AsteriskSCF::Media::V1::FormatSeq filterFormats(const AsteriskSCF::Media::V1::FormatSeq& possibleFormats,
+ const Ice::Current& current)
+ {
+ return possibleFormats;
+ }
+
+ bool allowFormatChange(const AsteriskSCF::Media::V1::FormatPtr& newFormat, const Ice::Current& current)
+ {
+ return true;
+ }
+};
+
+class DummySinkController : public AsteriskSCF::TestChannel::V1::SinkController
+{
+public:
+ AsteriskSCF::Media::V1::FormatSeq filterFormats(const AsteriskSCF::Media::V1::FormatSeq& possibleFormats,
+ const Ice::Current& current)
+ {
+ return possibleFormats;
+ }
+
+ bool allowWrite(const Ice::Current& current)
+ {
+ return true;
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Media implementations for file playback and recording.
+//
+
+/**
+ * MatroskaMediaSource - a Media StreamSource object implementation.
+ * This source object is designed to be used along with a remote control
+ * object implemented elsewhere in this project to allow some extra
+ * control over the media information it produces. The remote control
+ * interface is useful for test suites or interactive testing through
+ * a command line interface or GUI app.
+ */
+class MatroskaMediaSource : public AsteriskSCF::Media::V1::StreamSource
+{
+public:
+ MatroskaMediaSource(const AsteriskSCF::System::Logging::Logger& logger,
+ const std::string& id,
+ const AsteriskSCF::TestChannel::V1::SourceControllerPrx& controller,
+ const std::string& defaultFile):
+ mId(id),
+ mController(controller),
+ mDefaultFilename(defaultFile),
+ mThread(new AsteriskSCF::TestUtil::MediaEchoThread),
+ mLogger(logger),
+ mStarted(false)
+ {
+ }
+
+ //
+ // Media StreamSource interface implementations.
+ //
+ void setSink(const AsteriskSCF::Media::V1::StreamSinkPrx& destination, const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " for " << AsteriskSCF::FilePlayback::getId(current);
+ IceUtil::Mutex::Lock lock(mMutex);
+ mSink = destination;
+ mThread->setSink(mSink);
+ }
+
+ AsteriskSCF::Media::V1::StreamSinkPrx getSink(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " for " << AsteriskSCF::FilePlayback::getId(current);
+ IceUtil::Mutex::Lock lock(mMutex);
+ return mSink;
+ }
+
+ AsteriskSCF::Media::V1::FormatSeq getFormats(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " for " << AsteriskSCF::FilePlayback::getId(current);
+ IceUtil::Mutex::Lock lock(mMutex);
+ //
+ // check our list of available formats and filter them through the controller.
+ //
+ if(mController)
+ {
+ return mController->filterFormats(mFormats);
+ }
+ return mFormats;
+ }
+
+ std::string getId(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " for " << AsteriskSCF::FilePlayback::getId(current);
+ //
+ // The id is set at construction and is immutable, there is no need for
+ // a lock here.
+ //
+ return mId;
+ }
+
+ void requestFormat(const AsteriskSCF::Media::V1::FormatPtr& newFormat, const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " for " << AsteriskSCF::FilePlayback::getId(current);
+ IceUtil::Mutex::Lock lock(mMutex);
+ //
+ // check to see if we can produce the format
+ // then ask the controller if we can switch
+ //
+ if(mController && !mController->allowFormatChange(newFormat))
+ {
+ throw AsteriskSCF::Media::V1::MediaFormatSwitchException();
+ }
+ mCurrentFormat = newFormat;
+ }
+
+ //
+ // Internal methods made available for remote control.
+ //
+
+ //
+ // Start the echo thread.
+ //
+ void startThread()
+ {
+ bool startThread = false;
+ {
+ IceUtil::Mutex::Lock lock(mMutex);
+ if(!mStarted)
+ {
+ mStarted = true;
+ startThread = true;
+ }
+ }
+ mThread->resume();
+ if(startThread)
+ {
+ mThread->start();
+ }
+ }
+
+ void pauseThread()
+ {
+ mThread->pause();
+ }
+
+ //
+ // Set the controller object.
+ //
+ void setController(const AsteriskSCF::TestChannel::V1::SourceControllerPrx& controller)
+ {
+ IceUtil::Mutex::Lock lock(mMutex);
+ mController = controller;
+ }
+
+ //
+ // Get the list of the tracks that are available in this source.
+ //
+ Ice::StringSeq getTracks()
+ {
+ IceUtil::Mutex::Lock lock(mMutex);
+ return mTracks;
+ }
+
+ //
+ // Play the track by name.
+ //
+ void playTrack(const std::string& name, const Ice::Current& current)
+ {
+ if(!mSink)
+ {
+ return;
+ }
+ std::string filename = name;
+ if(name.size() == 0)
+ {
+ filename = mDefaultFilename;
+ }
+ filename = getFilename(current.adapter->getCommunicator(), filename, mLogger);
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ "loading file " << filename;
+
+ //
+ // Find the track, read it into our encoder, and push the frames to the echo thread.
+ //
+
+ //
+ // The null arguments to this call basically encourage the sox library to
+ // figure out info about the file on its own. We'll be able to provide
+ // this info by the matroska track info.
+ //
+ bool done = false;
+
+ std::ifstream input(filename.c_str(), std::ios::binary | std::ios::in);
+ if(input.good())
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " file opened successfully " << filename;
+ }
+ else
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " file open failed " << filename;
+ return;
+ }
+ input.seekg(0);
+
+ //
+ // TODO: this and the media echo thread should be put into a separate file.
+ //
+
+ pjmedia_wave_hdr waveHeader;
+ int rdSize = sizeof(waveHeader) -8;
+ input.get((char*)(&waveHeader), rdSize + 1);
+ if(!input.good() || input.gcount() != rdSize)
+ {
+ std::cout << "header read reported: " << input.gcount() << " was supposed to have read " << rdSize << std::endl;
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " unable to read the wav header.";
+ return;
+ }
+ pjmedia_wave_hdr_file_to_host(&waveHeader);
+
+ if(waveHeader.riff_hdr.riff != PJMEDIA_RIFF_TAG ||
+ waveHeader.riff_hdr.wave != PJMEDIA_WAVE_TAG ||
+ waveHeader.fmt_hdr.fmt != PJMEDIA_FMT_TAG)
+ {
+ mLogger(Debug) << "Riff header " << waveHeader.riff_hdr.riff << " expected " << PJMEDIA_RIFF_TAG;
+ mLogger(Debug) << "Wave header " << waveHeader.riff_hdr.wave << " expected " << PJMEDIA_WAVE_TAG;
+ mLogger(Debug) << "Fmt header " << waveHeader.fmt_hdr.fmt << " expected " << PJMEDIA_FMT_TAG;
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " invalid file format for playback.";
+ return;
+ }
+
+ //
+ // TODO: expand to support other PCM file formats.
+ //
+ if(waveHeader.fmt_hdr.fmt_tag != PJMEDIA_WAVE_FMT_TAG_ULAW)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " sorry, we are only support ulaw right now.";
+ return;
+ }
+
+ pjmedia_wave_fmt_tag format = static_cast<pjmedia_wave_fmt_tag>(waveHeader.fmt_hdr.fmt_tag);
+ size_t bytesPerSample = waveHeader.fmt_hdr.bits_per_sample /8;
+
+ if(waveHeader.fmt_hdr.len > 16)
+ {
+ rdSize = waveHeader.fmt_hdr.len -16;
+ input.seekg(rdSize, std::ios_base::cur);
+ if(!input.good())
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " sorry, processing header.";
+ return;
+ }
+ }
+
+ //
+ // get to the actual audio data.
+ //
+ int readSize = 8;
+ while(true)
+ {
+ pjmedia_wave_subchunk subchunk;
+ input.get(reinterpret_cast<char*>(&subchunk), readSize +1);
+ if(input.gcount() != readSize || !input.good())
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " sorry, wave file was too short.";
+ return;
+ }
+ PJMEDIA_WAVE_NORMALIZE_SUBCHUNK(&subchunk);
+ if(subchunk.id == PJMEDIA_DATA_TAG)
+ {
+ waveHeader.data_hdr.data = PJMEDIA_DATA_TAG;
+ waveHeader.data_hdr.len = subchunk.len;
+ break;
+ }
+ readSize = subchunk.len;
+ input.seekg(readSize, std::ios_base::cur);
+ }
+
+ //
+ // XXX keeping track of this for now, but we probably don't need it.
+ //
+ size_t dataStart = input.tellg();
+
+ //
+ // TODO- more file validation.
+ //
+
+ if(waveHeader.fmt_hdr.nchan != 1)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " sorry, we are only support mono right now.";
+ return;
+ }
+ size_t sampleRate = waveHeader.fmt_hdr.sample_rate;
+ size_t bitsPerSample = waveHeader.fmt_hdr.bits_per_sample;
+ //
+ // TODO: variable clock rates? Media echo thread is set at 8000Hz by default.
+ //
+ size_t samplesPerFrame = 20 * sampleRate / 1000;
+
+ size_t bufSize = samplesPerFrame * bitsPerSample / 8;
+ std::auto_ptr<char> buf(new char[bufSize]);
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ "Using a buffer size of " << bufSize << " bytes.";
+
+ size_t sequence = 0;
+ AsteriskSCF::Media::V1::FrameSeq frames;
+ AsteriskSCF::Media::V1::AudioFormatPtr mediaFormat(new AsteriskSCF::Media::V1::AudioFormat);
+ mediaFormat->name = "ulaw";
+ mediaFormat->sampleRate = sampleRate;
+ mediaFormat->frameSize = 20;
+ mediaFormat->maximumFrameSize = 20;
+ mediaFormat->minimumFrameSize = 20;
+
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ "Format: " << format <<
+ ", Bytes per sample: " << bytesPerSample <<
+ ", Bits per sample: " << bitsPerSample <<
+ ", Data start: " << dataStart <<
+ ", Sample rate: " << sampleRate;
+
+ while(true)
+ {
+ input.get(buf.get(), bufSize +1);
+ size_t bytesRead = input.gcount();
+
+ if(bytesRead == 0)
+ {
+ done = true;
+ break;
+ }
+ //
+ // for the moment we are going to assume that the output is ulaw.
+ //
+ AsteriskSCF::Media::V1::AudioFramePtr frame = new AsteriskSCF::Media::V1::AudioFrame;
+ frame->mediaformat = mediaFormat;
+ frame->seqno = sequence++;
+ frame->timestamp = samplesPerFrame;
+ frame->payload.assign(buf.get(), buf.get()+bytesRead);
+ frames.push_back(frame);
+ }
+ mThread->pushFrames(frames);
+ }
+
+ void destroyImpl()
+ {
+ mThread->destroy();
+ }
+
+private:
+
+ // Lock for the internal state.
+ IceUtil::Mutex mMutex;
+
+ // Id string, required for the StreamSource interface implementation.
+ std::string mId;
+
+ // Remote control object to modify behavior.
+ AsteriskSCF::TestChannel::V1::SourceControllerPrx mController;
+
+ // Default file name or track.
+ const std::string mDefaultFilename;
+
+ // The media sink that will receive the data from the source.
+ AsteriskSCF::Media::V1::StreamSinkPrx mSink;
+
+ // The formats supported by the source.
+ AsteriskSCF::Media::V1::FormatSeq mFormats;
+
+ // Background thread for taking data from the source and
+ // writing the sink at the prescribed rate.
+ AsteriskSCF::TestUtil::MediaEchoThreadPtr mThread;
+
+ // Asterisk SCF logger object.
+ AsteriskSCF::System::Logging::Logger mLogger;
+
+ // The names of the tracks in this source, in string form.
+ Ice::StringSeq mTracks;
+
+ // The current format we are supposed to be using.
+ AsteriskSCF::Media::V1::FormatPtr mCurrentFormat;
+
+ // Flag for whether the media echo thread has been started or not.
+ bool mStarted;
+};
+typedef IceUtil::Handle<MatroskaMediaSource> MatroskaMediaSourcePtr;
+
+/**
+ * This servant is meant to act in tandem with a Media source implementation.
+ * it forward calls from the source remote control interfaces to a media source
+ * implemented by this service.
+ **/
+class MatroskaSourceRemoteControl : public AsteriskSCF::TestChannel::V1::Source
+{
+public:
+ MatroskaSourceRemoteControl(const MatroskaMediaSourcePtr& source,
+ const AsteriskSCF::System::Logging::Logger& logger) :
+ mLogger(logger),
+ mSource(source)
+ {
+ }
+
+ void start(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " for " << AsteriskSCF::FilePlayback::getId(current);
+ mSource->startThread();
+ }
+
+ void pause(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " for " << AsteriskSCF::FilePlayback::getId(current);
+ mSource->pauseThread();
+ }
+
+ void setController(const AsteriskSCF::TestChannel::V1::SourceControllerPrx& controller,
+ const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for "
+ << AsteriskSCF::FilePlayback::getId(current);
+ mSource->setController(controller);
+ }
+
+ Ice::StringSeq listTracks(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for "
+ << AsteriskSCF::FilePlayback::getId(current);
+ return mSource->getTracks();
+ }
+
+ void playTrack(const std::string& trackName, const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for "
+ << AsteriskSCF::FilePlayback::getId(current);
+ mSource->playTrack(trackName, current);
+ }
+
+ AsteriskSCF::Media::V1::StreamSinkPrx getTrackInput(const std::string& trackName, const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for "
+ << AsteriskSCF::FilePlayback::getId(current);
+ return 0; // XXX
+ }
+
+ //
+ // there isn't too much to do here except remove the reference to the relevant source object.
+ //
+ void destroy(const Ice::Current& current)
+ {
+ mSource->destroyImpl();
+ mSource = 0;
+ }
+private:
+ AsteriskSCF::System::Logging::Logger mLogger;
+ MatroskaMediaSourcePtr mSource;
+};
+typedef IceUtil::Handle<MatroskaSourceRemoteControl> MatroskaSourceRemoteControlPtr;
+
+/**
+ * MatroskaMediaSink - a Media StreamSink object implementation.
+ * This sink implementation's primary purpose is to act as a cache for
+ * frames written to a media session hosted by this service. The
+ * cached frames can be "copied" out to a Remote control source object.
+ */
+class MatroskaMediaSink : public AsteriskSCF::Media::V1::StreamSink
+{
+public:
+ MatroskaMediaSink(const AsteriskSCF::System::Logging::Logger& logger, const std::string& id,
+ const AsteriskSCF::TestChannel::V1::SinkControllerPrx& controller) :
+ mLogger(logger),
+ mId(id),
+ mController(controller)
+ {
+ }
+
+ void write(const AsteriskSCF::Media::V1::FrameSeq& frames, const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " for " << AsteriskSCF::FilePlayback::getId(current);
+ IceUtil::Mutex::Lock lock(mMutex);
+ mReceivedFrames.insert(mReceivedFrames.end(), frames.begin(), frames.end());
+ }
+
+ void setSource(const AsteriskSCF::Media::V1::StreamSourcePrx& source, const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " for " << AsteriskSCF::FilePlayback::getId(current);
+ IceUtil::Mutex::Lock lock(mMutex);
+ mSource = source;
+ }
+
+ AsteriskSCF::Media::V1::StreamSourcePrx getSource(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " for " << AsteriskSCF::FilePlayback::getId(current);
+ IceUtil::Mutex::Lock lock(mMutex);
+ return mSource;
+ }
+
+ AsteriskSCF::Media::V1::FormatSeq getFormats(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " for " << AsteriskSCF::FilePlayback::getId(current);
+ IceUtil::Mutex::Lock lock(mMutex);
+ return mFormats;
+ }
+
+ std::string getId(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ <<
+ " for " << AsteriskSCF::FilePlayback::getId(current);
+ return mId;
+ }
+
+
+private:
+
+ // Lock for the internal state.
+ IceUtil::Mutex mMutex;
+
+ // Logger object.
+ AsteriskSCF::System::Logging::Logger mLogger;
+
+ // Id string, required for the StreamSink interface implementation.
+ std::string mId;
+
+ // Remote control object to modify behavior.
+ AsteriskSCF::TestChannel::V1::SinkControllerPrx mController;
+
+ // The media source the sink accepts media from.
+ AsteriskSCF::Media::V1::StreamSourcePrx mSource;
+
+ // The formats supported by the sinks.
+ AsteriskSCF::Media::V1::FormatSeq mFormats;
+
+ // The frame cache received by the sink
+ AsteriskSCF::Media::V1::FrameSeq mReceivedFrames;
+};
+typedef IceUtil::Handle<MatroskaMediaSink> MatroskaMediaSinkPtr;
+
+class MatroskaSinkRemoteControl : public AsteriskSCF::TestChannel::V1::Sink
+{
+public:
+ MatroskaSinkRemoteControl(const MatroskaMediaSinkPtr& sink, const AsteriskSCF::System::Logging::Logger& logger) :
+ mSink(sink),
+ mLogger(logger)
+ {
+ }
+
+ void setController(const AsteriskSCF::TestChannel::V1::SinkControllerPrx& controller, const Ice::Current& current)
+ {
+ }
+
+ void startRecording(const Ice::Current& current)
+ {
+ }
+
+ void stopRecording(const Ice::Current& current)
+ {
+ }
+
+ void copyToSink(const AsteriskSCF::Media::V1::StreamSinkPrx& destinationSink, const Ice::Current& current)
+ {
+ }
+
+ void reset(const Ice::Current& current)
+ {
+ }
+
+ void destroy(const Ice::Current& current)
+ {
+ mSink = 0;
+ }
+
+private:
+ MatroskaMediaSinkPtr mSink;
+ AsteriskSCF::System::Logging::Logger mLogger;
+};
+typedef IceUtil::Handle<MatroskaSinkRemoteControl> MatroskaSinkRemoteControlPtr;
+
+class MatroskaMediaSession : public AsteriskSCF::Media::V1::Session
+{
+public:
+ MatroskaMediaSession(const Ice::ObjectAdapterPtr& adapter,
+ const AsteriskSCF::System::Logging::Logger& logger,
+ const std::string& file,
+ const std::string& id) :
+ mAdapter(adapter),
+ mLogger(logger),
+ mFilename(file),
+ mId(id),
+ mDestroyed(false)
+ {
+ }
+
+ ~MatroskaMediaSession()
+ {
+ destroyImpl();
+ }
+
+ void initialize()
+ {
+ mSink = new MatroskaMediaSink(mLogger, mId + ".Sink", 0);
+ mSinkRemoteControl = new MatroskaSinkRemoteControl(mSink, mLogger);
+ mSource = new MatroskaMediaSource(mLogger, mId + ".Source", 0, mFilename);
+ mSourceRemoteControl = new MatroskaSourceRemoteControl(mSource, mLogger);
+
+ Ice::CommunicatorPtr comm = mAdapter->getCommunicator();
+ Ice::Identity id = comm->stringToIdentity(mId + ".Sink");
+ mSinkPrx = AsteriskSCF::Media::V1::StreamSinkPrx::uncheckedCast(mAdapter->add(mSink, id));
+ mSinkControlPrx = AsteriskSCF::TestChannel::V1::SinkPrx::uncheckedCast(
+ mAdapter->addFacet(mSinkRemoteControl, id, "Control"));
+ id = comm->stringToIdentity(mId + ".Source");
+ mSourcePrx = AsteriskSCF::Media::V1::StreamSourcePrx::uncheckedCast(mAdapter->add(mSource, id));
+ mSourceControlPrx = AsteriskSCF::TestChannel::V1::SourcePrx::uncheckedCast(
+ mAdapter->addFacet(mSourceRemoteControl, id, "Control"));
+ }
+
+ void destroyImpl()
+ {
+ {
+ IceUtil::Mutex::Lock lock(mMutex);
+ if(mDestroyed)
+ {
+ return;
+ }
+ mDestroyed = true;
+ }
+ try
+ {
+ mSinkControlPrx->destroy();
+ mSourceControlPrx->destroy();
+ mAdapter->remove(mSinkPrx->ice_getIdentity());
+ mAdapter->remove(mSourcePrx->ice_getIdentity());
+ }
+ catch(const Ice::Exception& ex)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << '(' << __FUNCTION__ << ')' <<
+ ex.what();
+ }
+ }
+
+ AsteriskSCF::Media::V1::StreamSourceSeq getSources(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for "
+ << AsteriskSCF::FilePlayback::getId(current);
+ IceUtil::Mutex::Lock lock(mMutex);
+ if(mDestroyed)
+ {
+ throw Ice::ObjectNotExistException(__FILE__, __LINE__);
+ }
+ AsteriskSCF::Media::V1::StreamSourceSeq result;
+ result.push_back(mSourcePrx);
+ return result;
+ }
+
+ AsteriskSCF::Media::V1::StreamSinkSeq getSinks(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for "
+ << AsteriskSCF::FilePlayback::getId(current);
+ IceUtil::Mutex::Lock lock(mMutex);
+ if(mDestroyed)
+ {
+ throw Ice::ObjectNotExistException(__FILE__, __LINE__);
+ }
+ AsteriskSCF::Media::V1::StreamSinkSeq result;
+ result.push_back(mSinkPrx);
+ return result;
+ }
+
+ std::string getId(const Ice::Current& current)
+ {
+ IceUtil::Mutex::Lock lock(mMutex);
+ if(mDestroyed)
+ {
+ throw Ice::ObjectNotExistException(__FILE__, __LINE__);
+ }
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for "
+ << AsteriskSCF::FilePlayback::getId(current);
+ return mId;
+ }
+
+private:
+ IceUtil::Mutex mMutex;
+ Ice::ObjectAdapterPtr mAdapter;
+ AsteriskSCF::System::Logging::Logger mLogger;
+ const std::string mFilename;
+ const std::string mId;
+ MatroskaMediaSinkPtr mSink;
+ MatroskaSinkRemoteControlPtr mSinkRemoteControl;
+ AsteriskSCF::Media::V1::StreamSinkPrx mSinkPrx;
+ AsteriskSCF::TestChannel::V1::SinkPrx mSinkControlPrx;
+
+ MatroskaMediaSourcePtr mSource;
+ MatroskaSourceRemoteControlPtr mSourceRemoteControl;
+ AsteriskSCF::Media::V1::StreamSourcePrx mSourcePrx;
+ AsteriskSCF::TestChannel::V1::SourcePrx mSourceControlPrx;
+ bool mDestroyed;
+};
+typedef IceUtil::Handle<MatroskaMediaSession> MatroskaMediaSessionPtr;
+
+typedef AsteriskSCF::TestUtil::ListenerManagerT<AsteriskSCF::SessionCommunications::V1::SessionListenerPrx>
+ SessionListenerMgr;
+
+class FileSession : virtual public AsteriskSCF::SessionCommunications::V1::Session,
+ virtual public SessionListenerMgr
+{
+public:
+ FileSession(const Ice::ObjectAdapterPtr& adapter, const std::string& id, const std::string& name,
+ const AsteriskSCF::System::Logging::Logger& logger) :
+ SessionListenerMgr(adapter->getCommunicator(), id, true),
+ mAdapter(adapter),
+ mId(id),
+ mName(name),
+ mLogger(logger),
+ mDestroyed(false),
+ mMediaSession(new MatroskaMediaSession(adapter, logger, mName, id + ".Media")),
+ mInfo(new AsteriskSCF::SessionCommunications::V1::SessionInfo),
+ mStartTime(static_cast<time_t>(0))
+ {
+ mInfo->role = "callee";
+ }
+
+ void publishProxy(const AsteriskSCF::SessionCommunications::V1::SessionPrx& prx)
+ {
+ mProxy = prx;
+ }
+
+ void endpointProxy(const AsteriskSCF::SessionCommunications::V1::SessionEndpointPrx& prx)
+ {
+ mInfo->caller = prx;
+ mInfo->destination = prx;
+ }
+
+ void addListener_async(
+ const AsteriskSCF::SessionCommunications::V1::AMD_Session_addListenerPtr& cb,
+ const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener,
+ const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ addListenerImpl(listener);
+ cb->ice_response(updateInfo());
+ }
+
+ void addListenerImpl(const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener)
+ {
+ SessionListenerMgr::addListener(listener);
+ }
+
+ void indicate_async(
+ const AsteriskSCF::SessionCommunications::V1::AMD_Session_indicatePtr& cb,
+ const AsteriskSCF::SessionCommunications::V1::IndicationPtr& value,
+ const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ cb->ice_response();
+ }
+
+ void removeListener(const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener,
+ const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ SessionListenerMgr::removeListener(listener);
+ }
+
+ void connect(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ mStartTime = time(0);
+ mPublisher->indicated(mProxy, new AsteriskSCF::SessionCommunications::V1::ConnectedIndication(),
+ AsteriskSCF::SessionCommunications::V1::SessionCookies());
+ }
+
+ void flash(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ }
+
+ void getEndpoint_async(
+ const AsteriskSCF::SessionCommunications::V1::AMD_Session_getEndpointPtr& cb,
+ const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ cb->ice_response(mInfo->caller);
+ }
+
+ void getInfo_async(
+ const AsteriskSCF::SessionCommunications::V1::AMD_Session_getInfoPtr& cb,
+ const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ cb->ice_response(updateInfo());
+ }
+
+ void getMediaSession_async(
+ const AsteriskSCF::SessionCommunications::V1::AMD_Session_getMediaSessionPtr& cb,
+ const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ cb->ice_response(mMediaPrx);
+ }
+
+ void hold(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ mPublisher->indicated(mProxy, new AsteriskSCF::SessionCommunications::V1::HeldIndication(),
+ AsteriskSCF::SessionCommunications::V1::SessionCookies());
+ }
+
+ void progress(const AsteriskSCF::SessionCommunications::V1::ResponseCodePtr& responseCode, const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ }
+
+ void ring(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ mPublisher->indicated(mProxy, new AsteriskSCF::SessionCommunications::V1::RingingIndication(),
+ AsteriskSCF::SessionCommunications::V1::SessionCookies());
+ }
+
+ void start(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ //
+ // Initialize the media sessions now or later? I think I will create the media sessions when I create
+ // the file session but do something a little different once the session is started.
+ //
+ mPublisher->indicated(mProxy, new AsteriskSCF::SessionCommunications::V1::ConnectedIndication(),
+ AsteriskSCF::SessionCommunications::V1::SessionCookies());
+ }
+
+ void stop(const AsteriskSCF::SessionCommunications::V1::ResponseCodePtr& responseCode, const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ AsteriskSCF::SessionCommunications::V1::StoppedIndicationPtr indication(new AsteriskSCF::SessionCommunications::V1::StoppedIndication());
+ indication->response = responseCode;
+ mPublisher->indicated(mProxy, indication, AsteriskSCF::SessionCommunications::V1::SessionCookies());
+ mMediaSession->destroyImpl();
+ }
+
+ void unhold(const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ mPublisher->indicated(mProxy, new AsteriskSCF::SessionCommunications::V1::UnheldIndication(),
+ AsteriskSCF::SessionCommunications::V1::SessionCookies());
+ }
+
+ void getBridge_async(
+ const AsteriskSCF::SessionCommunications::V1::AMD_Session_getBridgePtr& cb,
+ const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ cb->ice_response(mBridge);
+ }
+
+ AsteriskSCF::SessionCommunications::V1::SessionInfoPtr setBridge(const AsteriskSCF::SessionCommunications::V1::BridgePrx& bridge,
+ const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener, const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ SessionListenerMgr::addListener(listener);
+ mBridge = bridge;
+ return updateInfo();
+ }
+
+ void setBridge_async(
+ const AsteriskSCF::SessionCommunications::V1::AMD_Session_setBridgePtr& cb,
+ const AsteriskSCF::SessionCommunications::V1::BridgePrx& newBridge,
+ const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener,
+ const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ SessionListenerMgr::addListener(listener);
+ mBridge = newBridge;
+ cb->ice_response(updateInfo());
+ }
+
+ void removeBridge_async(
+ const AsteriskSCF::SessionCommunications::V1::AMD_Session_removeBridgePtr& cb,
+ const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener,
+ const Ice::Current& current)
+ {
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ SessionListenerMgr::removeListener(listener);
+ mBridge = 0;
+ cb->ice_response();
+ }
+
+ void initialize()
+ {
+ mMediaSession->initialize();
+ mMediaPrx = AsteriskSCF::Media::V1::SessionPrx::uncheckedCast(mAdapter->add(mMediaSession,
+ mAdapter->getCommunicator()->stringToIdentity(mId + ".Media")));
+ }
+
+ void destroy()
+ {
+ mMediaSession->destroyImpl();
+ }
+
+ bool destroyed() const
+ {
+ IceUtil::Mutex::Lock lock(mMutex);
+ return mDestroyed;
+ }
+
+ AsteriskSCF::SessionCommunications::V1::SessionInfoPtr updateInfo()
+ {
+ IceUtil::Mutex::Lock lock(mMutex);
+ AsteriskSCF::SessionCommunications::V1::SessionInfoPtr result(
+ AsteriskSCF::SessionCommunications::V1::SessionInfoPtr::dynamicCast(mInfo->ice_clone()));
+ mInfo->connectedTime = (unsigned long)time(0) - (unsigned long)mStartTime;
+ mInfo->startTime = new AsteriskSCF::System::Time::V1::TimeMarker((long)mStartTime, 1.0);
+ return result;
+ }
+
+ void setCookies(const AsteriskSCF::SessionCommunications::V1::SessionCookies&, const Ice::Current&)
+ {
+ }
+
+ void removeCookies(const AsteriskSCF::SessionCommunications::V1::SessionCookies&, const Ice::Current&)
+ {
+ }
+
+ AsteriskSCF::SessionCommunications::V1::SessionCookies getCookies(const AsteriskSCF::SessionCommunications::V1::SessionCookies&, const Ice::Current&)
+ {
+ return AsteriskSCF::SessionCommunications::V1::SessionCookies();
+ }
+
+private:
+ IceUtil::Mutex mMutex;
+ Ice::ObjectAdapterPtr mAdapter;
+ std::string mId;
+ std::string mName;
+ AsteriskSCF::System::Logging::Logger mLogger;
+ bool mDestroyed;
+ AsteriskSCF::SessionCommunications::V1::SessionPrx mProxy;
+ AsteriskSCF::SessionCommunications::V1::BridgePrx mBridge;
+ MatroskaMediaSessionPtr mMediaSession;
+ AsteriskSCF::Media::V1::SessionPrx mMediaPrx;
+ AsteriskSCF::SessionCommunications::V1::SessionInfoPtr mInfo;
+ time_t mStartTime;
+};
+
+typedef IceUtil::Handle<FileSession> FileSessionPtr;
+
+struct FileSessionInfo
+{
+ FileSessionPtr servant;
+ AsteriskSCF::SessionCommunications::V1::SessionPrx proxy;
+};
+
+AsteriskSCF::SessionCommunications::V1::SessionPrx getProxies(const FileSessionInfo& info)
+{
+ return info.proxy;
+}
+
+}
+using namespace FBSPNS;
+
+namespace AsteriskSCF
+{
+namespace FilePlayback
+{
+class FilePlaybackEndpointImpl
+{
+public:
+ FilePlaybackEndpointImpl(const Ice::ObjectAdapterPtr& adapter, const AsteriskSCF::System::Logging::Logger& logger);
+ AsteriskSCF::SessionCommunications::V1::SessionPrx createSession(const std::string& destination,
+ const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener, const Ice::Current&);
+ AsteriskSCF::SessionCommunications::V1::SessionSeq getSessions(const Ice::Current&);
+ void addDefaultSessionListener(const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener, const Ice::Current& current);
+ void removeDefaultSessionListener(const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener, const Ice::Current& current);
+private:
+
+ AsteriskSCF::System::Logging::Logger mLogger;
+ Ice::ObjectAdapterPtr mAdapter;
+ std::vector<FileSessionInfo> mSessions;
+ std::vector<AsteriskSCF::SessionCommunications::V1::SessionListenerPrx> mDefaultListeners;
+ IceUtil::Mutex mMutex;
+
+ void reap();
+};
+}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// FilePlaybackEndpointImpl
+//
+AsteriskSCF::FilePlayback::FilePlaybackEndpointImpl::FilePlaybackEndpointImpl(const Ice::ObjectAdapterPtr& adapter,
+ const AsteriskSCF::System::Logging::Logger& logger):
+ mLogger(logger),
+ mAdapter(adapter)
+{
+}
+
+static bool
+nonIdChar(const char c)
+{
+ return !isalnum(c);
+}
+
+static std::string
+pathToId(const std::string& path)
+{
+ std::string result = path;
+ std::replace_if(result.begin(), result.end(), nonIdChar, '.');
+ return result;
+}
+
+AsteriskSCF::SessionCommunications::V1::SessionPrx AsteriskSCF::FilePlayback::FilePlaybackEndpointImpl::createSession(
+ const std::string& destination, const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener,
+ const Ice::Current& current)
+{
+ std::string myId = AsteriskSCF::FilePlayback::getId(current);
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << myId;
+ std::string file;
+ try
+ {
+ file = getFilename(current.adapter->getCommunicator(), destination, mLogger);
+ }
+ catch(const AsteriskSCF::TestChannel::V1::FileNotFoundException&)
+ {
+ throw AsteriskSCF::SessionCommunications::V1::SessionCreationException();
+ }
+ myId += '.';
+ myId += destination;
+ myId = pathToId(myId);
+ myId += '.';
+ myId += IceUtil::generateUUID();
+
+ FileSessionPtr newSession(new FileSession(mAdapter, myId, file, mLogger));
+ //
+ // The only place that is liable to throw after newSession is created is
+ // the addition to the object adapter. If that occurs, the adapter will not
+ // have a reference and the servant will be deleted when newSession goes
+ // out of scope.
+ //
+ try
+ {
+ //
+ // Order of calls is a little important here. "initialize()" is called
+ // on the new session before anything else as a kind of "check" to make
+ // sure nothing in the initialization of the session depends on the
+ // session being activated. That being said, calling initialize before
+ // adding it means that the objects are all ready to start receiving
+ // requests.
+ //
+ newSession->initialize();
+ for(std::vector<AsteriskSCF::SessionCommunications::V1::SessionListenerPrx>::const_iterator i = mDefaultListeners.begin();
+ i != mDefaultListeners.end(); ++i)
+ {
+ newSession->addListenerImpl(*i);
+ }
+ AsteriskSCF::SessionCommunications::V1::SessionPrx prx(
+ AsteriskSCF::SessionCommunications::V1::SessionPrx::uncheckedCast(
+ mAdapter->add(newSession, mAdapter->getCommunicator()->stringToIdentity(myId))));
+ newSession->publishProxy(prx);
+ if(listener)
+ {
+ newSession->addListenerImpl(listener);
+ }
+ FileSessionInfo info;
+ info.servant = newSession;
+ info.proxy = prx;
+
+ IceUtil::Mutex::Lock lock(mMutex);
+ mSessions.push_back(info);
+ return prx;
+ }
+ catch (const Ice::Exception& ex)
+ {
+ mLogger(Error) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " exception caught " << ex.what();
+ throw AsteriskSCF::SessionCommunications::V1::SessionCreationException(destination);
+ }
+}
+
+AsteriskSCF::SessionCommunications::V1::SessionSeq AsteriskSCF::FilePlayback::FilePlaybackEndpointImpl::getSessions(
+ const Ice::Current& current)
+{
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ IceUtil::Mutex::Lock lock(mMutex);
+ reap();
+ AsteriskSCF::SessionCommunications::V1::SessionSeq result(mSessions.size());
+ std::transform(mSessions.begin(), mSessions.end(), result.begin(), getProxies);
+ return result;
+}
+
+void AsteriskSCF::FilePlayback::FilePlaybackEndpointImpl::addDefaultSessionListener(const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener, const Ice::Current& current)
+{
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ if(!listener)
+ {
+ mLogger(Debug) << __FUNCTION__ << ":" <<
+ current.adapter->getCommunicator()->identityToString(current.id) << " attempting to add a null proxy";
+ throw AsteriskSCF::System::V1::NullProxyException();
+ }
+ IceUtil::Mutex::Lock lock(mMutex);
+ if(mDefaultListeners.end() == std::find_if(mDefaultListeners.begin(), mDefaultListeners.end(),
+ IdentityComparePred<AsteriskSCF::SessionCommunications::V1::SessionListenerPrx>(listener)))
+ {
+ mDefaultListeners.push_back(listener);
+ }
+}
+
+void AsteriskSCF::FilePlayback::FilePlaybackEndpointImpl::removeDefaultSessionListener(const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener, const Ice::Current& current)
+{
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+ std::cerr << __FUNCTION__ << ":" << current.adapter->getCommunicator()->identityToString(current.id) << std::endl;
+ if(!listener)
+ {
+ mLogger(Debug) << __FUNCTION__ << ":" << current.adapter->getCommunicator()->identityToString(current.id)
+ << " attempting to remove a null proxy";
+ throw AsteriskSCF::System::V1::NullProxyException();
+ }
+ IceUtil::Mutex::Lock lock(mMutex);
+ std::remove_if(mDefaultListeners.begin(), mDefaultListeners.end(),
+ IdentityComparePred<AsteriskSCF::SessionCommunications::V1::SessionListenerPrx>(listener));
+}
+
+void AsteriskSCF::FilePlayback::FilePlaybackEndpointImpl::reap()
+{
+ mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " reaping.";
+ std::vector<FileSessionInfo> remainingSessions;
+ for (std::vector<FileSessionInfo>::iterator i = mSessions.begin(); i != mSessions.end(); ++i)
+ {
+ if (!i->servant->destroyed())
+ {
+ remainingSessions.push_back(*i);
+ }
+ else
+ {
+ i->servant = 0;
+ mAdapter->remove(i->proxy->ice_getIdentity());
+ }
+ }
+ mSessions = remainingSessions;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// FilePlaybackEndpoint
+//
+AsteriskSCF::FilePlayback::FilePlaybackEndpoint::FilePlaybackEndpoint(const Ice::ObjectAdapterPtr& adapter,
+ const AsteriskSCF::System::Logging::Logger& logger) :
+ mImpl(new FilePlaybackEndpointImpl(adapter, logger)),
+ mId("fileplayback")
+{
+}
+
+AsteriskSCF::FilePlayback::FilePlaybackEndpoint::~FilePlaybackEndpoint()
+{
+ delete mImpl;
+}
+
+AsteriskSCF::SessionCommunications::V1::SessionPrx AsteriskSCF::FilePlayback::FilePlaybackEndpoint::createSession(const std::string& destination,
+ const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener, const Ice::Current& current)
+{
+ return mImpl->createSession(destination, listener, current);
+}
+
+AsteriskSCF::SessionCommunications::V1::SessionSeq AsteriskSCF::FilePlayback::FilePlaybackEndpoint::getSessions(const Ice::Current& current)
+{
+ return mImpl->getSessions(current);
+}
+
+std::string AsteriskSCF::FilePlayback::FilePlaybackEndpoint::getId(const Ice::Current&)
+{
+ return mId;
+}
+
+void AsteriskSCF::FilePlayback::FilePlaybackEndpoint::addDefaultSessionListener(
+ const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener,
+ const Ice::Current& current)
+{
+ mImpl->addDefaultSessionListener(listener, current);
+}
+
+void AsteriskSCF::FilePlayback::FilePlaybackEndpoint::removeDefaultSessionListener(
+ const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener,
+ const Ice::Current& current)
+{
+ mImpl->removeDefaultSessionListener(listener, current);
+}
diff --git a/src/FilePlaybackSessionEndpoint.h b/src/FilePlaybackSessionEndpoint.h
new file mode 100644
index 0000000..d3b830b
--- /dev/null
+++ b/src/FilePlaybackSessionEndpoint.h
@@ -0,0 +1,50 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#pragma once
+
+#include "FilePlaybackService.h"
+#include <AsteriskSCF/SessionCommunications/SessionCommunicationsIf.h>
+#include <AsteriskSCF/TestChannel/RemoteControlIf.h>
+#include <string>
+#include <Ice/Ice.h>
+#include <AsteriskSCF/logger.h>
+
+namespace AsteriskSCF
+{
+namespace FilePlayback
+{
+ class FilePlaybackEndpointImpl;
+
+ class FilePlaybackEndpoint : public AsteriskSCF::TestChannel::V1::SessionFactory
+ {
+ public:
+ FilePlaybackEndpoint(const Ice::ObjectAdapterPtr& adapter, const AsteriskSCF::System::Logging::Logger&);
+ ~FilePlaybackEndpoint();
+
+ AsteriskSCF::SessionCommunications::V1::SessionPrx createSession(const std::string& destination,
+ const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& callback, const Ice::Current&);
+ AsteriskSCF::SessionCommunications::V1::SessionSeq getSessions(const Ice::Current&);
+ std::string getId(const Ice::Current&);
+
+ void addDefaultSessionListener(const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener, const Ice::Current& current);
+ void removeDefaultSessionListener(const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener, const Ice::Current& current);
+ private:
+ FilePlaybackEndpointImpl* mImpl;
+ std::string mId;
+ };
+} // End of namespace FileplayBack
+} // End of namespace AsteriskSCF
diff --git a/src/ListenerManager.h b/src/ListenerManager.h
index 17f3932..8182b9a 100644
--- a/src/ListenerManager.h
+++ b/src/ListenerManager.h
@@ -10,10 +10,12 @@
#include <Ice/Ice.h>
#include <IceStorm/IceStorm.h>
#include <boost/thread/shared_mutex.hpp>
+#include <boost/thread/locks.hpp>
#include <string>
#include <algorithm>
#include <vector>
#include "InternalExceptions.h"
+#include <IceUtil/Thread.h>
namespace AsteriskSCF
{
@@ -23,66 +25,64 @@ namespace TestUtil
// Helper template for classes that need to implement listener style interfaces.
//
template <class T>
-class ListenerManagerT : public IceUtil::Shared
+class ListenerManagerT
{
typedef std::vector<T> ListenerSeq;
typename std::vector<T>::iterator ListenerIter;
-public:
- ListenerManagerT(const Ice::CommunicatorPtr& communicator, const std::string& topicName) :
- mCommunicator(communicator),
- mTopicName(topicName)
+
+ class InitializationThread : public IceUtil::Thread
{
- //
- // TODO: While this is being concocted for a single component, it would make more sense
- // to have the topic manager passed in during construction for more general usage.
- //
- const std::string propertyName = "TopicManager.Proxy";
- std::string topicManagerProperty = mCommunicator->getProperties()->getProperty(propertyName);
- if(topicManagerProperty.size() == 0)
+ public:
+ InitializationThread(ListenerManagerT* mgr) :
+ mMgr(mgr),
+ mStopped(false)
{
- throw ConfigException(propertyName, "Topic manager proxy property missing. "
- "Unable to initialize listener support.");
}
- try
- {
- mTopicManager = IceStorm::TopicManagerPrx::checkedCast(mCommunicator->stringToProxy(topicManagerProperty));
- }
- catch(const Ice::Exception&)
+ void run()
{
+ bool initialized = false;
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(mMonitor);
+ while(!mStopped && !initialized)
+ {
+ //
+ // TODO: Make configurable.
+ //
+ mMonitor.timedWait(IceUtil::Time::seconds(15));
+ initialized = mMgr->init();
+ }
}
- if(!mTopicManager)
+
+ void stop()
{
- throw ConfigException(propertyName, "Topic manager proxy is not a valid proxy or is unreachable");
+ IceUtil::Monitor<IceUtil::Mutex>::Lock lock(mMonitor);
+ mStopped = true;
+ mMonitor.notify();
}
+ private:
+ IceUtil::Monitor<IceUtil::Mutex> mMonitor;
+ ListenerManagerT* mMgr;
+ bool mStopped;
+ };
+
+public:
+ ListenerManagerT(const Ice::CommunicatorPtr& communicator, const std::string& topicName, bool enableBackgroundInit) :
+ mCommunicator(communicator),
+ mTopicName(topicName),
+ mInitialized(false)
+ {
try
{
- mTopic = mTopicManager->retrieve(mTopicName);
+ init();
}
- catch(const IceStorm::NoSuchTopic&)
+ catch(const Ice::Exception&)
{
}
-
- if(!mTopic)
+ if(!mInitialized && enableBackgroundInit)
{
- try
- {
- mTopic = mTopicManager->create(mTopicName);
- }
- catch(const IceStorm::TopicExists&)
- {
- //
- // In case there is a race condition when creating the topic.
- //
- mTopic = mTopicManager->retrieve(mTopicName);
- }
- }
-
- if(!mTopic)
- {
- throw ConfigException(propertyName,
- std::string("unable to create topic with the provided configuration :") + mTopicName);
+ mInitThread = new InitializationThread(this);
+ mInitThread->start();
}
}
@@ -101,6 +101,9 @@ public:
//
}
}
+ ///
+ // TODO: join on initialization thread for destruction.
+ //
}
//
@@ -114,17 +117,22 @@ public:
{
mListeners.push_back(listener);
}
- IceStorm::QoS qos;
- qos["reliability"] = "ordered";
- try
- {
- mTopic->subscribeAndGetPublisher(qos, listener);
- }
- catch(const IceStorm::AlreadySubscribed&)
+
+ if(mInitialized)
{
- //
- // This indicates some kind of inconsistent state.
- //
+ IceStorm::QoS qos;
+ qos["reliability"] = "ordered";
+
+ try
+ {
+ mTopic->subscribeAndGetPublisher(qos, listener);
+ }
+ catch(const IceStorm::AlreadySubscribed&)
+ {
+ //
+ // This indicates some kind of inconsistent state.
+ //
+ }
}
}
@@ -135,7 +143,10 @@ public:
if(i != mListeners.end())
{
mListeners.erase(i);
- mTopic->unsubscribe(listener);
+ if(mInitialized)
+ {
+ mTopic->unsubscribe(listener);
+ }
}
}
@@ -146,13 +157,112 @@ public:
return result;
}
+ bool isSuspended()
+ {
+ boost::shared_lock<boost::shared_mutex> lock(mLock);
+ return !mInitialized;
+ }
+
+ void stop()
+ {
+ if(mInitThread)
+ {
+ mInitThread->stop();
+ }
+ }
+
protected:
boost::shared_mutex mLock;
Ice::CommunicatorPtr mCommunicator;
std::string mTopicName;
IceStorm::TopicPrx mTopic;
IceStorm::TopicManagerPrx mTopicManager;
+ T mPublisher;
ListenerSeq mListeners;
+ IceUtil::Handle<InitializationThread> mInitThread;
+
+ bool mInitialized;
+
+ bool init()
+ {
+ boost::shared_lock<boost::shared_mutex> lock(mLock);
+ if(mInitialized)
+ {
+ return mInitialized;
+ }
+
+ //
+ // TODO: While this is being concocted for a single component, it would make more sense
+ // to have the topic manager passed in during construction for more general usage.
+ //
+ const std::string propertyName = "TopicManager.Proxy";
+ std::string topicManagerProperty = mCommunicator->getProperties()->getProperty(propertyName);
+ if(topicManagerProperty.size() == 0)
+ {
+ throw ConfigException(propertyName, "Topic manager proxy property missing. "
+ "Unable to initialize listener support.");
+ }
+
+ try
+ {
+ mTopicManager = IceStorm::TopicManagerPrx::checkedCast(mCommunicator->stringToProxy(topicManagerProperty));
+ }
+ catch(const Ice::Exception&)
+ {
+ return false;
+ }
+
+ try
+ {
+ mTopic = mTopicManager->retrieve(mTopicName);
+ }
+ catch(const IceStorm::NoSuchTopic&)
+ {
+ }
+
+ if(!mTopic)
+ {
+ try
+ {
+ mTopic = mTopicManager->create(mTopicName);
+ }
+ catch(const IceStorm::TopicExists&)
+ {
+ //
+ // In case there is a race condition when creating the topic.
+ //
+ mTopic = mTopicManager->retrieve(mTopicName);
+ }
+ }
+
+ if(!mTopic)
+ {
+ return mInitialized;
+ }
+ mPublisher = T::uncheckedCast(mTopic->getPublisher());
+
+ if(mListeners.size() > 0)
+ {
+ for(typename std::vector<T>::iterator i = mListeners.begin(); i != mListeners.end(); ++i)
+ {
+ IceStorm::QoS qos;
+ qos["reliability"] = "ordered";
+
+ try
+ {
+ mTopic->subscribeAndGetPublisher(qos, *i);
+ }
+ catch(const IceStorm::AlreadySubscribed&)
+ {
+ //
+ // This indicates some kind of inconsistent state.
+ //
+ }
+ }
+ }
+ mInitialized = true;
+ return mInitialized;
+ }
};
}
}
diff --git a/src/Logger.h b/src/Logger.h
deleted file mode 100644
index e045b41..0000000
--- a/src/Logger.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Asterisk Scalable Communications Framework
- *
- * Copyright (C) 2010 -- Digium, Inc.
- *
- * All rights reserved.
- */
-#pragma once
-
-#include <iostream>
-
-namespace AsteriskSCF
-{
-namespace TestUtil
-{
-//
-// Place holder log stream holder.
-//
-class Logger
-{
-public:
- Logger() :
- mTraceDebug(true)
- {
- }
-
- std::ostream& getTraceStream()
- {
- return std::cerr;
- }
-
- std::ostream& getErrorStream()
- {
- return std::cerr;
- }
-
- std::ostream& getDebugStream()
- {
- return std::cerr;
- }
-
- bool debugTracing() { return mTraceDebug; }
-
- std::ostream& getInfoStream()
- {
- return std::cerr;
- }
-
-private:
- bool mTraceDebug;
-};
-} // End of namespace TestUtil
-} // End of namespace AsteriskSCF
diff --git a/src/MediaEchoThread.cpp b/src/MediaEchoThread.cpp
index c9eccf1..dcd2ee7 100644
--- a/src/MediaEchoThread.cpp
+++ b/src/MediaEchoThread.cpp
@@ -19,37 +19,94 @@ using namespace AsteriskSCF::TestUtil;
MediaEchoThread::MediaEchoThread():
mDone(false),
- mPaused(true)
... 466 lines suppressed ...
--
asterisk-scf/integration/test_channel.git
More information about the asterisk-scf-commits
mailing list