diff --git a/README.md b/README.md index d6b265e..c346ff7 100644 --- a/README.md +++ b/README.md @@ -625,6 +625,13 @@ and the current service signals to transmit a Slideshow, the Slideshow window is displayed. It shows a slide after it has been received completely and without errors. +While the next slide is being received, a progress bar at the bottom of +the Slideshow window shows the fraction that has successfully been +received so far. It is always updated after a new segment has arrived +free from error. The first segments of the initial slide usually are +received before the corresponding header comes by. In this case a +pulsing bar is shown until the header is available. + Currently the following limitations apply: * slideshows in a separate sub-channel are not supported (just X-PAD); @@ -640,7 +647,7 @@ This software is licensed under the GNU General Public License Version 3 *Please note that the included FEC lib by KA9Q has a separate license!* DABlin - capital DAB experience -Copyright (C) 2015-2021 Stefan Pöschel +Copyright (C) 2015-2022 Stefan Pöschel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/dablin_gtk.cpp b/src/dablin_gtk.cpp index 5590483..61d497c 100644 --- a/src/dablin_gtk.cpp +++ b/src/dablin_gtk.cpp @@ -1,6 +1,6 @@ /* DABlin - capital DAB experience - Copyright (C) 2015-2021 Stefan Pöschel + Copyright (C) 2015-2022 Stefan Pöschel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -264,6 +264,7 @@ DABlinGTK::DABlinGTK(DABlinGTKOptions options) { fic_change_service.GetDispatcher().connect(sigc::mem_fun(*this, &DABlinGTK::FICChangeServiceEmitted)); pad_change_dynamic_label.GetDispatcher().connect(sigc::mem_fun(*this, &DABlinGTK::PADChangeDynamicLabelEmitted)); pad_change_slide.GetDispatcher().connect(sigc::mem_fun(*this, &DABlinGTK::PADChangeSlideEmitted)); + pad_file_progress.GetDispatcher().connect(sigc::mem_fun(*this, &DABlinGTK::PADFileProgressEmitted)); do_rec_status_update.GetDispatcher().connect(sigc::mem_fun(*this, &DABlinGTK::DoRecStatusUpdateEmitted)); do_datetime_sync.GetDispatcher().connect(sigc::mem_fun(*this, &DABlinGTK::DoDateTimeSyncEmitted)); do_datetime_update.GetDispatcher().connect(sigc::mem_fun(*this, &DABlinGTK::DoDateTimeUpdateEmitted)); @@ -1338,6 +1339,12 @@ void DABlinGTK::PADChangeSlideEmitted() { slideshow_window.TryToShow(); } +void DABlinGTK::PADFileProgressEmitted() { +// fprintf(stderr, "### PADFileProgressEmitted\n"); + + slideshow_window.UpdateFileProgress(pad_file_progress.Pop()); +} + void DABlinGTK::PADLengthError(size_t /*announced_xpad_len*/, size_t /*xpad_len*/) { fprintf(stderr, "\x1B[31m" "[X-PAD len]" "\x1B[0m" " "); } diff --git a/src/dablin_gtk.h b/src/dablin_gtk.h index 0db164f..ac6275d 100644 --- a/src/dablin_gtk.h +++ b/src/dablin_gtk.h @@ -1,6 +1,6 @@ /* DABlin - capital DAB experience - Copyright (C) 2015-2021 Stefan Pöschel + Copyright (C) 2015-2022 Stefan Pöschel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -326,6 +326,10 @@ class DABlinGTK : public Gtk::Window, EnsembleSourceObserver, EnsemblePlayerObse void PADChangeSlide(const MOT_FILE& slide) {pad_change_slide.PushAndEmit(slide);} void PADChangeSlideEmitted(); + GTKDispatcherQueue pad_file_progress; + void PADFileProgress(const double fraction) {pad_file_progress.PushAndEmit(fraction);} + void PADFileProgressEmitted(); + void PADLengthError(size_t announced_xpad_len, size_t xpad_len); public: DABlinGTK(DABlinGTKOptions options); diff --git a/src/dablin_gtk_sls.cpp b/src/dablin_gtk_sls.cpp index de3f430..49d7e59 100644 --- a/src/dablin_gtk_sls.cpp +++ b/src/dablin_gtk_sls.cpp @@ -1,6 +1,6 @@ /* DABlin - capital DAB experience - Copyright (C) 2018-2021 Stefan Pöschel + Copyright (C) 2018-2022 Stefan Pöschel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,6 +36,7 @@ DABlinGTKSlideshowWindow::DABlinGTKSlideshowWindow() { top_grid.attach(image, 0, 0, 1, 1); top_grid.attach_next_to(link_button, image, Gtk::POS_BOTTOM, 1, 1); + top_grid.attach_next_to(progress_file, link_button, Gtk::POS_BOTTOM, 1, 1); show_all_children(); @@ -98,6 +99,7 @@ void DABlinGTKSlideshowWindow::AwaitSlide() { image.set_tooltip_text("Waiting for slide..."); link_button.hide(); + progress_file.hide(); } void DABlinGTKSlideshowWindow::UpdateSlide(const MOT_FILE& slide) { @@ -153,4 +155,16 @@ void DABlinGTKSlideshowWindow::UpdateSlide(const MOT_FILE& slide) { } else { link_button.hide(); } + + // hide file progress + progress_file.hide(); +} + +void DABlinGTKSlideshowWindow::UpdateFileProgress(const double fraction) { + // update/show file progress + if(fraction == -1) + progress_file.pulse(); // unknown progress + else + progress_file.set_fraction(fraction); + progress_file.show(); } diff --git a/src/dablin_gtk_sls.h b/src/dablin_gtk_sls.h index 2ab8431..008045e 100644 --- a/src/dablin_gtk_sls.h +++ b/src/dablin_gtk_sls.h @@ -1,6 +1,6 @@ /* DABlin - capital DAB experience - Copyright (C) 2018-2021 Stefan Pöschel + Copyright (C) 2018-2022 Stefan Pöschel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,6 +33,7 @@ class DABlinGTKSlideshowWindow : public Gtk::Window { Gtk::Grid top_grid; Gtk::Image image; Gtk::LinkButton link_button; + Gtk::ProgressBar progress_file; Glib::RefPtr pixbuf_waiting; int prev_parent_x; @@ -47,6 +48,7 @@ class DABlinGTKSlideshowWindow : public Gtk::Window { void AwaitSlide(); void UpdateSlide(const MOT_FILE& slide); + void UpdateFileProgress(const double fraction); void ClearSlide() {image.clear();} bool IsEmptySlide() {return image.get_storage_type() == Gtk::ImageType::IMAGE_EMPTY;} }; diff --git a/src/mot_manager.cpp b/src/mot_manager.cpp index 2ad912d..abc4d81 100644 --- a/src/mot_manager.cpp +++ b/src/mot_manager.cpp @@ -196,7 +196,7 @@ bool MOTObject::IsToBeShown() { // abort, if incomplete/not yet triggered if(!header_received) return false; - if(!body.IsFinished() || result_file.body_size != body.GetSize()) + if(!body.IsFinished() || GetCurrentBodySize() != GetTotalBodySize()) return false; if(!result_file.trigger_time_now) return false; @@ -298,7 +298,7 @@ void MOTManager::HandleMOTDataGroup(const std::vector& dg) { return; - // add segment to MOT object (reset if necessary) + // add completed segment to MOT object (reset if necessary) if(current_transport_id != transport_id) { current_transport_id = transport_id; object = MOTObject(); @@ -307,8 +307,19 @@ void MOTManager::HandleMOTDataGroup(const std::vector& dg) { // check if object shall be shown bool display = object.IsToBeShown(); -// fprintf(stderr, "dg_type: %d, seg_number: %2d%s, transport_id: %5d, size: %4zu; display: %s\n", -// dg_type, seg_number, last_seg ? " (LAST)" : "", transport_id, seg_size, display ? "true" : "false"); + + // derive progress fraction (-1 = unknown, as header not yet received - but body segments) + double fraction = object.GetTotalBodySize() ? (double) object.GetCurrentBodySize() / (double) object.GetTotalBodySize() : -1; + +// fprintf(stderr, "dg_type: %d, seg_number: %2d%s, transport_id: %5d, seg_size: %4zu; display: %s, curr/total body size: %5zu/%5zu, fraction: %f\n", +// dg_type, seg_number, last_seg ? " (LAST)" : "", transport_id, seg_size, display ? "true" : "false", object.GetCurrentBodySize(), object.GetTotalBodySize(), fraction); + + /* update progress + * - if unknown, update invoked for each segment for visible pulse()! + * - if complete, only update when shown + */ + if(!(fraction == 1 && !display)) + observer->MOTFileProgress(fraction); // if object shall be shown, forward it if(display) diff --git a/src/mot_manager.h b/src/mot_manager.h index 905cd0d..11e5aab 100644 --- a/src/mot_manager.h +++ b/src/mot_manager.h @@ -52,7 +52,7 @@ struct MOT_FILE { static const int CONTENT_SUB_TYPE_HEADER_UPDATE = 0x000; MOT_FILE() : - body_size(-1), + body_size(0), content_type(-1), content_sub_type(-1), trigger_time_now(false) @@ -101,6 +101,9 @@ class MOTObject { void AddSeg(bool dg_type_header, int seg_number, bool last_seg, const uint8_t* data, size_t len); bool IsToBeShown(); MOT_FILE GetFile() {return result_file;} + + size_t GetCurrentBodySize() {return body.GetSize();} + size_t GetTotalBodySize() {return result_file.body_size;} }; @@ -110,6 +113,7 @@ class MOTManagerObserver { virtual ~MOTManagerObserver() {} virtual void MOTFileCompleted(const MOT_FILE& /*file*/) {} + virtual void MOTFileProgress(const double /*fraction*/) {} }; diff --git a/src/pad_decoder.h b/src/pad_decoder.h index 2c1cb8a..0f5bd5d 100644 --- a/src/pad_decoder.h +++ b/src/pad_decoder.h @@ -205,6 +205,7 @@ class PADDecoderObserver { virtual void PADChangeDynamicLabel(const DL_STATE& /*dl*/) {} virtual void PADChangeSlide(const MOT_FILE& /*slide*/) {} + virtual void PADFileProgress(const double /*fraction*/) {} virtual void PADLengthError(size_t /*announced_xpad_len*/, size_t /*xpad_len*/) {} }; @@ -226,6 +227,7 @@ class PADDecoder : public MOTManagerObserver { MOTManager *mot_manager; void MOTFileCompleted(const MOT_FILE& file); + void MOTFileProgress(const double fraction) {observer->PADFileProgress(fraction);} public: PADDecoder(PADDecoderObserver *observer, bool loose); ~PADDecoder();