libdcp
dcp_time.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
3 
4  This file is part of libdcp.
5 
6  libdcp is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10 
11  libdcp is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with libdcp. If not, see <http://www.gnu.org/licenses/>.
18 
19  In addition, as a special exception, the copyright holders give
20  permission to link the code of portions of this program with the
21  OpenSSL library under certain conditions as described in each
22  individual source file, and distribute linked combinations
23  including the two.
24 
25  You must obey the GNU General Public License in all respects
26  for all of the code used other than OpenSSL. If you modify
27  file(s) with this exception, you may extend this exception to your
28  version of the file(s), but you are not obligated to do so. If you
29  do not wish to do so, delete this exception statement from your
30  version. If you delete this exception statement from all source
31  files in the program, then also delete it here.
32 */
33 
34 
40 #include "raw_convert.h"
41 #include "dcp_time.h"
42 #include "exceptions.h"
43 #include "compose.hpp"
44 #include "dcp_assert.h"
45 #include <boost/algorithm/string.hpp>
46 #include <boost/optional.hpp>
47 #include <iostream>
48 #include <vector>
49 #include <cmath>
50 
51 
52 using std::ostream;
53 using std::string;
54 using std::vector;
55 using boost::is_any_of;
56 using boost::optional;
57 using namespace dcp;
58 
59 
60 Time::Time (int frame, double frames_per_second, int tcr_)
61 {
62  set (double (frame) / frames_per_second, tcr_);
63 }
64 
65 
66 Time::Time (double seconds, int tcr_)
67 {
68  set (seconds, tcr_);
69 }
70 
71 
78 void
79 Time::set (double seconds, int tcr_)
80 {
81  s = floor (seconds);
82  tcr = tcr_;
83 
84  e = int (round ((seconds - s) * tcr));
85 
86  if (s >= 60) {
87  m = s / 60;
88  s -= m * 60;
89  } else {
90  m = 0;
91  }
92 
93  if (m >= 60) {
94  h = m / 60;
95  m -= h * 60;
96  } else {
97  h = 0;
98  }
99 }
100 
101 
102 Time::Time (string time, optional<int> tcr_)
103 {
104  vector<string> b;
105  split (b, time, is_any_of (":"));
106 
107  if (b.size() < 3 || b[0].empty() || b[1].empty() || b[0].length() > 2 || b[1].length() > 2) {
108  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1", time)));
109  }
110 
111  if (!tcr_) {
112  /* Interop */
113  if (b.size() == 3) {
114  /* HH:MM:SS.s[s[s]] */
115  vector<string> bs;
116  split (bs, b[2], is_any_of ("."));
117  if (bs.size() != 2) {
118  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1", time)));
119  }
120 
121  h = raw_convert<int> (b[0]);
122  m = raw_convert<int> (b[1]);
123  if (bs[0].empty() || bs[0].length() > 2) {
124  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; %2 has bad length", time, bs[0])));
125  }
126  s = raw_convert<int> (bs[0]);
127  if (bs[1].empty() || bs[1].length() > 3) {
128  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; %2 has bad length", time, bs[1])));
129  }
130  e = raw_convert<int> (bs[1]);
131  tcr = 1000;
132  } else if (b.size() == 4) {
133  /* HH:MM:SS:EE[E] */
134  h = raw_convert<int> (b[0]);
135  m = raw_convert<int> (b[1]);
136  if (b[2].empty() || b[2].length() > 2) {
137  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; %2 has bad length", time, b[2])));
138  }
139  s = raw_convert<int> (b[2]);
140  if (b[3].empty() || b[3].length() > 3) {
141  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; %2 has bad length", time, b[3])));
142  }
143  e = raw_convert<int> (b[3]);
144  tcr = 250;
145  } else {
146  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1", time)));
147  }
148 
149  } else {
150  /* SMPTE: HH:MM:SS:EE[E].
151  * It seems like there can be any number of E digits but let's just allow 2 or 3 */
152  split (b, time, is_any_of (":"));
153  if (b.size() != 4) {
154  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; does not have 4 parts", time)));
155  }
156 
157  h = raw_convert<int> (b[0]);
158  m = raw_convert<int> (b[1]);
159  if (b[2].empty() || b[2].length() > 2) {
160  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; %2 has bad length", time, b[2])));
161  }
162  s = raw_convert<int> (b[2]);
163  if (b[3].empty() || b[3].length() > 3) {
164  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; %2 has bad length", time, b[3])));
165  }
166  e = raw_convert<int> (b[3]);
167  tcr = tcr_.get();
168  }
169 }
170 
171 
172 bool
173 dcp::operator== (Time const & a, Time const & b)
174 {
175  return (a.h == b.h && a.m == b.m && a.s == b.s && (a.e * b.tcr) == (b.e * a.tcr));
176 }
177 
178 
179 bool
180 dcp::operator!= (Time const & a, Time const & b)
181 {
182  return !(a == b);
183 }
184 
185 
186 bool
187 dcp::operator<= (Time const & a, Time const & b)
188 {
189  return a < b || a == b;
190 }
191 
192 
193 bool
194 dcp::operator>= (Time const & a, Time const & b)
195 {
196  return a > b || a == b;
197 }
198 
199 
200 bool
201 dcp::operator< (Time const & a, Time const & b)
202 {
203  if (a.h != b.h) {
204  return a.h < b.h;
205  }
206 
207  if (a.m != b.m) {
208  return a.m < b.m;
209  }
210 
211  if (a.s != b.s) {
212  return a.s < b.s;
213  }
214 
215  return (a.e * b.tcr) < (b.e * a.tcr);
216 }
217 
218 
219 bool
220 dcp::operator> (Time const & a, Time const & b)
221 {
222  if (a.h != b.h) {
223  return a.h > b.h;
224  }
225 
226  if (a.m != b.m) {
227  return a.m > b.m;
228  }
229 
230  if (a.s != b.s) {
231  return a.s > b.s;
232  }
233 
234  return (a.e * b.tcr) > (b.e * a.tcr);
235 }
236 
237 
238 ostream &
239 dcp::operator<< (ostream& s, Time const & t)
240 {
241  s << t.h << ":" << t.m << ":" << t.s << "." << t.e;
242  return s;
243 }
244 
245 
246 dcp::Time
247 dcp::operator+ (Time a, Time b)
248 {
249  Time r;
250 
251  /* Make sure we have a common tcr */
252  if (a.tcr != b.tcr) {
253  a.e *= b.tcr;
254  b.e *= a.tcr;
255  r.tcr = a.tcr * b.tcr;
256  } else {
257  r.tcr = a.tcr;
258  }
259 
260  r.e = a.e + b.e;
261  if (r.e >= r.tcr) {
262  r.e -= r.tcr;
263  r.s++;
264  }
265 
266  r.s += a.s + b.s;
267  if (r.s >= 60) {
268  r.s -= 60;
269  r.m++;
270  }
271 
272  r.m += a.m + b.m;
273  if (r.m >= 60) {
274  r.m -= 60;
275  r.h++;
276  }
277 
278  r.h += a.h + b.h;
279 
280  return r;
281 }
282 
283 
284 dcp::Time
285 dcp::operator- (Time a, Time b)
286 {
287  Time r;
288 
289  /* Make sure we have a common tcr */
290  if (a.tcr != b.tcr) {
291  a.e *= b.tcr;
292  b.e *= a.tcr;
293  r.tcr = a.tcr * b.tcr;
294  } else {
295  r.tcr = a.tcr;
296  }
297 
298  r.e = a.e - b.e;
299  if (r.e < 0) {
300  r.e += r.tcr;
301  r.s--;
302  }
303 
304  r.s += (a.s - b.s);
305  if (r.s < 0) {
306  r.s += 60;
307  r.m--;
308  }
309 
310  r.m += (a.m - b.m);
311  if (r.m < 0) {
312  r.m += 60;
313  r.h--;
314  }
315 
316  r.h += (a.h - b.h);
317 
318  return r;
319 }
320 
321 
322 float
323 dcp::operator/ (Time a, Time const & b)
324 {
325  int64_t const at = a.h * 3600 + a.m * 60 + a.s * float (a.e) / a.tcr;
326  int64_t const bt = b.h * 3600 + b.m * 60 + b.s * float (b.e) / b.tcr;
327  return float (at) / bt;
328 }
329 
330 
331 string
332 Time::as_string (Standard standard) const
333 {
334  char buffer[64];
335 
336  if (standard == Standard::SMPTE) {
337  snprintf (buffer, sizeof(buffer), "%02d:%02d:%02d:%02d", h, m, s, e);
338  } else {
339  snprintf (buffer, sizeof(buffer), "%02d:%02d:%02d:%03d", h, m, s, e);
340  }
341 
342  return buffer;
343 }
344 
345 
346 int64_t
348 {
349  return floor(int64_t(e) * double(tcr_) / tcr) + int64_t(s) * tcr_ + int64_t(m) * 60 * tcr_ + int64_t(h) * 60 * 60 * tcr_;
350 }
351 
352 
353 int64_t
355 {
356  return ceil(int64_t(e) * double(tcr_) / tcr) + int64_t(s) * tcr_ + int64_t(m) * 60 * tcr_ + int64_t(h) * 60 * 60 * tcr_;
357 }
358 
359 
360 double
362 {
363  return h * 3600 + m * 60 + s + double(e) / tcr;
364 }
365 
366 
367 Time
368 Time::rebase (int tcr_) const
369 {
370  long int e_ = lrintf (float (e) * tcr_ / tcr);
371  int s_ = s;
372  if (e_ >= tcr_) {
373  e_ -= tcr_;
374  ++s_;
375  }
376  int m_ = m;
377  if (s_ >= 60) {
378  s_ -= 60;
379  ++m_;
380  }
381  int h_ = h;
382  if (m_ >= 60) {
383  m_ -= 60;
384  ++h_;
385  }
386 
387  return Time (h_, m_, s_, e_, tcr_);
388 }
Any error that occurs when reading data from a DCP.
Definition: exceptions.h:106
A representation of time within a DCP.
Definition: dcp_time.h:73
int h
hours
Definition: dcp_time.h:117
int e
editable units (where 1 editable unit is 1 / tcr_ seconds)
Definition: dcp_time.h:120
int64_t as_editable_units_ceil(int tcr_) const
Definition: dcp_time.cc:354
int64_t as_editable_units_floor(int tcr_) const
Definition: dcp_time.cc:347
int m
minutes
Definition: dcp_time.h:118
double as_seconds() const
Definition: dcp_time.cc:361
Time rebase(int tcr_) const
Definition: dcp_time.cc:368
int tcr
timecode rate: the number of editable units per second.
Definition: dcp_time.h:121
void set(double seconds, int tcr)
Definition: dcp_time.cc:79
int s
seconds
Definition: dcp_time.h:119
std::string as_string(Standard standard) const
Definition: dcp_time.cc:332
DCP_ASSERT macro.
Time class.
Exceptions thrown by libdcp.
Namespace for everything in libdcp.
Definition: array_data.h:50
Methods for conversion to/from string.