[{"data":1,"prerenderedAt":341},["ShallowReactive",2],{"\u002F2015-09-10-ue4-taglib-usage-and-plugin":3},{"id":4,"title":5,"body":6,"date":316,"description":12,"extension":317,"meta":318,"navigation":321,"path":333,"seo":334,"stem":335,"tags":336,"__hash__":340},"blogs\u002F_legacy\u002F2015\u002F2015-09-10-ue4-taglib-usage-and-plugin.md","UE4中Taglib使用及插件制作",{"type":7,"value":8,"toc":306},"minimark",[9,13,16,21,24,27,38,41,47,50,54,57,60,71,74,77,80,84,87,93,96,99,105,108,114,117,120,125,128,134,137,140,145,148,154,157,162,165,170,173,176,180,183,186,195,198,201,207,211,214,217,220,226,229,235,238,244,247,258,265,268,271,277,280,286,289,295,298],[10,11,12],"p",{},"UE4使用第三方库时，为了方便跨平台编译、避免冲突和多次复用，将其制作成插件是不错的选择。",[10,14,15],{},"当前UE4版本为4.10.1。",[17,18,20],"h2",{"id":19},"taglib的使用","Taglib的使用",[10,22,23],{},"Taglib是一个用于读取多媒体文件标签的第三方库，之所以会使用到，是因为当前FMod在读取含有非英文的标签会得到错误的结果。",[10,25,26],{},"Taglib需要自己使用CMake进行配置和编译。如果只是在Windows下使用的话，最简单的方法是直接引用生成的.lib文件。在项目的.build.cs中，ModuleRules的实现里面添加：",[28,29,34],"pre",{"className":30,"code":32,"language":33},[31],"language-text","PublicIncludePaths.Add(\"F:\u002FLibs\u002Ftaglib-master\u002Finclude\u002Ftaglib\");\n\nPublicAdditionalLibraries.Add(\"F:\u002FLibs\u002Ftaglib-master\u002Ftaglib\u002FRelease\u002Ftag.lib\");\n","text",[35,36,32],"code",{"__ignoreMap":37},"",[10,39,40],{},"然后在需要使用的地方使用Taglib即可",[28,42,45],{"className":43,"code":44,"language":33},[31],"#define TAGLIB_STATIC\n\n#include \"fileref.h\"\n\n#include \"tag.h\"\n\n#include \"tpropertymap.h\"\n\n\u002F\u002F ……\n\n\u002F** TagLib Fetch Tag *\u002F\n\nMusicItem->AlubmName = \"\";\n\nMusicItem->ArtistName = \"\";\n\nTagLib::FileRef TLFile(*MusicFullPath);\n\nif (!TLFile.isNull() && TLFile.tag()){\n\n    OutputDebugStringA(\"TagLib Fetch Reading Begin.\\n\");\n\n    TagLib::Tag *tag = TLFile.tag();\n\n    std::wstring tWsName = tag->title().toWString();\n\n    if (tWsName.length()>0)    MusicItem->SetDisplayName(tWsName.c_str());\n\n    MusicItem->AlubmName = tag->album().toWString().c_str();\n\n    MusicItem->ArtistName = tag->artist().toWString().c_str();\n\n}\n\nelse{\n\n    OutputDebugStringA(\"TagLib Fetch Fails.\\n\");\n\n}\n\n\u002F** TagLib Fetch Tag - End *\u002F\n",[35,46,44],{"__ignoreMap":37},[10,48,49],{},"但是这样的方法在进行Android之类的平台上的编译时，会遇到很多麻烦。因此将其制作为UE4插件是最好的解决方法。",[17,51,53],{"id":52},"ue4插件制作","UE4插件制作",[10,55,56],{},"UE4的插件制作一直都处在WIP的状态，直到4.9版本开始，官方文档才变更为了可以使用的状态。因此，为了避免研究了之后官方大规模的更新，到了现在才开始研究。",[10,58,59],{},"UE4插件的制作相对还是比较简单的，在引擎中，官方就已经提供了4个示例插件，以其为基础进行插件开发即可。",[10,61,62,63,70],{},"插件的代码布局和UE4的游戏项目差不多，只有在Public中定义的类对外界才是可见的。详细的插件系统介绍可以参照[",[64,65,69],"a",{"href":66,"rel":67},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FProgramming\u002FPlugins\u002F",[68],"nofollow","官方文档","]。",[10,72,73],{},"插件的描述文件中最重要的地方是Modules中的Type参数，这个参数是决定插件的加载类型的。通常情况下，如果是在实际游戏逻辑中使用的插件的话，这里需要修改为Runtime。",[10,75,76],{},"如果是在项目的Plugins目录中添加插件目录进行开发的话，需要重新生成下项目文件",[10,78,79],{},"这样才能在vs中看到插件。还有比较容易忘记的是，直接继承自UObject的类，必须定义Blueprintable才可以在编辑器中可见。",[17,81,83],{"id":82},"taglib改造","Taglib改造",[10,85,86],{},"将taglib的源代码整个扔到Private目录中去，并在.build.cs中添加包含路径",[28,88,91],{"className":89,"code":90,"language":33},[31],"            PrivateIncludePaths.AddRange(\n\n                new string[] {\n\n                    \"TagLibs\u002FPrivate\",\"TagLibs\u002FPrivate\u002Ftaglib\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Ftoolkit\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fasf\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fmpeg\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fogg\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fogg\u002Fflac\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fflac\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fmpc\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fmp4\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fogg\u002Fvorbis\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fogg\u002Fspeex\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fogg\u002Fopus\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fmpeg\u002Fid3v2\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fmpeg\u002Fid3v2\u002Fframes\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fmpeg\u002Fid3v1\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fape\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fwavpack\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Ftrueaudio\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Friff\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Friff\u002Faiff\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Friff\u002Fwav\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fmod\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fs3m\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fit\", \"TagLibs\u002FPrivate\u002Ftaglib\u002Fxm\"\n\n                }\n\n                );\n",[35,92,90],{"__ignoreMap":37},[10,94,95],{},"要添加的比较多，主要是因为Taglib本身使用的就是这样的方法，稍微有点麻烦。",[10,97,98],{},"添加完路径之后，首先遇到的错误是",[100,101,102],"blockquote",{},[10,103,104],{},"2>EXEC : error : The first include statement in source file ‘F:\\Unreal\\PluginDev\\Plugins\\TagLibs\\Source\\TagLibs\\Private\\taglib\\mpeg\\id3v2\\id3v2header.cpp’ is trying to include the file ‘iostream’ as the precompiled header, but that file could not be located in any of the module’s include search paths.",[10,106,107],{},"这个错误的来源乍看之下很奇怪，其实在项目中有时候也会遇到。修正方法是，在cpp文件中首先包含模块头文件。",[28,109,112],{"className":110,"code":111,"language":33},[31],"#include \"TagLibsPrivatePCH.h\"\n",[35,113,111],{"__ignoreMap":37},[10,115,116],{},"添加了上面的文件之后，就会遇到Taglib内部大规模的namespace引发的错误，这一部分只能是一个一个的对命名空间进行控制修改了。",[10,118,119],{},"命名空间修正之后，会遇到如下错误",[100,121,122],{},[10,123,124],{},"error C2872: “DWORD”: 不明确的符号",[10,126,127],{},"在UE4内部有对DWORD进行禁止使用的机制，这里需要重新开启。开启的方法是使用UE4内部提供的机制。",[28,129,132],{"className":130,"code":131,"language":33},[31],"#include \"AllowWindowsPlatformTypes.h\"\n\n#include \u003Cwindows.h>\n\n#include \"HideWindowsPlatformTypes.h\"\n",[35,133,131],{"__ignoreMap":37},[10,135,136],{},"这样的话就可以使用DWORD等了。",[10,138,139],{},"在编译过程中还会遇到",[100,141,142],{},[10,143,144],{},"4310:编译器执行的转换将截断数据",[10,146,147],{},"这个错误出现点有点多，直接让编译器忽略比较快。",[28,149,152],{"className":150,"code":151,"language":33},[31],"#pragma warning(disable: 4310)\n",[35,153,151],{"__ignoreMap":37},[10,155,156],{},"经过上面的几个步骤，基本就可以正常的通过编译了。只是在打包的时候会有这样的错误：",[100,158,159],{},[10,160,161],{},"UnrealBuildTool: ERROR: Couldn’t find referenced module ‘PluginName’",[10,163,164],{},"或者",[100,166,167],{},[10,168,169],{},"Plugin ‘PluginName’ failed to load because module ‘PluginName’ could not be found. Please ensure the plugin is properly installed, otherwise consider disabling the plugin for this project",[10,171,172],{},"这时候需要在Development下对代码进行一次重新编译。",[10,174,175],{},"这样的话就可以在Windows下正常的使用了。",[17,177,179],{"id":178},"android平台","Android平台",[10,181,182],{},"插件在Android平台打包时会遇到很多编译器相关的错误，需要进行一些修正。",[10,184,185],{},"首先遇到的错误是",[100,187,188],{},[10,189,190,191],{},"error : delete called on ‘const TagLib::FileRef::FileTypeResolver’ that is abstract but has non-virtual destructor ",[192,193,194],"span",{},"-Werror,-Wdelete-non-virtual-dtor",[10,196,197],{},"这个错误原本是warning，据说是taglib为了保证其自身的向下兼容性等原因而遗留的。",[10,199,200],{},"因此，修正方法是让编译器忽略这个warning。直接在模块头文件上加上",[28,202,205],{"className":203,"code":204,"language":33},[31],"#if defined(__clang__)\n\n#pragma clang diagnostic ignored \"-Wdocumentation\"\n\n#pragma clang diagnostic ignored \"-Wshadow\"\n\n#pragma clang diagnostic ignored \"-Wdelete-non-virtual-dtor\"\n\n#pragma clang diagnostic ignored \"-Wunused-function\"\n\n#define HAVE_SNPRINTF\n\n#endif\n",[35,206,204],{"__ignoreMap":37},[17,208,210],{"id":209},"rtti","RTTI",[10,212,213],{},"整个改造过程最麻烦的问题出在了RTTI上，Taglib内部大量的使用了dynamic_cast进行类型检测，而不是使用virtual来进行函数重载。",[10,215,216],{},"而UE4和大多数游戏引擎一样，出于效率原因是禁止使用RTTI的，好在UE4提供了可以单独在插件和项目中打开RTTI的方法。",[10,218,219],{},"在插件的.build.cs中添加如下所示的代码即可：",[28,221,224],{"className":222,"code":223,"language":33},[31],"namespace UnrealBuildTool.Rules\n\n{\n\n    public class TagLibs : ModuleRules\n\n    {\n\n        public TagLibs(TargetInfo Target)\n\n        {\n\n            bUseRTTI = true; \u002F\u002F 打开RTTI\n\n            \u002F\u002F ... ...\n",[35,225,223],{"__ignoreMap":37},[10,227,228],{},"但是，这个开关对Android是没有作用的，我们需要编辑引擎源码进行修正。在AndroidToolChain.cs中，将所有的",[28,230,233],{"className":231,"code":232,"language":33},[31],"Result += \" -fno-rtti\";\n",[35,234,232],{"__ignoreMap":37},[10,236,237],{},"改为",[28,239,242],{"className":240,"code":241,"language":33},[31],"if (!CompileEnvironment.Config.bUseRTTI) Result += \" -fno-rtti\";\n",[35,243,241],{"__ignoreMap":37},[10,245,246],{},"混合使用RTTI的情况下需要注意的是，根据编译器的不同，将定义了virtual的多态类型在两种不同的编译方式之间混合使用时，其结果是不可预知的。具体而言可能会出现类似下面的错误：",[100,248,249,252],{},[10,250,251],{},"error: undefined reference to ‘typeinfo for UObject’",[10,253,254,255],{},"error: cannot pass object of non-trivial type ‘FString’ through variadic function; call will abort at runtime ",[192,256,257],{},"-Wnon-pod-varargs",[10,259,260,261,264],{},"结论上，就是在插件中要尽量避免使用UE4内部的类。详细的技术上的原因可以参照",[192,262,263],{},"这里","。",[17,266,267],{"id":267},"插件使用",[10,269,270],{},"插件制作完成后，使用起来就比较简单了。如果要在代码中使用TagLibs插件的话，在.build.cs中添加",[28,272,275],{"className":273,"code":274,"language":33},[31],"PrivateDependencyModuleNames.AddRange(new string[] { \"TagLibs\" });\n",[35,276,274],{"__ignoreMap":37},[10,278,279],{},"然后在要使用的类，一般是蓝图函数库的文件中添加包含",[28,281,284],{"className":282,"code":283,"language":33},[31],"#include \"ITagLibs.h\"\n",[35,285,283],{"__ignoreMap":37},[10,287,288],{},"以及使用",[28,290,293],{"className":291,"code":292,"language":33},[31],"ITagLibs::Get().GetMusicInfo(args…)\n",[35,294,292],{"__ignoreMap":37},[10,296,297],{},"即可。",[10,299,300,301,70],{},"其他详情可参见完成的Taglib插件源码，已上传至[",[64,302,305],{"href":303,"rel":304},"https:\u002F\u002Fgithub.com\u002Fsteinkrausls\u002FUE4-TagLibs",[68],"Github",{"title":37,"searchDepth":307,"depth":308,"links":309},2,3,[310,311,312,313,314,315],{"id":19,"depth":307,"text":20},{"id":52,"depth":307,"text":53},{"id":82,"depth":307,"text":83},{"id":178,"depth":307,"text":179},{"id":209,"depth":307,"text":210},{"id":267,"depth":307,"text":267},"2015-09-10","md",{"layout":319,"status":320,"published":321,"author":322,"author_login":323,"author_email":324,"author_url":325,"wordpress_id":326,"wordpress_url":327,"date_gmt":328,"excerpt":329},"post","publish",true,{"display_name":323,"login":323,"email":324,"url":325},"chaoshikari","chaoshikari@gmail.com","\u002F",1482,"\u002F\u002F?p=1482","2015-09-10 12:21:07 +0000",{"type":7,"value":330},[331],[10,332,12],{},"\u002F2015-09-10-ue4-taglib-usage-and-plugin",{"title":5,"description":12},"_legacy\u002F2015\u002F2015-09-10-ue4-taglib-usage-and-plugin",[337,338,339],"UE4","Taglib","插件","V3L6gr5PK2TZukj5tZ6xm2xk6Yp5Xa77uDIKBjvTTc8",1788763180624]