2 Copyright (c) 2011-2021, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
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.
29 /*! \file AS_02_internal.h
30 \version $Id: AS_02_internal.h ***
31 \brief AS-02 library, non-public common elements
34 #ifndef _AS_02_INTERNAL_H_
35 #define _AS_02_INTERNAL_H_
38 #include "AS_DCP_internal.h"
40 #include "AS_02_JXS.h"
42 using Kumu::DefaultLogSink;
47 #ifdef DEFAULT_02_MD_DECL
48 AS_02::MXF::AS02IndexReader *g_AS02IndexReader;
50 extern AS_02::MXF::AS02IndexReader *g_AS02IndexReader;
53 void default_md_object_init();
57 class h__AS02Reader : public ASDCP::MXF::TrackFileReader<ASDCP::MXF::OP1aHeader, AS_02::MXF::AS02IndexReader>
59 ASDCP_NO_COPY_CONSTRUCT(h__AS02Reader);
63 h__AS02Reader(const ASDCP::Dictionary*, const Kumu::IFileReaderFactory& fileReaderFactory);
65 virtual ~h__AS02Reader();
67 Result_t OpenMXFRead(const std::string& filename);
69 // USE FRAME WRAPPING...
70 Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
71 const byte_t* EssenceUL, ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC);
73 // OR CLIP WRAPPING...
74 // clip wrapping is handled directly by the essence-specific classes
75 // Result_t ReadyClip(const ui32_t& FrameNum, const byte_t* EssenceUL, ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC, ui64_t& position);
76 /// Result_t ReadClipBlock(ASDCP::FrameBuffer& FrameBuf, const ui32_t& read_size);
85 class AS02IndexWriterVBR : public ASDCP::MXF::Partition
87 ASDCP::MXF::IndexTableSegment* m_CurrentSegment;
88 ASDCP::MXF::Rational m_EditRate;
90 KM_NO_COPY_CONSTRUCT(AS02IndexWriterVBR);
94 const ASDCP::Dictionary* m_Dict;
95 ASDCP::IPrimerLookup* m_Lookup;
97 AS02IndexWriterVBR(const ASDCP::Dictionary*);
98 virtual ~AS02IndexWriterVBR();
101 void SetPrimerLookup(ASDCP::IPrimerLookup* lookup) {
106 Result_t WriteToFile(Kumu::FileWriter& Writer);
107 void Dump(FILE* = 0);
109 ui32_t GetDuration() const;
110 void PushIndexEntry(const ASDCP::MXF::IndexTableSegment::IndexEntry&);
111 void SetEditRate(const ASDCP::Rational& edit_rate);
116 class AS02IndexWriterCBR : public ASDCP::MXF::Partition
118 ASDCP::MXF::IndexTableSegment* m_CurrentSegment;
119 ASDCP::MXF::Rational m_EditRate;
121 KM_NO_COPY_CONSTRUCT(AS02IndexWriterCBR);
122 AS02IndexWriterCBR();
125 const ASDCP::Dictionary* m_Dict;
126 ASDCP::IPrimerLookup* m_Lookup;
130 AS02IndexWriterCBR(const ASDCP::Dictionary*);
131 virtual ~AS02IndexWriterCBR();
134 void SetPrimerLookup(ASDCP::IPrimerLookup* lookup) {
139 Result_t WriteToFile(Kumu::FileWriter& Writer);
140 ui32_t GetDuration() const;
141 void SetEditRate(const ASDCP::Rational& edit_rate, const ui32_t& sample_size);
146 template <class IndexWriterType>
147 class h__AS02Writer : public ASDCP::MXF::TrackFileWriter<ASDCP::MXF::OP1aHeader>
149 ASDCP_NO_COPY_CONSTRUCT(h__AS02Writer);
153 ui32_t m_PartitionSpace; // edit units per partition
154 IndexWriterType m_IndexWriter;
155 ui64_t m_ECStart; // offset of the first essence element
158 h__AS02Writer(const ASDCP::Dictionary *d) :
159 ASDCP::MXF::TrackFileWriter<ASDCP::MXF::OP1aHeader>(d), m_IndexWriter(d), m_ECStart(0) {}
165 // all the above for a single source clip
166 Result_t WriteAS02Header(const std::string& PackageLabel, const ASDCP::UL& WrappingUL,
167 const std::string& TrackName, const ASDCP::UL& EssenceUL,
168 const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate,
169 const std::vector<ASDCP::UL>* conformsToSpecifications = NULL)
171 if ( EditRate.Numerator == 0 || EditRate.Denominator == 0 )
173 DefaultLogSink().Error("Non-zero edit-rate reqired.\n");
177 InitHeader(MXFVersion_2011, conformsToSpecifications);
179 AddSourceClip(EditRate, EditRate/*TODO: for a moment*/, 0 /*no timecode track*/, TrackName, EssenceUL, DataDefinition, PackageLabel);
180 AddEssenceDescriptor(WrappingUL);
182 this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
183 this->m_RIP.PairArray.push_back(RIP::PartitionPair(0, 0)); // Header partition RIP entry
184 this->m_IndexWriter.MajorVersion = m_HeaderPart.MajorVersion;
185 this->m_IndexWriter.MinorVersion = m_HeaderPart.MinorVersion;
186 this->m_IndexWriter.OperationalPattern = this->m_HeaderPart.OperationalPattern;
187 this->m_IndexWriter.EssenceContainers = this->m_HeaderPart.EssenceContainers;
189 Result_t result = this->m_HeaderPart.WriteToFile(this->m_File, this->m_HeaderSize);
191 if ( KM_SUCCESS(result) )
193 this->m_PartitionSpace *= (ui32_t)floor( EditRate.Quotient() + 0.5 ); // convert seconds to edit units
194 this->m_ECStart = this->m_File.TellPosition();
195 this->m_IndexWriter.IndexSID = 129;
197 UL body_ul(this->m_Dict->ul(MDD_ClosedCompleteBodyPartition));
198 Partition body_part(this->m_Dict);
199 body_part.BodySID = 1;
200 body_part.MajorVersion = this->m_HeaderPart.MajorVersion;
201 body_part.MinorVersion = this->m_HeaderPart.MinorVersion;
202 body_part.OperationalPattern = this->m_HeaderPart.OperationalPattern;
203 body_part.EssenceContainers = this->m_HeaderPart.EssenceContainers;
204 body_part.ThisPartition = this->m_ECStart;
205 result = body_part.WriteToFile(this->m_File, body_ul);
206 this->m_RIP.PairArray.push_back(RIP::PartitionPair(1, body_part.ThisPartition)); // Second RIP Entry
212 Result_t FlushIndexPartition()
214 Result_t result = RESULT_OK;
215 if ( this->m_IndexWriter.GetDuration() > 0 )
217 this->m_IndexWriter.ThisPartition = this->m_File.TellPosition();
218 result = this->m_IndexWriter.WriteToFile(this->m_File);
219 this->m_RIP.PairArray.push_back(RIP::PartitionPair(0, this->m_IndexWriter.ThisPartition));
224 // standard method of writing the header and footer of a completed AS-02 file
226 Result_t WriteAS02Footer()
228 Result_t result = this->FlushIndexPartition();
230 // update all Duration properties
231 ASDCP::MXF::Partition footer_part(this->m_Dict);
232 DurationElementList_t::iterator dli = this->m_DurationUpdateList.begin();
234 for (; dli != this->m_DurationUpdateList.end(); ++dli )
236 **dli = this->m_FramesWritten;
239 this->m_EssenceDescriptor->ContainerDuration = this->m_FramesWritten;
240 footer_part.PreviousPartition = this->m_RIP.PairArray.back().ByteOffset;
242 Kumu::fpos_t here = this->m_File.TellPosition();
243 this->m_RIP.PairArray.push_back(RIP::PartitionPair(0, here)); // Last RIP Entry
244 this->m_HeaderPart.FooterPartition = here;
246 assert(this->m_Dict);
247 footer_part.MajorVersion = this->m_HeaderPart.MajorVersion;
248 footer_part.MinorVersion = this->m_HeaderPart.MinorVersion;
249 footer_part.OperationalPattern = this->m_HeaderPart.OperationalPattern;
250 footer_part.EssenceContainers = this->m_HeaderPart.EssenceContainers;
251 footer_part.FooterPartition = here;
252 footer_part.ThisPartition = here;
254 if (KM_SUCCESS(result))
256 UL footer_ul(this->m_Dict->ul(MDD_CompleteFooter));
257 result = footer_part.WriteToFile(this->m_File, footer_ul);
260 if ( KM_SUCCESS(result) )
261 result = this->m_RIP.WriteToFile(this->m_File);
263 if ( KM_SUCCESS(result) )
264 result = this->m_File.Seek(0);
266 if ( KM_SUCCESS(result) )
267 result = m_HeaderPart.WriteToFile(this->m_File, this->m_HeaderSize);
269 if ( KM_SUCCESS(result) )
271 ASDCP::MXF::RIP::const_pair_iterator i = this->m_RIP.PairArray.begin();
272 ui64_t previous_partition = 0;
274 for ( i = this->m_RIP.PairArray.begin(); KM_SUCCESS(result) && i != this->m_RIP.PairArray.end(); ++i )
276 ASDCP::MXF::Partition plain_part(this->m_Dict);
277 result = this->m_File.Seek(i->ByteOffset);
279 if ( KM_SUCCESS(result) )
280 result = plain_part.InitFromFile(this->m_File);
282 if ( KM_SUCCESS(result)
283 && ( plain_part.IndexSID > 0 || plain_part.BodySID > 0 ) )
285 plain_part.PreviousPartition = previous_partition;
286 plain_part.FooterPartition = footer_part.ThisPartition;
287 previous_partition = plain_part.ThisPartition;
288 result = this->m_File.Seek(i->ByteOffset);
290 if ( KM_SUCCESS(result) )
292 UL tmp_ul = plain_part.GetUL();
293 result = plain_part.WriteToFile(this->m_File, tmp_ul);
299 this->m_File.Close();
305 class h__AS02WriterFrame : public h__AS02Writer<AS_02::MXF::AS02IndexWriterVBR>
307 ASDCP_NO_COPY_CONSTRUCT(h__AS02WriterFrame);
308 h__AS02WriterFrame();
311 IndexStrategy_t m_IndexStrategy; // per SMPTE ST 2067-5
313 h__AS02WriterFrame(const Dictionary*);
314 virtual ~h__AS02WriterFrame();
316 Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
317 const ui32_t& MinEssenceElementBerLength,
318 AESEncContext* Ctx, HMACContext* HMAC);
322 template <typename IndexWriterType>
323 class h__AS02WriterClip : public h__AS02Writer<IndexWriterType>
325 ASDCP_NO_COPY_CONSTRUCT(h__AS02WriterClip);
326 h__AS02WriterClip() {}
329 ui64_t m_ECStart; // offset of the first essence element
330 ui64_t m_ClipStart; // state variable for clip-wrap-in-progress
331 IndexStrategy_t m_IndexStrategy; // per SMPTE ST 2067-5
333 h__AS02WriterClip(const Dictionary* d) :
334 h__AS02Writer<IndexWriterType>(d),
335 m_ECStart(0), m_ClipStart(0), m_IndexStrategy(AS_02::IS_FOLLOW)
337 virtual ~h__AS02WriterClip()
340 bool HasOpenClip() const { return m_ClipStart != 0; }
341 Result_t StartClip(const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext*)
345 DefaultLogSink().Error("Encryption not yet supported for PCM clip-wrap.\n");
349 if (m_ClipStart != 0)
351 DefaultLogSink().Error("Cannot open clip, clip already open.\n");
355 m_ClipStart = h__AS02Writer<IndexWriterType>::m_File.TellPosition();
356 byte_t clip_buffer[24] = { 0 };
357 memcpy(clip_buffer, EssenceUL, 16);
358 bool check = Kumu::write_BER(clip_buffer + 16, 0, 8);
360 return h__AS02Writer<IndexWriterType>::m_File.Write(clip_buffer, 24);
362 Result_t WriteClipBlock(const ASDCP::FrameBuffer& FrameBuf)
364 if (m_ClipStart == 0)
366 DefaultLogSink().Error("Cannot write clip block, no clip open.\n");
370 return h__AS02Writer<IndexWriterType>::m_File.Write(FrameBuf.RoData(), FrameBuf.Size());
372 Result_t FinalizeClip(ui32_t bytes_per_frame)
374 if (m_ClipStart == 0)
376 DefaultLogSink().Error("Cannot close clip, clip not open.\n");
380 ui64_t current_position = h__AS02Writer<IndexWriterType>::m_File.TellPosition();
381 Result_t result = h__AS02Writer<IndexWriterType>::m_File.Seek(m_ClipStart + 16);
383 if (KM_SUCCESS(result))
385 byte_t clip_buffer[8] = { 0 };
386 ui64_t size = static_cast<ui64_t>(h__AS02Writer<IndexWriterType>::m_FramesWritten) * bytes_per_frame;
387 bool check = Kumu::write_BER(clip_buffer, size, 8);
389 result = h__AS02Writer<IndexWriterType>::m_File.Write(clip_buffer, 8);
392 if (KM_SUCCESS(result))
394 result = h__AS02Writer<IndexWriterType>::m_File.Seek(current_position);
400 Result_t FinalizeClip(ui64_t total_bytes_written)
402 if (m_ClipStart == 0)
404 DefaultLogSink().Error("Cannot close clip, clip not open.\n");
408 ui64_t current_position = h__AS02Writer<IndexWriterType>::m_File.TellPosition();
409 Result_t result = h__AS02Writer<IndexWriterType>::m_File.Seek(m_ClipStart + 16);
411 if (KM_SUCCESS(result))
413 byte_t clip_buffer[8] = { 0 };
414 bool check = Kumu::write_BER(clip_buffer, total_bytes_written, 8);
416 result = h__AS02Writer<IndexWriterType>::m_File.Write(clip_buffer, 8);
419 if (KM_SUCCESS(result))
421 result = h__AS02Writer<IndexWriterType>::m_File.Seek(current_position);
432 #endif // _AS_02_INTERNAL_H_
435 // end AS_02_internal.h