当前位置: 首页 > news >正文

camera2对摄像头编码h264

MediaCodec编码摄像头数据

前置:保存的一些成员变量

// 摄像头开启的 handler
private Handler cameraHandler;
// Camera session 会话 handler
private Handler sessionHandler;
//这里是个Context都行
private AppCompatActivity mActivity;
// 这个摄像头所有需要显示的 Surface,以TextureView创建的Surface在一开始就已经加进去了,如果用SurfaceView的话,注意管理好SurfaceView因为onstop,onStart创建和销毁的Surface
private List<Surface> mViewSurfaces;
private CameraManager cameraManager;private String mCameraId;
private CameraDevice mDevice;
//Camera 配置信息
private CameraCharacteristics mCharacteristics;
private CameraCaptureSession mSession;// 编码输入的Surface,由MediaCodec创建
private Surface mStreamSurface;
// 摄像头选择的输出大小
private Size mSize;//编码器
private MediaCodec mMediaCodec;

一、选择要打开的摄像头,并保存对应配置参数。

  1. 获取打开的摄像头 ID,以及获取对应参数配置.
    try {
    String[] cameraIdList = cameraManager.getCameraIdList();
    // 遍历摄像头,获取第一个可用的摄像头,多个摄像头暂不播放
    for (String id : cameraIdList) {
    CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(id);
    // 获取该摄像头的说明数据
    int[] ints = cameraCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    if (ints != null) {
    Arrays.stream(ints).filter(value -> value == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE).close();
    if (ints.length == 0) {
    Log.d(TAG, “the camera has not basic supports.”);
    continue;
    }
    mCameraId = id;
    mCharacteristics = cameraCharacteristics;
    // 由于目前无具体需求,使用第一个可连接的摄像头进行显示,选完就返回,有需要的获取配置参数自行判定
    break;
    }
    }
    } catch (CameraAccessException e) {
    throw new RuntimeException(e);
    }

    说明:CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE 只是为了确定这个 Camera可用而已。

  2. 获取摄像头的所有的输出尺寸,选一个合适的
    //获取配置参数
    StreamConfigurationMap configurationMap = mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    //拿到对应 ImageFormat.YUV_420_888 所有可输出的尺寸,一般比较多,1280x720,1920x1080等等一大堆,传参有点重要下面说明会说.
    Stream stream = Arrays.stream(configurationMap.getOutputSizes(ImageFormat.YUV_420_888));
    //这里我选了宽高相乘最大的
    Optional max = stream
    .max(Comparator.comparingInt(item -> item.getWidth() * item.getHeight()));
    stream.close();
    //把输出尺寸保存起来,后面创建编码器要用,其实就是视频的分辨率
    max.ifPresent(value -> mSize = value);

3. 比较重要的参数configurationMap.getOutputSizes(ImageFormat.YUV_420_888)传参为什么是ImageFormat.YUV_420_888说明 比较重要的是,这个值不是随便设置的,是通过获取设备参数之后选的,只要是设备参数里有的都能用,但是没有的值,用了可是会出错的。

获取Camera图像格式的方法:
int[] outputFormats = configurationMap.getOutputFormats();

遍历打印一下,选一个就行,但是尽量按照参数说明搭配需求去选。

二、创建编码器

  1. 通过步骤一拿到的输出尺寸,配置编码格式.(只设置了必要的几个参数,其他的自选加入)

     //创建编码格式MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", mSize.getWidth(), mSize.getHeight());//设置码率mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, (int) (mSize.getHeight() * mSize.getWidth() * 0.2));//设置帧率60mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 60);//设置颜色格式,因为这里用的自动编码,就不用yuv420自己手动编了,直接用surfacemediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);//设置关键帧产生速度 1,单位是(帧/秒)mediaFormat.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1f);//摄像头编码角度会出现90度的偏差,设置正向旋转90显示正常效果,你要喜欢歪头90看其实也行mediaFormat.setInteger(MediaFormat.KEY_ROTATION, 90);Log.d(TAG, "MediaCodec format: " + mediaFormat);
    

说明:
(1) 编码格式创建中,video/avc 表示编码为 h264 编码,编码其实就是为了降低视频大小.

(2) 码率设置中,后面的乘以0.2只是为了降低码率,不然太清晰了。。。正常的大多数是乘以5,这样看着清晰多,当然大小也会增加.

(3) 帧率设置为24以上就行,不然就是PPT。

(4) 关键帧产生速度SDK25之后可以设为float类型,关键帧就是 i 帧,设置之后只是说尽量按这个速度产生,肯定不会一模一样的,0或者复数就是希望没帧都是关键帧

  1. 创建编码器
    try {
    //按照“video/avc”创建编码器,硬编码是基于硬件的,厂商不同会有不一样的硬编码实现,编码是Encode,别用成Decode解码器
    mMediaCodec = MediaCodec.createEncoderByType(“video/avc”);
    } catch (IOException e) {
    e.printStackTrace();
    }
    //配置编码器,MediaCodec状态机就不说了,这里只提使用。参数4也是说明是编码器,和创建时不一致也会报错
    mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    //创建输入Surface,和MediaFormat设置的KEY_COLOR_FORMAT参数是有联系的,format那里设错了就不能用自动挡的surface,且createInputSurface()方法必须在configure()之后,start()之前调用
    mStreamSurface = mMediaCodec.createInputSurface();
    //开启编码
    mMediaCodec.start();

说明:四个点
(1) MediaCodec.createEncoderByType(“video/avc”),创建编码器,MediaCodec.createDecoderByType(“video/avc”)是创建解码器的。byName那个的话想用得先获取硬件已实现的编码器的名字才能调,虽然不麻烦,但是懒得敲,而且因为硬件厂商实现不同,兼容性还得考虑。

(2) configure()很容易就报错,参数4编解码值是不一样的,以及MediaFormat的设置,如果和摄像头、手机不支持也会报错,所以前面写的那些代码就是为了拿的,不然随便写一个就行了。

(3) MediaCodec.createInputSuface()方法必须在 configure()配置编码之后,start()开始编码之前调用,点进实现也有说明,出现其实也挺好解决,你也可以用mMediaCodec.setInputSurface(创建的常显Surface)去用自己的Surface,没有常显Surface的话可以静态方法MediaCodec.createPersistentInputSurface()创建一个常显Surface.

(4) 在 start()开启编码之后,MediaFormat参数格式其实是可以改的,只是需要用Bundle,通过key-value的方式设值,然后mMediaCodec.setParameters(bundle)去动态设置。

三、操作摄像头

  1. 打开摄像头预览

摄像头 ID之前已经拿过了,所以直接open就行了。
cameraManager.openCamera(cameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
Log.d(TAG, "onOpened, camera: " + camera);
mDevice = camera;

            // Camera 打开之后,把创建的解码器 Surface 加入显示队列中addSurface(mStreamSurface);//创建请求CaptureRequest request = createCaptureRequest(mDevice, mViewSurfaces);//创建输出配置项List<OutputConfiguration> outputConfigurations = createOutputConfig(mViewSurfaces);try {SessionConfiguration config = new SessionConfiguration(SessionConfiguration.SESSION_REGULAR,outputConfigurations,new ThreadPoolExecutor(3, 5, 15, TimeUnit.SECONDS, new ArrayBlockingQueue<>(25)),new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession session) {mSession = session;try {//到这个回调,摄像头就可以正常预览了,编码器也会输出编码数据到输出队列了mSession.setRepeatingRequest(request, null, sessionHandler);} catch (CameraAccessException e) {throw new RuntimeException(e);}}@Overridepublic void onConfigureFailed(@NonNull CameraCaptureSession session) {Log.e(TAG, "onConfigureFailed, camera: " + session);if (mSession != null) mSession.close();if (mDevice != null) mDevice.close();mDevice = null;}});mDevice.createCaptureSession(config);} catch (CameraAccessException e) {error();}}@Overridepublic void onDisconnected(@NonNull CameraDevice camera) {Log.e(TAG, "onDisconnected, camera: " + camera);}@Overridepublic void onError(@NonNull CameraDevice camera, int error) {Log.e(TAG, "onError, camera: " + camera + " ,error : " + error);}}, cameraHandler);
  1. 创建CaptureRequest请求的函数(因为涉及到多个Surface,就单独创建了)

    private CaptureRequest createCaptureRequest(CameraDevice cameraDevice, List surfaces) {
    CaptureRequest.Builder captureRequest;
    try {
    //简单预览模式创建,有要求可以根据api去选video这些
    captureRequest = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    //聚焦模式,不设置也可以
    captureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
    } catch (CameraAccessException e) {
    throw new RuntimeException(e);
    }
    if (surfaces != null) {
    //把每一个需要输出摄像头数据的Surface都加入
    for (Surface surface : surfaces) {
    if (surface == null) {
    continue;
    }
    captureRequest.addTarget(surface);
    }
    }
    return captureRequest.build();
    }

  2. 创建List的函数,Surface比较多,用的比较新的api去创建的session,老的api已经被标注遗弃了,所以单独列个方法。

    private List createOutputConfig(List surfaces) {
    List configs = new ArrayList<>(surfaces.size());
    for (Surface surface : surfaces) {
    if (surface == null) {
    continue;
    }
    //SDK32,config.enableSurfaceSharing()开启分享后addSurface()添加新的输出面会有问题,所以就没以一个Config去添加所有Surface
    OutputConfiguration config = new OutputConfiguration(surface);
    //就只创建,啥也不做,有要求可以自己加
    configs.add(config);
    }
    return configs;
    }

四、获取编码器的编码数据

  1. 这部分网上挺多的,也不难.

    //这个是保存录像数据的
    FileOutputStream outputStream;
    //每个关键帧的数据前面都需要添加spsPps的数据,不然无法解码播放
    byte[] spsPps = null;

    boolean codecing = true;
    //编码输出信息的对象,赋值由MediaCodec做
    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();

         //当前可用的数据位置
    

    int outputBufferIndex;
    byte[] h264 = new byte[mCameraAdapter.getSize().getWidth() * mCameraAdapter.getSize().getHeight()];
    //死循环获取,也可以对MediaCodec设置callback获取
    while (codecing) {
    //获取未来一段时间可用的输出缓冲区,如果存在可用,返回值大于等于0,bufferInfo也会被赋值,最大等待时间是100000微秒,其实就是100毫秒
    outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 100000);
    Log.i(TAG, “dequeue output buffer outputBufferIndex=” + outputBufferIndex);

     //如果当前输出缓冲区没有可用的,返回负值,不同值含义不一样,有需要做判定即可if (outputBufferIndex < 0) {                continue;}ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex);Log.i(TAG, "Streaming   bufferInfo,flags: " + bufferInfo.flags+ ", size: " + bufferInfo.size+ ", presentationTimeUs: " + bufferInfo.presentationTimeUs+ ", offset: " + bufferInfo.offset);//调整数据位置,从offset开始。这样我们一会儿读取就不用传offset偏差值了。 outputBuffer.position(bufferInfo.offset);//改完位置,那肯定要改极限位置吧,不然你数据不就少了数据末尾长度为offset的这一小部分?这两步不做也可以,get的时候传offset也一样outputBuffer.limit(bufferInfo.offset + bufferInfo.size);//现在的spsPps还是空的,这是不能做写入文件,保存之类操作的if (spsPps == null) {//创建写入的文件,保存录像try {File file = new File(mContext.getDataDir().getAbsolutePath() + "camera.h264");boolean newFile = file.createNewFile();outputStream = new FileOutputStream(file, true);} catch (Exception ignored) {}// sps pps获取并保存,还有其他方式可以获取,不过不一定有效,最好检验一下ByteBuffer sps = mMediaCodec.getOutputFormat().getByteBuffer("csd-0");ByteBuffer pps = mMediaCodec.getOutputFormat().getByteBuffer("csd-1");byte[] spsBites = new byte[sps.remaining()];byte[] ppsBites = new byte[pps.remaining()];sps.get(spsBites);pps.get(ppsBites);spsPps = new byte[spsBites.length + ppsBites.length];System.arraycopy(spsBites, 0, spsPps, 0, spsBites.length);System.arraycopy(ppsBites, 0, spsPps, spsBites.length, ppsBites.length);} else {//现在已经有spsPps数据了,可以开始了try {if (bufferInfo.size > h264.length) {h264 = new byte[bufferInfo.size];}h264 = new byte[bufferInfo.size];//获取编码数据outputBuffer.get(h264, 0, bufferInfo.size);//数据校验一下,如果是关键帧需要在头部加入spsPps,校验完的trans,就是编码好的h264编码数据了byte[] trans = trans(h264, 0, bufferInfo.size, bufferInfo.flags);//保存录像数据到文件中.暂定2G大小if (outputStream.getChannel().size() < (2L << 30)) {outputStream.write(trans);}//结束标志到达if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {pusher.stop();isStreaming = false;}} catch (Exception e) {e.printStackTrace();} finally {//释放这个缓冲位置的数据,不释放就一直在,MediaCodec数据满了可不行mediaCodec.releaseOutputBuffer(outputBufferIndex, false);}}
    

    }

说明:
(1) 获取编码的 index,大于等于零才是有效的,不然不能用。

(2) 保存h264编码数据之前,需要先获取spsPps,不然关键帧之前没有spsPps,解码播放端也不加的话就没法看了。

(3) 获取数据注意offset,真正有效的数据是 bytes[offset] 到 bytes[offset+size]这个位置,至于你是get()的时候传offset拿,还是possition()limit()后传0拿,就看你心情了。

(4) 这个index的输出缓冲区用完之后,记得释放
(5) 这个录像保存只是做个样子,如果真是录像,建议用 MediaMuxer 来做,直接传判定 index 后 传 buffinfo 就行,简单方便。

  1. 这是校验是不是关键帧的,很简单明了

    private byte[] trans(byte[] h264, int offset, int length, int flags) {
    //关键帧判定
    if ((flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
    // 每个关键帧前面需要添加spsPps
    byte[] data = new byte[spsPps.length + length];
    System.arraycopy(spsPps, 0, data, 0, spsPps.length);
    System.arraycopy(h264, offset, data, spsPps.length, length);
    length = data.length;

         return data;} else {return h264;}
    

    }

五、播放保存的 h264 文件.

连上设备,我这边设置的文件位置是 Context.getDataDir().getAbsolutePath()/camera.h264,真实目录是 /data/data/包名/camera.h264,保存出来用vlc,或者ffmpeg命令ffplay播放就行了,亲测:windows自带的播放器无法解码(笑)。

相关文章:

camera2对摄像头编码h264

MediaCodec编码摄像头数据 前置&#xff1a;保存的一些成员变量 // 摄像头开启的 handler private Handler cameraHandler; // Camera session 会话 handler private Handler sessionHandler; //这里是个Context都行 private AppCompatActivity mActivity; // 这个摄像头所有需…...

Apache solr XXE 漏洞(CVE-2017-12629)

任务一&#xff1a; 复现环境中的漏洞 任务二&#xff1a; 利用XXE漏洞发送HTTP请求&#xff0c;在VPS服务器端接受请求&#xff0c;或收到DNS记录 任务三&#xff1a; 利用XXE漏洞读取本地的/etc/passwd文件 1.搭建环境 2.开始看wp的时候没有看懂为什么是core&#xff0c;然…...

​HTML代码混淆技术:原理、应用和实现方法详解

HTML代码混淆是一种常用的反爬虫技术&#xff0c;它可以有效地防止爬虫对网站数据的抓取。本文将详细介绍HTML代码混淆技术的原理、应用以及实现方法&#xff0c;帮助大家更好地了解和运用这一技术。 一、HTML代码混淆的原理 HTML代码混淆是指将HTML源码通过特定的算法进行加…...

quickapp_快应用_系统接口应用

系统接口 在项目中使用到的接口都需要在配置文件manifest.json中声明&#xff0c;不然会报如下警告 [WARN] 请在 manifest.json 文件里声明项目代码中用到的接口: system.storage, service.account, system.package, system.webview[1]检查某app是否在手机上安装 官方文档&a…...

sqlmap400报错问题解决

python sqlmap.py -r sql.txt --batch --techniqueB --tamperspace2comment --risk 3 --force-ssl–batch 选项全部默认 不用再手动输入 –techniqueB 使用布尔盲注&#xff0c;该参数是指出要求使用的注入方式 –tamperspace2comment使用特殊脚本&#xff0c;space2comment是把…...

【S32DS报错】-2-提示Error while launching command:arm-none-eabi-gdb –version错误

目录 1 Error错误提示 2 Error错误原因 3 如何消除Error错误 结尾 【S32K3_MCAL从入门到精通】合集&#xff1a; S32K3_MCAL从入门到精通https://blog.csdn.net/qfmzhu/category_12519033.html 1 Error错误提示 使用S32DSJ-LinK下载程序&#xff0c;在Dedug Configurati…...

Windows核心编程 HOOK

目录 HOOK概述 HOOK API SetWindowsHookExA 函数(winuser.h) UnhookWindowsHookEx 函数(winuser.h) NextHookEx 函数(winuser.h) 局部钩子 全局钩子 为什么全局钩子需要用dll作为过程函数&#xff1f; HOOK概述 本质&#xff1a;Windows消系统的消息过滤器。 全局钩子…...

P4 Qt如何添加qss样式表文件和添加图片资源

目录 前言 01 添加图片资源文件 02 添加qss文件 前言 &#x1f3ac; 个人主页&#xff1a;ChenPi &#x1f43b;推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ &#x1f525; 推荐专栏2: 《Qt基础_ChenPi的博客-CSDN博客》✨✨✨ &#x1f33a;本篇简介 &#xff1a;这一章…...

【华为OD题库-085】路灯照明II-Java

题目 在一条笔直的公路上安装了N个路灯&#xff0c;从位置0开始安装&#xff0c;路灯之间间距固定为100米。 每个路灯都有自己的照明半径&#xff0c;请计算第一个路灯和最后一个路灯之间&#xff0c;无法照明的区间的长度和。输入描述 第一行为一个数N&#xff0c;表示路灯个数…...

附录1、vuepress中的Markdown语法

# 一、标题 # 说明&#xff1a; #后面跟的内容就是标题&#xff0c;一个#就是一级标题&#xff0c;有几个#就是几级标题&#xff0c;例如2级标题就有两个##&#xff0c;markdown的2级和3级标题会默认自动作为子目录&#xff0c; 注意&#xff1a;#后面必须有个空格&#xff0…...

【matlab程序】matlab画螺旋图|旋转图

%% 数学之美====》螺旋线 % 海洋与大气科学 % 20231205 clear;clc;close all; n=10; t=0:0.01:2pin; R=1; xx=nan(length(t),1);yy=nan(length(t),1); for i=1:length(t) xx(i)=Rcos(t(i)); yy(i)=Rsin(t(i)); R=R+1; end figure set(gcf,‘position’,[50 50 1200 1200],‘col…...

计算三位数每位上数字的和

分数 10 作者 python课程组 单位 福州大学至诚学院 补充程序实现计算&#xff1a; 输入一个三位的整数&#xff08;不接受实数&#xff09;&#xff0c;求这个三位数每一位上数字的和是多少&#xff1f;例如&#xff1a;输入&#xff1a;382&#xff0c;输出&#xff1a;和为…...

Gavin Wood:财库保守主义偏离了初心,应探索 Fellowship 等更有效的资金部署机制

波卡创始人 Gavin Wood 博士最近接受了 The Kusamarian 的采访&#xff0c;分享了他的过往经历、对治理的看法&#xff0c;还聊到了 AI、以太坊、女巫攻击、财库等话题。本文整理自 PolkaWorld 对专访编译的部分内容&#xff0c;主要包含了 Gavin 对治理、财库提案、生态资金分…...

Linux: sudo: unable to execute /opt/sbin/adm: No such file or directory

因为脚本语言第一行是指定解释器,但是里面包含非法^M字符,导致后续的系统调用,找不到解释器,然后报错误找不到文件。 所以这里存在一个问题,就是错误提示里虽然显示文件找不到,而且也把文件打印了出来。但是非法的字符却没有打印出来。所以导致让人迷惑的可能。 sudo: un…...

一文详解Java单元测试Junit

文章目录 概述、Junit框架快速入门单元测试概述main方法测试的问题junit单元测试框架优点&#xff1a;使用步骤&#xff1a; 使用案例包结构 Junit框架的常见注解测试 概述、Junit框架快速入门 单元测试概述 就是针对最小的功能单元&#xff08;方法&#xff09;&#xff0c;…...

进制 + 原码,反码,补码

进制转换 整数部分 小数部分 原码 反码 补码 原码转补码&#xff1a; 左边和右边第一个1不变&#xff0c;中间取反。-0 除外。 计算机系统中数值一律用补码来存储的原因 其他 术语 进制表 进制数的表示 详细教程可转 爱编程的大丙...

2024年网络安全行业前景和技术自学

很多人不知道网络安全发展前景好吗&#xff1f;学习网络安全能做什么&#xff1f;今天为大家解答下 先说结论&#xff0c;网络安全的前景必然是超级好的 作为一个有丰富Web安全攻防、渗透领域老工程师&#xff0c;之前也写了不少网络安全技术相关的文章&#xff0c;不少读者朋…...

cocos 关于多个摄像机,动态添加节点的显示问题,需要动态修改layer。(跟随摄像机滚动)(神坑官网也不说明一下)

参考文章&#xff1a;Cocos 3.x 层级Layer - 简书 2D镜头跟随应该怎么实现呢 - Creator 3.x - Cocos中文社区 关于多个摄像机&#xff0c;动态添加节点的显示问题&#xff0c;需要动态修改layer&#xff1f; 场景&#xff1a;在制作摄像机跟随角色移动功能时&#xff0c;新增…...

freeswitch编译mod_av支持webrtc MCU通话

系统环境 一、FS相关网站 二、第三方库安装 1.apt安装 2.指定版本sofia-sip安装 3.指定版本spandsp安装 4.指定版本libks安装 5.指定版本openssl安装 三、指定版本FS安装 1.CPPFLAGS配置 2.编译器版本 3.FS配置编译 四、FS&#xff0c;fs_cli运行&#xff0c;模块加载 附录 1.安…...

K8s 入门指南(一):单节点集群环境搭建

前言 官方文档&#xff1a;Kubernetes 文档 | Kubernetes 系统配置 CentOS 7.9&#xff08;2 核 2 G&#xff09; 本文为 k8s 入门指南专栏&#xff0c;将会使用 kubeadm 搭建单节点 k8s 集群&#xff0c;详细讲解环境搭建部署的细节&#xff0c;专栏后面章节会以实战代码介绍…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...