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

Android程序中使用FFmpeg库

目录

前言

一、环境

二、创建APP

三. 添加FFmpeg库文件到app中

1. 复制ffmpeg头文件和so库到app中

2. 修改CMakeLists.txt文件内容.

3. 修改ffmpeglib.cpp 文件内容

4. 修改NativeLib.kt 文件添加方法和加载库

5. 调用

四. 增加解析视频文件信息功能

总结


前言

        前面有一篇记录了windows上👉 编译Android平台使用的FFmpeg库。想知道的同学可以去看一下😄。这一篇记录一下在android app上怎么使用这些库。


一、环境

  1. 安装Android studio, 方法就不介绍了,网上太多安装的方法了。
  2. 安装NDK和cmake,直接使用SDK monitor安装。 看过我的编译ffmpeg库的知道用的ndk 版本是:
    25.1.8937393

cmake版本:3.22.1

二、创建APP

android studio 创建module

创建成功后,目录结构跟下面差不多,只是没有assets和cpp这两个文件夹和NatvieLib文件。后面会说这两个文件夹和文件是干嘛的

 

三. 添加FFmpeg库文件到app中

1. 复制ffmpeg头文件和so库到app中

使用过NDK项目的都知道cpp这个是放CMakeLists.txt和所有的cpp文件的。

cpp这个文件夹下面创建一个ffmpeg文件夹用来放ffmpeg的头文件和so库文件。因为只编译了一个arm64-v8a 架构,所在在lib这个文件夹下面创建一个arm64-v8a用于放so库。目录结构如下图:

2. 修改CMakeLists.txt文件内容.

修改CMakeLists.txt文件内容编译ffmpeglib.cpp文件。

CMakeLists.txt文件内容如下,都添加注释了,不多说了。有不太清楚的可以自己创建一个Native library module,比对一下看看

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html.
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.# Sets the minimum CMake version required for this project.
cmake_minimum_required(VERSION 3.22.1)# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
# Since this is the top level CMakeLists.txt, the project name is also accessible
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
# build script scope).
project("ffmpeglib")#设定ffmpeg的头文件和so库文件到一个变量中
set(FFMPEG_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/../cpp/ffmpeg/include)
set(FFMPEG_LIB_DIR ${CMAKE_SOURCE_DIR}/../cpp/ffmpeg/lib/${ANDROID_ABI})# 输出调试信息,用于查看路径是否正确
message(STATUS "FFMPEG_INCLUDE_DIR: ${FFMPEG_INCLUDE_DIR}")
message(STATUS "FFMPEG_LIB_DIR: ${FFMPEG_LIB_DIR}")# 检查库文件是否存在
file(GLOB FFMPEG_LIB_FILES "${FFMPEG_LIB_DIR}/*.so")
if(NOT FFMPEG_LIB_FILES)message(FATAL_ERROR "No FFmpeg library files found in ${FFMPEG_LIB_DIR}. Please check the paths and ensure the libraries exist.")
endif()# 包含FFmpeg头文件,只有包含头文件后,在cpp中才能正确引用头文件
include_directories(${FFMPEG_INCLUDE_DIR})# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
# is preferred for the same purpose.
#
# In order to load a library into your app from Java/Kotlin, you must call
# System.loadLibrary() and pass the name of the library defined here;
# for GameActivity/NativeActivity derived applications, the same library name must be
# used in the AndroidManifest.xml file.
add_library(${CMAKE_PROJECT_NAME} SHARED# List C/C++ source files with relative paths to this CMakeLists.txt.ffmpeglib.cpp)# 显式指定库文件路径
set(avformat_LIBRARY ${FFMPEG_LIB_DIR}/libavformat.so)
set(avcodec_LIBRARY ${FFMPEG_LIB_DIR}/libavcodec.so)
set(avutil_LIBRARY ${FFMPEG_LIB_DIR}/libavutil.so)
set(swresample_LIBRARY ${FFMPEG_LIB_DIR}/libswresample.so)
set(swscale_LIBRARY ${FFMPEG_LIB_DIR}/libswscale.so)
set(avdevice_LIBRARY ${FFMPEG_LIB_DIR}/libavdevice.so)
set(avfilter_LIBRARY ${FFMPEG_LIB_DIR}/libavfilter.so)# 检测so库文件,输出找到的库文件路径, c++引用so库是不用带lib前缀和.so扩展名的
foreach (LIB avformat avcodec avutil swresample swscale avdevice avfilter)if(EXISTS ${${LIB}_LIBRARY})message(STATUS "${LIB}_LIBRARY: ${${LIB}_LIBRARY}")else()message(FATAL_ERROR "${LIB}_LIBRARY not found at ${${LIB}_LIBRARY}. Please check the paths and ensure the libraries exist.")endif()
endforeach()#链接库文件
target_link_libraries(${CMAKE_PROJECT_NAME}${avutil_LIBRARY}${swresample_LIBRARY}${swscale_LIBRARY}${avcodec_LIBRARY}${avdevice_LIBRARY}${avfilter_LIBRARY}${avformat_LIBRARY}# List libraries link to the target libraryc++_sharedandroidlog)

3. 修改ffmpeglib.cpp 文件内容

添加一个initFfmpeg 方法用来初始化ffmpeg

extern "C" JNIEXPORT void JNICALL
Java_com_bob_ffmpegdemo_NativeLib_initFfmpeg(JNIEnv *env, jobject /* this */) {// 初始化 FFmpegavformat_network_init();// 打印 FFmpeg 版本信息到日志const char *version = avformat_configuration();LOGD("FFmpeg version: %s", version);
}

4. 修改NativeLib.kt 文件添加方法和加载库

package com.bob.ffmpegdemoclass NativeLib {/*** A native method that is implemented by the 'ffmpglib' native library,* which is packaged with this application.*/external fun stringFromJNI(): Stringexternal fun initFfmpeg()companion object {// Used to load the 'ffmpeglib' library on application startup.init {System.loadLibrary("ffmpeglib")}}
}

5. 调用

可以在Activity文件中直接调用NativeLab这个类中和方法

    override fun onResume() {super.onResume()testFfmpeg()}private fun testFfmpeg() {val nativeLib = NativeLib()Log.d(TAG, "-------- ${nativeLib.stringFromJNI()}")nativeLib.initFfmpeg()}

直接运行app, 成功会输出下面的内容

四. 增加解析视频文件信息功能

通过前面三节内容后,ffmpeg的库就添加到app中了,但是只是输出了ffmpeg 编译的信息。不知道ffmpeg的功能是否能用。这节增加解析视频文件的功能

  •  直接在ffmpeglib.cpp文件中添加testOpenVideo方法解析视频
extern "C" JNIEXPORT jstring JNICALL
Java_com_bob_ffmpegdemo_NativeLib_testOpenVideo(JNIEnv *env, jobject /* this */, jstring filePath) {const char *path = env->GetStringUTFChars(filePath, NULL);// 添加 'file://' 前缀以确保正确解析路径std::string full_path = "file://" + std::string(path);LOGD("Attempting to open video file: %s", full_path.c_str());AVFormatContext *pFormatCtx = nullptr;int ret = avformat_open_input(&pFormatCtx, full_path.c_str(), NULL, NULL);if (ret < 0) {char errbuf[AV_ERROR_MAX_STRING_SIZE];av_strerror(ret, errbuf, sizeof(errbuf));LOGE("Failed to open video file: %s", errbuf);env->ReleaseStringUTFChars(filePath, path);return env->NewStringUTF("Failed to open video file.");}if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {LOGE("Failed to retrieve stream information.");avformat_close_input(&pFormatCtx);env->ReleaseStringUTFChars(filePath, path);return env->NewStringUTF("Failed to retrieve stream information.");}// 使用正确的函数名 av_dump_formatav_dump_format(pFormatCtx, 0, full_path.c_str(), 0); // 打印格式信息到标准输出// 计算持续时间和比特率int64_t duration = pFormatCtx->duration != AV_NOPTS_VALUE ?av_rescale_q(pFormatCtx->duration, AV_TIME_BASE_Q, {1, 1000}) : -1; // 转换为毫秒int64_t bitrate = pFormatCtx->bit_rate / 1000;// 解析流信息并处理无音频流的情况bool hasAudioStream = false;for (unsigned int i = 0; i < pFormatCtx->nb_streams; ++i) {AVStream *stream = pFormatCtx->streams[i];AVCodecParameters *codecpar = stream->codecpar;if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {LOGD("Video Stream: Codec %s, Resolution %dx%d",avcodec_get_name(codecpar->codec_id),codecpar->width, codecpar->height);} else if (codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {hasAudioStream = true;// 如果 channel_layout 存在,则使用它;否则提供一个默认值int channels = 2; // 默认立体声音频#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(58, 9, 100)if (codecpar->channel_layout) {channels = av_get_channel_layout_nb_channels(codecpar->channel_layout);}#endifLOGD("Audio Stream: Codec %s, Sample Rate %d Hz, Channels %d",avcodec_get_name(codecpar->codec_id),codecpar->sample_rate,channels);}}if (!hasAudioStream) {LOGD("No audio streams found in the video file.");}char info[1024];snprintf(info, sizeof(info), "Duration: %lld ms, Bitrate: %lld kbps",static_cast<long long>(duration),static_cast<long long>(bitrate));avformat_close_input(&pFormatCtx);env->ReleaseStringUTFChars(filePath, path);return env->NewStringUTF(info);
}

因为使用的是我自己录制的mp4文件,没有声音的。所以添加了hasAudioStream的判断

  •  修改NativeLib.kt 文件

增加: 

external fun testOpenVideo(path:String): String
  •  方法调用
        val outputDir = getExternalFilesDir(null)val outputFile = File(outputDir, FILENAME)if (!outputFile.exists()) {Log.e(TAG, "File does not exist at path: ${outputFile.absolutePath}")return} else if (!outputFile.canRead()) {Log.e(TAG, "File is not readable at path: ${outputFile.absolutePath}")return}val result = nativeLib.testOpenVideo(outputFile.absolutePath)Log.d(TAG, "-------- $result")

运行成功后

 


上面贴的CMakeLists.txt的内容已经是完整的。下面贴一下ffmpeglib.cpp, NativeLib.kt 和 MainActivity.kt 完整代码。

ffmpeglib.cpp

#include <jni.h>
#include <string>
#include <android/log.h>extern "C" {
#include "ffmpeg/include/libavformat/avformat.h"
}#define LOG_TAG "NativeLib"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)extern "C" JNIEXPORT jstring JNICALL
Java_com_bob_ffmpegdemo_NativeLib_stringFromJNI(JNIEnv *env,jobject /* this */) {std::string hello = "Hello from FFmpeg";return env->NewStringUTF(hello.c_str());
}extern "C" JNIEXPORT void JNICALL
Java_com_bob_ffmpegdemo_NativeLib_initFfmpeg(JNIEnv *env, jobject /* this */) {// 初始化 FFmpegavformat_network_init();// 打印 FFmpeg 版本信息到日志const char *version = avformat_configuration();LOGD("FFmpeg version: %s", version);
}extern "C" JNIEXPORT jstring JNICALL
Java_com_bob_ffmpegdemo_NativeLib_testOpenVideo(JNIEnv *env, jobject /* this */, jstring filePath) {const char *path = env->GetStringUTFChars(filePath, NULL);// 添加 'file://' 前缀以确保正确解析路径std::string full_path = "file://" + std::string(path);LOGD("Attempting to open video file: %s", full_path.c_str());AVFormatContext *pFormatCtx = nullptr;int ret = avformat_open_input(&pFormatCtx, full_path.c_str(), NULL, NULL);if (ret < 0) {char errbuf[AV_ERROR_MAX_STRING_SIZE];av_strerror(ret, errbuf, sizeof(errbuf));LOGE("Failed to open video file: %s", errbuf);env->ReleaseStringUTFChars(filePath, path);return env->NewStringUTF("Failed to open video file.");}if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {LOGE("Failed to retrieve stream information.");avformat_close_input(&pFormatCtx);env->ReleaseStringUTFChars(filePath, path);return env->NewStringUTF("Failed to retrieve stream information.");}// 使用正确的函数名 av_dump_formatav_dump_format(pFormatCtx, 0, full_path.c_str(), 0); // 打印格式信息到标准输出// 计算持续时间和比特率int64_t duration = pFormatCtx->duration != AV_NOPTS_VALUE ?av_rescale_q(pFormatCtx->duration, AV_TIME_BASE_Q, {1, 1000}) : -1; // 转换为毫秒int64_t bitrate = pFormatCtx->bit_rate / 1000;// 解析流信息并处理无音频流的情况bool hasAudioStream = false;for (unsigned int i = 0; i < pFormatCtx->nb_streams; ++i) {AVStream *stream = pFormatCtx->streams[i];AVCodecParameters *codecpar = stream->codecpar;if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {LOGD("Video Stream: Codec %s, Resolution %dx%d",avcodec_get_name(codecpar->codec_id),codecpar->width, codecpar->height);} else if (codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {hasAudioStream = true;// 如果 channel_layout 存在,则使用它;否则提供一个默认值int channels = 2; // 默认立体声音频#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(58, 9, 100)if (codecpar->channel_layout) {channels = av_get_channel_layout_nb_channels(codecpar->channel_layout);}#endifLOGD("Audio Stream: Codec %s, Sample Rate %d Hz, Channels %d",avcodec_get_name(codecpar->codec_id),codecpar->sample_rate,channels);}}if (!hasAudioStream) {LOGD("No audio streams found in the video file.");}char info[1024];snprintf(info, sizeof(info), "Duration: %lld ms, Bitrate: %lld kbps",static_cast<long long>(duration),static_cast<long long>(bitrate));avformat_close_input(&pFormatCtx);env->ReleaseStringUTFChars(filePath, path);return env->NewStringUTF(info);
}

NativeLib.kt

package com.bob.ffmpegdemoclass NativeLib {/*** A native method that is implemented by the 'ffmpglib' native library,* which is packaged with this application.*/external fun stringFromJNI(): Stringexternal fun initFfmpeg()external fun testOpenVideo(path:String): Stringcompanion object {// Used to load the 'ffmpeglib' library on application startup.init {System.loadLibrary("ffmpeglib")}}
}

 MainActivity.kt

package com.bob.ffmpegdemoimport android.os.Bundle
import android.util.Log
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.bob.ffmpegdemo.databinding.ActivityMainBinding
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStreamclass MainActivity : AppCompatActivity() {companion object {const val FILENAME = "abc.mp4"const val TAG = "TAG"}private val binding: ActivityMainBinding by lazy {ActivityMainBinding.inflate(layoutInflater)}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)enableEdgeToEdge()setContentView(binding.root)ViewCompat.setOnApplyWindowInsetsListener(binding.main) { v, insets ->val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)insets}copyAssetToFile()}private fun copyAssetToFile() {val dir = getExternalFilesDir(null)// Step 1: Create a file object in that directoryval outFile = File(dir, FILENAME)// Step 2: Copy the asset file to the external files directorytry {val `in`: InputStream = assets.open(FILENAME)val out: OutputStream = FileOutputStream(outFile)val buffer = ByteArray(1024)var read: Intwhile ((`in`.read(buffer).also { read = it }) != -1) {out.write(buffer, 0, read)}`in`.close()out.flush()out.close()Log.d(TAG, "Successfully copied $FILENAME to external files directory.")} catch (e: IOException) {Log.e(TAG, "Failed to copy asset file: " + e.message)}}override fun onResume() {super.onResume()testFfmpeg()}private fun testFfmpeg() {val nativeLib = NativeLib()Log.d(TAG, "-------- ${nativeLib.stringFromJNI()}")nativeLib.initFfmpeg()val outputDir = getExternalFilesDir(null)val outputFile = File(outputDir, FILENAME)if (!outputFile.exists()) {Log.e(TAG, "File does not exist at path: ${outputFile.absolutePath}")return} else if (!outputFile.canRead()) {Log.e(TAG, "File is not readable at path: ${outputFile.absolutePath}")return}val result = nativeLib.testOpenVideo(outputFile.absolutePath)Log.d(TAG, "-------- $result")}
}

总结

以上就是今天要讲的内容。

相关文章:

Android程序中使用FFmpeg库

目录 前言 一、环境 二、创建APP 三. 添加FFmpeg库文件到app中 1. 复制ffmpeg头文件和so库到app中 2. 修改CMakeLists.txt文件内容. 3. 修改ffmpeglib.cpp 文件内容 4. 修改NativeLib.kt 文件添加方法和加载库 5. 调用 四. 增加解析视频文件信息功能 总结 前言 前面…...

到华为考场考HCIE的注意事项和考试流程

大家好&#xff0c;我是张同学&#xff0c;来自成都职业技术学院2021级计算机网络专业。最近成功通过了 Datacom HCIE 考试&#xff0c;在这里和大家分享一下我的经验。 考证契机 在母校的培养下&#xff0c;我接触到ICT这个行业&#xff0c;打好了基础&#xff0c;开始了成…...

基于STM32的智能书架管理系统设计

目录 引言系统设计 硬件设计软件设计 系统功能模块 图书分类与存储模块环境监测与保护模块数据显示与用户交互模块远程管理与书籍推荐模块 控制算法 图书分类与存储管理算法环境监测与保护算法数据记录与推荐算法 代码实现 图书分类与存储代码环境监测与保护代码数据显示与远程…...

STL--list(双向链表)

目录 一、list 对象创建 1、默认构造函数 2、初始化列表 3、迭代器 4、全0初始化 5、全值初始化 6、拷贝构造函数 二、list 赋值操作 1、赋值 2、assign&#xff08;迭代器1&#xff0c;迭代器2&#xff09; 3、assign&#xff08;初始化列表&#xff09; 4、assig…...

构建高效稳定的网络环境

概述 网络技术是当今IT行业的重要组成部分&#xff0c;构建高效稳定的网络环境对于企业、个人和互联网发展至关重要。本文将探讨网络技术中的关键要素&#xff0c;包括网络协议、网络架构、网络安全和网络优化&#xff0c;并提供实用的技巧和最佳实践&#xff0c;以帮助您构建…...

2025美赛倒计时,数学建模五类模型40+常用算法及算法手册汇总

数学建模美赛倒计时&#xff0c;对于第一次参加竞赛且没有相关基础知识的同学来讲&#xff0c;掌握数学建模常用经典的模型算法知识&#xff0c;并熟练使用相关软件进行建模是关键。本文将介绍一些常用的模型算法&#xff0c;以及软件操作教程。 数学建模常用模型包括&#xf…...

ElasticSearch DSL查询之排序和分页

一、排序功能 1. 默认排序 在 Elasticsearch 中&#xff0c;默认情况下&#xff0c;查询结果是根据 相关度 评分&#xff08;score&#xff09;进行排序的。我们之前已经了解过&#xff0c;相关度评分是通过 Elasticsearch 根据查询条件与文档内容的匹配程度自动计算得出的。…...

C语言--数据在内存中的存储

数据在内存中的存储 主要研究整型和浮点型在内存中的存储。 1. 整数在内存中的存储 在学习操作符的时候&#xff0c;就了解过了下面的内容&#xff1a; 整数的2进制表示方法有三种&#xff0c;即原码、反码和补码。 有符号的整数&#xff0c;三种表示方法均有符号位和数值…...

qml ScrollView详解

1、概述 QML中的ScrollView是一个容器组件&#xff0c;它允许用户滚动查看其内容&#xff0c;当内容超出视口大小时特别有用。ScrollView提供了垂直和水平滚动条&#xff08;或触摸滚动&#xff09;&#xff0c;使用户能够访问被视口裁剪的内容部分。它常用于显示大量数据或复…...

通过frm和ibd文件恢复mysql数据

1.提取所有的文件名并查找出以frm结尾的 dir /t /b >1.txt 2.要准备的软件 1.mysql-utilities-1.6.5-winx64.msi 2.vcredist_x64.exe(c 2013) 3.利用frm生成sql文件 mysqlfrm --serverroot:123456localhost:3306 --port3308 D:\phpstudy_pro\Extensions\MySQL5.7.26\da…...

观察者模式 - 观察者模式的应用场景

引言 观察者模式&#xff08;Observer Pattern&#xff09;是设计模式中行为型模式的一种&#xff0c;它定义了对象之间的一对多依赖关系&#xff0c;使得当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都会自动收到通知并更新。观察者模式广泛应用于事件处理系统…...

【Mac】Python相关知识经验

一、给Python3安装第三方库 mac下给Python3安装第三方库pillow&#xff0c;处理图片 【安装方式】&#xff1a; 终端中输入命令&#xff1a;python3 -m pip install pillow 按回车&#xff0c;等待pillow下载安装 NOTE: 其他模块同理&#xff0c;如pytesseract 二、Python版…...

使用 JMeter 的 Autostop Listener 插件:自动化性能测试的守护者

在性能测试中&#xff0c;监控测试执行的状态并及时做出响应是至关重要的。如果测试过程中出现性能瓶颈或系统崩溃&#xff0c;继续运行测试可能会导致资源浪费或测试结果不准确。JMeter 的 Autostop Listener 插件正是为了解决这一问题而设计的。它允许你设置自动化停止条件&a…...

C# lock使用的逻辑和情景

情景&#xff1a;扣库存&#xff0c;会出现超扣的情况&#xff0c;因为同一个单子会有不同的工作人员使用&#xff0c;要保证数据的一致性。那么就用锁。 优化锁对象管理 使用 Lazy 初始化锁对象&#xff1a; 使用 ConcurrentDictionary 的 GetOrAdd 方法结合 Lazy 确保锁对象只…...

React 中hooks之 React useCallback使用方法总结

1. useCallback 基础概念 useCallback 是 React 的一个 Hook&#xff0c;用于记忆函数定义&#xff0c;避免在每次渲染时创建新的函数实例。它在需要将回调函数传递给经过优化的子组件时特别有用。 当state变化的时候引起组件重新渲染执行会导致某个方法被反复创建增加内存负担…...

期刊论文左下角添加通讯作者和横线的方法

一、添加脚注 二、写脚注内容 三、修改脚注分隔符&#xff08;添加横线&#xff09; 大概插入十个此符号&#xff0c;长度可微调。...

Docker使用 使用Dockerfile来创建镜像

本篇文章主要介绍了Docker使用Dockerfile来创建镜像&#xff0c; 本文学习Dcokerfile的基本命令,并且创建一个支持ssh服务的镜像. 1.Dockerfile 1.1基本案例 基本案例 dockerfile可以说是docker的描述符,该文件定义了docker镜像的所能拥有哪些东西.基本格式如下: 第一行指定…...

手写SOCKET进行HTTP通信

网络基础 我们电脑主板上都内置了多种网卡&#xff0c;一般主要有以下几类&#xff1a; 虚拟网卡&#xff08;loopback&#xff09; 注意&#xff0c;它是虚拟的&#xff0c;并不是物理网卡&#xff0c;也被称为是本地环回地址(或接口)&#xff0c;一般将127.0.0.1作为本地环回…...

深入理解 Java 的并发容器

目录 一、为何需要并发容器 二、Java 中的主要并发容器 1. ConcurrentHashMap 2. CopyOnWriteArrayList 3. ConcurrentLinkedQueue 4. BlockingQueue及其实现类 三、并发容器的应用场景 1. 缓存系统 2. 任务队列 3. 数据共享与传递 四、使用并发容器的注意事项 1. …...

四、CSS效果

一、box-shadow box-shadow:在元素的框架上添加阴影效果 /* x 偏移量 | y 偏移量 | 阴影颜色 */ box-shadow: 60px -16px teal; /* x 偏移量 | y 偏移量 | 阴影模糊半径 | 阴影颜色 */ box-shadow: 10px 5px 5px black; /* x 偏移量 | y 偏移量 | 阴影模糊半径 | 阴影扩散半…...

每日OJ_牛客_DP44兑换零钱_C++_Java

目录 牛客_DP44兑换零钱 题目解析 C代码 Java代码 牛客_DP44兑换零钱 兑换零钱_牛客题霸_牛客网 描述&#xff1a; 给定数组arr&#xff0c;arr中所有的值都为正整数且不重复。每个值代表一种面值的货币&#xff0c;每种面值的货币可以使用任意张&#xff0c;再给定一个a…...

Linux——入门基本指令汇总

目录 1. ls指令2. pwd3. whoami指令4. cd指令5. clear指令6. touch指令7. mkdir指令8. rm指令9. man指令10. cp指令11. mv指令12. cat指令13. tac指令14. more指令15. less指令16. head指令17. tail指令18. date指令19. cal指令20. find指令21. which指令22. alias指令23. grep…...

VOSK实现【离线中文语音】识别

Vosk是一款开源的离线语音识别工具包&#xff0c;具有以下功能&#xff1a; 多语言支持&#xff1a;能够对20多种语言和方言进行语音识别&#xff0c;如中文、英语、德语、法语、西班牙语等&#xff0c;可满足不同用户的语言需求。 模型轻量化&#xff1a;每种语言的模型大小仅…...

Qt 控件与布局管理

1. Qt 控件的父子继承关系 在 Qt 中&#xff0c;继承自 QWidget 的类&#xff0c;通常会在构造函数中接收一个 parent 参数。 这个参数用于指定当前空间的父控件&#xff0c;从而建立控件间的父子关系。 当一个控件被设置为另一控件的子控件时&#xff0c;它会自动成为该父控…...

Checkbutton组件

在这个案例中,我们将添加三个复选框(Checkbutton)组件,每个组件都有不同的初始状态。 准备就绪 本文案例是在上一篇《Combobox组件》上的扩展,你可以从这里下载其中的代码。 如何操作 我们正在创建三个状态不同的复选框组件。 第一个组件是禁用状态,并且其中有一个勾…...

Markdown Viewer 浏览器, vscode

使用VS Code插件打造完美的MarkDown编辑器&#xff08;插件安装、插件配置、markdown语法&#xff09;_vscode markdown-CSDN博客 右键 .md 文件&#xff0c;选择打开 方式 &#xff08;安装一些markdown的插件) vscode如何预览markdown文件 | Fromidea GitCode - 全球开发者…...

【QNX】QNX侧查看CPU的信息

目录 一 工具 ① top ▲ 使用top查看CPU信息 ▲ 输出 ② hogs ▲ 使用hogs查看CPU信息 ▲ 输出 ③ pidin ▲ 使用pidin查看CPU信息 ▲ 输出 二 对比 在QNX实时操作系统中&#xff0c;可查看CPU信息的方法有top、hogs以及pidin。 一 工具 ① top top命令不仅能够显…...

Android中关于View的几种属性赋值方式

我们以给TextView组件设置颜色属性展开讲解 1、xml中直接定义&#xff08;设定TextView为黑色&#xff09; 2、xml 中 引用style&#xff08;设定TextView为蓝色&#xff09; 3、在theme 中直接定义&#xff08;设定TextView紫色&#xff09; 4、在主题中添加对样式资源的引用…...

JavaScript网页基于tesseract.js提取图片中的文字,识别车牌,识别快递单号等

Tesseract是我们的老盆友了&#xff0c;以前写过基于JavaCV版本的Tesseract字符识别&#xff0c;现在tesseract终于可以在网页上面用了&#xff08;tesseract.js&#xff09;&#xff0c;让我们一起来试试效果。 注意&#xff1a;本章使用Vue模块化调用tesseract.js方式&#x…...

智慧金融合集:财税资金数据管理一体化大屏

随着科技的快速进步和数字化转型的加速&#xff0c;金融、税务等机构和企业面临的数据量呈现出爆炸式增长。传统的数据分析方法早已无法胜任现代业务的需求。为此&#xff0c;许多机构开始尝试创新的软件工具来更好的管理繁琐的数据。 通过图扑软件的数据可视化大屏&#xff0c…...

Android SystemUI——最近任务应用列表(十七)

对于最近任务应用列表来说,在 Android 原生 SystemUI 中是一个单独的组件。 <string-array name="config_systemUIServiceComponents" translatable="false">……<item>com.android.systemui.recents.Recents</item> </string-arra…...

工业相机 SDK 二次开发-Halcon 插件

本文介绍了 Halcon 连接相机时插件的使用。通过本套插件可连接海康 的工业相机。 一. 环境配置 1. 拷贝动态库 在 用 户 安 装 MVS 目 录 下 按 照 如 下 路 径 Development\ThirdPartyPlatformAdapter 找到目录为 HalconHDevelop 的文 件夹&#xff0c;根据 Halcon 版本找到对…...

AI Agent:数字文明的暗物质,如何悄然改变我们的世界?

AI Agent&#xff1a;数字文明的暗物质&#xff0c;如何悄然改变我们的世界&#xff1f; 引言 在人类文明的漫长黑夜中&#xff0c;人工智能&#xff08;Artificial Intelligence, AI&#xff09;如同第一缕曙光&#xff0c;正在撕裂数字与现实的边界。这是一个技术奇点临近的…...

vscode 自用插件

vscode按住ctrl鼠标左键无法跟踪跳转方法名&#xff0c;装这些插件就可以 vscode-elm-jump:常规的代码跳转定义 Vue CSS Peek:跳转css定义 vue-helper:变量函数只跳转定义 Vetur 代码提示 Baidu Comate 自动帮你写console.log Turbo Console Log: ctrl alt l 选中变量之后&am…...

Ragas-RAG能力评测

Ragas是一个框架&#xff0c;它可以帮助你从不同的方面评估你的问答&#xff08;QA&#xff09;流程。它为你提供了一些指标来评估你的问答系统的不同方面&#xff0c;具体包括&#xff1a; 评估检索&#xff08;context&#xff09;的指标&#xff1a;提供了上下文相关性&…...

【PCL】Segmentation 模块—— 条件欧几里得聚类(Conditional Euclidean Clustering)

1、简介 1.1 条件欧几里得聚类&#xff08;Conditional Euclidean Clustering&#xff09; 本文介绍了如何使用 pcl::ConditionalEuclideanClustering 类&#xff1a;这是一种基于欧几里得距离和用户自定义条件的点云聚类分割算法。 该类使用了与欧几里得聚类提取(Euclidean…...

#HarmonyOS篇:build-profile.json5里面配置productsoh-package.json5里面dependencies依赖引入

oh-package.json5 用于描述包名、版本、入口文件和依赖项等信息。 {"license": "","devDependencies": {},"author": "","name": "entry","description": "Please describe the basic…...

《探秘:人工智能如何为鸿蒙Next元宇宙网络传输与延迟问题破局》

在元宇宙的宏大愿景中&#xff0c;流畅的网络传输和低延迟是保障用户沉浸式体验的关键。鸿蒙Next结合人工智能技术&#xff0c;为解决这些问题提供了一系列创新思路和方法。 智能网络监测与预测 人工智能可以实时监测鸿蒙Next元宇宙中的网络状况&#xff0c;包括带宽、延迟、…...

java中的泛型

文章目录 java中的泛型泛型的使用1 快速入门2 泛型的介绍&#xff08;1&#xff09;使用泛型的好处&#xff08;2&#xff09;泛型的理解&#xff08;3&#xff09;泛型的语法&#xff08;4&#xff09;泛型使用的注意事项 3 自定义泛型&#xff08;1&#xff09;自定义泛型类&…...

PCF8563一款工业级、低功耗多功能时钟/日历芯片

PCF8563是PHILIPS&#xff08;现NXP&#xff09;公司生产的一款工业级、内含I2C总线接口功能的低功耗多功能时钟/日历芯片。以下是对该芯片的详细介绍&#xff1a; 一、主要特性 低功耗&#xff1a;典型值为0.25μA&#xff08;VDD3.0V&#xff0c;Tamb25℃&#xff09;。宽电…...

Servlet快速入门

Servlet 由于目前主流使用SpringBoot进行开发Servlet可以说是时代的眼泪&#xff0c;这篇文章主要介绍我基于SpringBoot对应Servlet的浅薄认知&#xff0c;有利于更好的理解前端界面和java服务器的数据交换过程 快速入门 我比较推荐这篇文章来对Servlet有一个大概的了解 都2…...

C语言初阶牛客网刷题——JZ17 打印从1到最大的n位数【难度:入门】

1.题目描述 牛客网OJ题链接 题目描述&#xff1a; 输入数字 n&#xff0c;按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3&#xff0c;则打印出 1、2、3 一直到最大的 3 位数 999。 用返回一个整数列表来代替打印n 为正整数&#xff0c;0 < n < 5 示例1 输入&…...

【组件分享】商品列表组件-最佳实践

商品列表组件 商品列表组件用于展示商品信息列表&#xff0c;支持多种布局方式和自定义配置。 基础用法 <template><ProGoodsList :goods-list"goodsList" :layout"layout" item-click"handleItemClick" /> </template>&l…...

【JAVA】BOSS系统发版艺术:构建高效、优雅的微服务部署策略

在现代软件开发领域&#xff0c;微服务架构与容器化部署已迅速成为行业新趋势。微服务架构通过将应用拆分成多个小型、自治的服务单元&#xff0c;每个服务承担某项特定的业务功能。而容器化部署则以其轻量级和高度可移植的特性&#xff0c;为这些微服务的有效打包、分发和运行…...

React的响应式

在 React 中&#xff0c;useState 是一个 Hook&#xff0c;用于在函数组件中定义和管理状态。 setCount 是由 useState 返回的第二个值&#xff0c;用于更新状态并触发组件重新渲染。它的本质是一个状态更新函数&#xff0c;背后是 React 的状态管理和调度机制。下面是对 setCo…...

deep face cam 部署报错解决

这里写自定义目录标题 使用conda创建py3.9环境使用按照readme.txt安装所有依赖后遇到的报错2.最后一个模块insightface安装报错3.运行run.py又报错原因:解决方法:4.缺少模块解决方法:pip命令安装5.AttributeError: NoneType object has no attribute configure解决方法:安装…...

图像处理基础(4):高斯滤波器详解

本文主要介绍了高斯滤波器的原理及其实现过程 高斯滤波器是一种线性滤波器&#xff0c;能够有效的抑制噪声&#xff0c;平滑图像。其作用原理和均值滤波器类似&#xff0c;都是取滤波器窗口内的像素的均值作为输出。其窗口模板的系数和均值滤波器不同&#xff0c;均值滤波器的…...

outlook附件限制最大5m如何解决

Outlook 附件大小限制为 5MB&#xff0c;通常由邮件服务器&#xff08;如 Exchange、Office 365、Gmail 等&#xff09;或本地 Outlook 配置决定。可以采取以下几种方法来解决该限制问题&#xff1a; 解决方案 1&#xff1a;调整服务器端限制&#xff08;管理员权限&#xff09…...

Word常见问题:嵌入图片无法显示完整

场景&#xff1a;在Word中&#xff0c;嵌入式图片显示不全&#xff0c;一部分图片在文字下方。如&#xff1a; 问题原因&#xff1a;因段落行距导致 方法一 快捷方式 选中图片&#xff0c;通过"ctrl1"快捷调整为1倍行距 方法二 通过工具栏调整 选中图片&#xff0…...

【面试题】java基础概念

以下是关于这道面试题的回答&#xff0c;包括JDK中一些相关概念的区别以及JIT的原理&#xff1a; JDK中相关概念区别 JDK、JRE和JVM JDK&#xff08;Java Development Kit&#xff09;&#xff1a;是Java开发工具包&#xff0c;它包含了JRE和一系列开发工具。JDK提供了编译、…...