DirectShow ISampleGrabber: сэмплы перевернуты, а цветные каналы обращены

Я должен использовать MS DirectShow для захвата видеокадров с камеры (мне просто нужны необработанные данные пикселей).
Мне удалось построить сеть Graph / Filter (фильтр устройства захвата и ISampleGrabber) и реализовать обратный вызов (ISampleGrabberCB). Я получаю образцы соответствующего размера.

Однако они всегда перевернуты (перевернуты вертикально, то есть не повернуты), а цветовые каналы имеют порядок BGR (не RGB).

Я пытался установить для поля biHeight в BITMAPINFOHEADER как положительные, так и отрицательные значения, но это не имеет никакого эффекта. Согласно документации MSDN, ISampleGrapper :: SetMediaType () в любом случае игнорирует блок формата для видеоданных.

Вот что я вижу (записано другой камерой, а не DS), и что дает мне DirectShow ISampleGrabber: «RGB» на самом деле красным, зеленым и синим соответственно:

Вот что я вижу (записано другой камерой, а не DS)

Вот что дает мне DirectShow ISampleGrabber.

Пример кода, который я использую, немного упрощен:

// Setting the media type...
AM_MEDIA_TYPE*   media_type   = 0 ;
this->ds.device_streamconfig->GetFormat(&media_type); // The IAMStreamConfig of the capture device
// Find the BMI header in the media type struct
BITMAPINFOHEADER* bmi_header;
if (media_type->formattype != FORMAT_VideoInfo) {
bmi_header = &((VIDEOINFOHEADER*)media_type->pbFormat)->bmiHeader;
} else if (media_type->formattype != FORMAT_VideoInfo2) {
bmi_header = &((VIDEOINFOHEADER2*)media_type->pbFormat)->bmiHeader;
} else {
return false;
}
// Apply changes
media_type->subtype  = MEDIASUBTYPE_RGB24;
bmi_header->biWidth  = width;
bmi_header->biHeight = height;
// Set format to video device
this->ds.device_streamconfig->SetFormat(media_type);
// Set format for sample grabber
// bmi_header->biHeight = -(height); // tried this for either and both interfaces, no effect
this->ds.sample_grabber->SetMediaType(media_type);

// Connect filter pins
IPin* out_pin= getFilterPin(this->ds.device_filter, OUT,  0); // IBaseFilter interface for the capture device
IPin* in_pin = getFilterPin(this->ds.sample_grabber_filter,  IN,  0); // IBaseFilter interface for the sample grabber filter
out_pin->Connect(in_pin, media_type);

// Start capturing by callback
this->ds.sample_grabber->SetBufferSamples(false);
this->ds.sample_grabber->SetOneShot(false);
this->ds.sample_grabber->SetCallback(this, 1);
// start recording
this->ds.media_control->Run(); // IMediaControl interface

Я проверяю типы возвращаемых данных для каждой функции и не получаю никаких ошибок.

Я благодарен за любую подсказку или идею.

Вещи, которые я уже пробовал:

  1. Установка отрицательного значения для поля biHeight либо для фильтра устройства захвата, либо для захвата образца, либо для обоих, либо ни для одного из них — не имеет никакого эффекта.

  2. Использование IGraphBuilder для подключения выводов — та же проблема.

  3. Подключение контактов перед сменой типа носителя — та же проблема.

  4. Проверка, действительно ли тип носителя был применен фильтром, запрашивая его снова — но это очевидно применено или по крайней мере сохранено.

  5. Интерпретировать изображение как полностью измененный байт (последний байт первым, первый байт последним) — тогда оно будет перевернуто горизонтально.

  6. Проверка, если это проблема с видеокамерой — когда я тестирую ее с VLC (захват DirectShow), она выглядит нормально.

4

Решение

Я заметил, что при использовании цветового пространства I420 поворот исчезает.
Кроме того, большинство современных кодеков (VP8) используется в качестве необработанного цветового пространства ввода / вывода I420.

Я написал простую функцию зеркального отображения в цветовом пространстве I420.

void Camera::OutputCallback(unsigned char* data, int len, uint32_t timestamp, void *instance_)
{
Camera *instance = reinterpret_cast<Camera*>(instance_);

Transport::RTPPacket packet;

packet.rtpHeader.ts = timestamp;

packet.payload = data;
packet.payloadSize = len;

if (instance->mirror)
{
Video::ResolutionValues rv = Video::GetValues(instance->resolution);
int k = 0;

// Chroma values
for (int i = 0; i != rv.height; ++i)
{
for (int j = rv.width; j != 0; --j)
{
int l = ((rv.width * i) + j);
instance->buffer[k++] = data[l];
}
}

// U values
for (int i = 0; i != rv.height/2; ++i)
{
for (int j = (rv.width/2); j != 0; --j)
{
int l = (((rv.width / 2) * i) + j) + rv.height*rv.width;
instance->buffer[k++] = data[l];
}
}

// V values
for (int i = 0; i != rv.height / 2; ++i)
{
for (int j = (rv.width / 2); j != 0; --j)
{
int l = (((rv.width / 2) * i) + j) + rv.height*rv.width + (rv.width/2)*(rv.height/2);
if (l == len)
{
instance->buffer[k++] = 0;
}
else
{
instance->buffer[k++] = data[l];
}
}
}

packet.payload = instance->buffer;
}

instance->receiver->Send(packet);
}
0

Другие решения

Мой быстрый взлом на это:

void Camera::OutputCallback(unsigned char* data, int len, void *instance_)
{
Camera *instance = reinterpret_cast<Camera*>(instance_);

int j = 0;
for (int i = len-4; i > 0; i-=4)
{
instance->buffer[j] = data[i];
instance->buffer[j + 1] = data[i + 1];
instance->buffer[j + 2] = data[i + 2];
instance->buffer[j + 3] = data[i + 3];
j += 4;
}

Transport::RTPPacket packet;

packet.payload = instance->buffer;
packet.payloadSize = len;

instance->receiver->Send(packet);
}

Это верно для цветового пространства RGB32, для других цветовых пространств этот код должен быть исправлен

-1

По вопросам рекламы [email protected]