DirectShow播放器(LAVFilter + EVR)开发例子

2023-05-16

LAVFilter是一套著名的DirectShow插件,包括Demux,Video Decoder,AudioDecoder,播放文件所需要的几个重要插件都包含进去了,并且支持播放的视音频格式非常广泛,FFmpeg支持的它几乎都支持(因为它底层是调用FFmpeg)。LAVFilter是我们开发Directshow播放器必不可少的插件,值的一提的是它既支持软解又支持硬解,功能非常强大。在Vista以上系统硬解的时候渲染器必须要用EVR,很多关于directshow播放器的例子都是解码器连接VMR进行播放的,连接EVR的很少介绍,这篇文章就给大家讲一下怎么用LAV + EVR来播放视频。

假如我们要播放一个视频文件,那么用Directshow需要构建一个Filter链路图,以下就是播放一个文件的链路图例子:

上图连接的渲染器是Video Renderer(VMR),但是如果要切换到硬解模式,则连接的渲染器要改成EVR(enhanced video renderer)。

我写了一个类,封装了构建和运行DirectShow FilterGraph的一些流程,下面是类的声明:


// CHDVideoPlayGraph

class CHDVideoPlayGraph : public CWnd
{
	DECLARE_DYNAMIC(CHDVideoPlayGraph)

public:
	CHDVideoPlayGraph();
	virtual ~CHDVideoPlayGraph();

	void SetNum(int num) { m_Num = num; }

	HRESULT   BuildGraph(LPCTSTR lpszSrcFile);
	void      StopCapture();

	BOOL      GetVideoSize(CSize & size);

	PLAYSTATE GetState() { return m_psCurrent; }

	BOOL      GetVideoStatis(DWORD & dwBitrate, UINT & dwFps);

	void      UnIntializeVideo();
	HRESULT   InitializeVideo(HWND hWnd);

	 HRESULT  SetupVideoWindow(HWND hVideoWnd);
	void      ResizeVideoWindow();

	DWORD     GetCurrentBitrate(); 

	void      SetVideoMediatype(AM_MEDIA_TYPE * pMt);
    void      SetAudioMediatype(AM_MEDIA_TYPE * pmt);
		       
	void      SetCaptureCallback(VideoCaptureCB captureCB) { m_CaptureCB = captureCB; }

	void      CheckRgb24Buffer();

	void      OnRecvVideo(int nNum,PBYTE pBuffer,long BufferLen); //被回调函数调用

	BOOL      HasAudioStream() { return m_bHasAudio; }
	void      SetAudioStream(BOOL bEnable) { m_bHasAudio = bEnable; }

	//void      SetVideoRecvPort(int port);

	LRESULT   OnRestartPlaying(WPARAM wParam, LPARAM lParam);

	void      SetHarewareDecode(BOOL bFlag);
	void      SetVMRMode(int nVMR);

    int       GetFrameRate();
protected:
	DECLARE_MESSAGE_MAP()

	afx_msg HRESULT OnGraphNotify(WPARAM wp, LPARAM lp);
	afx_msg void OnPaint();

	void      DisplayMesg(TCHAR* szFormat, ...);

	LRESULT   ClearInterfaces(WPARAM wp, LPARAM lp);
	void      CloseInterfaces();
	HRESULT   AddGraphToRot(IUnknown* pUnkGraph, DWORD* pdwRegister);
	void      RemoveGraphFromRot(DWORD pdwRegister);
	HRESULT   HandleGraphEvent();
    HRESULT   GetInterfaces();
	HRESULT   SetSyncClock();
	void      GetSyncClock();

	HRESULT  RenderFilter(IBaseFilter * pFilter);

private:
	UINT chFullScreen, chAlwaysOnTop;
    UINT            m_Num;
	CBrush          m_emptyBrush;
	DWORD           m_dwGraphRegister;
	HWND            m_hApp;
	IVideoWindow*   m_pVW; 
	IMediaControl*  m_pMC;
	IMediaEventEx*  m_pME;
	IGraphBuilder*  m_pGraph;
	ICaptureGraphBuilder2 *m_pCapture;

	VideoCaptureCB   m_CaptureCB;

    IBaseFilter   * m_pRenderer;

    IBaseFilter   * m_pNullFilter;
	IBaseFilter    * m_pVideoDec;
	IBaseFilter    * m_pAudioDec;

	PLAYSTATE          m_psCurrent; 
	BOOL               m_bHasAudio;
	AM_MEDIA_TYPE      m_AudioMediaType;
	AM_MEDIA_TYPE      m_VideoMediaType;
	ULONG              m_nWidth, m_nHeight;
	PBYTE              m_pRgb24;
	TCHAR              m_szFile[256];
    BOOL               m_bHarewareDecode; //1--硬解码, 0--软解码
	INT                m_nVMRRenderer; ///1--VMR7, 2--VMR9,3--EVR
	DWORD              m_dwStartTick;
};

我们首先要在代码中定义要引用到的几个Filter的CLSID,包括LAV和EVR的:

DEFINE_GUID(CLSID_EnhancedVideoRenderer,
	0xFA10746C, 0x9B63, 0x4B6C, 0xBC, 0x49, 0xFC, 0x30, 0x0E, 0xA5, 0xF2, 0x56);

DEFINE_GUID(CLSID_LAVSplitter,
  0x171252A0, 0x8820, 0x4AFE, 0x9D, 0xF8, 0x5C, 0x92, 0xB2, 0xD6, 0x6B, 0x04);

DEFINE_GUID(CLSID_LAVSource, 
   0xB98D13E7, 0x55DB, 0x4385, 0xA3, 0x3D, 0x09, 0xFD, 0x1B, 0xA2, 0x63, 0x38);

DEFINE_GUID(CLSID_LAVVideoDec,
	0xEE30215D, 0x164F, 0x4A92, 0xA4, 0xEB, 0x9D, 0x4C, 0x13, 0x39, 0x0F, 0x9F);

DEFINE_GUID(CLSID_LAVAudioDec,
    0xE8E73B6B, 0x4CB3, 0x44A4, 0xBE, 0x99, 0x4F, 0x7B, 0xCB, 0x96, 0xE4, 0x91);

为了支持软解和硬解,并且能连接不同的渲染器Renderer,我在类声明里定义了两个变量:一个是解码模式,另外一个是连接的渲染器类型。方便对不同的模式进行切换。

    BOOL               m_bHarewareDecode; //1--硬解码, 0--软解码
	INT                m_nVMRRenderer; 0--GDI, 1--VMR7, 2--VMR9

接着,我们看一下怎么构建DirectShow链接图:


HRESULT CHDVideoPlayGraph::BuildGraph(LPCTSTR lpszSrcFile)
{
	HRESULT hr;
	USES_CONVERSION;
      	
	CFileFind find;
	if(find.FindFile(lpszSrcFile) == FALSE)
	{
		AfxMessageBox("Can not Find File");
		return E_FAIL;
	}

	_tcscpy(m_szFile, lpszSrcFile);

    // Get DirectShow interfaces
    hr = GetInterfaces();
    if (FAILED(hr))
    {
        DisplayMesg(TEXT("Failed to get video interfaces!  hr=0x%x"), hr);
        return hr;
    }



  BOOL bIsWin7 = FALSE;
  TCHAR sRC[64] = {0};

  OSVERSIONINFO versionInfo;

  versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

  if(::GetVersionEx(&versionInfo))
  {
      
	switch(versionInfo.dwPlatformId)
	{
	case VER_PLATFORM_WIN32_WINDOWS:
	  if(versionInfo.dwMinorVersion >= 0 && versionInfo.dwMinorVersion <= 9 )
	  {
		 _tcscpy( sRC, _T("95"));
	  }
	  else if(versionInfo.dwMinorVersion >= 10 && versionInfo.dwMinorVersion <= 89 )
	  {
		  _tcscpy(sRC, _T("98"));
	  }
	  else if(versionInfo.dwMinorVersion == 90 )
	  {
		 _tcscpy(sRC, _T("Me"));
	  }
	  break;
	case VER_PLATFORM_WIN32_NT:
	  if(versionInfo.dwMajorVersion == 4)
	  {
	     
	  }
	  else if(versionInfo.dwMajorVersion == 5)
	  {
		  switch(versionInfo.dwMinorVersion)
		  {
			case 0:
				_tcscpy(sRC, _T("2000"));
				break;
			case 1:
				_tcscpy(sRC, _T("XP"));
				break;
			case 2:
			   _tcscpy(sRC, _T("2003"));
			   break;
			}//switch
			
	   }// else if(versionInfo.dwMajorVersion == 5)
		else 
		{
			_tcscpy(sRC, _T("Windows7"));
			bIsWin7 = TRUE;
		}
	  break;

	default:
	  break;
	}  //switch
  }
  else
  {
	   OutputDebugString("GetVersionEx Errro!\n");
  }

    char szSysInfo[100] = {0};
    sprintf(szSysInfo, "OSType is: %s \n", sRC);
    OutputDebugString(szSysInfo);
  
    CComPtr<IBaseFilter> pSrcBaseFilter;


	hr = ::AddFilterByCLSID(m_pGraph, CLSID_LAVSource, L"LAVSource Filter", &pSrcBaseFilter);
	if (FAILED(hr))
	{
		MessageBox("Add LAVSource Filter  Failed", "Error");
		return hr;
	}

	IFileSourceFilter * pFileSource = NULL;

	hr = pSrcBaseFilter->QueryInterface(IID_IFileSourceFilter, (void**)&pFileSource);
	if(pFileSource)
	{
		hr = pFileSource->Load(A2W(m_szFile), NULL);
		pFileSource->Release();
	}
		 
	if(FAILED(hr))
	{
		MessageBox("Load File Failed", "Error");
		return hr;
	}


 	 //Add Video Decode filter
	hr = ::AddFilterByCLSID(m_pGraph, CLSID_LAVVideoDec, L"LAVVideo  Decoder", &m_pVideoDec);
	if (FAILED(hr))
	{
		return hr;	
	}
 

     ILAVVideoSettings  * pLAVSetting = NULL;

	 hr = m_pVideoDec->QueryInterface(__uuidof(ILAVVideoSettings), (void**)&pLAVSetting);
	 if(SUCCEEDED(hr))
	 {
		 pLAVSetting->SetHWAccel(m_bHarewareDecode ? HWAccel_DXVA2Native : HWAccel_None);

		 TRACE("SetHWAccel HW: %d \n", m_bHarewareDecode);

		 pLAVSetting->Release();
	 }
	
	if(bIsWin7)
	{
		CComPtr<IMFVideoDisplayControl> m_spDisplayCtrl;  
        CComPtr<IMFGetService> pGetService;  

	     //Add Enhanced Video Renderer 
		 hr = ::AddFilterByCLSID(m_pGraph,  CLSID_EnhancedVideoRenderer,  L"Enhanced Video Renderer ", &m_pRenderer);
		 if(FAILED(hr))
		 {
			  return hr;
		 }

		hr = m_pRenderer->QueryInterface((__uuidof(IMFGetService)),(VOID ** )&pGetService);  
 		 if(FAILED(hr))
		 {
			  return hr;
		 }

		hr = pGetService->GetService(MR_VIDEO_RENDER_SERVICE,__uuidof(IMFVideoDisplayControl), (void **)&m_spDisplayCtrl);  
	  
		RECT Rect;  
		::GetClientRect (m_hApp, &Rect);  
		m_spDisplayCtrl->SetVideoWindow (m_hApp);  
		m_spDisplayCtrl->SetAspectRatioMode(MFVideoARMode_None);  
		m_spDisplayCtrl->SetVideoPosition(NULL, &Rect);  
	}
	else
	{
	   	 //Add Video Renderer 
		if(m_nVMRRenderer == 0)
		{
			hr = ::AddFilterByCLSID(m_pGraph, CLSID_VideoRenderer, L"Video Renderer ", &m_pRenderer);
			if(FAILED(hr))
			{
				return hr;
			}
		}
		else if(m_nVMRRenderer == 1)
		{
			hr = ::AddFilterByCLSID(m_pGraph, CLSID_VideoMixingRenderer, L"Video Renderer ", &m_pRenderer);
			if(FAILED(hr))
			{
				return hr;
			}
		}
		else if(m_nVMRRenderer == 2)
		{
			hr = ::AddFilterByCLSID(m_pGraph, CLSID_VideoMixingRenderer9, L"Video Renderer ", &m_pRenderer);
			if(FAILED(hr))
			{
				return hr;
			}
		}

       hr = m_pGraph->QueryInterface(IID_IVideoWindow, (LPVOID *) &m_pVW);
       if (FAILED(hr))
          return hr;
	}


    hr = RenderFilter(pSrcBaseFilter);

	 if (FAILED(hr))
     {
		 OutputDebugString(_T("RenderFilter Failed. \n"));
        return hr;
     }

	hr = SetupVideoWindow(m_hWnd);
    
	//SetSyncClock();

	IPin * pRenderInputPin = NULL;
	pRenderInputPin = GetInPin(m_pRenderer, 0);

	if(pRenderInputPin == NULL)
		return E_FAIL;

	AM_MEDIA_TYPE  amType;
	AM_MEDIA_TYPE * pmt = &amType;

	hr = pRenderInputPin->ConnectionMediaType(pmt);

	if(SUCCEEDED(hr))
	{
		if(pmt->formattype == FORMAT_VideoInfo)
		{
			VIDEOINFOHEADER * pVidHdr = (VIDEOINFOHEADER*) pmt->pbFormat;
			m_nWidth   = pVidHdr->bmiHeader.biWidth;
			m_nHeight  = pVidHdr->bmiHeader.biHeight;
		
		}
		else if(pmt->formattype == FORMAT_VideoInfo2)
		{
			VIDEOINFOHEADER2 * pVidHdr = (VIDEOINFOHEADER2*) pmt->pbFormat;
			m_nWidth   = pVidHdr->bmiHeader.biWidth;
			m_nHeight  = pVidHdr->bmiHeader.biHeight;
		
		}
		else if (pmt->formattype == FORMAT_MPEGVideo)
		{
			MPEG1VIDEOINFO *pVidHdr = (MPEG1VIDEOINFO *) pmt->pbFormat;
		
			m_nWidth  = pVidHdr->hdr.bmiHeader.biWidth;
			m_nHeight = pVidHdr->hdr.bmiHeader.biHeight;
		} 
		else if (pmt->formattype  == FORMAT_MPEG2Video)
		{
			MPEG2VIDEOINFO *pVidHdr = (MPEG2VIDEOINFO *) pmt->pbFormat;
		
			m_nWidth = pVidHdr->hdr.bmiHeader.biWidth;
			m_nHeight = pVidHdr->hdr.bmiHeader.biHeight;
		} 
		else
		{
	
		}

		FreeMediaType(amType);
	}

#ifdef REGISTER_FILTERGRAPH
    // Add our graph to the running object table, which will allow
    // the GraphEdit application to "spy" on our graph
    hr = AddGraphToRot(m_pGraph, &m_dwGraphRegister);
    if (FAILED(hr))
    {
        DisplayMesg(TEXT("Failed to register filter graph with ROT!  hr=0x%x"), hr);
        m_dwGraphRegister = 0;
    }
#endif

    // Start previewing video data
    hr = m_pMC->Run();
    if (FAILED(hr))
    {
        DisplayMesg(TEXT("Couldn't run the graph!  hr=0x%x"), hr);
        return hr;
    }
	
    // Remember current state
    m_psCurrent = RUNNING;
       
	m_dwStartTick = GetTickCount();

    return S_OK;
}

上面的BuildGraph函数,首先调用GetInterfaces接口获取Directshow组件的一些接口:

HRESULT CHDVideoPlayGraph::GetInterfaces()
{
    HRESULT hr;
	
	if(m_pGraph == NULL){
		// Create the filter graph
		hr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,
							   IID_IGraphBuilder, (void **) &m_pGraph);
		if (FAILED(hr))
		{
			TRACE("CoCreateInstance() failed \n");
			return hr;
		}
	}
	else
		return E_FAIL ;

	//hr = m_pGraph->QueryInterface(IID_IVideoWindow, (LPVOID *) &m_pVW); //使用VMR时需要获取该接口进行视频窗口控制,但EVR不需要获取该接口
 //   if (FAILED(hr))
 //       return hr;

    // Obtain interfaces for media control and Video Window
    hr = m_pGraph->QueryInterface(IID_IMediaControl,(LPVOID *) &m_pMC);
    if (FAILED(hr))
        return hr;
	
    hr = m_pGraph->QueryInterface(IID_IMediaEvent, (LPVOID *) &m_pME);
    if (FAILED(hr))
        return hr;
	
    // Set the window handle used to process graph events
    hr = m_pME->SetNotifyWindow((OAHWND)m_hApp, WM_GRAPHNOTIFY, 0);

	//m_pME->CancelDefaultHandling(EC_DISPLAY_CHANGED);
	
    return hr;
}

然后,依次把LAV Source Filter,LAV Video Decode Filter加进去。代码中会根据系统版本决定优先插入哪个Video Renderer,如果是Win7以上,则插入EVR;其他系统版本则根据m_nVMRRenderer的类型值来选择对应的Renderer(VMR7,VMR9)。

接着,调用RenderFilter自动将上面的Filter连接起来(可能还会加入其他Filter)。


HRESULT CHDVideoPlayGraph:: RenderFilter(IBaseFilter * pFilter)
{
	 CComPtr< IEnumPins > pEnum;

    if (!pFilter)
       return E_POINTER;

    HRESULT hr = pFilter->EnumPins(&pEnum);
    if(FAILED(hr)) 
        return hr;

    ULONG ulFound;
    IPin *pPin;
    hr = E_FAIL;

	int nOutputPinCount = 0;
	int nRenderedCount = 0;

    while(S_OK == pEnum->Next(1, &pPin, &ulFound))
    {
        PIN_DIRECTION pindir = (PIN_DIRECTION)3;

        pPin->QueryDirection(&pindir);
        if(pindir == PINDIR_OUTPUT)
        {
			IPin * pPin2 = NULL;
			pPin->ConnectedTo(&pPin2);
			if(pPin2)
			{
				//pPin->Disconnect(); //先断开连接

				pPin2->Release();
				pPin2 = NULL;
			}
			else
			{

			hr = m_pGraph->Render(pPin); //自动连接Output Pin的下一级链路
			if(SUCCEEDED(hr))
				nRenderedCount++;
			}

			nOutputPinCount++;
        } 


        pPin->Release();
    } 

	return nRenderedCount > 0 ? (nOutputPinCount == nRenderedCount ? S_OK : S_FALSE) : E_FAIL;
}

例子代码链接:https://download.csdn.net/download/zhoubotong2012/11874281

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

DirectShow播放器(LAVFilter + EVR)开发例子 的相关文章

随机推荐

  • 【kubernetes/k8s概念】CNI详解

    1 为什么CNI CNI是Container Network Interface的是一个标准的 xff0c 通用的接口 现在容器平台 xff1a docker xff0c kubernetes xff0c mesos xff0c 容器网络解
  • 【kubernetes/k8s概念】CNI plugin bridge源码分析

    什么是bridge bridge是一个虚拟网络设备 xff0c 可以配置IP MAC地址等 xff1b 其次 xff0c bridge是一个虚拟交换机 xff0c 和物理交换机有类似的功能 普通的网络设备只有两端 xff0c 从一端进来的数
  • 【kubernetes/k8s概念】istio 原理与架构

    看着有点蒙 xff0c 摘录一下 xff0c 待有时间分析源码 WHY Istio 当应用被拆分为多个微服务后 xff0c 进程内的方法调用变成了进程间的远程调用 引入了对大量服务的连接 管理和监控的复杂性 随着微服务出现 xff0c 微服
  • 【docker基础知识】docker坑问题汇总

    1 Got starting container process caused 34 process linux go 301 running exec setns process for init caused 34 exit statu
  • 【kubernetes/k8s概念】k8s 坑问题汇总

    1 Pod始终处于Pending状态 如果Pod保持在Pending的状态 xff0c 意味着无法被正常的调度到节点上 由于某种系统资源无法满足Pod运行的需求 系统没有足够的资源 xff1a 已经用尽了集群中所有的CPU或内存资源 需要清
  • 【华为机试036】素数伴侣

    题目描述 xff1a 若两个正整数的和为素数 xff0c 则这两个正整数称之为 素数伴侣 xff0c 如2和5 6和13 xff0c 它们能应用于通信加密 现在密码学会请你设计一个程序 xff0c 从已有的N xff08 N为偶数 xff0
  • ERROR: Cannot uninstall ‘PyYAML‘. It is a distutils installed project and thus we cannot...

    具体错误 xff1a ERROR Cannot uninstall 39 PyYAML 39 It is a distutils installed project and thus we cannot accurately determi
  • 局域网vnc远程控制软件,盘点三款特别好用的局域网vnc远程控制软件

    局域网vnc远程控制软件是什么软件 xff0c 其实看名字是看的出来的 xff0c 这是一款远程控制软件 xff0c 远程控制软件一般的要求都是方便快捷 下面小编给大家介绍三款特别好用的局域网vnc远程控制软件 第一款 xff1a IIS7
  • vnc访问被拒绝怎么办,vnc访问被拒绝怎么办,为什么会被拒绝?

    vnc远程控制连接被拒绝的原因 xff0c 服务器作为网站建设的常用设备 xff0c 在服务器运行过程中起到举足轻重的作用 用户在选择服务器是常用的方式有服务器租用 虚拟主机租用以及服务器托管 xff0c 通过进行文件以及数据的下载 上传等
  • vnc使用图文教程,vnc使用图文教程(含图片教程)

    vnc使用图文教程不知道大家找到过没有 xff0c 毕竟在网上这种教程是很少的 xff0c 因为使用的人都是一些经常使用的 xff0c 但是对于小编这种基础能力差的 xff0c 还是需要vnc使用图文教程的 xff0c 所以小编也是努力了很
  • python中的MapReduce函数和过程浅析

    map reduce思想是Google的JeffDean在2008年在论文 MapReduce Simplified Data Processing on Large Clusters 中提出的 xff0c 而python中沿用了这种思想并
  • openMP学习笔记之一 -- 杂记

    1 使用libffi启动执行 xff0c ffi全称Foreign Function Interface xff0c 参考https www cnblogs com findumars p 4882620 html的介绍 xff0c 2 在
  • Vue系列之—Vue-router详解

    目录 一 简介 1 1 单页面应用 1 2 路由管理器 二 起步 2 1 动态路由匹配 2 2 路由组件传参 2 3 嵌套路由 声明式 编程式导航 命名路由 命名视图 重定向和别名 三 进阶 导航守卫 全局的守卫 路由独享的守卫 一 简介
  • vscode 代码格式化及快捷键

    VSCode 代码格式化 快捷键 On Windows Shift 43 Alt 43 F On Mac Shift 43 Option 43 F On Ubuntu Ctrl 43 Shift 43 I 代码格式化 vscode默认启用了
  • ORB-SLAM2添加稠密建图线程

    注 xff1a 本篇文章只是对高翔博士稠密点云代码进行的简述 xff0c 内容主要包括的是在ORB SLAM2基础上怎么添加稠密建图线程 xff0c 并未对高翔博士代码进行改动 本文章仅用于自己学习记录 xff0c 若有侵权麻烦私聊联系删除
  • ubuntu挂载sd卡到分区目录+修改docker镜像存储位置

    ubuntu挂载sd卡到分区目录 43 修改docker镜像存储位置 一 挂载SD卡到 data 1 查看Linux硬盘信息 lsblk 或 fdisk l lsblk 新的硬盘 xff0c 最好删除之前的分区 xff0c 再新建分区 de
  • Ubuntu虚拟机安装EDA工具:VCS+Verdi+dve2018方法教程

    上个月刚完成Ubuntu虚拟机的安装 xff0c 本教程的基础是你已经安装好了Ubuntu的虚拟机 xff0c 最好是和笔者版本接近的Ubuntu xff0c 具体安装方法已在之前的文章中介绍过了 xff1a https blog csdn
  • 基于deepstream-test3添加跟踪插件和4类sinkType输出(包括rtsp)

    基于deepstream test3添加目标跟踪插件和4类sinkType输出 xff08 包括rtsp输入输出 xff09 deepstream C C 43 43 deepstream官方示例没有给出一个单管道的多类输入和4类sinkT
  • 国外学位论文查找

    转自 xff1a http blog chinaunix net uid 20517852 id 1936377 html 国外博士论文下载的网站 xff0c 不知道以前发过没有 http search ohiolink edu etd i
  • DirectShow播放器(LAVFilter + EVR)开发例子

    LAVFilter是一套著名的DirectShow插件 xff0c 包括Demux xff0c Video Decoder xff0c AudioDecoder xff0c 播放文件所需要的几个重要插件都包含进去了 xff0c 并且支持播放