yurikhan: (Default)
[personal profile] yurikhan

Нет, всё-таки бардак творится в мире Windows.

Допустим, нам надо показать пользователю AVI-файл. Покадрово. При этом нам понадобится с кадрами в процессе показывания ещё что-то делать, поэтому вариант «бросить на форму медиаплеерный контрол» не канает.

Значит, берём MSDN в руки, и вперёд. Там есть такой раздельчик — Video for Windows. И вроде как всё выглядит довольно просто. Открыть файл, взять у него первый видеопоток, получить у потока декомпрессор:

void CChildView::OnFileOpen()
{
  CFileDialog fd(TRUE, ".avs", 0, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST,
    "Video files (*.avi;*.avs)|*.avi;*.avs|"
    "All files (*.*)|*.*||", GetParent(), 0);
  if (IDOK != fd.DoModal()) return;
  ASSERT_SUCCESS(AVIFileOpen(&m_pAviFile, fd.GetFileName(), OF_READ | OF_SHARE_DENY_WRITE, 0));
  ASSERT_SUCCESS(AVIFileGetStream(m_pAviFile, &m_pAviStream, streamtypeVIDEO, 0));
  m_pGetFrame = ASSERT_NOTNULL(AVIStreamGetFrameOpen(m_stream, ???));
  m_pos = 0;
  Invalidate();
}

???: Здесь нам предлагают дать указатель на структуру BITMAPINFOHEADER, заполненную форматом, в котором мы бы хотели получать декомпрессированные данные. При этом утверждается, что, если оно не умеет разжимать в этот формат, то вернёт NULL.

Кроме того, есть ещё такое специальное значение AVIGETFRAMEF_BESTDISPLAYFMT, которое обещает разжимать в тот формат, который наиболее подходит дисплею.

Замечательно. Берём это волшебное значение, и поехали.

Теперь нужно научиться показывать кадры. MSDN настоятельно рекомендует для этого использовать функции DrawDib. Хорошо.

void CChildView::OnPaint()
{
  CPaintDC dc(this);
  if (!m_getFrame) return;
  LPVOID pFrame = ASSERT_NOTNULL(AVIStreamGetFrame(m_getFrame, m_pos));
  LPBITMAPINFOHEADER pHeader = reinterpret_cast<LPBITMAPINFOHEADER>(pFrame);
  LPVOID pData = reinterpret_cast<BYTE*>(pFrame) +
    pHeader->biSize + // пропустить заголовок
    pHeader->biClrUsed * sizeof(RGBQUAD); // и сколько там у него цветов в палитре
  ASSERT_TRUE(DrawDibDraw(m_hDrawDib, dc, 
    0, 0, pHeader->biWidth, pHeader->biHeight, 
    pHeader, pData, 
    0, 0, pHeader->biWidth, pHeader->biHeight, 0));
}

Что не так на этой картинке?

Да вроде бы всё работает, рисуется, кадры сменяются.

Теперь переключаемся из 32-битного цвета в 16-битный. Перезапускаем программу. И привет:

Assertion failed: DrawDibDraw: The operation completed successfully.

Убираем AVIGETFRAMEF_BESTDISPLAYFMT, ставим вместо него NULL, всё работает.

Смотрим, чего там такое нам дают. Выясняется, что в 32-битном цвете нам всегда дают 32-битную картинку, тогда как в 16-битном по умолчанию дают 24-битную, а «наиболее подходящий» формат — 16-битный. И когда приходит время эту 16-битную картинку рисовать на экран, DrawDibDraw вызывает SetDIBitsToDevice, которая вызывает какие-то функции видеодрайвера, которые её не осиливают. Хотя, если самому позвать SetDIBitsToDevice, то что-то рисуется. Хотя и с неправильными цветами.

Спасибо хоть, что в синьку не падают.

(will be screened)
(will be screened if not validated)
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

Profile

yurikhan: (Default)
Yuri Khan

August 2018

S M T W T F S
   1234
567891011
12131415161718
19202122232425
26 2728293031 

Links

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated 2026-02-06 18:34
Powered by Dreamwidth Studios