Commit 6edd4759 authored by 957dd's avatar 957dd

加入了app配网,0107进行了调整,视频流还需调整

parent 7f0fd331
No preview for this file type
This diff is collapsed.
......@@ -10,5 +10,8 @@ void car0107_notify_control_activity(void);
long long car0107_last_control_ms(void);
int car0107_esc_is_active(void);
void car0107_steering_set_angle_sync(int angle);
void car0107_esc_thread_start(void);
void car0107_esc_thread_wake(void);
void car0107_esc_thread_close(void);
#endif
......@@ -71,12 +71,10 @@ static void car0107_idle_nudge_steering(int turn_left)
{
int angle = turn_left ? CAR0107_IDLE_STEER_LEFT : CAR0107_IDLE_STEER_RIGHT;
/* 刷新电调中位,避免长时间无 PWM 后接收机休眠 */
pwmWrite(PWM_PIN_SPEED, 75);
/* 速度 PWM 只由持有0107硬件锁的控制路径写,idle线程不直接碰速度。 */
car0107_steering_set_angle_sync(angle);
delay_ms(CAR0107_IDLE_STEER_HOLD_MS);
car0107_steering_set_angle_sync(90);
pwmWrite(PWM_PIN_SPEED, 75);
}
static void car0107_idle_monitor_task(void *arg)
......@@ -128,6 +126,7 @@ void car0107_idle_startup(int device_id)
}
car0107_idle_lock();
s_idle_ctx.shutdown = false;
if (thread_pool_add_task(s_idle_ctx.pool, car0107_idle_monitor_task, NULL) != 0) {
my_zlog_error("car0107 idle monitor task submit failed");
}
......@@ -150,5 +149,7 @@ void car0107_idle_thread_close(void)
s_idle_ctx.pool_ready = false;
}
my_zlog_info("car0107 idle monitor thread pool closed");
car0107_esc_thread_close();
my_zlog_info("car0107 idle monitor and ESC threads closed");
}
......@@ -41,6 +41,7 @@ struct MppH264Source {
size_t frame_capacity;
guint keyframe_req;
volatile gboolean pending_idr;
volatile gboolean pending_recover;
gulong enc_sink_probe_id;
guint bus_watch_id;
gchar *sprop_parameter_sets;
......@@ -64,10 +65,13 @@ static gboolean bus_watch_cb(GstBus *bus, GstMessage *msg, gpointer user_data)
GError *err = NULL;
gchar *dbg = NULL;
gst_message_parse_error(msg, &err, &dbg);
my_zlog_error("mpp_h264_source: pipeline error: %s (%s)",
my_zlog_error("mpp_h264_source: pipeline error: %s (%s), recovering",
err ? err->message : "unknown", dbg ? dbg : "");
g_clear_error(&err);
g_free(dbg);
/* ERROR 后 pipeline 进入错误态,appsink 永远拉不到帧 -> 黑屏。
* NULL->PLAYING 重置,让 pull 线程的 recover 路径重新唤醒管道。 */
src->pending_recover = TRUE;
break;
}
case GST_MESSAGE_WARNING: {
......@@ -273,12 +277,11 @@ static void configure_v4l2_src(GstElement *src, gboolean compressed_mjpeg)
return;
g_object_set(src, "do-timestamp", TRUE, NULL);
/*
* MJPEG 压缩流用 mmap;原始 NV12/YUYV 再尝试 DMABUF 零拷贝进 MPP。
* MJPEG 压缩流用 mmap;原始 NV12/YUYV 统一用 mmap(io-mode=2)。
* DMABUF(4) 在非 RK 平台/无 dma-buf 导出的 USB 摄像头上会 negotiation 失败 -> 黑屏。
* mmap 兼容性最好,性能损失可接受(720p@24fps)。
*/
if (compressed_mjpeg)
g_object_set(src, "io-mode", 2, NULL);
else
g_object_set(src, "io-mode", 4, NULL);
g_object_set(src, "io-mode", 2, NULL);
}
static GstElement *make_h264_encoder(gboolean prefer_mpp, gboolean use_test,
......@@ -318,9 +321,9 @@ static GstElement *make_h264_encoder(gboolean prefer_mpp, gboolean use_test,
"bitrate", MPP_VIDEO_START_BPS / 1000,
"key-int-max", MPP_VIDEO_GOP,
"byte-stream", TRUE,
"profile", "main",
"profile", "baseline",
NULL);
my_zlog_info("mpp_h264_source: x264enc software fallback");
my_zlog_info("mpp_h264_source: x264enc software fallback (baseline, matches SDP)");
return enc;
}
......@@ -815,6 +818,10 @@ gboolean mpp_h264_source_pull(MppH264Source *src, gboolean force_idr,
if (!src || !src->appsink || !data || !size)
return FALSE;
/* bus 报 ERROR 后 pipeline 卡死,这里及时恢复避免长期黑屏 */
if (src->pending_recover && mpp_h264_source_recover(src))
src->pending_idr = TRUE;
if (timeout_ms < 1)
timeout_ms = (1000 / WEBRTCPUSH_H264_FPS) + 60;
......@@ -872,6 +879,7 @@ gboolean mpp_h264_source_recover(MppH264Source *src)
my_zlog_warn("mpp_h264_source: recovering pipeline (NULL -> PLAYING)");
src->pending_idr = FALSE;
src->pending_recover = FALSE;
gst_element_set_state(src->pipeline, GST_STATE_NULL);
g_usleep(300000);
ret = gst_element_set_state(src->pipeline, GST_STATE_PLAYING);
......
......@@ -363,6 +363,38 @@ static gchar *sdp_patch_h264_answer(const gchar *sdp, const gchar *sprop)
return out;
}
static gchar *sdp_ensure_msid_semantic(const gchar *sdp)
{
const gchar *mline;
if (!sdp || strstr(sdp, "a=msid-semantic:"))
return g_strdup(sdp);
mline = strstr(sdp, "m=");
if (!mline)
return g_strdup(sdp);
{
GString *out = g_string_new_len(sdp, (gsize)(mline - sdp));
g_string_append(out, "a=msid-semantic: WMS car-stream\r\n");
g_string_append(out, mline);
my_zlog_info("libdatachannel: SDP answer patched with msid-semantic");
return g_string_free(out, FALSE);
}
}
static gchar *build_h264_profile_fmtp(RtcClient *client)
{
const gchar *sprop = mpp_h264_source_get_sprop(client->h264_source);
if (sprop && sprop[0])
return g_strdup_printf(
"profile-level-id=%s;packetization-mode=1;level-asymmetry-allowed=1;"
"sprop-parameter-sets=%s",
WEBRTCPUSH_H264_PROFILE_LEVEL_ID, sprop);
return g_strdup_printf(
"profile-level-id=%s;packetization-mode=1;level-asymmetry-allowed=1",
WEBRTCPUSH_H264_PROFILE_LEVEL_ID);
}
static void RTC_API on_local_description(int pc, const char *sdp,
const char *type, void *ptr)
{
......@@ -376,6 +408,11 @@ static void RTC_API on_local_description(int pc, const char *sdp,
return;
sprop = mpp_h264_source_get_sprop(client->h264_source);
patched = sdp_patch_h264_answer(sdp, sprop);
{
gchar *with_msid = sdp_ensure_msid_semantic(patched ? patched : sdp);
g_free(patched);
patched = with_msid;
}
json = build_description_json(client->app, "answer", patched ? patched : sdp);
g_free(patched);
if (!json) {
......@@ -474,42 +511,57 @@ static void RTC_API on_state_change(int pc, rtcState state, void *ptr)
static void RTC_API on_track_open(int track, void *ptr)
{
RtcClient *client = ptr;
gboolean video_open = FALSE;
gboolean audio_open = FALSE;
if (!client)
return;
g_mutex_lock(&client->lock);
if (client->track == track) {
client->track_open = TRUE;
client->source_pts_valid = FALSE;
video_open = TRUE;
}
if (client->audio_track == track) {
client->audio_track_open = TRUE;
client->audio_pts_valid = FALSE;
audio_open = TRUE;
}
g_mutex_unlock(&client->lock);
frame_queue_clear(client);
request_idr(client, TRUE);
if (video_open) {
frame_queue_clear(client);
request_idr(client, TRUE);
my_zlog_info("libdatachannel: H264 track open, requesting IDR");
} else if (audio_open) {
my_zlog_info("libdatachannel: Opus track open");
}
signal_track_ready(client);
my_zlog_info("libdatachannel: track open (video=%d audio=%d), requesting IDR",
client->track == track ? 1 : 0,
client->audio_track == track ? 1 : 0);
}
static void RTC_API on_track_closed(int track, void *ptr)
{
RtcClient *client = ptr;
gboolean video_closed = FALSE;
gboolean audio_closed = FALSE;
if (!client)
return;
g_mutex_lock(&client->lock);
if (client->track == track)
if (client->track == track) {
client->track_open = FALSE;
if (client->audio_track == track)
video_closed = TRUE;
}
if (client->audio_track == track) {
client->audio_track_open = FALSE;
audio_closed = TRUE;
}
g_mutex_unlock(&client->lock);
frame_queue_clear(client);
if (video_closed)
frame_queue_clear(client);
signal_track_ready(client);
my_zlog_warn("libdatachannel: track closed (video=%d audio=%d)",
client->track == track ? 1 : 0,
client->audio_track == track ? 1 : 0);
video_closed ? 1 : 0, audio_closed ? 1 : 0);
}
static void RTC_API on_track_error(int track, const char *error, void *ptr)
......@@ -523,7 +575,7 @@ static void RTC_API on_pli(int track, void *ptr)
{
RtcClient *client = ptr;
int dropped = 0;
if (!client)
if (!client || client->track != track)
return;
if (!request_idr(client, FALSE))
my_zlog_debug("libdatachannel: PLI throttled, skip duplicate IDR request");
......@@ -531,8 +583,7 @@ static void RTC_API on_pli(int track, void *ptr)
client->source_pts_valid = FALSE;
g_mutex_unlock(&client->lock);
g_mutex_lock(&client->send_lock);
if (client->track == track)
dropped = rtcClearPacingQueue(track);
dropped = rtcClearPacingQueue(track);
g_mutex_unlock(&client->send_lock);
frame_queue_clear(client);
my_zlog_info("libdatachannel: PLI received, dropped %d queued RTP packet(s), next is IDR",
......@@ -1130,10 +1181,12 @@ static gboolean setup_track(RtcClient *client, int pc,
track_init.name = "car-video";
track_init.msid = "car-stream";
track_init.trackId = "car-video";
track_init.profile =
"profile-level-id=" WEBRTCPUSH_H264_PROFILE_LEVEL_ID
";packetization-mode=1;level-asymmetry-allowed=1";
track = rtcAddTrackEx(pc, &track_init);
{
gchar *profile = build_h264_profile_fmtp(client);
track_init.profile = profile;
track = rtcAddTrackEx(pc, &track_init);
g_free(profile);
}
if (track < 0)
return FALSE;
......
......@@ -155,6 +155,7 @@ static gboolean idle_offer(gpointer user_data) {
OfferIdle *o = user_data;
AppState *app = o->app;
my_zlog_info("webrtcpush: applying phone offer (libdatachannel)");
if (rtc_client_handle_offer(app, o->sdp) != 0)
my_zlog_error("libdatachannel: failed to apply phone offer");
g_free(o->sdp);
......@@ -230,9 +231,11 @@ static void schedule_offer(AppState *app, const gchar *sdp) {
if (!app->member_id) {
g_free(s_pending_offer_sdp);
s_pending_offer_sdp = g_strdup(sdp);
g_print("Offer received before id; holding SDP\n");
my_zlog_info("webrtcpush: phone offer held (awaiting memberId)");
return;
}
my_zlog_info("webrtcpush: phone offer received (%zu bytes), debouncing",
sdp ? strlen(sdp) : 0);
g_free(s_debounced_offer_sdp);
s_debounced_offer_sdp = g_strdup(sdp);
if (s_debounce_offer_id)
......@@ -333,7 +336,8 @@ static void dispatch_json(const gchar *payload) {
g_free(s_app->member_id);
s_app->member_id = g_strdup(json_node_get_string(mn));
}
g_print("memberId=%s\n", s_app->member_id ? s_app->member_id : "?");
my_zlog_info("webrtcpush: signaling memberId=%s",
s_app->member_id ? s_app->member_id : "?");
if (s_app->device_offerer) {
if (s_app->call_enable) {
gst_webrtc_pipeline_start_offer_retry(s_app);
......@@ -350,10 +354,10 @@ static void dispatch_json(const gchar *payload) {
if (!g_strcmp0(t, "num")) {
if (json_object_has_member(root, "message")) {
gint64 n = json_object_get_int_member(root, "message");
g_print("num peers: %" G_GINT64_FORMAT "\n", n);
my_zlog_info("webrtcpush: signaling num peers=%" G_GINT64_FORMAT, n);
if (n < 2)
g_print(" (waiting for phone: https://jywy.yd-ss.com/client.html?dev=%s)\n",
s_app->dev_room ? s_app->dev_room : "?");
my_zlog_info("webrtcpush: waiting for phone https://jywy.yd-ss.com/client.html?dev=%s",
s_app->dev_room ? s_app->dev_room : "?");
}
g_object_unref(parser);
return;
......@@ -384,7 +388,7 @@ static void dispatch_json(const gchar *payload) {
if (!g_strcmp0(t, "leave")) {
if (rtc_client_is_active(s_app)) {
g_print("peer left: reset libdatachannel peer and wait for a new offer\n");
my_zlog_info("webrtcpush: peer left, reset libdatachannel peer");
g_idle_add(idle_pipeline_teardown, s_app);
}
g_object_unref(parser);
......@@ -461,18 +465,17 @@ static void on_ws_closed(SoupWebsocketConnection *conn, gpointer user_data) {
guint code = conn ? soup_websocket_connection_get_close_code(conn) : 0;
gboolean keep_pipeline = s_app && rtc_client_media_open(s_app);
g_print("WebSocket closed code=%u; reconnecting signaling", code);
if (s_ws) {
g_object_unref(s_ws);
s_ws = NULL;
}
if (!s_ws_exiting && s_app) {
if (keep_pipeline)
g_print(" (keep WebRTC pipeline)\n");
my_zlog_warn("webrtcpush: WebSocket closed code=%u, reconnect (keep media)", code);
else if (rtc_client_is_active(s_app))
g_print(" (reset libdatachannel peer)\n");
my_zlog_warn("webrtcpush: WebSocket closed code=%u, reconnect (reset peer)", code);
else
g_print("\n");
my_zlog_warn("webrtcpush: WebSocket closed code=%u, reconnect", code);
reset_signaling_session(s_app, !keep_pipeline);
if (s_reconnect_id)
g_source_remove(s_reconnect_id);
......@@ -485,7 +488,7 @@ static void on_ws_connected(GObject *source_object, GAsyncResult *res, gpointer
GError *err = NULL;
s_ws = soup_session_websocket_connect_finish(SOUP_SESSION(source_object), res, &err);
if (err) {
g_printerr("WebSocket connect failed: %s\n", err->message);
my_zlog_error("webrtcpush: WebSocket connect failed: %s", err->message);
g_error_free(err);
if (!s_ws_exiting && !s_reconnect_id)
s_reconnect_id = g_timeout_add_seconds(2, reconnect_cb, NULL);
......@@ -493,7 +496,8 @@ static void on_ws_connected(GObject *source_object, GAsyncResult *res, gpointer
}
g_signal_connect(s_ws, "message", G_CALLBACK(on_ws_message), NULL);
g_signal_connect(s_ws, "closed", G_CALLBACK(on_ws_closed), NULL);
g_print("WebSocket connected (libsoup)\n");
my_zlog_info("webrtcpush: WebSocket connected dev=%s",
s_app && s_app->dev_room ? s_app->dev_room : "?");
schedule_tx_flush();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment