From: Steinar H. Gunderson Date: Tue, 26 Feb 2019 23:35:19 +0000 (+0100) Subject: Make it possible to queue and play clips with no cue-out set (infinite clips). Note... X-Git-Tag: 1.8.3~27 X-Git-Url: https://git.sesse.net/?p=nageru;a=commitdiff_plain;h=f78363119d05e83b2665d89b147945fc0cdec020 Make it possible to queue and play clips with no cue-out set (infinite clips). Note that you can only have one of them in the clip list for the time being. --- diff --git a/futatabi/export.cpp b/futatabi/export.cpp index d513cf0..a865ac6 100644 --- a/futatabi/export.cpp +++ b/futatabi/export.cpp @@ -227,7 +227,7 @@ void export_interpolated_clip(const string &filename, const vector &clips) for (const Clip &clip : clips) { clips_with_id.emplace_back(ClipWithID{ clip, 0 }); } - double total_length = compute_total_time(clips_with_id); + TimeRemaining total_length = compute_total_time(clips_with_id); promise done_promise; future done = done_promise.get_future(); @@ -237,8 +237,8 @@ void export_interpolated_clip(const string &filename, const vector &clips) player.set_done_callback([&done_promise] { done_promise.set_value(); }); - player.set_progress_callback([¤t_value, total_length](const std::map &player_progress, double time_remaining) { - current_value = 1.0 - time_remaining / total_length; + player.set_progress_callback([¤t_value, total_length](const std::map &player_progress, TimeRemaining time_remaining) { + current_value = 1.0 - time_remaining.t / total_length.t; // Nothing to do about the infinite clips. }); player.play(clips_with_id); while (done.wait_for(std::chrono::milliseconds(100)) != future_status::ready && !progress.wasCanceled()) { diff --git a/futatabi/mainwindow.cpp b/futatabi/mainwindow.cpp index 9b6b478..9bea809 100644 --- a/futatabi/mainwindow.cpp +++ b/futatabi/mainwindow.cpp @@ -246,7 +246,7 @@ MainWindow::MainWindow() live_player_done(); }); }); - live_player->set_progress_callback([this](const map &progress, double time_remaining) { + live_player->set_progress_callback([this](const map &progress, TimeRemaining time_remaining) { post_to_main_thread([this, progress, time_remaining] { live_player_clip_progress(progress, time_remaining); }); @@ -424,11 +424,9 @@ void MainWindow::queue_clicked() if (!selected->hasSelection()) { Clip clip = *cliplist_clips->back(); clip.stream_idx = 0; - if (clip.pts_out != -1) { - playlist_clips->add_clip(clip); - playlist_selection_changed(); - ui->playlist->scrollToBottom(); - } + playlist_clips->add_clip(clip); + playlist_selection_changed(); + ui->playlist->scrollToBottom(); return; } @@ -440,15 +438,13 @@ void MainWindow::queue_clicked() clip.stream_idx = ui->preview_display->get_stream_idx(); } - if (clip.pts_out != -1) { - playlist_clips->add_clip(clip); - playlist_selection_changed(); - ui->playlist->scrollToBottom(); - if (!ui->playlist->selectionModel()->hasSelection()) { - // TODO: Figure out why this doesn't always seem to actually select the row. - QModelIndex bottom = playlist_clips->index(playlist_clips->size() - 1, 0); - ui->playlist->setCurrentIndex(bottom); - } + playlist_clips->add_clip(clip); + playlist_selection_changed(); + ui->playlist->scrollToBottom(); + if (!ui->playlist->selectionModel()->hasSelection()) { + // TODO: Figure out why this doesn't always seem to actually select the row. + QModelIndex bottom = playlist_clips->index(playlist_clips->size() - 1, 0); + ui->playlist->setCurrentIndex(bottom); } } @@ -487,6 +483,9 @@ void MainWindow::preview_clicked() } else { clip.stream_idx = ui->preview_display->get_stream_idx(); } + if (clip.pts_out == -1) { + clip.pts_out = clip.pts_in + int64_t(TIMEBASE) * 86400 * 7; // One week; effectively infinite, but without overflow issues. + } preview_player->play(clip); preview_playing = true; enable_or_disable_preview_button(); @@ -688,7 +687,11 @@ void MainWindow::play_clicked() vector clips; for (unsigned row = start_row; row < playlist_clips->size(); ++row) { - clips.emplace_back(*playlist_clips->clip_with_id(row)); + ClipWithID clip = *playlist_clips->clip_with_id(row); + if (clip.clip.pts_out == -1) { + clip.clip.pts_out = clip.clip.pts_in + int64_t(TIMEBASE) * 86400 * 7; // One week; effectively infinite, but without overflow issues. + } + clips.emplace_back(clip); } live_player->play(clips); playlist_clips->set_progress({ { start_row, 0.0f } }); @@ -743,7 +746,7 @@ void MainWindow::live_player_done() playlist_selection_changed(); } -void MainWindow::live_player_clip_progress(const map &progress, double time_remaining) +void MainWindow::live_player_clip_progress(const map &progress, TimeRemaining time_remaining) { playlist_clips->set_progress(progress); set_output_status(format_duration(time_remaining) + " left"); @@ -1009,7 +1012,7 @@ void MainWindow::playlist_selection_changed() for (size_t row = selected->selectedRows().front().row(); row < playlist_clips->size(); ++row) { clips.emplace_back(*playlist_clips->clip_with_id(row)); } - double remaining = compute_total_time(clips); + TimeRemaining remaining = compute_total_time(clips); set_output_status(format_duration(remaining) + " ready"); } } @@ -1279,15 +1282,7 @@ void MainWindow::enable_or_disable_queue_button() if (cliplist_clips->empty()) { enabled = false; } else { - QItemSelectionModel *selected = ui->clip_list->selectionModel(); - if (!selected->hasSelection()) { - Clip clip = *cliplist_clips->back(); - enabled = clip.pts_out != -1; - } else { - QModelIndex index = selected->currentIndex(); - Clip clip = *cliplist_clips->clip(index.row()); - enabled = clip.pts_out != -1; - } + enabled = true; } ui->queue_btn->setEnabled(enabled); diff --git a/futatabi/mainwindow.h b/futatabi/mainwindow.h index 7328a5e..592309a 100644 --- a/futatabi/mainwindow.h +++ b/futatabi/mainwindow.h @@ -4,6 +4,7 @@ #include "clip_list.h" #include "db.h" #include "midi_mapper.h" +#include "player.h" #include "state.pb.h" #include @@ -143,7 +144,7 @@ private: void speed_lock_clicked(); void preview_player_done(); void live_player_done(); - void live_player_clip_progress(const std::map &progress, double time_remaining); + void live_player_clip_progress(const std::map &progress, TimeRemaining time_remaining); void set_output_status(const std::string &status); void playlist_duplicate(); void playlist_remove(); diff --git a/futatabi/player.cpp b/futatabi/player.cpp index 876c8dc..fa094be 100644 --- a/futatabi/player.cpp +++ b/futatabi/player.cpp @@ -272,7 +272,7 @@ void Player::play_playlist_once() // NOTE: None of this will take into account any snapping done below. double clip_progress = calc_progress(*clip, in_pts_for_progress); map progress{ { clip_list[clip_idx].id, clip_progress } }; - double time_remaining; + TimeRemaining time_remaining; if (next_clip != nullptr && time_left_this_clip <= next_clip_fade_time) { double next_clip_progress = calc_progress(*next_clip, in_pts_secondary_for_progress); progress[clip_list[clip_idx + 1].id] = next_clip_progress; @@ -340,7 +340,7 @@ void Player::play_playlist_once() ss.imbue(locale("C")); ss.precision(3); ss << "Futatabi " NAGERU_VERSION ";PLAYING;"; - ss << fixed << time_remaining; + ss << fixed << time_remaining.t; ss << ";" << format_duration(time_remaining) << " left"; subtitle = ss.str(); } @@ -603,30 +603,34 @@ void Player::release_queue_spot() new_clip_changed.notify_all(); } -double compute_time_left(const vector &clips, size_t currently_playing_idx, double progress_currently_playing) +TimeRemaining compute_time_left(const vector &clips, size_t currently_playing_idx, double progress_currently_playing) { // Look at the last clip and then start counting from there. - double remaining = 0.0; + TimeRemaining remaining { 0, 0.0 }; double last_fade_time_seconds = 0.0; for (size_t row = currently_playing_idx; row < clips.size(); ++row) { const Clip &clip = clips[row].clip; double clip_length = double(clip.pts_out - clip.pts_in) / TIMEBASE / clip.speed; - if (row == currently_playing_idx) { - // A clip we're playing: Subtract the part we've already played. - remaining = clip_length * (1.0 - progress_currently_playing); + if (clip_length >= 86400.0) { // More than one day. + ++remaining.num_infinite; } else { - // A clip we haven't played yet: Subtract the part that's overlapping - // with a previous clip (due to fade). - remaining += max(clip_length - last_fade_time_seconds, 0.0); + if (row == currently_playing_idx) { + // A clip we're playing: Subtract the part we've already played. + remaining.t = clip_length * (1.0 - progress_currently_playing); + } else { + // A clip we haven't played yet: Subtract the part that's overlapping + // with a previous clip (due to fade). + remaining.t += max(clip_length - last_fade_time_seconds, 0.0); + } } last_fade_time_seconds = min(clip_length, clip.fade_time_seconds); } return remaining; } -string format_duration(double t) +string format_duration(TimeRemaining t) { - int t_ms = lrint(t * 1e3); + int t_ms = lrint(t.t * 1e3); int ms = t_ms % 1000; t_ms /= 1000; @@ -635,6 +639,16 @@ string format_duration(double t) int m = t_ms; char buf[256]; - snprintf(buf, sizeof(buf), "%d:%02d.%03d", m, s, ms); + if (t.num_infinite > 1 && t.t > 0.0) { + snprintf(buf, sizeof(buf), "%zu clips + %d:%02d.%03d", t.num_infinite, m, s, ms); + } else if (t.num_infinite > 1) { + snprintf(buf, sizeof(buf), "%zu clips", t.num_infinite); + } else if (t.num_infinite == 1 && t.t > 0.0) { + snprintf(buf, sizeof(buf), "%zu clip + %d:%02d.%03d", t.num_infinite, m, s, ms); + } else if (t.num_infinite == 1) { + snprintf(buf, sizeof(buf), "%zu clip", t.num_infinite); + } else { + snprintf(buf, sizeof(buf), "%d:%02d.%03d", m, s, ms); + } return buf; } diff --git a/futatabi/player.h b/futatabi/player.h index 79ab64d..da5a443 100644 --- a/futatabi/player.h +++ b/futatabi/player.h @@ -20,6 +20,11 @@ class VideoStream; class QSurface; class QSurfaceFormat; +struct TimeRemaining { + size_t num_infinite; + double t; +}; + class Player : public QueueInterface { public: enum StreamOutput { @@ -79,7 +84,7 @@ public: // Not thread-safe to set concurrently with playing. // Will be called back from the player thread. // The keys in the given map are row members in the vector given to play(). - using progress_callback_func = std::function &progress, double time_remaining)>; + using progress_callback_func = std::function &progress, TimeRemaining time_remaining)>; void set_progress_callback(progress_callback_func cb) { progress_callback = cb; } // QueueInterface. @@ -141,13 +146,13 @@ private: const StreamOutput stream_output; }; -double compute_time_left(const std::vector &clips, size_t currently_playing_idx, double progress_currently_playing); +TimeRemaining compute_time_left(const std::vector &clips, size_t currently_playing_idx, double progress_currently_playing); -static inline double compute_total_time(const std::vector &clips) +static inline TimeRemaining compute_total_time(const std::vector &clips) { return compute_time_left(clips, 0, 0.0); } -std::string format_duration(double t); +std::string format_duration(TimeRemaining t); #endif // !defined(_PLAYER_H)