2015年读书记录

年初的时候定下计划2015年一年至少阅读20本书,不管是小说还是专业书,现在离2015结束只有短短几天时间了,就梳理一下看过的书好了,顺带检查一下是否完成了任务。可惜结果很令我伤心。满心以为今年能读20本书,最后满打满算也只算读了16本书。今年欠了4本的债,明年2016年期望自己能够看下24本书吧。

小说

火星救援

看完电影之后还是不过瘾,于是乎补完小说。小说比电影更加精彩,电影因为时长的原因做了很多删减和改编,看小说更能体会到那种绝境的生存下去的力量。

小说摘录

  • 地球土壤中存在着几十种不同类型的细菌,它们对于植物的生长很关键。只有这样,它们才能繁殖得像……嗯,像细菌感染那样。
  • 为了营救我,一定花费了数亿美元。所有这些,就为了救我这个傻不愣登的植物学家,为吗呢? 好吧,我想我知道答案。有部分是因为我代表了进步、科学,还有我们梦想了几个世纪的行星际未来。但说真的,他们这么做的真正原因是:每个人都有一种本能,那就是把同伴救出来,有时候可能看上去不太像,但事实确实如此。
  • 远足的人在深山里迷路了,人们会发动搜救。火车出了事故,人们会排成长队献血。地震毁了一座城市,全世界的人都会捐出紧急物资。这种本能扎根于人类社会,每一种文化都不例外。是的,有些傻逼对此嗤之以鼻,但有多得多的人愿意这么做。正因为这样,才会有几十亿人站在我这边。

蝇王

舍友推荐,舍友因为是英语文学专业,推荐的这本黑色预言式的小说非常的赞,小说对人性的刻画实在是不能让人忘记。这本小说更让我认识了威廉·戈尔丁,英国很著名的小说家。这部小说主要讲述的就是因为战争流失到荒岛的一群小孩,因为利益形成各自集团,之间发生的冲突与矛盾。

摘录

  • 建立在社会理性基础上的民主在专制和暴力面前显得是那么的疲弱无力。
  • 苏格拉底说:“认识你自己”,至今仍是一句天启式的至理名言。在人类发展史上,人类对自身的恶的认识的确是极不清楚的。而人要认识自己,最深刻的莫过于认识自己的人性,如哲学家黎鸣所说:“自知者莫过于知己之人性,自胜者莫过于克服自己人性的弱点、抑制自己人性中潜在的恶念。”
  • 阻碍一个人进步的最大的敌人,往往是这个人自己,同理,阻碍一个文化发展的最大的敌人,往往是这个文化自身。
  • 暴风雨没有留下丝毫痕迹,海滩被冲刷得干干净净,就象被擦得锃亮的刀片。

1984

乔治奥威尔 反乌托邦小说,很早以前就想看不过没有坚持下来,今年总算是看完了。然后摘录一些警句。

  • 控制好人的思想,党就可以控制真理。
  • 人越来越工具化,越来越根据自己的利益和位置来改变事实。
  • 双重思想
  • 在成功的思想控制之下,一个人再也不会有他想法的对立面,而只知道什么才是“真理”的对立面。
  • 真理部
  • 思想罪
  • 谁掌握历史,谁就掌握未来;谁掌握现在,谁就掌握历史。
  • 一方面知道两者之间的矛盾,一方面又两者都相信;利用逻辑来反逻辑;一方面批判道德,一方面又自认为有道德
  • 档案司本身仅是真理部的一个部门而已。真理部的主要工作不是重建过去,而是向大洋国公民提供报纸、电影、课本、电屏节目、比赛、小说——也就是每种可以想象到的信息、指示或娱乐,从雕像到标语,从抒情诗到生物学论文,从小孩子用的拼写书到新话词典。真理部不仅要满足党的各种各样的需求,而且在较低层次上为了服务群众,各种工作也在全力进行着。
  • 你难道看不出新话的唯一目标就是窄化思想范围吗?到了最后,我们将会让思想罪变得完全不可能再犯,因为没有单词可以表达它。每种必要的概念将被一个单词精确地表达出来,这个单词的意义有严格规定,其他次要意义将被消除,然后被忘掉。
  • 如果有希望,它一定是在群众身上,因为只有在那里,在那些被漠视的大批人身上,在占大洋国人口百分之八十五的人身上,才有可能产生将党摧毁的力量。党无法从内部推翻,其敌人——如果有敌人的话——无法走到一起并相互确认。
  • 除非他们觉醒,否则永远不会反抗;但除非他们反抗,否则不会觉醒。
  • 只要他们继续干活、繁衍,他们别的行为就无关紧要。
  • 在党自身内部,没有几个人的观念是革命前就形成的。上一代人的绝大多数都在五六十年代的大清洗中被消灭了,幸存下来的极少数早就吓破了胆,思想上已经完全投降。
  • 只要让他们一直处于工作中,他们的态度便无关紧要
  • 战争是否真正发生着没有关系,而且因为不可能取得决定性胜利,战争进程的顺势逆势也没有关系,需要的只是应当保持战争状态。
  • 党有两个目标,一是征服全世界,二是一劳永逸地消灭独立思考的可能性。因此,党要解决的最主要难题有两个,一是如何在并非本人自愿透露的情况下发现他正在想什么,二是在没有预警的情况下于几秒钟内消灭上亿人口。
  • 二十世纪中叶进行的所谓“消灭私有财产”运动,其实意味着财富集中到了比以前少得多的人手里,不同之处是新的财富拥有者是个集团,而不是许多单独的人。
  • 大众从来不会自发造反,他们也从来不会仅仅因为受到压迫而造反。确实,只要不让他们掌握做比较的标准,他们就根本永远意识不到自己在受压迫。过去周期性发生的经济危机毫无必要,如今也不允许发生,但是其他情形,具有同样大范围的混乱状况能够而且确实会发生,只是不会带来政治性后果,因为不满不可能被表达得清晰有力。
  • 因此,从我们目前统治者的角度来说,唯一的真正危险,是从他们自身阶层分化出一个由能干、未尽其才、渴望权力的人所组成的集团,从而产生出自由主义和怀疑主义精神。
  • 寡头统治的要旨不是父传子、子传孙,而是坚持死者加诸生者的某种世界观和生活方式。只要它能指派自己的后继者,统治集团就永远会是统治集团。党所关心的不是血统上的永存,而是自身的不朽。只要等级化结构永远保持不变,至于是谁掌握权力并非重要。
  • “双重思想”意味着在一个人的脑子里,同时拥有两种相互矛盾的信念,而且两种都接受。
  • 混淆事实方面,也揭示了一种厚颜无耻的行径。和平部负责战争,真理部制造谎言,仁爱部负责拷打,富足部则制造饥饿
  • 此处的“政治”是最广义上的含义。即渴望将世界向某一方向推动,改变人们应该努力实现的那种社会的概念。同样,没有哪本书完全无政治倾向。那种艺术应与政治无关的观点本身即为一种政治态度。

哲学

论自由

约翰密尔,其实是很薄的书,因为当时英语课做presentation查资料找到,所以一直保存在待看列表。

全书要义可以概括为两条基本原则:一、个人的行为只要不涉及他人的利害,个人就有完全的行动自由,不必向社会负责;他人对于这个人的行为不得干涉,至多可以进行忠告、规劝或避而不理。二、只有当个人的行为危害到他人利益时,个人才应当接受社会的或法律的惩罚。社会只有在这个时候,才对个人的行为有裁判权,也才能对个人施加强制力量。  摘录自豆瓣

摘录

  • 公民自由或称社会自由,也就是要探讨社会所能合法施用于个人的权力的性质和限度
  • 自由,是指对于政治统治者的暴虐的防御。
  • 社会作为集体而凌驾于构成它的各别个人时,它的肆虐手段并不限于通过其政治机构而做出的措施。
  • 这两种思想方式各借对方的缺陷现出己方的功用,也在很大程度上各靠对方的反对才把己方保持在理性和健康的限度之内。
  • 真理在很大程度上乃是对立物的协调和结合问题
  • 在人类智力的现有状态下,只有通过意见分歧才能使真理的各个方面得到公平比赛的机会。
  • 似是而非地进行论证、对事实或论据予以压制、把案情的各项因素举陈错误、或者把反对方面的意见表述错误。
  • 在这一类触犯当中论战者所能犯的最坏的一种乃是把持见相反的人诋毁为坏的和不道德的人。
  • 人类应当有自由去形成意见并且无保留地发表意见,这所以成为必要的理由
  • 个人的自由必须约制在这样一个界限上,就是必须不使自己成为他人的妨碍。
  • 这些是他自己的本性经过他自己的教养加以发展和校改的表现——就称为具有一个性格
  • 总之,情事一到对于个人或公众有了确定的损害或者有了确定的损害之虞的时候,它就被提在自由的范围之外而被放进道德或法律的范围之内了。
  • 但积久却变成他们部落的习俗;而习俗在东方就等于宗教。
  • 第一,个人的行动只要不涉及自身以外什么人的利害,个人就不必向社会负责交代。
  • 第二,关于对他人利益有害的行动,个人则应当负责交代,并且还应当承受或是社会的或是法律的惩罚,假如社会的意见认为需要用这种或那种惩罚来保护它自己的话。
  • 总之,凡目标在于使人们不可能得到或难于得到某一货物的干涉都属于这一类。这类干涉可以反对之处,不在它们侵犯了生产者或销售者的自由,而在它们侵犯了购买者的自由。
  • 自由原则不能要求一个人有不要自由的自由。
  • 要知道,由国家强制教育是一回事,由国家亲自指导那个教育是完全不同的另一回事;人们所举的反对国家教育的一切理由,对于前者并不适用,对于后者则是适用的。
  • 不必要地增加政府的权力,会有很大的祸患。
  • 国家的价值,从长远看来,归根结蒂还在组成它的全体个人的价值。一个国家若只图在管理技巧方面或者在事务细节实践上所表现的类似的东西方面稍稍较好一些,而竟把全体个人智力的扩展和提高这一基本利益推迟下来;一个国家若只为——即使是为着有益的目的——使人们成为它手中较易制驭的工具而阻碍他们的发展,那么,它终将看到,小的人不能真正做出大的事;它还将看到,它不惜牺牲一切而求得的机器的完善,由于它为求机器较易使用而宁愿撤去了机器的基本动力,结果将使它一无所用。

快乐学哲学

快乐学哲学 英语课课程材料,通俗易懂的语言介绍西方哲学。推荐看英文原版。

技术

学习bash

学习bash 今年系统的学习一下Linux,但Linux体系太复杂,先从bash看起。

bash cookbook

更加详细一点的bash教程

参考bash 学习笔记.

鸟哥的Linux私房菜 基础学习篇(第二版) (第三版)

很经典,很详细,却也略显啰嗦的Linux入门书

笔记可以参考 linux 学习笔记

Boost程序库完全开发指南

因项目需要C++,Python的结合,看了boost.python部分,后来又自学了boost中的property_tree用来解析json。其他部分未详细看,只大略扫过。

Pro Git

学期中做了个讲座,大略的看了一些知识,Git实在强大,很多特性到现在我也没搞懂。这书要一直备着。

笔记参考git 学习笔记

Python核心编程(第二版)

只学了个大概,因为平时用C++多。

大数据时代

年初的时候读的,似乎已经忘得差不多了,总之“大数据”很玄乎。

其他

剧本结构设计

剧本结构设计 忘记了什么时候200-100的时候买的了,喜欢看电影,而我们总是谈论到剧情设计,于是就买了一本看。

Evernote超效率数字笔记术

一直订阅着这位作者的博客,看完这本书之后就全面使用Evernote中,放弃了OneNote,和Google Keep

摘录

  • 「剪貼」是一種被動而靜態的行爲,而「記錄」才是主動而動態的行動。

大教堂与集市

在观看纪录片《操作系统革命》中提到的这本书的作者,正好实验室有这本书,就拿了看了。这本书主要将软件开发的两大方式,正如书名所述,一种是公司内部集中式的开发,另外一种就是类似 GitHub 上那种分工合作式开发。这两者各有利弊,但作者作为 GNU 的倡导者必然是支持开源的。

别告诉我你会记笔记

别告诉我你会记笔记 学会记笔记是很重要的事情。

摘录

  • 成功的人总是把失败的经验写在笔记本上,然后重新出发
  • 你上谷歌搜索,只能找到别人的经验,唯有自己的笔记本,才是专属于你的资历累积。

看到最后一句话其实感触挺大,回想起 Google Reader 关闭,这真是 Google 做出的很差的一件事情。Reader 中的东西都曾经是阅读过,甚至做过笔记的东西,那也就是自己的东西,再怎么使用 Google 搜索,出来的东西也都不是自己的经验。所以现在我也在培养我隔断时间总结上一阶段的习惯,这样自己总结,自己写下来的东西才真正属于自己。

期待2016年美好的开始。


2015-12-29 book , reading

一个中国的缩影

很久没有看到这样反正真实中国的电影了,近两年的国产电影不是被早恋打胎的青春片,就是被各种古怪离奇的古装片,在就是无厘头的喜剧片所霸占。幸而有这样一部电影能够让2015年的国产电影画上一个稍稍圆满的句号。

剧情上,这样一部电影融入了太多真实的新闻,以至于会让人一一和某某事件联系上,人们往往说,电影往往是现实的表现。而这样一部老炮儿正是在真实这件事情上花了太多的功夫。从老北京的腔调到巷口胡同,从酒吧夜宿到马路飙车,从故友借钱到朋友援手,一件一件的小事敲打着人们的心,虽然故事发生在北京,虽然故事的主人公有着特殊的经历,可正是从这样的角度让我们看到了现代人们的人情冷漠,看到了整个中国的一个缩影,城管会不顾形象的殴打底层的百姓,匆匆的行人不会停下匆忙的脚步帮助路边无助的少女,甚至在看到别人跳楼时无情的煽动,更甚至于几十年的友情会在金钱面前变得一文不值。电影中的情节虽是虚构,却真实的让人发抖。电影看至一半就无法忍住打转的眼泪,同情六爷以及他的遭遇,为六爷的仁义感动。我们的社会正迈向黑暗,却少有六爷这样的人出面维护。为何我们会不相信路边求助的少女?为何我们不敢搀扶跌倒的老人?为何我们的眼里只有金钱?是我们少了什么东西吗?是我们被欺骗多次吗?亦或是像电影中说得那样“现在的人们,遇事都躲”。越来越多的人都不讲理,不讲规矩。往小了说就是百姓的日常百态,而往大了说就是三环十二少无法无天,在大就是他爹千万的账单。社会越来越没有理,越来越混乱,而这也仅仅是电影所表现出来的一部分而已。

剧情

在说到剧本设计上面,到现在很少的国产电影能够做到情节上的呼应,很多的时候就是主角想要做什么就凭空能够出现什么,而老炮儿在这方面做的非常的棒。现在能够列举的很多情节在前后发展中都能找打影子,六爷用报纸裹挟遗物扔到理发店呼应话匣子在理发店发现用报纸卷着的房产证,六爷心脏病发作呼应早先话匣子一直说心脏的问题,六爷在路边给建工女孩200元钱呼应片末女孩从远方寄来的挂号信,六爷在片尾穿军大衣用日本军刀独自约架呼应儿子和话匣子对话中对六爷十几年前一人单挑十几人的回忆。这样的剧本设计无意让故事的说服力大大增强。电影虽然有很多未交待的事情,但也正是因为这样才造就一部好电影的剧情。一部好电影就应该是将故事的一个切片展现给人们看,甚至能让人们产生就是电影结束了故事还在继续的错觉。如果真能做到那样,这样的电影离佳片也就不远了。而老炮儿正是这样一部电影,剧情没有交代六爷的妻子,也没有交代六爷故友的生活种种,同样也没有交代三环十二少,但是通过一些侧面的描述,通过电影的叙事交代,电影想要讲述的故事是完整的,那就够了。

表演

不得不说的冯小刚表演,早在电影上映之前就被无数的消息所报道,冯小刚凭借此片获得了金马影帝。一直很想看一看一个导演的表演功力,但是不得不说我被冯小刚的演技震撼到失语。可能原先还有一丝的“嘲讽”,只刚刚开头的城管戏,就让我对冯小刚刮目相看。以至于到最后和三环十二少演对手戏,再到拜访故人借钱救子,一丝一毫的表情动作,刷新了我对冯小刚的认识。片中饰演的六爷有着无比庞大的气场,平时看只是胡同巷子里面普普通通的市井小民,而一旦遇事,六爷凭借自己地位打抱不平,用“规矩”解决所有事情。

六爷的几场戏深深的印在了我的脑海里,一是开场和城管的戏,再就是到修车厂和三环十二少的戏,再到朋友(洋火儿)家借钱,到最后颐和园后野湖。每一个和冯小刚演对手戏的演员,无不给冯小刚的气场所压倒。

而其他的表演也就不想说什么了,这部电影只需要看冯小刚一个人演就够了,而六爷身边的一身肌肉的闷三儿(张涵予),无时不刻不在帮助六爷的话匣子(许晴),还有老实憨厚的灯罩儿,他们都个性十足,表演也到没有太大的瑕疵。尤其是闷三儿在修车厂,还有六爷被打闷三儿带领一帮人砸修车厂的几个镜头让张涵予非常帅气。只是在六爷面前就显得弱很多了。

因此最后在剧本上给7.5分,表演给8分,娱乐性思考性给8分。


2015-12-29 movie , china

2015年最喜欢的十部引进片

观影风向标做了一期引进片的排名,我顺便也简单地回忆了一下。然后根据2015年引进片豆列,找出了10部我非常喜欢的引进片。

就像观影风向标中所说,每个人都有一个自己的排名,没有最终的标准答案,每个人都有自己的喜好,不需要迁就谁,喜欢就好。

王牌特工:特工学院

当之无愧的年度最佳—-王牌特工:特工学院 2015-04-19,记得这部电影上映的时候并不是很了解,只是之后听了节目并看到一些影评觉得非常值得一看,在最后快下线的时候找了很远的一家影院看了。虽然当时同去的朋友对最后脑袋放烟花的镜头颇有微词,但是依然不减这部电影的魅力,我最喜欢的几大类电影中特工片一直靠前。而自从谍影重重马特达蒙不在参演,碟中谍系列,007系列偏离出我的口味稍远之后,很久没有看到一部对胃口的特工片了。不过很遗憾的是国内上映版的阉割。

火星救援

年度最佳科幻—-火星救援 2015-11-25,如果说2014年的星际穿越让我开始重拾起对宇宙的兴趣,那么这一部火星救援让我重拾起了对科幻片的兴趣。演员上有我很喜欢的马特达蒙,还有很多很多熟悉的脸孔,故事情节上有我喜欢的火星宇宙题材,这样一部电影如何能让我忘记。

模仿游戏

模仿游戏 2015-02-19,虽然是一部去年的电影,但是要是放到今年依然是一部非常棒的传记类电影,先不说图灵在计算机界的名声和地位,就卷福的生动表演也能让这部电影出彩。

头脑特工队

最佳动画—-头脑特工队 2015-11-01,天马行空的想象力,虽然说不上什么道理却非常有说服力的情节设定,以及让我找回的那份童真,无疑让这部电影在我这里的得分要高于大白 (●—●),说到超能陆战队,当然也是很不错的一部电影,剧本,情节设计毫无问题,只是我觉得过于好莱坞流水线产品,我在年初的时候就看完了这部片子,那时候这部片子还没有在国内上映,而过了一些时候之后,突然满大街,身边大家都在说着大白,我其实有些疑惑,后来才发现是因为国内上了,然后布天盖地的宣传,其实我不太明白是因为宣传造就了这部电影还是因为其他。

我的个神啊

我的个神啊 PK 2015-05-29,宗教题材的电影,观影风向标中波米总结的很好,这样一部电影有着自己的底线,没有否定宗教,也没有否定信仰,只是否定了打着宗教信仰招摇撞骗的神棍。这样一部电影在我们这样一个无神论的国家里,没有批判什么,但是身边的好多人却都以为这样的一部电影是反宗教的,真是太天真。

前目的地

前目的地 2014-12-04,虽然在14年年末的时候已经看了,看到时候甚至一度以为是一部超级烂的电影,因为剧情设定在前半段时间推进太缓慢,在酒店叙事那一段差点让我睡着,但是之后的剧情发展让我深深的膜拜这部电影。

超能查派

超能查派 2015-05-21,个人很喜欢的电影,一直对机器,人性很感兴趣,导演很聪明,将机器人形容成新生的婴儿,他的成长环境决定了机器人的人格,他的是非判断基于他所学习到的东西,而这不正也是一个人之所以为为一个人的基本常识吗?我们如何做一个判断?无非也就是从生活经历或听,或读,或看到的来下决定。当然这样一部电影,有主题,有思想,有特效,对我来说也就够了。

碟中谍5:神秘国度

碟中谍5:神秘国度 2015-09-13 之前也提到过碟中谍系列的特工片一直是很喜欢的系列,当然这一部也差,只是相较其他传达思考的电影来说,这真是纯粹的爆米花电影了。

速度与激情7

速度与激情7 2015-04-12 话题大片啦,加上保罗沃克的离世,加上片尾的煽情歌曲,这样一部有剧情,有动作,有视效的大片总有其一席之位。

歌曲改变人生

歌曲改变人生 Begin Again 2014-12-01 舍友推荐,虽然去年年末已看过,放到今年这样一部音乐类电影依然给我非常棒的感觉。

其他推荐影片

  • 机械姬 Ex Machina

想看却未看的引进片

  • 极速风流 Rush

    两年前的片子,如果不听节目真的不知道这样一部电影。

  • 思悼

    年末的时候不止一遍的在各种榜单上看过,总之有其上榜的理由吧。

  • 小王子

    准备看小说,然后再补一下好了。


2015-12-28 movie

记一天

早晨 初雪

morning snow

中午 披萨

pizza

中午 火锅

hotpot

饭后

chair

人生如此夫复何求。


2015-12-27 holiday

boost 学习笔记 4:智能指针 smart_ptr

对应书中第三章 内存管理,着重讲 boost 实现的智能指针,和内存池pool 等概念。众所周知,C++没有提供Java中的垃圾回收机制,因此 boost 实现智能指针用来管理内存避免一些问题。C++继承 C 高效灵活地指针,但是同样带了了很多问题:

  • 内存泄露 memory leak
  • 野指针 wild pointer
  • 越界访问 access denied

虽然STL提供了 auto_ptr,但是受限太多(不能放到容器中,因为不支持拷贝构造函数和赋值),因此很少有人使用。

智能指针(smart_ptr)是Boost各组件中,应用最为广泛的一个。使用智能指针需包含以下头文件,如果只使用智能指针 shared_ptr 可以只包含同名头文件。

#include <boost/smart_ptr.hpp>
using namespace boost;

Boost从很早就提供了如下的智能指针,并且功能一直保持稳定:

  • scoped_ptr:不可拷贝与赋值,承载new,只能在 scoped_ptr 声明的作用域内使用。
  • scoped_array:不可拷贝与赋值,承载new []。
  • shared_ptr:可拷贝,承载new。boost 库中重要组成,重点学习。
  • shared_array:可拷贝,承载new []。
  • weak_ptr:弱引用。
  • intrusive_ptr:需要自实现计数功能的,引用计数智能指针。

有其他任何问题,请查阅官方文档: http://www.boost.org/doc/libs/1_60_0/libs/smart_ptr/smart_ptr.htm

scoped_ptr

主要特点

  • scoped_ptr 只限于作用域内使用
  • 指针管理权不可转移,不支持拷贝构造函数与赋值操作。

从名字就可以看出,这种智能指针只限于作用域内使用,无法转移内置指针的管理权(不支持拷贝、=赋值等) 但是作用也很显然,例如:

void test()
{
    int* p = new int(3);
    ...
    delete p;
}

假设定义到delete之中…发生了异常,那么p就无法被delete,造成了内存泄漏。使用scoped_ptr就可以很好解决这个问题,只需要new的时候放到scoped_ptr之中就可以了。

主要用法

scoped_ptr 常用方法:

假设:

scoped_ptr<T> ptr_t(new T); // 假设内置指针为p_t

则:

  • ptr_t->get(),返回内部管理的指针,但禁止在get()出来的指针上执行delete。
  • ptr_t->xxx(),等同于p_t->xxx()
  • ptr_t.reset(),delete内部持有的p_t。
  • 假设T支持直接赋值,*ptr_t = xxx。
  • 再次强调,scoped_ptr不能做拷贝、赋值等转移指针管理权限的事情。因此,class内置域为scoped_ptr<T>是不允许的,除非class也禁止拷贝、赋值

例子:

// scoped_ptr usage
scoped_ptr<string> sp(new string("text"));
cout << *sp << endl;
cout << sp->size() << endl;

// pointer 管理权移交 scoped_ptr
auto_ptr<int> ap(new int(10));
scoped_ptr<int> scoped(ap);
assert(ap.get() == 0);

ap.reset(new int(20));
cout << *ap << ", " << *scoped << endl;

auto_ptr<int> ap2;
ap2 = ap;
assert(ap.get() == 0);			// ap is null-pointer

具体例子如下:

#include <iostream>
#include <boost/smart_ptr.hpp>

class SmallClass
{
    public:
        SmallClass(int x_val)
        {
            x = x_val;
            std::cout << "SmallClass construct " << x << std::endl;
        }

        virtual ~SmallClass()
        {
            std::cout << "SmallClass destory " << x << std::endl;
        }

        int GetX()
        {
            return x;
        }

    private:
        int x;
};

int main()
{
    std::cout << "main start" << std::endl;

    // scoped_ptr on basic
    boost::scoped_ptr<int> int_ptr(new int);
    *int_ptr = 100;
    ++*int_ptr;
    std::cout << *int_ptr << std::endl;

    // scoped_ptr on class
    boost::scoped_ptr<SmallClass> sc_ptr(new SmallClass(0));
    std::cout << sc_ptr->GetX() << std::endl;
    sc_ptr.reset();
    std::cout << "main end" << std::endl;

    return 0;
}

scoped_array

主要特点

同 scoped_ptr 基本一样,只不过可接受数组的new [],多了下标访问操作,其他类似。

  • 构造函数指针必须是 new[] 的结果,而不能是 new 表达式的结果
  • 没有 *, -> 操作符重载,因为 scoped_array 持有的不是一个普通指针
  • 析构函数使用 delete[] 释放资源,而不是 delete
  • 提供 operator[] 操作符重载,可以像普通数组一样用下标访问
  • 没有 begin(), end() 等类似容器迭代器操作函数

主要用法

scoped_array 轻巧方便,没有给程序增加额外负担,但是 scoped_array 功能有限,不能动态增长,也没有迭代器支持,不能搭配 STL 算法,仅有一个纯粹的“裸”数组接口。在需要动态数组的情况下我们应该使用 std::vector 。

例子如下:

#include <iostream>
#include <string>
#include <vector>
#include <boost/smart_ptr.hpp>

using namespace std;
using namespace boost;

int main(int argc, const char * argv[]) {

    scoped_array<int> scopedarr(new int[100]);
    scopedarr[0] = 100;           // 赋值
//    *(scopedarr+1) = 200;       // error

	// fill array with 100 value 2
    fill_n(&scopedarr[0], 100, 2);
    cout << scopedarr[2] << endl;

    scopedarr[3] = scopedarr[0]+scopedarr[1];
    cout << scopedarr[3] << endl;

    cout << scopedarr.get()[3] << endl;

    return 0;
}

shared_ptr

主要特点

最像指针的“智能指针”,是 boost.smart_ptr 库中最有价值,最重要的组成部分。支持拷贝构造函数、支持赋值操作。重载了*和->操作符用来模仿原始指针的行为。目前已成为tr1标准的一部分,发展自原始的auto_ptr,内置引用计数。

  • 支持拷贝构造函数,赋值操作
  • 重载 * 和 -> 操作符模仿原始指针
  • 内置引用计数

使用时候有3点要特别注意:

  1. 禁止get()得到指针地址后,执行delete,这个同scoped_ptr。
  2. 禁止循环引用,否则会出内存泄漏。
  3. 不要将shared_ptr用于函数的临时参数:

    // 下面这个是OK的。 void ok() { shared_ptr p(new int(2)); f(p, g()); }

    // 下面这个就可能内存泄漏! void bad() { f(shared_ptr(new int(2)), g()); }

主要使用

看看基本的使用例子。

#include <iostream>
#include <string>
#include <vector>
#include <random>
#include <boost/smart_ptr.hpp>
#include <boost/make_shared.hpp>

using namespace boost;

void print_count_func(shared_ptr<int> p){
    std::cout << "cout: " << p.use_count() << " value= " << *p << std::endl;
}

class shared {
private:
    shared_ptr<int> p;

public:
    shared(shared_ptr<int> _p):p(_p) {}
    void print(){
        std::cout << "cout: " << p.use_count() << " value= " << *p << std::endl;
    }
};

int main(int argc, const char * argv[]) {

    // 构造函数
    // 无参数构造空指针 shared_ptr
    shared_ptr<int> p0;

    // shared_ptr(Y * p) 获得指向类型 T 的指针 p 的管理权
    shared_ptr<int> p1(new int);
    *p1 = 12;

    // shared_ptr(shared_ptr const & r) 从另外 shared_ptr 获取管理权,两个 shared_ptr 同时共享一个指针管理权
    shared_ptr<int> p2(p1);                 // p1 引用计数加1
    std::cout << "p1 count: " << p1.use_count() << " value: " << *p2 << std::endl;
    *p2 = 13;
    std::cout << "p1 count: " << p1.use_count() << " value: " << *p2 << std::endl;
    p2.reset();
    std::cout << "p1 count: " << p1.use_count() << std::endl;


    // shared_ptr(std::auto_ptr<Y> & r) 从 auto_ptr 获得指针管理权, 同时 auto_ptr 失去管理权
    std::auto_ptr<int> p;
    shared_ptr<int> p3(p);

    // operator= 赋值操作符,从另外一个 shared_ptr 或 auto_ptr 获得指针管理权,等同于构造函数
    shared_ptr<int> p4 = p1;

    // shared_ptr(Y * p, D d) 类似于 shared_ptr(Y * p)
    // 第一个参数是要被管理的指针,第二个删除参数 d 告诉 shared_ptr 在析构时不使用 delete 来操作指针 p,而使用 d 来操作,把 delete p 换成 d(p)


    // usage
    shared_ptr<int> sp(new int(100));
    print_count_func(sp);
    shared shared1(sp), shared2(sp);

    shared1.print();
    shared2.print();

    *sp = 200;

    print_count_func(sp);
    shared1.print();

    // make_shared
    // include <boost/make_shared.hpp>

    shared_ptr<std::string> strp = make_shared<std::string>("make_test");
    shared_ptr<std::vector<int> > vecp = make_shared< std::vector<int> >(10, 3);

    std::cout << *strp << " " << vecp->size() << std::endl;

    // shared_ptr in vector
    // 因为 shared_ptr 实现了拷贝构造、=赋值构造等函数,因此可以完美的放入STL容器中。
    typedef std::vector< shared_ptr<int> > vs;
    vs v(10);

    int i = 0;
    for (vs::iterator pos = v.begin(); pos != v.end(); ++pos) {
        *pos = make_shared<int>(++i);
        std::cout << *(*pos) << ", ";
    }
    std::cout << std::endl;

    shared_ptr<int> ptest = v[9];
    *ptest = 100;
    std::cout << *v[9] << std::endl;


    return 0;
}

然后来讨论类中成员使用shared_ptr的例子,先扯一个稍微远一点的。假设我们类的成员需要new出来,先来看一个错误的例子:

#include <iostream>
#include <string>
#include <vector>
#include <random>
#include <boost/smart_ptr.hpp>
#include <boost/make_shared.hpp>

using namespace boost;

class ClassOne
{
public:
    ClassOne(int data_param):data(NULL)
    {
        init(data_param);
        std::cout << "construct" << std::endl;
    }

    virtual ~ClassOne()
    {
        if(data)
        {
            delete data;
        }
        data = NULL;
    }

    void init(int data_param)
    {
        if(data)
        {
            delete data;
        }
        data = new int(data_param);
    }

private:
    int* data;
};

int main(int argc, const char * argv[]) {
    ClassOne c1(10);
    ClassOne c2(c1);
    ClassOne c3 = c2;

    return 0;
}

上面的ClassOne没有问题,但是会由编译器生成默认的拷贝、赋值构造函数,于是,当c3=c2或者c2(c1)时, 指针data的地址被复制了多份,c1、c2、c3各持有一份,析构的时候就被delete了3次,于是memory error是必须的了。遇到这种情况传统做法就是,c1、c2、c3各保存独立的data区域,即深拷贝:自己写拷贝构造、赋值构造函数。

#include <iostream>
#include <string>
#include <vector>
#include <random>
#include <boost/smart_ptr.hpp>
#include <boost/make_shared.hpp>

using namespace boost;

class ClassOne
{
public:
    ClassOne():data(NULL) {}
    ClassOne(int data_param):data(NULL)
    {
        init(data_param);
        std::cout << "construct" << std::endl;
    }

    virtual ~ClassOne()
    {
        if (data) {
            delete data;
        }
        data = NULL;
    }

    ClassOne(const ClassOne& rhs){
        std::cout<< "copy " <<std::endl;
        data = NULL;
        init(*rhs.data);
    }

    ClassOne& operator = (const ClassOne& rhs){
        std::cout<< "assign " <<std::endl;
        delete data;               // delete your own old data
        data = new int(*rhs.data); // clone the rhs's data
        return *this;
    }

    void init(int data_param)
    {
        delete data;
        data = new int(data_param);
    }

private:
    int* data;
};

int main(int argc, const char * argv[]) {
    ClassOne c1(10);
    ClassOne c2(c1);       // 实际调用的是B(A)拷贝操作 call copy constructor
    ClassOne c3;
    c3 = c1;               // 申明之后进行赋值运算 call assignment function
    return 0;
}

现在我们假设另外一种情况,即data仍然需要从堆上new出来,但可以被若干实例共享。此时可以用 shared_ptr,而且甚至不需要编写拷贝构造、=赋值构造,就可以。如下:

#include <iostream>
#include <string>
#include <vector>
#include <random>
#include <boost/smart_ptr.hpp>
#include <boost/make_shared.hpp>

using namespace boost;

class ClassOne
{
public:
    ClassOne(int data_param):ptr_data(new int)
    {
        init(data_param);
        std::cout << "construct" << std::endl;
    }

    virtual ~ClassOne()
    {

    }

    int get_data() const{
        return *ptr_data;
    }

    long ptr_count() const {
        return ptr_data.use_count();
    }

    void init(int data_param)
    {
        *ptr_data = data_param;
    }

private:
    shared_ptr<int> ptr_data;
};

int main(int argc, const char * argv[]) {
    ClassOne c1(10);
    ClassOne c2(c1);
    ClassOne c3 = c2;

    std::cout << c1.ptr_count() << std::endl;
    std::cout << c2.get_data() << std::endl;

    return 0;
}

实际上,c++编译器自动生成的拷贝、=赋值构造函数完成了ptr_data的赋值拷贝工作,而智能指针赋值拷贝的同时,引用计数也加1了。在默认析构函数也是如此,析构函数执行之后,会调用所有成员(ptr_data)的析构函数,检查引用计数都为0后,会delete掉这个int。 从而完美的完成了无内存泄漏的、无内存出错的、多个实例之间的指针变量共享。

shared_array

同scoped_array类似,shared_array是shared_ptr的数组版本,不再赘述。

weak_ptr

weak_ptr 被设计为与 shared_ptr 共同工作,可以从一个 shared_ptr 或者另一个 weak_ptr 对象构造,获得资源的观测权。但是 weak_ptr 没有共享资源,它的构造不会引起指针引用计数的增加,同时,在析构的时候也不回引起引用计数的减少。

shared_ptr看起来已经很完美了,但有个致命缺陷:不能管理循环引用的对象。 看如下的例子:

#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>

class parent;
class children;

typedef boost::shared_ptr<parent> parent_ptr;
typedef boost::shared_ptr<children> children_ptr;

class parent
{
public:
    ~parent() { std::cout <<"destroying parent\n"; }

public:
    children_ptr children;
};

class children
{
public:
    ~children() { std::cout <<"destroying children\n"; }

public:
    parent_ptr parent;
};

void test()
{
    parent_ptr father(new parent());
    children_ptr son(new children);

    father->children = son;
    son->parent = father;
}

void main()
{
    std::cout<<"begin test...\n";
    test();
    std::cout<<"end test.\n";
}

由于parent和child相互引用,他们的计数永远都为1,所以这样使用shared_ptr必然会导致内存泄漏。 boost::weak_ptr必须从一个boost::share_ptr或另一个boost::weak_ptr转换而来,这也说明,进行该对象的内存管理的是那个强引用的boost::share_ptr。boost::weak_ptr只是提供了对管理对象的一个访问手段。 弱引用不更改引用计数,类似普通指针,只要把循环引用的一方使用弱引用,即可解除循环引用。 还有两个常用的功能函数:expired()用于检测所管理的对象是否已经释放;lock()用于获取所管理的对象的强引用指针。

class children
{
public:
    ~children() { std::cout <<"destroying children\n"; }

public:
    boost::weak_ptr<parent> parent;
};

再次强调,weak_ptr必须从shared_ptr而来。

#include <boost/smart_ptr.hpp>
#include <iostream>

int main()
{
    boost::shared_ptr<int> ptr(new int(10));
    std::cout << ptr.use_count() << std::endl;
    boost::weak_ptr<int> ptr_weak(ptr);
    std::cout << ptr.use_count() << std::endl;
    std::cout << ptr_weak.expired() << std::endl;
    return 0;
}

intrusive_ptr

intrusive_ptr是一种“侵入式”的引用计数指针,实际并不提供引用计数功能,而是要求被存储的对象自己实现引用计数功能。可以应用于以下两种情形:

  • 对内存占用要求非常严格,要求必须与原始指针一样;
  • 现存代码已经有了引用计数机制管理的对象。 它提供intrusive_ptr_add_ref和intrusive_ptr_release函数接口供boost::intrusive_ptr调用。

参考


2015-12-24 boost , C++

boost 学习笔记 3: date_time

date_time 日期相关的库,是 boost 中少数需要编译的库,但是实践中,在 Linker 链接器中链接 libboost_date_time.a 即可。也就是在链接时,加上 -l(boost_date_time) 参数。

头文件

使用 date_timer 需要包含以下头文件。 diedaiqi #include <boost/date_time/gregorian/gregorian.hpp> using namespace boost::gregorian;

date基本使用

将时间想象成无线延伸的实数轴,时间点是数轴上的一个点,时间段就是区间,时长是有正负号的标量,两个时间点差,不属于数轴。时间点,时间段,时长之间可以进行运算,有些有实际意义,例如时间点+时长=时间点,时长+时长=时长,时间段交集∩时间段=时间段,时间点属于∈时间段等等,有些无意义等等。

date_time 库支持无限时间和无效时间(NADT, Not Available Date Time)。

日期基于格里高利历,支持从 1400-01-01 到 9999-12-31 之间的日期计算,不支持公元前日期,名字空间 boost::gregorian, 需要包含上文所述头文件。

构造

date 是 date_time 库处理日期的核心类,使用32位整数作为内部存储,以天位单位表示时间点概念,日期的构造方法

date d1;					// 无效日期
date d2(2010,1,1);
date d3(2000, Jan, 1);
date d4(d2);				// 拷贝构造

//you can compare between d1 d2 d3 d4 with == < >

也可以直接使用字符串来构造:

date d0 = from_string("1999-12-31");
date d5 ( from_string("2005/1/1") );
date d6 = from_undelimited_string("20001109");

或者直接从工厂类构造,调用 day_clock 的静态成员函数 local_day() 或者 universal_day() 返回当天的日期对象,分别是本地日期和 UTC 日期。 local_day() 依赖于操作系统时区设置。

// local date and UTC date
cout << day_clock::local_day() << endl;
cout << day_clock::universal_day() << endl;

特殊的日期:

date neg(neg_infin);			//negative infinite time
date pos(pos_infin);			//positive infinite time
date notdate(not_a_date_time);	//not a date time
date maxdate(max_date_time);	//max date
date mindate(min_date_time); 	//min date
cout << neg << endl << pos << endl << maxdate << endl << mindate <<endl;

date访问

date 成员函数 year(), month(), day() 返回年月日; year_month_day() 返回 date::ymd_type 结构,一次性获取年月日信息。

date nowdate = day_clock::local_day();
date::ymd_type ymd = nowdate.year_month_day();
cout << ymd.year << endl << ymd.month << endl << ymd.day << endl;

// day_of_week() return Monday, Tuesday..
// day_of_year() return number of the day in this year, max 366
// end_of_month() return the date of end of month
cout << nowdate.day_of_week() << endl;
cout << nowdate.day_of_year() << endl;
cout << nowdate.end_of_month() << endl;

day_of_week() 返回 date 星期数, 0 表示星期天; day_of_year() 返回 date 是当年第几天,最多366; end_of_month() 返回当月最后一天的 date 对象。

week_number() 返回date所在周是当年的第几个周,范围0到53. 如果年初的几天为去年的周,则周数为53,即第0周。

date 有5个 is_xxx() 函数,用于检验日期是否是一个特殊日期

  • is_infinity() 是否无限日期
  • is_neg_infinity() 是否负无限日期
  • is_pos_infinity() 是否正无限日期
  • is_not_a_date() 是否无效日期
  • is_special() 是否任意一个特殊日期

date输出

date 对象转成字符串输出

date now = day_clock::local_day();
// date output
cout << to_simple_string(now) << endl;			//YYYY-mmm-DD  mmm English
cout << to_iso_string(now) << endl;				//YYYYMMDD
cout << to_iso_extended_string(now) << endl;		//YYYY-MM-DD
cout << now << endl;

支持流输入

date inputdate;
cout << "Input a date: ";						// 2010-Jan-01
cin >> inputdate;								//default using YYYY-mmm-DD English abbr
cout << "\nThe input date is: " << inputdate << endl;

日期长度 date_duration

日期长度类 date_duration , 天为单位,度量时间长度的标量。值为任意整数,可正可负。

成员函数 days() 返回时长天数, is_special() 和 is_negative() 可判断 date_duration 对象是否为特殊值,或者负值, unit() 返回时长最小单位,即1

date_duration 支持全序比较操作 ==, != , <, <= 等等,支持加减法,递增递减,支持除整数,其他乘法,取模,取余不支持。

date_time 库为 date_duration 定义了一个常用的 typedef:days 名字。

days dd1(10);			// ten days
weeks w(2);				// two weeks
months m(5);			// five months
years y(2);				// two years

months m2 = y + m;		// two years and 5 months

日期计算

日期支持简单加减运算

// date compute
date dstart(2000,1,1), dend(2008,8,8);
cout << dend - dstart << endl;			// 3142 days

dstart += days(10);						// 2000-1-11
dstart += months(2);					// 2000-3-11
dstart -= weeks(1);						// 2000-3-4
dstart += years(4);						// 2004-3-4

//something need to be noticed
date endofmonth(2010, 3, 30);
endofmonth -= months(1);				//2010-2-28
endofmonth -= months(1);				//2010-1-31
endofmonth += months(2); 				//2010-3-31

日期区间 date_period

date_period 类来表示日期区间,时间轴上是一个左闭右开区间,端点是两个date对象,左值必须小于右值。

// date period
date_period pd(dstart, dend);			// (date, date)
date_period pd1(dstart, days(10));		// (date, days)

cout << pd << endl;

pd.shift(days(10));						// shift days 平移N天,长度不变
cout << pd << endl;
pd.expand(days(3));						// expand days 像两端扩展三天,长度加2n天
cout << pd << endl;

成员函数 begin() & end() 返回日期区间两个端点, end() 返回 last() 后的第一天。

date_period 可以全序比较运算,依据区间端点,即第一个区间的end() 和第二个区间的 begin(), 判断两个区间在时间轴上的位置大小。如果日期区间相交或者包含,比较操作无意义。

date_period 支持输入输出操作符,默认的输入输出格式是 YYYY-mmm-DD/YYYY-mmm-DD.

date date2014 = from_string("2014-01-01");
date nowdate = day_clock::local_day();
date_period pdl(date2014, nowdate);
date one_day_in_2015 = from_undelimited_string("20150101");
cout << pdl.contains(one_day_in_2015) << endl;

/*
 * date_period
 * is_before(), is_after()
 * contains()
 * intersects()				区间是否存在交集
 * intersections()			返回两个区间的交集
 * is_adjacent()			是否相邻
 * merge()					返回两个区间并集,如果无交集或者不相邻则返回无效区间
 * span()					合并两区间及两者间的间隔
 */

日期迭代器

/*
 * date iterator
 * use only ++iterator, --iterator, don't use iterator++, iterator--
 */
date d(2006,11,26);
day_iterator d_iter(d);
++d_iter;

year_iterator y_iter(d,3);		// 增减步长为3年
++y_iter;						// 增加3年
cout << y_iter->year();

综合运用

void print_one_month(){
	date nowdate = day_clock::local_day();
	date month_start(nowdate.year(), nowdate.month(), 1);
	date month_end = nowdate.end_of_month();

	for(day_iterator d_iter(month_start); d_iter != month_end; ++d_iter){
		cout << *d_iter << " " << d_iter->day_of_week() << "\t";
		if(d_iter->day_of_week() == boost::date_time::Sunday){
			cout << endl;
		}
	}
}
/*
 * input your birthday and years after that output the day of
 * future birthday
 */
void birthday_week(){
	date birthday(1992, 06, 05);
	years y30(30);
	date future_birthday = birthday + y30;
	cout << future_birthday.day_of_week() << endl;
}
/*
 * printout the days passed since you were born.
 */
void days_passed(){
	date birthday(1990, 10, 8);
	date nowdate = day_clock::local_day();
	cout << "Since you were born, there were " << nowdate - birthday << " days passed!"<< endl;
}

时间长度 time_duration

度量基本小时、分钟和秒钟,秒以下精确到微秒。

time_duration 有子类,度量不同时间分辨率,分别是: hours,minutes,seconds,millisec/milliseconds,microsec/microseconds和nanosec/nanoseconds。

#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::posix_time;

// 2 h 01 m 06.001 s
time_duration td(1, 60, 60, 1000*1000*6 + 1000);

hours h(1);				// one hour
minutes m(10);
seconds s(30);
millisec ms(1);

time_duration td1 = h + m + s + ms;

time_duration td2 = duration_from_string("1:10:30:001");
cout << td2 << endl;			// 01:10:30.001000
cout << to_simple_string(td1) << endl;
cout << to_iso_string(td2) << endl;

时间点 ptime

构造函数中同时指定date和time_duration对象,ptime等于一个日期加上当天的时间偏移,轻量级对象,可以被高效的任意拷贝和赋值,支持全序比较和加减运算。

using namespace boost::gregorian;

ptime p(date(2010,3,5), hours(1));
ptime p1 = time_from_string("2012-09-11 09:09:09");
cout << p1 << endl;
ptime p2 = from_iso_string("20100909T011001");
cout << p2 << endl;

ptime nowtime = second_clock::local_time();			// second accurate
ptime nowtime2 = microsec_clock::universal_time();	// milli second accurate

ptime pnot(not_a_date_time);
ptime pinf(pos_infin);

cout << to_simple_string(nowtime) << endl;
cout << to_iso_string(nowtime) << endl;
cout << to_iso_extended_string(nowtime) << endl;

时间区间 time_period

time_period tp1(nowtime, hours(8));					// start from nowtime, last for eight hours
tp1.shift(hours(2));
tp1.expand(hours(1));

时间迭代器和日期迭代器,时间迭代器需要时间点和时间长度来构造。

ptime p(day_clock::local_day(),hours(10));
for (time_iterator t_iter(p, minutes(10)); t_iter < p+hours(1); ++ t_iter) {
    cout << *t_iter << endl;
}

2015-12-23 boost , C++

boost 学习笔记 2: timer

C++中操作时间的类。timer 是一个很小的库,提供简易的度量时间和进度显示的功能,可用于测试性能计时任务等大多数情况。timer 库包含三个组件, 计时器类 timer, progress_timer 和进度条指示类 progress_display。对应书中第二章内容。

timer

头文件

timer 位于boost命名空间,需要包含头文件 <boost/timer.hpp> 即:

#include <boost/timer.hpp>
using namespace boost;

基本方法

timer变量声明之后即开始计时工作,之后可以调用 elapsed() 方法来测量对象自创建之后所流逝的时间。成员函数 elapsed_max()elapsed_min() 方法返回 timer 能够测量的时间最大值和最小值,输出单位秒。timer类的实现代码很少,可参考源码学习。

/**
	 * timer simple and useful, suitable for short time task.
	 * Do not use it to calculate time for long time.
	 * For long time calculate check date_time lib
	 */
using boost::timer;
timer t;															// declare timer start counting
cout << "max timespan:" << t.elapsed_max() / 3600 << "h" << endl;
cout << "min timespan:" << t.elapsed_min() << "s" << endl;
cout << "now time elapsed:" << t.elapsed() << "s" << endl;

timer 实现源码

使用标准库头文件 中的 std::clock() 函数。

class timer
{
 public:
         timer() { _start_time = std::clock(); } // postcondition: elapsed()==0
//         timer( const timer& src );      // post: elapsed()==src.elapsed()
//        ~timer(){}
//  timer& operator=( const timer& src );  // post: elapsed()==src.elapsed()
  void   restart() { _start_time = std::clock(); } // post: elapsed()==0
  double elapsed() const                  // return elapsed time in seconds
    { return  double(std::clock() - _start_time) / CLOCKS_PER_SEC; }

  double elapsed_max() const   // return estimated maximum value for elapsed()
  // Portability warning: elapsed_max() may return too high a value on systems
  // where std::clock_t overflows or resets at surprising values.
  {
    return (double((std::numeric_limits<std::clock_t>::max)())
       - double(_start_time)) / double(CLOCKS_PER_SEC); 
  }

  double elapsed_min() const            // return minimum value for elapsed()
   { return double(1)/double(CLOCKS_PER_SEC); }

 private:
  std::clock_t _start_time;
}; // timer

使用建议

  • 不适合高精度计时,精度依赖操作系统或编译器
  • 不适合大跨度时间段测量,最大跨度只有几百小时,如需要天、月甚至年做时间单位,可使用 date_time 库

progress_timer

头文件

#include <boost/progress.hpp>
using namespace boost;

基本用法

在定义 progress_timer 之后,析构对象时会自动输出流逝时间。可以使用花括号,来使 progress_timer 实现计时功能。

progress_timer t;
// do some work
cout << t.elapsed() << endl;	//print out elapsed time for the task

progress_timer 精度问题,只能保留到小数点后两位,精确到百分之一秒。

progress_display

头文件

#include <boost/progress.hpp>
using namespace boost;

基本用法

在构造函数中传入总数,然后迭代更新进度条。

vector<string> v(100);
ofstream fs("test.txt");
progress_display pd(v.size());					// 构造函数,传入进度总数

vector<string>::iterator pos;
for (pos = v.begin(); pos != v.end(); ++pos){	// 遍历迭代,处理字符串,写文件
    fs << *pos << endl;
    ++pd;										// 更新进度
}

progress_display 问题

progress_display 向标准输出 cout 输出字符,无法将进度条和输出分离,如果循环中带有输出,则显示会很难看。


2015-12-22 boost , C++

ffmpeg 入门笔记

那天需要将一段视频文件转成 gif,偶遇 ffmpeg,于是就学习了一下,它真的很强大。在看资料的过程中也是挺有趣的,发现其实 kmplayer 以及国内的 QQ 影音,暴风等等,都不同程度的使用了 ffmpeg,可是根据 ffmpeg 的开源许可 LGPL,任何使用 ffmpeg 的软件都必须开源,于是乎 QQ 影音,暴风都上了 ffmpeg 的耻辱柱,如果没接触到 ffmpeg 还真不知道有这一茬,国内的黑心厂商真是拿开源社区的东西都不遵循开源协议。

下面就直接进正题吧:

几个概念

在进入 ffmpeg 入门之前有一些基本概念需要了解,我在查看 ffmpeg 的时候回头查阅了这些资料,觉得先行了解比较好,这些概念都是视频或者音频中的基本概念。

比特率

比特率,英文为 bit rate,描述每秒钟输出多少 KB 的参数,单位是 Kbps,也就是 kbit/s,8Kbit/s = 1KB/s。也就是说 800Kbps 意思就是每秒视频就要占用 100KB 磁盘空间。对于音频文件也存在比特率,同理。压缩同一个视频,视频比特率越大,文件体积越大。视频比特率越大,画质越好,马赛克越少。

MP3 一般使用的比特率为 8~320kbps。

举一个例子:

bitrate 可以理解为 file size / duration

比如一个视频文件 20.8 M 时长为 1min,那么

bitrate = 20.8M bit/60s = 20.8 * 1024 * 1024 * 8 bit/60s = 2831 Kbps

假设音频的码率为固定 128 Kbps,那么视频的码率就是

2831 Kbps - 128 Kbps = 2703 Kbps

H.264 标准建议

视频大小 分辨率 建议码率
480P 720X480 1800Kbps
720P 1280X720 3500Kbps
1080P 1920X1080 8500Kbps

可变码率

可变码率叫做 Variable Bitrate (VBR),也叫作动态比特率编码,VBR 指的是编码器的输出码率可以根据编码器输入源信号的复杂度自适应调整,目的是为了达到输出质量保持不变的同时节省存储空间。VBR 适用于存储,不太适用流式传输,可以更有效的地利用有限空间。

固定码率

固定码率叫做 Constant Bitrate (CBR),CBR 指的是编码器输出码率固定,CBR 不适合存储,CBR 对于复杂内容可能没有足够码率进行编码,从而导致质量下降,同时会在简单内容部分浪费一些码率。

帧数

帧数,又被叫做帧率,指的是每秒钟播放的图片数,单位 fps(英文:Frames Per Second),每秒的帧数或者帧率表示视频文件或者图形处理器场景时每秒钟能够更新的次数。

高的帧率可以得到更流畅、更逼真的画面。一般来说 30fps 就是可以接受的,但是将性能提升至 60fps 则可以明显提升交互感和逼真感,但是一般来说超过 75fps 一般就不容易察觉到有明显的流畅度提升了。如果帧率超过屏幕刷新率只会浪费图形处理的能力,因为显示器不能以这么快的速度更新,这样超过刷新率的帧率就浪费掉了。

在同一视频,同一码率的情况下,帧数越大,则画质越不好。尤其是运动的画面。因为每张画面会分担每秒有限的文件体积,如果画面越多,那么每张画面所能表现的内容就越有限。

当画面的 FPS 达到 60 帧 / 秒时,已经能满足绝大部分应用需求。一般情况下,如果能够保证游戏画面的平均 FPS 能够达到 30 帧 / 秒,那么画面已经基本流畅;能够达到 50 帧 / 秒,就基本可以体会到行云流水的感觉了。一般人很难分辨出 60 帧 / 秒与 100 帧 / 秒有什么不同。

分辨率

最好理解的概念了,表示画面的大小,单位是像素 px。

和编码率的关系:越高的分辨率,需要越高的编码率,因为图像的细节多了,需要的文件体积也应该增大,否则还不如画面小一些,你会发现同一码率,画面越大,图像的马赛克程度越明显。

采样率

每秒钟对音频信号的采样次数,采样频率越高声音还原度越高,声音更加自然。单位是赫兹 Hz。音频文件一般使用的采样率是 44100 Hz ,也就是一秒钟采样 44100 次,之所以使用这个数值是因为经过了反复实验,人们发现这个采样精度最合适,低于这个值就会有较明显的损失,而高于这个值人的耳朵已经很难分辨,而且增大了数字音频所占用的空间。我们所使用的 CD 的采样标准就是 44.1k,目前 44.1k 还是一个最通行的标准。

安装

Debian/Ubuntu/Linux Mint 下安装 ffmpeg 很简单:

apt-get install ffmpeg

其他操作系统安装方法,参考官网

如果想要手工编译 ffmpeg 可以参考官方 wiki。 Ubuntu/Debian/Mint 系手工编译 ffmpeg 参考 wiki

用法举例

显示文件信息

显示视频信息

ffmpeg -i input.avi

将视频拆分图片 批量截图

将视频拆分多张图片,每一帧图片,保存到 frames 文件夹下,命名 frame001.png 这种。可以加上 -r 参数以用来限制每秒的帧数,-r 10 就表示每秒 10 帧。

ffmpeg -i input.mp4 frames/frame%03d.png

图片合成视频

将多张图片合成视频

ffmpeg -i frames/frame%3d.png output.mp4

从视频中提取音频

从视频文件中提取音频并保存为 mp3

ffmpeg -i input.mp4 -f mp3 output.mp3

如果需要可以在中间加上 -ar 44100 -ac 2 -ab 192 系数,表示采样率 44100 ,通道 2 立体声,码率 192 kb/s.

将声音合成到视频

将声音合成到视频中

ffmpeg -i input_music.mp3 -i input_video.mp4 output.mp4

转化格式

格式之间转换 大部分的情况下直接运行一下即可

ffmpeg -i input.mp4 output.avi

将 flv 转码 MP4

ffmpeg -i input.flv -vcodec copy -acodec copy out.mp4

-vcodec copy-acodec copy 表示所使用的视频和音频编码格式,为原样拷贝。

转换文件格式

ffmpeg -y -i input_video.mp4 -bitexact -vcodec h263 -b 128 -r 15 -s 176x144 -acodec aac -ac 2 -ar 22500 -ab 24 -f 3gp test.3gp

ffmpeg -y -i test.wmv -ac 1 -acodec libamr_nb -ar 8000 -ab 12200 -s 176x144 -b 128 -r 15 test.3gp

视频切片操作

对视频切片操作

比如需要从视频第 1 分 45 秒地方,剪 10 秒画面,-ss 表示开始位置,-t 表示延长时间

ffmpeg -i input.mp4 -ss 00:01:45 -t 10 output.mp4

加速减速视频

加速视频

ffmpeg -i input.mp4 -vf “setpts=0.5*PTS” output.mp4

同理减速视频

ffmpeg -i input.mp4 -vf “setpts=2.0*PTS” output.mp4

此操作对音频无影响

视频截图

视频 10 秒的地方 (-ss 参数)截取一张 1920x1080 尺寸大小的,格式为 jpg 的图片 -ss后跟的时间单位为秒

ffmpeg -i input_video.mp4 -y -f image2 -t 0.001 -ss 10 -s 1920x1080 output.jpg

或者

ffmpeg -i input_video.mp4 -ss 00:00:06.000 -vframes 1 output.png

合成 gif

把视频的前 30 帧转换成一个 Gif

ffmpeg -i input_video.mp4 -vframes 30 -y -f gif output.gif

将视频转成 gif

ffmpeg -ss 00:00:00.000 -i input.mp4 -pix_fmt rgb24 -r 10 -s 320x240 -t 00:00:10.000 output.gif

将输入的文件从 (-ss) 设定的时间开始以 10 帧频率,输出到 320x240 大小的 gif 中,时间长度为 -t 设定的参数。通过这样转换出来的 gif 一般都比较大,可以使用 ImageMagick 来优化图片的大小。

 convert -layers Optimize output.gif output_optimized.gif

把 frame.[001-100].jpg 序列帧和 bg.mp3 音频文件利用 mpeg4 编码方式合成分辨率 720p 的视频文件 output.avi:

ffmpeg -i bg.mp3 -i frame.%3d.jpg -s hd720 -vcodec mpeg4 output.avi

转换码率

ffmpeg 码率相关的参数主要有 -minrate, maxrate, -b:v

ffmpeg -i input.mp4 -b:v 2000k output.mp4

也就是把原始视频转换成 2 Mbps 码率视频。ffmpeg 官方建议,在设置 -b:v 时,同时加上 -bufsize 用于设置码率控制缓冲器大小,让整体码率更加趋近于希望的只,减少波动。

ffmpeg -i input.mp4 -b:v 2000k -bufsize 2000k output.mp4

-minrate-maxrate 比较简单,设置码率不要低于或者超过某一个阈值

ffmpeg -i input.mp4 -b:v 2000k -bufsize 2000k -maxrate 2500k output.mp4

查看 ffmpeg 支持格式

要查看你的 ffmpeg 支持哪些格式,可以用如下命令:

ffmpeg -formats | less

设置输出文件编码率 64 kbit/s, To set the video bitrate of the output file to 64 kbit/s:

 ffmpeg -i input.avi -b:v 64k -bufsize 64k output.avi

设置输出文件帧率为 24 fps,To force the frame rate of the output file to 24 fps:

 ffmpeg -i input.avi -r 24 output.avi

强制输入文件以 1 帧,输出文件 24 帧 , To force the frame rate of the input file (valid for raw formats only) to 1 fps and the frame rate of the output file to 24 fps:

 ffmpeg -r 1 -i input.mp4 -r 24 output.avi

下面几步分别是,创建frames文件夹,利用 ffmpeg 将视频文件以每秒 10 帧输出成图像保存到 frames 文件夹中,再利用 ImageMagick 将图片组成 gif。其中 convert 命令来自 ImageMagick。

mkdir frames
ffmpeg -i input.mp4 -r 10 frames/frame%03d.png
convert -delay 5 -loop 0 frames/frame*.png output.gif

Source: http://superuser.com/a/556031

利用 ffmpeg 屏幕录制

参考:https://trac.ffmpeg.org/wiki/Capture/Desktop

添加水印

ffmpeg -i input.mp4 -i picture.png -filter_complex overlay="(main_w/2)-(overlay_w/2):(main_h/2)-(overlay_h)/2" output.mp4

picture.png 为水印图片, overlay 为水印位置

ffmpeg 使用语法

ffmpeg 使用语法:

ffmpeg [global_options] {[input_file_options] -i input_file} ... {[output_file_options] output_file} ...

如果没有输入文件,那么视音频捕捉就会起作用。

作为通用的规则,选项一般用于下一个特定的文件。如果你给 –b 64 选项,改选会设置下一个视频速率。对于原始输入文件,格式选项可能是需要的。

缺省情况下,ffmpeg 试图尽可能的无损转换,采用与输入同样的音频视频参数来输出。

通用选项

-L license 显示协议
-h 帮助
-formats 显示可用的格式,编解码的,协议的
-decoders 可用解码器
-encoders 可用编码器

主要选项

-i 	filename 输入文件
-y 	覆盖输出文件
-n 	不覆盖输出文件,如果输出文件存在则退出
-t 	duration (input/output)
    设置纪录时间 hh:mm:ss[.xxx] 格式的记录时间也支持,在 `-i` 之前使用,则对输入文件限制记录时间;如果对输出文件使用,则是限制输出文件的时长。

-ss position
    搜索到指定的时间 [-]hh:mm:ss[.xxx] 的格式也支持 ,更多[参考](https://ffmpeg.org/ffmpeg-utils.html#time-duration-syntax)

-title string 设置标题
-author string 设置作者
-copyright string 设置版权
-comment string 设置评论
-f fmt 强迫采用格式 fmt 输出

-c[:stream_specifier] codec (input/output, per-stream)
-codec[:stream_specifier] codec (input/output, per-stream)
    给输入文件指定解码器,给输出文件指定编码器, codec 为编码器名字,如果 codec 值为 `copy` 则默认为和原视频一致。

-vcodec codec
    vcodec 是 -codec:v 的一个别称,强制使用 codec 编解码方式,未设定时使用与输入流相同的编码器。如果用 copy 表示原始编解码数据必须被拷贝。

-target type 设置目标文件类型 (vcd,svcd,dvd) 所有的格式选项(比特率,编解码以及缓冲区大小)自动设置,只需要输入如下的就可以了:

    ffmpeg -i input.avi -target vcd /tmp/vcd.mpg

-hq 激活高质量设置
-itsoffset offset 设置以秒为基准的时间偏移,该选项影响所有后面的输入文件。该偏移被加到输入文件的时戳,定义一个正偏移意味着相应的流被延迟了 offset 秒。 [-]hh:mm:ss[.xxx] 的格式也支持

### 视频选项 {#video-options}

-vframes number (output)
    设置视频输出帧数,是`-frames:v`的别称。

-b bitrate 设置比特率,缺省 200kb/s
-r fps 设置帧率 缺省 25
-s size 设置画面的宽高

    设置帧大小,分辨率, 格式为 wxh 缺省为原视频大小。下面的简写也可以直接使用:
    ntsc 720x480
    snits 640x480
    hd720 1280x720
    hd1080 1920x1080
    更多[参考](https://ffmpeg.org/ffmpeg-utils.html#toc-Video-size)

-aspect aspect 设置画面比例 4:3 16:9 或 1.3333 1.7777
-croptop size 设置顶部切除带大小 像素单位
-cropbottom size –cropleft size –cropright size
-padtop size 设置顶部补齐的大小 像素单位

-padbottom size –padleft size –padright size –padcolor color 设置补齐条颜色 (hex,6 个 16 进制的数,红:绿:兰排列,比如 000000 代表黑色)

-vn 不做视频记录,输出无视频内容
-bt tolerance 设置视频码率容忍度 kbit/s
-maxrate bitrate 设置最大视频码率容忍度
-minrate bitreate 设置最小视频码率容忍度

-bufsize size 设置码率控制缓冲区大小

-sameq 使用同样视频质量作为源(VBR)

-pass n 选择处理遍数(1 或者 2)。两遍编码非常有用。第一遍生成统计信息,第二遍生成精确的请求的码率

-passlogfile file 选择两遍的纪录文件名为 file

高级视频选项

-g gop_size 设置图像组大小

-intra 仅适用帧内编码

-qscale q 使用固定的视频量化标度 (VBR)

-qmin q 最小视频量化标度 (VBR)

-qmax q 最大视频量化标度 (VBR)

-qdiff q 量化标度间最大偏差 (VBR)

-qblur blur 视频量化标度柔化 (VBR)

-qcomp compression 视频量化标度压缩 (VBR)

-rc_init_cplx complexity 一遍编码的初始复杂度

-b_qfactor factor 在 p 和 b 帧间的 qp 因子

-i_qfactor factor 在 p 和 i 帧间的 qp 因子

-b_qoffset offset 在 p 和 b 帧间的 qp 偏差

-i_qoffset offset 在 p 和 i 帧间的 qp 偏差

-rc_eq equation 设置码率控制方程 默认 tex^qComp

-rc_override override 特定间隔下的速率控制重载

-me method 设置运动估计的方法 可用方法有 zero phods log x1 epzs(缺省) full

-dct_algo algo 设置 dct 的算法 可用的有 0 FF_DCT_AUTO 缺省的 DCT 1 FF_DCT_FASTINT 2 FF_DCT_INT 3 FF_DCT_MMX 4 FF_DCT_MLIB 5 FF_DCT_ALTIVEC

-idct_algo algo 设置 idct 算法。可用的有 0 FF_IDCT_AUTO 缺省的 IDCT 1 FF_IDCT_INT 2 FF_IDCT_SIMPLE 3 FF_IDCT_SIMPLEMMX 4 FF_IDCT_LIBMPEG2MMX 5 FF_IDCT_PS2 6 FF_IDCT_MLIB 7 FF_IDCT_ARM 8 FF_IDCT_ALTIVEC 9 FF_IDCT_SH4 10 FF_IDCT_SIMPLEARM

-er n 设置错误残留为 n 1 FF_ER_CAREFULL 缺省 2 FF_ER_COMPLIANT 3 FF_ER_AGGRESSIVE 4 FF_ER_VERY_AGGRESSIVE

-ec bit_mask 设置错误掩蔽为 bit_mask, 该值为如下值的位掩码 1 FF_EC_GUESS_MVS (default=enabled) 2 FF_EC_DEBLOCK (default=enabled)

-bf frames 使用 frames B 帧,支持 mpeg1,mpeg2,mpeg4

-mbd mode 宏块决策 0 FF_MB_DECISION_SIMPLE 使用 mb_cmp 1 FF_MB_DECISION_BITS 2 FF_MB_DECISION_RD

-4mv 使用 4 个运动矢量 仅用于 mpeg4

-part 使用数据划分 仅用于 mpeg4

-bug param 绕过没有被自动监测到编码器的问题

-strict strictness 跟标准的严格性

-aic 使能高级帧内编码 h263+

-umv 使能无限运动矢量 h263+

-deinterlace 不采用交织方法

-interlace 强迫交织法编码仅对 mpeg2 和 mpeg4 有效。当你的输入是交织的并且你想要保持交织以最小图像损失的时候采用该选项。可选的方法是不交织,但是损失更大

-psnr 计算压缩帧的 psnr

-vstats 输出视频编码统计到 vstats_hhmmss.log

-vhook module 插入视频处理模块 module 包括了模块名和参数,用空格分开

### 音频选项 {#audio-options}

-aframes number (output)
    设置输出文件音频帧数,是`-frames:a` 的别名

-ab bitrate
    设置音频码率,声音比特率,-ac 设为立体声时要以一半比特率来设置,比如 192kbps 的就设置成 96,高品质音乐建议 160kbps(80) 一上

-ar freq
    设置音频采样率,一般设置 44100

-ac channels
    设置通道,声道数, 缺省为 1, 1 为单声道,2 为立体声

-an 不使用音频纪录

-acodec codec
    使用 codec 编解码,是`-codec:a`的别名

音频 / 视频捕获选项

-vd device 设置视频捕获设备。比如 /dev/video0

-vc channel 设置视频捕获通道 DV1394 专用

-tvstd standard 设置电视标准 NTSC PAL(SECAM)

-dv1394 设置 DV1394 捕获

-av device 设置音频设备 比如 /dev/dsp

### 高级选项 {#advance-option}

-map file:stream 设置输入流映射

-debug 打印特定调试信息

-benchmark 为基准测试加入时间

-hex 倾倒每一个输入包

-bitexact 仅使用位精确算法 用于编解码测试

-ps size 设置包大小,以 bits 为单位

-re 以本地帧频读数据,主要用于模拟捕获设备

-loop 循环输入流。只工作于图像流,用于 ffserver 测试

附录 1:ffmpeg 简略帮助

Hyper fast Audio and Video encoder
usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...

Getting help:
    -h      -- print basic options
    -h long -- print more options
    -h full -- print all options (including all format and codec specific options, very long)
    -h type=name -- print all options for the named decoder/encoder/demuxer/muxer/filter
    See man ffmpeg for detailed description of the options.

Print help / information / capabilities:
-L                  show license
-h topic            show help
-? topic            show help
-help topic         show help
--help topic        show help
-version            show version
-buildconf          show build configuration
-formats            show available formats
-devices            show available devices
-codecs             show available codecs
-decoders           show available decoders
-encoders           show available encoders
-bsfs               show available bit stream filters
-protocols          show available protocols
-filters            show available filters
-pix_fmts           show available pixel formats
-layouts            show standard channel layouts
-sample_fmts        show available audio sample formats
-colors             show available color names
-sources device     list sources of the input device
-sinks device       list sinks of the output device
-hwaccels           show available HW acceleration methods

Global options (affect whole program instead of just one file:
-loglevel loglevel  set logging level
-v loglevel         set logging level
-report             generate a report
-max_alloc bytes    set maximum size of a single allocated block
-y                  overwrite output files
-n                  never overwrite output files
-ignore_unknown     Ignore unknown stream types
-stats              print progress report during encoding
-max_error_rate ratio of errors (0.0: no errors, 1.0: 100% error  maximum error rate
-bits_per_raw_sample number  set the number of bits per raw sample
-vol volume         change audio volume (256=normal)

Per-file main options:
-f fmt              force format
-c codec            codec name
-codec codec        codec name
-pre preset         preset name
-map_metadata outfile[,metadata]:infile[,metadata]  set metadata information of outfile from infile
-t duration         record or transcode "duration" seconds of audio/video
-to time_stop       record or transcode stop time
-fs limit_size      set the limit file size in bytes
-ss time_off        set the start time offset
-sseof time_off     set the start time offset relative to EOF
-seek_timestamp     enable/disable seeking by timestamp with -ss
-timestamp time     set the recording timestamp ('now' to set the current time)
-metadata string=string  add metadata
-program title=string:st=number...  add program with specified streams
-target type        specify target file type ("vcd", "svcd", "dvd", "dv" or "dv50" with optional prefixes "pal-", "ntsc-" or "film-")
-apad               audio pad
-frames number      set the number of frames to output
-filter filter_graph  set stream filtergraph
-filter_script filename  read stream filtergraph description from a file
-reinit_filter      reinit filtergraph on input parameter changes
-discard            discard
-disposition        disposition

Video options:
-vframes number     set the number of video frames to output
-r rate             set frame rate (Hz value, fraction or abbreviation)
-s size             set frame size (WxH or abbreviation)
-aspect aspect      set aspect ratio (4:3, 16:9 or 1.3333, 1.7777)
-bits_per_raw_sample number  set the number of bits per raw sample
-vn                 disable video
-vcodec codec       force video codec ('copy' to copy stream)
-timecode hh:mm:ss[:;.]ff  set initial TimeCode value.
-pass n             select the pass number (1 to 3)
-vf filter_graph    set video filters
-ab bitrate         audio bitrate (please use -b:a)
-b bitrate          video bitrate (please use -b:v)
-dn                 disable data

Audio options:
-aframes number     set the number of audio frames to output
-aq quality         set audio quality (codec-specific)
-ar rate            set audio sampling rate (in Hz)
-ac channels        set number of audio channels
-an                 disable audio
-acodec codec       force audio codec ('copy' to copy stream)
-vol volume         change audio volume (256=normal)
-af filter_graph    set audio filters

Subtitle options:
-s size             set frame size (WxH or abbreviation)
-sn                 disable subtitle
-scodec codec       force subtitle codec ('copy' to copy stream)
-stag fourcc/tag    force subtitle tag/fourcc
-fix_sub_duration   fix subtitles duration
-canvas_size size   set canvas size (WxH or abbreviation)
-spre preset        set the subtitle options to the indicated preset

附录 2:常用视频文件格式详解

常见的视频格式:

  1. AVI 格式   它的英文全称为 Audio Video Interleaved,即音频视频交错格式。它于 1992 年被 Microsoft 公司推出,随 Windows3.1 一起被人们所认识和熟知。所谓“音频视频交错”,就是可以将视频和音频交织在一起进行同步播放。这种视频格式的优点是图像质量好,可以跨多个平台使用,但是其缺点是体积过于庞大,而且更加糟糕的是压缩标准不统一,因此经常会遇到高版本 Windows 媒体播放器播放不了采用早期编码编辑的 AVI 格式视频,而低版本 Windows 媒体播放器又播放不了采用最新编码编辑的 AVI 格式视频。其实解决的方法也非常简单,我们将在后面的视频转换、视频修复部分中给出解决的方案。

  2. DV-AVI 格式   DV 的英文全称是 Digital Video Format,是由索尼、松下、JVC 等多家厂商联合提出的一种家用数字视频格式。目前非常流行的数码摄像机就是使用这种格式记录视频数据的。它可以通过电脑的 IEEE 1394 端口传输视频数据到电脑,也可以将电脑中编辑好的的视频数据回录到数码摄像机中。这种视频格式的文件扩展名一般也是.avi,所以我们习惯地叫它为 DV-AVI 格式。

  3. MPEG 格式   它的英文全称为 Moving Picture Expert Group,即运动图像专家组格式,家里常看的 VCD、SVCD、DVD 就是这种格式。MPEG 文件格式是运动图像压缩算法的国际标准,它采用了有损压缩方法从而减少运动图像中的冗余信息。MPEG 的压缩方法说的更加深入一点就是保留相邻两幅画面绝大多数相同的部分,而把后续图像中和前面图像有冗余的部分去除,从而达到压缩的目的。目前 MPEG 格式有三个压缩标准,分别是 MPEG-1、MPEG-2、和 MPEG-4,另外,MPEG-7 与 MPEG-21 仍处在研发阶段。   MPEG-1:制定于 1992 年,它是针对 1.5Mbps 以下数据传输率的数字存储媒体运动图像及其伴音编码而设计的国际标准。也就是我们通常所见到的 VCD 制作格式。这种视频格式的文件扩展名包括.mpg、.mlv、.mpe、.mpeg 及 VCD 光盘中的.dat 文件等。   MPEG-2:制定于 1994 年,设计目标为高级工业标准的图像质量以及更高的传输率。这种格式主要应用在 DVD/SVCD 的制作(压缩)方面,同时在一些 HDTV(高清晰电视广播)和一些高要求视频编辑、处理上面也有相当的应用。这种视频格式的文件扩展名包括.mpg、.mpe、.mpeg、.m2v 及 DVD 光盘上的.vob 文件等。   MPEG-4:制定于 1998 年,MPEG-4 是为了播放流式媒体的高质量视频而专门设计的,它可利用很窄的带度,通过帧重建技术,压缩和传输数据,以求使用最少的数据获得最佳的图像质量。MPEG-4 最有吸引力的地方在于它能够保存接近于 DVD 画质的小体积视频文件。这种视频格式的文件扩展名包括.asf、.mov 和 DivX 、AVI 等。

  4. DivX 格式   这是由 MPEG-4 衍生出的另一种视频编码(压缩)标准,也即我们通常所说的 DVDrip 格式,它采用了 MPEG4 的压缩算法同时又综合了 MPEG-4 与 MP3 各方面的技术,说白了就是使用 DivX 压缩技术对 DVD 盘片的视频图像进行高质量压缩,同时用 MP3 或 AC3 对音频进行压缩,然后再将视频与音频合成并加上相应的外挂字幕文件而形成的视频格式。其画质直逼 DVD 并且体积只有 DVD 的数分之一。

  5. MOV 格式   美国 Apple 公司开发的一种视频格式,默认的播放器是苹果的 QuickTimePlayer。具有较高的压缩比率和较完美的视频清晰度等特点,但是其最大的特点还是跨平台性,即不仅能支持 MacOS,同样也能支持 Windows 系列。

  6. ASF 格式   它的英文全称为 Advanced Streaming format,它是微软为了和现在的 Real Player 竞争而推出的一种视频格式,用户可以直接使用 Windows 自带的 Windows Media Player 对其进行播放。由于它使用了 MPEG-4 的压缩算法,所以压缩率和图像的质量都很不错。

  7. WMF 格式   它的英文全称为 Windows Media Video,也是微软推出的一种采用独立编码方式并且可以直接在网上实时观看视频节目的文件压缩格式。WMV 格式的主要优点包括:本地或网络回放、可扩充的媒体类型、可伸缩的媒体类型、多语言支持、环境独立性、丰富的流间关系以及扩展性等。

  8. RM 格式   Networks 公司所制定的音频视频压缩规范称之为 Real Media,用户可以使用 RealPlayer 或 RealOne Player 对符合 RealMedia 技术规范的网络音频 / 视频资源进行实况转播,并且 RealMedia 还可以根据不同的网络传输速率制定出不同的压缩比率,从而实现在低速率的网络上进行影像数据实时传送和播放。这种格式的另一个特点是用户使用 RealPlayer 或 RealOne Player 播放器可以在不下载音频 / 视频内容的条件下实现在线播放。

  9. RMVB 格式   这是一种由 RM 视频格式升级延伸出的新视频格式,它的先进之处在于 RMVB 视频格式打破了原先 RM 格式那种平均压缩采样的方式,在保证平均压缩比的基础上合理利用比特率资源,就是说静止和动作场面少的画面场景采用较低的编码速率,这样可以留出更多的带宽空间,而这些带宽会在出现快速运动的画面场景时被利用。这样在保证了静止画面质量的前提下,大幅地提高了运动图像的画面质量,从而图像质量和文件大小之间就达到了微妙的平衡。

参考


2015-12-21 ffmpeg , linux , media , movie , mp4 , mp3 , format , ffplay , ffprobe

boost 学习笔记 1: lexical_cast

开始接触boost是因为项目中用到C++与Python的相互调用传值,后来找到一本《boost程序库完全开发指南》感觉boost库很强大,就学了一下。所以boost学习笔记基本沿用《boost程序库完全开发指南》书中脉络。

因为C++是强类型语言,所以对于Python,perl之类的动态语言来说很麻烦的一件事情就是类型转换,虽然C中也提供了atoi(),atof()之类的函数,但是总体也还是很麻烦。幸而有了lexical_cast,他可以用来进行字符串、整数/浮点数之间的字面转换。

头文件

lexical_cast 位于boost命名空间,为了使用 lexical_cast,需要包含头文件 <boost/lexical_cast.hpp>,即:

#include <boost/lexical_cast.hpp>
using namespace boost;

基本方法

lexical_cast在转换字符串时,字符串中只能包含数字和小数点,不能出现除e/E以外的英文字符或者其他非数字字符。

using boost::lexical_cast;
int a = lexical_cast<int>("123");					// string -> int
long b = lexical_cast<long>("2015");				// string -> long
double c = lexical_cast<double>("123.12");			// string -> double
float pai = lexical_cast<float>("3.14159");			// string -> float

std::cout << a << b << c << pai << std::endl;

数字转换成字符串时不支持高级格式控制。

string str = lexical_cast<string>(123);				// int -> string
std::cout << str << std::endl;
cout << lexical_cast<string>(1.234) << endl;		// float -> string
cout << lexical_cast<string>(0x11) << endl;			// 16进制 -> string

// lexical_cast can cast 0 & 1 to bool, but only support 0 & 1, not support True or False string
bool bl = lexical_cast<bool>("1");					// string -> bool, only support 1 & 0

异常操作

当 lexical_cast 无法执行转换时会抛出异常 bad_lexical_cast ,它是 std::bad_cast 的派生类。可以使用 try/catch 块来保护代码。

try{
	cout << lexical_cast<int>("0x100");
	cout << lexical_cast<bool>("false");
}catch (bad_lexical_cast& e){
	cout << "error: " << e.what() << endl;
}

代码运行结果:

error: bad lexical cast: source type value could not be interpreted as target

转换对象要求

lexical_cast 对转换对象有如下要求:

  1. 转换起点对象是可流输出的,即定义了 operator«
  2. 转换终点对象是可流输入的,即定义了 operator»
  3. 转换终点对象必须是可缺省构造和可拷贝构造的

C++中内建类型(int,double等)和std::string 都是符合三个条件的。

自定义类

如果想要将 lexical_cast 用于自定义类,实现 java 中 Object.toString() 的用法,只需要满足 lexical_cast 的要求,实现流输出操作符 operator« 即可。

class demo_class{
    friend ostream& operator<<(ostream& os, const demo_class& x){
        os << "class content" << endl;
        return os;
    }
};
int main() {
    demo_class demo = demo_class();
    cout << lexical_cast<string>(demo) << endl;
}

输出结果:

class content


2015-12-21 boost , C++

boost 学习笔记 0: 安装环境

这篇文章讲如何安装 boost 库,最完整的教程永远在官网。以下内容部分翻译自官方文档。

首先来看一看在Linux下安装 boost 库。

Ubuntu/Debian/Linux Mint 安装boost

运行以下命令:

sudo apt-get install libboost-all-dev

然后输入密码,安装,安装完之后目录在 /usr/include/boost 下。

Linux下使用

Eclipse或者其他IDE中使用 boost 时,需要以下几步,引入头文件,添加库。

C/C++ Build, Cross G++ Linker,Libraries, 添加相应Libraries(-l),并添加相应Library search path(-L) /usr/include

boost config

Mac

Max 下安装 boost 最简单的方式,就是用 brew:

brew install boost

或者手动安装,参考boost官网

简单翻译:

  1. 下载 boost_1_60_0.tar.bz2
  2. 解压 tar –bzip2 -xf /path/to/boost_1_60_0.tar.bz2
  3. 一部分组件(Header-Only Libraries)在完成1和2以后就能直接用,因为是直接写在hpp的inline函数,但是要利用其它功能,需要build boost库里面的各个组件(步骤4-6)

    需要单独编译的库有:

    • Boost.Chrono
    • Boost.Context
    • Boost.Filesystem
    • Boost.GraphParallel
    • Boost.IOStreams
    • Boost.Locale
    • Boost.MPI
    • Boost.ProgramOptions
    • Boost.Python (see the Boost.Python build documentation before building and installing it)
    • Boost.Regex
    • Boost.Serialization
    • Boost.Signals
    • Boost.System
    • Boost.Thread
    • Boost.Timer
    • Boost.Wave
  4. 进入解压后的目录 cd path/to/boost_1_60_0
  5. 输入./bootstrap.sh 开始配置,添加 --prefix 选择安装地址 ./bootstrap.sh –prefix=path/to/installation/prefix
  6. 输入 ./b2 install 开始安装

备注:如果第5步直接输入./bootstrap.sh 则默认会安装到/usr/local下面的include和lib目录下,而/usr是在Macintosh HD下面的一个隐藏目录,到此boost就安装到了电脑上,可以使用它进行编程了。

Mac下默认安装地址是在 /usr/local/include/usr/local/lib 下,因此在配置环境的时候需要注意将boost地址写入。

xcode配置boost环境

基本思路和Linux中一样,添加头文件搜索路径,添加lib搜索路径,引用相应lib文件。

  1. 用xcode创建控制台应用程序,在项目设置->build Settings->Search Paths->Header Search Paths和Library Search Paths里面分别添加上述默认安装地址 /usr/local/include//usr/local/lib 目录
  2. 在项目设置->build Phases->Link Library With Libraries里面点加号,选择option,找到上述lib目录,选中里面以.a 或者 .dylib 结尾的文件,添加即可

参考:YouTube

其他操作系统请参考以上 YouTube Playlist,我收集整理了一些基本够用了。


2015-12-20 boost , C++

电子书

最近文章

  • 使用 Huginn 搭建自己的 IFTTT IFTTT, Zapier
  • notion 使用记录 很早就有人推荐 Notion,但是注册了用了一下,本以为就是一个在线的 Google Docs,可昨天在豆瓣看到一篇文章介绍比 Trello 更加智能的代替品,然后一看就是 Notion,于是就再来研究下 Notion。然后发现原来 Notion 可以是 Google Calendar, 可以是 Trello,可以是 Google Docs,可以是 todo list,可以是 Google Excel。甚至可以导入 word,markdown,html,csv。
  • Google Cloud Platform 使用向导 Google Cloud 提供 300 刀的初始优惠,而最近我的 Linode 节点越来越不稳定,时常撞墙,所以不得不再别人强烈推荐下注册了一下 GCP。这里就记录一下遇到的问题,其他具体的细节网上已经够多,就不再赘述。
  • html 转 pdf 命令行工具 wkhtmltopdf 最近因为用 HTML 写了一个文档,当想要输出时保存为 PDF,而 Chrome 自带的打印功能,本来就能够快速的保存为 PDF,但是却保留不了页面中的链接,所以找到了这个 wkhtmltopdf.
  • freemarker Java 模板引擎 FreeMarker is a free Java-based template engine, originally focusing on dynamic web page generation with MVC software architecture. However, it is a general purpose template engine, with no dependency on servlets or HTTP or HTML, and is thus often used for generating source code, configuration files or e-mails. by wikiPedia