DirectShow: рендеринг для YV12
От: Vicul  
Дата: 22.10.18 11:32
Оценка:
Пытаюсь разобраться, как происходит преобразование YV12 фрейма. Для простоты создаю вот такой DS граф



С камеры снимаю фреймы размером 640x480, после ЛАВ декодер преобразовывает его в YV12 и передает его на DS рендер.
С Выхода декодера вижу следующее:

Ширина фрейма преобразовалась с 640 на 1024, соответственно, вместо ожидаемого размера 1.5*640*480=460800 вижу 737280,
т.е пересчитанный для ширины 1024. По теории страйд может отличаться от ширины кадра на рендере. Но почему на столько и почему 1024?

Когда я вместо ЛАВ декодера ставлю свой фильтр с преобразованием RGB24 в YV12 (https://gist.github.com/thedeemon/8052fb98f8ba154510d7),
у меня выходит искаженное изображение, хотя параметры такие же:



И еще, почему в VIDEOINFOHEADER2 установлен флаг деинтерлейсинга — dwInterlaceFlags, хотя в ЛАВ декодере он отключен? Т.е. выходит
, что простым преобразованием RGB24 в YV12 не обойтись, и в мой фильтр нужно добавлять деинтерлейсинг, чтобы нормально заработал DS рендер?
Re: DirectShow: рендеринг для YV12
От: Videoman Россия https://hts.tv/
Дата: 23.10.18 08:55
Оценка: 3 (1)
Здравствуйте, Vicul, Вы писали:

V>Ширина фрейма преобразовалась с 640 на 1024, соответственно, вместо ожидаемого размера 1.5*640*480=460800 вижу 737280,

V>т.е пересчитанный для ширины 1024. По теории страйд может отличаться от ширины кадра на рендере. Но почему на столько и почему 1024?

Сначала фильтры договариваются на 640х480. Как только Renderer аллоцирует Direct3D surface в видеопамяти, при старте, он берет его реальные размеры и реальный страйд. Это происходит c использованием механизма Dynamic Reconnect from Downstream. У себя в фильтре ты это ловишь как установленное поле media type у output семпла. Для YV12 страйд у тебя будет: для Y плоскости 1024 * 12 / 8 = 1536 байт, для CyCb плоскостей: 512 * 12 / 8 = 768 байт. Плоскости копируешь построчно сразу в память Direct3D surface сначала Y, затем сразу CbCy.

V>Когда я вместо ЛАВ декодера ставлю свой фильтр с преобразованием RGB24 в YV12 (https://gist.github.com/thedeemon/8052fb98f8ba154510d7),

V>у меня выходит искаженное изображение, хотя параметры такие же:

V> Image: fail.jpg


V> И еще, почему в VIDEOINFOHEADER2 установлен флаг деинтерлейсинга — dwInterlaceFlags, хотя в ЛАВ декодере он отключен? Т.е. выходит

V>, что простым преобразованием RGB24 в YV12 не обойтись, и в мой фильтр нужно добавлять деинтерлейсинг, чтобы нормально заработал DS рендер?

Нет, это флаг для Renderera. Для его использования, если поддерживается, нужно установить соответствующее свойство в его настройках. На копирование буфера это никак не влияет.
Re[2]: DirectShow: рендеринг для YV12
От: Vicul  
Дата: 23.10.18 09:27
Оценка:
Спасибо за инфу
Re[2]: DirectShow: рендеринг для YV12
От: Vicul  
Дата: 23.10.18 10:50
Оценка:
V> Плоскости копируешь построчно сразу в память Direct3D surface сначала Y, затем сразу CbCy.

Значит для моего трансформ фильтра метод

HRESULT CVideoGrabberFilter::Transform(IMediaSample *pIn, IMediaSample *pOut)


Где я тупо копирую преобразованный буфер в YV12 c pIn на pOut не подходит?
Re[3]: DirectShow: рендеринг для YV12
От: Videoman Россия https://hts.tv/
Дата: 23.10.18 10:57
Оценка: 2 (1)
Здравствуйте, Vicul, Вы писали:

V>

V>HRESULT CVideoGrabberFilter::Transform(IMediaSample *pIn, IMediaSample *pOut)


V>Где я тупо копирую преобразованный буфер в YV12 c pIn на pOut не подходит?


Как раз подходит. Но нужно копировать все плоскости по строкам с учетом страйда. pOut->GetBuffer() сразу даст указатель на кусок в видео-памяти. Только вот копировать из pOut не желательно, можно сильно просадить производительность на некоторых системах. На всякий случай псевдокод для Y:
for (y = 0; y < 480; ++y) {

copy(src, dst, 640);

src += 640;
dst += stride;

}
Re[4]: DirectShow: рендеринг для YV12
От: Vicul  
Дата: 23.10.18 13:11
Оценка:
V>Как раз подходит. Но нужно копировать все плоскости по строкам с учетом страйда. pOut->GetBuffer() сразу даст указатель на кусок в видео-памяти. Только вот копировать из pOut не желательно, можно сильно просадить производительность на некоторых системах. На всякий случай псевдокод для Y:
V>
V>for (y = 0; y < 480; ++y) {

V>copy(src, dst, 640);

V>src += 640;
V>dst += stride;

V>}
V>


Картинку получил нормальную, но с цветом проблема



Вот код

CVideoGrabberFilter::Transform(IMediaSample *pIn, IMediaSample *pOut)
{
    BYTE* pSrcBuf = 0;
    pIn->GetPointer(&pSrcBuf);
    BYTE* pDstBuf = 0;
    pOut->GetPointer(&pDstBuf);
    SIZE size;
    size.cx = 640;
    size.cy = 480;
    int nLen = pOut->GetActualDataLength();

    BYTE* pDstTmp = new BYTE[nLen];
    CopyYuv420p_calcBGR24(pDstTmp, pSrcBuf, size.cx, size.cy);
    BYTE* pDst = pDstTmp;
    int stride = 1024;
    int nLenTmp = nLen;
    //Y 
    for (int y = 0; y < size.cy; ++y)
    {
        memcpy(pDstBuf, pDst, size.cx);
        pDst += size.cx;
        pDstBuf += stride;
        nLenTmp -= stride;
    }
    stride /= 2;
    //U and V
    for (int y = 0; y < size.cy; ++y)
    {
        memcpy(pDstBuf, pDst, size.cx);
        pDst += size.cx;
        pDstBuf += stride;
        nLenTmp -= stride;
    }
    delete[] pDstTmp;
}
Re[5]: DirectShow: рендеринг для YV12
От: Videoman Россия https://hts.tv/
Дата: 23.10.18 13:18
Оценка: 3 (1)
Здравствуйте, Vicul, Вы писали:


V>>Как раз подходит. Но нужно копировать все плоскости по строкам с учетом страйда. pOut->GetBuffer() сразу даст указатель на кусок в видео-памяти. Только вот копировать из pOut не желательно, можно сильно просадить производительность на некоторых системах. На всякий случай псевдокод для Y:

V>>
V>>for (y = 0; y < 480; ++y) {

V>>copy(src, dst, 640);

V>>src += 640;
V>>dst += stride;

V>>}
V>>


V>Картинку получил нормальную, но с цветом проблема


V>Image: failcolor.jpg


V>Вот код


V>
V>CVideoGrabberFilter::Transform(IMediaSample *pIn, IMediaSample *pOut)
V>{
V>    BYTE* pSrcBuf = 0;
    pIn->>GetPointer(&pSrcBuf);
V>    BYTE* pDstBuf = 0;
    pOut->>GetPointer(&pDstBuf);
V>    SIZE size;
V>    size.cx = 640;
V>    size.cy = 480;
V>    int nLen = pOut->GetActualDataLength();

V>    BYTE* pDstTmp = new BYTE[nLen];
V>    CopyYuv420p_calcBGR24(pDstTmp, pSrcBuf, size.cx, size.cy);
V>    BYTE* pDst = pDstTmp;
V>    int stride = 1024;
V>    int nLenTmp = nLen;
V>    //Y 
V>    for (int y = 0; y < size.cy; ++y)
V>    {
V>        memcpy(pDstBuf, pDst, size.cx);
V>        pDst += size.cx;
V>        pDstBuf += stride;
V>        nLenTmp -= stride;
V>    }
V>    stride /= 2;
V>    //U and V
V>    for (int y = 0; y < size.cy; ++y)
V>    {
V>        memcpy(pDstBuf, pDst, size.cx);
V>        pDst += size.cx;
V>        pDstBuf += stride;
V>        nLenTmp -= stride;
V>    }
V>    delete[] pDstTmp;
V>}
V>


Э... там три плоскоскти Y = 4x4, Cb = 1x2, Cr = 1x2. Вы забыли красную компоненту скопировать.
Re[6]: DirectShow: рендеринг для YV12
От: Vicul  
Дата: 24.10.18 07:12
Оценка:
V>Э... там три плоскоскти Y = 4x4, Cb = 1x2, Cr = 1x2. Вы забыли красную компоненту скопировать.

Спасибо заработало. Правильный код здесь, может кому время сэкономит

CVideoGrabberFilter::Transform(IMediaSample *pIn, IMediaSample *pOut)
{
    BYTE* pSrcBuf = 0;
    pIn->GetPointer(&pSrcBuf);
    BYTE* pDstBuf = 0;
    pOut->GetPointer(&pDstBuf);
    SIZE size;
    size.cx = 640;
    size.cy = 480;
    int nLen = pOut->GetActualDataLength();

    BYTE* pDstTmp = new BYTE[nLen];
    YV12ConverterFromRGB24(pSrcBufEnd, pDstTmp, size.cx, size.cy);
    BYTE* pDst = pDstTmp;
    int stride = 1024;
    int nLenTmp = nLen;
    //Y 
    for (int y = 0; y < size.cy; ++y)
    {
        memcpy(pDstBuf, pDst, size.cx);
        pDst += size.cx;
        pDstBuf += stride;
        nLenTmp -= stride;
    }
    stride /= 2;
    size.cy /= 2;
    size.cx /= 2;
    //U and V 
    for (int y = 0; y < size.cy; y++ )
   { 
    memcpy(pDstBuf, pDst, size.cx );
    pDst += size.cx;
    pDstBuf += stride;

    memcpy(pDstBuf, pDst, size.cx);
    pDst += size.cx;
    pDstBuf += stride;
    }
    delete[] pDstTmp;
}
Re[7]: DirectShow: рендеринг для YV12
От: Videoman Россия https://hts.tv/
Дата: 24.10.18 08:22
Оценка:
Здравствуйте, Vicul, Вы писали:

V>Спасибо заработало. Правильный код здесь, может кому время сэкономит...


Код у вас правильный только для вашего конкретного случая. Лучше не прибивать константы гвоздями, а точно брать из текущего контекста. Страйд задается конкретной реализацией Direct3D и драйвером оборудования. Он не обязан быть 1024 байта и это все определяется аллокацией видеопамяти. По-этому лучше делать так:

Если (SUCCEED(pOut->GetMediaType()), то значит рендерер хочет динамический сменить тип, и это ваш случай. Дальше из этого медиа-типа вы берете VIDEOINFOHEADER и от туда width. Он всегда должен быть больше либо равен вашему size.cx. Дальше страйд вы считаете как vi.vihWidth * 12 /8 — в байтах. Не забудьте после всего вызвать SetMediaType() у фильтра с новым медиа-типом и освободить ресурсы AM_MEDIA_TYPE.
Re[8]: DirectShow: рендеринг для YV12
От: Vicul  
Дата: 24.10.18 12:01
Оценка:
V>Код у вас правильный только для вашего конкретного случая. Лучше не прибивать константы гвоздями, а точно брать из текущего контекста. Страйд задается конкретной реализацией Direct3D и драйвером оборудования. Он не обязан быть 1024
Правильно, константы там не должно быть. Это только код "напопроб", проверить идею. Ну, а в приложении, в первом вызове Transform(), я вызываю GetMediaType(), где я по ширине кадра определяю уже устаканившийся страйд без умножения на 12/8. Умножение мне пока не нужно, потому что граф всегда настраивается на одно только разрешение камеры. А если надо установить новое разрешение, я создаю новый граф. Пока так проще.

Еще раз спасибо за помощь.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.