2 Copyright (c) 2018, Bjoern Stresing, Patrick Bichiou, Wolfgang Ruppel,
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
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.
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.
31 #include "AS_02_ACES.h"
33 #include "AS_02_internal.h"
36 #include <openssl/sha.h>
46 AS_02::ACES::MIME2str(AS_02::ACES::MIMEType_t m)
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";
53 using Kumu::GenRandomValue;
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 //------------------------------------------------------------------------------------------
61 typedef std::map<Kumu::UUID, Kumu::UUID> ResourceMap_t;
63 ASDCP::Rational AS_02::ConvertToRational(double in)
66 //rational approximation to given real number
67 //David Eppstein / UC Irvine / 8 Aug 1993
70 i32_t maxden = std::numeric_limits<i32_t>::max();
75 /* initialize matrix */
76 m[0][0] = m[1][1] = 1;
77 m[0][1] = m[1][0] = 0;
79 /* loop finding terms until denom gets too big */
80 while(m[1][0] * (ai = (long)x) + m[1][1] <= maxden)
83 t = m[0][0] * ai + m[0][1];
86 t = m[1][0] * ai + m[1][1];
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
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 */
97 //printf("%ld/%ld, error = %e\n", m[0][0], m[1][0], startx - ((double)m[0][0] / (double)m[1][0]));
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]));
107 std::ostream& AS_02::ACES::operator<<(std::ostream& strm, const PictureDescriptor& PDesc)
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;
134 for(ui32_t i = 0; i < PDesc.Channels.size(); i++)
136 if(PDesc.Channels[i].name.empty() == false)
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;
145 strm << "Number of other entries: " << PDesc.Other.size();
150 void AS_02::ACES::PictureDescriptorDump(const PictureDescriptor &PDesc, FILE *stream /*= NULL*/)
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");
178 for(ui32_t i = 0; i < PDesc.Channels.size(); i++)
180 if(PDesc.Channels[i].name.empty() == false)
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);
189 fprintf(stream, "Number of other entries: %lu\n", PDesc.Other.size());
192 AS_02::Result_t AS_02::ACES::ACES_PDesc_to_MD(const PictureDescriptor &PDesc, const ASDCP::Dictionary &dict, ASDCP::MXF::RGBAEssenceDescriptor &EssenceDescriptor)
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")
211 EssenceDescriptor.PictureEssenceCoding = dict.ul(MDD_ACESUncompressedMonoscopicWithoutAlpha); //Picture Essence Coding Label per 2065-5 section 8.2
212 EssenceDescriptor.PixelLayout = RGBALayout(ACESPixelLayoutMonoscopicWOAlpha);
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")
216 EssenceDescriptor.PictureEssenceCoding = dict.ul(MDD_ACESUncompressedMonoscopicWithAlpha); //Picture Essence Coding Label per 2065-5
217 EssenceDescriptor.PixelLayout = RGBALayout(ACESPixelLayoutMonoscopicWAlpha);
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")
222 return RESULT_NOTIMPL;
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")
227 return RESULT_NOTIMPL;
231 return RESULT_NOTIMPL;
235 //static Kumu::UUID CreateTargetFrameAssetId(const std::string& target_frame_file);
238 AS_02::ACES::CreateTargetFrameAssetId(Kumu::UUID& rID, const std::string& target_frame_file)
240 Kumu::FileReader reader;
241 Result_t result = Kumu::RESULT_OK;
242 result = reader.OpenRead(target_frame_file);
243 if (KM_SUCCESS(result))
245 byte_t* read_buffer = (byte_t*)malloc(reader.Size());
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);
251 } else result = Kumu::RESULT_FAIL;
257 AS_02::ACES::create_4122_type5_id(const byte_t* subject_name, Kumu::fsize_t size, const byte_t* ns_id)
261 SHA1_Update(&ctx, ns_id, NS_ID_LENGTH);
262 SHA1_Update(&ctx, subject_name, size);
264 const ui32_t sha_len = 20;
265 byte_t bin_buf[sha_len];
266 SHA1_Final(bin_buf, &ctx);
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);
278 void AS_02::ACES::FrameBuffer::Dump(FILE *stream /*= NULL*/, ui32_t dump_bytes /*= NULL*/) const
281 if(stream == 0) stream = stderr;
282 fprintf(stream, "Frame: %06u, %7u bytes", m_FrameNumber, m_Size);
284 if(dump_bytes > 0) Kumu::hexdump(m_Data, dump_bytes, stream);
287 class AS_02::ACES::MXFReader::h__Reader : public AS_02::h__AS02Reader
289 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
290 ResourceMap_t m_ResourceMap;
291 ASDCP::MXF::RGBAEssenceDescriptor *m_EssenceDescriptor;
294 h__Reader(const Dictionary& d) :
295 AS_02::h__AS02Reader(d), m_EssenceDescriptor(NULL) {}
297 AS_02::ACES::ResourceList_t m_Anc_Resources;
299 virtual ~h__Reader() {}
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);
309 Result_t AS_02::ACES::MXFReader::h__Reader::OpenRead(const std::string& filename)
312 Result_t result = OpenMXFRead(filename.c_str());
314 if(KM_SUCCESS(result))
316 InterchangeObject* tmp_iobj = 0;
318 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
320 if(ASDCP_SUCCESS(result))
322 if(m_EssenceDescriptor == NULL)
324 m_EssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
325 FillAncillaryResourceDescriptor(m_Anc_Resources);
330 DefaultLogSink().Error("RGBAEssenceDescriptor not found.\n");
333 std::list<InterchangeObject*> ObjectList;
334 m_HeaderPart.GetMDObjectsByType(OBJ_TYPE_ARGS(Track), ObjectList);
336 if(ObjectList.empty())
338 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
339 return AS_02::RESULT_AS02_FORMAT;
345 Result_t AS_02::ACES::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, AS_02::ACES::FrameBuffer& FrameBuf, ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
347 if(!m_File.IsOpen()) return RESULT_INIT;
350 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_ACESFrameWrappedEssence), Ctx, HMAC); //PB:new UL
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)
358 ResourceMap_t::const_iterator ri = m_ResourceMap.find(uuid);
359 if(ri == m_ResourceMap.end())
362 DefaultLogSink().Error("No such resource: %s\n", uuid.EncodeHex(buf, 64));
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)))
371 DescObject = static_cast<TargetFrameSubDescriptor*>(tmp_iobj);
373 RIP::const_pair_iterator pi;
374 RIP::PartitionPair TmpPair;
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)
382 if((*pi).BodySID == DescObject->TargetFrameEssenceStreamID)
389 if(TmpPair.ByteOffset == 0)
391 DefaultLogSink().Error("Body SID not found in RIP set: %d\n", DescObject->TargetFrameEssenceStreamID);
392 return RESULT_FORMAT;
395 // seek to the start of the partition
396 if((Kumu::fpos_t)TmpPair.ByteOffset != m_LastPosition)
398 m_LastPosition = TmpPair.ByteOffset;
399 result = m_File.Seek(TmpPair.ByteOffset);
402 // read the partition header
403 ASDCP::MXF::Partition GSPart(m_Dict);
404 result = GSPart.InitFromFile(m_File);
406 if(ASDCP_SUCCESS(result))
409 if(DescObject->TargetFrameEssenceStreamID != GSPart.BodySID)
412 DefaultLogSink().Error("Generic stream partition body differs: %s\n", uuid.EncodeHex(buf, 64));
413 return RESULT_FORMAT;
416 // read the essence packet
418 if(ASDCP_SUCCESS(result))
419 result = ReadEKLVPacket(0, sequence, FrameBuf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC);
426 AS_02::Result_t AS_02::ACES::MXFReader::h__Reader::FillAncillaryResourceDescriptor(AS_02::ACES::ResourceList_t &ancillary_resources)
429 assert(m_EssenceDescriptor);
430 ASDCP::MXF::RGBAEssenceDescriptor* TDescObj = (ASDCP::MXF::RGBAEssenceDescriptor*)m_EssenceDescriptor;
432 Array<Kumu::UUID>::const_iterator sdi = TDescObj->SubDescriptors.begin();
433 TargetFrameSubDescriptor* DescObject = NULL;
434 Result_t result = RESULT_OK;
436 for(; sdi != TDescObj->SubDescriptors.end() && KM_SUCCESS(result); sdi++)
438 InterchangeObject* tmp_iobj = NULL;
439 result = m_HeaderPart.GetMDObjectByID(*sdi, &tmp_iobj);
440 if (!tmp_iobj->IsA(m_Dict->ul(MDD_TargetFrameSubDescriptor)))
442 DescObject = static_cast<TargetFrameSubDescriptor*>(tmp_iobj);
443 if(KM_SUCCESS(result) && DescObject)
445 AncillaryResourceDescriptor TmpResource;
446 memcpy(TmpResource.ResourceID, DescObject->TargetFrameAncillaryResourceID.Value(), UUIDlen);
448 if(DescObject->MediaType.find("image/png") != std::string::npos)
450 TmpResource.Type = AS_02::ACES::MT_PNG;
452 else if(DescObject->MediaType.find("image/tiff") != std::string::npos)
454 TmpResource.Type = AS_02::ACES::MT_TIFF;
458 TmpResource.Type = AS_02::ACES::MT_UNDEF;
461 ancillary_resources.push_back(TmpResource);
462 m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->TargetFrameAncillaryResourceID, *sdi));
466 DefaultLogSink().Error("Broken sub-descriptor link\n");
467 return RESULT_FORMAT;
475 AS_02::ACES::MXFReader::DumpHeaderMetadata(FILE* stream) const
477 if ( m_Reader && m_Reader->m_File.IsOpen() )
479 m_Reader->m_HeaderPart.Dump(stream);
486 AS_02::ACES::MXFReader::DumpIndex(FILE* stream) const
488 if ( m_Reader && m_Reader->m_File.IsOpen() )
490 m_Reader->m_IndexAccess.Dump(stream);
496 class AS_02::ACES::MXFWriter::h__Writer : public AS_02::h__AS02WriterFrame{
498 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
502 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
503 ui32_t m_EssenceStreamID;
505 h__Writer(const Dictionary& d) : h__AS02WriterFrame(d), m_EssenceStreamID(10)
508 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
511 virtual ~h__Writer() {}
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);
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)
530 if(!m_State.Test_BEGIN())
532 KM_RESULT_STATE_HERE();
536 if(m_IndexStrategy != AS_02::IS_FOLLOW)
538 DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n");
539 return Kumu::RESULT_NOTIMPL;
542 Result_t result = m_File.OpenWrite(filename.c_str());
544 if(KM_SUCCESS(result))
546 m_IndexStrategy = IndexStrategy;
547 m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream()
548 m_HeaderSize = HeaderSize;
550 if(essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor)))
552 DefaultLogSink().Error("Essence descriptor is not a ACES Picture Essence Descriptor.\n");
553 essence_descriptor->Dump();
554 return AS_02::RESULT_AS02_FORMAT;
557 m_EssenceDescriptor = essence_descriptor;
559 ASDCP::MXF::InterchangeObject_list_t::iterator i;
560 for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
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)) )
567 DefaultLogSink().Error("Essence sub-descriptor is not an ACESPictureSubDescriptor or a TargetFrameSubDescriptor.\n");
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
576 result = m_State.Goto_INIT();
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)
586 if(!m_State.Test_INIT())
588 KM_RESULT_STATE_HERE();
592 Result_t result = RESULT_OK;
593 ui32_t EssenceStreamID_backup = m_EssenceStreamID;
595 if(KM_SUCCESS(result))
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();
602 if(KM_SUCCESS(result))
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));
608 if(KM_SUCCESS(result))
610 this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
614 m_EssenceStreamID = EssenceStreamID_backup;
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
622 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::WriteFrame(const AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx, ASDCP::HMACContext *HMAC)
625 if(FrameBuf.Size() == 0)
627 DefaultLogSink().Error("The frame buffer size is zero.\n");
631 Result_t result = RESULT_OK;
633 if(m_State.Test_READY())
635 result = m_State.Goto_RUNNING(); // first time through
638 if(KM_SUCCESS(result))
640 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
647 // Closes the MXF file, writing the index and other closing information.
648 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::Finalize()
651 if(!m_State.Test_RUNNING())
653 KM_RESULT_STATE_HERE();
657 Result_t result = m_State.Goto_FINAL();
659 if(KM_SUCCESS(result))
661 result = WriteAS02Footer();
667 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::WriteAncillaryResource(const AS_02::ACES::FrameBuffer &FrameBuf, AESEncContext *Ctx , HMACContext *HMAC )
670 if(!m_State.Test_RUNNING())
672 KM_RESULT_STATE_HERE();
676 Kumu::fpos_t here = m_File.Tell();
679 // create generic stream partition header
680 static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
681 ASDCP::MXF::Partition GSPart(m_Dict);
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;
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);
696 if(KM_SUCCESS(result))
698 ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
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);
707 AS_02::ACES::MXFWriter::MXFWriter()
712 AS_02::ACES::MXFWriter::~MXFWriter()
717 ASDCP::MXF::OP1aHeader& AS_02::ACES::MXFWriter::OP1aHeader()
722 assert(g_OP1aHeader);
723 return *g_OP1aHeader;
725 return m_Writer->m_HeaderPart;
728 ASDCP::MXF::RIP& AS_02::ACES::MXFWriter::RIP()
736 return m_Writer->m_RIP;
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*/)
745 if(essence_descriptor == NULL)
747 DefaultLogSink().Error("Essence descriptor object required.\n");
751 m_Writer = new AS_02::ACES::MXFWriter::h__Writer(DefaultSMPTEDict());
752 m_Writer->m_Info = Info;
754 Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, strategy, partition_space, header_size);
756 if(KM_SUCCESS(result)) result = m_Writer->SetSourceStream(ACES_PACKAGE_LABEL, edit_rate);
757 if(KM_FAILURE(result)) m_Writer.release();
761 AS_02::Result_t AS_02::ACES::MXFWriter::WriteFrame(const FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx /*= NULL*/, ASDCP::HMACContext *HMAC /*= NULL*/)
764 if(m_Writer.empty()) return RESULT_INIT;
765 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
768 AS_02::Result_t AS_02::ACES::MXFWriter::Finalize()
771 if(m_Writer.empty()) return RESULT_INIT;
772 return m_Writer->Finalize();
776 AS_02::Result_t AS_02::ACES::MXFWriter::WriteAncillaryResource(const AS_02::ACES::FrameBuffer &rBuf, ASDCP::AESEncContext *Ctx , ASDCP::HMACContext *HMAC )
782 return m_Writer->WriteAncillaryResource(rBuf, Ctx, HMAC);
786 AS_02::ACES::MXFReader::MXFReader()
789 m_Reader = new h__Reader(DefaultCompositeDict());
792 AS_02::ACES::MXFReader::~MXFReader()
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()
804 assert(g_OP1aHeader);
805 return *g_OP1aHeader;
807 return m_Reader->m_HeaderPart;
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()
817 assert(g_AS02IndexReader);
818 return *g_AS02IndexReader;
820 return m_Reader->m_IndexAccess;
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()
833 return m_Reader->m_RIP;
836 AS_02::Result_t AS_02::ACES::MXFReader::OpenRead(const std::string& filename) const
839 return m_Reader->OpenRead(filename);
842 AS_02::Result_t AS_02::ACES::MXFReader::Close() const
845 if(m_Reader && m_Reader->m_File.IsOpen())
853 AS_02::Result_t AS_02::ACES::MXFReader::FillWriterInfo(ASDCP::WriterInfo& Info) const
856 if(m_Reader && m_Reader->m_File.IsOpen())
858 Info = m_Reader->m_Info;
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
867 if(m_Reader && m_Reader->m_File.IsOpen())
868 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
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
876 if(m_Reader && m_Reader->m_File.IsOpen())
877 return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
882 AS_02::Result_t AS_02::ACES::MXFReader::FillAncillaryResourceList(AS_02::ACES::ResourceList_t &ancillary_resources) const
885 if(m_Reader && m_Reader->m_File.IsOpen())
887 ancillary_resources = m_Reader->m_Anc_Resources;
893 bool AS_02::ACES::channel::operator==(const channel &Other) const
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;
904 bool AS_02::ACES::box2i::operator==(const box2i &Other) const
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;
914 bool AS_02::ACES::keycode::operator==(const keycode &Other) const
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;
927 bool AS_02::ACES::v2f::operator==(const v2f &Other) const
930 if(x != Other.x) return false;
931 if(y != Other.y) return false;
935 bool AS_02::ACES::v3f::operator==(const v3f &Other) const
938 if(x != Other.x) return false;
939 if(y != Other.y) return false;
940 if(z != Other.z) return false;
944 bool AS_02::ACES::chromaticities::operator==(const chromaticities &Other) const
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;
954 bool AS_02::ACES::timecode::operator==(const timecode &Other) const
957 if(timeAndFlags != Other.timeAndFlags) return false;
958 if(userData != Other.userData) return false;
962 bool AS_02::ACES::PictureDescriptor::operator==(const PictureDescriptor &Other) const
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++)
979 if(Channels.at(i) != Other.Channels.at(i)) return false;