Install additional header file.
[asdcplib.git] / src / KLV.cpp
1 /*
2 Copyright (c) 2005-2006, 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 byte_t* label)
49 {
50   Result_t result = KLVPacket::InitFromBuffer(buf, buf_len);
51
52   if ( ASDCP_SUCCESS(result) )
53     result = ( memcmp(m_KeyStart, label, SMPTE_UL_LENGTH) == 0 ) ?
54       RESULT_OK : RESULT_FAIL;
55
56   return result;
57 }
58
59 //
60 ASDCP::Result_t
61 ASDCP::KLVPacket::InitFromBuffer(const byte_t* buf, ui32_t buf_len)
62 {
63   m_KeyStart = m_ValueStart = 0;
64   m_KLLength = m_ValueLength = 0;
65
66   if ( memcmp(buf, SMPTE_UL_START, 4) != 0 )
67     {
68       DefaultLogSink().Error("Unexpected UL preamble: %02x.%02x.%02x.%02x\n",
69                              buf[0], buf[1], buf[2], buf[3]);
70       return RESULT_FAIL;
71     }
72
73   ui32_t ber_len = Kumu::BER_length(buf + SMPTE_UL_LENGTH);
74
75   if ( ber_len > ( buf_len - SMPTE_UL_LENGTH ) )
76     {
77       DefaultLogSink().Error("BER encoding length exceeds buffer size\n");
78       return RESULT_FAIL;
79     }
80
81   if ( ber_len == 0 )
82     {
83       DefaultLogSink().Error("KLV format error, zero BER length not allowed\n");
84       return RESULT_FAIL;
85     }
86
87   ui64_t tmp_size;
88   if ( ! Kumu::read_BER(buf + SMPTE_UL_LENGTH, &tmp_size) )
89        return RESULT_FAIL;
90
91   assert (tmp_size <= 0xFFFFFFFFL);
92   m_ValueLength = (ui32_t) tmp_size;
93   m_KLLength = SMPTE_UL_LENGTH + Kumu::BER_length(buf + SMPTE_UL_LENGTH);
94   m_KeyStart = buf;
95   m_ValueStart = buf + m_KLLength;
96   return RESULT_OK;
97 }
98
99 //
100 bool
101 ASDCP::KLVPacket::HasUL(const byte_t* ul)
102 {
103   if ( m_KeyStart == 0 )
104     return false;
105
106   return ( memcmp(ul, m_KeyStart, SMPTE_UL_LENGTH) == 0 ) ? true : false;
107 }
108
109 //
110 ASDCP::Result_t
111 ASDCP::KLVPacket::WriteKLToBuffer(ASDCP::FrameBuffer& Buffer, const byte_t* label, ui32_t length)
112 {
113   if ( Buffer.Size() + kl_length > Buffer.Capacity() )
114     {
115       DefaultLogSink().Error("Small write buffer\n");
116       return RESULT_FAIL;
117     }
118   
119   memcpy(Buffer.Data() + Buffer.Size(), label, SMPTE_UL_LENGTH);
120
121   if ( ! Kumu::write_BER(Buffer.Data() + Buffer.Size() + SMPTE_UL_LENGTH, length, MXF_BER_LENGTH) )
122     return RESULT_FAIL;
123
124   Buffer.Size(Buffer.Size() + kl_length);
125   return RESULT_OK;
126 }
127
128 //
129 void
130 ASDCP::KLVPacket::Dump(FILE* stream, bool show_hex)
131 {
132   if ( stream == 0 )
133     stream = stderr;
134
135   if ( m_KeyStart != 0 )
136     {
137       assert(m_ValueStart);
138       UL TmpUL(m_KeyStart);
139       char buf[64];
140       fprintf(stream, "%s", TmpUL.EncodeString(buf, 64));
141
142       const MDDEntry* Entry = Dict::FindUL(m_KeyStart);
143       fprintf(stream, "  len: %7u (%s)\n", m_ValueLength, (Entry ? Entry->name : "Unknown"));
144
145       if ( show_hex && m_ValueLength < 1000 )
146         Kumu::hexdump(m_ValueStart, Kumu::xmin(m_ValueLength, (ui32_t)64), stream);
147     }
148   else
149     {
150       fprintf(stream, "*** Malformed KLV packet ***\n");
151     }
152 }
153
154 // 
155 ASDCP::Result_t
156 ASDCP::KLVFilePacket::InitFromFile(const Kumu::FileReader& Reader, const byte_t* label)
157 {
158   Result_t result = KLVFilePacket::InitFromFile(Reader);
159
160   if ( ASDCP_SUCCESS(result) )
161     result = ( memcmp(m_KeyStart, label, SMPTE_UL_LENGTH) == 0 ) ?
162       RESULT_OK : RESULT_FAIL;
163
164   return result;
165 }
166
167 // TODO: refactor to use InitFromBuffer
168 ASDCP::Result_t
169 ASDCP::KLVFilePacket::InitFromFile(const Kumu::FileReader& Reader)
170 {
171   ui32_t read_count;
172   byte_t tmp_data[tmp_read_size];
173   ui64_t tmp_size;
174   m_KeyStart = m_ValueStart = 0;
175   m_KLLength = m_ValueLength = 0;
176   m_Buffer.Size(0);
177
178   Result_t result = Reader.Read(tmp_data, tmp_read_size, &read_count);
179
180   if ( ASDCP_FAILURE(result) )
181     return result;
182
183   if ( read_count < (SMPTE_UL_LENGTH + 1) )
184     {
185       DefaultLogSink().Error("Short read of Key and Length got %u\n", read_count);
186       return RESULT_READFAIL;
187     }
188
189   if ( memcmp(tmp_data, SMPTE_UL_START, 4) != 0 )
190     {
191       DefaultLogSink().Error("Unexpected UL preamble: %02x.%02x.%02x.%02x\n",
192                              tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]);
193       return RESULT_FAIL;
194     }
195
196   if ( ! Kumu::read_BER(tmp_data + SMPTE_UL_LENGTH, &tmp_size) )
197     {
198       DefaultLogSink().Error("BER Length decoding error\n");
199       return RESULT_FAIL;
200     }
201
202   if ( tmp_size > MAX_KLV_PACKET_LENGTH )
203     {
204       Kumu::ui64Printer tmp_size_str(tmp_size);
205       DefaultLogSink().Error("Packet length %s exceeds internal limit\n", tmp_size_str.c_str());
206       return RESULT_FAIL;
207     }
208
209   ui32_t remainder = 0;
210   ui32_t ber_len = Kumu::BER_length(tmp_data + SMPTE_UL_LENGTH);
211   m_KLLength = SMPTE_UL_LENGTH + ber_len;
212   assert(tmp_size <= 0xFFFFFFFFL);
213   m_ValueLength = (ui32_t) tmp_size;
214   ui32_t packet_length = m_ValueLength + m_KLLength;
215
216   result = m_Buffer.Capacity(packet_length);
217
218   if ( ASDCP_FAILURE(result) )
219     return result;
220
221   m_KeyStart = m_Buffer.Data();
222   m_ValueStart = m_Buffer.Data() + m_KLLength;
223   m_Buffer.Size(packet_length);
224
225   // is the whole packet in the tmp buf?
226   if ( packet_length <= tmp_read_size )
227     {
228       assert(packet_length <= read_count);
229       memcpy(m_Buffer.Data(), tmp_data, packet_length);
230
231       if ( (remainder = read_count - packet_length) != 0 )
232         {
233           DefaultLogSink().Warn("Repositioning pointer for short packet\n");
234           Kumu::fpos_t pos = Reader.Tell();
235           assert(pos > remainder);
236           result = Reader.Seek(pos - remainder);
237         }
238     }
239   else
240     {
241       if ( read_count < tmp_read_size )
242         {
243           DefaultLogSink().Error("Short read of packet body, expecting %u, got %u\n",
244                                  m_Buffer.Size(), read_count);
245           return RESULT_READFAIL;
246         }
247
248       memcpy(m_Buffer.Data(), tmp_data, tmp_read_size);
249       remainder = m_Buffer.Size() - tmp_read_size;
250
251       if ( remainder > 0 )
252         {
253           result = Reader.Read(m_Buffer.Data() + tmp_read_size, remainder, &read_count);
254       
255           if ( read_count != remainder )
256             {
257               DefaultLogSink().Error("Short read of packet body, expecting %u, got %u\n",
258                                      remainder+tmp_read_size, read_count+tmp_read_size);
259               result = RESULT_READFAIL;
260             }
261         }
262     }
263
264   return result;
265 }
266
267 //
268 ASDCP::Result_t
269 ASDCP::KLVFilePacket::WriteKLToFile(Kumu::FileWriter& Writer, const byte_t* label, ui32_t length)
270 {
271   byte_t buffer[kl_length];
272   memcpy(buffer, label, SMPTE_UL_LENGTH);
273
274   if ( ! Kumu::write_BER(buffer+SMPTE_UL_LENGTH, length, MXF_BER_LENGTH) )
275     return RESULT_FAIL;
276
277   ui32_t write_count;
278   Writer.Write(buffer, kl_length, &write_count);
279   assert(write_count == kl_length);
280   return RESULT_OK;
281 }
282
283
284 //
285 // end KLV.cpp
286 //