Archive

Archive for November, 2015

视频编码原理简介

November 24th, 2015 2 comments

要彻底理解视频编码原理,看书都是虚的,需要实际动手,实现一个简单的视频编码器:

知识准备:基本图像处理知识,信号的时域和频域问题,熟练掌握傅立叶正反变换,一维、二维傅立叶变换,以及其变种,dct变换,快速dct变换。

来自知乎问题:http://www.zhihu.com/question/22567173/answer/73610451

第一步:实现有损图像压缩和解压

参考 JPEG原理,将 RGB->YUV,然后 Y/U/V 看成三张不同的图片,将其中一张图片分为 8×8的 block进行 dct变换(可以直接进行二维 dct变换,或者按一定顺序将 8×8的二维数组整理成一个64字节的一维数组),还是得到一个 8×8的整数频率数据。于是表示图像大轮廓的低频信号(人眼敏感的信号)集中在 8×8的左上角;表示图像细节的高频信号集中在右下角。

接着将其量化,所谓量化,就是信号采样的步长,8×8的整数频率数据块,每个数据都要除以对应位置的步长,左上角相对重要的低频信号步长是 1,也就是说 0-255,是多少就是多少。而右下角是不太重要的高频信号,比如步长取 10,那么这些位置的数据都要 /10,实际解码的时候再将他们 x10恢复出来,这样经过编码的时候 /10 和解码的时候 x10,那么步长为 10的信号1, 13, 25, 37就会变成规矩的:0, 10, 20, 30, 对小于步长 10的部分我们直接丢弃了,因为高频不太重要。

经过量化以后,8×8 的数据块左上角的数据由于步长小,都是比较离散的,而靠近右下角的高频数据,都比较统一,或者是一串0,因此图像大量的细节被我们丢弃了,这时候,我们用无损压缩方式,比如 lzma2算法(jpeg是 rle + huffman)将这 64个 byte压缩起来,由于后面高频数据步长大,做了除法以后,这些值都比较小,而且比较靠近,甚至右下部分都是一串0,十分便于压缩。

JPEG 图像有个问题就是低码率时 block边界比较严重,现代图片压缩技术往往要配合一些 de-block算法,比如最简单的就是边界部分几个像素点和周围插值模糊一下。

做到这里我们实现了一个同 jpeg类似的静态图片有损压缩算法。在视频里面用来保存I帧数据。

第二步:实现宏块误差计算

视频由连续的若干图像帧组成,分为 I帧,P帧,所谓I帧,就是不依赖就可以独立解码的视频图像帧,而 P帧则需要依赖前面已解码的视频帧,配合一定数据才能生成出来。所以视频中 I帧往往都比较大,而P帧比较小,如果播放器一开始收到了 P帧那么是无法播放的,只有收到下一个I帧才能开始播放。I帧多了视频就变大,I帧少了,数据量是小了,但视频受到丢包或者数据错误的影响却又会更严重。

Read more…

Categories: 编程技术 Tags:

多平台下录屏方式

November 16th, 2015 No comments

随便记录下,想得起来的多少写多少:

Windows:
GDI 全屏:fullscreen desktop BitBlt 速度是 20ms / 帧
GDI 窗口:Win7+可以录制游戏和非游戏,XP以前只能录制普通界面,截不到游戏窗口
DirectDraw 全屏:full screen primary surface 异步到 offscreen-surface XP下闪电速度 1ms / 帧
D3D9: render target -> offscreen surface XP下 16ms Win7 下 10ms
D3D Hook: Hook Device::Present -> 保存 StateBlock -> 截屏 -> 恢复 StateBlock 极速!

Linux:
framebuffer: 打开 /dev/fb0 映射到内存,直接读里面内容,部分显卡驱动启用硬件加速绘图后,framebuffer会被跳过,直接调用 framebuffer 导致读取出来的东西为空。

Android:
framebuffer: 需要 root 权限才能访问 framebuffer
RootView: 取得 root view 然后得到 drawing cache 然后保存,兼容性好,慢。
OpenGL:直接 glReadPixel 保险,但是会卡到应用。
OpenGL FBO: 更改渲染的 FBO ,让渲染到纹理,从纹理取下来,还行,速度还好。
screenshot: SurfaceComposerClient::getBuiltInDisplay Android 4.0 以后,性能不错。
native buffer: ANativeWindowBuffer -> GraphicBuffer -> 映射读取, 性能不错

iOS:
OpenGL: 直接 glReadPixel 同 Android,同样会卡到应用
OpenGL FBO: 渲染到 FBO,读取下来,还行。

大部分方法可以用 Hook 的方式注入到目标程序,或者系统 API,在不需要目标程序改一行代码的情况下实现功能。

Categories: 图形编程 Tags:

做编译器或操作系统哪个更有趣味?

November 2nd, 2015 No comments

同范畴类似的东西中,虚拟机开发比操作系统和编译器都有意思:

操作系统能玩的好玩的不多,做完进程管理和内存管理,其他就是脏活累活了,要得到一个成型可用的东西,没有一个团队弄不了,个人玩不转。以前调侃过这个话题:

关于LMOS自主操作系统的发展,大家有什么建议? – 韦易笑的回答

而编译器方面,优化和代码生成有llvm,用不着自己开发,其他部分基本上是一个固定套路,照着文档照着书,按固定套路来即可。

以 Lua为例,最精巧的部分就是 Lua虚拟机的实现,整个 Lua-5.3 代码中,编译部分只占2000行,剩下两万行全在实现虚拟机。大家津津乐道的各种 lua 奇技淫巧全都在它的虚拟机实现部分中。

Ruby更是如此,整个ruby代码除去库实现外,基本在实现ruby的虚拟机,编译器部分作者都懒得怎么写,直接一个的 .y文件搞定,精力都在ruby虚拟机实现上。

虚拟机难是难在开头的设计,怎样最精巧,怎样没有逻辑漏洞,别写着写着发现前后逻辑矛盾了,设计好以后就要开始架构了,先从最简单的 switch case opcode实现起来,很快你就能得到反馈看到自己的工作成果,接下来给虚拟机实现符号表,object,实现 gc,实现各种容器,优化性能,在内存中翻译成本地指令码,然后实现多线程,再给你的虚拟机实现一门汇编语言,每走一步都很有意思。且虚拟机相关的技术目前还在日新月异的发展着,光一个gc,每年都能看到很多新方法出现,大有需要继续改进迭代的地方。

实现虚拟机需要覆盖很多知识面,虚拟机设计的好,还可以嫁接很多其他语言,让这些新语言来这个虚拟机上运行。

大家夸 v8 都是说它虚拟机运行速度快,内存少,没人说 v8 的编译部分如何如何,以至于很多新语言都以v8虚拟机为运行环境。

同样,每年都有很多人尝试重新实现 lua vm,引入更灵巧的机制或者使用更新的 JIT方法。来pk官方runtime, 以及 luajit,好多人或多或少在某些方面都能并发出很多奇思妙想。

相反,从来没人碰 lua 的编译部分,为啥啊?就那样了,还碰它干嘛,没意思了啊。

Categories: 编程技术 Tags: ,
Wordpress Social Share Plugin powered by Ultimatelysocial