Update of /cvsroot/ingex/ingex/libMXF++/examples/OPAtomReader In directory 23jxhf1.ch3.sourceforge.com:/tmp/cvs-serv27946/examples/OPAtomReader Added Files: FixedSizeEssenceParser.cpp FixedSizeEssenceParser.h FrameOffsetIndexTable.cpp FrameOffsetIndexTable.h Makefile OPAtomClipReader.cpp OPAtomClipReader.h OPAtomContentPackage.cpp OPAtomContentPackage.h OPAtomTrackReader.cpp OPAtomTrackReader.h PCMEssenceParser.cpp PCMEssenceParser.h RawEssenceParser.cpp RawEssenceParser.h VariableSizeEssenceParser.cpp VariableSizeEssenceParser.h test_opatomreader.cpp Log Message: Add initial implementation of an OP-Atom reader suitable for the file recovery utility --- NEW FILE: PCMEssenceParser.cpp --- /* * $Id: PCMEssenceParser.cpp,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Parse raw PCM essence data * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #include <libMXF++/MXF.h> #include "PCMEssenceParser.h" using namespace std; using namespace mxfpp; PCMEssenceParser::PCMEssenceParser(File *file, int64_t essence_length, mxfUL essence_label, mxfRational edit_rate, const FileDescriptor *file_descriptor) : RawEssenceParser(file, essence_length, essence_label) { // only support 625 or 525 line frame rates MXFPP_ASSERT((edit_rate.numerator == 25 && edit_rate.denominator == 1) || (edit_rate.numerator == 30000 && edit_rate.denominator == 1001)); const GenericSoundEssenceDescriptor *sound_descriptor = dynamic_cast<const GenericSoundEssenceDescriptor*>(file_descriptor); if (!sound_descriptor) throw MXFException("FileDescriptor for PCM audio is not a GenericSoundEssenceDescriptor"); if (sound_descriptor->haveAudioSamplingRate()) { mxfRational sampling_rate = sound_descriptor->getAudioSamplingRate(); // only support 48kHz MXFPP_ASSERT(sampling_rate.numerator == 48000 && sampling_rate.denominator == 1); } if (!sound_descriptor->haveQuantizationBits()) throw MXFException("Sound descriptor does not specify quantization bits"); if (!sound_descriptor->haveChannelCount()) throw MXFException("Sound descriptor does not specify channel count"); mBytesPerSample = sound_descriptor->getChannelCount() * ((sound_descriptor->getQuantizationBits() + 7) / 8); if (edit_rate.numerator == 25 && edit_rate.denominator == 1) { mSequenceLen = 1; mFrameSizeSequence[0] = 1920 * mBytesPerSample; mFrameSequenceSize = mFrameSizeSequence[0]; } else { // 30000/1001 mSequenceLen = 5; mFrameSizeSequence[0] = 1602 * mBytesPerSample; mFrameSizeSequence[1] = 1601 * mBytesPerSample; mFrameSizeSequence[2] = 1602 * mBytesPerSample; mFrameSizeSequence[3] = 1601 * mBytesPerSample; mFrameSizeSequence[4] = 1602 * mBytesPerSample; mFrameSequenceSize = (1602 * 3 + 1601 * 2) * mBytesPerSample; } DetermineDuration(); } PCMEssenceParser::~PCMEssenceParser() { } bool PCMEssenceParser::Read(DynamicByteArray *data, uint32_t *num_samples) { if (IsEOF()) return false; uint32_t frame_size = (uint32_t)(mFrameSizeSequence[mPosition % mSequenceLen]); data->allocate(frame_size); uint32_t count = mFile->read(data->getBytes(), frame_size); if (count != frame_size) { mFile->seek(-count, SEEK_CUR); mDuration = mPosition; return false; } data->setSize(frame_size); *num_samples = frame_size / mBytesPerSample; mEssenceOffset += frame_size; mPosition++; return true; } bool PCMEssenceParser::Seek(int64_t position) { if (position >= mDuration) return false; // whole sequence offset int64_t offset = (position / mSequenceLen) * mFrameSequenceSize; // partial sequence offset int remainder = (int)(position % mSequenceLen); int i; for (i = 0; i < remainder; i++) offset += mFrameSizeSequence[i]; mFile->seek(mEssenceStartOffset + offset, SEEK_SET); mEssenceOffset = offset; mPosition = position; return true; } int64_t PCMEssenceParser::DetermineDuration() { if (mDuration >= 0) return mDuration; int64_t file_size = mFile->size(); int64_t essence_length = file_size - mEssenceStartOffset; if (mEssenceLength > 0 && essence_length > mEssenceLength) essence_length = mEssenceLength; // whole sequences mDuration = mSequenceLen * (essence_length / mFrameSequenceSize); // partial sequence int64_t remainder = essence_length - mDuration * mFrameSequenceSize / mSequenceLen; int i; for (i = 0; i < mSequenceLen; i++) { remainder -= mFrameSizeSequence[i]; if (remainder < 0) break; mDuration++; } return mDuration; } --- NEW FILE: VariableSizeEssenceParser.h --- /* * $Id: VariableSizeEssenceParser.h,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Parse raw essence data with variable frame size * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #ifndef __VARIABLE_SIZE_ESSENCE_PARSER_H__ #define __VARIABLE_SIZE_ESSENCE_PARSER_H__ #include "RawEssenceParser.h" class MJPEGParseState { public: MJPEGParseState(); void Reset(); public: uint32_t resolution_id; DynamicByteArray buffer; uint32_t position; uint32_t prev_position; bool end_of_field; bool field2; int skip_count; bool have_len_byte1; bool have_len_byte2; int marker_state; }; class VariableSizeEssenceParser : public RawEssenceParser { public: VariableSizeEssenceParser(mxfpp::File *file, int64_t essence_length, mxfUL essence_label, mxfRational edit_rate, const mxfpp::FileDescriptor *file_descriptor, FrameOffsetIndexTableSegment *index_table); virtual ~VariableSizeEssenceParser(); public: // from RawEssenceParser virtual bool Read(DynamicByteArray *data, uint32_t *num_samples); virtual bool Seek(int64_t position); virtual int64_t DetermineDuration(); private: uint32_t DetermineUncFrameSize(const mxfpp::FileDescriptor *file_descriptor); bool ParseMJPEGImage(DynamicByteArray *image_data); bool ProcessMJPEGImageData(DynamicByteArray *image_data, bool *have_image); bool UpdateIndexTable(DynamicByteArray *image_data, int64_t position); private: FrameOffsetIndexTableSegment *mIndexTable; bool mOwnIndexTable; bool mIndexTableIsComplete; uint32_t mFrameSizeEstimate; MJPEGParseState mMJPEGParseState; DynamicByteArray mSeekData; }; #endif --- NEW FILE: FixedSizeEssenceParser.cpp --- /* * $Id: FixedSizeEssenceParser.cpp,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Parse raw essence data with fixed frame size * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #include <cstring> #include <libMXF++/MXF.h> #include <mxf/mxf_labels_and_keys.h> #include <mxf/mxf_avid.h> #include "FixedSizeEssenceParser.h" using namespace std; using namespace mxfpp; FixedSizeEssenceParser::FixedSizeEssenceParser(File *file, int64_t essence_length, mxfUL essence_label, const FileDescriptor *file_descriptor, uint32_t frame_size) : RawEssenceParser(file, essence_length, essence_label) { mFrameSize = frame_size; if (frame_size == 0 && (mxf_equals_ul(&essence_label, &MXF_EC_L(SD_Unc_625_50i_422_135_ClipWrapped)) || mxf_equals_ul(&essence_label, &MXF_EC_L(HD_Unc_1080_50i_422_ClipWrapped)))) { DetermineUncFrameSize(file_descriptor); } else { MXFPP_ASSERT(frame_size != 0); } DetermineDuration(); } FixedSizeEssenceParser::~FixedSizeEssenceParser() { } bool FixedSizeEssenceParser::Read(DynamicByteArray *data, uint32_t *num_samples) { if (mFrameSize <= 0 || IsEOF()) return false; data->allocate(mFrameSize); uint32_t count = mFile->read(data->getBytes(), mFrameSize); if (count != mFrameSize) { mFile->seek(-count, SEEK_CUR); mDuration = mPosition; return false; } data->setSize(mFrameSize); *num_samples = 1; mEssenceOffset += mFrameSize; mPosition++; return true; } bool FixedSizeEssenceParser::Seek(int64_t position) { if (mFrameSize <= 0 || position >= mDuration) return false; mFile->seek(mEssenceStartOffset + mFrameSize * position, SEEK_SET); mEssenceOffset = mFrameSize * position; mPosition = position; return true; } int64_t FixedSizeEssenceParser::DetermineDuration() { if (mDuration >= 0) return mDuration; if (mFrameSize <= 0) { if (!DetermineFrameSize()) return -1; } int64_t file_size = mFile->size(); int64_t essence_length = file_size - mEssenceStartOffset; if (mEssenceLength > 0 && essence_length > mEssenceLength) essence_length = mEssenceLength; mDuration = essence_length / mFrameSize; return mDuration; } bool FixedSizeEssenceParser::DetermineFrameSize() { if (memcmp(&mEssenceLabel, &MXF_EC_L(AvidIMX30_625_50), sizeof(mEssenceLabel)) == 0 || memcmp(&mEssenceLabel, &MXF_EC_L(AvidIMX40_625_50), sizeof(mEssenceLabel)) == 0 || memcmp(&mEssenceLabel, &MXF_EC_L(AvidIMX50_625_50), sizeof(mEssenceLabel)) == 0 || memcmp(&mEssenceLabel, &MXF_EC_L(AvidIMX30_525_60), sizeof(mEssenceLabel)) == 0 || memcmp(&mEssenceLabel, &MXF_EC_L(AvidIMX40_525_60), sizeof(mEssenceLabel)) == 0 || memcmp(&mEssenceLabel, &MXF_EC_L(AvidIMX50_525_60), sizeof(mEssenceLabel)) == 0) { return DetermineMPEGFrameSize(); } return false; } bool FixedSizeEssenceParser::DetermineMPEGFrameSize() { if (mFrameSize > 0) return true; int64_t first_frame_start = -1; int64_t second_frame_start = -1; int64_t offset = 0; unsigned char buffer[4096]; size_t num_read; size_t i; int num_zeros = 0; bool have_start_code = 0; // MPEG elementary stream // start code prefix is 0000 0000 0000 0000 0000 0001 (2 0x00 bytes followed by 0x01) // sequence start code is 0xb3 // try find the start of the first and second frame, where the difference in offsets gives the frame size while (second_frame_start < 0) { num_read = mFile->read(buffer, sizeof(buffer)); i = 0; while (i < num_read) { if (have_start_code) { if (buffer[i] == 0xb3) { if (first_frame_start < 0) { first_frame_start = offset - 3; } else { second_frame_start = offset - 3; break; } } have_start_code = 0; } else if (buffer[i] == 0x00) { num_zeros++; } else { if (buffer[i] == 0x01 && num_zeros >= 2) have_start_code = 1; num_zeros = 0; } i++; offset++; } if (num_read != sizeof(buffer)) break; } if (second_frame_start > 0) { mEssenceStartOffset += first_frame_start; if (mEssenceLength > 0) mEssenceLength -= first_frame_start; mFrameSize = second_frame_start - first_frame_start; } else if (first_frame_start > 0) { // assume a single frame which ends at the end of the essence data or file mEssenceStartOffset += first_frame_start; if (mEssenceLength > 0) { mEssenceLength -= first_frame_start; mFrameSize = mEssenceLength; } else { mFrameSize = offset - first_frame_start; } } mFile->seek(mEssenceStartOffset, SEEK_SET); return mFrameSize > 0; } void FixedSizeEssenceParser::DetermineUncFrameSize(const FileDescriptor *file_descriptor) { const CDCIEssenceDescriptor *cdci_descriptor = dynamic_cast<const CDCIEssenceDescriptor*>(file_descriptor); MXFPP_CHECK(cdci_descriptor); // Use the Avid FrameSampleSize extension item if available and non-zero if (cdci_descriptor->haveItem(&MXF_ITEM_K(GenericPictureEssenceDescriptor, FrameSampleSize))) { int32_t frame_sample_size = cdci_descriptor->getInt32Item(&MXF_ITEM_K(GenericPictureEssenceDescriptor, FrameSampleSize)); if (frame_sample_size > 0) mFrameSize = (uint32_t)frame_sample_size; } if (mFrameSize == 0) { uint32_t stored_width = 0, stored_height = 0; uint32_t h_subsamp = 0, v_subsamp = 0; int field_factor = 1; uint32_t image_start_offset = 0, image_end_offset = 0; // only 8-bit is currently supported MXFPP_CHECK(!cdci_descriptor->haveComponentDepth() || cdci_descriptor->getComponentDepth() == 8); if (cdci_descriptor->haveFrameLayout()) { uint8_t frame_layout = cdci_descriptor->getFrameLayout(); switch (frame_layout) { case 1: // separate fields case 4: // segmented frame // stored_height is the height of one of the 2 fields field_factor = 2; break; case 0: // full frame case 2: // single_field case 3: // mixed fields case 255: // distinguished value default: field_factor = 1; break; } } if (cdci_descriptor->haveHorizontalSubsampling()) h_subsamp = cdci_descriptor->getHorizontalSubsampling(); MXFPP_CHECK(h_subsamp != 0); if (cdci_descriptor->haveVerticalSubsampling()) v_subsamp = cdci_descriptor->getVerticalSubsampling(); MXFPP_CHECK(v_subsamp != 0); if (cdci_descriptor->haveStoredWidth()) stored_width = cdci_descriptor->getStoredWidth(); MXFPP_CHECK(stored_width != 0); if (cdci_descriptor->haveStoredHeight()) stored_height = cdci_descriptor->getStoredHeight(); MXFPP_CHECK(stored_height != 0); if (cdci_descriptor->haveImageStartOffset()) image_start_offset = cdci_descriptor->getImageStartOffset(); if (cdci_descriptor->haveImageEndOffset()) image_end_offset = cdci_descriptor->getImageEndOffset(); mFrameSize = (uint32_t)(image_start_offset + image_end_offset + field_factor * stored_width * stored_height * (1 + 2.0 / (h_subsamp * v_subsamp)) + 0.5); } } --- NEW FILE: RawEssenceParser.h --- /* * $Id: RawEssenceParser.h,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Parse raw essence data * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #ifndef __RAW_ESSENCE_PARSER_H__ #define __RAW_ESSENCE_PARSER_H__ #include <vector> #include "DynamicByteArray.h" #include "FrameOffsetIndexTable.h" namespace mxfpp { class File; class FileDescriptor; }; class RawEssenceParser { public: static RawEssenceParser* Create(mxfpp::File *file, int64_t essence_length, mxfUL essence_label, const mxfpp::FileDescriptor *file_descriptor, mxfRational edit_rate, uint32_t frame_size, FrameOffsetIndexTableSegment *index_table); public: virtual ~RawEssenceParser(); mxfUL GetEssenceContainerLabel() { return mEssenceLabel; } int64_t GetDuration() { return mDuration; } int64_t GetPosition() { return mPosition; } int64_t GetEssenceOffset() { return mEssenceOffset; } bool IsEOF(); public: virtual bool Read(DynamicByteArray *data, uint32_t *num_samples) = 0; virtual bool Seek(int64_t position) = 0; virtual int64_t DetermineDuration() = 0; protected: RawEssenceParser(mxfpp::File *file, int64_t essence_length, mxfUL essence_label); protected: mxfpp::File *mFile; int64_t mEssenceLength; mxfUL mEssenceLabel; int64_t mEssenceStartOffset; int64_t mPosition; int64_t mDuration; int64_t mEssenceOffset; }; #endif --- NEW FILE: OPAtomClipReader.h --- /* * $Id: OPAtomClipReader.h,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Read a clip consisting of a set of MXF OP-Atom files * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #ifndef __OPATOM_CLIP_READER_H__ #define __OPATOM_CLIP_READER_H__ #include <vector> #include <map> #include "OPAtomTrackReader.h" class OPAtomClipReader { public: static std::pair<OPAtomOpenResult, std::string> Open(const std::vector<std::string> &track_filenames, OPAtomClipReader **clip_reader); static std::string ErrorToString(OPAtomOpenResult result); public: ~OPAtomClipReader(); public: int64_t GetDuration(); int64_t DetermineDuration(); int64_t GetPosition(); const OPAtomContentPackage* Read(); bool Seek(int64_t position); bool IsEOF(); const std::vector<OPAtomTrackReader*>& GetTrackReaders() { return mTrackReaders; } private: OPAtomClipReader(const std::vector<OPAtomTrackReader*> &track_readers); private: OPAtomContentPackage mContentPackage; std::vector<OPAtomTrackReader*> mTrackReaders; int64_t mPosition; int64_t mDuration; }; #endif --- NEW FILE: test_opatomreader.cpp --- /* * $Id: test_opatomreader.cpp,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Use to test the OP-Atom reader * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #define __STDC_FORMAT_MACROS 1 #include <libMXF++/MXF.h> #include "OPAtomClipReader.h" using namespace std; using namespace mxfpp; int main(int argc, const char **argv) { vector<string> filenames; if (argc != 2) { fprintf(stderr, "Usage: %s <mxf filename>\n", argv[0]); return 1; } filenames.push_back(argv[1]); OPAtomClipReader *clip_reader; pair<OPAtomOpenResult, string> result = OPAtomClipReader::Open(filenames, &clip_reader); if (result.first != OP_ATOM_SUCCESS) { fprintf(stderr, "Failed to open file '%s': %s\n", result.second.c_str(), OPAtomClipReader::ErrorToString(result.first).c_str()); return 1; } printf("Duration = %"PRId64"\n", clip_reader->GetDuration()); const OPAtomContentPackage *content_package; FILE *output = fopen("/tmp/test.raw", "wb"); while (true) { content_package = clip_reader->Read(); if (!content_package) { if (!clip_reader->IsEOF()) { fprintf(stderr, "Failed to read content package\n"); } break; } printf("writing %d bytes from essence offset 0x%"PRIx64"\n", content_package->GetEssenceDataISize(0), content_package->GetEssenceDataI(0)->GetEssenceOffset()); fwrite(content_package->GetEssenceDataIBytes(0), content_package->GetEssenceDataISize(0), 1, output); } printf("Duration = %"PRId64"\n", clip_reader->GetDuration()); fclose(output); clip_reader->Seek(0); content_package = clip_reader->Read(); if (!content_package) { if (!clip_reader->IsEOF()) { fprintf(stderr, "Failed to read content package\n"); } } delete clip_reader; return 0; } --- NEW FILE: OPAtomClipReader.cpp --- /* * $Id: OPAtomClipReader.cpp,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Read a clip consisting of a set of MXF OP-Atom files * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #include <libMXF++/MXF.h> #include "OPAtomClipReader.h" using namespace std; using namespace mxfpp; pair<OPAtomOpenResult, string> OPAtomClipReader::Open(const vector<std::string> &track_filenames, OPAtomClipReader **clip_reader) { MXFPP_ASSERT(!track_filenames.empty()); vector<OPAtomTrackReader*> track_readers; string filename; try { OPAtomOpenResult result; OPAtomTrackReader *track_reader; size_t i; for (i = 0; i < track_filenames.size(); i++) { filename = track_filenames[i]; result = OPAtomTrackReader::Open(filename, &track_reader); if (result != OP_ATOM_SUCCESS) throw result; track_readers.push_back(track_reader); } *clip_reader = new OPAtomClipReader(track_readers); return make_pair(OP_ATOM_SUCCESS, ""); } catch (const OPAtomOpenResult &ex) { size_t i; for (i = 0; i < track_readers.size(); i++) delete track_readers[i]; return make_pair(ex, filename); } catch (...) { size_t i; for (i = 0; i < track_readers.size(); i++) delete track_readers[i]; return make_pair(OP_ATOM_FAIL, filename); } } string OPAtomClipReader::ErrorToString(OPAtomOpenResult result) { return OPAtomTrackReader::ErrorToString(result); } OPAtomClipReader::OPAtomClipReader(const std::vector<OPAtomTrackReader*> &track_readers) { MXFPP_ASSERT(!track_readers.empty()); mTrackReaders = track_readers; mPosition = 0; mDuration = -1; GetDuration(); } OPAtomClipReader::~OPAtomClipReader() { size_t i; for (i = 0; i < mTrackReaders.size(); i++) delete mTrackReaders[i]; } int64_t OPAtomClipReader::GetDuration() { if (mDuration >= 0) return mDuration; int64_t min_duration = -1; int64_t duration = -1; size_t i; for (i = 0; i < mTrackReaders.size(); i++) { duration = mTrackReaders[i]->GetDuration(); if (duration < 0) { min_duration = -1; break; } if (min_duration < 0 || duration < min_duration) min_duration = duration; } mDuration = min_duration; return mDuration; } int64_t OPAtomClipReader::DetermineDuration() { int64_t min_duration = -1; int64_t duration = -1; size_t i; for (i = 0; i < mTrackReaders.size(); i++) { duration = mTrackReaders[i]->DetermineDuration(); if (duration < 0) { min_duration = -1; break; } if (min_duration < 0 || duration < min_duration) min_duration = duration; } mDuration = min_duration; return mDuration; } int64_t OPAtomClipReader::GetPosition() { return mPosition; } const OPAtomContentPackage* OPAtomClipReader::Read() { int64_t prev_position = GetPosition(); size_t i; for (i = 0; i < mTrackReaders.size(); i++) { if (!mTrackReaders[i]->Read(&mContentPackage)) { size_t j; for (j = 0; j <= i; j++) mTrackReaders[j]->Seek(prev_position); return 0; } } mPosition++; return &mContentPackage; } bool OPAtomClipReader::Seek(int64_t position) { int64_t prev_position = GetPosition(); size_t i; for (i = 0; i < mTrackReaders.size(); i++) { if (!mTrackReaders[i]->Seek(position)) { size_t j; for (j = 0; j <= i; j++) mTrackReaders[j]->Seek(prev_position); return false; } } mPosition = position; return true; } bool OPAtomClipReader::IsEOF() { size_t i; for (i = 0; i < mTrackReaders.size(); i++) { if (mTrackReaders[i]->IsEOF()) return true; } return false; } --- NEW FILE: FixedSizeEssenceParser.h --- /* * $Id: FixedSizeEssenceParser.h,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Parse raw essence data with fixed frame size * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #ifndef __FIXED_SIZE_ESSENCE_PARSER_H__ #define __FIXED_SIZE_ESSENCE_PARSER_H__ #include "RawEssenceParser.h" class FixedSizeEssenceParser : public RawEssenceParser { public: FixedSizeEssenceParser(mxfpp::File *file, int64_t essence_length, mxfUL essence_label, const mxfpp::FileDescriptor *file_descriptor, uint32_t frame_size); virtual ~FixedSizeEssenceParser(); public: // from RawEssenceParser virtual bool Read(DynamicByteArray *data, uint32_t *num_samples); virtual bool Seek(int64_t position); virtual int64_t DetermineDuration(); private: bool DetermineFrameSize(); bool DetermineMPEGFrameSize(); void DetermineUncFrameSize(const mxfpp::FileDescriptor *file_descriptor); private: uint32_t mFrameSize; }; #endif --- NEW FILE: Makefile --- TOPLEVEL = ../.. include $(TOPLEVEL)/vars.mk COMMON_DIR = ../Common INCLUDES += -I$(COMMON_DIR) COMMON_OBJECTS = $(COMMON_DIR)/.objs/DynamicByteArray.o SOURCES = OPAtomContentPackage.cpp \ FrameOffsetIndexTable.cpp \ RawEssenceParser.cpp \ FixedSizeEssenceParser.cpp \ VariableSizeEssenceParser.cpp \ PCMEssenceParser.cpp \ OPAtomClipReader.cpp \ OPAtomTrackReader.cpp OBJECTS = $(patsubst %.cpp,.objs/%.o,$(SOURCES)) DEPENDENCIES = $(patsubst %.cpp,.deps/%.d,$(SOURCES)) .PHONY: all all: libOPAtomReader.a test_opatomreader .PHONY: common common: $(MAKE) -C $(COMMON_DIR) all libOPAtomReader.a: $(OBJECTS) common $(AR) $@ $(OBJECTS) $(COMMON_OBJECTS) .deps/%.d : %.cpp @echo Generating dependency file for $<; \ mkdir -p .deps; \ $(COMPILE) $(INCLUDES) -MM $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\.objs/\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ .objs/%.o: %.cpp @mkdir -p .objs $(COMPILE) -c $(INCLUDES) $< -o $@ test_opatomreader: .objs/test_opatomreader.o libOPAtomReader.a $(COMPILE) .objs/test_opatomreader.o -L. -L$(LIBMXFPP_DIR)/libMXF++ -L$(LIBMXF_DIR)/lib -lOPAtomReader -lMXF++ -lMXF $(UUIDLIB) -o $@ .PHONY: clean clean: @rm -f *~ *.a test_opatomreader @rm -Rf .objs @rm -Rf .deps ifneq "$(MAKECMDGOALS)" "clean" -include $(DEPENDENCIES) -include .deps/test_opatomreader.d endif --- NEW FILE: OPAtomTrackReader.cpp --- /* * $Id: OPAtomTrackReader.cpp,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Read a single OP-Atom file representing a clip track * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #include <cstring> #include <libMXF++/MXF.h> #include <libMXF++/extensions/TaggedValue.h> #include <mxf/mxf_avid.h> #include "OPAtomTrackReader.h" using namespace std; using namespace mxfpp; const char * const ERROR_STRINGS[] = { "success", "general error", "could not open for reading", "no header partition found", "file is not OP-Atom", "error reading header metadata", "no file source package found", "essence data not found" }; OPAtomOpenResult OPAtomTrackReader::Open(std::string filename, OPAtomTrackReader **track_reader) { File *file = 0; Partition *header_partition = 0; try { // open the file try { file = File::openRead(filename); } catch (...) { throw OP_ATOM_FILE_OPEN_READ_ERROR; } // read the header partition pack and check the operational pattern header_partition = Partition::findAndReadHeaderPartition(file); if (!header_partition) throw OP_ATOM_NO_HEADER_PARTITION; if (!is_op_atom(header_partition->getOperationalPattern())) throw OP_ATOM_NOT_OP_ATOM; *track_reader = new OPAtomTrackReader(filename, file, header_partition); delete header_partition; header_partition = 0; return OP_ATOM_SUCCESS; } catch (const OPAtomOpenResult &ex) { delete file; delete header_partition; return ex; } catch (...) { delete file; delete header_partition; return OP_ATOM_FAIL; } } string OPAtomTrackReader::ErrorToString(OPAtomOpenResult result) { size_t index = (size_t)(-1 * (int)result); MXFPP_ASSERT(index < sizeof(ERROR_STRINGS) / sizeof(char*)); return ERROR_STRINGS[index]; } OPAtomTrackReader::OPAtomTrackReader(string filename, File *file, Partition *header_partition) { mFilename = filename; mTrackId = 0; mDurationInMetadata = -1; mIsPicture = true; DataModel *data_model = 0; AvidHeaderMetadata *header_metadata = 0; FrameOffsetIndexTableSegment *index_table = 0; try { int64_t essence_length = 0; mxfUL essence_label = g_Null_UL; mxfRational edit_rate = (mxfRational){0, 1}; FileDescriptor *file_descriptor = 0; uint32_t frame_size = 0; mxfKey key; uint8_t llen; uint64_t len; // get essence container label vector<mxfUL> container_labels = header_partition->getEssenceContainers(); MXFPP_CHECK(container_labels.size() == 1); essence_label = container_labels[0]; // read the header metadata data_model = new DataModel(); header_metadata = new AvidHeaderMetadata(data_model); TaggedValue::registerObjectFactory(header_metadata); file->readNextNonFillerKL(&key, &llen, &len); if (!mxf_is_header_metadata(&key)) throw OP_ATOM_FAIL; header_metadata->read(file, header_partition, &key, llen, len); Preface *preface = header_metadata->getPreface(); ContentStorage *content = preface->getContentStorage(); vector<GenericPackage*> packages = content->getPackages(); // get the file source package and descriptor SourcePackage *fsp; size_t i; for (i = 0; i < packages.size(); i++) { fsp = dynamic_cast<SourcePackage*>(packages[i]); if (!fsp || !fsp->haveDescriptor()) continue; file_descriptor = dynamic_cast<FileDescriptor*>(fsp->getDescriptor()); if (file_descriptor) break; } if (!file_descriptor) throw OP_ATOM_NO_FILE_PACKAGE; // get the material track info Track *mp_track = 0; for (i = 0; i < packages.size(); i++) { MaterialPackage *mp = dynamic_cast<MaterialPackage*>(packages[i]); if (!mp) continue; vector<GenericTrack*> tracks = mp->getTracks(); size_t j; for (j = 0; j < tracks.size(); j++) { Track *track = dynamic_cast<Track*>(tracks[j]); if (!track) continue; StructuralComponent *track_sequence = track->getSequence(); Sequence *sequence = dynamic_cast<Sequence*>(track_sequence); SourceClip *source_clip = dynamic_cast<SourceClip*>(track_sequence); if (sequence) { vector<StructuralComponent*> components = sequence->getStructuralComponents(); if (components.size() != 1) continue; source_clip = dynamic_cast<SourceClip*>(components[0]); } if (!source_clip) continue; mxfUMID fsp_umid = fsp->getPackageUID(); mxfUMID sc_umid = source_clip->getSourcePackageID(); if (memcmp(&fsp_umid, &sc_umid, sizeof(fsp_umid)) == 0) { mp_track = track; edit_rate = mp_track->getEditRate(); mTrackId = mp_track->getTrackID(); mDurationInMetadata = source_clip->getDuration(); break; } } if (mp_track) break; } if (!mp_track) throw OP_ATOM_HEADER_ERROR; // get the index table info (if present and complete) int64_t file_pos = file->tell(); try { file->readNextNonFillerKL(&key, &llen, &len); while (!IndexTableSegment::isIndexTableSegment(&key)) { file->skip(len); file->readNextNonFillerKL(&key, &llen, &len); } if (IndexTableSegment::isIndexTableSegment(&key)) { index_table = FrameOffsetIndexTableSegment::read(file, len); mxfRational index_edit_rate = index_table->getIndexEditRate(); if (memcmp(&index_edit_rate, &edit_rate, sizeof(index_edit_rate)) == 0) frame_size = index_table->getEditUnitByteCount(); } } catch (...) { mxf_log_warn("Ignore errors - failed to find or read the index table segment\n"); // do nothing } file->seek(file_pos, SEEK_SET); // position file at start of essence data try { file->readNextNonFillerKL(&key, &llen, &len); while (!mxf_is_gc_essence_element(&key) && !mxf_avid_is_essence_element(&key)) { file->skip(len); file->readNextNonFillerKL(&key, &llen, &len); } } catch (...) { throw OP_ATOM_ESSENCE_DATA_NOT_FOUND; } essence_length = len; mEssenceParser = RawEssenceParser::Create(file, essence_length, essence_label, file_descriptor, edit_rate, frame_size, index_table); if (!mEssenceParser) throw MXFException("Failed to create essence parser"); mDataModel = data_model; mHeaderMetadata = header_metadata; mIndexTable = index_table; } catch (...) { delete data_model; delete header_metadata; delete index_table; throw; } } OPAtomTrackReader::~OPAtomTrackReader() { delete mEssenceParser; delete mHeaderMetadata; delete mDataModel; delete mIndexTable; } mxfUL OPAtomTrackReader::GetEssenceContainerLabel() { return mEssenceParser->GetEssenceContainerLabel(); } int64_t OPAtomTrackReader::GetDuration() { int64_t essence_duration = mEssenceParser->GetDuration(); if (mDurationInMetadata > 0 && essence_duration > mDurationInMetadata) return mDurationInMetadata; else return essence_duration; } int64_t OPAtomTrackReader::DetermineDuration() { int64_t essence_duration = mEssenceParser->DetermineDuration(); if (mDurationInMetadata > 0 && essence_duration > mDurationInMetadata) return mDurationInMetadata; else return essence_duration; } int64_t OPAtomTrackReader::GetPosition() { return mEssenceParser->GetPosition(); } bool OPAtomTrackReader::Read(OPAtomContentPackage *content) { OPAtomContentElement *element = content->GetEssenceData(mTrackId, false); if (!element) { element = content->AddElement(mTrackId); element->mMaterialTrackId = mTrackId; element->mIsPicture = mIsPicture; } int64_t essence_offset = mEssenceParser->GetEssenceOffset(); if (!mEssenceParser->Read(&element->mEssenceData, &element->mNumSamples)) return false; element->mEssenceOffset = essence_offset; return true; } bool OPAtomTrackReader::Seek(int64_t position) { return mEssenceParser->Seek(position); } bool OPAtomTrackReader::IsEOF() { return mEssenceParser->IsEOF(); } --- NEW FILE: OPAtomContentPackage.h --- /* * $Id: OPAtomContentPackage.h,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Holds the essence data for each track in the clip * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #ifndef __OPATOM_CONTENT_PACKAGE_H__ #define __OPATOM_CONTENT_PACKAGE_H__ #include <map> #include <vector> #include "DynamicByteArray.h" class OPAtomClipReader; class OPAtomTrackReader; class OPAtomContentElement { public: friend class OPAtomClipReader; friend class OPAtomTrackReader; public: OPAtomContentElement(); bool IsPicture() const { return mIsPicture; } uint32_t GetMaterialTrackId() const { return mMaterialTrackId; } const DynamicByteArray* GetEssenceData() const { return &mEssenceData; } uint32_t GetSize() const { return mEssenceData.getSize(); } const unsigned char* GetBytes() const { return mEssenceData.getBytes(); } int64_t GetEssenceOffset() const { return mEssenceOffset; } uint32_t GetNumSamples() const { return mNumSamples; } private: void Reset(); private: bool mIsPicture; uint32_t mMaterialTrackId; DynamicByteArray mEssenceData; int64_t mEssenceOffset; uint32_t mNumSamples; }; class OPAtomContentPackage { public: friend class OPAtomClipReader; friend class OPAtomTrackReader; public: OPAtomContentPackage(); virtual ~OPAtomContentPackage(); int64_t GetPosition() const { return mPosition; } bool HaveEssenceData(uint32_t mp_track_id) const; const OPAtomContentElement* GetEssenceData(uint32_t mp_track_id) const; const unsigned char* GetEssenceDataBytes(uint32_t mp_track_id) const; uint32_t GetEssenceDataSize(uint32_t mp_track_id) const; size_t NumEssenceData() const { return mEssenceDataVector.size(); } const OPAtomContentElement* GetEssenceDataI(size_t index) const; const unsigned char* GetEssenceDataIBytes(size_t index) const; uint32_t GetEssenceDataISize(size_t index) const; private: OPAtomContentElement* GetEssenceData(uint32_t mp_track_id, bool check) const; OPAtomContentElement* AddElement(uint32_t mp_track_id); private: int64_t mPosition; std::vector<OPAtomContentElement*> mEssenceDataVector; std::map<uint32_t, OPAtomContentElement*> mEssenceData; }; #endif --- NEW FILE: RawEssenceParser.cpp --- /* * $Id: RawEssenceParser.cpp,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Parse raw essence data * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #include <libMXF++/MXF.h> #include <mxf/mxf_labels_and_keys.h> #include <mxf/mxf_avid_labels_and_keys.h> #include "RawEssenceParser.h" #include "FixedSizeEssenceParser.h" #include "VariableSizeEssenceParser.h" #include "PCMEssenceParser.h" using namespace std; using namespace mxfpp; #define NUM_SUPPORTED_FORMATS (sizeof(SUPPORTED_FORMATS) / sizeof(SupportedFormat)) typedef enum { FIXED_FRAME_SIZE_PARSER, PCM_PARSER, VARIABLE_FRAME_SIZE_PARSER } ParserType; typedef struct { const mxfUL essence_label; ParserType parser_type; uint32_t fixed_frame_size; } SupportedFormat; const SupportedFormat SUPPORTED_FORMATS[] = { {MXF_EC_L(IECDV_25_525_60_ClipWrapped), FIXED_FRAME_SIZE_PARSER, 120000}, {MXF_EC_L(IECDV_25_625_50_ClipWrapped), FIXED_FRAME_SIZE_PARSER, 144000}, {MXF_EC_L(DVBased_25_525_60_ClipWrapped), FIXED_FRAME_SIZE_PARSER, 120000}, {MXF_EC_L(DVBased_25_625_50_ClipWrapped), FIXED_FRAME_SIZE_PARSER, 144000}, {MXF_EC_L(DVBased_50_525_60_ClipWrapped), FIXED_FRAME_SIZE_PARSER, 240000}, {MXF_EC_L(DVBased_50_625_50_ClipWrapped), FIXED_FRAME_SIZE_PARSER, 288000}, {MXF_EC_L(DVBased_100_1080_50_I_ClipWrapped), FIXED_FRAME_SIZE_PARSER, 576000}, {MXF_EC_L(DVBased_100_720_50_P_ClipWrapped), FIXED_FRAME_SIZE_PARSER, 288000}, {MXF_EC_L(DNxHD1080i120ClipWrapped), FIXED_FRAME_SIZE_PARSER, 606208}, {MXF_EC_L(DNxHD1080i185ClipWrapped), FIXED_FRAME_SIZE_PARSER, 917504}, {MXF_EC_L(DNxHD1080p36ClipWrapped), FIXED_FRAME_SIZE_PARSER, 188416}, {MXF_EC_L(DNxHD720p120ClipWrapped), FIXED_FRAME_SIZE_PARSER, 303104}, {MXF_EC_L(DNxHD720p185ClipWrapped), FIXED_FRAME_SIZE_PARSER, 458752}, {MXF_EC_L(SD_Unc_625_50i_422_135_ClipWrapped), FIXED_FRAME_SIZE_PARSER, 0}, {MXF_EC_L(HD_Unc_1080_50i_422_ClipWrapped), FIXED_FRAME_SIZE_PARSER, 0}, {MXF_EC_L(AvidIMX30_625_50), FIXED_FRAME_SIZE_PARSER, 0}, {MXF_EC_L(AvidIMX40_625_50), FIXED_FRAME_SIZE_PARSER, 0}, {MXF_EC_L(AvidIMX50_625_50), FIXED_FRAME_SIZE_PARSER, 0}, {MXF_EC_L(AvidIMX30_525_60), FIXED_FRAME_SIZE_PARSER, 0}, {MXF_EC_L(AvidIMX40_525_60), FIXED_FRAME_SIZE_PARSER, 0}, {MXF_EC_L(AvidIMX50_525_60), FIXED_FRAME_SIZE_PARSER, 0}, {MXF_EC_L(AvidMJPEGClipWrapped), VARIABLE_FRAME_SIZE_PARSER, 0}, {MXF_EC_L(BWFClipWrapped), PCM_PARSER, 0}, {MXF_EC_L(AES3ClipWrapped), PCM_PARSER, 0}, }; RawEssenceParser* RawEssenceParser::Create(File *file, int64_t essence_length, mxfUL essence_label, const FileDescriptor *file_descriptor, mxfRational edit_rate, uint32_t frame_size, FrameOffsetIndexTableSegment *index_table) { int64_t fixed_frame_size; size_t i; for (i = 0; i < NUM_SUPPORTED_FORMATS; i++) { if (mxf_equals_ul(&essence_label, &SUPPORTED_FORMATS[i].essence_label)) { switch (SUPPORTED_FORMATS[i].parser_type) { case FIXED_FRAME_SIZE_PARSER: { fixed_frame_size = SUPPORTED_FORMATS[i].fixed_frame_size; if (fixed_frame_size == 0) fixed_frame_size = frame_size; return new FixedSizeEssenceParser(file, essence_length, essence_label, file_descriptor, fixed_frame_size); } case PCM_PARSER: { // TODO: edit rate passed in should be the video edit rate mxfRational edit_rate = (mxfRational){25, 1}; return new PCMEssenceParser(file, essence_length, essence_label, edit_rate, file_descriptor); } case VARIABLE_FRAME_SIZE_PARSER: { return new VariableSizeEssenceParser(file, essence_length, essence_label, edit_rate, file_descriptor, index_table); } } } } return 0; } RawEssenceParser::RawEssenceParser(File *file, int64_t essence_length, mxfUL essence_label) { mFile = file; mEssenceLength = essence_length; mEssenceLabel = essence_label; mPosition = 0; mDuration = -1; mEssenceOffset = 0; mEssenceStartOffset = mFile->tell(); } RawEssenceParser::~RawEssenceParser() { delete mFile; } bool RawEssenceParser::IsEOF() { return mDuration >= 0 && mPosition >= mDuration; } --- NEW FILE: FrameOffsetIndexTable.h --- /* * $Id: FrameOffsetIndexTable.h,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Basic index table with frame offsets * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #ifndef __MXFPP_FRAME_OFFSET_INDEX_TABLE_H__ #define __MXFPP_FRAME_OFFSET_INDEX_TABLE_H__ #include <vector> #include "libMXF++/IndexTable.h" class FrameOffsetIndexTableSegment : public mxfpp::IndexTableSegment { public: static FrameOffsetIndexTableSegment* read(mxfpp::File *file, uint64_t segment_len); public: FrameOffsetIndexTableSegment(); virtual ~FrameOffsetIndexTableSegment(); bool haveFrameOffset(int64_t position); int64_t getFrameOffset(int64_t position); bool getLastIndexOffset(int64_t *offset, int64_t *position); int64_t getDuration(); void appendFrameOffset(int64_t offset); private: std::vector<int64_t> mFrameOffsets; }; #endif --- NEW FILE: FrameOffsetIndexTable.cpp --- /* * $Id: FrameOffsetIndexTable.cpp,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Basic index table with frame offsets * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #include <memory> #include <libMXF++/MXF.h> #include <mxf/mxf_avid.h> #include "FrameOffsetIndexTable.h" using namespace std; using namespace mxfpp; int add_frame_offset_index_entry(void *data, uint32_t num_entries, MXFIndexTableSegment *segment, int8_t temporal_offset, int8_t key_frame_offset, uint8_t flags, uint64_t stream_offset, uint32_t *slice_offset, mxfRational *pos_table) { FrameOffsetIndexTableSegment *index_table = static_cast<FrameOffsetIndexTableSegment*>(data); (void)segment; (void)temporal_offset; (void)key_frame_offset; (void)flags; (void)slice_offset; (void)pos_table; index_table->appendFrameOffset(stream_offset); return 1; } FrameOffsetIndexTableSegment* FrameOffsetIndexTableSegment::read(File* file, uint64_t segment_len) { auto_ptr<FrameOffsetIndexTableSegment> index_table(new FrameOffsetIndexTableSegment()); mxf_free_index_table_segment(&index_table->_cSegment); MXFPP_CHECK(mxf_avid_read_index_table_segment_2(file->getCFile(), segment_len, mxf_default_add_delta_entry, 0, add_frame_offset_index_entry, index_table.get(), &index_table->_cSegment)); return index_table.release(); } FrameOffsetIndexTableSegment::FrameOffsetIndexTableSegment() : IndexTableSegment() { setIndexDuration(-1); } FrameOffsetIndexTableSegment::~FrameOffsetIndexTableSegment() { } bool FrameOffsetIndexTableSegment::haveFrameOffset(int64_t position) { return position < (int64_t)mFrameOffsets.size(); } int64_t FrameOffsetIndexTableSegment::getFrameOffset(int64_t position) { MXFPP_CHECK(position < (int64_t)mFrameOffsets.size()); return mFrameOffsets[position]; } bool FrameOffsetIndexTableSegment::getLastIndexOffset(int64_t *offset, int64_t *position) { if (mFrameOffsets.empty()) return false; *position = mFrameOffsets.size() - 1; *offset = mFrameOffsets[*position]; return true; } int64_t FrameOffsetIndexTableSegment::getDuration() { if (getIndexDuration() >= 0) return getIndexDuration(); // -1 because the last entry is the last frame's end offset return mFrameOffsets.size() - 1; } void FrameOffsetIndexTableSegment::appendFrameOffset(int64_t offset) { if (mFrameOffsets.size() == mFrameOffsets.capacity()) mFrameOffsets.reserve(mFrameOffsets.capacity() + 8192); return mFrameOffsets.push_back(offset); } --- NEW FILE: VariableSizeEssenceParser.cpp --- /* * $Id: VariableSizeEssenceParser.cpp,v 1.1 2009/10/22 16:37:31 philipn Exp $ * * Parse raw essence data with variable frame size * * Copyright (C) 2009 British Broadcasting Corporation. * All Rights Reserved. * * Author: Philip de Nier * * 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 the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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. */ #include <libMXF++/MXF.h> #include <mxf/mxf_labels_and_keys.h> #include <mxf/mxf_avid.h> #include "VariableSizeEssenceParser.h" using namespace std; using namespace mxfpp; MJPEGParseState::MJPEGParseState() { resolution_id = 0; position = 0; prev_position = 0; end_of_field = false; field2 = false; skip_count = 0; have_len_byte1 = false; have_len_byte2 = false; marker_state = 0; } void MJPEGParseState::Reset() { position = 0; prev_position = 0; end_of_field = false; field2 = false; skip_count = 0; have_len_byte1 = false; have_len_byte2 = false; marker_state = 0; buffer.setSize(0); } VariableSizeEssenceParser::VariableSizeEssenceParser(File *file, int64_t essence_length, mxfUL essence_label, mxfRational edit_rate, const FileDescriptor *file_descriptor, FrameOffsetIndexTableSegment *index_table) : RawEssenceParser(file, essence_length, essence_label) { (void)edit_rate; mFrameSizeEstimate = 0; if (index_table) { mIndexTable = index_table; mOwnIndexTable = false; mIndexTableIsComplete = true; mDuration = mIndexTable->getDuration(); } else { // currently only support MJPEG MXFPP_ASSERT(mxf_equals_ul(&MXF_EC_L(AvidMJPEGClipWrapped), &essence_label)); mIndexTable = new FrameOffsetIndexTableSegment(); mOwnIndexTable = true; mIndexTable->appendFrameOffset(0); mIndexTableIsComplete = false; mDuration = -1; uint32_t unc_frame_size = DetermineUncFrameSize(file_descriptor); if (file_descriptor->haveItem(&MXF_ITEM_K(GenericPictureEssenceDescriptor, ResolutionID))) mMJPEGParseState.resolution_id = file_descriptor->getUInt32Item(&MXF_ITEM_K(GenericPictureEssenceDescriptor, ResolutionID)); else mxf_log_warn("Missing Avid ResolutionID item in descriptor\n"); switch (mMJPEGParseState.resolution_id) { case g_AvidMJPEG21_ResolutionID: mFrameSizeEstimate = unc_frame_size / (2 * 0.8); break; case g_AvidMJPEG31_ResolutionID: mFrameSizeEstimate = unc_frame_size / (3 * 0.8); break; case g_AvidMJPEG101_ResolutionID: ... [truncated message content] |