我在Windows C ++应用程序中使用LibVLC来显示视频流。
我想监视传入的音频样本,以生成传递给我的应用程序另一部分的计量数据。我有libvlc_audio_set_callbacks
这样的工作,除了a:我正在监听输出音频,所以它受静音等影响而且b:当我启用它时,音频无法听到(诚然,这是文档中描述的行为。
有没有办法用LibVLC API实际执行此操作,还是需要编写音频过滤器插件?
或者,我可以使用一些现有的通用“音频嗅探”插件吗?
答案 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时发现,它并非真正用于此类用途。它可以被扭曲成一个可用的形状,但它永远不会像它应该的那样完美。它虽然是开源的,但冒险者可以通过修改源代码来提供更有效的解决方案。