]> git.sesse.net Git - nageru/blob - clip_list.cpp
Read timebase from the input video.
[nageru] / clip_list.cpp
1 #include "mainwindow.h"
2
3 #include <math.h>
4 #include <string>
5 #include <vector>
6
7 #include "clip_list.h"
8 #include "timebase.h"
9 #include "ui_mainwindow.h"
10
11 using namespace std;
12
13 string pts_to_string(int64_t pts)
14 {
15         int64_t t = lrint((pts / double(TIMEBASE)) * 1e3);  // In milliseconds.
16         int ms = t % 1000;
17         t /= 1000;
18         int sec = t % 60;
19         t /= 60;
20         int min = t % 60;
21         t /= 60;
22         int hour = t;
23
24         char buf[256];
25         snprintf(buf, sizeof(buf), "%d:%02d:%02d.%03d", hour, min, sec, ms);
26         return buf;
27 }
28
29 string duration_to_string(int64_t pts_diff)
30 {
31         int64_t t = lrint((pts_diff / double(TIMEBASE)) * 1e3);  // In milliseconds.
32         int ms = t % 1000;
33         t /= 1000;
34         int sec = t % 60;
35         t /= 60;
36         int min = t;
37
38         char buf[256];
39         snprintf(buf, sizeof(buf), "%d:%02d.%03d", min, sec, ms);
40         return buf;
41 }
42
43 int ClipList::rowCount(const QModelIndex &parent) const {
44         if (parent.isValid()) return 0;
45         return clips.size();
46 }
47
48 int PlayList::rowCount(const QModelIndex &parent) const {
49         if (parent.isValid()) return 0;
50         return clips.size();
51 }
52
53 int ClipList::columnCount(const QModelIndex &parent) const {
54         if (parent.isValid()) return 0;
55         return int(Column::NUM_COLUMNS);
56 }
57
58 int PlayList::columnCount(const QModelIndex &parent) const {
59         if (parent.isValid()) return 0;
60         return int(Column::NUM_COLUMNS);
61 }
62
63 QVariant ClipList::data(const QModelIndex &parent, int role) const {
64         if (!parent.isValid())
65                 return QVariant();
66         const int row = parent.row(), column = parent.column();
67         if (size_t(row) >= clips.size())
68                 return QVariant();
69
70         if (role == Qt::TextAlignmentRole) {
71                 switch (Column(column)) {
72                 case Column::IN:
73                 case Column::OUT:
74                 case Column::DURATION:
75                         return Qt::AlignRight + Qt::AlignVCenter;
76                 default:
77                         return Qt::AlignLeft + Qt::AlignVCenter;
78                 }
79         }
80
81         if (role != Qt::DisplayRole && role != Qt::EditRole)
82                 return QVariant();
83
84         switch (Column(column)) {
85         case Column::IN:
86                 return QString::fromStdString(pts_to_string(clips[row].pts_in));
87         case Column::OUT:
88                 if (clips[row].pts_out >= 0) {
89                         return QString::fromStdString(pts_to_string(clips[row].pts_out));
90                 } else {
91                         return QVariant();
92                 }
93         case Column::DURATION:
94                 if (clips[row].pts_out >= 0) {
95                         return QString::fromStdString(duration_to_string(clips[row].pts_out - clips[row].pts_in));
96                 } else {
97                         return QVariant();
98                 }
99         case Column::CAMERA_1:
100         case Column::CAMERA_2:
101         case Column::CAMERA_3:
102         case Column::CAMERA_4: {
103                 unsigned stream_idx = column - int(Column::CAMERA_1);
104                 return QString::fromStdString(clips[row].descriptions[stream_idx]);
105         }
106         default:
107                 return "";
108         }
109 }
110
111 QVariant PlayList::data(const QModelIndex &parent, int role) const {
112         if (!parent.isValid())
113                 return QVariant();
114         const int row = parent.row(), column = parent.column();
115         if (size_t(row) >= clips.size())
116                 return QVariant();
117
118         if (role == Qt::TextAlignmentRole) {
119                 switch (Column(column)) {
120                 case Column::PLAYING:
121                         return Qt::AlignCenter;
122                 case Column::IN:
123                 case Column::OUT:
124                 case Column::DURATION:
125                         return Qt::AlignRight + Qt::AlignVCenter;
126                 case Column::CAMERA:
127                         return Qt::AlignCenter;
128                 default:
129                         return Qt::AlignLeft + Qt::AlignVCenter;
130                 }
131         }
132
133         if (role != Qt::DisplayRole && role != Qt::EditRole)
134                 return QVariant();
135
136         switch (Column(column)) {
137         case Column::PLAYING:
138                 return (row == currently_playing_index) ? "→" : "";
139         case Column::IN:
140                 return QString::fromStdString(pts_to_string(clips[row].pts_in));
141         case Column::OUT:
142                 if (clips[row].pts_out >= 0) {
143                         return QString::fromStdString(pts_to_string(clips[row].pts_out));
144                 } else {
145                         return QVariant();
146                 }
147         case Column::DURATION:
148                 if (clips[row].pts_out >= 0) {
149                         return QString::fromStdString(duration_to_string(clips[row].pts_out - clips[row].pts_in));
150                 } else {
151                         return QVariant();
152                 }
153         case Column::CAMERA:
154                 return qlonglong(clips[row].stream_idx + 1);
155         case Column::DESCRIPTION:
156                 return QString::fromStdString(clips[row].descriptions[clips[row].stream_idx]);
157         default:
158                 return "";
159         }
160 }
161
162 QVariant ClipList::headerData(int section, Qt::Orientation orientation, int role) const {
163         if (role != Qt::DisplayRole)
164                 return QVariant();
165         if (orientation != Qt::Horizontal)
166                 return QVariant();
167
168         switch (Column(section)) {
169         case Column::IN:
170                 return "In";
171         case Column::OUT:
172                 return "Out";
173         case Column::DURATION:
174                 return "Duration";
175         case Column::CAMERA_1:
176                 return "Camera 1";
177         case Column::CAMERA_2:
178                 return "Camera 2";
179         case Column::CAMERA_3:
180                 return "Camera 3";
181         case Column::CAMERA_4:
182                 return "Camera 4";
183         default:
184                 return "";
185         }
186 }
187
188 QVariant PlayList::headerData(int section, Qt::Orientation orientation, int role) const {
189         if (role != Qt::DisplayRole)
190                 return QVariant();
191         if (orientation != Qt::Horizontal)
192                 return QVariant();
193
194         switch (Column(section)) {
195         case Column::PLAYING:
196                 return "";
197         case Column::IN:
198                 return "In";
199         case Column::OUT:
200                 return "Out";
201         case Column::DURATION:
202                 return "Duration";
203         case Column::CAMERA:
204                 return "Camera";
205         case Column::DESCRIPTION:
206                 return "Description";
207         default:
208                 return "";
209         }
210 }
211
212 Qt::ItemFlags ClipList::flags(const QModelIndex &index) const
213 {
214         if (!index.isValid())
215                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
216         const int row = index.row(), column = index.column();
217         if (size_t(row) >= clips.size())
218                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
219
220         switch (Column(column)) {
221         case Column::CAMERA_1:
222         case Column::CAMERA_2:
223         case Column::CAMERA_3:
224         case Column::CAMERA_4:
225                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
226         default:
227                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
228         }
229 }
230
231 Qt::ItemFlags PlayList::flags(const QModelIndex &index) const
232 {
233         if (!index.isValid())
234                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
235         const int row = index.row(), column = index.column();
236         if (size_t(row) >= clips.size())
237                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
238
239         switch (Column(column)) {
240         case Column::DESCRIPTION:
241         case Column::CAMERA:
242                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
243                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
244         default:
245                 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
246         }
247 }
248
249 bool ClipList::setData(const QModelIndex &index, const QVariant &value, int role)
250 {
251         if (!index.isValid() || role != Qt::EditRole) {
252                 return false;
253         }
254
255         const int row = index.row(), column = index.column();
256         if (size_t(row) >= clips.size())
257                 return false;
258
259         switch (Column(column)) {
260         case Column::CAMERA_1:
261         case Column::CAMERA_2:
262         case Column::CAMERA_3:
263         case Column::CAMERA_4: {
264                 unsigned stream_idx = column - int(Column::CAMERA_1);
265                 clips[row].descriptions[stream_idx] = value.toString().toStdString();
266                 emit_data_changed(row);
267                 return true;
268         }
269         default:
270                 return false;
271         }
272 }
273
274 bool PlayList::setData(const QModelIndex &index, const QVariant &value, int role)
275 {
276         if (!index.isValid() || role != Qt::EditRole) {
277                 return false;
278         }
279
280         const int row = index.row(), column = index.column();
281         if (size_t(row) >= clips.size())
282                 return false;
283
284         switch (Column(column)) {
285         case Column::DESCRIPTION:
286                 clips[row].descriptions[clips[row].stream_idx] = value.toString().toStdString();
287                 emit_data_changed(row);
288                 return true;
289         case Column::CAMERA: {
290                 bool ok;
291                 int camera_idx = value.toInt(&ok);
292                 if (!ok || camera_idx < 1 || camera_idx > NUM_CAMERAS) {
293                         return false;
294                 }
295                 clips[row].stream_idx = camera_idx - 1;
296                 emit_data_changed(row);
297                 return true;
298         }
299         default:
300                 return false;
301         }
302 }
303
304 void ClipList::add_clip(const Clip &clip)
305 {
306         beginInsertRows(QModelIndex(), clips.size(), clips.size());
307         clips.push_back(clip);
308         endInsertRows();
309 }
310
311 void PlayList::add_clip(const Clip &clip)
312 {
313         beginInsertRows(QModelIndex(), clips.size(), clips.size());
314         clips.push_back(clip);
315         endInsertRows();
316 }
317
318 void PlayList::duplicate_clips(size_t first, size_t last)
319 {
320         beginInsertRows(QModelIndex(), first, last);
321         clips.insert(clips.begin() + first, clips.begin() + first, clips.begin() + last + 1);
322         endInsertRows();
323 }
324
325 void PlayList::erase_clips(size_t first, size_t last)
326 {
327         beginRemoveRows(QModelIndex(), first, last);
328         clips.erase(clips.begin() + first, clips.begin() + last + 1);
329         endRemoveRows();
330 }
331
332 void PlayList::move_clips(size_t first, size_t last, int delta)
333 {
334         if (delta == -1) {
335                 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first - 1);
336                 rotate(clips.begin() + first - 1, clips.begin() + first, clips.begin() + last + 1);
337         } else {
338                 beginMoveRows(QModelIndex(), first, last, QModelIndex(), first + (last-first+1) + 1);
339                 first = clips.size() - first - 1;
340                 last = clips.size() - last - 1;
341                 rotate(clips.rbegin() + last - 1, clips.rbegin() + last, clips.rbegin() + first + 1);
342         }
343         endMoveRows();
344 }
345
346 void ClipList::emit_data_changed(size_t row)
347 {
348         emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
349 }
350
351 void PlayList::emit_data_changed(size_t row)
352 {
353         emit dataChanged(index(row, 0), index(row, int(Column::NUM_COLUMNS)));
354 }
355
356 void PlayList::set_currently_playing(int index)
357 {
358         int old_index = currently_playing_index;
359         if (index != old_index) {
360                 currently_playing_index = index;
361                 if (old_index != -1) {
362                         emit_data_changed(old_index);
363                 }
364                 if (index != -1) {
365                         emit_data_changed(index);
366                 }
367         }
368 }