HarmonyOS学习第18天:多媒体功能全解析
一、开篇引入
在当今数字化时代,多媒体已经深度融入我们的日常生活。无论是在工作中通过视频会议进行沟通协作,还是在学习时借助在线课程的音频讲解加深理解,亦或是在休闲时光用手机播放音乐放松身心、观看视频打发时间,多媒体功能都无处不在,成为我们生活中不可或缺的一部分。对于应用开发者而言,为 HarmonyOS 应用增添音频和视频功能,不仅能够满足用户日益增长的多样化需求,还能显著提升应用的竞争力和用户体验。接下来,就让我们一起深入探索如何在 HarmonyOS 应用中实现音频和视频的播放、录制功能,以及处理多媒体文件的格式转换和编辑。
二、音频播放功能实现
(一)MediaPlayer 基本介绍
在 HarmonyOS 多媒体开发中,MediaPlayer 是一个极为核心的类,承担着播放音频和视频的重要职责。它就像是一位专业的 “演奏家”,能够识别多种常见的音频格式,如 MP3、AAC、WAV 等 ,并将这些音频文件中的数字信号转化为美妙的声音。无论是播放本地存储的音乐文件,还是从网络上实时获取的音频流,MediaPlayer 都能游刃有余地应对,为用户带来丰富的听觉体验。通过它,开发者可以轻松实现音频的播放、暂停、停止、跳转等操作,为应用增添强大的音频交互功能。
(二)创建 MediaPlayer 实例
创建 MediaPlayer 实例是实现音频播放的第一步。在 HarmonyOS 中,我们可以通过以下代码简单地创建一个 MediaPlayer 对象:
| MediaPlayer mediaPlayer = new MediaPlayer(); |
需要注意的是,创建实例时应确保在合适的生命周期方法中进行,比如在 Ability 的 onStart 方法中。这样可以保证 MediaPlayer 在应用启动时被正确初始化,避免出现空指针异常等问题。同时,要记得为 MediaPlayer 对象添加必要的异常处理机制,以应对可能出现的错误情况,确保应用的稳定性。
(三)设置音频源
设置音频源是告诉 MediaPlayer 要播放的音频文件来自哪里。MediaPlayer 提供了多种设置音频源的方式,常见的有以下两种:
使用本地文件路径:如果音频文件存储在本地设备上,可以直接使用文件路径来设置音频源。例如:
| try { mediaPlayer.setDataSource("/data/local/tmp/your_audio_file.mp3"); } catch (Exception e) { e.printStackTrace(); } |
这里的/data/local/tmp/your_audio_file.mp3需要替换为实际的音频文件路径。在使用本地文件路径时,要确保应用具有访问该文件的权限,否则会导致设置音频源失败。
使用 URI:当音频文件的位置可以通过统一资源标识符(URI)来表示时,我们可以使用setDataSource(Context context, Uri uri)方法来设置音频源。例如,从应用的资源目录中获取音频文件:
| Uri uri = Uri.parse("dataability:///resource/raw/your_audio_file.mp3"); try { mediaPlayer.setDataSource(getContext(), uri); } catch (Exception e) { e.printStackTrace(); } |
其中dataability:///resource/raw/your_audio_file.mp3是音频文件在资源目录下的 URI,开发者需要根据实际的资源路径进行修改。使用 URI 的方式更加灵活,适用于从不同来源获取音频文件的场景。
(四)准备与播放音频
在设置好音频源后,需要调用prepareAsync()方法来准备音频文件。这个方法是异步的,它会在后台线程中进行音频文件的准备工作,避免阻塞主线程,从而保证应用的流畅运行。当音频准备完成后,会触发OnPreparedListener回调。在这个回调中,我们可以调用start()方法来启动音频播放。示例代码如下:
| mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mp.start(); } }); |
这种异步准备和回调机制,使得音频播放的过程更加高效和稳定,尤其是在处理较大的音频文件或网络音频流时,能够避免因长时间的准备工作而导致应用界面卡顿,为用户提供更优质的播放体验。
(五)释放资源
当音频播放完成或者不再需要播放音频时,务必调用release()方法来释放 MediaPlayer 占用的资源。这是一个非常重要的步骤,如果不及时释放资源,可能会导致内存泄漏,影响应用的性能,甚至导致应用崩溃。释放资源的代码如下:
| if (mediaPlayer != null) { mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; } |
通常在 Ability 的 onStop 或 onDestroy 方法中进行资源释放操作,以确保在应用暂停或销毁时,MediaPlayer 占用的资源能够被正确回收,为其他应用或系统进程腾出宝贵的资源空间。
三、音频录制功能实现
(一)权限申请
在 HarmonyOS 应用中实现音频录制功能,首先必须申请麦克风权限,这是获取音频输入的关键前提。因为音频录制涉及到对用户声音的采集,属于敏感操作,所以 HarmonyOS 将麦克风权限归类为用户授权类型。这意味着应用不仅要在配置文件中声明该权限,还需要在运行时动态请求用户授权。
在使用的模块下的module.json5中添加权限声明,示例代码如下:
| { "name": "ohos.permission.MICROPHONE", "reason": "$string:permission_desc_for_MICROPHONE", "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } } |
其中,reason字段用于说明申请权限的原因,会在向用户请求授权时展示,所以要填写清晰明了的描述,让用户能够理解应用为什么需要该权限;usedScene字段指定了权限的使用场景,这里表明在EntryAbility中使用,并且是在使用相关功能时才申请。
在代码中动态申请授权,可以参考以下示例:
| import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; import common from '@ohos.app.ability.common'; export function requestMicrophonePermission(context: common.UIAbilityContext, permissionResult: (allow: boolean) => void): void { let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); let permissions: Array<string> = ['ohos.permission.MICROPHONE']; atManager.requestPermissionsFromUser(context, permissions).then((data) => { let grantStatus: Array<number> = data.authResults; let length: number = grantStatus.length; for (let i = 0; i < length; i++) { if (grantStatus[i] === 0) { permissionResult(true); console.debug("麦克风授权成功:用户授权"); } else { permissionResult(false); console.debug("麦克风授权失败:用户拒绝"); return; } } }).catch((err: BusinessError) => { permissionResult(false); console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`); }); } |
在上述代码中,首先创建了AtManager实例,然后调用requestPermissionsFromUser方法向用户请求麦克风权限。该方法会返回一个 Promise,通过then方法处理授权成功的情况,通过catch方法处理请求权限过程中可能出现的错误。当用户授权成功时,grantStatus[i]的值为 0,此时调用permissionResult(true)通知调用者权限已被授予;如果用户拒绝授权,grantStatus[i]的值不为 0,调用permissionResult(false)并提示用户拒绝授权。
(二)AVRecorder 配置
AVRecorder 是 HarmonyOS 中用于音频和视频录制的重要类,在使用它进行音频录制前,需要进行一系列关键配置。以下是一些主要的配置参数:
音频源类型(audioSourceType):用于指定音频输入源,通常设置为media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,表示使用麦克风作为音频输入源。这是最常见的音频录制场景,例如录制语音备忘录、会议记录等。
采样率(audioSampleRate):它决定了每秒采集的音频样本数量,单位是赫兹(Hz)。常见的采样率有 44100Hz、48000Hz 等 。较高的采样率可以更准确地还原声音,但会占用更多的存储空间和处理资源。在选择采样率时,需要根据实际需求和应用场景进行权衡。例如,对于一般的语音录制,44100Hz 已经足够;而对于高质量的音乐录制,可能需要选择 48000Hz 甚至更高的采样率。
编码格式(audioCodec):指定音频的编码方式,当前 HarmonyOS 中 AVRecorder 支持的音频编码格式如media.CodecMimeType.AUDIO_AAC 。不同的编码格式在压缩比、音质和兼容性等方面存在差异。AAC 是一种广泛应用的音频编码格式,它在保持较高音质的同时,能够实现较好的压缩效果,文件体积相对较小,因此在很多场景中都被优先选用。
封装格式(fileFormat):确定录制生成的音频文件的封装格式,例如media.ContainerFormatType.CFT_MPEG_4A表示生成的音频文件为 M4A 格式 。封装格式决定了音频数据如何存储在文件中,以及文件的结构和元数据信息。M4A 格式是一种常见的音频封装格式,它与 AAC 编码格式配合良好,能够有效地存储和传输音频数据。
以下是一个 AVRecorder 配置的示例代码:
| import media from '@ohos.multimedia.media'; let avProfile = { audioBitrate: 100000, // 音频比特率 audioChannels: 2, // 音频声道数 audioCodec: media.CodecMimeType.AUDIO_AAC, // 音频编码格式,当前只支持aac audioSampleRate: 48000, // 音频采样率 fileFormat: media.ContainerFormatType.CFT_MPEG_4A // 封装格式,当前只支持m4a }; let avConfig = { audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, // 音频输入源,这里设置为麦克风 profile: avProfile, url: 'fd://35' // 参考应用文件访问与管理中的开发示例获取创建的音频文件fd填入此处 }; |
在这个示例中,首先定义了一个avProfile对象,设置了音频录制的各项参数,包括音频比特率、声道数、编码格式、采样率和封装格式。然后创建了avConfig对象,将音频源类型设置为麦克风,并关联了前面定义的avProfile对象。同时,还需要指定录制文件的存储位置,这里使用fd://35的形式,其中35需要替换为实际通过文件操作获取到的文件描述符(fd)。在实际应用中,获取文件描述符的过程需要参考 HarmonyOS 的文件访问与管理相关文档和示例代码,确保能够正确创建和访问用于存储录制音频的文件。
(三)录音操作控制
完成权限申请和 AVRecorder 配置后,就可以进行录音操作控制了。这包括开始、暂停、恢复和停止录音等操作,同时要妥善处理录音过程中可能出现的异常情况,以保证录音功能的稳定性和可靠性。
开始录音:调用 AVRecorder 的start()方法开始录音。在调用该方法前,需要确保 AVRecorder 已经完成配置并处于prepared状态。示例代码如下:
| avRecorder.start().then(() => { console.log('录音开始成功'); }).catch((err) => { console.error(`录音开始失败,错误信息:${err.message}`); }); |
在这个代码片段中,start()方法返回一个 Promise,通过then方法处理录音开始成功的情况,在控制台打印 “录音开始成功”;通过catch方法捕获可能出现的错误,并在控制台输出错误信息,以便开发者进行调试和问题排查。
暂停录音:当需要暂停录音时,调用pause()方法。但要注意,只有在 AVRecorder 处于started状态时,调用pause()方法才是合理的状态切换。示例代码如下:
| if (avRecorder.state ==='started') { avRecorder.pause().then(() => { console.log('录音暂停成功'); }).catch((err) => { console.error(`录音暂停失败,错误信息:${err.message}`); }); } |
这里首先检查 AVRecorder 的当前状态是否为started,如果是,则调用pause()方法。同样,通过then和catch方法分别处理暂停成功和失败的情况。
恢复录音:如果之前暂停了录音,可以调用resume()方法恢复录音。只有在 AVRecorder 处于paused状态时,才能成功调用该方法。示例代码如下:
| if (avRecorder.state === 'paused') { avRecorder.resume().then(() => { console.log('录音恢复成功'); }).catch((err) => { console.error(`录音恢复失败,错误信息:${err.message}`); }); } |
此代码逻辑与暂停录音类似,先检查状态,再进行恢复操作,并处理相应的结果。
停止录音:当录音完成或需要停止录音时,调用stop()方法。停止录音后,通常还需要调用reset()方法重置 AVRecorder 的状态,使其回到idle状态,以便进行下一次录音配置和操作;最后调用release()方法释放 AVRecorder 占用的资源,避免内存泄漏。示例代码如下:
| if (avRecorder.state ==='started' || avRecorder.state === 'paused') { avRecorder.stop().then(() => { console.log('录音停止成功'); avRecorder.reset().then(() => { console.log('AVRecorder已重置'); avRecorder.release().then(() => { console.log('AVRecorder资源已释放'); }).catch((err) => { console.error(`释放AVRecorder资源失败,错误信息:${err.message}`); }); }).catch((err) => { console.error(`重置AVRecorder失败,错误信息:${err.message}`); }); }).catch((err) => { console.error(`录音停止失败,错误信息:${err.message}`); }); } |
这段代码展示了停止录音的完整流程,先判断 AVRecorder 的状态是否为started或paused,如果是,则依次调用stop()、reset()和release()方法,并在每个操作成功或失败时进行相应的日志记录和错误处理。
在录音过程中,还可能出现各种异常情况,例如设备故障、权限被收回等。为了及时处理这些异常,可以为 AVRecorder 添加错误监听事件。示例代码如下:
| avRecorder.on('error', (err) => { console.error(`录音过程中出现错误,错误代码:${err.code},错误信息:${err.message}`); // 这里可以添加一些错误处理逻辑,比如提示用户、尝试重新录制等 }); |
通过on('error', callback)方法,当 AVRecorder 在录音过程中发生错误时,会调用传入的回调函数callback,在回调函数中可以获取到错误信息,包括错误代码和详细的错误描述,开发者可以根据这些信息进行针对性的处理,例如向用户显示友好的错误提示,或者尝试重新初始化 AVRecorder 并进行录音操作,以提高应用的稳定性和用户体验。
四、视频播放功能实现
(一)播放框架概述
HarmonyOS 的视频播放框架旨在为开发者提供高效、便捷且强大的视频播放能力。其设计目标围绕着低消耗、简单易用和灵活扩展展开。低消耗意味着在播放视频时,框架能够优化资源的使用,降低设备的功耗,确保即使在长时间播放视频的情况下,设备也能保持良好的性能表现,不会出现过热、卡顿等问题。简单易用体现在框架提供了简洁明了的 API,无论是对于经验丰富的开发者还是初学者,都能快速上手并实现基本的视频播放功能。同时,它还提供了多种接口形式,如 JS 接口和结合 ArkUI 提供的 UI 控件接口,满足不同开发场景和习惯的需求。灵活扩展则使得框架能够适应不断发展的视频技术和多样化的应用需求,开发者可以根据实际情况对播放引擎进行增强、扩展或替换,以实现更丰富的功能和更好的播放效果。
从架构上看,播放框架主要由中间件和硬件适配层组成。中间件的核心是基于引擎提供各种各样的服务能力,目前 HarmonyOS 提供了 GStreamer 引擎和 HiStreamer 引擎 ,这两套引擎功能都较为齐全,能够支持多种常见的视频格式和编码方式。例如,GStreamer 引擎是一个功能强大的多媒体框架,它具有丰富的插件生态系统,能够处理各种复杂的多媒体任务;HiStreamer 引擎则以其轻量化和高效性著称,基于传统的 pipeline,通过插件化机制增强了音视频的编解码和解析能力,在处理一些对性能要求较高的视频播放场景时表现出色。在硬件适配层,即 HDF(Hardware Driver Foundation)层,提供了兼容设计,能够适配不同的硬件设备,确保视频播放功能在各种终端设备上都能稳定运行。这种分层架构的设计,使得播放框架具有良好的可维护性和可扩展性,开发者可以专注于应用层的开发,而无需过多关注底层硬件的差异和复杂的多媒体处理细节。
(二)使用 MediaPlayer 播放视频
使用 MediaPlayer 播放视频的步骤与播放音频有相似之处,但也存在一些关键的区别。在创建 MediaPlayer 实例方面,与音频播放一致,通过MediaPlayer mediaPlayer = new MediaPlayer();即可创建。然而,在设置视频源时,同样可以使用本地文件路径或 URI 的方式。例如,使用本地文件路径设置视频源:
| try { mediaPlayer.setDataSource("/data/local/tmp/your_video_file.mp4"); } catch (Exception e) { e.printStackTrace(); } |
这里的/data/local/tmp/your_video_file.mp4需替换为实际的视频文件路径,并且要确保应用具有访问该文件的权限。使用 URI 设置视频源时,示例如下:
| Uri uri = Uri.parse("dataability:///resource/raw/your_video_file.mp4"); try { mediaPlayer.setDataSource(getContext(), uri); } catch (Exception e) { e.printStackTrace(); } |
其中dataability:///resource/raw/your_video_file.mp4是视频文件在资源目录下的 URI,开发者需根据实际资源路径修改。
与音频播放最大的不同在于,视频播放需要设置一个 Surface 用于显示视频画面。Surface 是一个可以在其上绘制图形的对象,在视频播放中,它充当了视频画面的载体。我们可以通过创建 SurfaceView 来获取 Surface,然后将其设置给 MediaPlayer。首先,在 XML 布局文件中添加 SurfaceView:
| <ohos.agp.components.SurfaceView ohos:id="$+id:surface_view" ohos:height="match_parent" ohos:width="match_parent" /> |
然后在代码中获取 SurfaceView 并设置给 MediaPlayer:
| SurfaceView surfaceView = (SurfaceView) findComponentById(ResourceTable.Id_surface_view); Surface surface = surfaceView.getSurface(); mediaPlayer.setVideoSurface(surface); |
在准备和播放视频阶段,与音频播放类似,调用prepareAsync()方法异步准备视频,当准备完成后,通过OnPreparedListener回调中的start()方法启动播放:
| mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mp.start(); } }); |
通过以上步骤,就可以实现基本的视频播放功能,将视频文件的内容在设备屏幕上展示出来。
(三)处理视频播放的交互
为了提升用户体验,视频播放应用通常需要实现一系列交互功能,如播放进度控制、音量调节、暂停与播放切换等。
播放进度控制可以通过 SeekBar 组件和 MediaPlayer 的seekTo(int msec)方法来实现。SeekBar 是一个拖动条组件,用户可以通过拖动它来改变视频的播放位置。首先,在 XML 布局文件中添加 SeekBar:
| <ohos.agp.components.SeekBar ohos:id="$+id:seek_bar" ohos:height="wrap_content" ohos:width="match_parent" /> |
然后在代码中获取 SeekBar 并为其添加进度改变监听器。当用户拖动 SeekBar 时,监听器会获取当前的进度值,并调用seekTo(int msec)方法将视频播放位置调整到对应的时间点。示例代码如下:
| SeekBar seekBar = (SeekBar) findComponentById(ResourceTable.Id_seek_bar); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { mediaPlayer.seekTo(progress); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { // 开始拖动时的操作 } @Override public void onStopTrackingTouch(SeekBar seekBar) { // 停止拖动时的操作 } }); |
同时,为了实时更新 SeekBar 的进度,使其与视频的实际播放进度保持一致,可以通过一个定时任务来获取视频的当前播放位置,并更新 SeekBar 的进度。例如,使用 Handler 实现定时更新:
| Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { if (mediaPlayer!= null) { int currentPosition = mediaPlayer.getCurrentPosition(); seekBar.setProgress(currentPosition); } handler.postDelayed(this, 1000); } }; handler.post(runnable); |
这样,每隔 1 秒就会获取一次视频的当前播放位置,并更新 SeekBar 的进度,让用户能够直观地了解视频的播放进度。
音量调节可以通过 AudioManager 类来实现。AudioManager 是 HarmonyOS 中用于管理音频相关设置的类,我们可以通过它来获取当前的音量,并实现音量的增加和减少操作。首先获取 AudioManager 实例:
| AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); |
然后通过getStreamVolume(int streamType)方法获取当前的音量,streamType参数指定音频流类型,对于视频播放,通常使用AudioManager.STREAM_MUSIC。例如:
| int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); |
实现音量增加和减少的操作可以通过adjustVolume(int direction, int flags)方法,direction参数指定调节方向,AudioManager.ADJUST_RAISE表示增加音量,AudioManager.ADJUST_LOWER表示减少音量。示例代码如下:
| // 增加音量 audioManager.adjustVolume(AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI); // 减少音量 audioManager.adjustVolume(AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI); |
这里的AudioManager.FLAG_SHOW_UI标志表示在调节音量时显示系统的音量调节 UI,让用户能够直观地看到音量的变化。
暂停与播放切换是视频播放中最基本的交互功能之一,通过 MediaPlayer 的pause()和start()方法即可实现。可以在界面上添加一个按钮,当用户点击按钮时,根据当前视频的播放状态来决定执行暂停还是播放操作。示例代码如下:
| Button playPauseButton = (Button) findComponentById(ResourceTable.Id.play_pause_button); playPauseButton.setOnClickListener(new Component.OnClickListener() { @Override public void onClick(Component component) { if (mediaPlayer.isPlaying()) { mediaPlayer.pause(); playPauseButton.setText("播放"); } else { mediaPlayer.start(); playPauseButton.setText("暂停"); } } }); |
通过以上对播放进度控制、音量调节、暂停与播放切换等交互功能的实现,能够为用户提供更加丰富和便捷的视频播放体验,满足用户在观看视频过程中的各种操作需求。
五、视频录制功能实现
(一)相机权限与配置
在 HarmonyOS 应用中实现视频录制功能,首先需要申请相机权限。相机权限属于敏感权限,HarmonyOS 对其管理较为严格,应用不仅要在module.json5文件中声明权限,还需要在运行时动态请求用户授权。在module.json5文件中添加相机权限声明的示例代码如下:
| { "name": "ohos.permission.CAMERA", "reason": "$string:permission_desc_for_CAMERA", "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" } } |
这里的reason字段用于向用户解释申请权限的原因,usedScene字段指定了权限的使用场景,表明在EntryAbility中使用该权限,并且是在使用相关功能时才申请。
在运行时动态请求相机权限,可以使用abilityAccessCtrl模块中的requestPermissionsFromUser方法。示例代码如下:
| import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; import common from '@ohos.app.ability.common'; export function requestCameraPermission(context: common.UIAbilityContext, permissionResult: (allow: boolean) => void): void { let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); let permissions: Array<string> = ['ohos.permission.CAMERA']; atManager.requestPermissionsFromUser(context, permissions).then((data) => { let grantStatus: Array<number> = data.authResults; let length: number = grantStatus.length; for (let i = 0; i < length; i++) { if (grantStatus[i] === 0) { permissionResult(true); console.debug("相机授权成功:用户授权"); } else { permissionResult(false); console.debug("相机授权失败:用户拒绝"); return; } } }).catch((err: BusinessError) => { permissionResult(false); console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`); }); } |
在上述代码中,首先创建了AtManager实例,然后调用requestPermissionsFromUser方法向用户请求相机权限。该方法返回一个 Promise,通过then方法处理授权成功的情况,通过catch方法处理请求权限过程中可能出现的错误。当用户授权成功时,grantStatus[i]的值为 0,此时调用permissionResult(true)通知调用者权限已被授予;如果用户拒绝授权,grantStatus[i]的值不为 0,调用permissionResult(false)并提示用户拒绝授权。
配置相机参数是实现高质量视频录制的关键步骤。相机参数包括分辨率、帧率、对焦模式、曝光模式等 。不同的参数设置会对视频的质量和性能产生显著影响。例如,较高的分辨率可以提供更清晰的图像细节,但会占用更多的存储空间和网络带宽;较高的帧率可以使视频播放更加流畅,但也会增加设备的处理负担。因此,在配置相机参数时,需要根据实际需求和设备性能进行权衡和选择。
以设置分辨率和帧率为例,在 HarmonyOS 中可以使用CameraConfiguration类来配置相机参数。示例代码如下:
| import ohos.media.camera.CameraConfiguration; import ohos.media.camera.CameraDevice; CameraDevice cameraDevice = getCameraDevice(); // 获取相机设备实例 CameraConfiguration configuration = new CameraConfiguration(cameraDevice); configuration.setPreviewSize(1920, 1080); // 设置预览分辨率为1920x1080 configuration.setPreviewFpsRange(new int[]{30, 30}); // 设置预览帧率为30fps cameraDevice.applyConfiguration(configuration); // 应用配置 |
在上述代码中,首先获取相机设备实例,然后创建CameraConfiguration对象,并通过setPreviewSize和setPreviewFpsRange方法分别设置预览分辨率和帧率。最后,调用applyConfiguration方法将配置应用到相机设备上。除了分辨率和帧率,还可以通过CameraConfiguration类设置其他相机参数,如对焦模式、曝光模式等 。例如,设置自动对焦模式的代码如下:
| configuration.setFocusMode(CameraConfiguration.FOCUS_MODE_CONTINUOUS_PICTURE); |
设置自动曝光模式的代码如下:
| configuration.setExposureMode(CameraConfiguration.EXPOSURE_MODE_AUTO); |
通过合理配置这些相机参数,可以满足不同场景下的视频录制需求,为用户提供高质量的视频录制体验。
(二)使用 Camera 进行视频录制
在 HarmonyOS 中,使用 Camera 进行视频录制的核心步骤是获取视频流并结合 AVRecorder 进行录制。首先,需要创建相机输入和输出流。相机输入流用于获取相机采集的数据,相机输出流则用于将视频数据输出到 AVRecorder 进行录制。
创建相机输入流的示例代码如下:
| import camera from '@ohos.multimedia.camera'; // 获取相机管理器 let cameraManager: camera.CameraManager = camera.getCameraManager(context); // 获取相机列表 let cameraArray: Array<camera.CameraDevice> = cameraManager.getSupportedCameras(); // 创建相机输入 let cameraInput: camera.CameraInput = cameraManager.createCameraInput(cameraArray[0]); |
在上述代码中,首先通过camera.getCameraManager(context)获取相机管理器,然后使用getSupportedCameras方法获取设备支持的相机列表。最后,选择第一个相机设备并通过createCameraInput方法创建相机输入流。
创建相机预览输出流和视频输出流的示例代码如下:
| // 创建预览输出流 let previewOutput: camera.PreviewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId); // 创建视频输出流 let videoProfile: undefined | camera.VideoProfile = videoProfilesArray.find((profile: camera.VideoProfile) => { return profile.size.width === aVRecorderProfile.videoFrameWidth && profile.size.height === aVRecorderProfile.videoFrameHeight; }); let videoOutput: camera.VideoOutput | undefined = undefined; if (videoProfile) { videoOutput = cameraManager.createVideoOutput(videoProfile, videoSurfaceId); } |
在这段代码中,首先创建预览输出流,其中previewProfilesArray是相机支持的预览配置文件数组,surfaceId是用于显示预览画面的 Surface 的 ID。然后,从相机支持的视频配置文件数组videoProfilesArray中找到与 AVRecorder 配置的视频分辨率相匹配的视频配置文件videoProfile,并使用createVideoOutput方法创建视频输出流,videoSurfaceId是 AVRecorder 用于接收视频数据的 Surface 的 ID。
创建好相机输入和输出流后,需要配置相机会话,将输入流和输出流添加到会话中,并启动会话。示例代码如下:
| // 创建会话 let videoSession: camera.CaptureSession | undefined = undefined; videoSession = cameraManager.createCaptureSession(); videoSession.beginConfig(); // 添加相机输入流 videoSession.addInput(cameraInput); // 添加预览输出流 videoSession.addOutput(previewOutput); // 添加视频输出流 if (videoOutput) { videoSession.addOutput(videoOutput); } // 提交会话配置 videoSession.commitConfig(); // 启动会话 videoSession.start(); |
在上述代码中,首先创建相机会话videoSession,然后使用beginConfig方法开始配置会话。接着,将相机输入流、预览输出流和视频输出流添加到会话中。完成配置后,调用commitConfig方法提交配置,并使用start方法启动会话。此时,相机开始采集视频数据,并将数据输出到预览输出流和视频输出流中。
AVRecorder 是 HarmonyOS 中用于音视频录制的重要类,它负责将相机输出的视频流进行编码、封装,并保存为视频文件。在使用 AVRecorder 进行视频录制前,需要进行一系列配置,包括设置视频源类型、编码格式、封装格式、视频分辨率、帧率等参数。配置 AVRecorder 的示例代码如下:
| import media from '@ohos.multimedia.media'; // 创建AVRecorder实例 let avRecorder: media.AVRecorder = await media.createAVRecorder(); // 配置AVRecorder参数 let aVRecorderProfile: media.AVRecorderProfile = { fileFormat: media.ContainerFormatType.CFT_MPEG_4, // 视频文件封装格式,只支持MP4 videoBitrate: 2000000, // 视频比特率 videoCodec: media.CodecMimeType.VIDEO_AVC, // 视频文件编码格式,支持avc格式 videoFrameWidth: 640, // 视频分辨率的宽 videoFrameHeight: 480, // 视频分辨率的高 videoFrameRate: 30 // 视频帧率 }; let aVRecorderConfig: media.AVRecorderConfig = { videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV, // 视频源类型,支持YUV和ES两种格式 profile: aVRecorderProfile, url: 'fd://' + file.fd, // 参考应用文件访问与管理中的开发示例获取创建的音频文件fd填入此处 rotation: 0 // 视频旋转角度,默认为0不旋转,支持的值为0、90、180、270 }; // 准备AVRecorder await avRecorder.prepare(aVRecorderConfig); |
在上述代码中,首先创建 AVRecorder 实例,然后配置录制参数。aVRecorderProfile对象设置了视频的封装格式、比特率、编码格式、分辨率和帧率等参数;aVRecorderConfig对象设置了视频源类型、配置文件、录制文件的存储路径(url)和视频旋转角度。最后,调用prepare方法准备 AVRecorder,使其处于可录制状态。
完成上述步骤后,相机采集的视频流就会通过视频输出流传递给 AVRecorder,AVRecorder 根据配置的参数对视频流进行编码、封装,并将录制的视频保存到指定的文件路径中。
(三)录制控制与文件保存
实现录制的开始、暂停、停止操作是视频录制功能的基本需求。在 HarmonyOS 中,通过 AVRecorder 和相机输出流的相关方法可以轻松实现这些控制操作。
开始录制时,需要同时启动相机的视频输出流和 AVRecorder 的录制功能。示例代码如下:
| // 启动相机视频输出流 if (videoOutput) { videoOutput.start(); } // 启动AVRecorder录制 avRecorder.start().then(() => { console.log('视频录制开始成功'); }).catch((err) => { console.error(`视频录制开始失败,错误信息:${err.message}`); }); |
在上述代码中,首先判断videoOutput是否存在,如果存在则调用其start方法启动相机视频输出流。然后,调用 AVRecorder 的start方法开始录制视频,start方法返回一个 Promise,通过then方法处理录制开始成功的情况,通过catch方法捕获并处理可能出现的错误。
暂停录制时,需要同时暂停相机的视频输出流和 AVRecorder 的录制功能。示例代码如下:
| // 暂停相机视频输出流 if (videoOutput) { videoOutput.stop(); } // 暂停AVRecorder录制 if (avRecorder.state ==='started') { avRecorder.pause().then(() => { console.log('视频录制暂停成功'); }).catch((err) => { console.error(`视频录制暂停失败,错误信息:${err.message}`); }); } |
在这段代码中,首先判断videoOutput是否存在,如果存在则调用其stop方法暂停相机视频输出流。然后,检查 AVRecorder 的当前状态是否为started,如果是则调用pause方法暂停录制,并通过then和catch方法处理暂停操作的结果。
停止录制时,同样需要停止相机的视频输出流和 AVRecorder 的录制功能,并释放相关资源。示例代码如下:
| // 停止相机视频输出流 if (videoOutput) { videoOutput.stop(); } // 停止AVRecorder录制 if (avRecorder.state ==='started' || avRecorder.state === 'paused') { avRecorder.stop().then(() => { console.log('视频录制停止成功'); avRecorder.reset().then(() => { console.log('AVRecorder已重置'); avRecorder.release().then(() => { console.log('AVRecorder资源已释放'); }).catch((err) => { console.error(`释放AVRecorder资源失败,错误信息:${err.message}`); }); }).catch((err) => { console.error(`重置AVRecorder失败,错误信息:${err.message}`); }); }).catch((err) => { console.error(`视频录制停止失败,错误信息:${err.message}`); }); } |
在上述代码中,首先停止相机视频输出流。然后,检查 AVRecorder 的状态,如果处于started或paused状态,则调用stop方法停止录制。停止录制后,依次调用reset方法重置 AVRecorder 的状态,使其回到初始状态,以便进行下一次录制配置和操作;最后调用release方法释放 AVRecorder 占用的资源,避免内存泄漏,并通过then和catch方法处理每个操作的结果。
将录制的视频保存到指定路径是视频录制功能的最终目标。在配置 AVRecorder 时,通过aVRecorderConfig对象的url参数指定了录制文件的存储路径,例如url: 'fd://' + file.fd ,其中file.fd是通过文件操作获取到的文件描述符(fd)。在实际应用中,需要根据具体的文件访问与管理方式获取文件描述符,并确保应用具有对该文件的读写权限。
在录制完成后,视频文件就会保存到指定的路径中。为了方便用户管理和使用录制的视频,还可以在应用中提供一些文件管理功能,例如显示录制视频的列表、提供视频的播放和分享功能等。通过这些功能,用户可以更加便捷地查看和使用自己录制的视频,提升应用的实用性和用户体验。
六、多媒体文件格式转换和编辑
(一)格式转换的必要性
在多媒体应用开发中,不同设备和平台对多媒体格式的支持存在显著差异。例如,某些老旧设备可能仅支持常见的 MP3 音频格式和 MP4 视频格式,而对于一些新兴的、高压缩比或特殊用途的格式,如 FLAC 音频格式、AV1 视频格式等则无法识别和播放。在不同的平台上,像 Windows 系统原生支持的多媒体格式与 HarmonyOS、iOS 系统所支持的格式也不尽相同。这就导致当我们开发的应用需要在多种设备和平台上运行时,如果多媒体文件格式不兼容,就会出现无法播放或显示异常的问题。因此,为了确保多媒体内容能够在各种设备和平台上稳定、流畅地运行,实现多媒体文件格式转换是非常必要的。它能够使我们的应用适应不同的环境,满足更广泛用户的需求,提升应用的通用性和用户体验。
(二)HarmonyOS 的格式转换工具与 API
在 HarmonyOS 中,AVCodec Kit 是实现多媒体文件格式转换的重要工具。它提供了丰富的 API,能够实现音频、视频格式的转换。以视频格式转换为例,我们可以使用AVTranscoder类来完成转换操作。
首先,需要创建AVTranscoder实例,代码如下:
| import { media } from '@ohos.multimedia.media'; let avTranscoder: media.AVTranscoder; media.createAVTranscoder().then((transcoder: media.AVTranscoder) => { avTranscoder = transcoder; }, (error) => { console.error(`createAVTranscoder failed`); }); |
在创建实例后,需要设置转码的源文件和目标文件。这里的源文件和目标文件可以通过文件描述符(fd)来指定。例如,设置源文件属性fdSrc和目标文件属性fdDst:
| // 设置转码的源文件属性fdSrc avTranscoder.fdSrc = sourceFileFd; // 设置转码的目标文件属性fdDst avTranscoder.fdDst = targetFileFd; |
其中sourceFileFd和targetFileFd需要根据实际情况获取,比如使用本地资源转码时,要确认资源文件可用,并使用应用沙箱路径访问对应资源;如果使用ResourceManager.getRawFd打开 HAP 资源文件描述符,可参考ResourceManager API 参考。
接下来,配置视频转码参数,调用prepare()接口。配置参数时要注意,prepare接口的入参avConfig中仅设置转码相关的配置参数,示例代码如下:
| let avConfig: media.AVTranscoderConfig = { audioBitrate: 100000, // 音频比特率 audioCodec: media.CodecMimeType.AUDIO_AAC, fileFormat: media.ContainerFormatType.CFT_MPEG_4A, videoBitrate: 2000000, // 视频比特率 videoCodec: media.CodecMimeType.VIDEO_AVC, videoFrameWidth: 640, // 视频分辨率的宽 videoFrameHeight: 480 // 视频分辨率的高 }; avTranscoder.prepare(avConfig).then(() => { console.log('Invoke prepare succeeded.'); }, (err) => { console.error(`Invoke prepare failed, code is ${err.code}, message is ${err.message}`); }); |
完成上述配置后,就可以调用start()方法开始转码:
| avTranscoder.start(); |
通过以上步骤,利用 AVCodec Kit 提供的AVTranscoder类及其相关 API,就能够在 HarmonyOS 应用中实现视频格式的转换。对于音频格式转换,原理和步骤类似,只需根据音频的特点和需求,合理配置相关参数即可。
(三)多媒体文件编辑功能
HarmonyOS 提供了一系列 API 来实现多媒体文件的剪辑、合并等编辑操作。以视频剪辑为例,我们可以利用AVTranscoder类结合时间轴的概念来实现。通过设置起始时间和结束时间,AVTranscoder可以从原始视频中提取出指定时间段的内容,从而实现剪辑功能。例如,假设我们要剪辑一个视频,从第 5 秒开始,到第 10 秒结束,代码实现如下:
| // 设置转码参数时,添加剪辑相关参数 let avConfig: media.AVTranscoderConfig = { // 其他转码参数... startTime: 5000, // 起始时间,单位毫秒 endTime: 10000 // 结束时间,单位毫秒 }; // 配置转码参数 avTranscoder.prepare(avConfig).then(() => { // 开始转码,此时输出的视频即为剪辑后的视频 avTranscoder.start(); }, (err) => { console.error(`Invoke prepare failed, code is ${err.code}, message is ${err.message}`); }); |
对于视频合并操作,可以通过将多个视频的轨道数据进行合并来实现。首先,需要使用解封装器(如AVDemuxer)将每个视频文件解封装,获取其视频流数据和音频流数据。然后,将这些流数据按照一定的顺序合并到一个新的视频轨道和音频轨道中。最后,使用封装器(如AVMuxer)将合并后的轨道数据封装成一个新的视频文件。在 HarmonyOS 中,虽然没有直接提供一个简单的合并 API,但通过组合使用这些音视频处理类,可以实现视频合并的功能。具体实现过程涉及到对音视频数据的读取、处理和重新封装,需要开发者具备一定的音视频处理知识和编程技巧。音频文件的剪辑和合并操作原理与视频类似,也是通过对音频数据的读取、处理和重新封装来实现。例如,音频剪辑可以通过解封装音频文件,定位到指定的时间点,读取该时间范围内的音频数据,然后重新封装成新的音频文件;音频合并则是将多个音频文件的音频数据依次合并到一个新的音频文件中。通过这些 API 和操作,开发者可以在 HarmonyOS 应用中实现丰富的多媒体文件编辑功能,满足用户对多媒体内容个性化处理的需求。
七、常见问题与解决方案
在 HarmonyOS 应用开发中,实现音频和视频功能时可能会遇到各种问题,下面为大家列举一些常见问题及解决方案:
格式不支持:当使用 MediaPlayer 播放音频或视频时,可能会遇到不支持的文件格式,导致无法播放。解决方案是在播放前对文件格式进行检查和转换。可以通过查询 HarmonyOS 支持的多媒体格式列表,确认文件格式是否在支持范围内。对于不支持的格式,利用 AVCodec Kit 提供的转码工具和 API,将其转换为支持的格式。例如,将 FLAC 音频格式转换为 MP3 格式,将 AVI 视频格式转换为 MP4 格式等。
权限不足:在进行音频录制、视频录制或访问多媒体文件时,如果权限不足,会导致操作失败。对于权限问题,首先要在module.json5文件中正确声明所需权限,如麦克风权限(ohos.permission.MICROPHONE)、相机权限(ohos.permission.CAMERA)、读取音频文件权限(ohos.permission.READ_AUDIO)等 。然后在运行时,使用abilityAccessCtrl模块中的requestPermissionsFromUser方法动态请求用户授权。在请求授权时,要向用户清晰地解释申请权限的原因,提高用户授权的成功率。同时,在权限申请成功或失败时,要进行相应的逻辑处理,比如权限申请失败时,提示用户开启权限的方法,引导用户进行操作。
资源冲突:当多个组件同时使用多媒体资源时,可能会出现资源冲突的情况。例如,同时有两个音频播放任务在竞争音频输出设备,或者视频录制和相机预览同时占用相机资源等。为了避免资源冲突,在开发过程中,要合理规划多媒体资源的使用。可以使用单例模式来管理多媒体资源,确保同一时间只有一个组件能够访问和使用特定的资源。例如,创建一个音频播放管理器类,使用单例模式来控制音频播放的实例,当有新的音频播放请求时,先检查当前是否有正在播放的音频,如果有,则暂停或停止当前播放,再进行新的播放操作。在进行视频录制和相机预览等操作时,也要进行资源的协调和管理,避免冲突的发生。
八、总结与展望
在 HarmonyOS 应用开发中,实现音频和视频功能为应用带来了丰富的交互体验和更广阔的应用场景。通过 MediaPlayer、AVRecorder、Camera 等关键 API,我们能够轻松实现音频和视频的播放、录制功能,并且利用 AVCodec Kit 等工具完成多媒体文件的格式转换和编辑操作。在这个过程中,我们需要关注权限申请、资源管理以及各种异常情况的处理,以确保应用的稳定性和可靠性。
随着技术的不断发展,未来多媒体开发的趋势将更加注重用户体验的提升、跨平台兼容性以及与新兴技术的融合。例如,随着 5G 技术的普及,高清、流畅的视频流传输将成为常态,这对视频播放和录制功能提出了更高的要求;人工智能技术在多媒体内容分析、智能编辑等方面的应用也将日益广泛;虚拟现实(VR)和增强现实(AR)技术与多媒体的结合,将为用户带来沉浸式的体验,开辟新的应用领域。
希望本文能够为大家在 HarmonyOS 多媒体开发的道路上提供有价值的参考和帮助。相信大家在实践过程中,能够不断探索和创新,开发出更多功能强大、体验优秀的多媒体应用。
相关文章:
HarmonyOS学习第18天:多媒体功能全解析
一、开篇引入 在当今数字化时代,多媒体已经深度融入我们的日常生活。无论是在工作中通过视频会议进行沟通协作,还是在学习时借助在线课程的音频讲解加深理解,亦或是在休闲时光用手机播放音乐放松身心、观看视频打发时间,多媒体功…...
在rocklinux里面批量部署安装rocklinx9
部署三台Rockylinux9服务器 实验要求 1. 自动安装ubuntu server20以上版本 2. 自动部署三台Rockylinux9服务器,最小化安装,安装基础包,并设定国内源,设静态IP 实验步骤 安装软件 # yum源必须有epel源 # dnf install -y epel-re…...
Manus:成为AI Agent领域的标杆
一、引言 官网:Manus 随着人工智能技术的飞速发展,AI Agent(智能体)作为人工智能领域的重要分支,正逐渐从概念走向现实,并在各行各业展现出巨大的应用潜力。在众多AI Agent产品中,Manus以其独…...
【Java开发指南 | 第三十四篇】IDEA没有Java Enterprise——解决方法
读者可订阅专栏:Java开发指南 |【CSDN秋说】 文章目录 1、新建Java项目2、单击项目名,并连续按两次shift键3、在搜索栏搜索"添加框架支持"4、勾选Web应用程序5、最终界面6、添加Tomcat 1、新建Java项目 2、单击项目名,并连续按两次…...
WinForm模态与非模态窗体
1、模态窗体 1)定义: 模态窗体是指当窗体显示时,用户必须先关闭该窗体,才能继续与应用程序的其他部分进行交互。 2)特点: 窗体以模态方式显示时,会阻塞主窗体的操作。用户必须处理完模态窗体上…...
静态时序分析:SDC约束命令set_ideal_network详解
相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 set_ideal_network命令可以将当前设计中的一组端口或引脚标记为理想网络源(设置端口或引脚对象的ideal_network_source属性为true)&#…...
【学习方法】技术开发者的提问智慧:如何高效获得解答?
技术开发者的提问智慧:如何高效获得解答? 在技术开发过程中,每个人都会遇到无法解决的问题。此时,我们通常会向团队、社区或论坛求助。然而,为什么有些人的问题能迅速得到解答,而有些人的问题却石沉大海&a…...
C++:入门详解(关于C与C++基本差别)
目录 一.C的第一个程序 二.命名空间(namespace) 1.命名空间的定义与使用: (1)命名空间里可以定义变量,函数,结构体等多种类型 (2)命名空间调用(…...
服务器上的nginx因漏洞扫描需要升级
前言 最近客户联系说nginx存在安全漏洞 F5 Nginx 安全漏洞(CVE-2024-7347) F5Nginx是美国F5公司的一款轻量级Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like协议下发行。F5 Nginx存在安全漏洞,该漏洞源于可能允许攻击者使用特制的…...
1688商品列表商品详情API接口全面解析
1688作为中国领先的B2B电子商务平台,汇聚了海量的商品资源,为商家和采购商提供了丰富的交易机会。为了更方便地获取和利用这些商品信息,1688平台提供了商品列表API接口,允许第三方开发者通过编程方式获取平台上的商品列表数据。本…...
【爬虫】开篇词
一、网络爬虫概述 二、网络爬虫的应用场景 三、爬虫的痛点 四、需要掌握哪些技术? 在这个信息爆炸的时代,如何高效地获取和处理海量数据成为一项核心技能。无论是数据分析、商业情报、学术研究,还是人工智能训练,网络爬虫&…...
如何在SpringBoot中灵活使用异步事件?
在现代的应用开发中,事件驱动的架构越来越受到欢迎。当我们在使用SpringBoot时,了解如何实现异步事件变得尤为重要。通过事件机制,我们能够在系统中实现松耦合的组件,让不同模块之间能够有效沟通,而无需直接依赖。本文…...
S19文件格式详解:汽车ECU软件升级中的核心镜像格式
文章目录 引言一、S19文件格式的起源与概述二、S19文件的核心结构三、S19在汽车ECU升级中的应用场景四、S19与其他格式的对比五、S19文件实例解析六、工具链支持与安全考量七、未来趋势与挑战结语引言 在汽车电子控制单元(ECU)的软件升级过程中,S19文件(也称为Motorola S-…...
git安装(windows)+vscode配置
安装git for windows在使用 Git 之前,建议设置全局的用户名称和电子邮件地址,这样每次提交代码时就可以自动关联您的身份信息。设置一次后,您无需每次都输入这些信息,Git 将自动使用您配置的全局用户信息。如果需要针对特定项目使…...
Python性能优化面试题及参考答案
目录 解释字典与列表在查找操作中的时间复杂度差异,如何利用哈希表特性提升性能? 为什么在只读场景下使用元组(tuple)比列表(list)更高效? 如何用 collections.deque 优化频繁的队列插入 / 删除操作? defaultdict 相比普通字典在哪些场景下能减少冗余代码并提升效率…...
【十四】Golang 接口
💢欢迎来到张胤尘的开源技术站 💥开源如江河,汇聚众志成。代码似星辰,照亮行征程。开源精神长,传承永不忘。携手共前行,未来更辉煌💥 文章目录 接口接口定义接口初始化接口嵌套空接口存储任意类…...
ngx_openssl_create_conf
ngx_openssl_create_conf 声明在 src\event\ngx_event_openssl.c static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); 定义在 src\event\ngx_event_openssl.c static void * ngx_openssl_create_conf(ngx_cycle_t *cycle) {ngx_openssl_conf_t *oscf;oscf ngx_…...
54-WLAN 无线局域网配置方案-三层
一、网络拓扑说明 本 WLAN 网络由交换机(LSW1)、无线控制器(AC1)、无线接入点(AP1\2)以及无线客户端(STA1)组成。 用途VLANAC100AP200业务300 二、设备配置 二、设备配置 &#x…...
JVM 类加载原理之双亲委派机制(JDK8版本)
对 Java 程序的运行过程而言,类的加载依赖类加载器完成,而在 Java 默认的类加载器又分为启动类加载器、扩展类加载器和应用程序类加载器三种,但是一个类通常仅仅需要被加载一次即可,双亲委派机制即规定各个类该被何种类加载器加载…...
Mysql快速学习——《一》: Mysql的基础架构
了解mysql的基础架构, 理解大概的实现思想, 更有利与我们知之所以然, 是我们学习mysql起来思路更清晰, 效率更高. 思维导图: mysql 基础架构 mysql基础架构.png 1. 连接器 Mysql作为服务器,一个客户端的Sql连接过来就需要分配一个线程进行处理,这个线程…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
