From 1fdb4369c7fe50e8b0ade1dad7145bc072fce740 Mon Sep 17 00:00:00 2001 From: Philip de Nier Date: Mon, 11 Nov 2024 10:03:31 +0000 Subject: [PATCH] raw2bmx: Add support for ANC/VBI frame wrapped in KLV --- apps/raw2bmx/raw2bmx.cpp | 51 +++++++++--- include/bmx/essence_parser/CMakeLists.txt | 1 + include/bmx/essence_parser/KLVEssenceReader.h | 60 ++++++++++++++ include/bmx/essence_parser/KLVEssenceSource.h | 6 ++ src/essence_parser/CMakeLists.txt | 1 + src/essence_parser/KLVEssenceReader.cpp | 82 +++++++++++++++++++ src/essence_parser/KLVEssenceSource.cpp | 49 ++++++++--- 7 files changed, 227 insertions(+), 23 deletions(-) create mode 100644 include/bmx/essence_parser/KLVEssenceReader.h create mode 100644 src/essence_parser/KLVEssenceReader.cpp diff --git a/apps/raw2bmx/raw2bmx.cpp b/apps/raw2bmx/raw2bmx.cpp index 97e22329..26bfce8c 100644 --- a/apps/raw2bmx/raw2bmx.cpp +++ b/apps/raw2bmx/raw2bmx.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -153,6 +154,7 @@ struct RawInput RawEssenceReader *raw_reader; WaveReader *wave_reader; + KLVEssenceReader *klv_reader; uint32_t channel_count; TimedTextManifestParser *timed_text_manifest; @@ -256,14 +258,20 @@ static uint32_t read_samples(RawInput *input, uint32_t max_samples_per_read) uint32_t num_frame_samples = input->sample_sequence[input->sample_sequence_offset]; input->sample_sequence_offset = (input->sample_sequence_offset + 1) % input->sample_sequence_size; - if (input->raw_reader) + if (input->raw_reader) { return (input->raw_reader->ReadSamples(num_frame_samples) == num_frame_samples ? 1 : 0); - else + } else if (input->klv_reader) { + BMX_ASSERT(num_frame_samples == 1); + return (input->klv_reader->ReadValue() ? 1 : 0); + } else { return (input->wave_reader->Read(num_frame_samples) == num_frame_samples ? 1 : 0); + } } else { BMX_ASSERT(input->sample_sequence_size == 1 && input->sample_sequence[0] == 1); if (input->raw_reader) return input->raw_reader->ReadSamples(max_samples_per_read); + else if (input->klv_reader) + return (input->klv_reader->ReadValue() ? 1 : 0); else return input->wave_reader->Read(max_samples_per_read); } @@ -327,6 +335,12 @@ static bool open_raw_reader(RawInput *input) { input->raw_reader = new D10RawEssenceReader(essence_source); } + else if (input->parse_klv && + (input->essence_type == ANC_DATA || + input->essence_type == VBI_DATA)) + { + input->klv_reader = new KLVEssenceReader(dynamic_cast(essence_source)); + } else { input->raw_reader = new RawEssenceReader(essence_source); @@ -392,6 +406,7 @@ static void clear_input(RawInput *input) { delete input->raw_reader; delete input->wave_reader; + delete input->klv_reader; delete input->filter; delete input->timed_text_manifest; delete input->wave_chunk_refs; @@ -834,8 +849,8 @@ static void usage(const char *cmd) printf(" --vc3_1080i_1260 Raw VC3/DNxHD 1920x1080i 85 Mbps input file\n"); printf(" --pcm Raw PCM audio input file\n"); printf(" --wave Wave PCM audio input file\n"); - printf(" --anc Raw ST 436 Ancillary data. Currently requires the --anc-const option\n"); - printf(" --vbi Raw ST 436 Vertical Blanking Interval data. Currently requires the --vbi-const option\n"); + printf(" --anc Raw ST 436 Ancillary data. Requires the --anc-const option or frame wrapped in KLV and the --klv option\n"); + printf(" --vbi Raw ST 436 Vertical Blanking Interval data. Requires the --vbi-const option or frame wrapped in KLV and the --klv option\n"); printf(" --tt Manifest file containing Timed Text metadata\n"); printf("\n\n"); printf("Notes:\n"); @@ -4045,10 +4060,10 @@ int main(int argc, const char** argv) fprintf(stderr, "Multiple '%s' inputs are not permitted\n", argv[cmdln_index]); return 1; } - if (input.anc_const_size == 0) + if (input.anc_const_size == 0 && !input.parse_klv) { usage_ref(argv[0]); - fprintf(stderr, "Missing or zero '--anc-const' option for input '%s'\n", argv[cmdln_index]); + fprintf(stderr, "Missing or zero '--anc-const' or '--klv' options for input '%s'\n", argv[cmdln_index]); return 1; } input.essence_type = ANC_DATA; @@ -4071,7 +4086,7 @@ int main(int argc, const char** argv) fprintf(stderr, "Multiple '%s' inputs are not permitted\n", argv[cmdln_index]); return 1; } - if (input.vbi_const_size == 0) + if (input.vbi_const_size == 0 && !input.parse_klv) { usage_ref(argv[0]); fprintf(stderr, "Missing or zero '--vbi-const' option for input '%s'\n", argv[cmdln_index]); @@ -5593,10 +5608,12 @@ int main(int argc, const char** argv) clip_track->SetChannelAssignment(audio_layout_mode_label); break; case ANC_DATA: - clip_track->SetConstantDataSize(input->anc_const_size); + if (input->anc_const_size) + clip_track->SetConstantDataSize(input->anc_const_size); break; case VBI_DATA: - clip_track->SetConstantDataSize(input->vbi_const_size); + if (input->vbi_const_size) + clip_track->SetConstantDataSize(input->vbi_const_size); break; case TIMED_TEXT: clip_track->SetTimedTextSource(input->timed_text_manifest); @@ -5863,12 +5880,14 @@ int main(int argc, const char** argv) case ANC_DATA: input->sample_sequence[0] = 1; input->sample_sequence_size = 1; - input->raw_reader->SetFixedSampleSize(input->anc_const_size); + if (input->raw_reader && input->anc_const_size) + input->raw_reader->SetFixedSampleSize(input->anc_const_size); break; case VBI_DATA: input->sample_sequence[0] = 1; input->sample_sequence_size = 1; - input->raw_reader->SetFixedSampleSize(input->vbi_const_size); + if (input->raw_reader && input->vbi_const_size) + input->raw_reader->SetFixedSampleSize(input->vbi_const_size); break; case TIMED_TEXT: break; @@ -6206,6 +6225,16 @@ int main(int argc, const char** argv) input->raw_reader->GetSampleData(), input->raw_reader->GetSampleDataSize(), num_samples); } + } else if (input->klv_reader) { + num_samples = input->klv_reader->GetValueSize() ? 1 : 0; + if (num_samples) { + output_track->WriteSamples( + output_channel_index, + input->klv_reader->GetValue(), + input->klv_reader->GetValueSize(), + num_samples + ); + } } else { Frame *frame = input->wave_reader->GetTrack(input_channel_index)->GetFrameBuffer()->GetLastFrame(false); BMX_ASSERT(frame); diff --git a/include/bmx/essence_parser/CMakeLists.txt b/include/bmx/essence_parser/CMakeLists.txt index aeb1f14d..700c43eb 100644 --- a/include/bmx/essence_parser/CMakeLists.txt +++ b/include/bmx/essence_parser/CMakeLists.txt @@ -9,6 +9,7 @@ list(APPEND bmx_headers bmx/essence_parser/FileEssenceSource.h bmx/essence_parser/FilePatternEssenceSource.h bmx/essence_parser/J2CEssenceParser.h + bmx/essence_parser/KLVEssenceReader.h bmx/essence_parser/KLVEssenceSource.h bmx/essence_parser/MJPEGEssenceParser.h bmx/essence_parser/MPEG2AspectRatioFilter.h diff --git a/include/bmx/essence_parser/KLVEssenceReader.h b/include/bmx/essence_parser/KLVEssenceReader.h new file mode 100644 index 00000000..6ae9e1ce --- /dev/null +++ b/include/bmx/essence_parser/KLVEssenceReader.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024, British Broadcasting Corporation + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the British Broadcasting Corporation nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + */ + +#ifndef BMX_KLV_ESSENCE_READER_H_ +#define BMX_KLV_ESSENCE_READER_H_ + +#include +#include + + +namespace bmx +{ + +class KLVEssenceReader +{ +public: + KLVEssenceReader(KLVEssenceSource *essence_source); + ~KLVEssenceReader(); + + uint32_t ReadValue(); + + unsigned char* GetValue() const { return mValueBuffer.GetBytes(); } + uint32_t GetValueSize() const { return mValueBuffer.GetSize(); } + +private: + KLVEssenceSource *mEssenceSource; + ByteArray mValueBuffer; +}; + + +}; + + +#endif diff --git a/include/bmx/essence_parser/KLVEssenceSource.h b/include/bmx/essence_parser/KLVEssenceSource.h index 611669c6..5d3dbe22 100644 --- a/include/bmx/essence_parser/KLVEssenceSource.h +++ b/include/bmx/essence_parser/KLVEssenceSource.h @@ -59,6 +59,11 @@ class KLVEssenceSource : public EssenceSource virtual int GetErrno() const; virtual std::string GetStrError() const; +public: + bool PositionInV(uint64_t *size); + + uint64_t GetOffsetInV() const { return mValueLen - mRemValueLen; } + private: typedef enum { @@ -73,6 +78,7 @@ class KLVEssenceSource : public EssenceSource uint32_t mTrackNum; KLVState mState; uint64_t mValueLen; + uint64_t mRemValueLen; }; diff --git a/src/essence_parser/CMakeLists.txt b/src/essence_parser/CMakeLists.txt index 6cb02dfe..49cfdea7 100644 --- a/src/essence_parser/CMakeLists.txt +++ b/src/essence_parser/CMakeLists.txt @@ -8,6 +8,7 @@ list(APPEND bmx_sources essence_parser/FileEssenceSource.cpp essence_parser/FilePatternEssenceSource.cpp essence_parser/J2CEssenceParser.cpp + essence_parser/KLVEssenceReader.cpp essence_parser/KLVEssenceSource.cpp essence_parser/MJPEGEssenceParser.cpp essence_parser/MPEG2AspectRatioFilter.cpp diff --git a/src/essence_parser/KLVEssenceReader.cpp b/src/essence_parser/KLVEssenceReader.cpp new file mode 100644 index 00000000..aa09496a --- /dev/null +++ b/src/essence_parser/KLVEssenceReader.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2024, British Broadcasting Corporation + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the British Broadcasting Corporation nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define __STDC_LIMIT_MACROS + +#include + +#include +#include +#include + +using namespace std; +using namespace bmx; + + +KLVEssenceReader::KLVEssenceReader(KLVEssenceSource *essence_source) +{ + mEssenceSource = essence_source; +} + +KLVEssenceReader::~KLVEssenceReader() +{ +} + +uint32_t KLVEssenceReader::ReadValue() +{ + // Position at the next non-zero Value + uint64_t v_size = 0; + while (v_size == 0) { + if (!mEssenceSource->PositionInV(&v_size) || v_size > UINT32_MAX) { + if (v_size > UINT32_MAX) + log_warn("KLV value size %" PRIu64 " > max uint32 is not supported\n", v_size); + mValueBuffer.SetSize(0); + return 0; + } + } + + // Expect to be at the start of the V because the read below reads the whole V + BMX_CHECK(mEssenceSource->GetOffsetInV() == 0); + + mValueBuffer.Allocate(v_size); + + uint32_t read_size = mEssenceSource->Read(mValueBuffer.GetBytes(), v_size); + if (read_size != v_size) { + log_warn("Incomplete KLV; only read %u of %u\n", read_size, v_size); + mValueBuffer.SetSize(0); + return 0; + } + + mValueBuffer.SetSize(read_size); + return read_size; +} diff --git a/src/essence_parser/KLVEssenceSource.cpp b/src/essence_parser/KLVEssenceSource.cpp index 952eae0f..0c5a1a82 100644 --- a/src/essence_parser/KLVEssenceSource.cpp +++ b/src/essence_parser/KLVEssenceSource.cpp @@ -60,6 +60,7 @@ KLVEssenceSource::KLVEssenceSource(EssenceSource *child_source) mTrackNum = 0; mState = READ_KL_STATE; mValueLen = 0; + mRemValueLen = 0; } KLVEssenceSource::KLVEssenceSource(EssenceSource *child_source, const mxfKey *key) @@ -69,6 +70,7 @@ KLVEssenceSource::KLVEssenceSource(EssenceSource *child_source, const mxfKey *ke mTrackNum = 0; mState = READ_KL_STATE; mValueLen = 0; + mRemValueLen = 0; } KLVEssenceSource::KLVEssenceSource(EssenceSource *child_source, const mxfKey *key, uint64_t start_value_len) @@ -78,6 +80,7 @@ KLVEssenceSource::KLVEssenceSource(EssenceSource *child_source, const mxfKey *ke mTrackNum = 0; mState = READ_V_STATE; mValueLen = start_value_len; + mRemValueLen = start_value_len; } KLVEssenceSource::KLVEssenceSource(EssenceSource *child_source, uint32_t track_num) @@ -87,6 +90,7 @@ KLVEssenceSource::KLVEssenceSource(EssenceSource *child_source, uint32_t track_n mTrackNum = track_num; mState = READ_KL_STATE; mValueLen = 0; + mRemValueLen = 0; } KLVEssenceSource::~KLVEssenceSource() @@ -100,15 +104,16 @@ uint32_t KLVEssenceSource::Read(unsigned char *data, uint32_t size) unsigned char len_buffer[9]; uint32_t total_read = 0; - while (total_read < size) { + while (total_read < size || (size == 0 && mState == READ_KL_STATE)) { if (mState == READ_KL_STATE) { + uint64_t value_len; if (mChildSource->Read(&key.octet0, mxfKey_extlen) != mxfKey_extlen) break; if (mChildSource->Read(len_buffer, 1) != 1) break; - mValueLen = 0; + value_len = 0; if (len_buffer[0] < 0x80) { - mValueLen = len_buffer[0]; + value_len = len_buffer[0]; } else { uint8_t i; uint8_t len_size = len_buffer[0] & 0x7f; @@ -116,11 +121,11 @@ uint32_t KLVEssenceSource::Read(unsigned char *data, uint32_t size) if (mChildSource->Read(&len_buffer[1], len_size) != len_size) break; for (i = 0; i < len_size; i++) { - mValueLen <<= 8; - mValueLen |= len_buffer[i + 1]; + value_len <<= 8; + value_len |= len_buffer[i + 1]; } - if (mValueLen > INT64_MAX) { - log_error("Unsupported KLV length %" PRIu64 " in essence source\n", mValueLen); + if (value_len > INT64_MAX) { + log_error("Unsupported KLV length %" PRIu64 " in essence source\n", value_len); break; } } @@ -136,22 +141,29 @@ uint32_t KLVEssenceSource::Read(unsigned char *data, uint32_t size) mKey = key; mState = READ_V_STATE; } - if (mState != READ_V_STATE && !mChildSource->Skip(mValueLen)) + + if (mState != READ_V_STATE && !mChildSource->Skip(value_len)) break; + + if (mState == READ_V_STATE) { + mValueLen = value_len; + mRemValueLen = value_len; + } + } else if (mState == READ_V_STATE) { uint32_t num_read = 0; uint32_t next_read = size - total_read; - if (next_read > mValueLen) - next_read = (uint32_t)mValueLen; + if (next_read > mRemValueLen) + next_read = (uint32_t)mRemValueLen; if (data) num_read = mChildSource->Read(&data[total_read], next_read); else if (mChildSource->Skip(next_read)) num_read = next_read; total_read += num_read; - mValueLen -= num_read; + mRemValueLen -= num_read; if (num_read < next_read) break; - if (mValueLen == 0) + if (mRemValueLen == 0) mState = READ_KL_STATE; } else { break; @@ -209,3 +221,16 @@ string KLVEssenceSource::GetStrError() const return mChildSource->GetStrError(); } +bool KLVEssenceSource::PositionInV(uint64_t *size) +{ + *size = 0; + + if (mState == READ_KL_STATE) + Read(0, 0); + + if (mState != READ_V_STATE) + return false; + + *size = mRemValueLen; + return true; +}