Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
car-controlserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wenzhongjian
car-controlserver
Commits
2eb47ff8
Commit
2eb47ff8
authored
Jul 01, 2026
by
957dd
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
未修改完第二次保存提交
parent
edadce9a
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
1576 additions
and
254 deletions
+1576
-254
.gitignore
.gitignore
+3
-0
CMakeLists.txt
CMakeLists.txt
+0
-5
device_wifi_change.c
app/device_change/device_wifi_change.c
+6
-5
device_wifi_manager.c
app/device_change/device_wifi_manager.c
+665
-145
pthread_open.c
app/main/pthread_open.c
+5
-1
main
build/main
+0
-0
hardware_iic.c
drivers/gpio/hardware_iic.c
+1
-1
audioplay.c
drivers/sensors/audioplay.c
+69
-8
CMakeLists.txt
modules/CMakeLists.txt
+1
-0
mqtt_infor_handle.c
modules/mqtt/mqtt_infor_handle.c
+5
-3
mqtt_init.c
modules/mqtt/mqtt_init.c
+3
-0
runtime_deps.c
modules/runtime_deps/runtime_deps.c
+2
-7
mpp_h264_source.c
modules/webrtcpush/mpp_h264_source.c
+504
-0
mpp_h264_source.h
modules/webrtcpush/mpp_h264_source.h
+25
-0
rtc_client.c
modules/webrtcpush/rtc_client.c
+206
-48
webrtcpush_config.h
modules/webrtcpush/webrtcpush_config.h
+15
-15
webrtcpush_run.c
modules/webrtcpush/webrtcpush_run.c
+4
-3
wifi_save_11_mqtt.jsonl
test/wifi_save_11_mqtt.jsonl
+0
-11
rtc_test_720p.h264
testdata/rtc_test_720p.h264
+0
-0
pacinghandler.hpp
third_party/libdatachannel/include/rtc/pacinghandler.hpp
+1
-0
rtc.h
third_party/libdatachannel/include/rtc/rtc.h
+4
-0
rtcpnackresponder.hpp
third_party/libdatachannel/include/rtc/rtcpnackresponder.hpp
+4
-0
capi.cpp
third_party/libdatachannel/src/capi.cpp
+34
-1
pacinghandler.cpp
third_party/libdatachannel/src/pacinghandler.cpp
+9
-0
rtcpnackresponder.cpp
third_party/libdatachannel/src/rtcpnackresponder.cpp
+9
-0
zlog.conf
zlog.conf
+1
-1
No files found.
.gitignore
View file @
2eb47ff8
...
@@ -4,6 +4,9 @@ deviceback
...
@@ -4,6 +4,9 @@ deviceback
Deviceld.txt
Deviceld.txt
main
main
# 本地日报/笔记,不提交
docs/
# CMake / build 输出(默认全部忽略)
# CMake / build 输出(默认全部忽略)
build/*
build/*
build/.ninja_*
build/.ninja_*
...
...
CMakeLists.txt
View file @
2eb47ff8
...
@@ -95,9 +95,4 @@ target_compile_options(main PRIVATE ${WEBRTCPUSH_CFLAGS})
...
@@ -95,9 +95,4 @@ target_compile_options(main PRIVATE ${WEBRTCPUSH_CFLAGS})
# 安装规则
# 安装规则
install
(
TARGETS main DESTINATION bin
)
install
(
TARGETS main DESTINATION bin
)
# carstart.service 使用 /home/orangepi/car/master/main
add_custom_command
(
TARGET main POST_BUILD
COMMAND
${
CMAKE_COMMAND
}
-E copy $<TARGET_FILE:main>
${
CMAKE_SOURCE_DIR
}
/main
COMMENT
"Copy main to project root for carstart.service"
)
enable_testing
()
enable_testing
()
app/device_change/device_wifi_change.c
View file @
2eb47ff8
...
@@ -8,7 +8,8 @@ static char s_wifi_last_password[SSID_MAX_LEN]= {0} ;
...
@@ -8,7 +8,8 @@ static char s_wifi_last_password[SSID_MAX_LEN]= {0} ;
//获取当前连接wifi
//获取当前连接wifi
void
get_current_wifi
()
{
void
get_current_wifi
()
{
//显示已连接代码
g_current_ssid
[
0
]
=
'\0'
;
FILE
*
fp
=
popen
(
"nmcli -t -f ACTIVE,SSID dev wifi"
,
"r"
);
FILE
*
fp
=
popen
(
"nmcli -t -f ACTIVE,SSID dev wifi"
,
"r"
);
if
(
!
fp
)
{
if
(
!
fp
)
{
my_zlog_error
(
"{
\"
error
\"
:
\"
无法执行扫描命令
\"
}"
);
my_zlog_error
(
"{
\"
error
\"
:
\"
无法执行扫描命令
\"
}"
);
...
@@ -413,7 +414,7 @@ void connect_wifi_jking() {
...
@@ -413,7 +414,7 @@ void connect_wifi_jking() {
if
(
system
(
"sudo reboot"
)
!=
0
){
//重启香橙派
if
(
system
(
"sudo reboot"
)
!=
0
){
//重启香橙派
my_zlog_error
(
"reboot fail"
);
my_zlog_error
(
"reboot fail"
);
}
}
my_zlog_
debug
(
"重启成功"
);
my_zlog_
info
(
"重启成功"
);
}
}
}
}
...
@@ -500,7 +501,7 @@ void wifi_change_recmqtt(cJSON *body){
...
@@ -500,7 +501,7 @@ void wifi_change_recmqtt(cJSON *body){
if
(
system
(
"sudo reboot"
)
!=
0
){
//重启香橙派
if
(
system
(
"sudo reboot"
)
!=
0
){
//重启香橙派
my_zlog_error
(
"reboot fail"
);
my_zlog_error
(
"reboot fail"
);
}
}
my_zlog_
debug
(
"重启成功"
);
my_zlog_
info
(
"重启成功"
);
}
}
if
(
ret
==
0
){
if
(
ret
==
0
){
if
(
can_access_internet
()
==
0
){
if
(
can_access_internet
()
==
0
){
...
@@ -508,7 +509,7 @@ void wifi_change_recmqtt(cJSON *body){
...
@@ -508,7 +509,7 @@ void wifi_change_recmqtt(cJSON *body){
my_zlog_debug
(
"ssid:%S"
,
g_current_ssid
);
my_zlog_debug
(
"ssid:%S"
,
g_current_ssid
);
if
(
system
(
"sudo reboot"
)
!=
0
){
//重启香橙派
if
(
system
(
"sudo reboot"
)
!=
0
){
//重启香橙派
my_zlog_error
(
"reboot fail"
);
my_zlog_error
(
"reboot fail"
);
my_zlog_
debug
(
"重启成功"
);
my_zlog_
info
(
"重启成功"
);
}
else
{
}
else
{
delete_wifi_conf
();
delete_wifi_conf
();
if
(
change_wifi_connect
(
default_SSID
,
default_password
)
==
0
)
delete_wifi_by_ssid
(
g_current_ssid
);
if
(
change_wifi_connect
(
default_SSID
,
default_password
)
==
0
)
delete_wifi_by_ssid
(
g_current_ssid
);
...
@@ -528,7 +529,7 @@ void wifi_change_recmqtt(cJSON *body){
...
@@ -528,7 +529,7 @@ void wifi_change_recmqtt(cJSON *body){
if
(
system
(
"sudo reboot"
)
!=
0
){
//重启香橙派
if
(
system
(
"sudo reboot"
)
!=
0
){
//重启香橙派
my_zlog_error
(
"reboot fail"
);
my_zlog_error
(
"reboot fail"
);
}
}
my_zlog_
debug
(
"重启成功"
);
my_zlog_
info
(
"重启成功"
);
}
}
}
}
...
...
app/device_change/device_wifi_manager.c
View file @
2eb47ff8
...
@@ -3,98 +3,245 @@
...
@@ -3,98 +3,245 @@
#include "device_wifi_manager.h"
#include "device_wifi_manager.h"
#include "modules_common.h"
#include "modules_common.h"
#define MAX_WIFI_ENTRIES
11
#define MAX_WIFI_ENTRIES
32
#define MAX_SAVED_WIFI_SLOTS 10
#define MAX_SAVED_WIFI_SLOTS 10
#define SSID_MAX_LENGTH 64
#define SSID_MAX_LENGTH 64
#define WIFI_PSK_MAX_LENGTH 63
#define NM_CONN_DIR "/etc/NetworkManager/system-connections/"
#define NM_CONN_DIR "/etc/NetworkManager/system-connections/"
static
ThreadPool_t
*
s_wifi_pool
=
NULL
;
static
pthread_mutex_t
s_wifi_pool_mutex
=
PTHREAD_MUTEX_INITIALIZER
;
typedef
struct
{
typedef
struct
{
char
ssid
[
SSID_MAX_LENGTH
];
char
ssid
[
SSID_MAX_LENGTH
];
time_t
mtime
;
time_t
mtime
;
}
saved_wifi_entry_t
;
}
saved_wifi_entry_t
;
typedef
struct
{
char
ssid
[
SSID_MAX_LENGTH
];
char
password
[
WIFI_PSK_MAX_LENGTH
+
1
];
}
wifi_save_task_t
;
typedef
struct
{
char
ssid
[
SSID_MAX_LENGTH
];
}
wifi_delete_task_t
;
static
void
wifi_save_worker
(
void
*
arg
);
static
void
wifi_delete_worker
(
void
*
arg
);
static
int
submit_wifi_task
(
void
(
*
worker
)(
void
*
),
void
*
arg
,
const
char
*
what
);
static
int
is_default_wifi_ssid
(
const
char
*
ssid
)
{
static
int
is_default_wifi_ssid
(
const
char
*
ssid
)
{
return
ssid
!=
NULL
&&
strcmp
(
ssid
,
default_SSID
)
==
0
;
return
ssid
!=
NULL
&&
strcmp
(
ssid
,
default_SSID
)
==
0
;
}
}
static
int
connection_name_from_filename
(
const
char
*
filename
,
char
*
ssid_out
,
size_t
ssid_size
)
{
static
int
entry_index_by_name
(
const
saved_wifi_entry_t
*
entries
,
int
count
,
const
char
*
dot
=
strrchr
(
filename
,
'.'
);
const
char
*
name
)
{
if
(
dot
&&
strcmp
(
dot
,
".nmconnection"
)
==
0
)
{
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
size_t
name_len
=
(
size_t
)(
dot
-
filename
);
if
(
strcmp
(
entries
[
i
].
ssid
,
name
)
==
0
)
if
(
name_len
==
0
||
name_len
>=
ssid_size
)
{
return
i
;
return
-
1
;
}
strncpy
(
ssid_out
,
filename
,
name_len
);
ssid_out
[
name_len
]
=
'\0'
;
return
0
;
}
}
return
-
1
;
}
static
void
trim_line
(
char
*
line
)
{
size_t
n
;
if
(
!
line
)
return
;
n
=
strcspn
(
line
,
"
\r\n
"
);
line
[
n
]
=
'\0'
;
}
static
int
read_profile_id
(
const
char
*
filepath
,
char
*
id_out
,
size_t
id_size
)
{
FILE
*
fp
;
char
line
[
256
];
if
(
strlen
(
filename
)
>=
ssid_size
)
{
fp
=
fopen
(
filepath
,
"r"
);
if
(
!
fp
)
return
-
1
;
return
-
1
;
while
(
fgets
(
line
,
sizeof
(
line
),
fp
))
{
trim_line
(
line
);
if
(
strncmp
(
line
,
"id="
,
3
)
==
0
)
{
strncpy
(
id_out
,
line
+
3
,
id_size
-
1
);
id_out
[
id_size
-
1
]
=
'\0'
;
fclose
(
fp
);
return
id_out
[
0
]
?
0
:
-
1
;
}
}
}
strncpy
(
ssid_out
,
filename
,
ssid_size
-
1
);
ssid_out
[
ssid_size
-
1
]
=
'\0'
;
fclose
(
fp
)
;
return
0
;
return
-
1
;
}
}
static
int
is_wifi_nmconnection_file
(
const
char
*
filepath
)
{
static
int
is_wifi_profile_file
(
const
char
*
filepath
)
{
FILE
*
fp
=
fopen
(
filepath
,
"r"
);
FILE
*
fp
;
if
(
!
fp
)
{
char
line
[
256
];
fp
=
fopen
(
filepath
,
"r"
);
if
(
!
fp
)
return
0
;
return
0
;
}
char
line
[
256
];
int
is_wifi
=
0
;
while
(
fgets
(
line
,
sizeof
(
line
),
fp
))
{
while
(
fgets
(
line
,
sizeof
(
line
),
fp
))
{
if
(
strncmp
(
line
,
"type=wifi"
,
9
)
==
0
||
trim_line
(
line
);
strncmp
(
line
,
"type=802-11-wireless"
,
20
)
==
0
)
{
if
(
strcmp
(
line
,
"type=wifi"
)
==
0
||
is_wifi
=
1
;
strcmp
(
line
,
"type=802-11-wireless"
)
==
0
)
{
break
;
fclose
(
fp
);
return
1
;
}
}
}
}
fclose
(
fp
);
fclose
(
fp
);
return
is_wifi
;
return
0
;
}
}
static
int
wifi_connection_exists
(
const
char
*
ssid
)
{
static
int
is_wifi_connection_type
(
const
char
*
type
)
{
return
type
&&
(
strcmp
(
type
,
"802-11-wireless"
)
==
0
||
strcmp
(
type
,
"wifi"
)
==
0
);
}
/* 解析 nmcli -t 一行:支持 name:device:type 与 name:type 两种格式 */
static
int
parse_nmcli_connection_line
(
const
char
*
line
,
char
*
name
,
size_t
name_sz
,
char
*
device
,
size_t
dev_sz
,
char
*
type
,
size_t
type_sz
)
{
const
char
*
c1
;
const
char
*
c2
;
size_t
len
;
if
(
!
line
||
!
name
||
!
device
||
!
type
)
return
-
1
;
name
[
0
]
=
device
[
0
]
=
type
[
0
]
=
'\0'
;
c1
=
strchr
(
line
,
':'
);
if
(
!
c1
)
return
-
1
;
len
=
(
size_t
)(
c1
-
line
);
if
(
len
>=
name_sz
)
len
=
name_sz
-
1
;
memcpy
(
name
,
line
,
len
);
name
[
len
]
=
'\0'
;
c2
=
strchr
(
c1
+
1
,
':'
);
if
(
!
c2
)
{
strncpy
(
type
,
c1
+
1
,
type_sz
-
1
);
type
[
type_sz
-
1
]
=
'\0'
;
trim_line
(
type
);
return
0
;
}
len
=
(
size_t
)(
c2
-
c1
-
1
);
if
(
len
>=
dev_sz
)
len
=
dev_sz
-
1
;
memcpy
(
device
,
c1
+
1
,
len
);
device
[
len
]
=
'\0'
;
strncpy
(
type
,
c2
+
1
,
type_sz
-
1
);
type
[
type_sz
-
1
]
=
'\0'
;
trim_line
(
type
);
return
0
;
}
/* 无需读文件内容,stat 即可拿到 mtime(普通用户也可 stat root 600 文件) */
static
time_t
wifi_profile_mtime_from_disk
(
const
char
*
ssid
)
{
char
path
[
512
];
char
path
[
512
];
struct
stat
st
;
DIR
*
dir
;
struct
dirent
*
entry
;
time_t
oldest
=
0
;
size_t
ssid_len
;
if
(
!
ssid
||
!
ssid
[
0
])
return
0
;
snprintf
(
path
,
sizeof
(
path
),
"%s%s.nmconnection"
,
NM_CONN_DIR
,
ssid
);
snprintf
(
path
,
sizeof
(
path
),
"%s%s.nmconnection"
,
NM_CONN_DIR
,
ssid
);
return
access
(
path
,
F_OK
)
==
0
;
if
(
stat
(
path
,
&
st
)
==
0
)
oldest
=
st
.
st_mtime
;
ssid_len
=
strlen
(
ssid
);
dir
=
opendir
(
NM_CONN_DIR
);
if
(
!
dir
)
return
oldest
;
while
((
entry
=
readdir
(
dir
))
!=
NULL
)
{
if
(
!
strstr
(
entry
->
d_name
,
".nmconnection"
))
continue
;
if
(
strncmp
(
entry
->
d_name
,
ssid
,
ssid_len
)
!=
0
)
continue
;
if
(
entry
->
d_name
[
ssid_len
]
!=
'\0'
&&
entry
->
d_name
[
ssid_len
]
!=
'-'
&&
entry
->
d_name
[
ssid_len
]
!=
'.'
)
continue
;
snprintf
(
path
,
sizeof
(
path
),
"%s%s"
,
NM_CONN_DIR
,
entry
->
d_name
);
if
(
stat
(
path
,
&
st
)
==
0
&&
(
oldest
==
0
||
st
.
st_mtime
<
oldest
))
oldest
=
st
.
st_mtime
;
}
closedir
(
dir
);
return
oldest
;
}
}
static
int
collect_saved_wifi_entries
(
saved_wifi_entry_t
*
entries
,
int
max_count
)
{
static
void
merge_wifi_entry
(
saved_wifi_entry_t
*
entries
,
int
*
count
,
int
max_count
,
DIR
*
dir
=
opendir
(
NM_CONN_DIR
);
const
char
*
ssid
,
time_t
mtime
)
{
if
(
!
dir
)
{
int
idx
;
my_zlog_error
(
"无法打开NetworkManager配置目录"
);
time_t
mt
;
return
-
1
;
if
(
!
ssid
||
!
ssid
[
0
]
||
!
entries
||
!
count
)
return
;
idx
=
entry_index_by_name
(
entries
,
*
count
,
ssid
);
mt
=
mtime
>
0
?
mtime
:
wifi_profile_mtime_from_disk
(
ssid
);
if
(
idx
>=
0
)
{
if
(
mt
>
0
&&
(
entries
[
idx
].
mtime
==
0
||
mt
<
entries
[
idx
].
mtime
))
entries
[
idx
].
mtime
=
mt
;
return
;
}
}
if
(
*
count
>=
max_count
)
return
;
strncpy
(
entries
[
*
count
].
ssid
,
ssid
,
sizeof
(
entries
[
*
count
].
ssid
)
-
1
);
entries
[
*
count
].
ssid
[
sizeof
(
entries
[
*
count
].
ssid
)
-
1
]
=
'\0'
;
entries
[
*
count
].
mtime
=
mt
;
(
*
count
)
++
;
}
static
int
collect_saved_wifi_entries_from_files
(
saved_wifi_entry_t
*
entries
,
int
max_count
)
{
DIR
*
dir
;
struct
dirent
*
entry
;
struct
dirent
*
entry
;
int
count
=
0
;
int
count
=
0
;
dir
=
opendir
(
NM_CONN_DIR
);
if
(
!
dir
)
{
my_zlog_error
(
"无法打开NetworkManager配置目录"
);
return
-
1
;
}
while
((
entry
=
readdir
(
dir
))
!=
NULL
&&
count
<
max_count
)
{
while
((
entry
=
readdir
(
dir
))
!=
NULL
&&
count
<
max_count
)
{
if
(
entry
->
d_type
!=
DT_REG
&&
entry
->
d_type
!=
DT_UNKNOWN
)
{
char
filepath
[
512
];
continue
;
char
profile_id
[
SSID_MAX_LENGTH
]
;
}
struct
stat
st
;
char
ssid
[
SSID_MAX_LENGTH
];
if
(
entry
->
d_type
!=
DT_REG
&&
entry
->
d_type
!=
DT_UNKNOWN
)
if
(
connection_name_from_filename
(
entry
->
d_name
,
ssid
,
sizeof
(
ssid
))
!=
0
)
{
continue
;
if
(
!
strstr
(
entry
->
d_name
,
".nmconnection"
))
continue
;
continue
;
}
char
filepath
[
512
];
snprintf
(
filepath
,
sizeof
(
filepath
),
"%s%s"
,
NM_CONN_DIR
,
entry
->
d_name
);
snprintf
(
filepath
,
sizeof
(
filepath
),
"%s%s"
,
NM_CONN_DIR
,
entry
->
d_name
);
if
(
!
is_wifi_
nmconnection_file
(
filepath
))
{
if
(
!
is_wifi_
profile_file
(
filepath
))
continue
;
continue
;
}
if
(
read_profile_id
(
filepath
,
profile_id
,
sizeof
(
profile_id
))
!=
0
)
continue
;
struct
stat
st
;
if
(
stat
(
filepath
,
&
st
)
!=
0
)
if
(
stat
(
filepath
,
&
st
)
!=
0
)
{
continue
;
continue
;
{
int
idx
=
entry_index_by_name
(
entries
,
count
,
profile_id
);
if
(
idx
>=
0
)
{
if
(
st
.
st_mtime
<
entries
[
idx
].
mtime
)
entries
[
idx
].
mtime
=
st
.
st_mtime
;
continue
;
}
}
}
strncpy
(
entries
[
count
].
ssid
,
ss
id
,
sizeof
(
entries
[
count
].
ssid
)
-
1
);
strncpy
(
entries
[
count
].
ssid
,
profile_
id
,
sizeof
(
entries
[
count
].
ssid
)
-
1
);
entries
[
count
].
ssid
[
sizeof
(
entries
[
count
].
ssid
)
-
1
]
=
'\0'
;
entries
[
count
].
ssid
[
sizeof
(
entries
[
count
].
ssid
)
-
1
]
=
'\0'
;
entries
[
count
].
mtime
=
st
.
st_mtime
;
entries
[
count
].
mtime
=
st
.
st_mtime
;
count
++
;
count
++
;
...
@@ -104,94 +251,431 @@ static int collect_saved_wifi_entries(saved_wifi_entry_t *entries, int max_count
...
@@ -104,94 +251,431 @@ static int collect_saved_wifi_entries(saved_wifi_entry_t *entries, int max_count
return
count
;
return
count
;
}
}
/* 以 nmcli 列表为准,兼容 name:type 两列输出 */
static
int
collect_saved_wifi_entries_from_nmcli
(
saved_wifi_entry_t
*
entries
,
int
max_count
)
{
FILE
*
fp
;
char
line
[
256
];
char
name
[
SSID_MAX_LENGTH
];
char
device
[
64
];
char
conn_type
[
64
];
int
count
=
0
;
fp
=
popen
(
"nmcli -t -f NAME,TYPE connection show"
,
"r"
);
if
(
!
fp
)
return
0
;
while
(
fgets
(
line
,
sizeof
(
line
),
fp
)
&&
count
<
max_count
)
{
trim_line
(
line
);
if
(
!
line
[
0
])
continue
;
if
(
parse_nmcli_connection_line
(
line
,
name
,
sizeof
(
name
),
device
,
sizeof
(
device
),
conn_type
,
sizeof
(
conn_type
))
!=
0
)
continue
;
if
(
!
is_wifi_connection_type
(
conn_type
))
continue
;
{
int
idx
=
entry_index_by_name
(
entries
,
count
,
name
);
if
(
idx
>=
0
)
continue
;
}
strncpy
(
entries
[
count
].
ssid
,
name
,
sizeof
(
entries
[
count
].
ssid
)
-
1
);
entries
[
count
].
ssid
[
sizeof
(
entries
[
count
].
ssid
)
-
1
]
=
'\0'
;
entries
[
count
].
mtime
=
wifi_profile_mtime_from_disk
(
name
);
count
++
;
}
pclose
(
fp
);
return
count
;
}
static
int
collect_saved_wifi_entries
(
saved_wifi_entry_t
*
entries
,
int
max_count
)
{
saved_wifi_entry_t
file_entries
[
MAX_WIFI_ENTRIES
];
saved_wifi_entry_t
nmcli_entries
[
MAX_WIFI_ENTRIES
];
int
count
=
0
;
int
file_count
;
int
nmcli_count
;
file_count
=
collect_saved_wifi_entries_from_files
(
file_entries
,
MAX_WIFI_ENTRIES
);
if
(
file_count
<
0
)
file_count
=
0
;
for
(
int
i
=
0
;
i
<
file_count
;
i
++
)
merge_wifi_entry
(
entries
,
&
count
,
max_count
,
file_entries
[
i
].
ssid
,
file_entries
[
i
].
mtime
);
nmcli_count
=
collect_saved_wifi_entries_from_nmcli
(
nmcli_entries
,
MAX_WIFI_ENTRIES
);
for
(
int
i
=
0
;
i
<
nmcli_count
;
i
++
)
merge_wifi_entry
(
entries
,
&
count
,
max_count
,
nmcli_entries
[
i
].
ssid
,
nmcli_entries
[
i
].
mtime
);
return
count
;
}
static
int
nmcli_wifi_connection_exists
(
const
char
*
ssid
)
{
FILE
*
fp
;
char
line
[
256
];
char
name
[
SSID_MAX_LENGTH
];
char
device
[
64
];
char
conn_type
[
64
];
if
(
!
ssid
||
!
ssid
[
0
])
return
0
;
fp
=
popen
(
"nmcli -t -f NAME,TYPE connection show"
,
"r"
);
if
(
!
fp
)
return
0
;
while
(
fgets
(
line
,
sizeof
(
line
),
fp
))
{
trim_line
(
line
);
if
(
parse_nmcli_connection_line
(
line
,
name
,
sizeof
(
name
),
device
,
sizeof
(
device
),
conn_type
,
sizeof
(
conn_type
))
!=
0
)
continue
;
if
(
!
is_wifi_connection_type
(
conn_type
))
continue
;
if
(
strcmp
(
name
,
ssid
)
==
0
)
{
pclose
(
fp
);
return
1
;
}
}
pclose
(
fp
);
return
0
;
}
static
int
wifi_connection_exists
(
const
char
*
ssid
)
{
saved_wifi_entry_t
entries
[
MAX_WIFI_ENTRIES
];
int
count
=
collect_saved_wifi_entries
(
entries
,
MAX_WIFI_ENTRIES
);
if
(
count
<
0
)
return
nmcli_wifi_connection_exists
(
ssid
);
if
(
entry_index_by_name
(
entries
,
count
,
ssid
)
>=
0
)
return
1
;
return
nmcli_wifi_connection_exists
(
ssid
);
}
static
int
count_non_default_saved_wifi
(
void
)
{
static
int
count_non_default_saved_wifi
(
void
)
{
saved_wifi_entry_t
entries
[
MAX_WIFI_ENTRIES
];
saved_wifi_entry_t
entries
[
MAX_WIFI_ENTRIES
];
int
count
=
collect_saved_wifi_entries
(
entries
,
MAX_WIFI_ENTRIES
);
int
count
=
collect_saved_wifi_entries
(
entries
,
MAX_WIFI_ENTRIES
);
if
(
count
<
0
)
{
if
(
count
<
0
)
return
-
1
;
return
-
1
;
}
int
non_default_count
=
0
;
int
non_default_count
=
0
;
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
if
(
!
is_default_wifi_ssid
(
entries
[
i
].
ssid
))
{
if
(
!
is_default_wifi_ssid
(
entries
[
i
].
ssid
))
non_default_count
++
;
non_default_count
++
;
}
}
}
return
non_default_count
;
return
non_default_count
;
}
}
static
int
find_oldest_non_default_wifi
(
char
*
ssid_out
,
size_t
ssid_size
)
{
static
int
connection_is_active
(
const
char
*
name
)
{
saved_wifi_entry_t
entries
[
MAX_WIFI_ENTRIES
];
FILE
*
fp
;
int
count
=
collect_saved_wifi_entries
(
entries
,
MAX_WIFI_ENTRIES
);
char
line
[
256
];
if
(
count
<=
0
)
{
char
conn_name
[
SSID_MAX_LENGTH
];
return
-
1
;
char
device
[
64
];
char
conn_type
[
64
];
if
(
!
name
||
!
name
[
0
])
return
0
;
fp
=
popen
(
"nmcli -t -f NAME,DEVICE,TYPE connection show"
,
"r"
);
if
(
!
fp
)
return
0
;
while
(
fgets
(
line
,
sizeof
(
line
),
fp
))
{
trim_line
(
line
);
if
(
parse_nmcli_connection_line
(
line
,
conn_name
,
sizeof
(
conn_name
),
device
,
sizeof
(
device
),
conn_type
,
sizeof
(
conn_type
))
!=
0
)
continue
;
if
(
strcmp
(
conn_name
,
name
)
!=
0
)
continue
;
if
(
!
is_wifi_connection_type
(
conn_type
))
continue
;
pclose
(
fp
);
return
device
[
0
]
!=
'\0'
;
}
}
int
found
=
0
;
pclose
(
fp
);
time_t
oldest_mtime
=
0
;
return
0
;
}
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
/* wlan0 当前绑定的连接配置名,与 .nmconnection 里 id= 一致 */
if
(
is_default_wifi_ssid
(
entries
[
i
].
ssid
))
{
static
void
get_active_wifi_profile_name
(
char
*
name_out
,
size_t
name_size
)
{
FILE
*
fp
;
char
line
[
256
];
char
*
value
;
if
(
!
name_out
||
name_size
==
0
)
return
;
name_out
[
0
]
=
'\0'
;
fp
=
popen
(
"nmcli -t -f GENERAL.CONNECTION dev show wlan0"
,
"r"
);
if
(
!
fp
)
return
;
while
(
fgets
(
line
,
sizeof
(
line
),
fp
))
{
trim_line
(
line
);
if
(
strncmp
(
line
,
"GENERAL.CONNECTION:"
,
19
)
!=
0
)
continue
;
continue
;
}
value
=
line
+
19
;
if
(
!
found
||
entries
[
i
].
mtime
<
oldest_mtime
)
{
if
(
!
value
[
0
]
||
strcmp
(
value
,
"--"
)
==
0
)
oldest_mtime
=
entries
[
i
].
mtime
;
break
;
strncpy
(
ssid_out
,
entries
[
i
].
ssid
,
ssid_size
-
1
);
strncpy
(
name_out
,
value
,
name_size
-
1
);
ssid_out
[
ssid_size
-
1
]
=
'\0'
;
name_out
[
name_size
-
1
]
=
'\0'
;
found
=
1
;
break
;
}
}
}
return
found
?
0
:
-
1
;
pclose
(
fp
)
;
}
}
static
int
enforce_saved_wifi_limit
(
const
char
*
new_ssid
)
{
static
int
wifi_profile_is_in_use
(
const
char
*
ssid
)
{
if
(
is_default_wifi_ssid
(
new_ssid
)
||
wifi_connection_exists
(
new_ssid
))
{
char
active_profile
[
SSID_MAX_LENGTH
];
if
(
!
ssid
||
!
ssid
[
0
])
return
0
;
return
0
;
get_active_wifi_profile_name
(
active_profile
,
sizeof
(
active_profile
));
if
(
active_profile
[
0
]
&&
strcmp
(
ssid
,
active_profile
)
==
0
)
return
1
;
if
(
g_current_ssid
[
0
]
&&
strcmp
(
ssid
,
g_current_ssid
)
==
0
)
return
1
;
/* 仅当 DEVICE 非空才算在用,且兼容 name:type 两列输出 */
return
connection_is_active
(
ssid
)
!=
0
;
}
static
int
remove_wifi_profile_files
(
const
char
*
ssid
)
{
DIR
*
dir
;
struct
dirent
*
entry
;
char
filepath
[
512
];
char
profile_id
[
SSID_MAX_LENGTH
];
int
removed
=
0
;
size_t
ssid_len
;
if
(
!
ssid
||
!
ssid
[
0
])
return
-
1
;
ssid_len
=
strlen
(
ssid
);
snprintf
(
filepath
,
sizeof
(
filepath
),
"%s%s.nmconnection"
,
NM_CONN_DIR
,
ssid
);
if
(
unlink
(
filepath
)
==
0
)
removed
=
1
;
dir
=
opendir
(
NM_CONN_DIR
);
if
(
!
dir
)
return
removed
?
0
:
-
1
;
while
((
entry
=
readdir
(
dir
))
!=
NULL
)
{
if
(
!
strstr
(
entry
->
d_name
,
".nmconnection"
))
continue
;
snprintf
(
filepath
,
sizeof
(
filepath
),
"%s%s"
,
NM_CONN_DIR
,
entry
->
d_name
);
if
(
strncmp
(
entry
->
d_name
,
ssid
,
ssid_len
)
==
0
&&
(
entry
->
d_name
[
ssid_len
]
==
'\0'
||
entry
->
d_name
[
ssid_len
]
==
'-'
||
entry
->
d_name
[
ssid_len
]
==
'.'
))
{
if
(
unlink
(
filepath
)
==
0
)
removed
=
1
;
continue
;
}
if
(
read_profile_id
(
filepath
,
profile_id
,
sizeof
(
profile_id
))
!=
0
)
continue
;
if
(
strcmp
(
profile_id
,
ssid
)
!=
0
)
continue
;
if
(
unlink
(
filepath
)
==
0
)
removed
=
1
;
}
}
int
count
=
count_non_default_saved_wifi
();
closedir
(
dir
);
if
(
count
<
0
)
{
return
removed
?
0
:
-
1
;
}
/* 同名连接在 nmcli 里可能有多条,循环删到干净 */
static
int
delete_nmcli_connection_by_name
(
const
char
*
ssid
)
{
char
command
[
280
];
int
deleted
=
0
;
if
(
!
ssid
||
!
ssid
[
0
])
return
-
1
;
for
(
int
i
=
0
;
i
<
8
;
i
++
)
{
snprintf
(
command
,
sizeof
(
command
),
"nmcli connection delete '%s' 2>/dev/null"
,
ssid
);
if
(
system
(
command
)
!=
0
)
break
;
deleted
=
1
;
}
return
deleted
?
0
:
-
1
;
}
/* 与 MQTT 2009 共用:删配置文件 + nmcli 连接(仅未在用的) */
static
int
device_wifi_delete_saved_profile
(
const
char
*
ssid
)
{
int
file_rc
;
int
nmcli_rc
;
if
(
!
ssid
||
!
ssid
[
0
])
return
-
1
;
return
-
1
;
if
(
is_default_wifi_ssid
(
ssid
))
{
my_zlog_warn
(
"默认WiFi %s 不允许删除"
,
default_SSID
);
return
-
2
;
}
if
(
wifi_profile_is_in_use
(
ssid
))
{
my_zlog_warn
(
"WiFi %s 正在使用,跳过删除"
,
ssid
);
return
-
3
;
}
file_rc
=
remove_wifi_profile_files
(
ssid
);
nmcli_rc
=
delete_nmcli_connection_by_name
(
ssid
);
if
(
file_rc
==
0
||
nmcli_rc
==
0
)
return
0
;
my_zlog_error
(
"删除WiFi失败(文件rc=%d, nmcli rc=%d): %s"
,
file_rc
,
nmcli_rc
,
ssid
);
return
-
1
;
}
/* 从最旧往新找:跳过默认 WiFi 与正在使用的配置 */
static
int
find_oldest_deletable_wifi
(
char
*
ssid_out
,
size_t
ssid_size
)
{
saved_wifi_entry_t
entries
[
MAX_WIFI_ENTRIES
];
int
count
=
collect_saved_wifi_entries
(
entries
,
MAX_WIFI_ENTRIES
);
int
best
=
-
1
;
if
(
count
<=
0
)
return
-
1
;
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
if
(
is_default_wifi_ssid
(
entries
[
i
].
ssid
))
continue
;
if
(
wifi_profile_is_in_use
(
entries
[
i
].
ssid
))
continue
;
if
(
best
<
0
||
entries
[
i
].
mtime
<
entries
[
best
].
mtime
)
best
=
i
;
}
}
while
(
count
>=
MAX_SAVED_WIFI_SLOTS
)
{
if
(
best
<
0
)
char
oldest_ssid
[
SSID_MAX_LENGTH
];
return
-
1
;
if
(
find_oldest_non_default_wifi
(
oldest_ssid
,
sizeof
(
oldest_ssid
))
!=
0
)
{
my_zlog_error
(
"非默认WiFi已达上限,但未找到可删除的最旧配置"
);
strncpy
(
ssid_out
,
entries
[
best
].
ssid
,
ssid_size
-
1
);
ssid_out
[
ssid_size
-
1
]
=
'\0'
;
return
0
;
}
static
int
enforce_saved_wifi_limit
(
const
char
*
new_ssid
)
{
char
oldest_ssid
[
SSID_MAX_LENGTH
];
int
count
;
if
(
is_default_wifi_ssid
(
new_ssid
)
||
wifi_connection_exists
(
new_ssid
))
return
0
;
for
(
int
guard
=
0
;
guard
<
MAX_WIFI_ENTRIES
;
guard
++
)
{
int
rc
;
get_current_wifi
();
count
=
count_non_default_saved_wifi
();
if
(
count
<
0
)
return
-
1
;
if
(
count
<
MAX_SAVED_WIFI_SLOTS
)
return
0
;
if
(
find_oldest_deletable_wifi
(
oldest_ssid
,
sizeof
(
oldest_ssid
))
!=
0
)
{
my_zlog_error
(
"非默认WiFi已满%d个,且无未在用的配置可删(当前非默认%d个)"
,
MAX_SAVED_WIFI_SLOTS
,
count
);
return
-
1
;
return
-
1
;
}
}
if
(
delete_wifi_by_ssid
(
oldest_ssid
)
!=
0
)
{
rc
=
device_wifi_delete_saved_profile
(
oldest_ssid
);
my_zlog_error
(
"删除最旧WiFi失败: %s"
,
oldest_ssid
);
if
(
rc
==
-
3
)
{
my_zlog_warn
(
"最旧WiFi %s 正在使用,尝试删除下一旧配置"
,
oldest_ssid
);
continue
;
}
if
(
rc
!=
0
)
{
my_zlog_error
(
"删除最旧未使用WiFi失败: %s (rc=%d)"
,
oldest_ssid
,
rc
);
return
-
1
;
return
-
1
;
}
}
my_zlog_info
(
"非默认WiFi已
达%d个上限,删除最旧配置: %s"
,
MAX_SAVED_WIFI_SLOTS
,
oldest_ssid
);
my_zlog_info
(
"非默认WiFi已
满%d个,删除最旧未使用配置: %s"
,
count
--
;
MAX_SAVED_WIFI_SLOTS
,
oldest_ssid
)
;
}
}
return
0
;
my_zlog_error
(
"非默认WiFi自动清理超过最大轮次"
);
return
-
1
;
}
}
static
int
update_wifi_password
(
const
char
*
ssid
,
const
char
*
passwor
d
)
{
static
void
chmod_wifi_profile_files
(
const
char
*
ssi
d
)
{
char
cmd
[
256
];
char
path
[
512
];
snprintf
(
cmd
,
sizeof
(
cmd
),
DIR
*
dir
;
"nmcli connection modify
\"
%s
\"
wifi-sec.key-mgmt wpa-psk wifi-sec.psk
\"
%s
\"
"
,
struct
dirent
*
entry
;
ssid
,
password
)
;
char
profile_id
[
SSID_MAX_LENGTH
]
;
if
(
system
(
cmd
)
!=
0
)
{
snprintf
(
path
,
sizeof
(
path
),
"%s%s.nmconnection"
,
NM_CONN_DIR
,
ssid
);
my_zlog_error
(
"更新WiFi密码失败: %s"
,
ssid
);
chmod
(
path
,
0600
);
dir
=
opendir
(
NM_CONN_DIR
);
if
(
!
dir
)
return
;
while
((
entry
=
readdir
(
dir
))
!=
NULL
)
{
if
(
!
strstr
(
entry
->
d_name
,
".nmconnection"
))
continue
;
snprintf
(
path
,
sizeof
(
path
),
"%s%s"
,
NM_CONN_DIR
,
entry
->
d_name
);
if
(
read_profile_id
(
path
,
profile_id
,
sizeof
(
profile_id
))
!=
0
)
continue
;
if
(
strcmp
(
profile_id
,
ssid
)
==
0
)
chmod
(
path
,
0600
);
}
closedir
(
dir
);
}
/* 与老代码一致:用 nmcli 保存,避免直接写文件触发 NM 重载 wlan0 */
static
int
save_wifi_via_nmcli
(
const
char
*
ssid
,
const
char
*
password
)
{
char
command
[
512
];
int
ret
;
if
(
!
wifi_connection_exists
(
ssid
))
{
snprintf
(
command
,
sizeof
(
command
),
"nmcli connection add type wifi con-name '%s' ifname wlan0 ssid '%s'"
,
ssid
,
ssid
);
ret
=
system
(
command
);
if
(
ret
!=
0
)
{
my_zlog_error
(
"nmcli add 失败: %s (ret=%d)"
,
ssid
,
ret
);
return
-
1
;
}
}
snprintf
(
command
,
sizeof
(
command
),
"nmcli connection modify '%s' connection.autoconnect no "
"wifi-sec.key-mgmt wpa-psk wifi-sec.psk '%s'"
,
ssid
,
password
);
ret
=
system
(
command
);
if
(
ret
!=
0
)
{
my_zlog_error
(
"nmcli modify 失败: %s (ret=%d)"
,
ssid
,
ret
);
return
-
1
;
return
-
1
;
}
}
my_zlog_info
(
"已更新WiFi密码: %s"
,
ssid
);
chmod_wifi_profile_files
(
ssid
);
return
0
;
}
static
int
validate_wifi_credentials
(
const
char
*
ssid
,
const
char
*
password
)
{
if
(
!
ssid
||
!
password
||
strlen
(
ssid
)
==
0
||
strlen
(
password
)
==
0
)
{
my_zlog_error
(
"SSID或密码为空"
);
return
-
1
;
}
if
(
strlen
(
ssid
)
>=
SSID_MAX_LENGTH
)
{
my_zlog_error
(
"SSID过长"
);
return
-
1
;
}
if
(
strlen
(
password
)
>
WIFI_PSK_MAX_LENGTH
)
{
my_zlog_error
(
"WiFi密码过长"
);
return
-
1
;
}
if
(
strpbrk
(
ssid
,
"
\"
`$|;
\\
"
)
||
strpbrk
(
password
,
"
\"
`$|;
\\
"
))
{
my_zlog_error
(
"WiFi凭据含非法字符"
);
return
-
1
;
}
return
0
;
return
0
;
}
}
/*成功发送3010:0保存成功,1删除成功,2保存失败,3默认WiFi不可删,4删除失败*/
/*成功发送3010:0保存成功,1删除成功,2保存失败,3默认WiFi不可删,4删除失败*/
void
device_wifi_public_sendmqtt
(
int
wifi_status
)
{
void
device_wifi_public_sendmqtt
(
int
wifi_status
)
{
get_current_wifi
();
cJSON
*
root
=
cJSON_CreateObject
();
cJSON
*
root
=
cJSON_CreateObject
();
cJSON
*
body
=
cJSON_CreateObject
();
cJSON
*
body
=
cJSON_CreateObject
();
cJSON
*
head
=
cJSON_CreateObject
();
cJSON
*
head
=
cJSON_CreateObject
();
...
@@ -210,90 +694,126 @@ void device_wifi_public_sendmqtt(int wifi_status) {
...
@@ -210,90 +694,126 @@ void device_wifi_public_sendmqtt(int wifi_status) {
cJSON_Delete
(
root
);
cJSON_Delete
(
root
);
}
}
void
device_wifi_rec_delete
(
cJSON
*
body
)
{
int
get_saved_wifi_list
(
char
ssid_list
[][
SSID_MAX_LENGTH
],
int
max_count
)
{
cJSON
*
wifi_ssid
=
cJSON_GetObjectItem
(
body
,
"wifi_ssid"
);
saved_wifi_entry_t
entries
[
MAX_WIFI_ENTRIES
];
if
(
!
cJSON_IsString
(
wifi_ssid
))
{
int
limit
=
max_count
<
MAX_WIFI_ENTRIES
?
max_count
:
MAX_WIFI_ENTRIES
;
return
;
int
count
=
collect_saved_wifi_entries
(
entries
,
limit
);
}
if
(
count
<
0
)
return
-
1
;
char
*
ssid
=
wifi_ssid
->
valuestring
;
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
if
(
is_default_wifi_ssid
(
ssid
))
{
strncpy
(
ssid_list
[
i
],
entries
[
i
].
ssid
,
SSID_MAX_LENGTH
-
1
);
my_zlog_warn
(
"默认WiFi %s 不允许删除"
,
default_SSID
);
ssid_list
[
i
][
SSID_MAX_LENGTH
-
1
]
=
'\0'
;
device_wifi_public_sendmqtt
(
3
);
return
;
}
}
if
(
delete_wifi_by_ssid
(
ssid
)
==
0
)
{
return
count
;
my_zlog_info
(
"删除成功"
);
device_wifi_public_sendmqtt
(
1
);
}
else
{
my_zlog_info
(
"删除失败"
);
device_wifi_public_sendmqtt
(
4
);
}
}
}
int
orange_pi_save_wifi
(
const
char
*
ssid
,
const
char
*
password
)
{
int
orange_pi_save_wifi
(
const
char
*
ssid
,
const
char
*
password
)
{
if
(
!
ssid
||
!
password
||
strlen
(
ssid
)
==
0
||
strlen
(
password
)
==
0
)
{
if
(
validate_wifi_credentials
(
ssid
,
password
)
!=
0
)
fprintf
(
stderr
,
"[ERROR] SSID或密码为空
\n
"
);
return
-
1
;
if
(
!
wifi_connection_exists
(
ssid
)
&&
enforce_saved_wifi_limit
(
ssid
)
!=
0
)
return
-
1
;
return
-
1
;
}
if
(
s
trpbrk
(
ssid
,
"
\"
`$|"
)
||
strpbrk
(
password
,
"
\"
`$|"
)
)
{
if
(
s
ave_wifi_via_nmcli
(
ssid
,
password
)
!=
0
)
{
fprintf
(
stderr
,
"[ERROR] 检测到非法字符
\n
"
);
my_zlog_error
(
"[ERROR] 保存配置失败: %s"
,
ssid
);
return
-
1
;
return
-
1
;
}
}
if
(
wifi_connection_exists
(
ssid
))
{
my_zlog_info
(
"[SUCCESS] 已保存WiFi配置: %s"
,
ssid
);
return
update_wifi_password
(
ssid
,
password
);
return
0
;
}
static
int
ensure_wifi_pool
(
void
)
{
pthread_mutex_lock
(
&
s_wifi_pool_mutex
);
if
(
!
s_wifi_pool
)
s_wifi_pool
=
thread_pool_init
(
1
,
1
);
pthread_mutex_unlock
(
&
s_wifi_pool_mutex
);
return
s_wifi_pool
?
0
:
-
1
;
}
static
void
wifi_save_worker
(
void
*
arg
)
{
wifi_save_task_t
*
task
=
arg
;
int
rc
;
if
(
!
task
)
return
;
rc
=
orange_pi_save_wifi
(
task
->
ssid
,
task
->
password
);
device_wifi_public_sendmqtt
(
rc
==
0
?
0
:
2
);
free
(
task
);
}
static
void
wifi_delete_worker
(
void
*
arg
)
{
wifi_delete_task_t
*
task
=
arg
;
int
rc
;
if
(
!
task
)
return
;
rc
=
device_wifi_delete_saved_profile
(
task
->
ssid
);
if
(
rc
==
0
)
{
my_zlog_info
(
"删除成功"
);
device_wifi_public_sendmqtt
(
1
);
}
else
if
(
rc
==
-
2
)
{
device_wifi_public_sendmqtt
(
3
);
}
else
{
my_zlog_info
(
"删除失败"
);
device_wifi_public_sendmqtt
(
4
);
}
}
free
(
task
);
}
if
(
enforce_saved_wifi_limit
(
ssid
)
!=
0
)
{
static
int
submit_wifi_task
(
void
(
*
worker
)(
void
*
),
void
*
arg
,
const
char
*
what
)
{
if
(
ensure_wifi_pool
()
!=
0
||
!
s_wifi_pool
)
{
my_zlog_error
(
"%s: wifi thread pool init failed"
,
what
);
free
(
arg
);
return
-
1
;
return
-
1
;
}
}
if
(
thread_pool_add_task
(
s_wifi_pool
,
worker
,
arg
)
!=
0
)
{
char
cmd
[
512
];
my_zlog_error
(
"%s: thread_pool_add_task failed"
,
what
);
snprintf
(
cmd
,
sizeof
(
cmd
),
free
(
arg
);
"nmcli connection add type wifi con-name
\"
%s
\"
ifname wlan0 ssid
\"
%s
\"
wifi-sec.key-mgmt wpa-psk wifi-sec.psk
\"
%s
\"
"
,
ssid
,
ssid
,
password
);
if
(
system
(
cmd
)
!=
0
)
{
my_zlog_error
(
"[ERROR] 保存配置失败: %s"
,
ssid
);
return
-
1
;
return
-
1
;
}
}
my_zlog_info
(
"[SUCCESS] 已保存WiFi配置: %s"
,
ssid
);
return
0
;
return
0
;
}
}
void
device_wifi_rec_sava
(
cJSON
*
body
)
{
void
device_wifi_rec_sava
(
cJSON
*
body
)
{
cJSON
*
wifi_ssid
=
cJSON_GetObjectItem
(
body
,
"wifi_ssid"
);
cJSON
*
wifi_ssid
=
cJSON_GetObjectItem
(
body
,
"wifi_ssid"
);
cJSON
*
wifi_password
=
cJSON_GetObjectItem
(
body
,
"wifi_password"
);
cJSON
*
wifi_password
=
cJSON_GetObjectItem
(
body
,
"wifi_password"
);
if
(
cJSON_IsString
(
wifi_ssid
)
&&
cJSON_IsString
(
wifi_password
))
{
wifi_save_task_t
*
task
;
char
*
ssid
=
wifi_ssid
->
valuestring
;
char
*
password
=
wifi_password
->
valuestring
;
if
(
!
cJSON_IsString
(
wifi_ssid
)
||
!
cJSON_IsString
(
wifi_password
))
my_zlog_debug
(
"接收到保存WiFi名称和密码"
)
;
return
;
if
(
orange_pi_save_wifi
(
ssid
,
password
)
==
0
)
{
device_wifi_public_sendmqtt
(
0
);
task
=
calloc
(
1
,
sizeof
(
*
task
)
);
}
else
{
if
(
!
task
)
{
device_wifi_public_sendmqtt
(
2
);
device_wifi_public_sendmqtt
(
2
);
}
return
;
}
}
strncpy
(
task
->
ssid
,
wifi_ssid
->
valuestring
,
sizeof
(
task
->
ssid
)
-
1
);
strncpy
(
task
->
password
,
wifi_password
->
valuestring
,
sizeof
(
task
->
password
)
-
1
);
my_zlog_debug
(
"接收到保存WiFi,后台 nmcli 保存"
);
if
(
submit_wifi_task
(
wifi_save_worker
,
task
,
"save wifi"
)
!=
0
)
device_wifi_public_sendmqtt
(
2
);
}
}
int
get_saved_wifi_list
(
char
ssid_list
[][
SSID_MAX_LENGTH
],
int
max_count
)
{
void
device_wifi_rec_delete
(
cJSON
*
body
)
{
saved_wifi_entry_t
entries
[
MAX_WIFI_ENTRIES
];
cJSON
*
wifi_ssid
=
cJSON_GetObjectItem
(
body
,
"wifi_ssid"
);
int
limit
=
max_count
<
MAX_WIFI_ENTRIES
?
max_count
:
MAX_WIFI_ENTRIES
;
wifi_delete_task_t
*
task
;
int
count
=
collect_saved_wifi_entries
(
entries
,
limit
);
if
(
count
<
0
)
{
return
-
1
;
}
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
if
(
!
cJSON_IsString
(
wifi_ssid
))
strncpy
(
ssid_list
[
i
],
entries
[
i
].
ssid
,
SSID_MAX_LENGTH
-
1
);
return
;
ssid_list
[
i
][
SSID_MAX_LENGTH
-
1
]
=
'\0'
;
}
return
count
;
task
=
calloc
(
1
,
sizeof
(
*
task
));
if
(
!
task
)
{
device_wifi_public_sendmqtt
(
4
);
return
;
}
strncpy
(
task
->
ssid
,
wifi_ssid
->
valuestring
,
sizeof
(
task
->
ssid
)
-
1
);
my_zlog_debug
(
"接收到删除WiFi,后台删配置文件: %s"
,
task
->
ssid
);
if
(
submit_wifi_task
(
wifi_delete_worker
,
task
,
"delete wifi"
)
!=
0
)
device_wifi_public_sendmqtt
(
4
);
}
}
void
device_send_saved_wifi
()
{
void
device_send_saved_wifi
()
{
...
...
app/main/pthread_open.c
View file @
2eb47ff8
...
@@ -211,7 +211,11 @@ void *thread_open_browser(void *arg)
...
@@ -211,7 +211,11 @@ void *thread_open_browser(void *arg)
while
(
1
)
{
while
(
1
)
{
if
(
webrtcpush_should_use_mpp
())
{
if
(
webrtcpush_should_use_mpp
())
{
my_zlog_info
(
"WEBRTCPUSH: libdatachannel native push mode"
);
my_zlog_info
(
"WEBRTCPUSH: libdatachannel native push mode"
);
(
void
)
system
(
"pkill chromium"
);
{
int
pkill_rc
=
system
(
"pkill chromium"
);
if
(
pkill_rc
==
-
1
)
my_zlog_warn
(
"pkill chromium: system() failed"
);
}
delay_s
(
2
);
delay_s
(
2
);
while
(
s_webrtc_index
==
1
&&
webrtcpush_should_use_mpp
())
{
while
(
s_webrtc_index
==
1
&&
webrtcpush_should_use_mpp
())
{
if
(
webrtcpush_is_running
())
{
if
(
webrtcpush_is_running
())
{
...
...
build/main
View file @
2eb47ff8
No preview for this file type
drivers/gpio/hardware_iic.c
View file @
2eb47ff8
...
@@ -303,7 +303,7 @@ int hardware_iic_config_init(){
...
@@ -303,7 +303,7 @@ int hardware_iic_config_init(){
}
else
if
(
res
==
0
){
}
else
if
(
res
==
0
){
delay_ms
(
200
);
delay_ms
(
200
);
int
ret
=
system
(
"sudo reboot"
);
//重启香橙派
int
ret
=
system
(
"sudo reboot"
);
//重启香橙派
if
(
ret
==
0
)
my_zlog_
debug
(
"重启成功"
);
if
(
ret
==
0
)
my_zlog_
info
(
"重启成功"
);
return
1
;
return
1
;
}
}
}
}
drivers/sensors/audioplay.c
View file @
2eb47ff8
...
@@ -8,6 +8,7 @@
...
@@ -8,6 +8,7 @@
#include <stdio.h>
#include <stdio.h>
#include <strings.h>
#include <strings.h>
#include <unistd.h>
#include <unistd.h>
#include <sys/wait.h>
#define AUDIO_USB_ALSA_DEVICE "hw:2,0"
#define AUDIO_USB_ALSA_DEVICE "hw:2,0"
...
@@ -260,6 +261,31 @@ void audioplay_cycle(){
...
@@ -260,6 +261,31 @@ void audioplay_cycle(){
}
}
}
}
static
void
trim_line
(
char
*
s
)
{
if
(
!
s
)
return
;
s
[
strcspn
(
s
,
"
\r\n
"
)]
=
'\0'
;
}
static
int
pulse_pactl_exit_code
(
int
status
)
{
if
(
status
==
-
1
)
return
-
1
;
if
(
WIFEXITED
(
status
))
return
WEXITSTATUS
(
status
);
return
-
1
;
}
static
int
pulse_output_means_already_loaded
(
const
char
*
output
)
{
if
(
!
output
||
!
output
[
0
])
return
0
;
return
strstr
(
output
,
"already loaded"
)
!=
NULL
||
strstr
(
output
,
"Already loaded"
)
!=
NULL
||
strstr
(
output
,
"Module exists"
)
!=
NULL
;
}
static
int
pulse_module_exists
(
const
char
*
module_name
,
const
char
*
device
)
static
int
pulse_module_exists
(
const
char
*
module_name
,
const
char
*
device
)
{
{
FILE
*
fp
=
popen
(
"pactl list modules short 2>/dev/null"
,
"r"
);
FILE
*
fp
=
popen
(
"pactl list modules short 2>/dev/null"
,
"r"
);
...
@@ -283,23 +309,58 @@ static int pulse_module_exists(const char *module_name, const char *device)
...
@@ -283,23 +309,58 @@ static int pulse_module_exists(const char *module_name, const char *device)
static
int
pulse_load_alsa_module
(
const
char
*
module_name
,
const
char
*
label
)
static
int
pulse_load_alsa_module
(
const
char
*
module_name
,
const
char
*
label
)
{
{
char
cmd
[
192
];
char
cmd
[
256
];
char
output
[
512
];
char
line
[
256
];
if
(
pulse_module_exists
(
module_name
,
AUDIO_USB_ALSA_DEVICE
))
{
if
(
pulse_module_exists
(
module_name
,
AUDIO_USB_ALSA_DEVICE
))
{
my_zlog_debug
(
"%s 已注册 (%s %s),跳过"
,
label
,
module_name
,
AUDIO_USB_ALSA_DEVICE
);
my_zlog_info
(
"%s 已注册,跳过 (%s device=%s)"
,
label
,
module_name
,
AUDIO_USB_ALSA_DEVICE
);
return
0
;
return
0
;
}
}
snprintf
(
cmd
,
sizeof
(
cmd
),
snprintf
(
cmd
,
sizeof
(
cmd
),
"pactl load-module %s device=%s 2>&1"
,
"pactl load-module %s device=%s >/dev/null 2>&1"
,
module_name
,
AUDIO_USB_ALSA_DEVICE
);
module_name
,
AUDIO_USB_ALSA_DEVICE
);
int
rc
=
system
(
cmd
);
if
(
rc
==
0
)
{
FILE
*
fp
=
popen
(
cmd
,
"r"
);
my_zlog_debug
(
"%s 注册成功"
,
label
);
if
(
!
fp
)
{
my_zlog_warn
(
"%s 注册失败: 无法执行 pactl (%s)"
,
label
,
strerror
(
errno
));
return
-
1
;
}
output
[
0
]
=
'\0'
;
while
(
fgets
(
line
,
sizeof
(
line
),
fp
)
!=
NULL
)
{
trim_line
(
line
);
if
(
!
line
[
0
])
continue
;
if
(
output
[
0
])
{
strncat
(
output
,
"; "
,
sizeof
(
output
)
-
strlen
(
output
)
-
1
);
}
strncat
(
output
,
line
,
sizeof
(
output
)
-
strlen
(
output
)
-
1
);
}
int
status
=
pclose
(
fp
);
int
exit_code
=
pulse_pactl_exit_code
(
status
);
if
(
exit_code
==
0
)
{
my_zlog_info
(
"%s 注册成功 (%s device=%s)%s%s"
,
label
,
module_name
,
AUDIO_USB_ALSA_DEVICE
,
output
[
0
]
?
", pactl: "
:
""
,
output
[
0
]
?
output
:
""
);
return
0
;
}
if
(
pulse_output_means_already_loaded
(
output
)
||
pulse_module_exists
(
module_name
,
AUDIO_USB_ALSA_DEVICE
))
{
my_zlog_info
(
"%s 已注册,跳过 (%s device=%s)%s%s"
,
label
,
module_name
,
AUDIO_USB_ALSA_DEVICE
,
output
[
0
]
?
": "
:
""
,
output
[
0
]
?
output
:
""
);
return
0
;
return
0
;
}
}
my_zlog_warn
(
"%s 注册失败,返回状态码: %d"
,
label
,
rc
);
my_zlog_warn
(
"%s 注册失败 (%s device=%s): exit=%d%s%s"
,
label
,
module_name
,
AUDIO_USB_ALSA_DEVICE
,
exit_code
,
output
[
0
]
?
", pactl: "
:
", pactl 无输出"
,
output
[
0
]
?
output
:
""
);
return
-
1
;
return
-
1
;
}
}
...
...
modules/CMakeLists.txt
View file @
2eb47ff8
find_package
(
PkgConfig REQUIRED
)
find_package
(
PkgConfig REQUIRED
)
pkg_check_modules
(
WEBRTCPUSH_GST REQUIRED
pkg_check_modules
(
WEBRTCPUSH_GST REQUIRED
gstreamer-1.0>=1.18
gstreamer-1.0>=1.18
gstreamer-app-1.0
gstreamer-webrtc-1.0
gstreamer-webrtc-1.0
gstreamer-sdp-1.0
gstreamer-sdp-1.0
gstreamer-video-1.0
gstreamer-video-1.0
...
...
modules/mqtt/mqtt_infor_handle.c
View file @
2eb47ff8
...
@@ -429,9 +429,11 @@ int device_message_receive(cJSON *json)
...
@@ -429,9 +429,11 @@ int device_message_receive(cJSON *json)
my_zlog_debug
(
"删除已保存wifi"
);
my_zlog_debug
(
"删除已保存wifi"
);
break
;
break
;
case
2011
:
case
2011
:
int
res
=
system
(
"sudo reboot"
);
// 重启香橙派
{
if
(
res
==
0
)
int
res
=
system
(
"sudo reboot"
);
my_zlog_info
(
"重启成功"
);
if
(
res
==
0
)
my_zlog_info
(
"重启成功"
);
}
break
;
break
;
case
2012
:
case
2012
:
refresh_cam
();
refresh_cam
();
...
...
modules/mqtt/mqtt_init.c
View file @
2eb47ff8
...
@@ -9,6 +9,7 @@ mqttclient g_clients_t[MAX_SERVERS];
...
@@ -9,6 +9,7 @@ mqttclient g_clients_t[MAX_SERVERS];
static
char
s_uuid_mqtt_topic_id
[
MAX_SERVERS
][
56
];
static
char
s_uuid_mqtt_topic_id
[
MAX_SERVERS
][
56
];
static
bool
s_mosquitto_lib_inited
=
false
;
static
bool
s_mosquitto_lib_inited
=
false
;
static
pthread_mutex_t
s_mqtt_publish_mutex
=
PTHREAD_MUTEX_INITIALIZER
;
// struct mosquitto *mosq;
// struct mosquitto *mosq;
// 新增:用于向其他MQTT服务器发送通知的函数
// 新增:用于向其他MQTT服务器发送通知的函数
...
@@ -47,6 +48,7 @@ int mqtt_publish_to_all(const char *topic, const char *payload, int qos)
...
@@ -47,6 +48,7 @@ int mqtt_publish_to_all(const char *topic, const char *payload, int qos)
return
0
;
return
0
;
}
}
pthread_mutex_lock
(
&
s_mqtt_publish_mutex
);
for
(
int
i
=
0
;
i
<
total_count
;
i
++
)
{
for
(
int
i
=
0
;
i
<
total_count
;
i
++
)
{
if
(
!
g_clients_t
[
i
].
mosq
||
g_clients_t
[
i
].
permanently_failed
)
{
if
(
!
g_clients_t
[
i
].
mosq
||
g_clients_t
[
i
].
permanently_failed
)
{
continue
;
continue
;
...
@@ -55,6 +57,7 @@ int mqtt_publish_to_all(const char *topic, const char *payload, int qos)
...
@@ -55,6 +57,7 @@ int mqtt_publish_to_all(const char *topic, const char *payload, int qos)
sent_count
++
;
sent_count
++
;
}
}
}
}
pthread_mutex_unlock
(
&
s_mqtt_publish_mutex
);
return
sent_count
;
return
sent_count
;
}
}
...
...
modules/runtime_deps/runtime_deps.c
View file @
2eb47ff8
...
@@ -345,14 +345,9 @@ static int ensure_webrtc_stack(void)
...
@@ -345,14 +345,9 @@ static int ensure_webrtc_stack(void)
static
int
ensure_libdatachannel_stack
(
void
)
static
int
ensure_libdatachannel_stack
(
void
)
{
{
/* libdatachannel and its ICE/SRTP dependencies are linked into main. */
if
(
ensure_webrtc_stack
()
!=
0
)
if
(
access
(
WEBRTCPUSH_H264_FILE
,
R_OK
)
!=
0
)
{
my_zlog_error
(
"runtime_deps: H264 test file is not readable: %s"
,
WEBRTCPUSH_H264_FILE
);
return
-
1
;
return
-
1
;
}
my_zlog_info
(
"runtime_deps: libdatachannel native sender ready (MPP live encode)"
);
my_zlog_info
(
"runtime_deps: libdatachannel native sender ready, H264=%s"
,
WEBRTCPUSH_H264_FILE
);
return
0
;
return
0
;
}
}
...
...
modules/webrtcpush/mpp_h264_source.c
0 → 100644
View file @
2eb47ff8
#include "mpp_h264_source.h"
#include "webrtcpush_config.h"
#include "webrtcpush_log.h"
#include <gst/app/gstappsink.h>
#include <gst/video/video-event.h>
#include <string.h>
#define MPP_VIDEO_WIDTH 1280
#define MPP_VIDEO_HEIGHT 720
#define MPP_VIDEO_GOP WEBRTCPUSH_H264_GOP
#define MPP_VIDEO_START_BPS 1400000u
#define MPP_VIDEO_MIN_BPS WEBRTCPUSH_MIN_BITRATE
#define MPP_VIDEO_MAX_BPS WEBRTCPUSH_MAX_BITRATE
#define WRTC_V4L2_CAP_VIDEO_CAPTURE (1u << 0)
#define WRTC_V4L2_CAP_VIDEO_CAPTURE_MPLANE (1u << 12)
struct
MppH264Source
{
GstElement
*
pipeline
;
GstElement
*
enc
;
GstElement
*
parse
;
GstElement
*
appsink
;
gboolean
use_mpp
;
guint8
*
frame_buf
;
size_t
frame_size
;
size_t
frame_capacity
;
guint
keyframe_req
;
};
static
gchar
*
pick_v4l2_capture_device
(
void
)
{
for
(
int
i
=
0
;
i
<
32
;
i
++
)
{
gchar
*
path
=
g_strdup_printf
(
"/dev/video%d"
,
i
);
if
(
!
g_file_test
(
path
,
G_FILE_TEST_EXISTS
))
{
g_free
(
path
);
continue
;
}
gchar
*
sys_cap
=
g_strdup_printf
(
"/sys/class/video4linux/video%d/device_caps"
,
i
);
gboolean
use
=
FALSE
;
if
(
g_file_test
(
sys_cap
,
G_FILE_TEST_EXISTS
))
{
gchar
*
contents
=
NULL
;
if
(
g_file_get_contents
(
sys_cap
,
&
contents
,
NULL
,
NULL
))
{
guint64
caps
=
g_ascii_strtoull
(
g_strstrip
(
contents
),
NULL
,
0
);
use
=
(
caps
&
WRTC_V4L2_CAP_VIDEO_CAPTURE
)
!=
0
||
(
caps
&
WRTC_V4L2_CAP_VIDEO_CAPTURE_MPLANE
)
!=
0
;
g_free
(
contents
);
}
}
else
{
use
=
TRUE
;
}
g_free
(
sys_cap
);
if
(
use
)
return
path
;
g_free
(
path
);
}
return
NULL
;
}
static
void
configure_leaky_queue
(
GstElement
*
q
)
{
if
(
!
q
)
return
;
g_object_set
(
q
,
"max-size-buffers"
,
1
,
"max-size-bytes"
,
0
,
"max-size-time"
,
(
guint64
)
0
,
"leaky"
,
2
,
NULL
);
}
static
void
configure_v4l2_low_latency
(
GstElement
*
src
)
{
if
(
!
src
)
return
;
g_object_set
(
src
,
"do-timestamp"
,
TRUE
,
NULL
);
}
static
GstElement
*
make_h264_encoder
(
gboolean
prefer_mpp
,
gboolean
use_test
,
gboolean
*
use_mpp_out
)
{
GstElement
*
enc
=
NULL
;
if
(
prefer_mpp
&&
!
use_test
)
{
enc
=
gst_element_factory_make
(
"mpph264enc"
,
"enc"
);
if
(
enc
)
{
*
use_mpp_out
=
TRUE
;
g_object_set
(
enc
,
"gop"
,
MPP_VIDEO_GOP
,
"rc-mode"
,
1
,
"bps"
,
MPP_VIDEO_START_BPS
,
"bps-min"
,
(
guint
)(
MPP_VIDEO_START_BPS
*
0
.
80
),
"bps-max"
,
(
guint
)(
MPP_VIDEO_START_BPS
*
1
.
08
),
"header-mode"
,
1
,
"profile"
,
66
,
"level"
,
31
,
"qos"
,
TRUE
,
"zero-copy-pkt"
,
TRUE
,
NULL
);
my_zlog_info
(
"mpp_h264_source: mpph264enc %dx%d@%dfps GOP=%d"
,
MPP_VIDEO_WIDTH
,
MPP_VIDEO_HEIGHT
,
WEBRTCPUSH_H264_FPS
,
MPP_VIDEO_GOP
);
return
enc
;
}
my_zlog_warn
(
"mpp_h264_source: mpph264enc unavailable"
);
}
enc
=
gst_element_factory_make
(
"x264enc"
,
"enc"
);
if
(
enc
)
{
g_object_set
(
enc
,
"tune"
,
"zerolatency"
,
"speed-preset"
,
"ultrafast"
,
"bitrate"
,
MPP_VIDEO_START_BPS
/
1000
,
"key-int-max"
,
MPP_VIDEO_GOP
,
"byte-stream"
,
TRUE
,
"profile"
,
"baseline"
,
NULL
);
my_zlog_info
(
"mpp_h264_source: x264enc software fallback"
);
return
enc
;
}
return
NULL
;
}
static
gboolean
buffer_is_idr
(
const
guint8
*
data
,
size_t
size
)
{
size_t
i
=
0
;
while
(
i
+
3
<
size
)
{
size_t
sc
=
0
;
if
(
i
+
4
<=
size
&&
data
[
i
]
==
0
&&
data
[
i
+
1
]
==
0
&&
data
[
i
+
2
]
==
0
&&
data
[
i
+
3
]
==
1
)
sc
=
4
;
else
if
(
data
[
i
]
==
0
&&
data
[
i
+
1
]
==
0
&&
data
[
i
+
2
]
==
1
)
sc
=
3
;
else
{
i
++
;
continue
;
}
i
+=
sc
;
if
(
i
>=
size
)
break
;
if
((
data
[
i
]
&
0x1f
)
==
5
)
return
TRUE
;
}
return
FALSE
;
}
static
gboolean
send_force_key_unit_on_pad
(
GstPad
*
pad
)
{
GstEvent
*
event
;
if
(
!
pad
)
return
FALSE
;
event
=
gst_video_event_new_upstream_force_key_unit
(
GST_CLOCK_TIME_NONE
,
TRUE
,
0
);
return
gst_pad_send_event
(
pad
,
event
);
}
static
void
request_keyframe
(
MppH264Source
*
src
)
{
gboolean
ok
=
FALSE
;
if
(
!
src
)
return
;
src
->
keyframe_req
++
;
/* Upstream force-key-unit 必须交给编码器的 src pad 处理。 */
if
(
src
->
enc
)
{
GstPad
*
pad
=
gst_element_get_static_pad
(
src
->
enc
,
"src"
);
ok
=
send_force_key_unit_on_pad
(
pad
);
if
(
pad
)
gst_object_unref
(
pad
);
}
if
(
ok
)
my_zlog_info
(
"mpp_h264_source: requested IDR (%u)"
,
src
->
keyframe_req
);
else
my_zlog_warn
(
"mpp_h264_source: IDR request not handled"
);
}
void
mpp_h264_source_request_keyframe
(
MppH264Source
*
src
)
{
request_keyframe
(
src
);
}
static
gboolean
copy_frame
(
MppH264Source
*
src
,
GstBuffer
*
buffer
)
{
GstMapInfo
map
;
if
(
!
gst_buffer_map
(
buffer
,
&
map
,
GST_MAP_READ
))
return
FALSE
;
if
(
map
.
size
>
src
->
frame_capacity
)
{
src
->
frame_buf
=
g_realloc
(
src
->
frame_buf
,
map
.
size
);
src
->
frame_capacity
=
map
.
size
;
}
memcpy
(
src
->
frame_buf
,
map
.
data
,
map
.
size
);
src
->
frame_size
=
map
.
size
;
gst_buffer_unmap
(
buffer
,
&
map
);
return
TRUE
;
}
MppH264Source
*
mpp_h264_source_start
(
const
char
*
video_device
,
char
**
error_message
)
{
MppH264Source
*
src
;
GstElement
*
pipe
,
*
vsrc
,
*
caps_jpeg
=
NULL
,
*
jpegparse
=
NULL
,
*
jpegdec
=
NULL
;
GstElement
*
vrate
,
*
conv
,
*
caps1
,
*
enc
,
*
parse
,
*
q
,
*
caps_h264
,
*
sink
;
gchar
*
auto_v4l
=
NULL
;
const
gchar
*
vdev
=
video_device
;
gboolean
use_test
=
FALSE
;
gboolean
use_auto
=
FALSE
;
gboolean
use_mjpg
=
FALSE
;
gboolean
use_mpp
=
FALSE
;
gboolean
use_mpp_jpeg
=
FALSE
;
GstStateChangeReturn
ret
;
if
(
!
gst_is_initialized
())
gst_init
(
NULL
,
NULL
);
src
=
g_new0
(
MppH264Source
,
1
);
use_test
=
(
vdev
&&
g_strcmp0
(
vdev
,
"test"
)
==
0
);
use_auto
=
(
!
vdev
||
g_strcmp0
(
vdev
,
"auto"
)
==
0
);
if
(
use_auto
&&
!
use_test
)
{
auto_v4l
=
pick_v4l2_capture_device
();
if
(
auto_v4l
)
{
my_zlog_info
(
"mpp_h264_source: auto V4L2 %s"
,
auto_v4l
);
vdev
=
auto_v4l
;
}
else
{
my_zlog_warn
(
"mpp_h264_source: no V4L2 device, videotestsrc"
);
use_test
=
TRUE
;
}
}
pipe
=
gst_pipeline_new
(
"mpp-h264-pipe"
);
if
(
use_test
)
{
vsrc
=
gst_element_factory_make
(
"videotestsrc"
,
"vsrc"
);
g_object_set
(
vsrc
,
"is-live"
,
TRUE
,
NULL
);
}
else
{
vsrc
=
gst_element_factory_make
(
"v4l2src"
,
"vsrc"
);
g_object_set
(
vsrc
,
"device"
,
vdev
,
NULL
);
configure_v4l2_low_latency
(
vsrc
);
caps_jpeg
=
gst_element_factory_make
(
"capsfilter"
,
"caps_jpeg"
);
jpegparse
=
gst_element_factory_make
(
"jpegparse"
,
"jpegparse"
);
jpegdec
=
gst_element_factory_make
(
"mppjpegdec"
,
"jpegdec"
);
if
(
jpegdec
)
{
use_mpp_jpeg
=
TRUE
;
g_object_set
(
jpegdec
,
"format"
,
23
,
"fast-mode"
,
TRUE
,
NULL
);
}
else
{
jpegdec
=
gst_element_factory_make
(
"jpegdec"
,
"jpegdec"
);
}
if
(
caps_jpeg
&&
jpegparse
&&
jpegdec
)
{
gchar
*
jc_str
=
g_strdup_printf
(
"image/jpeg,width=%d,height=%d,framerate=30/1"
,
MPP_VIDEO_WIDTH
,
MPP_VIDEO_HEIGHT
);
GstCaps
*
jc
=
gst_caps_from_string
(
jc_str
);
g_object_set
(
caps_jpeg
,
"caps"
,
jc
,
NULL
);
gst_caps_unref
(
jc
);
g_free
(
jc_str
);
use_mjpg
=
TRUE
;
my_zlog_info
(
"mpp_h264_source: JPEG decoder=%s"
,
use_mpp_jpeg
?
"mppjpegdec"
:
"jpegdec software fallback"
);
}
}
conv
=
gst_element_factory_make
(
"videoconvert"
,
"conv"
);
vrate
=
gst_element_factory_make
(
"videorate"
,
"vrate"
);
caps1
=
gst_element_factory_make
(
"capsfilter"
,
"caps1"
);
q
=
gst_element_factory_make
(
"queue"
,
"vq"
);
enc
=
make_h264_encoder
(
TRUE
,
use_test
,
&
use_mpp
);
parse
=
gst_element_factory_make
(
"h264parse"
,
"parse"
);
caps_h264
=
gst_element_factory_make
(
"capsfilter"
,
"caps_h264"
);
sink
=
gst_element_factory_make
(
"appsink"
,
"sink"
);
if
(
!
pipe
||
!
vsrc
||
!
conv
||
!
vrate
||
!
caps1
||
!
q
||
!
enc
||
!
parse
||
!
caps_h264
||
!
sink
||
(
!
use_test
&&
!
use_mjpg
))
{
g_set_error_literal
(
NULL
,
0
,
0
,
"GStreamer element create failed"
);
if
(
error_message
)
*
error_message
=
g_strdup
(
"GStreamer element create failed"
);
g_free
(
auto_v4l
);
g_free
(
src
);
return
NULL
;
}
g_object_set
(
vrate
,
"max-rate"
,
WEBRTCPUSH_H264_FPS
,
"drop-only"
,
TRUE
,
NULL
);
configure_leaky_queue
(
q
);
g_object_set
(
parse
,
"config-interval"
,
-
1
,
NULL
);
{
gchar
*
cap_str
=
g_strdup_printf
(
"video/x-raw,format=%s,width=%d,height=%d,framerate=%d/1"
,
use_mpp
?
"NV12"
:
"I420"
,
use_test
?
640
:
MPP_VIDEO_WIDTH
,
use_test
?
480
:
MPP_VIDEO_HEIGHT
,
WEBRTCPUSH_H264_FPS
);
GstCaps
*
caps
=
gst_caps_from_string
(
cap_str
);
g_object_set
(
caps1
,
"caps"
,
caps
,
NULL
);
gst_caps_unref
(
caps
);
g_free
(
cap_str
);
}
{
GstCaps
*
hc
=
gst_caps_from_string
(
"video/x-h264,stream-format=byte-stream,alignment=au"
);
g_object_set
(
caps_h264
,
"caps"
,
hc
,
NULL
);
gst_caps_unref
(
hc
);
}
g_object_set
(
sink
,
"emit-signals"
,
FALSE
,
"sync"
,
FALSE
,
"max-buffers"
,
1
,
"drop"
,
TRUE
,
NULL
);
if
(
use_mjpg
)
{
gst_bin_add_many
(
GST_BIN
(
pipe
),
vsrc
,
caps_jpeg
,
jpegparse
,
jpegdec
,
conv
,
vrate
,
caps1
,
q
,
enc
,
parse
,
caps_h264
,
sink
,
NULL
);
if
(
!
gst_element_link_many
(
vsrc
,
caps_jpeg
,
jpegparse
,
jpegdec
,
conv
,
vrate
,
caps1
,
q
,
enc
,
parse
,
caps_h264
,
sink
,
NULL
))
{
if
(
error_message
)
*
error_message
=
g_strdup
(
"failed to link MJPG capture chain"
);
gst_object_unref
(
pipe
);
g_free
(
auto_v4l
);
g_free
(
src
);
return
NULL
;
}
}
else
{
gst_bin_add_many
(
GST_BIN
(
pipe
),
vsrc
,
conv
,
vrate
,
caps1
,
q
,
enc
,
parse
,
caps_h264
,
sink
,
NULL
);
if
(
!
gst_element_link_many
(
vsrc
,
conv
,
vrate
,
caps1
,
q
,
enc
,
parse
,
caps_h264
,
sink
,
NULL
))
{
if
(
error_message
)
*
error_message
=
g_strdup
(
"failed to link video chain"
);
gst_object_unref
(
pipe
);
g_free
(
auto_v4l
);
g_free
(
src
);
return
NULL
;
}
}
src
->
pipeline
=
pipe
;
src
->
enc
=
enc
;
src
->
parse
=
parse
;
src
->
appsink
=
sink
;
src
->
use_mpp
=
use_mpp
;
gst_pipeline_set_latency
(
GST_PIPELINE
(
pipe
),
0
);
ret
=
gst_element_set_state
(
pipe
,
GST_STATE_PLAYING
);
if
(
ret
==
GST_STATE_CHANGE_FAILURE
)
{
if
(
error_message
)
*
error_message
=
g_strdup
(
"pipeline failed to reach PLAYING"
);
mpp_h264_source_stop
(
src
);
g_free
(
auto_v4l
);
return
NULL
;
}
my_zlog_info
(
"mpp_h264_source: live encoder started (mpp=%d)"
,
use_mpp
?
1
:
0
);
g_free
(
auto_v4l
);
return
src
;
}
static
gboolean
pull_sample
(
MppH264Source
*
src
,
gboolean
require_idr
,
gint
timeout_ms
,
const
uint8_t
**
data
,
size_t
*
size
,
gboolean
*
is_idr
,
guint64
*
pts_ns
)
{
GstAppSink
*
appsink
;
GstSample
*
sample
;
GstBuffer
*
buffer
;
gboolean
idr
;
GstClockTime
pts
;
gint64
deadline
;
gint
attempts
=
0
;
if
(
!
src
||
!
src
->
appsink
||
!
data
||
!
size
||
timeout_ms
<
1
)
return
FALSE
;
appsink
=
GST_APP_SINK
(
src
->
appsink
);
deadline
=
g_get_monotonic_time
()
+
(
gint64
)
timeout_ms
*
1000
;
while
(
attempts
<
40
)
{
gint
wait_ms
=
(
gint
)((
deadline
-
g_get_monotonic_time
())
/
1000
);
if
(
wait_ms
<
1
)
break
;
sample
=
gst_app_sink_try_pull_sample
(
appsink
,
(
guint64
)
wait_ms
*
GST_MSECOND
);
if
(
!
sample
)
{
if
(
g_get_monotonic_time
()
>=
deadline
)
break
;
continue
;
}
buffer
=
gst_sample_get_buffer
(
sample
);
if
(
!
buffer
||
!
copy_frame
(
src
,
buffer
))
{
gst_sample_unref
(
sample
);
attempts
++
;
continue
;
}
pts
=
GST_BUFFER_PTS
(
buffer
);
idr
=
buffer_is_idr
(
src
->
frame_buf
,
src
->
frame_size
);
gst_sample_unref
(
sample
);
if
(
require_idr
&&
!
idr
)
{
attempts
++
;
continue
;
}
*
data
=
src
->
frame_buf
;
*
size
=
src
->
frame_size
;
if
(
is_idr
)
*
is_idr
=
idr
;
if
(
pts_ns
)
*
pts_ns
=
GST_CLOCK_TIME_IS_VALID
(
pts
)
?
(
guint64
)
pts
:
G_MAXUINT64
;
return
TRUE
;
}
return
FALSE
;
}
gboolean
mpp_h264_source_pull
(
MppH264Source
*
src
,
gboolean
force_idr
,
const
uint8_t
**
data
,
size_t
*
size
,
gboolean
*
is_idr
,
guint64
*
pts_ns
,
gint
timeout_ms
)
{
gint
idr_timeout
;
if
(
!
src
||
!
src
->
appsink
||
!
data
||
!
size
)
return
FALSE
;
if
(
timeout_ms
<
1
)
timeout_ms
=
80
;
if
(
force_idr
)
{
idr_timeout
=
timeout_ms
<
2000
?
2000
:
timeout_ms
;
request_keyframe
(
src
);
if
(
pull_sample
(
src
,
TRUE
,
idr_timeout
,
data
,
size
,
is_idr
,
pts_ns
))
return
TRUE
;
my_zlog_warn
(
"mpp_h264_source: IDR wait timeout, fallback to latest frame"
);
return
pull_sample
(
src
,
FALSE
,
200
,
data
,
size
,
is_idr
,
pts_ns
);
}
return
pull_sample
(
src
,
FALSE
,
timeout_ms
,
data
,
size
,
is_idr
,
pts_ns
);
}
void
mpp_h264_source_set_bitrate
(
MppH264Source
*
src
,
guint
bitrate_bps
)
{
GstElementFactory
*
f
;
const
gchar
*
name
;
if
(
!
src
||
!
src
->
enc
||
!
bitrate_bps
)
return
;
if
(
bitrate_bps
<
MPP_VIDEO_MIN_BPS
)
bitrate_bps
=
MPP_VIDEO_MIN_BPS
;
if
(
bitrate_bps
>
MPP_VIDEO_MAX_BPS
)
bitrate_bps
=
MPP_VIDEO_MAX_BPS
;
f
=
gst_element_get_factory
(
src
->
enc
);
name
=
f
?
gst_plugin_feature_get_name
(
GST_PLUGIN_FEATURE
(
f
))
:
NULL
;
if
(
name
&&
g_str_has_prefix
(
name
,
"mpph264enc"
))
{
guint
bmin
=
(
guint
)(
bitrate_bps
*
0
.
80
);
guint
bmax
=
(
guint
)(
bitrate_bps
*
1
.
08
);
if
(
bmin
<
MPP_VIDEO_MIN_BPS
)
bmin
=
MPP_VIDEO_MIN_BPS
;
if
(
bmax
>
MPP_VIDEO_MAX_BPS
)
bmax
=
MPP_VIDEO_MAX_BPS
;
g_object_set
(
src
->
enc
,
"bps"
,
bitrate_bps
,
"bps-min"
,
bmin
,
"bps-max"
,
bmax
,
NULL
);
}
else
if
(
name
&&
g_str_has_prefix
(
name
,
"x264enc"
))
{
g_object_set
(
src
->
enc
,
"bitrate"
,
bitrate_bps
/
1000
,
NULL
);
}
}
gboolean
mpp_h264_source_is_mpp_encoder
(
const
MppH264Source
*
src
)
{
return
src
&&
src
->
use_mpp
;
}
void
mpp_h264_source_stop
(
MppH264Source
*
src
)
{
if
(
!
src
)
return
;
if
(
src
->
pipeline
)
{
gst_element_set_state
(
src
->
pipeline
,
GST_STATE_NULL
);
gst_object_unref
(
src
->
pipeline
);
src
->
pipeline
=
NULL
;
}
src
->
enc
=
NULL
;
src
->
parse
=
NULL
;
src
->
appsink
=
NULL
;
g_free
(
src
->
frame_buf
);
g_free
(
src
);
}
modules/webrtcpush/mpp_h264_source.h
0 → 100644
View file @
2eb47ff8
#ifndef MPP_H264_SOURCE_H
#define MPP_H264_SOURCE_H
#include <glib.h>
#include <stddef.h>
#include <stdint.h>
typedef
struct
MppH264Source
MppH264Source
;
/* video_device: "auto" | /dev/videoN | "test" (videotestsrc) */
MppH264Source
*
mpp_h264_source_start
(
const
char
*
video_device
,
char
**
error_message
);
void
mpp_h264_source_stop
(
MppH264Source
*
src
);
/* 拉一帧 Annex-B AU;数据在下次 pull 前有效。timeout_ms:appsink 等待毫秒。 */
gboolean
mpp_h264_source_pull
(
MppH264Source
*
src
,
gboolean
force_idr
,
const
uint8_t
**
data
,
size_t
*
size
,
gboolean
*
is_idr
,
guint64
*
pts_ns
,
gint
timeout_ms
);
void
mpp_h264_source_set_bitrate
(
MppH264Source
*
src
,
guint
bitrate_bps
);
void
mpp_h264_source_request_keyframe
(
MppH264Source
*
src
);
gboolean
mpp_h264_source_is_mpp_encoder
(
const
MppH264Source
*
src
);
#endif
modules/webrtcpush/rtc_client.c
View file @
2eb47ff8
#include "rtc_client.h"
#include "rtc_client.h"
#include "
h264_reader
.h"
#include "
mpp_h264_source
.h"
#include "webrtcpush_config.h"
#include "webrtcpush_config.h"
#include "webrtcpush_log.h"
#include "webrtcpush_log.h"
#include "ws_signaling.h"
#include "ws_signaling.h"
...
@@ -27,7 +27,7 @@ typedef struct {
...
@@ -27,7 +27,7 @@ typedef struct {
GMutex
lock
;
GMutex
lock
;
GMutex
send_lock
;
GMutex
send_lock
;
GThread
*
sender_thread
;
GThread
*
sender_thread
;
H264Reader
*
reader
;
MppH264Source
*
h264_source
;
gboolean
stopping
;
gboolean
stopping
;
gboolean
track_open
;
gboolean
track_open
;
gboolean
need_idr
;
gboolean
need_idr
;
...
@@ -35,9 +35,13 @@ typedef struct {
...
@@ -35,9 +35,13 @@ typedef struct {
int
pc
;
int
pc
;
int
track
;
int
track
;
guint32
rtp_timestamp
;
guint32
rtp_timestamp
;
guint32
rtp_timestamp_base
;
guint64
source_pts_base
;
guint64
last_source_pts
;
gboolean
source_pts_valid
;
guint
target_bitrate
;
guint
target_bitrate
;
guint
remb_ceiling
;
guint
remb_ceiling
;
gint64
last_bitrate_ra
ise
_us
;
gint64
last_bitrate_ra
mp
_us
;
gchar
*
remote_ice_ufrag
;
gchar
*
remote_ice_ufrag
;
gchar
*
video_mid
;
gchar
*
video_mid
;
gint
video_mline_index
;
gint
video_mline_index
;
...
@@ -244,6 +248,7 @@ static void RTC_API on_state_change(int pc, rtcState state, void *ptr)
...
@@ -244,6 +248,7 @@ static void RTC_API on_state_change(int pc, rtcState state, void *ptr)
if
(
state
==
RTC_CONNECTED
)
{
if
(
state
==
RTC_CONNECTED
)
{
g_mutex_lock
(
&
client
->
lock
);
g_mutex_lock
(
&
client
->
lock
);
client
->
need_idr
=
TRUE
;
client
->
need_idr
=
TRUE
;
client
->
source_pts_valid
=
FALSE
;
g_mutex_unlock
(
&
client
->
lock
);
g_mutex_unlock
(
&
client
->
lock
);
}
}
}
}
...
@@ -257,6 +262,7 @@ static void RTC_API on_track_open(int track, void *ptr)
...
@@ -257,6 +262,7 @@ static void RTC_API on_track_open(int track, void *ptr)
if
(
client
->
track
==
track
)
{
if
(
client
->
track
==
track
)
{
client
->
track_open
=
TRUE
;
client
->
track_open
=
TRUE
;
client
->
need_idr
=
TRUE
;
client
->
need_idr
=
TRUE
;
client
->
source_pts_valid
=
FALSE
;
}
}
g_mutex_unlock
(
&
client
->
lock
);
g_mutex_unlock
(
&
client
->
lock
);
my_zlog_info
(
"libdatachannel: H264 track open, requesting SPS/PPS+IDR"
);
my_zlog_info
(
"libdatachannel: H264 track open, requesting SPS/PPS+IDR"
);
...
@@ -284,13 +290,19 @@ static void RTC_API on_track_error(int track, const char *error, void *ptr)
...
@@ -284,13 +290,19 @@ static void RTC_API on_track_error(int track, const char *error, void *ptr)
static
void
RTC_API
on_pli
(
int
track
,
void
*
ptr
)
static
void
RTC_API
on_pli
(
int
track
,
void
*
ptr
)
{
{
RtcClient
*
client
=
ptr
;
RtcClient
*
client
=
ptr
;
(
void
)
track
;
int
dropped
=
0
;
if
(
!
client
)
if
(
!
client
)
return
;
return
;
g_mutex_lock
(
&
client
->
lock
);
g_mutex_lock
(
&
client
->
lock
);
client
->
need_idr
=
TRUE
;
client
->
need_idr
=
TRUE
;
client
->
source_pts_valid
=
FALSE
;
g_mutex_unlock
(
&
client
->
lock
);
g_mutex_unlock
(
&
client
->
lock
);
my_zlog_info
(
"libdatachannel: PLI received, next frame is SPS/PPS+IDR"
);
g_mutex_lock
(
&
client
->
send_lock
);
if
(
client
->
track
==
track
)
dropped
=
rtcClearPacingQueue
(
track
);
g_mutex_unlock
(
&
client
->
send_lock
);
my_zlog_info
(
"libdatachannel: PLI received, dropped %d queued RTP packet(s), next is IDR"
,
dropped
>
0
?
dropped
:
0
);
}
}
static
guint
clamp_bitrate
(
guint
bitrate
)
static
guint
clamp_bitrate
(
guint
bitrate
)
...
@@ -302,56 +314,162 @@ static guint clamp_bitrate(guint bitrate)
...
@@ -302,56 +314,162 @@ static guint clamp_bitrate(guint bitrate)
return
bitrate
;
return
bitrate
;
}
}
static
guint
pacing_bitrate_for_encoder
(
guint
encoder_bitrate
)
{
guint64
pacing
=
((
guint64
)
clamp_bitrate
(
encoder_bitrate
)
*
WEBRTCPUSH_PACING_HEADROOM_PERCENT
)
/
100
;
if
(
pacing
>
WEBRTCPUSH_PACING_MAX_BITRATE
)
pacing
=
WEBRTCPUSH_PACING_MAX_BITRATE
;
return
(
guint
)
pacing
;
}
static
void
apply_bitrate
(
RtcClient
*
client
,
int
track
,
guint
bitrate
)
{
guint
b
;
if
(
!
client
)
return
;
b
=
clamp_bitrate
(
bitrate
);
g_mutex_lock
(
&
client
->
lock
);
client
->
target_bitrate
=
b
;
g_mutex_unlock
(
&
client
->
lock
);
if
(
track
>=
0
)
{
g_mutex_lock
(
&
client
->
send_lock
);
if
(
client
->
track
==
track
)
rtcSetPacingBitrate
(
track
,
pacing_bitrate_for_encoder
(
b
));
g_mutex_unlock
(
&
client
->
send_lock
);
}
mpp_h264_source_set_bitrate
(
client
->
h264_source
,
b
);
}
/* 与 gst_webrtc_pipeline::ramp_video_bitrate 同档升降步长 */
static
void
ramp_bitrate_toward_ceiling
(
RtcClient
*
client
,
int
track
)
{
guint
cur
;
guint
tgt
;
guint
next
;
guint
diff
;
guint
step
;
if
(
!
client
||
track
<
0
)
return
;
g_mutex_lock
(
&
client
->
lock
);
cur
=
client
->
target_bitrate
;
tgt
=
client
->
remb_ceiling
;
g_mutex_unlock
(
&
client
->
lock
);
if
(
!
cur
||
!
tgt
||
cur
==
tgt
)
return
;
diff
=
(
cur
>
tgt
)
?
(
cur
-
tgt
)
:
(
tgt
-
cur
);
if
(
cur
<
tgt
)
{
step
=
diff
/
8
;
if
(
step
<
50000
)
step
=
50000
;
if
(
step
>
150000
)
step
=
150000
;
next
=
cur
+
step
;
if
(
next
>
tgt
)
next
=
tgt
;
}
else
{
step
=
diff
/
3
;
if
(
step
<
100000
)
step
=
100000
;
if
(
step
>
600000
)
step
=
600000
;
next
=
(
cur
>
step
)
?
(
cur
-
step
)
:
0
;
if
(
next
<
tgt
)
next
=
tgt
;
}
if
(
next
==
cur
)
return
;
apply_bitrate
(
client
,
track
,
next
);
my_zlog_info
(
"libdatachannel: bitrate ramp %u -> %u kbps (REMB ceiling %u kbps)"
,
cur
/
1000
,
next
/
1000
,
tgt
/
1000
);
}
static
void
RTC_API
on_remb
(
int
track
,
unsigned
int
bitrate
,
void
*
ptr
)
static
void
RTC_API
on_remb
(
int
track
,
unsigned
int
bitrate
,
void
*
ptr
)
{
{
RtcClient
*
client
=
ptr
;
RtcClient
*
client
=
ptr
;
guint
current
;
guint
current
;
guint
next
=
0
;
guint
ceiling
;
guint
ceiling
;
(
void
)
track
;
guint
next
=
0
;
gboolean
severe_down
=
FALSE
;
int
dropped
=
0
;
if
(
!
client
||
bitrate
==
0
)
if
(
!
client
||
bitrate
==
0
)
return
;
return
;
ceiling
=
clamp_bitrate
((
guint
)(((
guint64
)
bitrate
*
85
)
/
100
));
ceiling
=
clamp_bitrate
(
(
guint
)(((
guint64
)
bitrate
*
WEBRTCPUSH_REMB_UTIL_PERCENT
)
/
100
));
g_mutex_lock
(
&
client
->
lock
);
g_mutex_lock
(
&
client
->
lock
);
current
=
client
->
target_bitrate
;
current
=
client
->
target_bitrate
;
client
->
remb_ceiling
=
ceiling
;
client
->
remb_ceiling
=
ceiling
;
if
(
ceiling
<
current
)
{
if
(
ceiling
<
current
)
{
client
->
target_bitrate
=
ceiling
;
client
->
target_bitrate
=
ceiling
;
next
=
ceiling
;
next
=
ceiling
;
severe_down
=
((
guint64
)
ceiling
*
100
)
<
((
guint64
)
current
*
75
);
if
(
severe_down
)
{
client
->
need_idr
=
TRUE
;
client
->
source_pts_valid
=
FALSE
;
}
}
}
g_mutex_unlock
(
&
client
->
lock
);
g_mutex_unlock
(
&
client
->
lock
);
if
(
next
)
{
if
(
next
)
{
rtcSetPacingBitrate
(
track
,
next
);
g_mutex_lock
(
&
client
->
send_lock
);
my_zlog_info
(
"libdatachannel: REMB=%u, pacing immediately down to %u bps"
,
if
(
client
->
track
==
track
)
{
bitrate
,
next
);
if
(
severe_down
)
dropped
=
rtcClearPacingQueue
(
track
);
rtcSetPacingBitrate
(
track
,
pacing_bitrate_for_encoder
(
next
));
}
g_mutex_unlock
(
&
client
->
send_lock
);
mpp_h264_source_set_bitrate
(
client
->
h264_source
,
next
);
if
(
severe_down
)
{
my_zlog_info
(
"libdatachannel: REMB=%u kbps, encoder=%u kbps pacing=%u kbps, resync dropped=%d RTP packets"
,
bitrate
/
1000
,
next
/
1000
,
pacing_bitrate_for_encoder
(
next
)
/
1000
,
dropped
>
0
?
dropped
:
0
);
}
else
{
my_zlog_info
(
"libdatachannel: REMB=%u kbps, encoder=%u kbps pacing=%u kbps"
,
bitrate
/
1000
,
next
/
1000
,
pacing_bitrate_for_encoder
(
next
)
/
1000
);
}
}
else
if
(
ceiling
>
current
)
{
my_zlog_debug
(
"libdatachannel: REMB=%u kbps, ceiling %u kbps (ramping up)"
,
bitrate
/
1000
,
ceiling
/
1000
);
}
}
}
}
static
void
maybe_ra
ise
_bitrate
(
RtcClient
*
client
,
int
track
,
gint64
now_us
)
static
void
maybe_ra
mp
_bitrate
(
RtcClient
*
client
,
int
track
,
gint64
now_us
)
{
{
guint
next
=
0
;
if
(
!
client
||
track
<
0
)
return
;
g_mutex_lock
(
&
client
->
lock
);
g_mutex_lock
(
&
client
->
lock
);
if
(
client
->
remb_ceiling
>
client
->
target_bitrate
&&
if
(
now_us
-
client
->
last_bitrate_ramp_us
<
now_us
-
client
->
last_bitrate_raise_us
>=
2000000
)
{
(
gint64
)
WEBRTCPUSH_BITRATE_RAMP_MS
*
1000
)
{
guint
raised
=
(
guint
)(((
guint64
)
client
->
target_bitrate
*
112
)
/
100
);
g_mutex_unlock
(
&
client
->
lock
);
if
(
raised
>
client
->
remb_ceiling
)
return
;
raised
=
client
->
remb_ceiling
;
client
->
target_bitrate
=
clamp_bitrate
(
raised
);
client
->
last_bitrate_raise_us
=
now_us
;
next
=
client
->
target_bitrate
;
}
}
client
->
last_bitrate_ramp_us
=
now_us
;
g_mutex_unlock
(
&
client
->
lock
);
g_mutex_unlock
(
&
client
->
lock
);
if
(
next
)
{
ramp_bitrate_toward_ceiling
(
client
,
track
);
g_mutex_lock
(
&
client
->
send_lock
);
}
if
(
client
->
track
==
track
)
rtcSetPacingBitrate
(
track
,
next
);
static
guint32
pts_delta_to_rtp
(
guint64
delta_ns
)
g_mutex_unlock
(
&
client
->
send_lock
);
{
my_zlog_info
(
"libdatachannel: pacing slowly up to %u bps"
,
next
);
const
guint64
second
=
G_GUINT64_CONSTANT
(
1000000000
);
}
guint64
ticks
=
(
delta_ns
/
second
)
*
90000
;
ticks
+=
((
delta_ns
%
second
)
*
90000
)
/
second
;
return
(
guint32
)
ticks
;
}
}
static
gpointer
sender_thread_main
(
gpointer
data
)
static
gpointer
sender_thread_main
(
gpointer
data
)
...
@@ -370,6 +488,7 @@ static gpointer sender_thread_main(gpointer data)
...
@@ -370,6 +488,7 @@ static gpointer sender_thread_main(gpointer data)
const
guint8
*
frame
;
const
guint8
*
frame
;
size_t
frame_size
;
size_t
frame_size
;
gboolean
is_idr
;
gboolean
is_idr
;
guint64
source_pts
=
G_MAXUINT64
;
gint64
now
;
gint64
now
;
g_mutex_lock
(
&
client
->
lock
);
g_mutex_lock
(
&
client
->
lock
);
...
@@ -377,8 +496,6 @@ static gpointer sender_thread_main(gpointer data)
...
@@ -377,8 +496,6 @@ static gpointer sender_thread_main(gpointer data)
open
=
client
->
track_open
;
open
=
client
->
track_open
;
track
=
client
->
track
;
track
=
client
->
track
;
force_idr
=
client
->
need_idr
;
force_idr
=
client
->
need_idr
;
if
(
open
&&
force_idr
)
client
->
need_idr
=
FALSE
;
timestamp
=
client
->
rtp_timestamp
;
timestamp
=
client
->
rtp_timestamp
;
g_mutex_unlock
(
&
client
->
lock
);
g_mutex_unlock
(
&
client
->
lock
);
...
@@ -391,21 +508,46 @@ static gpointer sender_thread_main(gpointer data)
...
@@ -391,21 +508,46 @@ static gpointer sender_thread_main(gpointer data)
}
}
now
=
g_get_monotonic_time
();
now
=
g_get_monotonic_time
();
maybe_ra
ise
_bitrate
(
client
,
track
,
now
);
maybe_ra
mp
_bitrate
(
client
,
track
,
now
);
if
(
next_frame_us
==
0
||
now
-
next_frame_us
>
frame_us
*
4
)
{
if
(
next_frame_us
==
0
||
now
-
next_frame_us
>
frame_us
*
4
)
next_frame_us
=
now
;
next_frame_us
=
now
;
force_idr
=
TRUE
;
}
if
(
now
<
next_frame_us
)
{
if
(
now
<
next_frame_us
)
{
g_usleep
((
gulong
)(
next_frame_us
-
now
));
g_usleep
((
gulong
)(
next_frame_us
-
now
));
continue
;
continue
;
}
}
if
(
!
h264_reader_next
(
client
->
reader
,
force_idr
,
&
frame
,
&
frame_size
,
&
is_idr
))
{
{
my_zlog_error
(
"libdatachannel: failed to get H264 frame"
);
gint
pull_timeout
=
force_idr
?
4000
:
120
;
g_usleep
(
100000
);
if
(
!
mpp_h264_source_pull
(
client
->
h264_source
,
force_idr
,
&
frame
,
&
frame_size
,
&
is_idr
,
&
source_pts
,
pull_timeout
))
{
my_zlog_warn
(
"libdatachannel: live H264 frame pull timeout"
);
next_frame_us
=
now
+
frame_us
/
2
;
continue
;
}
}
g_mutex_lock
(
&
client
->
lock
);
if
(
client
->
need_idr
&&
!
is_idr
)
{
g_mutex_unlock
(
&
client
->
lock
);
my_zlog_warn
(
"libdatachannel: discard non-IDR while waiting for resync"
);
next_frame_us
=
g_get_monotonic_time
();
continue
;
continue
;
}
}
if
(
source_pts
!=
G_MAXUINT64
)
{
if
(
!
client
->
source_pts_valid
||
source_pts
<
client
->
last_source_pts
)
{
client
->
source_pts_base
=
source_pts
;
client
->
last_source_pts
=
source_pts
;
client
->
rtp_timestamp_base
=
client
->
rtp_timestamp
;
client
->
source_pts_valid
=
TRUE
;
}
timestamp
=
client
->
rtp_timestamp_base
+
pts_delta_to_rtp
(
source_pts
-
client
->
source_pts_base
);
client
->
last_source_pts
=
source_pts
;
}
else
{
timestamp
=
client
->
rtp_timestamp
;
}
g_mutex_unlock
(
&
client
->
lock
);
g_mutex_lock
(
&
client
->
send_lock
);
g_mutex_lock
(
&
client
->
send_lock
);
if
(
!
rtcIsOpen
(
track
)
||
if
(
!
rtcIsOpen
(
track
)
||
...
@@ -419,7 +561,9 @@ static gpointer sender_thread_main(gpointer data)
...
@@ -419,7 +561,9 @@ static gpointer sender_thread_main(gpointer data)
g_mutex_unlock
(
&
client
->
send_lock
);
g_mutex_unlock
(
&
client
->
send_lock
);
g_mutex_lock
(
&
client
->
lock
);
g_mutex_lock
(
&
client
->
lock
);
client
->
rtp_timestamp
+=
timestamp_step
;
client
->
rtp_timestamp
=
timestamp
+
timestamp_step
;
if
(
is_idr
)
client
->
need_idr
=
FALSE
;
g_mutex_unlock
(
&
client
->
lock
);
g_mutex_unlock
(
&
client
->
lock
);
next_frame_us
+=
frame_us
;
next_frame_us
+=
frame_us
;
}
}
...
@@ -496,6 +640,7 @@ static void destroy_peer(RtcClient *client)
...
@@ -496,6 +640,7 @@ static void destroy_peer(RtcClient *client)
client
->
track_open
=
FALSE
;
client
->
track_open
=
FALSE
;
client
->
answer_sent
=
FALSE
;
client
->
answer_sent
=
FALSE
;
client
->
need_idr
=
TRUE
;
client
->
need_idr
=
TRUE
;
client
->
source_pts_valid
=
FALSE
;
clear_local_ice
(
client
);
clear_local_ice
(
client
);
g_clear_pointer
(
&
client
->
remote_ice_ufrag
,
g_free
);
g_clear_pointer
(
&
client
->
remote_ice_ufrag
,
g_free
);
g_clear_pointer
(
&
client
->
video_mid
,
g_free
);
g_clear_pointer
(
&
client
->
video_mid
,
g_free
);
...
@@ -557,7 +702,8 @@ static gboolean setup_track(RtcClient *client, int pc,
...
@@ -557,7 +702,8 @@ static gboolean setup_track(RtcClient *client, int pc,
rtcChainRtcpNackResponder
(
track
,
WEBRTCPUSH_NACK_PACKETS
)
<
0
||
rtcChainRtcpNackResponder
(
track
,
WEBRTCPUSH_NACK_PACKETS
)
<
0
||
rtcChainPliHandler
(
track
,
on_pli
)
<
0
||
rtcChainPliHandler
(
track
,
on_pli
)
<
0
||
rtcChainRembHandler
(
track
,
on_remb
)
<
0
||
rtcChainRembHandler
(
track
,
on_remb
)
<
0
||
rtcChainPacingHandler
(
track
,
WEBRTCPUSH_INITIAL_BITRATE
,
rtcChainPacingHandler
(
track
,
pacing_bitrate_for_encoder
(
WEBRTCPUSH_INITIAL_BITRATE
),
WEBRTCPUSH_PACING_INTERVAL_MS
)
<
0
)
{
WEBRTCPUSH_PACING_INTERVAL_MS
)
<
0
)
{
rtcDeleteTrack
(
track
);
rtcDeleteTrack
(
track
);
return
FALSE
;
return
FALSE
;
...
@@ -565,6 +711,8 @@ static gboolean setup_track(RtcClient *client, int pc,
...
@@ -565,6 +711,8 @@ static gboolean setup_track(RtcClient *client, int pc,
g_mutex_lock
(
&
client
->
lock
);
g_mutex_lock
(
&
client
->
lock
);
client
->
rtp_timestamp
=
packetizer
.
timestamp
;
client
->
rtp_timestamp
=
packetizer
.
timestamp
;
client
->
rtp_timestamp_base
=
packetizer
.
timestamp
;
client
->
source_pts_valid
=
FALSE
;
g_mutex_unlock
(
&
client
->
lock
);
g_mutex_unlock
(
&
client
->
lock
);
*
track_out
=
track
;
*
track_out
=
track
;
return
TRUE
;
return
TRUE
;
...
@@ -591,24 +739,29 @@ int rtc_client_start(AppState *app)
...
@@ -591,24 +739,29 @@ int rtc_client_start(AppState *app)
{
{
RtcClient
*
client
;
RtcClient
*
client
;
char
*
error_message
=
NULL
;
char
*
error_message
=
NULL
;
const
char
*
video_device
=
WEBRTCPUSH_VIDEO_DEVICE
;
if
(
!
app
)
if
(
!
app
)
return
-
1
;
return
-
1
;
if
(
app
->
video_device
&&
app
->
video_device
[
0
])
video_device
=
app
->
video_device
;
client
=
g_new0
(
RtcClient
,
1
);
client
=
g_new0
(
RtcClient
,
1
);
client
->
app
=
app
;
client
->
app
=
app
;
client
->
pc
=
-
1
;
client
->
pc
=
-
1
;
client
->
track
=
-
1
;
client
->
track
=
-
1
;
client
->
target_bitrate
=
WEBRTCPUSH_INITIAL_BITRATE
;
client
->
target_bitrate
=
WEBRTCPUSH_INITIAL_BITRATE
;
client
->
remb_ceiling
=
WEBRTCPUSH_INITIAL_BITRATE
;
client
->
remb_ceiling
=
WEBRTCPUSH_INITIAL_BITRATE
;
client
->
last_bitrate_ra
ise
_us
=
g_get_monotonic_time
();
client
->
last_bitrate_ra
mp
_us
=
g_get_monotonic_time
();
client
->
pending_local_ice
=
g_queue_new
();
client
->
pending_local_ice
=
g_queue_new
();
client
->
pending_remote_ice
=
g_queue_new
();
client
->
pending_remote_ice
=
g_queue_new
();
g_mutex_init
(
&
client
->
lock
);
g_mutex_init
(
&
client
->
lock
);
g_mutex_init
(
&
client
->
send_lock
);
g_mutex_init
(
&
client
->
send_lock
);
client
->
reader
=
h264_reader_open
(
WEBRTCPUSH_H264_FILE
,
&
error_message
);
if
(
!
client
->
reader
)
{
client
->
h264_source
=
mpp_h264_source_start
(
video_device
,
&
error_message
);
my_zlog_error
(
"libdatachannel: cannot open %s: %s"
,
if
(
!
client
->
h264_source
)
{
WEBRTCPUSH_H264_FILE
,
error_message
?
error_message
:
"unknown"
);
my_zlog_error
(
"libdatachannel: live encoder start failed: %s"
,
error_message
?
error_message
:
"unknown"
);
g_free
(
error_message
);
g_free
(
error_message
);
g_queue_free
(
client
->
pending_local_ice
);
g_queue_free
(
client
->
pending_local_ice
);
g_queue_free
(
client
->
pending_remote_ice
);
g_queue_free
(
client
->
pending_remote_ice
);
...
@@ -617,10 +770,14 @@ int rtc_client_start(AppState *app)
...
@@ -617,10 +770,14 @@ int rtc_client_start(AppState *app)
g_free
(
client
);
g_free
(
client
);
return
-
1
;
return
-
1
;
}
}
app
->
rtc_client
=
client
;
app
->
rtc_client
=
client
;
client
->
sender_thread
=
g_thread_new
(
"rtc-h264-send"
,
sender_thread_main
,
client
);
client
->
sender_thread
=
g_thread_new
(
"rtc-h264-send"
,
sender_thread_main
,
client
);
my_zlog_info
(
"libdatachannel: loaded %zu H264 frames from %s"
,
mpp_h264_source_set_bitrate
(
client
->
h264_source
,
client
->
target_bitrate
);
h264_reader_frame_count
(
client
->
reader
),
WEBRTCPUSH_H264_FILE
);
my_zlog_info
(
"libdatachannel: MPP live source started device=%s mpp=%d fps=%d"
,
video_device
,
mpp_h264_source_is_mpp_encoder
(
client
->
h264_source
)
?
1
:
0
,
WEBRTCPUSH_H264_FPS
);
return
0
;
return
0
;
}
}
...
@@ -649,7 +806,7 @@ void rtc_client_stop(AppState *app)
...
@@ -649,7 +806,7 @@ void rtc_client_stop(AppState *app)
clear_local_ice
(
client
);
clear_local_ice
(
client
);
g_mutex_unlock
(
&
client
->
lock
);
g_mutex_unlock
(
&
client
->
lock
);
g_queue_free
(
client
->
pending_local_ice
);
g_queue_free
(
client
->
pending_local_ice
);
h264_reader_close
(
client
->
reader
);
mpp_h264_source_stop
(
client
->
h264_source
);
g_mutex_clear
(
&
client
->
send_lock
);
g_mutex_clear
(
&
client
->
send_lock
);
g_mutex_clear
(
&
client
->
lock
);
g_mutex_clear
(
&
client
->
lock
);
app
->
rtc_client
=
NULL
;
app
->
rtc_client
=
NULL
;
...
@@ -714,8 +871,9 @@ int rtc_client_handle_offer(AppState *app, const char *sdp)
...
@@ -714,8 +871,9 @@ int rtc_client_handle_offer(AppState *app, const char *sdp)
client
->
remote_ice_ufrag
=
g_strdup
(
video
.
ice_ufrag
);
client
->
remote_ice_ufrag
=
g_strdup
(
video
.
ice_ufrag
);
client
->
target_bitrate
=
WEBRTCPUSH_INITIAL_BITRATE
;
client
->
target_bitrate
=
WEBRTCPUSH_INITIAL_BITRATE
;
client
->
remb_ceiling
=
WEBRTCPUSH_INITIAL_BITRATE
;
client
->
remb_ceiling
=
WEBRTCPUSH_INITIAL_BITRATE
;
client
->
last_bitrate_ra
ise
_us
=
g_get_monotonic_time
();
client
->
last_bitrate_ra
mp
_us
=
g_get_monotonic_time
();
client
->
need_idr
=
TRUE
;
client
->
need_idr
=
TRUE
;
client
->
source_pts_valid
=
FALSE
;
g_mutex_unlock
(
&
client
->
lock
);
g_mutex_unlock
(
&
client
->
lock
);
my_zlog_info
(
"libdatachannel: applying offer mid=%s pt=%d ufrag=%s"
,
my_zlog_info
(
"libdatachannel: applying offer mid=%s pt=%d ufrag=%s"
,
...
...
modules/webrtcpush/webrtcpush_config.h
View file @
2eb47ff8
...
@@ -4,32 +4,36 @@
...
@@ -4,32 +4,36 @@
/*
/*
* WEBRTCPUSH_USE_MPP — 推流方式(与 Chromium 浏览器互斥,共用 thread_open_browser 线程)
* WEBRTCPUSH_USE_MPP — 推流方式(与 Chromium 浏览器互斥,共用 thread_open_browser 线程)
* 0:走 Chromium 浏览器推流(opencamsh,原有逻辑)
* 0:走 Chromium 浏览器推流(opencamsh,原有逻辑)
* 1:走 libdatachannel 原生推流
,不打开浏览器
* 1:走 libdatachannel 原生推流
(摄像头 → MPP/x264 → libdatachannel)
*
*
*
WEBRTCPUSH_USE_MPP 是历史配置名。第一阶段原生分支从本地 Annex-B
*
原生分支:v4l2 采集 → mpph264enc(或 x264enc 回退)→ libdatachannel 发送。
*
H.264 文件发送;验证通路后,只替换输入为 MPP 实时编码,入口仍是这一条
。
*
音频尚未接入,视频跑通后再接 ALSA→Opus track
。
*/
*/
#define WEBRTCPUSH_USE_MPP 1
#define WEBRTCPUSH_USE_MPP 1
/* libdatachannel 第一阶段:720p Annex-B 测试流。 */
/* 正常固定 24fps;弱网以降码率为主,后续若启用动态帧率也不得低于 22fps。 */
#define WEBRTCPUSH_H264_FILE PROJECT_ROOT_DIR "/testdata/rtc_test_720p.h264"
#define WEBRTCPUSH_H264_FPS 24
#define WEBRTCPUSH_H264_FPS 25
#define WEBRTCPUSH_H264_MIN_FPS 22
#define WEBRTCPUSH_H264_GOP (WEBRTCPUSH_H264_FPS * 3)
/*
REMB 慢升快降,最高码率按当前要求限制为 3.2 Mbps。
*/
/*
与 gst_webrtc_pipeline / jywy 浏览器推流对齐的码率策略
*/
#define WEBRTCPUSH_INITIAL_BITRATE 1
000000U
#define WEBRTCPUSH_INITIAL_BITRATE 1
400000U
/* 起播 1.4 Mbps */
#define WEBRTCPUSH_MIN_BITRATE
25
0000U
#define WEBRTCPUSH_MIN_BITRATE
50
0000U
#define WEBRTCPUSH_MAX_BITRATE 3200000U
#define WEBRTCPUSH_MAX_BITRATE 3200000U
#define WEBRTCPUSH_REMB_UTIL_PERCENT 82U
/* 链路容量扣除音频/RTP 余量 */
#define WEBRTCPUSH_BITRATE_RAMP_MS 1000U
/* 每秒向目标码率逼近一档 */
#define WEBRTCPUSH_PACING_INTERVAL_MS 5U
#define WEBRTCPUSH_PACING_INTERVAL_MS 5U
#define WEBRTCPUSH_PACING_HEADROOM_PERCENT 120U
#define WEBRTCPUSH_PACING_MAX_BITRATE 3750000U
#define WEBRTCPUSH_NACK_PACKETS 1024U
#define WEBRTCPUSH_NACK_PACKETS 1024U
/* 视频采集:auto | /dev/video
0 | test(
测试图案) */
/* 视频采集:auto | /dev/video
N | test(videotestsrc
测试图案) */
#define WEBRTCPUSH_VIDEO_DEVICE "auto"
#define WEBRTCPUSH_VIDEO_DEVICE "auto"
/*
/*
* 麦克风采集(仅上行推流到手机,与车机喇叭播放 audioplay/tts 无关)。
* 麦克风采集(仅上行推流到手机,与车机喇叭播放 audioplay/tts 无关)。
* 留空表示不推音频;hw:X,Y 运行时会转为 plughw:X,Y,并优先经 Pulse 的 input 源采集。
* 留空表示不推音频;hw:X,Y 运行时会转为 plughw:X,Y,并优先经 Pulse 的 input 源采集。
*/
*/
/* auto:读 /proc/asound/pcm 选第一个 capture;hw:X,Y 固定卡号 */
#define WEBRTCPUSH_ALSA_DEVICE "auto"
#define WEBRTCPUSH_ALSA_DEVICE "auto"
/*
/*
...
@@ -41,10 +45,6 @@
...
@@ -41,10 +45,6 @@
/* WebRTC 信令 WebSocket 主机(路径 /websocket?dev=设备号) */
/* WebRTC 信令 WebSocket 主机(路径 /websocket?dev=设备号) */
#define WEBRTCPUSH_SIGNAL_HOST "signal.yd-ss.com"
#define WEBRTCPUSH_SIGNAL_HOST "signal.yd-ss.com"
/*
* 1:g_print / 关键步骤打 INFO(Release 下也可见);0:走 my_zlog_debug
* 编译 Debug:cmake -DCMAKE_BUILD_TYPE=Debug ..
*/
#define WEBRTCPUSH_DEBUG 0
#define WEBRTCPUSH_DEBUG 0
#endif
#endif
modules/webrtcpush/webrtcpush_run.c
View file @
2eb47ff8
...
@@ -172,7 +172,7 @@ int webrtcpush_run_blocking(void)
...
@@ -172,7 +172,7 @@ int webrtcpush_run_blocking(void)
s_app
.
prefer_mpp
=
TRUE
;
s_app
.
prefer_mpp
=
TRUE
;
s_app
.
device_offerer
=
FALSE
;
s_app
.
device_offerer
=
FALSE
;
s_app
.
verbose
=
WEBRTCPUSH_DEBUG
?
TRUE
:
FALSE
;
s_app
.
verbose
=
WEBRTCPUSH_DEBUG
?
TRUE
:
FALSE
;
s_app
.
enable_audio
=
FALSE
;
/*
Phase 1 validates the H.264 transport first.
*/
s_app
.
enable_audio
=
FALSE
;
/*
视频先行;音频后续 ALSA→Opus track
*/
s_app
.
loop
=
g_main_loop_new
(
NULL
,
FALSE
);
s_app
.
loop
=
g_main_loop_new
(
NULL
,
FALSE
);
...
@@ -198,9 +198,10 @@ int webrtcpush_run_blocking(void)
...
@@ -198,9 +198,10 @@ int webrtcpush_run_blocking(void)
}
}
s_running
=
TRUE
;
s_running
=
TRUE
;
my_zlog_info
(
"webrtcpush libdatachannel started dev=%s
fil
e=%s fps=%d bitrate=%u..%u"
,
my_zlog_info
(
"webrtcpush libdatachannel started dev=%s
devic
e=%s fps=%d bitrate=%u..%u"
,
s_app
.
dev_room
?
s_app
.
dev_room
:
"?"
,
s_app
.
dev_room
?
s_app
.
dev_room
:
"?"
,
WEBRTCPUSH_H264_FILE
,
WEBRTCPUSH_H264_FPS
,
s_app
.
video_device
?
s_app
.
video_device
:
WEBRTCPUSH_VIDEO_DEVICE
,
WEBRTCPUSH_H264_FPS
,
WEBRTCPUSH_INITIAL_BITRATE
,
WEBRTCPUSH_MAX_BITRATE
);
WEBRTCPUSH_INITIAL_BITRATE
,
WEBRTCPUSH_MAX_BITRATE
);
g_main_loop_run
(
s_app
.
loop
);
g_main_loop_run
(
s_app
.
loop
);
...
...
test/wifi_save_11_mqtt.jsonl
deleted
100644 → 0
View file @
edadce9a
{"head":{"message_type":2008},"body":{"wifi_ssid":"test_wifi_01","wifi_password":"pass0001"}}
{"head":{"message_type":2008},"body":{"wifi_ssid":"test_wifi_02","wifi_password":"pass0002"}}
{"head":{"message_type":2008},"body":{"wifi_ssid":"test_wifi_03","wifi_password":"pass0003"}}
{"head":{"message_type":2008},"body":{"wifi_ssid":"test_wifi_04","wifi_password":"pass0004"}}
{"head":{"message_type":2008},"body":{"wifi_ssid":"test_wifi_05","wifi_password":"pass0005"}}
{"head":{"message_type":2008},"body":{"wifi_ssid":"test_wifi_06","wifi_password":"pass0006"}}
{"head":{"message_type":2008},"body":{"wifi_ssid":"test_wifi_07","wifi_password":"pass0007"}}
{"head":{"message_type":2008},"body":{"wifi_ssid":"test_wifi_08","wifi_password":"pass0008"}}
{"head":{"message_type":2008},"body":{"wifi_ssid":"test_wifi_09","wifi_password":"pass0009"}}
{"head":{"message_type":2008},"body":{"wifi_ssid":"test_wifi_10","wifi_password":"pass0010"}}
{"head":{"message_type":2008},"body":{"wifi_ssid":"test_wifi_11","wifi_password":"pass0011"}}
testdata/rtc_test_720p.h264
deleted
100644 → 0
View file @
edadce9a
File deleted
third_party/libdatachannel/include/rtc/pacinghandler.hpp
View file @
2eb47ff8
...
@@ -25,6 +25,7 @@ class RTC_CPP_EXPORT PacingHandler : public MediaHandler {
...
@@ -25,6 +25,7 @@ class RTC_CPP_EXPORT PacingHandler : public MediaHandler {
public
:
public
:
PacingHandler
(
double
bitsPerSecond
,
std
::
chrono
::
milliseconds
sendInterval
);
PacingHandler
(
double
bitsPerSecond
,
std
::
chrono
::
milliseconds
sendInterval
);
void
setBitrate
(
double
bitsPerSecond
);
void
setBitrate
(
double
bitsPerSecond
);
size_t
clear
();
void
outgoing
(
message_vector
&
messages
,
const
message_callback
&
send
)
override
;
void
outgoing
(
message_vector
&
messages
,
const
message_callback
&
send
)
override
;
...
...
third_party/libdatachannel/include/rtc/rtc.h
View file @
2eb47ff8
...
@@ -430,6 +430,10 @@ RTC_C_EXPORT int rtcChainPacingHandler(int tr, unsigned int bitrate,
...
@@ -430,6 +430,10 @@ RTC_C_EXPORT int rtcChainPacingHandler(int tr, unsigned int bitrate,
// Update a previously chained pacing handler without rebuilding the track
// Update a previously chained pacing handler without rebuilding the track
RTC_C_EXPORT
int
rtcSetPacingBitrate
(
int
tr
,
unsigned
int
bitrate
);
RTC_C_EXPORT
int
rtcSetPacingBitrate
(
int
tr
,
unsigned
int
bitrate
);
// Drop queued RTP packets before resynchronizing with a fresh key frame.
// Returns the number of dropped packets, or a negative RTC_ERR_* value.
RTC_C_EXPORT
int
rtcClearPacingQueue
(
int
tr
);
// Transform seconds to timestamp using track's clock rate, result is written to timestamp
// Transform seconds to timestamp using track's clock rate, result is written to timestamp
RTC_C_EXPORT
int
rtcTransformSecondsToTimestamp
(
int
id
,
double
seconds
,
uint32_t
*
timestamp
);
RTC_C_EXPORT
int
rtcTransformSecondsToTimestamp
(
int
id
,
double
seconds
,
uint32_t
*
timestamp
);
...
...
third_party/libdatachannel/include/rtc/rtcpnackresponder.hpp
View file @
2eb47ff8
...
@@ -23,6 +23,7 @@ public:
...
@@ -23,6 +23,7 @@ public:
static
const
size_t
DefaultMaxSize
=
512
;
static
const
size_t
DefaultMaxSize
=
512
;
RtcpNackResponder
(
size_t
maxSize
=
DefaultMaxSize
);
RtcpNackResponder
(
size_t
maxSize
=
DefaultMaxSize
);
void
clear
();
void
incoming
(
message_vector
&
messages
,
const
message_callback
&
send
)
override
;
void
incoming
(
message_vector
&
messages
,
const
message_callback
&
send
)
override
;
void
outgoing
(
message_vector
&
messages
,
const
message_callback
&
send
)
override
;
void
outgoing
(
message_vector
&
messages
,
const
message_callback
&
send
)
override
;
...
@@ -64,6 +65,9 @@ private:
...
@@ -64,6 +65,9 @@ private:
/// Stores packet
/// Stores packet
/// @param packet Packet
/// @param packet Packet
void
store
(
message_ptr
packet
);
void
store
(
message_ptr
packet
);
/// Drops all packets retained for retransmission.
void
clear
();
};
};
const
shared_ptr
<
Storage
>
mStorage
;
const
shared_ptr
<
Storage
>
mStorage
;
...
...
third_party/libdatachannel/src/capi.cpp
View file @
2eb47ff8
...
@@ -14,6 +14,7 @@
...
@@ -14,6 +14,7 @@
#include <algorithm>
#include <algorithm>
#include <chrono>
#include <chrono>
#include <exception>
#include <exception>
#include <limits>
#include <mutex>
#include <mutex>
#include <type_traits>
#include <type_traits>
#include <unordered_map>
#include <unordered_map>
...
@@ -32,6 +33,7 @@ std::unordered_map<int, shared_ptr<Track>> trackMap;
...
@@ -32,6 +33,7 @@ std::unordered_map<int, shared_ptr<Track>> trackMap;
std
::
unordered_map
<
int
,
shared_ptr
<
RtcpSrReporter
>>
rtcpSrReporterMap
;
std
::
unordered_map
<
int
,
shared_ptr
<
RtcpSrReporter
>>
rtcpSrReporterMap
;
std
::
unordered_map
<
int
,
shared_ptr
<
RtpPacketizationConfig
>>
rtpConfigMap
;
std
::
unordered_map
<
int
,
shared_ptr
<
RtpPacketizationConfig
>>
rtpConfigMap
;
std
::
unordered_map
<
int
,
shared_ptr
<
PacingHandler
>>
pacingHandlerMap
;
std
::
unordered_map
<
int
,
shared_ptr
<
PacingHandler
>>
pacingHandlerMap
;
std
::
unordered_map
<
int
,
shared_ptr
<
RtcpNackResponder
>>
rtcpNackResponderMap
;
#endif
#endif
#if RTC_ENABLE_WEBSOCKET
#if RTC_ENABLE_WEBSOCKET
std
::
unordered_map
<
int
,
shared_ptr
<
WebSocket
>>
webSocketMap
;
std
::
unordered_map
<
int
,
shared_ptr
<
WebSocket
>>
webSocketMap
;
...
@@ -122,6 +124,7 @@ void eraseTrack(int tr) {
...
@@ -122,6 +124,7 @@ void eraseTrack(int tr) {
rtcpSrReporterMap
.
erase
(
tr
);
rtcpSrReporterMap
.
erase
(
tr
);
rtpConfigMap
.
erase
(
tr
);
rtpConfigMap
.
erase
(
tr
);
pacingHandlerMap
.
erase
(
tr
);
pacingHandlerMap
.
erase
(
tr
);
rtcpNackResponderMap
.
erase
(
tr
);
#endif
#endif
userPointerMap
.
erase
(
tr
);
userPointerMap
.
erase
(
tr
);
}
}
...
@@ -133,10 +136,12 @@ size_t eraseAll() {
...
@@ -133,10 +136,12 @@ size_t eraseAll() {
trackMap
.
clear
();
trackMap
.
clear
();
peerConnectionMap
.
clear
();
peerConnectionMap
.
clear
();
#if RTC_ENABLE_MEDIA
#if RTC_ENABLE_MEDIA
count
+=
rtcpSrReporterMap
.
size
()
+
rtpConfigMap
.
size
()
+
pacingHandlerMap
.
size
();
count
+=
rtcpSrReporterMap
.
size
()
+
rtpConfigMap
.
size
()
+
pacingHandlerMap
.
size
()
+
rtcpNackResponderMap
.
size
();
rtcpSrReporterMap
.
clear
();
rtcpSrReporterMap
.
clear
();
rtpConfigMap
.
clear
();
rtpConfigMap
.
clear
();
pacingHandlerMap
.
clear
();
pacingHandlerMap
.
clear
();
rtcpNackResponderMap
.
clear
();
#endif
#endif
#if RTC_ENABLE_WEBSOCKET
#if RTC_ENABLE_WEBSOCKET
count
+=
webSocketMap
.
size
()
+
webSocketServerMap
.
size
();
count
+=
webSocketMap
.
size
()
+
webSocketServerMap
.
size
();
...
@@ -172,6 +177,7 @@ void eraseChannel(int id) {
...
@@ -172,6 +177,7 @@ void eraseChannel(int id) {
rtcpSrReporterMap
.
erase
(
id
);
rtcpSrReporterMap
.
erase
(
id
);
rtpConfigMap
.
erase
(
id
);
rtpConfigMap
.
erase
(
id
);
pacingHandlerMap
.
erase
(
id
);
pacingHandlerMap
.
erase
(
id
);
rtcpNackResponderMap
.
erase
(
id
);
#endif
#endif
return
;
return
;
}
}
...
@@ -1388,6 +1394,10 @@ int rtcChainRtcpNackResponder(int tr, unsigned int maxStoredPacketsCount) {
...
@@ -1388,6 +1394,10 @@ int rtcChainRtcpNackResponder(int tr, unsigned int maxStoredPacketsCount) {
auto
track
=
getTrack
(
tr
);
auto
track
=
getTrack
(
tr
);
auto
responder
=
std
::
make_shared
<
RtcpNackResponder
>
(
maxStoredPacketsCount
);
auto
responder
=
std
::
make_shared
<
RtcpNackResponder
>
(
maxStoredPacketsCount
);
track
->
chainMediaHandler
(
responder
);
track
->
chainMediaHandler
(
responder
);
{
std
::
lock_guard
lock
(
mutex
);
rtcpNackResponderMap
[
tr
]
=
responder
;
}
return
RTC_ERR_SUCCESS
;
return
RTC_ERR_SUCCESS
;
});
});
}
}
...
@@ -1449,6 +1459,29 @@ int rtcSetPacingBitrate(int tr, unsigned int bitrate) {
...
@@ -1449,6 +1459,29 @@ int rtcSetPacingBitrate(int tr, unsigned int bitrate) {
});
});
}
}
int
rtcClearPacingQueue
(
int
tr
)
{
return
wrap
([
&
]
{
shared_ptr
<
PacingHandler
>
handler
;
shared_ptr
<
RtcpNackResponder
>
nackResponder
;
{
std
::
lock_guard
lock
(
mutex
);
auto
it
=
pacingHandlerMap
.
find
(
tr
);
if
(
it
==
pacingHandlerMap
.
end
())
throw
std
::
invalid_argument
(
"Pacing handler does not exist"
);
handler
=
it
->
second
;
if
(
auto
nackIt
=
rtcpNackResponderMap
.
find
(
tr
);
nackIt
!=
rtcpNackResponderMap
.
end
())
nackResponder
=
nackIt
->
second
;
}
const
size_t
count
=
handler
->
clear
();
if
(
nackResponder
)
nackResponder
->
clear
();
return
count
>
static_cast
<
size_t
>
(
std
::
numeric_limits
<
int
>::
max
())
?
std
::
numeric_limits
<
int
>::
max
()
:
static_cast
<
int
>
(
count
);
});
}
int
rtcTransformSecondsToTimestamp
(
int
id
,
double
seconds
,
uint32_t
*
timestamp
)
{
int
rtcTransformSecondsToTimestamp
(
int
id
,
double
seconds
,
uint32_t
*
timestamp
)
{
return
wrap
([
&
]
{
return
wrap
([
&
]
{
auto
config
=
getRtpConfig
(
id
);
auto
config
=
getRtpConfig
(
id
);
...
...
third_party/libdatachannel/src/pacinghandler.cpp
View file @
2eb47ff8
...
@@ -24,6 +24,15 @@ void PacingHandler::setBitrate(double bitsPerSecond) {
...
@@ -24,6 +24,15 @@ void PacingHandler::setBitrate(double bitsPerSecond) {
mBytesPerSecond
.
store
(
bitsPerSecond
/
8
);
mBytesPerSecond
.
store
(
bitsPerSecond
/
8
);
}
}
size_t
PacingHandler
::
clear
()
{
std
::
lock_guard
<
std
::
mutex
>
lock
(
mMutex
);
const
size_t
count
=
mRtpBuffer
.
size
();
while
(
!
mRtpBuffer
.
empty
())
mRtpBuffer
.
pop
();
mBudget
=
0.
;
return
count
;
}
void
PacingHandler
::
schedule
(
const
message_callback
&
send
)
{
void
PacingHandler
::
schedule
(
const
message_callback
&
send
)
{
if
(
mHaveScheduled
.
exchange
(
true
))
{
if
(
mHaveScheduled
.
exchange
(
true
))
{
return
;
return
;
...
...
third_party/libdatachannel/src/rtcpnackresponder.cpp
View file @
2eb47ff8
...
@@ -20,6 +20,8 @@ namespace rtc {
...
@@ -20,6 +20,8 @@ namespace rtc {
RtcpNackResponder
::
RtcpNackResponder
(
size_t
maxSize
)
RtcpNackResponder
::
RtcpNackResponder
(
size_t
maxSize
)
:
mStorage
(
std
::
make_shared
<
Storage
>
(
maxSize
))
{}
:
mStorage
(
std
::
make_shared
<
Storage
>
(
maxSize
))
{}
void
RtcpNackResponder
::
clear
()
{
mStorage
->
clear
();
}
void
RtcpNackResponder
::
incoming
(
message_vector
&
messages
,
const
message_callback
&
send
)
{
void
RtcpNackResponder
::
incoming
(
message_vector
&
messages
,
const
message_callback
&
send
)
{
for
(
const
auto
&
message
:
messages
)
{
for
(
const
auto
&
message
:
messages
)
{
if
(
message
->
type
!=
Message
::
Control
)
if
(
message
->
type
!=
Message
::
Control
)
...
@@ -108,6 +110,13 @@ void RtcpNackResponder::Storage::store(message_ptr packet) {
...
@@ -108,6 +110,13 @@ void RtcpNackResponder::Storage::store(message_ptr packet) {
}
}
}
}
void
RtcpNackResponder
::
Storage
::
clear
()
{
std
::
lock_guard
lock
(
mutex
);
storage
.
clear
();
oldest
.
reset
();
newest
.
reset
();
}
}
// namespace rtc
}
// namespace rtc
#endif
/* RTC_ENABLE_MEDIA */
#endif
/* RTC_ENABLE_MEDIA */
zlog.conf
View file @
2eb47ff8
...
@@ -9,4 +9,4 @@ file perms = 600
...
@@ -9,4 +9,4 @@ file perms = 600
millisecond
=
"%d(%Y-%m-%d %H:%M:%S).%ms [%V] %m%n"
millisecond
=
"%d(%Y-%m-%d %H:%M:%S).%ms [%V] %m%n"
[
rules
]
[
rules
]
my_log
.*
"/home/orangepi/car/master/log/log_2026-0
6-30
.log"
;
millisecond
my_log
.*
"/home/orangepi/car/master/log/log_2026-0
7-01
.log"
;
millisecond
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment