Merge pull request #20 from cinecert/htj2c
[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)) )
563                && ( (*i)->GetUL() != UL(m_Dict->ul(MDD_TargetFrameSubDescriptor)) )
564                && ( (*i)->GetUL() != UL(m_Dict->ul(MDD_ContainerConstraintsSubDescriptor)) )
565                           )
566         {
567           DefaultLogSink().Error("Essence sub-descriptor is not an ACESPictureSubDescriptor or a TargetFrameSubDescriptor.\n");
568           (*i)->Dump();
569         }
570
571       m_EssenceSubDescriptorList.push_back(*i);
572       if (!(*i)->InstanceUID.HasValue()) GenRandomValue((*i)->InstanceUID);
573       m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
574       *i = 0; // parent will only free the ones we don't keep
575     }
576     result = m_State.Goto_INIT();
577   }
578   return result;
579 }
580
581 // Automatically sets the MXF file's metadata from the first aces codestream stream.
582 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::SetSourceStream(const std::string &label, const ASDCP::Rational &edit_rate)
583 {
584
585   assert(m_Dict);
586   if(!m_State.Test_INIT())
587   {
588     KM_RESULT_STATE_HERE();
589     return RESULT_STATE;
590   }
591
592   Result_t result = RESULT_OK;
593   ui32_t EssenceStreamID_backup = m_EssenceStreamID;
594
595   if(KM_SUCCESS(result))
596   {
597     memcpy(m_EssenceUL, m_Dict->ul(MDD_ACESFrameWrappedEssence), SMPTE_UL_LENGTH);
598     m_EssenceUL[SMPTE_UL_LENGTH - 1] = 1; // first (and only) essence container
599     Result_t result = m_State.Goto_READY();
600   }
601
602   if(KM_SUCCESS(result))
603   {
604     result = WriteAS02Header(label, UL(m_Dict->ul(MDD_MXFGCFrameWrappedACESPictures)), //Essence Container Label per 2065-5 section 8.1 (frame wrapping)
605                              PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
606                              edit_rate, derive_timecode_rate_from_edit_rate(edit_rate));
607
608     if(KM_SUCCESS(result))
609     {
610       this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
611     }
612   }
613
614   m_EssenceStreamID = EssenceStreamID_backup;
615   return result;
616 }
617
618 // Writes a frame of essence to the MXF file. If the optional AESEncContext
619 // argument is present, the essence is encrypted prior to writing.
620 // Fails if the file is not open, is finalized, or an operating system
621 // error occurs.
622 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::WriteFrame(const AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx, ASDCP::HMACContext *HMAC)
623 {
624
625   if(FrameBuf.Size() == 0)
626   {
627     DefaultLogSink().Error("The frame buffer size is zero.\n");
628     return RESULT_PARAM;
629   }
630
631   Result_t result = RESULT_OK;
632
633   if(m_State.Test_READY())
634   {
635     result = m_State.Goto_RUNNING(); // first time through
636   }
637
638   if(KM_SUCCESS(result))
639   {
640     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
641     m_FramesWritten++;
642   }
643
644   return result;
645 }
646
647 // Closes the MXF file, writing the index and other closing information.
648 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::Finalize()
649 {
650
651   if(!m_State.Test_RUNNING())
652   {
653     KM_RESULT_STATE_HERE();
654     return RESULT_STATE;
655   }
656
657   Result_t result = m_State.Goto_FINAL();
658
659   if(KM_SUCCESS(result))
660   {
661     result = WriteAS02Footer();
662   }
663
664   return result;
665 }
666
667 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::WriteAncillaryResource(const AS_02::ACES::FrameBuffer &FrameBuf, AESEncContext *Ctx , HMACContext *HMAC )
668 {
669
670   if(!m_State.Test_RUNNING())
671   {
672     KM_RESULT_STATE_HERE();
673     return RESULT_STATE;
674   }
675
676   Kumu::fpos_t here = m_File.Tell();
677   assert(m_Dict);
678
679   // create generic stream partition header
680   static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
681   ASDCP::MXF::Partition GSPart(m_Dict);
682
683   GSPart.MajorVersion = m_HeaderPart.MajorVersion;
684   GSPart.MinorVersion = m_HeaderPart.MinorVersion;
685   GSPart.ThisPartition = here;
686   GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
687   GSPart.BodySID = m_EssenceStreamID;
688   GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
689
690   m_RIP.PairArray.push_back(RIP::PartitionPair(m_EssenceStreamID++, here));
691   GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
692   //GSPart.EssenceContainers.push_back(UL(m_Dict->ul(MDD_ACESFrameWrappedEssence))); //MDD_ACESEssence
693   UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition));
694   Result_t result = GSPart.WriteToFile(m_File, TmpUL);
695
696   if(KM_SUCCESS(result))
697   {
698     ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
699
700     result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
701                                m_StreamOffset, FrameBuf, GenericStream_DataElement.Value(),
702                                MXF_BER_LENGTH, Ctx, HMAC);
703   }
704   return result;
705 }
706
707 AS_02::ACES::MXFWriter::MXFWriter()
708 {
709
710 }
711
712 AS_02::ACES::MXFWriter::~MXFWriter()
713 {
714
715 }
716
717 ASDCP::MXF::OP1aHeader& AS_02::ACES::MXFWriter::OP1aHeader()
718 {
719
720   if(m_Writer.empty())
721   {
722     assert(g_OP1aHeader);
723     return *g_OP1aHeader;
724   }
725   return m_Writer->m_HeaderPart;
726 }
727
728 ASDCP::MXF::RIP& AS_02::ACES::MXFWriter::RIP()
729 {
730
731   if(m_Writer.empty())
732   {
733     assert(g_RIP);
734     return *g_RIP;
735   }
736   return m_Writer->m_RIP;
737 }
738
739 AS_02::Result_t AS_02::ACES::MXFWriter::OpenWrite(const std::string &filename, const ASDCP::WriterInfo &Info,
740     ASDCP::MXF::FileDescriptor *essence_descriptor,
741     ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
742     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*/)
743 {
744
745   if(essence_descriptor == NULL)
746   {
747     DefaultLogSink().Error("Essence descriptor object required.\n");
748     return RESULT_PARAM;
749   }
750
751   m_Writer = new AS_02::ACES::MXFWriter::h__Writer(DefaultSMPTEDict());
752   m_Writer->m_Info = Info;
753
754   Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, strategy, partition_space, header_size);
755
756   if(KM_SUCCESS(result)) result = m_Writer->SetSourceStream(ACES_PACKAGE_LABEL, edit_rate);
757   if(KM_FAILURE(result)) m_Writer.release();
758   return result;
759 }
760
761 AS_02::Result_t AS_02::ACES::MXFWriter::WriteFrame(const FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx /*= NULL*/, ASDCP::HMACContext *HMAC /*= NULL*/)
762 {
763
764   if(m_Writer.empty()) return RESULT_INIT;
765   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
766 }
767
768 AS_02::Result_t AS_02::ACES::MXFWriter::Finalize()
769 {
770
771   if(m_Writer.empty()) return RESULT_INIT;
772   return m_Writer->Finalize();
773 }
774
775
776 AS_02::Result_t AS_02::ACES::MXFWriter::WriteAncillaryResource(const AS_02::ACES::FrameBuffer &rBuf, ASDCP::AESEncContext *Ctx , ASDCP::HMACContext *HMAC )
777 {
778
779   if(m_Writer.empty())
780     return RESULT_INIT;
781
782   return m_Writer->WriteAncillaryResource(rBuf, Ctx, HMAC);
783 }
784
785
786 AS_02::ACES::MXFReader::MXFReader()
787 {
788
789   m_Reader = new h__Reader(DefaultCompositeDict());
790 }
791
792 AS_02::ACES::MXFReader::~MXFReader()
793 {
794
795 }
796
797 // Warning: direct manipulation of MXF structures can interfere
798 // with the normal operation of the wrapper.  Caveat emptor!
799 ASDCP::MXF::OP1aHeader& AS_02::ACES::MXFReader::OP1aHeader()
800 {
801
802   if(m_Reader.empty())
803   {
804     assert(g_OP1aHeader);
805     return *g_OP1aHeader;
806   }
807   return m_Reader->m_HeaderPart;
808 }
809
810 // Warning: direct manipulation of MXF structures can interfere
811 // with the normal operation of the wrapper.  Caveat emptor!
812 AS_02::MXF::AS02IndexReader& AS_02::ACES::MXFReader::AS02IndexReader()
813 {
814
815   if(m_Reader.empty())
816   {
817     assert(g_AS02IndexReader);
818     return *g_AS02IndexReader;
819   }
820   return m_Reader->m_IndexAccess;
821 }
822
823 // Warning: direct manipulation of MXF structures can interfere
824 // with the normal operation of the wrapper.  Caveat emptor!
825 ASDCP::MXF::RIP& AS_02::ACES::MXFReader::RIP()
826 {
827
828   if(m_Reader.empty())
829   {
830     assert(g_RIP);
831     return *g_RIP;
832   }
833   return m_Reader->m_RIP;
834 }
835
836 AS_02::Result_t AS_02::ACES::MXFReader::OpenRead(const std::string& filename) const
837 {
838
839   return m_Reader->OpenRead(filename);
840 }
841
842 AS_02::Result_t AS_02::ACES::MXFReader::Close() const
843 {
844
845   if(m_Reader && m_Reader->m_File.IsOpen())
846   {
847     m_Reader->Close();
848     return RESULT_OK;
849   }
850   return RESULT_INIT;
851 }
852
853 AS_02::Result_t AS_02::ACES::MXFReader::FillWriterInfo(ASDCP::WriterInfo& Info) const
854 {
855
856   if(m_Reader && m_Reader->m_File.IsOpen())
857   {
858     Info = m_Reader->m_Info;
859     return RESULT_OK;
860   }
861   return RESULT_INIT;
862 }
863
864 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
865 {
866
867   if(m_Reader && m_Reader->m_File.IsOpen())
868     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
869
870   return RESULT_INIT;
871 }
872
873 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
874 {
875
876   if(m_Reader && m_Reader->m_File.IsOpen())
877     return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
878   
879   return RESULT_INIT;
880 }
881
882 AS_02::Result_t AS_02::ACES::MXFReader::FillAncillaryResourceList(AS_02::ACES::ResourceList_t &ancillary_resources) const
883 {
884
885   if(m_Reader && m_Reader->m_File.IsOpen())
886   {
887     ancillary_resources = m_Reader->m_Anc_Resources;
888     return RESULT_OK;
889   }
890   return RESULT_INIT;
891 }
892
893 bool AS_02::ACES::channel::operator==(const channel &Other) const
894 {
895
896   if(name != Other.name) return false;
897   if(pixelType != Other.pixelType) return false;
898   if(pLinear != Other.pLinear) return false;
899   if(xSampling != Other.xSampling) return false;
900   if(ySampling != Other.ySampling) return false;
901   return true;
902 }
903
904 bool AS_02::ACES::box2i::operator==(const box2i &Other) const
905 {
906
907   if(xMin != Other.xMin) return false;
908   if(yMin != Other.yMin) return false;
909   if(xMax != Other.xMax) return false;
910   if(yMax != Other.yMax) return false;
911   return true;
912 }
913
914 bool AS_02::ACES::keycode::operator==(const keycode &Other) const
915 {
916
917   if(filmMfcCode != Other.filmMfcCode) return false;
918   if(filmType != Other.filmType) return false;
919   if(prefix != Other.prefix) return false;
920   if(count != Other.count) return false;
921   if(perfOffset != Other.perfOffset) return false;
922   if(perfsPerFrame != Other.perfsPerFrame) return false;
923   if(perfsPerCount != Other.perfsPerCount) return false;
924   return true;
925 }
926
927 bool AS_02::ACES::v2f::operator==(const v2f &Other) const
928 {
929
930   if(x != Other.x) return false;
931   if(y != Other.y) return false;
932   return true;
933 }
934
935 bool AS_02::ACES::v3f::operator==(const v3f &Other) const
936 {
937
938   if(x != Other.x) return false;
939   if(y != Other.y) return false;
940   if(z != Other.z) return false;
941   return true;
942 }
943
944 bool AS_02::ACES::chromaticities::operator==(const chromaticities &Other) const
945 {
946
947   if(red != Other.red) return false;
948   if(green != Other.green) return false;
949   if(blue != Other.blue) return false;
950   if(white != Other.white) return false;
951   return true;
952 }
953
954 bool AS_02::ACES::timecode::operator==(const timecode &Other) const
955 {
956
957   if(timeAndFlags != Other.timeAndFlags) return false;
958   if(userData != Other.userData) return false;
959   return true;
960 }
961
962 bool AS_02::ACES::PictureDescriptor::operator==(const PictureDescriptor &Other) const
963 {
964
965   if(EditRate != Other.EditRate) return false;
966   if(SampleRate != Other.SampleRate) return false;
967   if(AcesImageContainerFlag != Other.AcesImageContainerFlag) return false;
968   if(Chromaticities != Other.Chromaticities) return false;
969   if(Compression != Other.Compression) return false;
970   if(LineOrder != Other.LineOrder) return false;
971   if(DataWindow != Other.DataWindow) return false;
972   if(DisplayWindow != Other.DisplayWindow) return false;
973   if(PixelAspectRatio != Other.PixelAspectRatio) return false;
974   if(ScreenWindowCenter != Other.ScreenWindowCenter) return false;
975   if(ScreenWindowWidth != Other.ScreenWindowWidth) return false;
976   if(Channels.size() != Other.Channels.size()) return false;
977   for(int i = 0; i < Channels.size(); i++)
978   {
979     if(Channels.at(i) != Other.Channels.at(i)) return false;
980   }
981   return true;
982 }