RTSP 播放器 |
您所在的位置:网站首页 › videojs播放rtsp › RTSP 播放器 |
最近需要做一个RTSP流媒体播放器,研究了一下,封装了一个RTSP播放类CRTSPPlayer,解码库采用ffmpeg。由于需求比较简单,时间也有限,目前只实现了播放、停止、暂停几个基本的接口。下面是基于CRTSPPlayer类实现的简单RTSP播放器。
目前视频只测试了H264格式,其它格式的视频还未做测试。播放器也支持直接打开本地视频播放,但播放的帧率和原始视频的码率不同步。目前还不清楚如何处理这个问题,希望懂这方面的大侠指教。 另外,还有一个开源的库VLC也可以用来开发流媒体播放器,它支持多种流媒体协议,如RTP、RTSP等,CodeProject上已经有牛人在VLCLib的基础上封装可更易使用的库VLCWrapper(地址:)。用它可以很方便的开发视频播放器。 以下是CRTSPPlayer完整的代码: 头文件: [cpp] /******************************************************************** 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; };
源文件: [cpp] /******************************************************************** 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_TITLE const 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 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 video if (av_open_input_file(&m_pFormatContext, filePath, NULL, 0, NULL) != 0) { return -2; // Couldn't open file } delete[] filePath; #else // Open video if (av_open_input_file(&m_pFormatContext, pFileName, NULL, 0, NULL) != 0) { return -2; // Couldn't open file } #endif // Retrieve stream information if (av_find_stream_info(m_pFormatContext) streams[m_nStreamIndex]->codec; // Find the decoder for the video stream m_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 packets if (m_pCodec->capabilities & CODEC_CAP_TRUNCATED) { m_pCodecContext->flags |= CODEC_FLAG_TRUNCATED; // we do not send complete frames } // Open codec if (avcodec_open(m_pCodecContext, m_pCodec) width, m_pCodecContext->height); m_pBufRGB = new BYTE [numBytes]; memset(m_pBufRGB,0,numBytes); // Assign appropriate parts of buffer to image planes in m_pFrameRGB avpicture_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 codec if (m_pCodecContext) { avcodec_close(m_pCodecContext); //av_free(m_pCodec); m_pCodecContext = NULL; m_pCodec = NULL; } // Close the video file if (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 stream do { // Read new packet if (av_read_frame(m_pFormatContext, &m_struPacket) 0) { // Decode the next chunk of data bytesDecoded = avcodec_decode_video(m_pCodecContext, m_pFrameYUV, &frameFinished, rawData, bytesRemaining); // Was there an error? if (bytesDecoded 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); #endif StretchBlt( 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; } |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |