性能比肩美拍秒拍的Android视频录制编辑特效解决方案
前言
在进行Android平台的音视频开发时,Java层API的支持在MediaCodec之前还相对抽象,功能受限。MediaCodec虽在后期推出,但也存在兼容性问题以及各厂商实现不一致的情况。开发者开始转向NDK寻求更丰富的源码天空拍照音视频处理能力,但NDK提供的API并不全面,尤其是音视频处理方面。因此,开发者们考虑使用开源的C/C++框架,如ffmpeg、x、mp3lame、faac等。然而,这些框架在不同平台如ARM和mips的支持上存在局限,且软解软编导致编码速度较慢,无法满足高帧率录制需求。因此,本文旨在提供一个性能更佳、兼容性更强的Android视频录制编辑解决方案。
NDK可用API介绍
在NDK中,开发者可以利用一些API进行音视频处理。例如,OpenSL可直接在C++层操作音频设备,进行录音和播放声音;EGL可用于创建OpenGL环境,进行视频图像渲染、图像处理等;OpenGL(ES)提供C++层的OpenGL接口;OpenMAXIL为视频播放提供抽象接口。此外,mapbox修改源码还需注意的是,OpenMAXAL虽然提供了抽象接口,但不支持Android平台的摄像头使用,因此需要从Java层获取摄像头数据。
选择开源框架
在处理音频编码问题时,考虑到ffmpeg、x、mp3lame和faac等开源框架的性能与兼容性,选择ffmpeg2.7.5版本进行文件解析、图像拉伸、像素格式转换以及大多数解码器,x作为H编码器,并使用最新版本进行优化,faac编码器虽存在速度问题,但通过曲线救国的方式解决了音频编码问题。最后,引入OpenGL2D/3D引擎,如COCOS2D-X,用于视频特效处理,同时简化了COCOS2D-X的回收机制,使其更符合项目需求。
完整解决方案
为解决音频编码速度慢的问题,采用ffmpeg直接处理视频编码,而音频数据则写入文件。这样既能灵活配置编码参数,实现快速编码,又能避免磁盘写入速度的瓶颈。同时,家具溯源码多线程异步写入数据可以满足编码速度与帧率的匹配需求。引入OpenGL2D/3D引擎,如COCOS2D-X,用于添加视频特效,并简化其回收机制,提高性能。
主副线程模式
为确保OpenGL操作的线程安全,设计了主副线程模式。主线程负责UI的响应,而副线程则用于执行其他耗时任务,如OpenGL渲染等。通过任务接口实现多任务调度,提高整体性能和稳定性。
总结与优化
选择合适的API版本(ffmpeg2.7.5、x最新版本)并开启优化选项(asm,neon等)。采用分步编码策略,视频数据直接调用x编码,音频数据写入文件。引入COCOS2D-X作为特效引擎,简化其回收机制。设计主副线程模式,确保OpenGL操作在单一线程内执行,提高性能稳定性。
源码与演示
完整工程源码已发布,支持API及以上版本。操作演示和视频生成位置已提供链接。需要注意的智能电饭煲源码API调用细节如下:
1、com.android.video.camera.EFCameraView类中设置当前选用的摄像头分辨率宽度和高度。
2、jni/WORKER/EFRecordWorker.cpp中的createRecordWorker函数内,配置当前录制视频的各种基本参数。
3、jni/WORKER/EFRecordWorker.cpp的on_create_worker函数内,设置OpenGL绘制帧率,与视频帧率不同,请根据实际需求设置。
感谢社区反馈,针对优化建议:
1、使用更优的AAC开源方案,推荐FDKAAC。
2、尝试升级OpenGL版本,使用GLES 3.0实现快速获取渲染结果图像。
在Android上进行音视频处理,结合特定版本的API和开源框架,可以实现更高效、兼容性强的解决方案。随着技术的不断演进,Android平台在音视频处理方面的能力也在不断提升。
å¦ä½å¨Androidä¸å®ç°FrameBufferåOverlayçblend
1.SurfaceFlingeræ¯ä¸ä¸ªæå¡ï¼ä¸»è¦æ¯è´è´£åæåçªå£çSurfaceï¼ç¶åéè¿OpenGLESæ¾ç¤ºå°FrameBufferä¸ã
2.DisplayHardwareæ¯å¯¹æ¾ç¤ºè®¾å¤çæ½è±¡ï¼å æ¬FrameBufferåOverlayãå è½½FrameBufferåOverlayæ件ï¼å¹¶åå§åOpenGLES:
view plain
mNativeWindow = new FramebufferNativeWindow();
framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {
overlay_control_open(module, &mOverlayEngine);
}
surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
eglMakeCurrent(display, surface, surface, context);
3.FramebufferNativeWindow æ¯framebuffer çæ½è±¡ï¼å®è´è´£å è½½libgrallocï¼å¹¶æå¼framebuffer设å¤ãFramebufferNativeWindow并ä¸ç´æ¥ä½¿ç¨ framebufferï¼èæ¯èªå·±å建äºä¸¤ä¸ªBufferï¼
queueBufferè´è´£æ¾ç¤ºä¸ä¸ªBufferå°å±å¹ä¸ï¼å®è°ç¨fb->postå»æ¾ç¤ºã
dequeueBufferè·åä¸ä¸ªç©ºé²çBufferï¼ç¨æ¥å¨åå°ç»å¶ã
è¿ä¸¤ä¸ªå½æ°ç±eglSwapBuffersè°è¿æ¥ï¼è°å°
view plain
egl_window_surface_v2_t::swapBuffersï¼
nativeWindow->queueBuffer(nativeWindow, buffer);
nativeWindow->dequeueBuffer(nativeWindow, &buffer);
4.msm7k/liboverlayæ¯Overlayçå®ç°ï¼ä¸å ¶å®å¹³å°ä¸åçæ¯ï¼é«éå¹³å°ä¸çOverlay并ä¸æ¯æä¾ä¸ä¸ªframebuffer设å¤ï¼èéè¿fb0çioctlæ¥å®ç°çï¼ioctlå为两类æä½ï¼
OverlayControlChannelç¨äºè®¾ç½®åæ°ï¼æ¯å¦è®¾ç½®Overlayçä½ç½®ï¼å®½åº¦åé«åº¦ï¼
view plain
bool OverlayControlChannel::setPosition(int x, int y, uint_t w, uint_t h) {
ov.dst_rect.x = x;
ov.dst_rect.y = y;
ov.dst_rect.w = w;
ov.dst_rect.h = h;
ioctl(mFD, MSMFB_OVERLAY_SET, &ov);
}
OverlayDataChannelç¨äºæ¾ç¤ºOverlayï¼å ¶ä¸æéè¦çå½æ°å°±æ¯queueBuffer:
view plain
bool OverlayDataChannel::queueBuffer(uint_t offset) {
mOvData.data.offset = offset;
ioctl(mFD, MSMFB_OVERLAY_PLAY, odPtr))
}
5.msm7k/libgralloc æ¯æ¾ç¤ºç¼åçæ½è±¡ï¼å æ¬framebufferåæ®éSurfaceçBufferãframebufferåªæ¯/dev/graphic/fb0çå è£ ï¼SurfaceçBufferåæ¯å¯¹/dev/pmemãashmemåGPUå å(msm_hw3dm)çå è£ ï¼å®çç®æ 主è¦æ¯æ¹ä¾¿ç¡¬ä»¶å éï¼å 为 DMAä¼ è¾ä½¿ç¨ç©çå°åï¼è¦æ±å åå¨ç©çå°åä¸è¿ç»ã
6.msm7k/libcopybitè¿æ¯2Då éåºï¼ä¸»è¦è´è´£Surfaceçæ伸ãæ转ååæçæä½ãå®æ两ç§å®ç°æ¹å¼ï¼
copybit.cpp: åºäºfb0çioctl(MSMFB_BLIT)çå®ç°ã
copybit_c2d.cpp: åºäºkgslçå®ç°ï¼åªæ¯å¯¹libC2D2.soçå è£ ï¼libC2D2.soåºè¯¥æ¯ä¸å¼æºçã
7.pmem
misc/pmem.c: 对ç©çå åç管çï¼ç®æ³åç¨æ·ç©ºé´çæ¥å£ã
board-msm7x.cå®ä¹äºç©çå åç缺ç大å°ï¼
view plain
#define MSM_PMEM_MDP_SIZE 0x1B
#define MSM_PMEM_ADSP_SIZE 0xB
#define MSM_PMEM_AUDIO_SIZE 0x5B
#define MSM_FB_SIZE 0x
#define MSM_GPU_PHYS_SIZE SZ_2M
#define PMEM_KERNEL_EBI1_SIZE 0x1C
msm_msm7x2x_allocate_memory_regionsåé å 大åå åç¨äºç»pmemåäºæ¬¡åé ã
8.KGSL
Kernel Graphics System Layer (KGSL)ï¼3Då¾å½¢å é驱å¨ç¨åºï¼æºä»£ç drivers/gpu/msmç®å½ä¸ï¼å®æ¯å¯¹GPUçå è£ ï¼ç»OpenGLES 2.0æä¾æ½è±¡çæ¥å£ã
9.msm_hw3dm
è¿ä¸ªæå¨å æ ¸ä¸æ²¡ææ¾å°ç¸å ³ä»£ç ã
.msm_fb
msm_fb.c: framebuffer, overlayåblitçç¨æ·æ¥å£ã
mdp_dma.c: å¯¹å ·ä½æ¾ç¤ºè®¾å¤çå è£ ï¼æä¾ä¸¤ç§framebufferæ´æ°çæ¹å¼ï¼
mdp_refresh_screenï¼ å®æ¶æ´æ°ã
mdp_dma_pan_update: éè¿pan display主å¨æ´æ°ã
mdp_dma_lcdc.cï¼é对LCDå®ç°çæ¾ç¤ºè®¾å¤ï¼mdp_lcdc_updateç¨æ´æ°framebufferã
opengl和skia哪个快
从Honeycomb[3.x]版本起,Andorid便支持GPU加速,但目前Android并没有使用Skia GPU进行Webkit渲染。Skia GPU使用OpenGL进行后台加速渲染,未来也许会代替Skia。
很多人觉得,裸k 源码即使Android成功使用了GPU加速Webkit渲染,在访问浏览如雅虎等一般的网站时,用户也感觉不到太大的差异。因为Webkit的资源大多数消耗在了Javascript脚本和布局定位上。
我们觉得Webkit使用GPU加速渲染的最大意义无非是HTML5 Canvas[HTML5的动态绘图效果]。Android渲染Canvas动画实在太慢,导致Web开发者根本无法在Android上用Canvas开发网页游戏[要注意的是,目前很多手机和平板的应用程序以HTML5做为界面,并使用Webkit工作,这也是很多应用在Android系统上感觉不流畅的重要因素。
Android Webkit开发平台[NDK]使用Skia GPU加速测试
我们对Android系统使用Skia GPU加速的Webkit进行了测试。我们手上已经有Android Webkit NDK的WAC2.0版本,我使用了某个提交版本的Skia源码,并开启Skia GPU加速将其编译进NDK中。
我并没有使用Canvas加速,因为这还要增加修改GraphicsContextSkia API的工作,所以并未测试Canvas渲染的性能。
为了使用Skia GPU加速,我做了以下两点:
1,新增了一个使用GLSurfaceView的eglContext内容。
2,在WebView.cpp中使用SkGpuCanvas代替SkCanvas。 我在系统版本为2.3.2的Nexus S上测试,并禁用了屏幕合成加速和Webkit后备缓存,结果出乎意料,Skia GPU反而降低了绘图性能,比Skia使用CPU渲染的时候慢了两倍以上。
当用户滚动雅虎网站页面的时候,每一帧都会使Webkit对页面元素进行重绘。页面元素包括%的文本,%的矩形和%的图像,Skia GPU加速渲染时候反而慢了五倍。
你看到图表后也许会觉得Skia GPU渲染SVG动画时是要比CPU快那么一丁点了。不过Webkit在渲染SVG动画的时候出了一些问题,它绝大多数时间花在了定位布局SVG元素上,而不是渲染SVG元素。所以我不敢确定Skia使用GPU加速时是不是真的变快了。
Skia在栅格化文本的时候使用的是CPU而不是GPU,它将文本缓存为材质贴图。因此Skia GPU加速并不会增加滚动文本时的速度。
我一开始觉得Skia GPU加速会在绘制飞舞的浏览器图标时理应能速度更快了,毕竟那是位图动画,是GPU的强项。结果,Skia GPU渲染慢了倍由于还没有得到详细结果,所以我们需要做进一步的研究,以找到问题的原因。
当你构建Skia的时候,你会得到一个跑分程序,运行之后,你会看到使用CPU和GPU渲染时的性能差异。下面是一些测试得分中的重点项目。
SurfaceTexture详解
ä¹å讲å°äº flutterçTexture
SurfaceTexture æ¯ Surface å OpenGL ES (GLES) 纹ççç»åãSurfaceTexture ç¨äºæä¾è¾åºå° GLES 纹çç Surface
SurfaceTexture å å«ä¸ä¸ª BufferQueueãå½ç产æ¹å°æ°çç¼å²åºæå ¥éåæ¶ï¼onFrameAvailable() åè°ä¼éç¥åºç¨ãç¶åï¼åºç¨è°ç¨ updateTexImage()ï¼è¿ä¼éæ¾å åå æçç¼å²åºï¼ä»éåä¸è·åæ°ç¼å²åºå¹¶æ§è¡ EGL è°ç¨ï¼ä»è使 GLES å¯å°æ¤ç¼å²åºä½ä¸ºå¤é¨çº¹ç使ç¨ã
å ³é®æ¹æ³ï¼
SurfaceTexture(int texName, boolean singleBufferMode)æé æ¹æ³
setOnFrameAvailableListener 设置åè°ï¼å½ç产è åå¤å¥½æ°ç帧åä¼è°ç¨Listener
updateTexImage æ´æ°textureå°æå®çGLESContext
detachFromGLContext
attachToGLContext
解ç»/ç»å® å½åGLContext
getTransformMatrix 设置ééæ ·çº¹çç©éµï¼å½æ¸²æçæ¶åä¼ç¨å°è¿ä¸ªæ°æ®
release() å®å ¨éæ¾ SufaceTextureç buffers并ä¸å§Surfaceç¶æ置为abandoned
android-8.0.0_r1 æºç 解æï¼
GLConsumeråæ°è§£éï¼
bqæ¯BufferQueueå建BufferConsumer
tex 表示è¦å°å¾åæµä¼ è¾å°çOpenGL ES纹çå称ã
texTargetæå®äºåªä¸ªçº¹çå°è¢«ç»å®
useFenceSync表示æ¯å¦éè¦åæ¥è®¿é®ç¼å²åº
å¯ä»¥ä»ä¸ä¸ªOpenGL ESä¸ä¸æä¸å离GLConsumerï¼ç¶ååå«ä½¿ç¨detachFromContextåattachToContextæ¹æ³å°GLConsumeréå å°å¦ä¸ä¸ªä¸ä¸æã
å¦æ设置texåæ°åä¼éè¿attachToContextå°GLConsumeréå å°OpenGL ES contextä¸ã
第ä¸æ¬¡è°ç¨updateTexImageæä¼ç»å®ï¼ä¹åææ对updateTexImageçè°ç¨å¿ 须使ç¨ç¸åçå½åOpenGL ES contextè¿è¡
acquireBufferLockedå建EglImage并设置å°EglSlotsä¸
updateAndReleaseLocked æ´æ° EglImage
createIfNeeded å¦æEGLDisplayæ¹åæè cropæ¹ååä¼å建EglImage
bindToTextureTarget å°è°ç¨glEGLImageTargetTexture2DOESå»ç»å®imageå°æå®çç®æ 纹ç
è¿éå建EGLImageKHRï¼EGLImageKHRç¨äºå ±äº«EGLèµæº
EGLçShareContextæ¯å¸¸è§çå ±äº«ä¸ä¸æçæ¹å¼ï¼iOSå¹³å°çEAGLå«ShareGroupï¼ã
å½share_contextåæ°ä¼ å ¥å¦ä¸ä¸ªEGLçcontextæ¶ï¼è¿ä¸¤ä¸ªEGLContextå°±å¯ä»¥å ±äº«çº¹ç以åVBOçã
éè¦æ³¨æçæ¯container objectsä¸è½è¢«å ±äº«ï¼æ¯å¦ï¼
Framebuffer objects
Vertex array objects
Transform feedback objects
Program pipeline objects
åèï¼ /project/deep-android-v1/classes.html
EGLImageKHR: mon (out/host/linux-x/obj/STATIC_LIBRARIES/libGLcommon_intermediates/libGLcommon.a)
解决:sudo ln -s /usr/lib/i-linux-gnu/mesa/libGL.so.1 /usr/lib/i-linux-gnu/libGL.so
. 错误:make: *** [out/host/linux-x/obj/EXECUTABLES/obbtool_intermediates/Main.o] Error 1
后来发现了,原来是Ubuntu .里的gcc和g++版本太高了,于是执行下面的操作:
sudo apt-get install gcc-4.4
sudo apt-get install g++-4.4
sudo rm -rf /usr/bin/gcc /usr/bin/g++
sudo ln -s /usr/bin/gcc-4.4 /usr/bin/gcc
sudo ln -s /usr/bin/g++-4.4 /usr/bin/g++
把默认的4.6版本换为了4.4,继续编译源码,又出现了另一个错误,大致提示为:
g++ selected multilib '' not installed
继续奋战吧,安装相应的工具吧:sudo apt-get install g++-4.4-multilib,现在正在make -j8(开启多线程编译(不推荐),可能有时候会出现问题,最好是直接make)
2. 解决各种依赖问题
首先安装一些库
?View Code BASH
1 sudo apt-get install gnupg flex bison gperf libsdl1.2-dev libesd0-dev
2 sudo apt-get install libwxgtk2.6-dev squashfs-tools build-essential
3 sudo apt-get install zlib1g-dev pngcrush schedtool ia-libs libncurses5-dev
这些库可能不全,如果出现问题,再google一下吧
3. error: “_FORTIFY_SOURCE” redefined [-Werror]
这个问题,据说与gcc版本有关,4.4版不会出现。
后来在google code上找到了使用gcc 4.6编译的方法
修改build/core/combo/HOST_linux-x.mk文件line
?View Code BASH
1 -HOST_GLOBAL_CFLAGS += -D_FORTIFY_SOURCE=0
2 +HOST_GLOBAL_CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0
这是CyanogenMod打上的补丁
4. No rule to make target ‘out/target/product/generic/obj/lib/libcamera.so’
修改 /home/Android-2.3.4/frameworks/base/services/camera/libcameraservice/Android.mk,USE_CAMERA_STUB:=false -> true
?View Code BASH
1 LOCAL_PATH:= $(call my-dir)
2
3 # Set USE_CAMERA_STUB if you don't want to use the hardware camera.
4
5 # force these builds to use camera stub only
6 ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),)
7 USE_CAMERA_STUB:=true
8 endif
9
#########CHANGE THIS LINE############
USE_CAMERA_STUB:=true
ifeq ($(USE_CAMERA_STUB),)
USE_CAMERA_STUB:=false
endif
Android HWUI 源码研究 View Canvas RenderThread ViewRootImpl skia
HUWUI是Android系统中负责应用可视化元素绘制的核心组件,其架构主要在C++层实现,从Java层接收View绘制信息,通过唯一的渲染线程使用skia技术完成渲染任务。整体上,从应用程序到UI线程,再到渲染线程,形成了清晰的层级关系。
HUWUI的构建主要包括三个核心类,它们分别是:RecordingCanvas、Canvas、RenderNode、RenderProxy、RenderThread、CanvasContext、IRenderPipeline。在Java层,主要涉及两类Canvas,RecordingCanvas用于记录绘制指令,Canvas则是直接用于渲染。RecordingCanvas在构造时创建,而Canvas在调用时创建。这两个类在C++层分别对应SkiaRecordingCanvas和SkiaCanvas,后者直接引用SkCanvas。
在全局循环中,UI线程与渲染线程之间的协同操作至关重要。具体流程包括:新创建Activity后,附着到对应的PhoneWindow,然后调用PhoneWindow的setContentView方法,将View添加到DecorView作为子节点。接着,DecorView与ViewRootImpl对接,完成View的更新与渲染。整个过程包含了measure、layout和draw等复杂子流程。
渲染线程创建与核心对象紧密关联,主要包括RenderProxy、RenderThread和DrawFrameTask。RenderProxy负责Java层信息的衔接,RenderThread作为进程唯一的渲染线程,持有DrawFrameTask和CanvasContext,完成一帧的绘制任务。指令记录流程的核心在于使用C++层的RecordingCanvas将View属性和绘制信息记录到DisplayList中,进而完成指令的渲染。
Surface、ANativeWindow、EGLSurface的创建流程在ViewRootImpl的performTraversals函数中初始化。ReliableSurface的封装和EGL与Skia环境的创建主要在RenderThread的requireGlContext函数中实现。从源码分析,这一过程通常在三个地方调用。
View树与RenderNode树之间的协作关系明确,一个Application进程对应多个Activity,每个Activity与一个PhoneWindow绑定,PhoneWindow持有DecorView,DecorView对应一个ViewRootImpl,而ViewRootImpl与ThreadedRender模块对接。ThreadedRender与C++层的RenderProxy一一对应,RenderProxy持有关键对象,如RenderThread、CanvasContext、DrawFrameTask等。RenderThread是单例模式,进程唯一,负责一帧绘制的逻辑。
在RenderPipeline模块中,关键操作包括makeCurrent、draw和swapBuffers。Native Canvas在这一过程中扮演了桥梁角色,接收Java API调用,而RecordingCanvas完成Op记录,最终DisplayListData存储这些Op。
skia的核心资源主要在三个使用场景中发挥作用,具体细节需深入分析,这些资源对于实现高效、稳定的渲染效果至关重要。
安卓系统 android 2.1 android 2.2 android 2.3 有什么区别?
安卓系统 android 2.1 android 2.2 android 2.3 区别如下:
1.Android 2.1: 年 月 日,又一个主要版本升级以创纪录的速度放出。这次,大版本升级到了Android 2.1 “Eclair” 。
Android 2.0.1 SDK 于 年 月 3 日 发布,之后是 年 1 月 日的 2.1 版本。很多用户和围观群众可能会奇怪:“为什么 Android 会用甜点作为它们系统版本的代号?”,这个命名方法开始于 Andoird 1.5 发布的时候。作为每个版本代表的甜点的尺寸越变越大,然后按照字母数序:小蛋糕,甜甜圈还有松饼。之前人们预计 2.2 版本的代号会是“馅饼”,但这个被最终证明是错误的,“FroYo”(冻酸奶)才是Android 2.2这个伴随 Google Nexus One 发布的新版的最新代号。
2.谷歌于北京时间年5月日晚上:点在旧金山Moscone会展中心举办Google I/O 大会第二天的会议,Google正式发布了代号是“froyo 冻酸奶”的Android手机操作系统2.2版。
3.北京时间年月7日凌晨,Google正式对外发布了他们的智能手机操作系统Android 2.3,也就被大家所熟知的AndroidGingerbread(姜饼)系统。虽然在版本方面Android2.3相对于前作而言的提升并不算多,但是从功能以及界面的变化上来看还是十分明显的。
2024-11-19 09:26
2024-11-19 09:14
2024-11-19 08:44
2024-11-19 08:19
2024-11-19 08:08