我试图包装一小块方便的C++代码,该代码旨在使用VFW在windows上生成video+audio,C++库lives 这里,描述如下:
为Windows使用视频(因此它不是可移植的)。如果您想在某个地方快速录制一段视频,并且不想自己费力地浏览VfW文档,这很方便。
我想使用Python上的C++库,所以我决定使用swig结束它。
问题是,当涉及到音频编码时,我遇到了一些问题,由于某种原因,我试图理解为什么生成的视频被破坏了,似乎音频没有被正确地写入视频文件中。这意味着,如果我尝试用VLC或任何类似的视频播放器打开视频,我会收到一条消息,说视频播放器无法识别音频或视频编解码器。视频图像是好的,所以这肯定是一个问题,我写音频到文件的方式。
我同时附加了swig接口和一个小Python测试,试图成为原始c++测试的一个端口。
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编写的包。
我对出了什么问题的一些怀疑是我在酒杯上创建打字机地图的方式,也许这不太好.或者问题就在writer.WriteAudioFrame(struct.pack(f'{len(samples)}i', *samples), int(samples_per_frame))
中,我不知道会发生什么,我将音频缓冲区从C++发送到C++包装器的方式肯定不是很好。
那么,您是否知道如何修复附加的代码,以便test.py能够生成与c++测试类似的正确音频的视频?
当生成ok时,视频将显示一个神奇的滚动棋盘,以催眠的正弦波作为音频背景:D
附加注释:
( 1)上述代码似乎不使用writer.SetAudioFormat
,AVIFileCreateStreamA
和AVIStreamSetFormat
函数需要使用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;
不幸的是,我不知道如何将这些东西包装在导航器上。我试过使用%include windows.i并将其直接包含在块%{
.%}
上,但我所得到的只是一堆错误:/
2)我完全不喜欢修改aviwriter.h & aviwriter.cpp,因为这基本上是外部工作代码。
3)假设我能够包装WAVEFORMATEX
以便可以在Python上使用它,那么您如何使用memset类似于test.cpp
呢?ie:memset(&wfx,0,sizeof(wfx));
发布于 2018-05-13 23:11:18
有两项建议:
short
测试,将数据打包为int
格式,而不是音频格式的数据。音频数据是16位,而不是32位。对包装格式使用“h”分机。例如,struct.pack(f'{len(samples)}h', *samples)
。WAVEFORMATX
,编辑aviwriter.i
。然后从Python调用writer.SetAudioFormat(wfx)
。memset()
是不必要的。在python中,您可以手动将字段cbSize
设置为零,这应该足够了。其他六个字段是强制性的,所以无论如何都要设置它们。这个结构看起来不打算在将来进行修改,因为它没有一个struct字段,而且cbSize
的语义(将任意数据附加到结构的末尾)与扩展也有冲突。航空公司:
%inline %{
typedef unsigned short WORD;
typedef unsigned long DWORD;
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;
%}
test.py:
from aviwriter import WAVEFORMATEX
稍后在test.py中:
wfx = WAVEFORMATEX()
wfx.wFormatTag = 1 #WAVE_FORMAT_PCM
wfx.nChannels = 1
wfx.nSamplesPerSec = sampleRate
wfx.nAvgBytesPerSec = sampleRate * 2
wfx.nBlockAlign = 2
wfx.wBitsPerSample = 16
writer.SetAudioFormat(wfx)
注释:,因为aviwriter.h只提供tWAVEFORMATEX
的前向声明,没有向tWAVEFORMATEX
提供其他信息,从而阻止生成get/set包装器。您可以要求SWIG包装一个声明结构的Windows报头.打开一罐蠕虫,因为这些头太大太复杂,暴露了更多的问题。相反,您可以像上面所做的那样分别定义WAVEFORMATEX
。不过,C++类型WORD
和DWORD
仍未声明。包含SWIG文件windows.i
只创建包装器,例如,它允许将Python文件中的字符串"WORD“理解为表示内存中的16位数据。但这并不是从WORD
的角度来声明C++类型。要解决这个问题,在这个WORD
语句中添加%inline
和DWORD
的类型,aviwriter.i
将强制SWIG将代码直接内联到包装器C++文件中,使声明可用。这还会触发生成get/set包装器。或者,如果您愿意编辑,可以将内联的代码包含在aviwriter.h中。
简而言之,这里的想法是将所有类型完全封装到独立的头或声明块中。请记住,.i和.h文件有单独的功能(包装器和数据转换,而不是包装的功能)。类似地,请注意如何在aviwriter.h
中包含两次aviwriter.i
,一次用于触发C++所需的包装器的生成,一次是在C++所需的生成的包装代码中声明类型。
发布于 2018-05-13 00:09:33
根据我在代码中看到的,您没有初始化音频格式。这是在原始test.cpp
代码中通过在第44行调用writer.SetAudioFormat(&wfx);
来完成的,然后将其设置为mono44.1 kHz PCM。我相信,由于您不初始化,空白标题是写的,并且视频播放器无法打开未知的格式。
更新
因为您只需要传递二进制头结构,并且不需要使用该结构并在aviwriter.i
中声明它。您可以直接从Python中使用以下代码:
import struct
from collection import namedtuple
WAVEFORMATEX = namedtuple('WAVEFORMATEX', 'wFormatTag nChannels nSamplesPerSec nAvgBytesPerSec nBlockAlign wBitsPerSample cbSize ')
wfx = WAVEFORMATEX(
wFormatTag = 1,
nChannels = 1,
nSamplesPerSec = sampleRate,
nAvgBytesPerSec = sampleRate * 2,
nBlockAlign = 2,
wBitsPerSample = 16,
cbSize = 0)
audio_format_obj = struct.pack('<HHIIHHH', *list(wfx))
writer.SetAudioFormat(audio_format_obj)
这将自动解决您的第二和第三个问题。
至于memset(&wfx,0,sizeof(wfx));
,这只是旧C实现结构中所有变量的零的一种丑陋的方式。
正如@MichaelsonBritt所提到的,您的音频数据格式必须与报头中的声明相匹配。但与其转换为16位short
,你可以声明两个频道,所以你将得到立体声声音与一个频道静音。
https://stackoverflow.com/questions/50212416
复制