使用自定义视频编写器库编写音频的错误

2023-12-19

我正在尝试包装一小段方便的 C++ 代码,旨在使用 VFW 在 Windows 上生成视频+音频,C++ 库存在here http://www.farbrausch.de/%7Efg/code/aviwriter/描述说:

使用 Windows 视频(因此不可移植)。如果你愿意的话很方便 快速在某个地方录制视频,而不必费力地走过 VfW 自己提供文档。

我想在 Python 上使用 C++ 库,所以我决定使用 swig 来包装它。

问题是,我在编码音频时遇到了一些问题,出于某种原因,我试图理解为什么生成的视频被破坏,音频似乎没有正确写入视频文件中。这意味着,如果我尝试使用 VLC 或任何类似的视频播放器打开视频,我会收到一条消息,指出视频播放器无法识别音频或视频编解码器。视频图像很好,所以这肯定是我将音频写入文件的方式有问题。

我附上了 swig 接口和一个小的 Python 测试,试图成为原始版本的端口c++ test http://www.farbrausch.de/%7Efg/code/aviwriter/test.cpp.

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 and AVIStreamSetFormat。问题是我不知道如何在 swig 上导出这个结构,这样我就可以像在 Python 上一样使用它test.cpp,从 Mmreg.h 我看到结构如下所示:

    typedef 结构 tWAVEFORMATEX { WORD wFormatTag; /* 格式类型/ WORD nChannels; /通道数(即单声道、立体声...)/ DWORD nSamplesPerSec; /采样率/ DWORD nAvgBytesPerSec; /用于缓冲区估计/ WORD nBlockAlign; /数据块大小/ WORD wBitsPerSample; /单声道数据每个样本的位数/ WORD cbSize; /大小的字节数 额外信息(cbSize之后)*/

    } 波形格式;

不幸的是我不知道如何在 aviwriter.i 上包装这些东西?我尝试过使用 %include windows.i 并将这些内容直接包含在块上%{...%}但我得到的只是一堆错误:/

  1. 我不想修改 aviwriter.h && aviwriter.cpp ,因为这基本上是外部工作代码。

  2. 假设我能够包裹WAVEFORMATEX所以我可以在Python上使用它,你如何使用memset类似于test.cpp? ie: memset(&wfx,0,sizeof(wfx));


两个建议:

  • 首先,将数据打包为short代替int对于音频格式,按照 C++ 测试。音频数据是 16 位,而不是 32 位。使用“h”扩展名作为打包格式。例如,struct.pack(f'{len(samples)}h', *samples).

  • 其次,看下面的代码修改。暴露WAVEFORMATX通过 SWIG,通过编辑aviwriter.i。然后打电话writer.SetAudioFormat(wfx)来自Python。

  • 在我的测试中,memset()没有必要。从 python 中,您可以手动设置该字段cbSize为零,这应该足够了。其他六个字段是强制性的,因此您无论如何都需要设置它们。看起来这个结构体将来不会被修改,因为它没有结构体大小字段,而且也没有语义cbSize(将任意数据附加到结构的末尾)无论如何都与扩展冲突。

aviwriter.i:

%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)

SWIG 的注意事项:由于 aviwriter.h 仅提供了前向声明tWAVEFORMATEX,不会向 SWIG 提供其他信息,从而防止生成 get/set 包装器。您可以要求 SWIG 包装一个声明结构的 Windows 标头……并打开一堆蠕虫,因为这些标头太大且复杂,从而暴露出更多问题。相反,您可以单独定义WAVEFORMATEX如上所述。 C++ 类型WORD and DWORD不过,仍然没有宣布。包括 SWIG 文件windows.i仅创建包装器,例如,允许将 Python 脚本文件中的字符串“WORD”理解为指示内存中的 16 位数据。但这并没有声明WORD从 C++ 角度来看类型。要解决此问题,请添加 typedefWORD and DWORD在这个%inline中的声明aviwriter.i强制 SWIG 将该代码直接复制到包装器 C++ 文件中,从而使声明可用。这也会触发生成 get/set 包装器。或者,如果您愿意编辑它,也可以将内联代码包含在 aviwriter.h 中。

简而言之,这里的想法是将所有类型完全封装到独立的标头或声明块中。请记住,.i 和 .h 文件具有单独的功能(包装器和数据转换,与被包装的功能)。同样,请注意如何aviwriter.h被包含两次aviwriter.i,一次触发生成 Python 所需的包装器,一次在生成的包装器代码中声明 C++ 所需的类型。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用自定义视频编写器库编写音频的错误 的相关文章

随机推荐

  • 无法通过JAVA连接到HIVE2

    Referring to Hive2 https cwiki apache org confluence display Hive HiveServer2 Clients HiveServer2Clients JDBC created a
  • iPhone / iOS Facebook SDK - 您可以在应用程序内登录并保留登录凭据吗?

    是否可以使用 Facebook iOS SDK 在应用程序内进行身份验证 而不是转到 Safari 并保留这些身份验证凭据以供下次启动应用程序时使用 当我尝试在模拟器中使用演示应用程序时 它总是转到 safari 进行身份验证 这看起来有点
  • WPF UIElement.IsHitTestVisible=false;还在回击吗?

    我从 FrameworkElement 派生一个控件以用作 VisualCollection 的容器 因为我正在使用 DrawingVisuals 进行大量自定义渲染 创建游戏地图 我的容器有几个不同的实例 彼此层叠 我只想命中测试影响当前
  • 在什么条件下单位是一种类型?

    在此被标记为重复之前 我知道这个问题与使用单位作为类型参数时有关编译错误的各种问题有关 一些例子 Why is unit用作通用接口参数时 F 类型系统会以不同方式对待吗 https stackoverflow com q 26296401
  • 自动解码 TRESTResponse 中的 GZIP?

    似乎不可能为 TRESTClient 分配压缩器或拦截 如果我将 TRESTRequest AcceptEncoding 设置为 gzip deflate 我会收到来自支持 gzip 的服务器的 gzip 编码响应 然而 在 TIdHTTP
  • 是否有办法将 javascript 代码注入到 iframe 中执行,而无需删除并重新附加包含它的脚本标记?

    Context 我正在构建一个实时 HTML CSS 和 Javascript 编辑器 可以访问到here http experiments muditameta com qckmeddler 源码可以访问here https github
  • erlang nif 共享库上未定义的符号

    我在尝试将我的共享库 erlang nif 链接到另一个共享库 libpurple 时遇到麻烦 该共享库使用 dlopen 加载其他共享库 插件 问题是mylib so链接到libpurple so libpurple so使用dlopen
  • 使用 Jquery 删除逗号

    我需要一些从字符串中删除逗号的代码 我目前在 PHP 的 number format 中有各种数字 我使用 Jquery 将某些内容发布到更新页面 并且我需要从类中删除逗号 例如 这是一些代码 span class money 1 234
  • 如何默认显示连接线?

    你好 我刚刚开始使用这个 jquery 树 并想知道是否有任何属性可以设置以使连接线始终可见 选项中没有可用的属性将连接线设置为始终可见 但您可以使用以下命令向对象添加一个类fancytree container打开连接器的类 如果您希望连
  • 在 jQuery 中一起使用 :visible 和 :first-child

    我试图在 jQuery 中同时使用 visible 和 first child 伪选择器 但似乎没有成功 我有以下 HTML div a class action style display none Item One a a class
  • 如何在 Hadoop 中访问和操作 pdf 文件的数据?

    我想使用hadoop读取PDF文件 这怎么可能 我只知道hadoop只能处理txt文件 那么有没有办法将PDF文件解析为txt 给我一些建议 一个简单的方法是创建一个序列文件 http hadoop apache org common do
  • Android 媒体播放器停止后无法播放

    我有 5 首歌曲的音乐播放列表 我只希望只要我在应用程序中 播放和停止按钮就可以工作 当我想要的时候我可以停止音乐并开始另一个 现在这是如何工作的 音乐在 播放 按钮上播放 当我单击 停止 按钮时 它停止 但后来我想播放其他歌曲 或再次播放
  • 由于构建后步骤,未加载本机 dll 的符号 (pdb)

    我有一个用符号构建的本机发行版 dll 有一个构建后步骤会修改 dll 构建后步骤会进行一些压缩 并可能附加一些数据 pdb 文件仍然有效 但是 WinDbg 和 Visual Studio 2008 在构建后步骤之后都不会加载 dll 的
  • PHP 中的类发生了什么?

    如果我有这段代码 则会回显字符串 test 这是 PHP 5 3 中的内容 这是一些不应该依赖的疏忽 还是在 PHP 中实现多重继承的某种方式 class Test1 function getName return this gt name
  • 反应改变点击时列表项的类别

    我有一个像这样的反应元素 import React PropTypes Component from react class AlbumList extends Component constructor props super props
  • 为什么delete可以对const指针执行,而free却不能?

    我刚刚注意到指针传递给delete can be const合格而通过的人free不能 这对我来说确实是一个惊喜 在 C 中重载为operator delete应该有一个像这样的签名 void operator delete void p
  • 联合类型和额外属性

    当使用可以同时是联合类型情况的参数调用函数时 有没有办法让 TypeScript 编译器产生错误 例子 interface Name name string interface Email email string type NameOrE
  • Sails.js:如何使用水线连接多个模型?

    我有 3 个模型 大陆 国家和城市 我想加入这些模型以获得结果 大陆 js attributes continent Id type string primaryKey true continent Name type string des
  • 对象 Switch 语句的高性能 Objective C 替代方案

    我有一个函数 我想接受一个 NSString 和一个 int 参数 然后使用 switch 语句返回一个计算值 就像将 int 乘以某个常量一样 具体取决于提供的 NSString 内容 显然 switch 语句不适用于 Objective
  • 使用自定义视频编写器库编写音频的错误

    我正在尝试包装一小段方便的 C 代码 旨在使用 VFW 在 Windows 上生成视频 音频 C 库存在here http www farbrausch de 7Efg code aviwriter 描述说 使用 Windows 视频 因此