[Soft] Показать avi-файл
2006-09-17 03:34Нет, всё-таки бардак творится в мире 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-битный. Перезапускаем программу. И привет:
Убираем AVIGETFRAMEF_BESTDISPLAYFMT, ставим вместо него NULL, всё работает.
Смотрим, чего там такое нам дают. Выясняется, что в 32-битном цвете нам всегда дают 32-битную картинку, тогда как в 16-битном по умолчанию дают 24-битную, а «наиболее подходящий» формат — 16-битный. И когда приходит время эту 16-битную картинку рисовать на экран, DrawDibDraw вызывает SetDIBitsToDevice, которая вызывает какие-то функции видеодрайвера, которые её не осиливают. Хотя, если самому позвать SetDIBitsToDevice, то что-то рисуется. Хотя и с неправильными цветами.
Спасибо хоть, что в синьку не падают.
no subject
Date: 2006-10-06 19:43 (UTC)no subject
Date: 2006-10-06 19:57 (UTC)