Я использую библиотеку компонентов Windows Imaging для кодирования большого количества изображений. В идеале я хотел бы настроить кодер один раз с определенными свойствами, а затем повторно использовать этот кодер для всех моих изображений. Однако во всех примерах, которые я видел, кажется, что кодер создан для одного изображения.
Я читаю и записываю из / в байтовые потоки, а не файлы, и одновременно может быть запущено несколько потоков.
Вот фрагмент кода:
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:
Я пишу в IStream, который создается с помощью CreateStreamOnHGlobal. Могу ли я повторно использовать pStream
а также pOutputStream
для нескольких изображений? Есть проблемы с безопасностью потоков?
Выпуск 2:
Какую часть этого фрагмента можно выполнить один раз, а какую часть нужно повторить для разных изображений? Все инициализации, похоже, связаны друг с другом.
Все встроенные объекты WIC были обновлены в Windows 7 для обеспечения многопоточности, как описано здесь: 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 существует в основном как вспомогательная функция для создания потока из файла, буфера памяти фиксированного размера или раздела существующего потока. Причина, по которой существует метод InitializeFromIStream, заключается в том, что IWICStream не поддерживает метод Clone. InitializeFromIStream — это обходной путь, который позволяет получить новый объект IStream с независимым курсором, когда Clone не поддерживается, но способ, которым это происходит, не является (и не может быть) потокобезопасным. (Чтобы это работало, к базовому потоку должен обращаться только один поток за один раз. Обычно объект кодера или декодера будет в этом уверен, если поток назначен только одному кодеру / декодеру за один раз. )
Поскольку вы беспокоитесь о производительности, вы должны знать, что частичная запись в потоки HGLOBAL, как это могут делать кодеры WIC, равна O (n ** 2), поскольку расширение HGLOBAL предполагает копирование всех существующих данных в новое местоположение. ,
Если вы собираетесь получить доступ к WIC из нескольких потоков до Windows 7, я бы предложил использовать однопоточные апартаменты и убедиться, что объекты, которые вы инициализируете в одном потоке, доступны только из этого потока. Это сэкономит вам расходы на маршалинг вызовов в другой поток.
Должна быть возможность сохранить объект IPropertyBag2 с вашими настройками и использовать его для инициализации всех растровых фреймов, если класс кодировщика одинаков и вы не пытаетесь использовать один и тот же из нескольких однопоточных квартир (или однопоточная и многопоточная квартира).
Тем не менее, я думаю, что вы слишком озабочены вещами, которые будут иметь незначительное влияние на производительность, когда вы должны быть более заинтересованы в процессе записи данных изображения. Использование типа потока, которому не нужно постоянно копировать память (возможно, что-то, что использует закадровый буфер памяти фиксированного размера, но может при необходимости сообщать меньший размер), может помочь, если ваши файлы изображений будут большими (хотя , если вы работаете с действительно большими изображениями, вам, вероятно, следует рассмотреть возможность использования вместо этого imagemagick). Также может помочь предоставление каждому потоку полностью независимых объектов (поскольку один объект, скорее всего, в любом случае сможет выполнять работу только в одном потоке за раз).
Других решений пока нет …