Mac下为Android Studio编译Ffmpeg(二)Android Studio部分

2015 年 1 月 27 日

Android Studio和Eclipse不太一样,它有一定的自动生成Android.mk并自动搞定JNI的能力。
但目前还并不足以让我们使用起来Ffmpeg库。
因此我们的思路是禁用掉Android Studio自动ndk-build的功能,手动编译我们的C代码达到目的。

首先当然要新建一个Android Studio项目。
我们使用$ROOT_DIR指代项目根目录。

1、Android Studio配置ndk路径

$ROOT_DIR/local.properties原先只配置了sdk。

给它增加一行ndk的配置

2、配置build.gradle

项目里面有两个build.gradle,一个在根目录下,一个在$ROOT_DIR/app/src下,我们要修改的是后者。
A>添加这一段以禁用自动ndk-build。

B>添加这一段让它知道用库

修改后的build.gradle是这样的。

3、生成头文件

执行命令,注意路径要根据自己的情况更改。

会生成这个文件$ROOT_DIR/app/src/main/jni/com_example_chengang_myapplication_MainActivity.h

4、编写C文件

$ROOT_DIR/app/src/main/jni/ovsplayer.c内容如下:

5、编写Java文件

$ROOT_DIR/app/src/main/java/com/example/chengang/myapplication/MainActivity.java内容如下。
其中getStringFromNative()方法是我们实现的,打印了Ffmpeg库的版本号(我编译的这个是3673444)和视频文件的信息出来。

附上布局文件是这样的。

6、编写项目Android.mk

Android.mk放到$ROOT_DIR/app/build/intermediates/ndk/debug/下。
注意其中的绝对路径”/Users/chengang/Code/Android/MyApplication4″是咱们的$ROOT_DIR。

简单说下LOCAL_LDLIBS和LOCAL_SHARED_LIBRARIES的区别,前者链接系统库,后者链接第三方库。
并不能换着用。

7、编译C代码

在终端下执行这个命令编译C代码。

8、运行项目

回到Android Studio中Ctrl+R运行项目。
会看到手机上打印出Ffmpeg库的版本号(我编译的这个是3673444)和视频文件的信息出来。
Ffmpeg库已经可以调用了,然后继续写自己的逻辑就好了。

9、另外

另外说两个C代码中可能遇见的错误:
A>如果你通过标准库想得到文件大小,又写错了文件名。安卓上,在不存在的文件的句柄上执行fseek会报如下错误:”could not disable core file generation.”;
B>如果avformat_open_input()返回-1330794744了。那有两种可能,一是你忘记调用av_register_all()了,二是编译Ffmpeg的时候没有编译进对应的DEMUXER或者指定了disable-everything。

本文前半配置ndk的部分见这个链接:Mac下为Android Studio编译Ffmpeg(一)ndk部分

Tags

Mac下为Android Studio编译Ffmpeg(一)ndk部分

本文参考http://www.roman10.net/how-to-build-ffmpeg-with-ndk-r9/。
但它只适合做编码,而且没有Android Studio配置的部分。

1、下载ndk

我下的是r10d版本。

2、解压ndk

不要解压,文件权限会出错。执行之,会自动解压,然后mv到想放的地方。我放到了”/usr/local/bin/android-ndk-r10d”(此目录之后用$NDK_DIR指代)。

3、下载Ffmpeg

我下的是2.5.3版本。

4、解压Ffmpeg

解压Ffmpeg到$NDK_DIR/sources/ffmpeg-2.5.3。

5、修改Ffmpeg编译配置

在ffmpeg-2.5.3目录下把configure文件中的这几行,目的是去掉默认生成的库名字libavcodec.so.55最后那个”55″的版本号。

6、编译Ffmpeg

在ffmpeg-2.5.3目录下创建文件build_android.sh。
注意前三行要按照自己的路径正确配置。

保存后执行

编译会花上一段时间。

7、查看编译结果

编译完成后$NDK_DIR/sources/ffmpeg-2.5.3下面会多出一个android目录,里面就是我们想要的编译好的库。

其中libavcodec.so、libavfilter.so、libavformat.so、libavutil.so、libswresample.so、libswscale.so都是软链,没有用,可以删掉。

8、给Ffmpeg库写Android.mk使其可用

创建$NDK_DIR/sources/ffmpeg-2.5.3/android/arm/Android.mk文件,内容如下:
要注意其中.so前面的数字应该改成你的ffmpeg版本编译出来的数字。

至此ndk配置完毕,后面是配置Android Studio的部分。
见这个链接:Mac下为Android Studio编译Ffmpeg(二)Android Studio部分

Tags

Rust官方指南摘抄(八)并发任务

2015 年 1 月 6 日

Rust官方指南摘抄系列内容如下:
(一)语法基础
(二)猜数游戏
(三)模块、测试和宏
(四)指针
(五)模式匹配
(六)方法语法、泛型和抽象方法
(七)闭包和迭代器
(八)并发任务

并发原语

1、Rust使用“任务”来描述并发。任务是轻量级的原语,抛弃了不安全的内存共享模式,使用消息传递作为交流手段。

2、任务是作为一个库被实现的,并不是语言的一部分。所以今后可能会出现更多的并发库去适应各种并发环境。

3、任务使用spawn()方法来表达。

4、spawn接收一个proc,凡是proc用过的变量,往后都不能使用了。
这是为了并发安全而考虑的,虽然其他很多语言允许这种用,但是Rust决定禁止这种方法,并选择在编译器直接报错。

消息传递

5、如果任务想和外面通信,可以使用频道(channel)。
很像linux里的pipe是不。

注意.recv()方法是阻塞的,它的非阻塞版本是.try_recv()。

6、和pipe一样,如果需要双向通信。
那就需要两个频道。

7、Sender都是Receiver泛型定义的,所以频道可以传输各种数据类型。
但只能是一种数据类型,如果往一个频道里先传了int,后续又传string是不行的。

Future

1、从基本的原语可以衍生出很多并发模型,Rust的标准库中也有一些。

2、比如说频道之外的另一种沟通方式Future。

错误处理

1、fail!宏可以帮助我们传递失败消息。

2、失败的任务不可恢复。但是它可以提醒其它任务它失败了。
task::try会帮助我们。

Tags

Rust官方指南摘抄(七)闭包和迭代器

Rust官方指南摘抄系列内容如下:
(一)语法基础
(二)猜数游戏
(三)模块、测试和宏
(四)指针
(五)模式匹配
(六)方法语法、泛型和抽象方法
(七)闭包和迭代器
(八)并发任务

闭包

1、Rust中的匿名函数被称为闭包。

2、闭包可以读取它被定义的那个作用域内的变量,比如下面这个不接受参数的闭包。

3、闭包借走了它从环境中读取的变量,在它归还之间,要遵守Rust的拥有者原则。

4、闭包特性可以帮助我们获取函数式编程两大优点之一高阶函数(另一个是惰性计算),造出“返回函数的函数”。

过程

1、Rust中的闭包还有一种形式被称为过程,使用proc关键字来定义。

2、过程是为并发而设计的,我们会在并发的章节继续谈到它。目前我们只需要知道,同一个过程不可以被调用两次。

迭代器

1、rang函数的返回,就是一个迭代器。所谓迭代器,都可以调用.next()方法。

其实等价于显式的.next()调用如下:

2、rang()在Rust中是反模式的。下面这种写法并不被提倡。

下面这样写更好一些。代码更清晰,而且执行效率更高。
因为我们不再依赖vec的索引,.iter()帮助我们避免了不必要的边界检查。

3、值得注意的是,迭代器给我们的是数据的引用而不是数据的拷贝。
它临时借来了数据,避免了数据拷贝的开销。
当然,无论把哪个交给println!(),它都会帮我们搞定它。

消费者

1、进一步阐明为什么range()是反模式的,我们需要明确三个概念。
a> 迭代器:返回一组数据;(range()就是这类)
b> 迭代适配器:作用在迭代器之上,返回一个数据被处理过的新的迭代器;
c> 消费者:作用在迭代器之上,返回最终数据。

2、消费者之一 collect(),最常用的消费者,把迭代器里的数据弄成一组返回回来。
需要指明数据类型。

3、消费者之二 find(),找出符合条件的值。它可以接受一个闭包。

4、消费者之三 fold()。带状态存储的迭代器消费者。

就这段代码而言。
每一轮的计算结果会存放在sum里面。最终total变量的值等于 fold中第一个参数0与迭代器中所有值的合。

5、借助迭代器和消费者,我们可以实现函数式编程的另一个重要优点“惰性计算”。
值只在真正被下一个流程需要的时候才真正被生成和计算。
这使得我们直接申请一个1到无穷大的数组而完全不用考虑内存和CPU资源的问题。

6、按照上面说的,再来一个高级迭代器。

迭代适配器

1、迭代适配器之一 filter()。

2、迭代适配器之二 map()。

但这会得到warning

因为是惰性的,迭代器要求后续操作一定要明确把值取走。

3、迭代适配器之三 take()。下例明确取5个。

4、我们可以让迭代器打头、迭代适配器放在中间,消费者放在最后结合起来用。就像下面这个例子。

Tags

Rust官方指南摘抄(六)方法语法、泛型和抽象方法

2015 年 1 月 5 日

Rust官方指南摘抄系列内容如下:
(一)语法基础
(二)猜数游戏
(三)模块、测试和宏
(四)指针
(五)模式匹配
(六)方法语法、泛型和抽象方法
(七)闭包和迭代器
(八)并发任务

方法语法

1、一般我们方法嵌套方式是这样的:

但如果我们想弄成这样:

impl关键字可以帮忙。

2、也可以不用&self,在new的时候就开始搞。

这种写法在Rust代码里面很常见。

范型

1、感觉有点像C++的模版,范型也叫参数多态(parametric polymorphism)。它让我们可以把指定具体类型的工作推迟到实际实例化时去做。

2、我们来试一下用T表示范型,比如说这样。

3、范型可以帮忙我们在多个数据类型共用同一套方法的时候大幅减少代码量。

4、虽然习惯上用T指代type,用E指代error。但它们其实可以是任何一个大写字母。

5、在返回结果时尤其好用。

如果顺利,我们可以返回计算结果。
如果有问题,我们返回字符串来描述失败原因。

6、实战一小下。

7、显然范型需要配套泛型函数。它“可能”长这个样子。

但编译时会报错

因为编译器不知道范型T是否可以做”==”的操作,所以报错了。,
想解决这个问题,就需要用到Rust中Traits的概念了。
往下看。

抽象方法

1、用trait关键字声明目前的类是否拥有某个方法。类似C语言的.h文件。

2、一旦引入了trait的概念。就可以检查某种数据类型是否拥有某个方法。

3、一个比较完整的例子。这样我们就可以使用泛型,但依然在编译期检查出那些非法的输入类型。

如果我们输入

会得到错误

4、我们可以给任何东西增加抽象方法,甚至是基本类型int。

5、为了防止上面那种事情影响到别人呢,Rust规定trait必须把自己框在一个范围内。

6、最后,按照惯例,Rust要表扬一下自己。
Rust是用单态(monomorphization)实现这一堆特性的。
比如我们写这样的代码。

它会在编译期就被展开成类似如下的代码:

在运行期一切都已经静态化了,并不会在运行期去猜测使用哪个方法。所以不会在运行期引入额外的开销。
但因为把方法拷贝了多份,编译出来的文件会稍大一些。

Tags

Rust官方指南摘抄(五)模式匹配

Rust官方指南摘抄系列内容如下:
(一)语法基础
(二)猜数游戏
(三)模块、测试和宏
(四)指针
(五)模式匹配
(六)方法语法、泛型和抽象方法
(七)闭包和迭代器
(八)并发任务

Rust里面的match有点类似Perl里面的given,玩法真的很多样。

1、最基本的。下划线表示其它所有情况。

2、合并两种情况。

3、范围。

4、捕获。

5、面对包含可变值的枚举类型可以用..来判断有没有值。

6、支持内嵌if。

7、指针也可以玩。

8、可以用ref来创建一个引用。

9、可以解构结构体。

10、可以部分匹配结构体。

11、以上的玩法都可以结合起来玩。

12、模式匹配确实很强大,可以大幅减少代码量。

Tags

Rust官方指南摘抄(四)指针

Rust官方指南摘抄系列内容如下:
(一)语法基础
(二)猜数游戏
(三)模块、测试和宏
(四)指针
(五)模式匹配
(六)方法语法、泛型和抽象方法
(七)闭包和迭代器
(八)并发任务

Rust提供了包括引用、Box、引用计数在内的数种资源使用方式。

引用

2、一种指针叫做引用。&表示引用,*表示解引用。

3、和正常变量一样,如果要可改,要加上mut关键字。变量和引用都要加。

4、可以被多个引用。

5、但是如果是可变的,就不能被多引用了。

6、Rust在编译器做引用的检查和到指针的转换,在运行期就是和C语言一样指针,没有额外开销。

所有权、借入和生命周期

1、Rust认为,一般程序语言处理内存的GC模式(程序员负责申请内存,语言负责释放内存)虽然经过了历史的检验,但在某些情况下并不是最优解。Rust引入了“所有权”的概念,谁申请的资源,谁就是那个资源的“所有者”,它有责任负责在必要时释放掉这个资源(资源不单单指内存)。
When you allocate heap memory, you need a mechanism to free that memory. Many languages let the programmer control the allocation, and then use a garbage collector to handle the deallocation. This is a valid, time-tested strategy, but it’s not without its drawbacks. Because the programmer does not have to think as much about deallocation, allocation becomes something commonplace, because it’s easy. And if you need precise control over when something is deallocated, leaving it up to your runtime can make this difficult.

Rust chooses a different path, and that path is called ownership. Any binding that creates a resource is the owner of that resource.

2、资源的所有者拥有以下特权:
a> 控制资源何时释放;
b> 如果资源是不可改变的,可以决定把这个资源“借”(引用)给所有你愿意借出的人;
c> 如果资源是可改变的,可以决定把这个资源“借”(引用)给某一个人;

3、资源的所有者拥有以下约束:
a> 如果你已经借出了资源,无论它是否是可改变的,你都不能改变它,也不能mut的把它再借给其它人了;
b> 如果你mut借出了资源,你就不能再存取它了,也不能再把它借给别人;

4、这个引用的“借出”机制,其实就是“共享读锁”和“独占写锁”。

5、借用人从借走到归还的这段时间,Rust称之为“生命周期”。

6、Rust有一个精密的检查机制,保证在编译期就找出违反以上规则的动作,并保证这一套机制不在运行期引入额外的开销。检查出来的错误被称为“生命周期错误”。

7、这是Rust中最重要的概念,看一下代码。

8、如果是你借用者(而非借出者),你基本没有什么特权,但是有一些规则要遵守。
a> 如果借用的是不可改变的资源,可以从中读取;
b> 如果借用的是可以改变的资源,可以从中读取,也可以往里面写入;
c> 可以把指针借给别人,但是注意下一条;
d> 如果你再次借出了,你要保证你归还之前,别人先还给你了。

9、如果上面的第四条被违反了,你借了一个资源,又继续借出了,对方没有还给你,但是资源拥有者回收了资源。这种情况就被称为“悬垂指针”(dangling pointer)。Rust的编译器会帮助我们避免这种情况发生。

会导致以下的报错。

10、有关所有者的深入话题可以在这个网址查看http://doc.rust-lang.org/guide-ownership.html,包括用’a来做类型签名。

BOX

1、之前我们的引用占用的内存都在栈上。box关键字是来帮助我们在堆上获取内存的。

2、box申请来的内存在变量超出作用域时会被自动回收。

3、Rust并不是通过GC来释放掉内存,它做的事等价下面这段C代码。

4、使用box时,如果被借出,原指针就不再可用。

这样是可以的。

Rc和Arc

1、box不允许我们把堆上的内存分配给多个指针同时使用。如果我们确实需要这样做,Rc和Arc可以帮助我们。

2、Rc是指引用计数,Arc指自动引用计数。

3、两者之间的区别在于Arc多了一个线程安全模型。当我们没有使用多线程的时候,使用Rc会使我们的程序得到更好的性能。

4、用法演示。

当x和y都超出作用范围时,内存会被自动释放。

5、如果存在循环引用,Rust无法帮助我们释放内存,就会引起内存泄漏。

6、关于Rc和Arc更多的讨论看这个链接http://doc.rust-lang.org/0.12.0/guide-pointers.html#rc-and-arc

Tags

Rust官方指南摘抄(三)模块、测试和宏

2015 年 1 月 4 日

Rust官方指南摘抄系列内容如下:
(一)语法基础
(二)猜数游戏
(三)模块、测试和宏
(四)指针
(五)模式匹配
(六)方法语法、泛型和抽象方法
(七)闭包和迭代器
(八)并发任务

模块写法

注意只有带有pub关键字的方法,才可以被模块外的代码调用。

单元测试

方法上一行写着#[test]的就是测试代码。
测试代码可以直接放在src目录里的代码内。
也可以放在tests目录下面的测试代码内。
测试代码只能针对pub方法测试。
模块本身内的测试代码可以测试private方法。

模块本身内的测试代码。

tests目录内的测试代码。

使用cargo test名字运行测试,两种测试代码都会被执行。

安全

1、有两种情况会突破Rust的安全模型。一、FFI嵌入的C代码实现的方法;二、实现某些抽象类时(比如基于共享内存的代码,Rust希望一块内存只有一个拥有者)。

2、在不安全的代码块前面用unsafe关键字告诉Rust编译器:这块的安全性我也搞定,相信我。

3、Rust保证,只要出现段错误,那肯定是由unsafe关键字标明的代码段引起的。

1、方法可以帮助我们把对变量重复的操作抽象出来。宏则可以帮忙我们做语法层的抽象。

2、宏是产生代码的代码,它可以帮助我们把一些计算和类型检查从运行期移到编译期来做。

3、使用!()来调用宏。一来可以使得宏明确区别于方法,二来可以方便告诉编译器宏到哪里结束了。

4、可以使用命令rustc example.rs –pretty=expanded将宏展开。

5、关于如何编写宏等关于宏的进一步话题看这个地址http://doc.rust-lang.org/0.12.0/guide-macros.html。

Tags

Rust官方指南摘抄(二)猜数游戏

2015 年 1 月 2 日

Rust官方指南摘抄系列内容如下:
(一)语法基础
(二)猜数游戏
(三)模块、测试和宏
(四)指针
(五)模式匹配
(六)方法语法、泛型和抽象方法
(七)闭包和迭代器
(八)并发任务

Cargo的用法

1、Cargo是Rust的Makefile,类似Ruby的Gemfile,Perl的Makefile.PL。

2、几个常用命令。

如何引用库

1、以标准库中的读取键盘输入的std::io::stdin()方法为例。方法一,直接写全方法的整个namespace。

2、方法二,把方法import进来。

3、方法三,把方法所在的模块import进来。这是推荐的做法。

标准库文档的地址

1、内置库的文档集合在下面这个地址。注意这个页面上的搜索条比较好用。

http://doc.rust-lang.org/0.12.0/std/

类型转换

1、当某个方法可以返回多个类型的值时,可以用尖括号来声明想要的返回类型(下文中的表示返回正整数,还可以传)。

2、一些情况下Rust会自动判断上下文来做自动类型转换。Option是Rust内置的一个类型。

3、字符串到数字强制类型转换的两种方法。

4、最新版中from_str()已经deprecated,推荐使用.parse()来做类型转换。

trim()方法

trim可以用来去掉行末的”\n”。

一个完整的猜数游戏

程序会随机选择一个1~99之间的数字让游戏者来猜。
每猜一个数会告诉他比正确答案大了还是小了。
猜中后程序退出。

Rust官方指南摘抄(一)语法基础

2015 年 1 月 1 日

Rust官方指南摘抄系列内容如下:
(一)语法基础
(二)猜数游戏
(三)模块、测试和宏
(四)指针
(五)模式匹配
(六)方法语法、泛型和抽象方法
(七)闭包和迭代器
(八)并发任务

变量绑定

1、Rust是一个静态类型语言,但是包含类型推断特性;
Rust is a statically typed language, which means that we specify our types up front. So why does our first example compile? Well, Rust has this thing called “type inference.” If it can figure out what the type of something is, Rust doesn’t require you to actually type it out.

2、变量默认是不可改变的;
By default, bindings are immutable. This code will not compile:

It will give you this error:

If you want a binding to be mutable, you can use mut:

There is no single reason that bindings are immutable by default, but we can think about it through one of Rust’s primary focuses: safety.

表达式VS声明

1、rust里面只有2种声明:let和“表达式声明”,其它的东西都是表达式(意味着有返回值)。
So what’s the difference? Expressions return a value, and statements do not. In many languages, if is a statement, and therefore, let x = if … would make no sense. But in Rust, if is an expression, which means that it returns a value. We can then use this value to initialize the binding.

2、对变量重新赋值也是表达式,但是和C不同,它的值并不是给变量的新值;
Note that assigning to an already-bound variable (e.g. y = 5i) is still an expression, although its value is not particularly useful. Unlike C, where an assignment evaluates to the assigned value (e.g. 5i in the previous example), in Rust the value of an assignment is the unit type () (which we’ll cover later).

3、在一个表达式后面加一个分号就会让rust抛弃它的值,使之变成一个“表达式声明”;
The semicolon turns any expression into a statement by throwing away its value and returning unit instead.

4、unit类型是一种rust特有的一种并不常用的类型,它的表示是()。“表达式声明”会返回这种类型;

方法

1、方法声明如下:

2、方法参数不会做类型推导,必须显式声明类型;
Unlike let, you must declare the types of function arguments.
This is a deliberate design decision. While full-program inference is possible, languages which have it, like Haskell, often suggest that documenting your types explicitly is a best-practice. We agree that forcing functions to declare types while allowing for inference inside of function bodies is a wonderful sweet spot between full inference and no inference.

3、带返回值的方法,rust只允许返回一个值。注意最后不能加分号,那会使得表达式变成声明,从而返回值会变成()。

4、rust当然也支持return关键字显式返回;

注释

1、rust支持普通的行注释。如下:

2、rust支持“文档注释”,用///表示,支持Markdown语法。可以用rustdoc导出成HTML等格式。如下:

复合数据类型

元组

1、元组是一组相同或不同类型的数据的集合。
Tuples are an ordered list of a fixed size. Like this:

The parenthesis and commas form this two-length tuple. Here’s the same code, but with the type annotated:

2、元组可以通过let来取值。

3、两个元组只有个数、类型、值全部相等时,才相等。

4、元组可以用来让函数返回多个值。

结构体

1、结构体就是元组的每个元素都给取了一个名字。存取方法如下。

2、结构体命名建议首字母大写,骆驼命名法;

3、默认不能改,除非使用mut关键字;

元组结构体

1、元组结构体就是整个结构体有名字,但里面的元素没有名字的一种类型。是结构体的简化版本,不常用。

2、Guide说下面这种情况下元组结构体就很有用,但我没看懂有用在哪。

枚举

1、枚举是Rust中非常有用的一个特性,在Rust标准库中被广泛使用。
比如下面这个标准库提供的枚举变量。

2、枚举里面可以放一个可变的值。
This enum has two variants, one of which has a value:

3、枚举也可以这样放下多个值

Match方法

1、Match是用来解决其他语言中Switch()的问题的,但更强大。

2、上例中的“ _ => println!(“something else”),”表示上面都没匹配到的时候执行的方法,如果去掉会编译出错。Match要求所有的情况。

3、Match也是一个表达式,可以拿它的返回值。

循环

1、Rust不支持C那种传统的循环。Rust的for循环长这样。后面的expression是一个迭代器。

2、While循环长这样。

3、死循环有两种,有些微不同,推荐使用loop。

4、支持break跳出循环,continue进入下一次循环。

字符串

1、Rust有两种字符串。&str和String。前者类似Ruby中的Symbol,是静态不可变的。后者则放在堆上,动态可变。

2、这样就是&str。

3、&str用.to_string()转String。

4、String用.as_slice()转&str。

5、&str和String之间必须用.to_string()或者.as_slice()转成相同类型后才可以做相等判断。

6、String->&str便宜。&str->String需要开内存,贵。
Converting a String to a &str is cheap, but converting the &str to a String involves allocating memory. No reason to do that unless you have to!

矢量

1、Rust中的矢量类似其他语言中的Array。和Rust的字符串一样,有多种类型,共三种。

2、可变长矢量。叹号表示是宏而不是方法,宏后面可以用小括号活着中括号,是等价的。

3、不可变长矢量。

4、Slice矢量。

5、三种矢量都支持下标取值。

6、三种矢量都支持.iter()迭代器。

7、往矢量中添加元素的方法是.push(),不可变长矢量不可以.push()。