基於<最簡單的基於FFMPEG+SDL的視頻播放器 ver2 (采用SDL2.0)>的一些個人總結


 最近因為項目接近收尾階段,所以變的沒有之前那么忙了,所以最近重新拿起了之前的一些FFMPEG和SDL的相關流媒體播放器的例子在看。

同時自己也用FFMPEG2.01,SDL2.01結合MFC以及網上羅列的一些資料,打算打造一款自己的簡易播放器。

最先開始是閱讀了<An ffmpeg and SDL Tutorial>以及來源與(http://blog.csdn.net/love4mario/article/details/17652355)中的中文資料,同時認真對tutorial01-08中的代碼,也進行一次閱讀。

然后打算修改tutorial07中的代碼來做播放器的代碼。

修改完了以后,編譯也通過了,然后播放出來的畫面卻是這個樣子的:

開始的時候,不理解者幾個函數里面的參數,感覺是這里設置出錯了。

在經過查找資料以后,得到的個人理解為:

/**
* \brief Update the given texture rectangle with new pixel data.
* \param texture The texture to update
* \param rect A pointer to the rectangle of pixels to update, or NULL to update the entire texture.
* \param pixels The raw pixel data.
* \param pitch The number of bytes between rows of pixel data.
* \return 0 on success, or -1 if the texture is not valid.
* \note This is a fairly slow function.
*/
extern DECLSPEC int SDLCALL SDL_UpdateTexture(SDL_Texture * texture,const SDL_Rect * rect,const void *pixels, int pitch);

SDL_UpdateTexture( SDL_Texture * texture,        /*需要更新的Texture,*/
              const SDL_Rect * rect,         /*更新的區域,如果為NULL,表示更新整個區域*/
            const void *pixels,            /*the raw pixel data,元素像素數據*/
           int pitch);                      /*the number of bytes between rows of pixel data,每一行原始數據的大小*/

------------------------------------------------------------------------------------------------------------------------------------------

/**

* \brief Copy a portion of the texture to the current rendering target.
* \param renderer The renderer which should copy parts of a texture.
* \param texture The source texture.
* \param srcrect A pointer to the source rectangle, or NULL for the entire  texture.
* \param dstrect A pointer to the destination rectangle, or NULL for the entire rendering target.
* \return 0 on success, or -1 on error
*/
extern DECLSPEC int SDLCALL SDL_RenderCopy(SDL_Renderer * renderer,   /*從texture復制到rendering的目標*/
                         SDL_Texture * texture,    /*源texture*/
                         const SDL_Rect * srcrect,   /*源拷貝的范圍*/
                         const SDL_Rect * dstrect);  /*目的拷貝范圍*/

於是繼續進行修改:

 

 

這個時候,畫面已經能正常顯示了,但是並沒有達到我的目的,我的目的本來是想讓整個綠色區域(也就算一個static控件)全部鋪滿視頻。

所以這個時候,我去群里問了一下,有好心的朋友告訴我,你看一下是不是轉化的時候設置有問題。

於是我這個時候我就拿很有代表性的例子,也比較簡單的例子<最簡單的基於FFMPEG+SDL的視頻播放器 ver2 (采用SDL2.0>來做測試,查看時哪里出了問題。

這個例子雖然簡單,但是很有代表性,因為代碼工程不大,容易調試和找問題,同時它又把重點函數全部提了出來。

下一步,調試<最簡單的基於FFMPEG+SDL的視頻播放器 ver2 (采用SDL2.0>:

代碼段1:

1     AVFrame    *pFrame,*pFrameYUV;
2     pFrame=avcodec_alloc_frame();
3     pFrameYUV=avcodec_alloc_frame();
4     uint8_t *out_buffer=(uint8_t*)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, 
5                                                  pCodecCtx->width, pCodecCtx->height);
6     avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, 
7                           pCodecCtx->width, pCodecCtx->height);

代碼段2:

1     int screen_w=0,screen_h=0;
2     SDL_Window *screen; 
3     //SDL 2.0 Support for multiple windows
4     screen_w = pCodecCtx->width;
5     screen_h = pCodecCtx->height;
6     screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
7                             screen_w, screen_h,
8                             SDL_WINDOW_OPENGL);

代碼段3:

 1     //IYUV: Y + U + V  (3 planes)
 2     //YV12: Y + V + U  (3 planes)
 3     SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer,     SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,
 4                                     pCodecCtx->width,pCodecCtx->height);  
 5 
 6     SDL_Rect sdlRect;  
 7     sdlRect.x = 0;  
 8     sdlRect.y = 0;  
 9     sdlRect.w = screen_w;  
10     sdlRect.h = screen_h;  

 

代碼段4:

1     struct SwsContext *img_convert_ctx;
2     img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, 
3                        pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); 

上面這4段代碼就是轉換參數相關的代碼,因為這里面的代碼,它在設置顯示的時候:

 screen_w = pCodecCtx->width;

screen_h = pCodecCtx->height;

所以我們在參考上面代碼時候,目的和源設置一樣的,所以不知道上面這些關鍵函數到底是跟顯示有什么關系,哪些應該需要設置為我們想要的顯示效果,哪些參數必須使用源流中的參數。

所以現在我打算改變一下參數,讓它最后顯示出來的視頻大小為寬度為800,高度為600.

所以我們再創建窗口的時候,

screen_w =800;

screen_h =600;

那么還需要哪些關鍵函數必須跟着做變化呢?

首先來看代碼1:

 

AVFrame *pFrame,*pFrameYUV;這兩個指針,前面一個用來指向解碼以后的幀數據緩存,后面一個pFrameYUV用來指向數據轉換以后的緩存,就算我們最后顯示的時候取數據的緩存指針。

即:

SDL_UpdateTexture( sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0] ); 

這個函數在進行更新數據的時候,需要從pFrameYUV指向的內存數據區去取數據,然后更新到sdlTexture緩沖區。

{

在這個更新的過程,其實並不是僅僅的進行數據緩沖區的拷貝,因為這個函數還需要做很多的事情.()

 

SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,
                        screen_w,screen_h);

這個函數指定顯示的時候,數據格式:SDL_PIXELFORMAT_YV12

}

 

 

而渲染數據進行拷貝的時候:

SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, NULL); 

就是把開始更新到sdlTexture數據緩沖區的數據,再進行一次拷貝到渲染緩沖區。最后就可以進行渲染顯示了。

 

那么在數據格式進行轉換之前,我們需要設置什么呢?

img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, /*源數據格式信息*/
                  screen_w,screen_h,PIX_FMT_YUV420P,            /*目的數據格式信息*/

                  SWS_BICUBIC, NULL, NULL, NULL);

 

轉換前的獲取轉換上下文,這個函數將告訴我們在進行轉換的時候,需要將源數據---(轉換成怎么樣的)--->目的數據格式信息。

注意:

也許你也跟我有一樣的疑問,為什么這里設置轉換參數的時候,目的數據格式為:PIX_FMT_YUV420P 而不是我們最后顯示的數據格式:YV12.

確實是這樣的,這里我們設置的轉換目的參數為PIX_FMT_YUV420P 而不是YV12.

顯示的數據格式為yv12,我們可以從

SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,screen_w,screen_h);

這個函數指定,這里就是對顯示的數據進行初始化。

那么數據從PIX_FMT_YUV420P ----------------------------->YV12又是那個函數做的呢?

這個過程就是我們上面的函數SDL_UpdateTexture中實現的,它不僅會將pFrameYUV指向的緩存區進行拷貝,而且會將PIX_FMT_YUV420P --拷貝成->YV12格式。

 

上面這個過程我們明白了從轉換后處理的數據到顯示數據的一個過程。那么如果源數據的寬度和高度並不是我們想要的,我們需要對源數據進行拉伸或者放大,那么我們轉換后的數據緩沖區是不是就和源數據緩沖區的大小不一樣?

這是肯定的。

所以轉換前后存放數據的緩沖區大小也就不一樣,所以這里我們需要為轉換后的數據緩沖區申請緩沖區大小,同時需要對申請以后得到的轉換后的數據緩沖區進行一定格式的划分。

這個過程我們由下面這個函數來完成:

uint8_t *out_buffer=(uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P,
                      screen_w,screen_h));

申請轉換以后的數據緩沖區大小。
avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P,
            screen_w,screen_h);

對轉換后存放數據的數據緩沖區,進行按格式存放划分。

 

---------------------------------------------------------------------------------------------

其中查找資料的過程中,記錄下資料的出處:

1)SDL實現overlay方式雙屏顯示的應用流程分析(thinkvd開發日志)

  http://blog.163.com/tfn2008@yeah/blog/static/110321319201275111614200/

2) 播放器(一)-FFMPEG-SDL-MFC-本地文件

  http://blog.csdn.net/zhuweigangzwg/article/details/17223841

3)SDL2.0在mfc窗口中顯示yuv的一種方法  

  http://airmanisvip.blog.163.com/blog/static/18058158201261015741216/ 

4)如何基於FFMPEG和SDL寫一個少於1000行代碼的視頻播放器

  http://blog.csdn.net/love4mario/article/details/17652355

5)最簡單的基於FFMPEG+SDL的視頻播放器 ver2 (采用SDL2.0)

  http://blog.csdn.net/leixiaohua1020/article/details/38868499

6)SDL 2.0 API by Name

  https://wiki.libsdl.org/CategoryAPI

-----------------------------------------------------------------------------------------------

本文出自:http://www.cnblogs.com/lihaiping/p/4025138.html

個人的理解,如果有誤,請指正。望努力拍磚。

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM