音频计量与LibVLC

Audio Metering with LibVLC

本文关键字:LibVLC 音频      更新时间:2023-10-16

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

我想监视传入的音频样本,以生成传递到应用程序的另一部分的计量数据。我有这种类型的libvlc_audio_set_callbacks工作,除了a:我正在监控输出音频,所以它受到静音等的影响,b:当我启用此功能时,无法听到音频(不可否认,这是文档中描述的行为)。

是否有一种方法实际做到这一点与LibVLC API,或者我需要写一个音频过滤器插件?

或者,是否有一些现有的通用"音频嗅探"插件,我可以使用?

现在回复有点晚了,但这是一个我很难解决的问题,其他人可能也在同样的地方(即使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。在duplicate中,有两个目标。

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

在我的例子中,我记录到文件(而不是显示到视频窗口),所以你会想用你自己的第一个目的地替换。第二个目的地是我们使用smem将音频(和/或视频)发送到回调方法的地方:

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

注意smem中的音频,它必须在PCM中(或从文档中看来),因此我们将其转码为带符号的16位小端序(s16l)(这是末尾的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");

构造输出链时的另一个警告是注意引号和括号。可能会有一些指导方针,但对我来说,这是一个尝试和错误。

最后,在smem中使用time-sync选项时,我从未注意到合理的差异。我确信它的设置很重要,但我无法确定如何设置。

我在使用libVLC时发现它并不是为这种用途而设计的。它可以扭曲成可用的形状,但它永远不会像它应该的那样表现得很好。它是开源的,所以喜欢冒险的人可以通过修改源代码来制定一个更有效的解决方案。