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》