libdcp
types.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 "compose.hpp"
41 #include "dcp_assert.h"
42 #include "exceptions.h"
43 #include "raw_convert.h"
44 #include "types.h"
45 #include "warnings.h"
46 LIBDCP_DISABLE_WARNINGS
47 #include <libxml++/libxml++.h>
48 LIBDCP_ENABLE_WARNINGS
49 #include <boost/algorithm/string.hpp>
50 #include <string>
51 #include <vector>
52 #include <cmath>
53 #include <cstdio>
54 #include <iomanip>
55 
56 
57 using std::string;
58 using std::ostream;
59 using std::vector;
60 using namespace dcp;
61 using namespace boost;
62 
63 
64 bool dcp::operator== (dcp::Size const & a, dcp::Size const & b)
65 {
66  return (a.width == b.width && a.height == b.height);
67 }
68 
69 
70 bool dcp::operator!= (dcp::Size const & a, dcp::Size const & b)
71 {
72  return !(a == b);
73 }
74 
75 
80 {
81  vector<string> b;
82  split (b, s, is_any_of (" "));
83  if (b.size() != 2) {
84  boost::throw_exception (XMLError("malformed fraction " + s + " in XML node"));
85  }
86  numerator = raw_convert<int> (b[0]);
87  denominator = raw_convert<int> (b[1]);
88 }
89 
90 
91 string
92 Fraction::as_string () const
93 {
94  return String::compose ("%1 %2", numerator, denominator);
95 }
96 
97 
98 bool
99 dcp::operator== (Fraction const & a, Fraction const & b)
100 {
101  return (a.numerator == b.numerator && a.denominator == b.denominator);
102 }
103 
104 
105 bool
106 dcp::operator!= (Fraction const & a, Fraction const & b)
107 {
108  return (a.numerator != b.numerator || a.denominator != b.denominator);
109 }
110 
111 
113 {
114 
115 }
116 
117 
118 Colour::Colour (int r_, int g_, int b_)
119  : r (r_)
120  , g (g_)
121  , b (b_)
122 {
123 
124 }
125 
126 
127 Colour::Colour (string argb_hex)
128 {
129  int alpha;
130  if (sscanf (argb_hex.c_str(), "%2x%2x%2x%2x", &alpha, &r, &g, &b) != 4) {
131  boost::throw_exception (XMLError ("could not parse colour string"));
132  }
133 }
134 
135 
136 string
138 {
139  char buffer[9];
140  snprintf (buffer, sizeof(buffer), "FF%02X%02X%02X", r, g, b);
141  return buffer;
142 }
143 
144 
145 string
147 {
148  char buffer[7];
149  snprintf (buffer, sizeof(buffer), "%02X%02X%02X", r, g, b);
150  return buffer;
151 }
152 
153 
154 bool
155 dcp::operator== (Colour const & a, Colour const & b)
156 {
157  return (a.r == b.r && a.g == b.g && a.b == b.b);
158 }
159 
160 
161 bool
162 dcp::operator!= (Colour const & a, Colour const & b)
163 {
164  return !(a == b);
165 }
166 
167 
168 string
169 dcp::effect_to_string (Effect e)
170 {
171  switch (e) {
172  case Effect::NONE:
173  return "none";
174  case Effect::BORDER:
175  return "border";
176  case Effect::SHADOW:
177  return "shadow";
178  }
179 
180  boost::throw_exception (MiscError("unknown effect type"));
181 }
182 
183 
184 Effect
185 dcp::string_to_effect (string s)
186 {
187  if (s == "none") {
188  return Effect::NONE;
189  } else if (s == "border") {
190  return Effect::BORDER;
191  } else if (s == "shadow") {
192  return Effect::SHADOW;
193  }
194 
195  boost::throw_exception (ReadError("unknown subtitle effect type"));
196 }
197 
198 
199 string
200 dcp::halign_to_string (HAlign h)
201 {
202  switch (h) {
203  case HAlign::LEFT:
204  return "left";
205  case HAlign::CENTER:
206  return "center";
207  case HAlign::RIGHT:
208  return "right";
209  }
210 
211  boost::throw_exception (MiscError("unknown subtitle halign type"));
212 }
213 
214 
215 HAlign
216 dcp::string_to_halign (string s)
217 {
218  if (s == "left") {
219  return HAlign::LEFT;
220  } else if (s == "center") {
221  return HAlign::CENTER;
222  } else if (s == "right") {
223  return HAlign::RIGHT;
224  }
225 
226  boost::throw_exception (ReadError("unknown subtitle halign type"));
227 }
228 
229 
230 string
231 dcp::valign_to_string (VAlign v)
232 {
233  switch (v) {
234  case VAlign::TOP:
235  return "top";
236  case VAlign::CENTER:
237  return "center";
238  case VAlign::BOTTOM:
239  return "bottom";
240  }
241 
242  boost::throw_exception (MiscError("unknown subtitle valign type"));
243 }
244 
245 
246 VAlign
247 dcp::string_to_valign (string s)
248 {
249  if (s == "top") {
250  return VAlign::TOP;
251  } else if (s == "center") {
252  return VAlign::CENTER;
253  } else if (s == "bottom") {
254  return VAlign::BOTTOM;
255  }
256 
257  boost::throw_exception (ReadError("unknown subtitle valign type"));
258 }
259 
260 
261 string
262 dcp::direction_to_string (Direction v)
263 {
264  switch (v) {
265  case Direction::LTR:
266  return "ltr";
267  case Direction::RTL:
268  return "rtl";
269  case Direction::TTB:
270  return "ttb";
271  case Direction::BTT:
272  return "btt";
273  }
274 
275  boost::throw_exception (MiscError("unknown subtitle direction type"));
276 }
277 
278 
279 Direction
280 dcp::string_to_direction (string s)
281 {
282  if (s == "ltr" || s == "horizontal") {
283  return Direction::LTR;
284  } else if (s == "rtl") {
285  return Direction::RTL;
286  } else if (s == "ttb" || s == "vertical") {
287  return Direction::TTB;
288  } else if (s == "btt") {
289  return Direction::BTT;
290  }
291 
292  boost::throw_exception (ReadError("unknown subtitle direction type"));
293 }
294 
295 
301 string
302 dcp::content_kind_to_string (ContentKind kind)
303 {
304  switch (kind) {
305  case ContentKind::FEATURE:
306  return "feature";
307  case ContentKind::SHORT:
308  return "short";
309  case ContentKind::TRAILER:
310  return "trailer";
311  case ContentKind::TEST:
312  return "test";
313  case ContentKind::TRANSITIONAL:
314  return "transitional";
315  case ContentKind::RATING:
316  return "rating";
317  case ContentKind::TEASER:
318  return "teaser";
319  case ContentKind::POLICY:
320  return "policy";
321  case ContentKind::PUBLIC_SERVICE_ANNOUNCEMENT:
322  return "psa";
323  case ContentKind::ADVERTISEMENT:
324  return "advertisement";
325  case ContentKind::EPISODE:
326  return "episode";
327  case ContentKind::PROMO:
328  return "promo";
329  }
330 
331  DCP_ASSERT (false);
332 }
333 
334 
340 dcp::ContentKind
342 {
343  transform (kind.begin(), kind.end(), kind.begin(), ::tolower);
344 
345  if (kind == "feature") {
346  return ContentKind::FEATURE;
347  } else if (kind == "short") {
348  return ContentKind::SHORT;
349  } else if (kind == "trailer") {
350  return ContentKind::TRAILER;
351  } else if (kind == "test") {
352  return ContentKind::TEST;
353  } else if (kind == "transitional") {
354  return ContentKind::TRANSITIONAL;
355  } else if (kind == "rating") {
356  return ContentKind::RATING;
357  } else if (kind == "teaser") {
358  return ContentKind::TEASER;
359  } else if (kind == "policy") {
360  return ContentKind::POLICY;
361  } else if (kind == "psa") {
362  return ContentKind::PUBLIC_SERVICE_ANNOUNCEMENT;
363  } else if (kind == "advertisement") {
364  return ContentKind::ADVERTISEMENT;
365  } else if (kind == "episode") {
366  return ContentKind::EPISODE;
367  } else if (kind == "promo") {
368  return ContentKind::PROMO;
369  }
370 
371  throw BadContentKindError (kind);
372 }
373 
374 
375 string
376 dcp::marker_to_string (dcp::Marker m)
377 {
378  switch (m) {
379  case Marker::FFOC:
380  return "FFOC";
381  case Marker::LFOC:
382  return "LFOC";
383  case Marker::FFTC:
384  return "FFTC";
385  case Marker::LFTC:
386  return "LFTC";
387  case Marker::FFOI:
388  return "FFOI";
389  case Marker::LFOI:
390  return "LFOI";
391  case Marker::FFEC:
392  return "FFEC";
393  case Marker::LFEC:
394  return "LFEC";
395  case Marker::FFMC:
396  return "FFMC";
397  case Marker::LFMC:
398  return "LFMC";
399  }
400 
401  DCP_ASSERT (false);
402 }
403 
404 
406 dcp::marker_from_string (string s)
407 {
408  if (s == "FFOC") {
409  return Marker::FFOC;
410  } else if (s == "LFOC") {
411  return Marker::LFOC;
412  } else if (s == "FFTC") {
413  return Marker::FFTC;
414  } else if (s == "LFTC") {
415  return Marker::LFTC;
416  } else if (s == "FFOI") {
417  return Marker::FFOI;
418  } else if (s == "LFOI") {
419  return Marker::LFOI;
420  } else if (s == "FFEC") {
421  return Marker::FFEC;
422  } else if (s == "LFEC") {
423  return Marker::LFEC;
424  } else if (s == "FFMC") {
425  return Marker::FFMC;
426  } else if (s == "LFMC") {
427  return Marker::LFMC;
428  }
429 
430  DCP_ASSERT (false);
431 }
432 
433 
434 ContentVersion::ContentVersion ()
435  : id ("urn:uuid:" + make_uuid())
436 {
437 
438 }
439 
440 
441 ContentVersion::ContentVersion (cxml::ConstNodePtr node)
442  : id(node->string_child("Id"))
443  , label_text(node->string_child("LabelText"))
444 {
445 
446 }
447 
448 
449 ContentVersion::ContentVersion (string label_text_)
450  : id ("urn:uuid:" + make_uuid())
451  , label_text (label_text_)
452 {
453 
454 }
455 
456 
457 void
458 ContentVersion::as_xml (xmlpp::Element* parent) const
459 {
460  auto cv = parent->add_child("ContentVersion");
461  cv->add_child("Id")->add_child_text(id);
462  cv->add_child("LabelText")->add_child_text(label_text);
463 }
464 
465 
466 Luminance::Luminance (cxml::ConstNodePtr node)
467  : _value(raw_convert<float>(node->content()))
468  , _unit(string_to_unit(node->string_attribute("units")))
469 {
470 
471 }
472 
473 
474 Luminance::Luminance (float value, Unit unit)
475  : _unit (unit)
476 {
477  set_value (value);
478 }
479 
480 
481 void
482 Luminance::set_value (float v)
483 {
484  if (v < 0) {
485  throw dcp::MiscError (String::compose("Invalid luminance value %1", v));
486  }
487 
488  _value = v;
489 }
490 
491 
492 void
493 Luminance::as_xml (xmlpp::Element* parent, string ns) const
494 {
495  auto lum = parent->add_child("Luminance", ns);
496  lum->set_attribute("units", unit_to_string(_unit));
497  lum->add_child_text(raw_convert<string>(_value, 3));
498 }
499 
500 
501 string
502 Luminance::unit_to_string (Unit u)
503 {
504  switch (u) {
505  case Unit::CANDELA_PER_SQUARE_METRE:
506  return "candela-per-square-metre";
507  case Unit::FOOT_LAMBERT:
508  return "foot-lambert";
509  default:
510  DCP_ASSERT (false);
511  }
512 
513  return {};
514 }
515 
516 
517 Luminance::Unit
518 Luminance::string_to_unit (string u)
519 {
520  if (u == "candela-per-square-metre") {
521  return Unit::CANDELA_PER_SQUARE_METRE;
522  } else if (u == "foot-lambert") {
523  return Unit::FOOT_LAMBERT;
524  }
525 
526  throw XMLError (String::compose("Invalid luminance unit %1", u));
527 }
528 
529 
530 float
531 Luminance::value_in_foot_lamberts () const
532 {
533  switch (_unit) {
534  case Unit::CANDELA_PER_SQUARE_METRE:
535  return _value / 3.426;
536  case Unit::FOOT_LAMBERT:
537  return _value;
538  default:
539  DCP_ASSERT (false);
540  }
541 }
542 
543 
544 bool
545 dcp::operator== (Luminance const& a, Luminance const& b)
546 {
547  return fabs(a.value() - b.value()) < 0.001 && a.unit() == b.unit();
548 }
549 
550 
551 MainSoundConfiguration::MainSoundConfiguration (string s)
552 {
553  vector<string> parts;
554  boost::split (parts, s, boost::is_any_of("/"));
555  if (parts.size() != 2) {
556  throw MainSoundConfigurationError (s);
557  }
558 
559  if (parts[0] == "51") {
560  _field = MCASoundField::FIVE_POINT_ONE;
561  } else if (parts[0] == "71") {
562  _field = MCASoundField::SEVEN_POINT_ONE;
563  } else {
564  throw MainSoundConfigurationError (s);
565  }
566 
567  vector<string> channels;
568  boost::split (channels, parts[1], boost::is_any_of(","));
569 
570  if (channels.size() > 16) {
571  throw MainSoundConfigurationError (s);
572  }
573 
574  for (auto i: channels) {
575  if (i == "-") {
576  _channels.push_back(optional<Channel>());
577  } else {
578  _channels.push_back(mca_id_to_channel(i));
579  }
580  }
581 }
582 
583 
584 MainSoundConfiguration::MainSoundConfiguration (MCASoundField field, int channels)
585  : _field (field)
586 {
587  _channels.resize (channels);
588 }
589 
590 
591 string
592 MainSoundConfiguration::to_string () const
593 {
594  string c;
595  if (_field == MCASoundField::FIVE_POINT_ONE) {
596  c = "51/";
597  } else {
598  c = "71/";
599  }
600 
601  for (auto i: _channels) {
602  if (!i) {
603  c += "-,";
604  } else {
605  c += channel_to_mca_id(*i, _field) + ",";
606  }
607  }
608 
609  if (c.length() > 0) {
610  c = c.substr(0, c.length() - 1);
611  }
612 
613  return c;
614 }
615 
616 
617 optional<Channel>
618 MainSoundConfiguration::mapping (int index) const
619 {
620  DCP_ASSERT (static_cast<size_t>(index) < _channels.size());
621  return _channels[index];
622 }
623 
624 
625 void
626 MainSoundConfiguration::set_mapping (int index, Channel c)
627 {
628  DCP_ASSERT (static_cast<size_t>(index) < _channels.size());
629  _channels[index] = c;
630 }
631 
632 
633 string
634 dcp::status_to_string (Status s)
635 {
636  switch (s) {
637  case Status::FINAL:
638  return "final";
639  case Status::TEMP:
640  return "temp";
641  case Status::PRE:
642  return "pre";
643  default:
644  DCP_ASSERT (false);
645  }
646 }
647 
648 
649 Status
650 dcp::string_to_status (string s)
651 {
652  if (s == "final") {
653  return Status::FINAL;
654  } else if (s == "temp") {
655  return Status::TEMP;
656  } else if (s == "pre") {
657  return Status::PRE;
658  }
659 
660  DCP_ASSERT (false);
661 }
662 
663 
664 Channel
665 dcp::mca_id_to_channel (string id)
666 {
667  if (id == "L") {
668  return Channel::LEFT;
669  } else if (id == "R") {
670  return Channel::RIGHT;
671  } else if (id == "C") {
672  return Channel::CENTRE;
673  } else if (id == "LFE") {
674  return Channel::LFE;
675  } else if (id == "Ls" || id == "Lss") {
676  return Channel::LS;
677  } else if (id == "Rs" || id == "Rss") {
678  return Channel::RS;
679  } else if (id == "HI") {
680  return Channel::HI;
681  } else if (id == "VIN") {
682  return Channel::VI;
683  } else if (id == "Lrs") {
684  return Channel::BSL;
685  } else if (id == "Rrs") {
686  return Channel::BSR;
687  } else if (id == "DBOX") {
688  return Channel::MOTION_DATA;
689  } else if (id == "FSKSync") {
690  return Channel::SYNC_SIGNAL;
691  } else if (id == "SLVS") {
692  return Channel::SIGN_LANGUAGE;
693  }
694 
695  throw UnknownChannelIdError (id);
696 }
697 
698 
699 string
700 dcp::channel_to_mca_id (Channel c, MCASoundField field)
701 {
702  switch (c) {
703  case Channel::LEFT:
704  return "L";
705  case Channel::RIGHT:
706  return "R";
707  case Channel::CENTRE:
708  return "C";
709  case Channel::LFE:
710  return "LFE";
711  case Channel::LS:
712  return field == MCASoundField::FIVE_POINT_ONE ? "Ls" : "Lss";
713  case Channel::RS:
714  return field == MCASoundField::FIVE_POINT_ONE ? "Rs" : "Rss";
715  case Channel::HI:
716  return "HI";
717  case Channel::VI:
718  return "VIN";
719  case Channel::BSL:
720  return "Lrs";
721  case Channel::BSR:
722  return "Rrs";
723  case Channel::MOTION_DATA:
724  return "DBOX";
725  case Channel::SYNC_SIGNAL:
726  return "FSKSync";
727  case Channel::SIGN_LANGUAGE:
728  return "SLVS";
729  default:
730  break;
731  }
732 
733  DCP_ASSERT (false);
734 }
735 
736 
737 string
738 dcp::channel_to_mca_name (Channel c, MCASoundField field)
739 {
740  switch (c) {
741  case Channel::LEFT:
742  return "Left";
743  case Channel::RIGHT:
744  return "Right";
745  case Channel::CENTRE:
746  return "Center";
747  case Channel::LFE:
748  return "LFE";
749  case Channel::LS:
750  return field == MCASoundField::FIVE_POINT_ONE ? "Left Surround" : "Left Side Surround";
751  case Channel::RS:
752  return field == MCASoundField::FIVE_POINT_ONE ? "Right Surround" : "Right Side Surround";
753  case Channel::HI:
754  return "Hearing Impaired";
755  case Channel::VI:
756  return "Visually Impaired-Narrative";
757  case Channel::BSL:
758  return "Left Rear Surround";
759  case Channel::BSR:
760  return "Right Rear Surround";
761  case Channel::MOTION_DATA:
762  return "D-BOX Motion Code Primary Stream";
763  case Channel::SYNC_SIGNAL:
764  return "FSK Sync";
765  case Channel::SIGN_LANGUAGE:
766  return "Sign Language Video Stream";
767  default:
768  break;
769  }
770 
771  DCP_ASSERT (false);
772 }
773 
774 
775 ASDCP::UL
776 dcp::channel_to_mca_universal_label (Channel c, MCASoundField field, ASDCP::Dictionary const* dict)
777 {
778  static byte_t sync_signal[] = {
779  0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, 0x03, 0x02, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00
780  };
781 
782  static byte_t sign_language[] = {
783  0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, 0x0d, 0x0f, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00
784  };
785 
786  switch (c) {
787  case Channel::LEFT:
788  return dict->ul(ASDCP::MDD_DCAudioChannel_L);
789  case Channel::RIGHT:
790  return dict->ul(ASDCP::MDD_DCAudioChannel_R);
791  case Channel::CENTRE:
792  return dict->ul(ASDCP::MDD_DCAudioChannel_C);
793  case Channel::LFE:
794  return dict->ul(ASDCP::MDD_DCAudioChannel_LFE);
795  case Channel::LS:
796  return dict->ul(field == MCASoundField::FIVE_POINT_ONE ? ASDCP::MDD_DCAudioChannel_Ls : ASDCP::MDD_DCAudioChannel_Lss);
797  case Channel::RS:
798  return dict->ul(field == MCASoundField::FIVE_POINT_ONE ? ASDCP::MDD_DCAudioChannel_Rs : ASDCP::MDD_DCAudioChannel_Rss);
799  case Channel::HI:
800  return dict->ul(ASDCP::MDD_DCAudioChannel_HI);
801  case Channel::VI:
802  return dict->ul(ASDCP::MDD_DCAudioChannel_VIN);
803  case Channel::BSL:
804  return dict->ul(ASDCP::MDD_DCAudioChannel_Lrs);
805  case Channel::BSR:
806  return dict->ul(ASDCP::MDD_DCAudioChannel_Rrs);
807  case Channel::MOTION_DATA:
808  return dict->ul(ASDCP::MDD_DBOXMotionCodePrimaryStream);
809  case Channel::SYNC_SIGNAL:
810  return ASDCP::UL(sync_signal);
811  case Channel::SIGN_LANGUAGE:
812  return ASDCP::UL(sign_language);
813  default:
814  break;
815  }
816 
817  DCP_ASSERT (false);
818 }
819 
820 
821 vector<dcp::Channel>
822 dcp::used_audio_channels ()
823 {
824  return {
828  Channel::LFE,
829  Channel::LS,
830  Channel::RS,
831  Channel::HI,
832  Channel::VI,
833  Channel::BSL,
834  Channel::BSR,
835  Channel::MOTION_DATA,
836  Channel::SYNC_SIGNAL,
837  Channel::SIGN_LANGUAGE
838  };
839 }
840 
An RGB colour.
Definition: types.h:301
int g
green component, from 0 to 255
Definition: types.h:318
int b
blue component, from 0 to 255
Definition: types.h:319
int r
red component, from 0 to 255
Definition: types.h:317
std::string to_argb_string() const
Definition: types.cc:137
std::string to_rgb_string() const
Definition: types.cc:146
A fraction (i.e. a thing with an integer numerator and an integer denominator).
Definition: types.h:214
A miscellaneous exception.
Definition: exceptions.h:94
Any error that occurs when reading data from a DCP.
Definition: exceptions.h:106
An XML error.
Definition: exceptions.h:164
DCP_ASSERT macro.
Exceptions thrown by libdcp.
Namespace for everything in libdcp.
Definition: array_data.h:50
Marker
Definition: types.h:358
@ FFOI
first frame of intermission
@ FFEC
first frame of end credits
@ LFMC
last frame of moving credits
@ LFOI
last frame of intermission
@ FFMC
first frame of moving credits
@ FFTC
first frame of title credits
@ LFOC
last frame of composition
@ FFOC
first frame of composition
@ LFEC
last frame of end credits
@ LFTC
last frame of title credits
Direction
Definition: types.h:191
@ LTR
left-to-right
@ BTT
bottom-to-top
@ RTL
right-to-left
@ TTB
top-to-bottom
HAlign
Definition: types.h:166
@ RIGHT
horizontal position is distance from right of screen to right of subtitle
@ LEFT
horizontal position is distance from left of screen to left of subtitle
@ CENTER
horizontal position is distance from centre of screen to centre of subtitle
std::string content_kind_to_string(ContentKind kind)
Definition: types.cc:302
VAlign
Definition: types.h:178
@ BOTTOM
vertical position is distance from bottom of screen to bottom of subtitle
@ TOP
vertical position is distance from top of screen to top of subtitle
@ CENTER
vertical position is distance from centre of screen to centre of subtitle
ContentKind content_kind_from_string(std::string kind)
Definition: types.cc:341
Channel
Definition: types.h:96
@ LFE
low-frequency effects (sub)
@ CENTRE
centre
@ RS
right surround
@ LS
left surround
P raw_convert(Q, int precision=16, bool fixed=false)
Definition: raw_convert.h:57
Status
Definition: types.h:377
@ FINAL
final version
@ TEMP
temporary version (picture/sound unfinished)
@ PRE
pre-release (picture/sound finished)
Methods for conversion to/from string.
The integer, two-dimensional size of something.
Definition: types.h:71
Miscellaneous types.