[asterisk-scf-commits] team/beagles/fileplayback.git branch "master" created.

Commits to the Asterisk SCF project code repositories asterisk-scf-commits at lists.digium.com
Thu Dec 16 11:51:45 UTC 2010


branch "master" has been created
        at  f320f0e9cb5e44d62109ea68c7418e7601c0b7e9 (commit)

- Log -----------------------------------------------------------------
commit f320f0e9cb5e44d62109ea68c7418e7601c0b7e9
Author: Brent Eagles <beagles at digium.com>
Date:   Wed Dec 15 14:15:53 2010 -0330

    Fix several issues with the fileplayback handler.

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 147056a..a4bbbe5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -27,6 +27,7 @@ endif()
 include_directories(${logger_dir}/common)
 include_directories(${logger_dir}/client/src)
 asterisk_scf_component_build_icebox(test_channel)
+target_link_libraries(test_channel logging-client)
 
 asterisk_scf_component_init(console_driver CXX)
 asterisk_scf_component_add_slice(console_driver CommandsIf)
diff --git a/src/FilePlaybackService.h b/src/FilePlaybackService.h
index 475fcdb..bae7ecb 100644
--- a/src/FilePlaybackService.h
+++ b/src/FilePlaybackService.h
@@ -38,7 +38,7 @@ namespace Logging
 
 namespace FilePlayback
 {
-std::string getId(const Ice::Current& current)
+inline std::string getId(const Ice::Current& current)
 {
     return current.adapter->getCommunicator()->identityToString(current.id);
 }
diff --git a/src/FilePlaybackSessionEndpoint.cpp b/src/FilePlaybackSessionEndpoint.cpp
new file mode 100644
index 0000000..c209d3d
--- /dev/null
+++ b/src/FilePlaybackSessionEndpoint.cpp
@@ -0,0 +1,1029 @@
+/*
+ * 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 <System/Time/TimeIf.h>
+#include <IceUtil/UUID.h>
+#include <logger.h>
+
+#include <boost/filesystem.hpp>
+#include "ListenerManager.h"
+#include "MediaEchoThread.h"
+#include <RemoteControl.h>
+#include <IceUtil/Mutex.h>
+#include <IceUtil/Handle.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
+//   * this is probably going to require libsox to create temporary files since Matroska doesn't 
+//     really support ulaw, alaw, etc. This probably isn't that big of a deal.
+//   * 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::LoggerPtr& 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::RemoteControl::V1::FileNotFoundException(basePath.string());
+    }
+    return basePath.string();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Default controller objects.
+//
+class DummySourceController : public AsteriskSCF::RemoteControl::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::RemoteControl::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::LoggerPtr& logger,
+      const std::string& id, 
+      const AsteriskSCF::RemoteControl::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::RemoteControl::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, std::ios::binary | std::ios::in);
+        mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << 
+            " file opened successfully " << filename;
+
+        //
+        // These values aren't the greatest, they are just hacked in there
+        // to make sure that the pacing works right.
+        //
+        const size_t bufSize = 20;
+        std::unique_ptr<char> buf(new char[bufSize]);
+        size_t sequence = 0;
+        AsteriskSCF::Media::V1::FrameSeq frames;
+        while(true)
+        {
+            input.get(buf.get(), bufSize);
+            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 = mCurrentFormat;
+            frame->seqno = sequence++;
+            frame->timestamp = bytesRead * frame->seqno;
+            frame->payload.assign(buf.get(), buf.get()+bufSize);
+            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::RemoteControl::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::LoggerPtr 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::RemoteControl::V1::Source
+{
+public:
+    MatroskaSourceRemoteControl(const MatroskaMediaSourcePtr& source,
+      const AsteriskSCF::System::Logging::LoggerPtr& 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::RemoteControl::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::LoggerPtr 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::LoggerPtr& logger, const std::string& id, 
+      const AsteriskSCF::RemoteControl::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::LoggerPtr mLogger;
+
+    // Id string, required for the StreamSink interface implementation.
+    std::string mId;
+
+    // Remote control object to modify behavior. 
+    AsteriskSCF::RemoteControl::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::RemoteControl::V1::Sink
+{
+public:
+    MatroskaSinkRemoteControl(const MatroskaMediaSinkPtr& sink, const AsteriskSCF::System::Logging::LoggerPtr& logger) :
+        mSink(sink),
+        mLogger(logger)
+    {
+    }
+
+    void setController(const AsteriskSCF::RemoteControl::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::LoggerPtr mLogger;
+};
+typedef IceUtil::Handle<MatroskaSinkRemoteControl> MatroskaSinkRemoteControlPtr;
+
+class MatroskaMediaSession : public AsteriskSCF::Media::V1::Session
+{
+public:
+    MatroskaMediaSession(const Ice::ObjectAdapterPtr& adapter, 
+      const AsteriskSCF::System::Logging::LoggerPtr& 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::RemoteControl::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::RemoteControl::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());
+            mAdapter->remove(mSinkControlPrx->ice_getIdentity());
+            mAdapter->remove(mSourceControlPrx->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::LoggerPtr mLogger;
+    const std::string mFilename;
+    const std::string mId;
+    MatroskaMediaSinkPtr mSink;
+    MatroskaSinkRemoteControlPtr mSinkRemoteControl;
+    AsteriskSCF::Media::V1::StreamSinkPrx mSinkPrx;
+    AsteriskSCF::RemoteControl::V1::SinkPrx mSinkControlPrx;
+
+    MatroskaMediaSourcePtr mSource;
+    MatroskaSourceRemoteControlPtr mSourceRemoteControl;
+    AsteriskSCF::Media::V1::StreamSourcePrx mSourcePrx;
+    AsteriskSCF::RemoteControl::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::LoggerPtr& 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;
+    }
+
+    AsteriskSCF::SessionCommunications::V1::SessionInfoPtr addListener(const AsteriskSCF::SessionCommunications::V1::SessionListenerPrx& listener, 
+      const Ice::Current& current)
+    {
+        mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+        SessionListenerMgr::addListener(listener);
+        return updateInfo();
+    }
+
+    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);
+    }
+
+    void flash(const Ice::Current& current)
+    {
+        mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+    }
+
+    AsteriskSCF::SessionCommunications::V1::SessionEndpointPrx getEndpoint(const Ice::Current& current)
+    {
+        mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+        return mInfo->caller;
+    }
+
+    AsteriskSCF::SessionCommunications::V1::SessionInfoPtr getInfo(const Ice::Current& current)
+    {
+        mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+        return updateInfo();
+    }
+
+    AsteriskSCF::Media::V1::SessionPrx getMediaSession(const Ice::Current& current)
+    {
+        mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+        return mMediaPrx;
+    }
+
+    void hold(const Ice::Current& current)
+    {
+        mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+    }
+
+    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);
+    }
+
+    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.
+        // 
+    }
+
+    void stop(const AsteriskSCF::SessionCommunications::V1::ResponseCodePtr& responseCode, const Ice::Current& current)
+    {
+        mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+    }
+
+    void unhold(const Ice::Current& current)
+    {
+        mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+    }
+
+    AsteriskSCF::SessionCommunications::V1::BridgePrx getBridge(const Ice::Current& current) 
+    {
+        mLogger(Debug) << __FILE__ << ':' << __LINE__ << ' ' << __FUNCTION__ << " for " << AsteriskSCF::FilePlayback::getId(current);
+        return 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 removeBridge(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;
+    }
+
+    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;
+    }
+
+private:
+    IceUtil::Mutex mMutex;
+    Ice::ObjectAdapterPtr mAdapter;
+    std::string mId;
+    std::string mName;
+    AsteriskSCF::System::Logging::LoggerPtr 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::LoggerPtr& 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&);
+private:
+
+    AsteriskSCF::System::Logging::LoggerPtr mLogger;
+    Ice::ObjectAdapterPtr mAdapter;
+    std::vector<FileSessionInfo> mSessions;
+    IceUtil::Mutex mMutex;
+
+    void reap();
+};
+}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// FilePlaybackEndpointImpl
+//
+AsteriskSCF::FilePlayback::FilePlaybackEndpointImpl::FilePlaybackEndpointImpl(const Ice::ObjectAdapterPtr& adapter,
+  const AsteriskSCF::System::Logging::LoggerPtr& logger):
+    mLogger(logger),
+    mAdapter(adapter)
+{
+}
+
+static bool
+nonIdChar(const char c)
+{
+    return !isalpha(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::RemoteControl::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();
+        AsteriskSCF::SessionCommunications::V1::SessionPrx prx(
+          AsteriskSCF::SessionCommunications::V1::SessionPrx::uncheckedCast(
+              mAdapter->add(newSession, mAdapter->getCommunicator()->stringToIdentity(myId))));
+        newSession->publishProxy(prx);
+        if(listener)
+        {
+            newSession->addListener(listener, current);
+        }
+        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::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::LoggerPtr& 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;
+}
+
diff --git a/src/FilePlaybackSessionEndpoint.h b/src/FilePlaybackSessionEndpoint.h
index c4fb9e6..09429c6 100644
--- a/src/FilePlaybackSessionEndpoint.h
+++ b/src/FilePlaybackSessionEndpoint.h
@@ -36,9 +36,11 @@ namespace FilePlayback
         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&);
 
     private:
         FilePlaybackEndpointImpl* mImpl;
+        std::string mId;
     };
 } // End of namespace FileplayBack
 } // End of namespace AsteriskSCF
diff --git a/src/MediaEchoThread.cpp b/src/MediaEchoThread.cpp
index 21edf11..92c5fd3 100644
--- a/src/MediaEchoThread.cpp
+++ b/src/MediaEchoThread.cpp
@@ -40,15 +40,20 @@ void MediaEchoThread::run()
     // is done without holding the lock. A useful improvement might be to add
     // an upper limit to the number of frames that can be "queued".
     //
+    std::cerr << "entering thread" << std::endl;
     bool done = false;
     AsteriskSCF::Media::V1::FrameSeq currentFrames;
     size_t numberOfFramesWritten = 0;
     AsteriskSCF::Media::V1::StreamSinkPrx sink;
     while(true)
     {
+        std::cerr << "looping" << std::endl;
         {
             IceUtil::Monitor<IceUtil::Mutex>::Lock lock(mMonitor);
-            mMonitor.wait();
+            if(mFrames.size() == 0 && !mDone)
+            {
+                mMonitor.wait();
+            }
 
             //
             // Copy our working state. For this iteration.
@@ -56,6 +61,7 @@ void MediaEchoThread::run()
             sink = mSink;
             if(!done)
             {
+                std::cerr << "not done yet " << std::endl;
                 if(numberOfFramesWritten != 0)
                 {
                     mFrames.erase(mFrames.begin(), mFrames.begin() + numberOfFramesWritten);
@@ -66,10 +72,12 @@ void MediaEchoThread::run()
         }
         if(done)
         {
+            std::cerr << "done! thread terminating" << std::endl;
             break;
         }
 
         AsteriskSCF::Media::V1::FrameSeq::iterator current = currentFrames.begin();
+        std::cerr << "writing " << currentFrames.size() << " frames" << std::endl;
         while(current != currentFrames.end() && sink != 0)
         {
             AsteriskSCF::Media::V1::FrameSeq buf(current, current+1);
@@ -95,6 +103,7 @@ void MediaEchoThread::run()
 void MediaEchoThread::pushFrames(const AsteriskSCF::Media::V1::FrameSeq& newFrames)
 {
     IceUtil::Monitor<IceUtil::Mutex>::Lock lock(mMonitor);
+    std::cerr << "pushing " << newFrames.size() << " frames" << std::endl;
     mFrames.insert(mFrames.end(), newFrames.begin(), newFrames.end());
     if(!mPaused)
     {
diff --git a/src/RemoteControl.ice b/src/RemoteControl.ice
index 8d066a3..697a5b1 100644
--- a/src/RemoteControl.ice
+++ b/src/RemoteControl.ice
@@ -25,6 +25,16 @@ module RemoteControl
 module V1
 {
 
+exception FileNotFoundException
+{
+    string filename;
+};
+
+exception TrackNotFoundException
+{
+    string trackname;
+};
+
 interface SourceController
 {
     Media::V1::FormatSeq filterFormats(Media::V1::FormatSeq possibleFormats);
@@ -40,9 +50,11 @@ interface SinkController
 interface Source
 {
     void start();
+    void pause();
     void setController(SourceController* controller);
     Ice::StringSeq listTracks();
-    void playTrack(string trackName);
+    void playTrack(string trackName)
+        throws FileNotFoundException, TrackNotFoundException;
 
     Media::V1::StreamSink* getTrackInput(string trackName);
 
diff --git a/src/Service.cpp b/src/Service.cpp
index 6212ed9..dd5dc3d 100644
--- a/src/Service.cpp
+++ b/src/Service.cpp
@@ -58,6 +58,8 @@ void TestChannelDriver::start(const std::string& name, const Ice::CommunicatorPt
     AsteriskSCF::Core::Discovery::V1::ServiceLocatorParamsPtr params = new AsteriskSCF::Core::Discovery::V1::ServiceLocatorParams();
     params->category = "TestChannel";
     mServiceManagement->addLocatorParams(params, "");
+    params->category = "FilePlayback";
+    mServiceManagement->addLocatorParams(params, "");
 }
 
 void TestChannelDriver::stop()
diff --git a/src/TestEndpoint.cpp b/src/TestEndpoint.cpp
index e9a7fc3..f8b3eff 100644
--- a/src/TestEndpoint.cpp
+++ b/src/TestEndpoint.cpp
@@ -29,9 +29,12 @@
 #include "Logger.h"
 #include "MediaSession.h"
 #include "CommandsIf.h"
-
+#include "FilePlaybackService.h"
+#include "FilePlaybackSessionEndpoint.h"
+#include <logger.h>
 
 using namespace AsteriskSCF::TestUtil;
+using namespace AsteriskSCF::System::Logging;
 
 namespace
 {
@@ -529,12 +532,22 @@ public:
             AsteriskSCF::Core::Endpoint::V1::BaseEndpointPrx::checkedCast(adapter->add(mManager, adapter->getCommunicator()->stringToIdentity(mManagerId + ".Endpoint")));
         mCommands = new CommandImpl(mManager);
         adapter->add(mCommands, adapter->getCommunicator()->stringToIdentity(mManagerId + ".Commands"));
+
+        mFilePlayback = new AsteriskSCF::FilePlayback::FilePlaybackEndpoint(adapter, getLoggerFactory().getLogger("AsteriskSCF.FilePlayback"));
+        mFilePlaybackPrx = AsteriskSCF::SessionCommunications::V1::SessionEndpointPrx::checkedCast(adapter->add(mFilePlayback, adapter->getCommunicator()->stringToIdentity(mManagerId + ".Playback")));
     }
 
     AsteriskSCF::Core::Endpoint::V1::EndpointSeq lookup(const std::string& destination, const Ice::Current&)
     {
         AsteriskSCF::Core::Endpoint::V1::EndpointSeq result;
-        result.push_back(mEndpointProxy);
+        if(destination[0] == '9')
+        {
+            result.push_back(mFilePlaybackPrx);
+        }
+        else
+        {
+            result.push_back(mEndpointProxy);
+        }
         return result;
     }
 
@@ -545,6 +558,8 @@ private:
     EndpointManagerPtr mManager;
     AsteriskSCF::TestChannel::V1::CommandsPtr mCommands;
     AsteriskSCF::Core::Endpoint::V1::BaseEndpointPrx mEndpointProxy;
+    AsteriskSCF::SessionCommunications::V1::SessionEndpointPtr mFilePlayback;
+    AsteriskSCF::SessionCommunications::V1::SessionEndpointPrx mFilePlaybackPrx;
 };
 
 typedef IceUtil::Handle<EndpointLocatorI> EndpointLocatorIPtr;

commit 48ad88acdf49e421480e19707377031c87f167d3
Author: Brent Eagles <beagles at digium.com>
Date:   Fri Dec 10 21:09:04 2010 -0330

    Add some interface definitions intended for media control.
    Add skeletons of servants to integrate matroska handlers into.
    Add frequency based scheduling to media echo thread.
    Updated listener manager, also removing IceUtil::Shared from the inheritance to allow it to be "multiply"
    inherited into some servant classes.. making that a bit easier.
    
    TODO:
     - integrate the matroska related code
     - document slice
     - add READMEs
     - fully test pacing
     - test test test

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 33d3155..147056a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,17 +1,31 @@
+asterisk_scf_slice_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../slice)
+asterisk_scf_compile_slice(RemoteControl.ice lib "Media remote control API" test_channel)
 asterisk_scf_component_init(test_channel CXX)
 asterisk_scf_component_add_slice(test_channel EndpointIf)
 asterisk_scf_component_add_slice(test_channel ComponentServiceIf)
 asterisk_scf_component_add_slice(test_channel SessionCommunicationsIf)
 asterisk_scf_component_add_slice(test_channel RoutingIf)
 asterisk_scf_component_add_slice(test_channel CommandsIf)
+asterisk_scf_component_add_slice(test_channel MediaIf)
+asterisk_scf_component_add_slice(test_channel RemoteControl)
 asterisk_scf_component_add_file(test_channel Service.cpp)
 asterisk_scf_component_add_file(test_channel MediaEchoThread.cpp)
+asterisk_scf_component_add_file(test_channel MediaEchoThread.h)
 asterisk_scf_component_add_file(test_channel MediaSession.cpp)
+asterisk_scf_component_add_file(test_channel MediaSession.h)
 asterisk_scf_component_add_file(test_channel TestEndpoint.cpp)
 asterisk_scf_component_add_file(test_channel TestEndpoint.h)
+asterisk_scf_component_add_file(test_channel FilePlaybackSessionEndpoint.h)
+asterisk_scf_component_add_file(test_channel FilePlaybackSessionEndpoint.cpp)
 asterisk_scf_component_add_ice_libraries(test_channel IceStorm)
 asterisk_scf_component_add_boost_libraries(test_channel thread)
 asterisk_scf_component_add_boost_libraries(test_channel date_time)
+asterisk_scf_component_add_boost_libraries(test_channel filesystem)
+if(NOT logger_dir)
+   message(FATAL_ERROR "The logger directory could not be found ${logger_dir}")
+endif()
+include_directories(${logger_dir}/common)
+include_directories(${logger_dir}/client/src)
 asterisk_scf_component_build_icebox(test_channel)
 
 asterisk_scf_component_init(console_driver CXX)
diff --git a/src/FilePlaybackService.h b/src/FilePlaybackService.h
new file mode 100644
index 0000000..475fcdb
--- /dev/null
+++ b/src/FilePlaybackService.h
@@ -0,0 +1,47 @@
+/*
+ * 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>
+
+//
+// Some service-wide definitions.
+//
+namespace AsteriskSCF
+{
+namespace System
+{
+namespace Logging
+{
+    class Logger;
+    //
+    // Temporary until the logger is a reference counted entity.
+    //
+    typedef Logger& LoggerPtr; 
+
+} // End of namespace Logging
+} // End of namespace System
+
+namespace FilePlayback
+{
+std::string getId(const Ice::Current& current)
+{
+    return current.adapter->getCommunicator()->identityToString(current.id);
+}
+}
+
+} // End of namespace AsteriskSCF
diff --git a/src/FilePlaybackSessionEndpoint.h b/src/FilePlaybackSessionEndpoint.h
new file mode 100644
index 0000000..c4fb9e6
--- /dev/null
+++ b/src/FilePlaybackSessionEndpoint.h
@@ -0,0 +1,44 @@
+/*
+ * 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 <SessionCommunications/SessionCommunicationsIf.h>
+#include <string>
+#include <Ice/Ice.h>
+
+namespace AsteriskSCF
+{
+namespace FilePlayback
+{
+    class FilePlaybackEndpointImpl;
+
+    class FilePlaybackEndpoint : public AsteriskSCF::SessionCommunications::V1::SessionEndpoint
+    {
+    public:
+        FilePlaybackEndpoint(const Ice::ObjectAdapterPtr& adapter, const AsteriskSCF::System::Logging::LoggerPtr&);
+        ~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&);
+
+    private:
+        FilePlaybackEndpointImpl* mImpl;
+    };
+} // 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/MediaEchoThread.cpp b/src/MediaEchoThread.cpp
index c9eccf1..21edf11 100644
--- a/src/MediaEchoThread.cpp
+++ b/src/MediaEchoThread.cpp
@@ -19,28 +19,75 @@ using namespace AsteriskSCF::TestUtil;
 
 MediaEchoThread::MediaEchoThread():
     mDone(false),
-    mPaused(true)
+    mPaused(true),
+    mInterval(IceUtil::Time::microSecondsDouble(1000000.0 / 8 * 1024))
+{
+}
+
+MediaEchoThread::MediaEchoThread(const unsigned long rateInHertz) :
+    mDone(false),
+    mPaused(true),
+    mInterval(IceUtil::Time::microSecondsDouble(1000000.0 / rateInHertz))
 {
 }
 
 void MediaEchoThread::run()
 {
-    IceUtil::Monitor<IceUtil::Mutex>::Lock lock(mMonitor);
-    while(!mDone)
+    //
+    // This loop is structured so that new frames can continuously be added as
+    // they are being sent.  The lock on the "main" frame sequence is only held
+    // long enough to copy the current frames and some other key state. Writing
+    // is done without holding the lock. A useful improvement might be to add
+    // an upper limit to the number of frames that can be "queued".
+    //
+    bool done = false;
+    AsteriskSCF::Media::V1::FrameSeq currentFrames;
+    size_t numberOfFramesWritten = 0;
+    AsteriskSCF::Media::V1::StreamSinkPrx sink;
+    while(true)
     {
-        mMonitor.wait();
+        {
+            IceUtil::Monitor<IceUtil::Mutex>::Lock lock(mMonitor);
+            mMonitor.wait();
 
-        if(!mDone && mFrames.size() > 0 && mSink != 0)
+            //
+            // Copy our working state. For this iteration.
+            done = mDone;
+            sink = mSink;
+            if(!done)
+            {
+                if(numberOfFramesWritten != 0)
+                {
+                    mFrames.erase(mFrames.begin(), mFrames.begin() + numberOfFramesWritten);
+                    numberOfFramesWritten = 0;
+                }
+                currentFrames.assign(mFrames.begin(), mFrames.end());
+            }
+        }
+        if(done)
+        {
+            break;
+        }
+
+        AsteriskSCF::Media::V1::FrameSeq::iterator current = currentFrames.begin();
+        while(current != currentFrames.end() && sink != 0)
         {
+            AsteriskSCF::Media::V1::FrameSeq buf(current, current+1);
             try
             {
-                mSink->write(mFrames);
-                mFrames.clear();
+                sink->write(buf);
+
+                //
+                // Waiting for the configured interval creates the proper frame "rate".
+                //
+                IceUtil::ThreadControl::sleep(mInterval);
             }
             catch(const Ice::Exception&)
             {
-                // TODO: log and continue
+                // TODO: log and continue.. the current frame will not be re-attempted.
             }
+            ++numberOfFramesWritten;
+            ++current;
         }
     }
 }
diff --git a/src/MediaEchoThread.h b/src/MediaEchoThread.h
index 6fc87fb..cbbd14c 100644
--- a/src/MediaEchoThread.h
+++ b/src/MediaEchoThread.h
@@ -22,13 +22,15 @@ class MediaEchoThread : public virtual IceUtil::Thread
 {
 public:
     MediaEchoThread();
+    MediaEchoThread(const unsigned long rateInHz);
     void run();
     void pushFrames(const AsteriskSCF::Media::V1::FrameSeq& newFrames);
     void destroy();
     void setSink(const AsteriskSCF::Media::V1::StreamSinkPrx& prx);
 
     //
-    // Use when paused... causes the loop to run once.
+    // Use when paused... causes the loop to run until the currently buffered
+    // frames have been sent.
     //
     void echo();
     void pause();
@@ -40,6 +42,13 @@ private:
     AsteriskSCF::Media::V1::FrameSeq mFrames;
     bool mDone;
     bool mPaused;
+    IceUtil::Time mInterval;
+
+    //
+    // Not implemented.
+    //
+    MediaEchoThread(const MediaEchoThread&);
+    void operator=(const MediaEchoThread&);
 };
 typedef IceUtil::Handle<MediaEchoThread> MediaEchoThreadPtr;
 
diff --git a/src/RemoteControl.ice b/src/RemoteControl.ice
new file mode 100644
index 0000000..8d066a3
--- /dev/null
+++ b/src/RemoteControl.ice
@@ -0,0 +1,66 @@
+/*
+ * 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 <Media/MediaIf.ice>
+#include <Ice/BuiltinSequences.ice>
+
+module AsteriskSCF
+{
+module RemoteControl
+{
+module V1
+{
+
+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 setController(SourceController* controller);
+    Ice::StringSeq listTracks();
+    void playTrack(string trackName);
+
+    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();
+};
+
+
+}; /* End of module V1 */
+}; /* End of module RemoteControl */
+}; /* End of module AsteriskSCF */
diff --git a/src/TestEndpoint.cpp b/src/TestEndpoint.cpp
index e912020..e9a7fc3 100644
--- a/src/TestEndpoint.cpp
+++ b/src/TestEndpoint.cpp
@@ -59,11 +59,12 @@ typedef IceUtil::Handle<InternalManagerIf> InternalManagerPtr;
 // Specialization of ListenerManagerT helper template for SessionListeners. Basically takes care of invoking the
 // appropriate method on a publisher.
 //
-class SessionListenerMgr : public AsteriskSCF::TestUtil::ListenerManagerT<AsteriskSCF::SessionCommunications::V1::SessionListenerPrx>
+class SessionListenerMgr : public AsteriskSCF::TestUtil::ListenerManagerT<AsteriskSCF::SessionCommunications::V1::SessionListenerPrx>,
+                           public IceUtil::Shared
 {
 public:
     SessionListenerMgr(const Ice::CommunicatorPtr& communicator, const std::string& topicName) :
-        AsteriskSCF::TestUtil::ListenerManagerT<AsteriskSCF::SessionCommunications::V1::SessionListenerPrx>(communicator, topicName)
+        AsteriskSCF::TestUtil::ListenerManagerT<AsteriskSCF::SessionCommunications::V1::SessionListenerPrx>(communicator, topicName, true)
     {
         mPublisher = AsteriskSCF::SessionCommunications::V1::SessionListenerPrx::uncheckedCast(mTopic->getPublisher());
     }

commit 4bca5d0f23c4c5273d8f23f6b934144b49ca089f
Author: David M. Lee <dlee at digium.com>
Date:   Tue Nov 30 16:47:44 2010 -0600

    Move EndpointLocatorI to anonymous namespace.
    
    Old versions of gcc (v4.1.2) erroneously warn when class members are
    from an anonymous namespace.  While there's technically nothing wrong
    with it, the class EndpointLocatorI is only used in this file, so can
    safely be moved to an anonymous namespace.

diff --git a/src/TestEndpoint.cpp b/src/TestEndpoint.cpp
index 630ea70..e912020 100644
--- a/src/TestEndpoint.cpp
+++ b/src/TestEndpoint.cpp
@@ -515,12 +515,6 @@ public:
 private:
     EndpointManagerPtr mMgr;
 };
-}
-
-namespace AsteriskSCF
-{
-namespace TestUtil
-{
 
 class EndpointLocatorI : public AsteriskSCF::Core::Routing::V1::EndpointLocator
 {
@@ -554,6 +548,13 @@ private:
 
 typedef IceUtil::Handle<EndpointLocatorI> EndpointLocatorIPtr;
 
+}
+
+namespace AsteriskSCF
+{
+namespace TestUtil
+{
+
 AsteriskSCF::Core::Routing::V1::EndpointLocatorPtr TestEndpoint::create(const Ice::ObjectAdapterPtr& adapter, const std::string& id)
 {
 

commit 93501e90428b9c45b02316d13eee8b087c4fea6c
Author: David M. Lee <dlee at digium.com>
Date:   Mon Nov 15 14:03:39 2010 -0600

    Updated indentation to match the style guide.

diff --git a/src/ConsoleDriver.h b/src/ConsoleDriver.h
index 605e876..ed267e6 100644
--- a/src/ConsoleDriver.h
+++ b/src/ConsoleDriver.h
@@ -28,7 +28,7 @@ class ConsoleDriver : virtual public IceUtil::Thread
 {
 public:
     ConsoleDriver();
-    
+
     void run();
     void setHandler(const AsteriskSCF::TestChannel::V1::CommandsPrx& handler);
     void write(const std::string& msg);
diff --git a/src/InternalExceptions.h b/src/InternalExceptions.h
index 9f43732..c5e104b 100644
--- a/src/InternalExceptions.h
+++ b/src/InternalExceptions.h
@@ -17,37 +17,37 @@ namespace AsteriskSCF
 {
 namespace TestUtil
 {
-    class ConfigException : public std::exception
-    {
-    public:
+class ConfigException : public std::exception
+{
+public:
 
-        ConfigException(const std::string& propertyName, const std::string& message) 
+    ConfigException(const std::string& propertyName, const std::string& message)
+    {
+        std::stringstream msg;
+        msg << propertyName << " configuration error: ";
+        if(message.size() != 0)
         {
-            std::stringstream msg;
-            msg << propertyName << " configuration error: ";
-            if(message.size() != 0)
-            {
-                msg << message;
-            }
-            else
-            {
-                msg << "(no message)";
-            }
-            mWhat = msg.str();
+            msg << message;
         }
-
... 6043 lines suppressed ...


-- 
team/beagles/fileplayback.git



More information about the asterisk-scf-commits mailing list