Compare commits

...

6 Commits

Author SHA1 Message Date
wangzhongjie
5efb9cd735 fix: 视频优化 2025-06-09 16:21:42 +08:00
wangzhongjie
796c4c4171 fix: 视频优化 2025-06-09 16:10:13 +08:00
wangzhongjie
690f28381f fix: 视频优化 2025-06-06 13:37:15 +08:00
wangzhongjie
36490c4797 fix: 优化重构结构 2025-06-05 14:59:10 +08:00
wangzhongjie
d06f1c1d5a fix: 视频界面设置重构 2025-06-05 14:46:39 +08:00
wangzhongjie
a0a93fd347 fix: 提交一个声明文件 2025-06-05 09:43:18 +08:00
12 changed files with 753 additions and 624 deletions

View File

@ -3,7 +3,7 @@ import { carConfigurationInfo, uploadExamCarCheckResult } from '../api/checkCar'
import TopLogo from './compontents/TopLogo';
import testNapi from '@ohos.hiserialsdk';
import { DwMapData, PassData, RealNumData, StackValueData, WarnFlagData, WarnFlagTipData } from '../mock';
import { BaseInfoType, RouteParamsType } from '../model/Common';
import { RouteParamsType } from '../model/Common';
import { CarCheckDataType, CarConfigurationParams, CarInfoType } from '../model';
import { BusinessError } from '@ohos.base';
import { voiceService } from '../utils/Voice';
@ -149,12 +149,12 @@ struct Index {
this.checkListCopy = JSON.parse(JSON.stringify(this.checkList))
return
}
const baseInfo = AppStorage.get<BaseInfoType>("baseInfo")
const deviceNo = AppStorage.get<string>("deviceNo")
//模拟真实数据
const param: CarConfigurationParams = {
body: {
"carIdString": this.carInfo.carId || "", //考车ID
"deviceNo": baseInfo?.deviceNo || ""
"deviceNo": deviceNo || ""
}
}

View File

@ -76,17 +76,11 @@ struct Index {
}
async onPageShow(): Promise<void> {
this.baseInfo = AppStorage.get<BaseInfoType>('baseInfo')!
await UseAuth(this.context)
this.avPlayer.playAudio(['welcome.wav'])
this.baseInfo = AppStorage.get<BaseInfoType>('baseInfo')!
this.initParams()
AppStorage.setOrCreate('singlePlay', false)
this.context.resourceManager.getRawFileContent("welcome.wav")
.then(() => {
this.avPlayer.playAudio(['welcome.wav'])
})
.catch((error: BusinessError) => {
console.log("getRawFileContent promise error is " + error);
});
this.num = 0
AppStorage.setOrCreate('lsh', '1111111111111')
}

View File

@ -126,7 +126,6 @@ struct Index {
const folderPath = await fileUtil.initFolder(`/config`);
fileUtil.addFile(`${folderPath}/deviceNo.txt`, JSON.stringify(param))
AppStorage.setOrCreate<string>('deviceNo', this.ip)
// upDateTableByArray('DeviceInfoTable', [{ deviceId: this.ip }])
registrationDeviceNo(param).then((res: ApiResponseType) => {
if (res.registrationDeviceNoRsp && res.registrationDeviceNoRsp.head &&
res.registrationDeviceNoRsp.head.resultCode == '0') {

View File

@ -1,631 +1,243 @@
import common from '@ohos.app.ability.common';
import promptAction from '@ohos.promptAction';
import { GlobalConfig } from '../config/index';
import { VideoConfigData } from '../mock';
import { CommonType, RecordHandleType, VideoConfig, VideoItemType } from '../model';
import { endRecordVideo, startRecordVideo, takePhoto } from '../utils/Video';
import FileUtils from '../utils/FileUtils';
import TopLogo from './compontents/TopLogo';
import { VideoConfigData } from '../mock'
import { RecordHandleType, VideoConfig, VideoItemType } from '../model'
import FileUtils from '../utils/FileUtils'
import HeaderComponent from './compontents/Header'
import VideoConfigComponent from './VideoConfig/Config'
import common from '@ohos.app.ability.common'
import { GlobalConfig } from '../config'
import Prompt from '@system.prompt'
import { endRecordVideo, startRecordVideo, takePhoto } from '../utils/Video'
@Entry
@Component
struct Index {
@State ratio: number = 1700 / 960
@State videoSrc: string = 'rtsp://admin12345qwe@192.168.36.94:554/h264/ch3/main/av_stream'
@State previewUri: Resource = $r('app.media.2_nor')
struct VideoConfigPage {
@State videoConfig: VideoConfig = VideoConfigData
@State oldVideoConfig: VideoConfig = VideoConfigData
@State curRate: PlaybackSpeed = PlaybackSpeed.Speed_Forward_1_00_X
@State inputFontSize: number = 10 //12
@State rocordHandleObj: RecordHandleType = {
@State openFlag: boolean = true
// 是否自动播放
@State isAutoPlay: boolean = true
@State showControls: boolean = false
// 弹窗
dialogController: CustomDialogController = new CustomDialogController({
builder: VideoConfigComponent({
videoConfig: this.videoConfig,
back: () => {
console.log("返回")
this.videoConfig = this.oldVideoConfig
},
save: (config: VideoConfig) => {
console.log("保存", config)
this.videoConfig = config
this.saveVideoConfig()
},
}),
alignment: DialogAlignment.Bottom,
customStyle: true
})
@State recordHandleObj: RecordHandleType = {
rocord_handle1: 0,
rocord_handle2: 0,
rocord_handle3: 0,
rocord_handle4: 0
}
@State isAutoPlay: boolean = true
@State showFlag: boolean = false
@State videoStartFlag: boolean = false
@State takePhotoFlag: boolean = false
@State showControls: boolean = false
@State @Watch('outClick') outFlag: boolean = false;
@State oldParam: VideoConfig = VideoConfigData
@State param: VideoConfig = VideoConfigData
@State openFlag: boolean = true
@State lsArr: Array<CommonType> = [
{ key: '第一路' },
{ key: '第二路' },
{ key: '第三路' },
{ key: '第四路' },
]
// 文件操作
private fileUtil!: FileUtils
private context = getContext(this) as common.UIAbilityContext;
// 视频控制器
private controller1: VideoController = new VideoController()
private controller2: VideoController = new VideoController()
private controller3: VideoController = new VideoController()
private controller4: VideoController = new VideoController()
@State videoArr: Array<VideoItemType> = [
// 视频列表
@State videoList: Array<VideoItemType> = [
{ td: 'td1', controller: this.controller1, src: '' },
{ td: 'td2', controller: this.controller2, src: '' },
{ td: 'td3', controller: this.controller3, src: '' },
{ td: 'td4', controller: this.controller4, src: '' },
]
private inputController: TextInputController = new TextInputController()
aboutToAppear(): void {
this.fileUtil = new FileUtils(this.context)
this.readVideoConfig()
}
// 读取视频配置
async readVideoConfig() {
const data = await this.fileUtil.readFile(GlobalConfig.commonFileWriteAddress + '/config/config3.txt');
this.videoConfig = JSON.parse(data)
this.oldVideoConfig = JSON.parse(data);
}
// 保存视频配置
async saveVideoConfig() {
const folderPath = await this.fileUtil.initFolder(`/config`);
console.log(this.oldVideoConfig.videoNum, this.videoConfig.videoNum, "查看")
this.fileUtil.addFile(`${folderPath}/config3.txt`, JSON.stringify(this.videoConfig))
}
async getFileHandleCode(td: number): Promise<Boolean> {
return new Promise(async (resolve, reject) => {
try {
const record_handle = await startRecordVideo(this.videoConfig, td, this.context, 'lp')
Reflect.set(this.recordHandleObj, 'rocord_handle' + td, record_handle)
resolve(true)
} catch (error) {
reject(false)
}
})
}
build() {
Column() {
TopLogo({ outFlag: $outFlag })
Flex({ justifyContent: FlexAlign.SpaceBetween }) {
Flex({ wrap: FlexWrap.Wrap, direction: FlexDirection.Row }) {
ForEach(this.videoArr, (item: VideoItemType) => {
Video({
src: this.openFlag ?
// `rtsp://${this.param.userName}:${this.param.pwd}@${this.param.ip}:${this.param.port}/h264/ch${this.param[item.td]}/main/av_stream` : '',
`rtsp://${this.param.userName}:${this.param.pwd}@${this.param.ip}:${this.param.port}/h264/ch${Reflect.get(this.param,
'port')}/main/av_stream` : '',
currentProgressRate: this.curRate,
controller: item.controller
})
.width(200 * this.ratio)
.muted(true)
.height(180 * this.ratio)
.autoPlay(this.isAutoPlay)
.controls(this.showControls)
.margin(10 * this.ratio)
Flex({
justifyContent: FlexAlign.SpaceBetween,
direction: FlexDirection.Column
}) {
HeaderComponent({
shortLogo: true,
shouBackArea: true
})
Flex() {
// 左边视频监控区域
Flex({
wrap: FlexWrap.Wrap
}) {
ForEach(this.videoList, (item: VideoItemType) => {
Row() {
Video({
src: this.openFlag ?
`rtsp://${this.videoConfig.userName}:${this.videoConfig.pwd}@${this.videoConfig.ip}:${this.videoConfig.port}/h264/ch${Reflect.get(this.videoConfig,
'port')}/main/av_stream` : '',
currentProgressRate: this.curRate,
controller: item.controller
})
.muted(true)
.autoPlay(this.isAutoPlay)
.controls(this.showControls)
.width("100%")
.height("100%")
}.width("50%").height("50%").border({
color: "red",
width: 1
}).padding(20)
})
}.margin({ top: 37 * this.ratio, left: 30 * this.ratio }).width(600 * this.ratio).height(600 * this.ratio)
}.width("70%").height("100%").padding(40)
// 右边视频配置区域
Column() {
Image($r('app.media.shezhi'))
.width(215 * this.ratio)
.height(64 * this.ratio)
.margin({ bottom: 10 * this.ratio })
.onClick(() => {
this.showFlag = true
})
Image($r('app.media.luxiang'))
.width(215 * this.ratio)
.height(64 * this.ratio)
.margin({ bottom: 10 * this.ratio })
.onClick(async () => {
if (!this.param.videoRecord4 && !this.param.videoRecord3 && !this.param.videoRecord2 &&
!this.param.videoRecord1) {
promptAction.showToast({
// 设置
ButtonComponent({
type: "0",
click: () => {
this.dialogController.open()
}
})
// 录像
ButtonComponent({
type: "1",
click: async () => {
if (!this.videoConfig.videoRecord4 && !this.videoConfig.videoRecord3 && !this.videoConfig.videoRecord2 && !this.videoConfig.videoRecord1) {
Prompt.showToast({
message: '请选择录像通道',
duration: 3000
})
return
}
if (this.videoStartFlag) {
return
}
if (this.rocordHandleObj.rocord_handle1 || this.rocordHandleObj.rocord_handle2 ||
this.rocordHandleObj.rocord_handle3 || this.rocordHandleObj.rocord_handle4) {
return
}
this.videoStartFlag = true
// for (let i = 1; i <= 4; i++) {
// if (this.param['videoRecord'+i]) {
// await this.getfilehandleCode(i)
// }
// }
for (let i = 1; i <= 4; i++) {
if (Reflect.get(this.param, 'videoRecord' + i)) {
await this.getFileHandleCode(i);
if (Reflect.get(this.videoConfig, 'videoRecord' + i)) {
await this.getFileHandleCode(i)
}
}
this.videoStartFlag = false
promptAction.showToast({
message: '录像开始',
// 录像开始
Prompt.showToast({
message: '录像开始!',
duration: 3000
})
})
Image($r('app.media.tingzhi'))
.width(215 * this.ratio)
.height(64 * this.ratio)
.margin({ bottom: 10 * this.ratio })
.onClick(() => {
// for (let i = 1; i <= 4; i++) {
// console.log('rocord_handle', i, JSON.stringify(this.rocordHandleObj))
// if (this.rocordHandleObj['rocord_handle'+i]) {
// endRecordVideo(this.rocordHandleObj)
// this.rocordHandleObj['rocord_handle'+i] = 0
// }
// }
}
})
// 停止
ButtonComponent({
type: "2",
click: () => {
for (let i = 1; i <= 4; i++) {
if (Reflect.get(this.rocordHandleObj, 'rocord_handle' + i)) {
Reflect.set(this.rocordHandleObj, 'rocord_handle' + i, 0);
if (Reflect.get(this.recordHandleObj, 'rocord_handle' + i) !== 0) {
endRecordVideo(Reflect.get(this.recordHandleObj, 'rocord_handle' + i))
Reflect.set(this.recordHandleObj, 'rocord_handle' + i, 0)
}
}
endRecordVideo(this.rocordHandleObj);
promptAction.showToast({
message: '录像结束',
Prompt.showToast({
message: '录像已停止!',
duration: 3000
})
})
Image($r('app.media.zhuatu'))
.width(215 * this.ratio)
.height(64 * this.ratio)
.onClick(async () => {
}
})
// 抓图
ButtonComponent({
type: "3",
click: async () => {
const arr = ['1', '2', '3', '4']
if (!arr.includes(this.param.pztd||"")) {
promptAction.showToast({
if (!arr.includes(this.videoConfig.pztd)) {
Prompt.showToast({
message: '请填写正确的拍照通道',
duration: 3000
})
return
}
if (this.takePhotoFlag) {
return
}
this.takePhotoFlag = true
try {
await takePhoto(this.param, this.context, 'pz/')
promptAction.showToast({
await takePhoto(this.videoConfig, this.context, 'pz/')
Prompt.showToast({
message: '抓图完成',
duration: 3000
})
this.takePhotoFlag = false
// }).catch((err) => {
// console.log('daihai err: ' + err)
// })
} catch (error) {
console.log('daihai', error)
console.log("抓图失败", error)
}
})
.margin({ bottom: 10 * this.ratio })
}.margin({ right: 38 * this.ratio, top: 110 * this.ratio })
}.backgroundColor('#1A1A1A')
// Column() {
// Text('提示信息:')
// .fontSize(18 * this.ratio)
// .fontColor('#fff')
// .margin({ left: 29 * this.ratio, top: 13 * this.ratio })
// .align(Alignment.Start)
// .width('100%')
// }.width('100%').height(70 * this.ratio).backgroundColor('#333230').position({ x: 0, y: 470 * this.ratio })
if (this.showFlag) {
Column() {
Flex({ justifyContent: FlexAlign.SpaceBetween }) {
Text('视频参数设置')
.width('100%')
.align(Alignment.Start)
.fontSize(24 * this.ratio)
.fontColor('#333333')
.margin({ left: 20 * this.ratio, top: 22 * this.ratio })
Row() {
Image($r('app.media.fh')).width(117 * this.ratio).height(50 * this.ratio).onClick(() => {
// this.oldParam = JSON.parse(JSON.stringify(this.param))
this.param = JSON.parse(JSON.stringify(this.oldParam))
this.showFlag = false
})
Image($r('app.media.bc'))
.width(117 * this.ratio)
.height(50 * this.ratio)
.margin({ left: 15 * this.ratio })
.onClick(() => {
console.log('111111')
this.writeConfig()
})
}.margin({ right: 135 * this.ratio, top: 10 * this.ratio })
}
Column() {
Row() {
Text('视频路数').fontColor('#333333').fontSize(16 * this.ratio).margin({ left: 13 * this.ratio })
TextInput({ text: this.param.videoNum, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(0)
.width(34 * this.ratio)
.height(26 * this.ratio)
.padding({ top: 0, bottom: 0 })
.margin({ left: 10 * this.ratio })
.fontSize(this.inputFontSize * this.ratio)
.onChange((value) => {
this.param.spls = value
})
Column() {
Image($r('app.media.shang')).width(15 * this.ratio).height(15 * this.ratio).onClick(() => {
this.param.videoNum = (parseInt(this.param?.videoNum||"") + 1).toString()
})
Image($r('app.media.xia')).width(15 * this.ratio).height(15 * this.ratio).onClick(() => {
this.param.videoNum = (parseInt(this.param.videoNum||"") - 1).toString()
})
}.margin({ left: 5 * this.ratio, right: 10 * this.ratio })
Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
.select(this.param.faceFlag || false)
.width(22 * this.ratio)
.height(22 * this.ratio)
.onChange((value: boolean) => {
this.param.faceFlag = value
console.info('Checkbox1 change is' + value)
})
Text('启用人脸比对').fontColor('#333333').fontSize(16 * this.ratio)
TextInput({ text: this.param.rlls, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(0)
.width(34 * this.ratio)
.fontSize(this.inputFontSize * this.ratio)
.height(26 * this.ratio)
.padding({ top: 0, bottom: 0 })
.margin({ left: 10 * this.ratio, right: 15 * this.ratio })
.onChange((value) => {
this.param.rlls = value
})
Text('过程拍照通道').fontColor('#333333').fontSize(16 * this.ratio)
TextInput({ text: this.param.pztd, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(0)
.padding({ top: 0, bottom: 0 })
.width(34 * this.ratio)
.height(26 * this.ratio)
.fontSize(this.inputFontSize * this.ratio)
.margin({ left: 10 * this.ratio, right: 15 * this.ratio })
.onChange((value: string) => {
this.param.pztd = value
})
Text('设备类型').fontColor('#333333').fontSize(16 * this.ratio)
TextInput({ text: '海康', controller: this.inputController })
.borderRadius(0)
.padding({ bottom: 0, top: 0 })
.width(67 * this.ratio)
.height(26 * this.ratio)
.fontSize(this.inputFontSize * this.ratio)
.margin({ left: 10 * this.ratio, right: 15 * this.ratio })
Text('视频遮挡报警阀值:').fontColor('#333333').fontSize(16 * this.ratio)
TextInput({ text: this.param.zdyz, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(0)
.padding({ top: 0, bottom: 0 })
.width(34 * this.ratio)
.height(26 * this.ratio)
.fontSize(this.inputFontSize * this.ratio)
.margin({ left: 10 * this.ratio, right: 15 * this.ratio })
.onChange((value: string) => {
this.param.zdyz = value
})
// Text('k').fontColor('#333333').fontSize(16 * this.ratio)
// Text('链接类型').fontColor('#333333').fontSize(16 * this.ratio)
// TextInput({ text: this.param.ljlx, controller: this.inputController })
// .type(InputType.Normal)
// .borderRadius(0)
// .padding({ top: 0, bottom: 0 })
// .width(34 * this.ratio)
// .height(26 * this.ratio)
// .fontSize(this.inputFontSize * this.ratio)
// .margin({ left: 10 * this.ratio, right: 5 * this.ratio })
}.width('100%').align(Alignment.Start).backgroundColor('#E5E3DF')
Row() {
Column() {
Row() {
Row() {
}.width(70 * this.ratio)
Text('IP地址')
.width(158 * this.ratio)
.fontSize(16 * this.ratio)
.fontColor('#666666')
.margin({ right: 10 * this.ratio })
.textAlign(TextAlign.Center)
Text('通道号')
.width(96 * this.ratio)
.fontSize(16 * this.ratio)
.fontColor('#666666')
.margin({ right: 10 * this.ratio })
.textAlign(TextAlign.Center)
Text('用户名')
.width(120 * this.ratio)
.fontSize(16 * this.ratio)
.fontColor('#666666')
.margin({ right: 10 * this.ratio })
.textAlign(TextAlign.Center)
Text('密码')
.width(120 * this.ratio)
.fontSize(16 * this.ratio)
.fontColor('#666666')
.margin({ right: 10 * this.ratio })
.textAlign(TextAlign.Center)
Text('端口号')
.width(60 * this.ratio)
.fontSize(16 * this.ratio)
.fontColor('#666666')
.margin({ right: 10 * this.ratio })
.textAlign(TextAlign.Center)
}
ForEach(this.lsArr, (item: CommonType, index) => {
Row() {
Text(item.key)
.width(70 * this.ratio)
.fontColor('#333333')
.fontSize(16 * this.ratio)
.textAlign(TextAlign.Center)
TextInput({ text: this.param.ip, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(2)
.width(158 * this.ratio)
.height(26 * this.ratio)
.fontSize(this.inputFontSize * this.ratio)
.padding({ top: 0, bottom: 0 })
.margin({ right: 10 * this.ratio })
.onChange((value: string) => {
this.param.ip = value
})
// TextInput({ text: this.param['td'+(Number(index) + 1)], controller: this.inputController })
TextInput({
text: Reflect.get(this.param, 'td' + (Number(index) + 1)),
controller: this.inputController
})
.type(InputType.Normal)
.borderRadius(2)
.width(96 * this.ratio)
.height(26 * this.ratio)
.padding({ top: 0, bottom: 0 })
.margin({ right: 10 * this.ratio })
.fontSize(this.inputFontSize * this.ratio)
.onChange((value: string) => {
// this.param['td'+(Number(index) + 1)] = value
Reflect.set(this.param, 'td' + (Number(index) + 1), value);
})
TextInput({ text: this.param.userName, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(2)
.width(120 * this.ratio)
.height(26 * this.ratio)
.margin({ right: 10 * this.ratio })
.padding({ top: 0, bottom: 0 })
.fontSize(this.inputFontSize * this.ratio)
.onChange((value: string) => {
this.param.userName = value
})
TextInput({ text: this.param.pwd, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(2)
.width(120 * this.ratio)
.height(26 * this.ratio)
.fontSize(this.inputFontSize * this.ratio)
.margin({ right: 10 * this.ratio })
.padding({ top: 0, bottom: 0 })
.onChange((value: string) => {
this.param.pwd = value
})
TextInput({ text: this.param.port, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(2)
.width(60 * this.ratio)
.height(26 * this.ratio)
.padding({ top: 0, bottom: 0 })
.margin({ right: 10 * this.ratio })
.fontSize(this.inputFontSize * this.ratio)
.onChange((value: string) => {
this.param.port = value
})
}.margin({ top: 10 * this.ratio })
})
}.width(672 * this.ratio).height(174 * this.ratio).backgroundColor('#EDEBE8')
Column() {
Row() {
Text('录像范围').fontSize(16 * this.ratio).fontColor('#333333').margin({ top: 10 * this.ratio })
Row() {
Column() {
Row() {
Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
.select(this.param.videoRecord1 || false)
.width(22 * this.ratio)
.height(22 * this.ratio)
.onChange((value: boolean) => {
this.param.videoRecord1 = value
console.info('Checkbox1 change is' + value)
})
Text('一路').fontSize(16 * this.ratio).fontColor('#333333')
Checkbox({ name: 'checkbox2', group: 'checkboxGroup' })
.select(this.param.videoRecord2 || false)
.width(22 * this.ratio)
.height(22 * this.ratio)
.onChange((value: boolean) => {
this.param.videoRecord2 = value
console.info('Checkbox1 change is' + value)
})
Text('二路').fontSize(16 * this.ratio).fontColor('#333333')
}
Row() {
Checkbox({ name: 'checkbox3', group: 'checkboxGroup' })
.select(this.param.videoRecord3 || false)
.width(22 * this.ratio)
.height(22 * this.ratio)
.onChange((value: boolean) => {
this.param.videoRecord3 = value
console.info('Checkbox1 change is' + value)
})
Text('三路').fontSize(16 * this.ratio).fontColor('#333333')
Checkbox({ name: 'checkbox4', group: 'checkboxGroup' })
.select(this.param.videoRecord4 || false)
.width(22 * this.ratio)
.height(22 * this.ratio)
.onChange((value: boolean) => {
this.param.videoRecord4 = value
console.info('Checkbox1 change is' + value)
})
Text('四路').fontSize(16 * this.ratio).fontColor('#333333')
}
}
}
}.alignItems(VerticalAlign.Top).justifyContent(FlexAlign.Start)
Row() {
Text('视频遮挡').fontSize(16 * this.ratio).fontColor('#333333').margin({ top: 10 * this.ratio })
Row() {
Column() {
Row() {
Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
.select(this.param.spzd1 || false)
.width(22 * this.ratio)
.height(22 * this.ratio)
.onChange((value: boolean) => {
this.param.spzd1 = value
})
Text('一路').fontSize(16 * this.ratio).fontColor('#333333')
Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
.select(this.param.spzd2 || false)
.width(22 * this.ratio)
.height(22 * this.ratio)
.onChange((value: boolean) => {
this.param.spzd2 = value
})
Text('二路').fontSize(16 * this.ratio).fontColor('#333333')
}
Row() {
Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
.select(this.param.spzd3 || false)
.width(22 * this.ratio)
.height(22 * this.ratio)
.onChange((value: boolean) => {
this.param.spzd3 = value
})
Text('三路').fontSize(16 * this.ratio).fontColor('#333333')
Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
.select(this.param.spzd4 || false)
.width(22 * this.ratio)
.height(22 * this.ratio)
.onChange((value: boolean) => {
this.param.spzd4 = value
})
Text('四路').fontSize(16 * this.ratio).fontColor('#333333')
}
}
}
}.alignItems(VerticalAlign.Top).justifyContent(FlexAlign.Start)
}.width(267 * this.ratio).height(174 * this.ratio).margin({ left: 7 * this.ratio, top: 10 * this.ratio })
}.width('100%')
Row() {
Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
.select(this.param.shuiying || false)
.width(22 * this.ratio)
.height(22 * this.ratio)
.onChange((value: boolean) => {
console.info('Checkbox1 change is' + value)
})
Text('照片叠加文字').fontColor('#333333').fontSize(16 * this.ratio)
Text('位置').fontColor('#333333').fontSize(16 * this.ratio).margin({ left: 24 * this.ratio })
TextInput({ text: this.param.wz, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(0)
.width(79 * this.ratio)
.height(26 * this.ratio)
.padding({ top: 0, bottom: 0 })
.fontSize(this.inputFontSize * this.ratio)
.margin({ left: 10 * this.ratio })
Text('叠加内容').fontColor('#333333').fontSize(16 * this.ratio).margin({ left: 24 * this.ratio })
TextInput({ text: this.param.text1, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(0)
.width(79 * this.ratio)
.height(26 * this.ratio)
.padding({ top: 0, bottom: 0 })
.margin({ left: 10 * this.ratio })
.fontSize(this.inputFontSize * this.ratio)
Text('+').fontColor('#333333').fontSize(16 * this.ratio).margin({ left: 5 * this.ratio })
TextInput({ text: this.param.text2, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(0)
.width(79 * this.ratio)
.height(26 * this.ratio)
.padding({ top: 0, bottom: 0 })
.margin({ left: 10 * this.ratio })
.fontSize(this.inputFontSize * this.ratio)
Text('+').fontColor('#333333').fontSize(16 * this.ratio).margin({ left: 5 * this.ratio })
TextInput({ text: this.param.text3, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(0)
.width(79 * this.ratio)
.padding({ top: 0, bottom: 0 })
.height(26 * this.ratio)
.margin({ left: 10 * this.ratio })
.fontSize(this.inputFontSize * this.ratio)
Text('分隔符').fontColor('#333333').fontSize(16 * this.ratio).margin({ left: 24 * this.ratio })
TextInput({ text: this.param.dolt, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(0)
.width(67 * this.ratio)
.height(26 * this.ratio)
.margin({ left: 10 * this.ratio })
.padding({ top: 0, bottom: 0 })
.fontSize(this.inputFontSize * this.ratio)
Text('文字大小').fontColor('#333333').fontSize(16 * this.ratio).margin({ left: 24 * this.ratio })
TextInput({ text: this.param.fontSize, controller: this.inputController })
.type(InputType.Normal)
.borderRadius(0)
.width(34 * this.ratio)
.height(26 * this.ratio)
.margin({ left: 10 * this.ratio })
.fontSize(this.inputFontSize * this.ratio)
.padding({ top: 0, bottom: 0 })
}.width('100%').align(Alignment.Start).backgroundColor('#E5E3DF')
}.width(946 * this.ratio).height(336 * this.ratio).backgroundColor('#E5E3DF').margin({ top: 13 * this.ratio })
}
})
}
.width('100%')
.height(395 * this.ratio)
.backgroundColor('#CCC4B8')
.position({ x: 0, y: 146 * this.ratio })
.border({ radius: { topLeft: 24 * this.ratio, topRight: 24 * this.ratio } })
.width("30%")
.height("100%")
.padding(100)
.justifyContent(FlexAlign.End)
.alignItems(HorizontalAlign.Center)
}
}.backgroundColor('#1A1A1A').width('100%').height('100%')
}
aboutToAppear() {
// this.ratio = globalThis.ratio
this.ratio = AppStorage.get<number>('ratio') || 0
this.openFlag = true
const fileUtil = new FileUtils(this.context)
this.fileUtil = fileUtil
this.getVideoConfig()
}
async getFileHandleCode(td: number): Promise<void> {
return new Promise(async (resolve, reject) => {
const record_handle = await startRecordVideo(this.param, td, this.context, 'lp')
// this.rocordHandleObj['rocord_handle'+td] = record_handle
Reflect.set(this.rocordHandleObj, 'rocord_handle' + td, record_handle);
resolve()
}
.width("100%")
.height("100%")
.backgroundImage($r('app.media.index_bg'))
.backgroundImageSize({
width: "100%",
height: "100%"
})
}
}
@Component
struct ButtonComponent {
@State type: string = "0"
click?: () => void
// 使用映射表来存储类型与图片资源的对应关系
private readonly imageResources: Record<string, Resource> = {
"3": $r('app.media.zhuatu'),
"2": $r('app.media.tingzhi'),
"1": $r('app.media.luxiang'),
"0": $r('app.media.shezhi')
}
outClick() {
this.openFlag = false
}
async onPageShow() {
}
async getVideoConfig() {
const data = await this.fileUtil.readFile(GlobalConfig.commonFileWriteAddress + '/config/config3.txt');
this.oldParam = JSON.parse(data)
this.param = JSON.parse(data)
}
async writeConfig() {
this.oldParam = JSON.parse(JSON.stringify(this.param))
this.videoArr = JSON.parse(JSON.stringify(this.videoArr))
const folderPath = await this.fileUtil.initFolder(`/config`);
this.fileUtil.addFile(`${folderPath}/config3.txt`, JSON.stringify(this.param))
this.showFlag = false
build() {
Image(this.imageResources[this.type])
.width(500)
.height(100)
.objectFit(ImageFit.Contain)
.margin({
top: 20
})
.onClick(() => {
this.click && this.click()
})
}
}

View File

@ -0,0 +1,17 @@
@Component
export default struct CheckboxComponent {
@State value: boolean = false
change?: (value: boolean) => void
build() {
Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
.width(40)
.height(40)
.select(this.value)
.onChange((value: boolean) => {
console.info('Checkbox1 change is' + value)
this.value = value;
this.change?.(value);
})
}
}

View File

@ -0,0 +1,342 @@
import { VideoConfig } from '../../model';
import CheckboxComponent from './CheckBox';
import LabelComponent from './Label';
import RouteSettingComponent from './RouteSetting';
import TextInputComponent from './TextInput';
@CustomDialog
export default struct VideoConfigComponent {
@Prop videoConfig: VideoConfig
save?: (value: VideoConfig) => void;
back?: () => void;
private controller?: CustomDialogController;
build() {
Column() {
Flex({
justifyContent: FlexAlign.SpaceBetween,
alignItems: ItemAlign.Center
}) {
Text("视频参数设置").fontSize(30).fontWeight(FontWeight.Bold)
Row() {
Image($r('app.media.fh')).height(55).objectFit(ImageFit.Contain).onClick(() => {
this.controller?.close()
this.back?.();
})
Image($r('app.media.bc')).height(55).objectFit(ImageFit.Contain).onClick(() => {
this.controller?.close()
this.save?.(this.videoConfig);
})
}
}.padding({
left: 20,
right: 20
}).height(60)
Column() {
// 第一行
Row() {
LabelComponent({
text: "视频路数",
})
TextInputComponent({
value: this.videoConfig?.videoNum,
change: (value: string) => {
this.videoConfig.videoNum = value;
}
})
partitionSpace()
CheckboxComponent({
value: this.videoConfig.faceFlag,
change: (value: boolean) => {
this.videoConfig.faceFlag = value;
}
})
LabelComponent({
text: "启用人脸对比"
})
TextInputComponent({
value: this.videoConfig.rlls,
change: (value: string) => {
this.videoConfig.rlls = value;
}
})
partitionSpace()
LabelComponent({
text: "拍照通道"
})
TextInputComponent({
value: this.videoConfig.pztd,
change: (value: string) => {
this.videoConfig.pztd = value;
}
})
partitionSpace()
LabelComponent({
text: "设备类型"
})
TextInputComponent({
value: "海康"
})
partitionSpace()
LabelComponent({
text: "视频遮挡报警阈值"
})
TextInputComponent({
value: this.videoConfig.zdyz,
change: (value: string) => {
this.videoConfig.zdyz = value;
}
})
}
// 第二行
Flex() {
Column() {
titleComponent()
blockComponent({
label: "第一路",
num: 1,
videoConfig: this.videoConfig,
change: (value: VideoConfig) => {
this.videoConfig = value;
}
})
blockComponent({
label: "第二路",
num: 2,
videoConfig: this.videoConfig,
change: (value: VideoConfig) => {
this.videoConfig = value;
}
})
blockComponent({
label: "第三路",
num: 3,
videoConfig: this.videoConfig,
change: (value: VideoConfig) => {
this.videoConfig = value;
}
})
blockComponent({
label: "第四路",
num: 4,
videoConfig: this.videoConfig,
change: (value: VideoConfig) => {
this.videoConfig = value;
}
})
}.width("75%")
Column() {
RouteSettingComponent({
videoConfig: this.videoConfig,
change: (value: VideoConfig) => {
this.videoConfig = value;
},
})
RouteSettingComponent({
title: "视频遮挡",
videoConfig: this.videoConfig,
change: (value: VideoConfig) => {
this.videoConfig = value;
},
})
}.width("25%")
.padding({
top: 50
})
}
// 第三行
Row() {
CheckboxComponent({
value: this.videoConfig.shuiying,
change: (value: boolean) => {
this.videoConfig.shuiying = value;
}
})
LabelComponent({
text: "照片叠加文字"
})
partitionSpace()
LabelComponent({
text: "位置"
})
TextInputComponent({
value: this.videoConfig.wz,
change: (value: string) => {
this.videoConfig.wz = value;
}
})
partitionSpace()
LabelComponent({
text: "叠加内容"
})
TextInputComponent({
value: this.videoConfig.text1,
change: (value: string) => {
this.videoConfig.text1 = value;
}
})
LabelComponent({
text: "+"
})
TextInputComponent({
value: this.videoConfig.text2,
change: (value: string) => {
this.videoConfig.text2 = value;
}
})
LabelComponent({
text: "+"
})
TextInputComponent({
value: this.videoConfig.text3,
change: (value: string) => {
this.videoConfig.text3 = value;
}
})
partitionSpace()
partitionSpace()
LabelComponent({
text: "分隔符"
})
TextInputComponent({
value: this.videoConfig.dolt,
change: (value: string) => {
this.videoConfig.dolt = value;
}
})
partitionSpace()
LabelComponent({
text: "文字大小"
})
TextInputComponent({
value: this.videoConfig.fontSize,
change: (value: string) => {
this.videoConfig.fontSize = value;
}
})
}.margin({
top: 20
})
}.backgroundColor("#E3E2DE").width("100%").height("100%").margin(10)
}.height("60%")
.width("100%")
.backgroundColor("#C9C4B9")
.borderRadius({
topLeft: 20,
topRight: 20
})
}
}
@Component
struct blockComponent {
@State label: string = "第一路"
@Prop videoConfig: VideoConfig
@State num: number = 1
change?: (value: VideoConfig) => void;
build() {
Row() {
Row() {
Text(this.label).fontSize(20)
}.width(100)
// IP地址
TextInputComponent({
value: this.videoConfig.ip,
widthValue: 200,
change: (value: string) => {
this.videoConfig.ip = value;
this.change?.(this.videoConfig);
}
})
// 通道号
TextInputComponent({
value: Reflect.get(this.videoConfig, `td${this.num}`) || '',
widthValue: 100,
change: (value: string) => {
Reflect.set(this.videoConfig, `td${this.num}`, value);
this.change?.(this.videoConfig);
}
})
// 用户名
TextInputComponent({
value: this.videoConfig.userName,
widthValue: 200,
change: (value: string) => {
this.videoConfig.userName = value;
this.change?.(this.videoConfig);
}
})
// 密码
TextInputComponent({
value: this.videoConfig.pwd,
widthValue: 200,
change: (value: string) => {
this.videoConfig.pwd = value;
this.change?.(this.videoConfig);
}
})
// 端口号
TextInputComponent({
value: this.videoConfig.port,
widthValue: 100,
change: (value: string) => {
this.videoConfig.port = value;
this.change?.(this.videoConfig);
}
})
}.margin({
top: 10
})
}
}
@Component
struct partitionSpace {
build() {
Row() {
}.width(20).height(20)
}
}
@Component
struct titleComponent {
build() {
Row() {
Row() {
}.width(100)
Row() {
Text("IP地址")
}.width(200).margin({ left: 15, right: 15 }).alignItems(VerticalAlign.Center).justifyContent(FlexAlign.Center)
Row() {
Text("通道号")
}.width(100).margin({ left: 15, right: 15 }).alignItems(VerticalAlign.Center).justifyContent(FlexAlign.Center)
Row() {
Text("用户名")
}.width(200).margin({ left: 15, right: 15 }).alignItems(VerticalAlign.Center).justifyContent(FlexAlign.Center)
Row() {
Text("密码")
}.width(200).margin({ left: 15, right: 15 }).alignItems(VerticalAlign.Center).justifyContent(FlexAlign.Center)
Row() {
Text("端口号")
}.width(100).margin({ left: 15, right: 15 }).alignItems(VerticalAlign.Center).justifyContent(FlexAlign.Center)
}.height(50)
}
}

View File

@ -0,0 +1,11 @@
@Component
export default struct LabelComponent {
@State text: string = "视频路数"
build() {
Text(this.text)
.fontSize(24)
.fontWeight(FontWeight.Bold)
}
}

View File

@ -0,0 +1,129 @@
import { VideoConfig } from '../../model'
import CheckboxComponent from './CheckBox'
import LabelComponent from './Label'
@Component
export default struct RouteSettingComponent {
@State title: string = "录像范围"
@Prop videoConfig: VideoConfig
change?: (value: VideoConfig) => void
build() {
Row() {
Row() {
Text(this.title).fontSize(24)
}.width(150).justifyContent(FlexAlign.End)
Flex({
wrap: FlexWrap.Wrap
}) {
if (this.title === "录像范围") {
Row() {
LabelComponent({
text: "一路"
})
CheckboxComponent({
value: this.videoConfig.videoRecord1,
change: (value: boolean) => {
this.videoConfig.videoRecord1 = value;
this.change?.(this.videoConfig);
}
})
}
Row() {
LabelComponent({
text: "二路"
})
CheckboxComponent({
value: this.videoConfig.videoRecord2,
change: (value: boolean) => {
this.videoConfig.videoRecord2 = value;
this.change?.(this.videoConfig);
}
})
}
Row() {
LabelComponent({
text: "三路"
})
CheckboxComponent({
value: this.videoConfig.videoRecord3,
change: (value: boolean) => {
this.videoConfig.videoRecord3 = value;
this.change?.(this.videoConfig);
}
})
}
Row() {
LabelComponent({
text: "四路"
})
CheckboxComponent({
value: this.videoConfig.videoRecord4,
change: (value: boolean) => {
this.videoConfig.videoRecord4 = value;
this.change?.(this.videoConfig);
}
})
}
} else {
Row() {
LabelComponent({
text: "一路"
})
CheckboxComponent({
value: this.videoConfig.spzd1,
change: (value: boolean) => {
this.videoConfig.spzd1 = value;
this.change?.(this.videoConfig);
}
})
}
Row() {
LabelComponent({
text: "二路"
})
CheckboxComponent({
value: this.videoConfig.spzd2,
change: (value: boolean) => {
this.videoConfig.spzd2 = value;
this.change?.(this.videoConfig);
}
})
}
Row() {
LabelComponent({
text: "三路"
})
CheckboxComponent({
value: this.videoConfig.spzd3,
change: (value: boolean) => {
this.videoConfig.spzd3 = value;
this.change?.(this.videoConfig);
}
})
}
Row() {
LabelComponent({
text: "四路"
})
CheckboxComponent({
value: this.videoConfig.spzd4,
change: (value: boolean) => {
this.videoConfig.spzd4 = value;
this.change?.(this.videoConfig);
}
})
}
}
}.width(300)
}.justifyContent(FlexAlign.Start)
}
}

View File

@ -0,0 +1,22 @@
@Component
export default struct TextInputComponent {
@Prop value: string
@State widthValue: number = 120
change?: (value: string) => void
build() {
TextInput({
text: this.value,
})
.type(InputType.Normal)
.borderRadius(0)
.fontSize(20)
.width(this.widthValue)
.height(50)
.margin({ left: 15, right: 15 })
.onChange((value) => {
this.value = value;
this.change?.(value);
})
}
}

View File

@ -16,6 +16,8 @@ export default struct HeaderComponent {
@State shortLogo: boolean = true
@State time: string = ""
@State timer: number = 0
@State shouBackArea: boolean = false
@State customizeReturnArea: boolean = false
// 自定义返回区域
@BuilderParam
backAreaBuilder?: () => void = this.customBuilder
@ -66,45 +68,45 @@ export default struct HeaderComponent {
left: 10
})
}
if (this.shortLogo) {
if (this.backAreaBuilder) {
this.backAreaBuilder()
} else {
if (this.customizeReturnArea && this.backAreaBuilder) {
this.backAreaBuilder()
} else {
if (this.shouBackArea) {
Image($r('app.media.topB_back')).height(100).onClick(() => {
router.back()
})
}
} else {
Row() {
Image($r('app.media.btn_setting')).height(100).onClick(() => {
console.log("点击设置")
router.pushUrl({
url: 'pages/Settings',
}, router.RouterMode.Single);
})
Image($r('app.media.btn_back')).height(100).onClick(() => {
Prompt.showDialog({
title: "提示",
message: "确定要退出吗?",
buttons: [
{
text: '取消',
color: '#666666'
},
{
text: '退出',
color: '#666666'
}
],
success: () => {
(getContext(this) as common.UIAbilityContext).terminateSelf()
}
} else {
Row() {
Image($r('app.media.btn_setting')).height(100).onClick(() => {
console.log("点击设置")
router.pushUrl({
url: 'pages/Settings',
}, router.RouterMode.Single);
})
})
Image($r('app.media.btn_back')).height(100).onClick(() => {
Prompt.showDialog({
title: "提示",
message: "确定要退出吗?",
buttons: [
{
text: '取消',
color: '#666666'
},
{
text: '退出',
color: '#666666'
}
],
success: () => {
(getContext(this) as common.UIAbilityContext).terminateSelf()
}
})
})
}
}
}
}
}.width("100%")
.height(100)
}
}

View File

@ -29,6 +29,7 @@ export default class VoiceAnnounce {
async playAudio(urls: string[], shit?: boolean, callbackFn?: Function) {
const isStopped = this.isStopped;
const queue = this.queue;
console.log(TAG, "播放")
const tempUrls: Queue[] = urls.map((url, index) => {
const callback: Function | undefined = (index === urls.length - 1) ? callbackFn : undefined
@ -94,6 +95,7 @@ class AVPlayer {
// 以下为使用资源管理接口获取打包在HAP内的媒体资源文件并通过fdSrc属性进行播放示例
async play(name: string, callback: Function): Promise<boolean> {
console.log(TAG, 'play => 开始播放语音')
try {
//检查SD中的语音
// console.info('surenjun name',name)
@ -128,7 +130,7 @@ class AVPlayer {
this.endCallback = callback
this.avPlayer = await media.createAVPlayer();
return new Promise(async (resolve, reject) => {
await this.setAVPlayerCallback(() => {
this.setAVPlayerCallback(() => {
resolve(true)
});
try {
@ -148,8 +150,7 @@ class AVPlayer {
async queryFile(displayName: string): Promise<mediaLibrary.FileAsset> {
return new Promise(async (resolve, reject) => {
const context = AppStorage.get<common.UIAbilityContext>('context')
const mediaLib = mediaLibrary.getMediaLibrary(context);
const mediaLib = mediaLibrary.getMediaLibrary(this.context);
let fileResult = await mediaLib.getFileAssets({
selections: `media_type=? AND display_name = ?`,
selectionArgs: [`${mediaLibrary.MediaType.AUDIO}`, displayName],
@ -197,14 +198,14 @@ class AVPlayer {
case 'prepared': // prepare调用成功后上报该状态机
console.info(TAG, '播放资源播放')
this.avPlayer!.play();
// this.voiceStatus = 'playing'
// this.voiceStatus = 'playing'
break;
case 'playing': // play成功调用后触发该状态机上报
break;
case 'paused': // pause成功调用后触发该状态机上报
break;
case 'completed': // 播放结束后触发该状态机上报
// this.voiceStatus = 'completed'
// this.voiceStatus = 'completed'
this.avPlayer!.stop(); //调用播放结束接口
break;
case 'stopped': // stop接口成功调用后触发该状态机上报

View File

@ -34,8 +34,8 @@ const FILE_ASSET_FETCH_COLUMNS = [
export async function startRecordVideo(param: VideoConfig, td: number, context: common.UIAbilityContext, dir: string,
path?: string,
index?: number): Promise<number> {
path?: string,
index?: number): Promise<number> {
return new Promise(async (resolve, reject) => {
let video_uri = `rtsp://${param.userName}:${param.pwd}@${param.ip}:${param.port}/h264/ch${td}/main/av_stream`;
if (rtsp_server != null) {
@ -168,7 +168,7 @@ export async function takePhoto(
if (flag === 0) {
return new Promise<takePhotoParam>((resolve, reject) => {
rtsp_server.detectVideoSnapshotSize(video_uri, fileName, false,(err: BusinessError, snapResult: record.RtspResult) => {
rtsp_server.detectVideoSnapshotSize(video_uri, fileName, false, (err: BusinessError, snapResult: record.RtspResult) => {
if (snapResult && snapResult.errorCode === 0) {
const result: takePhotoParam = {
fileSize: snapResult.fileSize,
@ -197,7 +197,7 @@ export async function takePhoto(
const time = GetCurrentTime();
const date = time.split(' ')[0];
const dirName = dir || date;
rtsp_server.detectVideoSnapshotSize(video_uri, fileName, true,(err: BusinessError, snapResult: record.RtspResult) => {
rtsp_server.getVideoSnapshot(context, video_uri, fileName, dirName, true, (err: BusinessError, snapResult: record.RtspResult) => {
if (snapResult && snapResult.errorCode === 0) {
const result: takePhotoParam = {
fileSize: snapResult.fileSize,
@ -234,7 +234,7 @@ export async function deleteAllFileByPiC(dirName: string, type = 1, context: com
}
export async function deleteAllVideos(context: common.UIAbilityContext, type: photoAccessHelper.AlbumType,
subType: photoAccessHelper.AlbumSubtype): Promise<void> {
subType: photoAccessHelper.AlbumSubtype): Promise<void> {
let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.Album> | null = null;
let count = 0;
try {