Android Camera架构浅析 amp;amp; Qualcomm 8X camera daemon进程浅析

2023-10-06 20:00

本文主要是介绍Android Camera架构浅析 amp;amp; Qualcomm 8X camera daemon进程浅析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转自:http://www.cokco.cn/thread-7779-1-1.html

Camera

先看一下抽象层的主要流程:

首先启动一个守护进程

Main()(camdaemon.c)

int qcamsvr_start(void)( qcamsvr.c)

{

1.        server_fd = open(server_dev_name, O_RDWR);//打开服务对应的文件节点

2.        if (mctl_load_comps()) //加载所有需要的组件

3.        rc = qcamsvr_load_gesture_lib(&gesture_info.gesture_lib);//加载手势库

4.        ez_server_socket_id = eztune_setup_server("127.0.0.1", "55555");

if (pipe(ez_cmd_pipe)

ez_prev_server_socket_id = eztune_setup_server("127.0.0.1", "55556");

if(pipe(ez_prev_cmd_pipe)

//创建两个socket端口,同时建立两个pipe文件对两个端口进行监控

5.        if (get_mctl_node_info(server_fd, &mctl_node_info))//通过服务节点获取服务的相关信息

{

//此处获取的是内核中调用msm_sensor_register()注册的sensor节点信息

}

6.        sub.type = V4L2_EVENT_ALL;

rc = ioctl(server_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);//通过服务设备文件的ioctl接口,订阅所有的事件

7. config_arg.server_fd = server_fd;

  config_arg.ez_read_fd = ez_cmd_pipe[0];

  config_arg.ez_write_fd = ez_cmd_pipe[1];

  config_arg.ez_prev_read_fd = ez_prev_cmd_pipe[0];

  config_arg.ez_prev_write_fd = ez_prev_cmd_pipe[1];//初始化配置线程的参数

8.下面就是一个循环,对这几个文件进行poll

   do {

    fds[0].fd = server_fd;

    fds[0].events = POLLPRI;

    fds[1].fd = ez_server_socket_id;

    fds[1].events = POLLIN;

    fds[2].fd = ez_prev_server_socket_id;

    fds[2].events = POLLIN;

    rc = poll(fds, 3, timeoutms);

if (fds[0].revents & POLLPRI) { /* Server Node Wake Up *

 //对服务的设备文件进行监视,当遇到打开事件的时候,立即创建一个配置线程

      rc = qcamsvr_process_server_node_event(&config_arg, &mctl_node_info,

         &gesture_info);

}

//线面就是对两个socket进程监视和处理。

     if ((fds[1].revents & POLLIN) == POLLIN) { /* EzTune Server */

        int client_socket_id;

        client_socket_id = accept(ez_server_socket_id,

          (struct sockaddr *)&addr_client_inet, &addr_client_len);

          write(ez_cmd_pipe[1], &client_socket_id, sizeof(int));

        }

      }

 

      if ((fds[2].revents & POLLIN) == POLLIN) { /* EzTune Prev Server */

        int client_socket_id;

        client_socket_id = accept(ez_prev_server_socket_id,

          (struct sockaddr *)&addr_client_inet, &addr_client_len);

          write(ez_prev_cmd_pipe[1], &client_socket_id, sizeof(int));

        }

      }

    } /* Else for Poll rc */

  } while (1);

}

 

下面进入配置线程创建的流程:

   //取出服务节点产生的事件,然后根据配置节点的名称,分发给各自独立的主控制线程

1.        static int qcamsvr_process_server_node_event()

{

//下命令让服务模块的事件出队列进行处理

rc = ioctl(config_arg->server_fd, VIDIOC_DQEVENT, &v4l2_evt);

if (v4l2_evt.type == V4L2_EVENT_PRIVATE_START + MSM_GES_RESP_V4L2)

{

//如果是手势事件,则进行一系列的处理

if (ctrl->type == MSM_V4L2_GES_OPEN) {

//设置主控线程的接口

p_gesture_info->cam_mctl.svr_ops.launch_mctl_thread =

          create_v4l2_conf_thread;

//设置主控线程的退出接口

p_gesture_info->cam_mctl.svr_ops.release_mctl_thread =

          destroy_v4l2_cam_conf_thread;

//设置camera使能

p_gesture_info->cam_mctl.svr_ops.camera_available =

          qcamsvr_camera_available;

    //设置服务设备文件的文件句柄

p_gesture_info->cam_mctl.svr_ops.server_fd = config_arg->server_fd;

//创建手势服务

 status = p_gesture_info->gesture_lib.gesture_service_create(

          &p_gesture_info->cam_mctl, &p_gesture_info->observer);

}

else if (ctrl->type == MSM_V4L2_GES_CLOSE) {

//消亡手势服务

status = p_gesture_info->gesture_lib.gesture_service_send_data(ctrl)

}

if ((status == CAMERA_SUCCESS) &&

        (ctrl->type != MSM_V4L2_GES_CLOSE)) {

        //如果成功,且文件打开,则向手势服务发送数据

status = p_gesture_info->gesture_lib.gesture_service_send_data(ctrl);

        if (status != CAMERA_SUCCESS) {

          LOGE("gesture_service_send_data failed");

        }

 } else {

        if (ctrl->type == MSM_V4L2_GES_CLOSE) {

          ctrl->status = CAM_CTRL_SUCCESS;

        } else {

          LOGE("gesture send failure message");

          ctrl->status = CAM_CTRL_FAILED;

        }

//将操作结果反馈给camera服务

v4l2_ioctl.ioctl_ptr = ctrl;

qcamsvr_send_ctrl_cmd_done(config_arg->server_fd, &v4l2_ioctl);

//如果是camera事件,则进行一系列的处理

}

else if (v4l2_evt.type == V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_V4L2)

{

if (ctrl->type == MSM_V4L2_OPEN) {

//通过pipe进行一些初始化工作

//创建一个核心的线程

if ((tmp_mctl_struct->handle =

          create_v4l2_conf_thread(config_arg)) == NULL)

//反馈结果给camera服务端

ctrl->status = CAM_CTRL_SUCCESS;

        v4l2_ioctl.ioctl_ptr = ctrl;

        qcamsvr_send_ctrl_cmd_done(config_arg->server_fd, &v4l2_ioctl);

}

else if (ctrl->type == MSM_V4L2_CLOSE){

//进行一些消亡工作

//通过写一些pipe

if (destroy_v4l2_cam_conf_thread(tmp_mctl_struct->handle) < 0) //消亡只线程

ctrl->status = CAM_CTRL_SUCCESS;

        v4l2_ioctl.ioctl_ptr = ctrl;

//反馈结果给camera服务

qcamsvr_send_ctrl_cmd_done(config_arg->server_fd, &v4l2_ioctl);

}

else {

//通过pipe写一些命令,等待配置返回

}

}

 

//首先来看一下刚刚的线程创建函数

void *create_v4l2_conf_thread(struct config_thread_arguments* arg)

{

//核心工作就是创建了个线程

rc = pthread_create(&pme->cam_mctl_thread_id, NULL, cam_mctl_thread, pme);

}

//下面进入创建的配置线程的主函数:

static void *cam_mctl_thread(void *data)(mctl.c)

{

//首先初始化需要监控的文件句柄

  pipe_readfd = arg->read_fd;

  pipe_writefd = arg->write_fd;

  server_fd = arg->server_fd;

  ez_pipe_readfd = arg->ez_read_fd;

  ez_client_fd = -1;

  ez_prev_pipe_readfd = arg->ez_prev_read_fd;

  ez_prev_client_fd = -1;

//向对应的配置节点下命令监控所有事件(此文件句柄具体标识什么意思暂时还没搞清楚)

sub.type = V4L2_EVENT_ALL;

rc = ioctl(cam_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);

//下面开始进入循环的监控

do {

    //文件句柄初始化

    fds[0].fd = cam_fd;

    fds[0].events = POLLPRI;

    fds[1].fd = pipe_readfd;

    fds[1].events = POLLPRI | POLLIN;

    fds[2].fd = ez_pipe_readfd;

    fds[2].events = POLLIN;

    fds[3].fd = ez_client_fd;

    fds[3].events = POLLIN;

    fds[4].fd = ez_prev_pipe_readfd;

    fds[4].events = POLLIN;

    fds[5].fd = ez_prev_client_fd;

fds[5].events = POLLIN;

/* evt/msg from qcam server */

if (ctrl->type == MSM_V4L2_CLOSE) {

//关闭所有的资源

config_shutdown_pp(pme->p_cfg_ctrl);

//反馈结果给服务

rc = mctl_send_ctrl_cmd_done(pme->p_cfg_ctrl, NULL, TRUE);

}

else {

     //此函数为用户控件的APP处理对应的命令

     if (mctl_proc_v4l2_request(pme, ctrl) < 0)

}

/* evt/msg from config node */

rc = ioctl(cam_fd, VIDIOC_DQEVENT, &v4l2_event);//下事件出队列的命令

if (v4l2_event.type ==

            V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_DIV_FRAME_EVT_MSG) {

//进程对应的帧转移

mctl_pp_divert_frame(p_cfg_ctrl,

              (void *)&(event_data.isp_data.div_frame));

}else if(v4l2_event.type ==

            V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_MCTL_PP_EVENT) {

//处理后置的事件

mctl_pp_proc_event(p_cfg_ctrl,

              (void *)&(event_data.isp_data.pp_event_info));

}

else if (v4l2_event.type ==

            V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_STAT_EVT_MSG) {

//处理正常的事件消息

mctl_proc_event_message (pme, isp_adsp);

}

else {

            CDBG_HIGH("%s: Error: should not be here", __func__);

}

 

/* evt/msg from eztune pipe */

if (ez_client_fd > 0)

        mctl_eztune_server_connect(pme, ez_client_fd);

/* evt/msg from eztune client */

if (ez_client_fd > 0) {

mctl_eztune_read_and_proc_cmd(EZ_MCTL_SOCKET_CMD);

 

/* evt/msg from eztune prev pipe */

if (ez_prev_client_fd > 0)

          mctl_eztune_prev_server_connect(pme, ez_prev_client_fd);

      }

 

/* evt/msg from eztune prev client */

 if ((fds[5].revents & POLLIN) == POLLIN) {

        if (ez_prev_client_fd > 0) {

         mctl_eztune_read_and_proc_cmd(EZ_MCTL_PREV_SOCKET_CMD);

        }

        }

}wile(TRUE)

//循环结束取消订阅所有消息

if (ioctl(cam_fd, VIDIOC_UNSUBSCRIBE_EVENT, &sub) < 0)

}

先来看看camera在硬件抽象层的接口:

主要是三点:

1.        preview:预览

2.        recording 录像

3.        picture 拍照

模块接口函数:

  get_number_of_cameras: get_number_of_cameras,

  get_camera_info: get_camera_info,

 

camera_info_t

typedef struct {

  int  modes_supported;//支持的模式

  int8_t camera_id;//id标识

  cam_position_t position;//前摄还是后摄

  uint32_t sensor_mount_angle;//角度

}camera_info_t;

 

我们目前使用的高通8X平台:

Camera模块:

此模块有一个全局的camera服务结构体实例,用于全局管理各种子系统设备。

子系统设备常见的有:

enum msm_cam_subdev_type {

CSIPHY_DEV,

CSID_DEV,

CSIC_DEV,

ISPIF_DEV,

VFE_DEV,

AXI_DEV,

VPE_DEV,

SENSOR_DEV,

ACTUATOR_DEV,

EEPROM_DEV,

GESTURE_DEV,

};

定义一个抽象的camera服务设备:

struct msm_cam_server_dev {

/* config node device*/

struct platform_device *server_pdev;

/* server node v4l2 device */

struct v4l2_device v4l2_dev;

struct video_device *video_dev;

struct media_device media_dev;

 

/* info of sensors successfully probed*/

struct msm_camera_info camera_info;

/* info of configs successfully created*/

struct msm_cam_config_dev_info config_info;

/* active working camera device - only one allowed at this time*/

struct msm_cam_v4l2_device *pcam_active;

/* number of camera devices opened*/

atomic_t number_pcam_active;

struct v4l2_queue_util server_command_queue;

 

/* This queue used by the config thread to send responses back to the

 * control thread.  It is accessed only from a process context.

 */

struct msm_cam_server_queue server_queue[MAX_NUM_ACTIVE_CAMERA];

uint32_t server_evt_id;

 

struct msm_cam_server_mctl_inst mctl[MAX_NUM_ACTIVE_CAMERA];

uint32_t mctl_handle_cnt;

 

int use_count;

/* all the registered ISP subdevice*/

struct msm_isp_ops *isp_subdev[MSM_MAX_CAMERA_CONFIGS];

/* info of MCTL nodes successfully probed*/

struct msm_mctl_node_info mctl_node_info;

struct mutex server_lock;

struct mutex server_queue_lock;

/*v4l2 subdevs*/

struct v4l2_subdev *csiphy_device[MAX_NUM_CSIPHY_DEV];

struct v4l2_subdev *csid_device[MAX_NUM_CSID_DEV];

struct v4l2_subdev *csic_device[MAX_NUM_CSIC_DEV];

struct v4l2_subdev *ispif_device;

struct v4l2_subdev *vfe_device[MAX_NUM_VFE_DEV];

struct v4l2_subdev *axi_device[MAX_NUM_AXI_DEV];

struct v4l2_subdev *vpe_device[MAX_NUM_VPE_DEV];

struct v4l2_subdev *gesture_device;

};

 

从控制流的角度来分析下camera的流程。

首先camera会启动一个daemon进程来进行核心的操作。

启动daemon进程的地方:

init.target.rc文件中

#start camera server as daemon

service qcamerasvr /system/bin/mm-qcamera-daemon

        class late_start

        user system

        group system camera inet

生成此mm-qcamera-daemon bin档的地方:

android\vendor\qcom\proprietary\mm-camera\apps\appslib\Android.mk

mk文件生成了mm-qcamera-daemon bin

 

Daemon进程的入口函数:mian()(camdaemon.c)

一个camera的守护进程在init进程中,开启的一个service

此线程与具体的sensor相关联,负责对sensor进行具体细节的操作

此为daemon进程的主线程,从server node收集事件,纷发给mctl thread,根据configname,与server节点进行队列,不断轮询其事件队列,获取command,进行全局处理

mctl_pp_poll_thread

mctl thread

main daemon thread

Daemon

 

 

 

此线程与kernelconfig节点进行通信,轮询节点的消息队列中获得command,进行全局处理

(每一个config节点都对应一个mctl thread

 

 

 

 

 

 

 

 

 

 

 

 


抽象层到内核层的大致流程:

抽象层主要通过server nodeconfig nodecommand下到内核,对应的节点驱动将command通过事件队列进行管理。

daemon进程通过开启对应的线程,不停的对事件队列进行轮询,处理上层下的command

 

main daemon thread中重要的任务:

一:将sensor操作关联的硬件组件加载进来,还要加载一些必备的库,为camera的正式工作铺垫环境:

   AXI_comp_create

   sensor_comp_create

   flash_led_comp_create

   flash_strobe_comp_create

   CAMIF_comp_create

   VFE_comp_create

   ACTUATOR_comp_create

   eeprom_comp_create

   mctl_load_stats_proc_lib

   mctl_load_frame_proc_lib

二.线程的循环工作

线程,顾名思义,肯定有一个封闭的循环体,在循环体中做一些核心的操作

Daemon进程的主线程轮询服务节点的event queue,获取事件,纷发给各自的mctl thread

Daemon进行的主线程主要处理一下基类事件“

   MSM_GES_RESP_V4L2 

Open:主要进行初始化,铺垫环境,开启处理camera细节活动的线程

Close:进行一些善后工作

   MSM_CAM_RESP_V4L2:处理opencolse

Open:主要进行初始化,铺垫环境,开启处理camera细节活动的线程

Close:进行一些善后工作

   其他一些事件都是通过pipe通信直接写入到①②两点创建的线程中(send command through pipe and wait for config to return

mctl thread中重要的任务:

一.   打开confing节点文件

二.   调用create_camfd_receive_socket猜测是与硬件抽象层进行直接通信的

三.   创建mctl_pp_poll_thread线程,

四.   初始化camera的几个feature:

  zoom_init_ctrl

  bestshot_init

  hdr_init

.通过pipe通信获取server节点的控制事件,事件由Daemon进程的主控线程获取并且通过pipe传递过来

六.通过监测config节点的事件获取config节点对应的控制command

主要监测三类事件:

   MSM_CAM_RESP_DIV_FRAME_EVT_MSG

   MSM_CAM_RESP_MCTL_PP_EVENT

   MSM_CAM_RESP_STAT_EVT_MSG

将这三个事件以command的形式,通过pipe通信发送到()中创建的PP线程中

mctl_pp_poll_thread中重要的任务:

一:对几个pipe文件进行监测,与其他线程进行交互

几种事件:

/* Events on pipe between mctl thread - mctl pp thread */

/* Events on user created socket */

/* Events on mctl pp node */

/* Events on pipe between mctl pp thread and c2d thread */



http://blog.csdn.net/qq69696698/article/details/7399321

1、Camera成像原理介绍

Camera工作流程图

image

Camera的成像原理可以简单概括如下:

景物(SCENE)通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上,然后转为电信号,经过A/D(模数转换)转换后变为数字图像信号,再送到数字信号处理芯片(DSP)中加工处理,再通过IO接口传输到CPU中处理,通过DISPLAY就可以看到图像了。

电荷耦合器件(CCD)互补金属氧化物半导体(CMOS)接收光学镜头传递来的影像,经模/数转换器(A/D)转换成数字信号,经过编码后存储。

流程如下: 
1、CCD/CMOS将被摄体的光信号转变为电信号—电子图像(模拟信号) 
2、由模/数转换器(ADC)芯片来将模拟信号转化为数字信号 
3、数字信号形成后,由DSP或编码库对信号进行压缩并转化为特定的图像文件格式储存

数码相机的光学镜头与传统相机相同,将影像聚到感光器件上,即(光)电荷耦合器件(CCD) 。CCD替代了传统相机中的感光胶片的位置,其功能是将光信号转换成电信号,与电视摄像相同。

CCD是半导体器件,是数码相机的核心,其内含器件的单元数量决定了数码相机的成像质量——像素,单元越多,即像素数高,成像质量越好,通常情况下像素的高低代表了数码相机的档次和技术指标。

2、Android Camera框架

Android的Camera子系统提供一个拍照和录制视频的框架。

它将Camera的上层应用与Application Framework、用户库串接起来,而正是这个用户库来与Camera的硬件层通信,从而实现操作camera硬件。

image

3、Android Camera的代码结构

Android的Camera代码主要在以下的目录中: 
Camera的JAVA部分 
packages/apps/Camera/。其中Camera.java是主要实现的文件。这部分内容编译成为目标是Camera.apk 
com.android.camera这个包,几个主要的类文件如下: 
PhotoViewer:GalleryPicker.java(所有图片集)--->ImageGallery.java(某个Folder下图片列表)--->ViewImage.java(看某张具体图片) 
VideoPlayer:GalleryPicker.java(所有视频集) --->MovieView.java(看某一个视频) 
Camera:Camera.java(Camera取景及拍照) 
VideoCamera:VideoCamera.java(VideoCamera取景及摄像)

 

Camera的framework供上层应用调用的部分

 

base/core/java/android/hardware/Camera.java

这部分目标是framework.jar

 

Camera的JNI部分 
frameworks/base/core/jni/android_hardware_Camera.cpp 
这部分内容编译成为目标是libandroid_runtime.so。

Camera UI库部分 
frameworks/base/libs/ui/camera 
这部分的内容被编译成库libcamera_client.so。

Camera服务部分 
frameworks/base/camera/libcameraservice/ 
这部分内容被编译成库libcameraservice.so。

Camera HAL层部分 
hardware/msm7k/libcamera 
或 
vendor/qcom/android-open/libcamera2 
为了实现一个具体功能的Camera,在HAL层需要一个硬件相关的Camera库(例如通过调用video for linux驱动程序和Jpeg编码程序实现或者直接用各个chip厂商实现的私有库来实现,比如Qualcomm实现的libcamera.so和libqcamera.so),实现CameraHardwareInterface规定的接口,来调用相关的库,驱动相关的driver,实现对camera硬件的操作。这个库将被Camera的服务库libcameraservice.so调用。

未完待续

在下一篇中,我会以两条路径来详细介绍Camera HAL的实现:自己依据V4l2规范来实现CameraHardwareInterface; Qualcomm的Camera架构(QualcommCameraHardware和mm-camera/mm-still)。当然,在涉及到Qualcomm私有库部分,为避免不必要的麻烦,我会一笔带过。敬请见谅!

这篇关于Android Camera架构浅析 amp;amp; Qualcomm 8X camera daemon进程浅析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/153892

相关文章

通信系统网络架构_2.广域网网络架构

1.概述          通俗来讲,广域网是将分布于相比局域网络更广区域的计算机设备联接起来的网络。广域网由通信子网于资源子网组成。通信子网可以利用公用分组交换网、卫星通信网和无线分组交换网构建,将分布在不同地区的局域网或计算机系统互连起来,实现资源子网的共享。 2.网络组成          广域网属于多级网络,通常由骨干网、分布网、接入网组成。在网络规模较小时,可仅由骨干网和接入网组成

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开

android 免费短信验证功能

没有太复杂的使用的话,功能实现比较简单粗暴。 在www.mob.com网站中可以申请使用免费短信验证功能。 步骤: 1.注册登录。 2.选择“短信验证码SDK” 3.下载对应的sdk包,我这是选studio的。 4.从头像那进入后台并创建短信验证应用,获取到key跟secret 5.根据技术文档操作(initSDK方法写在setContentView上面) 6.关键:在有用到的Mo

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

Android我的二维码扫描功能发展史(完整)

最近在研究下二维码扫描功能,跟据从网上查阅的资料到自己勉强已实现扫描功能来一一介绍我的二维码扫描功能实现的发展历程: 首页通过网络搜索发现做android二维码扫描功能看去都是基于google的ZXing项目开发。 2、搜索怎么使用ZXing实现自己的二维码扫描:从网上下载ZXing-2.2.zip以及core-2.2-source.jar文件,分别解压两个文件。然后把.jar解压出来的整个c

android 带与不带logo的二维码生成

该代码基于ZXing项目,这个网上能下载得到。 定义的控件以及属性: public static final int SCAN_CODE = 1;private ImageView iv;private EditText et;private Button qr_btn,add_logo;private Bitmap logo,bitmap,bmp; //logo图标private st

Android多线程下载见解

通过for循环开启N个线程,这是多线程,但每次循环都new一个线程肯定很耗内存的。那可以改用线程池来。 就以我个人对多线程下载的理解是开启一个线程后: 1.通过HttpUrlConnection对象获取要下载文件的总长度 2.通过RandomAccessFile流对象在本地创建一个跟远程文件长度一样大小的空文件。 3.通过文件总长度/线程个数=得到每个线程大概要下载的量(线程块大小)。

时间服务器中,适用于国内的 NTP 服务器地址,可用于时间同步或 Android 加速 GPS 定位

NTP 是什么?   NTP 是网络时间协议(Network Time Protocol),它用来同步网络设备【如计算机、手机】的时间的协议。 NTP 实现什么目的?   目的很简单,就是为了提供准确时间。因为我们的手表、设备等,经常会时间跑着跑着就有误差,或快或慢的少几秒,时间长了甚至误差过分钟。 NTP 服务器列表 最常见、熟知的就是 www.pool.ntp.org/zo

高仿精仿愤怒的小鸟android版游戏源码

这是一款很完美的高仿精仿愤怒的小鸟android版游戏源码,大家可以研究一下吧、 为了报复偷走鸟蛋的肥猪们,鸟儿以自己的身体为武器,仿佛炮弹一样去攻击肥猪们的堡垒。游戏是十分卡通的2D画面,看着愤怒的红色小鸟,奋不顾身的往绿色的肥猪的堡垒砸去,那种奇妙的感觉还真是令人感到很欢乐。而游戏的配乐同样充满了欢乐的感觉,轻松的节奏,欢快的风格。 源码下载

Android SurfaceFlinger——图形内存分配器(十一)

前面的文章中的图层合成器(HWC),这里我们接着看一下 SurfaceFlinger 中的另一个重要服务——图形内存分配器。 一、简介         android.hardware.graphics.allocator@2.0 是 Android 系统中硬件抽象层(HAL)的一个组件,专门用于图形内存的分配和管理。它是 SurfaceFlinger 在处理图形数据时所依赖的