gpt4 book ai didi

audio - 如何无损连接 ogg vorbis 文件?

转载 作者:行者123 更新时间:2023-12-04 21:14:18 25 4
gpt4 key购买 nike

我正在尝试将多个 ogg vorbis 文件连接成一个。

我知道理论上应该足够了:

cat 1.ogg 2.ogg > combined.ogg

但这有缺点:
  • 并非所有播放器都支持这样创建的文件(gstreamer 不支持)
  • 这样做的玩家不会平滑地连接它们,而是会产生丑陋的瞬间停顿
  • 寻求似乎不可能

  • 我不想降低质量,所以我可以将它们重新编码为像 flac 这样的无损格式,但这会让文件大小爆炸。

    似乎没有工具可以做到这一点。例如,oggCat 将重新编码音频,从而导致质量和 ffmpeg concat demuxer 的轻微损失。不适用于所有输入文件。我开了 this superuser question找到一个工具,但是当我认为没有工具时就写了我自己的工具。

    所以我尝试使用 libogg 和 libvorbis 手动将输入文件中的 ogg 数据包连接到输出文件的 ogg 页面中。假设是,所有 ogg 输入文件都使用完全相同的参数进行编码。

    我想出了以下代码:

    #include <ogg/ogg.h>
    #include <vorbis/codec.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdbool.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <time.h>

    int read_page(int fd, ogg_sync_state *state, ogg_page *page)
    {
    int ret;
    ssize_t bytes;

    while(ogg_sync_pageout(state, page) != 1) {
    char *buffer = ogg_sync_buffer(state, 4096);
    if (buffer == NULL) {
    fprintf(stderr, "ogg_sync_buffer failed\n");
    return -1;
    }
    bytes = read(fd, buffer, 4096);
    if (bytes == 0) {
    return -1;
    }
    ret = ogg_sync_wrote(state, bytes);
    if (ret != 0) {
    fprintf(stderr, "ogg_sync_wrote failed\n");
    return -1;
    }
    }
    return 0;
    }

    int main(int argc, char *argv[])
    {
    int ret;
    ogg_sync_state state;
    ogg_page page;
    int serial;
    ogg_stream_state sstate;
    bool found_bos;
    ogg_packet packet;
    int fd;
    int i;
    vorbis_info info;
    vorbis_comment comment;
    int vorbis_header_read;
    ssize_t bytes;
    ogg_stream_state out_stream;
    ogg_page out_page;

    if (argc < 2) {
    fprintf(stderr, "usage: %s file.ogg\n", argv[0]);
    return 1;
    }

    srand(time(NULL));
    ogg_stream_init(&out_stream, rand());

    // go through all input files
    for (i = 1; i < argc; i++) {
    vorbis_header_read = 0;
    found_bos = false;

    fd = open(argv[i], O_RDONLY);
    if (fd < 0) {
    fprintf(stderr, "cannot open %s\n", argv[1]);
    return 1;
    }

    ret = ogg_sync_init(&state);
    if (ret != 0) {
    fprintf(stderr, "ogg_sync_init failed\n");
    return 1;
    }

    vorbis_info_init(&info);
    vorbis_comment_init(&comment);

    // go through all ogg pages
    while (read_page(fd, &state, &page) == 0) {
    serial = ogg_page_serialno(&page);

    if (ogg_page_bos(&page)) {
    if (found_bos) {
    fprintf(stderr, "cannot handle more than one stream\n");
    return 1;
    }
    ret = ogg_stream_init(&sstate, serial);
    if (ret != 0) {
    fprintf(stderr, "ogg_stream_init failed\n");
    return 1;
    }
    found_bos = true;
    }

    if (!found_bos) {
    fprintf(stderr, "cannot continue without bos\n");
    return 1;
    }

    ret = ogg_stream_pagein(&sstate, &page);
    if (ret != 0) {
    fprintf(stderr, "ogg_stream_pagein failed\n");
    return 1;
    }

    // if this is the last page, then only write it if we are in the
    // last file
    if (ogg_page_eos(&page) && i != argc - 1) {
    continue;
    }

    // go through all (hopefully vorbis) packets
    while((ret = ogg_stream_packetout(&sstate, &packet)) != 0) {
    if (ret != 1) {
    fprintf(stderr, "ogg_stream_packetout failed\n");
    return 1;
    }

    // test if this stream is vorbis
    if (vorbis_header_read == 0) {
    ret = vorbis_synthesis_idheader(&packet);
    if (ret == 0) {
    fprintf(stderr, "stream is not vorbis\n");
    return 1;
    }
    }

    // read exactly three vorbis headers
    if (vorbis_header_read < 3) {
    ret = vorbis_synthesis_headerin(&info, &comment, &packet);
    if (ret != 0) {
    fprintf(stderr, "vorbis_synthesis_headerin failed\n");
    return 1;
    }
    // if this is the first file, copy the header packet to the
    // output
    if (i == 1) {
    ret = ogg_stream_packetin(&out_stream, &packet);
    if (ret != 0) {
    fprintf(stderr, "ogg_stream_packetin failed\n");
    return 1;
    }
    }
    vorbis_header_read++;
    continue;
    }

    // if this is the first file, write a page to the output
    if (vorbis_header_read == 3 && i == 1) {
    while ((ret = ogg_stream_flush(&out_stream, &out_page)) != 0) {
    bytes = write(STDOUT_FILENO, out_page.header, out_page.header_len);
    if (bytes != out_page.header_len) {
    fprintf(stderr, "write failed\n");
    return 1;
    }
    bytes = write(STDOUT_FILENO, out_page.body, out_page.body_len);
    if (bytes != out_page.body_len) {
    fprintf(stderr, "write failed\n");
    return 1;
    }
    }
    vorbis_header_read++;
    }

    ogg_stream_packetin(&out_stream, &packet);
    do {
    ret = ogg_stream_pageout(&out_stream, &out_page);
    if (ret == 0) break;
    bytes = write(STDOUT_FILENO, out_page.header, out_page.header_len);
    if (bytes != out_page.header_len) {
    fprintf(stderr, "write failed\n");
    return 1;
    }
    bytes = write(STDOUT_FILENO, out_page.body, out_page.body_len);
    if (bytes != out_page.body_len) {
    fprintf(stderr, "write failed\n");
    return 1;
    }
    } while (!ogg_page_eos(&out_page));

    }
    }

    vorbis_info_clear(&info);
    vorbis_comment_clear(&comment);

    ret = ogg_sync_clear(&state);
    if (ret != 0) {
    fprintf(stderr, "ogg_sync_clear failed\n");
    return 1;
    }

    ret = ogg_stream_clear(&sstate);
    if (ret != 0) {
    fprintf(stderr, "ogg_stream_clear failed\n");
    return 1;
    }

    close(fd);
    }

    ogg_stream_clear(&out_stream);

    return 0;
    }

    这几乎有效,但在 vorbis 流加入的点插入几乎听不见的咔嗒声。

    如何正确地做到这一点?

    完全可以做到吗?

    最佳答案

    这是一个有趣的... :)

    如果你能承受文件之间几毫秒的静默/倾斜,只需在两个流之间放置几个静默数据包(我必须检查每个数据包中确切位模式的规范,但这应该不难以确定您是否可以访问解码器的源代码)。

    如果您负担不起静音/歪斜,您可能需要重新编码,因为唯一的其他选择是旋转压缩数据以更改波形连接部分的斜率......

    编辑

    另一种选择是在文件连接点对 PCM 数据应用平滑算法。这并不容易做到,但其想法是您希望波形在文件之间“平滑”。这就是我所拥有的......

    编辑 2

    为了清楚起见,假设源文件使用相同的参数,问题的示例代码几乎可以完美运行。它缺少的一件事是一种防止接缝被听到的方法。我建议放入几个无声包可以处理它,但对于那些负担不起的人,可以(作为纯粹的猜测)考虑将接缝周围两个包的地板上的乘数减少 1,以使接缝不太明显。

    关于audio - 如何无损连接 ogg vorbis 文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27980960/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com