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
3585cc8c
Commit
3585cc8c
authored
Jul 10, 2026
by
957dd
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
c语音推流使用cpu软件解码,硬件h264编码,提升流畅度
parent
4687e6c8
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
172 additions
and
274 deletions
+172
-274
main
build/main
+0
-0
audioplay.c
drivers/sensors/audioplay.c
+99
-11
audioplay.c.bak.20260710_111731
drivers/sensors/audioplay.c.bak.20260710_111731
+0
-0
CMakeLists.txt
modules/CMakeLists.txt
+4
-0
audio_sink.c
modules/webrtcpush/audio_sink.c
+23
-243
audio_sink.h
modules/webrtcpush/audio_sink.h
+8
-0
mpp_h264_source.c
modules/webrtcpush/mpp_h264_source.c
+0
-0
rtc_client.c
modules/webrtcpush/rtc_client.c
+0
-0
webrtcpush_config.h
modules/webrtcpush/webrtcpush_config.h
+37
-19
zlog.conf
zlog.conf
+1
-1
No files found.
build/main
View file @
3585cc8c
No preview for this file type
drivers/sensors/audioplay.c
View file @
3585cc8c
#include "common.h"
#include "audioplay.h"
#include "audio_sink.h"
/* USB声卡排队锁 */
#include "device_identity.h"
#include "mqtt_init.h"
#include "http_config_mqtt.h"
#include "audiotts_play.h"
#include "wifi_autoconfig.h"
#include <stdio.h>
#include <pthread.h>
#include <strings.h>
#include <unistd.h>
#include <sys/wait.h>
#define AUDIO_USB_ALSA_DEVICE "hw:2,0"
#define AUDIO_LOCAL_ALSA_DEVICE "plughw:2,0"
#define AUDIO_LOCAL_PLAY_TIMEOUT_SEC 8
static
int
s_audio_status
=
7
;
static
char
s_urlbuf
[
512
];
...
...
@@ -21,6 +25,7 @@ static double s_audio_volume=0.8;
static
int
s_local_play_pending
=
0
;
static
char
s_local_filepath
[
512
];
static
pthread_mutex_t
s_local_play_mutex
=
PTHREAD_MUTEX_INITIALIZER
;
static
int
local_is_cn_lang
(
const
char
*
lang
)
{
return
lang
!=
NULL
&&
(
strcmp
(
lang
,
AUDIO_LANG_ZH
)
==
0
||
strcmp
(
lang
,
"cn"
)
==
0
);
...
...
@@ -74,12 +79,16 @@ static int local_resolve_filepath(const char *filename, const char *language_ove
}
static
void
local_queue_play
(
const
char
*
filename
,
const
char
*
language
)
{
if
(
!
local_resolve_filepath
(
filename
,
language
,
s_local_filepath
,
sizeof
(
s_local_filepath
)))
{
char
resolved
[
512
];
if
(
!
local_resolve_filepath
(
filename
,
language
,
resolved
,
sizeof
(
resolved
)))
{
my_zlog_warn
(
"2017 本地音频不存在: %s"
,
filename
);
return
;
}
pthread_mutex_lock
(
&
s_local_play_mutex
);
snprintf
(
s_local_filepath
,
sizeof
(
s_local_filepath
),
"%s"
,
resolved
);
s_local_play_pending
=
1
;
my_zlog_info
(
"2017 已排队本地音频: %s"
,
s_local_filepath
);
pthread_mutex_unlock
(
&
s_local_play_mutex
);
my_zlog_info
(
"2017 已排队本地音频: %s"
,
resolved
);
}
void
audioplay_local_mqtt_receive
(
cJSON
*
body
)
{
...
...
@@ -143,6 +152,76 @@ static double audioplay_volume_clamp(double v) {
int
audio_wheat_init
();
static
int
audio_system_exit_code
(
int
status
)
{
if
(
status
==
-
1
)
{
return
-
1
;
}
if
(
WIFEXITED
(
status
))
{
return
WEXITSTATUS
(
status
);
}
if
(
WIFSIGNALED
(
status
))
{
return
128
+
WTERMSIG
(
status
);
}
return
-
1
;
}
static
void
shell_single_quote
(
char
*
out
,
size_t
size
,
const
char
*
in
)
{
size_t
pos
=
0
;
if
(
!
out
||
size
==
0
)
{
return
;
}
out
[
pos
++
]
=
'\''
;
if
(
in
)
{
for
(
const
char
*
p
=
in
;
*
p
&&
pos
+
5
<
size
;
p
++
)
{
if
(
*
p
==
'\''
)
{
const
char
*
esc
=
"'
\\
''"
;
for
(
const
char
*
e
=
esc
;
*
e
&&
pos
+
1
<
size
;
e
++
)
{
out
[
pos
++
]
=
*
e
;
}
}
else
{
out
[
pos
++
]
=
*
p
;
}
}
}
if
(
pos
+
1
<
size
)
{
out
[
pos
++
]
=
'\''
;
}
out
[
pos
]
=
'\0'
;
}
static
int
play_local_audio_file
(
const
char
*
filepath
)
{
char
quoted_path
[
1024
];
char
command
[
2048
];
int
ret
;
int
exit_code
;
shell_single_quote
(
quoted_path
,
sizeof
(
quoted_path
),
filepath
);
/* 排队等待USB声卡: 如果audio_sink正在播放手机音频, 等它释放 */
audio_sink_lock_alsa
();
snprintf
(
command
,
sizeof
(
command
),
/* sync=true 防止 EOS 提前关 ALSA 丢尾部(只播前半段); channels=2 上混规避 USB 声卡单声道 ring_buffer CRITICAL */
"timeout %ds gst-launch-1.0 -q filesrc location=%s ! mpegaudioparse ! mpg123audiodec ! audioconvert ! audioresample ! audio/x-raw,channels=2 ! alsasink device=%s sync=true >/dev/null 2>&1"
,
AUDIO_LOCAL_PLAY_TIMEOUT_SEC
,
quoted_path
,
AUDIO_LOCAL_ALSA_DEVICE
);
ret
=
system
(
command
);
exit_code
=
audio_system_exit_code
(
ret
);
if
(
exit_code
!=
0
)
{
my_zlog_warn
(
"本地音频 GStreamer 播放失败 exit=%d,尝试 ffplay: %s"
,
exit_code
,
filepath
);
snprintf
(
command
,
sizeof
(
command
),
"timeout %ds ffplay -nodisp -autoexit -loglevel warning %s"
,
AUDIO_LOCAL_PLAY_TIMEOUT_SEC
,
quoted_path
);
ret
=
system
(
command
);
exit_code
=
audio_system_exit_code
(
ret
);
}
audio_sink_unlock_alsa
();
return
exit_code
;
}
//接收音频播放
void
audioplay_mqtt_receive
(
cJSON
*
json
)
{
// 解析"audioLink"字段(修正了原始JSON中的拼写错误)
...
...
@@ -212,7 +291,7 @@ void audioplay_send_mqtt() {
//音频播放
void
audioplay_cycle
(){
char
command
[
1024
];
char
command
[
2048
];
int
ret
;
while
(
1
){
if
(
s_audio_status
==
0
){
...
...
@@ -241,16 +320,25 @@ void audioplay_cycle(){
audioplay_send_mqtt
();
}
char
local_filepath
[
sizeof
(
s_local_filepath
)];
int
local_play_pending
=
0
;
pthread_mutex_lock
(
&
s_local_play_mutex
);
if
(
s_local_play_pending
)
{
s_local_play_pending
=
0
;
snprintf
(
command
,
sizeof
(
command
),
"ffplay -nodisp -autoexit -loglevel quiet
\"
%s
\"
"
,
s_local_filepath
);
my_zlog_debug
(
"播放本地音频: %s"
,
s_local_filepath
);
ret
=
system
(
command
);
if
(
WIFEXITED
(
ret
)
&&
WEXITSTATUS
(
ret
)
==
0
)
{
my_zlog_debug
(
"本地音频播放完成: %s"
,
s_local_filepath
);
snprintf
(
local_filepath
,
sizeof
(
local_filepath
),
"%s"
,
s_local_filepath
);
local_play_pending
=
1
;
}
pthread_mutex_unlock
(
&
s_local_play_mutex
);
if
(
local_play_pending
)
{
int
exit_code
;
my_zlog_info
(
"播放本地音频: %s"
,
local_filepath
);
exit_code
=
play_local_audio_file
(
local_filepath
);
if
(
exit_code
==
0
)
{
my_zlog_debug
(
"本地音频播放完成: %s"
,
local_filepath
);
}
else
{
my_zlog_warn
(
"本地音频播放失败
: %s"
,
s_
local_filepath
);
my_zlog_warn
(
"本地音频播放失败
exit=%d: %s"
,
exit_code
,
local_filepath
);
}
}
...
...
@@ -374,7 +462,7 @@ int audio_speaker_init() {
int
audio_init
(){
delay_s
(
5
);
delay_s
(
1
);
audio_wheat_init
();
delay_s
(
1
);
audio_speaker_init
();
...
...
drivers/sensors/audioplay.c.bak.20260710_111731
0 → 100644
View file @
3585cc8c
This diff is collapsed.
Click to expand it.
modules/CMakeLists.txt
View file @
3585cc8c
...
...
@@ -8,6 +8,7 @@ pkg_check_modules(WEBRTCPUSH_GST REQUIRED
)
pkg_check_modules
(
WEBRTCPUSH_JSON REQUIRED json-glib-1.0
)
pkg_check_modules
(
WEBRTCPUSH_SOUP REQUIRED libsoup-2.4
)
pkg_check_modules
(
WEBRTCPUSH_JPEG REQUIRED libjpeg
)
file
(
GLOB_RECURSE MODULES_SOURCES
${
CMAKE_CURRENT_SOURCE_DIR
}
/logger/*.c
...
...
@@ -47,6 +48,7 @@ set(MODULES_INCLUDE_DIRS
${
WEBRTCPUSH_GST_INCLUDE_DIRS
}
${
WEBRTCPUSH_JSON_INCLUDE_DIRS
}
${
WEBRTCPUSH_SOUP_INCLUDE_DIRS
}
${
WEBRTCPUSH_JPEG_INCLUDE_DIRS
}
PARENT_SCOPE
)
...
...
@@ -54,6 +56,7 @@ set(WEBRTCPUSH_LIBRARIES
${
WEBRTCPUSH_GST_LIBRARIES
}
${
WEBRTCPUSH_JSON_LIBRARIES
}
${
WEBRTCPUSH_SOUP_LIBRARIES
}
${
WEBRTCPUSH_JPEG_LIBRARIES
}
PARENT_SCOPE
)
...
...
@@ -61,5 +64,6 @@ set(WEBRTCPUSH_CFLAGS
${
WEBRTCPUSH_GST_CFLAGS_OTHER
}
${
WEBRTCPUSH_JSON_CFLAGS_OTHER
}
${
WEBRTCPUSH_SOUP_CFLAGS_OTHER
}
${
WEBRTCPUSH_JPEG_CFLAGS_OTHER
}
PARENT_SCOPE
)
modules/webrtcpush/audio_sink.c
View file @
3585cc8c
#include "audio_sink.h"
#include "webrtcpush_config.h"
#include "webrtcpush_log.h"
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
/* USB声卡排队锁: audio_sink 和本地音频串行访问 plughw:2,0 */
static
GMutex
g_alsa_device_lock
;
static
AudioSink
*
g_audio_sink_singleton
=
NULL
;
/*
* 手机→设备方向的音频播放管道(按键模式):
* appsrc(opus payload) → opusdec → audioconvert → audioresample → volume → alsasink
*
* 手机端是"按住说话"模式(最长 15s),不是持续推流,因此:
* - on_audio_message 收到包后入队,立即返回(不阻塞 libdatachannel 线程)
* - 独立线程消费队列,维护"按键会话"
* - 会话开始(首包到达):pipeline 切到 PLAYING
* - 会话结束(500ms 无包 或 15s 超时):flush appsrc + pipeline 切到 READY
* 切到 READY 释放 ALSA 设备,避免 alsasink 持续占用/空转
* 手机→设备方向的音频不再走 RTP:手机端 audio 为 recvonly(只收不发),
* 手机→设备的音频通过 DataChannel 发送 MP4,由 decodebin 播放。
* 因此本模块不再创建 GStreamer 接收管道,仅保留 USB 声卡全局锁,
* 供 DataChannel 播放线程与本地提示音串行访问 ALSA 设备使用。
*/
#define AUDIO_SINK_SESSION_TIMEOUT_MS 500
/* 500ms 无包认为按键结束 */
#define AUDIO_SINK_SESSION_MAX_MS 15000
/* 单次按键最长 15s */
typedef
struct
{
uint8_t
*
data
;
size_t
size
;
}
SinkPacket
;
struct
AudioSink
{
GstElement
*
pipeline
;
GstElement
*
appsrc
;
GstElement
*
vol
;
GAsyncQueue
*
queue
;
/* 待处理 Opus 包队列 */
GMutex
lock
;
/* 保护 pipeline 状态切换 */
GThread
*
thread
;
/* 消费线程 */
gboolean
quit
;
/* 退出标志 */
gint64
session_start_us
;
/* 当前按键会话开始时间(0=无会话) */
gint64
last_packet_us
;
/* 最后一个包到达时间 */
gboolean
pipeline_playing
;
/* pipeline 当前是否 PLAYING */
GMutex
lock
;
};
static
void
flush_queue
(
AudioSink
*
src
)
{
SinkPacket
*
pkt
;
while
((
pkt
=
g_async_queue_try_pop
(
src
->
queue
))
!=
NULL
)
{
g_free
(
pkt
->
data
);
g_free
(
pkt
);
}
}
void
audio_sink_lock_alsa
(
void
)
{
g_mutex_lock
(
&
g_alsa_device_lock
);
}
void
audio_sink_unlock_alsa
(
void
)
{
g_mutex_unlock
(
&
g_alsa_device_lock
);
}
static
void
set_pipeline_state_locked
(
AudioSink
*
src
,
GstState
state
)
void
audio_sink_interrupt
(
AudioSink
*
src
)
{
if
(
!
src
->
pipeline
)
return
;
/* 切到 READY 时 flush appsrc,避免旧数据残留导致下次会话首帧异常 */
if
(
state
==
GST_STATE_READY
&&
src
->
appsrc
)
{
gst_element_send_event
(
src
->
pipeline
,
gst_event_new_flush_start
());
gst_element_send_event
(
src
->
pipeline
,
gst_event_new_flush_stop
(
FALSE
));
}
gst_element_set_state
(
src
->
pipeline
,
state
);
src
->
pipeline_playing
=
(
state
==
GST_STATE_PLAYING
);
}
static
gpointer
audio_sink_thread
(
gpointer
data
)
{
AudioSink
*
src
=
data
;
gint64
now
;
GstBuffer
*
buf
;
while
(
1
)
{
/* 等待包,超时 100ms 用于检查会话超时 */
SinkPacket
*
pkt
=
g_async_queue_timeout_pop
(
src
->
queue
,
100
*
1000
);
now
=
g_get_monotonic_time
();
g_mutex_lock
(
&
src
->
lock
);
if
(
src
->
quit
)
{
g_mutex_unlock
(
&
src
->
lock
);
if
(
pkt
)
{
g_free
(
pkt
->
data
);
g_free
(
pkt
);
}
break
;
}
/* 会话超时检查 */
if
(
src
->
session_start_us
>
0
)
{
gint64
idle_ms
=
(
now
-
src
->
last_packet_us
)
/
1000
;
gint64
sess_ms
=
(
now
-
src
->
session_start_us
)
/
1000
;
if
(
idle_ms
>=
AUDIO_SINK_SESSION_TIMEOUT_MS
||
sess_ms
>=
AUDIO_SINK_SESSION_MAX_MS
)
{
if
(
src
->
pipeline_playing
)
{
set_pipeline_state_locked
(
src
,
GST_STATE_READY
);
my_zlog_info
(
"audio_sink: session ended (idle=%lldms sess=%lldms)"
,
(
long
long
)
idle_ms
,
(
long
long
)
sess_ms
);
}
src
->
session_start_us
=
0
;
flush_queue
(
src
);
/* 丢弃超时后到达的旧包 */
if
(
pkt
)
{
g_free
(
pkt
->
data
);
g_free
(
pkt
);
pkt
=
NULL
;
}
g_mutex_unlock
(
&
src
->
lock
);
continue
;
}
}
if
(
pkt
)
{
/* 新会话开始 */
if
(
src
->
session_start_us
==
0
)
{
src
->
session_start_us
=
now
;
my_zlog_info
(
"audio_sink: session start"
);
if
(
!
src
->
pipeline_playing
)
set_pipeline_state_locked
(
src
,
GST_STATE_PLAYING
);
}
src
->
last_packet_us
=
now
;
/* push 到 appsrc(pipeline PLAYING 状态) */
if
(
src
->
appsrc
&&
src
->
pipeline_playing
)
{
buf
=
gst_buffer_new_wrapped
(
g_memdup2
(
pkt
->
data
,
pkt
->
size
),
pkt
->
size
);
GST_BUFFER_DTS
(
buf
)
=
GST_CLOCK_TIME_NONE
;
GST_BUFFER_PTS
(
buf
)
=
GST_CLOCK_TIME_NONE
;
if
(
gst_app_src_push_buffer
(
GST_APP_SRC
(
src
->
appsrc
),
buf
)
!=
GST_FLOW_OK
)
{
my_zlog_warn
(
"audio_sink: push_buffer failed"
);
}
}
g_free
(
pkt
->
data
);
g_free
(
pkt
);
}
g_mutex_unlock
(
&
src
->
lock
);
}
return
NULL
;
(
void
)
src
;
/* RTP 接收管道已移除,无会话需要中断;DataChannel 播放通过 lock/unlock 串行访问 ALSA。 */
}
AudioSink
*
audio_sink_start
(
const
char
*
alsa_device
,
char
**
error_message
)
{
AudioSink
*
src
;
GstElement
*
pipe
,
*
asrc
,
*
dec
,
*
conv
,
*
resample
,
*
vol
,
*
sink
;
GstCaps
*
caps
;
GstStateChangeReturn
ret
;
if
(
!
alsa_device
||
!
alsa_device
[
0
])
{
(
void
)
alsa_device
;
if
(
error_message
)
*
error_message
=
g_strdup
(
"no ALSA device"
);
return
NULL
;
}
*
error_message
=
NULL
;
src
=
g_new0
(
AudioSink
,
1
);
pipe
=
gst_pipeline_new
(
"audio-sink-pipe"
);
asrc
=
gst_element_factory_make
(
"appsrc"
,
"asrc"
);
dec
=
gst_element_factory_make
(
"opusdec"
,
"dec"
);
conv
=
gst_element_factory_make
(
"audioconvert"
,
"conv"
);
resample
=
gst_element_factory_make
(
"audioresample"
,
"resample"
);
vol
=
gst_element_factory_make
(
"volume"
,
"vol"
);
sink
=
gst_element_factory_make
(
"alsasink"
,
"sink"
);
if
(
!
pipe
||
!
asrc
||
!
dec
||
!
conv
||
!
resample
||
!
vol
||
!
sink
)
{
if
(
error_message
)
*
error_message
=
g_strdup
(
"failed to create audio sink GStreamer elements"
);
if
(
pipe
)
gst_object_unref
(
pipe
);
g_free
(
src
);
return
NULL
;
}
caps
=
gst_caps_new_empty_simple
(
"audio/x-opus"
);
g_object_set
(
asrc
,
"caps"
,
caps
,
"format"
,
GST_FORMAT_BYTES
,
"is-live"
,
TRUE
,
"emit-signals"
,
FALSE
,
"min-latency"
,
(
gint64
)
0
,
"max-bytes"
,
(
guint64
)(
1
*
1024
*
1024
),
NULL
);
gst_caps_unref
(
caps
);
g_object_set
(
sink
,
"device"
,
alsa_device
,
"buffer-time"
,
(
gint64
)
20000
,
"latency-time"
,
(
gint64
)
10000
,
"sync"
,
FALSE
,
NULL
);
g_object_set
(
vol
,
"volume"
,
0
.
5
,
NULL
);
gst_bin_add_many
(
GST_BIN
(
pipe
),
asrc
,
dec
,
conv
,
resample
,
vol
,
sink
,
NULL
);
if
(
!
gst_element_link_many
(
asrc
,
dec
,
conv
,
resample
,
vol
,
sink
,
NULL
))
{
if
(
error_message
)
*
error_message
=
g_strdup
(
"failed to link audio sink chain"
);
gst_object_unref
(
pipe
);
g_free
(
src
);
return
NULL
;
}
src
->
pipeline
=
pipe
;
src
->
appsrc
=
asrc
;
src
->
vol
=
vol
;
src
->
queue
=
g_async_queue_new
();
g_mutex_init
(
&
src
->
lock
);
src
->
session_start_us
=
0
;
src
->
pipeline_playing
=
FALSE
;
/* 初始状态 READY(不占 ALSA 设备,等首包到来再 PLAYING) */
ret
=
gst_element_set_state
(
pipe
,
GST_STATE_READY
);
if
(
ret
==
GST_STATE_CHANGE_FAILURE
)
{
if
(
error_message
)
*
error_message
=
g_strdup
(
"audio sink pipeline failed to reach READY"
);
gst_element_set_state
(
pipe
,
GST_STATE_NULL
);
gst_object_unref
(
pipe
);
g_async_queue_unref
(
src
->
queue
);
g_mutex_clear
(
&
src
->
lock
);
g_free
(
src
);
return
NULL
;
}
src
->
thread
=
g_thread_new
(
"audio-sink"
,
audio_sink_thread
,
src
);
if
(
!
src
->
thread
)
{
if
(
error_message
)
*
error_message
=
g_strdup
(
"failed to create audio sink thread"
);
audio_sink_stop
(
src
);
return
NULL
;
}
my_zlog_info
(
"audio_sink: started device=%s opus=%uch %uHz (push-to-talk)"
,
alsa_device
,
WEBRTCPUSH_OPUS_CHANNELS
,
WEBRTCPUSH_OPUS_CLOCKRATE
);
g_audio_sink_singleton
=
src
;
my_zlog_info
(
"audio_sink: started (RTP receive removed, ALSA lock only)"
);
return
src
;
}
...
...
@@ -233,45 +43,15 @@ void audio_sink_stop(AudioSink *src)
{
if
(
!
src
)
return
;
if
(
src
->
thread
)
{
g_mutex_lock
(
&
src
->
lock
);
src
->
quit
=
TRUE
;
g_mutex_unlock
(
&
src
->
lock
);
g_thread_join
(
src
->
thread
);
src
->
thread
=
NULL
;
}
if
(
src
->
pipeline
)
{
gst_element_set_state
(
src
->
pipeline
,
GST_STATE_NULL
);
gst_object_unref
(
src
->
pipeline
);
}
if
(
src
->
queue
)
{
flush_queue
(
src
);
g_async_queue_unref
(
src
->
queue
);
}
if
(
g_audio_sink_singleton
==
src
)
g_audio_sink_singleton
=
NULL
;
g_mutex_clear
(
&
src
->
lock
);
g_free
(
src
);
}
gboolean
audio_sink_push_opus
(
AudioSink
*
src
,
const
uint8_t
*
data
,
size_t
size
)
{
SinkPacket
*
pkt
;
if
(
!
src
||
!
src
->
queue
||
!
data
||
size
==
0
)
return
FALSE
;
/* 入队,由消费线程处理(不阻塞 libdatachannel 回调线程) */
pkt
=
g_new0
(
SinkPacket
,
1
);
pkt
->
data
=
(
uint8_t
*
)
g_memdup2
(
data
,
size
);
pkt
->
size
=
size
;
g_async_queue_push
(
src
->
queue
,
pkt
);
return
TRUE
;
}
void
audio_sink_set_volume
(
AudioSink
*
src
,
double
volume
)
{
if
(
!
src
||
!
src
->
vol
)
return
;
if
(
volume
<
0
.
0
)
volume
=
0
.
0
;
if
(
volume
>
1
.
0
)
volume
=
1
.
0
;
g_object_set
(
src
->
vol
,
"volume"
,
volume
,
NULL
);
(
void
)
src
;
(
void
)
volume
;
/* 无 GStreamer 管道,音量设置为空操作(保留接口供 volume_control 调用)。 */
}
modules/webrtcpush/audio_sink.h
View file @
3585cc8c
...
...
@@ -17,4 +17,11 @@ gboolean audio_sink_push_opus(AudioSink *src, const uint8_t *data, size_t size);
/* 设置播放音量 0.0~1.0 */
void
audio_sink_set_volume
(
AudioSink
*
src
,
double
volume
);
/* USB声卡排队锁: 本地音频和audio_sink串行访问同一个USB声卡 */
void
audio_sink_lock_alsa
(
void
);
void
audio_sink_unlock_alsa
(
void
);
/* 中断当前按键会话, 释放ALSA设备让DataChannel音频能立即播放 */
void
audio_sink_interrupt
(
AudioSink
*
src
);
#endif
\ No newline at end of file
modules/webrtcpush/mpp_h264_source.c
View file @
3585cc8c
This diff is collapsed.
Click to expand it.
modules/webrtcpush/rtc_client.c
View file @
3585cc8c
This diff is collapsed.
Click to expand it.
modules/webrtcpush/webrtcpush_config.h
View file @
3585cc8c
...
...
@@ -18,36 +18,45 @@
/*
* 与 gst_webrtc_pipeline / jywy 浏览器推流对齐的码率策略。
* 首屏先用 900kbps,不像 1.4Mbps 那样猛冲,也不要低到一进来就糊。
* RTCP REMB 只作为码率趋势
,下降也做阶梯平滑,避免 MPP 动态切码率时卡顿
。
* RTCP REMB 只作为码率趋势
:小步慢降、慢升、带滞回,尽量接近浏览器的无感自适应
。
*/
#define WEBRTCPUSH_INITIAL_BITRATE 900000U
#define WEBRTCPUSH_MIN_BITRATE
5
00000U
#define WEBRTCPUSH_MAX_BITRATE 2
8
00000U
#define WEBRTCPUSH_MIN_BITRATE
8
00000U
#define WEBRTCPUSH_MAX_BITRATE 2
6
00000U
#define WEBRTCPUSH_REMB_UTIL_PERCENT 80U
#define WEBRTCPUSH_REMB_DOWN_MIN_STEP 100000U
#define WEBRTCPUSH_REMB_DOWN_CONFIRMATIONS 4U
#define WEBRTCPUSH_REMB_DOWN_CONFIRMATIONS 8U
#define WEBRTCPUSH_REMB_SEVERE_CONFIRMATIONS 2U
#define WEBRTCPUSH_REMB_SEVERE_PERCENT 65U
#define WEBRTCPUSH_BITRATE_RAMP_UP_MS 2500U
#define WEBRTCPUSH_BITRATE_RAMP_DOWN_MS 1500U
#define WEBRTCPUSH_REMB_DOWN_STEP_PERCENT 15U
#define WEBRTCPUSH_REMB_DOWN_STEP_MIN_BPS 80000U
#define WEBRTCPUSH_REMB_DOWN_STEP_MAX_BPS 180000U
#define WEBRTCPUSH_PACING_HEADROOM_PERCENT 140U
#define WEBRTCPUSH_BITRATE_RAMP_UP_MS 1800U
#define WEBRTCPUSH_BITRATE_RAMP_DOWN_MS 2000U
#define WEBRTCPUSH_REMB_DOWN_STEP_PERCENT 10U
#define WEBRTCPUSH_REMB_DOWN_STEP_MIN_BPS 60000U
#define WEBRTCPUSH_REMB_DOWN_STEP_MAX_BPS 120000U
/* REMB 低估保护:pacing 不堵时,过低浏览器估计不直接压糊 720p。 */
#define WEBRTCPUSH_REMB_SANE_FLOOR_BPS 2200000U
#define WEBRTCPUSH_REMB_SANE_RAW_MAX_BPS 1200000U
#define WEBRTCPUSH_REMB_SANE_PACING_MAX_MS 100U
#define WEBRTCPUSH_PACING_HEADROOM_PERCENT 180U
#define WEBRTCPUSH_PACING_INTERVAL_MS 5U
#define WEBRTCPUSH_PACING_MAX_BITRATE 4000000U
#define WEBRTCPUSH_PACING_MAX_QUEUE_MS 2
5
0U
#define WEBRTCPUSH_PACING_GUARD_COOLDOWN_MS
2
000U
#define WEBRTCPUSH_PACING_MAX_QUEUE_MS 2
6
0U
#define WEBRTCPUSH_PACING_GUARD_COOLDOWN_MS
1
000U
/* 首个/刚恢复的 IDR 允许短暂排队,避免首屏关键帧刚发出就被清队列 */
#define WEBRTCPUSH_PACING_IDR_GRACE_MS
80
0U
#define WEBRTCPUSH_PACING_IDR_GRACE_MS
25
0U
/* pacing 清队列后,若近期已有 IDR,不要反复强制 IDR 造成 I 帧风暴 */
#define WEBRTCPUSH_PACING_RESYNC_IDR_MS 5000U
/* RK MPP 运行中小幅改 bps 容易顿一下;小变化只调 pacing,少重配硬编。 */
#define WEBRTCPUSH_MPP_RECONFIG_MIN_DELTA_BPS 200000U
#define WEBRTCPUSH_MPP_RECONFIG_MIN_INTERVAL_MS 5000U
/* RTP 分片与 NACK(MTU=1200,留 SRTP/DTLS/FU 余量) */
#define WEBRTCPUSH_RTP_MAX_FRAGMENT 1050U
#define WEBRTCPUSH_NACK_PACKETS 512U
/* PLI/IDR 节流:避免频繁 force-key-unit 拉高瞬时码率 */
#define WEBRTCPUSH_PLI_IDR_THROTTLE_MS
500U
#define WEBRTCPUSH_PLI_IDR_THROTTLE_MS
2000U
/* 500->2000: 避免IDR风暴, 每个IDR都加大pacing堆积 */
/* 周期性推流指标日志间隔 */
#define WEBRTCPUSH_STATS_LOG_INTERVAL_MS 8000U
...
...
@@ -66,8 +75,7 @@
#define WEBRTCPUSH_MPP_POST_ENC_BUFFERS 1
/* 编码后保留 AU */
#define WEBRTCPUSH_APPSINK_MAX_BUFFERS 1
/* MJPEG 解压:1=GStreamer CPU jpegdec 优先;0=mppjpegdec 优先 */
#define WEBRTCPUSH_MJPEG_CPU_DECODE 1
/* native MJPEG 解压固定使用 libjpeg-turbo(jpeglib) 手动解码;旧 GStreamer 解码分支已删除 */
/* MPP 码控收紧:低 REMB 时实际 H264 不能长期高于目标太多 */
#define WEBRTCPUSH_MPP_BPS_MIN_PERCENT 60U
...
...
@@ -96,6 +104,15 @@
/* 音频播放(手机->设备):ALSA 喇叭设备 */
#define WEBRTCPUSH_AUDIO_PLAYBACK_DEVICE "plughw:2,0"
/*
* 手机->设备喊话音量策略:
* 0:后端返回 0 就按 0 播放(静音)
* 1:后端返回 0 时按 WEBRTCPUSH_AUDIO_ZERO_VOLUME_MIN 播放,便于测试喊话链路
*/
#define WEBRTCPUSH_AUDIO_ZERO_VOLUME_AS_MIN 0
#define WEBRTCPUSH_AUDIO_ZERO_VOLUME_MIN 0.5
/* 手机喊话结束后短暂保温,避免每个短包都冷启动声卡;到时仍会释放喇叭。 */
#define WEBRTCPUSH_AUDIO_SINK_IDLE_TIMEOUT_MS 3000U
/* 后端音量控制接口 */
#define WEBRTCPUSH_VOLUME_API_BASE "https://fcrs-api.yd-ss.com/api/drive/use/status/"
...
...
@@ -104,10 +121,11 @@
/*
* 设备侧 DataChannel(myDataChannel)与手机 createDataChannel('init') 争用 SCTP,
* 易触发 sctpenc association error 并导致管道闪断/进程崩溃。仅推流可不建。
* 设备侧 DataChannel(myDataChannel):
* - 前端 ondatachannel 后用 remoteChannel 发送 MP3 分片 + "EOF" 给设备播放;
* - 设备也通过该通道发送 Mbps 字符串,更新右上角网络显示。
*/
#define WEBRTCPUSH_ENABLE_DATACHANNEL
0
#define WEBRTCPUSH_ENABLE_DATACHANNEL
1
/* WebRTC 信令 WebSocket 主机(路径 /websocket?dev=设备号) */
#define WEBRTCPUSH_SIGNAL_HOST "signal.yd-ss.com"
...
...
zlog.conf
View file @
3585cc8c
...
...
@@ -9,4 +9,4 @@ file perms = 600
millisecond
=
"%d(%Y-%m-%d %H:%M:%S).%ms [%V] %m%n"
[
rules
]
my_log
.*
"/home/orangepi/car/master/log/log_2026-07-
08
.log"
;
millisecond
my_log
.*
"/home/orangepi/car/master/log/log_2026-07-
10
.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