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)) ) && ( (*i)->GetUL() != UL(m_Dict->ul(MDD_TargetFrameSubDescriptor)) ) )
564 DefaultLogSink().Error("Essence sub-descriptor is not an ACESPictureSubDescriptor or a TargetFrameSubDescriptor.\n");
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
573 result = m_State.Goto_INIT();
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)
583 if(!m_State.Test_INIT())
585 KM_RESULT_STATE_HERE();
589 Result_t result = RESULT_OK;
590 ui32_t EssenceStreamID_backup = m_EssenceStreamID;
592 if(KM_SUCCESS(result))
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();
599 if(KM_SUCCESS(result))
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));
605 if(KM_SUCCESS(result))
607 this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
611 m_EssenceStreamID = EssenceStreamID_backup;
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
619 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::WriteFrame(const AS_02::ACES::FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx, ASDCP::HMACContext *HMAC)
622 if(FrameBuf.Size() == 0)
624 DefaultLogSink().Error("The frame buffer size is zero.\n");
628 Result_t result = RESULT_OK;
630 if(m_State.Test_READY())
632 result = m_State.Goto_RUNNING(); // first time through
635 if(KM_SUCCESS(result))
637 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
644 // Closes the MXF file, writing the index and other closing information.
645 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::Finalize()
648 if(!m_State.Test_RUNNING())
650 KM_RESULT_STATE_HERE();
654 Result_t result = m_State.Goto_FINAL();
656 if(KM_SUCCESS(result))
658 result = WriteAS02Footer();
664 AS_02::Result_t AS_02::ACES::MXFWriter::h__Writer::WriteAncillaryResource(const AS_02::ACES::FrameBuffer &FrameBuf, AESEncContext *Ctx , HMACContext *HMAC )
667 if(!m_State.Test_RUNNING())
669 KM_RESULT_STATE_HERE();
673 Kumu::fpos_t here = m_File.Tell();
676 // create generic stream partition header
677 static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
678 ASDCP::MXF::Partition GSPart(m_Dict);
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;
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);
693 if(KM_SUCCESS(result))
695 ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
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);
704 AS_02::ACES::MXFWriter::MXFWriter()
709 AS_02::ACES::MXFWriter::~MXFWriter()
714 ASDCP::MXF::OP1aHeader& AS_02::ACES::MXFWriter::OP1aHeader()
719 assert(g_OP1aHeader);
720 return *g_OP1aHeader;
722 return m_Writer->m_HeaderPart;
725 ASDCP::MXF::RIP& AS_02::ACES::MXFWriter::RIP()
733 return m_Writer->m_RIP;
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*/)
742 if(essence_descriptor == NULL)
744 DefaultLogSink().Error("Essence descriptor object required.\n");
748 m_Writer = new AS_02::ACES::MXFWriter::h__Writer(DefaultSMPTEDict());
749 m_Writer->m_Info = Info;
751 Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, strategy, partition_space, header_size);
753 if(KM_SUCCESS(result)) result = m_Writer->SetSourceStream(ACES_PACKAGE_LABEL, edit_rate);
754 if(KM_FAILURE(result)) m_Writer.release();
758 AS_02::Result_t AS_02::ACES::MXFWriter::WriteFrame(const FrameBuffer &FrameBuf, ASDCP::AESEncContext *Ctx /*= NULL*/, ASDCP::HMACContext *HMAC /*= NULL*/)
761 if(m_Writer.empty()) return RESULT_INIT;
762 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
765 AS_02::Result_t AS_02::ACES::MXFWriter::Finalize()
768 if(m_Writer.empty()) return RESULT_INIT;
769 return m_Writer->Finalize();
773 AS_02::Result_t AS_02::ACES::MXFWriter::WriteAncillaryResource(const AS_02::ACES::FrameBuffer &rBuf, ASDCP::AESEncContext *Ctx , ASDCP::HMACContext *HMAC )
779 return m_Writer->WriteAncillaryResource(rBuf, Ctx, HMAC);
783 AS_02::ACES::MXFReader::MXFReader()
786 m_Reader = new h__Reader(DefaultCompositeDict());
789 AS_02::ACES::MXFReader::~MXFReader()
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()
801 assert(g_OP1aHeader);
802 return *g_OP1aHeader;
804 return m_Reader->m_HeaderPart;
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()
814 assert(g_AS02IndexReader);
815 return *g_AS02IndexReader;
817 return m_Reader->m_IndexAccess;
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()
830 return m_Reader->m_RIP;
833 AS_02::Result_t AS_02::ACES::MXFReader::OpenRead(const std::string& filename) const
836 return m_Reader->OpenRead(filename);
839 AS_02::Result_t AS_02::ACES::MXFReader::Close() const
842 if(m_Reader && m_Reader->m_File.IsOpen())
850 AS_02::Result_t AS_02::ACES::MXFReader::FillWriterInfo(ASDCP::WriterInfo& Info) const
853 if(m_Reader && m_Reader->m_File.IsOpen())
855 Info = m_Reader->m_Info;
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
864 if(m_Reader && m_Reader->m_File.IsOpen())
865 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
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
873 if(m_Reader && m_Reader->m_File.IsOpen())
874 return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
879 AS_02::Result_t AS_02::ACES::MXFReader::FillAncillaryResourceList(AS_02::ACES::ResourceList_t &ancillary_resources) const
882 if(m_Reader && m_Reader->m_File.IsOpen())
884 ancillary_resources = m_Reader->m_Anc_Resources;
890 bool AS_02::ACES::channel::operator==(const channel &Other) const
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;
901 bool AS_02::ACES::box2i::operator==(const box2i &Other) const
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;
911 bool AS_02::ACES::keycode::operator==(const keycode &Other) const
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;
924 bool AS_02::ACES::v2f::operator==(const v2f &Other) const
927 if(x != Other.x) return false;
928 if(y != Other.y) return false;
932 bool AS_02::ACES::v3f::operator==(const v3f &Other) const
935 if(x != Other.x) return false;
936 if(y != Other.y) return false;
937 if(z != Other.z) return false;
941 bool AS_02::ACES::chromaticities::operator==(const chromaticities &Other) const
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;
951 bool AS_02::ACES::timecode::operator==(const timecode &Other) const
954 if(timeAndFlags != Other.timeAndFlags) return false;
955 if(userData != Other.userData) return false;
959 bool AS_02::ACES::PictureDescriptor::operator==(const PictureDescriptor &Other) const
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++)
976 if(Channels.at(i) != Other.Channels.at(i)) return false;