From 252740d6f7d8924c6af30e55d2da487356a0acdc Mon Sep 17 00:00:00 2001 From: mikey Date: Fri, 12 Apr 2013 23:39:31 +0000 Subject: [PATCH] Added atmos support and new ULs per SMPTE 429-2:2013 - see README for deets. --- README | 12 + build-aux/config.guess | 22 +- build-aux/config.sub | 30 +- build-aux/depcomp | 182 +++++++--- build-aux/install-sh | 14 +- build-aux/ltmain.sh | 3 +- build-aux/missing | 99 +++--- configure.ac | 2 +- src/AS_DCP.h | 393 +++++++++++++++++--- src/AS_DCP_ATMOS.cpp | 455 +++++++++++++++++++++++ src/AS_DCP_DCData.cpp | 554 +++++++++++++++++++++++++++++ src/AS_DCP_DCData_internal.h | 94 +++++ src/AS_DCP_JP2K.cpp | 32 ++ src/AS_DCP_MPEG2.cpp | 9 + src/AS_DCP_MXF.cpp | 37 ++ src/AS_DCP_PCM.cpp | 42 ++- src/AS_DCP_TimedText.cpp | 4 +- src/AS_DCP_internal.h | 43 ++- src/AtmosSyncChannel_Generator.cpp | 149 ++++++++ src/AtmosSyncChannel_Generator.h | 151 ++++++++ src/AtmosSyncChannel_Mixer.cpp | 326 +++++++++++++++++ src/AtmosSyncChannel_Mixer.h | 91 +++++ src/CRC16.c | 86 +++++ src/CRC16.h | 45 +++ src/DCData_ByteStream_Parser.cpp | 121 +++++++ src/DCData_Sequence_Parser.cpp | 294 +++++++++++++++ src/Index.cpp | 2 +- src/MDD.cpp | 81 ++++- src/MDD.h | 106 +++--- src/MXF.cpp | 37 +- src/Makefile.am | 14 +- src/Metadata.cpp | 168 +++++++++ src/Metadata.h | 49 +++ src/PCMDataProviders.cpp | 210 +++++++++++ src/PCMDataProviders.h | 119 +++++++ src/PCM_Parser.cpp | 21 +- src/SyncCommon.h | 80 +++++ src/SyncEncoder.c | 346 ++++++++++++++++++ src/SyncEncoder.h | 86 +++++ src/UUIDInformation.c | 119 +++++++ src/UUIDInformation.h | 58 +++ src/Wav.cpp | 225 +++++++++++- src/Wav.h | 39 ++ src/WavFileWriter.h | 46 ++- src/asdcp-info.cpp | 48 ++- src/asdcp-unwrap.cpp | 192 ++++++++-- src/asdcp-wrap.cpp | 302 ++++++++++++++-- src/blackwave.cpp | 4 +- src/h__Reader.cpp | 35 +- win32/Makefile.mak | 4 +- 50 files changed, 5360 insertions(+), 321 deletions(-) create mode 100644 src/AS_DCP_ATMOS.cpp create mode 100644 src/AS_DCP_DCData.cpp create mode 100644 src/AS_DCP_DCData_internal.h create mode 100644 src/AtmosSyncChannel_Generator.cpp create mode 100644 src/AtmosSyncChannel_Generator.h create mode 100644 src/AtmosSyncChannel_Mixer.cpp create mode 100644 src/AtmosSyncChannel_Mixer.h create mode 100644 src/CRC16.c create mode 100644 src/CRC16.h create mode 100644 src/DCData_ByteStream_Parser.cpp create mode 100644 src/DCData_Sequence_Parser.cpp create mode 100644 src/PCMDataProviders.cpp create mode 100644 src/PCMDataProviders.h create mode 100644 src/SyncCommon.h create mode 100644 src/SyncEncoder.c create mode 100644 src/SyncEncoder.h create mode 100644 src/UUIDInformation.c create mode 100644 src/UUIDInformation.h diff --git a/README b/README index ccdc4d2..7def63f 100755 --- a/README +++ b/README @@ -139,6 +139,18 @@ command-line utilities all respond to -h. Change History +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 + and also Dolby Atmos track file support as a specialization. + o Added Dolby-contributed code to support generating the external + sync signal for d-cinema as proposed in ST 21DC. + o Added Dolby-contributed code to support RF64 WAVE files. + o Fixed UL error in ST 429-5 DM encoding (contributed by Dolby). + 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 diff --git a/build-aux/config.guess b/build-aux/config.guess index 49ba16f..aa04f04 100755 --- a/build-aux/config.guess +++ b/build-aux/config.guess @@ -4,7 +4,7 @@ # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012 Free Software Foundation, Inc. -timestamp='2012-01-01' +timestamp='2012-06-17' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -17,9 +17,7 @@ timestamp='2012-01-01' # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA -# 02110-1301, USA. +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -202,6 +200,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} @@ -863,6 +865,13 @@ EOF i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; @@ -1251,7 +1260,7 @@ EOF NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; - NSE-?:NONSTOP_KERNEL:*:*) + NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) @@ -1320,6 +1329,9 @@ EOF i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; + x86_64:VMkernel:*:*) + echo ${UNAME_MACHINE}-unknown-esx + exit ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 diff --git a/build-aux/config.sub b/build-aux/config.sub index d6b6b3c..aa2cf19 100755 --- a/build-aux/config.sub +++ b/build-aux/config.sub @@ -4,7 +4,7 @@ # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012 Free Software Foundation, Inc. -timestamp='2012-01-01' +timestamp='2012-06-17' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software @@ -21,9 +21,7 @@ timestamp='2012-01-01' # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA -# 02110-1301, USA. +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -132,6 +130,10 @@ case $maybe_os in os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] @@ -223,6 +225,12 @@ case $os in -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; -lynx*) os=-lynxos ;; @@ -247,6 +255,7 @@ case $basic_machine in # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ + | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ @@ -319,7 +328,7 @@ case $basic_machine in c6x) basic_machine=tic6x-unknown ;; - m6811 | m68hc11 | m6812 | m68hc12 | picochip) + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) basic_machine=$basic_machine-unknown os=-none ;; @@ -332,7 +341,10 @@ case $basic_machine in strongarm | thumb | xscale) basic_machine=arm-unknown ;; - + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; xscaleeb) basic_machine=armeb-unknown ;; @@ -355,6 +367,7 @@ case $basic_machine in # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ + | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ @@ -1339,7 +1352,7 @@ case $os in | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -openbsd* | -solidbsd* \ + | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ @@ -1530,6 +1543,9 @@ case $basic_machine in c4x-* | tic4x-*) os=-coff ;; + hexagon-*) + os=-elf + ;; tic54x-*) os=-coff ;; diff --git a/build-aux/depcomp b/build-aux/depcomp index bd0ac08..0544c68 100755 --- a/build-aux/depcomp +++ b/build-aux/depcomp @@ -1,10 +1,9 @@ #! /bin/sh # depcomp - compile a program generating dependencies as side-effects -scriptversion=2011-12-04.11; # UTC +scriptversion=2012-07-12.20; # UTC -# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010, -# 2011 Free Software Foundation, Inc. +# Copyright (C) 1999-2012 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -28,7 +27,7 @@ scriptversion=2011-12-04.11; # UTC case $1 in '') - echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) @@ -40,8 +39,8 @@ as side-effects. Environment variables: depmode Dependency tracking mode. - source Source file read by `PROGRAMS ARGS'. - object Object file output by `PROGRAMS ARGS'. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. @@ -57,6 +56,12 @@ EOF ;; esac +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' + if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 @@ -102,6 +107,12 @@ if test "$depmode" = msvc7msys; then depmode=msvc7 fi +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency informations. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what @@ -156,15 +167,14 @@ gcc) ## The second -e expression handles DOS-style file names with drive letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" -## This next piece of magic avoids the `deleted header file' problem. +## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. - tr ' ' ' -' < "$tmpdepfile" | -## Some versions of gcc put a space before the `:'. On the theory + tr ' ' "$nl" < "$tmpdepfile" | +## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. @@ -203,18 +213,15 @@ sgi) # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; - # the IRIX cc adds comments like `#:fec' to the end of the + # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. - tr ' ' ' -' < "$tmpdepfile" \ + tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ - tr ' -' ' ' >> "$depfile" + tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. - tr ' ' ' -' < "$tmpdepfile" \ + tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else @@ -226,10 +233,17 @@ sgi) rm -f "$tmpdepfile" ;; +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the - # current directory. Also, the AIX compiler puts `$object:' at the + # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` @@ -259,12 +273,11 @@ aix) test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then - # Each line is of the form `foo.o: dependent.h'. + # Each line is of the form 'foo.o: dependent.h'. # Do two passes, one to just change these to - # `$object: dependent.h' and one to simply `dependent.h:'. + # '$object: dependent.h' and one to simply 'dependent.h:'. sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" - # That's a tab and a space in the []. - sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" else # The sourcefile does not contain any dependencies, so just # store a dummy comment line, to avoid errors with the Makefile @@ -275,23 +288,26 @@ aix) ;; icc) - # Intel's C compiler understands `-MD -MF file'. However on - # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c + # Intel's C compiler anf tcc (Tiny C Compiler) understand '-MD -MF file'. + # However on + # $CC -MD -MF foo.d -c -o sub/foo.o sub/foo.c # ICC 7.0 will fill foo.d with something like # foo.o: sub/foo.c # foo.o: sub/foo.h - # which is wrong. We want: + # which is wrong. We want # sub/foo.o: sub/foo.c # sub/foo.o: sub/foo.h # sub/foo.c: # sub/foo.h: # ICC 7.1 will output # foo.o: sub/foo.c sub/foo.h - # and will wrap long lines using \ : + # and will wrap long lines using '\': # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... - + # tcc 0.9.26 (FIXME still under development at the moment of writing) + # will emit a similar output, but also prepend the continuation lines + # with horizontal tabulation characters. "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -eq 0; then : @@ -300,6 +316,85 @@ icc) exit $stat fi rm -f "$depfile" + # Each line is of the form 'foo.o: dependent.h', + # or 'foo.o: dep1.h dep2.h \', or ' dep3.h dep4.h \'. + # Do two passes, one to just change these to + # '$object: dependent.h' and one to simply 'dependent.h:'. + sed -e "s/^[ $tab][ $tab]*/ /" -e "s,^[^:]*:,$object :," \ + < "$tmpdepfile" > "$depfile" + sed ' + s/[ '"$tab"'][ '"$tab"']*/ /g + s/^ *// + s/ *\\*$// + s/^[^:]*: *// + /^$/d + /:$/d + s/$/ :/ + ' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + base=`echo "$source" | sed -e 's|^.*/||' -e 's/\.[-_a-zA-Z0-9]*$//'` + tmpdepfile="$base.d" + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir="$base.d-lock" + trap "echo '$0: caught signal, cleaning up...' >&2; rm -rf $lockdir" 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0 ; do + # mkdir is a portable test-and-set. + if mkdir $lockdir 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rm -rf $lockdir + break + else + ## the lock is being held by a different process, + ## wait until the winning process is done or we timeout + while test -d $lockdir && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to @@ -344,7 +439,7 @@ hp2) done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile" - # Add `dependent.h:' lines. + # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// @@ -359,9 +454,9 @@ hp2) tru64) # The Tru64 compiler uses -MD to generate dependencies as a side - # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put - # dependencies in `foo.d' instead, so we check for that too. + # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` test "x$dir" = "x$object" && dir= @@ -407,8 +502,7 @@ tru64) done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" - # That's a tab and a space in the []. - sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" else echo "#dummy" > "$depfile" fi @@ -443,11 +537,11 @@ msvc7) p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g -s/\(.*\)/ \1 \\/p +s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { - s/.*/ / + s/.*/'"$tab"'/ G p }' >> "$depfile" @@ -478,7 +572,7 @@ dashmstdout) shift fi - # Remove `-o $object'. + # Remove '-o $object'. IFS=" " for arg do @@ -498,15 +592,14 @@ dashmstdout) done test -z "$dashmflag" && dashmflag=-M - # Require at least two characters before searching for `:' + # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: - # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | - sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + sed 's:^['"$tab"' ]*[^:'"$tab"' ][^:][^:]*\:['"$tab"' ]*:'"$object"'\: :' > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" - tr ' ' ' -' < "$tmpdepfile" | \ + tr ' ' "$nl" < "$tmpdepfile" | \ ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" @@ -562,8 +655,7 @@ makedepend) # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" - sed '1,2d' "$tmpdepfile" | tr ' ' ' -' | \ + sed '1,2d' "$tmpdepfile" | tr ' ' "$nl" | \ ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" @@ -583,7 +675,7 @@ cpp) shift fi - # Remove `-o $object'. + # Remove '-o $object'. IFS=" " for arg do @@ -652,8 +744,8 @@ msvisualcpp) sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" - sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" - echo " " >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; diff --git a/build-aux/install-sh b/build-aux/install-sh index a9244eb..377bb86 100755 --- a/build-aux/install-sh +++ b/build-aux/install-sh @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2011-01-19.21; # UTC +scriptversion=2011-11-20.07; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -35,7 +35,7 @@ scriptversion=2011-01-19.21; # UTC # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent -# `make' implicit rules from creating a file called install from it +# 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written @@ -156,7 +156,7 @@ while test $# -ne 0; do -s) stripcmd=$stripprog;; -t) dst_arg=$2 - # Protect names problematic for `test' and other utilities. + # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac @@ -190,7 +190,7 @@ if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then fi shift # arg dst_arg=$arg - # Protect names problematic for `test' and other utilities. + # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac @@ -202,7 +202,7 @@ if test $# -eq 0; then echo "$0: no input file specified." >&2 exit 1 fi - # It's OK to call `install-sh -d' without argument. + # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi @@ -240,7 +240,7 @@ fi for src do - # Protect names problematic for `test' and other utilities. + # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac @@ -354,7 +354,7 @@ do if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writeable bit of parent directory when it shouldn't. + # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in diff --git a/build-aux/ltmain.sh b/build-aux/ltmain.sh index 63ae69d..9ae038c 100644 --- a/build-aux/ltmain.sh +++ b/build-aux/ltmain.sh @@ -5851,9 +5851,10 @@ func_mode_link () # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + # -stdlib=* select c++ std lib with clang -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ - -O*|-flto*|-fwhopr*|-fuse-linker-plugin) + -O*|-flto*|-fwhopr*|-fuse-linker-plugin|-stdlib=*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" func_append compile_command " $arg" diff --git a/build-aux/missing b/build-aux/missing index 86a8fc3..9a55648 100755 --- a/build-aux/missing +++ b/build-aux/missing @@ -1,10 +1,9 @@ #! /bin/sh # Common stub for a few missing GNU programs while installing. -scriptversion=2012-01-06.13; # UTC +scriptversion=2012-01-06.18; # UTC -# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006, -# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. +# Copyright (C) 1996-2012 Free Software Foundation, Inc. # Originally by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify @@ -26,7 +25,7 @@ scriptversion=2012-01-06.13; # UTC # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then - echo 1>&2 "Try \`$0 --help' for more information" + echo 1>&2 "Try '$0 --help' for more information" exit 1 fi @@ -34,7 +33,7 @@ run=: sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' sed_minuso='s/.* -o \([^ ]*\).*/\1/p' -# In the cases where this matters, `missing' is being run in the +# In the cases where this matters, 'missing' is being run in the # srcdir already. if test -f configure.ac; then configure_ac=configure.ac @@ -65,7 +64,7 @@ case $1 in echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... -Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +Handle 'PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an error status if there is no known handling for PROGRAM. Options: @@ -74,20 +73,20 @@ Options: --run try to run the given command, and emulate it if it fails Supported PROGRAM values: - aclocal touch file \`aclocal.m4' - autoconf touch file \`configure' - autoheader touch file \`config.h.in' + aclocal touch file 'aclocal.m4' + autoconf touch file 'configure' + autoheader touch file 'config.h.in' autom4te touch the output file, or create a stub one - automake touch all \`Makefile.in' files - bison create \`y.tab.[ch]', if possible, from existing .[ch] - flex create \`lex.yy.c', if possible, from existing .c + automake touch all 'Makefile.in' files + bison create 'y.tab.[ch]', if possible, from existing .[ch] + flex create 'lex.yy.c', if possible, from existing .c help2man touch the output file - lex create \`lex.yy.c', if possible, from existing .c + lex create 'lex.yy.c', if possible, from existing .c makeinfo touch the output file - yacc create \`y.tab.[ch]', if possible, from existing .[ch] + yacc create 'y.tab.[ch]', if possible, from existing .[ch] -Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and -\`g' are ignored when checking the name. +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. Send bug reports to ." exit $? @@ -99,8 +98,8 @@ Send bug reports to ." ;; -*) - echo 1>&2 "$0: Unknown \`$1' option" - echo 1>&2 "Try \`$0 --help' for more information" + echo 1>&2 "$0: Unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; @@ -127,7 +126,7 @@ case $1 in exit 1 elif test "x$2" = "x--version" || test "x$2" = "x--help"; then # Could not run --version or --help. This is probably someone - # running `$TOOL --version' or `$TOOL --help' to check whether + # running '$TOOL --version' or '$TOOL --help' to check whether # $TOOL exists and not knowing $TOOL uses missing. exit 1 fi @@ -139,27 +138,27 @@ esac case $program in aclocal*) echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`acinclude.m4' or \`${configure_ac}'. You might want - to install the \`Automake' and \`Perl' packages. Grab them from +WARNING: '$1' is $msg. You should only need it if + you modified 'acinclude.m4' or '${configure_ac}'. You might want + to install the Automake and Perl packages. Grab them from any GNU archive site." touch aclocal.m4 ;; autoconf*) echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`${configure_ac}'. You might want to install the - \`Autoconf' and \`GNU m4' packages. Grab them from any GNU +WARNING: '$1' is $msg. You should only need it if + you modified '${configure_ac}'. You might want to install the + Autoconf and GNU m4 packages. Grab them from any GNU archive site." touch configure ;; autoheader*) echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`acconfig.h' or \`${configure_ac}'. You might want - to install the \`Autoconf' and \`GNU m4' packages. Grab them +WARNING: '$1' is $msg. You should only need it if + you modified 'acconfig.h' or '${configure_ac}'. You might want + to install the Autoconf and GNU m4 packages. Grab them from any GNU archive site." files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` test -z "$files" && files="config.h" @@ -176,9 +175,9 @@ WARNING: \`$1' is $msg. You should only need it if automake*) echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. - You might want to install the \`Automake' and \`Perl' packages. +WARNING: '$1' is $msg. You should only need it if + you modified 'Makefile.am', 'acinclude.m4' or '${configure_ac}'. + You might want to install the Automake and Perl packages. Grab them from any GNU archive site." find . -type f -name Makefile.am -print | sed 's/\.am$/.in/' | @@ -187,10 +186,10 @@ WARNING: \`$1' is $msg. You should only need it if autom4te*) echo 1>&2 "\ -WARNING: \`$1' is needed, but is $msg. +WARNING: '$1' is needed, but is $msg. You might have modified some files without having the proper tools for further handling them. - You can get \`$1' as part of \`Autoconf' from any GNU + You can get '$1' as part of Autoconf from any GNU archive site." file=`echo "$*" | sed -n "$sed_output"` @@ -210,10 +209,10 @@ WARNING: \`$1' is needed, but is $msg. bison*|yacc*) echo 1>&2 "\ -WARNING: \`$1' $msg. You should only need it if - you modified a \`.y' file. You may need the \`Bison' package +WARNING: '$1' $msg. You should only need it if + you modified a '.y' file. You may need the Bison package in order for those modifications to take effect. You can get - \`Bison' from any GNU archive site." + Bison from any GNU archive site." rm -f y.tab.c y.tab.h if test $# -ne 1; then eval LASTARG=\${$#} @@ -240,10 +239,10 @@ WARNING: \`$1' $msg. You should only need it if lex*|flex*) echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified a \`.l' file. You may need the \`Flex' package +WARNING: '$1' is $msg. You should only need it if + you modified a '.l' file. You may need the Flex package in order for those modifications to take effect. You can get - \`Flex' from any GNU archive site." + Flex from any GNU archive site." rm -f lex.yy.c if test $# -ne 1; then eval LASTARG=\${$#} @@ -263,10 +262,10 @@ WARNING: \`$1' is $msg. You should only need it if help2man*) echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if +WARNING: '$1' is $msg. You should only need it if you modified a dependency of a manual page. You may need the - \`Help2man' package in order for those modifications to take - effect. You can get \`Help2man' from any GNU archive site." + Help2man package in order for those modifications to take + effect. You can get Help2man from any GNU archive site." file=`echo "$*" | sed -n "$sed_output"` test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` @@ -281,12 +280,12 @@ WARNING: \`$1' is $msg. You should only need it if makeinfo*) echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified a \`.texi' or \`.texinfo' file, or any other file +WARNING: '$1' is $msg. You should only need it if + you modified a '.texi' or '.texinfo' file, or any other file indirectly affecting the aspect of the manual. The spurious - call might also be the consequence of using a buggy \`make' (AIX, - DU, IRIX). You might want to install the \`Texinfo' package or - the \`GNU make' package. Grab either from any GNU archive site." + call might also be the consequence of using a buggy 'make' (AIX, + DU, IRIX). You might want to install the Texinfo package or + the GNU make package. Grab either from any GNU archive site." # The file to touch is that specified with -o ... file=`echo "$*" | sed -n "$sed_output"` test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` @@ -310,12 +309,12 @@ WARNING: \`$1' is $msg. You should only need it if *) echo 1>&2 "\ -WARNING: \`$1' is needed, and is $msg. +WARNING: '$1' is needed, and is $msg. You might have modified some files without having the - proper tools for further handling them. Check the \`README' file, + proper tools for further handling them. Check the 'README' file, it often tells you about the needed prerequisites for installing this package. You may also peek at any GNU archive site, in case - some other package would contain this missing \`$1' program." + some other package would contain this missing '$1' program." exit 1 ;; esac diff --git a/configure.ac b/configure.ac index 0c9af7f..59d982f 100644 --- a/configure.ac +++ b/configure.ac @@ -37,7 +37,7 @@ AC_PREREQ([2.59]) # 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.10.48], [asdcplib@cinecert.com]) +AC_INIT([asdcplib], [1.11.49], [asdcplib@cinecert.com]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_SRCDIR([src/KM_error.h]) diff --git a/src/AS_DCP.h b/src/AS_DCP.h index c0556ba..b9aba83 100755 --- a/src/AS_DCP.h +++ b/src/AS_DCP.h @@ -1,5 +1,5 @@ /* -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 @@ -25,7 +25,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*! \file AS_DCP.h - \version $Id$ + \version $Id$ \brief AS-DCP library, public interface The asdcplib library is a set of file access objects that offer simplified @@ -87,6 +87,7 @@ This project depends upon the following libraries: #define _AS_DCP_H_ #include +#include #include #include #include @@ -203,13 +204,15 @@ namespace ASDCP { // The file accessors in this library implement a bounded set of essence types. // This list will be expanded when support for new types is added to the library. enum EssenceType_t { - ESS_UNKNOWN, // the file is not a supported AS-DCP essence container - ESS_MPEG2_VES, // the file contains an MPEG video elementary stream - ESS_JPEG_2000, // the file contains one or more JPEG 2000 codestreams - ESS_PCM_24b_48k, // the file contains one or more PCM audio pairs - ESS_PCM_24b_96k, // the file contains one or more PCM audio pairs - ESS_TIMED_TEXT, // the file contains an XML timed text document and one or more resources - ESS_JPEG_2000_S, // the file contains one or more JPEG 2000 codestream pairs (stereoscopic) + ESS_UNKNOWN, // the file is not a supported AS-DCP essence container + ESS_MPEG2_VES, // the file contains an MPEG video elementary stream + ESS_JPEG_2000, // the file contains one or more JPEG 2000 codestreams + ESS_PCM_24b_48k, // the file contains one or more PCM audio pairs + ESS_PCM_24b_96k, // the file contains one or more PCM audio pairs + ESS_TIMED_TEXT, // the file contains an XML timed text document and one or more resources + 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 }; // Determine the type of essence contained in the given MXF file. RESULT_OK @@ -253,7 +256,7 @@ namespace ASDCP { if ( Numerator == rhs.Numerator && Denominator < rhs.Denominator ) return true; return false; } - + inline bool operator>(const Rational& rhs) { if ( Numerator > rhs.Numerator ) return true; if ( Numerator == rhs.Numerator && Denominator > rhs.Denominator ) return true; @@ -365,7 +368,7 @@ namespace ASDCP { static byte_t default_ProductUUID_Data[UUIDlen] = { 0x43, 0x05, 0x9a, 0x1d, 0x04, 0x32, 0x41, 0x01, 0xb8, 0x3f, 0x73, 0x68, 0x15, 0xac, 0xf3, 0x1d }; - + memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen); memset(AssetUUID, 0, UUIDlen); memset(ContextID, 0, UUIDlen); @@ -407,7 +410,7 @@ namespace ASDCP { // Initializes Rijndael CBC encryption context. // Returns error if the key argument is NULL. Result_t InitKey(const byte_t* key); - + // Initializes 16 byte CBC Initialization Vector. This operation may be performed // any number of times for a given key. // Returns error if the i_vec argument is NULL. @@ -601,22 +604,22 @@ namespace ASDCP { // MPEG2VideoDescriptor object. struct VideoDescriptor { - Rational EditRate; // - ui32_t FrameRate; // - Rational SampleRate; // - ui8_t FrameLayout; // - ui32_t StoredWidth; // - ui32_t StoredHeight; // - Rational AspectRatio; // - ui32_t ComponentDepth; // - ui32_t HorizontalSubsampling; // - ui32_t VerticalSubsampling; // - ui8_t ColorSiting; // - ui8_t CodedContentType; // - bool LowDelay; // - ui32_t BitRate; // - ui8_t ProfileAndLevel; // - ui32_t ContainerDuration; // + Rational EditRate; // + ui32_t FrameRate; // + Rational SampleRate; // + ui8_t FrameLayout; // + ui32_t StoredWidth; // + ui32_t StoredHeight; // + Rational AspectRatio; // + ui32_t ComponentDepth; // + ui32_t HorizontalSubsampling; // + ui32_t VerticalSubsampling; // + ui8_t ColorSiting; // + ui8_t CodedContentType; // + bool LowDelay; // + ui32_t BitRate; // + ui8_t ProfileAndLevel; // + ui32_t ContainerDuration; // }; // Print VideoDescriptor to std::ostream @@ -646,7 +649,7 @@ namespace ASDCP { { Capacity(size); } - + virtual ~FrameBuffer() {} // Sets the MPEG frame type of the picture data in the frame buffer. @@ -787,6 +790,12 @@ namespace ASDCP { // out of range, or if optional decrypt or HAMC operations fail. Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + // Calculates the first frame in transport order of the GOP in which the requested // frame is located. Calls ReadFrame() to fetch the frame at the calculated position. // Returns RESULT_INIT if the file is not open. @@ -836,12 +845,12 @@ namespace ASDCP { { Rational EditRate; // rate of frame wrapping Rational AudioSamplingRate; // rate of audio sample - ui32_t Locked; // + ui32_t Locked; // ui32_t ChannelCount; // number of channels ui32_t QuantizationBits; // number of bits per single-channel sample ui32_t BlockAlign; // number of bytes ber sample, all channels - ui32_t AvgBps; // - ui32_t LinkedTrackID; // + ui32_t AvgBps; // + ui32_t LinkedTrackID; // ui32_t ContainerDuration; // number of frames ChannelFormat_t ChannelFormat; // audio channel arrangement }; @@ -877,7 +886,7 @@ namespace ASDCP { FrameBuffer() {} FrameBuffer(ui32_t size) { Capacity(size); } virtual ~FrameBuffer() {} - + // Print debugging information to stream (stderr default) void Dump(FILE* = 0, ui32_t dump_bytes = 0) const; }; @@ -986,6 +995,12 @@ namespace ASDCP { // out of range, or if optional decrypt or HAMC operations fail. Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + // Print debugging information to stream void DumpHeaderMetadata(FILE* = 0) const; void DumpIndex(FILE* = 0) const; @@ -1018,7 +1033,7 @@ namespace ASDCP { ui8_t NumberOfLayers[sizeof(ui16_t)]; ui8_t MultiCompTransform; } SGcod; - + struct { ui8_t DecompositionLevels; @@ -1073,7 +1088,7 @@ namespace ASDCP { FrameBuffer() {} FrameBuffer(ui32_t size) { Capacity(size); } virtual ~FrameBuffer() {} - + // Print debugging information to stream (stderr default) void Dump(FILE* = 0, ui32_t dump_bytes = 0) const; }; @@ -1125,7 +1140,7 @@ namespace ASDCP { // alphabetically by filename. The parser will automatically parse enough data // from the first file to provide a complete set of stream metadata for the // MXFWriter below. If the "pedantic" parameter is given and is true, the - // parser will check the metadata for each codestream and fail if a + // parser will check the metadata for each codestream and fail if a // mismatch is detected. Result_t OpenRead(const char* filename, bool pedantic = false) const; @@ -1134,7 +1149,7 @@ namespace ASDCP { // picture. The parser will automatically parse enough data // from the first file to provide a complete set of stream metadata for the // MXFWriter below. If the "pedantic" parameter is given and is true, the - // parser will check the metadata for each codestream and fail if a + // parser will check the metadata for each codestream and fail if a // mismatch is detected. Result_t OpenRead(const std::list& file_list, bool pedantic = false) const; @@ -1227,6 +1242,12 @@ namespace ASDCP { // out of range, or if optional decrypt or HAMC operations fail. Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_FRAME if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + // Print debugging information to stream void DumpHeaderMetadata(FILE* = 0) const; void DumpIndex(FILE* = 0) const; @@ -1241,7 +1262,7 @@ namespace ASDCP { SP_LEFT, SP_RIGHT }; - + struct SFrameBuffer { JP2K::FrameBuffer Left; @@ -1344,6 +1365,12 @@ namespace ASDCP { Result_t ReadFrame(ui32_t frame_number, StereoscopicPhase_t phase, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_FRAME if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + // Print debugging information to stream void DumpHeaderMetadata(FILE* = 0) const; void DumpIndex(FILE* = 0) const; @@ -1359,7 +1386,7 @@ namespace ASDCP { struct TimedTextResourceDescriptor { byte_t ResourceID[UUIDlen]; - MIMEType_t Type; + MIMEType_t Type; TimedTextResourceDescriptor() : Type(MT_BIN) {} }; @@ -1368,7 +1395,7 @@ namespace ASDCP { struct TimedTextDescriptor { - Rational EditRate; // + Rational EditRate; // ui32_t ContainerDuration; byte_t AssetID[UUIDlen]; std::string NamespaceName; @@ -1396,7 +1423,7 @@ namespace ASDCP { FrameBuffer() { memset(m_AssetID, 0, UUIDlen); } FrameBuffer(ui32_t size) { Capacity(size); memset(m_AssetID, 0, UUIDlen); } virtual ~FrameBuffer() {} - + inline const byte_t* AssetID() const { return m_AssetID; } inline void AssetID(const byte_t* buf) { memcpy(m_AssetID, buf, UUIDlen); } inline const char* MIMEType() const { return m_MIMEType.c_str(); } @@ -1554,6 +1581,290 @@ namespace ASDCP { }; } // namespace TimedText + //--------------------------------------------------------------------------------- + // + namespace DCData + { + struct DCDataDescriptor + { + Rational EditRate; // Sample rate + ui32_t ContainerDuration; // number of frames + byte_t AssetID[UUIDlen]; // The UUID for the DCData track + byte_t DataEssenceCoding[UUIDlen]; // The coding for the data carried + }; + + // Print DCDataDescriptor to std::ostream + std::ostream& operator << (std::ostream& strm, const DCDataDescriptor& ddesc); + // Print debugging information to stream (stderr default) + void DCDataDescriptorDump(const DCDataDescriptor&, FILE* = 0); + + // + class FrameBuffer : public ASDCP::FrameBuffer + { + public: + FrameBuffer() {} + FrameBuffer(ui32_t size) { Capacity(size); } + virtual ~FrameBuffer() {} + + // Print debugging information to stream (stderr default) + void Dump(FILE* = 0, ui32_t dump_bytes = 0) const; + }; + + // An object which opens and reads a DC Data file. The file is expected + // to contain exactly one complete frame of DC data essence as an unwrapped (raw) + // byte stream. + class BytestreamParser + { + class h__BytestreamParser; + mem_ptr m_Parser; + ASDCP_NO_COPY_CONSTRUCT(BytestreamParser); + + public: + BytestreamParser(); + virtual ~BytestreamParser(); + + // Opens a file for reading, parses enough data to provide a complete + // set of stream metadata for the MXFWriter below. + // The frame buffer's PlaintextOffset parameter will be set to the first + // byte of the data segment. Set this value to zero if you want + // encrypted headers. + Result_t OpenReadFrame(const char* filename, FrameBuffer&) const; + + // Fill a DCDataDescriptor struct with the values from the file's bytestream. + // Returns RESULT_INIT if the file is not open. + Result_t FillDCDataDescriptor(DCDataDescriptor&) const; + }; + + // An object which reads a sequence of files containing DC Data. + class SequenceParser + { + class h__SequenceParser; + mem_ptr m_Parser; + ASDCP_NO_COPY_CONSTRUCT(SequenceParser); + + public: + SequenceParser(); + virtual ~SequenceParser(); + + // Opens a directory for reading. The directory is expected to contain one or + // more files, each containing the bytestream for exactly one frame. The files + // must be named such that the frames are in temporal order when sorted + // alphabetically by filename. + Result_t OpenRead(const char* filename) const; + + // Opens a file sequence for reading. The sequence is expected to contain one or + // more filenames, each naming a file containing the bytestream for exactly one + // frame. + Result_t OpenRead(const std::list& file_list) const; + + // Fill a DCDataDescriptor struct with default values. + // Returns RESULT_INIT if the directory is not open. + Result_t FillDCDataDescriptor(DCDataDescriptor&) const; + + // Rewind the directory to the beginning. + Result_t Reset() const; + + // Reads the next sequential frame in the directory and places it in the + // frame buffer. Fails if the buffer is too small or the direcdtory + // contains no more files. + // The frame buffer's PlaintextOffset parameter will be set to the first + // byte of the data segment. Set this value to zero if you want + // encrypted headers. + Result_t ReadFrame(FrameBuffer&) const; + }; + + // + class MXFWriter + { + class h__Writer; + mem_ptr m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFWriter); + + public: + MXFWriter(); + virtual ~MXFWriter(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OPAtomHeader& OPAtomHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + + // 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 WriterInfo&, + const DCDataDescriptor&, 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. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. + Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + + // Closes the MXF file, writing the index and revised header. + Result_t Finalize(); + }; + + // + class MXFReader + { + class h__Reader; + mem_ptr m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + MXFReader(); + virtual ~MXFReader(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OPAtomHeader& OPAtomHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const char* filename) const; + + // Returns RESULT_INIT if the file is not open. + Result_t Close() const; + + // Fill a DCDataDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillDCDataDescriptor(DCDataDescriptor&) 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(WriterInfo&) const; + + // Reads a frame of essence from the MXF file. If the optional AESEncContext + // argument is present, the essence is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + + } // namespace DCData + + //--------------------------------------------------------------------------------- + // + namespace ATMOS + { + struct AtmosDescriptor : public DCData::DCDataDescriptor + { + ui32_t FirstFrame; // Frame number of the frame to align with the FFOA of the picture track + ui16_t MaxChannelCount; // Max number of channels in bitstream + ui16_t MaxObjectCount; // Max number of objects in bitstream + byte_t AtmosID[UUIDlen]; // UUID of Atmos Project + ui8_t AtmosVersion; // ATMOS Coder Version used to create bitstream + }; + + // Print AtmosDescriptor to std::ostream + std::ostream& operator << (std::ostream& strm, const AtmosDescriptor& adesc); + // Print debugging information to stream (stderr default) + void AtmosDescriptorDump(const AtmosDescriptor&, FILE* = 0); + // Determine if a file is a raw atmos file + bool IsDolbyAtmos(const char* filename); + + // + class MXFWriter + { + + class h__Writer; + mem_ptr m_Writer; + ASDCP_NO_COPY_CONSTRUCT(MXFWriter); + + public: + MXFWriter(); + virtual ~MXFWriter(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OPAtomHeader& OPAtomHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + + // 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 WriterInfo&, + const AtmosDescriptor&, 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. + // Fails if the file is not open, is finalized, or an operating system + // error occurs. + Result_t WriteFrame(const DCData::FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + + // Closes the MXF file, writing the index and revised header. + Result_t Finalize(); + }; + + // + class MXFReader + { + class h__Reader; + mem_ptr m_Reader; + ASDCP_NO_COPY_CONSTRUCT(MXFReader); + + public: + MXFReader(); + virtual ~MXFReader(); + + // Warning: direct manipulation of MXF structures can interfere + // with the normal operation of the wrapper. Caveat emptor! + virtual MXF::OPAtomHeader& OPAtomHeader(); + virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter(); + + // Open the file for reading. The file must exist. Returns error if the + // operation cannot be completed. + Result_t OpenRead(const char* filename) const; + + // Returns RESULT_INIT if the file is not open. + Result_t Close() const; + + // Fill an AtmosDescriptor struct with the values from the file's header. + // Returns RESULT_INIT if the file is not open. + Result_t FillAtmosDescriptor(AtmosDescriptor&) 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(WriterInfo&) const; + + // Reads a frame of essence from the MXF file. If the optional AESEncContext + // argument is present, the essence is decrypted after reading. If the MXF + // file is encrypted and the AESDecContext argument is NULL, the frame buffer + // will contain the ciphertext frame data. If the HMACContext argument is + // not NULL, the HMAC will be calculated (if the file supports it). + // Returns RESULT_INIT if the file is not open, failure if the frame number is + // out of range, or if optional decrypt or HAMC operations fail. + Result_t ReadFrame(ui32_t frame_number, DCData::FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const; + + // Using the index table read from the footer partition, lookup the frame number + // and return the offset into the file at which to read that frame of essence. + // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is + // out of range. + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const; + + // Print debugging information to stream + void DumpHeaderMetadata(FILE* = 0) const; + void DumpIndex(FILE* = 0) const; + }; + + } // namespace ATMOS + + } // namespace ASDCP diff --git a/src/AS_DCP_ATMOS.cpp b/src/AS_DCP_ATMOS.cpp new file mode 100644 index 0000000..da23a0a --- /dev/null +++ b/src/AS_DCP_ATMOS.cpp @@ -0,0 +1,455 @@ +/* +Copyright (c) 2004-2013, 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_DCP_ATMOS.cpp + \version $Id$ + \brief AS-DCP library, Dolby Atmos essence reader and writer implementation +*/ + + +#include + +#include "AS_DCP.h" +#include "AS_DCP_DCData_internal.h" +#include "AS_DCP_internal.h" + +namespace ASDCP +{ +namespace ATMOS +{ + static std::string ATMOS_PACKAGE_LABEL = "File Package: SMPTE 382M frame wrapping of Dolby ATMOS data"; + static std::string ATMOS_DEF_LABEL = "Dolby ATMOS Data Track"; + static byte_t ATMOS_ESSENCE_CODING[SMPTE_UL_LENGTH] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x05, + 0x0e, 0x09, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00 }; +} // namespace ATMOS +} // namespace ASDCP + +// +std::ostream& +ASDCP::ATMOS::operator << (std::ostream& strm, const AtmosDescriptor& ADesc) +{ + char str_buf[40]; + strm << " EditRate: " << ADesc.EditRate.Numerator << "/" << ADesc.EditRate.Denominator << std::endl; + strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl; + strm << " DataEssenceCoding: " << UL(ADesc.DataEssenceCoding).EncodeString(str_buf, 40) << std::endl; + strm << " AtmosVersion: " << (unsigned) ADesc.AtmosVersion << std::endl; + strm << " MaxChannelCount: " << (unsigned) ADesc.MaxChannelCount << std::endl; + strm << " MaxObjectCount: " << (unsigned) ADesc.MaxObjectCount << std::endl; + strm << " AtmosID: " << UUID(ADesc.AtmosID).EncodeString(str_buf, 40) << std::endl; + strm << " FirstFrame: " << (unsigned) ADesc.FirstFrame << std::endl; + return strm; +} + +// +void +ASDCP::ATMOS::AtmosDescriptorDump(const AtmosDescriptor& ADesc, FILE* stream) +{ + char str_buf[40]; + char atmosID_buf[40]; + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "\ + EditRate: %d/%d\n\ + ContainerDuration: %u\n\ + DataEssenceCoding: %s\n\ + AtmosVersion: %u\n\ + MaxChannelCount: %u\n\ + MaxObjectCount: %u\n\ + AtmosID: %s\n\ + FirsFrame: %u\n", + ADesc.EditRate.Numerator, ADesc.EditRate.Denominator, + ADesc.ContainerDuration, + UL(ADesc.DataEssenceCoding).EncodeString(str_buf, 40), + ADesc.AtmosVersion, + ADesc.MaxChannelCount, + ADesc.MaxObjectCount, + UUID(ADesc.AtmosID).EncodeString(atmosID_buf, 40), + ADesc.FirstFrame); +} + +// +bool +ASDCP::ATMOS::IsDolbyAtmos(const char* filename) +{ + // TODO + // For now use an atmos extension + bool result = (0 == (std::string("atmos").compare(Kumu::PathGetExtension(std::string(filename))))); + return result; +} + + +//------------------------------------------------------------------------------------------ + + +class ASDCP::ATMOS::MXFReader::h__Reader : public ASDCP::DCData::h__Reader +{ + MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor; + + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + h__Reader(); + + public: + AtmosDescriptor m_ADesc; + + h__Reader(const Dictionary& d) : DCData::h__Reader(d), m_EssenceSubDescriptor(NULL), + m_ADesc() {} + ~h__Reader() {} + Result_t OpenRead(const char*); + Result_t MD_to_Atmos_ADesc(ATMOS::AtmosDescriptor& ADesc); +}; + +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::h__Reader::MD_to_Atmos_ADesc(ATMOS::AtmosDescriptor& ADesc) +{ + ASDCP_TEST_NULL(m_EssenceSubDescriptor); + Result_t result = MD_to_DCData_DDesc(ADesc); + if( ASDCP_SUCCESS(result) ) + { + MXF::DolbyAtmosSubDescriptor* ADescObj = m_EssenceSubDescriptor; + ADesc.MaxChannelCount = ADescObj->MaxChannelCount; + ADesc.MaxObjectCount = ADescObj->MaxObjectCount; + ::memcpy(ADesc.AtmosID, ADescObj->AtmosID.Value(), UUIDlen); + ADesc.AtmosVersion = ADescObj->AtmosVersion; + ADesc.FirstFrame = ADescObj->FirstFrame; + } + return result; +} + +// +// +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::h__Reader::OpenRead(const char* filename) +{ + Result_t result = DCData::h__Reader::OpenRead(filename); + + if( ASDCP_SUCCESS(result) ) + { + + if (NULL == m_EssenceSubDescriptor) + { + InterchangeObject* iObj = NULL; + result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(DolbyAtmosSubDescriptor), &iObj); + m_EssenceSubDescriptor = static_cast(iObj); + } + + if ( ASDCP_SUCCESS(result) ) + { + result = MD_to_Atmos_ADesc(m_ADesc); + } + } + + return result; +} + + +//------------------------------------------------------------------------------------------ + +ASDCP::ATMOS::MXFReader::MXFReader() +{ + m_Reader = new h__Reader(DefaultSMPTEDict()); +} + + +ASDCP::ATMOS::MXFReader::~MXFReader() +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + m_Reader->Close(); +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomHeader& +ASDCP::ATMOS::MXFReader::OPAtomHeader() +{ + if ( m_Reader.empty() ) + { + assert(g_OPAtomHeader); + return *g_OPAtomHeader; + } + + return m_Reader->m_HeaderPart; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomIndexFooter& +ASDCP::ATMOS::MXFReader::OPAtomIndexFooter() +{ + if ( m_Reader.empty() ) + { + assert(g_OPAtomIndexFooter); + return *g_OPAtomIndexFooter; + } + + return m_Reader->m_FooterPart; +} + +// Open the file for reading. The file must exist. Returns error if the +// operation cannot be completed. +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::OpenRead(const char* filename) const +{ + return m_Reader->OpenRead(filename); +} + +// +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::ReadFrame(ui32_t FrameNum, DCData::FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC); + + return RESULT_INIT; +} + +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const +{ + return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset); +} + + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::FillAtmosDescriptor(AtmosDescriptor& 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 +ASDCP::ATMOS::MXFReader::FillWriterInfo(WriterInfo& Info) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + Info = m_Reader->m_Info; + return RESULT_OK; + } + + return RESULT_INIT; +} + +// +void +ASDCP::ATMOS::MXFReader::DumpHeaderMetadata(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_HeaderPart.Dump(stream); +} + +// +void +ASDCP::ATMOS::MXFReader::DumpIndex(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_FooterPart.Dump(stream); +} + +// +ASDCP::Result_t +ASDCP::ATMOS::MXFReader::Close() const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + m_Reader->Close(); + return RESULT_OK; + } + + return RESULT_INIT; +} + + +//------------------------------------------------------------------------------------------ + +// +class ASDCP::ATMOS::MXFWriter::h__Writer : public DCData::h__Writer +{ + MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor; + + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + h__Writer(); + + public: + AtmosDescriptor m_ADesc; + + h__Writer(const Dictionary& d) : DCData::h__Writer(d), + m_EssenceSubDescriptor(NULL), m_ADesc() {} + + ~h__Writer(){} + + Result_t OpenWrite(const char*, ui32_t HeaderSize, const AtmosDescriptor& ADesc); + Result_t Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc); +}; + +// +ASDCP::Result_t +ASDCP::ATMOS::MXFWriter::h__Writer::Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc) +{ + ASDCP_TEST_NULL(m_EssenceDescriptor); + ASDCP_TEST_NULL(m_EssenceSubDescriptor); + MXF::DolbyAtmosSubDescriptor * ADescObj = m_EssenceSubDescriptor; + ADescObj->MaxChannelCount = ADesc.MaxChannelCount; + ADescObj->MaxObjectCount = ADesc.MaxObjectCount; + ADescObj->AtmosID.Set(ADesc.AtmosID); + ADescObj->AtmosVersion = ADesc.AtmosVersion; + ADescObj->FirstFrame = ADesc.FirstFrame; + return RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::ATMOS::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize, const AtmosDescriptor& ADesc) +{ + + m_EssenceSubDescriptor = new DolbyAtmosSubDescriptor(m_Dict); + DCData::SubDescriptorList_t subDescriptors; + subDescriptors.push_back(m_EssenceSubDescriptor); + Result_t result = DCData::h__Writer::OpenWrite(filename, HeaderSize, subDescriptors); + if ( ASDCP_FAILURE(result) ) + delete m_EssenceSubDescriptor; + + if ( ASDCP_SUCCESS(result) ) + { + m_ADesc = ADesc; + memcpy(m_ADesc.DataEssenceCoding, ATMOS_ESSENCE_CODING, SMPTE_UL_LENGTH); + result = Atmos_ADesc_to_MD(m_ADesc); + } + + return result; +} + + + + +//------------------------------------------------------------------------------------------ + +ASDCP::ATMOS::MXFWriter::MXFWriter() +{ +} + +ASDCP::ATMOS::MXFWriter::~MXFWriter() +{ +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomHeader& +ASDCP::ATMOS::MXFWriter::OPAtomHeader() +{ + if ( m_Writer.empty() ) + { + assert(g_OPAtomHeader); + return *g_OPAtomHeader; + } + + return m_Writer->m_HeaderPart; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomIndexFooter& +ASDCP::ATMOS::MXFWriter::OPAtomIndexFooter() +{ + if ( m_Writer.empty() ) + { + assert(g_OPAtomIndexFooter); + return *g_OPAtomIndexFooter; + } + + return m_Writer->m_FooterPart; +} + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ASDCP::ATMOS::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info, + const AtmosDescriptor& ADesc, ui32_t HeaderSize) +{ + if ( Info.LabelSetType != LS_MXF_SMPTE ) + { + DefaultLogSink().Error("Atmos support requires LS_MXF_SMPTE\n"); + return RESULT_FORMAT; + } + + m_Writer = new h__Writer(DefaultSMPTEDict()); + m_Writer->m_Info = Info; + + Result_t result = m_Writer->OpenWrite(filename, HeaderSize, ADesc); + + if ( ASDCP_SUCCESS(result) ) + result = m_Writer->SetSourceStream(ADesc, ATMOS_ESSENCE_CODING, ATMOS_PACKAGE_LABEL, + ATMOS_DEF_LABEL); + + if ( ASDCP_FAILURE(result) ) + m_Writer.release(); + + return result; +} + +// Writes a frame of essence to the MXF file. If the optional AESEncContext +// argument is present, the essence is encrypted prior to writing. +// Fails if the file is not open, is finalized, or an operating system +// error occurs. +ASDCP::Result_t +ASDCP::ATMOS::MXFWriter::WriteFrame(const DCData::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC) +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC); +} + +// Closes the MXF file, writing the index and other closing information. +ASDCP::Result_t +ASDCP::ATMOS::MXFWriter::Finalize() +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->Finalize(); +} + + +// +// end AS_DCP_ATMOS.cpp +// + + diff --git a/src/AS_DCP_DCData.cpp b/src/AS_DCP_DCData.cpp new file mode 100644 index 0000000..53e4497 --- /dev/null +++ b/src/AS_DCP_DCData.cpp @@ -0,0 +1,554 @@ +/* +Copyright (c) 2004-2013, 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_DCP_DCData.cpp + \version $Id$ + \brief AS-DCP library, Dcinema generic data essence reader and writer implementation +*/ + +#include + +#include "AS_DCP.h" +#include "AS_DCP_DCData_internal.h" +#include "AS_DCP_internal.h" + +namespace ASDCP +{ +namespace DCData +{ + static std::string DC_DATA_PACKAGE_LABEL = "File Package: SMPTE 382M frame wrapping of D-Cinema Generic data"; + static std::string DC_DATA_DEF_LABEL = "D-Cinema Generic Data Track"; +} // namespace DCData +} // namespace ASDCP + +// +std::ostream& +ASDCP::DCData::operator << (std::ostream& strm, const DCDataDescriptor& DDesc) +{ + char str_buf[40]; + strm << " EditRate: " << DDesc.EditRate.Numerator << "/" << DDesc.EditRate.Denominator << std::endl; + strm << " ContainerDuration: " << (unsigned) DDesc.ContainerDuration << std::endl; + strm << " DataEssenceCoding: " << UL(DDesc.DataEssenceCoding).EncodeString(str_buf, 40) << std::endl; + return strm; +} + +// +void +ASDCP::DCData::DCDataDescriptorDump(const DCDataDescriptor& DDesc, FILE* stream) +{ + char str_buf[40]; + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "\ + EditRate: %d/%d\n\ + ContainerDuration: %u\n\ + DataEssenceCoding: %s\n", + DDesc.EditRate.Numerator, DDesc.EditRate.Denominator, + DDesc.ContainerDuration, + UL(DDesc.DataEssenceCoding).EncodeString(str_buf, 40)); +} + + +//------------------------------------------------------------------------------------------ + + +ASDCP::Result_t +ASDCP::DCData::h__Reader::MD_to_DCData_DDesc(DCData::DCDataDescriptor& DDesc) +{ + ASDCP_TEST_NULL(m_EssenceDescriptor); + MXF::DCDataDescriptor* DDescObj = m_EssenceDescriptor; + DDesc.EditRate = DDescObj->SampleRate; + assert(DDescObj->ContainerDuration <= 0xFFFFFFFFL); + DDesc.ContainerDuration = static_cast(DDescObj->ContainerDuration); + memcpy(DDesc.DataEssenceCoding, DDescObj->DataEssenceCoding.Value(), SMPTE_UL_LENGTH); + return RESULT_OK; +} + +// +// +ASDCP::Result_t +ASDCP::DCData::h__Reader::OpenRead(const char* filename) +{ + Result_t result = OpenMXFRead(filename); + + if( ASDCP_SUCCESS(result) ) + { + if (NULL == m_EssenceDescriptor) + { + InterchangeObject* iObj = NULL; + result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(DCDataDescriptor), &iObj); + m_EssenceDescriptor = static_cast(iObj); + } + + if ( ASDCP_SUCCESS(result) ) + { + result = MD_to_DCData_DDesc(m_DDesc); + } + } + + // check for sample/frame rate sanity + if ( ASDCP_SUCCESS(result) + && m_DDesc.EditRate != EditRate_24 + && m_DDesc.EditRate != EditRate_25 + && m_DDesc.EditRate != EditRate_30 + && m_DDesc.EditRate != EditRate_48 + && m_DDesc.EditRate != EditRate_50 + && m_DDesc.EditRate != EditRate_60 + && m_DDesc.EditRate != EditRate_96 + && m_DDesc.EditRate != EditRate_100 + && m_DDesc.EditRate != EditRate_120 ) + { + DefaultLogSink().Error("DC Data file EditRate is not a supported value: %d/%d\n", // lu + m_DDesc.EditRate.Numerator, m_DDesc.EditRate.Denominator); + + return RESULT_FORMAT; + } + + if( ASDCP_SUCCESS(result) ) + result = InitMXFIndex(); + + if( ASDCP_SUCCESS(result) ) + result = InitInfo(); + + return result; +} + +// +// +ASDCP::Result_t +ASDCP::DCData::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) +{ + if ( ! m_File.IsOpen() ) + return RESULT_INIT; + + assert(m_Dict); + return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_DCDataEssence), Ctx, HMAC); +} + + +// +//------------------------------------------------------------------------------------------ + +class ASDCP::DCData::MXFReader::h__Reader : public DCData::h__Reader +{ + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + h__Reader(); + + public: + h__Reader(const Dictionary& d) : DCData::h__Reader(d) {} +}; + + +//------------------------------------------------------------------------------------------ + + +// +void +ASDCP::DCData::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const +{ + if ( stream == 0 ) + stream = stderr; + + fprintf(stream, "Frame: %06u, %7u bytes\n", m_FrameNumber, m_Size); + + if ( dump_len > 0 ) + Kumu::hexdump(m_Data, dump_len, stream); +} + + +//------------------------------------------------------------------------------------------ + +ASDCP::DCData::MXFReader::MXFReader() +{ + m_Reader = new h__Reader(DefaultSMPTEDict()); +} + + +ASDCP::DCData::MXFReader::~MXFReader() +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + m_Reader->Close(); +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomHeader& +ASDCP::DCData::MXFReader::OPAtomHeader() +{ + if ( m_Reader.empty() ) + { + assert(g_OPAtomHeader); + return *g_OPAtomHeader; + } + + return m_Reader->m_HeaderPart; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomIndexFooter& +ASDCP::DCData::MXFReader::OPAtomIndexFooter() +{ + if ( m_Reader.empty() ) + { + assert(g_OPAtomIndexFooter); + return *g_OPAtomIndexFooter; + } + + return m_Reader->m_FooterPart; +} + +// Open the file for reading. The file must exist. Returns error if the +// operation cannot be completed. +ASDCP::Result_t +ASDCP::DCData::MXFReader::OpenRead(const char* filename) const +{ + return m_Reader->OpenRead(filename); +} + +// +ASDCP::Result_t +ASDCP::DCData::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, + AESDecContext* Ctx, HMACContext* HMAC) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC); + + return RESULT_INIT; +} + +ASDCP::Result_t +ASDCP::DCData::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const +{ + return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset); +} + + +// Fill the struct with the values from the file's header. +// Returns RESULT_INIT if the file is not open. +ASDCP::Result_t +ASDCP::DCData::MXFReader::FillDCDataDescriptor(DCDataDescriptor& DDesc) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + DDesc = m_Reader->m_DDesc; + 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 +ASDCP::DCData::MXFReader::FillWriterInfo(WriterInfo& Info) const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + Info = m_Reader->m_Info; + return RESULT_OK; + } + + return RESULT_INIT; +} + +// +void +ASDCP::DCData::MXFReader::DumpHeaderMetadata(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_HeaderPart.Dump(stream); +} + + +// +void +ASDCP::DCData::MXFReader::DumpIndex(FILE* stream) const +{ + if ( m_Reader->m_File.IsOpen() ) + m_Reader->m_FooterPart.Dump(stream); +} + +// +ASDCP::Result_t +ASDCP::DCData::MXFReader::Close() const +{ + if ( m_Reader && m_Reader->m_File.IsOpen() ) + { + m_Reader->Close(); + return RESULT_OK; + } + + return RESULT_INIT; +} + + +//------------------------------------------------------------------------------------------ + + +// +ASDCP::Result_t +ASDCP::DCData::h__Writer::DCData_DDesc_to_MD(DCData::DCDataDescriptor& DDesc) +{ + ASDCP_TEST_NULL(m_EssenceDescriptor); + MXF::DCDataDescriptor* DDescObj = static_cast(m_EssenceDescriptor); + + DDescObj->SampleRate = DDesc.EditRate; + DDescObj->ContainerDuration = DDesc.ContainerDuration; + DDescObj->DataEssenceCoding.Set(DDesc.DataEssenceCoding); + + return RESULT_OK; +} + +// +ASDCP::Result_t +ASDCP::DCData::h__Writer::OpenWrite(char const* filename, ui32_t HeaderSize, + const SubDescriptorList_t& subDescriptors) +{ + if ( ! m_State.Test_BEGIN() ) + return RESULT_STATE; + + Result_t result = m_File.OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + { + m_HeaderSize = HeaderSize; + m_EssenceDescriptor = new MXF::DCDataDescriptor(m_Dict); + SubDescriptorList_t::const_iterator sDObj; + SubDescriptorList_t::const_iterator lastDescriptor = subDescriptors.end(); + for (sDObj = subDescriptors.begin(); sDObj != lastDescriptor; ++sDObj) + { + m_EssenceSubDescriptorList.push_back(*sDObj); + GenRandomValue((*sDObj)->InstanceUID); + m_EssenceDescriptor->SubDescriptors.push_back((*sDObj)->InstanceUID); + } + result = m_State.Goto_INIT(); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::DCData::h__Writer::SetSourceStream(DCDataDescriptor const& DDesc, + const byte_t * essenceCoding, + const std::string& packageLabel, + const std::string& defLabel) +{ + if ( ! m_State.Test_INIT() ) + return RESULT_STATE; + + if ( DDesc.EditRate != EditRate_24 + && DDesc.EditRate != EditRate_25 + && DDesc.EditRate != EditRate_30 + && DDesc.EditRate != EditRate_48 + && DDesc.EditRate != EditRate_50 + && DDesc.EditRate != EditRate_60 + && DDesc.EditRate != EditRate_96 + && DDesc.EditRate != EditRate_100 + && DDesc.EditRate != EditRate_120 ) + { + DefaultLogSink().Error("DCDataDescriptor.EditRate is not a supported value: %d/%d\n", + DDesc.EditRate.Numerator, DDesc.EditRate.Denominator); + return RESULT_RAW_FORMAT; + } + + assert(m_Dict); + m_DDesc = DDesc; + if (NULL != essenceCoding) + memcpy(m_DDesc.DataEssenceCoding, essenceCoding, SMPTE_UL_LENGTH); + Result_t result = DCData_DDesc_to_MD(m_DDesc); + + if ( ASDCP_SUCCESS(result) ) + { + memcpy(m_EssenceUL, m_Dict->ul(MDD_DCDataEssence), SMPTE_UL_LENGTH); + m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container + result = m_State.Goto_READY(); + } + + if ( ASDCP_SUCCESS(result) ) + { + ui32_t TCFrameRate = m_DDesc.EditRate.Numerator; + + result = WriteMXFHeader(packageLabel, UL(m_Dict->ul(MDD_DCDataWrapping)), + defLabel, UL(m_EssenceUL), UL(m_Dict->ul(MDD_DataDataDef)), + m_DDesc.EditRate, TCFrameRate); + } + + return result; +} + +// +ASDCP::Result_t +ASDCP::DCData::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, + ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC) +{ + Result_t result = RESULT_OK; + + if ( m_State.Test_READY() ) + result = m_State.Goto_RUNNING(); // first time through + + ui64_t StreamOffset = m_StreamOffset; + + if ( ASDCP_SUCCESS(result) ) + result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC); + + if ( ASDCP_SUCCESS(result) ) + { + IndexTableSegment::IndexEntry Entry; + Entry.StreamOffset = StreamOffset; + m_FooterPart.PushIndexEntry(Entry); + m_FramesWritten++; + } + return result; +} + +// Closes the MXF file, writing the index and other closing information. +// +ASDCP::Result_t +ASDCP::DCData::h__Writer::Finalize() +{ + if ( ! m_State.Test_RUNNING() ) + return RESULT_STATE; + + m_State.Goto_FINAL(); + + return WriteMXFFooter(); +} + + +// +//------------------------------------------------------------------------------------------ + + +class ASDCP::DCData::MXFWriter::h__Writer : public DCData::h__Writer +{ + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + h__Writer(); + + public: + h__Writer(const Dictionary& d) : DCData::h__Writer(d) {} +}; + + +//------------------------------------------------------------------------------------------ + +ASDCP::DCData::MXFWriter::MXFWriter() +{ +} + +ASDCP::DCData::MXFWriter::~MXFWriter() +{ +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomHeader& +ASDCP::DCData::MXFWriter::OPAtomHeader() +{ + if ( m_Writer.empty() ) + { + assert(g_OPAtomHeader); + return *g_OPAtomHeader; + } + + return m_Writer->m_HeaderPart; +} + +// Warning: direct manipulation of MXF structures can interfere +// with the normal operation of the wrapper. Caveat emptor! +// +ASDCP::MXF::OPAtomIndexFooter& +ASDCP::DCData::MXFWriter::OPAtomIndexFooter() +{ + if ( m_Writer.empty() ) + { + assert(g_OPAtomIndexFooter); + return *g_OPAtomIndexFooter; + } + + return m_Writer->m_FooterPart; +} + +// Open the file for writing. The file must not exist. Returns error if +// the operation cannot be completed. +ASDCP::Result_t +ASDCP::DCData::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info, + const DCDataDescriptor& DDesc, ui32_t HeaderSize) +{ + if ( Info.LabelSetType != LS_MXF_SMPTE ) + { + DefaultLogSink().Error("DC Data support requires LS_MXF_SMPTE\n"); + return RESULT_FORMAT; + } + + m_Writer = new h__Writer(DefaultSMPTEDict()); + m_Writer->m_Info = Info; + + Result_t result = m_Writer->OpenWrite(filename, HeaderSize, SubDescriptorList_t()); + + if ( ASDCP_SUCCESS(result) ) + result = m_Writer->SetSourceStream(DDesc, NULL, DC_DATA_PACKAGE_LABEL, DC_DATA_DEF_LABEL); + + if ( ASDCP_FAILURE(result) ) + m_Writer.release(); + + return result; +} + +// Writes a frame of essence to the MXF file. If the optional AESEncContext +// argument is present, the essence is encrypted prior to writing. +// Fails if the file is not open, is finalized, or an operating system +// error occurs. +ASDCP::Result_t +ASDCP::DCData::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC) +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC); +} + +// Closes the MXF file, writing the index and other closing information. +ASDCP::Result_t +ASDCP::DCData::MXFWriter::Finalize() +{ + if ( m_Writer.empty() ) + return RESULT_INIT; + + return m_Writer->Finalize(); +} + + +// +// end AS_DCP_DCData.cpp +// diff --git a/src/AS_DCP_DCData_internal.h b/src/AS_DCP_DCData_internal.h new file mode 100644 index 0000000..65d6c41 --- /dev/null +++ b/src/AS_DCP_DCData_internal.h @@ -0,0 +1,94 @@ +/* +Copyright (c) 2004-2012, 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_DCP_DCData_internal.h + \version $Id$ + \brief AS-DCP library, non-public common DCData reader and writer implementation +*/ + +#ifndef _AS_DCP_DCDATA_INTERNAL_H_ +#define _AS_DCP_DCDATA_INTERNAL_H_ + +#include + +#include "AS_DCP_internal.h" + +namespace ASDCP +{ + +namespace MXF +{ + class InterchangeObject; +} + +namespace DCData +{ + typedef std::list SubDescriptorList_t; + + class h__Reader : public ASDCP::h__ASDCPReader + { + MXF::DCDataDescriptor* m_EssenceDescriptor; + ASDCP_NO_COPY_CONSTRUCT(h__Reader); + h__Reader(); + + public: + DCDataDescriptor m_DDesc; + + h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d), m_EssenceDescriptor(0), + m_DDesc() {} + ~h__Reader() {} + Result_t OpenRead(const char*); + Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*); + Result_t MD_to_DCData_DDesc(DCData::DCDataDescriptor& DDesc); + }; + + class h__Writer : public ASDCP::h__Writer + { + ASDCP_NO_COPY_CONSTRUCT(h__Writer); + h__Writer(); + + public: + DCDataDescriptor m_DDesc; + byte_t m_EssenceUL[SMPTE_UL_LENGTH]; + + h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) { + memset(m_EssenceUL, 0, SMPTE_UL_LENGTH); + } + + ~h__Writer(){} + + Result_t OpenWrite(const char*, ui32_t HeaderSize, const SubDescriptorList_t& subDescriptors); + Result_t SetSourceStream(const DCDataDescriptor&, const byte_t*, const std::string&, const std::string&); + Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0); + Result_t Finalize(); + Result_t DCData_DDesc_to_MD(DCData::DCDataDescriptor& DDesc); +}; + + +} // namespace DCData +} // namespace ASDCP + +#endif // _AS_DCP_DCDATA_INTERNAL_H_ diff --git a/src/AS_DCP_JP2K.cpp b/src/AS_DCP_JP2K.cpp index 33d5b93..300a6a2 100755 --- a/src/AS_DCP_JP2K.cpp +++ b/src/AS_DCP_JP2K.cpp @@ -280,6 +280,22 @@ lh__Reader::MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc) return RESULT_OK; } +// Compares the actual floating point value of the rates. +// This allows, for example, {300000,1001} and {2997,100) to be considered equivalent. +// to 29.97. +bool +epsilon_compare(const ASDCP::Rational& left, const ASDCP::Rational& right, double epsilon = 0.001) +{ + bool result = false; + double difference = left.Quotient() - right.Quotient(); + + if (fabs(difference) < epsilon) + result = true; + + return result; +} +// end DOLBY + // // ASDCP::Result_t @@ -459,6 +475,8 @@ ASDCP::JP2K::MXFReader::MXFReader() ASDCP::JP2K::MXFReader::~MXFReader() { + if ( m_Reader && m_Reader->m_File.IsOpen() ) + m_Reader->Close(); } // Warning: direct manipulation of MXF structures can interfere @@ -510,6 +528,12 @@ ASDCP::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, return RESULT_INIT; } +ASDCP::Result_t +ASDCP::JP2K::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const +{ + return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset); +} + // Fill the struct with the values from the file's header. // Returns RESULT_INIT if the file is not open. @@ -663,6 +687,8 @@ ASDCP::JP2K::MXFSReader::MXFSReader() ASDCP::JP2K::MXFSReader::~MXFSReader() { + if ( m_Reader && m_Reader->m_File.IsOpen() ) + m_Reader->Close(); } // Warning: direct manipulation of MXF structures can interfere @@ -731,6 +757,12 @@ ASDCP::JP2K::MXFSReader::ReadFrame(ui32_t FrameNum, StereoscopicPhase_t phase, F return RESULT_INIT; } +ASDCP::Result_t +ASDCP::JP2K::MXFSReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const +{ + return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset); +} + // Fill the struct with the values from the file's header. // Returns RESULT_INIT if the file is not open. ASDCP::Result_t diff --git a/src/AS_DCP_MPEG2.cpp b/src/AS_DCP_MPEG2.cpp index 2081630..61ab7b7 100755 --- a/src/AS_DCP_MPEG2.cpp +++ b/src/AS_DCP_MPEG2.cpp @@ -333,6 +333,8 @@ ASDCP::MPEG2::MXFReader::MXFReader() ASDCP::MPEG2::MXFReader::~MXFReader() { + if ( m_Reader && m_Reader->m_File.IsOpen() ) + m_Reader->Close(); } // Warning: direct manipulation of MXF structures can interfere @@ -385,6 +387,13 @@ ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, } +// +ASDCP::Result_t +ASDCP::MPEG2::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const +{ + return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset); +} + // ASDCP::Result_t ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf, diff --git a/src/AS_DCP_MXF.cpp b/src/AS_DCP_MXF.cpp index 96fc959..4d039f0 100755 --- a/src/AS_DCP_MXF.cpp +++ b/src/AS_DCP_MXF.cpp @@ -191,6 +191,13 @@ ASDCP::EssenceType(const char* filename, EssenceType_t& type) type = ESS_MPEG2_VES; else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor))) ) type = ESS_TIMED_TEXT; + else if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DCDataDescriptor))) ) + { + if ( ASDCP_SUCCESS(TestHeader.GetMDObjectByType(OBJ_TYPE_ARGS(DolbyAtmosSubDescriptor))) ) + type = ESS_DCDATA_DOLBY_ATMOS; + else + type = ESS_DCDATA_UNKNOWN; + } } return result; @@ -205,6 +212,7 @@ ASDCP::RawEssenceType(const char* filename, EssenceType_t& type) ASDCP::FrameBuffer FB; Kumu::FileReader Reader; ASDCP::Wav::SimpleWaveHeader WavHeader; + ASDCP::RF64::SimpleRF64Header RF64Header; ASDCP::AIFF::SimpleAIFFHeader AIFFHeader; Kumu::XMLElement TmpElement("Tmp"); @@ -248,6 +256,16 @@ ASDCP::RawEssenceType(const char* filename, EssenceType_t& type) return RESULT_FORMAT; } } + else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) ) + { + switch ( RF64Header.samplespersec ) + { + case 48000: type = ESS_PCM_24b_48k; break; + case 96000: type = ESS_PCM_24b_96k; break; + default: + return RESULT_FORMAT; + } + } else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) ) { type = ESS_PCM_24b_48k; @@ -256,6 +274,10 @@ ASDCP::RawEssenceType(const char* filename, EssenceType_t& type) { type = ESS_TIMED_TEXT; } + else if ( ASDCP::ATMOS::IsDolbyAtmos(filename) ) + { + type = ESS_DCDATA_DOLBY_ATMOS; + } } } else if ( Kumu::PathIsDirectory(filename) ) @@ -298,6 +320,21 @@ ASDCP::RawEssenceType(const char* filename, EssenceType_t& type) return RESULT_FORMAT; } } + else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) ) + { + switch ( RF64Header.samplespersec ) + { + case 48000: type = ESS_PCM_24b_48k; break; + case 96000: type = ESS_PCM_24b_96k; break; + default: + return RESULT_FORMAT; + } + } + else if ( ASDCP::ATMOS::IsDolbyAtmos(Str.c_str()) ) + { + type = ESS_DCDATA_DOLBY_ATMOS; + } + } break; diff --git a/src/AS_DCP_PCM.cpp b/src/AS_DCP_PCM.cpp index 606a6f5..e52b27f 100755 --- a/src/AS_DCP_PCM.cpp +++ b/src/AS_DCP_PCM.cpp @@ -134,6 +134,35 @@ ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc) strm << " AvgBps: " << (unsigned) ADesc.AvgBps << std::endl; strm << " LinkedTrackID: " << (unsigned) ADesc.LinkedTrackID << std::endl; strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl; + strm << " ChannelFormat: "; + switch (ADesc.ChannelFormat) + { + case CF_NONE: + default: + strm << "No Channel Format"; + break; + + case CF_CFG_1: + strm << "Config 1 (5.1 with optional HI/VI)"; + break; + + case CF_CFG_2: + strm << "Config 2 (5.1 + center surround with optional HI/VI)"; + break; + + case CF_CFG_3: + strm << "Config 3 (7.1 with optional HI/VI)"; + break; + + case CF_CFG_4: + strm << "Config 4"; + break; + + case CF_CFG_5: + strm << "Config 5 (7.1 DS with optional HI/VI)"; + break; + } + strm << std::endl; return strm; } @@ -154,7 +183,8 @@ ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream) BlockAlign: %u\n\ AvgBps: %u\n\ LinkedTrackID: %u\n\ - ContainerDuration: %u\n", + ContainerDuration: %u\n\ + ChannelFormat: %u\n", ADesc.EditRate.Numerator, ADesc.EditRate.Denominator, ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator, ADesc.Locked, @@ -163,7 +193,8 @@ ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream) ADesc.BlockAlign, ADesc.AvgBps, ADesc.LinkedTrackID, - ADesc.ContainerDuration + ADesc.ContainerDuration, + ADesc.ChannelFormat ); } @@ -256,6 +287,7 @@ ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename) } else { + DefaultLogSink().Error("PCM EditRate not in expected value range.\n"); // or we just drop the hammer return RESULT_FORMAT; } @@ -370,6 +402,12 @@ ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf, } +ASDCP::Result_t +ASDCP::PCM::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const +{ + return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset); +} + // Fill the struct with the values from the file's header. // Returns RESULT_INIT if the file is not open. ASDCP::Result_t diff --git a/src/AS_DCP_TimedText.cpp b/src/AS_DCP_TimedText.cpp index 971fe08..694676d 100644 --- a/src/AS_DCP_TimedText.cpp +++ b/src/AS_DCP_TimedText.cpp @@ -92,7 +92,7 @@ ASDCP::TimedText::DescriptorDump(ASDCP::TimedText::TimedTextDescriptor const& TD fprintf(stream, "ContainerDuration: %u\n", TDesc.ContainerDuration); fprintf(stream, " AssetID: %s\n", TmpID.EncodeHex(buf, 64)); fprintf(stream, " NamespaceName: %s\n", TDesc.NamespaceName.c_str()); - fprintf(stream, " ResourceCount: %d\n", TDesc.ResourceList.size()); + fprintf(stream, " ResourceCount: %zu\n", TDesc.ResourceList.size()); TimedText::ResourceList_t::const_iterator ri; for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end(); ri++ ) @@ -567,7 +567,7 @@ ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedT { InitHeader(); AddDMSegment(m_TDesc.EditRate, 24, TIMED_TEXT_DEF_LABEL, - UL(m_Dict->ul(MDD_PictureDataDef)), TIMED_TEXT_PACKAGE_LABEL); + UL(m_Dict->ul(MDD_DataDataDef)), TIMED_TEXT_PACKAGE_LABEL); AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrapping))); diff --git a/src/AS_DCP_internal.h b/src/AS_DCP_internal.h index af6b553..5310100 100755 --- a/src/AS_DCP_internal.h +++ b/src/AS_DCP_internal.h @@ -25,7 +25,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*! \file AS_DCP_internal.h - \version $Id$ + \version $Id$ \brief AS-DCP library, non-public common elements */ @@ -62,7 +62,7 @@ namespace ASDCP std::vector result; const char* pstr = str; const char* r = strchr(pstr, '.'); - + while ( r != 0 ) { assert(r >= pstr); @@ -132,7 +132,7 @@ namespace ASDCP 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); - + // class KLReader : public ASDCP::KLVPacket { @@ -146,7 +146,7 @@ namespace ASDCP inline const byte_t* Key() { return m_KeyBuf; } inline const ui64_t Length() { return m_ValueLength; } inline const ui64_t KLLength() { return m_KLLength; } - + Result_t ReadKLFromFile(Kumu::FileReader& Reader); }; @@ -207,7 +207,7 @@ namespace ASDCP if ( KM_SUCCESS(result) ) { Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object); - + if ( KM_SUCCESS(cr_result) ) MD_to_CryptoInfo((CryptographicContext*)Object, m_Info, *m_Dict); } @@ -223,7 +223,9 @@ namespace ASDCP if ( KM_SUCCESS(result) ) result = m_HeaderPart.InitFromFile(m_File); - + else + DefaultLogSink().Error("ASDCP::h__Reader::OpenMXFRead, OpenRead failed\n"); + return result; } @@ -266,6 +268,28 @@ namespace ASDCP FrameNum, SequenceNum, FrameBuf, EssenceUL, Ctx, HMAC); } + // Get the position of a frame from a track file + Result_t LocateFrame(const ASDCP::MXF::Partition& CurrentPartition, + ui32_t FrameNum, Kumu::fpos_t& streamOffset, + i8_t& temporalOffset, i8_t& keyFrameOffset) + { + // look up frame index node + IndexTableSegment::IndexEntry TmpEntry; + + if ( KM_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) ) + { + DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum); + return RESULT_RANGE; + } + + // get frame position, temporal offset, and key frame ofset + streamOffset = CurrentPartition.BodyOffset + TmpEntry.StreamOffset; + temporalOffset = TmpEntry.TemporalOffset; + keyFrameOffset = TmpEntry.KeyFrameOffset; + + return RESULT_OK; + } + // void Close() { m_File.Close(); @@ -291,6 +315,9 @@ namespace ASDCP Result_t InitMXFIndex(); Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC); + Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, + i8_t& temporalOffset, i8_t& keyFrameOffset); + }; @@ -392,13 +419,13 @@ namespace ASDCP { public: byte_t Data[klv_intpack_size]; - + IntegrityPack() { memset(Data, 0, klv_intpack_size); } ~IntegrityPack() {} - + Result_t CalcValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC); Result_t TestValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC); }; diff --git a/src/AtmosSyncChannel_Generator.cpp b/src/AtmosSyncChannel_Generator.cpp new file mode 100644 index 0000000..b9b8c44 --- /dev/null +++ b/src/AtmosSyncChannel_Generator.cpp @@ -0,0 +1,149 @@ +/* +Copyright (c) 2013-2013, 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 AtmosSyncChannel_Generator.cpp + \version $Id$ + \brief Dolby Atmos sync channel generator implementation +*/ + +#include + +#include + +using namespace ASDCP; + +// +ASDCP::PCM::AtmosSyncChannelGenerator::AtmosSyncChannelGenerator(ui16_t bitsPerSample, ui32_t sampleRate, + const ASDCP::Rational& editRate, const byte_t* uuid) + : m_syncEncoder(), + m_audioTrackUUID(), + m_ADesc(), + m_syncSignalBuffer(NULL), + m_numSamplesPerFrame(0), + m_currentFrameNumber(0), + m_numBytesPerFrame(0), + m_isSyncEncoderInitialized(false) +{ + + m_ADesc.EditRate = editRate; + m_ADesc.ChannelCount = 1; + m_ADesc.QuantizationBits = bitsPerSample; + m_ADesc.AudioSamplingRate = Rational(sampleRate, 1); + m_ADesc.BlockAlign = ((bitsPerSample + 7) / 8); + m_ADesc.AvgBps = (sampleRate * m_ADesc.BlockAlign); + + memcpy(&m_audioTrackUUID.abyUUIDBytes[0], uuid, UUIDlen); + m_numSamplesPerFrame = (editRate.Denominator * sampleRate) / editRate.Numerator; + m_numBytesPerFrame = m_numSamplesPerFrame * m_ADesc.BlockAlign; + + if (bitsPerSample == 24) + { + ui32_t frameRate = editRate.Numerator/editRate.Denominator; // intentionally allowing for imprecise cast to int + m_isSyncEncoderInitialized = (SyncEncoderInit(&m_syncEncoder, sampleRate, frameRate, &m_audioTrackUUID) == SYNC_ENCODER_ERROR_NONE); + m_syncSignalBuffer = new float[m_numSamplesPerFrame]; + } + else + { + m_isSyncEncoderInitialized = false; + } +} + +ASDCP::PCM::AtmosSyncChannelGenerator::~AtmosSyncChannelGenerator() +{ + delete [] m_syncSignalBuffer; +} + +ASDCP::Result_t +ASDCP::PCM::AtmosSyncChannelGenerator::ReadFrame(FrameBuffer& OutFB) +{ + if (OutFB.Capacity() < m_numBytesPerFrame) + { + return RESULT_SMALLBUF; + } + + /** + * Update frame number and size. + */ + OutFB.FrameNumber(m_currentFrameNumber); + OutFB.Size(m_numBytesPerFrame); + + /** + * Get pointer to frame essence. + */ + byte_t* frameEssence = OutFB.Data(); + + if (m_isSyncEncoderInitialized) + { + /** + * Generate sync signal frame. + */ + int ret = EncodeSync(&m_syncEncoder, m_numSamplesPerFrame, m_syncSignalBuffer, m_currentFrameNumber); + if (ret == SYNC_ENCODER_ERROR_NONE) + { + for (unsigned int i = 0; i < m_numSamplesPerFrame; ++i) + { + /** + * Convert each encoded float sample to a signed 24 bit integer and + * copy into the essence buffer. + */ + i32_t sample = convertSampleFloatToInt24(m_syncSignalBuffer[i]); + memcpy(frameEssence, ((byte_t*)(&sample))+1, NUM_BYTES_PER_INT24); + frameEssence += NUM_BYTES_PER_INT24; + } + } + else + { + /** + * Encoding error, zero out the frame. + */ + memset(frameEssence, 0, m_numBytesPerFrame); + } + } + else + { + /** + * Sync encoder not initialize, zero out the frame. + */ + memset(frameEssence, 0, m_numBytesPerFrame); + } + ++m_currentFrameNumber; + return RESULT_OK; +} + +ASDCP::Result_t +ASDCP::PCM::AtmosSyncChannelGenerator::Reset() +{ + m_currentFrameNumber = 0; + return RESULT_OK; +} + +Result_t +ASDCP::PCM::AtmosSyncChannelGenerator::FillAudioDescriptor(AudioDescriptor& ADesc) const +{ + ADesc = m_ADesc; + return RESULT_OK; +} + diff --git a/src/AtmosSyncChannel_Generator.h b/src/AtmosSyncChannel_Generator.h new file mode 100644 index 0000000..d7f4219 --- /dev/null +++ b/src/AtmosSyncChannel_Generator.h @@ -0,0 +1,151 @@ +/* +Copyright (c) 2013-2013, 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 AtmosSyncChannel_Generator.h + \version $Id$ + \brief Dolby Atmos sync channel generator +*/ + +#ifndef _ATMOSSYNCCHANNEL_GENERATOR_H_ +#define _ATMOSSYNCCHANNEL_GENERATOR_H_ + +#include +#include "SyncEncoder.h" +#include "UUIDInformation.h" + +#define INT24_MAX 8388607.0 +#define INT24_MIN -8388608.0 + +namespace ASDCP +{ + namespace ATMOS + { + static const ui32_t SYNC_CHANNEL = 14; + } + + namespace PCM + { + + static const ui16_t NUM_BYTES_PER_INT24 = 3; + + class AtmosSyncChannelGenerator + { + SYNCENCODER m_syncEncoder; + UUIDINFORMATION m_audioTrackUUID; + AudioDescriptor m_ADesc; + float *m_syncSignalBuffer; + ui32_t m_numSamplesPerFrame; + ui32_t m_currentFrameNumber; + ui32_t m_numBytesPerFrame; + bool m_isSyncEncoderInitialized; + + ASDCP_NO_COPY_CONSTRUCT(AtmosSyncChannelGenerator); + + public: + /** + * Constructor + * + * @param bitsPerSample the number of bits in each sample of pcm data + * @param sampleRate the sampling rate + * @param editRate the edit rate of the associated picture track. + * @param atmosUUID the UUID of the associated ATMOS track file. + * + */ + AtmosSyncChannelGenerator(ui16_t bitsPerSample, ui32_t sampleRate, + const ASDCP::Rational& editRate, const byte_t* uuid); + ~AtmosSyncChannelGenerator(); + + /** + * Set the frame number when seeking + * Use override the default starting frame number for a new track or + * to set the frame number when doing random access. + * + * @param frameNumber + * + */ + void setFrameNumber(ui32_t frameNumber) { m_currentFrameNumber = frameNumber; }; + + /** + * Get the number of bytes per frame. + * + * @return Number of bytes per frame + * + */ + ui32_t getBytesPerFrame() { return m_numBytesPerFrame; } + + /** + * Generates the next frame of sync data. + * Generates the next frame of sync data and places it + * the frame buffer. Fails if the buffer is too small. + * **Automatically increments the frame number.** + * + * @param buf the buffer that the generated frame data will be written to. + * + * @return Kumu::RESULT_OK if the buffer is succesfully filled with sync + * data for the next frame. + */ + Result_t ReadFrame(FrameBuffer& buf); + + /** + * Reset the frame count. + * + * @return Kumu::RESULT_OK + */ + Result_t Reset(); + + /** + * Fill the AudioDescriptor with the relevant information. + * + * @return Kumu::RESULT_OK + */ + Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const; + + /** + * Converts a sample float into + * 24-bit PCM data. + * + */ + static inline i32_t convertSampleFloatToInt24(float sample) + { + if (sample >= 0.0) + { + return (static_cast(sample * INT24_MAX) << 8); + } + else + { + return (static_cast(-sample * INT24_MIN) << 8); + } + } + }; + + } // namespace PCM +} // namespace ASDCP + +#endif // _ATMOSSYNCCHANNEL_GENERATOR_H_ + +// +// end AtmosSyncChannel_Generator.h +// diff --git a/src/AtmosSyncChannel_Mixer.cpp b/src/AtmosSyncChannel_Mixer.cpp new file mode 100644 index 0000000..70cfbfd --- /dev/null +++ b/src/AtmosSyncChannel_Mixer.cpp @@ -0,0 +1,326 @@ +/* +Copyright (c) 2013-2013, 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 AtmosSyncChannel_Mixer.cpp + \version $Id$ + \brief Read WAV files(s), multiplex multiple PCM frame buffers including Atmos Sync into one +*/ + +#include + +#include + +#include +#include +#include + +using namespace ASDCP; +using namespace Kumu; + +// +ASDCP::AtmosSyncChannelMixer::AtmosSyncChannelMixer(const byte_t * trackUUID) + : m_inputs(), m_outputs(), m_trackUUID(), m_ADesc(), m_ChannelCount(0), m_FramesRead(0) +{ + ::memcpy(m_trackUUID, trackUUID, UUIDlen); +} + +ASDCP::AtmosSyncChannelMixer::~AtmosSyncChannelMixer() +{ + clear(); +} + +void +ASDCP::AtmosSyncChannelMixer::clear() +{ + m_outputs.clear(); + std::for_each(m_inputs.begin(), m_inputs.end(), delete_input()); + m_inputs.clear(); +} + +// +Result_t +ASDCP::AtmosSyncChannelMixer::OpenRead(ui32_t argc, const char** argv, const Rational& PictureRate) +{ + ASDCP_TEST_NULL_STR(argv); + PathList_t TmpFileList; + + for ( ui32_t i = 0; i < argc; ++i ) + TmpFileList.push_back(argv[i]); + + return OpenRead(TmpFileList, PictureRate); +} + +// +Result_t +ASDCP::AtmosSyncChannelMixer::OpenRead(const Kumu::PathList_t& argv, const Rational& PictureRate) +{ + Result_t result = RESULT_OK; + PathList_t::iterator fi; + Kumu::PathList_t file_list; + PCM::AudioDescriptor tmpDesc; + + if ( argv.size() == 1 && PathIsDirectory(argv.front()) ) + { + DirScanner Dir; + char name_buf[MaxFilePath]; + result = Dir.Open(argv.front().c_str()); + + if ( KM_SUCCESS(result) ) + result = Dir.GetNext(name_buf); + + while ( KM_SUCCESS(result) ) + { + if ( name_buf[0] != '.' ) // no hidden files + { + std::string tmp_path = argv.front() + "/" + name_buf; + file_list.push_back(tmp_path); + } + + result = Dir.GetNext(name_buf); + } + + if ( result == RESULT_ENDOFFILE ) + { + result = RESULT_OK; + file_list.sort(); + } + } + else + { + file_list = argv; + } + + for ( fi = file_list.begin(); KM_SUCCESS(result) && fi != file_list.end(); ++fi ) + { + result = OpenRead(*fi, PictureRate); + } + + if ( ASDCP_SUCCESS(result) && (m_ChannelCount < ATMOS::SYNC_CHANNEL)) + { + // atmos sync channel has not been added + result = MixInSilenceChannels(); + if ( ASDCP_SUCCESS(result) ) + result = MixInAtmosSyncChannel(); + } + + if ( ASDCP_SUCCESS(result) ) + { + m_ADesc.ChannelCount = m_ChannelCount; + m_ADesc.AvgBps = (ui32_t)(ceil(m_ADesc.AudioSamplingRate.Quotient()) * m_ADesc.BlockAlign); + } + else + { + clear(); + } + + return result; +} + +// +Result_t +ASDCP::AtmosSyncChannelMixer::OpenRead(const std::string& file, const Rational& PictureRate) +{ + Result_t result = RESULT_OK; + PCM::AudioDescriptor tmpDesc; + ui32_t numChannels = 0; + mem_ptr I = new WAVDataProvider; + result = I->OpenRead(file.c_str(), PictureRate); + + if ( ASDCP_SUCCESS(result)) + { + result = I->FillAudioDescriptor(tmpDesc); + } + + if ( ASDCP_SUCCESS(result) ) + { + + if ( m_ChannelCount == 0 ) + { + m_ADesc = tmpDesc; + } + else + { + + if ( tmpDesc.AudioSamplingRate != m_ADesc.AudioSamplingRate ) + { + DefaultLogSink().Error("AudioSamplingRate mismatch in PCM parser list."); + return RESULT_FORMAT; + } + + if ( tmpDesc.QuantizationBits != m_ADesc.QuantizationBits ) + { + DefaultLogSink().Error("QuantizationBits mismatch in PCM parser list."); + return RESULT_FORMAT; + } + + if ( tmpDesc.ContainerDuration < m_ADesc.ContainerDuration ) + m_ADesc.ContainerDuration = tmpDesc.ContainerDuration; + + m_ADesc.BlockAlign += tmpDesc.BlockAlign; + } + } + + + if ( ASDCP_SUCCESS(result) ) + { + numChannels = tmpDesc.ChannelCount; // default to all channels + if ((m_ChannelCount < ATMOS::SYNC_CHANNEL) && (m_ChannelCount + numChannels) > (ATMOS::SYNC_CHANNEL - 1)) + { + // need to insert an atmos channel between the channels of this file. + numChannels = ATMOS::SYNC_CHANNEL - m_ChannelCount - 1; + m_outputs.push_back(std::make_pair(numChannels, I.get())); + m_ChannelCount += numChannels; + MixInAtmosSyncChannel(); + numChannels = tmpDesc.ChannelCount - numChannels; + } + m_outputs.push_back(std::make_pair(numChannels, I.get())); + m_inputs.push_back(I); + I.release(); + m_ChannelCount += numChannels; + } + return result; +} + +Result_t +ASDCP::AtmosSyncChannelMixer::MixInSilenceChannels() +{ + Result_t result = RESULT_OK; + PCM::AudioDescriptor tmpDesc; + ui32_t numSilenceChannels = ATMOS::SYNC_CHANNEL - m_ChannelCount - 1; + if (numSilenceChannels > 0) + { + mem_ptr I = new SilenceDataProvider(numSilenceChannels, + m_ADesc.QuantizationBits, + m_ADesc.AudioSamplingRate.Numerator, + m_ADesc.EditRate); + result = I->FillAudioDescriptor(tmpDesc); + if ( ASDCP_SUCCESS(result) ) + { + m_ADesc.BlockAlign += tmpDesc.BlockAlign; + m_ChannelCount += tmpDesc.ChannelCount; + m_outputs.push_back(std::make_pair(numSilenceChannels, I.get())); + m_inputs.push_back(I); + I.release(); + assert(m_ChannelCount == (ATMOS::SYNC_CHANNEL - 1)); + } + } + return result; +} + +// +Result_t +ASDCP::AtmosSyncChannelMixer::MixInAtmosSyncChannel() +{ + Result_t result = RESULT_OK; + PCM::AudioDescriptor tmpDesc; + mem_ptr I = new AtmosSyncDataProvider(m_ADesc.QuantizationBits, + m_ADesc.AudioSamplingRate.Numerator, + m_ADesc.EditRate, m_trackUUID); + result = I->FillAudioDescriptor(tmpDesc); + if ( ASDCP_SUCCESS(result) ) + { + m_ADesc.BlockAlign += tmpDesc.BlockAlign; + m_ChannelCount += tmpDesc.ChannelCount; + m_outputs.push_back(std::make_pair(tmpDesc.ChannelCount, I.get())); + m_inputs.push_back(I); + I.release(); + assert(m_ChannelCount == ATMOS::SYNC_CHANNEL); + } + return result; +} + +// +Result_t +ASDCP::AtmosSyncChannelMixer::FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const +{ + ADesc = m_ADesc; + return RESULT_OK; +} + +// +Result_t +ASDCP::AtmosSyncChannelMixer::Reset() +{ + Result_t result = RESULT_OK; + SourceList::iterator it; + SourceList::iterator lastInput = m_inputs.end(); + + for ( it = m_inputs.begin(); it != lastInput && ASDCP_SUCCESS(result) ; ++it ) + result = (*it)->Reset(); + + return result; +} + + +//2 +Result_t +ASDCP::AtmosSyncChannelMixer::ReadFrame(PCM::FrameBuffer& OutFB) +{ + + + Result_t result = RESULT_OK; + SourceList::iterator iter; + SourceList::iterator lastInput = m_inputs.end(); + ui32_t bufSize = PCM::CalcFrameBufferSize(m_ADesc); + assert( bufSize <= OutFB.Capacity()); + + for ( iter = m_inputs.begin(); iter != lastInput && ASDCP_SUCCESS(result) ; ++iter ) + result = (*iter)->ReadFrame(); + + if ( ASDCP_SUCCESS(result) ) + { + OutFB.Size(bufSize); + byte_t* Out_p = OutFB.Data(); + byte_t* End_p = Out_p + OutFB.Size(); + ui32_t bytesWritten = 0; + OutputList::iterator iter; + OutputList::iterator lastOutput = m_outputs.end(); + + while ( Out_p < End_p && ASDCP_SUCCESS(result) ) + { + iter = m_outputs.begin(); + while ( iter != lastOutput && ASDCP_SUCCESS(result) ) + { + result = ((*iter).second)->PutSample((*iter).first, Out_p, &bytesWritten); + Out_p += bytesWritten; + ++iter; + } + } + + if ( ASDCP_SUCCESS(result) ) + { + assert(Out_p == End_p); + OutFB.FrameNumber(m_FramesRead++); + } + } + + return result; +} + + +// +// end AtmosSyncChannel_Mixer.cpp +// diff --git a/src/AtmosSyncChannel_Mixer.h b/src/AtmosSyncChannel_Mixer.h new file mode 100644 index 0000000..c6a27a3 --- /dev/null +++ b/src/AtmosSyncChannel_Mixer.h @@ -0,0 +1,91 @@ +/* +Copyright (c) 2013-2013, 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 AtmosSyncChannel_Mixer.h + \version $Id$ + \brief Read WAV files(s), multiplex multiple PCM frame buffers including Atmos Sync into one +*/ + +#ifndef _ATMOSSYNCCHANNEL_MIXER_H_ +#define _ATMOSSYNCCHANNEL_MIXER_H_ + +#include +#include +#include +#include + +namespace ASDCP +{ + + // + class AtmosSyncChannelMixer + { + typedef std::pair InputBus; + typedef std::vector OutputList; + typedef std::vector SourceList; + + SourceList m_inputs; + OutputList m_outputs; + byte_t m_trackUUID[ASDCP::UUIDlen]; + + Result_t OpenRead(const std::string& file, const Rational& PictureRate); + Result_t MixInSilenceChannels(); + Result_t MixInAtmosSyncChannel(); + void clear(); + + // functor for deleting + struct delete_input + { + void operator()(PCMDataProviderInterface* i) + { + delete i; + } + }; + + ASDCP_NO_COPY_CONSTRUCT(AtmosSyncChannelMixer); + + protected: + PCM::AudioDescriptor m_ADesc; + ui32_t m_ChannelCount; + ui32_t m_FramesRead; + + public: + AtmosSyncChannelMixer(const byte_t * trackUUID); + virtual ~AtmosSyncChannelMixer(); + + Result_t OpenRead(ui32_t argc, const char** argv, const Rational& PictureRate); + Result_t OpenRead(const Kumu::PathList_t& argv, const Rational& PictureRate); + Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const; + Result_t Reset(); + Result_t ReadFrame(PCM::FrameBuffer& OutFB); + }; +} // namespace ASDCP + +#endif // _ATMOSSYNCCHANNEL_MIXER_H_ + +// +// end AtmosSyncChannel_Mixer.h +// diff --git a/src/CRC16.c b/src/CRC16.c new file mode 100644 index 0000000..3e2f09e --- /dev/null +++ b/src/CRC16.c @@ -0,0 +1,86 @@ +/* +Copyright (c) 2013-2013, 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 CRC16.c + \version $Id$ + \brief Implementation of a CRC function +*/ + +#include "CRC16.h" + +static const unsigned short g_aushCRC16tab[256]= { + 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, + 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, + 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, + 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, + 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, + 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, + 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, + 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, + 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, + 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, + 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, + 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, + 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, + 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, + 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, + 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, + 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, + 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, + 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, + 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, + 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, + 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, + 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, + 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, + 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, + 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, + 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, + 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, + 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, + 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, + 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, + 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 +}; + +unsigned short CRC16(const void *pData, int ilength) +{ + int n; + unsigned short ushCRC; + unsigned char *puchData; + + + puchData = (unsigned char*)pData; + + ushCRC = 0; + for(n = 0; n < ilength; n ++){ + + ushCRC = (ushCRC << 8) ^ g_aushCRC16tab[((ushCRC>>8) ^ *puchData) & 0x00FF]; + puchData ++; + } + + return ushCRC; +} diff --git a/src/CRC16.h b/src/CRC16.h new file mode 100644 index 0000000..6d3beb3 --- /dev/null +++ b/src/CRC16.h @@ -0,0 +1,45 @@ +/* +Copyright (c) 2013-2013, 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 CRC16.h + \version $Id$ + \brief Declaration of a CRC function +*/ + +#ifndef _CRC_16_H_ +#define _CRC_16_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned short CRC16(const void *pData, int ilength); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/DCData_ByteStream_Parser.cpp b/src/DCData_ByteStream_Parser.cpp new file mode 100644 index 0000000..73565d4 --- /dev/null +++ b/src/DCData_ByteStream_Parser.cpp @@ -0,0 +1,121 @@ +/* +Copyright (c) 2013-2013, 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 AtmosSyncChannel_Mixer.h + \version $Id$ + \brief AS-DCP library, Digital Cinema Data bytestream essence reader +*/ + +#include "AS_DCP.h" +#include "KM_fileio.h" +#include "KM_log.h" +using Kumu::DefaultLogSink; + +//------------------------------------------------------------------------------------------ + +class ASDCP::DCData::BytestreamParser::h__BytestreamParser +{ + ASDCP_NO_COPY_CONSTRUCT(h__BytestreamParser); + +public: + DCDataDescriptor m_DDesc; + Kumu::FileReader m_File; + + h__BytestreamParser() + { + memset(&m_DDesc, 0, sizeof(m_DDesc)); + m_DDesc.EditRate = Rational(24,1); + } + + ~h__BytestreamParser() {} + + Result_t OpenReadFrame(const char* filename, FrameBuffer& FB) + { + ASDCP_TEST_NULL_STR(filename); + m_File.Close(); + Result_t result = m_File.OpenRead(filename); + + if ( ASDCP_SUCCESS(result) ) + { + Kumu::fsize_t file_size = m_File.Size(); + + if ( FB.Capacity() < file_size ) + { + DefaultLogSink().Error("FrameBuf.Capacity: %u frame length: %u\n", FB.Capacity(), (ui32_t)file_size); + return RESULT_SMALLBUF; + } + } + + ui32_t read_count; + + if ( ASDCP_SUCCESS(result) ) + result = m_File.Read(FB.Data(), FB.Capacity(), &read_count); + + if ( ASDCP_SUCCESS(result) ) + FB.Size(read_count); + + return result; + } +}; + + +//------------------------------------------------------------------------------------------ + +ASDCP::DCData::BytestreamParser::BytestreamParser() +{ +} + +ASDCP::DCData::BytestreamParser::~BytestreamParser() +{ +} + +// Opens the stream for reading, parses enough data to provide a complete +// set of stream metadata for the MXFWriter below. +ASDCP::Result_t +ASDCP::DCData::BytestreamParser::OpenReadFrame(const char* filename, FrameBuffer& FB) const +{ + const_cast(this)->m_Parser = new h__BytestreamParser; + return m_Parser->OpenReadFrame(filename, FB); +} + +// +ASDCP::Result_t +ASDCP::DCData::BytestreamParser::FillDCDataDescriptor(DCDataDescriptor& DDesc) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + DDesc = m_Parser->m_DDesc; + return RESULT_OK; +} + + +// +// end DCData_Bytestream_Parser.cpp +// + + + diff --git a/src/DCData_Sequence_Parser.cpp b/src/DCData_Sequence_Parser.cpp new file mode 100644 index 0000000..45594c5 --- /dev/null +++ b/src/DCData_Sequence_Parser.cpp @@ -0,0 +1,294 @@ +/* +Copyright (c) 2013-2013, 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 AtmosSyncChannel_Mixer.h + \version $Id$ + \brief AS-DCP library, DCinema data seqence reader implementation +*/ + +#include "AS_DCP.h" + +#include +#include +#include + +#include "KM_fileio.h" +#include "KM_log.h" + +using ASDCP::Result_t; + +//------------------------------------------------------------------------------------------ +namespace ASDCP +{ +namespace DCData +{ +class FileList; +} // namespace DCData +} // namespace ASDCP + + +class ASDCP::DCData::FileList : public std::list +{ + std::string m_DirName; + + public: + FileList() {} + ~FileList() {} + + const FileList& operator=(const std::list& pathlist) { + std::list::const_iterator i; + for ( i = pathlist.begin(); i != pathlist.end(); i++ ) + push_back(*i); + return *this; + } + + // + Result_t InitFromDirectory(const char* path) + { + char next_file[Kumu::MaxFilePath]; + Kumu::DirScanner Scanner; + + Result_t result = Scanner.Open(path); + + if ( ASDCP_SUCCESS(result) ) + { + m_DirName = path; + + while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) ) + { + if ( next_file[0] == '.' ) // no hidden files or internal links + continue; + + std::string Str(m_DirName); + Str += "/"; + Str += next_file; + + if ( ! Kumu::PathIsDirectory(Str) ) + push_back(Str); + } + + sort(); + } + + return result; + } +}; + +//------------------------------------------------------------------------------------------ + +class ASDCP::DCData::SequenceParser::h__SequenceParser +{ + ui32_t m_FramesRead; + Rational m_PictureRate; + FileList m_FileList; + FileList::iterator m_CurrentFile; + BytestreamParser m_Parser; + + Result_t OpenRead(); + + ASDCP_NO_COPY_CONSTRUCT(h__SequenceParser); + + public: + DCDataDescriptor m_DDesc; + + h__SequenceParser() : m_FramesRead(0) + { + memset(&m_DDesc, 0, sizeof(m_DDesc)); + m_DDesc.EditRate = Rational(24,1); + } + + ~h__SequenceParser() + { + Close(); + } + + Result_t OpenRead(const char* filename); + Result_t OpenRead(const std::list& file_list); + void Close() {} + + Result_t Reset() + { + m_FramesRead = 0; + m_CurrentFile = m_FileList.begin(); + return RESULT_OK; + } + + Result_t ReadFrame(FrameBuffer&); +}; + + +// +ASDCP::Result_t +ASDCP::DCData::SequenceParser::h__SequenceParser::OpenRead() +{ + if ( m_FileList.empty() ) + return RESULT_ENDOFFILE; + + m_CurrentFile = m_FileList.begin(); + BytestreamParser Parser; + FrameBuffer TmpBuffer; + + Kumu::fsize_t file_size = Kumu::FileSize((*m_CurrentFile).c_str()); + + if ( file_size == 0 ) + return RESULT_NOT_FOUND; + + assert(file_size <= 0xFFFFFFFFL); + Result_t result = TmpBuffer.Capacity((ui32_t) file_size); + + if ( ASDCP_SUCCESS(result) ) + result = Parser.OpenReadFrame((*m_CurrentFile).c_str(), TmpBuffer); + + if ( ASDCP_SUCCESS(result) ) + result = Parser.FillDCDataDescriptor(m_DDesc); + + // how big is it? + if ( ASDCP_SUCCESS(result) ) + m_DDesc.ContainerDuration = m_FileList.size(); + + return result; +} + +// +ASDCP::Result_t +ASDCP::DCData::SequenceParser::h__SequenceParser::OpenRead(const char* filename) +{ + ASDCP_TEST_NULL_STR(filename); + + Result_t result = m_FileList.InitFromDirectory(filename); + + if ( ASDCP_SUCCESS(result) ) + result = OpenRead(); + + return result; +} + + +// +ASDCP::Result_t +ASDCP::DCData::SequenceParser::h__SequenceParser::OpenRead(const std::list& file_list) +{ + m_FileList = file_list; + return OpenRead(); +} + +// +ASDCP::Result_t +ASDCP::DCData::SequenceParser::h__SequenceParser::ReadFrame(FrameBuffer& FB) +{ + if ( m_CurrentFile == m_FileList.end() ) + return RESULT_ENDOFFILE; + + // open the file + Result_t result = m_Parser.OpenReadFrame((*m_CurrentFile).c_str(), FB); + + if ( ASDCP_SUCCESS(result) ) + { + FB.FrameNumber(m_FramesRead++); + m_CurrentFile++; + } + + return result; +} + + +//------------------------------------------------------------------------------------------ + +ASDCP::DCData::SequenceParser::SequenceParser() +{ +} + +ASDCP::DCData::SequenceParser::~SequenceParser() +{ +} + +// Opens the stream for reading, parses enough data to provide a complete +// set of stream metadata for the MXFWriter below. +ASDCP::Result_t +ASDCP::DCData::SequenceParser::OpenRead(const char* filename) const +{ + const_cast(this)->m_Parser = new h__SequenceParser; + + Result_t result = m_Parser->OpenRead(filename); + + if ( ASDCP_FAILURE(result) ) + const_cast(this)->m_Parser.release(); + + return result; +} + +// +Result_t +ASDCP::DCData::SequenceParser::OpenRead(const std::list& file_list) const +{ + const_cast(this)->m_Parser = new h__SequenceParser; + + Result_t result = m_Parser->OpenRead(file_list); + + if ( ASDCP_FAILURE(result) ) + const_cast(this)->m_Parser.release(); + + return result; +} + + +// Rewinds the stream to the beginning. +ASDCP::Result_t +ASDCP::DCData::SequenceParser::Reset() const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + return m_Parser->Reset(); +} + +// Places a frame of data in the frame buffer. Fails if the buffer is too small +// or the stream is empty. +ASDCP::Result_t +ASDCP::DCData::SequenceParser::ReadFrame(FrameBuffer& FB) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + return m_Parser->ReadFrame(FB); +} + +// +ASDCP::Result_t +ASDCP::DCData::SequenceParser::FillDCDataDescriptor(DCDataDescriptor& DDesc) const +{ + if ( m_Parser.empty() ) + return RESULT_INIT; + + DDesc = m_Parser->m_DDesc; + return RESULT_OK; +} + + +// +// end DCData_Sequence_Parser.cpp +// + diff --git a/src/Index.cpp b/src/Index.cpp index 449d5bf..f377585 100755 --- a/src/Index.cpp +++ b/src/Index.cpp @@ -143,7 +143,7 @@ ASDCP::MXF::IndexTableSegment::Dump(FILE* stream) } else { - fprintf(stream, " IndexEntryArray: %du entries\n", IndexEntryArray.size()); + fprintf(stream, " IndexEntryArray: %zu entries\n", IndexEntryArray.size()); } } diff --git a/src/MDD.cpp b/src/MDD.cpp index 5627624..5afb75e 100644 --- a/src/MDD.cpp +++ b/src/MDD.cpp @@ -1,5 +1,5 @@ /* -Copyright (c) 2006-2012, John Hurst +Copyright (c) 2006-2013, John Hurst All rights reserved. Redistribution and use in source and binary forms, with or without @@ -917,6 +917,85 @@ static const ASDCP::MDDEntry s_MDD_Table[] = { { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0d, // 293 0x01, 0x03, 0x07, 0x01, 0x04, 0x00, 0x00, 0x00 }, {0}, false, "SoundfieldGroupLabelSubDescriptor_GroupOfSoundfieldGroupsLinkID" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x05, // 294 + 0x0e, 0x09, 0x06, 0x05, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCDataWrapping" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x05, // 295 + 0x0e, 0x09, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCDataEssence" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x05, // 296 + 0x0e, 0x09, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCDataDescriptor" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x05, // 297 + 0x0e, 0x09, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DolbyAtmosSubDescriptor" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 298 + 0x0e, 0x09, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00 }, + {0}, true, "DolbyAtmosSubDescriptor_AtmosVersion" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 299 + 0x0e, 0x09, 0x05, 0x07, 0x00, 0x00, 0x00, 0x00 }, + {0}, true, "DolbyAtmosSubDescriptor_MaxChannelCount" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 300 + 0x0e, 0x09, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00 }, + {0}, true, "DolbyAtmosSubDescriptor_MaxObjectCount" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 301 + 0x0e, 0x09, 0x05, 0x09, 0x00, 0x00, 0x00, 0x00 }, + {0}, true, "DolbyAtmosSubDescriptor_AtmosID" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 302 + 0x0e, 0x09, 0x05, 0x0A, 0x00, 0x00, 0x00, 0x00 }, + {0}, true, "DolbyAtmosSubDescriptor_FirstFrame" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01, // 303 + 0x01, 0x03, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00 }, + {0}, false, "DataDataDef" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 304 + 0x04, 0x02, 0x02, 0x10, 0x03, 0x02, 0x00, 0x00 }, + {0}, false, "DCAudioChannelCfg_MCA" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 305 + 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_L" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 306 + 0x03, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_R" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 307 + 0x03, 0x02, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_C" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 308 + 0x03, 0x02, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_LFE" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 309 + 0x03, 0x02, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Ls" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 310 + 0x03, 0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Rs" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 311 + 0x03, 0x02, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Lss" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 312 + 0x03, 0x02, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Rss" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 313 + 0x03, 0x02, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Lrs" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 314 + 0x03, 0x02, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Rrs" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 315 + 0x03, 0x02, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Lc" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 316 + 0x03, 0x02, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Rc" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 317 + 0x03, 0x02, 0x01, 0x0d, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_Cs" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 318 + 0x03, 0x02, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_HI" }, + { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 319 + 0x03, 0x02, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00 }, + {0}, false, "DCAudioChannel_VIN" }, + { {0}, {0}, false, 0 } }; diff --git a/src/MDD.h b/src/MDD.h index 358b874..ab8482f 100755 --- a/src/MDD.h +++ b/src/MDD.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2006-2012, John Hurst +Copyright (c) 2006-2013, John Hurst All rights reserved. Redistribution and use in source and binary forms, with or without @@ -290,46 +290,72 @@ namespace ASDCP { MDD_CryptographicContext_CipherAlgorithm, // 252 MDD_CryptographicContext_MICAlgorithm, // 253 MDD_CryptographicContext_CryptographicKeyID, // 254 - MDD_TimedTextWrapping, // 255 - MDD_TimedTextEssence, // 256 - MDD_TimedTextDescriptor, // 257 - MDD_TimedTextDescriptor_ResourceID, // 258 - MDD_TimedTextDescriptor_UCSEncoding, // 259 - MDD_TimedTextDescriptor_NamespaceURI, // 260 - MDD_TimedTextResourceSubDescriptor, // 261 - MDD_TimedTextResourceSubDescriptor_AncillaryResourceID, // 262 - MDD_TimedTextResourceSubDescriptor_MIMEMediaType, // 263 - MDD_TimedTextResourceSubDescriptor_EssenceStreamID_DEPRECATED, // 264 - MDD_GenericStreamPartition, // 265 - MDD_DMSegment_DataDefinition_DEPRECATED, // 266 - MDD_DMSegment_Duration_DEPRECATED, // 267 - MDD_DMSegment_TrackIDList, // 268 - MDD_StereoscopicPictureSubDescriptor, // 269 + MDD_TimedTextWrapping, // 255 + MDD_TimedTextEssence, // 256 + MDD_TimedTextDescriptor, // 257 + MDD_TimedTextDescriptor_ResourceID, // 258 + MDD_TimedTextDescriptor_UCSEncoding, // 259 + MDD_TimedTextDescriptor_NamespaceURI, // 260 + MDD_TimedTextResourceSubDescriptor, // 261 + MDD_TimedTextResourceSubDescriptor_AncillaryResourceID, // 262 + MDD_TimedTextResourceSubDescriptor_MIMEMediaType, // 263 + MDD_TimedTextResourceSubDescriptor_EssenceStreamID_DEPRECATED, // 264 + MDD_GenericStreamPartition, // 265 + MDD_DMSegment_DataDefinition_DEPRECATED, // 266 + MDD_DMSegment_Duration_DEPRECATED, // 267 + MDD_DMSegment_TrackIDList, // 268 + MDD_StereoscopicPictureSubDescriptor, // 269 MDD_WaveAudioDescriptor_ChannelAssignment, // 270 - MDD_GenericStream_DataElement, // 271 + MDD_GenericStream_DataElement, // 271 MDD_MXFInterop_GenericDescriptor_SubDescriptors, // 272 - MDD_Core_BodySID, // 273 - MDD_Core_IndexSID, // 274 - MDD_Core_OperationalPattern, // 275 - MDD_Core_EssenceContainers, // 276 - MDD_DCAudioChannelCfg_1_5p1, // 277 - MDD_DCAudioChannelCfg_2_6p1, // 278 - MDD_DCAudioChannelCfg_3_7p1, // 279 - MDD_DCAudioChannelCfg_4_WTF, // 280 - MDD_DCAudioChannelCfg_5_7p1_DS, // 281 - MDD_MCALabelSubDescriptor, // 282 - MDD_AudioChannelLabelSubDescriptor, // 283 - MDD_SoundfieldGroupLabelSubDescriptor, // 284 - MDD_GroupOfSoundfieldGroupsLabelSubDescriptor, // 285 - MDD_MCALabelSubDescriptor_MCALabelDictionaryID, // 286 - MDD_MCALabelSubDescriptor_MCALinkID, // 287 - MDD_MCALabelSubDescriptor_MCATagSymbol, // 288 - MDD_MCALabelSubDescriptor_MCATagName, // 289 - MDD_MCALabelSubDescriptor_MCAChannelID, // 290 - MDD_MCALabelSubDescriptor_RFC5646SpokenLanguage, // 291 - MDD_AudioChannelLabelSubDescriptor_SoundfieldGroupLinkID, // 292 - MDD_SoundfieldGroupLabelSubDescriptor_GroupOfSoundfieldGroupsLinkID, // 293 - MDD_Max + MDD_Core_BodySID, // 273 + MDD_Core_IndexSID, // 274 + MDD_Core_OperationalPattern, // 275 + MDD_Core_EssenceContainers, // 276 + MDD_DCAudioChannelCfg_1_5p1, // 277 + MDD_DCAudioChannelCfg_2_6p1, // 278 + MDD_DCAudioChannelCfg_3_7p1, // 279 + MDD_DCAudioChannelCfg_4_WTF, // 280 + MDD_DCAudioChannelCfg_5_7p1_DS, // 281 + MDD_MCALabelSubDescriptor, // 282 + MDD_AudioChannelLabelSubDescriptor, // 283 + MDD_SoundfieldGroupLabelSubDescriptor, // 284 + MDD_GroupOfSoundfieldGroupsLabelSubDescriptor, // 285 + MDD_MCALabelSubDescriptor_MCALabelDictionaryID, // 286 + MDD_MCALabelSubDescriptor_MCALinkID, // 287 + MDD_MCALabelSubDescriptor_MCATagSymbol, // 288 + MDD_MCALabelSubDescriptor_MCATagName, // 289 + MDD_MCALabelSubDescriptor_MCAChannelID, // 290 + MDD_MCALabelSubDescriptor_RFC5646SpokenLanguage, // 291 + MDD_AudioChannelLabelSubDescriptor_SoundfieldGroupLinkID, // 292 + MDD_SoundfieldGroupLabelSubDescriptor_GroupOfSoundfieldGroupsLinkID, // 293 + MDD_DCDataWrapping, // 294 + MDD_DCDataEssence, // 295 + MDD_DCDataDescriptor, // 296 + MDD_DolbyAtmosSubDescriptor, // 297 + MDD_DolbyAtmosSubDescriptor_AtmosVersion, // 298 + MDD_DolbyAtmosSubDescriptor_MaxChannelCount, // 299 + MDD_DolbyAtmosSubDescriptor_MaxObjectCount, // 300 + MDD_DolbyAtmosSubDescriptor_AtmosID, // 301 + MDD_DolbyAtmosSubDescriptor_FirstFrame, // 302 + MDD_DataDataDef, // 303 + MDD_DCAudioChannelCfg_MCA, // 304 + MDD_DCAudioChannel_L, // 305 + MDD_DCAudioChannel_R, // 306 + MDD_DCAudioChannel_C, // 307 + MDD_DCAudioChannel_LFE, // 308 + MDD_DCAudioChannel_Ls, // 309 + MDD_DCAudioChannel_Rs, // 310 + MDD_DCAudioChannel_Lss, // 311 + MDD_DCAudioChannel_Rss, // 312 + MDD_DCAudioChannel_Lrs, // 313 + MDD_DCAudioChannel_Rrs, // 314 + MDD_DCAudioChannel_Lc, // 315 + MDD_DCAudioChannel_Rc, // 316 + MDD_DCAudioChannel_Cs, // 317 + MDD_DCAudioChannel_HI, // 318 + MDD_DCAudioChannel_VIN, // 319 + MDD_Max }; // enum MDD_t @@ -342,7 +368,7 @@ namespace ASDCP { const MDD_t MDD_Preface_EssenceContainers = MDD_Core_EssenceContainers; const MDD_t MDD_Preface_OperationalPattern = MDD_Core_OperationalPattern; const MDD_t MDD_TimedTextResourceSubDescriptor_EssenceStreamID = MDD_Core_BodySID; - + } // namespaceASDCP diff --git a/src/MXF.cpp b/src/MXF.cpp index 7f042ee..1b9e558 100755 --- a/src/MXF.cpp +++ b/src/MXF.cpp @@ -725,12 +725,20 @@ ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader) } } } + else + { + DefaultLogSink().Error("OPAtomHeader::InitFromFile, SeekToRIP failed\n"); + } if ( ASDCP_SUCCESS(result) ) result = Reader.Seek(0); + else + DefaultLogSink().Error("OPAtomHeader::InitFromFile, Seek failed\n"); if ( ASDCP_SUCCESS(result) ) result = Partition::InitFromFile(Reader); // test UL and OP + else + DefaultLogSink().Error("OPAtomHeader::InitFromFile, Partition::InitFromFile failed\n"); if ( ASDCP_FAILURE(result) ) return result; @@ -774,7 +782,10 @@ ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader) result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count); if ( ASDCP_FAILURE(result) ) - return result; + { + DefaultLogSink().Error("OPAtomHeader::InitFromFile, Read failed\n"); + return result; + } if ( read_count != m_Buffer.Capacity() ) { @@ -1038,17 +1049,21 @@ ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader) { Result_t result = Partition::InitFromFile(Reader); // test UL and OP - // slurp up the remainder of the footer - ui32_t read_count; + // slurp up the remainder of the footer + ui32_t read_count = 0; - if ( ASDCP_SUCCESS(result) ) + if ( ASDCP_SUCCESS(result) ) { - assert (IndexByteCount <= 0xFFFFFFFFL); - result = m_Buffer.Capacity((ui32_t) IndexByteCount); + assert (IndexByteCount <= 0xFFFFFFFFL); + // At this point, m_Buffer may not have been initialized + // so it's capacity is zero and data pointer is NULL + // However, if IndexByteCount is zero then the capacity + // doesn't change and the data pointer is not set. + result = m_Buffer.Capacity((ui32_t) IndexByteCount); } - if ( ASDCP_SUCCESS(result) ) - result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count); + if ( ASDCP_SUCCESS(result) && m_Buffer.Data() ) + result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count); if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() ) { @@ -1056,6 +1071,12 @@ ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader) read_count, m_Buffer.Capacity()); return RESULT_FAIL; } + else if( ASDCP_SUCCESS(result) && !m_Buffer.Data() ) + { + DefaultLogSink().Error( "Buffer for footer partition not created: IndexByteCount = %u\n", + IndexByteCount ); + return RESULT_FAIL; + } if ( ASDCP_SUCCESS(result) ) result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity()); diff --git a/src/Makefile.am b/src/Makefile.am index 6cd2da3..fdbf3c4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -61,7 +61,8 @@ include_HEADERS += \ MXFTypes.h \ MXF.h \ Wav.h \ - PCMParserList.h + PCMParserList.h \ + AtmosSyncChannel_Generator.h nodist_include_HEADERS = TimedText_Transform.h endif @@ -89,7 +90,16 @@ libasdcp_la_SOURCES = MPEG2_Parser.cpp MPEG.cpp JP2K_Codestream_Parser.cpp \ AS_DCP_PCM.cpp AS_DCP_TimedText.cpp PCMParserList.cpp \ Wav.h WavFileWriter.h MXF.h Metadata.h \ JP2K.h AS_DCP.h AS_DCP_internal.h KLV.h MPEG.h MXFTypes.h MDD.h \ - PCMParserList.h S12MTimecode.h MDD.cpp + PCMParserList.h S12MTimecode.h MDD.cpp \ + AS_DCP_ATMOS.cpp AS_DCP_DCData.cpp AS_DCP_DCData_internal.h \ + DCData_ByteStream_Parser.cpp DCData_Sequence_Parser.cpp \ + AtmosSyncChannel_Generator.cpp AtmosSyncChannel_Generator.h \ + AtmosSyncChannel_Mixer.cpp AtmosSyncChannel_Mixer.h \ + PCMDataProviders.cpp PCMDataProviders.h \ + SyncEncoder.c SyncEncoder.h SyncCommon.h CRC16.c CRC16.h \ + UUIDInformation.c UUIDInformation.h + + if DEV_HEADERS nodist_libasdcp_la_SOURCES += TimedText_Transform.h TimedText_Transform.cpp endif diff --git a/src/Metadata.cpp b/src/Metadata.cpp index ac1db76..3507ba0 100755 --- a/src/Metadata.cpp +++ b/src/Metadata.cpp @@ -73,6 +73,8 @@ static InterchangeObject* MCALabelSubDescriptor_Factory(const Dictionary*& Dict) static InterchangeObject* AudioChannelLabelSubDescriptor_Factory(const Dictionary*& Dict) { return new AudioChannelLabelSubDescriptor(Dict); } static InterchangeObject* SoundfieldGroupLabelSubDescriptor_Factory(const Dictionary*& Dict) { return new SoundfieldGroupLabelSubDescriptor(Dict); } static InterchangeObject* GroupOfSoundfieldGroupsLabelSubDescriptor_Factory(const Dictionary*& Dict) { return new GroupOfSoundfieldGroupsLabelSubDescriptor(Dict); } +static InterchangeObject* DCDataDescriptor_Factory(const Dictionary*& Dict) { return new DCDataDescriptor(Dict); } +static InterchangeObject* DolbyAtmosSubDescriptor_Factory(const Dictionary*& Dict) { return new DolbyAtmosSubDescriptor(Dict); } void @@ -112,6 +114,8 @@ ASDCP::MXF::Metadata_InitTypes(const Dictionary*& Dict) SetObjectFactory(Dict->ul(MDD_AudioChannelLabelSubDescriptor), AudioChannelLabelSubDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor), SoundfieldGroupLabelSubDescriptor_Factory); SetObjectFactory(Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor), GroupOfSoundfieldGroupsLabelSubDescriptor_Factory); + SetObjectFactory(Dict->ul(MDD_DCDataDescriptor), DCDataDescriptor_Factory); + SetObjectFactory(Dict->ul(MDD_DolbyAtmosSubDescriptor), DolbyAtmosSubDescriptor_Factory); } //------------------------------------------------------------------------------------------ @@ -2888,6 +2892,170 @@ GroupOfSoundfieldGroupsLabelSubDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buf return InterchangeObject::WriteToBuffer(Buffer); } +//------------------------------------------------------------------------------------------ +// DCDataDescriptor + +// + +DCDataDescriptor::DCDataDescriptor(const Dictionary*& d) : GenericDataEssenceDescriptor(d), m_Dict(d) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_DCDataDescriptor); +} + +DCDataDescriptor::DCDataDescriptor(const DCDataDescriptor& rhs) : GenericDataEssenceDescriptor(rhs.m_Dict), m_Dict(rhs.m_Dict) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_DCDataDescriptor); + Copy(rhs); +} + +// +ASDCP::Result_t +DCDataDescriptor::InitFromTLVSet(TLVReader& TLVSet) +{ + // NOTE (this function can be removed if no attributes are defined for this descriptor) + assert(m_Dict); + Result_t result = GenericDataEssenceDescriptor::InitFromTLVSet(TLVSet); + return result; +} + +// +ASDCP::Result_t +DCDataDescriptor::WriteToTLVSet(TLVWriter& TLVSet) +{ + // NOTE (this function can be removed if no attributes are defined for this descriptor) + assert(m_Dict); + Result_t result = GenericDataEssenceDescriptor::WriteToTLVSet(TLVSet); + return result; +} + +// +void +DCDataDescriptor::Copy(const DCDataDescriptor& rhs) +{ + GenericDataEssenceDescriptor::Copy(rhs); +} + +// +void +DCDataDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + *identbuf = 0; + + if ( stream == 0 ) + stream = stderr; + + GenericDataEssenceDescriptor::Dump(stream); +} + +// +ASDCP::Result_t +DCDataDescriptor::InitFromBuffer(const byte_t* p, ui32_t l) +{ + return InterchangeObject::InitFromBuffer(p, l); +} + +// +ASDCP::Result_t +DCDataDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return InterchangeObject::WriteToBuffer(Buffer); +} + +//------------------------------------------------------------------------------------------ +// DolbyAtmosSubDescriptor + +// + +DolbyAtmosSubDescriptor::DolbyAtmosSubDescriptor(const Dictionary*& d) : InterchangeObject(d), m_Dict(d), FirstFrame(0), MaxChannelCount(0), MaxObjectCount(0), AtmosVersion(0) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_DolbyAtmosSubDescriptor); +} + +DolbyAtmosSubDescriptor::DolbyAtmosSubDescriptor(const DolbyAtmosSubDescriptor& rhs) : InterchangeObject(rhs.m_Dict), m_Dict(rhs.m_Dict) +{ + assert(m_Dict); + m_UL = m_Dict->ul(MDD_DolbyAtmosSubDescriptor); + Copy(rhs); +} + + +// +ASDCP::Result_t +DolbyAtmosSubDescriptor::InitFromTLVSet(TLVReader& TLVSet) +{ + assert(m_Dict); + Result_t result = InterchangeObject::InitFromTLVSet(TLVSet); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(DolbyAtmosSubDescriptor, AtmosID)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(DolbyAtmosSubDescriptor, FirstFrame)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(DolbyAtmosSubDescriptor, MaxChannelCount)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(DolbyAtmosSubDescriptor, MaxObjectCount)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi8(OBJ_READ_ARGS(DolbyAtmosSubDescriptor, AtmosVersion)); + return result; +} + +// +ASDCP::Result_t +DolbyAtmosSubDescriptor::WriteToTLVSet(TLVWriter& TLVSet) +{ + assert(m_Dict); + Result_t result = InterchangeObject::WriteToTLVSet(TLVSet); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(DolbyAtmosSubDescriptor, AtmosID)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(DolbyAtmosSubDescriptor, FirstFrame)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(DolbyAtmosSubDescriptor, MaxChannelCount)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(DolbyAtmosSubDescriptor, MaxObjectCount)); + if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi8(OBJ_WRITE_ARGS(DolbyAtmosSubDescriptor, AtmosVersion)); + return result; +} + +// +void +DolbyAtmosSubDescriptor::Copy(const DolbyAtmosSubDescriptor& rhs) +{ + InterchangeObject::Copy(rhs); + AtmosID = rhs.AtmosID; + FirstFrame = rhs.FirstFrame; + MaxChannelCount = rhs.MaxChannelCount; + MaxObjectCount = rhs.MaxObjectCount; + AtmosVersion = rhs.AtmosVersion; +} + +// +void +DolbyAtmosSubDescriptor::Dump(FILE* stream) +{ + char identbuf[IdentBufferLen]; + *identbuf = 0; + + if ( stream == 0 ) + stream = stderr; + + InterchangeObject::Dump(stream); + fprintf(stream, " %22s = %s\n", "AtmosID", AtmosID.EncodeString(identbuf, IdentBufferLen)); + fprintf(stream, " %22s = %d\n", "FirstFrame", FirstFrame); + fprintf(stream, " %22s = %d\n", "MaxChannelCount", MaxChannelCount); + fprintf(stream, " %22s = %d\n", "MaxObjectCount", MaxObjectCount); + fprintf(stream, " %22s = %d\n", "AtmosVersion", AtmosVersion); +} + +// +ASDCP::Result_t +DolbyAtmosSubDescriptor::InitFromBuffer(const byte_t* p, ui32_t l) +{ + return InterchangeObject::InitFromBuffer(p, l); +} + +// +ASDCP::Result_t +DolbyAtmosSubDescriptor::WriteToBuffer(ASDCP::FrameBuffer& Buffer) +{ + return InterchangeObject::WriteToBuffer(Buffer); +} + + // // end Metadata.cpp // diff --git a/src/Metadata.h b/src/Metadata.h index 507a151..6b9a50c 100755 --- a/src/Metadata.h +++ b/src/Metadata.h @@ -889,6 +889,55 @@ namespace ASDCP virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); }; + // + class DCDataDescriptor : public GenericDataEssenceDescriptor + { + DCDataDescriptor(); + + public: + const Dictionary*& m_Dict; + + DCDataDescriptor(const Dictionary*& d); + DCDataDescriptor(const DCDataDescriptor& rhs); + virtual ~DCDataDescriptor() {} + + const DCDataDescriptor& operator=(const DCDataDescriptor& rhs) { Copy(rhs); return *this; } + virtual void Copy(const DCDataDescriptor& rhs); + virtual const char* HasName() { return "DCDataDescriptor"; } + virtual Result_t InitFromTLVSet(TLVReader& TLVSet); + virtual Result_t WriteToTLVSet(TLVWriter& TLVSet); + virtual void Dump(FILE* = 0); + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + }; + + // + class DolbyAtmosSubDescriptor : public InterchangeObject + { + DolbyAtmosSubDescriptor(); + + public: + const Dictionary*& m_Dict; + UUID AtmosID; + ui32_t FirstFrame; + ui16_t MaxChannelCount; + ui16_t MaxObjectCount; + ui8_t AtmosVersion; + + DolbyAtmosSubDescriptor(const Dictionary*& d); + DolbyAtmosSubDescriptor(const DolbyAtmosSubDescriptor& rhs); + virtual ~DolbyAtmosSubDescriptor() {} + + const DolbyAtmosSubDescriptor& operator=(const DolbyAtmosSubDescriptor& rhs) { Copy(rhs); return *this; } + virtual void Copy(const DolbyAtmosSubDescriptor& rhs); + virtual const char* HasName() { return "DolbyAtmosSubDescriptor"; } + virtual Result_t InitFromTLVSet(TLVReader& TLVSet); + virtual Result_t WriteToTLVSet(TLVWriter& TLVSet); + virtual void Dump(FILE* = 0); + virtual Result_t InitFromBuffer(const byte_t* p, ui32_t l); + virtual Result_t WriteToBuffer(ASDCP::FrameBuffer&); + }; + } // namespace MXF } // namespace ASDCP diff --git a/src/PCMDataProviders.cpp b/src/PCMDataProviders.cpp new file mode 100644 index 0000000..7d2c152 --- /dev/null +++ b/src/PCMDataProviders.cpp @@ -0,0 +1,210 @@ +/* +Copyright (c) 20013-2013, 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 PCMDataProviders.cpp + \version $Id$ + \brief Implementation of PCM sample data providers for WAV, AtmosSync and Silence. +*/ + +#include + +#include + +using namespace ASDCP; +using namespace Kumu; + +ASDCP::PCMDataProviderInterface::~PCMDataProviderInterface() {} + +// +ASDCP::WAVDataProvider::WAVDataProvider() + : m_Parser(), m_FB(), m_ADesc(), m_SampleSize(0), m_ptr(NULL) +{} + +ASDCP::WAVDataProvider::~WAVDataProvider() +{} + +Result_t +ASDCP::WAVDataProvider::PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten) +{ + ASDCP_TEST_NULL(buf); + ASDCP_TEST_NULL(m_ptr); + if ( numChannels > m_ADesc.ChannelCount) + { + DefaultLogSink().Error("Requested %u channels from a wav file with %u channel.", numChannels, + m_ADesc.ChannelCount); + return RESULT_FAIL; + } + *bytesWritten = m_SampleSize * numChannels; + ::memcpy(buf, m_ptr, *bytesWritten); + m_ptr += *bytesWritten; + return RESULT_OK; +} + +Result_t +ASDCP::WAVDataProvider::ReadFrame() +{ + Result_t result = m_Parser.ReadFrame(m_FB); + m_ptr = ASDCP_SUCCESS(result) ? m_FB.RoData() : NULL; + return result; +} + +Result_t +ASDCP::WAVDataProvider::FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const +{ + ADesc = m_ADesc; + return RESULT_OK; +} + +Result_t +ASDCP::WAVDataProvider::Reset() +{ + return m_Parser.Reset(); +} + +Result_t +ASDCP::WAVDataProvider::OpenRead(const char* filename, const Rational& PictureRate) +{ + ASDCP_TEST_NULL_STR(filename); + + Result_t result = m_Parser.OpenRead(filename, PictureRate); + + if ( ASDCP_SUCCESS(result) ) + result = m_Parser.FillAudioDescriptor(m_ADesc); + + if ( ASDCP_SUCCESS(result) ) + { + m_ADesc.EditRate = PictureRate; + m_SampleSize = ((m_ADesc.QuantizationBits + 7) / 8); + result = m_FB.Capacity(PCM::CalcFrameBufferSize(m_ADesc)); + } + + return result; +} + +// +ASDCP::AtmosSyncDataProvider::AtmosSyncDataProvider(const ui16_t bitsPerSample, const ui32_t sampleRate, + const ASDCP::Rational& editRate, const byte_t* uuid) + : m_Generator(bitsPerSample, sampleRate, editRate, uuid), m_FB(), m_ADesc(), m_SampleSize() +{ + m_Generator.FillAudioDescriptor(m_ADesc); + m_SampleSize = PCM::CalcSampleSize(m_ADesc); + m_FB.Capacity(PCM::CalcFrameBufferSize(m_ADesc)); +} + +ASDCP::AtmosSyncDataProvider::~AtmosSyncDataProvider() +{} + +Result_t +ASDCP::AtmosSyncDataProvider::PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten) +{ + ASDCP_TEST_NULL(buf); + ASDCP_TEST_NULL(m_ptr); + if ( numChannels > m_ADesc.ChannelCount) + { + DefaultLogSink().Error("Requested %u channels from a wav file with %u channel.", numChannels, + m_ADesc.ChannelCount); + return RESULT_FAIL; + } + + (*bytesWritten) = m_SampleSize; + ::memcpy(buf, m_ptr, m_SampleSize); + m_ptr += m_SampleSize; + return RESULT_OK; +} + +Result_t +ASDCP::AtmosSyncDataProvider::ReadFrame() +{ + Result_t result = m_Generator.ReadFrame(m_FB); + m_ptr = ASDCP_SUCCESS(result) ? m_FB.RoData() : NULL; + return result; +} + +Result_t +ASDCP::AtmosSyncDataProvider::FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const +{ + ADesc = m_ADesc; + return RESULT_OK; +} + +Result_t +ASDCP::AtmosSyncDataProvider::Reset() +{ + return m_Generator.Reset(); +} + +// +ASDCP::SilenceDataProvider::SilenceDataProvider(const ui16_t numChannels, const ui16_t bitsPerSample, + const ui32_t sampleRate, const ASDCP::Rational& editRate) + : m_ADesc(), m_SampleSize(0) +{ + m_SampleSize = ((bitsPerSample + 7) / 8); + m_ADesc.EditRate = editRate; + m_ADesc.AudioSamplingRate = Rational(sampleRate, 1); + m_ADesc.ChannelCount = numChannels; + m_ADesc.QuantizationBits = bitsPerSample; + m_ADesc.BlockAlign = numChannels * m_SampleSize; + m_ADesc.AvgBps = sampleRate * m_ADesc.BlockAlign; +} + +ASDCP::SilenceDataProvider::~SilenceDataProvider() +{} + +Result_t +ASDCP::SilenceDataProvider::PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten) +{ + ASDCP_TEST_NULL(buf); + if ( numChannels > m_ADesc.ChannelCount) + { + DefaultLogSink().Error("Requested %u channels from a wav file with %u channel.", numChannels, + m_ADesc.ChannelCount); + return RESULT_FAIL; + } + (*bytesWritten) = m_SampleSize * numChannels; + ::memset(buf, 0, (*bytesWritten)); + return RESULT_OK; +} + +Result_t +ASDCP::SilenceDataProvider::ReadFrame() +{ + // no op + return RESULT_OK; +} + +Result_t +ASDCP::SilenceDataProvider::FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const +{ + ADesc = m_ADesc; + return RESULT_OK; +} + +Result_t +ASDCP::SilenceDataProvider::Reset() +{ + //no op + return RESULT_OK; +} diff --git a/src/PCMDataProviders.h b/src/PCMDataProviders.h new file mode 100644 index 0000000..9241fd3 --- /dev/null +++ b/src/PCMDataProviders.h @@ -0,0 +1,119 @@ +/* +Copyright (c) 2013-2013, 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 PCMDataProviders.h + \version $Id$ + \brief PCM sample data providers for WAV, AtmosSync and Silence. +*/ + +#ifndef _PCMDATAPROVIDERS_H_ +#define _PCMDATAPROVIDERS_H_ + +#include +#include + +namespace ASDCP +{ + + // PCM Data Provider Interface + class PCMDataProviderInterface + { + ASDCP_NO_COPY_CONSTRUCT(PCMDataProviderInterface); + + public: + PCMDataProviderInterface() {}; + virtual ~PCMDataProviderInterface() = 0; + virtual Result_t PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten) = 0; + virtual Result_t ReadFrame() = 0; + virtual Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const = 0; + virtual Result_t Reset() = 0; + }; + + // WAV file implementation of the PCM Data Provider Interface + class WAVDataProvider : public PCMDataProviderInterface + { + ASDCP_NO_COPY_CONSTRUCT(WAVDataProvider); + PCM::WAVParser m_Parser; + PCM::FrameBuffer m_FB; + PCM::AudioDescriptor m_ADesc; + const byte_t* m_ptr; + ui32_t m_SampleSize; + + public: + WAVDataProvider(); + virtual ~WAVDataProvider(); + virtual Result_t PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten); + virtual Result_t ReadFrame(); + virtual Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const; + virtual Result_t Reset(); + Result_t OpenRead(const char* filename, const Rational& PictureRate); + + }; + + // Atmos Sync Channel implementation of the PCM Data Provider Interface + class AtmosSyncDataProvider : public PCMDataProviderInterface + { + ASDCP_NO_COPY_CONSTRUCT(AtmosSyncDataProvider); + PCM::AtmosSyncChannelGenerator m_Generator; + PCM::FrameBuffer m_FB; + PCM::AudioDescriptor m_ADesc; + const byte_t* m_ptr; + ui32_t m_SampleSize; + + public: + AtmosSyncDataProvider(const ui16_t bitsPerSample, const ui32_t sampleRate, + const ASDCP::Rational& PictureRate, const byte_t* uuid); + virtual ~AtmosSyncDataProvider(); + virtual Result_t PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten); + virtual Result_t ReadFrame(); + virtual Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const; + virtual Result_t Reset(); + }; + + // Silence Channel(s) implementation of the PCM Data Provider Interface + class SilenceDataProvider : public PCMDataProviderInterface + { + ASDCP_NO_COPY_CONSTRUCT(SilenceDataProvider); + PCM::AudioDescriptor m_ADesc; + ui32_t m_SampleSize; + + public: + SilenceDataProvider(const ui16_t numChannels, const ui16_t bitsPerSample, + const ui32_t sampleRate, const ASDCP::Rational& editRate); + virtual ~SilenceDataProvider(); + virtual Result_t PutSample(const ui32_t numChannels, byte_t* buf, ui32_t* bytesWritten); + virtual Result_t ReadFrame(); + virtual Result_t FillAudioDescriptor(PCM::AudioDescriptor& ADesc) const; + virtual Result_t Reset(); + }; + +} // namespace ASDCP + +#endif // _PCMDATAPROVIDERS_H_ + +// +// end PCMDataProviders.h +// diff --git a/src/PCM_Parser.cpp b/src/PCM_Parser.cpp index 3fa6d7d..e3b693d 100755 --- a/src/PCM_Parser.cpp +++ b/src/PCM_Parser.cpp @@ -37,6 +37,7 @@ using Kumu::DefaultLogSink; using namespace ASDCP; using namespace ASDCP::PCM; using namespace ASDCP::Wav; +using namespace ASDCP::RF64; //------------------------------------------------------------------------------------------ @@ -47,8 +48,8 @@ class ASDCP::PCM::WAVParser::h__WAVParser Kumu::FileReader m_FileReader; bool m_EOF; ui32_t m_DataStart; - ui32_t m_DataLength; - ui32_t m_ReadCount; + ui64_t m_DataLength; + ui64_t m_ReadCount; ui32_t m_FrameBufferSize; ui32_t m_FramesRead; Rational m_PictureRate; @@ -129,6 +130,22 @@ ASDCP::PCM::WAVParser::h__WAVParser::OpenRead(const char* filename, const Ration 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(); + } + } } } diff --git a/src/SyncCommon.h b/src/SyncCommon.h new file mode 100644 index 0000000..bc5d498 --- /dev/null +++ b/src/SyncCommon.h @@ -0,0 +1,80 @@ +/* +Copyright (c) 2013-2013, 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 SyncCommon.h + \version $Id$ + \brief Common elements for ATMOS Sync Channel generation +*/ + +#ifndef _SYNC_COMMON_H_ +#define _SYNC_COMMON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BYTE +typedef unsigned char BYTE; +#endif + +#ifndef USHORT +typedef unsigned short USHORT; +#endif + +#ifndef INT +typedef int INT; +#endif + +#ifndef FLOAT +typedef float FLOAT; +#endif + +#define SYMBOL_RATE (12000) + +#define SYMBOL_LENGTH_48 (4) +#define SYMBOL_LENGTH_96 (8) + +#define SYNC_HEADER (0x4D56) +#define SYNC_HEADER1 (0x4D) +#define SYNC_HEADER2 (0x56) + +#define SYNC_HEADER_BITS (16) +#define FRAME_RATE_BITS (4) +#define RESERVE_BITS (2) +#define UUID_SUB_INDEX_BITS (2) +#define UUID_SUB_BITS (32) +#define FRAME_INDEX_BITS (24) +#define CRC_BITS (16) +#define MESSAGE_TOTAL_BITS (96) +#define MESSAGE_TOTAL_BYTES (12) + +#define MAX_PACKET (32) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/SyncEncoder.c b/src/SyncEncoder.c new file mode 100644 index 0000000..c553509 --- /dev/null +++ b/src/SyncEncoder.c @@ -0,0 +1,346 @@ +/* +Copyright (c) 2013-2013, 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 SyncEncoder.c + \version $Id$ + \brief Implementation of Atmos Sync Frame Encoder +*/ + +#include "SyncEncoder.h" +#include "CRC16.h" + +#include + +void ConstructFrame(LPSYNCENCODER pSyncEncoder, + INT iFrameIndex); + +FLOAT SEWriteBits( INT iSampleRate, /* In: Sample rate of signal */ + FLOAT *pfAudioBuffer, /* Out: Audio buffer containing signal */ + INT iBits, /* In: Number of bits to write */ + BYTE *pbyData, /* In: Data to write */ + FLOAT fSymbolPhase); /* In: Symbol phase */ + + + +INT SyncEncoderInit(LPSYNCENCODER pSyncEncoder, /* Out: SYNCENCODER structure to be initialized */ + INT iSampleRate, /* In: Signal sample rate */ + INT iFrameRate, /* In: frame rate */ + LPUUIDINFORMATION pUUID) /* In: UUID */ +{ + pSyncEncoder->iError = SYNC_ENCODER_ERROR_NONE; + + /* Check and set sample rate */ + pSyncEncoder->iSymbolLength = 1; + switch(iSampleRate){ + case 48000: + pSyncEncoder->iSampleRate = iSampleRate; + pSyncEncoder->iSymbolLength = SYMBOL_LENGTH_48; + break; + case 96000: + pSyncEncoder->iSampleRate = iSampleRate; + pSyncEncoder->iSymbolLength = SYMBOL_LENGTH_96; + break; + default: + pSyncEncoder->iError = SYNC_ENCODER_ERROR_INVALID_SR; + }; + + if(pSyncEncoder->iError != SYNC_ENCODER_ERROR_NONE){ + return pSyncEncoder->iError; + } + + /* check and set frame rate */ + switch(iFrameRate){ + case 24: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 0; + pSyncEncoder->iPacketsPerFrame = 4; + break; + case 25: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 1; + pSyncEncoder->iPacketsPerFrame = 4; + break; + case 30: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 2; + pSyncEncoder->iPacketsPerFrame = 4; + break; + case 48: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 3; + pSyncEncoder->iPacketsPerFrame = 2; + break; + case 50: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 4; + pSyncEncoder->iPacketsPerFrame = 2; + break; + case 60: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 5; + pSyncEncoder->iPacketsPerFrame = 2; + break; + case 96: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 6; + pSyncEncoder->iPacketsPerFrame = 1; + break; + case 100: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 7; + pSyncEncoder->iPacketsPerFrame = 1; + break; + case 120: + pSyncEncoder->iFrameRate = iFrameRate; + pSyncEncoder->iFrameRateCode = 8; + pSyncEncoder->iPacketsPerFrame = 1; + break; + default: + pSyncEncoder->iError = SYNC_ENCODER_ERROR_INVALID_FR; + }; + + if(pSyncEncoder->iError != SYNC_ENCODER_ERROR_NONE){ + return pSyncEncoder->iError; + } + + /* calculate required buffer length */ + pSyncEncoder->iAudioBufferLength = pSyncEncoder->iSampleRate / pSyncEncoder->iFrameRate; + + /* Calculate total packet bits including wash bits */ + pSyncEncoder->iPacketBits = pSyncEncoder->iAudioBufferLength / (pSyncEncoder->iSymbolLength * pSyncEncoder->iPacketsPerFrame); + + /* Initialize symbol phase */ + pSyncEncoder->fSymbolPhase = 1.0f; + + /* Initialize UUD information */ + pSyncEncoder->iUUIDSubIndex = 0; + memcpy(&pSyncEncoder->UUID,pUUID,sizeof(UUIDINFORMATION)); + + return pSyncEncoder->iError; +} + +INT GetSyncEncoderAudioBufferLength(LPSYNCENCODER pSyncEncoder) /* In: Sync encoder structure */ +{ + if(pSyncEncoder->iError != SYNC_ENCODER_ERROR_NONE){ + return pSyncEncoder->iError; + } + + return pSyncEncoder->iAudioBufferLength; +} + + + +INT EncodeSync( LPSYNCENCODER pSyncEncoder, /* In: Sync encoder structure */ + INT iBufferLength, /* In: Length of audio buffer */ + FLOAT *pfAudioBuffer, /* Out: Audio buffer with signal */ + INT iFrameIndex) /* In: Frame Index */ +{ + INT n; + INT iBufferIndex; + + + if(pSyncEncoder->iError != SYNC_ENCODER_ERROR_NONE){ + return pSyncEncoder->iError; + } + if(iBufferLength != pSyncEncoder->iAudioBufferLength){ + return SYNC_ENCODER_ERROR_INVALID_BL; + } + + iBufferIndex = 0; + for(n = 0; n < pSyncEncoder->iPacketsPerFrame; n ++){ + /* Construct message */ + ConstructFrame(pSyncEncoder,iFrameIndex); + + /* Write Message */ + pSyncEncoder->fSymbolPhase = SEWriteBits(pSyncEncoder->iSampleRate, + &pfAudioBuffer[iBufferIndex], + pSyncEncoder->iPacketBits, + pSyncEncoder->abyPacket, + pSyncEncoder->fSymbolPhase); + + iBufferIndex += (pSyncEncoder->iPacketBits * pSyncEncoder->iSymbolLength); + + } + + return pSyncEncoder->iError; +} + +void ConstructFrame(LPSYNCENCODER pSyncEncoder, + INT iFrameIndex) +{ + USHORT ushCRC; + BYTE byByte; + INT iUUIDIndex; + + /* Flush the packet buffer */ + memset(pSyncEncoder->abyPacket,0,MAX_PACKET); + + /* Sync Header */ + pSyncEncoder->abyPacket[0] = SYNC_HEADER1; + pSyncEncoder->abyPacket[1] = SYNC_HEADER2; + + /* Frame Rate code */ + byByte = 0; + byByte = (unsigned char)(pSyncEncoder->iFrameRateCode << 4); + + /* UUID sub index */ + byByte |= (unsigned char)(pSyncEncoder->iUUIDSubIndex & 0x3); + + pSyncEncoder->abyPacket[2] = byByte; + + /* UUID Sub */ + iUUIDIndex = pSyncEncoder->iUUIDSubIndex << 2; + pSyncEncoder->abyPacket[3] = pSyncEncoder->UUID.abyUUIDBytes[iUUIDIndex]; + pSyncEncoder->abyPacket[4] = pSyncEncoder->UUID.abyUUIDBytes[iUUIDIndex + 1]; + pSyncEncoder->abyPacket[5] = pSyncEncoder->UUID.abyUUIDBytes[iUUIDIndex + 2]; + pSyncEncoder->abyPacket[6] = pSyncEncoder->UUID.abyUUIDBytes[iUUIDIndex + 3]; + + /* Update UUID sub index */ + pSyncEncoder->iUUIDSubIndex ++; + pSyncEncoder->iUUIDSubIndex &= 0x3; + + /* Frame Index */ + byByte = (unsigned char)((iFrameIndex >> 16) & 0XFF); + pSyncEncoder->abyPacket[7] = byByte; + byByte = (unsigned char)((iFrameIndex >> 8) & 0XFF); + pSyncEncoder->abyPacket[8] = byByte; + byByte = (unsigned char)(iFrameIndex & 0XFF); + pSyncEncoder->abyPacket[9] = byByte; + + /* calculate CRC */ + ushCRC = CRC16(&pSyncEncoder->abyPacket[2],MESSAGE_TOTAL_BYTES - 4); + + /* Insert CRC */ + byByte = (unsigned char)((ushCRC >> 8) & 0XFF); + pSyncEncoder->abyPacket[10] = byByte; + byByte = (unsigned char)(ushCRC & 0XFF); + pSyncEncoder->abyPacket[11] = byByte; + +} + +static FLOAT g_afSymbol0_48[SYMBOL_LENGTH_48] = { + 0.3827f, + 0.9239f, + 0.9239f, + 0.3827f, +}; + +static FLOAT g_afSymbol1_48[SYMBOL_LENGTH_48] = { + 0.7071f, + 0.7071f, + -0.7071f, + -0.7071f, +}; + +static FLOAT g_afSymbol0_96[SYMBOL_LENGTH_96] = { + 0.1951f, + 0.5556f, + 0.8315f, + 0.9808f, + 0.9808f, + 0.8315f, + 0.5556f, + 0.1951f, +}; + +static FLOAT g_afSymbol1_96[SYMBOL_LENGTH_96] = { + 0.3827f, + 0.9239f, + 0.9239f, + 0.3827f, + -0.3827f, + -0.9239f, + -0.9239f, + -0.3827f, +}; + +/* Symbol gain */ +static FLOAT g_fGain = 0.1f; + +FLOAT SEWriteBits( INT iSampleRate, /* In: Sample rate of signal */ + FLOAT *pfAudioBuffer, /* Out: Audio buffer containing signal */ + INT iBits, /* In: Number of bits to write */ + BYTE *pbyData, /* In: Data to write */ + FLOAT fSymbolPhase) /* In: Symbol phase */ +{ + INT n; + INT i; + INT iSymbolLength; + FLOAT *pfSymbol0; + FLOAT *pfSymbol1; + BYTE byByte; + + /* Select the correct symbol length and symbol signal based on sample rate */ + switch (iSampleRate){ + case 96000: + iSymbolLength = SYMBOL_LENGTH_96; + pfSymbol0 = g_afSymbol0_96; + pfSymbol1 = g_afSymbol1_96; + break; + case 48000: + iSymbolLength = SYMBOL_LENGTH_48; + pfSymbol0 = g_afSymbol0_48; + pfSymbol1 = g_afSymbol1_48; + break; + default: + iSymbolLength = 0; + pfSymbol0 = g_afSymbol0_96; + pfSymbol1 = g_afSymbol1_96; + }; + + /* Write bits */ + n = 0; + i = 0; + while(n < iBits){ + INT k; + FLOAT *pfSymbol; + + /* Grab next byte of data */ + if(i == 0){ + byByte = *pbyData; + pbyData ++; + } + + pfSymbol = (byByte & 0x80) ? pfSymbol1 : pfSymbol0; + + for(k = 0; k < iSymbolLength; k ++){ + *pfAudioBuffer = *pfSymbol * fSymbolPhase * g_fGain; + pfAudioBuffer ++; + pfSymbol ++; + } + + fSymbolPhase *= (byByte & 0x80) ? 1.0f : -1.0f; + + byByte <<= 1; + + n ++; + + i ++; + i &= 0x7; + } + + return fSymbolPhase; +} diff --git a/src/SyncEncoder.h b/src/SyncEncoder.h new file mode 100644 index 0000000..4b97f5a --- /dev/null +++ b/src/SyncEncoder.h @@ -0,0 +1,86 @@ +/* +Copyright (c) 2013-2013, 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 SyncEncoder.h + \version $Id$ + \brief Declaration of Atmos Sync Frame Encoder +*/ + +#ifndef _SYNC_ENCODER_H_ +#define _SYNC_ENCODER_H_ + +#include "SyncCommon.h" +#include "UUIDInformation.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SyncEncoder{ + INT iSampleRate; /* Signal sample rate */ + INT iSymbolLength; /* Symbol Length */ + INT iFrameRate; /* Frame rate */ + INT iFrameRateCode; /* Frame rate code */ + INT iAudioBufferLength; /* Length of audio buffer */ + INT iPacketBits; /* Bits in each packet includes wash bits */ + INT iPacketsPerFrame; /* Number of packets per frame */ + FLOAT fSymbolPhase; /* Symbol phase */ + + INT iUUIDSubIndex; /* UUID transmission sub index */ + UUIDINFORMATION UUID; /* UUID */ + + BYTE abyPacket[MAX_PACKET]; + + INT iError; /* Error state */ +}SYNCENCODER,*LPSYNCENCODER; + +enum{ + SYNC_ENCODER_ERROR_NONE = 0, /* No error */ + SYNC_ENCODER_ERROR_INVALID_SR = -1, /* Invalid sample rate */ + SYNC_ENCODER_ERROR_INVALID_FR = -2, /* Invalid frame rate */ + SYNC_ENCODER_ERROR_INVALID_BL = -10, /* Buffer length is incorrect */ + SYNC_ENCODER_ERROR_UNKNOWN = -100, /* Unknown */ +}; + + +INT SyncEncoderInit(LPSYNCENCODER pSyncEncoder, /* Out: SYNCENCODER structure to be initialized */ + INT iSampleRate, /* In: Signal sample rate */ + INT iFrameRate, /* In: frame rate */ + LPUUIDINFORMATION pUUID); /* In: UUID */ + +INT GetSyncEncoderAudioBufferLength(LPSYNCENCODER pSyncEncoder); + +INT EncodeSync( LPSYNCENCODER pSyncEncoder, /* In: Sync encoder structure */ + INT iBufferLength, /* In: Length of audio buffer */ + FLOAT *pfAudioBuffer, /* Out: Audio buffer with signal */ + INT iFrameIndex); /* In: Frame Index */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/src/UUIDInformation.c b/src/UUIDInformation.c new file mode 100644 index 0000000..4c5eec9 --- /dev/null +++ b/src/UUIDInformation.c @@ -0,0 +1,119 @@ +/* +Copyright (c) 2013-2013, 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 SyncEncoder.h + \version $Id$ + \brief Implementation of Atmos Sync UUID +*/ + +#include "UUIDInformation.h" +#include + + +void UUIDSynthesize(LPUUIDINFORMATION pUUID) +{ + INT n; + + for(n = 0; n < 16; n ++){ + pUUID->abyUUIDBytes[n] = (BYTE)(rand() & 0xFF); + } + + pUUID->abyUUIDBytes[6] &= 0x0F; + pUUID->abyUUIDBytes[6] |= 0x40; + + pUUID->abyUUIDBytes[8] &= 0x0F; + pUUID->abyUUIDBytes[8] |= 0xA0; +} + +void UUIDPrint( FILE *pFilePtr, + LPUUIDINFORMATION pUUID) +{ + if(pFilePtr != NULL){ + INT n; + + for(n = 0; n < 16; n ++){ + fprintf(pFilePtr,"%02x",pUUID->abyUUIDBytes[n]); + } + } + else{ + INT n; + + for(n = 0; n < 16; n ++){ + fprintf(stdout,"%02x",pUUID->abyUUIDBytes[n]); + } + } +} + +void UUIDPrintFormated( FILE *pFilePtr, + LPUUIDINFORMATION pUUID) +{ + if(pFilePtr != NULL){ + INT n; + + for(n = 0; n < 4; n ++){ + fprintf(pFilePtr,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(pFilePtr,"-"); + for(n = 4; n < 6; n ++){ + fprintf(pFilePtr,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(pFilePtr,"-"); + for(n = 6; n < 8; n ++){ + fprintf(pFilePtr,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(pFilePtr,"-"); + for(n = 8; n < 10; n ++){ + fprintf(pFilePtr,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(pFilePtr,"-"); + for(n = 10; n < 16; n ++){ + fprintf(pFilePtr,"%02x",pUUID->abyUUIDBytes[n]); + } + } + else{ + INT n; + + for(n = 0; n < 4; n ++){ + fprintf(stdout,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(stdout,"-"); + for(n = 4; n < 6; n ++){ + fprintf(stdout,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(stdout,"-"); + for(n = 6; n < 8; n ++){ + fprintf(stdout,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(stdout,"-"); + for(n = 8; n < 10; n ++){ + fprintf(stdout,"%02x",pUUID->abyUUIDBytes[n]); + } + fprintf(stdout,"-"); + for(n = 10; n < 16; n ++){ + fprintf(stdout,"%02x",pUUID->abyUUIDBytes[n]); + } + } +} diff --git a/src/UUIDInformation.h b/src/UUIDInformation.h new file mode 100644 index 0000000..4bd0ff4 --- /dev/null +++ b/src/UUIDInformation.h @@ -0,0 +1,58 @@ +/* +Copyright (c) 2013-2013, 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 SyncEncoder.h + \version $Id$ + \brief Declaration of Atmos Sync UUID +*/ + +#ifndef _UUID_INFORMATION_H_ +#define _UUID_INFORMATION_H_ + +#include "SyncCommon.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct UUIInformation{ + BYTE abyUUIDBytes[16]; +} UUIDINFORMATION,*LPUUIDINFORMATION; + +void UUIDSynthesize(LPUUIDINFORMATION pUUID); + +void UUIDPrint( FILE *pFilePtr, + LPUUIDINFORMATION pUUID); + +void UUIDPrintFormated( FILE *pFilePtr, + LPUUIDINFORMATION pUUID); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/Wav.cpp b/src/Wav.cpp index 7275ab9..0f14850 100755 --- a/src/Wav.cpp +++ b/src/Wav.cpp @@ -44,7 +44,7 @@ ASDCP::Wav::SimpleWaveHeader::SimpleWaveHeader(ASDCP::PCM::AudioDescriptor& ADes nchannels = ADesc.ChannelCount; bitspersample = ADesc.QuantizationBits; samplespersec = (ui32_t)ceil(ADesc.AudioSamplingRate.Quotient()); - blockalign = nchannels * (bitspersample / 8); + blockalign = nchannels * ((bitspersample + 7) / 8); avgbps = samplespersec * blockalign; cbsize = 0; data_len = ASDCP::PCM::CalcFrameBufferSize(ADesc) * ADesc.ContainerDuration; @@ -363,7 +363,230 @@ ASDCP::AIFF::SimpleAIFFHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, return RESULT_OK; } +ASDCP::RF64::SimpleRF64Header::SimpleRF64Header(ASDCP::PCM::AudioDescriptor& ADesc) +{ + format = 1; + nchannels = ADesc.ChannelCount; + bitspersample = ADesc.QuantizationBits; + samplespersec = (ui32_t)ceil(ADesc.AudioSamplingRate.Quotient()); + blockalign = nchannels * ((bitspersample + 7) / 8); + avgbps = samplespersec * blockalign; + data_len = ASDCP::PCM::CalcFrameBufferSize(ADesc) * ADesc.ContainerDuration; +} + +// +void +ASDCP::RF64::SimpleRF64Header::FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::Rational PictureRate) const +{ + ADesc.EditRate = PictureRate; + + ADesc.LinkedTrackID = 0; + ADesc.Locked = 0; + ADesc.ChannelCount = nchannels; + ADesc.AudioSamplingRate = Rational(samplespersec, 1); + ADesc.AvgBps = avgbps; + ADesc.BlockAlign = blockalign; + ADesc.QuantizationBits = bitspersample; + ui32_t FrameBufferSize = ASDCP::PCM::CalcFrameBufferSize(ADesc); + ADesc.ContainerDuration = data_len / FrameBufferSize; + ADesc.ChannelFormat = PCM::CF_NONE; +} + +// +ASDCP::Result_t +ASDCP::RF64::SimpleRF64Header::WriteToFile(Kumu::FileWriter& OutFile) const +{ + static ui32_t fmt_len = + sizeof(format) + + sizeof(nchannels) + + sizeof(samplespersec) + + sizeof(avgbps) + + sizeof(blockalign) + + sizeof(bitspersample); + + ui32_t write_count = 0; + ui64_t RIFF_len = data_len + SimpleWavHeaderLength - 8; + DefaultLogSink().Debug("RIFF_len is %llu.\n", RIFF_len); + byte_t* tmp_header = NULL; + ui32_t header_len = 0; + + if (RIFF_len > MAX_RIFF_LEN) + { + DefaultLogSink().Debug("Will write out an RF64 wave file.\n"); + ui32_t data32_len = ((data_len < MAX_RIFF_LEN) ? data_len : MAX_RIFF_LEN); + ui64_t data64_len = ((data_len < MAX_RIFF_LEN) ? 0 : data_len); + static ui32_t ds64_len = + sizeof(RIFF_len) + + sizeof(data64_len) + + sizeof(SAMPLE_COUNT) + + sizeof(TABLE_LEN); + + header_len = SIMPLE_RF64_HEADER_LEN; + tmp_header = new byte_t[header_len]; + byte_t* p = tmp_header; + memcpy(p, &FCC_RF64, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(MAX_RIFF_LEN); p += 4; + memcpy(p, &Wav::FCC_WAVE, sizeof(fourcc)); p += 4; + memcpy(p, &FCC_ds64, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(ds64_len); p += 4; + *((ui64_t*)p) = KM_i64_LE(RIFF_len); p += 8; + *((ui64_t*)p) = KM_i64_LE(data64_len); p += 8; + *((ui64_t*)p) = KM_i64_LE(SAMPLE_COUNT); p += 8; + *((ui32_t*)p) = KM_i32_LE(TABLE_LEN); p += 4; + memcpy(p, &Wav::FCC_fmt_, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4; + *((ui16_t*)p) = KM_i16_LE(format); p += 2; + *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2; + *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4; + *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4; + *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2; + *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2; + memcpy(p, &Wav::FCC_data, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(data32_len); p += 4; + write_count = (p - tmp_header); + } + else + { + DefaultLogSink().Debug("Will write out a regular wave file.\n"); + header_len = SimpleWavHeaderLength; + tmp_header = new byte_t[header_len]; + byte_t* p = tmp_header; + memcpy(p, &Wav::FCC_RIFF, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(RIFF_len); p += 4; + memcpy(p, &Wav::FCC_WAVE, sizeof(fourcc)); p += 4; + memcpy(p, &Wav::FCC_fmt_, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4; + *((ui16_t*)p) = KM_i16_LE(format); p += 2; + *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2; + *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4; + *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4; + *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2; + *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2; + memcpy(p, &Wav::FCC_data, sizeof(fourcc)); p += 4; + *((ui32_t*)p) = KM_i32_LE(data_len); p += 4; + write_count = (p - tmp_header); + } + if (header_len != write_count) + { + DefaultLogSink().Warn("Expected to write %u bytes but wrote %u bytes for header.\n", + header_len, write_count); + } + write_count = 0; + ASDCP::Result_t r = OutFile.Write(tmp_header, header_len, &write_count); + delete [] tmp_header; + return r; +} + +// +ASDCP::Result_t +ASDCP::RF64::SimpleRF64Header::ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start) +{ + ui32_t read_count = 0; + ui32_t local_data_start = 0; + ASDCP::PCM::FrameBuffer TmpBuffer(Wav::MaxWavHeader); + if ( data_start == 0 ) + data_start = &local_data_start; + + Result_t result = InFile.Read(TmpBuffer.Data(), TmpBuffer.Capacity(), &read_count); + + if ( ASDCP_SUCCESS(result) ) + result = ReadFromBuffer(TmpBuffer.RoData(), read_count, data_start); + else + DefaultLogSink().Error("Failed to read %d bytes from file\n", Wav::MaxWavHeader); + + return result; +} + +ASDCP::Result_t +ASDCP::RF64::SimpleRF64Header::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start) +{ + if ( buf_len < SIMPLE_RF64_HEADER_LEN ) + return RESULT_SMALLBUF; + + *data_start = 0; + const byte_t* p = buf; + const byte_t* end_p = p + buf_len; + + fourcc test_RF64(p); p += 4; + if ( test_RF64 != FCC_RF64 ) + { + DefaultLogSink().Debug("File does not begin with RF64 header\n"); + return RESULT_RAW_FORMAT; + } + + ui32_t tmp_len = KM_i32_LE(*(ui32_t*)p); p += 4; + + fourcc test_WAVE(p); p += 4; + if ( test_WAVE != Wav::FCC_WAVE ) + { + DefaultLogSink().Debug("File does not contain a WAVE header\n"); + return RESULT_RAW_FORMAT; + } + + fourcc test_ds64(p); p += 4; + if ( test_ds64 != FCC_ds64 ) + { + DefaultLogSink().Debug("File does not contain a ds64 chunk\n"); + return RESULT_RAW_FORMAT; + } + ui32_t ds64_len = KM_i32_LE(*(ui32_t*)p); p += 4; + ui64_t RIFF_len = ((tmp_len == MAX_RIFF_LEN) ? KM_i64_LE(*(ui64_t*)p) : tmp_len); p += 8; + data_len = KM_i64_LE(*(ui64_t*)p); p += 8; + p += (ds64_len - 16); // skip rest of ds64 chunk + + fourcc test_fcc; + + while ( p < end_p ) + { + test_fcc = fourcc(p); p += 4; + ui32_t chunk_size = KM_i32_LE(*(ui32_t*)p); p += 4; + + if ( test_fcc == Wav::FCC_data ) + { + if ( chunk_size > RIFF_len ) + { + DefaultLogSink().Error("Chunk size %u larger than file: %u\n", chunk_size, RIFF_len); + return RESULT_RAW_FORMAT; + } + + if (chunk_size != MAX_RIFF_LEN) + data_len = chunk_size; + *data_start = p - buf; + break; + } + + if ( test_fcc == Wav::FCC_fmt_ ) + { + ui16_t format = KM_i16_LE(*(ui16_t*)p); p += 2; + + if ( format != Wav::WAVE_FORMAT_PCM && format != Wav::WAVE_FORMAT_EXTENSIBLE ) + { + DefaultLogSink().Error("Expecting uncompressed PCM data, got format type %hd\n", format); + return RESULT_RAW_FORMAT; + } + + nchannels = KM_i16_LE(*(ui16_t*)p); p += 2; + samplespersec = KM_i32_LE(*(ui32_t*)p); p += 4; + avgbps = KM_i32_LE(*(ui32_t*)p); p += 4; + blockalign = KM_i16_LE(*(ui16_t*)p); p += 2; + bitspersample = KM_i16_LE(*(ui16_t*)p); p += 2; + p += chunk_size - 16; // 16 is the number of bytes read in this block + } + else + { + p += chunk_size; + } + } + + if ( *data_start == 0 ) // can't have no data! + { + DefaultLogSink().Error("No data chunk found, file contains no essence\n"); + return RESULT_RAW_FORMAT; + } + + return RESULT_OK; +} // // end Wav.cpp diff --git a/src/Wav.h b/src/Wav.h index ef9c4e2..d2a9789 100755 --- a/src/Wav.h +++ b/src/Wav.h @@ -118,6 +118,45 @@ namespace ASDCP }; } // namespace Wav + + namespace RF64 + { + const fourcc FCC_RF64("RF64"); + const fourcc FCC_ds64("ds64"); + + + static const ui32_t MAX_RIFF_LEN = 0xFFFFFFFF; + static const ui32_t DS64_HEADER_LEN = 28; + static const ui32_t SIMPLE_RF64_HEADER_LEN = 80; + // + class SimpleRF64Header + { + public: + ui16_t format; + ui16_t nchannels; + ui32_t samplespersec; + ui32_t avgbps; + ui16_t blockalign; + ui16_t bitspersample; + ui64_t data_len; + + SimpleRF64Header() : + format(0), nchannels(0), samplespersec(0), avgbps(0), + blockalign(0), bitspersample(0), data_len(0) {} + + SimpleRF64Header(ASDCP::PCM::AudioDescriptor& ADesc); + + Result_t ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start); + Result_t ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start); + Result_t WriteToFile(Kumu::FileWriter& OutFile) const; + void FillADesc(ASDCP::PCM::AudioDescriptor& ADesc, Rational PictureRate) const; + + private: + static const ui64_t SAMPLE_COUNT = 0; + static const ui32_t TABLE_LEN = 0; + }; + + } // namespace RF64 } // namespace ASDCP #endif // _WAV_H_ diff --git a/src/WavFileWriter.h b/src/WavFileWriter.h index 3febc23..9611adc 100755 --- a/src/WavFileWriter.h +++ b/src/WavFileWriter.h @@ -136,22 +136,36 @@ class WavFileWriter assert(file_count && m_ChannelCount); ui32_t element_size = ASDCP::PCM::CalcFrameBufferSize(m_ADesc) / file_count; - - for ( ui32_t i = 0; i < file_count && ASDCP_SUCCESS(result); i++ ) - { - snprintf(filename, Kumu::MaxFilePath, "%s_%u.wav", file_root, (i + 1)); - m_OutFile.push_back(new WavFileElement(element_size)); - result = m_OutFile.back()->OpenWrite(filename); - - if ( ASDCP_SUCCESS(result) ) - { - ASDCP::PCM::AudioDescriptor tmpDesc = m_ADesc; - tmpDesc.ChannelCount = m_ChannelCount; - ASDCP::Wav::SimpleWaveHeader Wav(tmpDesc); - result = Wav.WriteToFile(*(m_OutFile.back())); - } - } - + if (split == ST_NONE) + { + snprintf(filename, Kumu::MaxFilePath, "%s", file_root); + m_OutFile.push_back(new WavFileElement(element_size)); + result = m_OutFile.back()->OpenWrite(filename); + if ( ASDCP_SUCCESS(result) ) + { + ASDCP::PCM::AudioDescriptor tmpDesc = m_ADesc; + tmpDesc.ChannelCount = m_ChannelCount; + ASDCP::RF64::SimpleRF64Header Wav(tmpDesc); + result = Wav.WriteToFile(*(m_OutFile.back())); + } + } + else + { + for ( ui32_t i = 0; i < file_count && ASDCP_SUCCESS(result); i++ ) + { + snprintf(filename, Kumu::MaxFilePath, "%s_%u.wav", file_root, (i + 1)); + m_OutFile.push_back(new WavFileElement(element_size)); + result = m_OutFile.back()->OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + { + ASDCP::PCM::AudioDescriptor tmpDesc = m_ADesc; + tmpDesc.ChannelCount = m_ChannelCount; + ASDCP::RF64::SimpleRF64Header Wav(tmpDesc); + result = Wav.WriteToFile(*(m_OutFile.back())); + } + } + } return result; } diff --git a/src/asdcp-info.cpp b/src/asdcp-info.cpp index 05e2c79..cbc4381 100755 --- a/src/asdcp-info.cpp +++ b/src/asdcp-info.cpp @@ -25,7 +25,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*! \file asdcp-info.cpp - \version $Id$ + \version $Id$ \brief AS-DCP file metadata utility This program provides metadata information about an AS-DCP file. @@ -125,7 +125,7 @@ public: bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first) bool showid_flag; // if true, show file identity info (the WriterInfo struct) bool showdescriptor_flag; // if true, show the essence descriptor - bool showcoding_flag; // if true, show the coding UL + bool showcoding_flag; // if true, show the coding UL bool showrate_flag; // if true and is image file, show bit rate bool max_bitrate_flag; // true if -t option given double max_bitrate; // if true and is image file, max bit rate for rate test @@ -145,7 +145,7 @@ public: help_flag = true; continue; } - + if ( argv[i][0] == '-' && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) ) && argv[i][2] == 0 ) @@ -191,7 +191,7 @@ public: if ( help_flag || version_flag ) return; - + if ( filenames.empty() ) { fputs("At least one filename argument is required.\n", stderr); @@ -270,6 +270,30 @@ class MyTextDescriptor : public TimedText::TimedTextDescriptor } }; +class MyDCDataDescriptor : public DCData::DCDataDescriptor +{ + public: + void FillDescriptor(DCData::MXFReader& Reader) { + Reader.FillDCDataDescriptor(*this); + } + + void Dump(FILE* stream) { + DCData::DCDataDescriptorDump(*this, stream); + } +}; + +class MyAtmosDescriptor : public ATMOS::AtmosDescriptor +{ + public: + void FillDescriptor(ATMOS::MXFReader& Reader) { + Reader.FillAtmosDescriptor(*this); + } + + void Dump(FILE* stream) { + ATMOS::AtmosDescriptorDump(*this, stream); + } +}; + // // template @@ -333,7 +357,7 @@ public: Result_t result = m_Reader.OPAtomHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor), reinterpret_cast(&descriptor)); - + if ( KM_SUCCESS(result) ) m_PictureEssenceCoding = descriptor->PictureEssenceCoding; } @@ -489,7 +513,7 @@ public: Result_t result = m_Reader.OPAtomHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_WaveAudioDescriptor), reinterpret_cast(&descriptor)); - + if ( KM_SUCCESS(result) ) { char buf[64]; @@ -572,7 +596,7 @@ show_file_info(CommandOptions& Options) { FileInfoWrapperwrapper; result = wrapper.file_info(Options, "JPEG 2000 stereoscopic pictures"); - + if ( KM_SUCCESS(result) ) { wrapper.get_PictureEssenceCoding(); @@ -592,6 +616,16 @@ show_file_info(CommandOptions& Options) FileInfoWrapperwrapper; result = wrapper.file_info(Options, "Timed Text"); } + else if ( EssenceType == ESS_DCDATA_UNKNOWN ) + { + FileInfoWrapper wrapper; + result = wrapper.file_info(Options, "D-Cinema Generic Data"); + } + else if ( EssenceType == ESS_DCDATA_DOLBY_ATMOS ) + { + FileInfoWrapper wrapper; + result = wrapper.file_info(Options, "Dolby ATMOS"); + } else { fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames.front().c_str()); diff --git a/src/asdcp-unwrap.cpp b/src/asdcp-unwrap.cpp index e9b9fee..7b8654b 100755 --- a/src/asdcp-unwrap.cpp +++ b/src/asdcp-unwrap.cpp @@ -25,7 +25,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*! \file asdcp-unwrap.cpp - \version $Id$ + \version $Id$ \brief AS-DCP file manipulation utility This program extracts picture, sound and text essence from AS-DCP files. @@ -92,7 +92,8 @@ Options:\n\ -b - Specify size in bytes of picture frame buffer\n\ Defaults to 4,194,304 (4MB)\n\ -d - Number of frames to process, default all\n\ - -f - Starting frame number, default 0\n\ + -e - Extension to use for Unknown D-Cinema Data files. default dcdata\n\ + -f - Starting frame number, default 0\n \ -G - Perform GOP start lookup test on MXF+Interop MPEG file\n\ -h | -help - Show help\n\ -k - Use key for ciphertext operations\n\ @@ -153,6 +154,7 @@ public: PCM::ChannelFormat_t channel_fmt; // audio channel arrangement const char* input_filename; std::string prefix_buffer; + const char* extension; // file extension to use for unknown D-Cinema Data track files. // Rational PictureRate() @@ -181,7 +183,7 @@ public: version_flag(false), help_flag(false), stereo_image_flag(false), number_width(6), start_frame(0), duration(0xffffffff), duration_flag(false), j2c_pedantic(true), picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_prefix(0), - channel_fmt(PCM::CF_NONE), input_filename(0) + channel_fmt(PCM::CF_NONE), input_filename(0), extension("dcdata") { memset(key_value, 0, KeyLen); memset(key_id_value, 0, UUIDlen); @@ -194,7 +196,7 @@ public: help_flag = true; continue; } - + if ( argv[i][0] == '-' && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) ) && argv[i][2] == 0 ) @@ -216,6 +218,11 @@ public: duration = abs(atoi(argv[i])); break; + case 'e': + TEST_EXTRA_ARG(i, 'e'); + extension = argv[i]; + break; + case 'f': TEST_EXTRA_ARG(i, 'f'); start_frame = abs(atoi(argv[i])); @@ -224,6 +231,20 @@ public: case 'G': mode = MMT_GOP_START; break; case 'h': help_flag = true; break; + case 'k': key_flag = true; + TEST_EXTRA_ARG(i, 'k'); + { + ui32_t length; + Kumu::hex2bin(argv[i], key_value, KeyLen, &length); + + if ( length != KeyLen ) + { + fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen); + return; + } + } + break; + case 'm': read_hmac = true; break; case 'p': @@ -276,7 +297,7 @@ public: if ( help_flag || version_flag ) return; - + if ( ( mode == MMT_EXTRACT || mode == MMT_GOP_START ) && input_filename == 0 ) { fputs("Option requires at least one filename argument.\n", stderr); @@ -369,11 +390,11 @@ read_MPEG2_file(CommandOptions& Options) if ( ! Options.no_write_flag ) { - ui32_t write_count = 0; - result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); - } + ui32_t write_count = 0; + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); } } + } return result; } @@ -499,13 +520,13 @@ read_JP2K_S_file(CommandOptions& Options) { if ( ! Options.no_write_flag ) { - Kumu::FileWriter OutFile; - ui32_t write_count; - snprintf(filename, filename_max, left_format, Options.file_prefix, i); - result = OutFile.OpenWrite(filename); + Kumu::FileWriter OutFile; + ui32_t write_count; + snprintf(filename, filename_max, left_format, Options.file_prefix, i); + result = OutFile.OpenWrite(filename); - if ( ASDCP_SUCCESS(result) ) - result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); + if ( ASDCP_SUCCESS(result) ) + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); } if ( Options.verbose_flag ) @@ -519,18 +540,18 @@ read_JP2K_S_file(CommandOptions& Options) { if ( ! Options.no_write_flag ) { - Kumu::FileWriter OutFile; - ui32_t write_count; - snprintf(filename, filename_max, right_format, Options.file_prefix, i); - result = OutFile.OpenWrite(filename); + Kumu::FileWriter OutFile; + ui32_t write_count; + snprintf(filename, filename_max, right_format, Options.file_prefix, i); + result = OutFile.OpenWrite(filename); - if ( ASDCP_SUCCESS(result) ) - result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); - } + if ( ASDCP_SUCCESS(result) ) + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); + } if ( Options.verbose_flag ) FrameBuffer.Dump(stderr, Options.fb_dump_size); - } + } } return result; @@ -602,14 +623,14 @@ read_JP2K_file(CommandOptions& Options) { if ( ! Options.no_write_flag ) { - Kumu::FileWriter OutFile; - char filename[256]; - ui32_t write_count; - snprintf(filename, 256, name_format, Options.file_prefix, i); - result = OutFile.OpenWrite(filename); - - if ( ASDCP_SUCCESS(result) ) - result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); + Kumu::FileWriter OutFile; + char filename[256]; + ui32_t write_count; + snprintf(filename, 256, name_format, Options.file_prefix, i); + result = OutFile.OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); } if ( Options.verbose_flag ) @@ -659,14 +680,14 @@ read_PCM_file(CommandOptions& Options) } else { - FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc)); + FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc)); } if ( Options.verbose_flag ) { fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); - PCM::AudioDescriptorDump(ADesc); - } + PCM::AudioDescriptorDump(ADesc); + } } if ( ASDCP_SUCCESS(result) ) @@ -691,10 +712,10 @@ read_PCM_file(CommandOptions& Options) if ( ! Options.no_write_flag ) { - OutWave.OpenWrite(ADesc, Options.file_prefix, - ( Options.split_wav ? WavFileWriter::ST_STEREO : - ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) )); - } + 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 ) @@ -730,10 +751,10 @@ read_PCM_file(CommandOptions& Options) if ( ! Options.no_write_flag ) { - result = OutWave.WriteFrame(FrameBuffer); - } + result = OutWave.WriteFrame(FrameBuffer); } } + } return result; } @@ -820,6 +841,90 @@ read_timed_text_file(CommandOptions& Options) if ( ASDCP_SUCCESS(result) ) result = Writer.Write(FrameBuffer.RoData(), FrameBuffer.Size(), &write_count); + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + } + } + + return result; +} + +// Read one or more plaintext DCData bytestreams from a plaintext ASDCP file +// Read one or more plaintext DCData bytestreams from a ciphertext ASDCP file +// Read one or more ciphertext DCData byestreams from a ciphertext ASDCP file +// +Result_t +read_DCData_file(CommandOptions& Options) +{ + AESDecContext* Context = 0; + HMACContext* HMAC = 0; + DCData::MXFReader Reader; + DCData::FrameBuffer FrameBuffer(Options.fb_size); + ui32_t frame_count = 0; + + Result_t result = Reader.OpenRead(Options.input_filename); + + if ( ASDCP_SUCCESS(result) ) + { + DCData::DCDataDescriptor DDesc; + Reader.FillDCDataDescriptor(DDesc); + + frame_count = DDesc.ContainerDuration; + + if ( Options.verbose_flag ) + { + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); + DCData::DCDataDescriptorDump(DDesc); + } + } + + if ( ASDCP_SUCCESS(result) && Options.key_flag ) + { + Context = new AESDecContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) && Options.read_hmac ) + { + WriterInfo Info; + Reader.FillWriterInfo(Info); + + if ( Info.UsesHMAC ) + { + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); + } + else + { + fputs("File does not contain HMAC values, ignoring -m option.\n", stderr); + } + } + } + + ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count); + if ( last_frame > frame_count ) + last_frame = frame_count; + + char name_format[64]; + snprintf(name_format, 64, "%%s%%0%du.%s", Options.number_width, Options.extension); + + for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ ) + { + result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC); + + if ( ASDCP_SUCCESS(result) ) + { + if ( ! Options.no_write_flag ) + { + Kumu::FileWriter OutFile; + char filename[256]; + ui32_t write_count; + snprintf(filename, 256, name_format, Options.file_prefix, i); + result = OutFile.OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); + } + if ( Options.verbose_flag ) FrameBuffer.Dump(stderr, Options.fb_dump_size); } @@ -888,6 +993,15 @@ main(int argc, const char** argv) result = read_timed_text_file(Options); break; + case ESS_DCDATA_UNKNOWN: + result = read_DCData_file(Options); + break; + + case ESS_DCDATA_DOLBY_ATMOS: + Options.extension = "atmos"; + result = read_DCData_file(Options); + break; + default: fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.input_filename); return 5; diff --git a/src/asdcp-wrap.cpp b/src/asdcp-wrap.cpp index bbff82a..12435ce 100755 --- a/src/asdcp-wrap.cpp +++ b/src/asdcp-wrap.cpp @@ -25,7 +25,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*! \file asdcp-wrap.cpp - \version $Id$ + \version $Id$ \brief AS-DCP file manipulation utility This program wraps d-cinema essence (picture, sound or text) into an AS-DCP @@ -48,6 +48,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include #include #include @@ -76,7 +77,7 @@ public: static byte_t default_ProductUUID_Data[UUIDlen] = { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22, 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 }; - + memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen); CompanyName = "WidgetCo"; ProductName = "asdcp-wrap"; @@ -117,7 +118,7 @@ USAGE: %s [-h|-help] [-V]\n\ \n\ %s [-3] [-a ] [-b ] [-C
    ] [-d ]\n\ [-e|-E] [-f ] [-j ] [-k ]\n\ - [-l