Невозможно создать файл AVI с видео и аудио (VFW) в приложении Delphi 6

Я пытаюсь написать приложение Delphi 6, которое принимает видео и аудио и записывает их в AVI с помощью служб VFW. Мне удалось наладить работу видеопотока. Он отлично работает в VLC или Windows Media Player. Но когда я затем добавляю аудиопоток и пытаюсь воспроизвести выходной AVI, проигрыватель VLC жалуется на то, что файл AVI поврежден. Он воспроизведет файл, и видео выглядит нормально, но нет звука. Я пробовал добавить аудиопоток перед записью видеопотока, а затем после него. Ни то, ни другое не сработало.

Кроме того, если я не добавляю аудиопоток, я могу щелкнуть правой кнопкой мыши выходной файл AVI в проводнике Windows, и на вкладке сводки я могу увидеть правильную информацию для видеоданных в потоке. После добавления потока щелчок правой кнопкой мыши вместо этого отображает сообщение о состоянии, в котором говорится, что свойства AVI не могут быть прочитаны.

ОБНОВЛЕНИЕ: я делаю что-то в корне неправильно. Оказывается, что-то, что я делаю, - это выдуваю заголовок верхнего уровня файла. Если я не создаю аудиопоток, с файлом все в порядке, и я вижу обычную информацию заголовка в самом начале файла. В тот момент, когда я вызываю для создания аудиопотока второй поток, который я создаю, заголовок в самом начале выходного файла AVI становится полностью пустым (нули). Даже если я не делаю вызов AVIStreamSetFormat () или запись любых данных (и даже если я это сделаю), просто делая этот второй вызов AVICreateStream () стирает самое начало файла при проверке файла с помощью шестнадцатеричного редактора. Что я мог делать, чтобы вызвать такую ​​сумму, если "повредил" выходной файл.

Мой вопрос:

1) Что я делаю не так? 2) Знаете ли вы хороший образец, который показывает, как создать AVI с чередованием аудио и видео, особенно тот, который показывает, как писать сжатый аудиопоток?

Вот код, который я использую для записи аудиопотока после того, как полностью записал видеопоток. В переменной уровня единицы измерения FAvi уже заполнено поле TWaveFormatEx (wfx). Я проверил содержимое, и все поля установлены на допустимые значения: частота дискретизации 8000 кГц, 1 канал, 16 бит на канал и тег формата 1 (WAVE_FORMAT_PCM). Выравнивание блоков, скорость передачи данных и т. Д. Также правильно заполнены:

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;

Вот код, который очищает аудио- и видеопоток при завершении:

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;

Уведомить: @RemyLebau


person Robert Oschler    schedule 06.05.2012    source источник
comment
Вы просили пример; ну, VirtualDub должен показать вам (не только), как обрабатывать аудио потоки и правильно настройте все структуры (см. AudioSource.cpp, InputFileMP3.cpp и InputFileWAV.cpp). Я был доволен этим несколько лет назад, когда пользовался им.   -  person TLama    schedule 07.05.2012
comment
Да, пользуюсь. Вот как я обнаружил, что создание аудиопотока каким-то образом искажало заголовок файла AVI с помощью Hex File Editor и помощника Riff Chunk.   -  person Robert Oschler    schedule 07.05.2012


Ответы (1)


На следующей странице есть компонент под названием "TAVIWriter":

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

который включает полный исходный код компонента и демонстрацию того, как его можно использовать.

person DwrCymru    schedule 06.05.2012
comment
Спасибо, посмотрю. Я только что сделал и обновил (ОБНОВЛЕНИЕ) свой пост. Буду признателен за ваш отзыв. - person Robert Oschler; 06.05.2012