[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