opencv ffmpeg推流

基于opencv采集推流
1.opencv采集rtsp解码 //可以基于usb 摄像机(调用系统驱动)和rtsp(调用ffmpeg 接口 转yuv加解码) 摄像机
2.ffmpeg缩放转换像素格式
3.ffmpeg编码H264
4.ffmpeg推流rtmp

sws_getCachedContext (像素格式转换 会清理之前的数据)


1.struct SwsContext *context,
2.int srcW,int srcH,enum AVPixelFormat srcFormat,
3.int dstW,int dstH
4.enum AVPixelFormat dstFormat, //AV_PIX_FMT_YUV420P
5.int flags. //SWS_BICUBIC
6.srcFilter,dstFilter,param

SWS_SCALE


1.c 上下文
2.srcSlice (原数据) srcStride(每一行数据长度)
3.srcSliceY srcSliceH(高度)
4.dst dstStride (输出每一行大小)
5.Returns 输出slice的高度

avcodec_find_encoder


1.AVCodecID
-AV_CODEC_ID_H264
2.avcodec_find_encoder_by_name
3.AVCodec 

avcodec_alloc_context3


1.AVCodecContext*
2.avcodec_alloc_context3
3.(const AVCodec *codec);


int avcodec_open2
(
AVCodecContext *avctx,
const AVCodec *codec,
AVDictionary **options);

avcodec_send_frame


1.AVCodecContext *avctx;
2.const AVFrame *frame;
3.avcodec_receive_packet

XRtmp.h

#pragma once
#include 
class AVCodecContext;
class AVPacket;
class XRtmp
{
public:
	//工厂生产方法
	static XRtmp *Get(unsigned char index = 0);
	//初始化封装器的上下文
	virtual bool Init(const char *url) = 0;
	//添加视频或者音频
	virtual bool AddStream(const AVCodecContext *c) = 0;
	
	//打开rtmp网络IO,发送封装头
	virtual bool SendHead() = 0;

	//rtmp 帧推流
	virtual bool SendFrame(AVPacket *pkt) = 0;

	virtual ~XRtmp();
protected:
	XRtmp();
private:

};

XRtmp.cpp

#include "XRtmp.h"
#include 
using namespace std;
extern "C"
{
	#include 
}

class CXRtmp : public XRtmp
{
public:
	void Close()
	{
		if(ic)
		{
			avformat_close_input(&ic);
			vs = NULL;
		}
		vc = NULL;
		url = "";
	}
	bool Init(const char *url)
	{
		///5 封装器和视频流配置
		//a.创建输出封装器上下文 
		int ret = avformat_alloc_output_context2(&ic,0,"flv",url);
		this->url = url;
		if(ret != 0)
		{
			char buf[1024] = {0};
			av_strerror(ret,buf,sizeof(buf) - 1);
			cout<<buf;
			return false;
		}
		return true;
	}

	bool AddStream(const AVCodecContext *c)
	{
		if(!c) return false;
	
		//b.添加视频流
		AVStream *st = avformat_new_stream(ic,NULL);
		if(!st)
		{
			cout<<"avformat_new_stream failed"<<endl;
			return false;
		}
		st->codecpar->codec_tag = 0;

		//从编码器复制参数
		avcodec_parameters_from_context(st->codecpar, c);
		av_dump_format(ic,0,url.c_str(),1);
		if(c->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			vc = c;
			vs = st;
		}
		return true;
	}

	bool SendHead()
	{
		//打开rtmp的网络输出IO
		int ret = avio_open(&ic->pb,url.c_str(),AVIO_FLAG_WRITE);
		if(ret != 0)
		{
			char buf[1024] = {0};
			av_strerror(ret,buf,sizeof(buf) - 1);
			cout<<buf<<endl;
			return false;
		}

		//写入封装头
		ret = avformat_write_header(ic,NULL);
		if(ret != 0)
		{
			char buf[1024] = {0};
			av_strerror(ret,buf,sizeof(buf) - 1);
			cout<<buf<<endl;
			return false;
		}
		return true;
	}

	bool SendFrame(AVPacket *pkt)
	{
		//推流
		pkt->pts = av_rescale_q(pkt->pts,vc->time_base,vs->time_base);
		pkt->dts = av_rescale_q(pkt->dts,vc->time_base,vs->time_base);
		int ret = av_interleaved_write_frame(ic,pkt);
		if(ret == 0)
		{
			cout<<"#"<<flush;
		}
		return true;
	}
private:
	//rtmp flv 封装器
	AVFormatContext *ic = NULL;

	//视频编码器
	const AVCodecContext *vc = NULL;
	
	AVStream *vs = NULL;
	std::string url = "";
};

//工厂生产方法
XRtmp* XRtmp::Get(unsigned char index)
{
	static CXRtmp cxr[255];

	static bool isFirst = true;
	if(isFirst)
	{
		//注册所有的封装器
		av_register_all();

		//注册所有网络协议
		avformat_network_init();
		isFirst = false;
	}
	return &cxr[index];
}

XRtmp::XRtmp()
{

}

XRtmp::~XRtmp()
{

}

XMediaEncode.h

#pragma once

class AVFrame;
class AVPacket;
class AVCodecContext;
//音视频编码接口类
class XMediaEncode
{
public:
	//输入参数
	int inWidth = 1280;
	int inHeight = 720;
	int inPixSize = 3;

	//输出参数
	int outWidth = 1280;
	int outHeight = 720;
	int bitrate = 4000000;//压缩后每秒视频的bit位大小50KB
	int fps = 25;

	//工厂生产方法
	static XMediaEncode *Get(unsigned char index = 0);
	
	//初始化像素格式转换的上下文初始化
	virtual bool InitScale() = 0;
	virtual AVFrame* RGBToYUV(char *rgb) = 0;

	//编码器的初始化
	virtual bool InitVideoCodec() = 0;

	//视频编码
	virtual AVPacket *EncodeVideo(AVFrame *frame) = 0;

	virtual ~XMediaEncode();

	//编码器的上下文
	AVCodecContext *vc = 0;
protected:
	XMediaEncode();

};

XMediaEncode.cpp


#include "XMediaEncode.h"
#include 
using namespace std;

extern "C"
{
	#include 
	#include 
	#include 
}

class CXMediaEncode:public XMediaEncode
{
public:
	void close()
	{
		if(vsc)
		{
			sws_freeContext(vsc);
			vsc = NULL;
		}

		if(yuv)
		{
			av_frame_free(&yuv);
		}

		if(vc)
		{
			avcodec_free_context(&vc);
		}
		vpts = 0;
		av_packet_unref(&packet);
	}

	bool InitVideoCodec()
	{

		//4初始化编码上下文
		//a 找到编码器
		AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
		if(!codec)
		{
			cout<<"Can't find h264 encoder!"<<endl;
			return false;
		}
		//b 创建编码器上下文
		vc = avcodec_alloc_context3(codec);
		if(!vc)
		{
			cout<<"avcodec_alloc_context3 failed!"<<endl;
		}
		//c 配置编码器参数
		vc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; //全局的参数
		vc->codec_id = codec->id;
		vc->thread_count = 8;

		vc->bit_rate = 50*1024*8; //压缩后每秒视频的bit位大小 50kb
		vc->width = outWidth;
		vc->height = outHeight;
		vc->time_base = {1,fps}; //时间基数
		vc->framerate = {fps,1};

		//画面组的大小,多少帧一个关键帧
		vc->gop_size = 50;
		vc->max_b_frames = 0;
		vc->pix_fmt = AV_PIX_FMT_YUV420P;

		//d 打开编码器
		int ret = avcodec_open2(vc,0,0);
		if(ret!=0)
		{
			char buf[1024] = {0};
			av_strerror(ret,buf,sizeof(buf) - 1);
			cout<<buf<<endl;
			return false;
		}
		cout<<"avcodec_open2 success!"<<endl;
		return true;
	}

	AVPacket *EncodeVideo(AVFrame* frame)
	{
		av_packet_unref(&packet);

		//h264编码	
		frame->pts = vpts;
		vpts++;
		int ret = avcodec_send_frame(vc,frame);
		if(ret!=0)
			return NULL;
		//每次都会调用av_frame_unref(frame)
		ret = avcodec_receive_packet(vc,&packet);
		if(ret != 0 || packet.size <= 0)
			return NULL;
		return &packet;
	}

	bool InitScale()
	{
		//2.初始化格式转换的上下文
		vsc = sws_getCachedContext(vsc,
			inWidth,inHeight,AV_PIX_FMT_BGR24,//原宽度高度
			outWidth,outHeight,AV_PIX_FMT_YUV420P,//输出宽,高,像素格式
			SWS_BICUBIC,//尺寸变化算法
			0,0,0
			);
		if(!vsc)
		{
			cout<<"sws_getCachedContext failed!";
			return false;
		}
		//3.输出的数据结构
		yuv = av_frame_alloc();
		yuv->format = AV_PIX_FMT_YUV420P;
		yuv->width = inWidth;
		yuv->height = inHeight;
		yuv->pts = 0;
		//分配yuv空间
		int ret = av_frame_get_buffer(yuv,32);
		if(ret != 0)
		{
			char buf[1024] = {0};
			av_strerror(ret, buf,sizeof(buf) - 1);
			throw logic_error(buf);
		}
		return true;
	}

	AVFrame* RGBToYUV(char *rgb)
	{
		//rgb to yuv
		//输入的数据格式
		uint8_t *indata[AV_NUM_DATA_POINTERS] = {0};
		//bgrbgrbgr
		//plane inData[0]bbbb gggg rrrr
		indata[0] = (uint8_t*)rgb;
		int insize[AV_NUM_DATA_POINTERS] = {0};
		//一行(宽)数据的字节数
		insize[0] = inWidth * inPixSize;

		int h = sws_scale(vsc,indata,insize,0,inHeight, //输入数据
			yuv->data,yuv->linesize);
		if(h<=0)
		{
			return NULL;
		}
		return yuv;
	}


private:
	SwsContext *vsc = NULL; //像素格式上下文
	AVFrame *yuv = NULL;
	AVPacket packet = {0};
	int vpts = 0;
};


XMediaEncode * XMediaEncode::Get(unsigned char index)
{
	static bool isFirst = true;
	if(isFirst)
	{
		//注册所有的编码器
		avcodec_register_all();
		isFirst = false;
	}
	static CXMediaEncode cxm[255];
	return &cxm[index];
}

XMediaEncode::XMediaEncode()
{

}

XMediaEncode::~XMediaEncode()
{

}

main.cpp

#include 
#include 
#include 
#include 		// 1. 更换包含头文件
#include 
#include 
#include 

#include "XMediaEncode.h"
#include "XRtmp.h"

using namespace cv;
using namespace std;

int main(int argc,char *argv)
{
	VideoCapture cam;
	string inUrl = "rtsp://test:[email protected]";
	//nginx-rtmp 直播服务器rtmp推流URL
	char *outUrl = "rtmp://0.0.0.0/live";

	//编码器和像素格式转换
	XMediaEncode *me =  XMediaEncode::Get(0);


	//封装和推流对象
 	XRtmp *xr = XRtmp::Get(0);

	Mat frame;
	namedWindow("video");
		
	int ret = 0;
	try
	{
		//cam.open(inUrl);
		//1.使用opencv打开rtsp相机
		cam.open(0);
		if(!cam.isOpened())
		{
			throw logic_error("cam open failed");
		}
		cout<<inUrl<<"cam open success"<<endl;
		int inWidth = cam.get(CAP_PROP_FRAME_WIDTH);
		int inHeight = cam.get(CAP_PROP_FRAME_HEIGHT);
		int fps = cam.get(CAP_PROP_FPS);

		//2.初始化像素格式转换的上下文初始化
		//3.输出的数据结构
		me->inWidth = inWidth;
		me->inHeight = inHeight;
		me->outWidth = inWidth;
		me->outHeight = inHeight;
		me->InitScale();

	 	if(!me->InitVideoCodec())
	 	{
	 		throw logic_error("InitVideoCodec failed!");
	 	}
		
	 	//初始化封装器的上下文
		xr->Init(outUrl);
		//添加视频或者音频
		xr->AddStream(me->vc); 		
		xr->SendHead();

		for(;;)
		{
			//读取rtsp视频帧,解码视频帧
			if(!cam.grab())
			{
				continue;
			}
			//yuv转为rgb
			
			//输入的数据结构


			if(!cam.retrieve(frame))
			{
				continue;
			}
			
			//rgb to yuv
			me->inPixSize = frame.elemSize();
			AVFrame *yuv = me->RGBToYUV((char*)frame.data);
			if(!yuv) continue;


			AVPacket *pkt = me->EncodeVideo(yuv);
			if(!pkt) continue;

			xr->SendFrame(pkt);
		}		
	}
	catch(exception &ex)
	{
		if(cam.isOpened())
			cam.release();

		cerr<<ex.what()<<endl;
	
	}
	return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.1)
project(opencv_example_project)

find_package(OpenCV REQUIRED)

message(STATUS "OpenCV library status:")
message(STATUS "    config: ${OpenCV_DIR}")
message(STATUS "    version: ${OpenCV_VERSION}")
message(STATUS "    libraries: ${OpenCV_LIBS}")
message(STATUS "    include path: ${OpenCV_INCLUDE_DIRS}")

find_library(AVCODEC_LIBRARY avcodec)
find_library(AVFORMAT_LIBRARY avformat)
find_library(AVUTIL_LIBRARY avutil)
find_library(AVDEVICE_LIBRARY avdevice)

add_executable(opencv_example main.cpp XMediaEncode.cpp XRtmp.cpp)

target_link_libraries(opencv_example PRIVATE 
	${OpenCV_LIBS} 
	pthread 
	swresample 
	m 
	swscale 
	avformat	
	avcodec 
	avutil 
	avfilter 
	avdevice 
	postproc 
	z 
	lzma  
	rt)

你可能感兴趣的:(ffmpeg,学习,opencv,学习,opencv,音视频,人工智能)