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

简介: 最近需要做一个RTSP流媒体播放器,研究了一下,封装了一个RTSP播放类CRTSPPlayer,解码库采用ffmpeg。由于需求比较简单,时间也有限,目前只实现了播放、停止、暂停几个基本的接口。下面是基于CRTSPPlayer类实现的简单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 plain copy
 
 
  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 "libavformat\avformat.h"  
  13. #include "libavcodec\avcodec.h"  
  14. #include "libswscale\swscale.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 plain copy
 
 
  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. }  
 
 
 
目录
相关文章
|
7月前
|
缓存 网络协议 开发工具
庖丁解牛之-Android平台RTSP|RTMP播放器设计
我们在做Android平台RTSP或者RTMP播放器开发的时候,需要注意的点非常多,以下,以大牛直播SDK(官方)的接口为例,大概介绍下相关接口设计:
110 0
|
7月前
|
编解码 开发工具 Android开发
Android平台RTSP轻量级服务|RTMP推送摄像头或屏幕之音频接口设计
好多开发者在做Android平台录像或者RTSP轻量级服务、RTMP推送相关模块时,对需要设计哪些常用接口会心存疑惑,本文主要以大牛直播SDK(官方)为例,简单介绍下Android平台直播推送SDK所有音频相关的接口,感兴趣的开发者可以看看。
|
7月前
|
监控 开发工具 C++
Windows平台RTMP/RTSP播放器如何实现实时音量调节
RTMP或RTSP直播播放音量调节,主要用于多实例(多窗口)播放场景下,比如同时播放4路RTMP或RTSP流,如果音频全部打开,几路audio同时打开,可能会影响用户体验,我们通用的做法是支持播放端实时静音,更细粒度的做法是可以实时调节每一路RTMP/RTSP流的音量。
174 0
|
Web App开发 编解码 安全
在高版本谷歌Chrome浏览器中用VLC播放海康、大华RTSP实时视频完全方案
随着互联网基础设施的完善以及4G、5G等技术的大规模商用,在Chrome、Firefox、Edge等浏览器播放RTSP视频流也慢慢成为了信息化系统的行业标准。早些年还可用VLC播放器在网页中播放RTSP视频流,好景不长,2015年Chrome、Firefox等浏览器取消了对 NPAPI插件的支持,导致在高版本的Chrome等网页中播放海康威视、大华等摄像头RTSP视频流也成了奢望。
2017 0
|
3月前
|
数据采集 编解码 图形学
Android平台Unity下如何通过WebCamTexture采集摄像头数据并推送至RTMP服务器或轻量级RTSP服务
Android平台Unity下如何通过WebCamTexture采集摄像头数据并推送至RTMP服务器或轻量级RTSP服务
103 0
|
9月前
设计并实现同时支持多种视频格式的流媒体点播系统
设计并实现同时支持多种视频格式的流媒体点播系统
120 0
|
7月前
|
监控 数据处理 开发工具
Windows平台RTSP播放器/RTMP播放器设计需要考虑的几个点
我们在实现Windows平台RTSP播放器或RTMP播放器的时候,需要考虑的点很多,比如多实例设计、多绘制模式兼容、软硬解码支持、快照、RTSP下TCP-UDP自动切换等,以下就其中几个方面,做个大概的探讨。
|
7月前
|
Web App开发 开发工具 Android开发
利用RTMP或RTSP实现跨平台一对一互动功能
目前市面上大多一对一互动都是基于WebRTC,缺点如下: 1. 服务器部署非常复杂,不利于私有部署,在一些私密性高的场景下,无法使用,如公安、市政等体系; 2. 传输基于UDP,很难保证传输质量,由于UDP是不可靠的传输协议,在复杂的公网网络环境下,各种突发流量、偶尔的传输错误、网络抖动、超时等等都会引起丢包异常,都会在一定程度上影响音视频通信的质量; 3. 难以应对复杂的互联网环境,如跨区跨运营商、低带宽、高丢包等场景; 4. 整个框架体系不够灵活,代码复杂度高,行话说的好:从demo到实用,中间还差1万个WebRTC。
|
7月前
|
编解码 网络协议 开发工具
跨平台低延迟的RTMP/RTSP直播播放器设计实现
2015年,当我们试图在市面上找一款专供直播播放使用的低延迟播放器,来配合测试我们的RTMP推送模块使用时,居然发现没有一款好用的,市面上的,如VLC或Vitamio,说白了都是基于FFMPEG,在点播这块支持格式很多,也非常优异,但是直播这块,特别是RTMP,延迟要几秒钟,对如纯音频、纯视频播放,快速启播、网络异常状态处理、集成复杂度等各方面,支持非常差,而且因为功能强大,bug很多,除了行业内资深的开发者能驾驭,好多开发者甚至连编译整体环境,都要耗费很大的精力。
256 0
|
7月前
|
编解码 网络协议 Android开发
Android平台RTMP|RTSP直播播放器功能进阶探讨
很多开发者在跟我聊天的时候,经常问我,为什么一个RTMP或RTSP播放器,你们需要设计那么多的接口,真的有必要吗?带着这样的疑惑,我们今天聊聊Android平台RTMP、RTSP播放器常规功能,如软硬解码设置、实时音量调节、实时快照、实时录像、视频view翻转和旋转、画面填充模式设定、解码后YUV、RGB数据回调等:
106 0