Chinaunix首页 | 论坛 | 博客
  • 博客访问: 226110
  • 博文数量: 56
  • 博客积分: 2480
  • 博客等级: 大尉
  • 技术积分: 475
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-28 10:57
文章分类

全部博文(56)

文章存档

2012年(36)

2011年(4)

2010年(2)

2009年(14)

我的朋友

分类: C/C++

2011-11-15 11:14:16

CoreAVC是高清视频解码的首选,其已成为h264高清视频的标准解码器,通常我们是将coreavc所带的音频解码器 视频解码器注册到系统后 由系统自动调用 但这样非常不稳定 首先 注册到系统的解码器有可能被任何人 任何其它一款软件解注册 其次如果存在其它解码器 而且其它解码器的优先级又高于coreavc 系统在解码h264高清视频的时候 是按优先级顺序选择的 coreavc极有可能注册了 但用不到 如何对专用媒体格式采用专用解码 提高系统利用率呢 手工调用解码器解码是不二之选 当然 有了微软Directshow 这一切变得更加简单

我在网乐播放器2.0新版中是这样解码h264的高清电影的:

思路:

下图是完全加载分离器 视频解码器 音频解码器 并且建立好解码链路后的过滤图表

从上图可知 原文件(即FileSource)的输出是先通过音视频分离器mp4splitter,分离出视频流 音频流 再分别送入视频解码器coreavc 和音频解码器coreaac 解码后的视频图像送入video render 默认视频设备输出 解码后的音频轨送入Audio render默认音频设备输出的

 

实现:
整个过程的vc++实现如下,其中用到了微软directshow技术:

首先载入视频文件(假设gb是IGraphBuilder的实例 并且已初始化)
IBaseFilter * pFilter=NULL;
USES_CONVERSION;

HRESULT hr=gb->AddSourceFilter(A2W(”c://mov//测试高清.mp4“),A2W("File Source"),&pFilter);
if (!SUCCEEDED(hr))
{
 HR_FAIL(-1,A2W("无法创建本地源过滤器!"));
}else
{ 
 //此时的过滤表

 //得到源文件的输出端口
 CComPtr pSrcOutPin=GetPin(pFilter,A2W("Out"),PINDIR_OUTPUT);
 if(pSrcOutPin) //如果能正确载入视频且找到源输出
   RenderMp4(pSrcOutPin);//调用RenderMp4 解码视频
}


//建立mp4解码链路 其中先建立了分离器 再将分离后的音视频分别送往音视频解码器
HRESULT Player::RenderMp4(IPin* pSrcOutPin)
{
  USES_CONVERSION; 
    IBaseFilter* pSpliterFilter=NULL;
    //从mp4splitter.ax动态加载视音频分离器
    CreateInstanceFromDll("MP4Splitter.ax",CLSID_MP4Splitter,IID_IBaseFilter,(void**)&pSpliterFilter);
    if (pSpliterFilter==NULL)
    {
     hr=-1;
     AfxMessageBox("缺少音视频分离器!");
     HR_FAIL(hr,A2W("CreateInstance MP4Splitter.ax"));
     }
     //将视音频分离器加入过滤表
    hr=gb->AddFilter(pSpliterFilter,L"MP4Splitter");
    HR_FAIL(hr,A2W("add SpliterFilter to graph"));

//加入分离器后的过滤表

    //找到分离器的输入端
     CComPtr pSpInPin=GetPin(pSpliterFilter,A2W("In"),PINDIR_INPUT);
     if(pSpInPin==NULL) return -1;
     //将源电影文件媒体的输出连到分离器,以便分离出视音频,分离后的视音频可由音频及视频解码器解码
     hr=gb->Connect(pSrcOutPin,pSpInPin);
     HR_FAIL(hr,A2W("connect pin pSrcOutPin pSpInPin"));

//连接源输出与分离器后



     //建立视频解码链路
     BOOL bVideo=SUCCEEDED(BuildMp4VideoChain(pSpliterFilter));
     //建立音频解码链路
     BOOL bAUdio=SUCCEEDED(BuildMp4AudioChain(pSpliterFilter));
     //由于一些电影可能没有音频,那么我们认为只要视频解码成功就算成功了
    if(bVideo)
     return S_OK;
    else 
     return -1;
}


//建立mp4视频解码链路
HRESULT Player::BuildMp4VideoChain(IBaseFilter* pSpliterFilter)
{
  USES_CONVERSION;
  HRESULT hr=S_OK;
  IBaseFilter* pVideoDecFilter=NULL;
 //从CoreAVC动态加载coreavc视频解码器
  CreateInstanceFromDll("CoreAVC.ax",CLSID_MP4VideoDec,IID_IBaseFilter,(void**)&pVideoDecFilter);
  if (pVideoDecFilter==NULL)
  {
   hr=-1;
   HR_FAIL(hr,L"CreateInstanceFromDll CoreAVC.ax");
  }
  //将视频解码器加入过滤表
  hr=gb->AddFilter(pVideoDecFilter,A2W("MP4VideoDec"));
  HR_FAIL(hr,A2W("add VideoDecFilter to graph"));
  //找到分离器的视频输出端
  CComPtr pSpVideoOutPin=GetPin(pSpliterFilter,A2W("Video"),PINDIR_OUTPUT);
  if(pSpVideoOutPin==NULL)
  {
   //直接找video端可能找不到,mp4分离器对不同视频输出,端口名称可能不一样
   pSpVideoOutPin=GetPin(pSpliterFilter,A2W("Imported with GPAC"),PINDIR_OUTPUT);
   if(pSpVideoOutPin==NULL)
    return -1;
  }
  //接下来找视频解码器输入端
  CComPtr pSpVideoDecInPin=GetPin(pVideoDecFilter,A2W("In"),PINDIR_INPUT);
  if(pSpVideoDecInPin==NULL) return -1;
  //将分离器视频输出端连到解码器视频输入端
  hr=gb->Connect(pSpVideoOutPin,pSpVideoDecInPin);
  HR_FAIL(hr,L"connect pin pSpVideoOutPin pSpVideoDecInPin");

 //连接分离器与视频解码输入端后



  //如果连接成功,我们就接着找视频解码器视频输出端
  CComPtr pVideoDecOutPin=GetPin(pVideoDecFilter,A2W("Out"),PINDIR_OUTPUT);
  if(pVideoDecOutPin==NULL) return -1;
  //现在可以把视频输出到视频设备了,最近一步 连接视频设备
  return ConnectToVideoRender(pVideoDecOutPin); //ConnectToVideoRender在后面实现

  //连接完视频设备后

}


//建立mp4音频解码链路
HRESULT Player::BuildMp4AudioChain(IBaseFilter* pSpliterFilter)
{
  USES_CONVERSION;
  HRESULT hr=S_OK;
  IBaseFilter* pAudioDecFilter=NULL;
  //从CoreAAC.ax加载 coreaac音频解码器
  CreateInstanceFromDll(“CoreAAC.ax",CLSID_MP4AudioDec,IID_IBaseFilter,(void**)&pAudioDecFilter);
   if (pAudioDecFilter==NULL)
   {
    hr=-1;
    HR_FAIL(hr,L"CreateInstanceFromDll CoreAAC.ax");
   }
   //将音频解码器加入到过滤表中
   hr=gb->AddFilter(pAudioDecFilter,A2W("MP4AudioDec"));
   HR_FAIL(hr,A2W("add AudioDecFilter to graph"));
   //接下来获取分离器的音频输出
   CComPtr pSpAudioOutPin=GetPin(pSpliterFilter,L"Audio",PINDIR_OUTPUT);
   if(pSpAudioOutPin==NULL)
   {
    pSpAudioOutPin=GetPin(pSpliterFilter,L"Sound",PINDIR_OUTPUT);
   }
   if(pSpAudioOutPin==NULL) return -1;
   //再得到解码器的音频输入
   CComPtr pAudioDecInPin=GetPin(pAudioDecFilter,A2W("In"),PINDIR_INPUT);
   if(pAudioDecInPin==NULL) return -1;
   //将分离器分离后的音频流与音频解码器输入端连接
   hr=gb->Connect(pSpAudioOutPin,pAudioDecInPin);
   HR_FAIL(hr,L"connect pin pSpAudioOutPin pSpAudioDecInPin");

//连接完分离器与音频解码后

   //如果连接成功,就可以获取音频解码器的输出端了,现在应该可以解码电影中的声音了
   CComPtr pAudioDecOutPin=GetPin(pAudioDecFilter,A2W("Out"),PINDIR_OUTPUT);
   if(pAudioDecOutPin==NULL) return -1;
   //最近一步,将音频解码器输出连接至系统声音设备
   return ConnectToAudioRender(pAudioDecOutPin); //ConnectToAudioRender在后面实现

 //将音频解码连到系统声音设备后



}


//将视频解码器解码后的视频图像呈现在视频设备上
HRESULT Player::ConnectToVideoRender(IPin* pVideoDecOutPin)
{
 USES_CONVERSION;
 CComPtr videoRender;
 //建立系统默认视频输出设备
 hr=videoRender.CoCreateInstance(CLSID_VideoRendererDefault );
 HR_FAIL(hr,A2W("videoRender CoCreateInstance"));
 //将视频输出设备加入过滤表
 hr=gb->AddFilter(videoRender,A2W("Video Render"));
 HR_FAIL(hr,A2W("addFilter videorender"));
 //找到视频输出设备的线路输入
 CComPtr videoRenderInPin=GetPin(videoRender,A2W("In"),PINDIR_INPUT);
 if (videoRenderInPin==NULL)
 {
  HR_FAIL(-1,A2W("videorender get input pin"));
 }
 //直接将视频解码的输出连至视频设备就完成视频输出了
 hr=gb->Connect(pVideoDecOutPin,videoRenderInPin);
 HR_FAIL(hr,A2W("connect pin pVideoDecOutPin,videoRenderInPin"));
 return S_OK;
}


//将音频解码器输出连至音频设备
HRESULT Player::ConnectToAudioRender(IPin* pAudioDecOutPin)
{
 USES_CONVERSION;
 CComPtr audioRender;
 //建立默认音频设备
 hr=audioRender.CoCreateInstance(CLSID_AudioRender);
 HR_FAIL(hr,A2W("audioRender CoCreateInstance"));
 //加入到过滤表中
 hr=gb->AddFilter(audioRender,A2W("Audio Render"));
 HR_FAIL(hr,A2W("addFilter audioRender"));
 //找到音频设备的输入端
 CComPtr audioRenderInPin=GetPin(audioRender,A2W("In"),PINDIR_INPUT);
 if (audioRenderInPin==NULL)
 {
  HR_FAIL(-1,A2W("audioRender get input pin"));
 }
 //将解码器的音频输出连至音频设备输入,如果成功的话,就可以输出声音了
 hr=gb->Connect(pAudioDecOutPin,audioRenderInPin);
 HR_FAIL(hr,L"connect pin pSpAudioDecOutPin audioRenderInPin");
 return S_OK;
}


//接下来看一下这个神秘的CreateInstanceFromDll函数
//不用注册COM组件 直接从动态库文件加载
HINSTANCE Player::CreateInstanceFromDll(LPCTSTR lpDllName, REFCLSID rclsid, REFIID riid, LPVOID * ppv)
{
 (*ppv) = NULL;
 HINSTANCE hDll = LoadLibrary( lpDllName ); //加载解码器动态库到内存
 if ( NULL == hDll )
 {
  return NULL;
 }
 typedef HRESULT (__stdcall *GETCLASS_PROC)(REFCLSID,REFIID,LPVOID*);
 //得到解码器动态库 DllGetClassObject函数地址
 GETCLASS_PROC procGetClassObject = (GETCLASS_PROC)GetProcAddress( hDll, "DllGetClassObject" );
 if( procGetClassObject )
 {
  IClassFactory* pFactory = NULL;
  HRESULT hr = procGetClassObject(rclsid, IID_IClassFactory, (void**)&pFactory);//创建类厂
  if( pFactory )
  {
   hr = pFactory->CreateInstance( NULL, riid, ppv);//生成实例,结果由ppv返回
   pFactory->Release();
   pFactory = NULL;
   if( NULL == *ppv )
   {
    FreeLibrary( hDll );
    return NULL;
   }
  }
  else
  {
   FreeLibrary( hDll );
   return NULL;
  }
 }
 else
 {
  FreeLibrary( hDll );
  return NULL;
 }
 return hDll;
}

阅读(4741) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~