本博文主要记录本人在使用DirectShow框架,进行播放器、流媒体处理开发过程中遇到的问题以及解决的方法。一方面作为笔记,方便日后参考,另一方面也记录一些疑难杂症,供大家查询。
问题:
1.使用Qt进行DirectShow开发时,遇上“无法解析的外部符号”
此部分错误包括“cocreateinstance”及“sysfree”等一些com件常用的操作。
本人此前已include相关的dshow.h和windows.h头文件,也在pro文件里面,添加了lib。此时,右键可以查找到报错的方法对应的头文件及定义。
解决:
网上有人指出,在Qt里面,构建——qmake(包含清理和重构)一次,如果pro文件引入正确,一般可以解决。
但我qmake多次,仍报错,包括清理生成的目录及重启Qt。最后发现是,VS下默认缺省添加的一些lib没有引入。
在我加入以下lib之后,qmake后成功运行。
kernel32.lib \
user32.lib \
gdi32.lib \
winspool.lib \
comdlg32.lib \
advapi32.lib \
shell32.lib \
ole32.lib \
oleaut32.lib \
uuid.lib \
odbc32.lib \
odbccp32.lib\
这边再说一下普遍的排除过程:
1.首先确定对应的lib添加进了pro中。(无法解析的外部符号,意指无法根据头文件找到对应的库)
2.尝试qmake及清理重构工程
3.pro中顺序,先INCLUDEPATH,再LIBS
4.检查库的版本,是否与Qt编译版本对应(win32及64)
2.开发DirectShow Filter的时候,遇上dll无法注册,显示“模块加载失败……找不到指定模块”
模块 加载失败,请确保该二进制存储在指定的路径中,或者调试它以检查该二进制或相关的.DLL文件是否有问题。找不到指定模块
问题描述:
进行一个转换filter开发的时候,一开始继承“CTransInplaceFilter”进行开发。此时过程中一切正常,而后发现需要更改输出格式,并重新分配Allocate和buffer大小,因此更改了继承,选择使用“CTransformFilter”进行开发。
更换了父类之后,修改了构造函数和createInstance等参数后,未进行调试,直接继续进行开发。
1.调试的时候,发现graphstudionext中选中Filter双击,无法再界面中出现Filter框体。
2.添加了断点,发现没有触发任何的构造、创建实例函数。
3.graphstudionext无法注销filter的注册,regsvr32无法注册也无法注销filter的注册(DLL)。
4.期间代码未做任何删减,仅有必要的函数override。
该问题除了无法正常注册filter.dll外,若已注册filter,则无法在链路中创建初始化。
解决:
通过拷贝项目,进行回滚测试,定位到了两个函数引起了DLL注册错误。
BreakConnect(PIN_DIRECTION dir)
CompleteConnect(PIN_DIRECTION direction, IPin *pReceivePin)
我在这两个函数里面调用了外部的FFMpeg库。
此时,我想起我没有将FFMpeg库对应的dll文件拷贝到工程生成的程序目录中。
因此,这是一个由于Filter(COM件)内包含了外部库调用,依赖库找不到dll文件引起的注册失败问题。
将FFMpeg的DLL文件拷贝到release(生成)的目录中,com件正常注册通过。
妈的,又遇到这个问题了,然后我居然忘记我上次解决的时候留下了笔记。一模一样的问题,使用了外部库,但是没有把dill文件与filter.dll放到同一个文件夹,导致只要使用了那个库,filter就没办法注册,注册了也没办法初始化。
3.使用DirectShow开发格式转换软件,使用Avi Muxer和File Writer,播放完成后自动停止链路。
之前在做DirectShow Filter用于VR的投影格式转换。后期需要做一个格式转换软件,因此考虑使用现成的DirectShow框架进行开发。
使用了x264+AVI Muxer+File Writer完成AVI媒体的封装。
此时,使用类似播放视频一样的链路进行控制的时候,会遇到两个问题:
1.IMediaSeeking,进度条
2.播放结束后自动暂停
与一般播放文件不一样的是,格式转换的DirectShow,需要使用Muxer封装Filter的IMediaSeeking接口。因为实际sample传递时,推Sample结束了,但Muxer可能还在运转封装。因此,使用Muxer的Seeking才是正确的进度。
当Sample全部传送完毕,链路应该要自动停止,完成写文件并释放。
此时,GraphStudioNext中完成封装写入会自动停止链路流动,但编程的时候,如果不使用pContrl->Stop(),链路并不会停止。因此,需要判断链路状态,并在完成封装写入后,停止链路。
此时,使用pControl的state或者判断Seeking的进度都是不完善的。IMediaControl的state反应的是控制的状态,而Seeking存在CurrentPosition不等于Duration的情况。
此时,应该使用IMediaEvent,获得链路播放状态。其中,EC_COMPLETE为链路播放完成时的枚举state。
long evCode, param1, param2;
bool bComplete = false;
if (!pEvent) return;
//得到所有的事件
while (SUCCEEDED(pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0))
{
pEvent->FreeEventParams(evCode, param1, param2);
switch(evCode)
{
case EC_USERABORT:
case EC_ERRORABORT:
case EC_COMPLETE:
bComplete = true;
break;
}
}
if (bComplete)
{
pControl->Stop(); // Important! You must stop the graph!
//关掉事件通报.
pEvent->SetNotifyWindow(NULL, 0, 0);
pEvent->Release();
pEvent = NULL;
SendDlgItemMessage(IDC_PROGRESS1, PBM_SETPOS, 0, 0);
KillTimer(hwnd, nIDEvent);
}
3.VS2017建立DirectShow工程的Error
环境:VS2017 工具集V141
SDK:DirectX9.0
报错如下:
videoctl.h文件中
严重性 代码 说明 项目 文件 行 禁止显示状态 错误 C3244 “CAggDirectDraw::~CAggDirectDraw(void)”: 该方法是由“<未知>”引入的,而不是“IDirectDraw” YUVWriter g:\coding\dx9sdk\samples\c++\directshow\baseclasses\videoctl.h 54 |
严重性 代码 说明 项目 文件 行 禁止显示状态 错误 C3254 “CAggDirectDraw”: 类包含显式重写“{dtor}”,但并不从包含函数声明的接口派生 YUVWriter g:\coding\dx9sdk\samples\c++\directshow\baseclasses\videoctl.h 54 |
严重性 代码 说明 项目 文件 行 禁止显示状态 错误 C2385 对“{dtor}”的访问不明确 YUVWriter g:\coding\dx9sdk\samples\c++\directshow\baseclasses\videoctl.h 54 |
代码部分如下:
class CAggDirectDraw : public IDirectDraw, public CUnknown
{
protected:
LPDIRECTDRAW m_pDirectDraw;
public:
DECLARE_IUNKNOWN
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv);
// Constructor and destructor
CAggDirectDraw(TCHAR *pName,LPUNKNOWN pUnk) ://报错部分
CUnknown(pName,pUnk),
m_pDirectDraw(NULL) { };
virtual CAggDirectDraw::~CAggDirectDraw() { };//报错部分
// Set the object we should be aggregating
void SetDirectDraw(LPDIRECTDRAW pDirectDraw) {
m_pDirectDraw = pDirectDraw;
}
// IDirectDraw methods…………下面不写了
transip.h文件中
严重性 代码 说明 项目 文件 行 禁止显示状态 错误 C4596 “Copy”: 成员声明中的非法限定名 YUVWriter g:\coding\dx9sdk\samples\c++\directshow\baseclasses\transip.h 214 |
严重性 代码 说明 项目 文件 行 禁止显示状态 错误(活动) E0427 成员声明中不允许限定名 YUVWriter g:\Coding\dx9sdk\Samples\C++\DirectShow\BaseClasses\transip.h 214 |
代码如下:
protected:
IMediaSample * CTransInPlaceFilter::Copy(IMediaSample *pSource);//报错部分
combaseapi.h文件中
严重性 代码 说明 项目 文件 行 禁止显示状态 错误 C2760 语法错误: 意外的令牌“标识符”,预期的令牌为“类型说明符” YUVWriter c:\program files (x86)\windows kits\8.1\include\um\combaseapi.h 229 |
代码如下:
extern "C++"
{
template<typename T> _Post_equal_to_(pp) _Post_satisfies_(return == pp) void** IID_PPV_ARGS_Helper(T** pp)
{
#pragma prefast(suppress: 6269, "Tool issue with unused static_cast")
static_cast<IUnknown*>(*pp); // make sure everyone derives from IUnknown
return reinterpret_cast<void**>(pp);
}
}
我用该SDK开发维护了多个DirectShow Filter,出问题的时候,我用以前开发的工程,一点点对着配置看。include、lib没有配错,工程也是正常的,但是就只有新的工程报错,旧的Filter工程正常。
然后,发现了一个比较简单的解决方案——
解决方案一:
将工程平台工具集,设定为V141(VS2017)之前的版本,如V120或V140,都可以解决这个问题。
但是,我以前工程都是在V141工具集下开发,没有任何异常。
抱着钻牛角尖的心理,一点点比对,终于发现问题所在。
简单看了一下报错的代码。其实语法是没有问题的,其中两个头文件,在类的定义里面,使用了“类名::方法”定义自身方法。这与某些C++规范要求是不太一致的。此时考虑可能是因为平台版本对语法检查的问题。
于是,对比发现了核心的问题
解决方案二:
“工程属性”——“C/C++”——"符合模式"——勾选“否”
问题解决了~