天天看點

嵌入式 RTSP流媒體播放器實作

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

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

        以下是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);  

    // 打開媒體檔案  

    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);  

    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  

#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 <= 0)    

    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;  

    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()  

    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)  

        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  

    if (av_open_input_file(&m_pFormatContext, pFileName, NULL, 0, NULL) != 0)  

#endif  

    // Retrieve stream information  

    if (av_find_stream_info(m_pFormatContext) < 0)  

        return -3; // Couldn't find stream information  

    // Find the first video stream  

    for (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 stream  

    m_pCodecContext = 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) < 0)  

        return -6; // Could not open codec  

    // Allocate video frame  

    m_pFrameYUV = avcodec_alloc_frame();  

    // Allocate an AVFrame structure  

    m_pFrameRGB = avcodec_alloc_frame();  

    // Determine required buffer size and allocate buffer  

    int 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_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;  

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)  

            {  

                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 it  

        while (bytesRemaining > 0)  

            // Decode the next chunk of data  

            bytesDecoded = 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 return  

            if (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;  

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);  

    StretchBlt(    

        hdc,    

        m_rcWnd.left,     

        m_rcWnd.top,     

        m_rcWnd.right-m_rcWnd.left,     

        m_rcWnd.bottom-m_rcWnd.top,     

        hMemDc,    

        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;  

}  

繼續閱讀