2

I'm trying to write a C++ Video class using the Video For Windows interface, based on concepts from this NeHe tutorial, but with more modern code (for OpenGL 3/4). In my function that initially loads the video (not retrieve the frames), I refer to AVIStreamGetFrameOpen(), which according to MSDN:

Returns a GetFrame object that can be used with the AVIStreamGetFrame function.

The same page also says:

If the system cannot find a decompressor that can decompress the stream to the given format, or to any RGB format, the function returns NULL.

My problem is that AVIStreamGetFrameOpen() returns NULL, which as stated, means no decompressor was found that matches the file. However, my file can be played with Windows Media Player without issues, which I believe means a decompressor should be available.

There seems to be a lack of documentation when it comes to VFW, and MSDN pages aren't always extremely useful. Anyone know what could be causing this issue?

Here's the code for the function in question:

bool Video::Load(std::string FileName) {
    try {
        if (this->bLoaded)
            this->UnLoad();

        AVIFileInit();

        if (AVIStreamOpenFromFile(&pavi, FileName.c_str(), streamtypeVIDEO, 0, OF_READ, NULL) != 0)
            throw "Failed to open the AVI video stream.";

        AVIStreamInfo(pavi, &psi, sizeof(psi));             // Reads Information About The Stream Into psi
        this->szWidth = psi.rcFrame.right - psi.rcFrame.left;           // Width Is Right Side Of Frame Minus Left
        this->szHeight = psi.rcFrame.bottom - psi.rcFrame.top;          // Height Is Bottom Of Frame Minus Top

        ulnLastFrame = AVIStreamLength(pavi);                // The Last Frame Of The Stream

        this->dDuration = AVIStreamSampleToTime(pavi, ulnLastFrame) / 1000.0f;
        this->dSecondsPerFrame = this->dDuration / ulnLastFrame;

        pgf = AVIStreamGetFrameOpen(pavi, NULL);              // Create The PGETFRAME Using Our Request Mode
        if (pgf == NULL)
            // ===== ERROR THROWN HERE =====
            throw "Failed to open the AVI GetFrame object.";

        glGenTextures(1, &this->unTexID);
        glBindTexture(GL_TEXTURE_2D, this->unTexID);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, this->szWidth, this->szHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
        glBindTexture(GL_TEXTURE_2D, 0);
    }
    catch (const char* e) {
        Message((std::string("Error: ") + e + "\nFile: \"" + FileName + "\"").c_str(), "Error");
        return false;
    }


    this->bLoaded = true;
    return true;
}

Ignore my weird variable prefixes.

TheTrueJard
  • 471
  • 4
  • 18
  • The Windows Media Player is using DirectShow for all it's video stuff, not Video For Windows (which is an old and outdated technology). These days most video codecs come purely as DirectShow filters. So either use that, or make your program completely independent from OS specific multimedia libraries and use a cross plattform codec framework (like gstreamer or ffmpeg/libavcodec. – datenwolf Aug 21 '16 at 08:41
  • @datenwolf Thanks for the reply. I've heard DirectShow is complicated to learn, but I'll push through it. Would you recommend any particular sources or should I just look through the MSDN pages? I haven't found much else. – TheTrueJard Aug 24 '16 at 05:09
  • Frankly I'd not even go with DirectShow but instead use ffmpeg/libavcodec; the only drawback of that is, that the API is not *that* stable, but it's usually not that difficult to keep track of changes. A couple of years ago I wrote https://github.com/datenwolf/aveasy where the original intention actually was getting video frames on textures. I haven't updated it for a long time, so it probably won't compile right of the shelf with a current version of ffmpeg. – datenwolf Aug 24 '16 at 06:13
  • @datenwolf Thanks for the suggestion. I don't know much about ffmpeg as of now, but I'll look into it and see if I can figure out how to implement it into my class. – TheTrueJard Aug 27 '16 at 04:40

2 Answers2

1

My problem is that AVIStreamGetFrameOpen() returns NULL, which as stated, means no decompressor was found that matches the file. However, my file can be played with Windows Media Player without issues, which I believe means a decompressor should be available.

Your assumption that decompressor should be available is incorrect.

Windows offers several baseline APIs related to video and audio: Video for Windows, DirectShow, Media Foundation, also Windows Media. There are also layers on top of that (AudioVideoPlayback, MediaElement etc.

There is certain interoperability between the APIs: at some times codecs and other objects are shared between APIs, or one API provides a compatibility wrapper over other's objects.

It is however not the case in your scenario. Video For Windows is a legacy API and it is unable to use codecs for newer APIs. Windows Media Player in turn uses Media Foundation as a primary API, and DirectShow as a fallback API for complicated scenarios where it gives a second chance attempt to play a file. Basically, the only reason Video For Windows is still present in current version of Windows is support of legacy applications: newly available video/audio related features are not available to VFW, not just Microsoft's but third party too.

Additionally, 32-bit codecs and 64-bit codecs are independent and certain piece of code can only use the codecs of respective bitness.

That is, there is no intersection between Windows Media Player and your code in terms of consuming the same API. The fact that Windows Media Player plays the file does not mean that your VFW code should be able to too. You are hitting the problem that there is no suitable decoder installed to read and decode video from the file (you don't mention the format, a blind guess here is it's AVI with H.264 video).

Roman R.
  • 68,205
  • 6
  • 94
  • 158
1

VFW is very old/legacy/deprecated.

According to https://en.wikipedia.org/wiki/Video_for_Windows, VFW supports these formats only:

  • msrle
  • msvideo1
  • cinepak
  • indeo 3.2

I'm reading NeHe Tutorials too. Face2.avi used in Lesson 35 is encoded in cinepak format.

E:\>ffmpeg -i Face2.avi -hide_banner
Input #0, avi, from 'Face2.avi':
  Duration: 00:00:03.04, start: 0.000000, bitrate: 1116 kb/s
    Stream #0:0: Video: cinepak (cvid / 0x64697663), rgb24, 160x120, 29.97 tbr, 29.97 tbn, 29.97 tbc
                        ~~~~~~~

If you have to use VFW, you can use ffmpeg for Windows to convert your video into cinepak or msvideo1 format:

ffmpeg -i your_video.xxx -vcodec cinepak output.avi

(Converting to cinepak takes much longer time than msvideo1, but result in a better much better quality.)

kbridge4096
  • 901
  • 1
  • 11
  • 22