我正在尝试编写一个 Delphi 6 应用程序,该应用程序获取视频和音频并使用 VFW 服务将其写入 AVI。我设法让视频流正常工作。它在 VLC 或 Windows Media Player 中播放良好。但是当我添加一个音频流并尝试播放输出 AVI 时,VLC 播放器抱怨 AVI 文件被破坏。它将播放文件,视频看起来不错,但没有音频。我尝试在写入视频流之前添加音频流,然后在它之后添加。都没有奏效。
此外,如果我不添加音频流,我可以在 Windows 资源管理器中右键单击输出 AVI 文件,在摘要选项卡中,我可以看到流中视频数据的正确信息。添加流后,右键单击会显示一条状态消息,说明无法读取 AVI 属性。
更新:我做的事情基本上是错误的。事实证明,我正在做的事情是破坏文件的顶级标题。如果我不创建音频流,则文件很好,我会在文件的开头看到通常的标题信息。 在我调用创建音频流的那一刻,我创建的第二个流,输出 AVI 文件开头的标题是完全空白的(零)。 即使我没有调用 AVIStreamSetFormat() 或写入任何数据(即使我这样做了),在使用十六进制编辑器检查文件时,只需进行第二次 AVICreateStream() 调用就会擦除文件的开头。如果对输出文件“损坏”,我能做什么会导致这个数量。
我的问题是:
1)我做错了什么?2) 你知道一个很好的示例,它展示了如何创建一个音频和视频交错的 AVI,尤其是一个展示如何编写压缩音频流的示例?
在我完全写出视频流之后,这是我用来编写音频流的代码。单元级变量 FAvi 已经填充了 TWaveFormatEx 字段 (wfx)。我检查了内容,所有字段都设置为 8000 Khz 采样率、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