视频会议中或者录播中应用RTP和谈接管h264视频

http://www.cesclub.com/bw/jishuzhongxin/bianchengyuyan/2012/1118/46838.html

我愿我能在横过孩子心中的路子上,摆脱了一切的束厄局促;……在那儿,理智以它的法令造为纸鸢而飞放,真谛也使事实从枷锁中了。

给别人和保护本身的,两者同样是高贵的事业此法度文章献给刚进公司的须要帮助的法度员,

       申明:1 该代码在windows上运行,用vs2010编译。

                   2 该代码要能解决移植的题目。 

                   3 rtp及时传输和谈可以应用udp,也可以应用tcp和谈

       起首,为了减小法度的难度,申明应用的库解码库为ffmpeg,刷视频数据的办法可以应用

                  1 SDL库 ,到sdl的源代码网站中并编译

                  2 直接应用gdi, 并且解决翻转题目。

                  3 应用opengl或者direct3d, 或者directdraw。

     根蒂根基常识:

      A  起首RTP 包布局一般为12字节,传输层和谈中UDP和谈和TCP和谈是可选的,都可以用,多半应用了UDP和谈,若是要扫盲,请链接到基维百 

          科http://zh.wikipedia.org/wiki/%E5%AE%9E%E6%97%B6%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE,应用tcp和谈的益处是和rtsp和谈

          相接洽关系的,涉及到nat转换 路由方面的常识,我后面会讲,而UDP和谈在h264等视频发送的时辰要重视的是分包题目,主如果MTU最大传输单位的题目,h264的

          nalu若是跨越最大传输单位,必须分别发送。

      B ffmpeg1.0 已经及其优良,包含ffmpeg库不要忘了 extern “C”extern "C"{#include #include #include #include }为了使得快速开辟出一个原型,应用boost的asio库,

         可以节俭一些时候。并且应用回调函数来解码和刷屏,以下是应用asio库来接管收集的包,默认应用了组播地址,也就是说假设该h264视频会传送到组播地址上,传送到

         组播地址的益处是调试便利,在局域网内接管都可以。

 

这是收集接管类的一个头文件示例,读者完全可以不应用boost库,自行写出:


#pragma once
#include "CodeReceive.h"

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>

#include "DrawRGB24.h"
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavutil/mathematics.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}

#include "h264head/h264-x264.h"

class CodeReceive2:public CBaseReceive
{
friend DWORD WINAPI ThreadProcReceive(LPVOID param);
public:
	CodeReceive2();
	~CodeReceive2(void);
protected:
	void CreateiocpReceiver(const boost::asio::ip::address& listen_address,
      const boost::asio::ip::address& multicast_address,
	  const unsigned short port);
	
	void handle_receive__direct(const boost::system::error_code& error,
      size_t bytes_recvd);



	BOOL CreateDecode()
	{
		if(_pDecode==NULL)
		{
			_pDecode= new H264DecoderContext();

			if(_pDecode == NULL)
				return FALSE;
			if(!_pDecode->Initialize())
				return FALSE;
		}
		return TRUE;
	}
	void DeleteDecode()
	{
		if(_pDecode!=NULL)
		{
			 _pDecode;
			_pDecode = NULL;
		}
	}


public:
	virtual int Pix_Fmt();
	virtual int Width()  ;
	virtual int Height() ;
	virtual BOOL StartReceive(string ip,unsigned short port) ;

    virtual void StopReceive() ;

	//这个画法是应用了SDL画法
	virtual void SetFunction(FrameCallback func) ;

	//这个是可以获取数据本身画,后面的版本是要用directshow vmr画法
	virtual void SetFunctionRGB24(FrameCallback_RGB24 func) ;

	//这个是内置的画法,通俗GDI画,参考OpenCV源代码,预览画像
	virtual void SetDrawhWnd(HWND hWnd0,HWND hWnd1) ;

   // static DWORD ThreadProc_Recv(LPVOID param);

private:

    boost::asio::io_service io_service_;
	boost::asio::ip::udp::socket socket_;
    boost::asio::ip::udp::endpoint sender_endpoint_;
    enum { max_length = 1500 };
    char data_[max_length];
    unsigned short _multicast_port;

	string _multicast_ip;
private:
	H264DecoderContext* _pDecode;

	AVFrame * _pFrameRGB;
	uint8_t * _RGBBuffer;
	struct SwsContext *_img_convert_ctx;
	//同时画两个窗口
    CDrawRGB24 _Draw;
   //  HANDLE _ThreadHandle  ;
	HWND _hWnd0;
	HWND _hWnd1;

	FrameCallback_RGB24 _functionRGB24;
};



类的cpp文件的接管函数的关键函数

void CodeReceive2::handle_receive__direct(const boost::system::error_code& error,
      size_t bytes_recvd)
{
    if (!error)
    {
		
		AVFrame * frame =_pDecode->DecodeFrames((const u_char*)data_,bytes_recvd);
		if(frame!=NULL)
		{
			int Width  = this->Width();//_pDecode->GetContext()->width;
			int Height = this->Height();//_pDecode->GetContext()->height;

#if 0  //若是须要用sdl衬着画面,可以打开这个
			if(_function )
				_function(frame,_pDecode->GetContext()->pix_fmt,
					_pDecode->GetContext()->width,
					_pDecode->GetContext()->height
		
					);
#endif            
			
			if(_RGBBuffer == NULL)
			{
				int numBytes;

				numBytes=avpicture_get_size(
					//PIX_FMT_RGB24, 
					PIX_FMT_BGR24,
					Width,
					Height);
				_RGBBuffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

				if(_pFrameRGB == NULL)
					_pFrameRGB = avcodec_alloc_frame();
				avpicture_fill((AVPicture *)_pFrameRGB, _RGBBuffer, PIX_FMT_BGR24,   Width, Height); 

				_img_convert_ctx = sws_getContext(Width, Height, 
					_pDecode->GetContext()->pix_fmt,//PIX_FMT_YUV420P, 
					Width, 
					Height, 
					PIX_FMT_BGR24, 
					SWS_BICUBIC, 
					NULL, 
					NULL, 
					NULL);
			}

			sws_scale(_img_convert_ctx, frame->data, frame->linesize, 0, Height, _pFrameRGB->data, _pFrameRGB->linesize);

			if(_hWnd0!=NULL || _hWnd1!=NULL)
				_Draw.Draw2(_hWnd0,_hWnd1,_pFrameRGB->data[0],Width,Height);

			//Sleep(5);
			if(_functionRGB24)
			{

				_functionRGB24(_pFrameRGB->data[0],_pDecode->GetContext()->pix_fmt,Width,Height);
			}
	
		}
	

        socket_.async_receive_(
          boost::asio::buffer(data_, max_length), sender_endpoint_,
          boost::bind(&CodeReceive2::handle_receive__direct, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));

    }
  }


  这里有让刚做ffmpeg或者图像的法度员困惑的一些题目,比如图像为什么接管的是倒立的?RGB实际上是BGR,能不克不及直接刷屏yuv420等等。图像倒立的题目是直播取到的图像本身就是倒立的,还是过程中倒立了,这个题目斗劲难以答复,比如摄像头D70,高清的HD1等等有一个开关拨一下就正,再返归去就倒立,不过拿通俗的USB摄像头,遵守正常采集,经过一系列的变换,你发明图像也是倒得,若是拿一个正常录下的h264视频,有的法度员播放时发明也是倒立的,若是是倒立的,有两个办法解决,一是ffmepg的sws_scale函数可以解决这个题目,一个是gdi刷屏可以解决这个题目,有的法度员会从头memcpy以下,把图像倒过来,如许也是可以的,但若是是高清720P或者1080P的图像较大,斗劲费事,最好是直接在过程中就解决掉这个题目。

   用gdi刷屏时,把SrcH负过来就可以让图像正过来。

   当然,用gdi必定效力不会很好,尤其做画中画的时辰或者多路图像的时辰,不克不及用这个,windows上可以用directx和较新的dxva。

  用下面这个来倒立图像

m_lpBmpInfo->bmiHeader.biHeight=   -SrcH;


void CDrawRGB24::Draw2(HWND hWnd, HWND hWnd2,unsigned char * buffer, int SrcW, int SrcH)
{
	HDC hDCDst1 = NULL;
	HDC hDCDst2 = NULL;
	RECT destRect1;
	RECT destRect2;
	if(hWnd!=NULL)
	{
		hDCDst1 = GetDC(hWnd);
		GetClientRect(hWnd,&destRect1);
	}
	if(hWnd2!=NULL)
	{
		hDCDst2 = GetDC(hWnd2);
		GetClientRect(hWnd2,&destRect2);
	}

	if(!m_bInit)
	{
		m_bInit = true;
		m_lpBmpInfo=new BITMAPINFO;
		m_lpBmpInfo->bmiHeader.biSize  = sizeof(BITMAPINFOHEADER);
		m_lpBmpInfo->bmiHeader.biWidth =   SrcW;
		m_lpBmpInfo->bmiHeader.biHeight=   -SrcH;
		m_lpBmpInfo->bmiHeader.biPlanes= 1;
		m_lpBmpInfo->bmiHeader.biBitCount      = 24;
		m_lpBmpInfo->bmiHeader.biCompression   = 0;
		m_lpBmpInfo->bmiHeader.biSizeImage     = 0;
		m_lpBmpInfo->bmiHeader.biXPelsPerMeter = 0;
		m_lpBmpInfo->bmiHeader.biYPelsPerMeter = 0;
		m_lpBmpInfo->bmiHeader.biClrUsed=0;
		m_lpBmpInfo->bmiHeader.biClrImportant  = 0;

		//CDC * dc =  CDC::FromHandle(hDCDst);
		//m_pMemDC = new CMemDC(*dc,DestRect);
	}

	if(hDCDst1!=NULL)
	{
		int DstWidth  = destRect1.right-destRect1.left;
		int DstHeight = destRect1.bottom- destRect1.top;
		SetStretchBltMode(hDCDst1,STRETCH_HALFTONE);
		::StretchDIBits(
			//m_pMemDC->GetDC().GetSafeHdc(),
			hDCDst1,
			0, 0, DstWidth, DstHeight,
			0, 0, SrcW, SrcH,
			buffer, m_lpBmpInfo, DIB_RGB_COLORS, SRCCOPY );
		ReleaseDC(hWnd,hDCDst1);
	}
	if(hDCDst2!=NULL)
	{
		int DstWidth  = destRect2.right-destRect2.left;
		int DstHeight = destRect2.bottom- destRect2.top;
		SetStretchBltMode(hDCDst2,STRETCH_HALFTONE);
		::StretchDIBits(
			//m_pMemDC->GetDC().GetSafeHdc(),
			hDCDst2,
			0, 0, DstWidth, DstHeight,
			0, 0, SrcW, SrcH,
			buffer, m_lpBmpInfo, DIB_RGB_COLORS, SRCCOPY );
		ReleaseDC(hWnd2,hDCDst2);
	}

}

全部的过程是收包,拿到包头时候戳等信息,去掉包头12字节,拿到h264 nalu数据,用ffmpeg解码,时候戳题目首要集中在音频和视频同步的上方,而pts和dts是同步最首要的信息,解码过程为:

   

AVFrame* H264DecoderContext::DecodeFrames(const u_char * src, unsigned & srcLen)
{

  RTPFrame srcRTP(src, srcLen);
  if (!_rxH264Frame->SetFromRTPFrame(srcRTP, flags)) {
	  _rxH264Frame->BeginNewFrame();
	  //sprintf(dst,"%s
","setrtpframe is not ok!");
	  flags = (_gotAGoodFrame ? requestIFrame : 0);
	  _gotAGoodFrame = false;
	  return NULL;
  }

  if (srcRTP.GetMarker()==0)
  {
		return NULL;
  } 

  if (_rxH264Frame->GetFrameSize()==0)
  {
	  _rxH264Frame->BeginNewFrame();
	  _skippedFrameCounter++;
	  flags = (_gotAGoodFrame ? requestIFrame : 0);
	  _gotAGoodFrame = false;
	  return NULL;
  }
  // look and see if we have read an I frame.
  if (_gotIFrame == 0)
  {
    _gotIFrame = 1;
  }
 

  int gotPicture = 0;
 // uint32_t bytesUsed = 0;

  // int bytesDecoded = avcodec_decode_video(_context,_outputFrame,&gotPicture,_rxH264Frame->GetFramePtr(),_rxH264Frame->GetFrameSize());

  int bytesDecoded = FFMPEGLibraryInstance.AvcodecDecodeVideo(_context, _outputFrame, &gotPicture, _rxH264Frame->GetFramePtr(), _rxH264Frame->GetFrameSize());
  _rxH264Frame->BeginNewFrame();
  if (!gotPicture) 
  {
	  _skippedFrameCounter++;
	  flags = (_gotAGoodFrame ? requestIFrame : 0);

	  _gotAGoodFrame = false;
	  return NULL;
  }

 //获得了一帧
  // w  = _context->width;
  // h  = _context->height;
  flags = 1;
  _frameCounter++;
  _gotAGoodFrame = true;


  return _outputFrame;
}

  代码临时只是为了演示,并不完全,不过根蒂根基过程是很是清楚的。过程中其实还须要处理惩罚一个斗劲傲首要的题目就是辨别率改变的题目,音视频同步的题目,播放过快或者过慢的题目,若是要测试发送的视频是否正确,可以应用vlc来接管测试。

  这是第一篇根蒂根基,后面再筹办斗劲完全的示例和用d3d,sdl刷屏,并且参加音频的解码,属于第二篇。

  未完待续。。。。。。

泰戈尔

更多网站建设信息、网站开发资讯,敬请咨询百微信息科技,021-57700304


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值