译文为作者辛苦翻译的产品(可能有部分翻译不是很准确,望知情者,联系:
tonxi@163.com
),转贴请注明原作者,为中国软件业进步努力着
DirectX Media Objects(一)
译者:tonxi
Microsoft® DirectX® Media Objects (DMOs)
是基于
COM
的数据流组件。在某些部分上,
DMOS
是类似于
Microsoft DirectShow filters
。比如对于
DirectShow filters, DMOs
也是输入数据到创建输出数据。而且,
DMOs
的
APIs
比相应的
DirectShow
的
APIs
要简单的多。因此,
DMOs
是比较容易被创建,测试和使用。
DMOs
可以被用于很多方面:
基于
DirectShow
的应用程序通过一个
DirectShow filter
叫作
DMO Wrapper filter
来使用
DMOs
。而
filters
与
DMOs
的区别是,
DMOs
对于应用程序是透明的。应用程序不用直接访问
DMO APIs.
基于
Microsoft DirectSound
的应用程序可以使用
audio effect DMOs
。另外,应用程序通过高级的
DirectSound APIs
从低级的
DMO APIs
隔离出来。
应用程序能够直接使用
DMOs.
关于
DMOs
这部分包括如下主题:
DMOs
的优势
DMO
的体系机构
DMOs
的优势
DMOs
提供了如下优势:
1
、它们通常要比
DirectShow filters
要小而且简单,因为它们支持较少的功能。
2
、它们要比
DirectShow filters
灵活,因为它们不需要一个
filter Graph
。当你需要
DirectShow
提供的一些服务时你可以让
DMOs
跟随
DirectShow
,这些服务例如,同步,智能连接,自动处理数据流程和线程管理。那些不需要这些服务的用户能够直接访问
DMOs
。
3
、
DMOs
始终是同步数据处理,这样就消除了你写一个
filter
需要考虑的多线程问题(译者注:
filter
有独立的数据传送线程,需要考虑多线程编程问题)
4
、与传统的
ACM
和
VCM codecs
不同的是,
DMOs
是基于组件模型(
COM
)的,因此,它们通过
QueryInterface
(接口)的方式进行扩充。
5
、
DMOs
支持一个比
ACM
或者
VCM codecs
更大众化的流模型
(
a more generalized streaming model
)。如同
DirectShow filters
,
DMOs
能支持多输入和多输出。
由于这些原因,
DMOs
现在被推荐为写编码器,解码器和音频效果的解决方案。许多其它的方案可能也很不错,这主要取决于应用程序的需要。
DMOs
与
DirectShow Filters
有什么不同?
DirectShow filters
运行离不开
DirectShow filter graph
。在
DirectShow
内,
Filter Graph Manager
用于应用程序和
filters
之间的调配。
DirectShow filters
要做很多数据流需求的工作,包括:
包括分配缓存。
协商媒体类型与连接其它的
filters
。
将数据通过
filter graph
。
发送事件给
Filter Graph Manager
。
多线程的同步。
相对比的是,一个
DMO
不需要做上面的这些事情。代替的是,这些任务被要求给使用
DMO
的
Client
去完成。
Client
负责分配缓存,填充它们数据和递送它们至
DMO
。
DMO
处理这些数据,并且
Client
从输出缓存中重新得到数据。
DMO
体系架构
这部分描述
DMO
的整个体系架构
Streams
(流)
一个
DMO
是一个有
m
个输入
n
个输出的对象。这些输入与输出被叫做流。每个
DMO
有至少一个流。流不是对
象;它们是通过索引数简单地从
DMO
上被引用的。这个流的索引数在设计时期就被固定了。
Media Types
(媒体类型)
所有数据都被指明使用一种媒体类型,它定义了如何说明数据的内容。举例,
320 x 240 24-bit RGB
的视频是一个类型,
44.1-kilohertz(kHZ) 16-bit stereo PCM
的音频是另外一种类型。媒体类型是被描述使用
DMO_MEDIA_TYPE
的结构。在
Client
能处理任何数据之前,它必须在
DMO
上为任何的流设置媒体类型。
Buffers
(缓存)
在默认的
DMO
模型里,
client
分配各个输入缓存和输出缓存。它填充数据至输入缓存然后递送它们至
DMO
处,接着
DMO
写入新的数据至输出缓存中。
自由地,一个
DMO
能支持“
in-place
”处理。通过“
in-place
”处理,
DMO
将输出缓存直接写入到输入缓存中,跳过初始数据。“
in-place
”处理消除了为个各个缓存分配的缓存的需求。在另一方面,它改变了初始数据,这一点并不能被一些应用程序所接收。
默认缓存模型(
non-in-place
)被
IMediaObjec
接口所支持。所有的
DMOs
必须实现这个接口。如果一个
DMO
支持“
in-place
”处理,它也有
IMediaObjectInPlace
接口。则那个
Client
被要求分配所有缓存,包括输入与输出。
使用
DMOs
This section describes how to use DMOs. It contains the following topics.
这部分描述如何使用
DMOs
。它包括如下主题:
直接使用
DMO
在
DirectShow
中使用
DMOs
直接使用
DMO
这部分描述一个应用程序怎样实现一个直接的
DMO
的
client
。应用程序递送输入至
DMO
,
DMO
创建输出,然后应用程序使用这个输出去绘制,更进一步处理或者其它处理。应用程序要负责一些问题,如内存分配,同步和线程问题。这些要求取决于应用程序的种类。
假如你正在写一个组件用于一个层在一个应用程序和一个
DMO
之间(举例,一个
ActiveX
控件使用一个
DMO
),这部分的信息同样适合你。更进一步讲,如果你正在写一个
DMO
,你应当阅读这部分内容,因为这部分内容描述了你的
DMO
必须实现的功能。
这部分内容包括如下主题:
设置
DMO
的
Media Types
(媒体类型)
DMO
的数据处理
DMO
的
In-Place
处理(
In-Place processing
)
DMO
的自由流处理(
Optional Streams
)
IMediaBufferr
的实现
设置
DMO
的
Media Types
(媒体类型)
在
DMO
能处理数据之前,
client
必须为任何流设置
media type
(媒体类型)(有一个局部例外:查看
Optional Streams(
自由流
)
)。为了寻找流的索引数,可以访问
ImediaObject::GetStreamCount
方法:
DWORD cInput = 0, cOutput = 0;
pDMO->GetStreamCount(&cInput, &cOutput);
这个方法返回两个值,输入的数与输出的数。这些值始终被固定在
DMO
中。
Preferred Types
(优先选择类型)
对于每个流,
DMO
分配一个可能的媒体类型列表,按照优先选择的顺序。举个例子,
preferred type
可能是
32-RGB
,
24-bit RGB
各
16-bitRGB
的顺序。当
client
设置媒体类型时,它能使用这些序列作为提示。为流检索一个
preferred type
,访问
IMediaObject::GetInputType
方法或
IMediaObject::GetOutputType
方法。为一个类型(开始于
0
)指定流的数和一个索引值。举例,下面的代码从第一个输入流中检索第一个
preferred type
:
DMO_MEDIA_TYPE mt
hr = pDMO->GetInputType(0, 0, &mt)
if (SUCCEEDED(hr))
{
// Examine this media type (not shown).
/* … */
// Free the format block.
MoFreeMediaType(&mt);
}
从一个给定的流中枚举所有的
preferred media types
,可以使用一个循环通过增量类型索引直到方法返回
DMO_E_NO_MORE_ITEMS
,如下面的例子:
DMO_MEDIA_TYPE mt;
DWORD dwType = 0;
while (hr = pDMO->GetInputType(0, dwType, &mt), SUCCEEDED(hr))
{
// Examine this media type (not shown).
/* … */
// Free the format block.
MoFreeMediaType(&mt);
++dwType;
}
你应当注意下面几个关于
preferred types
的注意点
DMO
可以返回一个类型没有
format
块。举例,一个
DMO
可以指定一个
video
类型,例如
24-bitRGB
,没有提供图像的宽和高。当你设置一个类型的时候,你必须提供一个完整的
format
块(当然,有些媒体类型除外,比如
MIDI
,从不要求一个
format
块,由于这个并不适用)。
DMO
并不被要求支持返回的每个联合的
preferred types
。举例,假如一个
DMO
有两个流,并且每个流有
4
个
preferred
类型,有
16
个可能的联合,但是,它们并不是所有都确保是正确的。
当
client
为一个流设置媒体类型时,
DMO
可能会为其它的流表示一个新的状态而更新
preferred types
。它并不要求这样做。而一些流,
DMO
可能不提供任何的
preferred types
。具有代表性地,一个
DMO
应当为一些流至少提供一些
preferred types
。
DMO
不被要求去提供一个它接受的完全的列表。有可能
DMO
支持“
unadvertised
”
types
,但是不当成
preferred types
来提供。
简而言之,
client
应当仅仅将
preferred types
视为指导方向。唯一能知道其中某种类型被支持的方法就是去测试它们,这个会在下个部分去描述。
为一个流设置媒体类型
使用
IMediaObject::SetInputType
和
IMediaObject::SetOutputType
方法为任何一个流设置媒体类型。你必须提供一个包涵完整媒体类型的描述的
DMO_MEDIA_TYPE
结构。下面的例子使用
44.1-kHz 16-bit stereo PCM audio
在
input stream 0
上设置媒体类型。
DMO_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(DMO_MEDIA_TYPE));
// Allocate memory for the format block.
HRESULT hr = MoInitMediaType(&mt, sizeof(WAVEFORMATEX));
if (SUCCEEDED(hr))
{
// Set the type GUIDs.
mt.majortype
= MEDIATYPE_Audio;
mt.subtype
= MEDIASUBTYPE_PCM;
mt.formattype = FORMAT_WaveFormatEx;
// Initialize the format block.
WAVEFORMATEX *pWave = reinterpret_cast<WAVEFORMATEX*>(mt.pbFormat);
pWave->wFormatTag = WAVE_FORMAT_PCM;
pWave->nChannels = 2;
pWave->nSamplesPerSec = 44100;
pWave->wBitsPerSample = 16;
pWave->nBlockAlign = (pWave->nChannels * pWave->wBitsPerSample) / 8;
pWave->nAvgBytesPerSec = pWave->nSamplesPerSec * pWave->nBlockAlign;
pWave->cbSize = 0;
// Set the media type.
hr = pDMO->SetInputType(0, &mt, 0);
// Release the format block.
MoFreeMediaType(&mt);
}
对于一个还没有设置的媒体类型,需要测试它,使用
DMO_SET_TYPE_TEST_ONLY
标志访问
SetInputType
或者
SetOutputType
来测试这个类型是否被接受。如果这个类型被接受,这个方法返回
S_OK
,否则返回
S_FALSE
。
if (S_OK == pDMO->SetInputType(0, &mt, DMO_SET_TYPEF_TEST_ONLY)
{
// Media type is OK.
}
由于对一个流进行设置能影响到另一个流,你也许需要清空一个流的媒体类型。可以这样做,使用
DMO_SET_TYPEF_CLEAR
标志访问
SetInputType
或者
SetOutputType
来进行清空。
对于一个
decoder DMO
,
client
将首先设置
input type
,然后选择一个
output type
。而对于一个
encoder DMO
,
client
将首先设置
output type
,然后才是
input type
。
DMO
的数据处理
这部分解释了如何使用
DMO
去处理一个数据流。这部分列举了默认状态的步骤。所有
DMOs
必须支持的方法都在这里描述了。这些方法为输入和输出使用单独的缓存。一些
DMOs
同样支持
in-place processing
,使用一个简单的缓存。关于
in-place
的详细信息,请看
In-Place Processing
。
分配缓存
client
负责为所有的缓存进行分配。在你对
DMO
设置了媒体类型后,将要查询
DMO
为每个流的分配请求。这些能改变主要依靠媒体类型。对于每个流,可以访问
IMediaObject::GetInputSizeInfo
或者
IMediaObject::GetOutputSizeInfo
方法。这些方法将返回下面的信息:
最小缓存大小,以字节形式
(
Alignment requirements, if any. A buffer is aligned if the start address is a multiple of some specified integer.
)
将适用于
lookahead
的
DMO
的最大数据统计数量。这上数字仅仅适用于输入流
(input streams)
。对于一些多样的数据(如,
MPEG encoding
),一个
DMO
也许需要为流的后期作出考虑。
lookahead
值指明了在它产生输出之前,
DMO
将需要多少输入数据。
client
必须分配缓存来匹配这些需求。更进一步讲,
DMO
可能有关于
client
怎样打包输入数据需要。举例,
DMO
可能需要每个缓存包涵正确的一个例子(或者
video frame
)。为了确定这些需要,可以访问
IMediaObject::GetInputStreamInfo
方法。这个
IMediaObject::GetOutputStreamInfo
方法返回相似的输出流信息。
在这个默认的流模型里,
client
不能传递原始缓存指针给
DMO
。代替的是,它使用一个轻量级的
COM
对象(可见的接口
IMediaBuffer
)。
IMediaBuffer
接口为一个内存块扮演了
COM wrapper
功能。因为它是一个
COM
对象,它支持访问统计(
reference counting
),这将有利于确认缓存没有被释放而仍然在使用。
注意:
IMediaBuffer
接口提供的一个功能类似于
DirectShow
里的
IMediaSample
接口。
client
必须实现
IMediaBuffer
对象。要查看详情,查看
Implementing
IMediaBuffer
。
数据处理
处理数据,要做以下几步:
1、
为每个输入流,填充一个输入数据缓存
2、
访问来
IMediaObject::ProcessInput
传递每个缓存
3、
访问
IMediaObject::ProcessOutput
来处理数据。这个方法带来一个缓存数组,都是输出流
4、
重复执行直到没有输入数据
ProcessInput
方法每次接受一个流输入。这个方法立即返回,而且
DMO
在
IMediaBuffer
对象上保持一个参考数。它处理完所有的缓存里的数据后,或者当
DMO
被应用程序
flush
的时候释放
IMediaBuffer
对象。不要再用
DMO
已经释放掉的一个缓存。为了确定一个输入流是否能接收更多数据,可以访问
IMediaObject::GetInputStatus
方法。如果那个流能接受更多输入,则这个方法返回
DMO_INPUT_STATUSF_ACCEPT_DATA
标志。
ProcessOutput
方法立即为所有的输出流生成输出。应用程序传送在
DMO_OUTPUT_DATA_BUFFER
结构数组里,任何一个输出流。(
The application passes in an array of
DMO_OUTPUT_DATA_BUFFER
structures, one for each output stream.
)
每个数据里的结构有一个指向
IMediaBuffer
对象的指针。
DMO
能向缓存中写入尽量多的数据。它同样设置了不同的标志去报告运行的状态。
DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE
标志指明了
DMO
能从存在的输入中产生更多的输出。在那个状态下,
client
能再次访问
ProcessOutput
。否则,它应当访问
ProcessInput
来调用再多的输入数据。
DMO
从不修改那些在输入缓存中的数据。它仅仅将数据写入到输出缓存中。
在你已经将所有的数据递送至一个输入流中之后,访问
IMediaObject::Discontinuity
方法。在你没处理完剩余的输出(或者
flush DMO
)的时候,
DMO
不接受更多的输入到那个流。
(
At any point after streaming begins
),
DMO
能够接收输入或产生输出或者两者都有。因此,不是
GetInputStatus
返回
DMO_INPUT_STATUSF_ACCEPT_DATA
,就是
ProcessOutput
返回
DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE
。应用程序要保持数据通顺可以通过测试这些标志和访问
ProcessInput
或
ProcessOutput
的结果。为了中断数据流通,可访问
IMediaObject::Flush
方法。这个方法使得
DMO
丢弃任何
DMO
内部正保持的缓存。
(未完待续)