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

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

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

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

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

功能強(qiáng)大

格式覆蓋全

跨平臺(tái)

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

跨平臺(tái)官網(wǎng):/常用的音視頻開發(fā)庫(kù)(4)Gstreamer全稱:VideoLanClient特征:支持大多數(shù)音視頻格式

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

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

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

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

//創(chuàng)建Filtergraph管理器對(duì)象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:

//清理變量回收內(nèi)存SAFE_RELEASE(pMediaEvent);SAFE_RELEASE(pMediaControl);SAFE_RELEASE(pGraphBuilder);CoUninitialize();system("pause");return0;}編譯,運(yùn)行--OK!2.4圖形界面DirectShow播放器例8-2一個(gè)基于MFC圖形界面的DirectShow的播放器參看課件提供的代碼自行學(xué)習(xí),課堂上不詳細(xì)講述了。第3節(jié)SDL多媒體開發(fā)庫(kù)SDL簡(jiǎn)介SDL開發(fā)設(shè)置SDL顯示流程SDL顯示圖片SDL事件驅(qū)動(dòng)SDL鍵盤事件3.1SDL簡(jiǎn)介SDL是SimpleDirectMediaLayer(簡(jiǎn)易直控媒體層)的縮寫。它是一個(gè)跨平臺(tái)的多媒體庫(kù),以用于直接控制底層的多媒體硬件的接口。這些多媒體功能包括了音頻、鍵盤和鼠標(biāo)(事件)、游戲搖桿等。其最為重要的是提供了2D圖形幀緩沖(framebuffer)的接口,以及為OpenGL與各種操作系統(tǒng)之間提供了統(tǒng)-的標(biāo)準(zhǔn)接口以實(shí)現(xiàn)3D圖形。從這些屬性我們可以看出,SDL基本上可以認(rèn)為是為以電腦游戲?yàn)楹诵拈_發(fā)的多媒體庫(kù)。C語(yǔ)言寫成,可在C++下工作官網(wǎng):/最新版本:SDL2.0第4節(jié)FFMpeg將會(huì)用到SDL顯示視頻圖像,因此有必要先學(xué)習(xí)一下SDL。3.2SDL開發(fā)設(shè)置從官網(wǎng)下載最新的SDL2(/download-2.0.php),或者從課件資料中拷貝SDL2,解壓,放到某個(gè)方便記憶的目錄位置,例如“D:\SDL2\SDL2”SDL開發(fā)設(shè)置以后在每一個(gè)需要SDL的VisualStudtio工程中,菜單“項(xiàng)目–xxx屬性”,在彈出的屬性頁(yè)對(duì)話框中,在配置屬性樹中點(diǎn)“VC++目錄”,然后在“包含目錄”中添加SDL中的include目錄,如“D:\SDL2\SDL2\include”;再在“庫(kù)目錄”中添加SDL中的lib目錄,如“D:\SDL2\SDL2\lib\x64”。請(qǐng)根據(jù)VS中編譯的版本是x64還是x86來(lái)正確選擇lib下的x64或x86目錄。SDL開發(fā)設(shè)置再在“鏈接器-輸入-附加依賴項(xiàng)”中添加“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顯示主要函數(shù)簡(jiǎn)介SDL_Init():初始化SDL系統(tǒng)SDL_CreateWindow():創(chuàng)建窗口SDL_WindowSDL_CreateRenderer():創(chuàng)建渲染器SDL_RendererSDL_CreateTexture():創(chuàng)建紋理SDL_TextureSDL_UpdateTexture():設(shè)置紋理的數(shù)據(jù)SDL_RenderCopy():將紋理的數(shù)據(jù)拷貝給渲染器SDL_RenderPresent():顯示SDL_Delay():工具函數(shù),用于延時(shí)。SDL_Quit():退出SDL系統(tǒng)3.4SDL顯示圖片例8-3使用SDL顯示一張bmp圖片步驟:(1)打開VS,創(chuàng)建一個(gè)控制臺(tái)工程。工程起名為“HelloSDL”(2)按照3.2節(jié)設(shè)置好SDL開發(fā)環(huán)境(3)打開HelloSDL.cpp,把原來(lái)的代碼替換成以下代碼:#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);//第三個(gè)參數(shù)是:開啟硬件//加速|(zhì)控制屏幕幀率不超過(guò)電腦默認(rèn)刷新率,如果改為0//的話CPU和GPU占用率會(huì)比較高

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)編譯,運(yùn)行--OK!單純的SDL只能顯示bmp格式的圖片(上例8-3中的SDL_LoadBMP()函數(shù))。如果需要顯示其他的格式圖片的話,如jpg、png等,需要借助SDL提供的其他工具SDL_Image,下載地址:/projects/SDL_image/。或者從課件資料中拷貝SDL2_Image,解壓,放到某個(gè)方便記憶的目錄位置,例如“D:\SDL2\SDL2_Image”。例8_4使用SDL顯示jpg、png圖片課件對(duì)本例講解從略,有興趣請(qǐng)參閱

課程資料中例子源碼。3.5SDL事件驅(qū)動(dòng)在例8-3中,我們采用SDL_Delay()函數(shù)來(lái)使窗口暫停。循環(huán)中一直延時(shí)等待,用戶都不能操作,光標(biāo)為等待圖標(biāo),最小化、關(guān)閉等按鈕也用不了,這不好。好的程序應(yīng)該能與用戶交互。為此,我們來(lái)學(xué)習(xí)SDL事件。例8-5支持SDL事件驅(qū)動(dòng)位圖顯示程序步驟:(1)打開VisualStudio,創(chuàng)建一個(gè)控制臺(tái)程序,工程名為“SDLEvent”(2)按照3.2節(jié)設(shè)置好SDL開發(fā)環(huán)境(3)打開SDLEvent.cpp,把原來(lái)的代碼替換成以下代碼:#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()函數(shù):

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

動(dòng),最小化和關(guān)閉按鈕都可以使用了。--OK!3.6SDL鍵盤事件我們以鍵盤事件為例,再深入一點(diǎn)學(xué)習(xí)SDL事件驅(qū)動(dòng)例8-6使用按鍵WASD控制窗口中圖形的運(yùn)動(dòng)。步驟:(1)打開VisualStudio,創(chuàng)建一個(gè)控制臺(tái)程序,工程名為“SDLKeyboard”(2)按照3.2節(jié)設(shè)置好SDL開發(fā)環(huán)境(3)打開SDLEvent.cpp,把原來(lái)的代碼替換成以下代碼:#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()//相關(guān)變量初始化{rtMoveRect.w=35;rtMoveRect.h=35;rtMoveRect.x=(WIN_WIDTH-rtMoveRect.w)/2;//一開始時(shí)居中

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

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

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

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

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

若需要調(diào)整控件間的

前后順序,按Ctrl+D調(diào)整

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

代碼移植思路:既然ffplay是一個(gè)功能完整的播放器(盡管沒(méi)有圖形界面),我們可以考慮把ffplay的代碼移植到我們的FFmGuiPlayer工程中。(7)把ffplay.c從例8-9ffplay工程中拷貝到工程目錄中,改名為ffplayCore.c,并將其添加到工程中(菜單“項(xiàng)目–添加現(xiàn)有項(xiàng)”–選中ffplayCore.c–按鈕“添加”)(8)新建一個(gè)ffplayCore.h頭文件,把原來(lái)ffplayCore.c中opt_add_vfilter()函數(shù)及前一句#ifCONFIG_AVFILTER之前的所有代碼,剪貼到ffplayCore.h中。即把原ffplayCore.c分拆成兩個(gè)文件。并將ffplayCore.h添加到工程中。(9)把config.h、cmdutils.h、cmdutils.c也從例8-9ffplay工程中拷貝到本工程目錄中,并將其添加到工程中。編譯,解決編譯錯(cuò)誤。若有編譯錯(cuò)誤,參見這里(10)把ffplayCore.c中的main()函數(shù)改為播放線程函數(shù)//刪/*Calledfromthemain*///刪intmain(intargc,char**argv)//此函數(shù)替代main,被對(duì)話框調(diào)用intffmfc_play(void*lpParam){//以下代碼移動(dòng)到新函數(shù)ffinit()中//intflags;//VideoState*is;//…...此處略寫多行……//av_init_packet(&flush_pkt);//flush_pkt.data=(uint8_t*)&flush_pkt;//以上代碼移動(dòng)到新函數(shù)ffinit()中//模擬控制臺(tái)argv參數(shù)的兩個(gè)變量調(diào)用parse_options(),里面會(huì)做一些視頻播放的準(zhǔn)備工作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);//模擬控制臺(tái)argv參數(shù)的兩個(gè)變量調(diào)

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

//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");//設(shè)置窗口縮放渲染質(zhì)量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;}//刪}//啟動(dòng)讀取數(shù)據(jù)線程//刪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;//進(jìn)入事件消息循環(huán)//刪event_loop(is);event_loop(g_is);//播放線程要退出時(shí)才會(huì)走到這里,例如影片被關(guān)閉停止

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

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

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

//設(shè)置日志級(jí)別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)進(jìn)程中斷和進(jìn)程終止的信號(hào)。這種方法多用在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(用于沖刷解碼器,即把解碼器中的殘余數(shù)據(jù)拿去渲染完)初始化av_init_packet(&flush_pkt);flush_pkt.data=(uint8_t*)&flush_pkt;return0;}(12)在ffplayCore.c頭部添加以下全局變量#include"ffplayCore.h"structMoviePlayInfog_MoviePlayInfo;//對(duì)話框類和ffplayCore.c共同使用的全局變量VideoState*g_is;//原來(lái)main()中的is改為此全局變量(13)把ffplayCore.h

溫馨提示

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

最新文檔

評(píng)論

0/150

提交評(píng)論