Merge pull request #18 from wruppelx/master
[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     {
110       DefaultLogSink().Error("KLV format error, BER decode failure.\n");
111       return RESULT_FAIL;
112     }
113
114   m_ValueLength = tmp_size;
115   m_KLLength = SMPTE_UL_LENGTH + Kumu::BER_length(buf + SMPTE_UL_LENGTH);
116   m_KeyStart = buf;
117   m_ValueStart = buf + m_KLLength;
118   return RESULT_OK;
119 }
120
121 //
122 bool
123 ASDCP::KLVPacket::HasUL(const byte_t* ul)
124 {
125   if ( m_KeyStart != 0 )
126     {
127       return UL(ul) == UL(m_KeyStart);
128     }
129
130   if ( m_UL.HasValue() )
131     {
132       return UL(ul) == m_UL;
133     }
134
135   return false;
136 }
137
138 //
139 ASDCP::Result_t
140 ASDCP::KLVPacket::WriteKLToBuffer(ASDCP::FrameBuffer& Buffer, const UL& label, ui32_t length)
141 {
142   assert(label.HasValue());
143
144   if ( Buffer.Size() + kl_length > Buffer.Capacity() )
145     {
146       DefaultLogSink().Error("Small write buffer\n");
147       return RESULT_FAIL;
148     }
149   
150   memcpy(Buffer.Data() + Buffer.Size(), label.Value(), label.Size());
151
152   if ( ! Kumu::write_BER(Buffer.Data() + Buffer.Size() + SMPTE_UL_LENGTH, length, MXF_BER_LENGTH) )
153     return RESULT_FAIL;
154
155   Buffer.Size(Buffer.Size() + kl_length);
156   return RESULT_OK;
157 }
158
159 //
160 void
161 ASDCP::KLVPacket::Dump(FILE* stream, const Dictionary& Dict, bool show_value)
162 {
163   char buf[64];
164
165   if ( stream == 0 )
166     stream = stderr;
167
168   if ( m_KeyStart != 0 )
169     {
170       assert(m_ValueStart);
171       UL TmpUL(m_KeyStart);
172       fprintf(stream, "%s", TmpUL.EncodeString(buf, 64));
173
174       const MDDEntry* Entry = Dict.FindULAnyVersion(m_KeyStart);
175       fprintf(stream, "  len: %7llu (%s)\n", m_ValueLength, (Entry ? Entry->name : "Unknown"));
176
177       if ( show_value && m_ValueLength < 1000 )
178         Kumu::hexdump(m_ValueStart, Kumu::xmin(m_ValueLength, (ui64_t)128), stream);
179     }
180   else if ( m_UL.HasValue() )
181     {
182       fprintf(stream, "%s\n", m_UL.EncodeString(buf, 64));
183     }
184   else
185     {
186       fprintf(stream, "*** Malformed KLV packet ***\n");
187     }
188 }
189
190 // 
191 ASDCP::Result_t
192 ASDCP::KLVFilePacket::InitFromFile(const Kumu::FileReader& Reader, const UL& label)
193 {
194   Result_t result = KLVFilePacket::InitFromFile(Reader);
195
196   if ( ASDCP_SUCCESS(result) )
197     result = ( UL(m_KeyStart) == label ) ? RESULT_OK : RESULT_FAIL;
198
199   return result;
200 }
201
202 // TODO: refactor to use InitFromBuffer
203 ASDCP::Result_t
204 ASDCP::KLVFilePacket::InitFromFile(const Kumu::FileReader& Reader)
205 {
206   ui32_t read_count;
207   byte_t tmp_data[tmp_read_size];
208   ui64_t tmp_size;
209   m_KeyStart = m_ValueStart = 0;
210   m_KLLength = m_ValueLength = 0;
211   m_Buffer.Size(0);
212
213   Result_t result = Reader.Read(tmp_data, tmp_read_size, &read_count);
214
215   if ( ASDCP_FAILURE(result) )
216     return result;
217
218   if ( read_count < (SMPTE_UL_LENGTH + 1) )
219     {
220       DefaultLogSink().Error("Short read of Key and Length got %u\n", read_count);
221       return RESULT_READFAIL;
222     }
223
224   if ( memcmp(tmp_data, SMPTE_UL_START, 4) != 0 )
225     {
226       DefaultLogSink().Error("Unexpected UL preamble: %02x.%02x.%02x.%02x\n",
227                              tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]);
228       return RESULT_FAIL;
229     }
230
231   if ( ! Kumu::read_BER(tmp_data + SMPTE_UL_LENGTH, &tmp_size) )
232     {
233       DefaultLogSink().Error("BER Length decoding error\n");
234       return RESULT_FAIL;
235     }
236
237   if ( tmp_size > MAX_KLV_PACKET_LENGTH )
238     {
239       Kumu::ui64Printer tmp_size_str(tmp_size);
240       DefaultLogSink().Error("Packet length %s exceeds internal limit\n", tmp_size_str.c_str());
241       return RESULT_FAIL;
242     }
243
244   ui32_t remainder = 0;
245   ui32_t ber_len = Kumu::BER_length(tmp_data + SMPTE_UL_LENGTH);
246   m_KLLength = SMPTE_UL_LENGTH + ber_len;
247   assert(tmp_size <= 0xFFFFFFFFL);
248   m_ValueLength = (ui32_t) tmp_size;
249   ui32_t packet_length = m_ValueLength + m_KLLength;
250
251   result = m_Buffer.Capacity(packet_length);
252
253   if ( ASDCP_FAILURE(result) )
254     return result;
255
256   m_KeyStart = m_Buffer.Data();
257   m_ValueStart = m_Buffer.Data() + m_KLLength;
258   m_Buffer.Size(packet_length);
259
260   // is the whole packet in the tmp buf?
261   if ( packet_length <= tmp_read_size )
262     {
263       assert(packet_length <= read_count);
264       memcpy(m_Buffer.Data(), tmp_data, packet_length);
265
266       if ( (remainder = read_count - packet_length) != 0 )
267         {
268           DefaultLogSink().Warn("Repositioning pointer for short packet\n");
269           Kumu::fpos_t pos = Reader.Tell();
270           assert(pos > remainder);
271           result = Reader.Seek(pos - remainder);
272         }
273     }
274   else
275     {
276       if ( read_count < tmp_read_size )
277         {
278           DefaultLogSink().Error("Short read of packet body, expecting %u, got %u\n",
279                                  m_Buffer.Size(), read_count);
280           return RESULT_READFAIL;
281         }
282
283       memcpy(m_Buffer.Data(), tmp_data, tmp_read_size);
284       remainder = m_Buffer.Size() - tmp_read_size;
285
286       if ( remainder > 0 )
287         {
288           result = Reader.Read(m_Buffer.Data() + tmp_read_size, remainder, &read_count);
289       
290           if ( read_count != remainder )
291             {
292               DefaultLogSink().Error("Short read of packet body, expecting %u, got %u\n",
293                                      remainder+tmp_read_size, read_count+tmp_read_size);
294               result = RESULT_READFAIL;
295             }
296         }
297     }
298
299   return result;
300 }
301
302 //
303 ASDCP::Result_t
304 ASDCP::KLVFilePacket::WriteKLToFile(Kumu::FileWriter& Writer, const UL& label, ui32_t length)
305 {
306   byte_t buffer[kl_length];
307   memcpy(buffer, label.Value(), label.Size());
308
309   if ( ! Kumu::write_BER(buffer+SMPTE_UL_LENGTH, length, MXF_BER_LENGTH) )
310     return RESULT_FAIL;
311
312   ui32_t write_count;
313   Writer.Write(buffer, kl_length, &write_count);
314   assert(write_count == kl_length);
315   return RESULT_OK;
316 }
317
318
319 //
320 // end KLV.cpp
321 //