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

在Jetpack Compose中使用ExoPlayer实现直播流和音频均衡器

在Jetpack Compose中使用ExoPlayer实现直播流和音频均衡器

背景

ExoPlayer与Media3的能力结合,为Android应用程序播放多媒体内容提供了强大的解决方案。在本教程中,我们将介绍如何设置带有Media3的ExoPlayer来支持使用M3U8 URL进行直播流。此外,我们还将探讨如何集成音频均衡器,为用户提供个性化的音频体验。

使用ExoPlayer进行直播流涉及到利用ExoPlayer库的强大能力,在互联网上实时播放多媒体内容。

在这个探索中,我们将深入了解ExoPlayer如何实现无缝播放直播流URL的复杂性,为用户提供沉浸式体验。

ExoPlayer如何处理直播流?

使用ExoPlayer进行直播流主要围绕着有效地处理音频和视频内容的实时传输。该过程包括几个关键阶段:

  1. 内容源(Content Source):使用摄像机和麦克风捕获直播内容,然后将此直播流可用于流媒体。
  2. 编码(Encoding):捕获的内容被编码为适用于流媒体的数字格式。这涉及将原始音频和视频数据压缩并转换为与流媒体协议兼容的格式。
  3. 流媒体服务器(Streaming Server):编码数据发送到充当中央枢纽的流媒体服务器。该服务器通过向用户设备发送数据包来管理多个观众的直播内容分发。
  4. ExoPlayer集成(ExoPlayer Integration):将具有强大能力的ExoPlayer集成到应用程序中,以处理直播流的播放。应用程序获取直播流URL,并配置ExoPlayer以处理流媒体协议(例如HLS或DASH)。
  5. 观众设备(Viewer’s Device):用户通过各种设备访问直播流,包括智能手机、平板电脑、计算机或智能电视。观众设备上的ExoPlayer实例解码接收到的数据,使他们可以实时观看或收听直播内容。

设置ExoPlayer以进行直播流

要将ExoPlayer集成到我们的应用程序中以进行直播流,我们需要遵循以下关键步骤:

1.添加依赖项
在项目的build.gradle文件中包含必要的依赖项:

// Exoplayer dependencies
implementation("androidx.media3:media3-exoplayer:1.2.0")
implementation("androidx.media3:media3-ui:1.2.0")
implementation("androidx.media3:media3-exoplayer-hls:1.2.0")

这些依赖项确保应用程序可以利用ExoPlayer的功能进行直播流。

  1. 创建ExoPlayerManager
    我们将创建一个管理器类来处理ExoPlayer实例。这可以确保应用程序的整个生命周期中只有一个播放器实例。
object ExoPlayerManager {private var exoPlayer: ExoPlayer? = nullfun getExoPlayer(context: Context): ExoPlayer {if (exoPlayer == null) {exoPlayer = ExoPlayer.Builder(context).build()}return exoPlayer!!}fun releaseExoPlayer() {exoPlayer?.release()exoPlayer = null}
}
  1. 初始化ExoPlayer
    在您的Composable函数中,使用示例HLS流URL初始化ExoPlayer:
@Composable
fun LiveStreamingScreen() {// Obtain the current context and lifecycle owner using LocalContext and LocalLifecycleOwnerval context = LocalContext.currentval lifecycleOwner = LocalLifecycleOwner.current// Remember the ExoPlayer instance to persist across recompositionsval exoPlayer = remember { ExoPlayerManager.getExoPlayer(context) }// Launch an effect to initialize ExoPlayer and set up the media sourceLaunchedEffect(key1 = Unit) {// Create a data source factory for handling media requestsval dataSourceFactory = DefaultHttpDataSource.Factory()// Define the URI for the sample HLS streamval uri = Uri.Builder().encodedPath("http://sample.vodobox.net/skate_phantom_flex_4k/skate_phantom_flex_4k.m3u8").build()val mediaItem = MediaItem.Builder().setUri(uri).build()// Create an HlsMediaSource from the media item for handling HTTP Live Streaming (HLS) content val internetVideoSource =HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem)exoPlayer.setMediaSource(internetVideoSource)exoPlayer.prepare()// Will be used in later implementation for EqualizerviewModel.onStart(exoPlayer.audioSessionId)}// ...
}
  1. 显示ExoPlayer视图

将ExoPlayer视图集成到您的Composable函数中

// ...
Box(modifier = Modifier.fillMaxSize()) {AndroidView(modifier =Modifier.fillMaxWidth().aspectRatio(1.4f).padding(top = 16.dp).background(Color.Black),factory = {PlayerView(context).apply {// Connect the ExoPlayer instance to the PlayerViewplayer = exoPlayer// Configure ExoPlayer settingsexoPlayer.repeatMode = Player.REPEAT_MODE_ONEexoPlayer.playWhenReady = falseuseController = true}})
}
// ...
  1. 观察生命周期事件并释放资源
    设置DisposableEffects来观察生命周期事件,并在组合函数被释放时释放ExoPlayer
// ...
// Observe lifecycle events (e.g., app resume and pause)
// and adjust ExoPlayer's playback state accordingly.
DisposableEffect(key1 = lifecycleOwner) {val observer = LifecycleEventObserver { _, event ->if (event == Lifecycle.Event.ON_RESUME) {exoPlayer.playWhenReady = true} else if (event == Lifecycle.Event.ON_PAUSE) {exoPlayer.playWhenReady = false}}lifecycleOwner.lifecycle.addObserver(observer)onDispose {lifecycleOwner.lifecycle.removeObserver(observer)}
}// Release the ExoPlayer when the composable is disposed
// This helps in proper resource management
DisposableEffect(key1 = Unit) {onDispose { ExoPlayerManager.releaseExoPlayer() }
}
// ...

设置音频均衡器

现在,让我们探讨如何在使用Jetpack Compose的Exoplayer设置中集成音频均衡器。这将允许用户通过调整预设均衡器设置或创建自定义均衡器配置来自定义音频体验。

音频均衡器通过提供对音频输出的细粒度控制来增强用户体验。

  1. 添加依赖项
    在项目的build.gradle文件中包含必要的依赖项:
    implementation("com.google.dagger:hilt-android:2.48")kapt("com.google.dagger:hilt-android-compiler:2.47")implementation("androidx.hilt:hilt-navigation-compose:1.1.0")//Gsonimplementation("com.google.code.gson:gson:2.9.1")

以上是示例依赖项,您可以根据您的项目需要进行相应的更改。

这些依赖项确保您的应用程序可以利用Hilt进行依赖注入和Gson以高效地将复杂数据存储在首选项中。

  1. 定义均衡器预设和增益值
    我们定义了一组预设,例如Flat, Acoustic,Rock,每个预设都有相应的增益值来控制音频频率。这些预设将作为用户自定义其音频体验的起点。
// Equalizer presets and gain values
val effectType = arrayListOf("Custom", "Flat", "Acoustic", "Dance","Hip Hop", "Jazz", "Pop", "Rock", "Podcast"
)// Constants for presets
const val PRESET_CUSTOM = 0
const val PRESET_FLAT = 1
const val PRESET_ACOUSTIC = 2
const val PRESET_DANCE_LOUNGE = 3
const val PRESET_HIP_HOP = 4
const val PRESET_JAZZ_BLUES = 5
const val PRESET_POP = 6
const val PRESET_ROCK = 7
const val PRESET_PODCAST = 8// Gain values for each preset
val FLAT = arrayListOf(0.0, 0.0, 0.0, 0.0, 0.0)
val ACOUSTIC = arrayListOf(0.44, 0.12, 0.12, 0.34, 0.2)
val DANCE = arrayListOf(0.52, 0.08, 0.28, 0.48, 0.06)
val HIP_HOPE = arrayListOf(0.44, 0.06, -0.14, 0.1, 0.38)
val JAZZ = arrayListOf(0.32, 0.0, 0.22, 0.1, 0.2)
val POP = arrayListOf(-0.14, 0.28, 0.38, 0.22, -0.2)
val ROCK = arrayListOf(0.38, 0.2, -0.04, 0.02, 0.34)
val PODCAST = arrayListOf(-0.12, 0.26, 0.36, 0.16, -0.2)

在上面的代码中,我们定义了一个EqualizerConfiguration数据类,它包含了预设设置和自定义频段设置的列表。EqualizerPreset类表示一个均衡器预设,包括名称和增益值的列表。CustomEqualizerBand类表示自定义的均衡器频段,包括频率和增益值。通过使用这些数据结构,我们可以轻松地管理和应用均衡器配置。

  1. 创建音频效果数据类
    音频效果数据类包含了关于所选效果类型及其相应增益值的重要信息。这个数据类充当用户偏好与音频均衡器实际实现之间的桥梁。
// 表示音频效果配置的数据类
data class AudioEffects(var selectedEffectType: Int = 0,var gainValues: ArrayList<Double>
)

在这里,selectedEffectType表示所选的音频预设,而gainValues则存储了不同频段的自定义增益值。这个数据类封装了用户的音频偏好设置。

  1. 创建AppModule进行依赖注入
    为了实现清晰模块化的依赖注入,我们引入了AppModule。这个模块使用@InstallIn(SingletonComponent::class)进行标注,提供了诸如Gson和SharedPreferences等必要的依赖项。
@Module
@InstallIn(SingletonComponent::class)
class AppModule {@Provides@Singletonfun provideGson(): Gson {val gsonBuilder = GsonBuilder()return gsonBuilder.create()}@Named(AUDIO_EFFECT_PREFERENCES)@Providesfun provideAudioEffectPreferences(application: Application): SharedPreferences {return application.getSharedPreferences(AUDIO_EFFECT_PREFERENCES, Context.MODE_PRIVATE)}
}

在这个模块中,provideGson提供了一个Gson的单例实例,而provideAudioEffectPreferences则提供了一个特定用于音频效果偏好设置的SharedPreferences实例。这个模块对于管理整个应用程序的依赖项非常重要。

  1. 使用SharedPreferencesGson实现均衡器偏好设置
    为了提供无缝的用户体验,我们将利用SharedPreferences来持久化与音频均衡器相关的用户偏好设置。此外,我们使用Gson进行高效的数据序列化,使我们能够将复杂的数据结构转换为易于存储和检索的格式。通过创建一个EqualizerPreferences类,我们确保用户不必重复设置他们的均衡器偏好设置。
const val AUDIO_EFFECT_PREFERENCES = "audio_effect_preferences"private const val AUDIO_EFFECT_IS_EQUALIZER_ENABLED = "is_equalizer_enabled"
private const val AUDIO_EFFECT_EQUALIZER_SETTING = "equalizer_audio_effect"
private const val AUDIO_EFFECT_LOWEST_BAND_LEVEL = "equalizer_lowest_band_level"@Singleton
class EqualizerPreferences
@Inject constructor(@param:Named(AUDIO_EFFECT_PREFERENCES) private val sharedPreferences: SharedPreferences,private val gson: Gson
) {var isEqualizerEnabled: Booleanget() = sharedPreferences.getBoolean(AUDIO_EFFECT_IS_EQUALIZER_ENABLED, false)set(isEnable) = sharedPreferences.edit().putBoolean(AUDIO_EFFECT_IS_EQUALIZER_ENABLED, isEnable).apply()// Getting and setting the user's audio preferencesvar audioEffects: AudioEffects?get() {val json = sharedPreferences.getString(AUDIO_EFFECT_EQUALIZER_SETTING, null)if (json != null) {try {return gson.fromJson(json, AudioEffects::class.java)} catch (t: Throwable) {t.printStackTrace()}}return null}set(audioEffects) {var json: String? = nullif (audioEffects != null) {json = gson.toJson(audioEffects)}sharedPreferences.edit().putString(AUDIO_EFFECT_EQUALIZER_SETTING, json).apply()}var lowestBandLevel: Intget() = sharedPreferences.getInt(AUDIO_EFFECT_LOWEST_BAND_LEVEL, 0)set(value) = sharedPreferences.edit().putInt(AUDIO_EFFECT_LOWEST_BAND_LEVEL, value).apply()
}

上述代码展示了如何使用SharedPreferences和Gson来保存和加载音频效果配置。saveAudioEffects方法将AudioEffects对象转换为JSON字符串,并将其保存在SharedPreferences中。loadAudioEffects方法从SharedPreferences中获取JSON字符串,并将其转换回AudioEffects对象。通过使用EqualizerPreferences类,我们可以方便地管理和访问均衡器偏好设置。

在这里,Gson在将我们的AudioEffects数据类转换为JSON字符串以存储在SharedPreferences中方面起着至关重要的作用。这确保了一种无缝且高效的方式来存储和检索复杂的数据结构。

  1. 创建一个音频均衡器ViewModel
    创建一个强大的AudioEqualizerViewModel,负责管理音频均衡器逻辑。这个ViewModel初始化均衡器,处理预设选择,并根据用户交互更新设置。
@HiltViewModel
class AudioEqualizerViewModel @Inject constructor(private val equalizerPreferences: EqualizerPreferences
) : ViewModel() {// MutableStateFlow to observe and emit changes in audio effectsval audioEffects = MutableStateFlow<AudioEffects?>(null)// Instance of the Equalizer class from the Android system libraryprivate var equalizer: Equalizer? = null// MutableStateFlow to observe and emit changes in the equalizer's enable/disable stateval enableEqualizer = MutableStateFlow(false)// Unique audio session ID associated with the Exoplayerprivate var audioSessionId = 0init {// Retrieve and set the initial equalizer enable/disable state and audio effects from preferencesenableEqualizer.value = equalizerPreferences.isEqualizerEnabledaudioEffects.tryEmit(equalizerPreferences.audioEffects)if (audioEffects.value == null) {audioEffects.tryEmit(AudioEffects(PRESET_FLAT, FLAT))}}// Will be called when exoplayer instance is created and we have audioSessionIdfun onStart(sessionId: Int) {audioSessionId = sessionIdequalizer?.enabled = enableEqualizer.valueequalizer = Equalizer(Int.MAX_VALUE, audioSessionId)// Set the lowest band level based on the equalizer's capabilitiesequalizerPreferences.lowestBandLevel = equalizer?.bandLevelRange?.get(0)?.toInt() ?: 0// Apply gain values to the equalizer based on the stored audio effectsaudioEffects.value?.gainValues?.forEachIndexed { index, value ->val bandLevel = (value * 1000).toInt().toShort()equalizer?.setBandLevel(index.toShort(), bandLevel)}}// Method called when a preset is selectedfun onSelectPreset(presetPosition: Int) {// Return if no audio effects are availableif (audioEffects.value == null) return// Determine the gain values based on the selected presetval gain = if (presetPosition == PRESET_CUSTOM) {ArrayList(audioEffects.value!!.gainValues)} else {ArrayList(getPresetGainValue(presetPosition))}// Update the audio effects with the selected preset and gain valuesaudioEffects.tryEmit(AudioEffects(presetPosition, gain))equalizerPreferences.audioEffects = audioEffects.value// Apply the gain values to the equalizerequalizer?.apply {gain.forEachIndexed { index, value ->val bandLevel = (value * 1000).toInt().toShort()setBandLevel(index.toShort(), bandLevel)}}}// Method called when a specific band level is changed by the userfun onBandLevelChanged(changedBand: Int, newGainValue: Int) {// Retrieve the lowest band level from preferencesval lowest = equalizerPreferences.lowestBandLevel// Calculate the new band levelval bandLevel = newGainValue.plus(lowest)// Apply the new band level to the equalizerequalizer?.setBandLevel(changedBand.toShort(), bandLevel.toShort())val list = ArrayList(audioEffects.value!!.gainValues)list[changedBand] = (newGainValue.toDouble() / 1000)audioEffects.tryEmit(AudioEffects(PRESET_CUSTOM,list))equalizerPreferences.audioEffects = audioEffects.value}// Method called to toggle the equalizer's enable/disable statefun toggleEqualizer() {enableEqualizer.tryEmit(!enableEqualizer.value)equalizer?.enabled = enableEqualizer.valueequalizerPreferences.isEqualizerEnabled = enableEqualizer.valueif (!enableEqualizer.value) {audioEffects.tryEmit(AudioEffects(PRESET_FLAT, FLAT))equalizerPreferences.audioEffects = audioEffects.value}}// Method to retrieve gain values for a specific presetprivate fun getPresetGainValue(index: Int): List<Double> {return when (index) {PRESET_FLAT -> FLATPRESET_ACOUSTIC -> ACOUSTICPRESET_DANCE_LOUNGE -> DANCEPRESET_HIP_HOP -> HIP_HOPEPRESET_JAZZ_BLUES -> JAZZPRESET_POP -> POPPRESET_ROCK -> ROCKPRESET_PODCAST -> PODCASTelse -> FLAT}}
}

这个ViewModel高效地管理音频均衡器的状态,处理用户交互,并确保使用SharedPreferences持久化用户偏好设置。

  1. 开发均衡器开关视图、预设视图和均衡器视图组件
    设计一个用户友好的均衡器开关、均衡器视图和预设视图组件,让用户可以可视化和调整均衡器设置。开关允许用户使用均衡器启用/禁用音频设置,而均衡器视图将包含不同频率带的滑块,提供高度可定制的音频体验。预设视图将包含一些预定义的效果类型,可以直接应用到均衡器上。

Switch View

Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween,verticalAlignment = Alignment.CenterVertically
) {Text(text = stringResource(R.string.equalizer_title_text),fontSize = MaterialTheme.typography.titleLarge.fontSize,fontWeight = FontWeight.SemiBold,color = Color.White)Switch(checked = enableEqualizer,onCheckedChange = { // Toggle the equalizer's enable/disable stateviewModel.toggleEqualizer() },colors =SwitchDefaults.colors(checkedTrackColor = Color.Black,checkedIconColor = Color.Black,uncheckedTrackColor = Color.White,uncheckedBorderColor = Color.Black,))
}

Equalizer SwitchView
EqualizerView

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EqualizerView(viewModel: AudioEqualizerViewModel) {// Frequency labels for the equalizer bandsval xAxisLabels = listOf("60Hz", "230Hz", "910Hz", "3kHz", "14kHz")// Collect the current state of audio effects from the ViewModelval audioEffects by viewModel.audioEffects.collectAsState()// Column layout to arrange UI elements verticallyColumn(modifier = Modifier.fillMaxWidth().fillMaxHeight().graphicsLayer {// Rotate the entire column to display frequency labels/sliders verticallyrotationZ = 270f},verticalArrangement = Arrangement.SpaceEvenly,horizontalAlignment = Alignment.CenterHorizontally) {// Iterate through frequency labels and create corresponding UI elementsfor (index in xAxisLabels.indices) {Row(modifier = Modifier.padding(top = 20.dp).width(220.dp)) {// Each frequency label and its corresponding slider are placed in a BoxBox {// Display the frequency label with rotationText(text = xAxisLabels[index],modifier = Modifier.wrapContentWidth().align(Alignment.CenterStart).rotate(90f),color = Color.White,fontSize = 8.sp,textAlign = TextAlign.Start)// Slider component for adjusting the gain value of each frequency bandSlider(modifier = Modifier.offset(x = 20.dp),// Bind the slider value to the corresponding gain value from the ViewModelvalue = audioEffects!!.gainValues[index].times(1000f).toFloat().coerceIn(-3000f, 3000f),onValueChange = {// Notify the ViewModel when a slider value changesviewModel.onBandLevelChanged(index, it.toInt())},valueRange = -3000f..3000f,colors = SliderDefaults.colors(thumbColor = Color.Black,activeTrackColor = Color.Black,inactiveTrackColor = Color.White),thumb = {// Customized appearance of the slider's thumbBox(modifier = Modifier.size(20.dp).border(1.dp,Color.White,CircleShape).clip(CircleShape).background(Color.Black, CircleShape))})}}}}
}

Equalizer View
PresetsView

@Composable
fun PresetsView(viewModel: AudioEqualizerViewModel) {// Collect the current state of audio effects from the ViewModelval audioEffects by viewModel.audioEffects.collectAsState()// Group the effect types into chunks of 4 for layoutval groupedList = effectType.chunked(4)// Row containing the title and dividersRow(verticalAlignment = Alignment.CenterVertically) {Divider(modifier = Modifier.weight(1f).height(4.dp).clip(RoundedCornerShape(4.dp)),color = Color.White,thickness = 1.dp)// Title textText(text = stringResource(R.string.presets_title_text),fontSize = MaterialTheme.typography.titleMedium.fontSize,fontWeight = FontWeight.Medium,color = Color.White,modifier = Modifier.wrapContentWidth().weight(0.5f).padding(4.dp).zIndex(1f),textAlign = TextAlign.Center)Divider(modifier = Modifier.weight(1f).height(4.dp).clip(RoundedCornerShape(4.dp)),color = Color.White,thickness = 1.dp)}Spacer(modifier = Modifier.height(20.dp))// Iterate through grouped effect types and create UI elementsfor (itemList in groupedList) {BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {// Calculate padding and spacing based on screen widthval horizontalPadding =if (maxWidth < 320.dp) 8.dp else if (maxWidth > 400.dp) 40.dp else 20.dpval horizontalSpacing = if (maxWidth > 400.dp) 24.dp else 16.dp// Row containing individual preset itemsRow(modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),horizontalArrangement = Arrangement.spacedBy(space = horizontalSpacing,alignment = Alignment.CenterHorizontally),verticalAlignment = Alignment.CenterVertically) {for (item in itemList) {// Get the index of the current itemval index by remember {mutableIntStateOf(effectType.indexOf(item))}// Create a clickable preset itemBoxWithConstraints(modifier = Modifier.wrapContentSize().border(1.dp,if (index == audioEffects?.selectedEffectType) Color.White else Color.Black,RoundedCornerShape(40.dp)).clip(RoundedCornerShape(40.dp)).clickable {// Notify the ViewModel when a preset is selectedviewModel.onSelectPreset(index)}.background(if (index == audioEffects?.selectedEffectType) Color.Black else Color.White),contentAlignment = Alignment.Center) {// Display the preset item textText(text = item,style = MaterialTheme.typography.bodySmall,modifier = Modifier.padding(horizontal = horizontalPadding,vertical = 12.dp),fontSize = 14.sp,color = if (index == audioEffects?.selectedEffectType) Color.White else Color.Black,maxLines = 1,overflow = TextOverflow.Ellipsis)}}}}}
}


现在,让我们使用AnimatedVisibility从父组件中调用上述函数!

@Composable
fun AudioEqualizerScreen() {val viewModel = hiltViewModel<AudioEqualizerViewModel>()val enableEqualizer by viewModel.enableEqualizer.collectAsState()Column {Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween,verticalAlignment = Alignment.CenterVertically) {Text(text = stringResource(R.string.equalizer_title_text),fontSize = MaterialTheme.typography.titleLarge.fontSize,fontWeight = FontWeight.SemiBold,color = Color.White)Switch(checked = enableEqualizer,onCheckedChange = { viewModel.toggleEqualizer() },colors =SwitchDefaults.colors(checkedTrackColor = Color.Black,checkedIconColor = Color.Black,uncheckedTrackColor = Color.White,uncheckedBorderColor = Color.Black,))}AnimatedVisibility(visible = enableEqualizer,enter = fadeIn() + slideInVertically { fullHeight -> -fullHeight / 2 },exit = fadeOut() + slideOutVertically { fullHeight -> -fullHeight / 3 }) {EqualizerView(viewModel = viewModel)}AnimatedVisibility(visible = enableEqualizer,enter = fadeIn() + slideInVertically { fullHeight -> -fullHeight / 2 },exit = fadeOut() + slideOutVertically { fullHeight -> -fullHeight / 2 }) {PresetsView(viewModel)}}
}

Final View

结论

在本篇博客中,我们为在Jetpack Compose应用程序中设置ExoPlayer进行实时流式传输和集成音频均衡器打下了基础。这个组合为带有可定制均衡器设置的音频流提供了无缝的用户体验。

Github

https://github.com/cp-megh-l/audio-equalizer-compose

相关文章:

在Jetpack Compose中使用ExoPlayer实现直播流和音频均衡器

在Jetpack Compose中使用ExoPlayer实现直播流和音频均衡器 背景 ExoPlayer与Media3的能力结合&#xff0c;为Android应用程序播放多媒体内容提供了强大的解决方案。在本教程中&#xff0c;我们将介绍如何设置带有Media3的ExoPlayer来支持使用M3U8 URL进行直播流。此外&#x…...

持续集成交付CICD:Jira 远程触发 Jenkins 实现更新 GitLab 分支

目录 一、实验 1.环境 2.GitLab 查看项目 3.Jira新建模块 4. Jira 通过Webhook 触发Jenkins流水线 3.Jira 远程触发 Jenkins 实现更新 GitLab 分支 二、问题 1.Jira 配置网络钩子失败 2. Jira 远程触发Jenkins 报错 一、实验 1.环境 &#xff08;1&#xff09;主机 …...

基于SSM的面向TCP_IP的网络互联实验平台

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SSM的面向TCP和IP的网络互联实验平台…...

【IDEA】try-catch自动生成中修改catch的内容

编辑器 --> 文件和代码模板 --> 代码 --> Catch Statement Body...

2024 十大AI预测

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

【Linux基础开发工具】gcc/g++使用make/Makefile

目录 前言 gcc/g的使用 1. 语言的发展 1.1 语言和编译器自举的过程 1.2 程序翻译的过程&#xff1a; 2. 动静态库的理解 Linux项目自动化构建工具-make/makefile 1. 快速上手使用 2. makefile/make执行顺序的理解 前言 了解完vim编辑器的使用&#xff0c;接下来就可以尝…...

Windows Nginx版本升级

记录windows系统上nginx版本从1.22.1直接升级到1.25.3&#xff0c;全程一步到位&#xff01; nginx官网: https://nginx.org/ C:\Windows\system32>cd C:\nginx# 查看当前nginx版本C:\nginx>nginx -v nginx version: nginx/1.22.1# 停止nginx服务C:\nginx>net stop ng…...

kubernetes集群 应用实践 kafka部署

kubernetes集群 应用实践 kafka部署 零.1、环境说明 零.2、kafka架构说明 zookeeper在kafka集群中的作用 一、Broker注册 二、Topic注册 三、Topic Partition选主 四、生产者负载均衡 五、消费者负载均衡 一、持久化存储资源准备 1.1 创建共享目录 [rootnfsserver ~]# mkdir -…...

Featured Based知识蒸馏及代码(3): Focal and Global Knowledge (FGD)

文章目录 1. 摘要2. Focal and Global 蒸馏的原理2.1 常规的feature based蒸馏算法2.2 Focal Distillation2.3 Global Distillation2.4 total loss3. 实验完整代码论文: htt...

CentOs 安装MySQL

1、拉取安装包 wget --no-check-certificate dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm 成功拉取 2、安装 yum install mysql-community-release-el6-5.noarch.rpm 过程中可能需要你同意一些东西&#xff0c;y 即可 然后稍微检查一下 yum repolist enabled…...

基于Java (spring-boot)的在线考试管理系统

一、项目介绍 系统功能说明 1、系统共有管理员、老师、学生三个角色&#xff0c;管理员拥有系统最高权限。 2、老师拥有考试管理、题库管理、成绩管理、学生管理四个模块。 3、学生可以参与考试、查看成绩、试题练习、留言等功能 二、作品包含 三、项目技术 后端语言&…...

5. 结构型模式 - 外观模式

亦称&#xff1a; Facade 意图 外观模式是一种结构型设计模式&#xff0c; 能为程序库、 框架或其他复杂类提供一个简单的接口 问题 假设你必须在代码中使用某个复杂的库或框架中的众多对象。 正常情况下&#xff0c; 你需要负责所有对象的初始化工作、 管理其依赖关系并按正确…...

微服务之配置中心与服务跟踪

zookeeper 配置中心 实现的架构图如下所示&#xff0c;采取数据加载到内存方式解决高效获取的问题&#xff0c;借助 zookeeper 的节点监听机制来实现实时感知。 配置中心数据分类 事件调度&#xff08;kafka&#xff09; 消息服务和事件的统一调度&#xff0c;常用用 kafka …...

链表 典型习题

160 相交链表&#xff1a;遍历&#xff0c;统计是否出现过 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Solution { public:ListNode *getIntersectionNode(L…...

面试题:JVM 对锁都进行了哪些优化?

文章目录 锁优化自旋锁和自适应自旋锁消除锁粗化逃逸分析方法逃逸线程逃逸通过逃逸分析&#xff0c;编译器对代码的优化 锁优化 jvm 在加锁的过程中&#xff0c;会采用自旋、自适应、锁消除、锁粗化等优化手段来提升代码执行效率。 自旋锁和自适应自旋 现在大多的处理器都是…...

SSM整合实战(Spring、SpringMVC、MyBatis)

五、SSM整合实战 目录 一、SSM整合理解 1. 什么是SSM整合&#xff1f;2. SSM整合核心理解五连问&#xff01; 2.1 SSM整合涉及几个IoC容器&#xff1f;2.2 每个IoC容器盛放哪些组件&#xff1f;2.3 IoC容器之间是什么关系&#xff1f;2.4 需要几个配置文件和对应IoC容器关系&…...

QT调用外部exe及无终端弹窗的解决方案、并实现进程输出信息获取

博主使用QT调用外部exe程序&#xff0c;外部exe程序有printf输出&#xff0c;起初使用的是C语言中的system()方法&#xff0c;但在笔记本上有概率出现终端窗口一闪而过的情况&#xff0c;后修改了调用方案。 1. QT调用外部exe 使用QT中的QProcess方法 #include <QProcess…...

大语言模型的三种主要架构 Decoder-Only、Encoder-Only、Encoder-Decoder

现代大型语言模型&#xff08;LLM&#xff09;的演变进化树&#xff0c;如下图&#xff1a; https://arxiv.org/pdf/2304.13712.pdf 基于 Transformer 模型以非灰色显示&#xff1a; decoder-only 模型在蓝色分支&#xff0c; encoder-only 模型在粉色分支&#xff0c; encod…...

【MySQL】外连接 where 和 on 的区别

力扣题 1、题目地址 1158. 市场分析 I 2、模拟表 User Column NameTypeuser_idintjoin_datedatefavorite_brandvarchar user_id 是此表主键&#xff08;具有唯一值的列&#xff09;。表中描述了购物网站的用户信息&#xff0c;用户可以在此网站上进行商品买卖。 Orders…...

【优化】XXLJOB修改为使用虚拟线程

【优化】XXLJOB修改为使用虚拟线程 新建这几个目录 类&#xff0c; 去找项目对应的xxljob的源码 主要是将 new Thread 改为 虚拟线程 Thread.ofVirtual().name("VT").unstarted 以下代码是 xxljob 2.3.0版本 举一反三 去修改对应版本的代码 <!-- 定…...

金蝶Apusic应用服务器 loadTree JNDI注入漏洞复现(QVD-2023-48297)

0x01 产品简介 金蝶Apusic应用服务器是一款企业级应用服务器,支持Java EE技术,适用于各种商业环境。 0x02 漏洞概述 由于金蝶Apusic应用服务器权限验证不当,导致攻击者可以向loadTree接口执行JNDI注入,造成远程代码执行漏洞。利用该漏洞需低版本JDK。(漏洞比较旧,8月份…...

PromptNER: Prompt Locating and Typing for Named Entity Recognition

原文链接&#xff1a; https://aclanthology.org/2023.acl-long.698.pdf ACL 2023 介绍 问题 目前将prompt方法应用在ner中主要有两种方法&#xff1a;对枚举的span类型进行预测&#xff0c;或者通过构建特殊的prompt来对实体进行定位。但作者认为这些方法存在以下问题&#xf…...

QT编写应用的界面自适应分辨率的解决方案

博主在工作机上完成QT软件开发&#xff08;控件大小与字体大小比例正常&#xff09;&#xff0c;部署到客户机后&#xff0c;发现控件大小与字体大小比例失调&#xff0c;具体表现为控件装不下字体&#xff0c;即字体显示不全&#xff0c;推测是软件不能自适应分辨率导致的。 文…...

Kubernetes pod ip 暴露

1. k8s pod 和 service 网络暴露 借助 iptables 的路由转发功能&#xff0c;打通k8s集群内的pod和service网络&#xff0c;与外部网络联通 # 查看集群的 pod 网段和 service 网段 kubectl -n kube-system describe cm kubeadm-config networking:dnsDomain: cluster.localpod…...

442. 数组中重复的数据

数组中重复的数据 描述 : 给你一个长度为 n 的整数数组 nums &#xff0c;其中 nums 的所有整数都在范围 [1, n] 内&#xff0c;且每个整数出现 一次 或 两次 。请你找出所有出现 两次 的整数&#xff0c;并以数组形式返回。 你必须设计并实现一个时间复杂度为 O(n) 且仅使用…...

Qt/C++视频监控Onvif工具/组播搜索/显示监控画面/图片参数调节/OSD管理/祖传原创

一、前言 能够写出简单易用而又不失功能强大的组件&#xff0c;一直是我的追求&#xff0c;简单主要体现在易用性&#xff0c;不能搞一些繁琐的流程和一些极难使用的API接口&#xff0c;或者一些看不懂的很难以理解的函数名称&#xff0c;一定是要越简单越好。功能强大主要体现…...

word2003 open word2007+

Win 7 C:\Documents and Settings\Administrator\Application Data\Microsoft\Templates 还是不行&#xff0c;重装office2003吧&#xff0c;再安装转换插件&#xff0c;但是再高版本好像没转换工具...

windows安装、基本使用vim

标题&#xff1a;windows安装、基本使用vim 1.下载并安装GVIM 百度网盘链接 提取码&#xff1a;2apr 进入安装界面&#xff0c;如下&#xff0c;勾选 其它都是默认即可 参考&#xff1b; 2.在powershell中使用vim 参考blog&#xff1a;window10安装vim编辑器 安装好后&…...

【SpringBoot快速入门】(1)SpringBoot的开发步骤、工程构建方法以及工程的快速启动详细讲解

目录 SpringBoot简介1 SpringBoot快速入门1.1 开发步骤1.1.1 创建新模块1.1.2 创建 Controller1.1.3 启动服务器1.1.4 进行测试 2 对比3 官网构建工程3.1 进入SpringBoot官网3.2 选择依赖3.3 生成工程 4 SpringBoot工程快速启动4.1 问题导入4.2 打包4.3 启动 之前我们已经学习的…...

Day69力扣打卡

打卡记录...

机器学习:手撕 AlphaGo(一)

图 1-1: AphaGo 结构概览 1. 前言 AlphaGo 是一个非常经典的模型&#xff0c;不论从影响力还是模型设计上。它的技术迭代演进路径&#xff1a;AlphaGo&#xff0c;AlphaGoZero&#xff0c;AlphaZero&#xff0c;MuZero 更是十分精彩。相信有很多同学因为听了 AlphaGo 的故事对…...

ElasticSearch学习篇9_文本相似度计算方法现状以及基于改进的 Jaccard 算法代码实现

背景 XOP亿级别题库的试题召回以及搜题的举一反三业务场景都涉及使用文本相似搜索技术&#xff0c;学习此方面技术以便更好的服务于业务场景。 目前基于集合的Jaccard算法以及基于编辑距离的Levenshtein在计算文本相似度场景中有着各自的特点&#xff0c;为了优化具体的计算时…...

大创项目推荐 深度学习+python+opencv实现动物识别 - 图像识别

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 inception_v3网络5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…...

Debezium系列之:Flink SQL消费Debezium数据格式,同步数据到下游存储系统

Debezium系列之:Flink SQL消费Debezium数据格式,同步数据到下游存储系统 一、Debezium二、依赖三、使用Debezium Format四、可用元数据五、Format参数六、重复的变更事件七、消费 Debezium Postgres Connector 产生的数据八、数据类型映射一、Debezium Debezium 是一个 CDC(…...

webrtc支持的最小宽度和高度

代码在&#xff1a;h264/sps_parser.cc // // IMPORTANT ONES! Now were getting to resolution. First we read the pic // width/height in macroblocks (16x16), which gives us the base resolution, // and then we continue on until we hit the frame crop offsets, wh…...

虚拟机对象的创建

虚拟机对象 虚拟机在Java堆中对象分配、布局和访问的访问过程 对象的创建 Java对象的创建步骤&#xff1a; 1&#xff09;类加载检查 虚拟机遇到一条 new 指令时&#xff0c;首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用&#xff0c;并且检查这个符号…...

阿里云吴结生:云计算是企业实现数智化的阶梯

云布道师 近年来&#xff0c;越来越多人意识到&#xff0c;我们正处在一个数据爆炸式增长的时代。IDC 预测 2027 年全球产生的数据量将达到 291 ZB&#xff0c;与 2022 年相比&#xff0c;增长了近 2 倍。其中 75% 的数据来自企业&#xff0c;每一个现代化的企业都是一家数据公…...

MySQL——复合查询

目录 一.基本查询回顾 二. 多表查询 三.自连接 四.子查询 1.单行子查询 2.多行子查询 3.多列子查询 4.在from子句中使用子查询 5.合并查询 一.基本查询回顾 准备数据库&#xff1a; 查询工资高于500或岗位为MANAGER的雇员&#xff0c;同时还要满足他们的姓名首字母为…...

mysql 23-3day 数据库授权(DCL)

目录 创建一个用户 并授权&#xff08;grant&#xff09;设置最大连接数客户端链接服务器创建用户删除用户修改用户修改密码root修改自己密码授予 mysql 权限收回权限收回权限刷新一下授权表mydql 知识点确保 mysql 用户为普通用户删除空口令账号安全建议 创建一个用户 并授权&…...

OpenHarmony之内核层解析~

OpenHarmony简介 技术架构 OpenHarmony整体遵从分层设计&#xff0c;从下向上依次为&#xff1a;内核层、系统服务层、框架层和应用层。系统功能按照“系统 > 子系统 > 组件”逐级展开&#xff0c;在多设备部署场景下&#xff0c;支持根据实际需求裁剪某些非必要的组件…...

Chatgpt如何共享可以防止封号!

ChatGPT 是一个基于 GPT-3.5/GPT-4 模型的对话系统&#xff0c;它主要用于处理自然语言对话。通过训练模型来模拟人类的语言行为&#xff0c;ChatGPT 可以通过文本交流与用户互动。每个新版本的 GPT 通常都会在模型规模、性能和其他方面有一些改进。在目前免费版GPT-3.5 中&…...

智能优化算法应用:基于社交网络算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于社交网络算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于社交网络算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.社交网络算法4.实验参数设定5.算法结果6.…...

thinkphp+vue+mysql酒店客房管理系统 b1g8z

本系统包括前台界面、用户界面和管理员界面、员工界面。在前台界面里游客和用户可以浏览客房信息、公告信息等&#xff0c;用户可以预定客房&#xff0c;在用户中心界面里&#xff0c;用户可以管理预定信息&#xff0c;管理员负责用户预定的审核以及客房的发布、用户的入住等。…...

nodejs+vue+ElementUi摄影作品图片分享工作室管理系统

第1周 2.21&#xff5e;2.27 查阅资料&#xff0c;学习vscode开发平台和vue框架技术 第2周 2.28&#xff5e;3.6 对软件功能需求进行分析, 软件功能模块划分及软件界面设计 第3周 3.7&#xff5e;3.13 撰写并提交毕业设计开题报告、英文资料翻译 第4周 3.14&#xff5…...

详解FreeRTOS:专栏总述

目录 1、理论篇 2、基础篇 3、进阶篇 4、高级篇 5、拓展篇 本专栏基于FreeRTOS底层源码介绍了嵌入式实时操作系统的概念&#xff0c;FreeRTOS任务创建、任务调度、任务同步与消息传递&#xff0c;软件定时器、事件通知等知识。 主要分为5方面内容&#xff1a;理论篇、基础…...

在 linux 服务器上安装Redis数据库

先打开我们的Linux服务器 终端执行 安装redis sudo yum install redis然后 他会提示你要占多少磁盘空间 例如 我这里是 1.7 M 没问题就 y 然后回车就可以了 然后 我们这里执行 redis-cli --version这样 就能看到版本了 然后 我们可以根据版本选择启动命令 使用systemctl命…...

阿里云经济型、通用算力型、计算型、通用型、内存型云服务器最新活动报价

阿里云作为国内领先的云计算服务提供商&#xff0c;提供了多种规格的云服务器供用户选择。为了满足不同用户的需求&#xff0c;阿里云推出了经济型、通用算力型、计算型、通用型和内存型等不同类型的云服务器。下面将详细介绍这些云服务器的最新活动报价。 一、阿里云特惠云服…...

回溯算法 典型习题

vector<vector<int>> res; vector<int> path;void dfs() {if (递归终止条件){res.push_back(path);return;}// 递归方向for (xxx) {path.push_back(val);dfs();path.pop_back();} } 1.涉及枚举 2.不确定 for 循环的次数 总结 枚举各种可能的情况。 0.直接…...

14. 从零用Rust编写正反向代理, HTTP文件服务器的实现过程及参数

wmproxy wmproxy是由Rust编写&#xff0c;已实现http/https代理&#xff0c;socks5代理&#xff0c; 反向代理&#xff0c;静态文件服务器&#xff0c;内网穿透&#xff0c;配置热更新等&#xff0c; 后续将实现websocket代理等&#xff0c;同时会将实现过程分享出来&#xff…...

【随笔】MD5加密字符串、文件apache、springframework实现

文章目录 一、引入依赖二、工具代码三、测试代码四、输出结果 一、引入依赖 commons-codec <dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.13</version> </dependency>二…...