c++ - 完全读取命名管道后,防止 FFmpeg 关闭
问题描述
我正在为一个大学项目做一个 C++ 应用程序,该项目需要一个视频文件(例如 matroska)并使用 FFmpeg(在std::system()
指令中嵌入命令)应用这些步骤:
提取帧块(例如每块 10 秒)和 .aac 音频块
对帧应用一些过滤器
使用 x264 编码视频块和音频块,并使用 RTSP 将其发送到侦听客户端。这一步是使用两个命名管道来实现的,一个用于视频,一个用于音频。
将流接收到另一个进程并使用 ffplay(在 localhost 或 lan 上)播放它。
我需要将我的流分成几块,因为我最终需要满足实时约束,所以我不能在我的整个输入视频文件之前应用过滤器,然后才开始流式传输到客户端。我的主要问题是,一旦 FFmpeg 清空了两个管道,它就会关闭;但其他视频和音频块仍需通过管道传输和流式传输。我需要 FFmpeg 来监听等待新数据的管道。
在 bash 中,我使用以下命令实现了这一点。
开始在提示中侦听 RTSP 流:
ffplay -rtsp_flags listen rtsp://127.0.0.1@127.0.0.1:8090
创建一个名为管道的视频和一个名为管道的音频:
mkfifo video_pipe
mkfifo audio_pipe
使用此命令可防止 FFmpeg 在视频管道清空时关闭:
exec 7<>video_pipe
(将其应用于管道视频就足够了,音频管道也不会出现问题)
激活 FFmpeg 命令
ffmpeg -probesize 2147483647 -re -s 1280x720 -pix_fmt rgb24 -i pipe:0 -vsync 0 -i audio_pipe -r 25 -vcodec libx264 -crf 23 -preset ultrafast -f rtsp -rtsp_transport tcp rtsp://127.0.0.1@127.0.0.1:8090 < video_pipe
然后在另一个提示中输入管道:
cat audiochunk.aac > audio_pipe & cat frame*.bmp > video_pipe
这些命令使用 3 个提示运行良好,然后我在 C++ 中尝试将它们嵌入指令中的std::system()
命令(使用不同的线程);一切正常,但是一旦 ffmpeg 命令清空视频管道(完成第一个块),它就会关闭。
exec
命令在这里似乎无效。如何防止 FFmpeg 关闭?
两天在这个问题上苦苦挣扎,查看所有可能的互联网解决方案。尽管很头疼,但我希望我很清楚,提前感谢您的建议。
更新: 我的 C++ 代码是这样的;我将它放在单个线程上的单个函数中,但它不会改变它的行为。我在 Ubuntu 18.04.2
void CameraThread::ffmpegJob()
{
std::string strvideo_length, command, timing;
long video_length, begin_chunk, end_chunk;
int begin_h, begin_m, begin_s, end_h, end_m, end_s;
command = "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 " + Configurations::file_name;
strvideo_length = execCmd(command.c_str());
strvideo_length.pop_back(); // remove \n character
video_length = strToPositiveDigit(strvideo_length);
if(video_length == -1)
{
std::cout << "Invalid input file";
return;
}
std::system("bash -c \"rm mst-temp/mst_video_pipe\"");
std::system("bash -c \"rm mst-temp/mst_audio_pipe\"");
std::system("bash -c \"mkfifo mst-temp/mst_video_pipe\"");
std::system("bash -c \"mkfifo mst-temp/mst_audio_pipe\"");
// Keep video pipe opened
std::system("bash -c \"exec 7<>mst-temp/mst_video_pipe\"");
std::string rtsp_url = "rtsp://" + Configurations::my_own_used_ip + "@" + Configurations::client_ip +
":" + std::to_string(Configurations::port + 1);
command = "ffmpeg -probesize 2147483647 -re -s 1280x720 -pix_fmt rgb24 -i pipe:0 "
"-i mst-temp/mst_audio_pipe -r 25 -vcodec libx264 -crf 23 -preset ultrafast -f rtsp "
"-rtsp_transport tcp " + rtsp_url + " < mst-temp/mst_video_pipe &"; // Using & to continue without block on command
std::system(command.c_str());
begin_chunk = -1 * VIDEO_CHUNK;
end_chunk = 0;
// Extract the complete audio track
command = "bash -c \"ffmpeg -i " + Configurations::file_name + " -vn mst-temp/audio/complete.aac -y\"";
std::system(command.c_str());
while(true)
{
// Define the actual video chunk (in seconds) to use, if EOF is reached, exit
begin_chunk += (end_chunk - begin_chunk);
if(begin_chunk == video_length)
break;
if(end_chunk + VIDEO_CHUNK <= video_length)
end_chunk += VIDEO_CHUNK;
else
end_chunk += (video_length - end_chunk);
// Set begin and end H, M, S variables
begin_h = static_cast<int>(begin_chunk / 3600);
begin_chunk -= (begin_h * 3600);
begin_m = static_cast<int>(begin_chunk / 60);
begin_chunk -= (begin_m * 60);
begin_s = static_cast<int>(begin_chunk);
end_h = static_cast<int>(end_chunk / 3600);
end_chunk -= (end_h * 3600);
end_m = static_cast<int>(end_chunk / 60);
end_chunk -= (end_m * 60);
end_s = static_cast<int>(end_chunk);
// Extract bmp frames and audio from video chunk
// Extract frames
timing = " -ss " + std::to_string(begin_h) + ":" + std::to_string(begin_m) +
":" + std::to_string(begin_s) + " -to " + std::to_string(end_h) +
":" + std::to_string(end_m) + ":" + std::to_string(end_s);
command = "bash -c \"ffmpeg -i " + Configurations::file_name + timing +
" -compression_algo raw -pix_fmt rgb24 mst-temp/frames/output%03d.bmp\"";
std::system(command.c_str());
// Extract audio
command = "bash -c \"ffmpeg -i mst-temp/audio/complete.aac" + timing +
" -vn mst-temp/audio/audiochunk.aac -y\"";
std::system(command.c_str());
// Apply elaborations on audio and frames.........................
// Write modified audio and frames to streaming pipes
command = "bash -c \"cat mst-temp/audio/audiochunk.aac > mst-temp/mst_audio_pipe & "
"cat mst-temp/frames/output*.bmp > mst-temp/mst_video_pipe\"";
std::system(command.c_str());
}
}
解决方案
推荐阅读
- matplotlib - plt.savefig 正在保存旧版本的情节
- javascript - 制作不同的随机数
- r - 在 QQplot 中为模型画线
- java - 如何通过 Log4j2 将日志发送到 Graylog?
- datetime - .Net Core 3.1 Web Api 的自定义 OData 日期时间序列化器
- python - 读取模板 JSON 文件并在 bash 或 Python 中使用替换创建新文件
- c# - WPF C# Mouse Down 事件以编程方式在 void
- r - 在 R 中有效地采样彩票号码
- python - 比较不需要模式匹配的 2 个列表
- html - 我的网站在本地托管时使用 flex 和 bootstrap 进行响应。它现在是在线托管的,flex 不能在移动网站上运行