使用ffmpeg来探测GB28181的ps流

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用ffmpeg来探测GB28181的ps流相关的知识,希望对你有一定的参考价值。

GB28181

GB28181 是我国内的标准,现在到2016修订后比较成熟,有很多可取之处,当然,依然是建立在sip协议之上,比起rtmp协议来说,他的优点是复用了rtp协议和sdp协议,这一点很优秀,也解决了很多问题。

ps流

ps流严格来说是适合传输的文件流。例如海康大华的GB28181 的ps流,事实上,用ffmpeg也是可以探测ps流的,准备好一个类,叫IOThread,从TThread类继承,这个类后面给出,只是为了简单启动一个线程,也不一定要这么写,完全是为了示例。

class IOThread :public TThread

	uint8_t* pIObuffer = NULL;
	AVIOContext* pb = NULL;
	AVInputFormat* piFmt = NULL;
	AVFormatContext* pFormatContext = NULL;
	unsigned int video_stream_index;
	queue<RTPPacket*> _queue;
	void *_RTPSession = NULL;
	std::mutex  _mutex;
	int64_t framebytes = 0;
	class Lock 
	private:
		std::lock_guard<std::mutex> m_lock;
	public:
		inline Lock(IOThread* parent) : m_lock(parent->_mutex) 
	;
public:
	static int fill_iobuffer(void* opaque, uint8_t* buf, int bufSize);
public:


	/*void AddBytes(int len)
	
		framebytes += len;
	*/
	int64_t GetBytes()
	
		return framebytes;
	

	void InitProbe(void *param);
	int  ReadNetPacket(uint8_t *buf, int buf_size);
	void Push(RTPPacket * pkt);
	void Run();
;

具体实现,这只是个技术示例,展示用,实际使用请写好,自行修改!



//因为可能启动多个IOThread 
int IOThread::fill_iobuffer(void* opaque, uint8_t* buf, int bufSize)

	IOThread * IOB = (IOThread*)opaque;

	return  IOB->ReadNetPacket(buf, bufSize);


//初始化探测器,每一路流都有一个探测器
void IOThread::InitProbe(void *param)

	_RTPSession = (MyRTPSession*)param;

void IOThread::Push(RTPPacket * pkt)

	//Lock lock(this);
	if (IsStop())
		return;
	Lock lock(this);
	_queue.push(pkt);


int IOThread::ReadNetPacket(uint8_t *buf, int buf_size)

	//rtp协议优化,一次读取两个到几个包
#define READ_PKT_NUM 3
	int nsize = 0;
	

	for (int i = 0; i < READ_PKT_NUM; i++)
	
		std::this_thread::sleep_for(std::chrono::milliseconds(5));

		Lock lock(this);
		if (_queue.empty())
			break;
		RTPPacket * pkt = _queue.front();
		_queue.pop();
		int plen = pkt->GetPayloadLength();
		
		memcpy(buf+nsize, pkt->GetPayloadData(), plen);
		nsize += plen;
		((MyRTPSession*)(_RTPSession))->DeletePacket(pkt);
	
	return nsize;



void IOThread::Run()

	if (_RTPSession == NULL) 
		printf("session is null\\n");
		return;
	
	//这里需要修正的是:如果一开始接收到的不是关键帧,前面开始过,没有停止
	//等待50毫秒
	while (1)
	
		if (IsStop())
			return;
		if (_queue.size() < READ_PKT_NUM)
		
			std::this_thread::sleep_for(std::chrono::milliseconds(5));
		
		else
			break;
	
	//先
	clock_t start, end,end2;
	start = clock();
	//缓存大小 看定义PKT_NUM
#define BUFFER_IO_LEN READ_PKT_NUM*1500
	pIObuffer = (uint8_t*)av_malloc(BUFFER_IO_LEN);
	pb = avio_alloc_context(
		pIObuffer,
		BUFFER_IO_LEN,
		0,
		this,
		fill_iobuffer,
		NULL,
		NULL);

	if (av_probe_input_buffer(pb, &piFmt, "", NULL, 0, 0) < 0)//探测从内存中获取到的媒体流的格式
	
		printf("Error: probe format failed\\n");
		return;
	

	//printf("input format:%s[%s]\\n", piFmt->name, piFmt->long_name);

	pFormatContext = avformat_alloc_context();
	pFormatContext->pb = pb;
	pFormatContext->flags = AVFMT_FLAG_CUSTOM_IO;
	pFormatContext->max_analyze_duration = 1000000;
	pFormatContext->fps_probe_size = 5;

	//AVInputFormat* pInputFormat = NULL;
	//pInputFormat = av_find_input_format("h264");
	//int ret = avformat_open_input(&pFormatContext, "", pInputFormat, NULL);
	int ret = avformat_open_input(&pFormatContext, "", piFmt, NULL);
	if (ret < 0)
	
		printf("Error: avformat_open_input failed----------------- \\n");
		//ASSERT(0);
		return;
	


	end = clock();
	printf("avformat_open_input() used time: %ld ms \\n", start - end);

	ret = avformat_find_stream_info(pFormatContext, NULL);
	if (ret < 0)
	
		printf("Error: avformat_find_stream_info failed----------------- \\n");
		return;
	
	for (int i = 0; i < pFormatContext->nb_streams; i++)
	
		if (AVMEDIA_TYPE_VIDEO == pFormatContext->streams[video_stream_index]->codecpar->codec_type)
		
			video_stream_index = i;
			//avCodecID = pFormatContext->streams[video_stream_index]->codec->codec_id;
			break;
		
	
	end2 = clock();
	printf("avformat_find_stream_info() used time: %ld ms\\n", end2- start);

#if 1
	//char filename[MAX_PATH];
	//_snprintf(filename, 128, "qianbo_ffmpeg.264");
	FILE * fp = fopen("qianbo_ffmpeg.264", "wb");
#endif
	//fwrite(data, 1, len, fp);
	AVPacket packet;
	while (1)
	
		if (IsStop())
			break;
		std::this_thread::sleep_for(std::chrono::milliseconds(20));
		av_init_packet(&packet);
		if (av_read_frame(pFormatContext, &packet) == 0)  //读成功
			if (packet.stream_index == video_stream_index)
			
				uint8_t * data = packet.data;
				//char buffer[256];
				//sprintf(buffer, "%02x %02x %02x %02x %02x %02x len : %d", data[0], data[1], data[2], data[3], data[4], data[5], packet.size);
				//printf("%s\\n",buffer);
				fwrite(data, 1, packet.size, fp);
			
			av_packet_unref(&packet);
		
	
	av_free(pIObuffer);
#if 1
	fclose(fp);

#endif

thread类

#ifndef _TTHREAD_RUN_ABLE_H_
#define _TTHREAD_RUN_ABLE_H_


#include <mutex>
#include <queue>
#include <thread>
#include <atomic>
#include <condition_variable>
using namespace std;

class TThread

private:

	//线程
	thread _thread;
	//等待信号
	std::mutex _signal_mutex;
	std::condition_variable _cond;
protected:
	volatile char _stop = true;
	//锁定运行状态
	std::mutex _mutex;
public:
	TThread()
	
	virtual ~TThread()
	

public:
	
	void Join()
	
		if (_thread.joinable())
			_thread.join();
	
	bool  IsStop()
	
		return _stop == 1 ? true : false;
	
	void WaitForSignal()
	
		std::unique_lock<std::mutex> ul(_signal_mutex);
		_cond.wait(ul);
	
	void Notify()
	
		_cond.notify_one();
	

	virtual int Start()
	
		if (_stop == 0)
			return -1;
		_stop = 0;
		_thread = std::thread(std::bind(&TThread::Run, this));
		return 0;
		
	
	virtual void Stop()
	
		//先通知,后停止
		Notify();
		_stop = 1; // true;
	

	virtual void Run() = 0;

;
#endif

以上就是使用ffmepg来探测流的过程,不详细说明,实践动手就行,这个方式也是比较好的,因为ffmpeg还是可以帮我们屏蔽一些细节,为了达到效果,还是要认真了解ps流,也需要了解各个不同厂家的ps流的详细不同情况。

感谢您看我的文章。

以上是关于使用ffmpeg来探测GB28181的ps流的主要内容,如果未能解决你的问题,请参考以下文章

GB28181 PS流解析

gb28181提高效率一种通用解析ps流得方法以及零拷贝

gb28181 ps流文件解析

GB28181技术基础之2 - H264与PS封包

GB28181 PS流传输格式详解

GB28181设备端PS流封装和发送