最近需要做一个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完整的代码:

头文件:

  1. /********************************************************************
  2. filename:   CRTSPPlayer.h
  3. created:    2013-03-25
  4. author:     firehood
  5. purpose:    ffmpeg库实现的RTSP视频播放器
  6. *********************************************************************/
  7. #pragma once
  8. #include "windows.h"
  9. extern "C"
  10. {
  11. #include "libavformat\avformat.h"
  12. #include "libavcodec\avcodec.h"
  13. #include "libswscale\swscale.h"
  14. };
  15. // 播放状态
  16. enum RTSP_PLAYSTATUS
  17. {
  18. RTSP_PLAYSTATUS_NONE,       // 未知状态(未播放)
  19. RTSP_PLAYSTATUS_PLAYING,    // 正在播放
  20. RTSP_PLAYSTATUS_PAUSE,      // 已暂停
  21. RTSP_PLAYSTATUS_STOP,       // 已停止
  22. };
  23. class CRTSPPlayer
  24. {
  25. public:
  26. CRTSPPlayer(HWND hWnd, LPRECT lpRect);
  27. ~CRTSPPlayer(void);
  28. public:
  29. // 打开媒体文件
  30. BOOL OpenMedia(LPCTSTR pFileName);
  31. // 播放
  32. void Play();
  33. // 暂停
  34. void Pause();
  35. // 停止
  36. void Stop();
  37. // 获取播放状态
  38. RTSP_PLAYSTATUS GetPlayStatus(void);
  39. private:
  40. // 解码初始化
  41. int DecodeInit(LPCTSTR pFileName);
  42. // 卸载
  43. void DecodeUninit();
  44. // 开始解码线程
  45. BOOL StartDecodeThread();
  46. // 停止解码线程
  47. void StopDecodeThread();
  48. // 解码线程
  49. static int WINAPI ThreadDecodeVideo(LPVOID lpParam);
  50. // 开始解码任务
  51. int BeginDecode();
  52. // 显示
  53. void Display();
  54. // 图像转换
  55. int ImgConvert(AVPicture * dst, PixelFormat dstFormt, const AVPicture * src, PixelFormat srcFormt, int src_width, int src_height);
  56. // 设置播放状态
  57. void SetPlayStatus(RTSP_PLAYSTATUS playStatus);
  58. private:
  59. HANDLE  m_hDecodeThread;
  60. BOOL    m_bExitDecodeThread;
  61. TCHAR   m_strFilePath[MAX_PATH];
  62. AVFormatContext* m_pFormatContext;
  63. AVCodecContext*  m_pCodecContext;
  64. AVCodec* m_pCodec;
  65. AVPacket m_struPacket;
  66. int m_nStreamIndex;
  67. AVFrame* m_pFrameYUV;
  68. AVFrame* m_pFrameRGB;
  69. int     m_nFrameWidth;
  70. int     m_nFrameHeight;
  71. BYTE*   m_pBufRGB;        // 解码后的RGB数据
  72. RTSP_PLAYSTATUS  m_nPlayStatus;
  73. HWND    m_hWnd;
  74. RECT    m_rcWnd;
  75. };

源文件:

  1. /********************************************************************
  2. filename:   CRTSPPlayer.cpp
  3. created:    2013-03-25
  4. author:     firehood
  5. purpose:    ffmpeg库实现的RTSP视频播放器
  6. *********************************************************************/
  7. #include "StdAfx.h"
  8. #include "RTSPPlayer.h"
  9. #pragma comment(lib, "avformat.lib")
  10. #pragma comment(lib, "avcodec.lib")
  11. #pragma comment(lib, "swscale.lib")
  12. #pragma comment(lib, "avutil.lib")
  13. #define SHOW_TITLE
  14. const char* WcharToUtf8(const wchar_t *pwStr)
  15. {
  16. if (pwStr == NULL)
  17. {
  18. return NULL;
  19. }
  20. int len = WideCharToMultiByte(CP_UTF8, 0, pwStr, -1, NULL, 0, NULL, NULL);
  21. if (len <= 0)
  22. {
  23. return NULL;
  24. }
  25. char *pStr = new char[len];
  26. WideCharToMultiByte(CP_UTF8, 0, pwStr, -1, pStr, len, NULL, NULL);
  27. return pStr;
  28. }
  29. CRTSPPlayer::CRTSPPlayer(HWND hWnd, LPRECT lpRect):
  30. m_hWnd(hWnd),
  31. m_rcWnd(*lpRect),
  32. m_hDecodeThread(NULL),
  33. m_bExitDecodeThread(FALSE),
  34. m_nFrameWidth(0),
  35. m_nFrameHeight(0),
  36. m_pFormatContext(NULL),
  37. m_pCodecContext(NULL),
  38. m_pCodec(NULL),
  39. m_nStreamIndex(-1),
  40. m_pFrameYUV(NULL),
  41. m_pFrameRGB(NULL),
  42. m_pBufRGB(NULL),
  43. m_nPlayStatus(RTSP_PLAYSTATUS_NONE)
  44. {
  45. memset(m_strFilePath,0,sizeof(m_strFilePath));
  46. }
  47. CRTSPPlayer::~CRTSPPlayer(void)
  48. {
  49. DecodeUninit();
  50. }
  51. // 打开媒体文件
  52. BOOL CRTSPPlayer::OpenMedia(LPCTSTR pFileName)
  53. {
  54. if(pFileName == NULL)
  55. return FALSE;
  56. DecodeUninit();
  57. memcpy(m_strFilePath,pFileName,sizeof(m_strFilePath));
  58. DecodeInit(m_strFilePath);
  59. return TRUE;
  60. }
  61. // 播放
  62. void CRTSPPlayer::Play()
  63. {
  64. if(GetPlayStatus() == RTSP_PLAYSTATUS_STOP)
  65. {
  66. DecodeInit(m_strFilePath);
  67. }
  68. BOOL bRet = StartDecodeThread();
  69. if(bRet)
  70. {
  71. SetPlayStatus(RTSP_PLAYSTATUS_PLAYING);
  72. }
  73. }
  74. // 暂停
  75. void CRTSPPlayer::Pause()
  76. {
  77. StopDecodeThread();
  78. SetPlayStatus(RTSP_PLAYSTATUS_PAUSE);
  79. }
  80. // 停止
  81. void CRTSPPlayer::Stop()
  82. {
  83. StopDecodeThread();
  84. DecodeUninit();
  85. SetPlayStatus(RTSP_PLAYSTATUS_STOP);
  86. }
  87. BOOL CRTSPPlayer::StartDecodeThread()
  88. {
  89. if(m_hDecodeThread == NULL)
  90. {
  91. m_hDecodeThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadDecodeVideo, this, 0, NULL);
  92. }
  93. return m_hDecodeThread ? TRUE : FALSE;
  94. }
  95. void CRTSPPlayer::StopDecodeThread()
  96. {
  97. if(m_hDecodeThread)
  98. {
  99. m_bExitDecodeThread = TRUE;
  100. WaitForSingleObject(m_hDecodeThread,INFINITE);
  101. CloseHandle(m_hDecodeThread);
  102. m_hDecodeThread = NULL;
  103. }
  104. }
  105. int CRTSPPlayer::ImgConvert(AVPicture * dst, PixelFormat dst_pix_fmt, const AVPicture * src, PixelFormat src_pix_fmt, int src_width, int src_height)
  106. {
  107. unsigned char * srcSlice[4];
  108. int srcStride[4] = {0};
  109. unsigned char * dstSlice[4];
  110. int dstStride[4] = {0};
  111. for (int i=0; i<4; i++)
  112. {
  113. srcSlice[i] = src->data[i];
  114. srcStride[i] = src->linesize[i];
  115. dstSlice[i] = dst->data[i];
  116. dstStride[i] = dst->linesize[i];
  117. }
  118. SwsContext *pSwsContext = sws_getContext(src_width, src_height, src_pix_fmt, src_width, src_height, dst_pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);
  119. int nRet = sws_scale(pSwsContext, srcSlice, srcStride, 0, src_height, dstSlice, dstStride);
  120. if (pSwsContext != NULL)
  121. {
  122. sws_freeContext(pSwsContext);
  123. }
  124. return nRet;
  125. }
  126. int WINAPI CRTSPPlayer::ThreadDecodeVideo(LPVOID lpParam)
  127. {
  128. CRTSPPlayer *pPlayer = (CRTSPPlayer*)lpParam;
  129. pPlayer->BeginDecode();
  130. return 0;
  131. }
  132. int CRTSPPlayer::DecodeInit(LPCTSTR pFileName)
  133. {
  134. if(pFileName == NULL)
  135. {
  136. return -1;
  137. }
  138. av_register_all();
  139. #ifdef  UNICODE
  140. const char *filePath = WcharToUtf8(pFileName);
  141. // Open video
  142. if (av_open_input_file(&m_pFormatContext, filePath, NULL, 0, NULL) != 0)
  143. {
  144. return -2; // Couldn't open file
  145. }
  146. delete[] filePath;
  147. #else
  148. // Open video
  149. if (av_open_input_file(&m_pFormatContext, pFileName, NULL, 0, NULL) != 0)
  150. {
  151. return -2; // Couldn't open file
  152. }
  153. #endif
  154. // Retrieve stream information
  155. if (av_find_stream_info(m_pFormatContext) < 0)
  156. {
  157. return -3; // Couldn't find stream information
  158. }
  159. // Find the first video stream
  160. for (UINT i=0; i<m_pFormatContext->nb_streams; i++)
  161. {
  162. if (m_pFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
  163. {
  164. m_nStreamIndex = i;
  165. break;
  166. }
  167. }
  168. if (m_nStreamIndex == -1)
  169. {
  170. return -4; // Didn't find a video stream
  171. }
  172. // Get a pointer to the codec context for the video stream
  173. m_pCodecContext = m_pFormatContext->streams[m_nStreamIndex]->codec;
  174. // Find the decoder for the video stream
  175. m_pCodec = avcodec_find_decoder(m_pCodecContext->codec_id);
  176. if (m_pCodec == NULL)
  177. {
  178. return -5 ; // Codec not found
  179. }
  180. // Inform the codec that we can handle truncated bitstreams -- i.e.,
  181. // bitstreams where frame boundaries can fall in the middle of packets
  182. if (m_pCodec->capabilities & CODEC_CAP_TRUNCATED)
  183. {
  184. m_pCodecContext->flags |= CODEC_FLAG_TRUNCATED;  // we do not send complete frames
  185. }
  186. // Open codec
  187. if (avcodec_open(m_pCodecContext, m_pCodec) < 0)
  188. {
  189. return -6; // Could not open codec
  190. }
  191. // Allocate video frame
  192. m_pFrameYUV = avcodec_alloc_frame();
  193. // Allocate an AVFrame structure
  194. m_pFrameRGB = avcodec_alloc_frame();
  195. // Determine required buffer size and allocate buffer
  196. int numBytes = avpicture_get_size(PIX_FMT_BGR24, m_pCodecContext->width, m_pCodecContext->height);
  197. m_pBufRGB = new BYTE [numBytes];
  198. memset(m_pBufRGB,0,numBytes);
  199. // Assign appropriate parts of buffer to image planes in m_pFrameRGB
  200. avpicture_fill((AVPicture *)m_pFrameRGB, m_pBufRGB, PIX_FMT_BGR24, m_pCodecContext->width, m_pCodecContext->height);
  201. m_nFrameWidth  = m_pCodecContext->width;
  202. m_nFrameHeight = m_pCodecContext->height;
  203. return 0;
  204. }
  205. void CRTSPPlayer::DecodeUninit()
  206. {
  207. // Close the codec
  208. if (m_pCodecContext)
  209. {
  210. avcodec_close(m_pCodecContext);
  211. //av_free(m_pCodec);
  212. m_pCodecContext = NULL;
  213. m_pCodec = NULL;
  214. }
  215. // Close the video file
  216. if (m_pFormatContext)
  217. {
  218. av_close_input_file(m_pFormatContext);
  219. m_pFormatContext = NULL;
  220. }
  221. if (m_pFrameYUV)
  222. {
  223. av_free(m_pFrameYUV);
  224. m_pFrameYUV = NULL;
  225. }
  226. if (m_pFrameRGB)
  227. {
  228. av_free(m_pFrameRGB);
  229. m_pFrameRGB = NULL;
  230. }
  231. if (m_pBufRGB)
  232. {
  233. delete [] m_pBufRGB;
  234. m_pBufRGB = NULL;
  235. }
  236. }
  237. int CRTSPPlayer::BeginDecode()
  238. {
  239. int bytesRemaining = 0, bytesDecoded;
  240. BYTE * rawData = NULL;
  241. int frameFinished = 0;
  242. m_struPacket.data = NULL;
  243. m_struPacket.size = 0;
  244. m_bExitDecodeThread = FALSE;
  245. while (!m_bExitDecodeThread && m_pFormatContext)
  246. {
  247. // Read the next packet, skipping all packets that aren't for this stream
  248. do
  249. {
  250. // Read new packet
  251. if (av_read_frame(m_pFormatContext, &m_struPacket) < 0)
  252. {
  253. return -2;
  254. }
  255. } while (m_struPacket.stream_index != m_nStreamIndex);
  256. bytesRemaining = m_struPacket.size;
  257. rawData = m_struPacket.data;
  258. // Work on the current packet until we have decoded all of it
  259. while (bytesRemaining > 0)
  260. {
  261. // Decode the next chunk of data
  262. bytesDecoded = avcodec_decode_video(m_pCodecContext, m_pFrameYUV, &frameFinished, rawData, bytesRemaining);
  263. // Was there an error?
  264. if (bytesDecoded < 0)
  265. {
  266. return -1;
  267. }
  268. bytesRemaining -= bytesDecoded;
  269. rawData += bytesDecoded;
  270. // Did we finish the current frame? Then we can return
  271. if (frameFinished)
  272. {
  273. ImgConvert(
  274. (AVPicture *)m_pFrameRGB,
  275. PIX_FMT_BGR24,
  276. (AVPicture *)m_pFrameYUV,
  277. m_pCodecContext->pix_fmt,
  278. m_pCodecContext->width,
  279. m_pCodecContext->height);
  280. Display();
  281. }
  282. }
  283. }
  284. m_hDecodeThread = NULL;
  285. return 0;
  286. }
  287. void CRTSPPlayer::Display()
  288. {
  289. HDC hdc = GetDC(m_hWnd);
  290. // 创建内存DC
  291. HDC hMemDc = CreateCompatibleDC(hdc);
  292. // 创建位图
  293. BITMAPINFOHEADER bmpHdr = {0};
  294. bmpHdr.biSize = sizeof (BITMAPINFOHEADER);
  295. bmpHdr.biWidth = m_nFrameWidth;
  296. bmpHdr.biHeight = -m_nFrameHeight;
  297. bmpHdr.biPlanes = 1;
  298. bmpHdr.biBitCount = 24;
  299. bmpHdr.biCompression = BI_RGB;
  300. BYTE *pData = NULL;
  301. HBITMAP hBitmap = CreateDIBSection (NULL, (BITMAPINFO *)&bmpHdr, DIB_RGB_COLORS, (void**)&pData, NULL, 0);
  302. try
  303. {
  304. memcpy(pData, m_pBufRGB, m_nFrameWidth * m_nFrameHeight * 3);
  305. }
  306. catch (CMemoryException* e)
  307. {
  308. }
  309. HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDc, hBitmap);
  310. #ifdef SHOW_TITLE
  311. // 设置字体参数
  312. LOGFONT logfont;
  313. memset(&logfont, 0, sizeof(LOGFONT));
  314. logfont.lfHeight = 40;
  315. logfont.lfWidth = 0;
  316. logfont.lfEscapement = 0;
  317. logfont.lfOrientation = 0;
  318. logfont.lfWeight = 30;
  319. logfont.lfItalic = 0;
  320. logfont.lfUnderline = 0;
  321. logfont.lfStrikeOut = 0;
  322. logfont.lfCharSet = DEFAULT_CHARSET;
  323. logfont.lfOutPrecision= OUT_DEFAULT_PRECIS;
  324. logfont.lfClipPrecision= OUT_DEFAULT_PRECIS;
  325. logfont.lfQuality = DEFAULT_QUALITY;
  326. logfont.lfPitchAndFamily= DEFAULT_PITCH;
  327. // 创建字体并选入环境
  328. HFONT hFont = CreateFontIndirect(&logfont);
  329. HFONT hOldFont = (HFONT)SelectObject(hMemDc, hFont);
  330. // 设置绘图环境
  331. SetBkMode(hMemDc, TRANSPARENT);
  332. SetTextColor(hMemDc, RGB(255, 255, 0));
  333. // 绘制文字
  334. TextOut(hMemDc,0,0,m_strFilePath,_tcslen(m_strFilePath));
  335. // 恢复环境释放字体
  336. SelectObject(hMemDc, hOldFont);
  337. #endif
  338. StretchBlt(
  339. hdc,
  340. m_rcWnd.left,
  341. m_rcWnd.top,
  342. m_rcWnd.right-m_rcWnd.left,
  343. m_rcWnd.bottom-m_rcWnd.top,
  344. hMemDc,
  345. 0,
  346. 0,
  347. m_nFrameWidth,
  348. m_nFrameHeight,
  349. SRCCOPY);
  350. // 恢复并释放环境
  351. SelectObject(hMemDc,hOldBitmap);
  352. DeleteObject(hBitmap);
  353. DeleteDC(hMemDc);
  354. }
  355. // 获取播放状态
  356. RTSP_PLAYSTATUS CRTSPPlayer::GetPlayStatus(void)
  357. {
  358. return m_nPlayStatus;
  359. }
  360. // 设置播放状态
  361. void CRTSPPlayer::SetPlayStatus(RTSP_PLAYSTATUS playStatus)
  362. {
  363. m_nPlayStatus = playStatus;
  364. }
 
 
 

嵌入式 RTSP流媒体播放器实现的更多相关文章

  1. EasyPlayerPro Windows流媒体播放器(RTSP/RTMP/HTTP/HLS/File/TCP/RTP/UDP都能播)发布啦

    EasyPlayerPro简介 EasyPlayerPro是一款全功能的流媒体播放器,支持RTSP.RTMP.HTTP.HLS.UDP.RTP.File等多种流媒体协议播放.支持本地文件播放,支持本地 ...

  2. RTSP/RTMP/HLS/HTTP流媒体播放器EasyPlayer

    EasyPlayer播放器系列项目 EasyPlayer是由EasyDarwin开源团队开发和维护的一个流媒体播放器系列项目,随着多年不断的发展和迭代,不断基于成功的实践经验,发展出包括有: Easy ...

  3. 基于Live555,ffmpeg的RTSP播放器直播与点播

    基于Live555,ffmpeg的RTSP播放器直播与点播 多路RTSP高清视频播放器下载地址:http://download.csdn.net/detail/u011352914/6604437多路 ...

  4. EasyPlayer RTSP播放器运行出现: Unable to load DLL 找不到指定的模块。exception from HRESULT 0x8007007E 解决方案

    最近有EasyPlayer RTSP播放器的开发者反馈,在一台新装的Windows Server 2008的操作系统上运行EasyPlayer RTSP播放器出现"Unable to loa ...

  5. EasyPlayer RTSP播放器OCX RegSvr32注册报错,DllRegisterServer调用失败,错误代码为0x80040200 解决方法

    问题描述 模块"EasyPlayer-RTSPWebActiveX.ocx" 已加载,但对DLLRegisterServer调用失败,错误代码为0x80040200. 解决方法 是 ...

  6. EasyPlayer windows RTSP播放器OCX插件使用说明

    鉴于大家对于EasyPlayer插件的使用还不太熟悉,特此写一篇插件的使用文档,供大家参考:EasyPlayer插件有两种,一种是基于IE的ActiveX控件,一种是基于FireFox(也支持多浏览器 ...

  7. EasyPlayer RTSP播放器对RTSP播放地址url的通用兼容修改意见

    问题反馈 最近在线上遇到一位老朋友咨询关于EasyPlayer播放器的事情,大概现象就是分别用EasyPlayer和vlc播放大华摄像机的RTSP流,流地址是:rtsp://admin:admin12 ...

  8. EasyPlayerPro(Windows)流媒体播放器开发之框架讲解

    EasyPlayerPro for Windows是基于ffmpeg进行开发的全功能播放器,开发过程中参考了很多开源的播放器,诸如vlc和ffplay等,其中最强大的莫过于vlc,但是鉴于vlc框架过 ...

  9. EasyPlayerPro(Windows)流媒体播放器开发之跨语言调用

    下面我们来讲解一下关于EasyPlayerPro接口的调用,主要分为C++和C#两种语言,C++也可以基于VC和QT进行开发,C++以VC MFC框架为例进行讲解,C#以Winform框架为例进行讲解 ...

随机推荐

  1. 负载均衡之Haproxy配置详解(及httpd配置)

    下图描述了使用keepalived+Haproxy主从配置来达到能够针对前段流量进行负载均衡到多台后端web1.web2.web3.img1.img2.但是由于haproxy会存在单点故障问题,因此使 ...

  2. Gravitational Teleport 是一个先进的 SSH 服务器,基于 Golang SSH 构建,完全兼容 OpenSSH

    Gravitational Teleport 是一个先进的 SSH 服务器,可通过 SSH 或者 HTTPS 远程访问 Linux 服务器.其目的是为了替代 sshd.Teleport 可以轻松让团队 ...

  3. 给View换字体

    注意,给View换字体是直接换.在Delegate里换的只是某一列的字体 class delegate : public QStyledItemDelegate { public: ) : QStyl ...

  4. apache 的ab 工具

    ab是apache 进行http服务器压力测试的一个工具.用来衡量apache 服务器的执行效率,能够检测出apache每秒能够处理的请求数. 一个使用的例子如下(windows下) ab -n -c ...

  5. 解决Hibernate Write operations are not allowed in read-only mode的方法

    错误信息: org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed i ...

  6. 深入理解Java内存模型(四)——volatile

    volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这 ...

  7. MTK6515 android打版软件配置(DrvGen.exe 使用)

    1 一.配置GPIO 2 二.配置emmc 3 三.配置LCM 3.1 1.增加LCM驱动文件 3.2 2.配置驱动文件 3.3 3.配置背光 4 四.配置touch panel 4.1 1.通过dc ...

  8. Server-Side UI Automation Provider - WPF Sample

    Server-Side UI Automation Provider - WPF Sample 2014-09-14 引用程序集 自动化对等类 WPF Sample 参考 引用程序集 返回 UIAut ...

  9. 浅析JavaScript引用类型之--Object、Array

    1.Object类型 对象是某个特定引用类型的实例,新对象有两种创建方式: i.使用new操作符调用构造函数来创建. var person = new Object(); person.name = ...

  10. 多线程安全的HTTPCLIENT

    private static HttpClient httpClient;     static {           HttpParams params = new BasicHttpParams ...