2
votes

I'm trying to write a Delphi 6 app that takes video and audio and writes it to an AVI using the VFW services. I managed to get the video stream working properly. It plays fine in VLC or Windows Media Player. But when I then add an audio stream and try to play the output AVI the VLC player complains about the AVI file being broken. It will play the file and the video looks fine, but there is no audio. I tried adding the audio stream before writing the video stream and then after it. Neither worked.

Also, if I don't add an audio stream I can right-click the output AVI file in Windows Explorer and in the summary tab I can see the proper information for the video data in the stream. After adding the stream right-clicking instead shows a status message saying the AVI properties could not be read.

UPDATE: I am doing something really basically wrong. It turns out, something I am doing is blowing the top level header of the file. If I don't create the audio stream the file is fine and I see the usual header information at the very start of the file. The instant I make the call to create the audio stream, the second stream I create, the header at the very start of the output AVI file is completely blank (zeroes). Even if I don't make a call to AVIStreamSetFormat() or write any data (and even if I do), just making that second AVICreateStream() call wipes the very start of the file, when examining the file with a hex editor. What could I be doing that is causing this amount if "damage" to the output file.

My question are:

1) What am I doing wrong? 2) Do you know of a good sample that shows how to create an AVI with audio and video interleaved, especially one that shows how to write a compressed audio stream too?

Here is the code I am using to write the audio stream, after I have completely written out the video stream. The unit level variable FAvi has the TWaveFormatEx field (wfx) filled in already. I examined the contents and the fields are all set to valid values of 8000 Khz sample rate, 1 channel, 16 bits per channel, and format tag of 1 (WAVE_FORMAT_PCM). The block-align, data rate, etc. are all properly filled in too:

function TAviWriterWithCompression.addAudioFrame(dat: Pointer; numbytes: Cardinal): HRESULT;
var
    bRetErr: boolean;
    numsamps: LongInt;
    ahdr: TAVISTREAMINFO;
    hr: HRESULT;
    lSampWritten, lBytesWritten: LONG;
begin
    Result := S_OK;
    bRetErr := true;

    if Assigned(FAvi_) then
    begin
        if Assigned(dat) and (numbytes <> 0) then
        begin
            if not FAvi_.iserr then
            begin
                if FAvi_.wfx.nChannels <> 0 then
                    bRetErr := false
                else
                    Result := LongInt(AVIERR_BADFORMAT);
            end
            else
                Result := LongInt(AVIERR_ERROR);
        end
        else
            Result := LongInt(AVIERR_BADPARAM);
    end
    else
        Result := LongInt(AVIERR_BADHANDLE);

    if bRetErr then
        // =========================== EXIT POINT ==============
        exit;

    if FAvi_.wfx.wBitsPerSample <= 0 then
    begin
         Result := LongInt(AVIERR_BADFORMAT);

         // =========================== EXIT POINT ==============
         exit;
    end; // if FAvi_.wfx.wBitsPerSample <= 0 then 

    numsamps := Trunc((numbytes * 8) / FAvi_.wfx.wBitsPerSample);

    if ((numsamps * FAvi_.wfx.wBitsPerSample / 8) <> numbytes) then
    begin
        Result := LongInt(AVIERR_BADPARAM);

        // =========================== EXIT POINT ==============
        exit;
    end; // if ((numsamps * FAvi_.wfx.wBitsPerSample/8) <> numbytes) then

    if not Assigned(FAvi_.theAs) then
    begin
        ZeroMemory(@ahdr, sizeof(ahdr));
        ahdr.fccType        := streamtypeAUDIO;
        ahdr.dwScale        := FAvi_.wfx.nBlockAlign;
        ahdr.dwRate         := FAvi_.wfx.nSamplesPerSec*FAvi_.wfx.nBlockAlign;
        ahdr.dwSampleSize   := FAvi_.wfx.nBlockAlign;

        ahdr.dwQuality      := DWORD(-1);

        hr := AVIFileCreateStream(FAvi_.pfile, FAvi_.theAs, ahdr);

        if hr <> AVIERR_OK then
        begin
            Result := hr;

            // Set the error flag.
            FAvi_.iserr := true;

            // =========================== EXIT POINT ==============
            exit;
        end; // if hr <> AVIERR_OK then

        hr := AVIStreamSetFormat(FAvi_.theAs, 0, @FAvi_.wfx, sizeof(FAvi_.wfx));

        if hr <> AVIERR_OK then
        begin
            Result := hr;

            // Set the error flag.
            FAvi_.iserr := true;

            // =========================== EXIT POINT ==============
            exit;
        end; // if hr <> AVIERR_OK then
    end; // if not Assigned(FAvi_.theAs) then

    // now we can write the data
    hr := AVIStreamWrite(FAvi_.theAs, FAvi_.nsamp, numsamps, dat, numbytes, AVIIF_KEYFRAME, @lSampWritten, @lBytesWritten);

    if hr <> AVIERR_OK then
    begin
        Result := hr;

        // Set the error flag in our utility object.
        FAvi_.iserr := true;

        // =========================== EXIT POINT ==============
        exit;
    end; // if hr <> AVIERR_OK then

    Inc(FAvi_.nsamp, numsamps);

    // Set the flag that tells it is no longer a virgin file and that
    //  attempting to set the compression is not allowed.
    FIsVirginFile := false;
end;

Here is the code that cleans up both the audio and video stream when finishing up:

destructor TAviWriterWithCompression.Destroy;
begin
    // Code goes here.
    if Assigned(FAvi_) then
    begin
        // Release the streams.
        if Assigned(FAvi_.theAs) then
        begin
            AVIStreamRelease(FAvi_.theAs);
            FAvi_.theAs := nil;
        end;

        if Assigned(FAvi_.thePsCompressed) then
        begin
            AVIStreamRelease(FAvi_.thePsCompressed);
            // FAvi_.thePsCompressed := nil;
        end;

        if Assigned(FAvi_.thePs) then
        begin
            AVIStreamRelease(FAvi_.thePs);
            // FAvi_.thePs := nil;
        end;

        if Assigned(FAvi_.pfile) then
        begin
            AVIFileRelease(FAvi_.pfile);
            // FAvi_.pfile := nil;
        end;

        AVIFileExit();
        // FreeAndNil(FAvi_);
    end; // if Assigned(FAvi_) then

    inherited Destroy;
end;

Notify: @RemyLebau

1
You were asking for an example; well, the VirtualDub should be able to show you (not only) how to handle the audio streams and setup all the structures properly (see AudioSource.cpp, InputFileMP3.cpp and InputFileWAV.cpp). I've been satisfied with it few years ago when I was using it.TLama
Yes I've been using it. That's how I discovered that creating the audio stream was somehow mangling the AVI file header using the Hex File Editor and Riff Chunk assist.Robert Oschler

1 Answers

1
votes

There is a component called "TAVIWriter" on the following page:

http://www.efg2.com/Lab/Library/Delphi/Graphics/FileFormatsAndConversion.htm

which includes full source code to the component and a demo of how it can be used.