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 namespace std;
53 using namespace boost;
54 using namespace dcp;
55 
56 
57 Time::Time (int frame, double frames_per_second, int tcr_)
58 {
59  set (double (frame) / frames_per_second, tcr_);
60 }
61 
62 
63 Time::Time (double seconds, int tcr_)
64 {
65  set (seconds, tcr_);
66 }
67 
68 
75 void
76 Time::set (double seconds, int tcr_)
77 {
78  s = floor (seconds);
79  tcr = tcr_;
80 
81  e = int (round ((seconds - s) * tcr));
82 
83  if (s >= 60) {
84  m = s / 60;
85  s -= m * 60;
86  } else {
87  m = 0;
88  }
89 
90  if (m >= 60) {
91  h = m / 60;
92  m -= h * 60;
93  } else {
94  h = 0;
95  }
96 }
97 
98 
99 Time::Time (string time, optional<int> tcr_)
100 {
101  vector<string> b;
102  split (b, time, is_any_of (":"));
103 
104  if (b.size() < 3 || b[0].empty() || b[1].empty() || b[0].length() > 2 || b[1].length() > 2) {
105  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1", time)));
106  }
107 
108  if (!tcr_) {
109  /* Interop */
110  if (b.size() == 3) {
111  /* HH:MM:SS.s[s[s]] */
112  vector<string> bs;
113  split (bs, b[2], is_any_of ("."));
114  if (bs.size() != 2) {
115  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1", time)));
116  }
117 
118  h = raw_convert<int> (b[0]);
119  m = raw_convert<int> (b[1]);
120  if (bs[0].empty() || bs[0].length() > 2) {
121  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; %2 has bad length", time, bs[0])));
122  }
123  s = raw_convert<int> (bs[0]);
124  if (bs[1].empty() || bs[1].length() > 3) {
125  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; %2 has bad length", time, bs[1])));
126  }
127  e = raw_convert<int> (bs[1]);
128  tcr = 1000;
129  } else if (b.size() == 4) {
130  /* HH:MM:SS:EE[E] */
131  h = raw_convert<int> (b[0]);
132  m = raw_convert<int> (b[1]);
133  if (b[2].empty() || b[2].length() > 2) {
134  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; %2 has bad length", time, b[2])));
135  }
136  s = raw_convert<int> (b[2]);
137  if (b[3].empty() || b[3].length() > 3) {
138  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; %2 has bad length", time, b[3])));
139  }
140  e = raw_convert<int> (b[3]);
141  tcr = 250;
142  } else {
143  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1", time)));
144  }
145 
146  } else {
147  /* SMPTE: HH:MM:SS:EE */
148  split (b, time, is_any_of (":"));
149  if (b.size() != 4) {
150  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; does not have 4 parts", time)));
151  }
152 
153  h = raw_convert<int> (b[0]);
154  m = raw_convert<int> (b[1]);
155  if (b[2].empty() || b[2].length() > 2) {
156  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; %2 has bad length", time, b[2])));
157  }
158  s = raw_convert<int> (b[2]);
159  if (b[3].empty() || b[3].length() > 2) {
160  boost::throw_exception (ReadError (String::compose ("unrecognised time specification %1; %2 has bad length", time, b[3])));
161  }
162  e = raw_convert<int> (b[3]);
163  tcr = tcr_.get();
164  }
165 }
166 
167 
168 bool
169 dcp::operator== (Time const & a, Time const & b)
170 {
171  return (a.h == b.h && a.m == b.m && a.s == b.s && (a.e * b.tcr) == (b.e * a.tcr));
172 }
173 
174 
175 bool
176 dcp::operator!= (Time const & a, Time const & b)
177 {
178  return !(a == b);
179 }
180 
181 
182 bool
183 dcp::operator<= (Time const & a, Time const & b)
184 {
185  return a < b || a == b;
186 }
187 
188 
189 bool
190 dcp::operator>= (Time const & a, Time const & b)
191 {
192  return a > b || a == b;
193 }
194 
195 
196 bool
197 dcp::operator< (Time const & a, Time const & b)
198 {
199  if (a.h != b.h) {
200  return a.h < b.h;
201  }
202 
203  if (a.m != b.m) {
204  return a.m < b.m;
205  }
206 
207  if (a.s != b.s) {
208  return a.s < b.s;
209  }
210 
211  return (a.e * b.tcr) < (b.e * a.tcr);
212 }
213 
214 
215 bool
216 dcp::operator> (Time const & a, Time const & b)
217 {
218  if (a.h != b.h) {
219  return a.h > b.h;
220  }
221 
222  if (a.m != b.m) {
223  return a.m > b.m;
224  }
225 
226  if (a.s != b.s) {
227  return a.s > b.s;
228  }
229 
230  return (a.e * b.tcr) > (b.e * a.tcr);
231 }
232 
233 
234 ostream &
235 dcp::operator<< (ostream& s, Time const & t)
236 {
237  s << t.h << ":" << t.m << ":" << t.s << "." << t.e;
238  return s;
239 }
240 
241 
242 dcp::Time
243 dcp::operator+ (Time a, Time b)
244 {
245  Time r;
246 
247  /* Make sure we have a common tcr */
248  if (a.tcr != b.tcr) {
249  a.e *= b.tcr;
250  b.e *= a.tcr;
251  r.tcr = a.tcr * b.tcr;
252  } else {
253  r.tcr = a.tcr;
254  }
255 
256  r.e = a.e + b.e;
257  if (r.e >= r.tcr) {
258  r.e -= r.tcr;
259  r.s++;
260  }
261 
262  r.s += a.s + b.s;
263  if (r.s >= 60) {
264  r.s -= 60;
265  r.m++;
266  }
267 
268  r.m += a.m + b.m;
269  if (r.m >= 60) {
270  r.m -= 60;
271  r.h++;
272  }
273 
274  r.h += a.h + b.h;
275 
276  return r;
277 }
278 
279 
280 dcp::Time
281 dcp::operator- (Time a, Time b)
282 {
283  Time r;
284 
285  /* Make sure we have a common tcr */
286  if (a.tcr != b.tcr) {
287  a.e *= b.tcr;
288  b.e *= a.tcr;
289  r.tcr = a.tcr * b.tcr;
290  } else {
291  r.tcr = a.tcr;
292  }
293 
294  r.e = a.e - b.e;
295  if (r.e < 0) {
296  r.e += r.tcr;
297  r.s--;
298  }
299 
300  r.s += (a.s - b.s);
301  if (r.s < 0) {
302  r.s += 60;
303  r.m--;
304  }
305 
306  r.m += (a.m - b.m);
307  if (r.m < 0) {
308  r.m += 60;
309  r.h--;
310  }
311 
312  r.h += (a.h - b.h);
313 
314  return r;
315 }
316 
317 
318 float
319 dcp::operator/ (Time a, Time const & b)
320 {
321  int64_t const at = a.h * 3600 + a.m * 60 + a.s * float (a.e) / a.tcr;
322  int64_t const bt = b.h * 3600 + b.m * 60 + b.s * float (b.e) / b.tcr;
323  return float (at) / bt;
324 }
325 
326 
327 string
328 Time::as_string (Standard standard) const
329 {
330  char buffer[64];
331 
332  if (standard == Standard::SMPTE) {
333  snprintf (buffer, sizeof(buffer), "%02d:%02d:%02d:%02d", h, m, s, e);
334  } else {
335  snprintf (buffer, sizeof(buffer), "%02d:%02d:%02d:%03d", h, m, s, e);
336  }
337 
338  return buffer;
339 }
340 
341 
342 int64_t
343 Time::as_editable_units_floor (int tcr_) const
344 {
345  return floor(int64_t(e) * double(tcr_) / tcr) + int64_t(s) * tcr_ + int64_t(m) * 60 * tcr_ + int64_t(h) * 60 * 60 * tcr_;
346 }
347 
348 
349 int64_t
350 Time::as_editable_units_ceil (int tcr_) const
351 {
352  return ceil(int64_t(e) * double(tcr_) / tcr) + int64_t(s) * tcr_ + int64_t(m) * 60 * tcr_ + int64_t(h) * 60 * 60 * tcr_;
353 }
354 
355 
356 double
357 Time::as_seconds () const
358 {
359  return h * 3600 + m * 60 + s + double(e) / tcr;
360 }
361 
362 
363 Time
364 Time::rebase (int tcr_) const
365 {
366  long int e_ = lrintf (float (e) * tcr_ / tcr);
367  int s_ = s;
368  if (e_ >= tcr_) {
369  e_ -= tcr_;
370  ++s_;
371  }
372  int m_ = m;
373  if (s_ >= 60) {
374  s_ -= 60;
375  ++m_;
376  }
377  int h_ = h;
378  if (m_ >= 60) {
379  m_ -= 60;
380  ++h_;
381  }
382 
383  return Time (h_, m_, s_, e_, tcr_);
384 }
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
int m
minutes
Definition: dcp_time.h:118
int tcr
timecode rate: the number of editable units per second.
Definition: dcp_time.h:121
int s
seconds
Definition: dcp_time.h:119
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.