[asterisk-scf-commits] asterisk-scf/integration/file_media_service.git branch "initial_development" updated.

Commits to the Asterisk SCF project code repositories asterisk-scf-commits at lists.digium.com
Fri Sep 30 06:22:24 CDT 2011


branch "initial_development" has been updated
       via  305536948c8fdc092042fd1593aa05dd39fb8242 (commit)
       via  d15a3b95c4ef4b6d6f9e6587c3776393ac220b23 (commit)
       via  4a07e77cda06ac0f059664dd2c4ef8dc42a381f9 (commit)
      from  91de09a86569f0cd197cacff890f77594c93677b (commit)

Summary of changes:
 CMakeLists.txt                        |    5 +
 src/CMakeLists.txt                    |    3 +
 src/ContainerImpl.cpp                 |   51 +++-
 src/MatroskaDefines.h                 |   92 +++++
 src/MatroskaUtils.cpp                 |  681 +++++++++++++++++++++++++++++++++
 src/MatroskaUtils.h                   |  155 ++++++++
 src/OpaqueContainerData.h             |   15 +-
 src/StreamImpl.cpp                    |   55 +++-
 src/StreamImpl.h                      |   11 +-
 test/CMakeLists.txt                   |   10 +
 test/UnitTest_ContainerRepository.cpp |  151 +++++++-
 11 files changed, 1214 insertions(+), 15 deletions(-)
 create mode 100755 src/MatroskaDefines.h
 create mode 100755 src/MatroskaUtils.cpp
 create mode 100755 src/MatroskaUtils.h


- Log -----------------------------------------------------------------
commit 305536948c8fdc092042fd1593aa05dd39fb8242
Author: Brent Eagles <beagles at digium.com>
Date:   Fri Sep 30 08:50:34 2011 -0230

    Simple mux test finally works... kind of. Duration is currently messed.

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 13589b2..7f8d7ff 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -24,6 +24,7 @@ astscf_component_add_files(FileMediaService
 	ReplicationListener.h
 	RepositoryConfigurationAdapter.h
 	RepositoryReplicationAdapter.h
+        MatroskaDefines.h
         MatroskaUtils.h
         MatroskaUtils.cpp
 )
diff --git a/src/MatroskaUtils.cpp b/src/MatroskaUtils.cpp
index 8281182..bc6f2fe 100755
--- a/src/MatroskaUtils.cpp
+++ b/src/MatroskaUtils.cpp
@@ -34,6 +34,7 @@
 
 #include <sstream>
 #include <map>
+#include <iostream>
 
 #include <time.h>
 
@@ -53,6 +54,16 @@ namespace MatroskaContainer
 namespace Implementation
 {
 
+int AudioTrackType::getType() const
+{
+    return track_audio;
+}
+
+int VideoTrackType::getType() const
+{
+    return track_video;
+}
+
 void Header::writeTo(const OpaqueContainerDataPtr& data)
 {
     EbmlHead header;
@@ -77,7 +88,7 @@ typedef boost::shared_ptr<SegmentImpl> SegmentImplPtr;
 class Cluster 
 {
 public:
-    Cluster(const boost::shared_ptr<KaxCluster>& cluster, const SegmentImplPtr& segment, 
+    Cluster(KaxCluster* cluster, const SegmentImplPtr& segment, 
         uint64 initialTimeCode, uint64 lastTimeCode);
     uint64 startTimeCode() const;
     void render(IOCallback* io, KaxCues* cues);
@@ -86,7 +97,7 @@ public:
     uint64 size() const;
 
 private:
-    boost::shared_ptr<KaxCluster> mCluster;
+    KaxCluster* mCluster;
     SegmentImplPtr mSegment;
     const uint64 mTimeCode;
     uint64 mSize;
@@ -99,7 +110,7 @@ public:
     TrackImpl(const SegmentImplPtr& segment, KaxTrackEntry* t, unsigned char number);
     void setCodecAndType(const CodecPtr& codec, const TrackTypePtr& trackType);
     void addTag(const string& name, const string& value);
-    void write(uint64 timeCode, uint32 duration, const vector<unsigned char>& data);
+    void write(uint64 timeCode, uint32 duration, const unsigned char* data, size_t len);
     TagSeq getTags();
     void finished();
     uint64 getID();
@@ -111,6 +122,9 @@ private:
     TrackTypePtr mTrackType;
     KaxBlockGroup* mGroup;
     TagSeq mTags;
+    uint64 mLastTimeCode;
+    uint32 mDuration;
+    uint64 mByteCount;
 };
 
 typedef boost::shared_ptr<TrackImpl> TrackImplPtr;
@@ -157,9 +171,12 @@ private:
 
     typedef map<uint64, TagSeq> TrackTags;
     TrackTags mTrackTags;
+
+    size_t mInitialSegmentSize;
+    uint64 mClusterCount;
 };
 
-Cluster::Cluster(const boost::shared_ptr<KaxCluster>& cluster, const SegmentImplPtr& segment, 
+Cluster::Cluster(KaxCluster* cluster, const SegmentImplPtr& segment, 
         uint64 initialTimeCode, uint64 lastTimeCode) :
       mCluster(cluster),
       mSegment(segment),
@@ -168,8 +185,9 @@ Cluster::Cluster(const boost::shared_ptr<KaxCluster>& cluster, const SegmentImpl
 {
     mCluster->SetParent(segment->kax());
     mCluster->SetGlobalTimecodeScale(segment->globalTimeScale());
-    mCluster->SetPreviousTimecode(lastTimeCode, segment->globalTimeScale());
+    mCluster->SetPreviousTimecode(lastTimeCode * segment->globalTimeScale(), segment->globalTimeScale());
     mCluster->InitTimecode(initialTimeCode, segment->globalTimeScale());
+    mCluster->EnableChecksum();
     KaxClusterTimecode& tc = GetChild<KaxClusterTimecode>(*mCluster);
     EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(&tc);
     assert(uintField);
@@ -189,7 +207,7 @@ void Cluster::render(IOCallback* io, KaxCues* cues)
 
 KaxCluster* Cluster::kax()
 {
-    return mCluster.get();
+    return mCluster;
 }
 
 uint64 Cluster::increaseSize(uint64 addThisMuch)
@@ -206,14 +224,12 @@ uint64 Cluster::size() const
 TrackImpl::TrackImpl(const SegmentImplPtr& segment, KaxTrackEntry* t, unsigned char number) :
     Track(number),
     mSegment(segment),
-    mTrack(t)
+    mTrack(t),
+    mGroup(0),
+    mLastTimeCode(0),
+    mDuration(0),
+    mByteCount(0)
 {
-    //
-    // We aren't supporting lacing at the moment.
-    //
-    EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(&GetChild<KaxTrackFlagLacing>(*mTrack));
-    assert(uintField);
-    *uintField = false;
 }
 
 void TrackImpl::setCodecAndType(const CodecPtr& codec, const TrackTypePtr& trackType)
@@ -319,28 +335,39 @@ void TrackImpl::addTag(const string& name, const string& value)
     mTags.push_back(Tag(name, value));
 }
 
-void TrackImpl::write(uint64 timeCode, uint32 duration, const vector<unsigned char>& data)
+void TrackImpl::write(uint64 timeCode, uint32 duration, const unsigned char* data, size_t len)
 {
     ClusterPtr cluster = mSegment->getCluster(timeCode);
-    if (mGroup)
+    if (!mGroup)
+    {
+        mGroup = &cluster->kax()->GetNewBlock();
+        mGroup->SetParent(*cluster->kax());
+        mGroup->SetParentTrack(*mTrack);
+    }
+    else if (mGroup->GetParentCluster() != cluster->kax())
     {
         mGroup = &cluster->kax()->GetNewBlock();
         mGroup->SetParent(*cluster->kax());
+        mGroup->SetParentTrack(*mTrack);
     }
+    mByteCount += len;
 
     KaxBlock& block = GetChild<KaxBlock>(*mGroup);
     block.SetParent(*cluster->kax());
-    //
-    // We aren't using lacing .. yet... so just add the frame.
-    //
-    SimpleDataBuffer buf((unsigned char*)(&data.begin()), static_cast<uint32>(data.size()), 0);
-    block.AddFrame(*mTrack, timeCode * mSegment->globalTimeScale(), buf);
-    cluster->increaseSize(buf.Size());
-    mSegment->addTime(timeCode + duration);
-    KaxBlockDuration& blockDuration = GetChild<KaxBlockDuration>(*mGroup);
-    EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(&blockDuration);
-    assert(uintField);
-    *uintField = duration;
+    binary* copiedData = new binary[len];
+    memcpy(copiedData, data, len);
+    SimpleDataBuffer* buf = new SimpleDataBuffer(copiedData, static_cast<uint32>(len), 0);
+    bool blockWantsMore = block.AddFrame(*mTrack, mLastTimeCode * mSegment->globalTimeScale(), *buf);
+    cluster->increaseSize(buf->Size());
+    mDuration += duration;
+    mGroup->SetBlockDuration(mDuration * mSegment->globalTimeScale());
+    if (!blockWantsMore)
+    {
+        mGroup = 0;
+        mLastTimeCode = mDuration + mLastTimeCode; 
+        mSegment->addTime(mDuration);
+        mDuration = 0;
+    }
 
     //
     // We are not doing references at this time so nothing to do there.
@@ -366,7 +393,9 @@ void TrackImpl::finished()
     {
         throw IncompleteException();
     }
+    mSegment->addTime(mDuration);
     mSegment->commit(shared_from_this());
+    cerr << "Track " << trackNumber() << " wrote " << mByteCount << " bytes" << endl;
 }
 
 uint64 TrackImpl::getID()
@@ -379,7 +408,9 @@ SegmentImpl::SegmentImpl() :
     mMaxTimePerCluster(1000),
     mPrevTime(0xFFFFFFFFFFFFFFFF),
     mPrevTrack(0xFFFF),
-    mLastTime(0)
+    mLastTime(0),
+    mInitialSegmentSize(0),
+    mClusterCount(0)
 {
     //
     // initialize helper members into the segment.
@@ -394,7 +425,7 @@ SegmentImpl::SegmentImpl() :
 void SegmentImpl::addTo(const OpaqueContainerDataPtr& data)
 {
     mContainer = data;
-    mSegment.WriteHead(*(data->getIO()), 10);
+    mInitialSegmentSize = mSegment.WriteHead(*(data->getIO()), 5);
     //
     // Make space for seek head.
     //
@@ -479,6 +510,9 @@ TrackPtr SegmentImpl::getTrack(unsigned char track)
         assert(stringField);
         *stringField = "eng";
         t->SetGlobalTimecodeScale(mTimeCodeScale);
+        uintField = dynamic_cast<EbmlUInteger*>(&GetChild<KaxTrackFlagLacing>(*t));
+        assert(uintField);
+        *uintField = true;
     }
     else
     {
@@ -495,6 +529,7 @@ void SegmentImpl::close()
         mActiveCluster->render(mContainer->getIO(), mCues);
         mActiveCluster.reset();
     }
+    cerr << "Cluster count : " << mClusterCount << endl;
 
     if (mCues && mCues->ListSize() != 0)
     {
@@ -524,6 +559,7 @@ void SegmentImpl::close()
             mSeekHead->IndexThis(*mCues, mSegment);
         }
     }
+    size_t trackSize = mTracks->Render(*mContainer->getIO());
 
     //
     // TODO: Add tag support.
@@ -573,18 +609,19 @@ void SegmentImpl::close()
             *unicodeField = utf;
         }
     }
-    tags.Render(*mContainer->getIO());
+    size_t tagsSize = tags.Render(*mContainer->getIO());
+
+    size_t metaSeekSize = 0;
     if (mSeekHead)
     {
         mSeekHead->IndexThis(tags, mSegment);
     }
     if (mIndexSpace)
     {
-        mIndexSpace->ReplaceWith(*mSeekHead, *mContainer->getIO());
+        metaSeekSize = mIndexSpace->ReplaceWith(*mSeekHead, *mContainer->getIO());
     }
     mContainer->getIO()->setFilePointer(0, seek_end);
-
-    uint64 newSize = mContainer->getIO()->getFilePointer() - mSegment.GetElementPosition() - mSegment.HeadSize();
+    uint64 newSize = mContainer->getIO()->getFilePointer(); //  - mSegment.GetElementPosition() - mSegment.HeadSize();
     if (mSegment.ForceSize(newSize))
     {
         mSegment.OverwriteHead(*mContainer->getIO());
@@ -607,7 +644,8 @@ ClusterPtr SegmentImpl::getCluster(uint64 currentTimeCode)
         mActiveCluster->render(mContainer->getIO(), mCues);
         oldTimeCode = mActiveCluster->startTimeCode();
     }
-    mActiveCluster.reset(new Cluster(boost::shared_ptr<KaxCluster>(new KaxCluster), shared_from_this(), currentTimeCode, oldTimeCode));
+    ++mClusterCount;
+    mActiveCluster.reset(new Cluster(new KaxCluster, shared_from_this(), currentTimeCode, oldTimeCode));
     return mActiveCluster;
 }
 
diff --git a/src/MatroskaUtils.h b/src/MatroskaUtils.h
index 9f71826..ce1aaf3 100755
--- a/src/MatroskaUtils.h
+++ b/src/MatroskaUtils.h
@@ -69,12 +69,20 @@ class TrackType
 public:
     virtual ~TrackType() {}
     virtual int getType() const = 0;
+};
+typedef boost::shared_ptr<TrackType> TrackTypePtr;
 
-protected:
-    
+class AudioTrackType : public TrackType
+{
+public:
+    int getType() const;
 };
 
-typedef boost::shared_ptr<TrackType> TrackTypePtr;
+class VideoTrackType : public TrackType
+{
+public:
+    int getType() const;
+};
 
 class Segment;
 typedef boost::shared_ptr<Segment> SegmentPtr;
@@ -107,12 +115,9 @@ public:
 
     virtual void setCodecAndType(const CodecPtr& codec, const TrackTypePtr& trackType) = 0;
     virtual void addTag(const std::string& name, const std::string& value) = 0;
-    virtual void write(uint64 timeCode, uint32 duration, const std::vector<unsigned char>& data) = 0;
-
+    virtual void write(uint64 timeCode, uint32 duration, const unsigned char* data, size_t len) = 0;
     virtual TagSeq getTags() = 0;
-
     virtual void finished() = 0;
-
     virtual uint64 getID() = 0;
 
 protected:
@@ -132,16 +137,11 @@ class Segment
     //
     Segment(const Segment&);
     void operator=(const Segment&);
-
-
 public:
 
     virtual ~Segment() {}
-
     virtual void addTo(const OpaqueContainerDataPtr& data) = 0;
-
     virtual TrackPtr getTrack(unsigned char trackNumber) = 0;
-
     virtual void close() = 0;
 
     static SegmentPtr create();
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 29dcb75..d452776 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -9,8 +9,14 @@ include_directories(${astscf-matroska_dir})
 astscf_slice_include_collection(FILEMEDIASERVICE)
 astscf_component_init(UnitTestContainerRepository)
 astscf_component_add_files(UnitTestContainerRepository UnitTest_ContainerRepository.cpp)
+astscf_component_add_files(UnitTestContainerRepository ../src/OpaqueContainerData.h)
+astscf_component_add_files(UnitTestContainerRepository ../src/ContainerImpl.h)
 astscf_component_add_files(UnitTestContainerRepository ../src/ContainerImpl.cpp)
+astscf_component_add_files(UnitTestContainerRepository ../src/ContainerInfoImpl.h)
 astscf_component_add_files(UnitTestContainerRepository ../src/ContainerInfoImpl.cpp)
+astscf_component_add_files(UnitTestContainerRepository ../src/MatroskaDefines.h)
+astscf_component_add_files(UnitTestContainerRepository ../src/MatroskaUtils.h)
+astscf_component_add_files(UnitTestContainerRepository ../src/MatroskaUtils.cpp)
 
 astscf_component_add_boost_libraries(UnitTestContainerRepository unit_test_framework core filesystem)
 astscf_component_add_slice_collection_libraries(UnitTestContainerRepository ASTSCF)
@@ -18,4 +24,8 @@ astscf_component_add_slice_collection_libraries(UnitTestContainerRepository FILE
 astscf_component_build_standalone(UnitTestContainerRepository)
 
 target_link_libraries(UnitTestContainerRepository logging-client astscf-ice-util-cpp astscf-matroska astscf-ebml)
+pjproject_link(UnitTestContainerRepository pjmedia)
+pjproject_link(UnitTestContainerRepository pjlib)
+pjproject_link(UnitTestContainerRepository pjlib-util)
+
 astscf_test_boost(UnitTestContainerRepository)
diff --git a/test/UnitTest_ContainerRepository.cpp b/test/UnitTest_ContainerRepository.cpp
index 2be115e..f30b2a3 100644
--- a/test/UnitTest_ContainerRepository.cpp
+++ b/test/UnitTest_ContainerRepository.cpp
@@ -23,11 +23,61 @@
 #include <boost/test/debug.hpp>
 #include <boost/bind.hpp>
 
+#include <ebml/StdIOCallback.h>
+#include <matroska/FileKax.h>
+
+#include "../src/MatroskaDefines.h"
+#include "../src/MatroskaUtils.h"
+#include "../src/OpaqueContainerData.h"
 #include "../src/ContainerRepository.cpp"
 
+#include <pjmedia.h>
+#include <pjlib.h>
+
 using namespace AsteriskSCF::MatroskaContainer::Implementation;
 using namespace AsteriskSCF::System::Logging;
 using namespace boost::unit_test;
+using namespace std;
+using namespace libmatroska;
+using namespace libebml;
+
+
+class TestOpaqueContainer : public OpaqueContainerData
+{
+public:
+    TestOpaqueContainer(const string& name, bool writing) :
+      mIO(0)
+    {
+        mIO = new StdIOCallback(name.c_str(), (writing ? MODE_CREATE : MODE_READ));
+        mMatroska.reset(new FileMatroska(*mIO));
+    }
+
+    ~TestOpaqueContainer()
+    {
+        mIO->close();
+        //
+        // Make sure we release the file before we delete the callback.. lest things go completely snakey.
+        //
+        mMatroska.reset();
+
+        delete mIO;
+    }
+
+    MatroskaFilePtr getFile() const
+    {
+        return mMatroska;
+    }
+
+    IOCallback* getIO() const 
+    {
+        return mIO;
+    }
+
+private:
+
+    MatroskaFilePtr mMatroska;
+    StdIOCallback* mIO;
+};
 
 class ContainerRepositoryTests
 {
@@ -118,6 +168,98 @@ public:
         }
     }
 
+    class Mono16BitWav : public AudioCodec
+    {
+    public:
+        Mono16BitWav(double samplingRate) :
+            mSampleRate(samplingRate)
+        {
+        }
+
+        string id() const
+        {
+            return MatroskaAudioPCM;
+        }
+
+        vector<unsigned char> bytes() const { return vector<unsigned char>(); }
+
+        unsigned int channels() { return 1; }
+        double sampleRate() { return mSampleRate; }
+        unsigned int bitDepth() { return 16; }
+        double outputSampleRate() { return 0.0; }
+    private:
+        double mSampleRate;
+    };
+
+    
+    
+    //
+    // MatroskaUtils directed tests.
+    //
+    void testSimpleMux()
+    {
+        string containerFileName = mCommunicator->getProperties()->getPropertyWithDefault("Test.SimpleMux.MkvFile", "simplemux.mkv");
+        OpaqueContainerDataPtr container = new TestOpaqueContainer(containerFileName, true);
+        BOOST_REQUIRE(container != 0);
+        Header newHeader;
+        newHeader.writeTo(container);
+        SegmentPtr segment(Segment::create());
+        BOOST_REQUIRE(segment);
+        segment->addTo(container);
+        TrackPtr track = segment->getTrack(1);
+        BOOST_REQUIRE(track);
+        TrackTypePtr trackType(new AudioTrackType);
+        CodecPtr codec (new Mono16BitWav(44.1));
+        track->setCodecAndType(codec, trackType);
+        track->addTag("test_tag", "some test value");
+        boost::shared_ptr<pj_caching_pool> cachingPool(new pj_caching_pool);
+        pj_caching_pool_init(cachingPool.get(), &pj_pool_factory_default_policy, 0); 
+        pj_pool_t* pool = pj_pool_create(&cachingPool->factory, "testpool", 1024, 1024, 0);
+        pjmedia_port* port = 0;
+        string wavFilename =  mCommunicator->getProperties()->getPropertyWithDefault("Test.SimpleMux.WavFile", "simplemux.wav");
+        pj_status_t result = pjmedia_wav_player_port_create(pool, wavFilename.c_str(), 20, PJMEDIA_FILE_NO_LOOP, 0, &port);
+        BOOST_REQUIRE(result == PJ_SUCCESS);
+        BOOST_REQUIRE(port != 0);
+        pjmedia_frame frame;
+        pj_int16_t frameDataBuf[441];
+        frame.buf = frameDataBuf;
+        frame.size = sizeof(frameDataBuf);
+        result = pjmedia_port_get_frame(port, &frame);
+        uint64 totalTime = 0;
+        assert(frame.type == PJMEDIA_TYPE_AUDIO);
+        uint64 totalBytes = 0;
+        try
+        {
+            while (result == PJ_SUCCESS && frame.type != PJMEDIA_FRAME_TYPE_NONE)
+            {
+                totalBytes += frame.size;
+                track->write(totalTime, 20, static_cast<const unsigned char*>(frame.buf), frame.size);
+                totalTime += 20;
+                result = pjmedia_port_get_frame(port, &frame);
+            }
+            cerr << "Wrote " << totalBytes << " bytes";
+
+            track->finished();
+            segment->close();
+            BOOST_MESSAGE("Yay, we successfully wrote the data.. maybe, you will have to check it");
+        }
+        catch (const exception& ex)
+        {
+            BOOST_MESSAGE(ex.what());
+            pjmedia_port_destroy(port);
+        }
+        catch (...)
+        {
+            BOOST_MESSAGE("UNEXPECTED EXCEPTION");
+            pjmedia_port_destroy(port);
+        }
+
+    }
+
+    void testMoreComplexMux()
+    {
+    }
+
 private:
     Ice::CommunicatorPtr mCommunicator;
     Ice::ObjectAdapterPtr mObjectAdapter;
@@ -128,8 +270,10 @@ boost::shared_ptr<ContainerRepositoryTests> globalTestSuite;
 
 bool init_unit_test()
 {
-    framework::master_test_suite().add(BOOST_TEST_CASE(boost::bind(&ContainerRepositoryTests::rootPathTest, globalTestSuite)));
-    framework::master_test_suite().add(BOOST_TEST_CASE(boost::bind(&ContainerRepositoryTests::rootPathScanTest, globalTestSuite)));
+    pj_init();
+    // framework::master_test_suite().add(BOOST_TEST_CASE(boost::bind(&ContainerRepositoryTests::rootPathTest, globalTestSuite)));
+    // framework::master_test_suite().add(BOOST_TEST_CASE(boost::bind(&ContainerRepositoryTests::rootPathScanTest, globalTestSuite)));
+    framework::master_test_suite().add(BOOST_TEST_CASE(boost::bind(&ContainerRepositoryTests::testSimpleMux, globalTestSuite)));
     return true;
 }
 
@@ -137,6 +281,7 @@ int main(int argc, char** argv)
 {
     Logger logger(getLoggerFactory().getLogger("AsteriskSCF.UnitTest.ContainerRepository"));
 
+    matroska_init();
     try
     {
         Ice::CommunicatorPtr communicator(Ice::initialize(argc, argv));
@@ -156,6 +301,6 @@ int main(int argc, char** argv)
         logger(Error) << "A complete unexpected exception has occurred... boo!";
         return EXIT_FAILURE;
     }
-        
+    matroska_done();
     return EXIT_SUCCESS;
 }
\ No newline at end of file

commit d15a3b95c4ef4b6d6f9e6587c3776393ac220b23
Author: Brent Eagles <beagles at digium.com>
Date:   Thu Sep 29 15:29:39 2011 -0230

    Added tags .. probably going to need them to identify multiple tracks.

diff --git a/src/MatroskaUtils.cpp b/src/MatroskaUtils.cpp
index f443218..8281182 100755
--- a/src/MatroskaUtils.cpp
+++ b/src/MatroskaUtils.cpp
@@ -29,8 +29,11 @@
 #include <matroska/KaxCues.h>
 #include <matroska/KaxSegment.h>
 #include <matroska/KaxVersion.h>
+#include <matroska/KaxTrackAudio.h>
+#include <matroska/KaxTrackVideo.h>
 
 #include <sstream>
+#include <map>
 
 #include <time.h>
 
@@ -50,28 +53,6 @@ namespace MatroskaContainer
 namespace Implementation
 {
 
-class BaseCodecImpl
-{
-public:
-    BaseCodecImpl(const string& codecString) :
-      mCodecString(codecString)
-    {
-    }
-
-    vector<unsigned char> bytes() const
-    {
-        return vector<unsigned char>();
-    }
-
-    string id() const
-    {
-        return mCodecString;
-    }
-
-private:
-    string mCodecString;
-};
-
 void Header::writeTo(const OpaqueContainerDataPtr& data)
 {
     EbmlHead header;
@@ -112,13 +93,16 @@ private:
 };
 typedef boost::shared_ptr<Cluster> ClusterPtr;
 
-class TrackImpl : public Track
+class TrackImpl : public Track, public boost::enable_shared_from_this<TrackImpl>
 {
 public:
     TrackImpl(const SegmentImplPtr& segment, KaxTrackEntry* t, unsigned char number);
     void setCodecAndType(const CodecPtr& codec, const TrackTypePtr& trackType);
+    void addTag(const string& name, const string& value);
     void write(uint64 timeCode, uint32 duration, const vector<unsigned char>& data);
+    TagSeq getTags();
     void finished();
+    uint64 getID();
 
 private:
     SegmentImplPtr mSegment;
@@ -126,8 +110,11 @@ private:
     CodecPtr mCodec;
     TrackTypePtr mTrackType;
     KaxBlockGroup* mGroup;
+    TagSeq mTags;
 };
 
+typedef boost::shared_ptr<TrackImpl> TrackImplPtr;
+
 class SegmentImpl : public Segment, public boost::enable_shared_from_this<SegmentImpl>
 {
 public:
@@ -144,7 +131,8 @@ public:
     KaxSegment& kax();
     uint64 globalTimeScale() const;
     uint64 addTime(uint64 moreTime);
-    void addGroupToCues(KaxBlock* block);
+
+    void commit(const TrackImplPtr& track);
 
 private:
     KaxSegment mSegment;
@@ -166,6 +154,9 @@ private:
     uint64 mPrevTime;
     uint16 mPrevTrack;
     uint64 mLastTime;
+
+    typedef map<uint64, TagSeq> TrackTags;
+    TrackTags mTrackTags;
 };
 
 Cluster::Cluster(const boost::shared_ptr<KaxCluster>& cluster, const SegmentImplPtr& segment, 
@@ -217,12 +208,115 @@ TrackImpl::TrackImpl(const SegmentImplPtr& segment, KaxTrackEntry* t, unsigned c
     mSegment(segment),
     mTrack(t)
 {
+    //
+    // We aren't supporting lacing at the moment.
+    //
+    EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(&GetChild<KaxTrackFlagLacing>(*mTrack));
+    assert(uintField);
+    *uintField = false;
 }
 
 void TrackImpl::setCodecAndType(const CodecPtr& codec, const TrackTypePtr& trackType)
 {
     mCodec = codec;
     mTrackType = trackType;
+    EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(&GetChild<KaxTrackType>(*mTrack));
+    assert(uintField);
+    *uintField = trackType->getType();
+    EbmlString* stringField = dynamic_cast<EbmlString*>(&GetChild<KaxCodecID>(*mTrack));
+    assert(stringField);
+    *stringField = codec->id();
+    KaxCodecPrivate& privateData = GetChild<KaxCodecPrivate>(*mTrack);
+    privateData.CopyBuffer((unsigned char*)&(codec->bytes().begin()), static_cast<uint32>(codec->bytes().size()));
+
+    if (mTrackType->getType() == track_audio)
+    {
+        KaxTrackAudio& audio = GetChild<KaxTrackAudio>(*mTrack);
+        AudioCodecPtr audioCodec = boost::dynamic_pointer_cast<AudioCodec>(codec);
+        assert(audioCodec);
+        if (audioCodec->channels() != 0)
+        {
+            uintField = dynamic_cast<EbmlUInteger*>(&GetChild<KaxAudioChannels>(audio));
+            assert(uintField);
+            *uintField = audioCodec->channels();
+        }
+
+        if (audioCodec->sampleRate() != 0)
+        {
+            KaxAudioSamplingFreq& freq = GetChild<KaxAudioSamplingFreq>(audio);
+            EbmlFloat* floatField = dynamic_cast<EbmlFloat*>(&freq);
+            assert(floatField);
+            *floatField = audioCodec->sampleRate();
+            freq.ValidateSize();
+        }
+
+        if (audioCodec->outputSampleRate() > 0.0)
+        {
+            KaxAudioOutputSamplingFreq& freq = GetChild<KaxAudioOutputSamplingFreq>(audio);
+            EbmlFloat* floatField = dynamic_cast<EbmlFloat*>(&freq);
+            assert(floatField);
+            *floatField = audioCodec->sampleRate();
+            freq.ValidateSize();
+        }
+
+        if (audioCodec->bitDepth() != 0)
+        {
+            uintField = dynamic_cast<EbmlUInteger*>(&GetChild<KaxAudioBitDepth>(audio));
+            assert(uintField);
+            *uintField = audioCodec->bitDepth();
+        }
+    }
+    else
+    {
+        VideoCodecPtr videoCodec = boost::dynamic_pointer_cast<VideoCodec>(codec);
+        assert(videoCodec);
+
+        KaxTrackVideo& video = GetChild<KaxTrackVideo>(*mTrack);
+        unsigned int defaultDisplayWidth = 0;
+        if (videoCodec->width() != 0)
+        {
+            uintField = dynamic_cast<EbmlUInteger*>(&GetChild<KaxVideoPixelWidth>(video));
+            assert(uintField);
+            *uintField = videoCodec->width();
+            defaultDisplayWidth = videoCodec->width();
+        }
+        if (videoCodec->displayWidth() != 0)
+        {
+            defaultDisplayWidth = videoCodec->displayWidth();
+        }
+
+        unsigned int defaultDisplayHeight = 0;
+        if (videoCodec->height() != 0)
+        {
+            uintField = dynamic_cast<EbmlUInteger*>(&GetChild<KaxVideoPixelHeight>(video));
+            assert(uintField);
+            *uintField = videoCodec->height();
+            defaultDisplayHeight = videoCodec->height();
+        }
+        if (videoCodec->displayHeight() != 0)
+        {
+            defaultDisplayHeight = videoCodec->displayHeight();
+        }
+
+        if (defaultDisplayWidth != 0)
+        {
+            uintField = dynamic_cast<EbmlUInteger*>(&GetChild<KaxVideoDisplayWidth>(video));
+            assert(uintField);
+            *uintField = defaultDisplayWidth;
+        }
+
+        if (defaultDisplayHeight != 0)
+        {
+            uintField = dynamic_cast<EbmlUInteger*>(&GetChild<KaxVideoDisplayHeight>(video));
+            assert(uintField);
+            *uintField = defaultDisplayHeight;
+        }
+    }
+}
+
+void TrackImpl::addTag(const string& name, const string& value)
+{
+    mTags.push_back(Tag(name, value));
 }
 
 void TrackImpl::write(uint64 timeCode, uint32 duration, const vector<unsigned char>& data)
@@ -230,7 +324,7 @@ void TrackImpl::write(uint64 timeCode, uint32 duration, const vector<unsigned ch
     ClusterPtr cluster = mSegment->getCluster(timeCode);
     if (mGroup)
     {
-        mGroup = cluster->kax()->GetNewBlock();
+        mGroup = &cluster->kax()->GetNewBlock();
         mGroup->SetParent(*cluster->kax());
     }
 
@@ -239,31 +333,45 @@ void TrackImpl::write(uint64 timeCode, uint32 duration, const vector<unsigned ch
     //
     // We aren't using lacing .. yet... so just add the frame.
     //
-    SimpleDataBuffer buf((unsigned char*)(&data.begin()), data.size(), 0);
+    SimpleDataBuffer buf((unsigned char*)(&data.begin()), static_cast<uint32>(data.size()), 0);
     block.AddFrame(*mTrack, timeCode * mSegment->globalTimeScale(), buf);
     cluster->increaseSize(buf.Size());
     mSegment->addTime(timeCode + duration);
     KaxBlockDuration& blockDuration = GetChild<KaxBlockDuration>(*mGroup);
-    EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(blockDuration);
+    EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(&blockDuration);
     assert(uintField);
     *uintField = duration;
 
     //
-    // We are not doing references at this time so we just update Cue if handling
-    // video.
+    // We are not doing references at this time so nothing to do there.
+    // TODO: Supposedly there is something we should be doing with the cues if 
+    // this is a video track.
     //
-    if (GetChild<KaxTrackType>(*mTrack) == track_video)
+    if ((uint8)(GetChild<KaxTrackType>(*mTrack)) == track_video)
     {
-        mSegment->addGroupToCues(block);
+        //
+        // TODO: update cues.
+        //
     }
 }
 
+TagSeq TrackImpl::getTags()
+{
+    return mTags;
+}
+
 void TrackImpl::finished()
 {
     if (!mTrackType || !mCodec)
     {
         throw IncompleteException();
     }
+    mSegment->commit(shared_from_this());
+}
+
+uint64 TrackImpl::getID()
+{
+    return static_cast<uint64>(GetChild<KaxTrackUID>(*mTrack));
 }
 
 SegmentImpl::SegmentImpl() :
@@ -280,6 +388,7 @@ SegmentImpl::SegmentImpl() :
     mIndexSpace = &GetChild<EbmlVoid>(mSegment);
     mTracks = &GetChild<KaxTracks>(mSegment);
     mLeftOverSeek = &GetChild<KaxSeekHead>(mSegment);
+    mCues = 0;
 }
 
 void SegmentImpl::addTo(const OpaqueContainerDataPtr& data)
@@ -354,7 +463,7 @@ TrackPtr SegmentImpl::getTrack(unsigned char track)
         }
         else
         {
-            t = &GetNextChild<KaxTrackEntry>(*mTracks, static_cast<KaxTrackEntry&>(*(*mTracks)[mTracks->ListSize()-1]));
+            t = &GetNextChild<KaxTrackEntry>(*mTracks, static_cast<KaxTrackEntry&>(*(*mTracks)[static_cast<unsigned int>(mTracks->ListSize()-1)]));
         }
         assert(t);
         KaxTrackNumber& trackNumber = GetChild<KaxTrackNumber>(*t);
@@ -383,7 +492,7 @@ void SegmentImpl::close()
 {
     if (mActiveCluster)
     {
-        mActiveCluster->render();
+        mActiveCluster->render(mContainer->getIO(), mCues);
         mActiveCluster.reset();
     }
 
@@ -392,7 +501,7 @@ void SegmentImpl::close()
         mCues->Render(*mContainer->getIO());
         if (mSeekHead)
         {
-            mSeekHead->IndexThis(*mCues, *mContainer->getIO());
+            mSeekHead->IndexThis(*mCues, mSegment);
         }
     }
         
@@ -412,7 +521,7 @@ void SegmentImpl::close()
         mLeftOverSeek->Render(*mContainer->getIO());
         if (mLeftOverSeek)
         {
-            mSeekHead->IndexThis(*mCues, *mContainer->getIO());
+            mSeekHead->IndexThis(*mCues, mSegment);
         }
     }
 
@@ -420,8 +529,55 @@ void SegmentImpl::close()
     // TODO: Add tag support.
     //
     KaxTags& tags = GetChild<KaxTags>(mSegment);
-    tags.Render(*mContainer->getIO());
+    KaxTag* lastTag = 0;
+    for (TrackTags::iterator i = mTrackTags.begin(); i != mTrackTags.end(); ++i)
+    {
+        KaxTag* current = 0;
+        if (lastTag)
+        {
+            current = &GetNextChild<KaxTag>(tags, *lastTag);
+        }
+        else
+        {
+            current = &GetChild<KaxTag>(tags);
+        }
+        lastTag = current;
 
+        KaxTagTargets& targets = GetChild<KaxTagTargets>(*current);
+        KaxTagTrackUID& trackUID = GetChild<KaxTagTrackUID>(targets);
+        EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(&trackUID);
+        assert(uintField);
+        *uintField = i->first;
+        KaxTagSimple* tagEntry = 0;
+        for (TagSeq::const_iterator t = i->second.begin(); t != i->second.end(); ++t)
+        {
+            KaxTagSimple* currentEntry = 0;
+            if (tagEntry)
+            {
+                currentEntry = &GetNextChild<KaxTagSimple>(*current, *tagEntry);
+            }
+            else
+            {
+                currentEntry = &GetChild<KaxTagSimple>(*current);
+            }
+            tagEntry = currentEntry;
+            EbmlUnicodeString* unicodeField = dynamic_cast<EbmlUnicodeString*>(&GetChild<KaxTagName>(*currentEntry));
+            assert(unicodeField);
+            UTFstring utf;
+            utf.SetUTF8(t->name);
+            *unicodeField = utf;
+
+            unicodeField = dynamic_cast<EbmlUnicodeString*>(&GetChild<KaxTagString>(*currentEntry));
+            assert(unicodeField);
+            utf.SetUTF8(t->value);
+            *unicodeField = utf;
+        }
+    }
+    tags.Render(*mContainer->getIO());
+    if (mSeekHead)
+    {
+        mSeekHead->IndexThis(tags, mSegment);
+    }
     if (mIndexSpace)
     {
         mIndexSpace->ReplaceWith(*mSeekHead, *mContainer->getIO());
@@ -451,7 +607,7 @@ ClusterPtr SegmentImpl::getCluster(uint64 currentTimeCode)
         mActiveCluster->render(mContainer->getIO(), mCues);
         oldTimeCode = mActiveCluster->startTimeCode();
     }
-    mActiveCluster.reset(new Cluster(new KaxCluster, shared_from_this(), currentTimeCode, oldTimeCode));
+    mActiveCluster.reset(new Cluster(boost::shared_ptr<KaxCluster>(new KaxCluster), shared_from_this(), currentTimeCode, oldTimeCode));
     return mActiveCluster;
 }
 
@@ -471,12 +627,9 @@ uint64 SegmentImpl::addTime(uint64 moreTime)
     return mLastTime;
 }
 
-void SegmentImpl::addGroupToCues(KaxBlock* block)
+void SegmentImpl::commit(const TrackImplPtr& track)
 {
-    if (mCues && block)
-    {
-        mCues->AddBlockGroup(block);
-    }
+    mTrackTags[track->getID()] = track->getTags();
 }
 
 SegmentPtr Segment::create()
diff --git a/src/MatroskaUtils.h b/src/MatroskaUtils.h
index 0b11d5c..9f71826 100755
--- a/src/MatroskaUtils.h
+++ b/src/MatroskaUtils.h
@@ -42,21 +42,36 @@ public:
     virtual std::vector<unsigned char> bytes() const = 0;
     virtual std::string id() const = 0;
 };
-
 typedef boost::shared_ptr<Codec> CodecPtr;
 
+class AudioCodec : public Codec
+{
+public:
+    virtual unsigned int channels() = 0;
+    virtual double sampleRate() = 0;
+    virtual unsigned int bitDepth() = 0;
+    virtual double outputSampleRate() = 0;
+};
+typedef boost::shared_ptr<AudioCodec> AudioCodecPtr;
+
+class VideoCodec : public Codec
+{
+public:
+    virtual unsigned int width() = 0;
+    virtual unsigned int height() = 0;
+    virtual unsigned int displayWidth() = 0;
+    virtual unsigned int displayHeight() = 0;
+};
+typedef boost::shared_ptr<VideoCodec> VideoCodecPtr;
+
 class TrackType
 {
 public:
     virtual ~TrackType() {}
-
-    int operator()() const
-    {
-        return getType();
-    }
+    virtual int getType() const = 0;
 
 protected:
-    virtual int getType() const = 0;
+    
 };
 
 typedef boost::shared_ptr<TrackType> TrackTypePtr;
@@ -64,6 +79,17 @@ typedef boost::shared_ptr<TrackType> TrackTypePtr;
 class Segment;
 typedef boost::shared_ptr<Segment> SegmentPtr;
 
+struct Tag
+{
+    std::string name;
+    std::string value;
+
+    Tag(const std::string& n, const std::string& v) :
+    name(n), value(v) {}
+};
+
+typedef std::vector<Tag> TagSeq;
+
 class Track
 {
 public:
@@ -80,8 +106,15 @@ public:
     }
 
     virtual void setCodecAndType(const CodecPtr& codec, const TrackTypePtr& trackType) = 0;
+    virtual void addTag(const std::string& name, const std::string& value) = 0;
     virtual void write(uint64 timeCode, uint32 duration, const std::vector<unsigned char>& data) = 0;
 
+    virtual TagSeq getTags() = 0;
+
+    virtual void finished() = 0;
+
+    virtual uint64 getID() = 0;
+
 protected:
     unsigned char mTrackNumber;
 

commit 4a07e77cda06ac0f059664dd2c4ef8dc42a381f9
Author: Brent Eagles <beagles at digium.com>
Date:   Thu Sep 29 13:31:58 2011 -0230

    New matroska muxing code.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4d78299..61df955 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,6 +5,11 @@
 astscf_project(FileMediaService 3.4)
 
 add_subdirectory(slice)
+
+if(WIN32)
+    add_definitions(-DEBML_DLL -DMATROSKA_DLL)
+endif()
+
 add_subdirectory(src)
 if(BUILD_TESTING)
     add_subdirectory(test)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 27edf7b..13589b2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -24,6 +24,8 @@ astscf_component_add_files(FileMediaService
 	ReplicationListener.h
 	RepositoryConfigurationAdapter.h
 	RepositoryReplicationAdapter.h
+        MatroskaUtils.h
+        MatroskaUtils.cpp
 )
 
 astscf_component_add_ice_libraries(FileMediaService IceStorm)
diff --git a/src/ContainerImpl.cpp b/src/ContainerImpl.cpp
index 18467da..c0d6650 100644
--- a/src/ContainerImpl.cpp
+++ b/src/ContainerImpl.cpp
@@ -17,6 +17,7 @@
 #include "ContainerImpl.h"
 #include "ContainerRepository.h"
 #include "OpaqueContainerData.h"
+#include "StreamImpl.h"
 
 #include <matroska/FileKax.h>
 #include <ebml/StdIOCallback.h>
@@ -25,6 +26,7 @@
 
 #include <Ice/Ice.h>
 #include <IceUtil/UUID.h>
+#include <IceUtil/Thread.h>
 
 #define BOOST_FILESYSTEM_VERSION 3
 #include <boost/filesystem.hpp>
@@ -62,8 +64,13 @@ public:
     {
     }
 
+    libebml::IOCallback* getIO()
+    {
+        return mIOCallback.get();
+    }
 private:
     StdIOCallbackPtr mIOCallback;
+
 };
 
 typedef boost::shared_ptr<MatroskaImpl> MatroskaImplPtr;
@@ -76,16 +83,46 @@ public:
     {
     }
 
-    FileKaxPtr getFile()
+    MatroskaFilePtr getFile() const
     {
         return boost::dynamic_pointer_cast<FileMatroska>(mMatroskaFile);
     }
 
+    libebml::IOCallback* getIO() const
+    {
+        return mMatroskaFile->getIO();
+    }
+
 private:
     MatroskaImplPtr mMatroskaFile;
 };
 typedef IceUtil::Handle<ContainerDataImpl> ContainerDataImplPtr;
 
+//
+// Forward declarations.
+// 
+class ContainerDataImpl;
+
+//
+// TODO: Port to thread pool.
+//
+// The container needs to buffer writes so that only one thread accesses the file at a time.
+//
+class FileIODriver : public IceUtil::Thread
+{
+public:
+    FileIODriver(ContainerDataImpl* container);
+
+    void run();
+    void stop();
+
+private:
+    IceUtil::Monitor<IceUtil::Mutex> mMonitor;
+    ContainerDataImpl* mContainer;
+    bool mDone;
+    
+};
+
 class ContainerServant : public ContainerImpl
 {
 public:
@@ -156,15 +193,17 @@ private:
     ContainerInfoImplPtr mInfo;
     Logger mLogger;
 
+    StreamInfoSeq mStreamInfo;
+    ContainerDataImplPtr mMatroskaData;
+
+    StreamImplSeq mStreams;
+
     ContainerInfoImplPtr getInfoImpl()
     {
         SharedLock lock(mLock);
         return mInfo;
     }
 
-    StreamInfoSeq mStreamInfo;
-    ContainerDataImplPtr mMatroskaData;
-
     ContainerDataImplPtr refresh(const string& filename, AsteriskSCF::Media::File::V1::FileOperations supportedOperations)
     {
         if (supportedOperations == AsteriskSCF::Media::File::V1::Both)
@@ -213,6 +252,10 @@ private:
             return 0;
         }
 
+        //
+        // File I/O is handled by the callback object. 
+        // TODO: we may have to derive from that to get the full functionality that we want.
+        //
         StdIOCallbackPtr callback(new StdIOCallback(filePath.string().c_str(), mode));
         ContainerDataImplPtr data(new ContainerDataImpl(MatroskaImplPtr(new MatroskaImpl(callback))));
         return data;
diff --git a/src/MatroskaDefines.h b/src/MatroskaDefines.h
new file mode 100755
index 0000000..f6756d6
--- /dev/null
+++ b/src/MatroskaDefines.h
@@ -0,0 +1,92 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2011, 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>
+
+namespace AsteriskSCF
+{
+namespace MatroskaContainer
+{
+namespace Implementation
+{
+
+const std::string MatroskaAudioAACv2Main = "A_AAC/MPEG2/MAIN";
+const std::string MatroskaAudioAACv2LC   = "A_AAC/MPEG2/LC";
+const std::string MatroskaAudioAACv2SSR  = "A_AAC/MPEG2/SSR";
+const std::string MatroskaAudioAACv2SBR  = "A_AAC/MPEG2/LC/SBR";
+const std::string MatroskaAudioAACv4Main = "A_AAC/MPEG4/MAIN";
+const std::string MatroskaAudioAACv4LC   = "A_AAC/MPEG4/LC";
+const std::string MatroskaAudioAACv4SSR  = "A_AAC/MPEG4/SSR";
+const std::string MatroskaAudioAACv4LTP  = "A_AAC/MPEG4/LTP";
+const std::string MatroskaAudioAACv4SBR  = "A_AAC/MPEG4/LC/SBR";
+const std::string MatroskaAudioAAC       = "A_AAC";
+const std::string MatroskaAudioAC3       = "A_AC3";
+const std::string MatroskaAudioEAC3      = "A_EAC3";
+const std::string MatroskaAudioDTS       = "A_DTS";
+const std::string MatroskaAudioMP2       = "A_MPEG/L2";
+const std::string MatroskaAudioMP3       = "A_MPEG/L3";
+const std::string MatroskaAudioPCM       = "A_PCM/INT/LIT";
+const std::string MatroskaAudioPCMvBE    = "A_PCM/INT/BIG";
+const std::string MatroskaAudioPCMvFloat = "A_PCM/FLOAT/IEEE";
+const std::string MatroskaAudioVorbis    = "A_VORBIS";
+const std::string MatroskaAudioACM       = "A_MS/ACM";
+const std::string MatroskaAudioQuicktime = "A_QUICKTIME";
+const std::string MatroskaAudioQDMC      = "A_QUICKTIME/QDMC";
+const std::string MatroskaAudioQDMC2     = "A_QUICKTIME/QDM2";
+const std::string MatroskaAudioRealv14_4 = "A_REAL/14_4";
+const std::string MatroskaAudioRealv28_8 = "A_REAL/28_8";
+const std::string MatroskaAudioRealvCOOK = "A_REAL/COOK";
+const std::string MatroskaAudioRealvSIPR = "A_REAL/SIPR";
+const std::string MatroskaAudioRealvATRC = "A_REAL/ATRC";
+const std::string MatroskaAudioFLAC      = "A_FLAC";
+const std::string MatroskaAudioMLP       = "A_MLP";
+const std::string MatroskaAudioTrueHD    = "A_TRUEHD";
+const std::string MatroskaAudioTTA       = "A_TTA1";
+const std::string MatroskaAudioWavPack4  = "A_WAVPACK4";
+
+const std::string MatroskaVideoMPEG1      = "V_MPEG1";
+const std::string MatroskaVideoMPEG2      = "V_MPEG2";
+const std::string MatroskaVideoMPEG4vSP   = "V_MPEG4/ISO/SP";
+const std::string MatroskaVideoMPEG4vASP  = "V_MPEG4/ISO/ASP";
+const std::string MatroskaVideoMPEG4vAP   = "V_MPEG4/ISO/AP";
+const std::string MatroskaVideoMPEG4vAVC  = "V_MPEG4/ISO/AVC";
+const std::string MatroskaVideoMSComp     = "V_MS/VFW/FOURCC";
+const std::string MatroskaVideoRealV1     = "V_REAL/RV10";
+const std::string MatroskaVideorealV2     = "V_REAL/RV20";
+const std::string MatroskaVideoRealV3     = "V_REAL/RV30";
+const std::string MatroskaVideoRealV4     = "V_REAL/RV40";
+const std::string MatroskaVideoQuicktime  = "V_QUICKTIME";
+const std::string MatroskaVideoTheora     = "V_THEORA";
+const std::string MatroskaVideoCorePicture= "V_CIPC";
+const std::string MatroskaVideoDirac      = "V_DIRAC";
+const std::string MatroskaVideoVP8        = "V_VP8";
+
+const std::string MatroskaSubtitleTextUTF8  = "S_TEXT/UTF8";
+const std::string MatroskaSubtitleTextSSA   = "S_TEXT/SSA";
+const std::string MatroskaSubtitleTextASS   = "S_TEXT/ASS";
+const std::string MatroskaSubtitleTextUSF   = "S_TEXT/USF";
+const std::string MatroskaSubtitleTextASCII = "S_TEXT/ASCII";
+const std::string MatroskaSubtitleVobSub    = "S_VOBSUB";
+const std::string MatroskaSubtitleVobSubZlib= "S_VOBSUB/ZLIB";
+const std::string MatroskaSubtitleKATE      = "S_KATE";
+const std::string MatroskaSubtitleHDMVvPGS  = "S_HDMV/PGS";
+
+const std::string MatroskaBlobVOB    = "B_VOBBTN";
+
+} /* End of namespace Implementation */
+} /* End of namespace MatroskaContainer */
+} /* End of namespace AsteriskSCF */
diff --git a/src/MatroskaUtils.cpp b/src/MatroskaUtils.cpp
new file mode 100755
index 0000000..f443218
--- /dev/null
+++ b/src/MatroskaUtils.cpp
@@ -0,0 +1,490 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2011, 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 "MatroskaUtils.h"
+
+#include <ebml/EbmlHead.h>
+#include <ebml/EbmlSubHead.h>
+#include <ebml/EbmlUInteger.h>
+#include <ebml/EbmlVoid.h>
+#include <ebml/EbmlUnicodeString.h>
+
+#include <matroska/KaxSeekHead.h>
+#include <matroska/KaxTracks.h>
+#include <matroska/KaxCluster.h>
+#include <matroska/KaxBlock.h>
+#include <matroska/KaxCues.h>
+#include <matroska/KaxSegment.h>
+#include <matroska/KaxVersion.h>
+
+#include <sstream>
+
+#include <time.h>
+
+#include <boost/uuid/uuid.hpp>
+#include <boost/uuid/uuid_generators.hpp>
+
+#include <boost/enable_shared_from_this.hpp>
+
+using namespace libebml;
+using namespace libmatroska;
+using namespace std;
+
+namespace AsteriskSCF
+{
+namespace MatroskaContainer
+{
+namespace Implementation
+{
+
+class BaseCodecImpl
+{
+public:
+    BaseCodecImpl(const string& codecString) :
+      mCodecString(codecString)
+    {
+    }
+
+    vector<unsigned char> bytes() const
+    {
+        return vector<unsigned char>();
+    }
+
+    string id() const
+    {
+        return mCodecString;
+    }
+
+private:
+    string mCodecString;
+};
+
+void Header::writeTo(const OpaqueContainerDataPtr& data)
+{
+    EbmlHead header;
+    EDocType& docType = GetChild<EDocType>(header);
+    EbmlString* stringField = dynamic_cast<EbmlString*>(&docType);
+    assert(stringField);
+    *stringField = "matroska";
+    EDocTypeVersion& docTypeVer = GetChild<EDocTypeVersion>(header);
+    EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(&docTypeVer); 
+    assert(uintField);
+    *uintField = 1;
+    EDocTypeReadVersion& docTypeReadVer = GetChild<EDocTypeReadVersion>(header);
+    uintField = dynamic_cast<EbmlUInteger*>(&docTypeReadVer);
+    assert(uintField);
+    *uintField = 1;
+    header.Render(*data->getIO());
+}
+
+class SegmentImpl;
+typedef boost::shared_ptr<SegmentImpl> SegmentImplPtr;
+
+class Cluster 
+{
+public:
+    Cluster(const boost::shared_ptr<KaxCluster>& cluster, const SegmentImplPtr& segment, 
+        uint64 initialTimeCode, uint64 lastTimeCode);
+    uint64 startTimeCode() const;
+    void render(IOCallback* io, KaxCues* cues);
+    KaxCluster* kax();
+    uint64 increaseSize(uint64 addThisMuch);
+    uint64 size() const;
+
+private:
+    boost::shared_ptr<KaxCluster> mCluster;
+    SegmentImplPtr mSegment;
+    const uint64 mTimeCode;
+    uint64 mSize;
+};
+typedef boost::shared_ptr<Cluster> ClusterPtr;
+
+class TrackImpl : public Track
+{
+public:
+    TrackImpl(const SegmentImplPtr& segment, KaxTrackEntry* t, unsigned char number);
+    void setCodecAndType(const CodecPtr& codec, const TrackTypePtr& trackType);
+    void write(uint64 timeCode, uint32 duration, const vector<unsigned char>& data);
+    void finished();
+
+private:
+    SegmentImplPtr mSegment;
+    KaxTrackEntry* mTrack;
+    CodecPtr mCodec;
+    TrackTypePtr mTrackType;
+    KaxBlockGroup* mGroup;
+};
+
+class SegmentImpl : public Segment, public boost::enable_shared_from_this<SegmentImpl>
+{
+public:
+    SegmentImpl();
+    void addTo(const OpaqueContainerDataPtr& data);
+    TrackPtr getTrack(unsigned char track);
+    void close();
+
+    //
+    // Internal implementation details.
+    //
+
+    ClusterPtr getCluster(uint64 currentTimeCode);
+    KaxSegment& kax();
+    uint64 globalTimeScale() const;
+    uint64 addTime(uint64 moreTime);
+    void addGroupToCues(KaxBlock* block);
+
+private:
+    KaxSegment mSegment;
+    KaxSeekHead* mSeekHead;
+    EbmlVoid* mIndexSpace;
+    KaxTracks* mTracks;
+    KaxSeekHead* mLeftOverSeek;
+    KaxCues* mCues;
+
+    OpaqueContainerDataPtr mContainer;
+
+    ClusterPtr mActiveCluster;
+
+    //
+    // Might be configurable in the future? For now.. we'll be content to leave it as constant member.
+    //
+    const uint64 mTimeCodeScale;
+    const uint64 mMaxTimePerCluster;
+    uint64 mPrevTime;
+    uint16 mPrevTrack;
+    uint64 mLastTime;
+};
+
+Cluster::Cluster(const boost::shared_ptr<KaxCluster>& cluster, const SegmentImplPtr& segment, 
+        uint64 initialTimeCode, uint64 lastTimeCode) :
+      mCluster(cluster),
+      mSegment(segment),
+      mTimeCode(initialTimeCode),
+      mSize(0)
+{
+    mCluster->SetParent(segment->kax());
+    mCluster->SetGlobalTimecodeScale(segment->globalTimeScale());
+    mCluster->SetPreviousTimecode(lastTimeCode, segment->globalTimeScale());
+    mCluster->InitTimecode(initialTimeCode, segment->globalTimeScale());
+    KaxClusterTimecode& tc = GetChild<KaxClusterTimecode>(*mCluster);
+    EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(&tc);
+    assert(uintField);
+    *uintField = initialTimeCode;
+}
+
+uint64 Cluster::startTimeCode() const
+{
+    return mTimeCode;
+}
+
+void Cluster::render(IOCallback* io, KaxCues* cues)
+{
+    mCluster->Render(*io, *cues);
+    mCluster->ReleaseFrames();
+}
+
+KaxCluster* Cluster::kax()
+{
+    return mCluster.get();
+}
+
+uint64 Cluster::increaseSize(uint64 addThisMuch)
+{
+    mSize += addThisMuch;
+    return mSize;
+}
+
+uint64 Cluster::size() const
+{
+    return mSize;
+}
+
+TrackImpl::TrackImpl(const SegmentImplPtr& segment, KaxTrackEntry* t, unsigned char number) :
+    Track(number),
+    mSegment(segment),
+    mTrack(t)
+{
+}
+
+void TrackImpl::setCodecAndType(const CodecPtr& codec, const TrackTypePtr& trackType)
+{
+    mCodec = codec;
+    mTrackType = trackType;
+}
+
+void TrackImpl::write(uint64 timeCode, uint32 duration, const vector<unsigned char>& data)
+{
+    ClusterPtr cluster = mSegment->getCluster(timeCode);
+    if (mGroup)
+    {
+        mGroup = cluster->kax()->GetNewBlock();
+        mGroup->SetParent(*cluster->kax());
+    }
+
+    KaxBlock& block = GetChild<KaxBlock>(*mGroup);
+    block.SetParent(*cluster->kax());
+    //
+    // We aren't using lacing .. yet... so just add the frame.
+    //
+    SimpleDataBuffer buf((unsigned char*)(&data.begin()), data.size(), 0);
+    block.AddFrame(*mTrack, timeCode * mSegment->globalTimeScale(), buf);
+    cluster->increaseSize(buf.Size());
+    mSegment->addTime(timeCode + duration);
+    KaxBlockDuration& blockDuration = GetChild<KaxBlockDuration>(*mGroup);
+    EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(blockDuration);
+    assert(uintField);
+    *uintField = duration;
+
+    //
+    // We are not doing references at this time so we just update Cue if handling
+    // video.
+    //
+    if (GetChild<KaxTrackType>(*mTrack) == track_video)
+    {
+        mSegment->addGroupToCues(block);
+    }
+}
+
+void TrackImpl::finished()
+{
+    if (!mTrackType || !mCodec)
+    {
+        throw IncompleteException();
+    }
+}
+
+SegmentImpl::SegmentImpl() :
+    mTimeCodeScale(1000000),
+    mMaxTimePerCluster(1000),
+    mPrevTime(0xFFFFFFFFFFFFFFFF),
+    mPrevTrack(0xFFFF),
+    mLastTime(0)
+{
+    //
+    // initialize helper members into the segment.
+    //
+    mSeekHead = &GetChild<KaxSeekHead>(mSegment);
+    mIndexSpace = &GetChild<EbmlVoid>(mSegment);
+    mTracks = &GetChild<KaxTracks>(mSegment);
+    mLeftOverSeek = &GetChild<KaxSeekHead>(mSegment);
+}
+
+void SegmentImpl::addTo(const OpaqueContainerDataPtr& data)
+{
+    mContainer = data;
+    mSegment.WriteHead(*(data->getIO()), 10);
+    //
+    // Make space for seek head.
+    //
+    mIndexSpace->SetSize(2 * 1024);
+    mIndexSpace->Render(*(data->getIO()));
+
+    KaxInfo& info = GetChild<KaxInfo>(mSegment);
+    KaxWritingApp& writingApp = GetChild<KaxWritingApp>(info);
+    EbmlUnicodeString* unicodeField = dynamic_cast<EbmlUnicodeString*>(&writingApp);
+    assert(unicodeField);
+    *unicodeField = L"AsteriskSCF";
+    stringstream os;
+    os << "AsteriskSCF Matroska Container Library with libmatroska " << KaxCodeVersion << " and libebml " << EbmlCodeVersion;
+    UTFstring muxerName;
+    muxerName.SetUTF8(os.str());
+    KaxMuxingApp& muxer = GetChild<KaxMuxingApp>(info);
+    unicodeField = dynamic_cast<EbmlUnicodeString*>(&muxer);
+    assert(unicodeField);
+    *unicodeField = muxerName;
+    KaxDateUTC& date = GetChild<KaxDateUTC>(info);
+    date.SetEpochDate(static_cast<int32>(time(0)));
+    KaxTimecodeScale& scale = GetChild<KaxTimecodeScale>(info);
+    EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(&scale);
+    assert(uintField);
+    *uintField = mTimeCodeScale;
+    KaxDuration& duration = GetChild<KaxDuration>(info);
+    EbmlFloat* floatField = dynamic_cast<EbmlFloat*>(&duration);
+    assert(floatField);
+    *floatField = 10000; // We set a dummy value for this right now.
+    KaxSegmentUID& uuid = GetChild<KaxSegmentUID>(info);
+    boost::uuids::uuid idValue = boost::uuids::random_generator()();
+    binary* buf = new binary[idValue.static_size()];
+    for (boost::uuids::uuid::const_iterator i = idValue.begin(); i != idValue.end(); ++i)
+    {
+        *buf = *i;
+    }
+    uuid.SetBuffer(buf, static_cast<uint32>(idValue.static_size()));
+    info.Render(*(data->getIO()));
+
+    mSeekHead->IndexThis(info, mSegment);
+    mCues = &GetChild<KaxCues>(mSegment);
+    mCues->SetGlobalTimecodeScale(mTimeCodeScale);
+    mTracks->Render(*(data->getIO()));
+    mSeekHead->IndexThis(*mTracks, mSegment);
+
+    //
+    // Ok.. so I think that is it for setup .. whew!
+    //
+}
+
+TrackPtr SegmentImpl::getTrack(unsigned char track) 
+{
+    if (track == 0)
+    {
+        return TrackPtr();
+    }
+    KaxTrackEntry* t = 0;
+    if (mTracks->ListSize() < track)
+    {
+        //
+        // We need to create a new track!
+        //
+        if (mTracks->ListSize() == 0)
+        {
+            t = &GetChild<KaxTrackEntry>(*mTracks);
+        }
+        else
+        {
+            t = &GetNextChild<KaxTrackEntry>(*mTracks, static_cast<KaxTrackEntry&>(*(*mTracks)[mTracks->ListSize()-1]));
+        }
+        assert(t);
+        KaxTrackNumber& trackNumber = GetChild<KaxTrackNumber>(*t);
+        EbmlUInteger* uintField = dynamic_cast<EbmlUInteger*>(&trackNumber);
+        assert(uintField);
+        *uintField = track;
+        KaxTrackUID& trackId = GetChild<KaxTrackUID>(*t);
+        uintField = dynamic_cast<EbmlUInteger*>(&trackId);
+        assert(uintField);
+        *uintField = rand();
+        KaxTrackLanguage& lang = GetChild<KaxTrackLanguage>(*t);
+        EbmlString* stringField = dynamic_cast<EbmlString*>(&lang);
+        assert(stringField);
+        *stringField = "eng";
+        t->SetGlobalTimecodeScale(mTimeCodeScale);
+    }
+    else
+    {
+        t = dynamic_cast<KaxTrackEntry*>((*mTracks)[track-1]);
+        assert(t);
+    }
+    return TrackPtr(new TrackImpl(shared_from_this(), t, track));
+}
+
+void SegmentImpl::close()
+{
+    if (mActiveCluster)
+    {
+        mActiveCluster->render();
+        mActiveCluster.reset();
+    }
+
+    if (mCues && mCues->ListSize() != 0)
+    {
+        mCues->Render(*mContainer->getIO());
+        if (mSeekHead)
+        {
+            mSeekHead->IndexThis(*mCues, *mContainer->getIO());
+        }
+    }
+        
+    KaxInfo& info = GetChild<KaxInfo>(mSegment);
+    KaxDuration& duration = GetChild<KaxDuration>(info);
+    EbmlFloat* floatField = dynamic_cast<EbmlFloat*>(&duration);
+    assert(floatField);
+    *floatField = (mLastTime * 1.0);
+    info.UpdateSize();
+    uint64 bookmark = mContainer->getIO()->getFilePointer();
+    mContainer->getIO()->setFilePointer(info.GetElementPosition());
+    info.Render(*mContainer->getIO());
+    mContainer->getIO()->setFilePointer(bookmark);
+
+    if (mLeftOverSeek && mLeftOverSeek->ListSize() != 0)
+    {
+        mLeftOverSeek->Render(*mContainer->getIO());
+        if (mLeftOverSeek)
+        {
+            mSeekHead->IndexThis(*mCues, *mContainer->getIO());
+        }
+    }
+
+    //
+    // TODO: Add tag support.
+    //
+    KaxTags& tags = GetChild<KaxTags>(mSegment);
+    tags.Render(*mContainer->getIO());
+
+    if (mIndexSpace)
+    {
+        mIndexSpace->ReplaceWith(*mSeekHead, *mContainer->getIO());
+    }
+    mContainer->getIO()->setFilePointer(0, seek_end);
+
+    uint64 newSize = mContainer->getIO()->getFilePointer() - mSegment.GetElementPosition() - mSegment.HeadSize();
+    if (mSegment.ForceSize(newSize))
+    {
+        mSegment.OverwriteHead(*mContainer->getIO());
+    }
+}
+
+//
+// Internal implementation details.
+//
+
+ClusterPtr SegmentImpl::getCluster(uint64 currentTimeCode)
+{
+    uint64 oldTimeCode = 0;
+    if (mActiveCluster)
+    {
+        if ((currentTimeCode - mActiveCluster->startTimeCode()) < mMaxTimePerCluster)
+        {
+            return mActiveCluster;
+        }
+        mActiveCluster->render(mContainer->getIO(), mCues);
+        oldTimeCode = mActiveCluster->startTimeCode();
+    }
+    mActiveCluster.reset(new Cluster(new KaxCluster, shared_from_this(), currentTimeCode, oldTimeCode));
+    return mActiveCluster;
+}
+
+KaxSegment& SegmentImpl::kax()
+{
+    return mSegment;
+}
+
+uint64 SegmentImpl::globalTimeScale() const
+{
+    return mTimeCodeScale;
+}
+
+uint64 SegmentImpl::addTime(uint64 moreTime)
+{
+    mLastTime += moreTime;
+    return mLastTime;
+}
+
+void SegmentImpl::addGroupToCues(KaxBlock* block)
+{
+    if (mCues && block)
+    {
+        mCues->AddBlockGroup(block);
+    }
+}
+
+SegmentPtr Segment::create()
+{
+    return SegmentPtr(new SegmentImpl);
+}
+
+} /* End of namespace Implementation */
+} /* End of namespace MatroskaContainer */
+} /* End of namespace AsteriskSCF */
+
diff --git a/src/MatroskaUtils.h b/src/MatroskaUtils.h
new file mode 100755
index 0000000..0b11d5c
--- /dev/null
+++ b/src/MatroskaUtils.h
@@ -0,0 +1,122 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2011, 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 "OpaqueContainerData.h"
+
+#include <ebml/EbmlTypes.h>
+#include <boost/shared_ptr.hpp>
+#include <vector>
+#include <string>
+
+namespace AsteriskSCF
+{
+namespace MatroskaContainer
+{
+namespace Implementation
+{
+
+class Header
+{
+public:
+    void writeTo(const OpaqueContainerDataPtr& data);
+};
+
+class Codec
+{
+public:
+    virtual ~Codec() {}
+
+    virtual std::vector<unsigned char> bytes() const = 0;
+    virtual std::string id() const = 0;
+};
+
+typedef boost::shared_ptr<Codec> CodecPtr;
+
+class TrackType
+{
+public:
+    virtual ~TrackType() {}
+
+    int operator()() const
+    {
+        return getType();
+    }
+
+protected:
+    virtual int getType() const = 0;
+};
+
+typedef boost::shared_ptr<TrackType> TrackTypePtr;
+
+class Segment;
+typedef boost::shared_ptr<Segment> SegmentPtr;
+
+class Track
+{
+public:
+
+    class IncompleteException
+    {
+    };
+
+    virtual ~Track() {}
+
+    unsigned char trackNumber()
+    {
+        return mTrackNumber;
+    }
+
+    virtual void setCodecAndType(const CodecPtr& codec, const TrackTypePtr& trackType) = 0;
+    virtual void write(uint64 timeCode, uint32 duration, const std::vector<unsigned char>& data) = 0;
+
+protected:
+    unsigned char mTrackNumber;
+
+    Track(unsigned char track) :
+      mTrackNumber(track)
+    {
+    }
+};
+typedef boost::shared_ptr<Track> TrackPtr;
+
+class Segment
+{
+    //
+    // Hidden.
+    //
+    Segment(const Segment&);
+    void operator=(const Segment&);
+
+
+public:
+
+    virtual ~Segment() {}
+
+    virtual void addTo(const OpaqueContainerDataPtr& data) = 0;
+
+    virtual TrackPtr getTrack(unsigned char trackNumber) = 0;
+
+    virtual void close() = 0;
+
+    static SegmentPtr create();
+
+protected:
+    Segment() {}
+};
+
+} /* End of namespace Implementation */
+} /* End of namespace MatroskaContainer */
+} /* End of namepsace AsteriskSCF */
\ No newline at end of file
diff --git a/src/OpaqueContainerData.h b/src/OpaqueContainerData.h
index 6ed3412..32dbfd5 100644
--- a/src/OpaqueContainerData.h
+++ b/src/OpaqueContainerData.h
@@ -21,8 +21,13 @@
 #include <boost/shared_ptr.hpp>
 
 /**
- * Forward declarations for matroska.
+ * Forward declarations for matroska and ebml
  */
+namespace libebml
+{
+class IOCallback;
+}
+
 namespace libmatroska
 {
 class FileMatroska;
@@ -34,12 +39,16 @@ namespace MatroskaContainer
 {
 namespace Implementation
 {
-typedef boost::shared_ptr<libmatroska::FileMatroska> FileKaxPtr;
+typedef boost::shared_ptr<libmatroska::FileMatroska> MatroskaFilePtr;
+
+class Element;
+typedef boost::shared_ptr<Element> ElementPtr;
 
 class OpaqueContainerData : public IceUtil::Shared
 {
 public:
-    virtual FileKaxPtr getFile() = 0;
+    virtual MatroskaFilePtr getFile() const = 0;
+    virtual libebml::IOCallback* getIO() const = 0;
 };
 typedef IceUtil::Handle<OpaqueContainerData> OpaqueContainerDataPtr;
 
diff --git a/src/StreamImpl.cpp b/src/StreamImpl.cpp
index bc3803e..7f9de2f 100644
--- a/src/StreamImpl.cpp
+++ b/src/StreamImpl.cpp
@@ -16,10 +16,14 @@
 
 #include "StreamImpl.h"
 #include <AsteriskSCF/logger.h>
+#include <matroska/FileKax.h>
+#include <matroska/KaxSegment.h>
 
 using namespace AsteriskSCF::System::Logging;
 using namespace AsteriskSCF::FileMediaService::MediaContainer::V1;
 using namespace std;
+using namespace libebml;
+using namespace libmatroska;
... 120 lines suppressed ...


-- 
asterisk-scf/integration/file_media_service.git



More information about the asterisk-scf-commits mailing list