1
+ #include " ../util/base64.hpp"
2
+ #include " vanc.h"
3
+
4
+ #include < locale>
5
+
6
+ #include < boost/lexical_cast.hpp>
7
+ #include < common/param.h>
8
+ #include < common/ptree.h>
9
+ #include < mutex>
10
+ #include < numeric>
11
+ #include < queue>
12
+ #include < vector>
13
+
14
+ namespace caspar { namespace decklink {
15
+
16
+ const uint8_t OP47_DID = 0x43 ;
17
+ const uint8_t OP47_SDID = 0x02 ;
18
+
19
+ class vanc_op47_strategy : public decklink_vanc_strategy
20
+ {
21
+ private:
22
+ static const std::wstring Name;
23
+
24
+ mutable std::mutex mutex_;
25
+ uint8_t line_number_;
26
+ uint8_t line_number_2_;
27
+ uint8_t sd_line_;
28
+ uint16_t counter_;
29
+
30
+ std::vector<uint8_t > dummy_header_;
31
+ std::queue<std::vector<uint8_t >> packets_;
32
+
33
+ public:
34
+ vanc_op47_strategy (uint8_t line_number, uint8_t line_number_2, const std::wstring& dummy_header)
35
+ : line_number_(line_number)
36
+ , line_number_2_(line_number_2)
37
+ , sd_line_(21 )
38
+ , counter_(1 )
39
+ , dummy_header_(dummy_header.empty() ? std::vector<uint8_t >() : base64_decode(dummy_header))
40
+ {
41
+ }
42
+
43
+ virtual bool has_data () const override
44
+ {
45
+ std::lock_guard<std::mutex> lock (mutex_);
46
+ return !packets_.empty () || !dummy_header_.empty ();
47
+ }
48
+ virtual vanc_packet pop_packet (bool field2) override
49
+ {
50
+ std::lock_guard<std::mutex> lock (mutex_);
51
+
52
+ if (field2 && line_number_2_ == 0 ) {
53
+ return {0 , 0 , 0 , {}};
54
+ }
55
+
56
+ if (packets_.empty ()) {
57
+ return {OP47_DID, OP47_SDID, field2 ? line_number_2_ : line_number_, sdp_encode (dummy_header_)};
58
+ }
59
+ auto packet = packets_.front ();
60
+ packets_.pop ();
61
+
62
+ return {OP47_DID, OP47_SDID, field2 ? line_number_2_ : line_number_, packet};
63
+ }
64
+
65
+ virtual bool try_push_data (const std::vector<std::wstring>& params) override
66
+ {
67
+ std::lock_guard<std::mutex> lock (mutex_);
68
+
69
+ try {
70
+ for (size_t index = 1 ; index < params.size (); ++index) {
71
+ packets_.push (sdp_encode (base64_decode (params.at (index))));
72
+ }
73
+ } catch (const boost::bad_lexical_cast& e) {
74
+ CASPAR_LOG (error) << " Failed to parse OP47 parameters: " << e.what ();
75
+ return false ;
76
+ }
77
+
78
+ return true ;
79
+ }
80
+
81
+ virtual const std::wstring& get_name () const override { return vanc_op47_strategy::Name; }
82
+
83
+ private:
84
+ std::vector<uint8_t > sdp_encode (const std::vector<uint8_t >& packet)
85
+ {
86
+ if (packet.size () != 45 ) {
87
+ throw std::runtime_error (" Invalid packet size for OP47: " + std::to_string (packet.size ()));
88
+ }
89
+
90
+ // The following is based on the specification "Free TV Australia Operational Practice OP- 47"
91
+
92
+ std::vector<uint8_t > result (103 );
93
+ result[0 ] = 0x51 ; // identifier
94
+ result[1 ] = 0x15 ; // identifier
95
+ result[2 ] = static_cast <uint8_t >(result.size ()); // size of the packet
96
+ result[3 ] = 0x02 ; // format-code
97
+
98
+ result[4 ] = sd_line_ | 0xE0 ; // VBI packet descriptor (odd field)
99
+ result[5 ] = sd_line_ | 0x60 ; // VBI packet descriptor (even field)
100
+ result[6 ] = 0 ; // VBI packet descriptor (not used)
101
+ result[7 ] = 0 ; // VBI packet descriptor (not used)
102
+ result[8 ] = 0 ; // VBI packet descriptor (not used)
103
+
104
+ memcpy (result.data () + 9 , packet.data (), packet.size ());
105
+ memcpy (result.data () + 9 + packet.size (), packet.data (), packet.size ());
106
+ result[99 ] = 0x74 ; // footer id
107
+ result[100 ] = (counter_ & 0xFF00 ) >> 8 ; // footer sequence counter
108
+ result[101 ] = counter_ & 0x00FF ; // footer sequence counter
109
+ result[102 ] = 0x0 ; // SPD checksum, will be set when calculated
110
+
111
+ auto sum = accumulate (result.begin (), result.end (), (uint8_t )0 );
112
+ result[102 ] = ~sum + 1 ;
113
+
114
+ counter_++; // this is rolling over at 65535 by design
115
+
116
+ return result;
117
+ }
118
+
119
+ std::vector<uint8_t > base64_decode (const std::wstring& encoded)
120
+ {
121
+ std::vector<char > buffer (encoded.size ());
122
+ std::use_facet<std::ctype<wchar_t >>(std::locale ())
123
+ .narrow (encoded.data (), encoded.data () + encoded.size (), ' ?' , buffer.data ());
124
+ auto str = std::string (buffer.data (), buffer.size ());
125
+ return base64::decode_into<std::vector<uint8_t >>(str);
126
+ }
127
+ };
128
+
129
+ const std::wstring vanc_op47_strategy::Name = L" OP47" ;
130
+
131
+ std::shared_ptr<decklink_vanc_strategy>
132
+ create_op47_strategy (uint8_t line_number, uint8_t line_number_2, const std::wstring& dummy_header)
133
+ {
134
+ return std::make_shared<vanc_op47_strategy>(line_number, line_number_2, dummy_header);
135
+ }
136
+
137
+ }} // namespace caspar::decklink
0 commit comments