Ragnarok Note

浅谈Android中Surface显示延迟处理

最近在使用GLES在Surface上渲染结果的时候,遇到了一个显示延迟的问题,当渲染时间过长,同时帧率要求比较高的时候,如果不加控制,就会造成显示的延迟,如果界面没有交互,倒是不会看出什么问题,如果是强交互的场景,这种情况下就会造成可感知的用户使用延迟了。处理这种情况的其中一种方式是给渲染的一帧赋予一个时间戳,用于给SurfaceFlinger进行对应的丢帧控制,具体什么原理呢,让我们一步一步来说明一下

Surface是什么?


从原来上讲,Android的 Surface 可以认为是目前图形架构中,作为 BufferQueue 的生产者端,Android会首先把内容渲染到Surface上,填充数据到GraphicBuffer上。而作为消费者端则为系统的SurfaceFlinger ,取出BufferQueue中的GraphicBuffer,并配合vysnc将数据送给HWC合成到屏幕上。

一般来说,Android会把所有可见的View渲染到一个由SurfaceFlinger创建的Surface上,但是这个Surface并不能由开发者直接操作,从App的开发角度来看,大部分情况下我们直接操作的Surface一般会从以下两个地方获取

  • SurfaceView / GLSurfaceView

    这两个组件是结合了Surface跟View的实现,特别的是,这两个view系统为其单独提供一层了Surface,并直接由SurfaceFlinger进行管理合成,因此实际显示在屏幕上的时候,并没有完全从属在当前的View的布局层次上,在布局对应的位置上,只是一个透明的占位符。而GLSurfaceView,则在SurfaceView的基础上提供了EGL的上下文,以便可以直接使用GLES在Surface上绘制内容

  • SurfaceTexture / TextureView

    SurfaceTexture是从Android 3 ...

谈谈关于Android视频编码的那些坑

Android的视频相关的开发,大概一直是整个Android生态,以及Android API中,最为分裂以及兼容性问题最为突出的一部分。摄像头,以及视频编码相关的API,Google一直对这方面的控制力非常差,导致不同厂商对这两个API的实现有不少差异,而且从API的设计来看,一直以来优化也相当有限,甚至有人认为这是“Android上最难用的API之一”

以微信为例,我们录制一个540p的mp4文件,对于Android来说,大体上是遵循这么一个流程:



大体上就是从摄像头输出的YUV帧经过预处理之后,送入编码器,获得编码好的h264视频流。

上面只是针对视频流的编码,另外还需要对音频流单独录制,最后再将视频流和音频流进行合成出最终视频。

这篇文章主要将会对视频流的编码中两个常见问题进行分析:

  1. 视频编码器的选择(硬编 or 软编)?
  2. 如何对摄像头输出的YUV帧进行快速预处理(镜像,缩放,旋转)?

视频编码器的选择

对于录制视频的需求,不少app都需要对每一帧数据进行单独处理,因此很少会直接用到MediaRecorder来直接录取视频,一般来说,会有这么两个选择

  • MediaCodec
  • FFMpeg+x264/openh264

我们来逐个解析一下


MediaCodec

MediaCodec是API 16之后Google推出的用于音视频编解码的一套偏底层的API,可以直接利用硬件加速进行视频的编解码。调用的时候需要先初始化MediaCodec作为视频的编码器 ...

如何获取Android系统中申请对象的信息

最近一直在做有关内存方面的优化工作,在做优化的过程,除了关注内存的申请量以及GC的情况之外,我们经常需要想方法找出是那些对象占用了大量内存,以及他们是如何导致GC的,这意味着我们需要获取对象申请的信息(大小,类型,堆栈等),我们这篇文章来介绍下几种获取对象申请信息的方法

Allocation Tracker

Allocation Tracker是android studio自带的一个功能,我们可以在MemoryMonitor中打开使用:

如上图,点击红框按钮,然后操作app,开始allocation tracking,当认为需要结束的时候,再次点击按钮,稍等片刻,即可以在android studio中dump出在 这段时间新申请 对象的信息:

这种使用方式相当直观,可以看到申请对象大小,数量,还有堆栈等,通过这些信息,我们可以作为我们接下来进行内存优化的参考

但是,对于这种获取申请对象信息的方法,会存在几个问题:

  1. 获取的信息过于分散,中间夹杂着不少其他的信息,不完全是app申请的,可能需要进行不少查找才能定位到具体的问题
  2. 跟TraceView一样,无法做到自动化分析,每次都需要开发者手工开始/结束,对于某些问题的分析可能会造成不便,而且对于批量分析来说也比较困难
  3. 虽然在allocation ...

Tricking Android MemoryFile

之前在做一个内存优化的时候,使用到了MemoryFile,由此发现了MemoryFile的一些特性以及一个非常trickly的使用方法,因此在这里记录一下

What is it

MemoryFile是android在最开始就引入的一套框架,其内部实际上是封装了android特有的内存共享机制Ashmem匿名共享内存,简单来说,Ashmem在Android内核中是被注册成一个特殊的字符设备,Ashmem驱动通过在内核的一个自定义slab缓冲区中初始化一段内存区域,然后通过mmap把申请的内存映射到用户的进程空间中(通过tmpfs),这样子就可以在用户进程中使用这里申请的内存了,另外,Ashmem的一个特性就是可以在系统内存不足的时候,回收掉被标记为"unpin"的内存,这个后面会讲到,另外,MemoryFile也可以通过Binder跨进程调用来让两个进程共享一段内存区域。由于整个申请内存的过程并不再Java层上,可以很明显的看出使用MemoryFile申请的内存实际上是并不会占用Java堆内存的。

MemoryFile暴露出来的用户接口可以说跟他的名字一样,基本上跟我们平时的文件的读写基本一致,也可以使用InputStream和OutputStream来对其进行读写等操作:

MemoryFile memoryFile = new MemoryFile(null, inputStream.available());
memoryFile.allowPurging(false);
OutputStream outputStream = memoryFile.getOutputStream();
outputStream ...

RxCamera, 一个RxJava风格的android camera封装

事实上这个库写了已经有一段时间了,由于最近工作上比较忙,所以现在才写一篇文章来总结

What's this

正如标题所说,RxCamera是一个基于RxJava而构建的一套android.hardware.camera封装的库。最初写这个库的目的是为了熟悉RxJava,并且而且也看到虽然在android开发已经有不少基于RxJava的库,但是关于音视频相关却少之又少,于是就动手实现了一下。目前这个库还处于非常早期的状态,API比较简陋,并且关于camera的设置还有很多没有做

How to use

RxCamera的README中已经有关于这个库使用的比较详细介绍了,我在这里再说明一下:

加入依赖

首先你需要在项目中加入对RxCamera项目的依赖:

repositories {
        jcenter()
}
dependencies {
    compile 'com.ragnarok.rxcamera:lib:0.0.1'
}

基本的使用

  • 配置camera的参数

android的原生camera api提供了不少的选项来配置打开摄像头时候的参数,例如预览的帧率,预览的分辨率,自动对焦等,在RxCamera中主要是通过一个RxCameraConfig对象来管理这些对象 ...