C++程序設計及互動多媒體開發(fā) 課件 羅立宏 第8章 視頻與音頻;第9章 Cocos2d-X開發(fā)基礎_第1頁
C++程序設計及互動多媒體開發(fā) 課件 羅立宏 第8章 視頻與音頻;第9章 Cocos2d-X開發(fā)基礎_第2頁
C++程序設計及互動多媒體開發(fā) 課件 羅立宏 第8章 視頻與音頻;第9章 Cocos2d-X開發(fā)基礎_第3頁
C++程序設計及互動多媒體開發(fā) 課件 羅立宏 第8章 視頻與音頻;第9章 Cocos2d-X開發(fā)基礎_第4頁
C++程序設計及互動多媒體開發(fā) 課件 羅立宏 第8章 視頻與音頻;第9章 Cocos2d-X開發(fā)基礎_第5頁
已閱讀5頁,還剩215頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

C++程序設計第8章視頻與音頻第1節(jié)音視頻開發(fā)概述第2節(jié)DirectShow音視頻開發(fā)第3節(jié)SDL多媒體開發(fā)庫第4節(jié)FFMpeg音視頻開發(fā)(一)第5節(jié)FFMpeg音視頻開發(fā)(二)第1節(jié)音視頻開發(fā)概述音視頻開發(fā)應用領域常用的音視頻開發(fā)庫1.1音視頻開發(fā)應用領域音視頻播放軟件音視頻采集、錄制視頻監(jiān)控音視頻編輯、格式轉化軟件網絡直播數字電視1.2常用的音視頻開發(fā)庫(1)微軟:VideoForWindows(VFW)→DirectShow

→MediaFoundation特征:Windows系統(tǒng)適用

難度:★★常用的音視頻開發(fā)庫(2)FFMpeg全稱:FastForwardMovingpictureexpertgroup特征:

功能強大

格式覆蓋全

跨平臺

難度:★★★★官網:/常用的音視頻開發(fā)庫(3)VLC全稱:VideoLanClient特征:支持大多數音視頻格式

跨平臺官網:/常用的音視頻開發(fā)庫(4)Gstreamer全稱:VideoLanClient特征:支持大多數音視頻格式

跨平臺官網:/第2節(jié)DirectShow音視頻開發(fā)DirectShow簡介DirectShow構架最簡單的DirectShow播放器MFC圖形界面DirectShow2.1DirectShow簡介DirectShow(簡稱DShow)是一個Windows平臺上的流媒體框架,提供了高質量的多媒體流采集和回放功能。它支持多種多樣的媒體文件格式,包括ASF、MPEG、AVI、MP3和WAV等多種文件。VideoForWindows(VFW)→DirectShow

→MediaFoundation開發(fā)語言:C++2.2DirectShow架構DirectShow是一個開放性的應用框架,也是一套基于COM的編程接口。DirectShow使用一種交FilterGraph的模型來管理整個數據流的處理過程;參與數據處理的各個功能模塊叫做Filter;各個Filter在FilterGraph中按一定的順序連接成一條"流水線"協(xié)同工作。Filter,它是最基本的軟件構件,過濾器通常在多媒體流中執(zhí)行一個操作。各個Filter在FilterGraph中按一定的順序連接成一條"流水線"協(xié)同工作。DirectShow的Filter與各種設備進行通信并對它們進行相應的控制,這些設備包括本地文件系統(tǒng)、電視卡、視頻采集卡、VFW編解碼器、顯示器和聲卡。通過這種方式,DirectShow成功的隔離了應用程序和各種復雜設備。2.3最簡單的DirectShow播放器例8-1一個最簡單的基于DirectShow的播放器2.3.1流程1.最簡單的基于DirectShow的視頻播放器的流程圖如右圖CoInitialize()CoCreateInstance(…,pGraph)pGraph->QueryInterface(…,pControl)pGraph->QueryInterface(…,pEvent)pGraph->RenderFile(“xxxx.mp4”)pControl->Run()pEvent->WaitForCompletionCoUnInitialize()成員變量:IGraphBuilder*pGraph;IMediaControl*pControl;IMediaEvent*pEvent;2.3.1流程2.流程圖中涉及到幾個接口:IGraphBuilder:繼承自IFilterGraph,用于構建FilterGraph。相比于IFilterGraph來說IGraphBuilder提供了一些更加“智能”的方法,例如RenderFile()方法。IMediaControl:提供和播放控制有關的一些接口。IMediaEvent:用來處理FilterGraph發(fā)出的事件。流程3.流程圖中關鍵函數CoInitialize():初始化COM運行環(huán)境。CoCreateInstance(…,pGraph):用指定的類標識符創(chuàng)建一個Com對象。在該播放器中類標識符為“CLSID_FilterGraph”,用于創(chuàng)建IGraphBuilder。pGraph->QueryInterface(…,pControl):通過QueryInterface()查詢某個組件是否支持某個特定的接口。在這里查詢IMediaControl接口。pGraph->QueryInterface(…,pEvent):同上。在這里查詢IMediaEvent接口。pGraph->RenderFile("xxxx.mp4"):為指定的文件智能的構建一個FilterGraph。pControl->Run():開始運行FilterGraph中的所有Filter。pEvent->WaitForCompletion():等待FilterGraph處理完所有數據。CoUninitialize():釋放CoInitialize()初始化的COM運行環(huán)境。2.3.2步驟啟動VisualStudio(如VS2022),創(chuàng)建一個控制臺項目。項目名稱可取為SimpleDShowPlayer把cpp文件中的代碼替換成以下代碼:#include<DShow.h>#pragmacomment(lib,“strmiids.lib”)//使用DirectShow需要這個庫文件//請嘗試去掉這句,而用這種方法替代:菜單“項目--***屬性”,彈出對話框中找//到“配置屬性--鏈接器--輸入--附加依賴項”,添加“strmiids.lib”。具有同樣效果//用宏定義定義釋放的過程#defineSAFE_RELEASE(filter){if(NULL!=filter){filter->Release();filter=NULL;}}intmain(){IGraphBuilder*pGraphBuilder=NULL; //Filtergraph管理器

IMediaControl*pMediaControl=NULL; //控制視頻/音頻的播放、暫停與停止

IMediaEvent*pMediaEvent=NULL; //捕獲播放過程中的事件CoInitializeEx(NULL,COINIT_APARTMENTTHREADED);//初始化COM運行環(huán)境

//創(chuàng)建Filtergraph管理器對象HRESULThr=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER,IID_IGraphBuilder,(void**)&pGraphBuilder);if(FAILED(hr)){printf("CoCreateInstanceFailed!\n");goto__exit;}

//獲取IMediaControl接口hr=pGraphBuilder->QueryInterface(IID_IMediaControl,(void**)&pMediaControl);if(FAILED(hr)){printf("QueryMediaControlInterfaceFailed!\n");goto__exit;}

//獲取IMediaEvent接口hr=pGraphBuilder->QueryInterface(IID_IMediaEvent,(void**)&pMediaEvent);if(FAILED(hr)){printf("QueryMediaEventInterfaceFailed!\n");goto__exit;}

//打開視頻文件

hr=pGraphBuilder->RenderFile(L“..\\..\\VideoForTest\\Megamind.avi”,NULL);//可改路徑文件

if(FAILED(hr)){printf("RenderFileFailed!\n");goto__exit;}

//視頻播放hr=pMediaControl->Run();if(FAILED(hr)){printf("RunFailed!\n");goto__exit;}{longcode=0;hr=pMediaEvent->WaitForCompletion(INFINITE,&code);//等待視頻播放完畢

if(FAILED(hr)){printf("WaitForCompletionFailed!\n");goto__exit;}}__exit:

//清理變量回收內存SAFE_RELEASE(pMediaEvent);SAFE_RELEASE(pMediaControl);SAFE_RELEASE(pGraphBuilder);CoUninitialize();system("pause");return0;}編譯,運行--OK!2.4圖形界面DirectShow播放器例8-2一個基于MFC圖形界面的DirectShow的播放器參看課件提供的代碼自行學習,課堂上不詳細講述了。第3節(jié)SDL多媒體開發(fā)庫SDL簡介SDL開發(fā)設置SDL顯示流程SDL顯示圖片SDL事件驅動SDL鍵盤事件3.1SDL簡介SDL是SimpleDirectMediaLayer(簡易直控媒體層)的縮寫。它是一個跨平臺的多媒體庫,以用于直接控制底層的多媒體硬件的接口。這些多媒體功能包括了音頻、鍵盤和鼠標(事件)、游戲搖桿等。其最為重要的是提供了2D圖形幀緩沖(framebuffer)的接口,以及為OpenGL與各種操作系統(tǒng)之間提供了統(tǒng)-的標準接口以實現(xiàn)3D圖形。從這些屬性我們可以看出,SDL基本上可以認為是為以電腦游戲為核心開發(fā)的多媒體庫。C語言寫成,可在C++下工作官網:/最新版本:SDL2.0第4節(jié)FFMpeg將會用到SDL顯示視頻圖像,因此有必要先學習一下SDL。3.2SDL開發(fā)設置從官網下載最新的SDL2(/download-2.0.php),或者從課件資料中拷貝SDL2,解壓,放到某個方便記憶的目錄位置,例如“D:\SDL2\SDL2”SDL開發(fā)設置以后在每一個需要SDL的VisualStudtio工程中,菜單“項目–xxx屬性”,在彈出的屬性頁對話框中,在配置屬性樹中點“VC++目錄”,然后在“包含目錄”中添加SDL中的include目錄,如“D:\SDL2\SDL2\include”;再在“庫目錄”中添加SDL中的lib目錄,如“D:\SDL2\SDL2\lib\x64”。請根據VS中編譯的版本是x64還是x86來正確選擇lib下的x64或x86目錄。SDL開發(fā)設置再在“鏈接器-輸入-附加依賴項”中添加“SDL2.lib;SDL2main.lib;”。把lib目錄下(注意正確選擇x64或x86目錄)的SDL2.dll拷貝工程目錄下3.3SDL2.0顯示流程SDL_Init()SDL_CreateWindow()SDL_CreateRenderer()SDL_CreateTexture()SDL_UpdateTexture()SDL_RenderCopy()SDL_RenderPresent()Decode()YUV/RGB顯示主要函數簡介SDL_Init():初始化SDL系統(tǒng)SDL_CreateWindow():創(chuàng)建窗口SDL_WindowSDL_CreateRenderer():創(chuàng)建渲染器SDL_RendererSDL_CreateTexture():創(chuàng)建紋理SDL_TextureSDL_UpdateTexture():設置紋理的數據SDL_RenderCopy():將紋理的數據拷貝給渲染器SDL_RenderPresent():顯示SDL_Delay():工具函數,用于延時。SDL_Quit():退出SDL系統(tǒng)3.4SDL顯示圖片例8-3使用SDL顯示一張bmp圖片步驟:(1)打開VS,創(chuàng)建一個控制臺工程。工程起名為“HelloSDL”(2)按照3.2節(jié)設置好SDL開發(fā)環(huán)境(3)打開HelloSDL.cpp,把原來的代碼替換成以下代碼:#defineSDL_MAIN_HANDLED#include<iostream>#include<SDL.h>usingnamespacestd;intmain(){//初始化SDLif(SDL_Init(SDL_INIT_VIDEO)){cout<<"SDL_InitError:"<<SDL_GetError()<<endl;return-1;}//創(chuàng)建窗口

SDL_Window*pWin=SDL_CreateWindow("HelloWorld!",100,100,640,480,SDL_WINDOW_SHOWN);if(pWin==nullptr){cout<<"SDL_CreateWindowError:"<<SDL_GetError()<<endl;return-1;}

//創(chuàng)建渲染器

SDL_Renderer*pRen=SDL_CreateRenderer(pWin,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);//第三個參數是:開啟硬件//加速|控制屏幕幀率不超過電腦默認刷新率,如果改為0//的話CPU和GPU占用率會比較高

if(pRen==nullptr){SDL_DestroyWindow(pWin);cout<<"SDL_CreateRenderError:"<<SDL_GetError()<<endl;SDL_Quit();return-1;}

//加載圖片

stringsImagePath="HelloSDL.bmp";SDL_Surface*pBmp=SDL_LoadBMP(sImagePath.c_str());if(pBmp==nullptr){SDL_DestroyRenderer(pRen);SDL_DestroyWindow(pWin);cout<<"SDL_LoadBMPError:"<<SDL_GetError()<<endl;SDL_Quit();return1;}

//創(chuàng)建紋理

SDL_Texture*pTex=SDL_CreateTextureFromSurface(pRen,pBmp);SDL_FreeSurface(pBmp);if(pTex==nullptr){SDL_DestroyRenderer(pRen);SDL_DestroyWindow(pWin);cout<<"SDL_CreateTextureFromSurfaceError:"<<SDL_GetError()<<endl;SDL_Quit();return1;}//渲染

for(inti=0;i<5;++i){SDL_RenderClear(pRen);SDL_RenderCopy(pRen,pTex,NULL,NULL);SDL_RenderPresent(pRen);SDL_Delay(500);}system("pause");

//釋放資源

SDL_DestroyTexture(pTex);SDL_DestroyRenderer(pRen);SDL_DestroyWindow(pWin);SDL_Quit();return0;}(4)把程序中要用到的HelloSDL.bmp

拷貝到工程目錄(5)編譯,運行--OK!單純的SDL只能顯示bmp格式的圖片(上例8-3中的SDL_LoadBMP()函數)。如果需要顯示其他的格式圖片的話,如jpg、png等,需要借助SDL提供的其他工具SDL_Image,下載地址:/projects/SDL_image/?;蛘邚恼n件資料中拷貝SDL2_Image,解壓,放到某個方便記憶的目錄位置,例如“D:\SDL2\SDL2_Image”。例8_4使用SDL顯示jpg、png圖片課件對本例講解從略,有興趣請參閱

課程資料中例子源碼。3.5SDL事件驅動在例8-3中,我們采用SDL_Delay()函數來使窗口暫停。循環(huán)中一直延時等待,用戶都不能操作,光標為等待圖標,最小化、關閉等按鈕也用不了,這不好。好的程序應該能與用戶交互。為此,我們來學習SDL事件。例8-5支持SDL事件驅動位圖顯示程序步驟:(1)打開VisualStudio,創(chuàng)建一個控制臺程序,工程名為“SDLEvent”(2)按照3.2節(jié)設置好SDL開發(fā)環(huán)境(3)打開SDLEvent.cpp,把原來的代碼替換成以下代碼:#defineSDL_MAIN_HANDLED#include"SDL.h"#include<iostream>usingnamespacestd;constintWindow_WIDTH=640,Window_HEIGHT=480;SDL_Window*window=NULL;SDL_Renderer*render=NULL;SDL_Texture*tex=NULL;boolInit(){window=SDL_CreateWindow(“MyfirstSDL",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,Window_WIDTH,Window_HEIGHT,SDL_WINDOW_SHOWN);if(window==NULL){cout<<"creatwindowerror\n";returnfalse;}render=SDL_CreateRenderer(window,-1,0);if(render==NULL){cout<<"creatrendererror\n";returnfalse;}returntrue;}boolLOAD_Image(){SDL_Surface*hello=NULL;hello=SDL_LoadBMP("HelloSDL.bmp");if(hello==NULL){cout<<"loadbmperror\n";returnfalse;}tex=SDL_CreateTextureFromSurface(render,hello);SDL_FreeSurface(hello);if(tex==NULL){cout<<"creatsurfacetexerror\n";returnfalse;}returntrue;}boolPut_Image(){SDL_RenderCopy(render,tex,NULL,NULL);SDL_RenderPresent(render);SDL_Delay(1000);returntrue;}voidQuit(){SDL_DestroyWindow(window);SDL_DestroyRenderer(render);SDL_DestroyTexture(tex);SDL_Quit();}intmain(intargc,char*args[]){if(Init()){if(LOAD_Image()){Put_Image();boolquit=false;SDL_Eventevent;while(!quit){while(SDL_PollEvent(&event)!=0){if(event.type==SDL_QUIT)quit=true;}}}Quit();}system("pause");return0;}SDL_PollEvent()函數:

查詢事件隊列,按先進先出的原則取出事件。例如:調用SDL_PollEvent()前隊列為:調用SDL_PollEvent()后隊列為:事件SDL_KeyboardeventSDL_MousementioneventSDL_joybutttonevent事件SDL_MousementioneventSDL_joybutttoneventSDL_Keyboardevent被Poll出來了(4)把程序中要用到的HelloSDL.bmp拷貝到工程目錄(5)編譯,運行。雖然顯示的圖片跟例8-3一樣,但現(xiàn)在光標正常,窗口也能拖

動,最小化和關閉按鈕都可以使用了。--OK!3.6SDL鍵盤事件我們以鍵盤事件為例,再深入一點學習SDL事件驅動例8-6使用按鍵WASD控制窗口中圖形的運動。步驟:(1)打開VisualStudio,創(chuàng)建一個控制臺程序,工程名為“SDLKeyboard”(2)按照3.2節(jié)設置好SDL開發(fā)環(huán)境(3)打開SDLEvent.cpp,把原來的代碼替換成以下代碼:#defineSDL_MAIN_HANDLED#include"SDL.h"#include"SDL_image.h"#include<iostream>#include<conio.h>usingnamespacestd;constintWIN_WIDTH=600;constintWIN_HEIGHT=450;SDL_Window*pWindow=NULL;//SDL窗口SDL_Renderer*pRender=NULL;//SDL渲染器SDL_Texture*pTex=NULL;//SDL紋理SDL_RectrtMoveRect;//鍵盤控制的矩形圖形boolInit()//相關變量初始化{rtMoveRect.w=35;rtMoveRect.h=35;rtMoveRect.x=(WIN_WIDTH-rtMoveRect.w)/2;//一開始時居中

rtMoveRect.y=(WIN_HEIGHT-rtMoveRect.h)/2;//一開始時居中

pWindow=SDL_CreateWindow(“SDLKeyboardEvent”,SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,WIN_WIDTH,WIN_HEIGHT,SDL_WINDOW_SHOWN);//創(chuàng)建窗口if(pWindow==NULL)

{cout<<"creatwindowerror\n";returnfalse;}//創(chuàng)建SDL渲染器pRender=SDL_CreateRenderer(pWindow,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);if(pRender==NULL){cout<<"creatrendererror\n";returnfalse;}returntrue;}//程序退出及相關清理工作voidQuit(){SDL_DestroyWindow(pWindow);SDL_DestroyRenderer(pRender);SDL_DestroyTexture(pTex);SDL_Quit();}intmain(intargc,char*args[]){if(Init()){charc=0;//用一個字符變量記錄當前按下的鍵boolquit=false;SDL_Eventevent;//SDL事件while(!quit){SDL_SetRenderDrawColor(pRender,0,0,0,255);//設置清屏顏色SDL_RenderClear(pRender);//清屏while(SDL_PollEvent(&event)!=0)//Poll一下SDL事件列表,如取出有事件,進入循環(huán){if(event.type==SDL_KEYDOWN){//如果取出的事件是鍵盤事件

c=event.key.keysym.sym;//記錄鍵值}if(event.type==SDL_QUIT){//如果取出的事件是退出事件quit=true;cout<<"quit\n";}}if(c==‘w’){//如果按鍵值是wrtMoveRect.y-=5;//矩形上移5像素if(rtMoveRect.y<=0){//如果碰到窗口上邊則反彈c='s';continue;}}if(c=='a'){//如果按鍵值是artMoveRect.x-=5;//矩形左移5像素if(rtMoveRect.x<=0){//如果碰到窗口左邊則反彈c='d';continue;}}if(c=='s'){//如果按鍵值是srtMoveRect.y+=5;//矩形下移5像素if(rtMoveRect.y>=WIN_HEIGHT-rtMoveRect.h){//如果碰到窗口下邊則反彈c='w';continue;}}if(c=='d'){//如果按鍵值是drtMoveRect.x+=5;//矩形右移5像素if(rtMoveRect.x>=WIN_WIDTH-rtMoveRect.w){//如果碰到窗口右邊則反彈c='a';continue;}}SDL_SetRenderDrawColor(pRender,255,0,0,255);//設置畫圖填充顏色SDL_RenderFillRect(pRender,&rtMoveRect);//根據當前位置畫填充矩形SDL_RenderPresent(pRender);//顯示cout<<c<<endl;}Quit();}system("pause");return0;}(5)編譯,運行。--OK!第4節(jié)FFMpeg音視頻開發(fā)(一)FFMpeg簡介FFMpeg開發(fā)設置FFMpeg流程最簡單的FFMpeg視頻播放器最簡單的FFMpeg音頻播放器音視頻同步4.1FFMpeg簡介FFmpeg是一套可以用來記錄、轉換數字音頻、視頻,并能將其轉化為流的開源庫。采用LGPL或GPL許可證。它提供了錄制、轉換以及流化音視頻的完整解決方案。它包含了非常先進的音頻/視頻編解碼庫libavcodec,并具有跨平臺性、高可移植性和高編解碼質量。C語言寫成,可在C++下工作官網:/FFMpeg直接使用FFmpeg不僅是一套開源程序開發(fā)庫。還是一組功能強大的應用程序。在控制臺中進入下載解壓好的FFMpeg目錄,嘗試使用ffmpeg.exe和ffplay.exe來轉換視頻格式和播放視頻。4.2FFMpeg開發(fā)設置從網上下載,或從課件資料中獲取FFMpeg,解壓,放到某個方便記憶的目錄位置,例如“D:\FFMpeg”FFMpeg開發(fā)設置以后在每一個需要FFMpeg的VisualStudtio工程中,菜單“項目–xxx屬性”,在彈出的屬性頁對話框中,在配置屬性樹中點“VC++目錄”,然后在“包含目錄”中添加FFMpeg中的include目錄,如“D:\FFMpeg\include”;再在“庫目錄”中添加FFMpeg中的lib目錄,如“D:\FFMpeg\lib\win64”。請根據VS中編譯的版本是win64還是win86來正確選擇lib下的x64或x86目錄。FFMpeg開發(fā)設置再在“鏈接器-輸入-附加依賴項”中添加“avcodec.lib;avformat.lib;avutil.lib;avdevice.lib;avfilter.lib;swresample.lib;swscale.lib;”。把lib目錄下(注意正確選擇win64或win86目錄)的所有dll拷貝到工程目錄下4.3最簡單的FFMpeg視頻播放器av_register_all()avformat_open_input()av_find_stream_info()avcodec_find_decoder()avcodec_open2()av_read_frame()AVPacket結束開始GetPacket?VideoPacket?truefalseavcodec_decode_video2truefalse例8-7一個最簡單的基于FFMpeg的視頻播放器4.3.1流程1.最簡單的基于FFMpeg的視頻播放器的流程圖如左圖4.3.2主要函數簡介av_register_all():初始化所有組件avformat_open_input():該函數用于打開多媒體數據av_find_stream_info():獲取視頻流信息avcodec_find_decoder():查找解碼器avcodec_open():打開解碼器av_read_frame():讀取視頻中一幀數據avcodec_decode_video2():解碼一幀視頻數據4.3.3步驟(1)啟動VisualStudio(如VS2022),創(chuàng)建一個控制臺項目。項目名稱可取為SimpleFfmVideo(2)按照3.2節(jié)設置好SDL開發(fā)環(huán)境(3)按照4.2節(jié)設置好FFMpeg開發(fā)環(huán)境(4)打開SimpleFfmVideo.cpp,替換寫入如下代碼:點擊打開代碼常見編譯錯誤解決辦法步驟(5)編譯,運行--OK!4.4最簡單的FFMpeg音頻播放器av_register_all()avformat_open_input()av_find_stream_info()avcodec_find_decoder()avcodec_open2()av_read_frame()AVPacket結束開始GetPacket?AudioPacket?truefalseavcodec_decode_audio4truefalse上小節(jié)例8-7沒有聲音例8-8一個最簡單的基于FFMpeg的音頻播放器4.4.1流程1.最簡單的基于FFMpeg的音頻播放器的流程圖如左圖4.4.2主要函數簡介av_register_all():初始化所有組件avformat_open_input():該函數用于打開多媒體數據av_find_stream_info():獲取視頻流信息avcodec_find_decoder():查找解碼器avcodec_open():打開解碼器av_read_frame():讀取視頻中一幀數據avcodec_decode_audio4():解碼一幀音頻數據4.4.3步驟(1)啟動VisualStudio(如VS2022),創(chuàng)建一個控制臺項目。項目名稱可取為SimpleFfmAudio(2)按照3.2節(jié)設置好SDL開發(fā)環(huán)境(3)按照4.2節(jié)設置好FFMpeg開發(fā)環(huán)境(4)打開SimpleFfmAudio.cpp,替換寫入如下代碼:點擊打開代碼常見編譯錯誤解決辦法4.5音視頻同步例8-7只播放視頻圖像,例8-8只播放聲音,是否兩個程序整合起來就是完美的帶聲音的視頻播放器了?——沒那么簡單,還需要音視頻同步。解協(xié)議數據(文件/流媒體)封裝格式數據(avi/mp4…)解封裝音頻解碼音頻壓縮數據視頻壓縮數據視頻解碼音頻原始數據視頻原始數據音視頻同步音視頻輸出設備4.5.1I、P、B幀視頻的播放過程可以簡單理解為一幀一幀的畫面按照時間順序呈現(xiàn)出來的過程。但是在實際應用中,并不是每一幀都是完整的畫面,因為如果每一幀畫面都是完整的圖片,那么一個視頻的體積就會很大,這樣對于網絡傳輸或者視頻數據存儲來說成本太高,所以通常會對視頻流中的一部分畫面進行壓縮(編碼)處理。由于壓縮處理的方式不同,視頻中的畫面幀就分為了不同的類別,其中包括:I幀、P幀、B幀。I、P、B幀I幀(Intracodedframes):I幀圖像采用幀內編碼方式,即只利用了單幀圖像內的空間相關性,而沒有利用時間相關性。I幀使用幀內壓縮,不使用運動補償,由于I幀不依賴其它幀,所以是隨機存取的入點,同時是解碼的基準幀。I幀主要用于接收機的初始化和信道的獲取,以及節(jié)目的切換和插入,I幀圖像的壓縮倍數相對較低。I幀圖像是周期性出現(xiàn)在圖像序列中的,出現(xiàn)頻率可由編碼器選擇。P幀(Predictedframes):P幀和B幀圖像采用幀間編碼方式,即同時利用了空間和時間上的相關性。P幀圖像只采用前向時間預測,可以提高壓縮效率和圖像質量。P幀圖像中可以包含幀內編碼的部分,即P幀中的每一個宏塊可以是前向預測,也可以是幀內編碼。B幀(Bi-directionalpredictedframes):B幀圖像采用雙向時間預測,可以大大提高壓縮倍數。值得注意的是,由于B幀圖像采用了未來幀作為參考,因此MPEG-2編碼碼流中圖像幀的傳輸順序和顯示順序是不同的。4.5.2DTS和PTS一個I幀可以不依賴其他幀就解碼出一幅完整的圖像,而P幀、B幀不行。P幀需要依賴視頻流中排在它前面的幀才能解碼出圖像。B幀則需要依賴視頻流中排在它前面或后面的幀才能解碼出圖像。這就帶來一個問題:在視頻流中,先到來的B幀無法立即解碼,需要等待它依賴的后面的I、P幀先解碼完成,這樣一來播放時間與解碼時間不一致了,順序打亂了,那這些幀該如何播放呢?這時就需要我們來了解另外兩個概念:DTS和PTS。DTS(DecodingTimeStamp):即解碼時間戳,這個時間戳的意義在于告訴播放器該在什么時候解碼這一幀的數據。PTS(PresentationTimeStamp):即顯示時間戳,這個時間戳用來告訴播放器該在什么時候顯示這一幀的數據。DTS和PTS當視頻流中沒有B幀時,通常DTS和PTS的順序是一致的。但如果有B幀時,就回到了我們前面說的問題:解碼順序和播放順序不一致了。比如一個視頻中,幀的顯示順序是:IBBP,現(xiàn)在我們需要在解碼B幀時知道P幀中信息,因此這幾幀在視頻流中的順序可能是:IPBB,這時候就體現(xiàn)出每幀都有DTS和PTS的作用了。DTS告訴我們該按什么順序解碼這幾幀圖像,PTS告訴我們該按什么順序顯示這幾幀圖像。順序大概如下:PTS:1423DTS:1234Stream:IPBB4.5.3時間基PST和DTS的單位是時間基(time_base)。時間基表示的就是每個刻度是多少秒。舉例1:例如如果把1秒分為25等份,你可以理解就是一把尺,那么每一格表示的就是1/25秒。此時的time_base={1,25}舉例2:例如如果你是把1秒分成90000份,每一個刻度就是1/90000秒,PST和DTS的值就是占多少個時間刻度(占多少個格子)。它的單位不是秒,而是時間刻度。4.5.4音視頻的同步要實現(xiàn)音視頻同步,通常需要選擇一個參考時鐘,參考時鐘上的時間是線性遞增的,編碼音視頻流時依據參考時鐘上的時間給每幀數據打上時間戳。在播放時,讀取數據幀上的時間戳,同時參考當前參考時鐘上的時間來安排播放。這里的說的時間戳就是我們前面說的PTS。實踐中,我們可以選擇的方法有:(1)同步視頻到音頻(2)同步音頻到視頻(3)同步音頻和視頻到外部時鐘。第5節(jié)FFMpeg音視頻開發(fā)(二)ffplay播放器程序GUI界面的FFMpeg播放器基于FFMpeg的DirectShow解碼器——LAVFiltersFFMpeg.exe錄音與錄像錄像與錄音程序開發(fā)5.1ffplay程序分析FFMpeg提供了一個開源的ffplay程序。這是一個使用了FFMpeg和SDL庫的、簡單的可移植的媒體播放器。打開例8-9,運行并閱讀代碼。例8-9是加了詳細注釋的VS版本的ffplay。例8-9ffplay程序運行并閱讀點擊打開代碼5.1.1ffplay程序功能指定視頻影片文件,可正常播放視頻和聲音。-在調試時,可在項目設置中(菜單“項目—項目屬性”—配置屬性—調試—命令參數)輸入視頻路徑和文件名;-在命令行運行時,可在ffplay_vs命令中直接跟視頻路徑和文件名。

鼠標功能-左鍵雙擊:切換全屏-右鍵左右拖動:快進快退鍵盤功能-F鍵:切換全屏-P鍵或空格鍵:暫停和繼續(xù)播放-M鍵:切換靜音-*鍵(小鍵盤上的乘號鍵,非Shift+8鍵)和0鍵:增加音量ffplay程序功能鍵盤功能(續(xù)):-/鍵(小鍵盤上的除號鍵,非主鍵盤中的斜杠鍵)和9鍵:減小音量-S鍵:播放下一幀,即一幀幀播放-W鍵:切換顯示模式-→鍵:前進10秒-←鍵:后退10秒-↑鍵:前進60秒-↓鍵:后退60秒-PageUp鍵:前進1章-PageDn鍵:后退1章-Q鍵或ESC鍵:退出5.1.2ffplay程序結構見下頁圖5.1.3ffplay流程圖見下頁圖其中音視頻同步在video_refresh()中(右上角流程)5.2GUI界面的FFMpeg播放器例8-10利用前面學到的知識,開發(fā)一個形如例8-2的基于MFC圖形界面的FFMpeg音視頻播放器。步驟

準備(1)打開VS,創(chuàng)建一個基于對話框的MFC工程。起名為“FFmGuiPlayer”(2)按照3.2節(jié)設置好SDL開發(fā)環(huán)境(3)按照4.2節(jié)設置好FFMpeg開發(fā)環(huán)境(4)制作一張bmp圖片,用作播放器的封面。放在工程的res目錄中。并把圖片導入到資源中,ID起名為“IDB_PLAYERCOVER”步驟

界面(5)在對話框上創(chuàng)建出各個控件。

若需要調整控件間的

前后順序,按Ctrl+D調整

(6)對其中3個控件添加變量??丶蘒D用途備注添加變量PictureControlIDC_BCKGRD視頻背后的灰色背景Type:RectanglePictureControlIDC_SCREEN顯示視頻Type:BitmapImage:IDB_PLAYERCOVER大小位置與背景控件IDC_BCKGRD一樣,在背景控件上面。視頻播放時我們讓屏幕控件IDC_SCREEN根據視頻長寬比而變化,而背景控件始終不變??丶蘒D用途備注添加變量SliderControlIDC_SLD_PROGRESS顯示播放進度類型:CSliderCtrl變量名:m_sldProgressEditControlIDC_EDT_CURRENT顯示當前播放時間ReadOnly:trueBorder:false類型:CEdit變量名:m_edtCurrentEditControlIDC_EDT_DURATION顯示視頻總時間ReadOnly:trueBorder:false類型:CEdit變量名:m_edtDurationStaticTextIDC_STA_SLASH分隔一下上兩個控件Caption:“/”ButtonIDC_BTN_OPEN打開并播放視頻文件Caption:“打開”ButtonIDC_BTN_PLAYPAUSE暫停/繼續(xù)播放視頻Caption:“暫停”ButtonIDC_BTN_STOP停止并關閉視頻Caption:“停止”ButtonIDC_BTN_FULLSCREEN全屏播放Caption:“全屏”步驟

代碼移植思路:既然ffplay是一個功能完整的播放器(盡管沒有圖形界面),我們可以考慮把ffplay的代碼移植到我們的FFmGuiPlayer工程中。(7)把ffplay.c從例8-9ffplay工程中拷貝到工程目錄中,改名為ffplayCore.c,并將其添加到工程中(菜單“項目–添加現(xiàn)有項”–選中ffplayCore.c–按鈕“添加”)(8)新建一個ffplayCore.h頭文件,把原來ffplayCore.c中opt_add_vfilter()函數及前一句#ifCONFIG_AVFILTER之前的所有代碼,剪貼到ffplayCore.h中。即把原ffplayCore.c分拆成兩個文件。并將ffplayCore.h添加到工程中。(9)把config.h、cmdutils.h、cmdutils.c也從例8-9ffplay工程中拷貝到本工程目錄中,并將其添加到工程中。編譯,解決編譯錯誤。若有編譯錯誤,參見這里(10)把ffplayCore.c中的main()函數改為播放線程函數//刪/*Calledfromthemain*///刪intmain(intargc,char**argv)//此函數替代main,被對話框調用intffmfc_play(void*lpParam){//以下代碼移動到新函數ffinit()中//intflags;//VideoState*is;//…...此處略寫多行……//av_init_packet(&flush_pkt);//flush_pkt.data=(uint8_t*)&flush_pkt;//以上代碼移動到新函數ffinit()中//模擬控制臺argv參數的兩個變量調用parse_options(),里面會做一些視頻播放的準備工作charsExeName[]="FFmGuiPlayer.exe";charsArgv2[256];char*pSimArgv[2];pSimArgv[0]=sExeName;strcpy_s(sArgv2,256,g_MoviePlayInfo.pPathFileName);pSimArgv[1]=sArgv2;parse_options(NULL,2,pSimArgv,options,opt_input_file);//模擬控制臺argv參數的兩個變量調

//用parse_options()本PPT中:草綠色代碼:注釋深綠色代碼:要刪除的代碼黃色代碼:新添加的代碼白色代碼:不變的代碼

//SDL創(chuàng)建窗口//刪if(!display_disable){//刪intflags=SDL_WINDOW_HIDDEN;//刪if(alwaysontop)//刪……略寫多行……//刪else//刪flags|=SDL_WINDOW_RESIZABLE;//刪window=SDL_CreateWindow(program_name,SDL_WINDOWPOS_UNDEFINED,//刪SDL_WINDOWPOS_UNDEFINED,default_width,default_height,flags);

window=g_MoviePlayInfo.pSdlWindow;SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY,"linear");//設置窗口縮放渲染質量if(window){renderer=SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);if(!renderer){av_log(NULL,AV_LOG_WARNING,"Failedtoinitializeahardwareacceleratedrenderer:%s\n",SDL_GetError());renderer=SDL_CreateRenderer(window,-1,0);}if(renderer){if(!SDL_GetRendererInfo(renderer,&renderer_info))av_log(NULL,AV_LOG_VERBOSE,"Initialized%srenderer.\n",renderer_);}}if(!window||!renderer||!renderer_info.num_texture_formats){av_log(NULL,AV_LOG_FATAL,"Failedtocreatewindoworrenderer:%s",SDL_GetError());//刪do_exit(NULL);return-1;}//刪}//啟動讀取數據線程//刪is=stream_open(input_filename,file_iformat);g_is=stream_open(g_MoviePlayInfo.pPathFileName,file_iformat);//刪if(!is){if(!g_is){av_log(NULL,AV_LOG_FATAL,"FailedtoinitializeVideoState!\n");//刪do_exit(NULL);return-1;}g_MoviePlayInfo.eState=MS_PLAY;//進入事件消息循環(huán)//刪event_loop(is);event_loop(g_is);//播放線程要退出時才會走到這里,例如影片被關閉停止

g_is=NULL;return0;}(11)把ffplayCore.c中的main()的前面增加一個ffinit()函數//FFMpeg和SDL初始化intffinit(){

//以下代碼從原來main()中移來,移來后還需修改intflags;

//刪VideoState*is;//這個is要換成全局變量g_isinit_dynload();

//設置日志級別av_log_set_flags(AV_LOG_SKIP_REPEATED);

//刪parse_loglevel(argc,argv,options);

//FFmpeg初始化#ifCONFIG_AVDEVICEavdevice_register_all();#endifavformat_network_init();

//初始化cmdutils(cmdutilities)init_opts();

//處理系統(tǒng)進程中斷和進程終止的信號。這種方法多用在Linux和Unix系統(tǒng)。signal(SIGINT,sigterm_handler);/*Interrupt(ANSI).signal(SIGTERM,sigterm_handler);/*Termination(ANSI).//刪show_banner(argc,argv,options);//刪parse_options(NULL,argc,argv,options,opt_input_file);//……多行……//刪exit(1);//刪}//SDL初始化if(display_disable){video_disable=1;}flags=SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER;if(audio_disable)flags&=~SDL_INIT_AUDIO;else{if(!SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"))SDL_setenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE","1",1);}if(display_disable)flags&=~SDL_INIT_VIDEO;////初始化SDLif(SDL_Init(flags)){av_log(NULL,AV_LOG_FATAL,"CouldnotinitializeSDL-%s\n",SDL_GetError());av_log(NULL,AV_LOG_FATAL,"(DidyousettheDISPLAYvariable?)\n");//刪exit(1);return1;}SDL_EventState(SDL_SYSWMEVENT,SDL_IGNORE);//忽略SDL_SYSWMEVENT事件SDL_EventState(SDL_USEREVENT,SDL_IGNORE);//忽略SDL_USEREVENT事件//flush_pkt(用于沖刷解碼器,即把解碼器中的殘余數據拿去渲染完)初始化av_init_packet(&flush_pkt);flush_pkt.data=(uint8_t*)&flush_pkt;return0;}(12)在ffplayCore.c頭部添加以下全局變量#include"ffplayCore.h"structMoviePlayInfog_MoviePlayInfo;//對話框類和ffplayCore.c共同使用的全局變量VideoState*g_is;//原來main()中的is改為此全局變量(13)把ffplayCore.h

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論