Change History
+YYYY-MM-DD - IMF/AS-02 support, bug fixes
+ o Fixed missing return statement in ArchivableString::ArchiveLength
+ (thanks to both Kristof Provost and Franck Chopin)
+ o Fixed broken sample alignment in RF64Writer (thanks to Dolby)
+ o Fixed win32 build (thanks to Dolby)
+ o Massive refactoring of internals to allow easier implementation
+ of AS-02. Some API changes were made as well (note that
+ OPAtomHeader is now OP1aHeader and RIP is no longer part of the
+ OP1aHeader.) If you are using this project as a library (and
+ especially if you are keeping patches against it) PLEASE TAKE
+ TIME TO EVALUATE THIS RELEASE THOUROUGHLY BEFORE ADDING IT TO
+ YOUR RELEASE PATH.
+ o Final integration of Fraunhoffer IIS code contribution. AS-02
+ files are now fully supported with some TODOs and one major
+ exception: LEAD indexes are not supported by the MXF writers
+ and interlace images are not yet supported.
+ o Added support for MCA labels (ST 428-12) to asdcp-wrap. Note
+ that this project is still in the early stages of interop testing
+ so errors are likely present and don't expect any server to
+ understand this feature.
+
+
2013-04-12 - Dolby Atmos support and more audio labels 1.11.49
o Significant code contribution from Dolby Laboratories to add
support for generic data track files as proposed in ST 21DC
o Added ULs for ST 428-12 and Amd. 429-2 2013. Please check!
-
2013-02-20 - bug fixes, enhancements 1.10.48
o Refactored internals of the AS-DCP file readers. While no
changes in behavior are intended, users are cautioned to test
MCAAudioContentKind
MCAAudioElementKind
+
2012-08-07 - bug fix, 1.10.46
o Added missing zero-initializers to time values when parsing a
timestamp string (in the case where the optional [Thh:mm.[:ss]]
# For example, if asdcplib version 1.0.0 were modified to accomodate changes
# in file format, and if no changes were made to AS_DCP.h, the new version would be
# 1.0.1. If changes were also required in AS_DCP.h, the new version would be 1.1.1.
-AC_INIT([asdcplib], [1.12.50a], [asdcplib@cinecert.com])
+AC_INIT([asdcplib], [1.12.50b], [asdcplib@cinecert.com])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_SRCDIR([src/KM_error.h])
/*
-Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
+Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+John Hurst
+
All rights reserved.
Redistribution and use in source and binary forms, with or without
format, labeled IMF Essence Component (AKA "AS-02" for historical
reasons), is described in the following document:
- o SMPTE 2067-5:201X (draft at this time) IMF Essence Component
+ o SMPTE 2067-5:2013 IMF Essence Component
The following use cases are supported by the module:
PCM audio streams
o Read header metadata from an AS-02 file
+
+NOTE: ciphertext support for clip-wrapped PCM is not yet complete.
*/
#ifndef _AS_02_H_
#define _AS_02_H_
-#include "AS_DCP.h"
-#include "MXF.h"
-
+#include "Metadata.h"
-namespace ASDCP {
- namespace MXF {
- // #include<Metadata.h> to use this
- class OPAtomHeader;
- };
-};
namespace AS_02
{
using Kumu::Result_t;
+ KM_DECLARE_RESULT(AS02_FORMAT, -116, "The file format is not proper OP-1a/AS-02.");
+
namespace MXF {
//
+ // reads distributed index tables and provides a uniform lookup with
+ // translated StreamOffest values (that is, StreamOffest is adjusted
+ // to the actual file position
class AS02IndexReader : public ASDCP::MXF::Partition
{
Kumu::ByteString m_IndexSegmentData;
ui32_t m_Duration;
+ ui32_t m_BytesPerEditUnit;
- // ui32_t m_BytesPerEditUnit;
-
- Result_t InitFromBuffer(const byte_t* p, ui32_t l);
+ Result_t InitFromBuffer(const byte_t* p, ui32_t l, const ui64_t& body_offset, const ui64_t& essence_container_offset);
ASDCP_NO_COPY_CONSTRUCT(AS02IndexReader);
AS02IndexReader();
public:
const ASDCP::Dictionary*& m_Dict;
- Kumu::fpos_t m_ECOffset;
ASDCP::IPrimerLookup *m_Lookup;
AS02IndexReader(const ASDCP::Dictionary*&);
virtual ~AS02IndexReader();
- Result_t InitFromFile(const Kumu::FileReader& reader, const ASDCP::MXF::RIP& rip);
+ Result_t InitFromFile(const Kumu::FileReader& reader, const ASDCP::MXF::RIP& rip, const bool has_header_essence);
ui32_t GetDuration() const;
void Dump(FILE* = 0);
Result_t GetMDObjectByID(const Kumu::UUID&, ASDCP::MXF::InterchangeObject** = 0);
Result_t Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegment::IndexEntry&) const;
};
+
+
+ // Returns size in bytes of a single sample of data described by ADesc
+ inline ui32_t CalcSampleSize(const ASDCP::MXF::WaveAudioDescriptor& d)
+ {
+ return (d.QuantizationBits / 8) * d.ChannelCount;
+ }
+
+ // Returns number of samples per frame of data described by ADesc
+ inline ui32_t CalcSamplesPerFrame(const ASDCP::MXF::WaveAudioDescriptor& d, const ASDCP::Rational& edit_rate)
+ {
+ double tmpd = d.AudioSamplingRate.Quotient() / edit_rate.Quotient();
+ return (ui32_t)ceil(tmpd);
+ }
+
+ // Returns the size in bytes of a frame of data described by ADesc
+ inline ui32_t CalcFrameBufferSize(const ASDCP::MXF::WaveAudioDescriptor& d, const ASDCP::Rational& edit_rate)
+ {
+ return CalcSampleSize(d) * CalcSamplesPerFrame(d, edit_rate);
+ }
+
} // namespace MXF
//---------------------------------------------------------------------------------
IS_LEAD,
IS_FOLLOW,
IS_FILE_SPECIFIC,
+ IS_MAX
};
namespace JP2K
// Open the file for writing. The file must not exist. Returns error if
// the operation cannot be completed or if nonsensical data is discovered
// in the essence descriptor.
- Result_t OpenWrite(const char* filename, const ASDCP::WriterInfo&,
- const ASDCP::JP2K::PictureDescriptor&,
- const IndexStrategy_t& Strategy = IS_FOLLOW,
- const ui32_t& PartitionSpace = 60, /* seconds per partition */
- const ui32_t& HeaderSize = 16384);
+ Result_t OpenWrite(const std::string& filename, const ASDCP::WriterInfo&,
+ ASDCP::MXF::FileDescriptor* essence_descriptor,
+ ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+ const ASDCP::Rational& edit_rate, const ui32_t& header_size = 16384,
+ const IndexStrategy_t& strategy = IS_FOLLOW, const ui32_t& partition_space = 10);
// Writes a frame of essence to the MXF file. If the optional AESEncContext
// argument is present, the essence is encrypted prior to writing.
// Open the file for reading. The file must exist. Returns error if the
// operation cannot be completed.
- Result_t OpenRead(const char* filename) const;
+ Result_t OpenRead(const std::string& filename) const;
// Returns RESULT_INIT if the file is not open.
Result_t Close() const;
- // Fill an AudioDescriptor struct with the values from the file's header.
- // Returns RESULT_INIT if the file is not open.
- Result_t FillPictureDescriptor(ASDCP::JP2K::PictureDescriptor&) const;
-
// Fill a WriterInfo struct with the values from the file's header.
// Returns RESULT_INIT if the file is not open.
Result_t FillWriterInfo(ASDCP::WriterInfo&) const;
{
// see AS_DCP.h for related data types
+ // An AS-02 PCM file is clip-wrapped, but the interface defined below mimics that used
+ // for frame-wrapped essence elsewhere in this library. The concept of frame rate
+ // therefore is only relevant to these classes and is not reflected in or affected by
+ // the contents of the MXF file. The frame rate that is set on the writer is only
+ // for compatibility with the existing parsers, samples are packed contiguously into
+ // the same clip-wrapped packet. Similarly, the edit rate must be set when initializing
+ // the reader to signal the number of samples to be read by each call to ReadFrame();
+
//
class MXFWriter
{
// Open the file for writing. The file must not exist. Returns error if
// the operation cannot be completed or if nonsensical data is discovered
// in the essence descriptor.
- Result_t OpenWrite(const char* filename, const ASDCP::WriterInfo&,
- const ASDCP::PCM::AudioDescriptor&, ui32_t HeaderSize = 16384);
+ Result_t OpenWrite(const std::string& filename, const ASDCP::WriterInfo&,
+ ASDCP::MXF::FileDescriptor* essence_descriptor,
+ ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+ const ASDCP::Rational& edit_rate, ui32_t HeaderSize = 16384);
// Writes a frame of essence to the MXF file. If the optional AESEncContext
// argument is present, the essence is encrypted prior to writing.
// Open the file for reading. The file must exist. Returns error if the
// operation cannot be completed.
- Result_t OpenRead(const char* filename) const;
+ Result_t OpenRead(const std::string& filename, const ASDCP::Rational& EditRate);
// Returns RESULT_INIT if the file is not open.
Result_t Close() const;
- // Fill an AudioDescriptor struct with the values from the file's header.
- // Returns RESULT_INIT if the file is not open.
- Result_t FillAudioDescriptor(ASDCP::PCM::AudioDescriptor&) const;
-
// Fill a WriterInfo struct with the values from the file's header.
// Returns RESULT_INIT if the file is not open.
Result_t FillWriterInfo(ASDCP::WriterInfo&) const;
/*
- Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+John Hurst
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*! \file AS_02_JP2K.cpp
\version $Id$
class AS_02::JP2K::MXFReader::h__Reader : public AS_02::h__AS02Reader
{
- RGBAEssenceDescriptor* m_RGBAEssenceDescriptor;
- CDCIEssenceDescriptor* m_CDCIEssenceDescriptor;
- JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
- ASDCP::Rational m_EditRate;
- ASDCP::Rational m_SampleRate;
- EssenceType_t m_Format;
-
ASDCP_NO_COPY_CONSTRUCT(h__Reader);
public:
PictureDescriptor m_PDesc; // codestream parameter list
h__Reader(const Dictionary& d) :
- AS_02::h__AS02Reader(d), m_RGBAEssenceDescriptor(0), m_CDCIEssenceDescriptor(0),
- m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
+ AS_02::h__AS02Reader(d) {}
virtual ~h__Reader() {}
- Result_t OpenRead(const char*);
+ Result_t OpenRead(const std::string&);
Result_t ReadFrame(ui32_t, ASDCP::JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
};
//
Result_t
-AS_02::JP2K::MXFReader::h__Reader::OpenRead(const char* filename)
+AS_02::JP2K::MXFReader::h__Reader::OpenRead(const std::string& filename)
{
- Result_t result = OpenMXFRead(filename);
+ Result_t result = OpenMXFRead(filename.c_str());
- if( ASDCP_SUCCESS(result) )
+ if( KM_SUCCESS(result) )
{
InterchangeObject* tmp_iobj = 0;
m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CDCIEssenceDescriptor), &tmp_iobj);
- m_CDCIEssenceDescriptor = static_cast<CDCIEssenceDescriptor*>(tmp_iobj);
- if ( m_CDCIEssenceDescriptor == 0 )
+ if ( tmp_iobj == 0 )
{
m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
- m_RGBAEssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
}
- if ( m_CDCIEssenceDescriptor == 0 && m_RGBAEssenceDescriptor == 0 )
+ if ( tmp_iobj == 0 )
{
DefaultLogSink().Error("RGBAEssenceDescriptor nor CDCIEssenceDescriptor found.\n");
- return RESULT_FORMAT;
}
m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
- m_EssenceSubDescriptor = static_cast<JPEG2000PictureSubDescriptor*>(tmp_iobj);
- if ( m_EssenceSubDescriptor == 0 )
+ if ( tmp_iobj == 0 )
{
DefaultLogSink().Error("JPEG2000PictureSubDescriptor not found.\n");
- return RESULT_FORMAT;
}
std::list<InterchangeObject*> ObjectList;
if ( ObjectList.empty() )
{
DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
- return RESULT_FORMAT;
- }
-
- if ( m_CDCIEssenceDescriptor != 0 )
- {
- m_EditRate = ((Track*)ObjectList.front())->EditRate;
- m_SampleRate = m_CDCIEssenceDescriptor->SampleRate;
- result = MD_to_JP2K_PDesc(*m_CDCIEssenceDescriptor, *m_EssenceSubDescriptor, m_EditRate, m_SampleRate, m_PDesc);
- }
- else if ( m_RGBAEssenceDescriptor != 0 )
- {
- m_EditRate = ((Track*)ObjectList.front())->EditRate;
- m_SampleRate = m_RGBAEssenceDescriptor->SampleRate;
- result = MD_to_JP2K_PDesc(*m_RGBAEssenceDescriptor, *m_EssenceSubDescriptor, m_EditRate, m_SampleRate, m_PDesc);
- }
-
- if ( m_PDesc.ContainerDuration == 0 )
- {
- m_PDesc.ContainerDuration = m_IndexAccess.GetDuration();
+ return RESULT_AS02_FORMAT;
}
}
// Open the file for reading. The file must exist. Returns error if the
// operation cannot be completed.
Result_t
-AS_02::JP2K::MXFReader::OpenRead(const char* filename) const
+AS_02::JP2K::MXFReader::OpenRead(const std::string& filename) const
{
return m_Reader->OpenRead(filename);
}
//
Result_t
-AS_02::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
- ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
+AS_02::JP2K::MXFReader::Close() const
{
if ( m_Reader && m_Reader->m_File.IsOpen() )
- return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
+ {
+ m_Reader->Close();
+ return RESULT_OK;
+ }
return RESULT_INIT;
}
-
-// Fill the struct with the values from the file's header.
-// Returns RESULT_INIT if the file is not open.
+//
Result_t
-AS_02::JP2K::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
+AS_02::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
+ ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
{
if ( m_Reader && m_Reader->m_File.IsOpen() )
- {
- PDesc = m_Reader->m_PDesc;
- return RESULT_OK;
- }
+ return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
return RESULT_INIT;
}
-
// Fill the struct with the values from the file's header.
// Returns RESULT_INIT if the file is not open.
Result_t
virtual ~h__Writer(){}
- Result_t OpenWrite(const char*, EssenceType_t type, const AS_02::IndexStrategy_t& IndexStrategy,
+ Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
+ ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+ const AS_02::IndexStrategy_t& IndexStrategy,
const ui32_t& PartitionSpace, const ui32_t& HeaderSize);
- Result_t SetSourceStream(const PictureDescriptor&, const std::string& label,
- ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0));
+ Result_t SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate);
Result_t WriteFrame(const ASDCP::JP2K::FrameBuffer&, ASDCP::AESEncContext*, ASDCP::HMACContext*);
Result_t Finalize();
};
// Open the file for writing. The file must not exist. Returns error if
// the operation cannot be completed.
Result_t
-AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const char* filename, EssenceType_t type, const AS_02::IndexStrategy_t& IndexStrategy,
+AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const std::string& filename,
+ ASDCP::MXF::FileDescriptor* essence_descriptor,
+ ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+ const AS_02::IndexStrategy_t& IndexStrategy,
const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize)
{
if ( ! m_State.Test_BEGIN() )
- return RESULT_STATE;
+ {
+ return RESULT_STATE;
+ }
if ( m_IndexStrategy != AS_02::IS_FOLLOW )
{
return Kumu::RESULT_NOTIMPL;
}
- Result_t result = m_File.OpenWrite(filename);
+ Result_t result = m_File.OpenWrite(filename.c_str());
- if ( ASDCP_SUCCESS(result) )
+ if ( KM_SUCCESS(result) )
{
m_IndexStrategy = IndexStrategy;
m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream()
m_HeaderSize = HeaderSize;
- m_EssenceDescriptor = new RGBAEssenceDescriptor(m_Dict);
- m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor(m_Dict);
- m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
+ if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor))
+ && essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_CDCIEssenceDescriptor)) )
+ {
+ DefaultLogSink().Error("Essence descriptor is not a RGBAEssenceDescriptor or CDCIEssenceDescriptor.\n");
+ essence_descriptor->Dump();
+ return RESULT_AS02_FORMAT;
+ }
+
+ m_EssenceDescriptor = essence_descriptor;
+
+ ASDCP::MXF::InterchangeObject_list_t::iterator i;
+ for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
+ {
+ if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_JPEG2000PictureSubDescriptor)) )
+ {
+ DefaultLogSink().Error("Essence sub-descriptor is not a JPEG2000PictureSubDescriptor.\n");
+ (*i)->Dump();
+ }
+
+ m_EssenceSubDescriptorList.push_back(*i);
+ GenRandomValue((*i)->InstanceUID);
+ m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
+ *i = 0; // parent will only free the ones we don't keep
+ }
- GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
- m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
result = m_State.Goto_INIT();
}
// Automatically sets the MXF file's metadata from the first jpeg codestream stream.
Result_t
-AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
+AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate)
{
assert(m_Dict);
if ( ! m_State.Test_INIT() )
- return RESULT_STATE;
-
- if ( LocalEditRate == ASDCP::Rational(0,0) )
- LocalEditRate = PDesc.EditRate;
-
- m_PDesc = PDesc;
- assert(m_Dict);
- Result_t result = JP2K_PDesc_to_MD(m_PDesc, *m_Dict,
- static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor),
- m_EssenceSubDescriptor);
-
- static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->ComponentMaxRef = 4095; /// TODO: set with magic or some such thing
- static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->ComponentMinRef = 0;
-
- if ( ASDCP_SUCCESS(result) )
{
- memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
- m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
- result = m_State.Goto_READY();
+ return RESULT_STATE;
}
- if ( ASDCP_SUCCESS(result) )
- {
- ui32_t TCFrameRate = ( m_PDesc.EditRate == EditRate_23_98 ) ? 24 : m_PDesc.EditRate.Numerator;
+ memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
+ m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
+ Result_t result = m_State.Goto_READY();
+ if ( KM_SUCCESS(result) )
+ {
result = WriteAS02Header(label, UL(m_Dict->ul(MDD_JPEG_2000Wrapping)),
PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
- LocalEditRate, TCFrameRate);
+ edit_rate, derive_timecode_rate_from_edit_rate(edit_rate));
}
return result;
//
Result_t
AS_02::JP2K::MXFWriter::h__Writer::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf,
- AESEncContext* Ctx, HMACContext* HMAC)
+ AESEncContext* Ctx, HMACContext* HMAC)
{
+ if ( FrameBuf.Size() == 0 )
+ {
+ DefaultLogSink().Error("The frame buffer size is zero.\n");
+ return RESULT_PARAM;
+ }
+
Result_t result = RESULT_OK;
if ( m_State.Test_READY() )
- result = m_State.Goto_RUNNING(); // first time through
-
- ui64_t StreamOffset = m_StreamOffset; // m_StreamOffset will be changed by the call to WriteEKLVPacket
-
- if ( ASDCP_SUCCESS(result) )
- result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
-
- if ( ASDCP_SUCCESS(result) )
- {
- IndexTableSegment::IndexEntry Entry;
- Entry.StreamOffset = StreamOffset;
- m_IndexWriter.PushIndexEntry(Entry);
+ {
+ result = m_State.Goto_RUNNING(); // first time through
+ }
+
+ if ( KM_SUCCESS(result) )
+ {
+ result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
+ m_FramesWritten++;
}
- m_FramesWritten++;
return result;
}
Result_t result = m_State.Goto_FINAL();
- if ( ASDCP_SUCCESS(result) )
- result = WriteAS02Footer();
+ if ( KM_SUCCESS(result) )
+ {
+ result = WriteAS02Footer();
+ }
return result;
}
// Open the file for writing. The file must not exist. Returns error if
// the operation cannot be completed.
Result_t
-AS_02::JP2K::MXFWriter::OpenWrite(const char* filename, const ASDCP::WriterInfo& Info,
- const ASDCP::JP2K::PictureDescriptor& PDesc,
- const IndexStrategy_t& Strategy,
- const ui32_t& PartitionSpace,
- const ui32_t& HeaderSize)
+AS_02::JP2K::MXFWriter::OpenWrite(const std::string& filename, const ASDCP::WriterInfo& Info,
+ ASDCP::MXF::FileDescriptor* essence_descriptor,
+ ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+ const ASDCP::Rational& edit_rate, const ui32_t& header_size,
+ const IndexStrategy_t& strategy, const ui32_t& partition_space)
{
+ if ( essence_descriptor == 0 )
+ {
+ DefaultLogSink().Error("Essence descriptor object required.\n");
+ return RESULT_PARAM;
+ }
+
m_Writer = new AS_02::JP2K::MXFWriter::h__Writer(DefaultSMPTEDict());
m_Writer->m_Info = Info;
- Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, Strategy, PartitionSpace, HeaderSize);
+ Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list,
+ strategy, partition_space, header_size);
- if ( ASDCP_SUCCESS(result) )
- result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
+ if ( KM_SUCCESS(result) )
+ result = m_Writer->SetSourceStream(JP2K_PACKAGE_LABEL, edit_rate);
- if ( ASDCP_FAILURE(result) )
+ if ( KM_FAILURE(result) )
m_Writer.release();
return result;
/*
- Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
+Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+John Hurst
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/*! \file AS_02_PCM.cpp
\version $Id$
\brief AS-02 library, PCM essence reader and writer implementation
static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M clip wrapping of wave audio";
static std::string SOUND_DEF_LABEL = "Sound Track";
-//this must be changed because the CBR_frame_size is only
-//
-static ui32_t
-calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
-{
- ui32_t CBR_frame_size = 0;
-
- if ( Info.EncryptedEssence )
- {
- CBR_frame_size =
- //TODO: correct?
- /*SMPTE_UL_LENGTH
- + MXF_BER_LENGTH
- + */klv_cryptinfo_size
- + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
- + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
- }
- else
- {
- CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc);
- }
-
- return CBR_frame_size;
-}
-
//------------------------------------------------------------------------------------------
class AS_02::PCM::MXFReader::h__Reader : public AS_02::h__AS02Reader
{
+ ui64_t m_ClipEssenceBegin;
+ ui64_t m_SamplesPerFrame;
+ ui32_t m_ContainerDuration;
+
ASDCP_NO_COPY_CONSTRUCT(h__Reader);
h__Reader();
public:
- ASDCP::PCM::AudioDescriptor m_ADesc;
-
- h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d) {}
+ h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_ClipEssenceBegin(0),
+ m_SamplesPerFrame(0), m_ContainerDuration(0) {}
virtual ~h__Reader() {}
- ASDCP::Result_t OpenRead(const char*);
+ ASDCP::Result_t OpenRead(const std::string&, const ASDCP::Rational& edit_rate);
ASDCP::Result_t ReadFrame(ui32_t, ASDCP::PCM::FrameBuffer&, ASDCP::AESDecContext*, ASDCP::HMACContext*);
-
-
- /// Result_t OpenMXFRead(const char* filename);
- // positions file before reading
- /// Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
- /// const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
-
- // reads from current position
- Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
- const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
-
-
};
-
+// TODO: This will ignore any body partitions past the first
//
//
ASDCP::Result_t
-AS_02::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
+AS_02::PCM::MXFReader::h__Reader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
{
- Result_t result = OpenMXFRead(filename);
+ ASDCP::MXF::WaveAudioDescriptor* wave_descriptor = 0;
+ IndexTableSegment::IndexEntry tmp_entry;
+ Result_t result = OpenMXFRead(filename.c_str());
- if( ASDCP_SUCCESS(result) )
+ if( KM_SUCCESS(result) )
{
- InterchangeObject* Object = 0;
- if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
+ if ( KM_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor),
+ reinterpret_cast<InterchangeObject**>(&wave_descriptor))) )
{
- if ( Object == 0 )
+ if ( wave_descriptor == 0 )
{
DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
- return RESULT_FORMAT;
+ return RESULT_AS02_FORMAT;
}
-
- result = MD_to_PCM_ADesc((ASDCP::MXF::WaveAudioDescriptor*)Object, m_ADesc);
}
}
- // check for sample/frame rate sanity
- if ( ASDCP_SUCCESS(result)
- && m_ADesc.EditRate != EditRate_24
- && m_ADesc.EditRate != EditRate_25
- && m_ADesc.EditRate != EditRate_30
- && m_ADesc.EditRate != EditRate_48
- && m_ADesc.EditRate != EditRate_50
- && m_ADesc.EditRate != EditRate_60
- && m_ADesc.EditRate != EditRate_23_98 )
+ if ( KM_SUCCESS(result) )
+ result = m_IndexAccess.Lookup(0, tmp_entry);
+
+ if ( KM_SUCCESS(result) )
+ result = m_File.Seek(tmp_entry.StreamOffset);
+
+ if ( KM_SUCCESS(result) )
{
- DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
- m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
+ assert(wave_descriptor);
+ KLReader reader;
+ result = reader.ReadKLFromFile(m_File);
- // oh, they gave us the audio sampling rate instead, assume 24/1
- if ( m_ADesc.EditRate == SampleRate_48k )
+ if ( KM_SUCCESS(result) )
{
- DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
- m_ADesc.EditRate = EditRate_24;
- }
- else
- {
- // or we just drop the hammer
- return RESULT_FORMAT;
+ if ( ! UL(reader.Key()).MatchIgnoreStream(m_Dict->ul(MDD_WAVEssenceClip)) )
+ {
+ const MDDEntry *entry = m_Dict->FindUL(reader.Key());
+
+ if ( entry == 0 )
+ {
+ char buf[64];
+ DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", UL(reader.Key()).EncodeString(buf, 64));
+ }
+ else
+ {
+ DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", entry->name);
+ }
+
+ return RESULT_AS02_FORMAT;
+ }
+
+ if ( wave_descriptor->BlockAlign == 0 )
+ {
+ DefaultLogSink().Error("EssenceDescriptor has corrupt BlockAlign value, unable to continue.\n");
+ return RESULT_AS02_FORMAT;
+ }
+
+ if ( reader.Length() % wave_descriptor->BlockAlign != 0 )
+ {
+ DefaultLogSink().Error("Clip length is not an even multiple of BlockAlign, unable to continue.\n");
+ return RESULT_AS02_FORMAT;
+ }
+
+ m_ClipEssenceBegin = m_File.Tell();
+ m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*wave_descriptor, edit_rate);
+ m_ContainerDuration = reader.Length() / m_SamplesPerFrame;
}
}
- // TODO: test file for sane CBR index BytesPerEditUnit
-
return result;
}
-
-//
//
ASDCP::Result_t
AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
{
if ( ! m_File.IsOpen() )
- return RESULT_INIT;
+ {
+ return RESULT_INIT;
+ }
+
+ if ( FrameNum > m_ContainerDuration )
+ {
+ return RESULT_RANGE;
+ }
+
+ assert(m_ClipEssenceBegin);
+ Result_t result = RESULT_OK;
+ ui64_t position = m_ClipEssenceBegin + ( FrameNum * m_SamplesPerFrame );
- assert(m_Dict);
- return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
+ if ( m_File.Tell() != position )
+ {
+ result = m_File.Seek(position);
+ }
+
+ if ( KM_SUCCESS(result) )
+ {
+ result = m_File.Read(FrameBuf.Data(), m_SamplesPerFrame);
+ }
+
+ if ( KM_SUCCESS(result) )
+ {
+ FrameBuf.Size(m_SamplesPerFrame);
+ }
+
+ return result;
}
+//------------------------------------------------------------------------------------------
+//
+
+
AS_02::PCM::MXFReader::MXFReader()
{
m_Reader = new h__Reader(DefaultCompositeDict());
}
-
AS_02::PCM::MXFReader::~MXFReader()
{
- if ( m_Reader && m_Reader->m_File.IsOpen() )
- m_Reader->Close();
}
// Warning: direct manipulation of MXF structures can interfere
// Open the file for reading. The file must exist. Returns error if the
// operation cannot be completed.
ASDCP::Result_t
-AS_02::PCM::MXFReader::OpenRead(const char* filename) const
+AS_02::PCM::MXFReader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
{
- return m_Reader->OpenRead(filename);
+ return m_Reader->OpenRead(filename, edit_rate);
+}
+
+//
+Result_t
+AS_02::PCM::MXFReader::Close() const
+{
+ if ( m_Reader && m_Reader->m_File.IsOpen() )
+ {
+ m_Reader->Close();
+ return RESULT_OK;
+ }
+
+ return RESULT_INIT;
}
// Reads a frame of essence from the MXF file. If the optional AESEncContext
}
-// Fill the struct with the values from the file's header.
-// Returns RESULT_INIT if the file is not open.
-ASDCP::Result_t
-AS_02::PCM::MXFReader::FillAudioDescriptor(ASDCP::PCM::AudioDescriptor& ADesc) const
-{
- if ( m_Reader && m_Reader->m_File.IsOpen() )
- {
- ADesc = m_Reader->m_ADesc;
- return RESULT_OK;
- }
-
- return RESULT_INIT;
-}
-
// Fill the struct with the values from the file's header.
// Returns RESULT_INIT if the file is not open.
ASDCP::Result_t
h__Writer();
public:
- ASDCP::PCM::AudioDescriptor m_ADesc;
- byte_t m_EssenceUL[SMPTE_UL_LENGTH];
- ui64_t m_KLV_start;
+ ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
+ byte_t m_EssenceUL[SMPTE_UL_LENGTH];
+ ui32_t m_BytesPerFrame;
+ ui32_t m_SamplesPerFrame;
- h__Writer(const Dictionary& d) : AS_02::h__AS02Writer(d), m_KLV_start(0){
+ h__Writer(const Dictionary& d) : AS_02::h__AS02Writer(d), m_WaveAudioDescriptor(0), m_BytesPerFrame(0), m_SamplesPerFrame(0)
+ {
memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
-
}
virtual ~h__Writer(){}
- Result_t OpenWrite(const char*, ui32_t HeaderSize);
- Result_t SetSourceStream(const ASDCP::PCM::AudioDescriptor&);
+ Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
+ ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size);
+ Result_t SetSourceStream(const ASDCP::Rational&);
Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
-
Result_t Finalize();
-
- //void AddSourceClip(const MXF::Rational& EditRate, ui32_t TCFrameRate,
- // const std::string& TrackName, const UL& EssenceUL,
- // const UL& DataDefinition, const std::string& PackageLabel);
- //void AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
- // const std::string& TrackName, const UL& DataDefinition,
- // const std::string& PackageLabel);
- //void AddEssenceDescriptor(const UL& WrappingUL);
- //Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
-
- ////new method to create BodyPartition for essence and index
- //Result_t CreateBodyPartPair();
- ////new method to finalize BodyPartion(index)
- //Result_t CompleteIndexBodyPart();
-
- // reimplement these functions in AS_02_PCM to support modifications for AS-02
- // Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,
- // const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC);
- // Result_t WriteAS02Footer();
-
};
// Open the file for writing. The file must not exist. Returns error if
// the operation cannot be completed.
ASDCP::Result_t
-AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
+AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ASDCP::MXF::FileDescriptor* essence_descriptor,
+ ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size)
{
+ assert(essence_descriptor);
+
+ if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_WaveAudioDescriptor)) )
+ {
+ DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
+ essence_descriptor->Dump();
+ return RESULT_AS02_FORMAT;
+ }
+
+ m_WaveAudioDescriptor = reinterpret_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
+
if ( ! m_State.Test_BEGIN() )
- return RESULT_STATE;
+ {
+ return RESULT_STATE;
+ }
- Result_t result = m_File.OpenWrite(filename);
+ Result_t result = m_File.OpenWrite(filename.c_str());
- if ( ASDCP_SUCCESS(result) )
+ if ( KM_SUCCESS(result) )
{
- m_HeaderSize = HeaderSize;
- m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
+ m_HeaderSize = header_size;
+ m_EssenceDescriptor = essence_descriptor;
+ m_WaveAudioDescriptor->SampleRate = m_WaveAudioDescriptor->AudioSamplingRate;
+
+ ASDCP::MXF::InterchangeObject_list_t::iterator i;
+ for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
+ {
+ if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_AudioChannelLabelSubDescriptor))
+ && (*i)->GetUL() != UL(m_Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
+ && (*i)->GetUL() != UL(m_Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
+ {
+ DefaultLogSink().Error("Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
+ (*i)->Dump();
+ }
+
+ m_EssenceSubDescriptorList.push_back(*i);
+ GenRandomValue((*i)->InstanceUID);
+ m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
+ *i = 0; // parent will only free the ones we don't keep
+ }
+
result = m_State.Goto_INIT();
}
// Automatically sets the MXF file's metadata from the WAV parser info.
ASDCP::Result_t
-AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::PCM::AudioDescriptor& ADesc)
+AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::Rational& edit_rate)
{
if ( ! m_State.Test_INIT() )
- return RESULT_STATE;
-
- if ( ADesc.EditRate != EditRate_24
- && ADesc.EditRate != EditRate_25
- && ADesc.EditRate != EditRate_30
- && ADesc.EditRate != EditRate_48
- && ADesc.EditRate != EditRate_50
- && ADesc.EditRate != EditRate_60
- && ADesc.EditRate != EditRate_23_98 )
{
- DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
- ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
- return RESULT_RAW_FORMAT;
+ return RESULT_STATE;
}
- if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
- {
- DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
- ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
- return RESULT_RAW_FORMAT;
- }
+ fprintf(stderr, "edit_rate=%d/%d\n", edit_rate.Numerator, edit_rate.Denominator);
- assert(m_Dict);
- m_ADesc = ADesc;
+ memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssenceClip), SMPTE_UL_LENGTH);
+ m_EssenceUL[15] = 1; // set the stream identifier
+ Result_t result = m_State.Goto_READY();
- Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
-
- if ( ASDCP_SUCCESS(result) )
+ if ( KM_SUCCESS(result) )
{
- memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
- //SMPTE 382M-2007: ByteNo. 15 - 02h - Wave Clip-Wrapped Element
- m_EssenceUL[SMPTE_UL_LENGTH-2] = 2; // 02h - Wave Clip-Wrapped Element
- m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
- result = m_State.Goto_READY();
- }
+ assert(m_WaveAudioDescriptor);
+ m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*m_WaveAudioDescriptor, edit_rate);
+ m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*m_WaveAudioDescriptor, edit_rate);
+ m_WaveAudioDescriptor->ContainerDuration = 0;
- if ( ASDCP_SUCCESS(result) )
- {
- ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98 ) ? 24 : m_ADesc.EditRate.Numerator;
+ fprintf(stderr, "m_BytesPerFrame=%d, m_SamplesPerFrame=%d\n", m_BytesPerFrame, m_SamplesPerFrame);
result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
- m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
+ m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate), m_BytesPerFrame);
}
return result;
//
//
ASDCP::Result_t
-AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
+AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
HMACContext* HMAC)
{
+ if ( frame_buf.Size() == 0 )
+ {
+ DefaultLogSink().Error("The frame buffer size is zero.\n");
+ return RESULT_PARAM;
+ }
+
+ if ( frame_buf.Size() % m_BytesPerFrame != 0 )
+ {
+ DefaultLogSink().Error("The frame buffer does not contain an integral number of sample sets.\n");
+ return RESULT_AS02_FORMAT;
+ }
+
Result_t result = RESULT_OK;
if ( m_State.Test_READY() )
- result = m_State.Goto_RUNNING(); // first time through
+ {
+ result = m_State.Goto_RUNNING(); // first time through
+ }
- if ( ASDCP_SUCCESS(result) )
- result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
+ if ( KM_SUCCESS(result) && ! HasOpenClip() )
+ {
+ result = StartClip(m_EssenceUL, Ctx, HMAC);
+ }
- if ( ASDCP_SUCCESS(result) )
- m_FramesWritten++;
+ if ( KM_SUCCESS(result) )
+ {
+ result = WriteClipBlock(frame_buf);
+ }
+
+ if ( KM_SUCCESS(result) )
+ {
+ m_FramesWritten++;
+ }
return result;
}
m_State.Goto_FINAL();
- return WriteAS02Footer();
+ Result_t result = FinalizeClip(m_BytesPerFrame);
+
+ if ( KM_SUCCESS(result) )
+ {
+ fprintf(stderr, "m_FramesWritten=%d, m_SamplesPerFrame=%d\n", m_FramesWritten, m_SamplesPerFrame);
+ m_FramesWritten = m_FramesWritten * m_SamplesPerFrame;
+ WriteAS02Footer();
+ }
+
+ return result;
}
// Open the file for writing. The file must not exist. Returns error if
// the operation cannot be completed.
ASDCP::Result_t
-AS_02::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
- const ASDCP::PCM::AudioDescriptor& ADesc, ui32_t HeaderSize)
+AS_02::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
+ ASDCP::MXF::FileDescriptor* essence_descriptor,
+ ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+ const ASDCP::Rational& edit_rate, ui32_t header_size)
{
+ if ( essence_descriptor == 0 )
+ {
+ DefaultLogSink().Error("Essence descriptor object required.\n");
+ return RESULT_PARAM;
+ }
+
+ if ( Info.EncryptedEssence )
+ {
+ DefaultLogSink().Error("Encryption not supported for ST 382 clip-wrap.\n");
+ return Kumu::RESULT_NOTIMPL;
+ }
+
m_Writer = new h__Writer(DefaultSMPTEDict());
m_Writer->m_Info = Info;
- Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
+ Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, header_size);
- if ( ASDCP_SUCCESS(result) )
- result = m_Writer->SetSourceStream(ADesc);
+ if ( KM_SUCCESS(result) )
+ result = m_Writer->SetSourceStream(edit_rate);
if ( ASDCP_FAILURE(result) )
m_Writer.release();
/*
- Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+John Hurst
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*! \file AS_02_internal.h
\version $Id: AS_02_internal.h ***
void default_md_object_init();
-
- //
- class AS02IndexWriter : public ASDCP::MXF::Partition
- {
- ASDCP::MXF::IndexTableSegment* m_CurrentSegment;
- ui32_t m_BytesPerEditUnit;
- ASDCP::MXF::Rational m_EditRate;
-
- KM_NO_COPY_CONSTRUCT(AS02IndexWriter);
- AS02IndexWriter();
-
- public:
- const ASDCP::Dictionary*& m_Dict;
- Kumu::fpos_t m_ECOffset;
- ASDCP::IPrimerLookup* m_Lookup;
+ namespace MXF
+ {
+ //
+ class AS02IndexWriter : public ASDCP::MXF::Partition
+ {
+ ASDCP::MXF::IndexTableSegment* m_CurrentSegment;
+ ui32_t m_BytesPerEditUnit;
+ ASDCP::MXF::Rational m_EditRate;
+
+ KM_NO_COPY_CONSTRUCT(AS02IndexWriter);
+ AS02IndexWriter();
+
+ public:
+ const ASDCP::Dictionary*& m_Dict;
+ ASDCP::IPrimerLookup* m_Lookup;
- AS02IndexWriter(const ASDCP::Dictionary*&);
- virtual ~AS02IndexWriter();
+ AS02IndexWriter(const ASDCP::Dictionary*&);
+ virtual ~AS02IndexWriter();
- Result_t WriteToFile(Kumu::FileWriter& Writer);
- void ResetCBR(Kumu::fpos_t offset);
- void Dump(FILE* = 0);
+ Result_t WriteToFile(Kumu::FileWriter& Writer);
+ void ResetCBR(Kumu::fpos_t offset);
+ void Dump(FILE* = 0);
- ui32_t GetDuration() const;
- void PushIndexEntry(const ASDCP::MXF::IndexTableSegment::IndexEntry&);
- void SetIndexParamsCBR(ASDCP::IPrimerLookup* lookup, ui32_t size, const ASDCP::Rational& Rate);
- void SetIndexParamsVBR(ASDCP::IPrimerLookup* lookup, const ASDCP::Rational& Rate, Kumu::fpos_t offset);
- };
+ ui32_t GetDuration() const;
+ void PushIndexEntry(const ASDCP::MXF::IndexTableSegment::IndexEntry&);
+ void SetIndexParamsCBR(ASDCP::IPrimerLookup* lookup, ui32_t size, const ASDCP::Rational& Rate);
+ void SetIndexParamsVBR(ASDCP::IPrimerLookup* lookup, const ASDCP::Rational& Rate, Kumu::fpos_t offset);
+ };
+ }
//
class h__AS02Reader : public ASDCP::MXF::TrackFileReader<ASDCP::MXF::OP1aHeader, AS_02::MXF::AS02IndexReader>
h__AS02Reader();
public:
- // Partition *m_pCurrentBodyPartition;
- // ui64_t m_EssenceStart;
- // std::vector<Partition*> m_BodyPartList;
- // ui32_t m_start_pos;
-
h__AS02Reader(const ASDCP::Dictionary&);
virtual ~h__AS02Reader();
Result_t OpenMXFRead(const char* filename);
+ // USE FRAME WRAPPING...
Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
const byte_t* EssenceUL, ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC);
- Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset);
+ // OR CLIP WRAPPING...
+ // clip wrapping is handled directly by the essence-specific classes
+ // Result_t ReadyClip(const ui32_t& FrameNum, const byte_t* EssenceUL, ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC, ui64_t& position);
+ /// Result_t ReadClipBlock(ASDCP::FrameBuffer& FrameBuf, const ui32_t& read_size);
+
+ // NOT BOTH!
};
//
h__AS02Writer();
public:
- AS02IndexWriter m_IndexWriter;
- ui32_t m_PartitionSpace; // edit units per partition
- IndexStrategy_t m_IndexStrategy; //Shim parameter index_strategy_frame/clip
+ ui64_t m_ECStart; // offset of the first essence element
+ ui64_t m_ClipStart; // state variable for clip-wrap-in-progress
+ ui32_t m_PartitionSpace; // edit units per partition
+ AS_02::MXF::AS02IndexWriter m_IndexWriter;
+ IndexStrategy_t m_IndexStrategy; // per SMPTE ST 2067-5
h__AS02Writer(const Dictionary&);
virtual ~h__AS02Writer();
const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate,
ui32_t TCFrameRate, ui32_t BytesPerEditUnit = 0);
+ // USE FRAME WRAPPING...
Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
AESEncContext* Ctx, HMACContext* HMAC);
+ // OR CLIP WRAPPING...
+ bool HasOpenClip() const;
+ Result_t StartClip(const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC);
+ Result_t WriteClipBlock(const ASDCP::FrameBuffer& FrameBuf);
+ Result_t FinalizeClip(ui32_t bytes_per_frame);
+
+ // NOT BOTH!
+
Result_t WriteAS02Footer();
};
JPEG 2000 stereoscopic codestream pairs
PCM audio streams
SMPTE 429-7 Timed Text XML with font and image resources
+ Proposed SMPTE Aux Data track file
+ Proposed Dolby (TM) Atmos track file
o Read essence from a plaintext or ciphertext AS-DCP file:
MPEG2 Video Elementary Stream
JPEG 2000 stereoscopic codestream pairs
PCM audio streams
SMPTE 429-7 Timed Text XML with font and image resources
+ Proposed SMPTE Aux Data track file
+ Proposed Dolby (TM) Atmos track file
o Read header metadata from an AS-DCP file
ESS_JPEG_2000_S, // the file contains one or more JPEG 2000 codestream pairs (stereoscopic)
ESS_DCDATA_UNKNOWN, // the file contains one or more D-Cinema Data bytestreams
ESS_DCDATA_DOLBY_ATMOS, // the file contains one or more DolbyATMOS bytestreams
+ ESS_MAX
};
// Determine the type of essence contained in the given MXF file. RESULT_OK
{
LS_MXF_UNKNOWN,
LS_MXF_INTEROP,
- LS_MXF_SMPTE
+ LS_MXF_SMPTE,
+ LS_MAX
};
//
CF_CFG_3, // 7.1 (SDDS) with optional HI/VI
CF_CFG_4, // Wild Track Format
CF_CFG_5, // 7.1 DS with optional HI/VI
+ CF_MAX
};
struct AudioDescriptor
if ( ASDCP_SUCCESS(result) )
{
- ui32_t TCFrameRate = ( m_PDesc.EditRate == EditRate_23_98 ) ? 24 : m_PDesc.EditRate.Numerator;
-
result = WriteASDCPHeader(label, UL(m_Dict->ul(MDD_JPEG_2000Wrapping)),
PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
- LocalEditRate, TCFrameRate);
+ LocalEditRate, derive_timecode_rate_from_edit_rate(m_PDesc.EditRate));
}
return result;
if ( ASDCP_SUCCESS(result) )
{
- ui32_t TCFrameRate = ( m_VDesc.EditRate == EditRate_23_98 ) ? 24 : m_VDesc.EditRate.Numerator;
-
result = WriteASDCPHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrapping)),
PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
- m_VDesc.EditRate, TCFrameRate);
+ m_VDesc.EditRate, derive_timecode_rate_from_edit_rate(m_VDesc.EditRate));
}
return result;
if ( ASDCP_SUCCESS(result) )
{
- ui32_t TCFrameRate = m_ADesc.EditRate.Numerator;
-
- if ( m_ADesc.EditRate == EditRate_23_98 )
- TCFrameRate = 24;
- else if ( m_ADesc.EditRate == EditRate_18 )
- TCFrameRate = 18;
- else if ( m_ADesc.EditRate == EditRate_22 )
- TCFrameRate = 22;
-
result = WriteASDCPHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
- m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
+ m_ADesc.EditRate, derive_timecode_rate_from_edit_rate(m_ADesc.EditRate),
+ calc_CBR_frame_size(m_Info, m_ADesc));
}
return result;
if ( ASDCP_SUCCESS(result) )
{
InitHeader();
- AddDMSegment(m_TDesc.EditRate, 24, TIMED_TEXT_DEF_LABEL,
+ // timecode rate and essence rate are the same
+ AddDMSegment(m_TDesc.EditRate, m_TDesc.EditRate, derive_timecode_rate_from_edit_rate(m_TDesc.EditRate), TIMED_TEXT_DEF_LABEL,
UL(m_Dict->ul(MDD_DataDataDef)), TIMED_TEXT_PACKAGE_LABEL);
AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrapping)));
//------------------------------------------------------------------------------------------
//
+ ui32_t derive_timecode_rate_from_edit_rate(const ASDCP::Rational& edit_rate);
+
Result_t MD_to_WriterInfo(MXF::Identification*, WriterInfo&);
Result_t MD_to_CryptoInfo(MXF::CryptographicContext*, WriterInfo&, const Dictionary&);
void AddDMScrypt(Partition& HeaderPart, SourcePackage& Package,
WriterInfo& Descr, const UL& WrappingUL, const Dictionary*& Dict);
- Result_t Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
+ Result_t Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict,
const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf,
ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
AESEncContext* Ctx, HMACContext* HMAC);
-
//
class KLReader : public ASDCP::KLVPacket
{
}
// positions file before reading
- Result_t ReadEKLVFrame(const ASDCP::MXF::Partition& CurrentPartition,
+ // allows external control of index offset
+ Result_t ReadEKLVFrame(const ui64_t& body_offset,
ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
{
return RESULT_RANGE;
}
- // get frame position and go read the frame's key and length
- Kumu::fpos_t FilePosition = CurrentPartition.BodyOffset + TmpEntry.StreamOffset;
+ // get relative frame position, apply offset and go read the frame's key and length
+ Kumu::fpos_t FilePosition = body_offset + TmpEntry.StreamOffset;
Result_t result = RESULT_OK;
if ( FilePosition != m_LastPosition )
return result;
}
+ // positions file before reading
+ // assumes "processed" index entries have absolute positions
+ Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
+ const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
+ {
+ // look up frame index node
+ IndexTableSegment::IndexEntry TmpEntry;
+
+ if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
+ {
+ DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
+ return RESULT_RANGE;
+ }
+
+ // get absolute frame position and go read the frame's key and length
+ Result_t result = RESULT_OK;
+
+ if ( TmpEntry.StreamOffset != m_LastPosition )
+ {
+ m_LastPosition = TmpEntry.StreamOffset;
+ result = m_File.Seek(TmpEntry.StreamOffset);
+ }
+
+ if ( KM_SUCCESS(result) )
+ result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
+
+ return result;
+ }
+
// reads from current position
Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
{
assert(m_Dict);
- return Read_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_LastPosition, m_CtFrameBuf,
+ return Read_EKLV_Packet(m_File, *m_Dict, m_Info, m_LastPosition, m_CtFrameBuf,
FrameNum, SequenceNum, FrameBuf, EssenceUL, Ctx, HMAC);
}
// Get the position of a frame from a track file
- Result_t LocateFrame(const ASDCP::MXF::Partition& CurrentPartition,
+ Result_t LocateFrame(const ui64_t& body_offset,
ui32_t FrameNum, Kumu::fpos_t& streamOffset,
i8_t& temporalOffset, i8_t& keyFrameOffset)
{
}
// get frame position, temporal offset, and key frame ofset
- streamOffset = CurrentPartition.BodyOffset + TmpEntry.StreamOffset;
+ streamOffset = body_offset + TmpEntry.StreamOffset;
temporalOffset = TmpEntry.TemporalOffset;
keyFrameOffset = TmpEntry.KeyFrameOffset;
template <class PackageT, class ClipT>
TrackSet<ClipT>
CreateTrackAndSequence(OP1aHeader& Header, PackageT& Package, const std::string TrackName,
- const MXF::Rational& EditRate, const UL& Definition, ui32_t TrackID, const Dictionary*& Dict)
+ const MXF::Rational& clip_edit_rate, const UL& Definition, ui32_t TrackID, const Dictionary*& Dict)
{
TrackSet<ClipT> NewTrack;
NewTrack.Track = new Track(Dict);
Header.AddChildObject(NewTrack.Track);
- NewTrack.Track->EditRate = EditRate;
+ NewTrack.Track->EditRate = clip_edit_rate;
Package.Tracks.push_back(NewTrack.Track->InstanceUID);
NewTrack.Track->TrackID = TrackID;
NewTrack.Track->TrackName = TrackName.c_str();
template <class PackageT>
TrackSet<TimecodeComponent>
CreateTimecodeTrack(OP1aHeader& Header, PackageT& Package,
- const MXF::Rational& EditRate, ui32_t TCFrameRate, ui64_t TCStart, const Dictionary*& Dict)
+ const MXF::Rational& tc_edit_rate, ui32_t tc_frame_rate, ui64_t TCStart, const Dictionary*& Dict)
{
assert(Dict);
UL TCUL(Dict->ul(MDD_TimecodeDataDef));
- TrackSet<TimecodeComponent> NewTrack = CreateTrackAndSequence<PackageT, TimecodeComponent>(Header, Package, "Timecode Track", EditRate, TCUL, 1, Dict);
+ TrackSet<TimecodeComponent> NewTrack =
+ CreateTrackAndSequence<PackageT, TimecodeComponent>(Header, Package, "Timecode Track",
+ tc_edit_rate, TCUL, 1, Dict);
NewTrack.Clip = new TimecodeComponent(Dict);
Header.AddChildObject(NewTrack.Clip);
NewTrack.Sequence->StructuralComponents.push_back(NewTrack.Clip->InstanceUID);
- NewTrack.Clip->RoundedTimecodeBase = TCFrameRate;
+ NewTrack.Clip->RoundedTimecodeBase = tc_frame_rate;
NewTrack.Clip->StartTimecode = TCStart;
NewTrack.Clip->DataDefinition = TCUL;
ST_READY, // ready to write frames
ST_RUNNING, // one or more frames written
ST_FINAL, // index written, file closed
+ ST_MAX
};
// implementation of h__WriterState class Goto_* methods
}
//
- void AddSourceClip(const MXF::Rational& EditRate, ui32_t TCFrameRate,
+ void AddSourceClip(const MXF::Rational& clip_edit_rate,
+ const MXF::Rational& tc_edit_rate, ui32_t TCFrameRate,
const std::string& TrackName, const UL& EssenceUL,
const UL& DataDefinition, const std::string& PackageLabel)
{
TrackSet<TimecodeComponent> MPTCTrack =
CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
- EditRate, TCFrameRate, 0, m_Dict);
+ tc_edit_rate, TCFrameRate, 0, m_Dict);
m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration));
m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration));
TrackSet<SourceClip> MPTrack =
CreateTrackAndSequence<MaterialPackage, SourceClip>(m_HeaderPart, *m_MaterialPackage,
- TrackName, EditRate, DataDefinition,
+ TrackName, clip_edit_rate, DataDefinition,
2, m_Dict);
m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration));
TrackSet<TimecodeComponent> FPTCTrack =
CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
- EditRate, TCFrameRate,
+ tc_edit_rate, TCFrameRate,
ui64_C(3600) * TCFrameRate, m_Dict);
m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration));
m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration));
TrackSet<SourceClip> FPTrack =
CreateTrackAndSequence<SourcePackage, SourceClip>(m_HeaderPart, *m_FilePackage,
- TrackName, EditRate, DataDefinition,
+ TrackName, clip_edit_rate, DataDefinition,
2, m_Dict);
m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration));
}
//
- void AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
+ void AddDMSegment(const MXF::Rational& clip_edit_rate,
+ const MXF::Rational& tc_edit_rate, ui32_t tc_frame_rate,
const std::string& TrackName, const UL& DataDefinition,
const std::string& PackageLabel)
{
TrackSet<TimecodeComponent> MPTCTrack =
CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
- EditRate, TCFrameRate, 0, m_Dict);
+ tc_edit_rate, tc_frame_rate, 0, m_Dict);
m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration));
m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration));
TrackSet<DMSegment> MPTrack =
CreateTrackAndSequence<MaterialPackage, DMSegment>(m_HeaderPart, *m_MaterialPackage,
- TrackName, EditRate, DataDefinition,
+ TrackName, clip_edit_rate, DataDefinition,
2, m_Dict);
m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration));
TrackSet<TimecodeComponent> FPTCTrack =
CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
- EditRate, TCFrameRate,
- ui64_C(3600) * TCFrameRate, m_Dict);
+ clip_edit_rate, tc_frame_rate,
+ ui64_C(3600) * tc_frame_rate, m_Dict);
m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration));
m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration));
TrackSet<DMSegment> FPTrack =
CreateTrackAndSequence<SourcePackage, DMSegment>(m_HeaderPart, *m_FilePackage,
- TrackName, EditRate, DataDefinition,
+ TrackName, clip_edit_rate, DataDefinition,
2, m_Dict);
m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration));
m_HeaderPart.AddChildObject(FPTrack.Clip);
FPTrack.Sequence->StructuralComponents.push_back(FPTrack.Clip->InstanceUID);
FPTrack.Clip->DataDefinition = DataDefinition;
- FPTrack.Clip->EventComment = "D-Cinema Timed Text";
+ FPTrack.Clip->EventComment = "ST 429-5 Timed Text";
m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration));
m_EssenceDescriptor->LinkedTrackID = FPTrack.Track->TrackID;
//
ASDCP::MXF::IndexTableSegment::IndexTableSegment(const Dictionary*& d) :
- InterchangeObject(d), m_Dict(d),
+ InterchangeObject(d), m_Dict(d), RtFileOffset(0), RtEntryOffset(0),
IndexStartPosition(0), IndexDuration(0), EditUnitByteCount(0),
IndexSID(129), BodySID(1), SliceCount(0), PosTableCount(0)
{
LOG_NOTICE, // application user info
LOG_ALERT, // application non-fatal or near-miss error
LOG_CRIT, // application fatal error
+ LOG_MAX
};
{ { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 319
0x03, 0x02, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00 },
{0}, false, "DCAudioChannel_VIN" },
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 320
+ 0x03, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00 },
+ {0}, false, "DCAudioSoundfield_51" },
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 321
+ 0x03, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00 },
+ {0}, false, "DCAudioSoundfield_71" },
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 322
+ 0x03, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00 },
+ {0}, false, "DCAudioSoundfield_SDS" },
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 323
+ 0x03, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00 },
+ {0}, false, "DCAudioSoundfield_61" },
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 324
+ 0x03, 0x02, 0x02, 0x05, 0x00, 0x00, 0x00, 0x00 },
+ {0}, false, "DCAudioSoundfield_M" },
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01, // 325
+ 0x0d, 0x01, 0x03, 0x01, 0x16, 0x01, 0x02, 0x00 },
+ {0}, false, "WAVEssenceClip" },
{ {0}, {0}, false, 0 }
};
MDD_DCAudioChannel_Cs, // 317
MDD_DCAudioChannel_HI, // 318
MDD_DCAudioChannel_VIN, // 319
+ MDD_DCAudioSoundfield_51, // 320
+ MDD_DCAudioSoundfield_71, // 321
+ MDD_DCAudioSoundfield_SDS, // 322
+ MDD_DCAudioSoundfield_61, // 323
+ MDD_DCAudioSoundfield_M, // 324
+ MDD_WAVEssenceClip, // 325
MDD_Max
}; // enum MDD_t
if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
{
delete object;
+
+ if ( p > end_p )
+ {
+ DefaultLogSink().Error("Fill item short read: %d.\n", p - end_p);
+ }
}
else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
{
}
+//------------------------------------------------------------------------------------------
+
+
+ASDCP::MXF::MCAConfigParser::MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
+{
+ label_map.insert(label_map_t::value_type("L", m_Dict->ul(MDD_DCAudioChannel_L)));
+ label_map.insert(label_map_t::value_type("R", m_Dict->ul(MDD_DCAudioChannel_R)));
+ label_map.insert(label_map_t::value_type("C", m_Dict->ul(MDD_DCAudioChannel_C)));
+ label_map.insert(label_map_t::value_type("LFE", m_Dict->ul(MDD_DCAudioChannel_LFE)));
+ label_map.insert(label_map_t::value_type("Ls", m_Dict->ul(MDD_DCAudioChannel_Ls)));
+ label_map.insert(label_map_t::value_type("Rs", m_Dict->ul(MDD_DCAudioChannel_Rs)));
+ label_map.insert(label_map_t::value_type("Lss", m_Dict->ul(MDD_DCAudioChannel_Lss)));
+ label_map.insert(label_map_t::value_type("Rss", m_Dict->ul(MDD_DCAudioChannel_Rss)));
+ label_map.insert(label_map_t::value_type("Lrs", m_Dict->ul(MDD_DCAudioChannel_Lrs)));
+ label_map.insert(label_map_t::value_type("Rrs", m_Dict->ul(MDD_DCAudioChannel_Rrs)));
+ label_map.insert(label_map_t::value_type("Lc", m_Dict->ul(MDD_DCAudioChannel_Lc)));
+ label_map.insert(label_map_t::value_type("Rc", m_Dict->ul(MDD_DCAudioChannel_Rc)));
+ label_map.insert(label_map_t::value_type("Cs", m_Dict->ul(MDD_DCAudioChannel_Cs)));
+ label_map.insert(label_map_t::value_type("HI", m_Dict->ul(MDD_DCAudioChannel_HI)));
+ label_map.insert(label_map_t::value_type("VIN", m_Dict->ul(MDD_DCAudioChannel_VIN)));
+ label_map.insert(label_map_t::value_type("51", m_Dict->ul(MDD_DCAudioSoundfield_51)));
+ label_map.insert(label_map_t::value_type("71", m_Dict->ul(MDD_DCAudioSoundfield_71)));
+ label_map.insert(label_map_t::value_type("SDS", m_Dict->ul(MDD_DCAudioSoundfield_SDS)));
+ label_map.insert(label_map_t::value_type("61", m_Dict->ul(MDD_DCAudioSoundfield_61)));
+ label_map.insert(label_map_t::value_type("M", m_Dict->ul(MDD_DCAudioSoundfield_M)));
+}
+
+//
+ui32_t
+ASDCP::MXF::MCAConfigParser::ChannelCount() const
+{
+ return m_ChannelCount;
+}
+
+// 51(L,R,C,LFE,Ls,Rs),HI,VIN
+bool
+ASDCP::MXF::MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
+{
+ std::string symbol_buf;
+ m_ChannelCount = 0;
+ ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0;
+ std::string::const_iterator i;
+
+ for ( i = s.begin(); i != s.end(); ++i )
+ {
+ if ( *i == '(' )
+ {
+ if ( current_soundfield != 0 )
+ {
+ fprintf(stderr, "Encountered '(', already processing a soundfield group.\n");
+ return false;
+ }
+
+ if ( symbol_buf.empty() )
+ {
+ fprintf(stderr, "Encountered '(', without leading soundfield group symbol.\n");
+ return false;
+ }
+
+ label_map_t::const_iterator i = label_map.find(symbol_buf);
+
+ if ( i == label_map.end() )
+ {
+ fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
+ return false;
+ }
+
+ if ( i->second.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
+ {
+ fprintf(stderr, "Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
+ return false;
+ }
+
+ current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(m_Dict);
+
+ GenRandomValue(current_soundfield->InstanceUID);
+ GenRandomValue(current_soundfield->MCALinkID);
+ current_soundfield->MCATagSymbol = "sg" + i->first;
+ current_soundfield->MCATagName = i->first;
+ current_soundfield->RFC5646SpokenLanguage = language;
+ current_soundfield->MCALabelDictionaryID = i->second;
+ push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
+ symbol_buf.clear();
+ }
+ else if ( *i == ')' )
+ {
+ if ( current_soundfield == 0 )
+ {
+ fprintf(stderr, "Encountered ')', not currently processing a soundfield group.\n");
+ return false;
+ }
+
+ if ( symbol_buf.empty() )
+ {
+ fprintf(stderr, "Soundfield group description contains no channels.\n");
+ return false;
+ }
+
+ label_map_t::const_iterator i = label_map.find(symbol_buf);
+
+ if ( i == label_map.end() )
+ {
+ fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
+ return false;
+ }
+
+ ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
+ new ASDCP::MXF::AudioChannelLabelSubDescriptor(m_Dict);
+
+ GenRandomValue(channel_descr->InstanceUID);
+ assert(current_soundfield);
+ channel_descr->MCALinkID = current_soundfield->MCALinkID;
+ channel_descr->MCAChannelID = m_ChannelCount++;
+ channel_descr->MCATagSymbol = "ch" + i->first;
+ channel_descr->MCATagName = i->first;
+ channel_descr->RFC5646SpokenLanguage = language;
+ channel_descr->MCALabelDictionaryID = i->second;
+ push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
+ symbol_buf.clear();
+ current_soundfield = 0;
+ }
+ else if ( *i == ',' )
+ {
+ if ( ! symbol_buf.empty() )
+ {
+ label_map_t::const_iterator i = label_map.find(symbol_buf);
+
+ if ( i == label_map.end() )
+ {
+ fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
+ return false;
+ }
+
+ if ( i->second.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
+ {
+ fprintf(stderr, "Not a channel symbol: '%s'\n", symbol_buf.c_str());
+ return false;
+ }
+
+ ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
+ new ASDCP::MXF::AudioChannelLabelSubDescriptor(m_Dict);
+
+ GenRandomValue(channel_descr->InstanceUID);
+
+ if ( current_soundfield != 0 )
+ {
+ channel_descr->MCALinkID = current_soundfield->MCALinkID;
+ }
+
+ channel_descr->MCAChannelID = m_ChannelCount++;
+ channel_descr->MCATagSymbol = "ch" + i->first;
+ channel_descr->MCATagName = i->first;
+ channel_descr->RFC5646SpokenLanguage = language;
+ channel_descr->MCALabelDictionaryID = i->second;
+ push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
+ symbol_buf.clear();
+ }
+ }
+ else if ( isalnum(*i) )
+ {
+ symbol_buf += *i;
+ }
+ else if ( ! isspace(*i) )
+ {
+ fprintf(stderr, "Unexpected character '%c'.\n", *i);
+ return false;
+ }
+ }
+
+ if ( ! symbol_buf.empty() )
+ {
+ label_map_t::const_iterator i = label_map.find(symbol_buf);
+
+ if ( i == label_map.end() )
+ {
+ fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
+ return false;
+ }
+
+ ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
+ new ASDCP::MXF::AudioChannelLabelSubDescriptor(m_Dict);
+
+ GenRandomValue(channel_descr->InstanceUID);
+
+ if ( current_soundfield != 0 )
+ {
+ channel_descr->MCALinkID = current_soundfield->MCALinkID;
+ }
+
+ channel_descr->MCAChannelID = m_ChannelCount++;
+ channel_descr->MCATagSymbol = "ch" + i->first;
+ channel_descr->MCATagName = i->first;
+ channel_descr->RFC5646SpokenLanguage = language;
+ channel_descr->MCALabelDictionaryID = i->second;
+ push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
+ }
+
+ return true;
+}
+
+
//
// end MXF.cpp
//
virtual void Dump(FILE* stream = 0);
};
+ //
+ typedef std::list<InterchangeObject*> InterchangeObject_list_t;
+
//
class Preface : public InterchangeObject
{
};
const Dictionary*& m_Dict;
+ ui64_t RtFileOffset; // not part of the MXF structure: used to manage runtime index access
+ ui64_t RtEntryOffset;
Rational IndexEditRate;
ui64_t IndexStartPosition;
virtual void SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset);
};
+ //---------------------------------------------------------------------------------
+ //
+
+ //
+ inline std::string to_lower(std::string str) {
+ std::transform(str.begin(), str.end(), str.begin(), ::tolower);
+ return str;
+ }
+
+ // ignore case when searching for audio labels
+ struct ci_comp
+ {
+ inline bool operator()(const std::string& a, const std::string& b) const {
+ return to_lower(a) < to_lower(b);
+ }
+ };
+
+ //
+ class MCAConfigParser : public InterchangeObject_list_t
+ {
+ typedef std::map<const std::string, const UL, ci_comp> label_map_t;
+ label_map_t label_map;
+ ui32_t m_ChannelCount;
+
+ const Dictionary*& m_Dict;
+
+ KM_NO_COPY_CONSTRUCT(MCAConfigParser);
+ MCAConfigParser();
+
+ public:
+ MCAConfigParser(const Dictionary*&);
+ bool DecodeString(const std::string& s, const std::string& language = "en");
+
+ // Valid only after a successful call to DecodeString
+ ui32_t ChannelCount() const;
+ };
} // namespace MXF
} // namespace ASDCP
class VersionType : public Kumu::IArchive
{
public:
- enum Release_t { RL_UNKNOWN, RL_RELEASE, RL_DEVELOPMENT, RL_PATCHED, RL_BETA, RL_PRIVATE };
+ enum Release_t { RL_UNKNOWN, RL_RELEASE, RL_DEVELOPMENT, RL_PATCHED, RL_BETA, RL_PRIVATE, RL_MAX };
ui16_t Major;
ui16_t Minor;
ui16_t Patch;
enum SplitType_t {
ST_NONE, // write all channels to a single WAV file
ST_MONO, // write each channel a separate WAV file
- ST_STEREO // write channel pairs to separate WAV files
+ ST_STEREO, // write channel pairs to separate WAV files
+ ST_MAX
};
ASDCP::Result_t
/*
-Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
+Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+John Hurst
+
All rights reserved.
Redistribution and use in source and binary forms, with or without
#include <AS_02.h>
#include <WavFileWriter.h>
+namespace ASDCP {
+ Result_t MD_to_PCM_ADesc(ASDCP::MXF::WaveAudioDescriptor* ADescObj, ASDCP::PCM::AudioDescriptor& ADesc);
+}
+
using namespace ASDCP;
const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
{
fprintf(stream, "\n\
%s (asdcplib %s)\n\n\
-Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
+Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
asdcplib may be copied only under the terms of the license found at\n\
the top of every file in the asdcplib distribution kit.\n\n\
Specify the -h (help) option for further information about %s\n\n",
bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
ui32_t picture_rate; // fps of picture when wrapping PCM
ui32_t fb_size; // size of picture frame buffer
+ Rational edit_rate; // frame buffer size for reading clip-wrapped PCM
const char* file_prefix; // filename pre for files written by the extract mode
byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
if ( ASDCP_SUCCESS(result) )
{
- JP2K::PictureDescriptor PDesc;
- Reader.FillPictureDescriptor(PDesc);
-
- frame_count = PDesc.ContainerDuration;
-
if ( Options.verbose_flag )
{
fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
- JP2K::PictureDescriptorDump(PDesc);
+ }
+
+ ASDCP::MXF::RGBAEssenceDescriptor *rgba_descriptor = 0;
+ ASDCP::MXF::CDCIEssenceDescriptor *cdci_descriptor = 0;
+
+ result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor),
+ reinterpret_cast<MXF::InterchangeObject**>(&rgba_descriptor));
+
+ if ( KM_SUCCESS(result) )
+ {
+ assert(rgba_descriptor);
+ frame_count = rgba_descriptor->ContainerDuration;
+
+ if ( Options.verbose_flag )
+ {
+ rgba_descriptor->Dump();
+ }
+ }
+ else
+ {
+ result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor),
+ reinterpret_cast<MXF::InterchangeObject**>(&cdci_descriptor));
+
+ if ( KM_SUCCESS(result) )
+ {
+ assert(cdci_descriptor);
+ frame_count = cdci_descriptor->ContainerDuration;
+
+ if ( Options.verbose_flag )
+ {
+ cdci_descriptor->Dump();
+ }
+ }
+ else
+ {
+ fprintf(stderr, "File does not contain an essence descriptor.\n");
+ frame_count = Reader.AS02IndexReader().GetDuration();
+ }
+ }
+
+ if ( frame_count == 0 )
+ {
+ frame_count = Reader.AS02IndexReader().GetDuration();
+ }
+
+ if ( frame_count == 0 )
+ {
+ fprintf(stderr, "Unable to determine file duration.\n");
+ return RESULT_FAIL;
}
}
AS_02::PCM::MXFReader Reader;
PCM::FrameBuffer FrameBuffer;
WavFileWriter OutWave;
- PCM::AudioDescriptor ADesc;
ui32_t last_frame = 0;
+ ASDCP::MXF::WaveAudioDescriptor *wave_descriptor = 0;
- Result_t result = Reader.OpenRead(Options.input_filename);
+ if ( Options.edit_rate == Rational(0,0) ) // todo, make this available to the CLI
+ {
+ Options.edit_rate = EditRate_24;
+ }
+
+ Result_t result = Reader.OpenRead(Options.input_filename, Options.edit_rate);
if ( ASDCP_SUCCESS(result) )
{
- Reader.FillAudioDescriptor(ADesc);
- ADesc.EditRate = Rational(1, 1);
- FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
+ if ( Options.verbose_flag )
+ {
+ fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
+ }
+
+ result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_WaveAudioDescriptor),
+ reinterpret_cast<MXF::InterchangeObject**>(&wave_descriptor));
+
+ if ( KM_SUCCESS(result) )
+ {
+ assert(wave_descriptor);
+ last_frame = wave_descriptor->ContainerDuration;
+
+ if ( Options.verbose_flag )
+ {
+ wave_descriptor->Dump();
+ }
+ }
+ else
+ {
+ fprintf(stderr, "File does not contain an essence descriptor.\n");
+ last_frame = Reader.AS02IndexReader().GetDuration();
+ }
+
+ if ( last_frame == 0 )
+ {
+ fprintf(stderr, "Unable to determine file duration.\n");
+ return RESULT_FAIL;
+ }
+
+ FrameBuffer.Capacity(AS_02::MXF::CalcFrameBufferSize(*wave_descriptor, Options.edit_rate));
if ( Options.verbose_flag )
- PCM::AudioDescriptorDump(ADesc);
+ {
+ wave_descriptor->Dump();
+ }
}
if ( ASDCP_SUCCESS(result) )
{
- last_frame = ADesc.ContainerDuration;
-
if ( Options.duration > 0 && Options.duration < last_frame )
last_frame = Options.duration;
if ( Options.start_frame > 0 )
{
- if ( Options.start_frame > ADesc.ContainerDuration )
+ if ( Options.start_frame > last_frame )
{
fprintf(stderr, "Start value greater than file duration.\n");
return RESULT_FAIL;
}
- last_frame = Kumu::xmin(Options.start_frame + last_frame, ADesc.ContainerDuration);
+ last_frame = Kumu::xmin(Options.start_frame + last_frame, last_frame);
}
- ADesc.ContainerDuration = last_frame - Options.start_frame;
- OutWave.OpenWrite(ADesc, Options.file_prefix,
- ( Options.split_wav ? WavFileWriter::ST_STEREO :
- ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
+ last_frame = last_frame - Options.start_frame;
+
+ PCM::AudioDescriptor ADesc;
+
+ result = MD_to_PCM_ADesc(wave_descriptor, ADesc);
+
+ if ( ASDCP_SUCCESS(result) )
+ {
+ ADesc.ContainerDuration = last_frame;
+ ADesc.EditRate = Options.edit_rate;
+
+ result = OutWave.OpenWrite(ADesc, Options.file_prefix,
+ ( Options.split_wav ? WavFileWriter::ST_STEREO :
+ ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
+ }
}
if ( ASDCP_SUCCESS(result) && Options.key_flag )
/*
-Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
+Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+John Hurst
+
All rights reserved.
Redistribution and use in source and binary forms, with or without
using namespace ASDCP;
const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
+const ASDCP::Dictionary *g_dict = 0;
const char*
return buf;
}
+
+
//------------------------------------------------------------------------------------------
//
// command line option parser class
{
fprintf(stream, "\n\
%s (asdcplib %s)\n\n\
-Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
+Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
asdcplib may be copied only under the terms of the license found at\n\
the top of every file in the asdcplib distribution kit.\n\n\
Specify the -h (help) option for further information about %s\n\n",
\n\
%s [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
[-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
- [-M] [-p <n>/<d>] [-s <seconds>] [-v] [-W]\n\
+ [-M] [-m <expr>] [-r <n>/<d>] [-s <seconds>] [-v] [-W]\n\
[-z|-Z] <input-file>+ <output-file>\n\n",
PROGRAM_NAME, PROGRAM_NAME);
-j <key-id-str> - Write key ID instead of creating a random value\n\
-k <key-string> - Use key for ciphertext operations\n\
-M - Do not create HMAC values when writing\n\
+ -m <expr> - Write MCA labels using <expr>. Example:\n\
+ 51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
-a <UUID> - Specify the Asset ID of the file\n\
-b <buffer-size> - Specify size in bytes of picture frame buffer\n\
Defaults to 4,194,304 (4MB)\n\
-d <duration> - Number of frames to process, default all\n\
-f <start-frame> - Starting frame number, default 0\n\
- -p <n>/<d> - Edit Rate of the output file. 24/1 is the default\n\
+ -r <n>/<d> - Edit Rate of the output file. 24/1 is the default\n\
-s <seconds> - Duration of a frame-wrapped partition (default 60)\n\
-v - Verbose, prints informative messages to stderr\n\
-W - Read input file only, do not write source file\n\
o All option arguments must be separated from the option by whitespace.\n\n");
}
+//
+static ASDCP::Rational
+decode_rational(const char* str_rat)
+{
+ assert(str_rat);
+ ui32_t Num = atoi(str_rat);
+ ui32_t Den = 0;
+
+ const char* den_str = strrchr(str_rat, ' ');
+ if ( den_str != 0 )
+ Den = atoi(den_str+1);
+
+ return ASDCP::Rational(Num, Den);
+}
//
//
class CommandOptions
byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
std::string out_file; //
- bool show_ul_values; /// if true, dump the UL table before going tp work.
+ bool show_ul_values_flag; /// if true, dump the UL table before going tp work.
Kumu::PathList_t filenames; // list of filenames to be processed
UL channel_assignment;
+ ASDCP::MXF::MCAConfigParser mca_config;
//new attributes for AS-02 support
AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
no_write_flag(false), version_flag(false), help_flag(false), start_frame(0),
- duration(0xffffffff), j2c_pedantic(true), edit_rate(30,1), fb_size(FRAME_BUFFER_SIZE),
- show_ul_values(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60)
+ duration(0xffffffff), j2c_pedantic(true), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
+ show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
+ mca_config(g_dict)
{
memset(key_value, 0, KeyLen);
memset(key_id_value, 0, UUIDlen);
case 'M': write_hmac = false; break;
- case 'p':
- TEST_EXTRA_ARG(i, 'p');
- /// TODO: VERY BROKEN, WANT RATIONAL
- edit_rate.Numerator = abs(atoi(argv[i]));
- edit_rate.Denominator = 1;
+ case 'm':
+ TEST_EXTRA_ARG(i, 'm');
+ if ( ! mca_config.DecodeString(argv[i]) )
+ {
+ return;
+ }
+ break;
+
+ case 'r':
+ TEST_EXTRA_ARG(i, 'r');
+ edit_rate = decode_rational(argv[i]);
break;
case 's':
partition_space = abs(atoi(argv[i]));
break;
+ case 'u': show_ul_values_flag = true; break;
case 'V': version_flag = true; break;
case 'v': verbose_flag = true; break;
case 'W': no_write_flag = true; break;
//------------------------------------------------------------------------------------------
// JPEG 2000 essence
+namespace ASDCP {
+ Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
+ const ASDCP::Dictionary& dict,
+ ASDCP::MXF::RGBAEssenceDescriptor *EssenceDescriptor,
+ ASDCP::MXF::JPEG2000PictureSubDescriptor *EssenceSubDescriptor);
+
+ Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
+}
+
// Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
// Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
//
HMACContext* HMAC = 0;
AS_02::JP2K::MXFWriter Writer;
JP2K::FrameBuffer FrameBuffer(Options.fb_size);
- JP2K::PictureDescriptor PDesc;
JP2K::SequenceParser Parser;
byte_t IV_buf[CBC_BLOCK_SIZE];
Kumu::FortunaRNG RNG;
+ ASDCP::MXF::RGBAEssenceDescriptor *essence_descriptor = 0;
+ ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
// set up essence parser
Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
// set up MXF writer
if ( ASDCP_SUCCESS(result) )
{
+ ASDCP::JP2K::PictureDescriptor PDesc;
Parser.FillPictureDescriptor(PDesc);
PDesc.EditRate = Options.edit_rate;
fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
JP2K::PictureDescriptorDump(PDesc);
}
+
+ // TODO: optionally set up CDCIEssenceDescriptor
+ essence_descriptor = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
+ essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
+
+ result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict, essence_descriptor,
+ reinterpret_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
+
+ /// TODO: set with magic or some such thing
+ essence_descriptor->ComponentMaxRef = 4095;
+ essence_descriptor->ComponentMinRef = 0;
}
if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
}
if ( ASDCP_SUCCESS(result) )
- result = Writer.OpenWrite(Options.out_file.c_str(), Info, PDesc, Options.index_strategy, Options.partition_space);
+ {
+ result = Writer.OpenWrite(Options.out_file, Info,
+ static_cast<ASDCP::MXF::FileDescriptor*>(essence_descriptor),
+ essence_sub_descriptors,
+ Options.edit_rate, 16384, Options.index_strategy, Options.partition_space);
+ // TODO: make 16384 part of CommandOptions
+ }
}
if ( ASDCP_SUCCESS(result) )
PCMParserList Parser;
AS_02::PCM::MXFWriter Writer;
PCM::FrameBuffer FrameBuffer;
- PCM::AudioDescriptor ADesc;
byte_t IV_buf[CBC_BLOCK_SIZE];
Kumu::FortunaRNG RNG;
+ ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
// set up essence parser
- Result_t result = Parser.OpenRead(Options.filenames, Rational(1, 1));
+ Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
// set up MXF writer
if ( ASDCP_SUCCESS(result) )
{
+ ASDCP::PCM::AudioDescriptor ADesc;
Parser.FillAudioDescriptor(ADesc);
ADesc.EditRate = Options.edit_rate;
fputs("AudioDescriptor:\n", stderr);
PCM::AudioDescriptorDump(ADesc);
}
+
+ essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
+
+ result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
+
+ if ( Options.mca_config.empty() )
+ {
+ essence_descriptor->ChannelAssignment = Options.channel_assignment;
+ }
+ else
+ {
+ if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
+ {
+ fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
+ Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
+ return RESULT_FAIL;
+ }
+
+ // this is the d-cinema MCA label, what is the one for IMF?
+ essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
+ }
}
if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
}
if ( ASDCP_SUCCESS(result) )
- result = Writer.OpenWrite(Options.out_file.c_str(), Info, ADesc);
-
- if ( ASDCP_SUCCESS(result) && Options.channel_assignment.HasValue() )
{
- MXF::WaveAudioDescriptor *descriptor = 0;
- Writer.OP1aHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_WaveAudioDescriptor),
- reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
- descriptor->ChannelAssignment = Options.channel_assignment;
+ result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
+ Options.mca_config, Options.edit_rate);
}
}
{
Result_t result = RESULT_OK;
char str_buf[64];
+ g_dict = &ASDCP::DefaultSMPTEDict();
+
CommandOptions Options(argc, argv);
if ( Options.version_flag )
if ( Options.help_flag )
usage();
- if ( Options.version_flag || Options.help_flag )
+ if ( Options.show_ul_values_flag )
+ {
+ g_dict->Dump(stdout);
+ }
+
+ if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
return 0;
if ( Options.error_flag )
return 3;
}
- if ( Options.show_ul_values )
- {
- DefaultSMPTEDict().Dump(stdout);
- }
-
EssenceType_t EssenceType;
result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
/*
-Copyright (c) 2003-2012, John Hurst
+Copyright (c) 2003-2013, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
0x0e, 0x16, 0x02, 0x02, 0x03, 0x01, 0x01, 0x03
};
+const ASDCP::Dictionary *g_dict = 0;
+
//------------------------------------------------------------------------------------------
//
// command line option parser class
\n\
%s [-3] [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
[-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
- [-l <label>] [-L] [-M] [-p <frame-rate>] [-s] [-v] [-W]\n\
- [-z|-Z] <input-file>+ <output-file>\n\n",
+ [-l <label>] [-L] [-M] [-m <expr>] [-p <frame-rate>] [-s] [-v]\n\
+ [-W] [-z|-Z] <input-file>+ <output-file>\n\n",
PROGRAM_NAME, PROGRAM_NAME);
fprintf(stream, "\
-j <key-id-str> - Write key ID instead of creating a random value\n\
-k <key-string> - Use key for ciphertext operations\n\
-M - Do not create HMAC values when writing\n\
+ -m <expr> - Write MCA labels using <expr>. Example:\n\
+ 51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
-a <UUID> - Specify the Asset ID of the file\n\
-b <buffer-size> - Specify size in bytes of picture frame buffer\n\
Defaults to 4,194,304 (4MB)\n\
byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
std::string out_file; //
- bool show_ul_values; /// if true, dump the UL table before going tp work.
+ bool show_ul_values_flag; /// if true, dump the UL table before going tp work.
Kumu::PathList_t filenames; // list of filenames to be processed
UL channel_assignment;
UL picture_coding;
ui32_t ffoa; /// first frame of action for atmos wrapping
ui32_t max_channel_count; /// max channel count for atmos wrapping
ui32_t max_object_count; /// max object count for atmos wrapping
-
+ ASDCP::MXF::MCAConfigParser mca_config;
//
Rational PictureRate()
channel_fmt(PCM::CF_NONE),
ffoa(0), max_channel_count(10), max_object_count(118), // hard-coded sample atmos properties
dolby_atmos_sync_flag(false),
- show_ul_values(false)
+ show_ul_values_flag(false),
+ mca_config(g_dict)
{
memset(key_value, 0, KeyLen);
memset(key_id_value, 0, UUIDlen);
case 'L': use_smpte_labels = true; break;
case 'M': write_hmac = false; break;
+ case 'm':
+ TEST_EXTRA_ARG(i, 'm');
+ if ( ! mca_config.DecodeString(argv[i]) )
+ {
+ return;
+ }
+ break;
+
case 'P':
TEST_EXTRA_ARG(i, 'P');
if ( ! picture_coding.DecodeHex(argv[i]) )
TEST_EXTRA_ARG(i, 'p');
picture_rate = abs(atoi(argv[i]));
break;
- case 's': dolby_atmos_sync_flag = true; break;
+
+ case 's': dolby_atmos_sync_flag = true; break;
+ case 'u': show_ul_values_flag = true; break;
case 'V': version_flag = true; break;
case 'v': verbose_flag = true; break;
case 'W': no_write_flag = true; break;
if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
{
MXF::RGBAEssenceDescriptor *descriptor = 0;
- Writer.OP1aHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_RGBAEssenceDescriptor),
+ Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
descriptor->PictureEssenceCoding = Options.picture_coding;
}
if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
{
MXF::RGBAEssenceDescriptor *descriptor = 0;
- Writer.OP1aHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_RGBAEssenceDescriptor),
+ Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
descriptor->PictureEssenceCoding = Options.picture_coding;
}
if ( ASDCP_SUCCESS(result) )
result = Writer.OpenWrite(Options.out_file.c_str(), Info, ADesc);
- if ( ASDCP_SUCCESS(result) && Options.channel_assignment.HasValue() )
+ if ( ASDCP_SUCCESS(result)
+ && ( Options.channel_assignment.HasValue()
+ || ! Options.mca_config.empty() ) )
{
- MXF::WaveAudioDescriptor *descriptor = 0;
- Writer.OP1aHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_WaveAudioDescriptor),
- reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
- descriptor->ChannelAssignment = Options.channel_assignment;
+ MXF::WaveAudioDescriptor *essence_descriptor = 0;
+ Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_WaveAudioDescriptor),
+ reinterpret_cast<MXF::InterchangeObject**>(&essence_descriptor));
+ assert(essence_descriptor);
+
+ if ( Options.mca_config.empty() )
+ {
+ essence_descriptor->ChannelAssignment = Options.channel_assignment;
+ }
+ else
+ {
+ if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
+ {
+ fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
+ Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
+ return RESULT_FAIL;
+ }
+
+ essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
+
+ // add descriptors to the essence_descriptor and header
+ ASDCP::MXF::InterchangeObject_list_t::iterator i;
+ for ( i = Options.mca_config.begin(); i != Options.mca_config.end(); ++i )
+ {
+ if ( (*i)->GetUL() != UL(g_dict->ul(MDD_AudioChannelLabelSubDescriptor))
+ && (*i)->GetUL() != UL(g_dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
+ && (*i)->GetUL() != UL(g_dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
+ {
+ fprintf(stderr, "Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
+ (*i)->Dump();
+ }
+
+ Writer.OP1aHeader().AddChildObject(*i);
+ essence_descriptor->SubDescriptors.push_back((*i)->InstanceUID);
+ *i = 0; // parent will only free the ones we don't keep
+ }
+ }
}
}
{
Result_t result = RESULT_OK;
char str_buf[64];
+ g_dict = &ASDCP::DefaultSMPTEDict();
+
CommandOptions Options(argc, argv);
if ( Options.version_flag )
if ( Options.help_flag )
usage();
- if ( Options.version_flag || Options.help_flag )
+ if ( Options.show_ul_values_flag )
+ {
+ if ( Options.use_smpte_labels )
+ DefaultSMPTEDict().Dump(stdout);
+ else
+ DefaultInteropDict().Dump(stdout);
+ }
+
+ if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
return 0;
if ( Options.error_flag )
return 3;
}
- if ( Options.show_ul_values )
- {
- if ( Options.use_smpte_labels )
- DefaultSMPTEDict().Dump(stdout);
- else
- DefaultInteropDict().Dump(stdout);
- }
-
EssenceType_t EssenceType;
result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
/*
- Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+John Hurst
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*! \file h__02_Reader.cpp
\version $Id$
using namespace ASDCP;
using namespace ASDCP::MXF;
-
static Kumu::Mutex sg_DefaultMDInitLock;
static bool sg_DefaultMDTypesInit = false;
static const ASDCP::Dictionary *sg_dict;
//
-AS_02::MXF::AS02IndexReader::AS02IndexReader(const ASDCP::Dictionary*& d) : m_Duration(0), ASDCP::MXF::Partition(m_Dict), m_Dict(d) {}
+AS_02::MXF::AS02IndexReader::AS02IndexReader(const ASDCP::Dictionary*& d) :
+ m_Duration(0), m_BytesPerEditUnit(0),
+ ASDCP::MXF::Partition(m_Dict), m_Dict(d) {}
+
AS_02::MXF::AS02IndexReader::~AS02IndexReader() {}
//
Result_t
-AS_02::MXF::AS02IndexReader::InitFromFile(const Kumu::FileReader& reader, const ASDCP::MXF::RIP& rip)
+AS_02::MXF::AS02IndexReader::InitFromFile(const Kumu::FileReader& reader, const ASDCP::MXF::RIP& rip, const bool has_header_essence)
{
+ typedef std::list<Kumu::mem_ptr<ASDCP::MXF::Partition> > body_part_array_t;
+ body_part_array_t body_part_array;
+ body_part_array_t::const_iterator body_part_iter;
+
ASDCP::MXF::Array<ASDCP::MXF::RIP::Pair>::const_iterator i;
+ Result_t result = m_IndexSegmentData.Capacity(128*Kumu::Kilobyte); // will be grown if needed
+ ui32_t first_body_sid = 0;
+
+ // create a list of body parts and index parts
+ for ( i = rip.PairArray.begin(); KM_SUCCESS(result) && i != rip.PairArray.end(); ++i )
+ {
+ if ( i->BodySID == 0 )
+ continue;
+
+ if ( first_body_sid == 0 )
+ {
+ first_body_sid = i->BodySID;
+ }
+ else if ( i->BodySID != first_body_sid )
+ {
+ DefaultLogSink().Error("RIP contains multipls BodySID identities.\n");
+ }
+
+ reader.Seek(i->ByteOffset);
+ ASDCP::MXF::Partition *this_partition = new ASDCP::MXF::Partition(m_Dict);
+ assert(this_partition);
+
+ result = this_partition->InitFromFile(reader);
+
+ if ( KM_FAILURE(result) )
+ {
+ delete this_partition;
+ return result;
+ }
+
+ if ( this_partition->BodySID != i->BodySID )
+ {
+ DefaultLogSink().Error("Partition BodySID %d does not match RIP BodySID %d.\n",
+ this_partition->BodySID, i->BodySID);
+ }
- Result_t result = m_IndexSegmentData.Capacity(128*Kumu::Kilobyte);
+ body_part_array.push_back(0);
+ body_part_array.back().set(this_partition);
+ }
+
+ if ( body_part_array.empty() )
+ {
+ DefaultLogSink().Error("File has no partitions with essence data.\n");
+ return RESULT_AS02_FORMAT;
+ }
+
+ body_part_iter = body_part_array.begin();
for ( i = rip.PairArray.begin(); KM_SUCCESS(result) && i != rip.PairArray.end(); ++i )
{
ASDCP::MXF::Partition plain_part(m_Dict);
result = plain_part.InitFromFile(reader);
- if ( KM_SUCCESS(result) && plain_part.IndexByteCount > 0 )
+ if ( KM_FAILURE(result) )
+ return result;
+
+ if ( plain_part.IndexByteCount > 0 )
{
- // slurp up the remainder of the footer
+ if ( body_part_iter == body_part_array.end() )
+ {
+ DefaultLogSink().Error("Index and Body partitions do not match.\n");
+ break;
+ }
+
+ if ( plain_part.ThisPartition == plain_part.FooterPartition )
+ {
+ DefaultLogSink().Warn("File footer partition contains index data.\n");
+ }
+
+ // slurp up the remainder of the partition
ui32_t read_count = 0;
assert (plain_part.IndexByteCount <= 0xFFFFFFFFL);
result = m_IndexSegmentData.Capacity(m_IndexSegmentData.Length() + bytes_this_partition);
- if ( ASDCP_SUCCESS(result) )
+ if ( KM_SUCCESS(result) )
result = reader.Read(m_IndexSegmentData.Data() + m_IndexSegmentData.Length(),
bytes_this_partition, &read_count);
- if ( ASDCP_SUCCESS(result) && read_count != bytes_this_partition )
+ if ( KM_SUCCESS(result) && read_count != bytes_this_partition )
{
- DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
+ DefaultLogSink().Error("Short read of index partition: got %u, expecting %u\n",
read_count, bytes_this_partition);
- return RESULT_FAIL;
+ return RESULT_AS02_FORMAT;
}
- if ( ASDCP_SUCCESS(result) )
+ if ( KM_SUCCESS(result) )
{
- result = InitFromBuffer(m_IndexSegmentData.RoData() + m_IndexSegmentData.Length(), bytes_this_partition);
+ ui64_t current_body_offset = 0;
+ ui64_t current_ec_offset = 0;
+ assert(body_part_iter != body_part_array.end());
+
+ assert(!body_part_iter->empty());
+ ASDCP::MXF::Partition *tmp_partition = body_part_iter->get();
+
+ if ( has_header_essence && tmp_partition->ThisPartition == 0 )
+ {
+ current_body_offset = 0;
+ current_ec_offset = tmp_partition->HeaderByteCount + tmp_partition->ArchiveSize();
+ }
+ else
+ {
+ current_body_offset = tmp_partition->BodyOffset;
+ current_ec_offset += tmp_partition->ThisPartition + tmp_partition->ArchiveSize();
+ }
+
+ result = InitFromBuffer(m_IndexSegmentData.RoData() + m_IndexSegmentData.Length(), bytes_this_partition, current_body_offset, current_ec_offset);
m_IndexSegmentData.Length(m_IndexSegmentData.Length() + bytes_this_partition);
+ ++body_part_iter;
+ }
+ }
+ }
+
+ if ( KM_SUCCESS(result) )
+ {
+ std::list<InterchangeObject*>::const_iterator ii;
+
+ for ( ii = m_PacketList->m_List.begin(); ii != m_PacketList->m_List.end(); ++ii )
+ {
+ if ( (*ii)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
+ {
+ m_Duration += static_cast<IndexTableSegment*>(*ii)->IndexDuration;
}
}
}
+#if 0
+ char identbuf[IdentBufferLen];
+ std::list<InterchangeObject*>::iterator j;
+ std::vector<ASDCP::MXF::IndexTableSegment::IndexEntry>::iterator k;
+ ui32_t entry_count = 0;
+
+ for ( j = m_PacketList->m_List.begin(); j != m_PacketList->m_List.end(); ++j )
+ {
+ assert(*j);
+ ASDCP::MXF::IndexTableSegment* segment = static_cast<ASDCP::MXF::IndexTableSegment*>(*j);
+
+ fprintf(stderr, " --------------------------------------\n");
+ fprintf(stderr, " IndexEditRate = %d/%d\n", segment->IndexEditRate.Numerator, segment->IndexEditRate.Denominator);
+ fprintf(stderr, " IndexStartPosition = %s\n", i64sz(segment->IndexStartPosition, identbuf));
+ fprintf(stderr, " IndexDuration = %s\n", i64sz(segment->IndexDuration, identbuf));
+ fprintf(stderr, " EditUnitByteCount = %u\n", segment->EditUnitByteCount);
+ fprintf(stderr, " IndexSID = %u\n", segment->IndexSID);
+ fprintf(stderr, " BodySID = %u\n", segment->BodySID);
+ fprintf(stderr, " SliceCount = %hu\n", segment->SliceCount);
+ fprintf(stderr, " PosTableCount = %hu\n", segment->PosTableCount);
+ fprintf(stderr, " RtFileOffset = %s\n", i64sz(segment->RtFileOffset, identbuf));
+ fprintf(stderr, " RtEntryOffset = %s\n", i64sz(segment->RtEntryOffset, identbuf));
+ fprintf(stderr, " IndexEntryArray:\n");
+
+ for ( k = segment->IndexEntryArray.begin(); k != segment->IndexEntryArray.end(); ++k )
+ {
+ fprintf(stderr, " 0x%010qx\n", k->StreamOffset);
+ ++entry_count;
+ }
+ }
+
+ fprintf(stderr, "Actual entries: %d\n", entry_count);
+#endif
+
return result;
}
//
ASDCP::Result_t
-AS_02::MXF::AS02IndexReader::InitFromBuffer(const byte_t* p, ui32_t l)
+AS_02::MXF::AS02IndexReader::InitFromBuffer(const byte_t* p, ui32_t l, const ui64_t& body_offset, const ui64_t& essence_container_offset)
{
Result_t result = RESULT_OK;
const byte_t* end_p = p + l;
- while ( ASDCP_SUCCESS(result) && p < end_p )
+ while ( KM_SUCCESS(result) && p < end_p )
{
// parse the packets and index them by uid, discard KLVFill items
InterchangeObject* object = CreateObject(m_Dict, p);
result = object->InitFromBuffer(p, end_p - p);
p += object->PacketLength();
- if ( ASDCP_SUCCESS(result) )
+ if ( KM_SUCCESS(result) )
{
- m_PacketList->AddPacket(object); // takes ownership
+ if ( object->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
+ {
+ static_cast<IndexTableSegment*>(object)->RtFileOffset = essence_container_offset;
+ static_cast<IndexTableSegment*>(object)->RtEntryOffset = body_offset;
+ m_PacketList->AddPacket(object); // takes ownership
+ }
+ else
+ {
+ delete object;
+ }
}
else
{
}
}
- if ( ASDCP_FAILURE(result) )
+ if ( KM_FAILURE(result) )
DefaultLogSink().Error("Failed to initialize AS02IndexReader\n");
- std::list<InterchangeObject*>::const_iterator i;
-
- for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
- {
- if ( (*i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
- {
- m_Duration += static_cast<IndexTableSegment*>(*i)->IndexDuration;
- }
- }
-
return result;
}
{
return m_Duration;
}
-
//
Result_t
{
if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
{
- IndexTableSegment* Segment = (IndexTableSegment*)(*li);
+ IndexTableSegment* Segment = static_cast<IndexTableSegment*>(*li);
ui64_t start_pos = Segment->IndexStartPosition;
if ( Segment->EditUnitByteCount > 0 )
if ( ! Segment->IndexEntryArray.empty() )
DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
- Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
+ Entry.StreamOffset = ((ui64_t)frame_num * Segment->EditUnitByteCount) + Segment->RtFileOffset;
return RESULT_OK;
}
else if ( (ui64_t)frame_num >= start_pos
ui64_t tmp = frame_num - start_pos;
assert(tmp <= 0xFFFFFFFFL);
Entry = Segment->IndexEntryArray[(ui32_t) tmp];
+ Entry.StreamOffset = Entry.StreamOffset - Segment->RtEntryOffset + Segment->RtFileOffset;
return RESULT_OK;
}
}
Result_t
AS_02::h__AS02Reader::OpenMXFRead(const char* filename)
{
+ bool has_header_essence = false;
Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::OpenMXFRead(filename);
if ( KM_SUCCESS(result) )
if ( m_RIP.PairArray.front().ByteOffset != 0 )
{
DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
- result = RESULT_FORMAT;
+ return RESULT_AS02_FORMAT;
+ }
+
+ Kumu::fpos_t first_partition_after_header = 0;
+ bool has_body_sid = false;
+ Array<RIP::Pair>::iterator r_i;
+
+ for ( r_i = m_RIP.PairArray.begin(); r_i != m_RIP.PairArray.end(); ++r_i )
+ {
+ if ( r_i->BodySID != 0 )
+ {
+ has_body_sid = true;
+ }
+
+ if ( first_partition_after_header == 0 && r_i->ByteOffset != 0 )
+ {
+ first_partition_after_header = r_i->ByteOffset;
+ }
+ }
+
+ // essence in header partition?
+ Kumu::fpos_t header_end = m_HeaderPart.HeaderByteCount + m_HeaderPart.ArchiveSize();
+ has_header_essence = header_end < first_partition_after_header;
+
+ if ( has_header_essence )
+ {
+ DefaultLogSink().Warn("File header partition contains essence data.\n");
+ }
+
+ if ( ! has_body_sid )
+ {
+ DefaultLogSink().Error("File contains no essence.\n");
+ return RESULT_AS02_FORMAT;
}
}
if ( KM_SUCCESS(result) )
{
- m_HeaderPart.BodyOffset = m_File.Tell();
m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
- result = m_IndexAccess.InitFromFile(m_File, m_RIP);
+ result = m_IndexAccess.InitFromFile(m_File, m_RIP, has_header_essence);
}
- m_File.Seek(m_HeaderPart.BodyOffset);
return result;
}
AS_02::h__AS02Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
{
- return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(m_HeaderPart, FrameNum, FrameBuf,
- EssenceUL, Ctx, HMAC);
+ return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
}
-Result_t
-AS_02::h__AS02Reader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
- i8_t& temporalOffset, i8_t& keyFrameOffset)
-{
- return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::LocateFrame(m_HeaderPart, FrameNum,
- streamOffset, temporalOffset, keyFrameOffset);
-}
-
-
//
// end h__02_Reader.cpp
//
/*
- Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+John Hurst
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*! \file h__02_Writer.cpp
\version $Id$
//------------------------------------------------------------------------------------------
//
-AS_02::AS02IndexWriter::AS02IndexWriter(const ASDCP::Dictionary*& d) :
- Partition(d), m_CurrentSegment(0), m_BytesPerEditUnit(0), m_Dict(d), m_ECOffset(0), m_Lookup(0)
+AS_02::MXF::AS02IndexWriter::AS02IndexWriter(const ASDCP::Dictionary*& d) :
+ Partition(d), m_CurrentSegment(0), m_BytesPerEditUnit(0), m_Dict(d), m_Lookup(0)
{
BodySID = 0;
IndexSID = 129;
}
-AS_02::AS02IndexWriter::~AS02IndexWriter() {}
+AS_02::MXF::AS02IndexWriter::~AS02IndexWriter() {}
//
Result_t
-AS_02::AS02IndexWriter::WriteToFile(Kumu::FileWriter& Writer)
+AS_02::MXF::AS02IndexWriter::WriteToFile(Kumu::FileWriter& Writer)
{
- // UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
-
assert(m_Dict);
- ASDCP::FrameBuffer FooterBuffer;
- ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
- Result_t result = FooterBuffer.Capacity(footer_size);
- ui32_t iseg_count = 0;
+ ASDCP::FrameBuffer index_body_buffer;
+ ui32_t index_body_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
+ Result_t result = index_body_buffer.Capacity(index_body_size);
+ ui64_t start_position = 0;
if ( m_CurrentSegment != 0 )
{
m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
+ start_position = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
m_CurrentSegment = 0;
}
std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
- for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
+ for ( ; pl_i != m_PacketList->m_List.end() && KM_SUCCESS(result); pl_i++ )
{
- if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
- {
- iseg_count++;
- IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
-
- if ( m_BytesPerEditUnit != 0 )
- {
- if ( iseg_count != 1 )
- return RESULT_STATE;
-
- /// Segment->IndexDuration = duration;
- }
- }
-
InterchangeObject* object = *pl_i;
object->m_Lookup = m_Lookup;
ASDCP::FrameBuffer WriteWrapper;
- WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
- FooterBuffer.Capacity() - FooterBuffer.Size());
+ WriteWrapper.SetData(index_body_buffer.Data() + index_body_buffer.Size(),
+ index_body_buffer.Capacity() - index_body_buffer.Size());
result = object->WriteToBuffer(WriteWrapper);
- FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
+ index_body_buffer.Size(index_body_buffer.Size() + WriteWrapper.Size());
+ delete *pl_i;
+ *pl_i = 0;
}
- if ( ASDCP_SUCCESS(result) )
+ m_PacketList->m_List.clear();
+
+ if ( KM_SUCCESS(result) )
{
- IndexByteCount = FooterBuffer.Size();
+ IndexByteCount = index_body_buffer.Size();
UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
result = Partition::WriteToFile(Writer, body_ul);
}
- if ( ASDCP_SUCCESS(result) )
+ if ( KM_SUCCESS(result) )
{
ui32_t write_count = 0;
- result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
- assert(write_count == FooterBuffer.Size());
+ result = Writer.Write(index_body_buffer.RoData(), index_body_buffer.Size(), &write_count);
+ assert(write_count == index_body_buffer.Size());
}
- return result;
-}
-
-//
-void
-AS_02::AS02IndexWriter::ResetCBR(Kumu::fpos_t offset)
-{
- m_ECOffset = offset;
-
- std::list<InterchangeObject*>::iterator i;
-
- for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
+ if ( KM_SUCCESS(result) )
{
- delete *i;
+ m_CurrentSegment = new IndexTableSegment(m_Dict);
+ assert(m_CurrentSegment);
+ AddChildObject(m_CurrentSegment);
+ m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
+ m_CurrentSegment->IndexEditRate = m_EditRate;
+ m_CurrentSegment->IndexStartPosition = start_position;
}
- m_PacketList->m_List.clear();
+ return result;
}
//
void
-AS_02::AS02IndexWriter::Dump(FILE* stream)
+AS_02::MXF::AS02IndexWriter::Dump(FILE* stream)
{
if ( stream == 0 )
stream = stderr;
//
ui32_t
-AS_02::AS02IndexWriter::GetDuration() const
+AS_02::MXF::AS02IndexWriter::GetDuration() const
{
ui32_t duration;
std::list<InterchangeObject*>::const_iterator i;
//
void
-AS_02::AS02IndexWriter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const ASDCP::Rational& Rate)
+AS_02::MXF::AS02IndexWriter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const ASDCP::Rational& Rate)
{
assert(lookup);
m_Lookup = lookup;
//
void
-AS_02::AS02IndexWriter::SetIndexParamsVBR(IPrimerLookup* lookup, const ASDCP::Rational& Rate, Kumu::fpos_t offset)
+AS_02::MXF::AS02IndexWriter::SetIndexParamsVBR(IPrimerLookup* lookup, const ASDCP::Rational& Rate, Kumu::fpos_t offset)
{
assert(lookup);
m_Lookup = lookup;
m_BytesPerEditUnit = 0;
m_EditRate = Rate;
- m_ECOffset = offset;
}
//
void
-AS_02::AS02IndexWriter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
+AS_02::MXF::AS02IndexWriter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
{
if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
{
}
else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
{ // no, this one is full, start another
+ DefaultLogSink().Warn("%s, line %d: This has never been tested.\n", __FILE__, __LINE__);
m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
- ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
+ ui64_t start_position = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
m_CurrentSegment = new IndexTableSegment(m_Dict);
assert(m_CurrentSegment);
AddChildObject(m_CurrentSegment);
m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
m_CurrentSegment->IndexEditRate = m_EditRate;
- m_CurrentSegment->IndexStartPosition = StartPosition;
+ m_CurrentSegment->IndexStartPosition = start_position;
}
m_CurrentSegment->IndexEntryArray.push_back(Entry);
//
AS_02::h__AS02Writer::h__AS02Writer(const ASDCP::Dictionary& d) : ASDCP::MXF::TrackFileWriter<ASDCP::MXF::OP1aHeader>(d),
- m_IndexWriter(m_Dict), m_PartitionSpace(0),
+ m_ECStart(0), m_ClipStart(0), m_IndexWriter(m_Dict), m_PartitionSpace(0),
m_IndexStrategy(AS_02::IS_FOLLOW) {}
AS_02::h__AS02Writer::~h__AS02Writer() {}
InitHeader();
- AddSourceClip(EditRate, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
+ AddSourceClip(EditRate, EditRate/*TODO: for a moment*/, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
AddEssenceDescriptor(WrappingUL);
m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // Header partition RIP entry
m_IndexWriter.OperationalPattern = m_HeaderPart.OperationalPattern;
if ( ASDCP_SUCCESS(result) )
{
m_PartitionSpace *= ceil(EditRate.Quotient()); // convert seconds to edit units
- Kumu::fpos_t ECoffset = m_File.Tell();
+ m_ECStart = m_File.Tell();
m_IndexWriter.IndexSID = 129;
if ( BytesPerEditUnit == 0 )
{
- m_IndexWriter.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
+ m_IndexWriter.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, m_ECStart);
}
else
{
body_part.BodySID = 1;
body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
- body_part.ThisPartition = m_File.Tell();
+ body_part.ThisPartition = m_ECStart;
result = body_part.WriteToFile(m_File, body_ul);
m_RIP.PairArray.push_back(RIP::Pair(1, body_part.ThisPartition)); // Second RIP Entry
}
Result_t
AS_02::h__AS02Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
{
+ ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
+
Result_t result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
m_StreamOffset, FrameBuf, EssenceUL, Ctx, HMAC);
+ if ( KM_SUCCESS(result) )
+ {
+ IndexTableSegment::IndexEntry Entry;
+ Entry.StreamOffset = this_stream_offset;
+ m_IndexWriter.PushIndexEntry(Entry);
+ }
+
if ( m_FramesWritten > 0 && ( m_FramesWritten % m_PartitionSpace ) == 0 )
{
m_IndexWriter.ThisPartition = m_File.Tell();
body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
body_part.ThisPartition = m_File.Tell();
+
+ body_part.BodyOffset = m_StreamOffset;
result = body_part.WriteToFile(m_File, body_ul);
m_RIP.PairArray.push_back(RIP::Pair(1, body_part.ThisPartition));
- m_IndexWriter.ResetCBR(m_File.Tell());
}
return result;
}
-// standard method of writing the header and footer of a completed MXF file
+//
+bool
+AS_02::h__AS02Writer::HasOpenClip() const
+{
+ return m_ClipStart != 0;
+}
+
+//
+Result_t
+AS_02::h__AS02Writer::StartClip(const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
+{
+ if ( Ctx != 0 )
+ {
+ DefaultLogSink().Error("Encryption not yet supported for PCM clip-wrap.\n");
+ return RESULT_STATE;
+ }
+
+ if ( m_ClipStart != 0 )
+ {
+ DefaultLogSink().Error("Cannot open clip, clip already open.\n");
+ return RESULT_STATE;
+ }
+
+ m_ClipStart = m_File.Tell();
+ byte_t clip_buffer[24] = {0};
+ memcpy(clip_buffer, EssenceUL, 16);
+ bool check = Kumu::write_BER(clip_buffer+16, 0, 8);
+ assert(check);
+ return m_File.Write(clip_buffer, 24);
+}
+
+//
+Result_t
+AS_02::h__AS02Writer::WriteClipBlock(const ASDCP::FrameBuffer& FrameBuf)
+{
+ if ( m_ClipStart == 0 )
+ {
+ DefaultLogSink().Error("Cannot write clip block, no clip open.\n");
+ return RESULT_STATE;
+ }
+
+ return m_File.Write(FrameBuf.RoData(), FrameBuf.Size());
+}
+
+//
+Result_t
+AS_02::h__AS02Writer::FinalizeClip(ui32_t bytes_per_frame)
+{
+ if ( m_ClipStart == 0 )
+ {
+ DefaultLogSink().Error("Cannot close clip, clip not open.\n");
+ return RESULT_STATE;
+ }
+
+ ui64_t current_position = m_File.Tell();
+ Result_t result = m_File.Seek(m_ClipStart+16);
+
+ if ( ASDCP_SUCCESS(result) )
+ {
+ byte_t clip_buffer[8] = {0};
+ bool check = Kumu::write_BER(clip_buffer, m_FramesWritten * bytes_per_frame, 8);
+ assert(check);
+ result = m_File.Write(clip_buffer, 8);
+ }
+
+ m_File.Seek(current_position);
+ m_ClipStart = 0;
+ return result;
+}
+
+// standard method of writing the header and footer of a completed AS-02 file
//
Result_t
AS_02::h__AS02Writer::WriteAS02Footer()
}
}
+ if ( m_RIP.PairArray.front().ByteOffset != 0 )
+ {
+ DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
+ result = RESULT_FORMAT;
+ }
+
//
if ( m_RIP.PairArray.size() < 2 )
{
DefaultLogSink().Error("ASDCP::h__ASDCPReader::OpenMXFRead, m_BodyPart.InitFromFile failed\n");
}
}
- else if ( m_RIP.PairArray.front().ByteOffset != 0 )
- {
- DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
- result = RESULT_FORMAT;
- }
}
if ( KM_SUCCESS(result) )
{
+ // this position will be at either
+ // a) the spot in the header partition where essence units appear, or
+ // b) right after the body partition header (where essence units appear)
m_HeaderPart.BodyOffset = m_File.Tell();
result = m_File.Seek(m_HeaderPart.FooterPartition);
ASDCP::h__ASDCPReader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
{
- return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::ReadEKLVFrame(m_HeaderPart, FrameNum, FrameBuf,
+ return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::ReadEKLVFrame(m_HeaderPart.BodyOffset, FrameNum, FrameBuf,
EssenceUL, Ctx, HMAC);
}
ASDCP::h__ASDCPReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
i8_t& temporalOffset, i8_t& keyFrameOffset)
{
- return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::LocateFrame(m_HeaderPart, FrameNum,
+ return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::LocateFrame(m_HeaderPart.BodyOffset, FrameNum,
streamOffset, temporalOffset, keyFrameOffset);
}
if ( ber_size < MXF_BER_LENGTH )
{
- DefaultLogSink().Error("BER size %d shorter than AS-DCP minimum %d.\n",
+ DefaultLogSink().Error("BER size %d shorter than AS-DCP/AS-02 minimum %d.\n",
ber_size, MXF_BER_LENGTH);
return RESULT_FORMAT;
}
return InitFromBuffer(m_KeyBuf, header_length);
}
+
+//------------------------------------------------------------------------------------------
+//
+
+
// base subroutine for reading a KLV packet, assumes file position is at the first byte of the packet
Result_t
-ASDCP::Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
+ASDCP::Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict,
const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf,
ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
using namespace ASDCP;
using namespace ASDCP::MXF;
+//
+ui32_t
+ASDCP::derive_timecode_rate_from_edit_rate(const ASDCP::Rational& edit_rate)
+{
+ double edit_rate_real = edit_rate.Quotient();
+ if ( ceil(edit_rate_real) == floor(edit_rate_real) )
+ {
+ return ceil(edit_rate_real);
+ }
+
+ return ( edit_rate_real - floor(edit_rate_real) < 0.5 ) ? floor(edit_rate_real) : ceil(edit_rate_real);
+}
+
//
// add DMS CryptographicFramework entry to source package
void
m_RIP.PairArray.push_back(RIP::Pair(1, 0)); // 2-part, essence in header
}
- AddSourceClip(EditRate, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
+ // timecode rate and essence rate are the same
+ AddSourceClip(EditRate, EditRate, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
AddEssenceDescriptor(WrappingUL);
Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
usage(FILE* stream = stdout)
{
fprintf(stream, "\
-USAGE: %s [-l <limit>] [-p <prefix>] [-s <suffix>] [-u] [-v] \n\
+USAGE: %s [-l <limit>] [-p <prefix>] [-s <suffix>] [-u|-U] [-v] \n\
(<type-name>|<type-ul>) <mxf-filename>+\n\
\n\
%s -d\n\
uses the input filename minus any extension\n\
-s <suffix> - Append <suffix> to output filenames\n\
-u - Unwrap the packet value (i.e., do not output KL)\n\
+ -U - Do not unwrap (default)\n\
-v - Verbose. Prints informative messages to stderr\n\
-V - Show version information\n\
\n\
\n", PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
}
-enum MajorMode_t {
- MMT_NONE,
- MMT_LIST,
- MMT_EXTRACT
-};
-
//
//
class CommandOptions
bool help_flag; // true if the help display option was selected
bool verbose_flag; // true if the informative messages option was selected
bool unwrap_mode; // true if we are to strip the K and L before writing
- MajorMode_t mode;
+ bool list_mode;
ASDCP::UL target_ul; // a UL value identifying the packets to be extracted
ui64_t extract_limit; // limit extraction to the given number of packets
std::string prefix; // output filename prefix
CommandOptions(int argc, const char** argv, const ASDCP::Dictionary& dict) :
error_flag(true), version_flag(false), help_flag(false),
- verbose_flag(false), unwrap_mode(false), mode(MMT_EXTRACT), extract_limit(ui64_C(-1))
+ verbose_flag(false), unwrap_mode(false), list_mode(false), extract_limit(ui64_C(-1))
{
for ( int i = 1; i < argc; ++i )
{
{
switch ( argv[i][1] )
{
- case 'd': mode = MMT_LIST; break;
+ case 'd': list_mode = true; break;
case 'h': help_flag = true; break;
case 'l':
break;
case 'u': unwrap_mode = true; break;
+ case 'U': unwrap_mode = false; break;
case 'V': version_flag = true; break;
case 'v': verbose_flag = true; break;
if ( help_flag || version_flag )
return;
- if ( mode == MMT_EXTRACT )
+ if ( ! list_mode )
{
if ( inFileList.empty() )
{
main(int argc, const char** argv)
{
const Dictionary *dict = &DefaultCompositeDict();
-
CommandOptions Options(argc, argv, *dict);
if ( Options.version_flag )
return 3;
}
- if ( Options.mode == MMT_LIST )
+ if ( Options.list_mode )
{
DefaultLogSink().UnsetFilterFlag(Kumu::LOG_ALLOW_WARN);
char buf[64];
Result_t result = RESULT_OK;
FileList_t::iterator fi;
- for ( fi = Options.inFileList.begin(); ASDCP_SUCCESS(result) && fi != Options.inFileList.end(); ++fi )
+ for ( fi = Options.inFileList.begin(); KM_SUCCESS(result) && fi != Options.inFileList.end(); ++fi )
{
if ( Options.verbose_flag )
fprintf(stderr, "Opening file %s\n", (fi->c_str()));
std::string this_prefix = Options.prefix.empty() ? Kumu::PathSetExtension(*fi, "") + "_" : Options.prefix;
Kumu::FileReader reader;
KLVFilePacket packet;
- char filename_buf[1024], buf1[64], buf2[64];
+ char filename_buf[1024];
ui64_t item_counter = 0;
result = reader.OpenRead(fi->c_str());
result = RESULT_OK;
}
- if ( ASDCP_FAILURE(result) )
+ if ( KM_FAILURE(result) )
{
fputs("Program stopped on error.\n", stderr);