概述:基于目前网上通过pipe调用ffmpeg的资料较少,特把自己研究的成果共享给大家。
需求:从相机拿图算法识别后,图片加入了算法效果图,又需要把图片重新推流成rtmp在前端播放。
实现手段:通过c#调用ffmpeg控制台传参,通过管道pipe无缝传图给ffmpeg,最后ffmpeg推流给nginx rtmp服务器。
说明:延时大改在5秒左右。
弊端:目前直播的画面不忍直视,写博客的目的只是说可以借鉴实现方式。
最终ffmpeg打印贴图
ffmpeg version N-99925-g96443ab4b0 Copyright (c) 2000-2020 the FFmpeg developers
built with gcc 9.3-win32 (GCC) 20200320
configuration: --prefix=/ffbuild/prefix --pkg-config-flags=--static --pkg-config=pkg-config --cross-prefix=x86_64-w64-mingw32- --arch=x86_64 --target-os=mingw32 --enable-gpl --enable-version3 --disable-debug --enable-shared --disable-static --disable-debug --disable-w32threads --enable-pthreads --enable-iconv --enable-zlib --enable-libxml2 --enable-libfreetype --enable-libfribidi --enable-gmp --enable-lzma --enable-fontconfig --enable-opencl --enable-libvmaf --enable-vulkan --enable-libvorbis --enable-amf --enable-libaom --enable-avisynth --enable-libdav1d --enable-libdavs2 --enable-ffnvcodec --enable-cuda-llvm --enable-libglslang --enable-libass --enable-libbluray --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvpx --enable-libwebp --enable-libmfx --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librav1e --enable-librubberband --enable-schannel --enable-sdl2 --enable-libsoxr --enable-libsrt --enable-libsvtav1 --enable-libtwolame --enable-libuavs3d --enable-libvidstab --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libzimg --extra-cflags=-DLIBTWOLAME_STATIC --extra-cxxflags= --extra-ldflags=-pthread --extra-libs=-lgomp
libavutil 56. 60.100 / 56. 60.100
libavcodec 58.112.103 / 58.112.103
libavformat 58. 64.100 / 58. 64.100
libavdevice 58. 11.102 / 58. 11.102
libavfilter 7. 90.100 / 7. 90.100
libswscale 5. 8.100 / 5. 8.100
libswresample 3. 8.100 / 3. 8.100
libpostproc 55. 8.100 / 55. 8.100
Loading Complete!
Input #0, rawvideo, from '\\.\pipe\pusher9':
Duration: N/A, start: 0.000000, bitrate: 199065 kb/s
Stream #0:0: Video: rawvideo (BGR[24] / 0x18524742), bgr24, 1280x720, 199065 kb/s, 9 tbr, 9 tbn, 9 tbc
Please use -b:a or -b:v, -b is ambiguous
Stream mapping:
Stream #0:0 -> #0:0 (rawvideo (native) -> h264 (libx264))
Press [q] to stop, [?] for help
[libx264 @ 000001932742b480] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 000001932742b480] profile Constrained Baseline, level 3.1, 4:2:0, 8-bit
[libx264 @ 000001932742b480] 264 - core 161 - H.264/MPEG-4 AVC codec - Copyleft 2003-2020 - http://www.videolan.org/x264.html - options: cabac=0 ref=1 deblock=0:0:0 analyse=0:0 me=dia subme=0 psy=1 psy_rd=1.00:0.00 mixed_ref=0 me_range=16 chroma_me=1 trellis=0 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=0 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=0 weightp=0 keyint=2 keyint_min=1 scenecut=0 intra_refresh=0 rc=abr mbtree=0 bitrate=7000 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=0
Output #0, flv, to 'rtmp://192.168.1.202/live/9':
Metadata:
encoder : Lavf58.64.100
Stream #0:0: Video: h264 (libx264) ([7][0][0][0] / 0x0007), yuv420p(tv, progressive), 1280x720, q=-1--1, 7000 kb/s, 9 fps, 1k tbn, 9 tbc
Metadata:
encoder : Lavc58.112.103 libx264
Side data:
cpb: bitrate max/min/avg: 0/0/7000000 buffer size: 0 vbv_delay: N/A
frame= 8 fps=0.0 q=10.0 size= 570kB time=00:00:00.00 bitrate=4671040.0kbits/s speed=0.00193x
frame= 12 fps= 12 q=21.0 size= 2145kB time=00:00:00.44 bitrate=39494.7kbits/s speed=0.433x
frame= 17 fps= 11 q=10.0 size= 3317kB time=00:00:01.00 bitrate=27146.4kbits/s speed=0.632x
frame= 22 fps= 10 q=21.0 size= 4822kB time=00:00:01.55 bitrate=25368.4kbits/s speed=0.738x
frame= 27 fps= 10 q=13.0 size= 5780kB time=00:00:02.11 bitrate=22417.6kbits/s speed=0.797x
frame= 33 fps= 10 q=14.0 size= 7175kB time=00:00:02.77 bitrate=21151.1kbits/s speed=0.862x
frame= 38 fps= 10 q=23.0 size= 8492kB time=00:00:03.33 bitrate=20866.8kbits/s speed=0.895x
frame= 44 fps= 10 q=24.0 size= 9751kB time=00:00:04.00 bitrate=19964.2kbits/s speed=0.93x
frame= 49 fps= 10 q=17.0 size= 10578kB time=00:00:04.55 bitrate=19016.5kbits/s speed=0.941x
frame= 55 fps= 10 q=17.0 size= 11782kB time=00:00:05.22 bitrate=18480.0kbits/s speed=0.962x
frame= 59 fps=9.9 q=18.0 size= 12519kB time=00:00:05.66 bitrate=18094.3kbits/s speed=0.956x
frame= 64 fps=9.8 q=25.0 size= 13580kB time=00:00:06.22 bitrate=17877.0kbits/s speed=0.958x
frame= 69 fps=9.8 q=19.0 size= 14298kB time=00:00:06.77 bitrate=17278.3kbits/s speed=0.961x
frame= 75 fps=9.8 q=20.0 size= 15295kB time=00:00:07.44 bitrate=16829.5kbits/s speed=0.977x
frame= 80 fps=9.8 q=26.0 size= 16257kB time=00:00:08.00 bitrate=16645.3kbits/s speed=0.984x
frame= 86 fps=9.9 q=26.0 size= 17186kB time=00:00:08.66 bitrate=16242.3kbits/s speed=0.995x
frame= 91 fps=9.8 q=21.0 size= 17817kB time=00:00:09.22 bitrate=15825.6kbits/s speed=0.998x
frame= 97 fps=9.9 q=21.0 size= 18711kB time=00:00:09.89 bitrate=15498.9kbits/s speed= 1x
frame= 102 fps=9.8 q=27.0 size= 19578kB time=00:00:10.44 bitrate=15355.3kbits/s speed= 1x
frame= 106 fps=9.7 q=27.0 size= 20170kB time=00:00:10.89 bitrate=15172.7kbits/s speed=0.997x
frame= 111 fps=9.7 q=22.0 size= 20723kB time=00:00:11.44 bitrate=14833.2kbits/s speed= 1x
frame= 116 fps=9.7 q=27.0 size= 21527kB time=00:00:12.00 bitrate=14694.4kbits/s speed=1.01x
frame= 121 fps=9.7 q=22.0 size= 22076kB time=00:00:12.55 bitrate=14401.8kbits/s speed=1.01x
frame= 126 fps=9.7 q=27.0 size= 22881kB time=00:00:13.11 bitrate=14295.3kbits/s speed=1.01x
frame= 131 fps=9.7 q=23.0 size= 23390kB time=00:00:13.66 bitrate=14019.2kbits/s speed=1.01x
frame= 137 fps=9.7 q=23.0 size= 24148kB time=00:00:14.33 bitrate=13800.9kbits/s speed=1.01x
frame= 143 fps=9.7 q=23.0 size= 24907kB time=00:00:15.00 bitrate=13601.9kbits/s speed=1.02x
frame= 148 fps=9.7 q=28.0 size= 25658kB time=00:00:15.55 bitrate=13510.8kbits/s speed=1.02x
frame= 153 fps=9.7 q=24.0 size= 26169kB time=00:00:16.11 bitrate=13305.3kbits/s speed=1.02x
frame= 158 fps=9.7 q=28.0 size= 26851kB time=00:00:16.66 bitrate=13196.7kbits/s speed=1.02x
frame= 163 fps=9.7 q=24.0 size= 27322kB time=00:00:17.22 bitrate=12995.4kbits/s speed=1.02x
frame= 168 fps=9.7 q=28.0 size= 28005kB time=00:00:17.77 bitrate=12903.6kbits/s speed=1.02x
frame= 174 fps=9.7 q=28.0 size= 28694kB time=00:00:18.44 bitrate=12744.1kbits/s speed=1.03x
frame= 180 fps=9.7 q=28.0 size= 29385kB time=00:00:19.11 bitrate=12595.2kbits/s speed=1.03x
frame= 186 fps=9.7 q=28.0 size= 30076kB time=00:00:19.77 bitrate=12456.7kbits/s speed=1.04x
frame= 190 fps=9.7 q=28.0 size= 30536kB time=00:00:20.22 bitrate=12369.8kbits/s speed=1.03x
。。。。。。。
上代码:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace IPClient.Core
{
public class RtmpPusher
{
private int height = 720;
private int width = 1280;
private int _camId;
private readonly string _ffmpeg_path;
private readonly string _rtmp_url;
private const int BUFFERSIZE = 1280 * 720 * 3;
private ConcurrentQueue<byte[]> _bgr24Buffer = new ConcurrentQueue<byte[]>();
public RtmpPusher(int camId)
{
this._camId = camId;
this._ffmpeg_path = @"D:\quickhigh\ffmpeg\bin\ffmpeg.exe";
this._rtmp_url = string.Format("rtmp://192.168.1.202/live/{0}", camId);
string pipeName = string.Format("pusher{0}", camId);
Task.Factory.StartNew((p) =>
{
DoWriteWork(p as string);
}, pipeName, TaskCreationOptions.LongRunning);
Task.Factory.StartNew((p) =>
{
DoRunFFMPEG(p as string);
}, pipeName, TaskCreationOptions.LongRunning);
}
public RtmpPusher StartNew(int camId)
{
return new RtmpPusher(camId);
}
//输入BMP图片字节,字节以BM开头
public void AddBuffer(byte[] buffer)
{
if (_bgr24Buffer.Count<6)
{
int dataLen = width * height * 3;
byte[] rgb24_buffer = new byte[width * height * 3];
//54字节是BMP固定信息
Array.Copy(buffer, 54, rgb24_buffer, 0, dataLen);
_bgr24Buffer.Enqueue(rgb24_buffer);
}
}
private void DoWriteWork(string pipeName)
{
byte[] buffer = null;
using (NamedPipeServerStream pipeServer =new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.None, BUFFERSIZE, BUFFERSIZE))
{
pipeServer.WaitForConnection();
using (StreamWriter sw = new StreamWriter(pipeServer))
{
sw.AutoFlush = true;
do
{
if (_bgr24Buffer.TryDequeue(out buffer))
{
char[] cBuffer = Encoding.ASCII.GetChars(buffer);
cBuffer = VerticalResverImage(cBuffer);
sw.Write(cBuffer, 0, cBuffer.Length);
}
Thread.Sleep(1);
}
while (pipeServer.IsConnected);
}
}
}
//垂直翻转图像
private char[] VerticalResverImage(char[] bytes)
{
char[] newBytes = new char[bytes.Length];
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
newBytes[((height - j - 1) * width + i) * 3] = bytes[(j * width + i) * 3];
newBytes[((height - j - 1) * width + i) * 3 + 1]= bytes[(j * width + i) * 3 + 1];
newBytes[((height - j - 1) * width + i) * 3 + 2] = bytes[(j * width + i) * 3 + 2];
}
}
return newBytes;
}
private void DoRunFFMPEG(string pipeName)
{
string args = string.Format(@"-y -f rawvideo -vcodec rawvideo -pix_fmt bgr24 -s 1280x720 -r 9 -i \\.\pipe\{0} -c:v libx264 -pix_fmt yuv420p -preset ultrafast -max_delay 100 -f flv -g 2 -b 7000000 ""{1}""", pipeName, _rtmp_url);
RunFFMPEG(args);
}
private void RunFFMPEG(string args)
{
Process p = new Process();
p.StartInfo.FileName = _ffmpeg_path;
p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;//不创建进程窗口
p.ErrorDataReceived += new DataReceivedEventHandler(ffmpeg_Output);
p.Start();
p.BeginErrorReadLine();
p.WaitForExit();
p.Close();
p.Dispose();
}
private void ffmpeg_Output(object sender, DataReceivedEventArgs e)
{
Trace.WriteLine(e.Data);
}
}
}
效果图(花屏现象网上有人说需要重新编译ffmpeg的udp.c文件改大10倍包大小,待研究)。
附上一篇国外的链接:
https://mathewsachin.github.io/blog/2017/07/28/ffmpeg-pipe-csharp.html