使用gstreamer将海康摄像头输出的YV12格式数据推流到RTMP服务器

代码如下:
Video.h

#pragma once
#include <functional>
#include "VideoRecoder.h"
#include "ISMSSDK.h"
#include <Windows.h>
#include <future>
#include <queue>

class Video
{
public:
	using Handler = std::function<void(const uint8_t*, size_t, const int width, const int height)>;
	Video();
	~Video();
	void Start(const uint16_t width, const uint16_t height, uint16_t fps, void* handle);
	void Stop();
	void StartServerStream();
	void StopServerStream();
	static void _stdcall OnDecodeced(long lPlayHandle, const char* pDataArray, int iDataLen, int iWidth, int iHeight,
		int iFrameType, int iTimeStamp, void* pUserData);
	void OnImageArive(const char* data, size_t size, int iFrameType, const int width, const int height);

private:
	using VideoRecoderPtr = std::unique_ptr<VideoRecoder>;
	VideoRecoderPtr _recoder;
	WindowDrawer _windowDrawer;
	long _lPlayHandle;
	Handler _recodeHandler;
	Handler _decodeHandler;
	uint16_t _width;
	uint16_t _height;
	uint16_t _fps;
	void* _form;
};

video.cpp

#include "Video.h"
#include <iostream>
#include <fstream>

const char* g_ip = "xxx";
int port = 80;
const char* userName = "xxx";
const char* password = "xxx";
std::string uuid = "asd";
enStreamType streamType = enStreamType::TYPE_DEFAULT_STREAM;

namespace pl = std::placeholders;

void __stdcall StreamCallBack(long lPlayHnadle, ISMS_STREAM_DATA_TYPE_EN enStreamDataType, const char* pDataArray, int iDataLen, void* pUserData)
{
}

void __stdcall MessageCallBack(long lPlayHandle, int iMsg, void* pUserData)
{
}

Video::Video()
	: _lPlayHandle(-1)
{
	AllocConsole();
	FILE *stream = nullptr;
	freopen_s(&stream, "CONOUT$", "w", stdout);

	if (ISMS_Init() != 0)
	{
		std::cout << "ISMS_init error" << std::endl;
		return;
	}
	else
	{
		std::cout << "ISMS_init success" << std::endl;
	}

	if (0 != ISMS_Login(g_ip, port, userName, password))
	{
		std::cout << "ISMS_Login error" << std::endl;
		return;
	}
	else
	{
		std::cout << "Login success" << std::endl;
	}

	_recoder = std::make_unique<VideoRecoder>();
}

Video::~Video()
{
}

void Video::Start(const uint16_t width, const uint16_t height, uint16_t fps, void* handle)
{
	_windowDrawer.start(handle);
	_width = width;
	_height = height;
	_fps = fps;
	_recoder->setSetting(_width, _height, _fps);
	StartServerStream();
}

void Video::Stop()
{
	_windowDrawer.stop();
	StopServerStream();
}

void Video::StartServerStream() 
{

	if (_lPlayHandle != -1)
	{
		ISMS_StopPreview(_lPlayHandle);
		std::cout << "ISMS_StopPreview" << std::endl;
	}

	if (!uuid.empty())
	{
		_lPlayHandle = ISMS_StartPreviewEx(uuid.c_str(), nullptr, streamType, StreamCallBack, MessageCallBack, OnDecodeced, this);
		std::cout << "ISMS_StartPreviewEx" << std::endl;
		int count = 0;
		while (_lPlayHandle < 0 && count < 5)
		{
			std::cout << "start stream error" << std::endl;
			Sleep(1000);
			_lPlayHandle = _lPlayHandle = ISMS_StartPreviewEx(uuid.c_str(), nullptr, streamType, StreamCallBack, MessageCallBack, OnDecodeced, this);
			count++;
		}
	}
	else
	{
		std::cout << "uuid is empty!!!!!!!!!!!" << std::endl;
	}

}

void Video::StopServerStream()
{
	ISMS_StopPreview(_lPlayHandle);
}

void Video::OnDecodeced(long lPlayHandle, const char* pDataArray, int iDataLen, int iWidth, int iHeight, int iFrameType, int iTimeStamp, void* pUserData)
{
	Video* _this = reinterpret_cast<Video*>(pUserData);
	_this->OnImageArive(
		pDataArray,
		iDataLen, iFrameType, 
		static_cast<uint16_t>(iWidth),
		static_cast<uint16_t>(iHeight) 
	);
}

void Video::OnImageArive(const char* data, size_t size, int iFrameType, const int width, const int height)
{
	_recoder->recode((uint8_t*)data, size, width, height);
}

videoRecoder.h

#pragma once
#include <cstdint>
#include <vector>
#include <thread>
#include <glib.h>
#include <gst/gst.h>
#include <gst/app/gstappsink.h>

class VideoRecoder {
public:
  VideoRecoder();
  ~VideoRecoder();
  void setSetting(const uint16_t width, const uint16_t height, uint16_t fps);
  void recode(const uint8_t* data, size_t size, const uint16_t width, const uint16_t height);
 
private:
  GstElement* _pipeline;
  GstElement* _source;
  GstElement* _queue0;
  GstElement* _convert;
  GstElement* _scale;
  GstElement* _rate;
  GstElement* _capsfilter;
  GstElement* _queue1;
  GstElement* _encode;
  GstElement* _parse;
  GstElement* _queue2;
  GstElement* _flvmu;
  GstElement* _queue3;
  GstElement* _sink;
};


VideoRecoder.cpp

#include "VideoRecoder.h"
#include <gst/app/gstappsrc.h>
#include <gst/video/video.h>
#include "Check.h"
#include <windows.h>
#include <iostream>

VideoRecoder::VideoRecoder() {
  gst_init(nullptr, nullptr);
  _pipeline   = chk(gst_pipeline_new(nullptr));
  _source     = chk(gst_element_factory_make("appsrc",       nullptr));
  _queue0     = chk(gst_element_factory_make("queue",        nullptr));
  _convert    = chk(gst_element_factory_make("videoconvert", nullptr));
  _scale      = chk(gst_element_factory_make("videoscale",   nullptr));
  _rate       = chk(gst_element_factory_make("videorate",    nullptr));
  _capsfilter = chk(gst_element_factory_make("capsfilter",   nullptr));
  _queue1     = chk(gst_element_factory_make("queue",        nullptr));
#ifdef USE_X264
  _encode	  = chk(gst_element_factory_make("x264enc",		 nullptr));
#else
  _encode	  = chk(gst_element_factory_make("openh264enc",	 nullptr));
#endif
  _parse	  = chk(gst_element_factory_make("h264parse",	 nullptr));
  _queue2	  = chk(gst_element_factory_make("queue",		 nullptr));
  _flvmu	  = chk(gst_element_factory_make("flvmux",		 nullptr));
  _sink       = chk(gst_element_factory_make("rtmpsink",      nullptr));

  g_object_set(_source, "is-live", true, "do-timestamp", true, "format", GST_FORMAT_TIME, NULL);
  g_object_set(_queue0, "leaky", 2, NULL);
  g_object_set(_queue1, "leaky", 2, NULL);
  g_object_set(_queue2, "leaky", 2, NULL);
  g_object_set(G_OBJECT(_sink), "location", "rtmp://hn.uniseas.com.cn:1935/hlxlive/stream", NULL);

  gst_bin_add_many(GST_BIN(_pipeline), _source, _queue0, _convert, _scale, _rate, _capsfilter, _queue1, _encode, _parse, _queue2, _flvmu, _sink, NULL);
  gst_element_link_many(_source, _queue0, _convert, _scale, _rate, _capsfilter, _queue1, _encode, _parse, _queue2, _flvmu, _sink, NULL);
  gst_element_set_state(_pipeline, GST_STATE_PLAYING);
}

VideoRecoder::~VideoRecoder() {
  gst_element_set_state(_pipeline, GST_STATE_NULL);
  gst_object_unref(_pipeline);
}

void VideoRecoder::setSetting(const uint16_t width, const uint16_t height, uint16_t fps) {
  GstCaps* caps = gst_caps_new_simple(
    "video/x-raw",
    "width", G_TYPE_INT, width,
    "height", G_TYPE_INT, height,
    "framerate", GST_TYPE_FRACTION, fps, 1,
    NULL);
  g_object_set(_capsfilter, "caps", caps, NULL);
  gst_caps_unref(caps);
}

void VideoRecoder::recode(const uint8_t* data, size_t size, const uint16_t width, const uint16_t height) {
  GstState state = GST_STATE_NULL;
  GstState pending = GST_STATE_NULL;
  gst_element_get_state(_pipeline, &state, &pending, 0);
  if (state == GST_STATE_PLAYING || pending == GST_STATE_PLAYING) {
    GstBuffer* buffer = gst_buffer_new_and_alloc(size);
    GstMapInfo map;
    gst_buffer_map(buffer, &map, GST_MAP_WRITE);
    memcpy(map.data, data, size);
    gst_buffer_unmap(buffer, &map);
    GstVideoInfo videoInfo;
    gst_video_info_set_format(&videoInfo, GST_VIDEO_FORMAT_YV12, width, height);
    GstCaps* caps = gst_video_info_to_caps(&videoInfo);
    GstSample* sample = gst_sample_new(buffer, caps, nullptr, nullptr);
    gst_caps_unref(caps);
    gst_app_src_push_sample(GST_APP_SRC(_source), sample);    
    gst_buffer_unref(buffer);
    gst_sample_unref(sample);
  }
}

启动rtmp服务器nginx-rtmp-module(下载地址:https://download.csdn.net/download/qq_23350817/12680515):
nginx.exe -c conf/nginx-win.conf

打开vlc,输入rtmp地址:rtmp://127.0.0.1:1935/live/1播放

猜你喜欢

转载自blog.csdn.net/qq_23350817/article/details/107796452