Change History
-2015-01-22 - PHDR feature, bug fixes
+
+2015-02-19 - PHDR feature, bug fixes
+ o Modified PCMParser and PCMParserList to return partial frame buffers at the
+ end of a WAV input file. This was needed to allow wrapping all samples into
+ an AS-02 audio file (which is clip-wrapped) in the case where the input file
+ has an odd number of samples with respect to the frame buffer size being
+ used. If there is more than one input file the length of the last buffer
+ will be determined by PCMParserList to be the shortest of the input files.
+ Prior to this change, partial samples in an input WAV file have been
+ abandoned (i.e., not written out to the MXF file). As a result, AS-DCP
+ applications will have to decide whether to abandon the partial last frame
+ (usually detected by testing frame_buffer.Size() != frame_buffer.Capacity())
+ or write it to the MXF file. Programs written to the old API will write the
+ partial frame (i.e., new behavior.) This should not be harmful since the
+ remainder of the buffer is zeroed and the output file will contain one
+ additional edit unit compared to the previous version.
+ o asdcp-wrap has been modified to test for the partial buffer and by default
+ will complain and abandon the buffer (i.e., same behavior but with a warning
+ message.) A new command line switch (-g) alters this behavior and writes
+ the buffer to the MXF file (it still warns the user that this condition is
+ present.)
+ o as-02-wrap now wraps all samples from the input file to the MXF file. There
+ is no padding, the clip is exactly the set of samples from the input.
+ o as-02 unwrap is temporarily restricted to creating wav files that are
+ aligned with the frame buffer size. This means that the output file will
+ be longer than the original WAV input in the case where the input file has
+ an odd number of samples with respect to the frame buffer size being used.
+ The pad samples are zero (silence).
+ o Modified CalcFramesFromDurationInSamples() to increment the count by one for
+ the case where the input file has an odd number of samples with respect to
+ the frame buffer size being used (previously it truncated the odd samples.)
+ o Fixed ST 429-5 / ST 2067-5 wrapping to increase header space when ancillary
+ resources are present (fixes a bug that cause the header to overflow the
+ allotted space when large numbers of PNG files were present.)
+ o Refactored GetXMLDocType() to use the XML parser.
+ o Added ParseFirstFromString() method to Kumu::XMLElement
+ o Removed Kumu::StringIsXML from the API.
+ o Added ASDCP::MXF::RGBALayout type
+ o Added J2CLayout property to JPEG2000PictureSubDescriptor
+ o Changed km_token_split() to retain empty elements in the output list
o Added PHDR wrapping for AS-02.
o Added J2CLayout property to the JPEG2000PictureSubDescriptor. This
support is preliminary: the Raw data type is being used instead of
# 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], [2.3.8], [asdcplib@cinecert.com])
+AC_INIT([asdcplib], [2.4.9], [asdcplib@cinecert.com])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_SRCDIR([src/KM_error.h])
}
// Returns number of frames for data described by ADesc, given a duration in samples and an edit rate
- inline ui32_t CalcFramesFromDurationInSamples(const ui32_t durationInSamples, const ASDCP::MXF::WaveAudioDescriptor& d,
+ inline ui32_t CalcFramesFromDurationInSamples(const ui32_t duration_samples, const ASDCP::MXF::WaveAudioDescriptor& d,
const ASDCP::Rational& edit_rate)
{
- return static_cast<ui32_t>(static_cast<ui64_t>(durationInSamples) *
- static_cast<ui64_t>(d.AudioSamplingRate.Denominator * edit_rate.Numerator) /
- static_cast<ui64_t>(d.AudioSamplingRate.Numerator * edit_rate.Denominator));
+ ui32_t spf = CalcSamplesPerFrame(d, edit_rate);
+ ui32_t frames = duration_samples / spf;
+
+ if ( duration_samples % spf != 0 )
+ {
+ ++frames;
+ }
+
+ return frames;
}
} // namespace MXF
/*
-Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+Copyright (c) 2011-2015, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
John Hurst
All rights reserved.
class AS_02::PCM::MXFReader::h__Reader : public AS_02::h__AS02Reader
{
- ui64_t m_ClipEssenceBegin;
- ui64_t m_SamplesPerFrame;
- ui32_t m_BytesPerFrame;
- ui32_t m_ContainerDuration;
+ ui64_t m_ClipEssenceBegin, m_ClipSize;
+ ui32_t m_ClipDurationFrames, m_BytesPerFrame;
ASDCP_NO_COPY_CONSTRUCT(h__Reader);
h__Reader();
public:
- h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_ClipEssenceBegin(0),
- m_SamplesPerFrame(0), m_BytesPerFrame(0), m_ContainerDuration(0) {}
+ h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_ClipEssenceBegin(0), m_ClipSize(0),
+ m_ClipDurationFrames(0) {}
virtual ~h__Reader() {}
ASDCP::Result_t OpenRead(const std::string&, const ASDCP::Rational& edit_rate);
}
m_ClipEssenceBegin = m_File.Tell();
- m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*wave_descriptor, edit_rate);
+ m_ClipSize = reader.Length();
m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*wave_descriptor, edit_rate);
- m_ContainerDuration = static_cast<ui32_t>(reader.Length() / ( m_SamplesPerFrame * AS_02::MXF::CalcSampleSize(*wave_descriptor)));
+ m_ClipDurationFrames = m_ClipSize / m_BytesPerFrame;
+ if ( m_ClipSize % m_BytesPerFrame > 0 )
+ {
+ ++m_ClipDurationFrames; // there is a partial frame at the end
+ }
}
}
return RESULT_INIT;
}
- if ( FrameNum > m_ContainerDuration )
+ if ( ! ( FrameNum < m_ClipDurationFrames ) )
{
return RESULT_RANGE;
}
assert(m_ClipEssenceBegin);
- ui64_t position = m_ClipEssenceBegin + ( FrameNum * m_BytesPerFrame );
+ ui64_t offset = FrameNum * m_BytesPerFrame;
+ ui64_t position = m_ClipEssenceBegin + offset;
Result_t result = RESULT_OK;
if ( m_File.Tell() != position )
if ( KM_SUCCESS(result) )
{
- result = m_File.Read(FrameBuf.Data(), m_BytesPerFrame);
- }
+ ui64_t remainder = m_ClipSize - offset;
+ ui32_t read_size = ( remainder < m_BytesPerFrame ) ? remainder : m_BytesPerFrame;
+ result = m_File.Read(FrameBuf.Data(), read_size);
- if ( KM_SUCCESS(result) )
- {
- FrameBuf.Size(m_BytesPerFrame);
+ if ( KM_SUCCESS(result) )
+ {
+ FrameBuf.Size(read_size);
+
+ if ( read_size < FrameBuf.Capacity() )
+ {
+ memset(FrameBuf.Data() + FrameBuf.Size(), 0, FrameBuf.Capacity() - FrameBuf.Size());
+ }
+ }
}
return result;
public:
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__AS02WriterClip(d), m_WaveAudioDescriptor(0), m_BytesPerFrame(0), m_SamplesPerFrame(0)
+ ui32_t m_BytesPerSample;
+
+ h__Writer(const Dictionary& d) : AS_02::h__AS02WriterClip(d), m_WaveAudioDescriptor(0), m_BytesPerSample(0)
{
memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
}
if ( KM_SUCCESS(result) )
{
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;
- assert(m_BytesPerFrame);
+ m_BytesPerSample = AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor);
result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingClip)),
SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate));
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() )
if ( KM_SUCCESS(result) )
{
- m_FramesWritten += m_SamplesPerFrame;
+ m_FramesWritten += frame_buf.Size() / m_BytesPerSample;
}
return result;
if ( KM_SUCCESS(result) )
{
- m_IndexWriter.m_Duration = m_FramesWritten;
+ m_WaveAudioDescriptor->ContainerDuration = m_IndexWriter.m_Duration = m_FramesWritten;
WriteAS02Footer();
}
return result;
}
+//
+static bool
+string_is_xml(const ASDCP::FrameBuffer& buffer)
+{
+ std::string ns_prefix, type_name, namespace_name;
+ Kumu::AttributeList doc_attr_list;
+ return GetXMLDocType(buffer.RoData(), buffer.Size(),
+ ns_prefix, type_name, namespace_name, doc_attr_list);
+}
+
//
ASDCP::Result_t
ASDCP::RawEssenceType(const std::string& filename, EssenceType_t& type)
{
type = ESS_PCM_24b_48k;
}
- else if ( Kumu::StringIsXML((const char*)FB.RoData(), FB.Size()) )
+ else if ( string_is_xml(FB) )
{
type = ESS_TIMED_TEXT;
}
resourceSubdescriptor->EssenceStreamID = m_EssenceStreamID++;
m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor);
m_EssenceDescriptor->SubDescriptors.push_back(resourceSubdescriptor->InstanceUID);
+ m_HeaderSize += resourceSubdescriptor->MIMEMediaType.ArchiveLength() + 20; // 20 == sizeof uuid + sizeof int32
}
m_EssenceStreamID = 10;
}
else
{
- DefaultLogSink().Error("Unable to write Interop timed-text MXF file. Use SMOTE DCP options instead.\n");
+ DefaultLogSink().Error("Unable to write Interop timed-text MXF file. Use SMPTE DCP options instead.\n");
return RESULT_FORMAT;
}
//
Kumu::PathCompList_t&
-Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
+Kumu::PathToComponents(const std::string& path, PathCompList_t& component_list, char separator)
{
std::string s;
s = separator;
- CList = km_token_split(Path, s);
- return CList;
+ PathCompList_t tmp_list = km_token_split(path, std::string(s));
+ PathCompList_t::const_iterator i;
+
+ for ( i = tmp_list.begin(); i != tmp_list.end(); ++i )
+ {
+ if ( ! i->empty() )
+ {
+ component_list.push_back(*i);
+ }
+ }
+
+ return component_list;
}
//
/*
-Copyright (c) 2005-2012, John Hurst
+Copyright (c) 2005-2015, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
while ( r != 0 )
{
assert(r >= pstr);
- if ( r > pstr )
- {
- std::string tmp_str;
- tmp_str.assign(pstr, r - pstr);
- components.push_back(tmp_str);
- }
-
+ std::string tmp_str;
+ tmp_str.assign(pstr, r - pstr);
+ components.push_back(tmp_str);
pstr = r + separator.size();
r = strstr(pstr, separator.c_str());
}
- if ( strlen(pstr) > 0 )
+ if ( strlen(pstr) >= 0 )
{
components.push_back(std::string(pstr));
}
/*
-Copyright (c) 2005-2012, John Hurst
+Copyright (c) 2005-2015, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
const char *km_strnstr(const char *s1, const char *s2, size_t n);
// Split the input string into tokens using the given separator. If the separator is not found the
- // entire string will be returned as a single-item list.
+ // entire string will be returned as a single-item list. Empty items will be recorded for
+ // adjacent instances of the separator. E.g., "/foo//bar/" will return ["", "foo", "", "bar", ""].
std::list<std::string> km_token_split(const std::string& str, const std::string& separator);
} // namespace Kumu
/*
-Copyright (c) 2005-2010, John Hurst
+Copyright (c) 2005-2015, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
return ParseString(document.c_str(), document.size());
}
+//
+bool
+Kumu::XMLElement::ParseFirstFromString(const ByteString& document)
+{
+ return ParseFirstFromString((const char*)document.RoData(), document.Length());
+}
+
+//
+bool
+Kumu::XMLElement::ParseFirstFromString(const std::string& document)
+{
+ return ParseFirstFromString(document.c_str(), document.size());
+}
+
//----------------------------------------------------------------------------------------------------
bool
Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
{
+ if ( doc_len == 0 )
+ {
+ return false;
+ }
+
XML_Parser Parser = XML_ParserCreateNS("UTF-8", '|');
if ( Parser == 0 )
XML_ParserFree(Parser);
if ( ! Ctx.Namespaces->empty() )
- m_NamespaceOwner = (void*)Ctx.Namespaces;
+ {
+ m_NamespaceOwner = (void*)Ctx.Namespaces;
+ }
return true;
}
-//------------------------------------------------------------------------------------------
-
-struct xph_test_wrapper
-{
- XML_Parser Parser;
- bool Status;
-
- xph_test_wrapper(XML_Parser p) : Parser(p), Status(false) {}
-};
-
-// expat wrapper functions, map callbacks to IASAXHandler
+// expat wrapper functions
//
static void
-xph_test_start(void* p, const XML_Char* name, const XML_Char** attrs)
+xph_start_one_shot(void* p, const XML_Char* name, const XML_Char** attrs)
{
- assert(p);
- xph_test_wrapper* Wrapper = (xph_test_wrapper*)p;
-
- Wrapper->Status = true;
- XML_StopParser(Wrapper->Parser, false);
+ xph_start(p, name, attrs);
+ XML_Parser parser = (XML_Parser)p;
+ XML_StopParser(parser, false);
}
-
//
bool
-Kumu::StringIsXML(const char* document, ui32_t len)
+Kumu::XMLElement::ParseFirstFromString(const char* document, ui32_t doc_len)
{
- if ( document == 0 )
- return false;
-
- if ( len == 0 )
- len = strlen(document);
+ if ( doc_len == 0 )
+ {
+ return false;
+ }
- XML_Parser Parser = XML_ParserCreate("UTF-8");
+ XML_Parser Parser = XML_ParserCreateNS("UTF-8", '|');
if ( Parser == 0 )
{
return false;
}
- xph_test_wrapper Wrapper(Parser);
- XML_SetUserData(Parser, (void*)&Wrapper);
- XML_SetStartElementHandler(Parser, xph_test_start);
+ ExpatParseContext Ctx(this);
+ XML_SetUserData(Parser, (void*)&Ctx);
+ XML_SetElementHandler(Parser, xph_start_one_shot, xph_end);
+ XML_SetCharacterDataHandler(Parser, xph_char);
+ XML_SetStartNamespaceDeclHandler(Parser, xph_namespace_start);
+
+ if ( ! XML_Parse(Parser, document, doc_len, 1) )
+ {
+ DefaultLogSink().Error("XML Parse error on line %d: %s\n",
+ XML_GetCurrentLineNumber(Parser),
+ XML_ErrorString(XML_GetErrorCode(Parser)));
+ XML_ParserFree(Parser);
+ return false;
+ }
- XML_Parse(Parser, document, len, 1);
XML_ParserFree(Parser);
- return Wrapper.Status;
+
+ if ( ! Ctx.Namespaces->empty() )
+ {
+ m_NamespaceOwner = (void*)Ctx.Namespaces;
+ }
+
+ return true;
}
+
+
#endif
//----------------------------------------------------------------------------------------------------
Kumu::XMLElement::ParseString(const char* document, ui32_t doc_len)
{
if ( doc_len == 0 )
- return false;
+ {
+ return false;
+ }
init_xml_dom();
//
bool
-Kumu::StringIsXML(const char* document, ui32_t len)
+Kumu::XMLElement::ParseFirstFromString(const char* document, ui32_t doc_len)
{
- if ( document == 0 || *document == 0 )
- return false;
+ if ( doc_len == 0 )
+ {
+ return false;
+ }
init_xml_dom();
- if ( len == 0 )
- len = strlen(document);
+ int errorCount = 0;
+ SAXParser* parser = new SAXParser();
- SAXParser parser;
+ parser->setValidationScheme(SAXParser::Val_Always);
+ parser->setDoNamespaces(true); // optional
+
+ MyTreeHandler* docHandler = new MyTreeHandler(this);
+ parser->setDocumentHandler(docHandler);
+ parser->setErrorHandler(docHandler);
XMLPScanToken token;
- bool status = false;
try
{
MemBufInputSource xmlSource(reinterpret_cast<const XMLByte*>(document),
- static_cast<const unsigned int>(len),
+ static_cast<const unsigned int>(doc_len),
"pidc_rules_file");
- if ( parser.parseFirst(xmlSource, token) )
+ if ( ! parser->parseFirst(xmlSource, token) )
{
- if ( parser.parseNext(token) )
- status = true;
+ ++errorCount;
+ }
+
+ if ( ! parser->parseNext(token) )
+ {
+ ++errorCount;
}
}
+ catch (const XMLException& e)
+ {
+ char* message = XMLString::transcode(e.getMessage());
+ DefaultLogSink().Error("Parser error: %s\n", message);
+ XMLString::release(&message);
+ errorCount++;
+ }
+ catch (const SAXParseException& e)
+ {
+ char* message = XMLString::transcode(e.getMessage());
+ DefaultLogSink().Error("Parser error: %s at line %d\n", message, e.getLineNumber());
+ XMLString::release(&message);
+ errorCount++;
+ }
catch (...)
{
+ DefaultLogSink().Error("Unexpected XML parser error\n");
+ errorCount++;
}
- return status;
+ if ( errorCount == 0 )
+ m_NamespaceOwner = (void*)docHandler->TakeNamespaceMap();
+
+ delete parser;
+ delete docHandler;
+
+ return errorCount > 0 ? false : true;
}
return false;
}
-//
bool
-Kumu::StringIsXML(const char* document, ui32_t len)
+Kumu::XMLElement::ParseFirstFromString(const char* document, ui32_t doc_len)
{
DefaultLogSink().Error("Kumu compiled without XML parser support.\n");
return false;
Kumu::GetXMLDocType(const byte_t* buf, ui32_t buf_len, std::string& ns_prefix, std::string& type_name, std::string& namespace_name,
AttributeList& doc_attr_list)
{
- assert(buf);
- const byte_t *p1 = buf, *p2;
- const byte_t *end_p = buf + buf_len;
+ XMLElement tmp_element("tmp");
- while ( p1 < end_p && *p1 )
+ if ( ! tmp_element.ParseFirstFromString((const char*)buf, buf_len) )
{
- if ( *p1 == '<' && isalpha(p1[1]) )
- {
- p2 = ++p1;
-
- // collect element name
- while ( p2 < end_p && *p2 && ! ( isspace(*p2) || *p2 == '>' ) )
- ++p2;
-
- if ( p2 < end_p )
- {
- const byte_t* separator = (byte_t*)strchr(reinterpret_cast<const char*>(p1), ':');
- if ( separator != 0 && separator < p2 )
- {
- ns_prefix.assign(reinterpret_cast<const char*>(p1), separator - p1);
- p1 = separator + 1;
- }
-
- type_name.assign(reinterpret_cast<const char*>(p1), p2 - p1);
- break;
- }
- }
- else if ( *p1 == '<' && ( ( p1 + 3 ) < end_p ) && p1[1] == '!' && p1[2] == '-' && p1[3] == '-' )
- {
- p1 += 4;
-
- for (;;)
- {
- while ( *p1 != '>' && p1 < end_p )
- {
- ++p1;
- }
-
- if ( *(p1-2) == '-' && *(p1-1) == '-' && *p1 == '>' )
- {
- break;
- }
-
- ++p1;
- }
- }
- else
- {
- ++p1;
- }
+ return false;
}
- if ( isspace(*p2) )
- {
- const byte_t *p3 = p2+1;
- while ( p3 < end_p && *p3 && *p3 != '>' )
- {
- ++p3;
- }
-
- if ( *p3 != '>' )
- {
- return false; // not well-formed XML
- }
-
- std::string attr_str;
- attr_str.assign(reinterpret_cast<const char*>(p2+1), p3 - p2 - 1);
-
- // normalize whitespace so the subesquent split works properly
- for ( int j = 0; j < attr_str.length(); ++j )
- {
- if ( attr_str[j] != ' ' && isspace(attr_str[j]) )
- {
- attr_str[j] = ' ';
- }
- }
-
- std::list<std::string> doc_attr_nvpairs = km_token_split(attr_str, " ");
-
- std::list<std::string>::iterator i;
- std::map<std::string, std::string> ns_map;
-
- for ( i = doc_attr_nvpairs.begin(); i != doc_attr_nvpairs.end(); ++i )
- {
- // trim leading and trailing whitespace an right-most character, i.e., \"
- std::string trimmed = i->substr(i->find_first_not_of(" "), i->find_last_not_of(" \r\n\t"));
- std::list<std::string> nv_tokens = km_token_split(trimmed, "=\"");
+ const XMLNamespace* ns = tmp_element.Namespace();
- if ( nv_tokens.size() != 2 )
- {
- continue;
- }
-
- NVPair nv_pair;
- nv_pair.name = nv_tokens.front();
- nv_pair.value = nv_tokens.back();
- doc_attr_list.push_back(nv_pair);
- ns_map.insert(std::map<std::string,std::string>::value_type(nv_pair.name, nv_pair.value));
- }
-
- std::string doc_ns_name_selector = ns_prefix.empty() ? "xmlns" : "xmlns:"+ns_prefix;
- std::map<std::string,std::string>::iterator j = ns_map.find(doc_ns_name_selector);
-
- if ( j != ns_map.end() )
- {
- namespace_name = j->second;
- }
+ if ( ns != 0 )
+ {
+ ns_prefix = ns->Prefix();
+ namespace_name = ns->Name();
}
- else if ( *p2 != '>' )
- {
- return false; // not well-formed XML
- }
- return ! type_name.empty();
+ type_name = tmp_element.GetName();
+ doc_attr_list = tmp_element.GetAttributes();
+ return true;
}
-
//
// end KM_xml.cpp
//
/*
-Copyright (c) 2005-2011, John Hurst
+Copyright (c) 2005-2015, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
{
class XMLElement;
- // Return true if the given string contains an XML document (or the start of one).
- bool StringIsXML(const char* document, ui32_t len = 0);
-
//
struct NVPair
{
bool ParseString(const ByteString& document);
bool ParseString(const std::string& document);
+ bool ParseFirstFromString(const char* document, ui32_t doc_len);
+ bool ParseFirstFromString(const ByteString& document);
+ bool ParseFirstFromString(const std::string& document);
+
// building
void SetName(const char* name);
void SetBody(const std::string& value);
/*
-Copyright (c) 2005-2012, John Hurst
+Copyright (c) 2005-2015, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
}
+//----------------------------------------------------------------------------------------------------
+//
+
+
+ASDCP::MXF::RGBALayout::RGBALayout()
+{
+ memset(m_value, 0, RGBAValueLength);
+}
+
+ASDCP::MXF::RGBALayout::RGBALayout(const byte_t* value)
+{
+ memcpy(m_value, value, RGBAValueLength);
+}
+
+ASDCP::MXF::RGBALayout::~RGBALayout()
+{
+}
+
+static char
+get_char_for_code(byte_t c)
+{
+ for ( int i = 0; ASDCP::MXF::RGBALayoutTable[i].code != 0; ++i )
+ {
+ if ( ASDCP::MXF::RGBALayoutTable[i].code == c )
+ {
+ return ASDCP::MXF::RGBALayoutTable[i].symbol;
+ }
+ }
+
+ return '_';
+}
+
+//
+const char*
+ASDCP::MXF::RGBALayout::EncodeString(char* buf, ui32_t buf_len) const
+{
+ std::string tmp_str;
+ char tmp_buf[64];
+
+ for ( int i = 0; i < RGBAValueLength && m_value[i] != 0; i += 2 )
+ {
+ snprintf(tmp_buf, 64, "%c(%d)", get_char_for_code(m_value[i]), m_value[i+1]);
+
+ if ( ! tmp_str.empty() )
+ {
+ tmp_str += " ";
+ }
+
+ tmp_str += tmp_buf;
+ }
+
+ assert(tmp_str.size() < buf_len);
+ strncpy(buf, tmp_str.c_str(), tmp_str.size());
+ return buf;
+}
+
+
//----------------------------------------------------------------------------------------------------
//
/*
-Copyright (c) 2005-2014, John Hurst
+Copyright (c) 2005-2015, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
}
};
+ /*
+ The RGBALayout type shall be a fixed-size 8 element sequence with a total length
+ of 16 bytes, where each element shall consist of the RGBAComponent type with the
+ following fields:
+
+ Code (UInt8): Enumerated value specifying component (i.e., component identifier).
+ "0" is the layout terminator.
+
+ Depth (UInt8): Integer specifying the number of bits occupied (see also G.2.26)
+ 1->32 indicates integer depth
+ 253 = HALF (floating point 16-bit value)
+ 254 = IEEE floating point 32-bit value
+ 255 = IEEE floating point 64-bit value
+ 0 = RGBALayout terminator
+
+ A Fill component indicates unused bits. After the components have been specified,
+ the remaining Code and Size fields shall be set to zero (0).
+
+ For each component in the Pixel, one of the following Codes or the terminator
+ shall be specified (explained below):
+
+ Code ASCII
+
+ */
+ struct RGBALayoutTableEntry
+ {
+ byte_t code;
+ char symbol;
+ const char* label;
+ };
+
+ struct RGBALayoutTableEntry const RGBALayoutTable[] = {
+ { 0x52, 'R', "Red component" },
+ { 0x47, 'G', "Green component" },
+ { 0x42, 'B', "Blue component" },
+ { 0x41, 'A', "Alpha component" },
+ { 0x72, 'r', "Red component (LSBs)" },
+ { 0x67, 'g', "Green component (LSBs)" },
+ { 0x62, 'b', "Blue component (LSBs)" },
+ { 0x61, 'a', "Alpha component (LSBs)" },
+ { 0x46, 'F', "Fill component" },
+ { 0x50, 'P', "Palette code" },
+ { 0x55, 'U', "Color Difference Sample (e.g. U, Cb, I etc.)" },
+ { 0x56, 'V', "Color Difference Sample (e.g. V, Cr, Q etc.)" },
+ { 0x57, 'W', "Composite Video" },
+ { 0x58, 'X', "Non co-sited luma component" },
+ { 0x59, 'Y', "Luma component" },
+ { 0x5a, 'Z', "Depth component (SMPTE ST 268 compatible)" },
+ { 0x75, 'u', "Color Difference Sample (e.g. U, Cb, I etc.) (LSBs)" },
+ { 0x76, 'v', "Color Difference Sample (e.g. V, Cr, Q etc.) (LSBs)" },
+ { 0x77, 'w', "Composite Video (LSBs)" },
+ { 0x78, 'x', "Non co-sited luma component (LSBs)" },
+ { 0x79, 'y', "Luma component (LSBs)" },
+ { 0x7a, 'z', "Depth component (LSBs) (SMPTE ST 268 compatible)" },
+ { 0xd8, 'X', "The DCDM X color component (see SMPTE ST 428-1 X')" },
+ { 0xd9, 'Y', "The DCDM Y color component (see SMPTE ST 428-1 Y')" },
+ { 0xda, 'Z', "The DCDM Z color component (see SMPTE ST 428-1 Z')" },
+ { 0x00, '_', "Terminator" }
+ };
+
+
+ size_t const RGBAValueLength = 16;
+
+ byte_t const RGBAValue_RGB_10[RGBAValueLength] = { 'R', 10, 'G', 10, 'B', 10, 0, 0 };
+ byte_t const RGBAValue_RGB_8[RGBAValueLength] = { 'R', 8, 'G', 8, 'B', 8, 0, 0 };
+ byte_t const RGBAValue_YUV_10[RGBAValueLength] = { 'Y', 10, 'U', 10, 'V', 10, 0, 0 };
+ byte_t const RGBAValue_YUV_8[RGBAValueLength] = { 'Y', 8, 'U', 8, 'V', 8, 0, 0 };
+ byte_t const RGBAValue_DCDM[RGBAValueLength] = { 0xd8, 10, 0xd9, 10, 0xda, 10, 0, 0 };
+
+
+ class RGBALayout : public Kumu::IArchive
+ {
+ byte_t m_value[RGBAValueLength];
+
+ public:
+ RGBALayout();
+ RGBALayout(const byte_t* value);
+ ~RGBALayout();
+
+ RGBALayout(const RGBALayout& rhs) { Set(rhs.m_value); }
+ const RGBALayout& operator=(const RGBALayout& rhs) { Set(rhs.m_value); return *this; }
+
+ void Set(const byte_t* value) {
+ memcpy(m_value, value, RGBAValueLength);
+ }
+
+ const char* EncodeString(char* buf, ui32_t buf_len) const;
+
+ bool HasValue() const { return true; }
+ ui32_t ArchiveLength() const { return RGBAValueLength; }
+
+ bool Archive(Kumu::MemIOWriter* Writer) const {
+ return Writer->WriteRaw(m_value, RGBAValueLength);
+ }
+
+ bool Unarchive(Kumu::MemIOReader* Reader) {
+ if ( Reader->Remainder() < RGBAValueLength )
+ {
+ return false;
+ }
+
+ memcpy(m_value, Reader->CurrentData(), RGBAValueLength);
+ Reader->SkipOffset(RGBAValueLength);
+ return true;
+ }
+ };
+
+
//
class Raw : public Kumu::ByteString
{
/*
-Copyright (c) 2005-2015, John Hurst
+Copyright (c) 2005-2012, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
/*
-Copyright (c) 2005-2015, John Hurst
+Copyright (c) 2005-2012, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
optional_property<Raw> PictureComponentSizing;
optional_property<Raw> CodingStyleDefault;
optional_property<Raw> QuantizationDefault;
- optional_property<Raw> J2CLayout;
+ optional_property<RGBALayout> J2CLayout;
JPEG2000PictureSubDescriptor(const Dictionary*& d);
JPEG2000PictureSubDescriptor(const JPEG2000PictureSubDescriptor& rhs);
/*
-Copyright (c) 2004-2014, John Hurst
+Copyright (c) 2004-2015, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
{
ASDCP_TEST_NULL(p);
- memcpy(p, m_p, m_SampleSize);
- m_p += m_SampleSize;
- return RESULT_OK;
-}
+ if ( m_p != 0 )
+ {
+ if ( m_p < ( FB.RoData() + FB.Size() ) )
+ {
+ memcpy(p, m_p, m_SampleSize);
+ m_p += m_SampleSize;
+ return RESULT_OK;
+ }
+ }
+ return RESULT_ENDOFFILE;
+}
//
Result_t
Result_t result = RESULT_OK;
if ( size() == 1 )
- return front()->Parser.ReadFrame(OutFB);
+ {
+ return front()->Parser.ReadFrame(OutFB);
+ }
PCMParserList::iterator self_i;
assert(PCM::CalcFrameBufferSize(m_ADesc) <= OutFB.Capacity());
for ( self_i = begin(); self_i != end() && ASDCP_SUCCESS(result) ; self_i++ )
- result = (*self_i)->ReadFrame();
+ {
+ result = (*self_i)->ReadFrame();
+ }
if ( ASDCP_SUCCESS(result) )
{
- OutFB.Size(PCM::CalcFrameBufferSize(m_ADesc));
-
- // ui32_t sample_size = (PCM::CalcSampleSize(m_ADesc));
byte_t* Out_p = OutFB.Data();
- byte_t* End_p = Out_p + OutFB.Size();
+ byte_t* End_p = Out_p + OutFB.Capacity();
+ ui64_t total_sample_bytes = 0;
while ( Out_p < End_p && ASDCP_SUCCESS(result) )
{
while ( self_i != end() && ASDCP_SUCCESS(result) )
{
result = (*self_i)->PutSample(Out_p);
- Out_p += (*self_i)->SampleSize();
- self_i++;
+
+ if ( ASDCP_SUCCESS(result) )
+ {
+ Out_p += (*self_i)->SampleSize();
+ total_sample_bytes += (*self_i)->SampleSize();
+ self_i++;
+ }
}
}
- assert(Out_p == End_p);
+ OutFB.Size(total_sample_bytes);
+
+ if ( result == RESULT_ENDOFFILE )
+ {
+ result = RESULT_OK;
+ }
}
return result;
m_ADesc.ChannelFormat = PCM::CF_NONE;
Reset();
}
- else
- {
- SimpleRF64Header RF64Header;
- m_FileReader.Seek(0);
- result = RF64Header.ReadFromFile(m_FileReader, &m_DataStart);
-
- if ( ASDCP_SUCCESS(result) )
- {
- RF64Header.FillADesc(m_ADesc, PictureRate);
- m_FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(m_ADesc);
- m_DataLength = RF64Header.data_len;
- m_ADesc.ContainerDuration = m_DataLength / m_FrameBufferSize;
- m_ADesc.ChannelFormat = PCM::CF_NONE;
- Reset();
- }
- }
+ else
+ {
+ SimpleRF64Header RF64Header;
+ m_FileReader.Seek(0);
+ result = RF64Header.ReadFromFile(m_FileReader, &m_DataStart);
+
+ if ( ASDCP_SUCCESS(result) )
+ {
+ RF64Header.FillADesc(m_ADesc, PictureRate);
+ m_FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(m_ADesc);
+ m_DataLength = RF64Header.data_len;
+ m_ADesc.ContainerDuration = m_DataLength / m_FrameBufferSize;
+ m_ADesc.ChannelFormat = PCM::CF_NONE;
+ Reset();
+ }
+ }
}
}
{
FB.Size(0);
- if ( m_EOF || m_ReadCount >= m_DataLength )
- return RESULT_ENDOFFILE;
+ if ( m_EOF )
+ {
+ return RESULT_ENDOFFILE;
+ }
if ( FB.Capacity() < m_FrameBufferSize )
{
m_EOF = true;
if ( read_count > 0 )
- result = RESULT_OK;
+ {
+ result = RESULT_OK;
+ }
}
if ( ASDCP_SUCCESS(result) )
m_ReadCount += read_count;
FB.Size(read_count);
FB.FrameNumber(m_FramesRead++);
+
+ if ( read_count < FB.Capacity() )
+ {
+ memset(FB.Data() + FB.Size(), 0, FB.Capacity() - FB.Size());
+ }
}
return result;
/*
-Copyright (c) 2011-2014, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+Copyright (c) 2011-2015, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
John Hurst
All rights reserved.
{
fprintf(stream, "\n\
%s (asdcplib %s)\n\n\
-Copyright (c) 2011-2014, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
+Copyright (c) 2011-2015, 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",
FrameBuffer.Dump(stderr, Options.fb_dump_size);
}
+ if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
+ {
+ fprintf(stderr, "Last frame is incomplete, padding with zeros.\n");
+ // actually, it has already been zeroed for us, we just need to recognize the appropriate size
+ FrameBuffer.Size(FrameBuffer.Capacity());
+ }
+
result = OutWave.WriteFrame(FrameBuffer);
}
}
break;
default:
- fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.input_filename);
+ fprintf(stderr, "%s: Unknown file type, not AS-02 essence.\n", Options.input_filename);
return 5;
}
}
/*
-Copyright (c) 2011-2014, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+Copyright (c) 2011-2015, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
John Hurst
All rights reserved.
{
fprintf(stream, "\n\
%s (asdcplib %s)\n\n\
-Copyright (c) 2011-2014, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
+Copyright (c) 2011-2015, 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",
{
assert(str_rat);
ui32_t Num = atoi(str_rat);
- ui32_t Den = 0;
+ ui32_t Den = 1;
const char* den_str = strrchr(str_rat, '/');
if ( den_str != 0 )
if ( ASDCP_SUCCESS(result) )
{
- if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
- {
- fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
- fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
- result = RESULT_ENDOFFILE;
- continue;
- }
-
if ( Options.verbose_flag )
FrameBuffer.Dump(stderr, Options.fb_dump_size);
{
fprintf(stream, "\n\
%s (asdcplib %s)\n\n\
-Copyright (c) 2003-2014 John Hurst\n\n\
+Copyright (c) 2003-2015 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",
{
fprintf(stream, "\n\
%s (asdcplib %s)\n\n\
-Copyright (c) 2003-2014 John Hurst\n\n\
+Copyright (c) 2003-2015 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",
{
fprintf(stream, "\n\
%s (asdcplib %s)\n\n\
-Copyright (c) 2003-2014 John Hurst\n\n\
+Copyright (c) 2003-2015 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",
/*
-Copyright (c) 2003-2014, John Hurst
+Copyright (c) 2003-2015, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
{
fprintf(stream, "\n\
%s (asdcplib %s)\n\n\
-Copyright (c) 2003-2014 John Hurst\n\n\
+Copyright (c) 2003-2015 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 version_flag; // true if the version display option was selected
bool help_flag; // true if the help display option was selected
bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
+ bool write_partial_pcm_flag; // if true, write the last frame of PCM input even when it is incomplete
ui32_t start_frame; // frame number to begin processing
ui32_t duration; // number of frames to be processed
bool use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
encrypt_header_flag(true), write_hmac(true),
verbose_flag(false), fb_dump_size(0),
no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false),
- start_frame(0),
+ write_partial_pcm_flag(false), start_frame(0),
duration(0xffffffff), use_smpte_labels(false), j2c_pedantic(true),
fb_size(FRAME_BUFFER_SIZE),
channel_fmt(PCM::CF_NONE),
start_frame = abs(atoi(argv[i]));
break;
+ case 'g': write_partial_pcm_flag = true; break;
case 'h': help_flag = true; break;
case 'j': key_id_flag = true;
{
fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
- result = RESULT_ENDOFFILE;
- continue;
+
+ if ( Options.write_partial_pcm_flag )
+ {
+ result = RESULT_ENDOFFILE;
+ continue;
+ }
}
if ( Options.verbose_flag )
{
fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
- result = RESULT_ENDOFFILE;
- continue;
+
+ if ( Options.write_partial_pcm_flag )
+ {
+ result = RESULT_ENDOFFILE;
+ continue;
+ }
}
if ( Options.verbose_flag )
{
fprintf(stream, "\n\
%s (asdcplib %s)\n\n\
-Copyright (c) 2011-2014, John Hurst\n\n\
+Copyright (c) 2011-2015, 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",
{
fprintf(stream, "\n\
%s (asdcplib %s)\n\n\
-Copyright (c) 2011-2014, John Hurst\n\n\
+Copyright (c) 2011-2015, 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",
\r
!ifdef ENABLE_RANDOM_UUID\r
CXXFLAGS1 = /nologo /W3 /GR /EHsc /DWIN32 /DKM_WIN32 /D_CONSOLE /I. /I$(SRCDIR) /DASDCP_PLATFORM=\"win32\" \\r
- /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /DPACKAGE_VERSION=\"2.1.1\" \\r
+ /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /DPACKAGE_VERSION=\"2.4.9\" \\r
/I"$(WITH_OPENSSL)"\inc32 /DCONFIG_RANDOM_UUID=1\r
!else\r
CXXFLAGS1 = /nologo /W3 /GR /EHsc /DWIN32 /DKM_WIN32 /D_CONSOLE /I. /I$(SRCDIR) /DASDCP_PLATFORM=\"win32\" \\r
- /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /DPACKAGE_VERSION=\"2.1.1\" \\r
+ /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /DPACKAGE_VERSION=\"2.4.9\" \\r
/I"$(WITH_OPENSSL)"\inc32\r
!endif\r
LIB_EXE = lib.exe\r