From 8890d7f5278129bce95812198a71c12323a26729 Mon Sep 17 00:00:00 2001 From: markmental Date: Sat, 28 Mar 2026 10:56:27 -0400 Subject: [PATCH] Fix guide misalignment --- src/app.c | 2 +- src/app.o | Bin 14744 -> 14832 bytes src/channel.c | 11 +++++ src/channel.h | 1 + src/channel.o | Bin 9288 -> 9432 bytes src/frame_queue.o | Bin 4432 -> 4528 bytes src/player.c | 121 +++++++++++++++++++++++++++++++++++++++------- src/player.o | Bin 21296 -> 22096 bytes src/ui.c | 65 +++++++++++++++++++------ src/ui.o | Bin 35688 -> 35896 bytes 10 files changed, 167 insertions(+), 33 deletions(-) diff --git a/src/app.c b/src/app.c index bf0127f..2a7aff4 100644 --- a/src/app.c +++ b/src/app.c @@ -331,7 +331,7 @@ void app_run(App *app) { in_blackout = player_is_in_blackout(&app->player); now_ticks = SDL_GetTicks64(); - now_wall = time(NULL); + now_wall = channel_wall_time_from_ticks(app->app_start_time, app->app_start_ticks, now_ticks); if (app->last_blackout_state && !in_blackout) { app->startup_handoff_active = 1; app->startup_handoff_until = SDL_GetTicks() + 400; diff --git a/src/app.o b/src/app.o index be656684d39c3a76f530da293378a0ad12316cc8..b3f2dc433c1e75954a1d95c6d84b30447c266942 100644 GIT binary patch delta 2680 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 = 0; } +time_t channel_wall_time_from_ticks(time_t app_start_time, Uint64 app_start_ticks, Uint64 now_ticks) { + Uint64 elapsed_ticks; + + if (now_ticks < app_start_ticks) { + now_ticks = app_start_ticks; + } + + elapsed_ticks = now_ticks - app_start_ticks; + return app_start_time + (time_t) (elapsed_ticks / 1000); +} + double channel_live_position(const Channel *channel, time_t app_start_time, time_t now) { double elapsed; diff --git a/src/channel.h b/src/channel.h index 4ab0dba..a85cbb9 100644 --- a/src/channel.h +++ b/src/channel.h @@ -29,6 +29,7 @@ typedef struct ChannelList { int channel_list_load(ChannelList *list, const char *media_dir); void channel_list_destroy(ChannelList *list); +time_t channel_wall_time_from_ticks(time_t app_start_time, Uint64 app_start_ticks, Uint64 now_ticks); double channel_live_position(const Channel *channel, time_t app_start_time, time_t now); double channel_live_position_precise(const Channel *channel, Uint64 app_start_ticks, Uint64 now_ticks); const ProgramEntry *channel_resolve_program(const Channel *channel, diff --git a/src/channel.o b/src/channel.o index 88915cfb79676389a326c8a1222e183101295c87..e81015e35390c2a96763acd6b436f11c6d6f99ba 100644 GIT binary patch delta 612 zcmX@%al>efQl zX+gysxFG^C>ztwD8#ZqgdBiAEo|uypUy_-d8lP5_p9^FqXBSUiCnh#|fmp)kzhc4c zhVD>=dyh06AvCwM`&Gca73 ryi?hnOF$mN`@k^ym$Dh>41Ne#W3r`+Ip+Zp2+wYErHVPoDiw7AE_raX diff --git a/src/frame_queue.o b/src/frame_queue.o index 3eb478dcb57bff165aa8e25a558096212ffb0188..845926486ae8a6f9f8dc6bb000ab6533e4dcacd6 100644 GIT binary patch delta 1684 zcma)5YiJx*6rMY?*+;UOnM!pNPo7Zl)~sZk5oQR?wJKiH+umhSFBl zLX$E(p~JW#>9vpuiXbRWel*1g6)Wy!ZFbRuy8%J`wN=qwYk#F`pRVWJ*=^Q}j|+F^ z-1D8+ch0@VyBF3?#mK_6ws(sp@I{TEE>rEw5vKzbPE}f=^A!ZBW%xnk%El)s3(E0D zYGwgZrRj6p!+B8crq+!Pn;y`a*~2f{rU25g5>5jKW;4g6 z^-qGv{tzuPI|v@;$OL<=V&?Ihb6&KLqoG!?cp!6mY&Op& z9?kxg=Qcj~_YYK2!`>nH^@2T=bD+iLU~>*lip(KD&4Y~~#IrOHoKOT8-jHhvf-s9?q>|p;;H=x} zC{|)-m2?OlrF0bMZd72tm03D+5&2T*Mwj+=X4eY>u+@szSvteMl=QIcIC&J>gn`iU z!i#i0yw?E)X{>P=!t=GA!R?saspt*cl-F)`oWGe5TapEcaO!Vem z?>`N73)oroL2Hlb(ODM0V7SAuAu3C*$dfPU44wl<*` z*_vP|o~hS;tM3L>z``HyMm_JbeZdfM_}9luAonuT9wN{1?@83eyS?b?`ZZ^y|DYdb zo$aHksXE66*O|AMmtL>`hgI-@tG4G^MNwH+j=}V*aze~`*-rTm z)}zE4;MG$rl~mN5%|8$PN)g7r6CBzw#5ra0N_Fq8V>s$?QoA~`Mf@}e+Xoq} zlgkayGt%-nVW>mDPKAvPHc(lz=%O&8`yfn3h*Vu`MFyE#U2S}x_bz(W7v49|J-^@c z|Hgf_T>_dw3VTIQtM9)=t9jhS`O++hhmA(I3F;Uag(rn|7@q3sgnNL~n z&7I@i`CTvR_>HB9t6o7De$~gRYF-d&+J7l}$wi3GHBEhgbm&C&=RP)PH{x(g-kw3C zEec~nuA(3z=PDDE=ChapwG8?r*=ZzuW%6xT*uQSFbLAZ^SHyBf;Kg;VRtq~lDoGbm zYAwBCf8MGUi}$yiUz`zB+9FEAdfcWcX+3^h?n=0%6sklnrdJw@uEWSqMKAq<`IwQ? z5KRFGLz$G<8}^U!>{q<-8FWU?e!)C)*!12Otff8J#$e{#*gVest{2E9dHDa}MKDB6 z*SE$Pri<}?r^dtI9Xe^%ePhf-vb>LnXbVK56BjUL$?sc$z-sidLBweRrUc2e#en?1 za4H8!dBK)s*weAsypGnm)4%yUO_M%@8t&c-x8)?NLXM~>)5H>9VzH4lM)M$Ok1K*5 zdep@b$f2O{e5~)jcE%i+mqtySRvrn@b?IC%F}N6W2C5iA+Nv4H^aDtACsTdcI68!? zc-yw`fr;pcA+$4uehCM~RT3H>3afTic< zLJZDmcfz}RrY$z~3@dyC_nty?BFQ|#U+)!a_VzYXq$4}rHnAOw& E01yMVh5!Hn diff --git a/src/player.c b/src/player.c index 190d5e1..e30f03c 100644 --- a/src/player.c +++ b/src/player.c @@ -419,6 +419,80 @@ static int decode_context_open(DecodeContext *ctx, const ProgramEntry *program, return 0; } +static int decode_context_seek_to_seconds(DecodeContext *ctx, double seek_seconds) { + int64_t seek_target; + + if (!ctx || seek_seconds <= 0.0 || !ctx->format_context || ctx->video_stream_index < 0) { + return 0; + } + + seek_target = (int64_t) (seek_seconds / av_q2d(ctx->video_time_base)); + if (avformat_seek_file(ctx->format_context, ctx->video_stream_index, INT64_MIN, seek_target, INT64_MAX, 0) < 0) { + return -1; + } + + avcodec_flush_buffers(ctx->video_codec_context); + if (ctx->audio_codec_context) { + avcodec_flush_buffers(ctx->audio_codec_context); + } + + return 0; +} + +static int decode_context_sync_to_schedule(Player *player, + const Channel *channel, + DecodeContext *ctx, + const ProgramEntry **program, + int *current_program_index, + double *seek_seconds, + int force_reopen, + int *queued_any_audio, + int *queued_first_video) { + const ProgramEntry *scheduled_program; + int scheduled_program_index = -1; + double scheduled_seek_seconds = 0.0; + + if (!player || !channel || !ctx || !program || !current_program_index || !seek_seconds) { + return -1; + } + + scheduled_program = channel_resolve_program(channel, + player->app_start_ticks, + SDL_GetTicks64(), + &scheduled_seek_seconds, + &scheduled_program_index); + if (!scheduled_program) { + return -1; + } + + if (!force_reopen && *program == scheduled_program && *current_program_index == scheduled_program_index) { + *seek_seconds = scheduled_seek_seconds; + return 0; + } + + if (decode_context_open(ctx, scheduled_program, player) != 0) { + return -1; + } + if (decode_context_seek_to_seconds(ctx, scheduled_seek_seconds) != 0) { + player_set_error(player, "Unable to seek to scheduled program position"); + return -1; + } + + frame_queue_clear(&player->frame_queue); + player_clear_audio(player); + *program = scheduled_program; + *current_program_index = scheduled_program_index; + *seek_seconds = scheduled_seek_seconds; + if (queued_any_audio) { + *queued_any_audio = 0; + } + if (queued_first_video) { + *queued_first_video = 0; + } + + return 0; +} + static int decode_thread_main(void *userdata) { DecoderThreadArgs *args = (DecoderThreadArgs *) userdata; Player *player = args->player; @@ -438,29 +512,42 @@ static int decode_thread_main(void *userdata) { ctx.audio_stream_index = -1; free(args); - program = channel_resolve_program(channel, player->app_start_ticks, SDL_GetTicks64(), &seek_seconds, ¤t_program_index); - if (!program || decode_context_open(&ctx, program, player) != 0) { + if (decode_context_sync_to_schedule(player, + channel, + &ctx, + &program, + ¤t_program_index, + &seek_seconds, + 1, + &queued_any_audio, + &queued_first_video) != 0) { decode_context_reset(&ctx); return -1; } - if (seek_seconds > 0.0) { - int64_t seek_target = (int64_t) (seek_seconds / av_q2d(ctx.video_time_base)); - avformat_seek_file(ctx.format_context, ctx.video_stream_index, INT64_MIN, seek_target, INT64_MAX, 0); - avcodec_flush_buffers(ctx.video_codec_context); - if (ctx.audio_codec_context) { - avcodec_flush_buffers(ctx.audio_codec_context); - } - } - player_clear_audio(player); - while (!should_stop(player)) { + if (decode_context_sync_to_schedule(player, + channel, + &ctx, + &program, + ¤t_program_index, + &seek_seconds, + 0, + &queued_any_audio, + &queued_first_video) != 0) { + goto cleanup; + } + if (av_read_frame(ctx.format_context, ctx.packet) < 0) { - current_program_index = (current_program_index + 1) % channel->program_count; - program = channel_program_at_index(channel, current_program_index); - queued_any_audio = 0; - queued_first_video = 0; - if (!program || decode_context_open(&ctx, program, player) != 0) { + if (decode_context_sync_to_schedule(player, + channel, + &ctx, + &program, + ¤t_program_index, + &seek_seconds, + 1, + &queued_any_audio, + &queued_first_video) != 0) { goto cleanup; } continue; diff --git a/src/player.o b/src/player.o index 54c9795295bfa257d4b4ca9eecaf7d55f741a81a..2cb28d13e298f54c7e10397117bb7b638ce5090b 100644 GIT binary patch literal 22096 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 literal 21296 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 J#b;^#{{&`p7BK(- diff --git a/src/ui.c b/src/ui.c index 6fe3e6d..9e772c3 100644 --- a/src/ui.c +++ b/src/ui.c @@ -378,8 +378,18 @@ static void format_clock_label(char *buffer, size_t buffer_size, time_t now, int strftime(buffer, buffer_size, "%I:%M %p", &local_time); } -static void draw_timeline_header_cached(SDL_Renderer *renderer, const UiCache *cache, const GuideTheme *theme, SDL_Rect rect) { - int segments = 4; +static int timeline_marker_x(int guide_x_start, double pixels_per_minute, int minute_offset) { + return guide_x_start + (int) ((minute_offset * pixels_per_minute) + 0.5); +} + +static void draw_timeline_header_cached(SDL_Renderer *renderer, + const UiCache *cache, + const GuideTheme *theme, + SDL_Rect rect, + int guide_x_start, + double pixels_per_minute, + int timeline_width, + int visible_minutes) { SDL_Color ribbon_text = ensure_contrast(theme->ribbon_text, theme->ribbon_mid); SDL_Color ribbon_shadow = color_luma(theme->ribbon_mid) < 120 ? color_with_alpha(COLOR_BLACK, 255) : color_with_alpha(COLOR_TEXT_LIGHT, 255); @@ -391,11 +401,25 @@ static void draw_timeline_header_cached(SDL_Renderer *renderer, const UiCache *c theme->gloss, theme->panel_border); - for (int i = 0; i < segments; ++i) { - int x = rect.x + (rect.w * i) / segments; + (void) timeline_width; + for (int i = 0; i < 4; ++i) { + int minute_offset = (visible_minutes / 3) * i; + int x = timeline_marker_x(guide_x_start, pixels_per_minute, minute_offset); int centered_x = x - cache->timeline_labels[i].width / 2; + int min_center_x = guide_x_start; + int max_center_x = guide_x_start + timeline_width - cache->timeline_labels[i].width; SDL_Rect shadow_dst = {centered_x + 1, rect.y + 11, cache->timeline_labels[i].width, cache->timeline_labels[i].height}; SDL_Rect text_dst = {centered_x, rect.y + 10, cache->timeline_labels[i].width, cache->timeline_labels[i].height}; + + if (centered_x < min_center_x) { + centered_x = min_center_x; + } + if (centered_x > max_center_x) { + centered_x = max_center_x; + } + shadow_dst.x = centered_x + 1; + text_dst.x = centered_x; + SDL_SetTextureColorMod(cache->timeline_labels[i].texture, ribbon_shadow.r, ribbon_shadow.g, ribbon_shadow.b); SDL_RenderCopy(renderer, cache->timeline_labels[i].texture, NULL, &shadow_dst); SDL_SetTextureColorMod(cache->timeline_labels[i].texture, ribbon_text.r, ribbon_text.g, ribbon_text.b); @@ -493,7 +517,12 @@ static void draw_info_panel(SDL_Renderer *renderer, draw_text_clipped(renderer, fonts->small, description, &clip_rect, rect->x + 18, rect->y + 148, panel_text); } -static void draw_grid_background(SDL_Renderer *renderer, const GuideTheme *theme, const SDL_Rect *grid_rect, int row_height, double pixels_per_minute) { +static void draw_grid_background(SDL_Renderer *renderer, + const GuideTheme *theme, + const SDL_Rect *grid_rect, + int guide_x_start, + int row_height, + double pixels_per_minute) { fill_three_stop_gradient(renderer, grid_rect, theme->background_top, @@ -501,7 +530,7 @@ static void draw_grid_background(SDL_Renderer *renderer, const GuideTheme *theme theme->background_bottom); for (int minute = 0; minute <= 90; minute += 30) { - int x = GUIDE_X_START + (int) (minute * pixels_per_minute); + int x = timeline_marker_x(guide_x_start, pixels_per_minute, minute); SDL_Color strong_line = ensure_contrast(theme->grid_line, theme->background_mid); set_draw_color(renderer, strong_line); SDL_RenderDrawLine(renderer, x, grid_rect->y, x, grid_rect->y + grid_rect->h); @@ -674,6 +703,7 @@ void ui_render_guide(SDL_Renderer *renderer, int start_index = active_channel - 2; const Channel *selected_channel = NULL; double pixels_per_minute = timeline_w / 90.0; + time_t guide_view_start_time = now_wall - (30 * 60); if (channels && channels->count > 0 && active_channel >= 0 && active_channel < channels->count) { selected_channel = &channels->items[active_channel]; @@ -693,15 +723,22 @@ void ui_render_guide(SDL_Renderer *renderer, if (cache->timeline_label_slot != now_wall / 60 || cache->timeline_theme != theme) { char label[32]; for (int i = 0; i < 4; ++i) { - format_clock_label(label, sizeof(label), now_wall, (int) ((TIMELINE_VISIBLE_SECONDS / 60.0 / 4) * i)); + format_clock_label(label, sizeof(label), guide_view_start_time, 30 * i); text_texture_destroy(&cache->timeline_labels[i]); text_texture_init(&cache->timeline_labels[i], renderer, fonts->small, label, COLOR_TEXT_LIGHT); } cache->timeline_label_slot = now_wall / 60; cache->timeline_theme = theme; } - draw_timeline_header_cached(renderer, cache, theme, header_row); - draw_grid_background(renderer, theme, &grid, row_height, pixels_per_minute); + draw_timeline_header_cached(renderer, + cache, + theme, + header_row, + guide_x_start, + pixels_per_minute, + timeline_w, + (int) (TIMELINE_VISIBLE_SECONDS / 60.0)); + draw_grid_background(renderer, theme, &grid, guide_x_start, row_height, pixels_per_minute); if (start_index < 0) { start_index = 0; @@ -741,11 +778,10 @@ void ui_render_guide(SDL_Renderer *renderer, { const Channel *channel = &channels->items[channel_index]; - double live_position = 0.0; double program_seek = 0.0; const ProgramEntry *program = channel_resolve_program(channel, app_start_ticks, now_ticks, &program_seek, NULL); - time_t guide_view_start_time = now_wall - (30 * 60); - time_t program_start_time = now_wall - (time_t) live_position; + double guide_view_start_seconds = (double) guide_view_start_time; + double program_start_time = (double) now_wall; int block_x = guide_x_start; int block_w = 48; SDL_Rect block = {guide_x_start, timeline_rect.y + 4, 48, timeline_rect.h - 8}; @@ -764,9 +800,8 @@ void ui_render_guide(SDL_Renderer *renderer, if (!program) { continue; } - live_position = channel_live_position_precise(channel, app_start_ticks, now_ticks); - program_start_time = now_wall - (time_t) program_seek; - block_x = guide_x_start + (int) (((double) (program_start_time - guide_view_start_time)) / 60.0 * pixels_per_minute); + program_start_time -= program_seek; + block_x = guide_x_start + (int) ((((program_start_time - guide_view_start_seconds) / 60.0) * pixels_per_minute) + 0.5); block_w = (int) ((program->duration_seconds / 60.0) * pixels_per_minute); block = (SDL_Rect){block_x, timeline_rect.y + 4, SDL_max(block_w, 48), timeline_rect.h - 8}; title_rect = (SDL_Rect){block.x + 8, block.y + 8, block.w - 16, block.h - 16}; diff --git a/src/ui.o b/src/ui.o index c4a1f0ee9b4ad0cb6d76af8db14d72aa1169f919..470287a665f14624b13df3b625efd254ef1f5b34 100644 GIT binary patch delta 10448 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 delta 10167 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 R3cZVsM(=lAVr5hO{{ZkOEF1s;