Move all ostream operator<< into some test-only stream_operators.{cc,h} files.
[libdcp.git] / src / types.cc
1 /*
2     Copyright (C) 2012-2019 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 #include "raw_convert.h"
35 #include "types.h"
36 #include "exceptions.h"
37 #include "compose.hpp"
38 #include "dcp_assert.h"
39 #include <libxml++/libxml++.h>
40 #include <boost/algorithm/string.hpp>
41 #include <boost/foreach.hpp>
42 #include <string>
43 #include <vector>
44 #include <cmath>
45 #include <cstdio>
46 #include <iomanip>
47
48 using std::string;
49 using std::ostream;
50 using std::vector;
51 using namespace dcp;
52 using namespace boost;
53
54 bool dcp::operator== (dcp::Size const & a, dcp::Size const & b)
55 {
56         return (a.width == b.width && a.height == b.height);
57 }
58
59 bool dcp::operator!= (dcp::Size const & a, dcp::Size const & b)
60 {
61         return !(a == b);
62 }
63
64
65 /** Construct a Fraction from a string of the form <numerator> <denominator>
66  *  e.g. "1 3".
67  */
68 Fraction::Fraction (string s)
69 {
70         vector<string> b;
71         split (b, s, is_any_of (" "));
72         if (b.size() != 2) {
73                 boost::throw_exception (XMLError ("malformed fraction " + s + " in XML node"));
74         }
75         numerator = raw_convert<int> (b[0]);
76         denominator = raw_convert<int> (b[1]);
77 }
78
79 string
80 Fraction::as_string () const
81 {
82         return String::compose ("%1 %2", numerator, denominator);
83 }
84
85 bool
86 dcp::operator== (Fraction const & a, Fraction const & b)
87 {
88         return (a.numerator == b.numerator && a.denominator == b.denominator);
89 }
90
91 bool
92 dcp::operator!= (Fraction const & a, Fraction const & b)
93 {
94         return (a.numerator != b.numerator || a.denominator != b.denominator);
95 }
96
97
98 /** Construct a Colour, initialising it to black. */
99 Colour::Colour ()
100         : r (0)
101         , g (0)
102         , b (0)
103 {
104
105 }
106
107 /** Construct a Colour from R, G and B.  The values run between
108  *  0 and 255.
109  */
110 Colour::Colour (int r_, int g_, int b_)
111         : r (r_)
112         , g (g_)
113         , b (b_)
114 {
115
116 }
117
118 /** Construct a Colour from an ARGB hex string; the alpha value is ignored.
119  *  @param argb_hex A string of the form AARRGGBB, where e.g. RR is a two-character
120  *  hex value.
121  */
122 Colour::Colour (string argb_hex)
123 {
124         int alpha;
125         if (sscanf (argb_hex.c_str(), "%2x%2x%2x%2x", &alpha, &r, &g, &b) != 4) {
126                 boost::throw_exception (XMLError ("could not parse colour string"));
127         }
128 }
129
130 /** @return An ARGB string of the form AARRGGBB, where e.g. RR is a two-character
131  *  hex value.  The alpha value will always be FF (ie 255; maximum alpha).
132  */
133 string
134 Colour::to_argb_string () const
135 {
136         char buffer[9];
137         snprintf (buffer, sizeof(buffer), "FF%02X%02X%02X", r, g, b);
138         return buffer;
139 }
140
141 /** @return An RGB string of the form RRGGBB, where e.g. RR is a two-character
142  *  hex value.
143  */
144 string
145 Colour::to_rgb_string () const
146 {
147         char buffer[7];
148         snprintf (buffer, sizeof(buffer), "%02X%02X%02X", r, g, b);
149         return buffer;
150 }
151
152 /** operator== for Colours.
153  *  @param a First colour to compare.
154  *  @param b Second colour to compare.
155  */
156 bool
157 dcp::operator== (Colour const & a, Colour const & b)
158 {
159         return (a.r == b.r && a.g == b.g && a.b == b.b);
160 }
161
162 /** operator!= for Colours.
163  *  @param a First colour to compare.
164  *  @param b Second colour to compare.
165  */
166 bool
167 dcp::operator!= (Colour const & a, Colour const & b)
168 {
169         return !(a == b);
170 }
171
172
173 string
174 dcp::effect_to_string (Effect e)
175 {
176         switch (e) {
177         case Effect::NONE:
178                 return "none";
179         case Effect::BORDER:
180                 return "border";
181         case Effect::SHADOW:
182                 return "shadow";
183         }
184
185         boost::throw_exception (MiscError ("unknown effect type"));
186 }
187
188 Effect
189 dcp::string_to_effect (string s)
190 {
191         if (s == "none") {
192                 return Effect::NONE;
193         } else if (s == "border") {
194                 return Effect::BORDER;
195         } else if (s == "shadow") {
196                 return Effect::SHADOW;
197         }
198
199         boost::throw_exception (ReadError ("unknown subtitle effect type"));
200 }
201
202
203 string
204 dcp::halign_to_string (HAlign h)
205 {
206         switch (h) {
207         case HAlign::LEFT:
208                 return "left";
209         case HAlign::CENTER:
210                 return "center";
211         case HAlign::RIGHT:
212                 return "right";
213         }
214
215         boost::throw_exception (MiscError ("unknown subtitle halign type"));
216 }
217
218 HAlign
219 dcp::string_to_halign (string s)
220 {
221         if (s == "left") {
222                 return HAlign::LEFT;
223         } else if (s == "center") {
224                 return HAlign::CENTER;
225         } else if (s == "right") {
226                 return HAlign::RIGHT;
227         }
228
229         boost::throw_exception (ReadError ("unknown subtitle halign type"));
230 }
231
232 string
233 dcp::valign_to_string (VAlign v)
234 {
235         switch (v) {
236         case VAlign::TOP:
237                 return "top";
238         case VAlign::CENTER:
239                 return "center";
240         case VAlign::BOTTOM:
241                 return "bottom";
242         }
243
244         boost::throw_exception (MiscError ("unknown subtitle valign type"));
245 }
246
247 VAlign
248 dcp::string_to_valign (string s)
249 {
250         if (s == "top") {
251                 return VAlign::TOP;
252         } else if (s == "center") {
253                 return VAlign::CENTER;
254         } else if (s == "bottom") {
255                 return VAlign::BOTTOM;
256         }
257
258         boost::throw_exception (ReadError ("unknown subtitle valign type"));
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 Direction
279 dcp::string_to_direction (string s)
280 {
281         if (s == "ltr" || s == "horizontal") {
282                 return Direction::LTR;
283         } else if (s == "rtl") {
284                 return Direction::RTL;
285         } else if (s == "ttb" || s == "vertical") {
286                 return Direction::TTB;
287         } else if (s == "btt") {
288                 return Direction::BTT;
289         }
290
291         boost::throw_exception (ReadError ("unknown subtitle direction type"));
292 }
293
294 /** Convert a content kind to a string which can be used in a
295  *  &lt;ContentKind&gt; node.
296  *  @param kind ContentKind.
297  *  @return string.
298  */
299 string
300 dcp::content_kind_to_string (ContentKind kind)
301 {
302         switch (kind) {
303         case ContentKind::FEATURE:
304                 return "feature";
305         case ContentKind::SHORT:
306                 return "short";
307         case ContentKind::TRAILER:
308                 return "trailer";
309         case ContentKind::TEST:
310                 return "test";
311         case ContentKind::TRANSITIONAL:
312                 return "transitional";
313         case ContentKind::RATING:
314                 return "rating";
315         case ContentKind::TEASER:
316                 return "teaser";
317         case ContentKind::POLICY:
318                 return "policy";
319         case ContentKind::PUBLIC_SERVICE_ANNOUNCEMENT:
320                 return "psa";
321         case ContentKind::ADVERTISEMENT:
322                 return "advertisement";
323         case ContentKind::EPISODE:
324                 return "episode";
325         case ContentKind::PROMO:
326                 return "promo";
327         }
328
329         DCP_ASSERT (false);
330 }
331
332 /** Convert a string from a &lt;ContentKind&gt; node to a libdcp ContentKind.
333  *  Reasonably tolerant about varying case.
334  *  @param kind Content kind string.
335  *  @return libdcp ContentKind.
336  */
337 dcp::ContentKind
338 dcp::content_kind_from_string (string kind)
339 {
340         transform (kind.begin(), kind.end(), kind.begin(), ::tolower);
341
342         if (kind == "feature") {
343                 return ContentKind::FEATURE;
344         } else if (kind == "short") {
345                 return ContentKind::SHORT;
346         } else if (kind == "trailer") {
347                 return ContentKind::TRAILER;
348         } else if (kind == "test") {
349                 return ContentKind::TEST;
350         } else if (kind == "transitional") {
351                 return ContentKind::TRANSITIONAL;
352         } else if (kind == "rating") {
353                 return ContentKind::RATING;
354         } else if (kind == "teaser") {
355                 return ContentKind::TEASER;
356         } else if (kind == "policy") {
357                 return ContentKind::POLICY;
358         } else if (kind == "psa") {
359                 return ContentKind::PUBLIC_SERVICE_ANNOUNCEMENT;
360         } else if (kind == "advertisement") {
361                 return ContentKind::ADVERTISEMENT;
362         } else if (kind == "episode") {
363                 return ContentKind::EPISODE;
364         } else if (kind == "promo") {
365                 return ContentKind::PROMO;
366         }
367
368         throw BadContentKindError (kind);
369 }
370
371
372 string
373 dcp::marker_to_string (dcp::Marker m)
374 {
375         switch (m) {
376         case Marker::FFOC:
377                 return "FFOC";
378         case Marker::LFOC:
379                 return "LFOC";
380         case Marker::FFTC:
381                 return "FFTC";
382         case Marker::LFTC:
383                 return "LFTC";
384         case Marker::FFOI:
385                 return "FFOI";
386         case Marker::LFOI:
387                 return "LFOI";
388         case Marker::FFEC:
389                 return "FFEC";
390         case Marker::LFEC:
391                 return "LFEC";
392         case Marker::FFMC:
393                 return "FFMC";
394         case Marker::LFMC:
395                 return "LFMC";
396         }
397
398         DCP_ASSERT (false);
399 }
400
401 dcp::Marker
402 dcp::marker_from_string (string s)
403 {
404         if (s == "FFOC") {
405                 return Marker::FFOC;
406         } else if (s == "LFOC") {
407                 return Marker::LFOC;
408         } else if (s == "FFTC") {
409                 return Marker::FFTC;
410         } else if (s == "LFTC") {
411                 return Marker::LFTC;
412         } else if (s == "FFOI") {
413                 return Marker::FFOI;
414         } else if (s == "LFOI") {
415                 return Marker::LFOI;
416         } else if (s == "FFEC") {
417                 return Marker::FFEC;
418         } else if (s == "LFEC") {
419                 return Marker::LFEC;
420         } else if (s == "FFMC") {
421                 return Marker::FFMC;
422         } else if (s == "LFMC") {
423                 return Marker::LFMC;
424         }
425
426         DCP_ASSERT (false);
427 }
428
429 Rating::Rating (cxml::ConstNodePtr node)
430 {
431         agency = node->string_child("Agency");
432         label = node->string_child("Label");
433         node->done ();
434 }
435
436 void
437 Rating::as_xml (xmlpp::Element* parent) const
438 {
439         parent->add_child("Agency")->add_child_text(agency);
440         parent->add_child("Label")->add_child_text(label);
441 }
442
443 bool
444 dcp::operator== (Rating const & a, Rating const & b)
445 {
446         return a.agency == b.agency && a.label == b.label;
447 }
448
449 ContentVersion::ContentVersion ()
450         : id ("urn:uuid:" + make_uuid())
451 {
452
453 }
454
455
456 ContentVersion::ContentVersion (cxml::ConstNodePtr node)
457 {
458         id = node->string_child("Id");
459         label_text = node->string_child("LabelText");
460 }
461
462
463 ContentVersion::ContentVersion (string label_text_)
464         : id ("urn:uuid:" + make_uuid())
465         , label_text (label_text_)
466 {
467
468 }
469
470
471 void
472 ContentVersion::as_xml (xmlpp::Element* parent) const
473 {
474         xmlpp::Node* cv = parent->add_child("ContentVersion");
475         cv->add_child("Id")->add_child_text(id);
476         cv->add_child("LabelText")->add_child_text(label_text);
477 }
478
479
480 Luminance::Luminance (cxml::ConstNodePtr node)
481 {
482         _unit = string_to_unit (node->string_attribute("units"));
483         _value = raw_convert<float> (node->content());
484 }
485
486
487 Luminance::Luminance (float value, Unit unit)
488         : _unit (unit)
489 {
490         set_value (value);
491 }
492
493
494 void
495 Luminance::set_value (float v)
496 {
497         if (v < 0) {
498                 throw dcp::MiscError (String::compose("Invalid luminance value %1", v));
499         }
500
501         _value = v;
502 }
503
504
505 void
506 Luminance::as_xml (xmlpp::Element* parent, string ns) const
507 {
508         xmlpp::Element* lum = parent->add_child("Luminance", ns);
509         lum->set_attribute("units", unit_to_string(_unit));
510         lum->add_child_text(raw_convert<string>(_value, 3));
511 }
512
513
514 string
515 Luminance::unit_to_string (Unit u)
516 {
517         switch (u) {
518         case Unit::CANDELA_PER_SQUARE_METRE:
519                 return "candela-per-square-metre";
520         case Unit::FOOT_LAMBERT:
521                 return "foot-lambert";
522         default:
523                 DCP_ASSERT (false);
524         }
525
526         return "";
527 }
528
529
530 Luminance::Unit
531 Luminance::string_to_unit (string u)
532 {
533         if (u == "candela-per-square-metre") {
534                 return Unit::CANDELA_PER_SQUARE_METRE;
535         } else if (u == "foot-lambert") {
536                 return Unit::FOOT_LAMBERT;
537         }
538
539         throw XMLError (String::compose("Invalid luminance unit %1", u));
540 }
541
542
543 bool
544 dcp::operator== (Luminance const& a, Luminance const& b)
545 {
546         return fabs(a.value() - b.value()) < 0.001 && a.unit() == b.unit();
547 }
548
549
550 MainSoundConfiguration::MainSoundConfiguration (string s)
551 {
552         vector<string> parts;
553         boost::split (parts, s, boost::is_any_of("/"));
554         if (parts.size() != 2) {
555                 throw MainSoundConfigurationError (s);
556         }
557
558         if (parts[0] == "51") {
559                 _field = MCASoundField::FIVE_POINT_ONE;
560         } else if (parts[0] == "71") {
561                 _field = MCASoundField::SEVEN_POINT_ONE;
562         } else {
563                 throw MainSoundConfigurationError (s);
564         }
565
566         vector<string> channels;
567         boost::split (channels, parts[1], boost::is_any_of(","));
568
569         if (channels.size() > 16) {
570                 throw MainSoundConfigurationError (s);
571         }
572
573         BOOST_FOREACH (string i, channels) {
574                 if (i == "-") {
575                         _channels.push_back(optional<Channel>());
576                 } else {
577                         _channels.push_back(mca_id_to_channel(i));
578                 }
579         }
580 }
581
582
583 MainSoundConfiguration::MainSoundConfiguration (MCASoundField field, int channels)
584         : _field (field)
585 {
586         _channels.resize (channels);
587 }
588
589
590 string
591 MainSoundConfiguration::to_string () const
592 {
593         string c;
594         if (_field == MCASoundField::FIVE_POINT_ONE) {
595                 c = "51/";
596         } else {
597                 c = "71/";
598         }
599
600         for (auto i: _channels) {
601                 if (!i) {
602                         c += "-,";
603                 } else {
604                         c += channel_to_mca_id(*i, _field) + ",";
605                 }
606         }
607
608         if (c.length() > 0) {
609                 c = c.substr(0, c.length() - 1);
610         }
611
612         return c;
613 }
614
615
616 optional<Channel>
617 MainSoundConfiguration::mapping (int index) const
618 {
619         DCP_ASSERT (static_cast<size_t>(index) < _channels.size());
620         return _channels[index];
621 }
622
623
624 void
625 MainSoundConfiguration::set_mapping (int index, Channel c)
626 {
627         DCP_ASSERT (static_cast<size_t>(index) < _channels.size());
628         _channels[index] = c;
629 }
630
631
632 string
633 dcp::status_to_string (Status s)
634 {
635         switch (s) {
636         case Status::FINAL:
637                 return "final";
638         case Status::TEMP:
639                 return "temp";
640         case Status::PRE:
641                 return "pre";
642         default:
643                 DCP_ASSERT (false);
644         }
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         vector<Channel> c;
825         c.push_back (Channel::LEFT);
826         c.push_back (Channel::RIGHT);
827         c.push_back (Channel::CENTRE);
828         c.push_back (Channel::LFE);
829         c.push_back (Channel::LS);
830         c.push_back (Channel::RS);
831         c.push_back (Channel::HI);
832         c.push_back (Channel::VI);
833         c.push_back (Channel::BSL);
834         c.push_back (Channel::BSR);
835         c.push_back (Channel::MOTION_DATA);
836         c.push_back (Channel::SYNC_SIGNAL);
837         c.push_back (Channel::SIGN_LANGUAGE);
838         return c;
839 }
840