使用 LibVLC 进行音频计量
Audio Metering with LibVLC
我在 Windows C++ 应用程序中使用 LibVLC 来显示视频流。
我想监视传入的音频样本以生成传递到我应用程序另一部分的计量数据。我有这种使用 libvlc_audio_set_callbacks
除了 a:我正在监视输出音频,所以它受到静音等的影响和 b:当我启用它时,听不到音频(诚然,这是文档中描述的行为)。
有没有办法使用 LibVLC API 实际执行此操作,或者我是否需要编写一个音频过滤器插件?
或者,是否有一些现有的通用 'audio sniffing' 插件可供我使用?
这个回复有点晚了,但这是一个我难以解决的问题,其他人可能在同一个地方(即使 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=...}";
在我的示例中,我正在录制到文件(而不是显示到视频 window),因此您可能希望将第一个目的地替换为您自己的目的地。
第二个目的地是我们使用 smem
:
将音频(and/or 视频)发送到回调方法的地方
"..., 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 时发现它并不是真正为这种用途而设计的。它可以被扭曲成可用的形状,但它永远不会像它应该的那样表现。它是开源的,所以喜欢冒险的人可以通过修改源代码来做出更有效的解决方案。
我在 Windows C++ 应用程序中使用 LibVLC 来显示视频流。
我想监视传入的音频样本以生成传递到我应用程序另一部分的计量数据。我有这种使用 libvlc_audio_set_callbacks
除了 a:我正在监视输出音频,所以它受到静音等的影响和 b:当我启用它时,听不到音频(诚然,这是文档中描述的行为)。
有没有办法使用 LibVLC API 实际执行此操作,或者我是否需要编写一个音频过滤器插件?
或者,是否有一些现有的通用 'audio sniffing' 插件可供我使用?
这个回复有点晚了,但这是一个我难以解决的问题,其他人可能在同一个地方(即使 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=...}";
在我的示例中,我正在录制到文件(而不是显示到视频 window),因此您可能希望将第一个目的地替换为您自己的目的地。
第二个目的地是我们使用 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 时发现它并不是真正为这种用途而设计的。它可以被扭曲成可用的形状,但它永远不会像它应该的那样表现。它是开源的,所以喜欢冒险的人可以通过修改源代码来做出更有效的解决方案。