• 嵌入式 RTSP流媒体播放器实现


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

    头文件:

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

    源文件:

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