sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; if (!CreatePipe(&hOutputRead,&hOutputWrite,&sa,0)) { printf("Error On CreatePipe1"); return; } sa2.nLength = sizeof(SECURITY_ATTRIBUTES); sa2.lpSecurityDescriptor = NULL;
sa2.bInheritHandle = TRUE; if (!CreatePipe(&hInputRead,&hInputWrite,&sa2,0)) { printf("Error On CreatePipe2"); return; }
STARTUPINFO si; PROCESS_INFORMATION pi; si.cb = sizeof(STARTUPINFO); GetStartupInfo(&si); si.hStdError = hOutputWrite; /// si.hStdOutput = hOutputWrite; ///写句柄赋予标准输出(或标准错误)句柄 si.hStdInput = hInputRead; /// //当父进程向子进程发送数据时,用SetStdHandle() //将管道的读句柄赋予标准输入句柄;在从子进程接收数据时, //则用SetStdHandle()将管道的写句柄赋予标准输出(或标准错误)句柄。 //然后,父进程可以调用进程创建函数CreateProcess()生成子进程。 // 如果父进程要发送数据到子进程,父进程可调用WriteFile() // 将数据写入到管道(传递管道写句柄给函数),子进程则调用GetStdHandle() // 取得管道的读句柄,将该句柄传入ReadFile()后从管道读取数据。
CloseHandle(hInputRead); CloseHandle(hOutputWrite); char szInPut[20] = "dir\r\n "; // 父进程向子进程发送数据,输入 WriteFile(hInputWrite, szInPut, strlen(szInPut), &dwWritten, NULL); char buffer[4096] = {0}; DWORD bytesRead; // 父进程向子进程得到数据,输出 while (true) {
if(ReadFile(hOutputRead,buffer,4095,&bytesRead,NULL) == NULL)
{ break; } printf(buffer);
CloseHandle(hInputWrite); CloseHandle(hOutputRead);}
本文链接
先说明一下,我不希望本文变成语言争论贴。希望下面的文章能让我们客观理性地了解C++这个语言。(另,我觉得技术争论不要停留在非黑即白的二元价值观上,这样争论无非就是比谁的嗓门大,比哪一方的观点强,毫无价值。我们应该多看看技术是怎么演进的,怎么取舍的。)
事由
周五的时候,我在我的微博上发了一个贴说了一下一个网友给我发来的C++程序的规范和内存管理写的不是很好(后来我删除了,因为当事人要求),我并非批判,只是想说明其实程序员是需要一些“疫苗”的,并以此想开一个“程序员疫苗的网站”,结果,@简悦云风同学直接回复到:“不要用 C++ 直接用 C , 就没那么多坑了。”就把这个事带入了语言之争。
我又发了一条微博:
@左耳朵耗子 : 说C++比C的坑更多的人我可以理解,但理性地思考一下。C语言的坑也不少啊,如果说C语言有90个坑,那么C++就是100个坑(另,我看很多人都把C语言上的坑也归到了C++上来),但是C++你得到的东西更多,封装,多态,继承扩展,泛型编程,智能指针,……,你得到了500%东西,但却只多了10%的坑,多值啊。
结果引来了更多的回复(只节选了一些言论):
- @淘宝褚霸也在微博里说:“自从5年前果断扔掉C++,改用了ansi c后,我的生活质量大大提升,没有各种坑坑我。”
- @Laruence在其微博里说: “我确实用不到, C语言灵活运用struct, 可以很好的满足这些需求.//@左耳朵耗子: 封装,继承,多态,模板,智能指针,这也用不到?这也学院派?//@Laruence: 问题是, 这些东西我都用不到… C语言是工程师搞的, C++是学院派搞的”
那么,C++的坑真的多么?我还请大家理性地思考一下。
C++真的比C差吗?
我们先来看一个图——《各种程序员的嘴脏的对比》,从这个图上看,C程序员比C++的程序员在注释中使用fuck的字眼多一倍。这说明了什么?我个人觉得这说明C程序员没有C++程序员淡定。
不要太纠结上图,只是轻松一下,我没那么无聊,让我们来看点真正的论据。
相信用过C++的程序员知道,C++的很多特性主要就是解决C语言中的各种不完美和缺陷:(注:C89、C99中许多的改进正是从C++中所引进的)
- 用namespace解决了很C函数重名的问题。
- 用const/inline/template代替了宏,解决了C语言中宏的各种坑。
- 用const的类型解决了很多C语言中变量值莫名改变的问题。
- 用引用代替指针,解决了C语言中指针的各种坑。这个在Java里得到彻底地体现。
- 用强类型检查和四种转型,解决了C语言中乱转型的各种坑。
- 用封装(构造,析构,拷贝构造,赋值重载)解决了C语言中各种复制一个结构体(struct)或是一个数据结构(link, hashtable, list, array等)中浅拷贝的内存问题的各种坑。
- 用封装让你可以在成员变量加入getter/setter,而不会像C一样只有文件级的封装。
- 用函数重载、函数默认参数,解决了C中扩展一个函数搞出来像func2()之类的ugly的东西。
- 用继承多态和RTTI解决了C中乱转struct指针和使用函数指针的诸多让代码ugly的问题。
- 用RAII,智能指针的方式,解决了C语言中因为出现需要释放资源的那些非常ugly的代码的问题。
- 用OO和GP解决各种C语言中用函数指针,对指针乱转型,及一大砣if-else搞出来的ugly的泛型。
- 用STL解决了C语言中算法和数据结构的N多种坑。
上述的这些东西填了不知有多少的C语言编程和维护的坑。少用指针,多用引用,试试autoptr,用用封装,继承,多态和函数重载…… 你面对的坑只会比C少,不会多。
C++的坑有多少?
C++的坑真的不多,如果你能花两到三周的时候读一下《Effecitve C++》里的那50多个条款,你就知道C++里的坑并不多,而且,有很多条款告诉我们C++是怎么解决C的坑的。然后,你可以读读《Exceptional C++》和《More Exceptional C++》,你可以了解一下C++各种问题的解决方法和一些常见的经典错误。
当然,C++在解决了很多C语的坑的同时,也因为OO和泛型又引入了一些坑。消一些,加一些,我个人感觉上总体上只比C多10%左右吧。但是你有了开发速度更快,代码更易读,更易维护的500%的利益。
另外,不可否认的是,C++中的代码出了错误,有时候很难搞,而且似乎用C++的人会觉得C++更容易出错?我觉得主要是下面几个原因:
- C和C++都没学好,大多数人用C++写C,所以,C的坑和C++的坑合并了。
- C++太灵活了,想怎么搞就怎么搞,所以,各种不经意地滥用和乱搞。
另外,C++的编译对标准C++的实现各异,支持地也千差万别,所以会有一些比较奇怪的问题,但是如果你一般用用C++的封装,继承,多态,以及namespace,const, refernece, inline, templete, overloap, autoptr,还有一些OO 模式,并不会出现奇怪的问题。
而对于STL中的各种坑,我觉得是程序员们还对GP(泛型编程)理解得还不够,STL是泛型编程的顶级实践!属于是大师级的作品,一般人很难理解。必需承认STL写出来的代码和编译错误的确相当复杂晦涩,太难懂了。这也是C++的一个诟病。
这和Linus说的一样 —— “C++是一门很恐怖的语言,而比它更恐怖的是很多不合格的程序员在使用着它”。注意我飘红了“很多不合格的程序员”!
我觉得C++并不适合初级程序员使用,C++只适合高级程序员使用(参看《21天学好C++》和《C++学习自信心曲线》),正如《Why C++》中说的,C++适合那些对开发维护效率和系统性能同时关注的高级程序员使用。
这就好像飞机一样,开飞机很难,开飞机要注意的东西太多太多,对驾驶员的要求很高,但你不能说飞机这个工具很烂,开飞机的坑太多。(注:我这里并不是说C++是飞机,C是汽车,C++和C的差距,比飞机到汽车的差距少太多太多,这里主要是类比,我们对待C++语言的心态!)
C++的初衷
理解C++设计的最佳读本是《C++演化和设计》,在这本书中Stroustrup说了些事:
1)Stroustrup对C是非常欣赏,实际上早期C++许多的工作是对于C的强化和净化,并把完全兼容C作为强制性要求。C89、C99中
不经意间发现,大半年没写博客了,自觉汗颜。实则2012后半年,家中的事一样接着一样发生,实在是没有时间。快过年了,总算忙里偷闲,把最近的一些技术成果,总结成了文章,与大家分享。
前些日子,也是项目需要,花了一些时间研究了HTTP Live Streaming(HLS)技术,并实现了一个HLS编码器HLSLiveEncoder,当然,C++写的。其功能是采集摄像头与麦克风,实时进行H.264视频编码和AAC音频编码,并按照HLS的协议规范,生成分段的标准TS文件以及m3u8索引文件。通过我的HLSLiveEncoder和第三方Http服务器(例如:Nginx),成功实现了HTTP Live Streaming直播,并在iphone上测试通过。我就把这当中的一些收获写在这里。
HLS技术要点分析
HTTP Live Streaming(HLS)是苹果公司(Apple Inc.)实现的基于HTTP的流媒体传输协议,可实现流媒体的直播和点播,主要应用在iOS系统,为iOS设备(如iPhone、iPad)提供音视频直播和点播方案。HLS点播,基本上就是常见的分段HTTP点播,不同在于,它的分段非常小。要实现HLS点播,重点在于对媒体文件分段,目前有不少开源工具可以使用,这里我就不再讨论,只谈HLS直播技术。
相对于常见的流媒体直播协议,例如RTMP协议、RTSP协议、MMS协议等,HLS直播最大的不同在于,直播客户端获取到的,并不是一个完整的数据流。HLS协议在服务器端将直播数据流存储为连续的、很短时长的媒体文件(MPEG-TS格式),而客户端则不断的下载并播放这些小文件,因为服务器端总是会将最新的直播数据生成新的小文件,这样客户端只要不停的按顺序播放从服务器获取到的文件,就实现了直播。由此可见,基本上可以认为,HLS是以点播的技术方式来实现直播。由于数据通过HTTP协议传输,所以完全不用考虑防火墙或者代理的问题,而且分段文件的时长很短,客户端可以很快的选择和切换码率,以适应不同带宽条件下的播放。不过HLS的这种技术特点,决定了它的延迟一般总是会高于普通的流媒体直播协议。
根据以上的了解要实现HTTP Live Streaming直播,需要研究并实现以下技术关键点
其中第1点和第2点,我之前的文章中已经提到过了,而最后一点,我们可以借助现有的HTTP服务器,所以,实现第3点和第4点是关键所在。
程序框架与实现
通过以上分析,实现HLS LiveEncoder直播编码器,其逻辑和流程基本上很清楚了:分别开启音频与视频编码线程,通过DirectShow(或其他)技术来实现音视频采集,随后分别调用libx264和libfaac进行视频和音频编码。两个编码线程实时编码音视频数据后,根据自定义的分片策略,存储在某个MPEG-TS格式分段文件中,当完成一个分段文件的存储后,更新m3u8索引文件。如下图所示:
上图中HLSLiveEncoder当收到视频和音频数据后,需要首先判断,当前分片是否应该结束,并创建新分片,以延续TS分片的不断生成。需要注意的是,新的分片,应当从关键帧开始,防止播放器解码失败。核心代码如下所示:
TsMuxer的接口也是比较简单的。
HLS分段生成策略和m3u8
1. 分段策略
- HLS的分段策略,基本上推荐是10秒一个分片,当然,具体时间还要根据分好后的分片的实际时长做标注
- 通常来说,为了缓存等方面的原因,在索引文件中会保留最新的三个分片地址,以类似“滑动窗口”的形式,进行更新。
2. m3u8文件简介
m3u8,是HTTP Live Streaming直播的索引文件。m3u8基本上可以认为就是.m3u格式文件,区别在于,m3u8文件使用UTF-8字符编码。
#EXT-X-MEDIA-SEQUENCE 第一个TS分片的序列号
#EXT-X-TARGETDURATION 每个分片TS的最大的时长
#EXT-X-ALLOW-CACHE 是否允许cache
#EXT-X-ENDLIST m3u8文件结束符
#EXTINF extra info,分片TS的信息,如时长,带宽等
一个简单的m3u8索引文件
运行效果
在Nginx工作目录下启动HLSLiveEncoder,并用VLC播放器连接播放
通过iPhone播放的效果
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HaibinNet网络科技,合作请联系QQ。(转载请注明作者和出处~)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
本文链接