proposal for the MediaAccessFacade (interface to bakend), incl. Mock test

This commit is contained in:
Fischlurch 2007-09-24 16:20:41 +02:00
parent 1d462845dd
commit 38b47b7f93
5 changed files with 209 additions and 112 deletions

View file

@ -23,6 +23,11 @@
#include "backend/mediaaccessfacade.hpp"
#include "common/util.hpp"
using util::isnil;
using cinelerra::error::Invalid;
namespace backend_interface
{
@ -30,6 +35,29 @@ namespace backend_interface
* (actually a cinelerra::test::MockInjector) */
Singleton<MediaAccessFacade> MediaAccessFacade::instance;
typedef MediaAccessFacade::FileHandle FileHandle;
typedef MediaAccessFacade::ChanHandle ChanHandle;
FileHandle
MediaAccessFacade::queryFile (const char* name) throw(Invalid)
{
if (isnil (name))
throw Invalid ("empty filename passed to MediaAccessFacade.");
UNIMPLEMENTED ("delegate to backend: query accessability of file");
return 0;
}
ChanDesc
MediaAccessFacade::queryChannel (FileHandle fhandle, uint chanNo) throw()
{
UNIMPLEMENTED ("delegate to backend: query channel information");
ChanDesc nix;
return nix;
}
} // namespace backend_interface

View file

@ -26,27 +26,85 @@
#include "common/singleton.hpp"
#include "common/error.hpp"
namespace backend_interface
{
struct ChanDesc;
/**
/******************************************************************
* Interface to the backend layer:
* provides functions for querying (opening) a media file,
* detecting the channels or streams found within this file etc.
* Implemention delegating to the actual backend functions.
*
* convention: data passed by pointer is owned by the originator;
* it should be copied if needed byond the control flow
* of the invoked function.
*/
class MediaAccessFacade
struct MediaAccessFacade
{
public:
typedef void* FileHandle;
typedef void* ChanHandle;
static Singleton<MediaAccessFacade> instance;
/** request for testing the denoted files accessability
* @param name path and filename of the media file.
* @throw invalid when passing empty filename
* @return opaque handle usable for querying channel
* information from this file, NULL if the
* file is not acessible.
*/
virtual FileHandle queryFile (const char* name) throw(cinelerra::error::Invalid);
/** request for information about the n-th channel
* of the file refered by FileHandle.
* @return ChanDesc which may contain \c NULL values if
* the file doesn't contain this much channels.
*/
virtual ChanDesc queryChannel (FileHandle, uint chanNo) throw();
virtual ~MediaAccessFacade () {}
};
/**
* Description of one channel found in a
* media file; result of querying the channel.
*/
struct ChanDesc
{
/** identifier which can be used to create a name
* for the media asset corresponding to this channel.
* May be NULL or empty and need not be unique.
*/
const char* chanID;
/** identifier characterizing the access method (or codec)
* needed to get at the media data. This should be rather
* a high level description of the media stream type,
* e.g. "H264" -- anyhow, it will be used to find a
* codec asset for this channel.
*/
const char* codecID;
/** opaque handle, which will be used later to open this
* channel and retrieve some frames from it
*/
MediaAccessFacade::ChanHandle handle;
ChanDesc (const char* chanName=0, const char* codec=0,
MediaAccessFacade::ChanHandle h=0)
: chanID(chanName),
codecID(codec),
handle(h)
{ }
};
} // namespace backend_interface

View file

@ -2,5 +2,11 @@ TESTING "Proc Layer combined operations Test Suite" ./test-components --group=op
TEST "MediaAccessMock_test" MediaAccessMock_test <<END
out: accessing "test-1" ...
out: Channel-0: nameID=video codecID=ID
END
PLANNED "RenderSegment_test" RenderSegment_test <<END
END

View file

@ -42,7 +42,7 @@ namespace cinelerra
{
namespace test
{
/**
* Client Class normally to be instantiated as Singleton.
@ -53,53 +53,58 @@ namespace cinelerra
int callCnt;
char* typid;
format msg;
public:
TestSingletonO(char* ty="TestSingletonO")
: callCnt (0), typid(ty), msg ("%s::doIt() call=%d\n")
{
TRACE (singleton, "ctor %s", typid);
}
TestSingletonO(char* ty="TestSingletonO")
: callCnt (0), typid(ty), msg ("%s::doIt() call=%d\n")
{
TRACE (singleton, "ctor %s", typid);
}
virtual ~TestSingletonO()
{
TRACE (singleton, "dtor %s", typid);
}
{
TRACE (singleton, "dtor %s", typid);
}
void doIt ()
{
++callCnt;
cout << msg % typid % callCnt;
}
int getCnt () {return callCnt; }
{
++callCnt;
cout << msg % typid % callCnt;
}
int getCnt ()
{
return callCnt;
}
};
/**
* Mock-1 to replace the Client Class...
*/
struct Mock_1 : TestSingletonO
{
Mock_1() : TestSingletonO("Mock_1") {};
Mock_1() : TestSingletonO("Mock_1")
{};
};
/**
* Mock-2 to replace the Client Class...
*/
struct Mock_2 : TestSingletonO
{
Mock_2() : TestSingletonO("Mock_2") {};
Mock_2() : TestSingletonO("Mock_2")
{};
};
/*******************************************************************
* @test inject a Mock object into the Singleton Factory,
* to be returned and used in place of the original object.
@ -110,104 +115,104 @@ namespace cinelerra
class SingletonTestMock_test : public Test
{
Singleton<TestSingletonO> instance;
virtual void run(Arg arg)
{
string scenario = isnil(arg)? "default" : arg[1];
if (scenario == "default")
injectBoth();
else
virtual void run(Arg arg)
{
string scenario = isnil(arg)? "default" : arg[1];
if (scenario == "default")
injectBoth();
else
if (scenario == "noMock")
noMock();
else
if (scenario == "onlyMock")
onlyMock();
else
if (scenario == "firstMock")
firstMock();
}
else
if (scenario == "onlyMock")
onlyMock();
else
if (scenario == "firstMock")
firstMock();
}
/** @test complete use sequence: first access the Client Object,
/** @test complete use sequence: first access the Client Object,
* then replace it by two different mocks, and finally
* restore the original Client Object
*/
void injectBoth ()
{
TestSingletonO* sing = &instance();
sing->doIt();
sing->doIt();
ASSERT (sing->getCnt() == 2);
instance.injectSubclass (new Mock_1);
sing = &instance();
sing->doIt();
sing->doIt();
sing->doIt();
sing->doIt();
sing->doIt();
ASSERT (sing->getCnt() == 5);
instance.injectSubclass (new Mock_2);
sing = &instance();
sing->doIt();
ASSERT (sing->getCnt() == 1);
instance.injectSubclass (0); // unshaddowing original instance
sing = &instance();
ASSERT (sing->getCnt() == 2);
sing->doIt();
ASSERT (sing->getCnt() == 3);
}
{
TestSingletonO* sing = &instance();
sing->doIt();
sing->doIt();
ASSERT (sing->getCnt() == 2);
instance.injectSubclass (new Mock_1);
sing = &instance();
sing->doIt();
sing->doIt();
sing->doIt();
sing->doIt();
sing->doIt();
ASSERT (sing->getCnt() == 5);
instance.injectSubclass (new Mock_2);
sing = &instance();
sing->doIt();
ASSERT (sing->getCnt() == 1);
instance.injectSubclass (0); // unshaddowing original instance
sing = &instance();
ASSERT (sing->getCnt() == 2);
sing->doIt();
ASSERT (sing->getCnt() == 3);
}
/** @test just use Singleton Factory normally without any Mock.
*/
void noMock ()
{
TestSingletonO& sing = instance();
sing.doIt();
}
{
TestSingletonO& sing = instance();
sing.doIt();
}
/** @test inject the Mock prior to using the Singleton Factory,
* thus the original Client Object shouldn't be created.
*/
void onlyMock ()
{
instance.injectSubclass (new Mock_1);
TestSingletonO& sing = instance();
sing.doIt();
}
{
instance.injectSubclass (new Mock_1);
TestSingletonO& sing = instance();
sing.doIt();
}
/** @test inject the Mock prior to using the Singleton Factory,
* but then reset the Mock, so following calls should
* create the original Client Object.
*/
void firstMock ()
{
instance.injectSubclass (new Mock_1);
TestSingletonO* sing = &instance();
sing->doIt();
sing->doIt();
ASSERT (sing->getCnt() == 2);
instance.injectSubclass (0);
sing = &instance();
sing->doIt();
ASSERT (sing->getCnt() == 1);
}
{
instance.injectSubclass (new Mock_1);
TestSingletonO* sing = &instance();
sing->doIt();
sing->doIt();
ASSERT (sing->getCnt() == 2);
instance.injectSubclass (0);
sing = &instance();
sing->doIt();
ASSERT (sing->getCnt() == 1);
}
};
/** Register this test class... */
LAUNCHER (SingletonTestMock_test, "unit common");
} // namespace test
} // namespace cinelerra

View file

@ -1305,10 +1305,10 @@ From experiences with other middle scale projects, I prefer having the test code
[img[Example: Interfaces/Namespaces of the ~Session-Subsystems|uml/fig130053.png]]
</pre>
</div>
<div title="Loading Media" modifier="Ichthyostega" created="200709220005" tags="design spec" changecount="1">
<div title="Loading Media" modifier="Ichthyostega" modified="200709240049" created="200709220005" tags="design spec" changecount="2">
<pre>Opening and accessing media files on disk poses several problems, most of which belong to the domain of cinelerra's data backend. Here, we focus on the questions related to making media data available to the EDL and the render engine. Each media will be represented by an MediaAsset object, which indeed could be a compound object (in case of MultichannelMedia). Building this asset object thus includes getting informations from the real file on disk. For delegating this to the backend, we use the following query interface:
* {{{openFile(char* name)}}} requests accessing the file and yields some (opaque) handle when successful.
* {{{getChannel(int)}}} will then be issued in sequence with ascending index numbers, until it returns {{{NULL}}}.
* {{{queryFile(char* name)}}} requests accessing the file and yields some (opaque) handle when successful.
* {{{queryChannel(FHandle, int)}}} will then be issued in sequence with ascending index numbers, until it returns {{{NULL}}}.
* the returned struct (pointer) will provide the following information:
** some identifier which can be used to create a name for the corresponding media (channel) asset
** some identifier characterizing the access method (codec) needed to get at the media data. This should be rather a high level description of the media stream type, e.g. &quot;H264&quot;