我正在使用Windows图像组件库对大量图像进行编码。理想情况下,我希望将编码器设置为具有某些属性一次,然后对所有图像重复使用该编码器。然而,在我所见过的所有示例中,似乎是为单个图像创建了编码器。
我正在读写字节流,而不是文件,可能会有多个线程同时运行。
下面是一段代码:
CComPtr<IWICBitmapEncoder> pEncoder;
CComPtr<IWICBitmapFrameEncode> pBitmapFrame;
CComPtr<IPropertyBag2> pPropertyBag;
CComPtr<IWICStream> pStream;
CComPtr<IStream> pOutputStream;
HRESULT hr;
// Setup memory stream, which is needed to stage raw image bits
if (CreateStreamOnHGlobal(NULL, TRUE, &pOutputStream) != S_OK)
{
LogAssert(false, "Could not create pOutputStream. Err (%d)", GetLastError());
}
//Setup WIC stream which encapsulates the output stream
hr = m_pFactory->CreateStream(&pStream);
hr = pStream->InitializeFromIStream(pOutputStream);
hr = m_pFactory->CreateEncoder(GUID_ContainerFormatWmp, NULL, &pEncoder);
hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
hr = pEncoder->CreateNewFrame(&pBitmapFrame, &pPropertyBag);
SetEncodingProperties(pPropertyBag);
hr = pBitmapFrame->Initialize(pPropertyBag);问题1:我正在写一个使用CreateStreamOnHGlobal创建的IStream。我可以对多个镜像重复使用pStream和pOutputStream吗?有没有线程安全问题?
问题2:这段代码的哪些部分可以一次完成,哪些部分需要针对不同的图像重复?所有的初始化似乎都是相互关联的。
发布于 2012-09-24 02:47:13
所有内置的WIC对象都在Windows7中更新为线程安全,如下所示:http://msdn.microsoft.com/en-us/library/ee720061%28v=vs.85%29.aspx#_multi_threaded_apartment_support
在以前的Windows版本中,对象不是线程安全的,并且Windows将自动将调用封送到另一个线程中,以便可以从多个线程访问它。这只有在正确初始化COM的情况下才能起作用-如果要从多个线程访问对象,则需要从所有可以访问对象的线程中使用多线程选项调用CoInitializeEx,并且需要确保CoInitializeEx返回成功(S_OK或S_FALSE)。
同时为一个IStream对象使用两个不同的编码器是不安全的。一旦在编码器对象上调用了Commit,就可以安全地在其他地方使用流,但是在另一个编码器中使用它并没有什么实际意义。您不能在一个文件中包含多个图像(除非图像格式支持多个帧,但是您只需要一个编码器对象)。我想您可以在使用另一个编码器之前将流大小设置为0,但分配HGLOBAL流可能不会比这样做更快。
在您的例子中创建IWICStream是没有意义的,因为您已经有了一个IStream,而这正是编码器所需要的。IWICStream主要作为一个方便的函数存在,用于从文件、固定大小的内存缓冲区或现有流的一部分创建流。克隆方法存在的原因是IWICStream不支持InitializeFromIStream方法。InitializeFromIStream是一种变通方法,可以在不支持克隆的情况下使用独立游标获取新的IStream对象,但是这样做的方式不是(也不可能是)线程安全的。(要使其正常工作,一次只能由一个线程访问底层流。通常,只要流一次只分配给一个编码器/解码器,编码器或解码器对象就会确保这一点。)
由于您关心性能,您应该知道,像WIC编码器可能做的那样,分片写入HGLOBAL流是O(n**2),因为扩大HGLOBAL涉及将所有现有数据复制到新位置。
如果你打算在Windows7之前从多个线程访问WIC,我建议你使用单线程单元,并确保你在一个线程中初始化的对象只能从那个线程访问。这将为您节省将调用编组到另一个线程的成本。
应该可以将IPropertyBag2对象与您的设置一起保存,并使用它来初始化所有位图帧,只要编码器类是相同的,并且您不会尝试从多个单线程单元(或单线程和多线程单元)使用相同的编码器类。
然而,我认为您过于关注那些对性能影响微不足道的事情,而您应该更关注图像数据的写入过程。如果图像文件很大,使用流类型可能会有所帮助(不过,如果您正在处理非常大的图像,您可能应该考虑使用imagemagick )。为每个线程提供完全独立的对象(因为单个对象很可能一次只能在单个线程中执行工作)也可能会有所帮助。
https://stackoverflow.com/questions/12548664
复制相似问题