ReadFileIntoString() modified to return OK when the file is empty
[asdcplib.git] / src / AS_02_ACES.cpp
1 /*
2 Copyright (c) 2018, Bjoern Stresing, Patrick Bichiou, Wolfgang Ruppel,
3 John Hurst
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10 1. Redistributions of source code must retain the above copyright
11   notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13   notice, this list of conditions and the following disclaimer in the
14   documentation and/or other materials provided with the distribution.
15 3. The name of the author may not be used to endorse or promote products
16   derived from this software without specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30
31 #include "AS_02_ACES.h"
32 //#include "info.h"
33 #include "AS_02_internal.h"
34 #include <limits>
35 #include <iostream>
36 #include <openssl/sha.h>
37
38 #ifdef min
39 #undef min
40 #undef max
41 #endif
42
43 using namespace Kumu;
44
45 const char*
46 AS_02::ACES::MIME2str(AS_02::ACES::MIMEType_t m)
47 {
48   if(m == AS_02::ACES::MT_PNG) return "image/png";
49   else if(m == AS_02::ACES::MT_TIFF) return "image/tiff";
50   return "application/octet-stream";
51 }
52
53 using Kumu::GenRandomValue;
54
55 //------------------------------------------------------------------------------------------
56 static std::string ACES_PACKAGE_LABEL = "File Package: frame wrapping of ACES codestreams";
57 static std::string PICT_DEF_LABEL = "Image Track";
58 //------------------------------------------------------------------------------------------
59
60
61 typedef std::map<Kumu::UUID, Kumu::UUID> ResourceMap_t;
62
63 ASDCP::Rational AS_02::ConvertToRational(double in)
64 {
65
66   //rational approximation to given real number
67   //David Eppstein / UC Irvine / 8 Aug 1993
68   i32_t m[2][2];
69   double x, startx;
70   i32_t maxden = std::numeric_limits<i32_t>::max();
71   i32_t ai;
72
73   startx = x = in;
74
75   /* initialize matrix */
76   m[0][0] = m[1][1] = 1;
77   m[0][1] = m[1][0] = 0;
78
79   /* loop finding terms until denom gets too big */
80   while(m[1][0] * (ai = (long)x) + m[1][1] <= maxden)
81   {
82     i32_t t;
83     t = m[0][0] * ai + m[0][1];
84     m[0][1] = m[0][0];
85     m[0][0] = t;
86     t = m[1][0] * ai + m[1][1];
87     m[1][1] = m[1][0];
88     m[1][0] = t;
89     if(x == (double)ai) break;     // AF: division by zero
90     x = 1 / (x - (double)ai);
91     if(x > (double)0x7FFFFFFF) break;  // AF: representation failure
92   }
93
94   /* now remaining x is between 0 and 1/ai */
95   /* approx as either 0 or 1/m where m is max that will fit in maxden */
96   /* first try zero */
97   //printf("%ld/%ld, error = %e\n", m[0][0], m[1][0], startx - ((double)m[0][0] / (double)m[1][0]));
98
99   return(ASDCP::Rational(m[0][0], m[1][0]));
100   /* now try other possibility */
101   //   ai = (maxden - m[1][1]) / m[1][0];
102   //   m[0][0] = m[0][0] * ai + m[0][1];
103   //   m[1][0] = m[1][0] * ai + m[1][1];
104   //   printf("%ld/%ld, error = %e\n", m[0][0], m[1][0], startx - ((double)m[0][0] / (double)m[1][0]));
105 }
106
107 std::ostream& AS_02::ACES::operator<<(std::ostream& strm, const PictureDescriptor& PDesc)
108 {
109
110   strm << "          EditRate: " << PDesc.EditRate.Numerator << "/" << PDesc.EditRate.Denominator << std::endl;
111   strm << "        SampleRate: " << PDesc.SampleRate.Numerator << "/" << PDesc.SampleRate.Denominator << std::endl;
112   strm << "    Chromaticities: " << std::endl;
113   strm << "               x_red: " << PDesc.Chromaticities.red.x << " y_red: " << PDesc.Chromaticities.red.y << std::endl;
114   strm << "             x_green: " << PDesc.Chromaticities.green.x << " y_green: " << PDesc.Chromaticities.green.y << std::endl;
115   strm << "              x_blue: " << PDesc.Chromaticities.blue.x << " y_blue: " << PDesc.Chromaticities.blue.y << std::endl;
116   strm << "             x_white: " << PDesc.Chromaticities.white.x << " y_white: " << PDesc.Chromaticities.white.y << std::endl;
117   strm << "       Compression: " << (unsigned)PDesc.Compression << std::endl;
118   strm << "         LineOrder: " << (unsigned)PDesc.LineOrder << std::endl;
119   strm << "        DataWindow: " << std::endl;
120   strm << "                xMin: " << PDesc.DataWindow.xMin << std::endl;
121   strm << "                yMin: " << PDesc.DataWindow.yMin << std::endl;
122   strm << "                xMax: " << PDesc.DataWindow.xMax << std::endl;
123   strm << "                yMax: " << PDesc.DataWindow.yMax << std::endl;
124   strm << "     DisplayWindow: " << std::endl;
125   strm << "                xMin: " << PDesc.DisplayWindow.xMin << std::endl;
126   strm << "                yMin: " << PDesc.DisplayWindow.yMin << std::endl;
127   strm << "                xMax: " << PDesc.DisplayWindow.xMax << std::endl;
128   strm << "                yMax: " << PDesc.DisplayWindow.yMax << std::endl;
129   strm << "  PixelAspectRatio: " << PDesc.PixelAspectRatio;
130   strm << "ScreenWindowCenter: " << "x: " << PDesc.ScreenWindowCenter.x << "y: " << PDesc.ScreenWindowCenter.y << std::endl;
131   strm << " ScreenWindowWidth: " << PDesc.ScreenWindowWidth;
132   strm << "          Channels: " << std::endl;
133
134   for(ui32_t i = 0; i < PDesc.Channels.size(); i++)
135   {
136     if(PDesc.Channels[i].name.empty() == false)
137     {
138       strm << "                Name: " << PDesc.Channels[i].name << std::endl;
139       strm << "           pixelType: " << PDesc.Channels[i].pixelType << std::endl;
140       strm << "             pLinear: " << PDesc.Channels[i].pLinear << std::endl;
141       strm << "           xSampling: " << PDesc.Channels[i].xSampling << std::endl;
142       strm << "           ySampling: " << PDesc.Channels[i].ySampling << std::endl;
143     }
144   }
145   strm << "Number of other entries: " << PDesc.Other.size();
146
147   return strm;
148 }
149
150 void AS_02::ACES::PictureDescriptorDump(const PictureDescriptor &PDesc, FILE *stream /*= NULL*/)
151 {
152
153   if(stream == NULL) stream = stderr;
154   fprintf(stream, "          EditRate: %i/%i\n", PDesc.EditRate.Numerator, PDesc.EditRate.Denominator);
155   fprintf(stream, "        SampleRate: %i/%i\n", PDesc.SampleRate.Numerator, PDesc.SampleRate.Denominator);
156   fprintf(stream, "    Chromaticities: \n");
157   fprintf(stream, "               x_red: %f y_red: %f\n", (double)PDesc.Chromaticities.red.x, (double)PDesc.Chromaticities.red.y);
158   fprintf(stream, "             x_green: %f y_green: %f\n", (double)PDesc.Chromaticities.green.x, (double)PDesc.Chromaticities.green.y);
159   fprintf(stream, "              x_blue: %f y_blue: %f\n", (double)PDesc.Chromaticities.blue.x, (double)PDesc.Chromaticities.blue.y);
160   fprintf(stream, "             x_white: %f y_white: %f\n", (double)PDesc.Chromaticities.white.x, (double)PDesc.Chromaticities.white.y);
161   fprintf(stream, "       Compression: %u\n", (unsigned)PDesc.Compression);
162   fprintf(stream, "         LineOrder: %u\n", (unsigned)PDesc.LineOrder);
163   fprintf(stream, "        DataWindow: \n");
164   fprintf(stream, "                xMin: %i\n", PDesc.DataWindow.xMin);
165   fprintf(stream, "                yMin: %i\n", PDesc.DataWindow.yMin);
166   fprintf(stream, "                xMax: %i\n", PDesc.DataWindow.xMax);
167   fprintf(stream, "                yMax: %i\n", PDesc.DataWindow.yMax);
168   fprintf(stream, "     DisplayWindow: \n");
169   fprintf(stream, "                xMin: %i\n", PDesc.DisplayWindow.xMin);
170   fprintf(stream, "                yMin: %i\n", PDesc.DisplayWindow.yMin);
171   fprintf(stream, "                xMax: %i\n", PDesc.DisplayWindow.xMax);
172   fprintf(stream, "                yMax: %i\n", PDesc.DisplayWindow.yMax);
173   fprintf(stream, "  PixelAspectRatio: %f \n", (double)PDesc.PixelAspectRatio);
174   fprintf(stream, "ScreenWindowCenter: x: %f y: %f\n", (double)PDesc.ScreenWindowCenter.x, (double)PDesc.ScreenWindowCenter.y);
175   fprintf(stream, " ScreenWindowWidth: %f\n", (double)PDesc.ScreenWindowWidth);
176   fprintf(stream, "          Channels: \n");
177
178   for(ui32_t i = 0; i < PDesc.Channels.size(); i++)
179   {
180     if(PDesc.Channels[i].name.empty() == false)
181     {
182       fprintf(stream, "                Name: %s\n", PDesc.Channels[i].name.c_str());
183       fprintf(stream, "           pixelType: %i\n", PDesc.Channels[i].pixelType);
184       fprintf(stream, "             pLinear: %u\n", PDesc.Channels[i].pLinear);
185       fprintf(stream, "           xSampling: %i\n", PDesc.Channels[i].xSampling);
186       fprintf(stream, "           ySampling: %i\n", PDesc.Channels[i].ySampling);
187     }
188   }
189   fprintf(stream, "Number of other entries: %lu\n", PDesc.Other.size());
190 }
191
192 AS_02::Result_t AS_02::ACES::ACES_PDesc_to_MD(const PictureDescriptor &PDesc, const ASDCP::Dictionary &dict, ASDCP::MXF::RGBAEssenceDescriptor &EssenceDescriptor)
193 {
194
195   EssenceDescriptor.ContainerDuration = PDesc.ContainerDuration;
196   EssenceDescriptor.SampleRate = PDesc.EditRate;
197   EssenceDescriptor.FrameLayout = 0x00;
198   EssenceDescriptor.StoredWidth = PDesc.DataWindow.xMax - PDesc.DataWindow.xMin + 1;
199   EssenceDescriptor.StoredHeight = PDesc.DataWindow.yMax - PDesc.DataWindow.yMin + 1;
200   EssenceDescriptor.DisplayWidth = PDesc.DisplayWindow.xMax - PDesc.DisplayWindow.xMin + 1;
201   EssenceDescriptor.DisplayHeight = PDesc.DisplayWindow.yMax - PDesc.DisplayWindow.yMin + 1;
202   EssenceDescriptor.DisplayXOffset = PDesc.DisplayWindow.xMin - PDesc.DataWindow.xMin;
203   EssenceDescriptor.DisplayYOffset = PDesc.DisplayWindow.yMin - PDesc.DataWindow.yMin;
204   ASDCP::Rational aspect_ratio(EssenceDescriptor.DisplayWidth, EssenceDescriptor.DisplayHeight);
205   if(aspect_ratio.Denominator != 0)  EssenceDescriptor.AspectRatio = AS_02::ConvertToRational(aspect_ratio.Quotient());
206   EssenceDescriptor.AlphaTransparency = 0x00;
207   EssenceDescriptor.ColorPrimaries = dict.ul(MDD_ColorPrimaries_ACES);
208   EssenceDescriptor.TransferCharacteristic = dict.ul(MDD_TransferCharacteristic_linear);
209   if(PDesc.Channels.size() == 3 && PDesc.Channels.at(0).name == "B" && PDesc.Channels.at(1).name == "G" && PDesc.Channels.at(2).name == "R")
210   {
211     EssenceDescriptor.PictureEssenceCoding = dict.ul(MDD_ACESUncompressedMonoscopicWithoutAlpha); //Picture Essence Coding Label per 2065-5 section 8.2
212     EssenceDescriptor.PixelLayout = RGBALayout(ACESPixelLayoutMonoscopicWOAlpha);
213   }
214   else if(PDesc.Channels.size() == 4 && PDesc.Channels.at(0).name == "A" && PDesc.Channels.at(1).name == "B" && PDesc.Channels.at(2).name == "G" && PDesc.Channels.at(3).name == "R")
215   {
216     EssenceDescriptor.PictureEssenceCoding = dict.ul(MDD_ACESUncompressedMonoscopicWithAlpha); //Picture Essence Coding Label per 2065-5
217     EssenceDescriptor.PixelLayout = RGBALayout(ACESPixelLayoutMonoscopicWAlpha);
218   }
219   else if(PDesc.Channels.size() == 6 && PDesc.Channels.at(0).name == "B" && PDesc.Channels.at(1).name == "G" && PDesc.Channels.at(2).name == "R" &&
220                             PDesc.Channels.at(3).name == "left.B" && PDesc.Channels.at(4).name == "left.G" && PDesc.Channels.at(5).name == "left.R")
221   {
222     return RESULT_NOTIMPL;
223   }
224   else if(PDesc.Channels.size() == 8 && PDesc.Channels.at(0).name == "A" && PDesc.Channels.at(1).name == "B" && PDesc.Channels.at(2).name == "G" && PDesc.Channels.at(3).name == "R" &&
225                             PDesc.Channels.at(4).name == "left.A" && PDesc.Channels.at(5).name == "left.B" && PDesc.Channels.at(6).name == "left.G" && PDesc.Channels.at(7).name == "left.R")
226   {
227     return RESULT_NOTIMPL;
228   }
229   else
230   {
231     return RESULT_NOTIMPL;
232   }
233   return RESULT_OK;
234 }
235 //static Kumu::UUID CreateTargetFrameAssetId(const std::string& target_frame_file);
236
237 AS_02::Result_t
238 AS_02::ACES::CreateTargetFrameAssetId(Kumu::UUID& rID, const std::string& target_frame_file)
239 {
240   Kumu::FileReader reader;
241   Result_t result = Kumu::RESULT_OK;
242   result = reader.OpenRead(target_frame_file);
243   if (KM_SUCCESS(result))
244   {
245     byte_t* read_buffer = (byte_t*)malloc(reader.Size());
246     if (read_buffer)
247     {
248       result = reader.Read(read_buffer, reader.Size());
249       rID = AS_02::ACES::create_4122_type5_id(read_buffer, reader.Size(), s_ns_id_target_frame_prefix);
250       free(read_buffer);
251     } else result = Kumu::RESULT_FAIL;
252   }
253   return result;
254 }
255
256 static Kumu::UUID
257 AS_02::ACES::create_4122_type5_id(const byte_t* subject_name, Kumu::fsize_t size, const byte_t* ns_id)
258 {
259   SHA_CTX ctx;
260   SHA1_Init(&ctx);
261   SHA1_Update(&ctx, ns_id, NS_ID_LENGTH);
262   SHA1_Update(&ctx, subject_name, size);
263
264   const ui32_t sha_len = 20;
265   byte_t bin_buf[sha_len];
266   SHA1_Final(bin_buf, &ctx);
267
268   // Derive the asset ID from the digest. Make it a type-5 UUID
269   byte_t buf[Kumu::UUID_Length];
270   memcpy(buf, bin_buf, Kumu::UUID_Length);
271   buf[6] &= 0x0f; // clear bits 4-7
272   buf[6] |= 0x50; // set UUID version 'digest'
273   buf[8] &= 0x3f; // clear bits 6&7
274   buf[8] |= 0x80; // set bit 7
275   return Kumu::UUID(buf);
276 }
277
278 void AS_02::ACES::FrameBuffer::Dump(FILE *stream /*= NULL*/, ui32_t dump_bytes /*= NULL*/) const
279 {
280
281   if(stream == 0) stream = stderr;
282   fprintf(stream, "Frame: %06u, %7u bytes", m_FrameNumber, m_Size);
283   fputc('\n', stream);
284   if(dump_bytes > 0) Kumu::hexdump(m_Data, dump_bytes, stream);
285 }
286
287 class AS_02::ACES::MXFReader::h__Reader : public AS_02::h__AS02Reader
288 {
289   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
290   ResourceMap_t m_ResourceMap;
291   ASDCP::MXF::RGBAEssenceDescriptor *m_EssenceDescriptor;
292
293 public:
294   h__Reader(const Dictionary& d) :
295     AS_02::h__AS02Reader(d), m_EssenceDescriptor(NULL) {}
296
297   AS_02::ACES::ResourceList_t m_Anc_Resources;
298
299   virtual ~h__Reader() {}
300
301   Result_t    OpenRead(const std::string&);
302   Result_t    FillAncillaryResourceDescriptor(AS_02::ACES::ResourceList_t &ancillary_resources);
303   Result_t    ReadFrame(ui32_t, AS_02::ACES::FrameBuffer&, AESDecContext*, HMACContext*);
304   Result_t    ReadAncillaryResource(const Kumu::UUID&, AS_02::ACES::FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC);
305 };
306
307
308
309 Result_t AS_02::ACES::MXFReader::h__Reader::OpenRead(const std::string& filename)
310 {
311
312   Result_t result = OpenMXFRead(filename.c_str());
313
314   if(KM_SUCCESS(result))
315   {
316     InterchangeObject* tmp_iobj = 0;
317
318     result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
319
320     if(ASDCP_SUCCESS(result))
321     {
322       if(m_EssenceDescriptor == NULL)
323       {
324         m_EssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
325         FillAncillaryResourceDescriptor(m_Anc_Resources);
326       }
327     }
328     else
329     {
330       DefaultLogSink().Error("RGBAEssenceDescriptor not found.\n");
331     }
332
333     std::list<InterchangeObject*> ObjectList;
334     m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
335
336     if(ObjectList.empty())
337     {
338       DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
339       return AS_02::RESULT_AS02_FORMAT;
340     }
341   }
342   return result;
343 }
344
345 Result_t AS_02::ACES::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, AS_02::ACES::FrameBuffer& FrameBuf, ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
346 {
347   if(!m_File.IsOpen()) return RESULT_INIT;
348
349   assert(m_Dict);
350   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_ACESFrameWrappedEssence), Ctx, HMAC); //PB:new UL
351
352 }
353
354 AS_02::Result_t AS_02::ACES::MXFReader::h__Reader::ReadAncillaryResource(const Kumu::UUID &uuid, AS_02::ACES::FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC)
355 {
356
357
358   ResourceMap_t::const_iterator ri = m_ResourceMap.find(uuid);
359   if(ri == m_ResourceMap.end())
360   {
361     char buf[64];
362     DefaultLogSink().Error("No such resource: %s\n", uuid.EncodeHex(buf, 64));
363     return RESULT_RANGE;
364   }
365   TargetFrameSubDescriptor* DescObject = 0;
366   // get the subdescriptor
367   InterchangeObject* tmp_iobj = 0;
368   Result_t result = m_HeaderPart.GetMDObjectByID((*ri).second, &tmp_iobj);
369   if (KM_SUCCESS(result) && tmp_iobj->IsA(m_Dict->ul(MDD_TargetFrameSubDescriptor)))
370   {
371     DescObject = static_cast<TargetFrameSubDescriptor*>(tmp_iobj);
372
373     RIP::const_pair_iterator pi;
374     RIP::PartitionPair TmpPair;
375     ui32_t sequence = 0;
376
377     // Look up the partition start in the RIP using the SID.
378     // Count the sequence length in because this is the sequence
379     // value needed to  complete the HMAC.
380     for(pi = m_RIP.PairArray.begin(); pi != m_RIP.PairArray.end(); ++pi, ++sequence)
381     {
382       if((*pi).BodySID == DescObject->TargetFrameEssenceStreamID)
383       {
384         TmpPair = *pi;
385         break;
386       }
387     }
388
389     if(TmpPair.ByteOffset == 0)
390     {
391       DefaultLogSink().Error("Body SID not found in RIP set: %d\n", DescObject->TargetFrameEssenceStreamID);
392       return RESULT_FORMAT;
393     }
394
395     // seek to the start of the partition
396     if((Kumu::fpos_t)TmpPair.ByteOffset != m_LastPosition)
397     {
398       m_LastPosition = TmpPair.ByteOffset;
399       result = m_File.Seek(TmpPair.ByteOffset);
400     }
401
402     // read the partition header
403     ASDCP::MXF::Partition GSPart(m_Dict);
404     result = GSPart.InitFromFile(m_File);
405
406     if(ASDCP_SUCCESS(result))
407     {
408       // check the SID
409       if(DescObject->TargetFrameEssenceStreamID != GSPart.BodySID)
410       {
411         char buf[64];
412         DefaultLogSink().Error("Generic stream partition body differs: %s\n", uuid.EncodeHex(buf, 64));
413         return RESULT_FORMAT;
414       }
415
416         // read the essence packet
417       assert(m_Dict);
418       if(ASDCP_SUCCESS(result))
419         result = ReadEKLVPacket(0, sequence, FrameBuf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC);
420     }
421   }
422
423   return result;
424 }
425
426 AS_02::Result_t AS_02::ACES::MXFReader::h__Reader::FillAncillaryResourceDescriptor(AS_02::ACES::ResourceList_t &ancillary_resources)
427 {
428
429   assert(m_EssenceDescriptor);
430   ASDCP::MXF::RGBAEssenceDescriptor* TDescObj = (ASDCP::MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
431
432   Array<Kumu::UUID>::const_iterator sdi = TDescObj->SubDescriptors.begin();
433   TargetFrameSubDescriptor* DescObject = NULL;
434   Result_t result = RESULT_OK;
435
436   for(; sdi != TDescObj->SubDescriptors.end() && KM_SUCCESS(result); sdi++)
437   {
438     InterchangeObject* tmp_iobj = NULL;
439     result = m_HeaderPart.GetMDObjectByID(*sdi, &tmp_iobj);
440     if (!tmp_iobj->IsA(m_Dict->ul(MDD_TargetFrameSubDescriptor)))
441         continue;
442     DescObject = static_cast<TargetFrameSubDescriptor*>(tmp_iobj);
443     if(KM_SUCCESS(result) && DescObject)
444     {
445       AncillaryResourceDescriptor TmpResource; 
446       memcpy(TmpResource.ResourceID, DescObject->TargetFrameAncillaryResourceID.Value(), UUIDlen);
447       
448       if(DescObject->MediaType.find("image/png") != std::string::npos)
449       {
450         TmpResource.Type = AS_02::ACES::MT_PNG;
451       }
452       else if(DescObject->MediaType.find("image/tiff") != std::string::npos)
453       {
454         TmpResource.Type = AS_02::ACES::MT_TIFF;
455       }
456       else
457       {
458         TmpResource.Type = AS_02::ACES::MT_UNDEF;
459       }
460
461       ancillary_resources.push_back(TmpResource);
462       m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->TargetFrameAncillaryResourceID, *sdi));
463     }
464     else
465     {
466       DefaultLogSink().Error("Broken sub-descriptor link\n");
467       return RESULT_FORMAT;
468     }
469   }
470
471   return result;
472 }
473
474 void
475 AS_02::ACES::MXFReader::DumpHeaderMetadata(FILE* stream) const
476 {
477   if ( m_Reader && m_Reader->m_File.IsOpen() )
478     {
479       m_Reader->m_HeaderPart.Dump(stream);
480     }
481 }
482
483
484 //
485 void
486 AS_02::ACES::MXFReader::DumpIndex(FILE* stream) const
487 {
488   if ( m_Reader && m_Reader->m_File.IsOpen() )
489     {
490       m_Reader->m_IndexAccess.Dump(stream);
491     }
492 }
493
494
495
496 class AS_02::ACES::MXFWriter::h__Writer : public AS_02::h__AS02WriterFrame{
497
498   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
499   h__Writer();
500
501 public:
502   byte_t m_EssenceUL[SMPTE_UL_LENGTH];
503   ui32_t m_EssenceStreamID;
504
505   h__Writer(const Dictionary& d) : h__AS02WriterFrame(d), m_EssenceStreamID(10)
506   {
507
508     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
509   }
510
511   virtual ~h__Writer() {}
512
513   Result_t OpenWrite(const std::string &filename, ASDCP::MXF::FileDescriptor *essence_descriptor,
514                      ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
515                      const AS_02::IndexStrategy_t &IndexStrategy,
516                      const ui32_t &PartitionSpace_sec, const ui32_t &HeaderSize);
517   Result_t SetSourceStream(const std::string &label, const ASDCP::Rational &edit_rate);
518   Result_t WriteFrame(const AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx, ASDCP::HMACContext *HMAC);
519   Result_t WriteAncillaryResource(const AS_02::ACES::FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
520   Result_t Finalize();
521 };
522
523 // Open the file for writing. The file must not exist. Returns error if
524 // the operation cannot be completed.
525 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::OpenWrite(const std::string &filename, ASDCP::MXF::FileDescriptor *essence_descriptor,
526        ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
527     const AS_02::IndexStrategy_t &IndexStrategy, const ui32_t &PartitionSpace_sec, const ui32_t &HeaderSize)
528 {
529
530   if(!m_State.Test_BEGIN())
531   {
532     KM_RESULT_STATE_HERE();
533     return RESULT_STATE;
534   }
535
536   if(m_IndexStrategy != AS_02::IS_FOLLOW)
537   {
538     DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n");
539     return Kumu::RESULT_NOTIMPL;
540   }
541
542   Result_t result = m_File.OpenWrite(filename.c_str());
543
544   if(KM_SUCCESS(result))
545   {
546     m_IndexStrategy = IndexStrategy;
547     m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream()
548     m_HeaderSize = HeaderSize;
549
550     if(essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor)))
551     {
552       DefaultLogSink().Error("Essence descriptor is not a ACES Picture Essence Descriptor.\n");
553       essence_descriptor->Dump();
554       return AS_02::RESULT_AS02_FORMAT;
555     }
556
557     m_EssenceDescriptor = essence_descriptor;
558
559     ASDCP::MXF::InterchangeObject_list_t::iterator i;
560     for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
561     {
562       if ( ( (*i)->GetUL() != UL(m_Dict->ul(MDD_ACESPictureSubDescriptor)) ) && ( (*i)->GetUL() != UL(m_Dict->ul(MDD_TargetFrameSubDescriptor)) ) )
563         {
564           DefaultLogSink().Error("Essence sub-descriptor is not an ACESPictureSubDescriptor or a TargetFrameSubDescriptor.\n");
565           (*i)->Dump();
566         }
567
568       m_EssenceSubDescriptorList.push_back(*i);
569       if (!(*i)->InstanceUID.HasValue()) GenRandomValue((*i)->InstanceUID);
570       m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
571       *i = 0; // parent will only free the ones we don't keep
572     }
573     result = m_State.Goto_INIT();
574   }
575   return result;
576 }
577
578 // Automatically sets the MXF file's metadata from the first aces codestream stream.
579 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::SetSourceStream(const std::string &label, const ASDCP::Rational &edit_rate)
580 {
581
582   assert(m_Dict);
583   if(!m_State.Test_INIT())
584   {
585     KM_RESULT_STATE_HERE();
586     return RESULT_STATE;
587   }
588
589   Result_t result = RESULT_OK;
590   ui32_t EssenceStreamID_backup = m_EssenceStreamID;
591
592   if(KM_SUCCESS(result))
593   {
594     memcpy(m_EssenceUL, m_Dict->ul(MDD_ACESFrameWrappedEssence), SMPTE_UL_LENGTH);
595     m_EssenceUL[SMPTE_UL_LENGTH - 1] = 1; // first (and only) essence container
596     Result_t result = m_State.Goto_READY();
597   }
598
599   if(KM_SUCCESS(result))
600   {
601     result = WriteAS02Header(label, UL(m_Dict->ul(MDD_MXFGCFrameWrappedACESPictures)), //Essence Container Label per 2065-5 section 8.1 (frame wrapping)
602                              PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
603                              edit_rate, derive_timecode_rate_from_edit_rate(edit_rate));
604
605     if(KM_SUCCESS(result))
606     {
607       this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
608     }
609   }
610
611   m_EssenceStreamID = EssenceStreamID_backup;
612   return result;
613 }
614
615 // Writes a frame of essence to the MXF file. If the optional AESEncContext
616 // argument is present, the essence is encrypted prior to writing.
617 // Fails if the file is not open, is finalized, or an operating system
618 // error occurs.
619 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::WriteFrame(const AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx, ASDCP::HMACContext *HMAC)
620 {
621
622   if(FrameBuf.Size() == 0)
623   {
624     DefaultLogSink().Error("The frame buffer size is zero.\n");
625     return RESULT_PARAM;
626   }
627
628   Result_t result = RESULT_OK;
629
630   if(m_State.Test_READY())
631   {
632     result = m_State.Goto_RUNNING(); // first time through
633   }
634
635   if(KM_SUCCESS(result))
636   {
637     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
638     m_FramesWritten++;
639   }
640
641   return result;
642 }
643
644 // Closes the MXF file, writing the index and other closing information.
645 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::Finalize()
646 {
647
648   if(!m_State.Test_RUNNING())
649   {
650     KM_RESULT_STATE_HERE();
651     return RESULT_STATE;
652   }
653
654   Result_t result = m_State.Goto_FINAL();
655
656   if(KM_SUCCESS(result))
657   {
658     result = WriteAS02Footer();
659   }
660
661   return result;
662 }
663
664 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::WriteAncillaryResource(const AS_02::ACES::FrameBuffer &FrameBuf, AESEncContext *Ctx , HMACContext *HMAC )
665 {
666
667   if(!m_State.Test_RUNNING())
668   {
669     KM_RESULT_STATE_HERE();
670     return RESULT_STATE;
671   }
672
673   Kumu::fpos_t here = m_File.Tell();
674   assert(m_Dict);
675
676   // create generic stream partition header
677   static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
678   ASDCP::MXF::Partition GSPart(m_Dict);
679
680   GSPart.MajorVersion = m_HeaderPart.MajorVersion;
681   GSPart.MinorVersion = m_HeaderPart.MinorVersion;
682   GSPart.ThisPartition = here;
683   GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
684   GSPart.BodySID = m_EssenceStreamID;
685   GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
686
687   m_RIP.PairArray.push_back(RIP::PartitionPair(m_EssenceStreamID++, here));
688   GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
689   //GSPart.EssenceContainers.push_back(UL(m_Dict->ul(MDD_ACESFrameWrappedEssence))); //MDD_ACESEssence
690   UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition));
691   Result_t result = GSPart.WriteToFile(m_File, TmpUL);
692
693   if(KM_SUCCESS(result))
694   {
695     ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
696
697     result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
698                                m_StreamOffset, FrameBuf, GenericStream_DataElement.Value(),
699                                MXF_BER_LENGTH, Ctx, HMAC);
700   }
701   return result;
702 }
703
704 AS_02::ACES::MXFWriter::MXFWriter()
705 {
706
707 }
708
709 AS_02::ACES::MXFWriter::~MXFWriter()
710 {
711
712 }
713
714 ASDCP::MXF::OP1aHeader& AS_02::ACES::MXFWriter::OP1aHeader()
715 {
716
717   if(m_Writer.empty())
718   {
719     assert(g_OP1aHeader);
720     return *g_OP1aHeader;
721   }
722   return m_Writer->m_HeaderPart;
723 }
724
725 ASDCP::MXF::RIP& AS_02::ACES::MXFWriter::RIP()
726 {
727
728   if(m_Writer.empty())
729   {
730     assert(g_RIP);
731     return *g_RIP;
732   }
733   return m_Writer->m_RIP;
734 }
735
736 AS_02::Result_t AS_02::ACES::MXFWriter::OpenWrite(const std::string &filename, const ASDCP::WriterInfo &Info,
737     ASDCP::MXF::FileDescriptor *essence_descriptor,
738     ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
739     const ASDCP::Rational &edit_rate, const AS_02::ACES::ResourceList_t &ancillary_resources /*= ResourceList_t()*/, const ui32_t &header_size /*= 16384*/, const AS_02::IndexStrategy_t &strategy /*= IS_FOLLOW*/, const ui32_t &partition_space /*= 10*/)
740 {
741
742   if(essence_descriptor == NULL)
743   {
744     DefaultLogSink().Error("Essence descriptor object required.\n");
745     return RESULT_PARAM;
746   }
747
748   m_Writer = new AS_02::ACES::MXFWriter::h__Writer(DefaultSMPTEDict());
749   m_Writer->m_Info = Info;
750
751   Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, strategy, partition_space, header_size);
752
753   if(KM_SUCCESS(result)) result = m_Writer->SetSourceStream(ACES_PACKAGE_LABEL, edit_rate);
754   if(KM_FAILURE(result)) m_Writer.release();
755   return result;
756 }
757
758 AS_02::Result_t AS_02::ACES::MXFWriter::WriteFrame(const FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx /*= NULL*/, ASDCP::HMACContext *HMAC /*= NULL*/)
759 {
760
761   if(m_Writer.empty()) return RESULT_INIT;
762   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
763 }
764
765 AS_02::Result_t AS_02::ACES::MXFWriter::Finalize()
766 {
767
768   if(m_Writer.empty()) return RESULT_INIT;
769   return m_Writer->Finalize();
770 }
771
772
773 AS_02::Result_t AS_02::ACES::MXFWriter::WriteAncillaryResource(const AS_02::ACES::FrameBuffer &rBuf, ASDCP::AESEncContext *Ctx , ASDCP::HMACContext *HMAC )
774 {
775
776   if(m_Writer.empty())
777     return RESULT_INIT;
778
779   return m_Writer->WriteAncillaryResource(rBuf, Ctx, HMAC);
780 }
781
782
783 AS_02::ACES::MXFReader::MXFReader()
784 {
785
786   m_Reader = new h__Reader(DefaultCompositeDict());
787 }
788
789 AS_02::ACES::MXFReader::~MXFReader()
790 {
791
792 }
793
794 // Warning: direct manipulation of MXF structures can interfere
795 // with the normal operation of the wrapper.  Caveat emptor!
796 ASDCP::MXF::OP1aHeader& AS_02::ACES::MXFReader::OP1aHeader()
797 {
798
799   if(m_Reader.empty())
800   {
801     assert(g_OP1aHeader);
802     return *g_OP1aHeader;
803   }
804   return m_Reader->m_HeaderPart;
805 }
806
807 // Warning: direct manipulation of MXF structures can interfere
808 // with the normal operation of the wrapper.  Caveat emptor!
809 AS_02::MXF::AS02IndexReader& AS_02::ACES::MXFReader::AS02IndexReader()
810 {
811
812   if(m_Reader.empty())
813   {
814     assert(g_AS02IndexReader);
815     return *g_AS02IndexReader;
816   }
817   return m_Reader->m_IndexAccess;
818 }
819
820 // Warning: direct manipulation of MXF structures can interfere
821 // with the normal operation of the wrapper.  Caveat emptor!
822 ASDCP::MXF::RIP& AS_02::ACES::MXFReader::RIP()
823 {
824
825   if(m_Reader.empty())
826   {
827     assert(g_RIP);
828     return *g_RIP;
829   }
830   return m_Reader->m_RIP;
831 }
832
833 AS_02::Result_t AS_02::ACES::MXFReader::OpenRead(const std::string& filename) const
834 {
835
836   return m_Reader->OpenRead(filename);
837 }
838
839 AS_02::Result_t AS_02::ACES::MXFReader::Close() const
840 {
841
842   if(m_Reader && m_Reader->m_File.IsOpen())
843   {
844     m_Reader->Close();
845     return RESULT_OK;
846   }
847   return RESULT_INIT;
848 }
849
850 AS_02::Result_t AS_02::ACES::MXFReader::FillWriterInfo(ASDCP::WriterInfo& Info) const
851 {
852
853   if(m_Reader && m_Reader->m_File.IsOpen())
854   {
855     Info = m_Reader->m_Info;
856     return RESULT_OK;
857   }
858   return RESULT_INIT;
859 }
860
861 AS_02::Result_t AS_02::ACES::MXFReader::ReadFrame(ui32_t FrameNum, AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESDecContext *Ctx /*= 0*/, ASDCP::HMACContext *HMAC /*= 0*/) const
862 {
863
864   if(m_Reader && m_Reader->m_File.IsOpen())
865     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
866
867   return RESULT_INIT;
868 }
869
870 AS_02::Result_t AS_02::ACES::MXFReader::ReadAncillaryResource(const Kumu::UUID &uuid, AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESDecContext *Ctx , ASDCP::HMACContext *HMAC ) const
871 {
872
873   if(m_Reader && m_Reader->m_File.IsOpen())
874     return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
875   
876   return RESULT_INIT;
877 }
878
879 AS_02::Result_t AS_02::ACES::MXFReader::FillAncillaryResourceList(AS_02::ACES::ResourceList_t &ancillary_resources) const
880 {
881
882   if(m_Reader && m_Reader->m_File.IsOpen())
883   {
884     ancillary_resources = m_Reader->m_Anc_Resources;
885     return RESULT_OK;
886   }
887   return RESULT_INIT;
888 }
889
890 bool AS_02::ACES::channel::operator==(const channel &Other) const
891 {
892
893   if(name != Other.name) return false;
894   if(pixelType != Other.pixelType) return false;
895   if(pLinear != Other.pLinear) return false;
896   if(xSampling != Other.xSampling) return false;
897   if(ySampling != Other.ySampling) return false;
898   return true;
899 }
900
901 bool AS_02::ACES::box2i::operator==(const box2i &Other) const
902 {
903
904   if(xMin != Other.xMin) return false;
905   if(yMin != Other.yMin) return false;
906   if(xMax != Other.xMax) return false;
907   if(yMax != Other.yMax) return false;
908   return true;
909 }
910
911 bool AS_02::ACES::keycode::operator==(const keycode &Other) const
912 {
913
914   if(filmMfcCode != Other.filmMfcCode) return false;
915   if(filmType != Other.filmType) return false;
916   if(prefix != Other.prefix) return false;
917   if(count != Other.count) return false;
918   if(perfOffset != Other.perfOffset) return false;
919   if(perfsPerFrame != Other.perfsPerFrame) return false;
920   if(perfsPerCount != Other.perfsPerCount) return false;
921   return true;
922 }
923
924 bool AS_02::ACES::v2f::operator==(const v2f &Other) const
925 {
926
927   if(x != Other.x) return false;
928   if(y != Other.y) return false;
929   return true;
930 }
931
932 bool AS_02::ACES::v3f::operator==(const v3f &Other) const
933 {
934
935   if(x != Other.x) return false;
936   if(y != Other.y) return false;
937   if(z != Other.z) return false;
938   return true;
939 }
940
941 bool AS_02::ACES::chromaticities::operator==(const chromaticities &Other) const
942 {
943
944   if(red != Other.red) return false;
945   if(green != Other.green) return false;
946   if(blue != Other.blue) return false;
947   if(white != Other.white) return false;
948   return true;
949 }
950
951 bool AS_02::ACES::timecode::operator==(const timecode &Other) const
952 {
953
954   if(timeAndFlags != Other.timeAndFlags) return false;
955   if(userData != Other.userData) return false;
956   return true;
957 }
958
959 bool AS_02::ACES::PictureDescriptor::operator==(const PictureDescriptor &Other) const
960 {
961
962   if(EditRate != Other.EditRate) return false;
963   if(SampleRate != Other.SampleRate) return false;
964   if(AcesImageContainerFlag != Other.AcesImageContainerFlag) return false;
965   if(Chromaticities != Other.Chromaticities) return false;
966   if(Compression != Other.Compression) return false;
967   if(LineOrder != Other.LineOrder) return false;
968   if(DataWindow != Other.DataWindow) return false;
969   if(DisplayWindow != Other.DisplayWindow) return false;
970   if(PixelAspectRatio != Other.PixelAspectRatio) return false;
971   if(ScreenWindowCenter != Other.ScreenWindowCenter) return false;
972   if(ScreenWindowWidth != Other.ScreenWindowWidth) return false;
973   if(Channels.size() != Other.Channels.size()) return false;
974   for(int i = 0; i < Channels.size(); i++)
975   {
976     if(Channels.at(i) != Other.Channels.at(i)) return false;
977   }
978   return true;
979 }