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