使用LibVLC进行音频计量

时间:2016-07-01 09:48:59

标签: c++ winapi audio libvlc

我在Windows C ++应用程序中使用LibVLC来显示视频流。

我想监视传入的音频样本,以生成传递给我的应用程序另一部分的计量数据。我有libvlc_audio_set_callbacks这样的工作,除了a:我正在监听输出音频,所以它受静音等影响而且b:当我启用它时,音频无法听到(诚然,这是文档中描述的行为。

有没有办法用LibVLC API实际执行此操作,还是需要编写音频过滤器插件?

或者,我可以使用一些现有的通用“音频嗅探”插件吗?

1 个答案:

答案 0 :(得分:1)

对于回复来说这有点晚了,但这是一个我难以挣扎的问题,而其他人可能在同一个地方(即使OP已经很久了!)。

关键是使用smem插件。这使您可以在播放时跟踪音频和视频(有一些严重的警告,见下文)。为了同时播放和计量,您需要duplicate流(拆分它)。下面是用于执行此操作的输出字符串:

std::ostringstream outstream;
outstream << ":sout=#duplicate{dst=std{access=file,dst=\"" << filepath << "\"},dst=\"transcode{acodec=s16l}:" << createFrameCallbackString(true) << "\"}";
libvlc_media_add_option(media, outstream.str().c_str());

我们需要创建一个输出链,在该输出链中拆分流,从而调用duplicate。一式两份,有两个目的地。

outstream << ":sout=#duplicate{dst=...,dst=...}";

在我的示例中,我录制到文件(而不是显示到视频窗口),因此您希望用自己的目标替换第一个目的地。 第二个目的地是我们使用smem将音频(和/或视频)发送到回调方法的地方:

"..., dst=\"transcode{acodec=s16l}:" << createFrameCallbackString(true) << "\"}"

注意有关瑕疵的音频,它必须在PCM中(或者在文档中看起来如此),因此我们将其转码为带符号的16位小端(s16l)(最后是L的L) ,而不是1)。 转码的间接费用是多少?在我的测试中它看起来很小,但如果你有性能问题值得关注。

配置smem的输出链如下:

class Player
{
//callback for frame monitoring
static void prerendercb(void *data, unsigned char** buffer, size_t size)
{
    //we must allocate a buffer in order for postrender to be called
    *buffer = (uint8_t *)malloc(size);
}

static void postrendercb(void *data, unsigned char* buffer, int width, int height, int pitch, size_t size, int64_t pts)
{
    Player* context = (Player*)data;
    //free the buffer
    free(buffer);
    //notify context of frame for processing
    context->OnFrame(/*pass it whatever information you need*/);
}
static void audio_prerendercb(void* data, unsigned char** buffer, size_t size)
{
    *buffer = (uint8_t *)malloc(size);
}
static void audio_postrendercb(void* data, unsigned char* buffer, unsigned int channels, unsigned int rate, 
        unsigned int nb_samples, unsigned int bits_per_sample, size_t size, int64_t pts)
{
    Player* context = (Player*)data;
    //free the buffer
    free(buffer);
    //notify context
    context->OnAudioFrame(/*whatever data is needed*/);
}

//Get an smem string for the audio/video callbacks
std::string Player::createFrameCallbackString(bool timesync) const
{
    std::ostringstream text;
    text <<
        "smem{" << "video-prerender-callback=" << ((long long int)(intptr_t)(void*)&prerendercb) << "," <<
                       "video-postrender-callback=" << ((long long int)(intptr_t)(void*)&postrendercb) << "," <<
                       "video-data=" << ((long long int)(intptr_t)(void*)this) << "," <<
                       "audio-prerender-callback=" << ((long long int)(intptr_t)(void*)&audio_prerendercb) << "," <<
                       "audio-postrender-callback=" << ((long long int)(intptr_t)(void*)&audio_postrendercb) << "," <<
                       "audio-data=" << ((long long int)(intptr_t)(void*)this) << "," <<
                       "time-sync=" << (timesync ? "1" : "0") <<
                       "}";

    return text.str();
}

}

<强>注意事项
除了代码转换问题,以及您必须分配缓冲区数据的事实,无论您是否关心它,最大的问题是libVLC处理帧异步。因此,您将倾向于在10,15帧被一次性处理的情况下获得突发,然后您暂时不会看到任何内容,然后是另一个突发。这肯定受到高速缓存大小的影响(在libVLC中有几个),但即使将高速缓存大小设置得尽可能低,您仍然会获得至少2或3帧的突发。 设置这些选项有很大帮助:

libvlc_media_add_option(media, ":clock-synchro=0"); //this setting is critical for realtime processing of audio stream!
libvlc_media_add_option(media, ":clock-jitter=0");

构建输出链时的另一个警告是注意引号和括号。可能会在某处发布指南,但对我来说这是一个试验和错误。

最后,在time-sync中使用smem选项时,我从未发现过合理的差异。我确定它的重要性,但我无法确定如何。

我在使用libVLC时发现,它并非真正用于此类用途。它可以被扭曲成一个可用的形状,但它永远不会像它应该的那样完美。它虽然是开源的,但冒险者可以通过修改源代码来提供更有效的解决方案。