o Added preliminary support for timed-text wrapping for AS-02. This
[asdcplib.git] / src / KLV.cpp
1 /*
2 Copyright (c) 2005-2009, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14    derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 /*! \file    KLV.cpp
28   \version $Id$
29   \brief   KLV objects
30 */
31
32 #include "KLV.h"
33 #include <KM_log.h>
34 using Kumu::DefaultLogSink;
35
36
37 // This is how much we read when we're reading from a file and we don't know
38 // how long the packet is. This gives us the K (16 bytes) and L (4-9 bytes)
39 // and the remaining bytes for an even read (tmp_read_size % 16 == 0)
40 const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH;
41 const ui32_t tmp_read_size = 32;
42
43 //------------------------------------------------------------------------------------------
44 //
45
46 // 
47 ASDCP::Result_t
48 ASDCP::KLVPacket::InitFromBuffer(const byte_t* buf, ui32_t buf_len, const UL& label)
49 {
50   Result_t result = KLVPacket::InitFromBuffer(buf, buf_len);
51
52   if ( ASDCP_SUCCESS(result) )
53     result = ( UL(m_KeyStart) == label ) ? RESULT_OK : RESULT_FAIL;
54
55   return result;
56 }
57
58 //
59 ASDCP::UL
60 ASDCP::KLVPacket::GetUL()
61 {
62   if ( m_KeyStart != 0 )
63     return UL(m_KeyStart);
64
65   return m_UL;
66 }
67
68 //
69 bool
70 ASDCP::KLVPacket::SetUL(const UL& new_ul)
71 {
72   if ( m_KeyStart != 0 )
73     return false;
74
75   m_UL = new_ul;
76   return true;
77 }
78
79 //
80 ASDCP::Result_t
81 ASDCP::KLVPacket::InitFromBuffer(const byte_t* buf, ui32_t buf_len)
82 {
83   m_KeyStart = m_ValueStart = 0;
84   m_KLLength = m_ValueLength = 0;
85
86   if ( memcmp(buf, SMPTE_UL_START, 4) != 0 )
87     {
88       DefaultLogSink().Error("Unexpected UL preamble: %02x.%02x.%02x.%02x\n",
89                              buf[0], buf[1], buf[2], buf[3]);
90       return RESULT_FAIL;
91     }
92
93   ui32_t ber_len = Kumu::BER_length(buf + SMPTE_UL_LENGTH);
94
95   if ( ber_len > ( buf_len - SMPTE_UL_LENGTH ) )
96     {
97       DefaultLogSink().Error("BER encoding length exceeds buffer size\n");
98       return RESULT_FAIL;
99     }
100
101   if ( ber_len == 0 )
102     {
103       DefaultLogSink().Error("KLV format error, zero BER length not allowed\n");
104       return RESULT_FAIL;
105     }
106
107   ui64_t tmp_size;
108   if ( ! Kumu::read_BER(buf + SMPTE_UL_LENGTH, &tmp_size) )
109        return RESULT_FAIL;
110
111   assert (tmp_size <= 0xFFFFFFFFL);
112   m_ValueLength = (ui32_t) tmp_size;
113   m_KLLength = SMPTE_UL_LENGTH + Kumu::BER_length(buf + SMPTE_UL_LENGTH);
114   m_KeyStart = buf;
115   m_ValueStart = buf + m_KLLength;
116   return RESULT_OK;
117 }
118
119 //
120 bool
121 ASDCP::KLVPacket::HasUL(const byte_t* ul)
122 {
123   if ( m_KeyStart != 0 )
124     {
125       return ( memcmp(ul, m_KeyStart, SMPTE_UL_LENGTH) == 0 ) ? true : false;
126     }
127
128   if ( m_UL.HasValue() )
129     {
130       return UL(ul) == m_UL;
131     }
132
133   return false;
134 }
135
136 //
137 ASDCP::Result_t
138 ASDCP::KLVPacket::WriteKLToBuffer(ASDCP::FrameBuffer& Buffer, const UL& label, ui32_t length)
139 {
140   assert(label.HasValue());
141
142   if ( Buffer.Size() + kl_length > Buffer.Capacity() )
143     {
144       DefaultLogSink().Error("Small write buffer\n");
145       return RESULT_FAIL;
146     }
147   
148   memcpy(Buffer.Data() + Buffer.Size(), label.Value(), label.Size());
149
150   if ( ! Kumu::write_BER(Buffer.Data() + Buffer.Size() + SMPTE_UL_LENGTH, length, MXF_BER_LENGTH) )
151     return RESULT_FAIL;
152
153   Buffer.Size(Buffer.Size() + kl_length);
154   return RESULT_OK;
155 }
156
157 //
158 void
159 ASDCP::KLVPacket::Dump(FILE* stream, const Dictionary& Dict, bool show_value)
160 {
161   char buf[64];
162
163   if ( stream == 0 )
164     stream = stderr;
165
166   if ( m_KeyStart != 0 )
167     {
168       assert(m_ValueStart);
169       UL TmpUL(m_KeyStart);
170       fprintf(stream, "%s", TmpUL.EncodeString(buf, 64));
171
172       const MDDEntry* Entry = Dict.FindUL(m_KeyStart);
173       fprintf(stream, "  len: %7u (%s)\n", m_ValueLength, (Entry ? Entry->name : "Unknown"));
174
175       if ( show_value && m_ValueLength < 1000 )
176         Kumu::hexdump(m_ValueStart, Kumu::xmin(m_ValueLength, (ui32_t)128), stream);
177     }
178   else if ( m_UL.HasValue() )
179     {
180       fprintf(stream, "%s\n", m_UL.EncodeString(buf, 64));
181     }
182   else
183     {
184       fprintf(stream, "*** Malformed KLV packet ***\n");
185     }
186 }
187
188 // 
189 ASDCP::Result_t
190 ASDCP::KLVFilePacket::InitFromFile(const Kumu::FileReader& Reader, const UL& label)
191 {
192   Result_t result = KLVFilePacket::InitFromFile(Reader);
193
194   if ( ASDCP_SUCCESS(result) )
195     result = ( UL(m_KeyStart) == label ) ? RESULT_OK : RESULT_FAIL;
196
197   return result;
198 }
199
200 // TODO: refactor to use InitFromBuffer
201 ASDCP::Result_t
202 ASDCP::KLVFilePacket::InitFromFile(const Kumu::FileReader& Reader)
203 {
204   ui32_t read_count;
205   byte_t tmp_data[tmp_read_size];
206   ui64_t tmp_size;
207   m_KeyStart = m_ValueStart = 0;
208   m_KLLength = m_ValueLength = 0;
209   m_Buffer.Size(0);
210
211   Result_t result = Reader.Read(tmp_data, tmp_read_size, &read_count);
212
213   if ( ASDCP_FAILURE(result) )
214     return result;
215
216   if ( read_count < (SMPTE_UL_LENGTH + 1) )
217     {
218       DefaultLogSink().Error("Short read of Key and Length got %u\n", read_count);
219       return RESULT_READFAIL;
220     }
221
222   if ( memcmp(tmp_data, SMPTE_UL_START, 4) != 0 )
223     {
224       DefaultLogSink().Error("Unexpected UL preamble: %02x.%02x.%02x.%02x\n",
225                              tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]);
226       return RESULT_FAIL;
227     }
228
229   if ( ! Kumu::read_BER(tmp_data + SMPTE_UL_LENGTH, &tmp_size) )
230     {
231       DefaultLogSink().Error("BER Length decoding error\n");
232       return RESULT_FAIL;
233     }
234
235   if ( tmp_size > MAX_KLV_PACKET_LENGTH )
236     {
237       Kumu::ui64Printer tmp_size_str(tmp_size);
238       DefaultLogSink().Error("Packet length %s exceeds internal limit\n", tmp_size_str.c_str());
239       return RESULT_FAIL;
240     }
241
242   ui32_t remainder = 0;
243   ui32_t ber_len = Kumu::BER_length(tmp_data + SMPTE_UL_LENGTH);
244   m_KLLength = SMPTE_UL_LENGTH + ber_len;
245   assert(tmp_size <= 0xFFFFFFFFL);
246   m_ValueLength = (ui32_t) tmp_size;
247   ui32_t packet_length = m_ValueLength + m_KLLength;
248
249   result = m_Buffer.Capacity(packet_length);
250
251   if ( ASDCP_FAILURE(result) )
252     return result;
253
254   m_KeyStart = m_Buffer.Data();
255   m_ValueStart = m_Buffer.Data() + m_KLLength;
256   m_Buffer.Size(packet_length);
257
258   // is the whole packet in the tmp buf?
259   if ( packet_length <= tmp_read_size )
260     {
261       assert(packet_length <= read_count);
262       memcpy(m_Buffer.Data(), tmp_data, packet_length);
263
264       if ( (remainder = read_count - packet_length) != 0 )
265         {
266           DefaultLogSink().Warn("Repositioning pointer for short packet\n");
267           Kumu::fpos_t pos = Reader.Tell();
268           assert(pos > remainder);
269           result = Reader.Seek(pos - remainder);
270         }
271     }
272   else
273     {
274       if ( read_count < tmp_read_size )
275         {
276           DefaultLogSink().Error("Short read of packet body, expecting %u, got %u\n",
277                                  m_Buffer.Size(), read_count);
278           return RESULT_READFAIL;
279         }
280
281       memcpy(m_Buffer.Data(), tmp_data, tmp_read_size);
282       remainder = m_Buffer.Size() - tmp_read_size;
283
284       if ( remainder > 0 )
285         {
286           result = Reader.Read(m_Buffer.Data() + tmp_read_size, remainder, &read_count);
287       
288           if ( read_count != remainder )
289             {
290               DefaultLogSink().Error("Short read of packet body, expecting %u, got %u\n",
291                                      remainder+tmp_read_size, read_count+tmp_read_size);
292               result = RESULT_READFAIL;
293             }
294         }
295     }
296
297   return result;
298 }
299
300 //
301 ASDCP::Result_t
302 ASDCP::KLVFilePacket::WriteKLToFile(Kumu::FileWriter& Writer, const UL& label, ui32_t length)
303 {
304   byte_t buffer[kl_length];
305   memcpy(buffer, label.Value(), label.Size());
306
307   if ( ! Kumu::write_BER(buffer+SMPTE_UL_LENGTH, length, MXF_BER_LENGTH) )
308     return RESULT_FAIL;
309
310   ui32_t write_count;
311   Writer.Write(buffer, kl_length, &write_count);
312   assert(write_count == kl_length);
313   return RESULT_OK;
314 }
315
316
317 //
318 // end KLV.cpp
319 //