gstreamer 编码cv::Mat rgb图片为 h264视频流,并推松到 rtmp服务器
#pragma once
#include <memory>
#include <mutex>
#include <opencv4/opencv2/opencv.hpp>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
namespace Base
{
class FFmpegStreamDecoder
{
public:
int32_t Init(int width,int height);
int32_t DeInit();
int32_t Decode(const uint8_t *data, size_t length,cv::Mat& bgrMat);
private:
std::mutex decode_mutex;
AVCodecContext *pCodecCtx = nullptr;
AVCodec *pCodec = nullptr;
AVCodecParserContext *pCodecParserCtx = nullptr;
SwsContext *pSwsCtx = nullptr;
AVFrame *pFrameYUV = nullptr;
AVFrame *pFrameRGB = nullptr;
uint8_t *rgbBuf = nullptr;
size_t bufSize = 0;
int32_t decode_width;
int32_t decode_hight;
std::mutex image_mutex;
};
}
#include "GstreamerEncoder.h"
#include <thread>
#include <Util/logger.h>
#include <chrono>
#include <core/Configure.hpp>
using namespace toolkit;
namespace Base
{
bool Encoder::Init()
{
isRuning.store(false);
pipeline = gst_pipeline_new("opencv-rtmp-pipeline");
appsrc = gst_element_factory_make("appsrc", "video-src");
videoconvert = gst_element_factory_make("videoconvert", "convert");
nvvidconv = gst_element_factory_make("nvvidconv", "nvconv");
encoder = gst_element_factory_make("nvv4l2h264enc", "encoder");
h264parse = gst_element_factory_make("h264parse", "parser");
flvmux = gst_element_factory_make("flvmux", "mux");
rtmpsink = gst_element_factory_make("rtmpsink", "sink");
if (!pipeline || !appsrc || !videoconvert || !encoder || !flvmux || !rtmpsink || !nvvidconv || !h264parse)
{
ErrorL << "GStreamer 元素创建失败";
return false;
}
std::string rtmpAddress = Configure::getInstance().RtmpAddress();
InfoL << "RTMP 服务器地址: " << rtmpAddress;
if (rtmpAddress.empty())
return false;
int fps = Configure::getInstance().Fps();
InfoL << "帧率: " << fps;
int bitrate = Configure::getInstance().Bitrate();
InfoL << "码率: " << bitrate;
int width = Configure::getInstance().ImageWidth();
int height = Configure::getInstance().ImageHeight();
InfoL << "图片大小W*H: " << width << " " << height;
if (width == 0 || height == 0)
{
return false;
}
// 设置推流地址
g_object_set(G_OBJECT(rtmpsink), "location", rtmpAddress.c_str(), nullptr);
// 设置编码器属性
g_object_set(encoder, "insert-sps-pps", TRUE, "idrinterval", 15, "iframeinterval", 15, "control-rate", 1, "bitrate", bitrate, "preset-level", 1, "profile", 1, "insert-vui", 1, NULL);
// 设置 appsrc 的属性
g_object_set(G_OBJECT(appsrc), "caps", gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "BGR", "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, "framerate", GST_TYPE_FRACTION, fps, 1, nullptr, NULL), NULL);
g_object_set(G_OBJECT(appsrc), "is-live", TRUE, "format", GST_FORMAT_TIME, "do-timestamp", TRUE, NULL);
// 元素加入管道
gst_bin_add_many(GST_BIN(pipeline), appsrc, videoconvert, nvvidconv, encoder, h264parse, flvmux, rtmpsink, nullptr);
// 链接
if (!gst_element_link_many(appsrc, videoconvert, nvvidconv, encoder, h264parse, flvmux, rtmpsink, nullptr))
{
ErrorL << "GStreamer 元素连接失败";
return false;
}
// 启动管道
auto ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE)
{
ErrorL << "Gstreamer 启动管道失败!" << std::endl;
return -1;
}
return true;
}
void Encoder::Start()
{
isRuning.store(true);
std::thread t([&]()
{
InfoL<<"编码 开始!";
while (isRuning.load())
{
cv::Mat frame;
if(Data::getInstance().imageQueue.tryPop(frame)){
// 创建 GstBuffer
int size = frame.total() * frame.elemSize();
GstBuffer *buffer = gst_buffer_new_and_alloc(size);
GstMapInfo map;
gst_buffer_map(buffer, &map, GST_MAP_WRITE);
memcpy(map.data, frame.data, size);
// 发送 buffer
GstFlowReturn ret;
g_signal_emit_by_name(appsrc, "push-buffer", buffer, &ret);
//析构
gst_buffer_unmap(buffer, &map);
gst_buffer_unref(buffer);
if (ret != GST_FLOW_OK) {
ErrorL << "推送失败,GstFlowReturn: " << ret;
break;
}
}
//std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
InfoL<<"编码 停止!"; });
t.detach();
}
void Encoder::DeInit()
{
isRuning.store(false);
std::this_thread::sleep_for(std::chrono::seconds(1));
// 关闭
if (appsrc)
{
GstFlowReturn retflow;
g_signal_emit_by_name(appsrc, "end-of-stream", &retflow);
InfoL << "EOS sended. Writing last several frame...";
g_usleep(4000000); // 等待4s,写数据
if (retflow != GST_FLOW_OK)
{
ErrorL << "We got some error when sending eos!";
}
}
if (pipeline)
{
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
pipeline = nullptr;
}
}
}
开始运行后,默认从一个队列取图。