![C++程序设计及互动多媒体开发 课件 罗立宏 第8章 视频与音频;第9章 Cocos2d-X开发基础_第1页](http://file4.renrendoc.com/view15/M01/1D/28/wKhkGWegayuAJDKUAABa7Yey5OY200.jpg)
![C++程序设计及互动多媒体开发 课件 罗立宏 第8章 视频与音频;第9章 Cocos2d-X开发基础_第2页](http://file4.renrendoc.com/view15/M01/1D/28/wKhkGWegayuAJDKUAABa7Yey5OY2002.jpg)
![C++程序设计及互动多媒体开发 课件 罗立宏 第8章 视频与音频;第9章 Cocos2d-X开发基础_第3页](http://file4.renrendoc.com/view15/M01/1D/28/wKhkGWegayuAJDKUAABa7Yey5OY2003.jpg)
![C++程序设计及互动多媒体开发 课件 罗立宏 第8章 视频与音频;第9章 Cocos2d-X开发基础_第4页](http://file4.renrendoc.com/view15/M01/1D/28/wKhkGWegayuAJDKUAABa7Yey5OY2004.jpg)
![C++程序设计及互动多媒体开发 课件 罗立宏 第8章 视频与音频;第9章 Cocos2d-X开发基础_第5页](http://file4.renrendoc.com/view15/M01/1D/28/wKhkGWegayuAJDKUAABa7Yey5OY2005.jpg)
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C++程序设计第8章视频与音频第1节音视频开发概述第2节DirectShow音视频开发第3节SDL多媒体开发库第4节FFMpeg音视频开发(一)第5节FFMpeg音视频开发(二)第1节音视频开发概述音视频开发应用领域常用的音视频开发库1.1音视频开发应用领域音视频播放软件音视频采集、录制视频监控音视频编辑、格式转化软件网络直播数字电视1.2常用的音视频开发库(1)微软:VideoForWindows(VFW)→DirectShow
→MediaFoundation特征:Windows系统适用
难度:★★常用的音视频开发库(2)FFMpeg全称:FastForwardMovingpictureexpertgroup特征:
功能强大
格式覆盖全
跨平台
难度:★★★★官网:/常用的音视频开发库(3)VLC全称:VideoLanClient特征:支持大多数音视频格式
跨平台官网:/常用的音视频开发库(4)Gstreamer全称:VideoLanClient特征:支持大多数音视频格式
跨平台官网:/第2节DirectShow音视频开发DirectShow简介DirectShow构架最简单的DirectShow播放器MFC图形界面DirectShow2.1DirectShow简介DirectShow(简称DShow)是一个Windows平台上的流媒体框架,提供了高质量的多媒体流采集和回放功能。它支持多种多样的媒体文件格式,包括ASF、MPEG、AVI、MP3和WAV等多种文件。VideoForWindows(VFW)→DirectShow
→MediaFoundation开发语言:C++2.2DirectShow架构DirectShow是一个开放性的应用框架,也是一套基于COM的编程接口。DirectShow使用一种交FilterGraph的模型来管理整个数据流的处理过程;参与数据处理的各个功能模块叫做Filter;各个Filter在FilterGraph中按一定的顺序连接成一条"流水线"协同工作。Filter,它是最基本的软件构件,过滤器通常在多媒体流中执行一个操作。各个Filter在FilterGraph中按一定的顺序连接成一条"流水线"协同工作。DirectShow的Filter与各种设备进行通信并对它们进行相应的控制,这些设备包括本地文件系统、电视卡、视频采集卡、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发出的事件。流程3.流程图中关键函数CoInitialize():初始化COM运行环境。CoCreateInstance(…,pGraph):用指定的类标识符创建一个Com对象。在该播放器中类标识符为“CLSID_FilterGraph”,用于创建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运行环境。2.3.2步骤启动VisualStudio(如VS2022),创建一个控制台项目。项目名称可取为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运行环境
//创建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节SDL多媒体开发库SDL简介SDL开发设置SDL显示流程SDL显示图片SDL事件驱动SDL键盘事件3.1SDL简介SDL是SimpleDirectMediaLayer(简易直控媒体层)的缩写。它是一个跨平台的多媒体库,以用于直接控制底层的多媒体硬件的接口。这些多媒体功能包括了音频、键盘和鼠标(事件)、游戏摇杆等。其最为重要的是提供了2D图形帧缓冲(framebuffer)的接口,以及为OpenGL与各种操作系统之间提供了统-的标准接口以实现3D图形。从这些属性我们可以看出,SDL基本上可以认为是为以电脑游戏为核心开发的多媒体库。C语言写成,可在C++下工作官网:/最新版本:SDL2.0第4节FFMpeg将会用到SDL显示视频图像,因此有必要先学习一下SDL。3.2SDL开发设置从官网下载最新的SDL2(/download-2.0.php),或者从课件资料中拷贝SDL2,解压,放到某个方便记忆的目录位置,例如“D:\SDL2\SDL2”SDL开发设置以后在每一个需要SDL的VisualStudtio工程中,菜单“项目–xxx属性”,在弹出的属性页对话框中,在配置属性树中点“VC++目录”,然后在“包含目录”中添加SDL中的include目录,如“D:\SDL2\SDL2\include”;再在“库目录”中添加SDL中的lib目录,如“D:\SDL2\SDL2\lib\x64”。请根据VS中编译的版本是x64还是x86来正确选择lib下的x64或x86目录。SDL开发设置再在“链接器-输入-附加依赖项”中添加“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系统SDL_CreateWindow():创建窗口SDL_WindowSDL_CreateRenderer():创建渲染器SDL_RendererSDL_CreateTexture():创建纹理SDL_TextureSDL_UpdateTexture():设置纹理的数据SDL_RenderCopy():将纹理的数据拷贝给渲染器SDL_RenderPresent():显示SDL_Delay():工具函数,用于延时。SDL_Quit():退出SDL系统3.4SDL显示图片例8-3使用SDL显示一张bmp图片步骤:(1)打开VS,创建一个控制台工程。工程起名为“HelloSDL”(2)按照3.2节设置好SDL开发环境(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;}//创建窗口
SDL_Window*pWin=SDL_CreateWindow("HelloWorld!",100,100,640,480,SDL_WINDOW_SHOWN);if(pWin==nullptr){cout<<"SDL_CreateWindowError:"<<SDL_GetError()<<endl;return-1;}
//创建渲染器
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;}
//创建纹理
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/。或者从课件资料中拷贝SDL2_Image,解压,放到某个方便记忆的目录位置,例如“D:\SDL2\SDL2_Image”。例8_4使用SDL显示jpg、png图片课件对本例讲解从略,有兴趣请参阅
课程资料中例子源码。3.5SDL事件驱动在例8-3中,我们采用SDL_Delay()函数来使窗口暂停。循环中一直延时等待,用户都不能操作,光标为等待图标,最小化、关闭等按钮也用不了,这不好。好的程序应该能与用户交互。为此,我们来学习SDL事件。例8-5支持SDL事件驱动位图显示程序步骤:(1)打开VisualStudio,创建一个控制台程序,工程名为“SDLEvent”(2)按照3.2节设置好SDL开发环境(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一样,但现在光标正常,窗口也能拖
动,最小化和关闭按钮都可以使用了。--OK!3.6SDL键盘事件我们以键盘事件为例,再深入一点学习SDL事件驱动例8-6使用按键WASD控制窗口中图形的运动。步骤:(1)打开VisualStudio,创建一个控制台程序,工程名为“SDLKeyboard”(2)按照3.2节设置好SDL开发环境(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);//创建窗口if(pWindow==NULL)
{cout<<"creatwindowerror\n";returnfalse;}//创建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事件列表,如取出有事件,进入循环{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节FFMpeg音视频开发(一)FFMpeg简介FFMpeg开发设置FFMpeg流程最简单的FFMpeg视频播放器最简单的FFMpeg音频播放器音视频同步4.1FFMpeg简介FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源库。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,并具有跨平台性、高可移植性和高编解码质量。C语言写成,可在C++下工作官网:/FFMpeg直接使用FFmpeg不仅是一套开源程序开发库。还是一组功能强大的应用程序。在控制台中进入下载解压好的FFMpeg目录,尝试使用ffmpeg.exe和ffplay.exe来转换视频格式和播放视频。4.2FFMpeg开发设置从网上下载,或从课件资料中获取FFMpeg,解压,放到某个方便记忆的目录位置,例如“D:\FFMpeg”FFMpeg开发设置以后在每一个需要FFMpeg的VisualStudtio工程中,菜单“项目–xxx属性”,在弹出的属性页对话框中,在配置属性树中点“VC++目录”,然后在“包含目录”中添加FFMpeg中的include目录,如“D:\FFMpeg\include”;再在“库目录”中添加FFMpeg中的lib目录,如“D:\FFMpeg\lib\win64”。请根据VS中编译的版本是win64还是win86来正确选择lib下的x64或x86目录。FFMpeg开发设置再在“链接器-输入-附加依赖项”中添加“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),创建一个控制台项目。项目名称可取为SimpleFfmVideo(2)按照3.2节设置好SDL开发环境(3)按照4.2节设置好FFMpeg开发环境(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上小节例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),创建一个控制台项目。项目名称可取为SimpleFfmAudio(2)按照3.2节设置好SDL开发环境(3)按照4.2节设置好FFMpeg开发环境(4)打开SimpleFfmAudio.cpp,替换写入如下代码:点击打开代码常见编译错误解决办法4.5音视频同步例8-7只播放视频图像,例8-8只播放声音,是否两个程序整合起来就是完美的带声音的视频播放器了?——没那么简单,还需要音视频同步。解协议数据(文件/流媒体)封装格式数据(avi/mp4…)解封装音频解码音频压缩数据视频压缩数据视频解码音频原始数据视频原始数据音视频同步音视频输出设备4.5.1I、P、B帧视频的播放过程可以简单理解为一帧一帧的画面按照时间顺序呈现出来的过程。但是在实际应用中,并不是每一帧都是完整的画面,因为如果每一帧画面都是完整的图片,那么一个视频的体积就会很大,这样对于网络传输或者视频数据存储来说成本太高,所以通常会对视频流中的一部分画面进行压缩(编码)处理。由于压缩处理的方式不同,视频中的画面帧就分为了不同的类别,其中包括:I帧、P帧、B帧。I、P、B帧I帧(Intracodedframes):I帧图像采用帧内编码方式,即只利用了单帧图像内的空间相关性,而没有利用时间相关性。I帧使用帧内压缩,不使用运动补偿,由于I帧不依赖其它帧,所以是随机存取的入点,同时是解码的基准帧。I帧主要用于接收机的初始化和信道的获取,以及节目的切换和插入,I帧图像的压缩倍数相对较低。I帧图像是周期性出现在图像序列中的,出现频率可由编码器选择。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,现在我们需要在解码B帧时知道P帧中信息,因此这几帧在视频流中的顺序可能是:IPBB,这时候就体现出每帧都有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音视频的同步要实现音视频同步,通常需要选择一个参考时钟,参考时钟上的时间是线性递增的,编码音视频流时依据参考时钟上的时间给每帧数据打上时间戳。在播放时,读取数据帧上的时间戳,同时参考当前参考时钟上的时间来安排播放。这里的说的时间戳就是我们前面说的PTS。实践中,我们可以选择的方法有:(1)同步视频到音频(2)同步音频到视频(3)同步音频和视频到外部时钟。第5节FFMpeg音视频开发(二)ffplay播放器程序GUI界面的FFMpeg播放器基于FFMpeg的DirectShow解码器——LAVFiltersFFMpeg.exe录音与录像录像与录音程序开发5.1ffplay程序分析FFMpeg提供了一个开源的ffplay程序。这是一个使用了FFMpeg和SDL库的、简单的可移植的媒体播放器。打开例8-9,运行并阅读代码。例8-9是加了详细注释的VS版本的ffplay。例8-9ffplay程序运行并阅读点击打开代码5.1.1ffplay程序功能指定视频影片文件,可正常播放视频和声音。-在调试时,可在项目设置中(菜单“项目—项目属性”—配置属性—调试—命令参数)输入视频路径和文件名;-在命令行运行时,可在ffplay_vs命令中直接跟视频路径和文件名。
鼠标功能-左键双击:切换全屏-右键左右拖动:快进快退键盘功能-F键:切换全屏-P键或空格键:暂停和继续播放-M键:切换静音-*键(小键盘上的乘号键,非Shift+8键)和0键:增加音量ffplay程序功能键盘功能(续):-/键(小键盘上的除号键,非主键盘中的斜杠键)和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利用前面学到的知识,开发一个形如例8-2的基于MFC图形界面的FFMpeg音视频播放器。步骤
准备(1)打开VS,创建一个基于对话框的MFC工程。起名为“FFmGuiPlayer”(2)按照3.2节设置好SDL开发环境(3)按照4.2节设置好FFMpeg开发环境(4)制作一张bmp图片,用作播放器的封面。放在工程的res目录中。并把图片导入到资源中,ID起名为“IDB_PLAYERCOVER”步骤
界面(5)在对话框上创建出各个控件。
若需要调整控件间的
前后顺序,按Ctrl+D调整
(6)对其中3个控件添加变量。控件ID用途备注添加变量PictureControlIDC_BCKGRD视频背后的灰色背景Type:RectanglePictureControlIDC_SCREEN显示视频Type:BitmapImage:IDB_PLAYERCOVER大小位置与背景控件IDC_BCKGRD一样,在背景控件上面。视频播放时我们让屏幕控件IDC_SCREEN根据视频长宽比而变化,而背景控件始终不变。控件ID用途备注添加变量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暂停/继续播放视频Caption:“暂停”ButtonIDC_BTN_STOP停止并关闭视频Caption:“停止”ButtonIDC_BTN_FULLSCREEN全屏播放Caption:“全屏”步骤
代码移植思路:既然ffplay是一个功能完整的播放器(尽管没有图形界面),我们可以考虑把ffplay的代码移植到我们的FFmGuiPlayer工程中。(7)把ffplay.c从例8-9ffplay工程中拷贝到工程目录中,改名为ffplayCore.c,并将其添加到工程中(菜单“项目–添加现有项”–选中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创建窗口//删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;//进入事件消息循环//删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();
//处理系统进程中断和进程终止的信号。这种方法多用在Linux和Unix系统。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. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 供货合同范例文库
- 全屋定制家具合同范本封面
- 2025年度智慧社区物业服务合同终止模板
- 光伏抵押合同范例
- 冷凝设备销售合同范例
- 产品服务代理 合同范本
- office格式合同范例
- 会展物料合同范本
- 产权分割商铺销售合同范本
- 2025年度教育节目制作与版权转让合同
- 特种设备及重要设备安全管理制度
- 《自主神经系统》课件
- 2025集团公司内部借款合同范本
- 辽宁省名校联盟2025届高三上学期1月份联合考试语文试题(含答案)
- 2025年山西地质集团社会招聘高频重点提升(共500题)附带答案详解
- 2024-2025学年辽宁省沈阳市沈河区七年级(上)期末英语试卷(含答案)
- 前牙即刻种植的临床应用
- 2024-2025学年初中七年级上学期数学期末综合卷(人教版)含答案
- 体育活动策划与组织课件
- 公司违规违纪连带处罚制度模版(2篇)
- 2025届高考物理二轮总复习第一编专题2能量与动量第1讲动能定理机械能守恒定律功能关系的应用课件
评论
0/150
提交评论