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); ...@@ -10,5 +10,8 @@ void car0107_notify_control_activity(void);
long long car0107_last_control_ms(void); long long car0107_last_control_ms(void);
int car0107_esc_is_active(void); int car0107_esc_is_active(void);
void car0107_steering_set_angle_sync(int angle); 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 #endif
...@@ -71,12 +71,10 @@ static void car0107_idle_nudge_steering(int turn_left) ...@@ -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; int angle = turn_left ? CAR0107_IDLE_STEER_LEFT : CAR0107_IDLE_STEER_RIGHT;
/* 刷新电调中位,避免长时间无 PWM 后接收机休眠 */ /* 速度 PWM 只由持有0107硬件锁的控制路径写,idle线程不直接碰速度。 */
pwmWrite(PWM_PIN_SPEED, 75);
car0107_steering_set_angle_sync(angle); car0107_steering_set_angle_sync(angle);
delay_ms(CAR0107_IDLE_STEER_HOLD_MS); delay_ms(CAR0107_IDLE_STEER_HOLD_MS);
car0107_steering_set_angle_sync(90); car0107_steering_set_angle_sync(90);
pwmWrite(PWM_PIN_SPEED, 75);
} }
static void car0107_idle_monitor_task(void *arg) static void car0107_idle_monitor_task(void *arg)
...@@ -128,6 +126,7 @@ void car0107_idle_startup(int device_id) ...@@ -128,6 +126,7 @@ void car0107_idle_startup(int device_id)
} }
car0107_idle_lock(); car0107_idle_lock();
s_idle_ctx.shutdown = false;
if (thread_pool_add_task(s_idle_ctx.pool, car0107_idle_monitor_task, NULL) != 0) { if (thread_pool_add_task(s_idle_ctx.pool, car0107_idle_monitor_task, NULL) != 0) {
my_zlog_error("car0107 idle monitor task submit failed"); my_zlog_error("car0107 idle monitor task submit failed");
} }
...@@ -150,5 +149,7 @@ void car0107_idle_thread_close(void) ...@@ -150,5 +149,7 @@ void car0107_idle_thread_close(void)
s_idle_ctx.pool_ready = false; 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 { ...@@ -41,6 +41,7 @@ struct MppH264Source {
size_t frame_capacity; size_t frame_capacity;
guint keyframe_req; guint keyframe_req;
volatile gboolean pending_idr; volatile gboolean pending_idr;
volatile gboolean pending_recover;
gulong enc_sink_probe_id; gulong enc_sink_probe_id;
guint bus_watch_id; guint bus_watch_id;
gchar *sprop_parameter_sets; gchar *sprop_parameter_sets;
...@@ -64,10 +65,13 @@ static gboolean bus_watch_cb(GstBus *bus, GstMessage *msg, gpointer user_data) ...@@ -64,10 +65,13 @@ static gboolean bus_watch_cb(GstBus *bus, GstMessage *msg, gpointer user_data)
GError *err = NULL; GError *err = NULL;
gchar *dbg = NULL; gchar *dbg = NULL;
gst_message_parse_error(msg, &err, &dbg); 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 : ""); err ? err->message : "unknown", dbg ? dbg : "");
g_clear_error(&err); g_clear_error(&err);
g_free(dbg); g_free(dbg);
/* ERROR 后 pipeline 进入错误态,appsink 永远拉不到帧 -> 黑屏。
* NULL->PLAYING 重置,让 pull 线程的 recover 路径重新唤醒管道。 */
src->pending_recover = TRUE;
break; break;
} }
case GST_MESSAGE_WARNING: { case GST_MESSAGE_WARNING: {
...@@ -273,12 +277,11 @@ static void configure_v4l2_src(GstElement *src, gboolean compressed_mjpeg) ...@@ -273,12 +277,11 @@ static void configure_v4l2_src(GstElement *src, gboolean compressed_mjpeg)
return; return;
g_object_set(src, "do-timestamp", TRUE, NULL); 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); g_object_set(src, "io-mode", 2, NULL);
else
g_object_set(src, "io-mode", 4, NULL);
} }
static GstElement *make_h264_encoder(gboolean prefer_mpp, gboolean use_test, 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, ...@@ -318,9 +321,9 @@ static GstElement *make_h264_encoder(gboolean prefer_mpp, gboolean use_test,
"bitrate", MPP_VIDEO_START_BPS / 1000, "bitrate", MPP_VIDEO_START_BPS / 1000,
"key-int-max", MPP_VIDEO_GOP, "key-int-max", MPP_VIDEO_GOP,
"byte-stream", TRUE, "byte-stream", TRUE,
"profile", "main", "profile", "baseline",
NULL); NULL);
my_zlog_info("mpp_h264_source: x264enc software fallback"); my_zlog_info("mpp_h264_source: x264enc software fallback (baseline, matches SDP)");
return enc; return enc;
} }
...@@ -815,6 +818,10 @@ gboolean mpp_h264_source_pull(MppH264Source *src, gboolean force_idr, ...@@ -815,6 +818,10 @@ gboolean mpp_h264_source_pull(MppH264Source *src, gboolean force_idr,
if (!src || !src->appsink || !data || !size) if (!src || !src->appsink || !data || !size)
return FALSE; return FALSE;
/* bus 报 ERROR 后 pipeline 卡死,这里及时恢复避免长期黑屏 */
if (src->pending_recover && mpp_h264_source_recover(src))
src->pending_idr = TRUE;
if (timeout_ms < 1) if (timeout_ms < 1)
timeout_ms = (1000 / WEBRTCPUSH_H264_FPS) + 60; timeout_ms = (1000 / WEBRTCPUSH_H264_FPS) + 60;
...@@ -872,6 +879,7 @@ gboolean mpp_h264_source_recover(MppH264Source *src) ...@@ -872,6 +879,7 @@ gboolean mpp_h264_source_recover(MppH264Source *src)
my_zlog_warn("mpp_h264_source: recovering pipeline (NULL -> PLAYING)"); my_zlog_warn("mpp_h264_source: recovering pipeline (NULL -> PLAYING)");
src->pending_idr = FALSE; src->pending_idr = FALSE;
src->pending_recover = FALSE;
gst_element_set_state(src->pipeline, GST_STATE_NULL); gst_element_set_state(src->pipeline, GST_STATE_NULL);
g_usleep(300000); g_usleep(300000);
ret = gst_element_set_state(src->pipeline, GST_STATE_PLAYING); 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) ...@@ -363,6 +363,38 @@ static gchar *sdp_patch_h264_answer(const gchar *sdp, const gchar *sprop)
return out; 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, static void RTC_API on_local_description(int pc, const char *sdp,
const char *type, void *ptr) const char *type, void *ptr)
{ {
...@@ -376,6 +408,11 @@ static void RTC_API on_local_description(int pc, const char *sdp, ...@@ -376,6 +408,11 @@ static void RTC_API on_local_description(int pc, const char *sdp,
return; return;
sprop = mpp_h264_source_get_sprop(client->h264_source); sprop = mpp_h264_source_get_sprop(client->h264_source);
patched = sdp_patch_h264_answer(sdp, sprop); 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); json = build_description_json(client->app, "answer", patched ? patched : sdp);
g_free(patched); g_free(patched);
if (!json) { if (!json) {
...@@ -474,42 +511,57 @@ static void RTC_API on_state_change(int pc, rtcState state, void *ptr) ...@@ -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) static void RTC_API on_track_open(int track, void *ptr)
{ {
RtcClient *client = ptr; RtcClient *client = ptr;
gboolean video_open = FALSE;
gboolean audio_open = FALSE;
if (!client) if (!client)
return; return;
g_mutex_lock(&client->lock); g_mutex_lock(&client->lock);
if (client->track == track) { if (client->track == track) {
client->track_open = TRUE; client->track_open = TRUE;
client->source_pts_valid = FALSE; client->source_pts_valid = FALSE;
video_open = TRUE;
} }
if (client->audio_track == track) { if (client->audio_track == track) {
client->audio_track_open = TRUE; client->audio_track_open = TRUE;
client->audio_pts_valid = FALSE; client->audio_pts_valid = FALSE;
audio_open = TRUE;
} }
g_mutex_unlock(&client->lock); g_mutex_unlock(&client->lock);
if (video_open) {
frame_queue_clear(client); frame_queue_clear(client);
request_idr(client, TRUE); 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); 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) static void RTC_API on_track_closed(int track, void *ptr)
{ {
RtcClient *client = ptr; RtcClient *client = ptr;
gboolean video_closed = FALSE;
gboolean audio_closed = FALSE;
if (!client) if (!client)
return; return;
g_mutex_lock(&client->lock); g_mutex_lock(&client->lock);
if (client->track == track) if (client->track == track) {
client->track_open = FALSE; client->track_open = FALSE;
if (client->audio_track == track) video_closed = TRUE;
}
if (client->audio_track == track) {
client->audio_track_open = FALSE; client->audio_track_open = FALSE;
audio_closed = TRUE;
}
g_mutex_unlock(&client->lock); g_mutex_unlock(&client->lock);
if (video_closed)
frame_queue_clear(client); frame_queue_clear(client);
signal_track_ready(client); signal_track_ready(client);
my_zlog_warn("libdatachannel: track closed (video=%d audio=%d)", my_zlog_warn("libdatachannel: track closed (video=%d audio=%d)",
client->track == track ? 1 : 0, video_closed ? 1 : 0, audio_closed ? 1 : 0);
client->audio_track == track ? 1 : 0);
} }
static void RTC_API on_track_error(int track, const char *error, void *ptr) 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) ...@@ -523,7 +575,7 @@ static void RTC_API on_pli(int track, void *ptr)
{ {
RtcClient *client = ptr; RtcClient *client = ptr;
int dropped = 0; int dropped = 0;
if (!client) if (!client || client->track != track)
return; return;
if (!request_idr(client, FALSE)) if (!request_idr(client, FALSE))
my_zlog_debug("libdatachannel: PLI throttled, skip duplicate IDR request"); my_zlog_debug("libdatachannel: PLI throttled, skip duplicate IDR request");
...@@ -531,7 +583,6 @@ static void RTC_API on_pli(int track, void *ptr) ...@@ -531,7 +583,6 @@ static void RTC_API on_pli(int track, void *ptr)
client->source_pts_valid = FALSE; client->source_pts_valid = FALSE;
g_mutex_unlock(&client->lock); g_mutex_unlock(&client->lock);
g_mutex_lock(&client->send_lock); g_mutex_lock(&client->send_lock);
if (client->track == track)
dropped = rtcClearPacingQueue(track); dropped = rtcClearPacingQueue(track);
g_mutex_unlock(&client->send_lock); g_mutex_unlock(&client->send_lock);
frame_queue_clear(client); frame_queue_clear(client);
...@@ -1130,10 +1181,12 @@ static gboolean setup_track(RtcClient *client, int pc, ...@@ -1130,10 +1181,12 @@ static gboolean setup_track(RtcClient *client, int pc,
track_init.name = "car-video"; track_init.name = "car-video";
track_init.msid = "car-stream"; track_init.msid = "car-stream";
track_init.trackId = "car-video"; track_init.trackId = "car-video";
track_init.profile = {
"profile-level-id=" WEBRTCPUSH_H264_PROFILE_LEVEL_ID gchar *profile = build_h264_profile_fmtp(client);
";packetization-mode=1;level-asymmetry-allowed=1"; track_init.profile = profile;
track = rtcAddTrackEx(pc, &track_init); track = rtcAddTrackEx(pc, &track_init);
g_free(profile);
}
if (track < 0) if (track < 0)
return FALSE; return FALSE;
......
...@@ -155,6 +155,7 @@ static gboolean idle_offer(gpointer user_data) { ...@@ -155,6 +155,7 @@ static gboolean idle_offer(gpointer user_data) {
OfferIdle *o = user_data; OfferIdle *o = user_data;
AppState *app = o->app; AppState *app = o->app;
my_zlog_info("webrtcpush: applying phone offer (libdatachannel)");
if (rtc_client_handle_offer(app, o->sdp) != 0) if (rtc_client_handle_offer(app, o->sdp) != 0)
my_zlog_error("libdatachannel: failed to apply phone offer"); my_zlog_error("libdatachannel: failed to apply phone offer");
g_free(o->sdp); g_free(o->sdp);
...@@ -230,9 +231,11 @@ static void schedule_offer(AppState *app, const gchar *sdp) { ...@@ -230,9 +231,11 @@ static void schedule_offer(AppState *app, const gchar *sdp) {
if (!app->member_id) { if (!app->member_id) {
g_free(s_pending_offer_sdp); g_free(s_pending_offer_sdp);
s_pending_offer_sdp = g_strdup(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; return;
} }
my_zlog_info("webrtcpush: phone offer received (%zu bytes), debouncing",
sdp ? strlen(sdp) : 0);
g_free(s_debounced_offer_sdp); g_free(s_debounced_offer_sdp);
s_debounced_offer_sdp = g_strdup(sdp); s_debounced_offer_sdp = g_strdup(sdp);
if (s_debounce_offer_id) if (s_debounce_offer_id)
...@@ -333,7 +336,8 @@ static void dispatch_json(const gchar *payload) { ...@@ -333,7 +336,8 @@ static void dispatch_json(const gchar *payload) {
g_free(s_app->member_id); g_free(s_app->member_id);
s_app->member_id = g_strdup(json_node_get_string(mn)); 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->device_offerer) {
if (s_app->call_enable) { if (s_app->call_enable) {
gst_webrtc_pipeline_start_offer_retry(s_app); gst_webrtc_pipeline_start_offer_retry(s_app);
...@@ -350,9 +354,9 @@ static void dispatch_json(const gchar *payload) { ...@@ -350,9 +354,9 @@ static void dispatch_json(const gchar *payload) {
if (!g_strcmp0(t, "num")) { if (!g_strcmp0(t, "num")) {
if (json_object_has_member(root, "message")) { if (json_object_has_member(root, "message")) {
gint64 n = json_object_get_int_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) if (n < 2)
g_print(" (waiting for phone: https://jywy.yd-ss.com/client.html?dev=%s)\n", my_zlog_info("webrtcpush: waiting for phone https://jywy.yd-ss.com/client.html?dev=%s",
s_app->dev_room ? s_app->dev_room : "?"); s_app->dev_room ? s_app->dev_room : "?");
} }
g_object_unref(parser); g_object_unref(parser);
...@@ -384,7 +388,7 @@ static void dispatch_json(const gchar *payload) { ...@@ -384,7 +388,7 @@ static void dispatch_json(const gchar *payload) {
if (!g_strcmp0(t, "leave")) { if (!g_strcmp0(t, "leave")) {
if (rtc_client_is_active(s_app)) { 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_idle_add(idle_pipeline_teardown, s_app);
} }
g_object_unref(parser); g_object_unref(parser);
...@@ -461,18 +465,17 @@ static void on_ws_closed(SoupWebsocketConnection *conn, gpointer user_data) { ...@@ -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; guint code = conn ? soup_websocket_connection_get_close_code(conn) : 0;
gboolean keep_pipeline = s_app && rtc_client_media_open(s_app); gboolean keep_pipeline = s_app && rtc_client_media_open(s_app);
g_print("WebSocket closed code=%u; reconnecting signaling", code);
if (s_ws) { if (s_ws) {
g_object_unref(s_ws); g_object_unref(s_ws);
s_ws = NULL; s_ws = NULL;
} }
if (!s_ws_exiting && s_app) { if (!s_ws_exiting && s_app) {
if (keep_pipeline) 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)) 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 else
g_print("\n"); my_zlog_warn("webrtcpush: WebSocket closed code=%u, reconnect", code);
reset_signaling_session(s_app, !keep_pipeline); reset_signaling_session(s_app, !keep_pipeline);
if (s_reconnect_id) if (s_reconnect_id)
g_source_remove(s_reconnect_id); g_source_remove(s_reconnect_id);
...@@ -485,7 +488,7 @@ static void on_ws_connected(GObject *source_object, GAsyncResult *res, gpointer ...@@ -485,7 +488,7 @@ static void on_ws_connected(GObject *source_object, GAsyncResult *res, gpointer
GError *err = NULL; GError *err = NULL;
s_ws = soup_session_websocket_connect_finish(SOUP_SESSION(source_object), res, &err); s_ws = soup_session_websocket_connect_finish(SOUP_SESSION(source_object), res, &err);
if (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); g_error_free(err);
if (!s_ws_exiting && !s_reconnect_id) if (!s_ws_exiting && !s_reconnect_id)
s_reconnect_id = g_timeout_add_seconds(2, reconnect_cb, NULL); 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 ...@@ -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, "message", G_CALLBACK(on_ws_message), NULL);
g_signal_connect(s_ws, "closed", G_CALLBACK(on_ws_closed), 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(); 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