RTSP流媒體播放器實現

來源:互聯網
上載者:User

        最近需要做一個RTSP流媒體播放器,研究了一下,封裝了一個RTSP播放類CRTSPPlayer,解碼庫採用ffmpeg。由於需求比較簡單,時間也有限,目前只實現了播放、停止、暫停幾個基本的介面。下面是基於CRTSPPlayer類實現的簡單RTSP播放器。

                                   

       目前視頻只測試了H264格式,其它格式的視頻還未做測試。播放器也支援直接開啟本地視頻播放,但播放的幀率和原始視頻的碼率不同步。目前還不清楚如何處理這個問題,希望懂這方面的大俠指教。

       另外,還有一個開源的庫VLC也可以用來開發流媒體播放器,它支援多種流媒體協議,如RTP、RTSP等,CodeProject上已經有牛人在VLCLib的基礎上封裝可更易使用的庫VLCWrapper(地址:http://www.codeproject.com/Articles/38952/VLCWrapper-A-Little-C-wrapper-Around-libvlc)。用它可以很方便的開發視頻播放器。

        以下是CRTSPPlayer完整的代碼:

標頭檔:

/******************************************************************** filename:   CRTSPPlayer.h created:    2013-03-25 author:     firehood purpose:    ffmpeg庫實現的RTSP視頻播放器*********************************************************************/ #pragma once#include "windows.h"extern "C"{#include "libavformat\avformat.h"#include "libavcodec\avcodec.h"#include "libswscale\swscale.h"};// 播放狀態enum RTSP_PLAYSTATUS{RTSP_PLAYSTATUS_NONE,       // 未知狀態(未播放)RTSP_PLAYSTATUS_PLAYING,    // 現正播放    RTSP_PLAYSTATUS_PAUSE,      // 已暫停RTSP_PLAYSTATUS_STOP,       // 已停止};class CRTSPPlayer{public:CRTSPPlayer(HWND hWnd, LPRECT lpRect);~CRTSPPlayer(void);public:// 開啟媒體檔案BOOL OpenMedia(LPCTSTR pFileName);// 播放void Play();// 暫停void Pause();// 停止void Stop();// 擷取播放狀態RTSP_PLAYSTATUS GetPlayStatus(void);private:// 解碼初始化int DecodeInit(LPCTSTR pFileName);// 卸載void DecodeUninit();// 開始解碼線程    BOOL StartDecodeThread();    // 停止解碼線程void StopDecodeThread();    // 解碼線程static int WINAPI ThreadDecodeVideo(LPVOID lpParam);    // 開始解碼任務int BeginDecode();    // 顯示void Display();    // 映像轉換int ImgConvert(AVPicture * dst, PixelFormat dstFormt, const AVPicture * src, PixelFormat srcFormt, int src_width, int src_height);    // 設定播放狀態    void SetPlayStatus(RTSP_PLAYSTATUS playStatus);private:    HANDLE  m_hDecodeThread;BOOL    m_bExitDecodeThread;    TCHAR   m_strFilePath[MAX_PATH];AVFormatContext* m_pFormatContext;AVCodecContext*  m_pCodecContext;AVCodec* m_pCodec;AVPacket m_struPacket;int m_nStreamIndex;AVFrame* m_pFrameYUV;AVFrame* m_pFrameRGB;int     m_nFrameWidth; int    m_nFrameHeight;BYTE*   m_pBufRGB;        // 解碼後的RGB資料RTSP_PLAYSTATUS  m_nPlayStatus;HWND    m_hWnd;RECT    m_rcWnd;};

源檔案:

/******************************************************************** filename:   CRTSPPlayer.cpp created:    2013-03-25 author:     firehood purpose:    ffmpeg庫實現的RTSP視頻播放器*********************************************************************/ #include "StdAfx.h"#include "RTSPPlayer.h"#pragma comment(lib, "avformat.lib")#pragma comment(lib, "avcodec.lib")#pragma comment(lib, "swscale.lib")#pragma comment(lib, "avutil.lib")#define SHOW_TITLEconst char* WcharToUtf8(const wchar_t *pwStr)  {  if (pwStr == NULL)  {  return NULL;  }  int len = WideCharToMultiByte(CP_UTF8, 0, pwStr, -1, NULL, 0, NULL, NULL);  if (len <= 0)  {  return NULL;  }  char *pStr = new char[len];  WideCharToMultiByte(CP_UTF8, 0, pwStr, -1, pStr, len, NULL, NULL);  return pStr;  }  CRTSPPlayer::CRTSPPlayer(HWND hWnd, LPRECT lpRect):m_hWnd(hWnd),m_rcWnd(*lpRect),m_hDecodeThread(NULL),m_bExitDecodeThread(FALSE),m_nFrameWidth(0),m_nFrameHeight(0),m_pFormatContext(NULL),m_pCodecContext(NULL),m_pCodec(NULL),m_nStreamIndex(-1),m_pFrameYUV(NULL),m_pFrameRGB(NULL),m_pBufRGB(NULL),m_nPlayStatus(RTSP_PLAYSTATUS_NONE){memset(m_strFilePath,0,sizeof(m_strFilePath));}CRTSPPlayer::~CRTSPPlayer(void){DecodeUninit();}// 開啟媒體檔案BOOL CRTSPPlayer::OpenMedia(LPCTSTR pFileName){if(pFileName == NULL)return FALSE;DecodeUninit();memcpy(m_strFilePath,pFileName,sizeof(m_strFilePath));DecodeInit(m_strFilePath);return TRUE;}// 播放void CRTSPPlayer::Play(){ if(GetPlayStatus() == RTSP_PLAYSTATUS_STOP){DecodeInit(m_strFilePath);}BOOL bRet = StartDecodeThread();if(bRet){SetPlayStatus(RTSP_PLAYSTATUS_PLAYING);}}// 暫停void CRTSPPlayer::Pause(){StopDecodeThread();SetPlayStatus(RTSP_PLAYSTATUS_PAUSE);}// 停止void CRTSPPlayer::Stop(){StopDecodeThread();DecodeUninit();SetPlayStatus(RTSP_PLAYSTATUS_STOP);}BOOL CRTSPPlayer::StartDecodeThread(){if(m_hDecodeThread == NULL){m_hDecodeThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadDecodeVideo, this, 0, NULL);}return m_hDecodeThread ? TRUE : FALSE;}void CRTSPPlayer::StopDecodeThread(){if(m_hDecodeThread){m_bExitDecodeThread = TRUE;WaitForSingleObject(m_hDecodeThread,INFINITE);CloseHandle(m_hDecodeThread);m_hDecodeThread = NULL;}}int CRTSPPlayer::ImgConvert(AVPicture * dst, PixelFormat dst_pix_fmt, const AVPicture * src, PixelFormat src_pix_fmt, int src_width, int src_height){unsigned char * srcSlice[4];int srcStride[4] = {0};unsigned char * dstSlice[4];int dstStride[4] = {0};for (int i=0; i<4; i++){srcSlice[i] = src->data[i];srcStride[i] = src->linesize[i];dstSlice[i] = dst->data[i];dstStride[i] = dst->linesize[i];}SwsContext *pSwsContext = sws_getContext(src_width, src_height, src_pix_fmt, src_width, src_height, dst_pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);int nRet = sws_scale(pSwsContext, srcSlice, srcStride, 0, src_height, dstSlice, dstStride);if (pSwsContext != NULL){sws_freeContext(pSwsContext);}return nRet;}int WINAPI CRTSPPlayer::ThreadDecodeVideo(LPVOID lpParam){CRTSPPlayer *pPlayer = (CRTSPPlayer*)lpParam;pPlayer->BeginDecode();return 0;}int CRTSPPlayer::DecodeInit(LPCTSTR pFileName){    if(pFileName == NULL){return -1;}av_register_all();#ifdef  UNICODE   const char *filePath = WcharToUtf8(pFileName); // Open videoif (av_open_input_file(&m_pFormatContext, filePath, NULL, 0, NULL) != 0){return -2; // Couldn't open file}delete[] filePath;#else// Open videoif (av_open_input_file(&m_pFormatContext, pFileName, NULL, 0, NULL) != 0){return -2; // Couldn't open file}#endif// Retrieve stream informationif (av_find_stream_info(m_pFormatContext) < 0){return -3; // Couldn't find stream information}// Find the first video streamfor (UINT i=0; i<m_pFormatContext->nb_streams; i++){if (m_pFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO){m_nStreamIndex = i;break;}}if (m_nStreamIndex == -1){return -4; // Didn't find a video stream}// Get a pointer to the codec context for the video streamm_pCodecContext = m_pFormatContext->streams[m_nStreamIndex]->codec;// Find the decoder for the video streamm_pCodec = avcodec_find_decoder(m_pCodecContext->codec_id);if (m_pCodec == NULL){return -5 ; // Codec not found}// Inform the codec that we can handle truncated bitstreams -- i.e.,// bitstreams where frame boundaries can fall in the middle of packetsif (m_pCodec->capabilities & CODEC_CAP_TRUNCATED){m_pCodecContext->flags |= CODEC_FLAG_TRUNCATED;  // we do not send complete frames}// Open codecif (avcodec_open(m_pCodecContext, m_pCodec) < 0){return -6; // Could not open codec}// Allocate video framem_pFrameYUV = avcodec_alloc_frame();// Allocate an AVFrame structurem_pFrameRGB = avcodec_alloc_frame();// Determine required buffer size and allocate bufferint numBytes = avpicture_get_size(PIX_FMT_BGR24, m_pCodecContext->width, m_pCodecContext->height);m_pBufRGB = new BYTE [numBytes];memset(m_pBufRGB,0,numBytes);// Assign appropriate parts of buffer to image planes in m_pFrameRGBavpicture_fill((AVPicture *)m_pFrameRGB, m_pBufRGB, PIX_FMT_BGR24, m_pCodecContext->width, m_pCodecContext->height);m_nFrameWidth  = m_pCodecContext->width;m_nFrameHeight = m_pCodecContext->height;return 0;}void CRTSPPlayer::DecodeUninit(){// Close the codecif (m_pCodecContext){avcodec_close(m_pCodecContext);//av_free(m_pCodec);m_pCodecContext = NULL;m_pCodec = NULL;}// Close the video fileif (m_pFormatContext){av_close_input_file(m_pFormatContext);m_pFormatContext = NULL;}if (m_pFrameYUV){av_free(m_pFrameYUV);m_pFrameYUV = NULL;}if (m_pFrameRGB){av_free(m_pFrameRGB);m_pFrameRGB = NULL;}if (m_pBufRGB){delete [] m_pBufRGB;m_pBufRGB = NULL;}}int CRTSPPlayer::BeginDecode(){int bytesRemaining = 0, bytesDecoded;BYTE * rawData = NULL;int frameFinished = 0;m_struPacket.data = NULL;m_struPacket.size = 0;m_bExitDecodeThread = FALSE;while (!m_bExitDecodeThread && m_pFormatContext){// Read the next packet, skipping all packets that aren't for this streamdo{// Read new packetif (av_read_frame(m_pFormatContext, &m_struPacket) < 0){return -2;}} while (m_struPacket.stream_index != m_nStreamIndex);bytesRemaining = m_struPacket.size;rawData = m_struPacket.data;// Work on the current packet until we have decoded all of itwhile (bytesRemaining > 0){// Decode the next chunk of databytesDecoded = avcodec_decode_video(m_pCodecContext, m_pFrameYUV, &frameFinished, rawData, bytesRemaining);// Was there an error?if (bytesDecoded < 0){return -1;}bytesRemaining -= bytesDecoded;rawData += bytesDecoded;// Did we finish the current frame? Then we can returnif (frameFinished){ImgConvert((AVPicture *)m_pFrameRGB,PIX_FMT_BGR24,(AVPicture *)m_pFrameYUV,m_pCodecContext->pix_fmt,m_pCodecContext->width,m_pCodecContext->height);Display();}}}m_hDecodeThread = NULL;return 0;}void CRTSPPlayer::Display(){HDC hdc = GetDC(m_hWnd);// 建立記憶體DC    HDC hMemDc = CreateCompatibleDC(hdc);     // 建立位元影像BITMAPINFOHEADER bmpHdr = {0};  bmpHdr.biSize = sizeof (BITMAPINFOHEADER);  bmpHdr.biWidth = m_nFrameWidth;  bmpHdr.biHeight = -m_nFrameHeight;  bmpHdr.biPlanes = 1;  bmpHdr.biBitCount = 24;  bmpHdr.biCompression = BI_RGB;  BYTE *pData = NULL;   HBITMAP hBitmap = CreateDIBSection (NULL, (BITMAPINFO *)&bmpHdr, DIB_RGB_COLORS, (void**)&pData, NULL, 0);  try{memcpy(pData, m_pBufRGB, m_nFrameWidth * m_nFrameHeight * 3);}catch (CMemoryException* e){}HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDc, hBitmap);#ifdef SHOW_TITLE// 設定字型參數LOGFONT logfont;memset(&logfont, 0, sizeof(LOGFONT));logfont.lfHeight = 40;logfont.lfWidth = 0;logfont.lfEscapement = 0;logfont.lfOrientation = 0;logfont.lfWeight = 30;logfont.lfItalic = 0;logfont.lfUnderline = 0;logfont.lfStrikeOut = 0;logfont.lfCharSet = DEFAULT_CHARSET;   logfont.lfOutPrecision= OUT_DEFAULT_PRECIS;   logfont.lfClipPrecision= OUT_DEFAULT_PRECIS;   logfont.lfQuality = DEFAULT_QUALITY;   logfont.lfPitchAndFamily= DEFAULT_PITCH;  // 建立字型並選入環境HFONT hFont = CreateFontIndirect(&logfont);HFONT hOldFont = (HFONT)SelectObject(hMemDc, hFont);// 設定繪圖環境SetBkMode(hMemDc, TRANSPARENT);      SetTextColor(hMemDc, RGB(255, 255, 0));// 繪製文字TextOut(hMemDc,0,0,m_strFilePath,_tcslen(m_strFilePath));// 恢複環境釋放字型SelectObject(hMemDc, hOldFont);#endifStretchBlt(  hdc,  m_rcWnd.left,   m_rcWnd.top,   m_rcWnd.right-m_rcWnd.left,   m_rcWnd.bottom-m_rcWnd.top,   hMemDc,  0,   0,   m_nFrameWidth,   m_nFrameHeight,   SRCCOPY);  // 恢複並釋放環境    SelectObject(hMemDc,hOldBitmap);  DeleteObject(hBitmap);  DeleteDC(hMemDc);  }// 擷取播放狀態RTSP_PLAYSTATUS CRTSPPlayer::GetPlayStatus(void){return m_nPlayStatus;}// 設定播放狀態void CRTSPPlayer::SetPlayStatus(RTSP_PLAYSTATUS playStatus){m_nPlayStatus = playStatus;}

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.