分类: 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
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
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
if(pSpVideoOutPin==NULL)
{
//直接找video端可能找不到,mp4分离器对不同视频输出,端口名称可能不一样
pSpVideoOutPin=GetPin(pSpliterFilter,A2W("Imported with GPAC"),PINDIR_OUTPUT);
if(pSpVideoOutPin==NULL)
return -1;
}
//接下来找视频解码器输入端
CComPtr
if(pSpVideoDecInPin==NULL) return -1;
//将分离器视频输出端连到解码器视频输入端
hr=gb->Connect(pSpVideoOutPin,pSpVideoDecInPin);
HR_FAIL(hr,L"connect pin pSpVideoOutPin pSpVideoDecInPin");
//连接分离器与视频解码输入端后
//如果连接成功,我们就接着找视频解码器视频输出端
CComPtr
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
if(pSpAudioOutPin==NULL)
{
pSpAudioOutPin=GetPin(pSpliterFilter,L"Sound",PINDIR_OUTPUT);
}
if(pSpAudioOutPin==NULL) return -1;
//再得到解码器的音频输入
CComPtr
if(pAudioDecInPin==NULL) return -1;
//将分离器分离后的音频流与音频解码器输入端连接
hr=gb->Connect(pSpAudioOutPin,pAudioDecInPin);
HR_FAIL(hr,L"connect pin pSpAudioOutPin pSpAudioDecInPin");
//连接完分离器与音频解码后
//如果连接成功,就可以获取音频解码器的输出端了,现在应该可以解码电影中的声音了
CComPtr
if(pAudioDecOutPin==NULL) return -1;
//最近一步,将音频解码器输出连接至系统声音设备
return ConnectToAudioRender(pAudioDecOutPin); //ConnectToAudioRender在后面实现
//将音频解码连到系统声音设备后
}
//将视频解码器解码后的视频图像呈现在视频设备上
HRESULT Player::ConnectToVideoRender(IPin* pVideoDecOutPin)
{
USES_CONVERSION;
CComPtr
//建立系统默认视频输出设备
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
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
//建立默认音频设备
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
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;
}