<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>空雪小屋 &#187; 函数库</title>
	<atom:link href="http://blog.sorayuki.net/?cat=10&#038;feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://blog.sorayuki.net</link>
	<description>现在域名是 blog.sorayuki.net ~</description>
	<lastBuildDate>Tue, 29 Nov 2022 08:27:41 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.0</generator>
	<item>
		<title>MPV播放器</title>
		<link>http://blog.sorayuki.net/?p=598</link>
		<comments>http://blog.sorayuki.net/?p=598#comments</comments>
		<pubDate>Sun, 19 Mar 2017 13:36:46 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[函数库]]></category>
		<category><![CDATA[多媒体]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=598</guid>
		<description><![CDATA[这个播放器挺神奇的，要说不带界面也不是不行。可以在有做播放器需求的时候，把它拿来套个壳，当作播放器内核来使用。 &#8230; <a href="http://blog.sorayuki.net/?p=598" class="more-link">继续阅读<span class="screen-reader-text">MPV播放器</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>这个播放器挺神奇的，要说不带界面也不是不行。可以在有做播放器需求的时候，把它拿来套个壳，当作播放器内核来使用。</p>
<p><span id="more-598"></span></p>
<p>最近有一个需求，要做个播放视频的功能。但是这东西，有音画同步，有缓冲，什么什么的各种要做的。要搞的东西挺多也比较复杂，讲真我是很不想去弄。但是需求还是要做的，就找各种各样现成的东西来试试看能不能套进去用。</p>
<p>我最初从文件到解码后的视频数据和音频数据这一部分，调用FFMPEG来做，它提供了从读取文件、拆分文件中的数据流、解码数据流整个过程中所需要的功能。但是得到解码后的数据之后，想要播放它还是要做缓冲和音画同步这类事情，在这个时候，我感到里面的不确定因素有点多，需要经验，可能会有卡个很久才能搞定的坑；这个坑自己挖有点太大了，就希望能有更直接的、套上去就能用的东西……</p>
<p>然后这个时候就想起了这么件事。很早以前大概高中的时候吧，用过一个叫MPlayer的播放器。它给我印象比较深的原因，是我用Windows下其他的播放器（当时用的那些播放器都是DirectShow的，然后FFDShow来解码）播放720p XviD的动画片，机器带不动，声音和画面不同步，画面越来越慢。但是MPlayer可以音画同步地播放。但是MPlayer的界面不太好看和方便，后来也用了个叫SMPlayer的播放器，这个播放器和普通的播放器长得差不多，但是它却是调用单独的exe程序来播放，这个播放器做了个图形界面。但是它却能把播放的视频的画面显示在自己的界面上面。界面的exe和播放器核心的exe之间是怎么通信的，以前也没有注意或者留意过，现在有这样需求了，我就琢磨着是不是可以学它做，用来实现这个做播放器的需求。</p>
<p>从网上下了一个SMPlayer以后，启动起来以后打开一个视频文件，然后就开了ProcessExplorer工具来看进程，果然看到了SMPlayer起了一个进程……但是叫MPV.exe而不是MPlayer.exe。（想起之前要做播放器的时候，问过隔壁部门的人。得到的建议是用MPV，不过在那会儿，我的理解是要找MPV的代码来修改或者什么的拿来用，没想到还有直接起exe这种方便的方法……）</p>
<p>ProcessExplorer里面看到SMPlayer执行的MPV的进程的参数列表里，看到了好多东西。有一个 --wid= 在网上查了之后，得知这个参数是指定把画面显示在哪个窗口上。我开了个记事本，用Spy++工具得到窗口句柄的值，然后把值通过这个参数送进去启动MPV，果然画面在记事本的窗口里播出了视频画面……不过在Windows的声音混合器里，看到实际发出声音的是MPV.exe进程而不是Notepad.exe进程。</p>
<p>然后就是怎么控制播放状态的问题。MPV.exe带有参数 --input-file=/dev/stdin （在Windows下也是这么写）的时候，控制是通过标准输入stdin给它送命令，比如 cycle pause up 可以控制视频的播放和暂停，seek可以控制跳转到哪里，这些命令后面带上换行 （\n）送进stdin就可以了。MPV可以把播放过程中的一些状态信息输出到标准错误输出（stderr）。要指定让MPV输出一些视频信息，可以通过 --term-status-msg 这样的参数给它一个输出格式。具体的说明可以在官网这里 https://mpv.io/manual/stable 找到。</p>
<p>所以最后，我在要播放的时候，用 --wid= 指定自己界面上一个空的窗口的句柄给它，然后用匿名管道（通过CreatePipe创建，在STARTUPINFO里面指定）重定向它的标准输入和标准错误输出，把它标准错误输出的文本分析之后显示在界面上，然后把界面上的例如拖动进度条、点击播放暂停这类的操作，用给它标准输入写入字符串的方式来控制它。就简单地做了个“播放器”。</p>
<p>具体的一些参数如果看说明还不太了解的，可以模仿SMPlayer的命令行来写。</p>
<p>最后说一下，这个播放器是GPL协议的。对于GPL协议的东西，如果只是通过运行exe的方式来调用它，不知道能允许做到什么样、是不是强制自己的程序要开源。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=598</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WDK编译boost(1.34.1)::regex</title>
		<link>http://blog.sorayuki.net/?p=517</link>
		<comments>http://blog.sorayuki.net/?p=517#comments</comments>
		<pubDate>Sun, 31 Aug 2014 16:38:02 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[VC]]></category>
		<category><![CDATA[函数库]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=517</guid>
		<description><![CDATA[Windows Driver Kit里面的STL库啊啥的，基本是停留在VC6时代。不过也有一个好处，可以链接到 &#8230; <a href="http://blog.sorayuki.net/?p=517" class="more-link">继续阅读<span class="screen-reader-text">WDK编译boost(1.34.1)::regex</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Windows Driver Kit里面的STL库啊啥的，基本是停留在VC6时代。不过也有一个好处，可以链接到msvcrt.dll、msvcp60.dll，因为这两个文件大家都有，所以可以减小最后生成的可执行文件的体积。然后比起VC6.0，WDK可以直接从微软那边下载来，而VC6直到最后也没像2005、2008、2010、2012、2013那样有个Express的免费版啥的。于是如果想自己弄了简单的小东西如果想要体积小、要链接到msvcrt.dll，感觉WDK是一个挺好的选择……</p>
<p><span id="more-517"></span></p>
<p>不过WDK里面的东西并不完善，所以Windows SDK可能还是要准备一个。考虑到要把它当作VC6来处理，所以最新能用的也就是Platform SDK 2003 February。其实本来没打算装这个，因为WDK里面已经带了一套Windows的头文件和库，但是在编译boost的那个bjam的时候，发现WDK里面没有TlHlp32.h文件（当然前提是你已经把api、crt那些的文件夹都加到Include环境变量里了），于是知道它带的东西还是有少的，就装个SDK来补充一下。</p>
<p>安装SDK的时候也还遇到了问题。因为这个SDK版本比较老，它有x86和ia64两个setup.exe、setup64.exe但是却没有给amd64（x86-64）用的setup，于是无法启动安装程序。上网搜索解决方案是直接到文件夹里面去运行它的那些msi安装包来安装组件。那些msi在安装的时候会提示选择要安装的组件、要安装到什么文件夹的。</p>
<p>现在的Boost已经抛弃对VC6.0的支持了，网上找了一圈相关信息，能支持VC6的最新版本是1.34.1（也有说1.34.1的Regex编译起来有问题，1.33.1更容易搞定，不过我没试验过），那就找到这个指定版本下载来，解压。</p>
<p>然后boost解压之后需要一个bjam控制之后的编译过程，在boost\tools\jam\src里可以找到一个build.bat，用它来构建bjam.exe。如果只有WDK、并且在bat的参数指定了MSVC、环境变量指定了MSVCDir为WDK的文件夹（具体开build.bat进去看，很容易找到），就会出现前面说的找不到TlHlp32.h的事情。</p>
<p>接下来，<a href="./?p=515">前一篇博客</a> 有提到WDK的编译器和msvcp60.dll配合有问题，编译器默认wchar_t就是wchar_t但是msvcp60认为它是unsigned short，于是在链接的时候出现符号链不上的问题。自己编译的时候还好办，知道/Zc:wchar_t，但是由别人编译的时候问题就有点麻烦，因为不知道怎么加。我研究了很久总之也没找到方法……不管怎么样，就算开-n的时候也能看到/Zc:wchar_t不过看不到/Zc:wchar_t。这样的命令行：<br />
bjam stage release toolset=msvc-6.0 --with-regex runtime-link=shared link=static<br />
最终开到boost\tools\build\v2\tools\msvc.jam文件里面，找到了/Zc:wchar_t，改成/Zc:wchar_t-，再编译，换了个错误，说swprintf函数什么什么不匹配。网上一搜，原来它有个BOOST_NO_SWPRINTF宏，索性就用上了。就是说把<br />
/Zc:wchar_t<br />
改成了<br />
/Zc:wchar_t- /DBOOST_NO_SWPRINTF</p>
<p>再编译，终于能出lib文件了。</p>
<p>仔细查看这一行的上下文，可以发现它是在你编译器指定是VC6.0的时候加入的特定处理。但是，明明有一个&lt;cxxflags&gt;在上面，试了好几种命令行写法，最终也没能成功把参数通过命令行弄进去。</p>
<p>总之经过这样一搞，最终成功在WDK里用起Boost.regex了。顺便一说，测试的结果，因为regex引擎加入进去的关系，最终生成的可执行文件大小只比VC2010用自带regex、静态链接生成的可执行文件的大小小了60KB左右（原来180KB现在120KB）</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=517</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>I make a live broadcast client for icecast</title>
		<link>http://blog.sorayuki.net/?p=183</link>
		<comments>http://blog.sorayuki.net/?p=183#comments</comments>
		<pubDate>Mon, 15 Oct 2012 11:47:47 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[Windows编程]]></category>
		<category><![CDATA[函数库]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=183</guid>
		<description><![CDATA[Hello, Recently I write a client for icecast. It can br &#8230; <a href="http://blog.sorayuki.net/?p=183" class="more-link">继续阅读<span class="screen-reader-text">I make a live broadcast client for icecast</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Hello,</p>
<p>Recently I write a client for icecast. It can broadcast the audio from both stereo mixer and microphone to an icecast server.</p>
<p>It’s mainly written in C++, and the GUI in C#, distributed in 3-clause BSD licence.</p>
<p>It uses boost and portaudio library.</p>
<p>Any comment is welcomed.</p>
<p>&nbsp;</p>
<p>
<div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:6fba3e7d-e464-4cc6-98ce-e0bb6a6ff1d7" class="wlWriterEditableSmartContent">
<p> <a href="http://blog.sorayuki.net/wp-content/uploads/2012/10/icecast_client_20121015.7z" target="_blank">Download Source code and binaries</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=183</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>使用Windows的匿名管道和Boost.Iostreams</title>
		<link>http://blog.sorayuki.net/?p=85</link>
		<comments>http://blog.sorayuki.net/?p=85#comments</comments>
		<pubDate>Thu, 11 Oct 2012 04:41:44 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[Windows编程]]></category>
		<category><![CDATA[函数库]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=85</guid>
		<description><![CDATA[因为Boost.Iostreams提供了封装Windows的Handle的支持，利用这个可以简化匿名管道的操作 &#8230; <a href="http://blog.sorayuki.net/?p=85" class="more-link">继续阅读<span class="screen-reader-text">使用Windows的匿名管道和Boost.Iostreams</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>因为Boost.Iostreams提供了封装Windows的Handle的支持，利用这个可以简化匿名管道的操作。我想要达到的目标，先实现这样一个简单的命令行：</p>
<pre>ffmpeg -i c:\1.mp3 -acodec pcm_s16le -ac 2 -ar 44100 -f s16le - | oggenc2 -o c:\1.ogg -q 1 -B 16 -C 2 -R 44100 -</pre>
<p>功能很简单，就是把一个mp3文件转换成ogg文件而已。然而要达到这个目的，耗了不少精力……<span id="more-85"></span></p>
<p>首先是管道。Windows提供了CreatePipe函数，于是简单易懂我来</p>
<pre>CreatePipe(&amp;_hInputPipeForRead, &amp;_hInputPipeForWrite, 0, 0);</pre>
<p>然后创建进程的时候，STARTUPINFO里的dwFlags加上STARTF_USESTDHANDLES，把管道赋值过去，调用</p>
<pre>CreateProcess(0, &amp;tmpbuf[0], 0, 0, FALSE, 0, 0, 0, &amp;si, &amp;pi)</pre>
<p>结果是ffmpeg秒退。看出错信息，无法打开输出。明显是管道不行。去Windows SDK的文档看了半天例子，琢磨了两下，是HANDLE没有被继承给子进程的原因。那好，把CreateProcess里的那个FALSE改成TRUE。运行，秒退（死目</p>
<p>再看那个例子，它创建管道的时候专门设置了一下安全属性。那我也来</p>
<pre> SECURITY_ATTRIBUTES saAttr;
 saAttr.nLength = sizeof(saAttr);
 saAttr.bInheritHandle = TRUE;
 saAttr.lpSecurityDescriptor = NULL;

 CreatePipe(&amp;_hInputPipeForRead, &amp;_hInputPipeForWrite, &amp;saAttr, 0);</pre>
<p>好，这下ffmpeg运行起来在进程列表里可以看到了， 没有秒退了。接下来自己写一个类来封装这个进程的输入输出，我的话是这么搞的</p>
<pre>class process_io {
 public:
   typedef char char_type;
   typedef bio::bidirectional_device_tag category;

   enum {SOURCE_NONE = 0, SOURCE_STDIN, SOURCE_STDOUT, SOURCE_STDERR};

   process_io(const char* strCmdLine, int inputSource = SOURCE_NONE, int outputSource = SOURCE_NONE);
   ~process_io();
   bool start();
   bool close();
   void terminate();
   bool wait(int milliseseconds);

   std::streamsize read(char* s, std::streamsize n);
   std::streamsize write(const char* s, std::streamsize n);
 private:
   ...
};</pre>
<p>析构函数里面终止进程。然后照着教学，来</p>
<pre>bio::stream&lt;process_io&gt; p1("d:\\tools\\ffmpeg.exe -i c:\\1.mp3 -acodec pcm_s16le -ar 44100 -ac 2 -f s16le -", process_io::SOURCE_NONE, process_io::SOURCE_STDOUT);</pre>
<p>……（真心死的快有木有啊）。这下连ffmpeg的进程都看不到了。在terminate里面下一个断点，运行，果断发现析构函数被调用了。这尼玛……我明明才创建对象你就给我析构。一想，什么时候一边构造一边还有析构，大概是它给我process_io复制一份自己留着了然后我的就被丢掉了吧。卧槽，“复制一份”？！boost你在干什么（扶额）</p>
<p>上网一搜，果然出来了这个</p>
<blockquote><p><a href="http://lists.boost.org/Archives/boost/2005/10/95913.php" target="_blank">http://lists.boost.org/Archives/boost/2005/10/95913.php</a></p></blockquote>
<p>因为我写的这个类没有复制构造函数，C++给我自己生成了一个，还是浅复制的。HANDLE不能给你这样搞啊（锤地）。那我就先把自己这个类的复制功能给弄残，声明一个复制构造函数但不实现。然后按照这邮件列表里面给出的回复，用boost::reference_wrapper给搞一下：</p>
<pre> bio::stream&lt;boost::reference_wrapper&lt;process_io&gt;&gt; p1;
 process_io p1_impl("d:\\tools\\ffmpeg.exe -i c:\\1.mp3 -acodec pcm_s16le -ar 44100 -ac 2 -f s16le -", process_io::SOURCE_NONE, process_io::SOURCE_STDOUT);
 p1.open(boost::ref(p1_impl));</pre>
<p>编译，通过。因为复制构造函数已经残了，所以确定它没有给我复制。</p>
<p>然后搞出了如下main函数用于测试代码能不能用：</p>
<pre>int main() {
  bio::stream&lt;boost::reference_wrapper&lt;process_io&gt;&gt; p1;
  bio::stream&lt;boost::reference_wrapper&lt;process_io&gt;&gt; p2;
  process_io p1_impl("d:\\tools\\ffmpeg.exe -i c:\\1.mp3 -acodec pcm_s16le -ar 44100 -ac 2 -f s16le -", process_io::SOURCE_NONE, process_io::SOURCE_STDOUT);
  process_io p2_impl("c:\\oggenc2 -o c:\\1.ogg -B 16 -R 44100 -C 2 -q 2 -", process_io::SOURCE_STDIN);
  p1.open(boost::ref(p1_impl));
  p2.open(boost::ref(p2_impl));
  while(!p1.eof()) {
    char buf[1024];
    p1.read(buf, 1024);
    int len = p1.gcount();
    p2.write(buf, len);
  }
  p1.close();
  p2.close();
  return 0;
}</pre>
<p>能转换，但是转换完进程就卡得死死的了。在p1.read那边不动了。</p>
<p>既然管道的另一边都已经被关闭了，为什么这边ReadFile不直接返回而是卡死？又仔细想想Windows SDK里面那个示例代码，自己创建了一个管道，一个用于读一个用于写，读的自己留着写的拿去给子进程写，写完以后子进程把写的关掉……等下，只有子进程关没用啊，自己这边也要关啊，这个HANDLE才算是完全释放了。哦我把HANDLE给子进程之后自己还留着。OK，那我就在CreateProcess之后把不用的HANDLE给关掉。再试，OK可以正常结束了。</p>
<p>补充：</p>
<p>多个进程互相交互的时候，又出了点问题。明明关掉了hStdInput的HANDLE，但是进程却没有认为读到了end，还在继续等待数据。又是多余的HANDLE的问题，但是怎么找却找不到哪里多出来：该关的关掉了。最后用processexporer查看，发现子进程也持有一个和我关掉的HANDLE的value一样的HANDLE。于是又想起Windows SDK上那个例子，果然还是因为没有完全按照例子做的缘故：自己留下来用来读写的HANDLE给继承到子进程里面去了，所以自己关掉管道的输入HANDLE以后子进程还持有一个输入HANDLE。方法就是用SetHandleInformation把自己留着用的那个HANDLE的继承属性去掉，才得以正常。</p>
<p>小总结一下：</p>
<p>1、要把管道的某个HANDLE传给子进程，要注意支持继承</p>
<p>2、复制构造函数如果没写的话，不确定人家会怎么用你的类，那就声明一个复制构造函数但是不实现</p>
<p>3、没用的HANDLE趁早关掉，如果允许继承的，因为管道一套是两个，所以如果设了继承那么就是两个都给你继承。</p>
<p>完整的代码下载： <a href="http://blog.sorayuki.net/wp-content/uploads/2012/10/pipe_test.7z">pipe_test</a></p>
<p>此代码只是试着玩的时候用的。代码稳定性、强度等都还…… -_,- 随便乱调用里面的函数程序还是会崩的（一堆东西都没检查嗯……</p>
<p>补充说明一下，boost.iostreams里面搞出来这类read和write不给你同时调用的，里面拿锁锁着。同时调用会有一个阻塞。不知道是不是可以设置成允许，我现在最新代码（没传上来）暂时是把input和output搞成两个类。</p>
<p>&nbsp;</p>
<p>pre</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=85</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
