年初的时候定下计划 2015 年一年至少阅读 20 本书,不管是小说还是专业书,现在离 2015 结束只有短短几天时间了,就梳理一下看过的书好了,顺带检查一下是否完成了任务。可惜结果很令我伤心。满心以为今年能读 20 本书,最后满打满算也只算读了 16 本书。今年欠了 4 本的债,明年 2016 年期望自己能够看下 24 本书吧。
看完电影之后还是不过瘾,于是乎补完小说。小说比电影更加精彩,电影因为时长的原因做了很多删减和改编,看小说更能体会到那种在绝境中想要生存下去的力量。
小说摘录
舍友推荐,舍友因为是英语文学专业,推荐的这本黑色预言式的小说非常的赞,小说对人性的刻画实在是不能让人忘记。这本小说更让我认识了威廉·戈尔丁,英国很著名的小说家。这部小说主要讲述的就是因为战争流失到荒岛的一群小孩,因为利益形成各自集团,之间发生的冲突与矛盾。
摘录
乔治奥威尔 反乌托邦小说,很早以前就想看不过没有坚持下来,今年总算是看完了。然后摘录一些警句。
约翰密尔,其实是很薄的书,因为当时英语课做 presentation 查资料找到,所以一直保存在待看列表。
全书要义可以概括为两条基本原则:
摘录
快乐学哲学 英语课课程材料,通俗易懂的语言介绍西方哲学。推荐看英文原版。
学习 bash 今年系统的学习一下 Linux,但 Linux 体系太复杂,先从 bash 看起。
更加详细一点的 bash 教程
参考bash 学习笔记.
很经典,很详细,却也略显啰嗦的 Linux 入门书
笔记可以参考 linux 学习笔记
因项目需要 C++,Python 的结合,看了 boost.python 部分,后来又自学了 boost 中的 property_tree 用来解析 json。其他部分未详细看,只大略扫过。
学期中做了个讲座,大略的看了一些知识,Git 实在强大,很多特性到现在我也没搞懂。这书要一直备着。
笔记参考git 学习笔记
只学了个大概,因为平时用 C++多。
年初的时候读的,似乎已经忘得差不多了,总之“大数据”很玄乎。
剧本结构设计 忘记了什么时候 200-100 的时候买的了,喜欢看电影,而我们总是谈论到剧情设计,于是就买了一本看。
一直订阅着这位作者的博客,看完这本书之后就全面使用 Evernote 中,放弃了 OneNote,和 Google Keep
摘录
在观看纪录片《操作系统革命》中提到的这本书的作者,正好实验室有这本书,就拿了看了。这本书主要将软件开发的两大方式,正如书名所述,一种是公司内部集中式的开发,另外一种就是类似 GitHub 上那种分工合作式开发。这两者各有利弊,但作者作为 GNU 的倡导者必然是支持开源的。
别告诉我你会记笔记 学会记笔记是很重要的事情。
摘录
看到最后一句话其实感触挺大,回想起 Google Reader 关闭,这真是 Google 做出的很差的一件事情。Reader 中的东西都曾经是阅读过,甚至做过笔记的东西,那也就是自己的东西,再怎么使用 Google 搜索,出来的东西也都不是自己的经验。所以现在我也在培养我隔断时间总结上一阶段的习惯,这样自己总结,自己写下来的东西才真正属于自己。
期待 2016 年美好的开始。
很久没有看到这样反正真实中国的电影了,近两年的国产电影不是被早恋打胎的青春片,就是被各种古怪离奇的古装片,在就是无厘头的喜剧片所霸占。幸而有这样一部电影能够让2015年的国产电影画上一个稍稍圆满的句号。
剧情上,这样一部电影融入了太多真实的新闻,以至于会让人一一和某某事件联系上,人们往往说,电影往往是现实的表现。而这样一部老炮儿正是在真实这件事情上花了太多的功夫。从老北京的腔调到巷口胡同,从酒吧夜宿到马路飙车,从故友借钱到朋友援手,一件一件的小事敲打着人们的心,虽然故事发生在北京,虽然故事的主人公有着特殊的经历,可正是从这样的角度让我们看到了现代人们的人情冷漠,看到了整个中国的一个缩影,城管会不顾形象的殴打底层的百姓,匆匆的行人不会停下匆忙的脚步帮助路边无助的少女,甚至在看到别人跳楼时无情的煽动,更甚至于几十年的友情会在金钱面前变得一文不值。电影中的情节虽是虚构,却真实的让人发抖。电影看至一半就无法忍住打转的眼泪,同情六爷以及他的遭遇,为六爷的仁义感动。我们的社会正迈向黑暗,却少有六爷这样的人出面维护。为何我们会不相信路边求助的少女?为何我们不敢搀扶跌倒的老人?为何我们的眼里只有金钱?是我们少了什么东西吗?是我们被欺骗多次吗?亦或是像电影中说得那样“现在的人们,遇事都躲”。越来越多的人都不讲理,不讲规矩。往小了说就是百姓的日常百态,而往大了说就是三环十二少无法无天,在大就是他爹千万的账单。社会越来越没有理,越来越混乱,而这也仅仅是电影所表现出来的一部分而已。
在说到剧本设计上面,到现在很少的国产电影能够做到情节上的呼应,很多的时候就是主角想要做什么就凭空能够出现什么,而老炮儿在这方面做的非常的棒。现在能够列举的很多情节在前后发展中都能找打影子,六爷用报纸裹挟遗物扔到理发店呼应话匣子在理发店发现用报纸卷着的房产证,六爷心脏病发作呼应早先话匣子一直说心脏的问题,六爷在路边给建工女孩200元钱呼应片末女孩从远方寄来的挂号信,六爷在片尾穿军大衣用日本军刀独自约架呼应儿子和话匣子对话中对六爷十几年前一人单挑十几人的回忆。这样的剧本设计无意让故事的说服力大大增强。电影虽然有很多未交待的事情,但也正是因为这样才造就一部好电影的剧情。一部好电影就应该是将故事的一个切片展现给人们看,甚至能让人们产生就是电影结束了故事还在继续的错觉。如果真能做到那样,这样的电影离佳片也就不远了。而老炮儿正是这样一部电影,剧情没有交代六爷的妻子,也没有交代六爷故友的生活种种,同样也没有交代三环十二少,但是通过一些侧面的描述,通过电影的叙事交代,电影想要讲述的故事是完整的,那就够了。
不得不说的冯小刚表演,早在电影上映之前就被无数的消息所报道,冯小刚凭借此片获得了金马影帝。一直很想看一看一个导演的表演功力,但是不得不说我被冯小刚的演技震撼到失语。可能原先还有一丝的“嘲讽”,只刚刚开头的城管戏,就让我对冯小刚刮目相看。以至于到最后和三环十二少演对手戏,再到拜访故人借钱救子,一丝一毫的表情动作,刷新了我对冯小刚的认识。片中饰演的六爷有着无比庞大的气场,平时看只是胡同巷子里面普普通通的市井小民,而一旦遇事,六爷凭借自己地位打抱不平,用“规矩”解决所有事情。
六爷的几场戏深深的印在了我的脑海里,一是开场和城管的戏,再就是到修车厂和三环十二少的戏,再到朋友(洋火儿)家借钱,到最后颐和园后野湖。每一个和冯小刚演对手戏的演员,无不给冯小刚的气场所压倒。
而其他的表演也就不想说什么了,这部电影只需要看冯小刚一个人演就够了,而六爷身边的一身肌肉的闷三儿(张涵予),无时不刻不在帮助六爷的话匣子(许晴),还有老实憨厚的灯罩儿,他们都个性十足,表演也到没有太大的瑕疵。尤其是闷三儿在修车厂,还有六爷被打闷三儿带领一帮人砸修车厂的几个镜头让张涵予非常帅气。只是在六爷面前就显得弱很多了。
因此最后在剧本上给7.5分,表演给8分,娱乐性思考性给8分。
观影风向标做了一期引进片的排名,我顺便也简单地回忆了一下。然后根据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:神秘国度 2015-09-13 之前也提到过碟中谍系列的特工片一直是很喜欢的系列,当然这一部也差,只是相较其他传达思考的电影来说,这真是纯粹的爆米花电影了。
速度与激情7 2015-04-12 话题大片啦,加上保罗沃克的离世,加上片尾的煽情歌曲,这样一部有剧情,有动作,有视效的大片总有其一席之位。
歌曲改变人生 Begin Again 2014-12-01 舍友推荐,虽然去年年末已看过,放到今年这样一部音乐类电影依然给我非常棒的感觉。
极速风流 Rush
两年前的片子,如果不听节目真的不知道这样一部电影。
思悼
年末的时候不止一遍的在各种榜单上看过,总之有其上榜的理由吧。
小王子
准备看小说,然后再补一下好了。
早晨 初雪
中午 披萨
中午 火锅
饭后
人生如此夫复何求。
对应书中第三章 内存管理,着重讲 boost 实现的智能指针,和内存池pool 等概念。众所周知,C++没有提供Java中的垃圾回收机制,因此 boost 实现智能指针用来管理内存避免一些问题。C++继承 C 高效灵活地指针,但是同样带了了很多问题:
虽然STL提供了 auto_ptr
,但是受限太多(不能放到容器中,因为不支持拷贝构造函数和赋值),因此很少有人使用。
智能指针(smart_ptr
)是Boost各组件中,应用最为广泛的一个。使用智能指针需包含以下头文件,如果只使用智能指针 shared_ptr 可以只包含同名头文件。
#include <boost/smart_ptr.hpp>
using namespace boost;
Boost从很早就提供了如下的智能指针,并且功能一直保持稳定:
有其他任何问题,请查阅官方文档: http://www.boost.org/doc/libs/1_60_0/libs/smart_ptr/smart_ptr.htm
从名字就可以看出,这种智能指针只限于作用域内使用,无法转移内置指针的管理权(不支持拷贝、=赋值等) 但是作用也很显然,例如:
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
则:
例子:
// 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_ptr 基本一样,只不过可接受数组的new [],多了下标访问操作,其他类似。
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;
}
最像指针的“智能指针”,是 boost.smart_ptr 库中最有价值,最重要的组成部分。支持拷贝构造函数、支持赋值操作。重载了*和->操作符用来模仿原始指针的行为。目前已成为tr1标准的一部分,发展自原始的auto_ptr,内置引用计数。
使用时候有3点要特别注意:
不要将shared_ptr用于函数的临时参数:
// 下面这个是OK的。
void ok()
{
shared_ptr
// 下面这个就可能内存泄漏!
void bad()
{
f(shared_ptr
看看基本的使用例子。
#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。 从而完美的完成了无内存泄漏的、无内存出错的、多个实例之间的指针变量共享。
同scoped_array类似,shared_array是shared_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是一种“侵入式”的引用计数指针,实际并不提供引用计数功能,而是要求被存储的对象自己实现引用计数功能。可以应用于以下两种情形:
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_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 成员函数 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() 函数,用于检验日期是否是一个特殊日期
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 , 天为单位,度量时间长度的标量。值为任意整数,可正可负。
成员函数 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对象,左值必须小于右值。
// 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 有子类,度量不同时间分辨率,分别是: 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;
构造函数中同时指定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 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;
}
C++中操作时间的类。timer 是一个很小的库,提供简易的度量时间和进度显示的功能,可用于测试性能计时任务等大多数情况。timer 库包含三个组件, 计时器类 timer, progress_timer 和进度条指示类 progress_display。对应书中第二章内容。
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;
使用标准库头文件
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
#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 精度问题,只能保留到小数点后两位,精确到百分之一秒。
#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 向标准输出 cout 输出字符,无法将进度条和输出分离,如果循环中带有输出,则显示会很难看。
那天需要将一段视频文件转成 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
# or
ffmpeg -i input.mp4 -vn output.mp3
# or
ffmpeg -i input.mp4 -vn -ar 44100 -ac 2 -ab 320 -f mp3 output.mp3
说明:
如果需要可以在中间加上 -ar 44100 -ac 2 -ab 192
系数,表示采样率 44100 ,通道 2 立体声,码率 192 kb/s.
-vn
表示不使用视频-ar
设置音频采样率-ac
设置音频 Channels-ab
设置音频比特率 bitrate-f
指定输出文件的格式将声音合成到视频中
ffmpeg -i input_music.mp3 -i input_video.mp4 output.mp4
将音频流和视频流合并:
ffmpeg -i input.mp4 -i input.mp3 -c copy -map 0:v:0 -map 1:a:0 output.mp4
-map
参数指定了从输入文件的视频流和音频流中输出文件格式之间转换 大部分的情况下直接运行一下即可
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
如果要精确控制切片的时间,比如想要剪切从 0 秒到 10 分的视频1
ffmpeg -ss 00:00:00 -to 00:10:00 -i input.mp4 -c copy 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
把视频的前 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
比如要强制输出文件的帧率为 24 fps:
ffmpeg -i input.avi -r 24 output.avi
对于一个非常大的文件,经常需要压缩文件大小可以使用
ffmpeg -i input.mp4 -vcodec h264 -acodec mp3 output.mp4
如果想要改变视频的分辨率
ffmpeg -i input.mp4 -filter:v scale=1280:720 -c:a copy output.mp4
# or
ffmpeg -i input.mp4 -s 1280x720 -c:a copy output.mp4
ffmpeg -i input.mp4 -filter:v "crop=w:h:x:y" output.mp4
说明:
-filter:v
过滤视频ffmpeg -i input.mp4 -aspect 16:9 output.mp4
常见的宽高比:
将一个音频文件编程一个视频
ffmpeg -loop 1 -i input_image.jpg -i input_audio.mp3 -c:v libx264 -c:a aac -strict experimental -b:a 192k -shortest output.mp4
ffmpeg -i ./file.mp4 -c:v libx264 -c:a copy -vf "ass=t.ass" out.mp4
要查看你的 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
参考: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 [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 测试
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
常见的视频格式:
AVI 格式 它的英文全称为 Audio Video Interleaved,即音频视频交错格式。它于 1992 年被 Microsoft 公司推出,随 Windows3.1 一起被人们所认识和熟知。所谓“音频视频交错”,就是可以将视频和音频交织在一起进行同步播放。这种视频格式的优点是图像质量好,可以跨多个平台使用,但是其缺点是体积过于庞大,而且更加糟糕的是压缩标准不统一,因此经常会遇到高版本 Windows 媒体播放器播放不了采用早期编码编辑的 AVI 格式视频,而低版本 Windows 媒体播放器又播放不了采用最新编码编辑的 AVI 格式视频。其实解决的方法也非常简单,我们将在后面的视频转换、视频修复部分中给出解决的方案。
DV-AVI 格式 DV 的英文全称是 Digital Video Format,是由索尼、松下、JVC 等多家厂商联合提出的一种家用数字视频格式。目前非常流行的数码摄像机就是使用这种格式记录视频数据的。它可以通过电脑的 IEEE 1394 端口传输视频数据到电脑,也可以将电脑中编辑好的的视频数据回录到数码摄像机中。这种视频格式的文件扩展名一般也是.avi,所以我们习惯地叫它为 DV-AVI 格式。
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 等。
DivX 格式 这是由 MPEG-4 衍生出的另一种视频编码(压缩)标准,也即我们通常所说的 DVDrip 格式,它采用了 MPEG4 的压缩算法同时又综合了 MPEG-4 与 MP3 各方面的技术,说白了就是使用 DivX 压缩技术对 DVD 盘片的视频图像进行高质量压缩,同时用 MP3 或 AC3 对音频进行压缩,然后再将视频与音频合成并加上相应的外挂字幕文件而形成的视频格式。其画质直逼 DVD 并且体积只有 DVD 的数分之一。
MOV 格式 美国 Apple 公司开发的一种视频格式,默认的播放器是苹果的 QuickTimePlayer。具有较高的压缩比率和较完美的视频清晰度等特点,但是其最大的特点还是跨平台性,即不仅能支持 MacOS,同样也能支持 Windows 系列。
ASF 格式 它的英文全称为 Advanced Streaming format,它是微软为了和现在的 Real Player 竞争而推出的一种视频格式,用户可以直接使用 Windows 自带的 Windows Media Player 对其进行播放。由于它使用了 MPEG-4 的压缩算法,所以压缩率和图像的质量都很不错。
WMF 格式 它的英文全称为 Windows Media Video,也是微软推出的一种采用独立编码方式并且可以直接在网上实时观看视频节目的文件压缩格式。WMV 格式的主要优点包括:本地或网络回放、可扩充的媒体类型、可伸缩的媒体类型、多语言支持、环境独立性、丰富的流间关系以及扩展性等。
RM 格式 Networks 公司所制定的音频视频压缩规范称之为 Real Media,用户可以使用 RealPlayer 或 RealOne Player 对符合 RealMedia 技术规范的网络音频 / 视频资源进行实况转播,并且 RealMedia 还可以根据不同的网络传输速率制定出不同的压缩比率,从而实现在低速率的网络上进行影像数据实时传送和播放。这种格式的另一个特点是用户使用 RealPlayer 或 RealOne Player 播放器可以在不下载音频 / 视频内容的条件下实现在线播放。
RMVB 格式 这是一种由 RM 视频格式升级延伸出的新视频格式,它的先进之处在于 RMVB 视频格式打破了原先 RM 格式那种平均压缩采样的方式,在保证平均压缩比的基础上合理利用比特率资源,就是说静止和动作场面少的画面场景采用较低的编码速率,这样可以留出更多的带宽空间,而这些带宽会在出现快速运动的画面场景时被利用。这样在保证了静止画面质量的前提下,大幅地提高了运动图像的画面质量,从而图像质量和文件大小之间就达到了微妙的平衡。
开始接触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 对转换对象有如下要求:
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
这篇文章讲如何安装 boost 库,最完整的教程永远在官网。以下内容部分翻译自官方文档。
首先来看一看在Linux下安装 boost 库。
运行以下命令:
sudo apt-get install libboost-all-dev
然后输入密码,安装,安装完之后目录在 /usr/include/boost
下。
Eclipse或者其他IDE中使用 boost 时,需要以下几步,引入头文件,添加库。
C/C++ Build, Cross G++ Linker,Libraries, 添加相应Libraries(-l),并添加相应Library search path(-L) /usr/include
Max 下安装 boost 最简单的方式,就是用 brew:
brew install boost
或者手动安装,参考boost官网
简单翻译:
一部分组件(Header-Only Libraries)在完成1和2以后就能直接用,因为是直接写在hpp的inline函数,但是要利用其它功能,需要build boost库里面的各个组件(步骤4-6)
需要单独编译的库有:
cd path/to/boost_1_60_0
./bootstrap.sh
开始配置,添加 --prefix
选择安装地址
./bootstrap.sh –prefix=path/to/installation/prefix./b2 install
开始安装备注:如果第5步直接输入./bootstrap.sh 则默认会安装到/usr/local下面的include和lib目录下,而/usr是在Macintosh HD下面的一个隐藏目录,到此boost就安装到了电脑上,可以使用它进行编程了。
Mac下默认安装地址是在 /usr/local/include
和 /usr/local/lib
下,因此在配置环境的时候需要注意将boost地址写入。
基本思路和Linux中一样,添加头文件搜索路径,添加lib搜索路径,引用相应lib文件。
/usr/local/include/
和 /usr/local/lib
目录参考:YouTube
其他操作系统请参考以上 YouTube Playlist,我收集整理了一些基本够用了。