【问题标题】:Is it possible to encode using the MRLE codec on Video for Windows under Windows 8?是否可以在 Windows 8 下使用 Video for Windows 上的 MRLE 编解码器进行编码?
【发布时间】:2014-05-11 00:08:17
【问题描述】:

问题

我有一个旧应用程序,它使用 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;
}

这将创建一个非常简单的视频。创建了一个灰色调的调色板。每一帧都会选择不同的灰色阴影。因此,所需的视频从白色过渡到黑色。这在 Win7 及更早版本上按预期发生。但在 Win8 上,视频只包含第一个白帧。

这似乎是 .avi 文件生成的问题。如果我在 Win8 上生成文件并在 Win7 上查看,则该文件无法正确播放。如果我在 Win7 上生成文件并在 Win8 上查看,则视频会按需要显示。

我知道 Video for Windows 是一个古老的遗留 API。但是,我正在编码的帧非常适合游程编码。在我支持的所有 Windows 版本上,默认情况下都可以使用 MRLE 编解码器。因此,我有充分的理由不愿意尝试使用更现代的多媒体 API 之一,直到我可以确定 Win8 上的 Video for Windows with MRLE 是一个失败的原因。

问题

是否可以在 Win8 上使用 Video for Windows 创建 MRLE 编码的 .avi 文件?如果有,怎么做?

【问题讨论】:

  • 我会说问题出在编解码器本身 - 它接受新数据,但提供了一些陈旧的缓冲区。
  • @RomanR。感谢您的关注。我可以看到这是一个你是专家的主题领域。你觉得我还有希望吗?我的意思是,RLE_8 似乎很容易实现。也许我需要做的就是将 RLE_8 编码的位图输入未压缩的流中。虽然有些事情告诉我它一定比这更复杂。
  • 我可以在我的系统上重复这个问题。据推测,可以将问题隔离到编解码器,直接使用ICOpen 直接打开它,然后用几个视频帧输入它以检查输出。或者那里可能缺少某些标志并影响输出。但是,如果这很好用,那么您可以获得压缩数据以供进一步写入并消除AVIMakeCompressedStream 并绕过压缩直接写入数据。也就是说,再次尝试使用 ICOpen 可能会提出一种解决方法并提供更多信息(尽管简单地实现 RLE8 最终可能会更容易)。
  • @Roman 感谢这些提示。我会试试看。
  • @RomanR。最后,我从来没有达到ICOpen,而是直奔颈静脉。我证明了手动编码的 RLE8 是可行的,然后继续在我的应用程序中实现了一个编码器。这非常有效。虽然由于某种原因,我的编码器没有像 MS 编码器那样压缩,所以我想我需要研究 MS 编码的位图,看看是否有任何明显的改进需要我做。非常感谢您的提示和建议,非常有帮助。

标签: winapi vfw


【解决方案1】:

这似乎确实是 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;
    }
}

显然,在实际应用中,您需要编写一个真正的 RLE8 编码器,但这证明了这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-26
    • 2012-12-05
    • 2019-07-15
    相关资源
    最近更新 更多