我有一个旧应用程序,使用MRLE编解码器在.avi容器内提供视频.该代码使用Video for Windows API.这项工作多年来一直令人钦佩,但我刚刚发现我的代码在Windows 8上运行不正常.
在Windows 8上,程序会创建一个.avi文件,但是当在例如Windows Media Player中查看时,视频播放的持续时间正确,但第一帧会显示整个时间.
我做了一个SSCCE来证明这个问题:
#include <Windows.h>
#include <vfw.h>
#include <cstdlib>
#include <iostream>
#pragma comment(lib, "vfw32.lib")
int main()
{
RECT frame = { 0, 0, 120, 100 };
AVIFileInit();
IAVIFile *pFile;
if (AVIFileOpenA(&pFile, "out.avi", OF_CREATE | OF_WRITE, NULL) != 0)
{
std::cout << "AVIFileOpen failed" << std::endl;
return 1;
}
AVISTREAMINFO si = { 0 };
si.fccType = streamtypeVIDEO;
si.fccHandler = mmioFOURCC('M', 'R', 'L', 'E');
si.dwScale = 1;
si.dwRate = 25;
si.dwQuality = (DWORD)-1;
si.rcFrame = frame;
IAVIStream *pStream;
if (AVIFileCreateStream(pFile, &pStream, &si) != 0)
{
std::cout << "AVIFileCreateStream failed" << std::endl;
return 1;
}
AVICOMPRESSOPTIONS co = { 0 };
co.fccType = si.fccType;
co.fccHandler = si.fccHandler;
co.dwQuality = si.dwQuality;
IAVIStream *pCompressedStream;
if (AVIMakeCompressedStream(&pCompressedStream, pStream, &co, NULL) != 0)
{
std::cout << "AVIMakeCompressedStream failed" << std::endl;
return 1;
}
size_t bmiSize = sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD);
BITMAPINFO *bmi = (BITMAPINFO*)std::malloc(bmiSize);
ZeroMemory(bmi, bmiSize);
bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi->bmiHeader.biWidth = si.rcFrame.right;
bmi->bmiHeader.biHeight = si.rcFrame.bottom;
bmi->bmiHeader.biPlanes = 1;
bmi->bmiHeader.biBitCount = 8;
bmi->bmiHeader.biCompression = BI_RGB;
bmi->bmiHeader.biSizeImage = bmi->bmiHeader.biWidth*bmi->bmiHeader.biHeight;
bmi->bmiHeader.biClrUsed = 256;
for (int i = 0; i < 256; i++)
{
RGBQUAD col = { i, i, i, 0 };
bmi->bmiColors[i] = col;
}
if (AVIStreamSetFormat(pCompressedStream, 0, bmi, bmiSize) != 0)
{
std::cout << "AVIStreamSetFormat failed" << std::endl;
return 1;
}
unsigned char *bits = new unsigned char[bmi->bmiHeader.biSizeImage];
for (int frame = 0; frame < 256; frame++)
{
std::memset(bits, 255-frame, bmi->bmiHeader.biSizeImage);
if (AVIStreamWrite(pCompressedStream, frame, 1, bits, bmi->bmiHeader.biSizeImage, 0, NULL, NULL) != 0)
{
std::cout << "AVIStreamWrite failed" << std::endl;
return 1;
}
}
if (AVIStreamRelease(pCompressedStream) != 0 || AVIStreamRelease(pStream) != 0)
{
std::cout << "AVIStreamRelease failed" << std::endl;
return 1;
}
if (AVIFileRelease(pFile) != 0)
{
std::cout << "AVIFileRelease failed" << std::endl;
return 1;
}
std::cout << "Succeeded" << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这会创建一个非常简单的视频.创建了一组灰色阴影.每个帧选择一个不同的灰色阴影.因此,所需的视频从白色过渡到黑色.这在Win7及更早版本中按预期发生.但在Win8上,视频只包含第一个白框.
这似乎是.avi文件生成的问题.如果我在Win8上生成文件并在Win7上查看,则文件无法正常播放.如果我在Win7上生成文件并在Win8上查看,则视频会根据需要显示.
我知道Video for Windows是一个古老的传统API.但是,我编码的帧非常适合行程编码.默认情况下,MRLE编解码器可用于我支持的所有Windows版本.因此,有充分的理由说明为什么我不愿意尝试使用更现代的多媒体API之一,直到我能确定带有MRLE的Win8上的Video for Windows是一个失败的原因.
是否可以在Win8上使用Video for Windows来创建MRLE编码的.avi文件?如果是这样怎么办?
这似乎确实是 Windows 8 中的一个错误。感谢 Roman 提出了一些解决该问题的想法。我可以确认手动执行 RLE 编码很容易,而且效果很好。
从问题中的代码开始,我们需要保留所有内容,并替换写入帧的 for 循环的内部结构。我们仍然需要创建压缩流,但我们不再写入它。相反,我们将 RLE8 编码数据写入原始流。
unsigned char *bits = new unsigned char[bmi->bmiHeader.biHeight*4 + 2];
for (int frame = 0; frame < 256; frame++)
{
size_t i = 0;
for (size_t y = 0; y < bmi->bmiHeader.biHeight; y++)
{
bits[i++] = bmi->bmiHeader.biWidth;
bits[i++] = 255-frame; // encoded run
bits[i++] = 0;
bits[i++] = 0; // EOL
}
bits[i++] = 0;
bits[i++] = 1; // EOB
if (AVIStreamWrite(pStream, frame, 1, bits, i, 0, NULL, NULL) != 0)
{
std::cout << "AVIStreamWrite failed" << std::endl;
return 1;
}
}
Run Code Online (Sandbox Code Playgroud)
显然,在实际应用程序中,您需要编写一个真正的 RLE8 编码器,但这证明了这一点。
| 归档时间: |
|
| 查看次数: |
583 次 |
| 最近记录: |