我正在尝试从我的 C++ 进程中调用 FFMPEG 以从 IP 摄像机流式传输视频。我使用的 FFMPEG 命令是ffmpeg.exe -rtsp_transport tcp -i rtsp://10.0.1.21/ONVIF/MediaInput?profile=1_def_profile4 -f image2pipe -pix_fmt rgb24 -vcodec rawvideo -r 15 -
. 我已经在命令提示符下验证了这个命令,它确实启动了一个视频流并将帧转储到stdout
. 我还在 Python 中编写了类似的代码,并且可以正常工作。
这是我用来使用 C++ 中上一段中的参数调用 FFMPEG 并从stdout
.
bool build_ffmpeg_arguments(const std::string &uri, std::vector<std::string> &args)
{
args.push_back("-rtsp_transport");
args.push_back("tcp");
args.push_back("-i");
args.push_back(uri);
args.push_back("-f");
args.push_back("image2pipe");
args.push_back("-pix_fmt");
args.push_back("rgb24");
args.push_back("-vcodec");
args.push_back("rawvideo");
args.push_back("-r");
args.push_back("15");
args.push_back("-");
return true;
}
boost::process::child start_ffmpeg(const std::string &uri,
const std::string &ffmpeg_path = "c:\\Tools\\ffmpeg.exe")
{
std::vector<std::string> args;
build_ffmpeg_arguments(uri, args);
boost::process::context ctx;
ctx.stdout_behavior = boost::process::capture_stream();
ctx.stderr_behavior = boost::process::capture_stream();
return boost::process::launch(ffmpeg_path, args, ctx);
}
bool read_frame(boost::process::pistream &is, int frame_size, std::vector<char> &frame_bytes)
{
char *buffer = new char[frame_size];
frame_bytes.clear();
is.read(buffer, frame_size);
int bytes_read = is.gcount();
frame_bytes.assign(buffer, buffer + bytes_read);
// std::cout << "Is Bad: " << is.bad() << std::endl;
// std::cout << "Is EOF: " << is.eof() << std::endl;
// std::cout << "gcount: " << bytes_read << std::endl;
delete[] buffer;
if(is.bad() || is.eof() || bytes_read < frame_size)
{
//We read in gunk, skip this time.
is.clear();
return false;
}
else
{
return true;
}
}
//This is where the code is invoked.
BOOST_AUTO_TEST_CASE(test_ffmpeg_stream)
{
std::string uri = "rtsp://10.0.1.21/ONVIF/MediaInput?profile=1_def_profile4";
int width = 320;
int height = 240;
int bpp = 3;
int bytes_expected = width * height * 3;
boost::process::child c = start_ffmpeg(uri);
boost::process::pistream &is = c.get_stdout();
boost::process::pistream &err = c.get_stderr();
std::vector<char> buffer;
bool result = read_frame(is, bytes_expected, buffer);
//BOOST_CHECK_EQUAL(true, result);
std::cout << "Buffer size: " << buffer.size() << std::endl;
std::string line;
while (std::getline(err, line))
std::cout << line << std::endl;
}
的输出stderr
表明参数可能传递错误。
ffmpeg version 2.8.3 Copyright (c) 2000-2015 the FFmpeg developers
built with gcc 5.2.0 (GCC)
configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-av
isynth --enable-bzlib --enable-fontconfig --enable-frei0r --enable-gnutls --enab
le-iconv --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --
enable-libdcadec --enable-libfreetype --enable-libgme --enable-libgsm --enable-l
ibilbc --enable-libmodplug --enable-libmp3lame --enable-libopencore-amrnb --enab
le-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-librtmp --en
able-libschroedinger --enable-libsoxr --enable-libspeex --enable-libtheora --ena
ble-libtwolame --enable-libvidstab --enable-libvo-aacenc --enable-libvo-amrwbenc
--enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enabl
e-libx264 --enable-libx265 --enable-libxavs --enable-libxvid --enable-lzma --ena
ble-decklink --enable-zlib
libavutil 54. 31.100 / 54. 31.100
libavcodec 56. 60.100 / 56. 60.100
libavformat 56. 40.101 / 56. 40.101
libavdevice 56. 4.100 / 56. 4.100
libavfilter 5. 40.101 / 5. 40.101
libswscale 3. 1.101 / 3. 1.101
libswresample 1. 2.101 / 1. 2.101
libpostproc 53. 3.100 / 53. 3.100
rtsp://10.0.1.21/ONVIF/MediaInput?profile=1_def_profile4: Unknown error
有没有办法显示带有boost::process::launch
正在调用的参数的完整命令行?有什么明显的我做错了boost::process
吗?
更新:
怀疑它可能是错误传递的命令行参数,我创建了一个虚拟可执行文件,打印出它接收到的命令行参数。它是 pure 的直接替代品,ffmpeg.exe
因此我可以看到正在传递哪些命令行。我得到的命令行是-rtsp_transport tcp -i rtsp://10.0.1.21/ONVIF/MediaInput?profile=1_def_profile4 -f image2pipe -pix_fmt rgb24 -vcodec rawvideo -r 15 -
. 使用该命令行手动调用 ffmpeg 可以按预期工作。然而不知何故,当通过boost::process
.
** 解决方案 **
看起来我需要设置上下文的环境字段。不知道为什么这可以解决问题,但确实可以。
boost::process::context ctx;
ctx.environment = boost::process::self::get_environment();
ctx.stdout_behavior = boost::process::capture_stream();
ctx.stderr_behavior = boost::process::capture_stream();