【发布时间】:2018-10-17 03:42:09
【问题描述】:
我正在尝试包装一些方便的 C++ 代码,该代码旨在使用 VFW 在 Windows 上生成视频+音频,C++ 库位于 here 并且描述说:
使用适用于 Windows 的视频(因此它不是便携式的)。如果你想方便 在某处快速录制视频,不想涉水 VfW 文档自己。
我想在 Python 上使用那个 C++ 库,所以我决定使用 swig 来完成它。
问题是,我在对音频进行编码时遇到了一些问题,出于某种原因,我试图了解生成的视频为何损坏,似乎音频没有正确写入视频文件中。这意味着,如果我尝试使用 VLC 或任何类似的视频播放器打开视频,我会收到一条消息,指出视频播放器无法识别音频或视频编解码器。视频图像很好,所以我将音频写入文件的方式肯定有问题。
我正在附加 swig 接口和一个小 Python 测试,它试图成为原始 c++ test 的一个端口。
aviwriter.i
%module aviwriter
%{
#include "aviwriter.h"
%}
%typemap(in) (const unsigned char* buffer) (char* buffer, Py_ssize_t length) %{
if(PyBytes_AsStringAndSize($input,&buffer,&length) == -1)
SWIG_fail;
$1 = (unsigned char*)buffer;
%}
%typemap(in) (const void* buffer) (char* buffer, Py_ssize_t length) %{
if(PyBytes_AsStringAndSize($input,&buffer,&length) == -1)
SWIG_fail;
$1 = (void*)buffer;
%}
%include "aviwriter.h"
test.py
import argparse
import sys
import struct
from distutils.util import strtobool
from aviwriter import AVIWriter
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-audio", action="store", default="1")
parser.add_argument('-width', action="store",
dest="width", type=int, default=400)
parser.add_argument('-height', action="store",
dest="height", type=int, default=300)
parser.add_argument('-numframes', action="store",
dest="numframes", type=int, default=256)
parser.add_argument('-framerate', action="store",
dest="framerate", type=int, default=60)
parser.add_argument('-output', action="store",
dest="output", type=str, default="checker.avi")
args = parser.parse_args()
audio = strtobool(args.audio)
framerate = args.framerate
num_frames = args.numframes
width = args.width
height = args.height
output = args.output
writer = AVIWriter()
if not writer.Init(output, framerate):
print("Couldn't open video file!")
sys.exit(1)
writer.SetSize(width, height)
data = [0]*width*height
sampleRate = 44100
samples_per_frame = 44100 / framerate
samples = [0]*int(samples_per_frame)
c1, s1, f1 = 24000.0, 0.0, 0.03
c2, s2, f2 = 1.0, 0.0, 0.0013
for frame in range(num_frames):
print(f"frame {frame}")
i = 0
for y in range(height):
for x in range(width):
on = ((x + frame) & 32) ^ ((y+frame) & 32)
data[i] = 0xffffffff if on else 0xff000000
i += 1
writer.WriteFrame(
struct.pack(f'{len(data)}L', *data),
width*4
)
if audio:
for i in range(int(samples_per_frame)):
c1 -= f1*s1
s1 += f1*c1
c2 += f2*s2
s2 -= f2*c2
val = s1 * (0.75 + 0.25 * c2)
if(frame == num_frames - 1):
val *= 1.0 * (samples_per_frame - 1 - i) / \
samples_per_frame
samples[i] = int(val)
if frame==0:
print(f"i={i} val={int(val)}")
writer.WriteAudioFrame(
struct.pack(f'{len(samples)}i', *samples),
int(samples_per_frame)
)
writer.Exit()
我不认为samples 的生成不正确,因为我已经将python 端生成的值与c++ 端生成的值进行了比较,不过只是为第0 帧编写的数据包。
我对问题的一些怀疑是我在 swig 上创建类型图的方式,也许这不太好......或者问题可能存在于writer.WriteAudioFrame(struct.pack(f'{len(samples)}i', *samples), int(samples_per_frame)) 行,我不知道可能是什么,绝对是我将音频缓冲区从 Python 发送到 C++ 包装器的方式不好。
那么,您知道如何修复附加的代码,以便 test.py 能够像 c++ 测试一样生成具有正确音频的视频吗?
生成成功后,视频将显示一个神奇的滚动棋盘格,并带有催眠正弦波作为音频背景:D
补充说明:
1) 上面的代码似乎没有使用writer.SetAudioFormat,这对于函数AVIFileCreateStreamA 和AVIStreamSetFormat 是必需的。问题是我不知道如何在 swig 上导出这个结构,这样我就可以在 Python 上以与 test.cpp 相同的方式使用它,从 Mmreg.h 我看到结构看起来像这样:
typedef struct tWAVEFORMATEX
{
WORD wFormatTag; /* format type */
WORD nChannels; /* number of channels (i.e. mono, stereo...) */
DWORD nSamplesPerSec; /* sample rate */
DWORD nAvgBytesPerSec; /* for buffer estimation */
WORD nBlockAlign; /* block size of data */
WORD wBitsPerSample; /* Number of bits per sample of mono data */
WORD cbSize; /* The count in bytes of the size of
extra information (after cbSize) */
} WAVEFORMATEX;
不幸的是,我不知道如何在 aviwriter.i 上包装这些东西?我尝试使用 %include windows.i 并将这些内容直接包含在块 %{...%} 但我得到的只是一堆错误:/
2) 我不希望修改 aviwriter.h && aviwriter.cpp,因为这基本上是外部工作代码。
3) 假设我能够包装 WAVEFORMATEX 以便在 Python 上使用它,那么你是如何使用类似于 test.cpp 的 memset 的?即:memset(&wfx,0,sizeof(wfx));
【问题讨论】:
标签: python c++ windows audio swig