我尝试使用libVLC库通过IP接口从Sony FCB-EV7520相机读取RTSP流,并将数据转换为OpenCV使用的格式,即Mat类型。我已经试图在几天内找到一个很好的例子,但到目前为止我找到的唯一结果是this和this。我按照第一个示例中的代码,使其适应RTSP用例,但我还没有检索Mat中的任何数据。但是,从终端输出我似乎实现了与相机的连接。你看到代码中有任何明显的缺陷吗?有没有其他(更容易)的方法来实现我的目标?还可以使用其他任何库吗?任何帮助,将不胜感激!我运行的代码是:
#include <stdio.h>
#include <vlc/vlc.h>
#include <stdint.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
struct VideoDataStruct {
int param;
};
int done = 0;
libvlc_media_player_t *mp;
unsigned int videoBufferSize = 0;
uint8_t *videoBuffer = 0;
void cbVideoPrerender(void *p_video_data, uint8_t **pp_pixel_buffer, int size) {
// Locking
// Locking a mutex here once I get the code to work.
if (size > videoBufferSize || !videoBuffer){
printf("Reallocate raw video buffer\n");
free(videoBuffer);
videoBuffer = (uint8_t *) malloc(size);
videoBufferSize = size;
}
*pp_pixel_buffer = videoBuffer;
}
void cbVideoPostrender(void *p_video_data, uint8_t *p_pixel_buffer, int width, int height, int pixel_pitch, int size, int64_t pts) {
// Unlocking
// Unlocking the mutex here once I get the code to work.
}
static void handleEvent(const libvlc_event_t* pEvt, void* pUserData) {
libvlc_time_t time;
switch(pEvt->type){
case libvlc_MediaPlayerTimeChanged:
time = libvlc_media_player_get_time(mp);
printf("MediaPlayerTimeChanged %lld ms\n", (long long)time);
break;
case libvlc_MediaPlayerEndReached:
printf ("MediaPlayerEndReached\n");
done = 1;
break;
default:
printf("%s\n", libvlc_event_type_name(pEvt->type));
}
}
int main(int argc, char* argv[]) {
// VLC pointers:
libvlc_instance_t *inst;
libvlc_media_t *m;
void *pUserData = 0;
VideoDataStruct dataStruct;
// VLC options:
char smem_options[1000];
// RV24:
sprintf(smem_options
, "#transcode{vcodec=h264}:smem{"
"video-prerender-callback=%lld,"
"video-postrender-callback=%lld,"
"video-data=%lld,"
"no-time-sync},"
, (long long int)(intptr_t)(void*)&cbVideoPrerender
, (long long int)(intptr_t)(void*)&cbVideoPostrender
, (long long int)(intptr_t)(void*)&dataStruct
);
const char * const vlc_args[] = {
"-I", "dummy", // Don't use any interface
"--ignore-config", // Don't use VLC's config
"--extraintf=logger", // Log anything
"--verbose=2", // Be verbose
"--sout", smem_options // Stream to memory
};
// We launch VLC
inst = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
/* Create a new item */
m = libvlc_media_new_location(inst, "rtsp://*****:*******@IP/videoinput_1/h264_1/media.stm");
/* Create a media player playing environement */
mp = libvlc_media_player_new_from_media (m);
libvlc_event_manager_t* eventManager = libvlc_media_player_event_manager(mp);
libvlc_event_attach(eventManager, libvlc_MediaPlayerTimeChanged, handleEvent, pUserData);
libvlc_event_attach(eventManager, libvlc_MediaPlayerEndReached, handleEvent, pUserData);
libvlc_event_attach(eventManager, libvlc_MediaPlayerPositionChanged, handleEvent, pUserData);
libvlc_video_set_format(mp, "h264", 1920, 1080, 1920* 3 );
/* play the media_player */
libvlc_media_player_play (mp);
while(1){
if(videoBuffer){ // Check for invalid input
Mat img = Mat(Size(1920, 1080), CV_8UC3, videoBuffer); // CV_8UC3 = 8 bits, 3 chanels
namedWindow("Display window", WINDOW_AUTOSIZE); // Create a window for display.
imshow("Display window", img); // Show our image inside it.
}
}
libvlc_release (inst);
}
我使用OpenCV 3.2和libVLC 2.2.5.1在Ubuntu 16.04上运行代码。如果您需要任何其他信息,请询问。
PS:我知道流正在工作,因为我可以通过VLC播放器的流媒体界面打开它。我也知道libVLC可以解码流,因为我已成功打开流的录制的mp4。
答案 0 :(得分:0)
不是一个完整的答案,但评论的时间太长了:
在cbVideoPrerender
中,videoBuffer
总是按照vlc的需要进行分配
但是,在main
中,您拥有循环while(1){ if (videoBuffer) { ... } }
,其中videoBuffer
从第一次调用cbVideoPrerender
时始终为真。这意味着从那时起,循环是无限且无阻塞的,因此视频输入和处理之间没有同步,如果你只想尝试获取第一张图像,那么你就太早了。
您的first link建议使用cbVideoPostrender
作为同步点,以便您知道可以读取帧并将其转换为所需格式。它在函数本身完成,但是您可以使用condition variable(queue,event,...)来处理另一个线程中的帧并将图像传递给openCV。
顺便说一句:通过在一个线程中设置并在没有线程机制(互斥,原子)的情况下在另一个线程中读取它来使用变量可能是一个坏主意。