3 #include "mainwindow.h"
4 #include "shared/timebase.h"
5 #include "ui_mainwindow.h"
13 string pts_to_string(int64_t pts)
15 int64_t t = lrint((pts / double(TIMEBASE)) * 1e3); // In milliseconds.
25 snprintf(buf, sizeof(buf), "%d:%02d:%02d.%03d", hour, min, sec, ms);
29 string duration_to_string(int64_t pts_diff)
31 int64_t t = lrint((pts_diff / double(TIMEBASE)) * 1e3); // In milliseconds.
39 snprintf(buf, sizeof(buf), "%d:%02d.%03d", min, sec, ms);
43 int ClipList::rowCount(const QModelIndex &parent) const
50 int PlayList::rowCount(const QModelIndex &parent) const
57 int ClipList::columnCount(const QModelIndex &parent) const
61 return int(Column::NUM_NON_CAMERA_COLUMNS) + num_cameras;
64 int PlayList::columnCount(const QModelIndex &parent) const
68 return int(Column::NUM_COLUMNS);
71 QVariant ClipList::data(const QModelIndex &parent, int role) const
73 if (!parent.isValid())
75 const int row = parent.row(), column = parent.column();
76 if (size_t(row) >= clips.size())
79 if (role == Qt::TextAlignmentRole) {
80 switch (Column(column)) {
83 case Column::DURATION:
84 return Qt::AlignRight + Qt::AlignVCenter;
86 return Qt::AlignLeft + Qt::AlignVCenter;
90 if (role != Qt::DisplayRole && role != Qt::EditRole)
93 switch (Column(column)) {
95 return QString::fromStdString(pts_to_string(clips[row].pts_in));
97 if (clips[row].pts_out >= 0) {
98 return QString::fromStdString(pts_to_string(clips[row].pts_out));
102 case Column::DURATION:
103 if (clips[row].pts_out >= 0) {
104 return QString::fromStdString(duration_to_string(clips[row].pts_out - clips[row].pts_in));
109 if (is_camera_column(column)) {
110 unsigned stream_idx = column - int(Column::CAMERA_1);
111 return QString::fromStdString(clips[row].descriptions[stream_idx]);
118 QVariant PlayList::data(const QModelIndex &parent, int role) const
120 if (!parent.isValid())
122 const int row = parent.row(), column = parent.column();
123 if (size_t(row) >= clips.size())
126 if (role == Qt::TextAlignmentRole) {
127 switch (Column(column)) {
128 case Column::PLAYING:
129 return Qt::AlignCenter;
132 case Column::DURATION:
133 case Column::FADE_TIME:
135 return Qt::AlignRight + Qt::AlignVCenter;
137 return Qt::AlignCenter;
139 return Qt::AlignLeft + Qt::AlignVCenter;
142 if (role == Qt::BackgroundRole) {
143 if (Column(column) == Column::PLAYING) {
144 auto it = current_progress.find(clips[row].id);
145 if (it != current_progress.end()) {
146 double play_progress = it->second;
148 // This only really works well for the first column, for whatever odd Qt reason.
149 QLinearGradient grad(QPointF(0, 0), QPointF(1, 0));
150 grad.setCoordinateMode(grad.QGradient::ObjectBoundingMode);
151 grad.setColorAt(0.0f, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.2f));
152 grad.setColorAt(play_progress, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.2f));
153 if (play_progress + 0.01f <= 1.0f) {
154 grad.setColorAt(play_progress + 0.01f, QColor::fromRgbF(0.0f, 0.0f, 1.0f, 0.0f));
165 if (role != Qt::DisplayRole && role != Qt::EditRole)
168 switch (Column(column)) {
169 case Column::PLAYING:
170 return current_progress.count(clips[row].id) ? "→" : "";
172 return QString::fromStdString(pts_to_string(clips[row].clip.pts_in));
174 if (clips[row].clip.pts_out >= 0) {
175 return QString::fromStdString(pts_to_string(clips[row].clip.pts_out));
179 case Column::DURATION:
180 if (clips[row].clip.pts_out >= 0) {
181 return QString::fromStdString(duration_to_string(clips[row].clip.pts_out - clips[row].clip.pts_in));
186 return qlonglong(clips[row].clip.stream_idx + 1);
187 case Column::DESCRIPTION:
188 return QString::fromStdString(clips[row].clip.descriptions[clips[row].clip.stream_idx]);
189 case Column::FADE_TIME: {
191 ss.imbue(locale("C"));
193 ss << fixed << clips[row].clip.fade_time_seconds;
194 return QString::fromStdString(ss.str());
196 case Column::SPEED: {
198 ss.imbue(locale("C"));
200 ss << fixed << clips[row].clip.speed;
201 return QString::fromStdString(ss.str());
208 QVariant ClipList::headerData(int section, Qt::Orientation orientation, int role) const
210 if (role != Qt::DisplayRole)
212 if (orientation != Qt::Horizontal)
215 switch (Column(section)) {
220 case Column::DURATION:
223 if (is_camera_column(section)) {
224 return QString::fromStdString("Camera " + to_string(section - int(Column::CAMERA_1) + 1));
231 QVariant PlayList::headerData(int section, Qt::Orientation orientation, int role) const
233 if (role != Qt::DisplayRole)
235 if (orientation != Qt::Horizontal)
238 switch (Column(section)) {
239 case Column::PLAYING:
245 case Column::DURATION:
249 case Column::DESCRIPTION:
250 return "Description";
251 case Column::FADE_TIME:
260 Qt::ItemFlags ClipList::flags(const QModelIndex &index) const
262 if (!index.isValid())
263 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
264 const int row = index.row(), column = index.column();
265 if (size_t(row) >= clips.size())
266 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
268 if (is_camera_column(column)) {
269 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
271 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
275 Qt::ItemFlags PlayList::flags(const QModelIndex &index) const
277 if (!index.isValid())
278 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
279 const int row = index.row(), column = index.column();
280 if (size_t(row) >= clips.size())
281 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
283 switch (Column(column)) {
284 case Column::DESCRIPTION:
286 case Column::FADE_TIME:
288 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
290 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
294 bool ClipList::setData(const QModelIndex &index, const QVariant &value, int role)
296 if (!index.isValid() || role != Qt::EditRole) {
300 const int row = index.row(), column = index.column();
301 if (size_t(row) >= clips.size())
304 if (is_camera_column(column)) {
305 unsigned stream_idx = column - int(Column::CAMERA_1);
306 clips[row].descriptions[stream_idx] = value.toString().toStdString();
307 emit_data_changed(row);
314 bool PlayList::setData(const QModelIndex &index, const QVariant &value, int role)
316 if (!index.isValid() || role != Qt::EditRole) {
320 const int row = index.row(), column = index.column();
321 if (size_t(row) >= clips.size())
324 switch (Column(column)) {
325 case Column::DESCRIPTION:
326 clips[row].clip.descriptions[clips[row].clip.stream_idx] = value.toString().toStdString();
327 emit_data_changed(row);
329 case Column::CAMERA: {
331 int camera_idx = value.toInt(&ok);
332 if (!ok || camera_idx < 1 || camera_idx > int(num_cameras)) {
335 clips[row].clip.stream_idx = camera_idx - 1;
336 emit_data_changed(row);
339 case Column::FADE_TIME: {
341 double val = value.toDouble(&ok);
342 if (!ok || !(val >= 0.0)) {
345 clips[row].clip.fade_time_seconds = val;
346 emit_data_changed(row);
349 case Column::SPEED: {
351 double val = value.toDouble(&ok);
352 if (!ok || !(val >= 0.001)) {
355 clips[row].clip.speed = val;
356 emit_data_changed(row);
364 void ClipList::add_clip(const Clip &clip)
366 beginInsertRows(QModelIndex(), clips.size(), clips.size());
367 clips.push_back(clip);
369 emit any_content_changed();
372 void PlayList::add_clip(const Clip &clip)
374 beginInsertRows(QModelIndex(), clips.size(), clips.size());
375 clips.emplace_back(ClipWithID{ clip, clip_counter++ });
377 emit any_content_changed();
380 void PlayList::duplicate_clips(size_t first, size_t last)
382 beginInsertRows(QModelIndex(), last + 1, last + 1 + (last - first));
384 vector<ClipWithID> new_clips;
385 for (auto it = clips.begin() + first; it <= clips.begin() + last; ++it) {
386 new_clips.emplace_back(ClipWithID{ it->clip, clip_counter++ }); // Give them new IDs.
388 clips.insert(clips.begin() + last + 1, new_clips.begin(), new_clips.end()); // Note: The new elements are inserted after the old ones.
390 emit any_content_changed();
393 void PlayList::erase_clips(size_t first, size_t last)
395 beginRemoveRows(QModelIndex(), first, last);
396 clips.erase(clips.begin() + first, clips.begin() + last + 1);
398 emit any_content_changed();
401 void PlayList::move_clips(size_t first, size_t last, int delta)
404 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first - 1);
405 rotate(clips.begin() + first - 1, clips.begin() + first, clips.begin() + last + 1);
407 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first + (last - first + 1) + 1);
408 first = clips.size() - first - 1;
409 last = clips.size() - last - 1;
410 rotate(clips.rbegin() + last - 1, clips.rbegin() + last, clips.rbegin() + first + 1);
413 emit any_content_changed();
416 void ClipList::emit_data_changed(size_t row)
418 emit dataChanged(index(row, 0), index(row, int(Column::NUM_NON_CAMERA_COLUMNS) + num_cameras));
419 emit any_content_changed();
422 void PlayList::emit_data_changed(size_t row)
424 emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
425 emit any_content_changed();
428 void ClipList::change_num_cameras(size_t num_cameras)
430 assert(num_cameras >= this->num_cameras);
431 if (num_cameras == this->num_cameras) {
435 beginInsertColumns(QModelIndex(), int(Column::NUM_NON_CAMERA_COLUMNS) + this->num_cameras, int(Column::NUM_NON_CAMERA_COLUMNS) + num_cameras - 1);
436 this->num_cameras = num_cameras;
438 emit any_content_changed();
441 void PlayList::set_progress(const map<uint64_t, double> &progress)
443 const int column = int(Column::PLAYING);
444 map<uint64_t, double> old_progress = move(this->current_progress);
445 this->current_progress = progress;
447 for (size_t row = 0; row < clips.size(); ++row) {
448 uint64_t id = clips[row].id;
449 if (current_progress.count(id) || old_progress.count(id)) {
450 emit dataChanged(this->index(row, column), this->index(row, column));
457 Clip deserialize_clip(const ClipProto &clip_proto)
460 clip.pts_in = clip_proto.pts_in();
461 clip.pts_out = clip_proto.pts_out();
462 for (int camera_idx = 0; camera_idx < min(clip_proto.description_size(), MAX_STREAMS); ++camera_idx) {
463 clip.descriptions[camera_idx] = clip_proto.description(camera_idx);
465 clip.stream_idx = clip_proto.stream_idx();
466 clip.fade_time_seconds = clip_proto.fade_time_seconds();
467 if (clip_proto.speed() < 0.001) {
468 clip.speed = 0.5; // Default.
470 clip.speed = clip_proto.speed();
475 void serialize_clip(const Clip &clip, ClipProto *clip_proto)
477 clip_proto->set_pts_in(clip.pts_in);
478 clip_proto->set_pts_out(clip.pts_out);
479 for (int camera_idx = 0; camera_idx < MAX_STREAMS; ++camera_idx) {
480 *clip_proto->add_description() = clip.descriptions[camera_idx];
482 clip_proto->set_stream_idx(clip.stream_idx);
483 clip_proto->set_fade_time_seconds(clip.fade_time_seconds);
484 clip_proto->set_speed(clip.speed);
489 ClipList::ClipList(const ClipListProto &serialized)
491 for (const ClipProto &clip_proto : serialized.clip()) {
492 clips.push_back(deserialize_clip(clip_proto));
496 ClipListProto ClipList::serialize() const
499 for (const Clip &clip : clips) {
500 serialize_clip(clip, ret.add_clip());
505 PlayList::PlayList(const ClipListProto &serialized)
507 for (const ClipProto &clip_proto : serialized.clip()) {
508 clips.emplace_back(ClipWithID{ deserialize_clip(clip_proto), clip_counter++ });
512 ClipListProto PlayList::serialize() const
515 for (const ClipWithID &clip : clips) {
516 serialize_clip(clip.clip, ret.add_clip());